Sunucu yetkisi teknikleri

*Bu içerik, yapay zekâ (beta) kullanılarak çevrildi ve hatalar içerebilir. Sayfayı İngilizce görüntülemek için buraya tıkla.

Bu kılavuz, sunucu yetkisi modelini kullanarak yüksek kaliteli, akıcı çok oyunculu oyunlar oluşturmak için çeşitli teknikleri özetlemektedir.

Öngörücü örnek oluşturma (örnek dikişleme)

Örnek dikişleme, istemci betiklerinin RunService:BindToSimulation() geri çağırmaları içinde Instances'ı öngörücü bir şekilde oluşturmasına olanak tanır. İstemci, sunucuya gidiş-dönüş beklemeden Instance'ı hemen oluşturur; sunucunun yetkili kopyası geldiğinde, istemci tarafından oluşturulan örnek ve sunucunun yetkili kopyası birleştirilir. Betiğinizin bakış açısından, Instance hemen var olur ve sunucu ile tutarlıdır.

Örnek dikişleme, bir örneğin mümkün olan en kısa sürede istemcide görünür ve aktif olması gerektiği durumlarda faydalıdır. Sunucu, istemcinin ihtiyaç duyduğu herhangi bir örneği (dünyaya yaptıkları etkilerle birlikte) sonunda çoğaltacak olsa da, bu işlem sunucu iletişimi nedeniyle en az bir gidiş-dönüş gecikmesine neden olur. Örnekler arasında bir roketatar ateşlemek ve fizik kısıtlamaları oluşturmak yer alır — dikişleme olmadan, istemci roketin onlardan uzakta patladığını görecek veya yeni kısıtlamalar kendilerine çoğaltıldığında bazı titremeler yaşayacaktır.

Teknik davranış

Örnek dikişleme, istemci ve sunucu üzerinde aynı belirleyici GUID'yi üreterek çalışır. GUID, oluşturulan Instance türü, kaynağın kimliği (aşağıya bakın), mevcut simülasyon karesi ve her karede sıfırlanan bir betik çağrı sayacı olmak üzere dört girdi ile türetilir.

Eğer istemci ve sunucu girdiler üzerinde hemfikir olursa, eşleşen GUID'ler üretir ve dikişleme başarılı olur.

Uygulama

Örnek dikişlemesini kullanmak için, ModuleScript içinde hem istemci hem de sunucu üzerinde gereksinim duyulan bir BindToSimulation() geri çağrısı içinde Instance.new(), Instance:Clone() veya Instance.fromExisting() çağrısını yapın. Sizin tarafınızda başka bir şey gerekmiyor; sistem GUID atamasını ve uzlaştırmayı otomatik olarak halleder.

Bir örneği DataModel'a ebeveyn olmadan önce, Name, Size veya Parent gibi simülasyon erişimi olmayan özellikleri serbestçe ayarlayabilirsiniz.

Simülasyon (ModuleScript) - BindToSimulation() Geri Çağrısında Örnek Oluştur
local RunService = game:GetService("RunService")
local Simulation = {}
Simulation.Initialize = function()
RunService:BindToSimulation(function(deltaTime)
local part = Instance.new("Part")
part.Name = "TahminEdilenParça"
part.Size = Vector3.new(2, 2, 2)
part.Parent = workspace -- Parça artık veri modelinde; simülasyon erişim dışı değişiklikler bundan sonra hata verecektir
-- Parça istemcide hemen var olur ve sunucu ile uzlaştırılacaktır
end)
end
return Simulation

Instance:Clone() ve Instance.fromExisting() doğru bir şekilde dikişlenir, eğer kaynak örnek hem istemciye hem de sunucuya çoğaltılmışsa; her iki taraf da eşleşen kaynak GUID'lerinden kopyalar ve eşleşen tahmin edilen GUID'ler üretir.

Simülasyon (ModuleScript) - BindToSimulation() Geri Çağrısında Örnek Kopyala
local RunService = game:GetService("RunService")
local Simulation = {}
local sourceTemplate -- çoğaltılmış bir Örnek
Simulation.Initialize = function()
RunService:BindToSimulation(function(deltaTime)
local cloned = sourceTemplate:Clone()
cloned.Parent = workspace
-- Kopyalanan hiyerarşi sunucunun yetkili kopyası ile dikişlenir
end)
end
return Simulation

Konum yumuşatma

