Урок 14 из 15

Сеть: задержка, репликация и владение объектом

Понимать, что происходит между клиентом и сервером, и не перегружать сеть.

Теория

Между действием игрока и ответом сервера всегда проходит время — пинг. На нормальном соединении это десятки миллисекунд, на мобильном интернете бывает в разы больше. Игра, которая ждёт ответ на каждое движение, ощущается вязкой.

Поэтому клиент показывает результат сразу, а сервер подтверждает следом. Показ — это только показ: счёт, урон и предметы остаются за сервером. Если серверу нужно поправить клиента, он просто присылает верное состояние.

Физикой движущейся детали управляет одна из сторон — это и есть network ownership. Персонажем обычно владеет его клиент: так управление отзывчивое. Важными для игры объектами лучше владеть серверу, иначе их поведение зависит от чужой машины.

  • Одно событие с таблицей лучше десяти событий подряд
  • Не отправляй позицию каждый кадр — движок уже реплицирует персонажа сам
  • SetNetworkOwner(nil) отдаёт деталь серверу
  • Проверяй игру при плохом соединении: в Studio есть эмуляция задержки

Пример

Сервер копит изменения и отправляет их пачкой раз в полсекунды.

ServerScriptService/Broadcast.lua
  1. local ReplicatedStorage = game:GetService("ReplicatedStorage")
  2. local update = ReplicatedStorage:WaitForChild("ScoreUpdate")
  3.  
  4. local pending = {}
  5.  
  6. local function queue(player, score)
  7. pending[player.Name] = score
  8. end
  9.  
  10. task.spawn(function()
  11. while true do
  12. task.wait(0.5)
  13.  
  14. if next(pending) then
  15. update:FireAllClients(pending)
  16. pending = {}
  17. end
  18. end
  19. end)
  20.  
  21. return queue

Одна посылка с таблицей вместо потока мелких сообщений: канал разгружается.

Задача

Есть деталь-снаряд, которую сервер запускает в полёт. Отдай владение физикой серверу и объясни в комментарии, почему не клиенту.

  • SetNetworkOwner работает только у незакреплённых деталей
  • Владелец физики определяет, где она считается на самом деле
Показать решение
ServerScriptService/Solution.lua
  1. local projectile = Instance.new("Part")
  2. projectile.Size = Vector3.new(1, 1, 1)
  3. projectile.Position = Vector3.new(0, 10, 0)
  4. projectile.Anchored = false
  5. projectile.Parent = workspace
  6.  
  7. -- Физику снаряда считает сервер: попадание влияет на счёт,
  8. -- а значит решение о нём не должно зависеть от машины игрока.
  9. projectile:SetNetworkOwner(nil)
  10.  
  11. projectile.AssemblyLinearVelocity = Vector3.new(0, 0, -80)

Проверь себя

Что такое network ownership детали?

Показать ответ

Владелец физики считает движение объекта и рассылает результат остальным.

Как уменьшить нагрузку на сеть?

Показать ответ

Пачка изменений дешевле потока мелких сообщений, а логика остаётся на сервере.