我對 Unity 還很陌生,我已經開始學習如何使用 Fishnet 網路。我創建了一個基本的玩家移動腳本,它可以比網路轉換更快地同步玩家位置。但我遇到了一個奇怪的問題,我不知道如何解決。
在我的場景中,我有一個網路管理器,它在連接后會生成我的播放器預制件——一個帶有播放器腳本和網路物件的簡單精靈。我沒有添加網路轉換,因為我將手動同步每個玩家的位置以減少客戶端之間的延遲。這是播放器腳本:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using FishNet.Object;
public class Player : NetworkBehaviour
{
private void Update()
{
if (IsOwner) //only the client that owns this object will run this code
{
//get input, send it to server
float horizontalInput = Input.GetAxisRaw("Horizontal");
float verticalInput = Input.GetAxisRaw("Vertical");
RpcMoveCharacter(horizontalInput, verticalInput);
}
//since this is an observers rpc, only the server will call it
RpcSendCharacterPosition(transform.position.x, transform.position.y);
}
[ServerRpc]
public void RpcMoveCharacter(float x, float y)
{
//change the position of the server's instance of the player
transform.Translate(x * 10f * Time.deltaTime * Vector3.right);
transform.Translate(y * 10f * Time.deltaTime * Vector3.up);
}
[ObserversRpc]
public void RpcSendCharacterPosition(float x, float y)
{
if (IsClientOnly)
{
//ensure clients' instance of the player match the server's' position
transform.position = new Vector2(x, y);
}
}
}
該腳本完美運行......除了一個問題:兩個玩家的玩家移動速度不一致。只有當我構建并運行我的游戲,然后連接兩個版本的游戲時才會出現這些問題。
當任一玩家是主機(服務器 客戶端)時,他們的玩家物件在兩個螢屏上均以中等速度移動。這是預期的速度。
當我從統一編輯器視窗運行的游戲版本只是一個客戶端時,玩家在兩個螢屏上的移動速度都很快——比預期快很多倍。
當我使用“構建并運行”創建的游戲版本只是一個客戶端時,玩家在兩個螢屏上的移動速度都很慢——比預期慢很多倍。
我已經測驗了我能想到的一切。我做的一項測驗是防止網路管理器生成播放器預制件,提前將播放器物件放置在場景中,然后將其轉換為:
private void Update()
{
if (IsOwner)
{
float horizontalInput = Input.GetAxisRaw("Horizontal");
float verticalInput = Input.GetAxisRaw("Vertical");
RpcMoveCharacter(horizontalInput, verticalInput);
}
RpcSendCharacterPosition(transform.position.x, transform.position.y);
}
[ServerRpc]
對此:
private void Update()
{
//now anyone can control the player object
float horizontalInput = Input.GetAxisRaw("Horizontal");
float verticalInput = Input.GetAxisRaw("Vertical");
RpcMoveCharacter(horizontalInput, verticalInput);
RpcSendCharacterPosition(transform.position.x, transform.position.y);
}
//same effect as note above
[ServerRpc (RequireOwnership = false)]
為了查看是否存在有關播放器生成功能的問題。我的更改效果為零——根本沒有任何改變。如果我的編輯器只是一個客戶端,它仍然移動播放器太快,如果我的構建只是一個客戶端,它仍然移動播放器太慢。
我嘗試的另一件事是制作一個全新的專案,以防我在上一個專案中奇怪地切換了設定或其他東西。一旦我創建了一個新專案,我所做的就是匯入魚網,將魚網的默認 NetworkManager 物件添加到我的場景中,創建一個名為 player 的簡單預制件,將一個網路物件和原始播放器腳本添加到播放器預制件中,將網路管理器設定為生成播放器預制件,然后再試一次。沒有運氣——一切都完全一樣。
有任何想法嗎?我被困在這里——我不知道還能嘗試什么,因為代碼/場景中的一切似乎都運行良好。我無法弄清楚為什么我的構建會與我的編輯器的播放模式不同,無論哪個是服務器(或主機),哪個只是客戶端。
謝謝!
uj5u.com熱心網友回復:
所以我真的不知道這到底是如何Fishnet
作業的。
但正如在任何網路中一般所說的那樣,你永遠不能依賴
- 您的所有設備都以相同的 FPS(每秒幀數)運行
- 您的聯網訊息立即到達服務器/其他客戶端
- 您的網路訊息到達服務器/其他客戶端的時間間隔與您結束它們的時間完全相同
所以我寧愿做的是
首先,不要每幀發送網路訊息,而是發送一些固定的時間間隔(例如,通常每 0.2 秒使用一次)
而是立即在本地處理所有本地移動
如果您將用戶輸入發送到服務器并且必須等到通過接收回傳的結果位置來應用它,這對 UX 來說將是非常糟糕的。這會導致 2 倍的網路延遲,這對于本地用戶來說是非常不可思議的。
而不是增量,而是同步結果位置值。
通過這種方式,您可以確保所有玩家都與實際產生的位置同步,并且對于稍后加入會話或由于網路延遲而可能錯過一些輸入訊息的玩家也可以立即作業。
所以我會做類似的事情
public class Player : NetworkBehaviour
{
// Interval in seconds how often to send your position to the server/clients
[SerializeField] private float sendInterval = 0.2f;
// How fast you can move in units per second
[SerializeField] private float moveSpeed = 10f;
// Use this to adjust your input sensitivities
[SerializeField] [Min(0)] private float inputSensitivityX = 1f;
[SerializeField] [Min(0)] private float inputSensitivityY = 1f;
// Might have to play a bit with this value to make smooth interpolation faster or slower
// 5 is an arbitrary value but works quite good from experience
// depends on your sendInterval and movespeed as well
[SerializeField] privte float interpolation = 5f;
// keeps track of passed time
private float sendTimer;
private Vector2 receivedTargetPosition;
private void Start()
{
if(!IsOwner)
{
receivedTargetPosition = transform.position;
}
}
private void Update()
{
//only the client that owns this object will run this code
if (IsOwner)
{
//get input
var horizontalInput = Input.GetAxisRaw("Horizontal");
var verticalInput = Input.GetAxisRaw("Vertical");
var input = new Vector2(horizontalInput * inputSensitivityX, verticalInput * inputSensitivityY);
// Makes sure that you always have maximum 1 magnitude for the input
input = Vector2.ClampMagnitude(input, 1f);
// use the rotation to already rotate this vector from local into world space
input = trasform.rotation * input;
// Here you want the deltaTime of THIS DEVICE
var movement = moveSpeed * Time.deltaTime * input;
// Move your player LOCALLY
transform.position = (Vector3)movement;
}
// If you are not the owner you rather apply the received position
else
{
// I would e.g. smoothly interpolate somewhat like
transform.position = Vector3.Lerp(transform.position, receivedTargetPosition, interpolation * Time.deltaTime);
}
// Check if next send time interval has passed
sendTimer = Time.deltaTime;
if(sendTimer >= sendInterval)
{
sendTimer = 0;
if(IsServer)
{
RpcSendPositionToClients(transform.position.x, transform.position.y);
}
else
{
RpcSendPositionToServer(transform.position.x, transform.position.y);
}
}
}
[ServerRpc]
public void RpcSendPositionToServer(float x, float y)
{
// just in case
// the owner already gets its position in Update so nothing to do
if(IsOwner) return;
//change the position of the server's instance of the player
receivedTargetPosition = new Vector2(x, y);
}
[ClientRpc]
public void RpcSendPositionToClients(float x, float y)
{
// Owner and server already know the positions
if(IsOwner || IsServer) return;
receivedTargetPosition = new Vector2(x, y);
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/467729.html
上一篇:在我重新啟動游戲之前,streamWriter不會應用更改
下一篇:檢測2個物件之間的碰撞