Yanlış tahmin edilen senkronize nesnelerin konumunu görsel olarak yumuşatmak için, simüle edilen nesneden farklı bir nesne render edebilirsiniz.

  1. Simüle edilen nesneyi görünmez yapın.
  2. Simüle edilen nesneyi takip etmek için kütlesiz, çarpışmayan, yalnızca görsel bir kopya olan bir renderleyici nesnesi oluşturun.
  3. Görünmez, simüle edilen nesnenin konumunu yumuşak bir şekilde takip eden bir betik renderleyici nesnesine ekleyin. Renderleme ve simülasyon arasındaki bu ayrım, görsel olarak yumuşak bir deneyim oluşturmak için renderleyici nesnesinin konumunu değiştirmeye olanak tanır.

Aşağıdaki örnek Script, render edilen nesnenin (ebeveyni) simüle edilen nesneyi yumuşak bir şekilde takip etmesini sağlar. Render edilen nesne, genellikle kabul edilebilir olan simüle edilen nesneden her zaman biraz "geride" kalır, ancak bazı durumlarda istenmeyen olabilir.

Renderleyici Parça ile BasePart Konumunu Yumuşak Bir Şekilde Takip Et
local RunService = game:GetService("RunService")
local TweenService = game:GetService("TweenService")
-- Yumuşak bir şekilde takip edilecek nesne
local smoothTarget:BasePart = workspace.SimulatedPart
-- Yumuşatılacak görsel nesne
local renderer:BasePart = script.Parent
-- Yumuşatma süresi; daha küçük daha hızlı demektir
local smoothTime = 0.07
-- Yumuşak konumu hesaplamak için gereken verileri saklayın
local smoothVelocity = Vector3.new()
-- Renderleyici nesnesinin fiziğini devre dışı bırakın
renderer.Massless = true
renderer.Anchored = true
renderer.CanCollide = false
RunService.RenderStepped:Connect(function(deltaTime: number)
-- Hedef nesneyi yumuşak bir şekilde takip et
local smoothPosition, smoothVelocity = TweenService:SmoothDamp(
renderer.Position,
smoothTarget.Position,
smoothVelocity,
smoothTime,
math.huge,
deltaTime)
renderer.Position = smoothPosition
end)

Soccer örnek oyunu, futbol topu için konum yumuşatmayı daha akıllıca açıp kapatmak için bu tekniğin bir varyasyonunu kullanır. Özellikle, futbol topu yalnızca simüle edilen top "yeterince" uzakta "zıpladığında" konumunu yumuşatır. Bu yaklaşım, her iki dünyanın en iyisini sunar: futbol topu normal koşullar altında görsel gecikme yaşamaz ve oyun, simüle edilen top beklenmedik bir şekilde yeni bir konuma zıpladığında, muhtemelen bir ağ artefaktı veya sunucu tarafı değişikliği nedeniyle, konumunu yumuşak bir şekilde iç içe geçirir.

Animasyon kodu yazma

Sunucu yetkisi altında, istemcinin simülasyonu, sunucu bir yanlış tahmini düzelttiğinde geri alınabilir ve yeniden simüle edilebilir. Geri alma sırasında, animasyon durumu geri sarılır, bu da daha önceki karelerde önbelleğe aldığınız AnimationTrack'lerin artık geçerli olmayabileceği anlamına gelir.

Animasyon mantığını yansıtma

Herhangi bir temel oyun mekaniği gibi, animasyonları kontrol etme mantığı sunucu ve istemci arasında senkronize olmalıdır, aksi takdirde yanlış tahminler ve titrek davranışlar olabilir. RunService:BindToSimulation() aracılığıyla işlevleri bağlayan bir desen için simülasyon senkronizasyonuna bakın; bu, hem istemci hem de sunucu üzerinde başlatılan bir ModuleScript içinde yapılır.

Parça önbelleklemeden kaçınma

Sunucu yetkisi olmayan betiklerde yaygın bir desen, AnimationTrack nesnelerini yükleme zamanında önbelleğe almak ve sonsuza dek yeniden kullanmaktır. Bu desen, sunucu bir yanlış tahmini düzelttiğinde ve istemci, düzeltme verileri ile simülasyonunu geri sarıp yeniden oynattığında, sunucu yetkili bir oyunda başarısız olur. Eğer betiğiniz hala durdurulmuş veya değiştirilmiş bir parçaya referans tutuyorsa, AdjustWeight() veya AdjustSpeed() gibi çağrılar, artık görsel olarak temsil edilmeyen bir parçayı etkiler.

İstemcide Parçaları Önbelleğe Al (Güvenilmez)
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local player = Players.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local humanoid = character:WaitForChild("Humanoid")
local animator = humanoid:WaitForChild("Animator")
-- Animasyon parçalarını önbelleğe al
local tracks = {}
tracks["WalkForward"] = animator:LoadAnimation(walkForwardAnim)
RunService:BindToSimulation(function(dt: number)
tracks["WalkForward"]:AdjustSpeed(1 + math.cos(time()))
end)

Parça nesnelerine tutunmak yerine, animasyon kimliklerini (veya Animation örneklerini) saklayın ve etkileşimde bulunmanız gerektiğinde canlı parçayı almak için Animator'ı sorgulayın. Bunun için iki API mevcuttur:

  • Animator:GetTrackByAnimationId() — Belirli bir animasyon kimliği için şu anda aktif olan parçayı döndürür veya o kimlikle aktif animasyon yoksa nil döner. Bu, hangi belirli animasyonu aradığınızı bildiğinizde kullanılır.
  • Animator:GetPlayingAnimationTracks() — Tüm aktif parçaları (çalan, azalan veya duraklatılmış) döndürür. Bu, aktif olan her şeyi (örneğin, tüm animasyonları durdurmak veya bazı kriterlere göre parçaları bulmak için) yinelemek istediğinizde kullanılır.

ModuleScript adı CustomAnimate olan ReplicatedStorage içinde:

CustomAnimate
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")
local CustomAnimate = {}
-- Animasyon referanslarını saklayın (yüklenmiş parçalar değil)
local animations = {
WalkForward = ReplicatedStorage.Animations.WalkForward,
}
local function getOrLoadTrack(animator: Animator, animation: Animation): AnimationTrack
local track = animator:GetTrackByAnimationId(animation.AnimationId)
if not track then
track = animator:LoadAnimation(animation)
end
return track
end
CustomAnimate.SyncAnimations = function(character)
local humanoid = character:WaitForChild("Humanoid")
local animator = humanoid:WaitForChild("Animator")
RunService:BindToSimulation(function(dt: number)
local walkTrack = getOrLoadTrack(animator, animations.WalkForward)
if not walkTrack.isPlaying then
walkTrack.Looped = true
walkTrack.Priority = Enum.AnimationPriority.Core
walkTrack:Play()
end
walkTrack:AdjustSpeed(1 + math.cos(time()))
end)
end
return CustomAnimate

Sesleri ve görsel efektleri oynatma

Öngörücü bir simülasyonda, istemcinin olacağını öngördüğü ancak sunucuda gerçekleşmeyen olaylar için efektler veya sesler tetiklemek mümkündür. Renderleme sistemi, yanlış tahmin edilen efektleri "geri almak" için hazırlıklı olmalıdır. Örneğin, bir istemci bir el bombası patladığını öngörebilir ve bir parçacık efekti tetikleyebilir, ancak başka bir oyuncu el bombasını etkisiz hale getirdiyse, istemci parçacık efektini gizlemelidir.

Öngörücü bir simülasyonu renderlemek için iyi bir strateji, simülasyon döngüsü içinde bir durum makinesi desenini senkronize etmek ve durumu bir render adım işlevinde değiştirmektir. Aşağıdaki örnek, bir el bombasını durum makinesi deseni ile simüle eder:

Bir El Bombası Takip Etmek için Basit Durum Makinesi (ModuleScript)
local module = {}
module.GrenadeStates = {
Idle = 0,
Lit = 1,
Exploded = 2,
Defused = 3,
}
module.GrenadeExplodeTime = 3.0
module.Initialize = function(grenade)
RunService:BindToSimulation(function(deltaTime)
-- Boş el bombası durumunu başlat
local grenadeState = grenade:GetAttribute("State")
if grenadeState == nil then
grenadeState = module.GrenadeStates.Idle
grenade:SetAttribute("State", grenadeState)
grenade:SetAttribute("Timer", 0.0)
end
-- El bombası zamanlayıcısını artır
local timer = grenade:GetAttribute("Timer")
timer = timer + deltaTime
grenade:SetAttribute("Timer", timer)
-- Alevlenmiş el bombalarını patlat
if grenadeState == module.GrenadeStates.Lit then
if timer >= module.GrenadeExplodeTime then
grenadeState = module.GrenadeStates.Exploded
grenade:SetAttribute("State", grenadeState)
grenade:SetAttribute("Timer", 0.0)
end
end
end)
end
return module

Önceki durum makinesi ile, senkronize el bombası durumuna dayalı olarak ayrı bir betinde el bombası efektlerini renderleyebilirsiniz:

Senkrone El Bombası Durumuna Dayalı Parçacıkları ve Sesleri Renderle
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")
local Simulation = require(ReplicatedStorage.Simulation)
local grenade = script.Parent
local previousGrenadeState = nil
-- El bombası durumunu belirtmek için örneği vurgula
local highlight = Instance.new("Highlight")
highlight.Parent = grenade
highlight.FillTransparency = 1
highlight.OutlineTransparency = 1
highlight.DepthMode = Enum.HighlightDepthMode.Occluded
RunService.RenderStepped:Connect(function(deltaTime: number)
local grenadeState = grenade:GetAttribute("State")
local grenadeTimer = grenade:GetAttribute("Timer")
-- El bombası alevlendiğinde alevli parçacıkları yay
grenade.LitEmitter.Enabled = grenadeState == Simulation.GrenadeStates.Lit
-- El bombası yeni patladığında patlama emiterini oynat
if previousGrenadeState ~= grenadeState then
if grenadeState == Simulation.GrenadeStates.Exploded and grenadeTimer < 0.2 then
grenade.ExplosionEmitter:Emit(100)
grenade.ExplosionSound:Play()
end
previousGrenadeState = grenadeState
end
-- El bombasının durumuna ve zamana göre vurgulama rengini değiştir
if grenadeState == Simulation.GrenadeStates.Lit then
highlight.FillColor = Color3.fromRGB(255, 0, 0)
highlight.FillTransparency = 1 - (grenadeTimer / Simulation.GrenadeExplodeTime)
elseif grenadeState == Simulation.GrenadeStates.Idle then
highlight.FillTransparency = 1
elseif grenadeState == Simulation.GrenadeStates.Exploded then
highlight.FillTransparency = 1
elseif grenadeState == Simulation.GrenadeStates.Defused then
highlight.FillColor = Color3.fromRGB(0, 255, 125)
highlight.FillTransparency = 0.5
end
end)

Ağ gecikmesine göre tasarım yapma

Belirli oyun mekaniği türleri, ağ üzerinden çok oyunculu oyunlara daha iyi uyum sağlar. Oyuncular, başka bir oyuncunun bir eylemi gerçekleştirdiği an ile o oyuncunun girişini aldıkları an arasında her zaman bir gecikme olacaktır. Süper akıcı bir çok oyunculu oyun oluşturmanın en iyi yolu, oyununuzu bu sınırlamaları göz önünde bulundurarak tasarlamaktır.

Örneğin, oyuncu hareketinde daha yavaş bir ivmeye sahip bir oyun, daha yüksek ivmeye sahip bir oyundan daha akıcı görünecektir çünkü ağ gecikmesinin neden olduğu konum farkı, daha yüksek ivmeye sahip bir oyundaki kadar büyük olmayacaktır.

Başka bir örnek olarak, oyuncuların bir girişi basarak anında büyük bir patlama tetikleyebileceği bir oyun mekaniği, patlamanın girişten sonra gecikmeli olduğu bir mekaniğe göre daha fazla ağ artefaktına sahip olacaktır, sanki bir fitili ateşliyormuş gibi. Bu, yeniden simülasyonu patlama etkisi yerine fitil etkisi üzerinde yapar ki bu, daha az fark edilen bir ağ artefaktıdır.

Diğer oyuncu girişlerini tahmin etme

Varsayılan olarak, Roblox her istemciden gelen girişleri diğer tüm istemcilere iletmez. Bunun oyununuz için doğru olup olmadığı, tasarımına bağlıdır:

  • Temel insansı hareket için, varsayılan davranış, diğer oyuncu karakterlerinin hareketlerinin yetkili sunucu durumundan dışarıya çıkarılmadığı anlamına gelir ve bu nedenle diğer oyuncu karakterleri yanlış tahmin etmeyecek, ancak biraz geçmişte render edilecektir.
  • Bir yarış oyununda ise, varsayılan davranış, istemcilerin diğer oyuncuların gaz pedalını veya diğer girişleri uygulayıp uygulamadıklarını bilmeyecekleri anlamına gelir, bu nedenle diğer arabalar yerel oyuncunun arkasında görünebilir, oysa aslında öndedirler. Bunu hafifletmek için, oyuncu girişlerini özellikler üzerinde saklayabilir ve bu senkronize özellikler üzerinde RunService:BindToSimulation() kullanarak işlem yapabilirsiniz; bu, aşağıdaki kod örneğinde ve Racing şablonunda gösterilmiştir. Bu yaklaşım, özellikleri simülasyonunuza giriş olarak kullanmanıza olanak tanır ve tamamen çoğaltılmış oyuncu girişlerine sahip olursunuz.
Özelliklerde Oyuncu Girişini Saklama (ModuleScript)
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local module = {}
module.storePlayerInput = function(player:Player, humanoidRootPart:BasePart)
local inputContext:InputContext = player.PlayerGui.InputContext
local throttle = inputContext.DefuseAction:GetState()
humanoidRootPart:SetAttribute("Throttle", throttle)
-- Diğer girişleri özelliklere yazın...
end
module.Initialize = function()
RunService:BindToSimulation(function(deltaTime)
if RunService:IsServer() then
-- Girişleri sunucudan tüm istemcilere ilet
for _, player in Players:GetPlayers() do
local humanoidRootPart:BasePart = player.Character.HumanoidRootPart
local inputContext:InputContext = player.PlayerGui.InputContext
module.storePlayerInput(player, humanoidRootPart)
end
else
-- Yerel oyuncu girişlerini özellikler olarak yaz
local player = Players.LocalPlayer
local humanoidRootPart:BasePart = player.Character.HumanoidRootPart
local inputContext:InputContext = player.PlayerGui.InputContext
module.storePlayerInput(player, humanoidRootPart)
end
-- Özellikleri oyunun girişleri olarak kullan
for _, player in Players:GetPlayers() do
local humanoidRootPart:BasePart = player.Character.HumanoidRootPart
local throttle = humanoidRootPart:GetAttribute("Throttle")
if throttle then
-- Gaz pedalını oyuncunun aracına uygula
end
end
end)
end)
return module

Hata ayıklama

Sunucu yetkili bir oyunu hata ayıklamak için kullanabileceğiniz bazı yeni araçlar ve teknikler vardır.

Sunucu yetkisi görselleştirici

CtrlShiftF6 (Windows) veya ⌘ShiftF6 (Mac) tuşlarına basmak, Studio'nun sunucu yetkisi görselleştiricisini açar; bu, birkaç anahtar bilgiyi gösterir:

AyrıntılarAçıklama
Örnek tahmin başarı oranıSon 8 saniye içinde doğru tahmin edilen örneklerin yüzdesi.
Giriş kabul oranıSunucuda zamanında gelen tüm oyuncu girişlerinin yüzdesi. Geç gelen girişler bu sayıyı düşürecektir.
İstemci-sunucu adım deltaİstemci ile sunucu arasındaki kare sayısı, istemcinin katılma süresi dahil. Bu sayının istikrarı, sunucuya bağlantınızın istikrarını temsil eder.
RCC kalp atışı FPSSunucudaki simülasyonun kare hızı. Bu sayı 59'un altına düşerse, sunucu simülasyonla başa çıkamaz ve oyun kalitesi düşecektir.
Tahmin edilen örnek sayısıİstemcinin tahmin ettiği örnek sayısı.
Giriş düşme nedeni sayıları

Sunucunun her neden için bir girişi düşürdüğü zaman sayısı:

  • [x] çok eski — Girişler geç geldi, bu da ağınızın kötüleştiği veya istemcinin simülasyonla başa çıkamadığı anlamına gelir.
  • [x] sıralı değil — Girişlerinizin yeniden düzenlenip atılmasına neden olan bir ağ hatası meydana geldi.
  • [x] tampon dolu — Sunucu girişinizi tamponlayamadı. Ya ağınız aniden iyileşti ya da sunucu simülasyonla başa çıkamadı.

Simülasyon yarıçapı

Otomatik tahmine (Enum.PredictionMode.Automatic) güveniyorsanız, Studio ayarlarında Bölge Etkin mi seçeneğini etkinleştirerek oyuncu karakterinizin etrafındaki tahmin yarıçapını görselleştirebilirsiniz (AltS Windows'ta; ⌥S Mac'te). Yeşil silindir, örneklerin tahmin edildiği karakterinizin etrafındaki alanı gösterir ve yarıçapı cihazın performans özelliklerine göre büyür ve küçülür.

Sunucu yetkisi çalışırken oyuncu karakterinin etrafındaki simülasyon yarıçapı
©2026 Roblox Corporation. Roblox, the Roblox logo and Powering Imagination are among our registered and unregistered trademarks in the U.S. and other countries.