文章目录
- 前言
- 添加相机
- 玩家添加对应组件
- 服务端权威(server authoritative)
- 客户端权威(client authoritative)
- 服务端同步位置
- 阅读与理解`PlayerTransformSync.cs`
- NetworkVariable
- UploadTransform
- SyncTransform
- 后话
前言
承接上篇,在Player上添加了移动脚本之后,还得同步每个玩家的位置。
添加相机
- 在
Main Camera
上添加一个CinemachineBrain
组件 - 新建一个空物体
Camera
,添加CinemachineVirtualCamera
与CinemachineCollider
这两个组件
先不手动绑定玩家,等后面在脚本控制摄像机的跟随对象。
玩家添加对应组件
- 确保玩家的预制体添加了
碰撞体
与其他两个Network
组件
在NetworkTransform
组件上选择要同步的轴,这里我只选择同步Position
与Rotation
的XYZ轴
服务端权威(server authoritative)
到目前位置,玩家位置已经是确实同步到了,但是还有一点是你移动的时候会把全部带有PlayerMove
脚本的角色都移动,下面要针对自己的角色才移动。所以得修改一下PlayerMove.cs
这个脚本
using System;
using Cinemachine;
using Cinemachine.Utility;
using UnityEngine;
using Unity.Netcode;public class PlayerMove : NetworkBehaviour
{public float Speed;public float VelocityDamping;public float JumpTime;public enum ForwardMode{Camera,Player,World};public ForwardMode InputForward;public bool RotatePlayer = true;public Action SpaceAction;public Action EnterAction;Vector3 m_currentVleocity;float m_currentJumpSpeed;float m_restY;private void Start(){if (IsOwner){GameObject.Find("===Camera===/Camera").GetComponent<CinemachineVirtualCamera>().Follow = transform;}}private void Reset(){Speed = 5;InputForward = ForwardMode.Camera;RotatePlayer = true;VelocityDamping = 0.5f;m_currentVleocity = Vector3.zero;JumpTime = 1;m_currentJumpSpeed = 0;}private void OnEnable(){m_currentJumpSpeed = 0;m_restY = transform.position.y;SpaceAction -= Jump;SpaceAction += Jump;}void Update(){if (!IsOwner) return;
#if ENABLE_LEGACY_INPUT_MANAGERVector3 fwd;switch (InputForward){case ForwardMode.Camera:fwd = Camera.main.transform.forward;break;case ForwardMode.Player:fwd = transform.forward;break;case ForwardMode.World:default:fwd = Vector3.forward;break;}fwd.y = 0;fwd = fwd.normalized;if (fwd.sqrMagnitude < 0.01f)return;Quaternion inputFrame = Quaternion.LookRotation(fwd, Vector3.up);Vector3 input = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));input = inputFrame * input;var dt = Time.deltaTime;var desiredVelocity = input * Speed;var deltaVel = desiredVelocity - m_currentVleocity;m_currentVleocity += Damper.Damp(deltaVel, VelocityDamping, dt);transform.position += m_currentVleocity * dt;if (RotatePlayer && m_currentVleocity.sqrMagnitude > 0.01f){var qA = transform.rotation;var qB = Quaternion.LookRotation((InputForward == ForwardMode.Player && Vector3.Dot(fwd, m_currentVleocity) < 0)? -m_currentVleocity: m_currentVleocity);transform.rotation = Quaternion.Slerp(qA, qB, Damper.Damp(1, VelocityDamping, dt));}// Process jumpif (m_currentJumpSpeed != 0)m_currentJumpSpeed -= 10 * dt;var p = transform.position;p.y += m_currentJumpSpeed * dt;if (p.y < m_restY){p.y = m_restY;m_currentJumpSpeed = 0;}transform.position = p;if (Input.GetKeyDown(KeyCode.Space) && SpaceAction != null)SpaceAction();if (Input.GetKeyDown(KeyCode.Return) && EnterAction != null)EnterAction();
#elseInputSystemHelper.EnableBackendsWarningMessage();
#endif}public void Jump(){m_currentJumpSpeed += 10 * JumpTime * 0.5f;}
}
编译构建之后发现可以正常使用,但是还有一个问题就是,只能是Server
端可以移动,Client
端没办法移动,这是一个BUG吗?显然不是。
客户端权威(client authoritative)
因为NetworkTransform
默认是服务端的权威验证,客户端没办法更新自己的位置,只能通过告知服务器我的位置更新了,然后服务器在告诉别人他的位置更新了。
如果想用客户端权威
就需要添加ClientNetworkTransform
来代替NetworkTransform
这个组件
去包管理器添加Git URL
包,https://github.com/Unity-Technologies/com.unity.multiplayer.samples.coop.git?path=/Packages/com.unity.multiplayer.samples.coop#main
在Player
上添加ClientNetworkTransform
,删除NetworkTransform
其他保持不变,编译运行就可以实现客户端移动了。
服务端同步位置
新建个脚本PlayerTransformSync.cs
,挂载到Player
上。移除ClientNetworkTransform
PlayerTransformSync.cs
:
using Unity.Netcode;
using UnityEngine;public class PlayerTransformSync : NetworkBehaviour
{private NetworkVariable<Vector3> _syncPos = new();private NetworkVariable<Quaternion> _syncRota = new();private void Update(){if (IsLocalPlayer){UploadTransform();}}private void FixedUpdate(){if (!IsLocalPlayer){SyncTransform();}}private void SyncTransform(){transform.position = _syncPos.Value;transform.rotation = _syncRota.Value;}private void UploadTransform(){if (IsServer){_syncPos.Value = transform.position;_syncRota.Value = transform.rotation;}else{UploadTransformServerRpc(transform.position, transform.rotation);}}[ServerRpc]private void UploadTransformServerRpc(Vector3 position, Quaternion rotation){_syncPos.Value = position;_syncRota.Value = rotation;}
}
一般情况下,都是用的服务器权威这种方式做位置同步。
阅读与理解PlayerTransformSync.cs
该脚本可分为四部分去理解:
- 创建网络同步字段
- 如果是主机就不需要向
Server
发送信息可直接同步 - 如果是客户则需要向
Server
发送信息请求同步 - 同步其他人的位置信息
NetworkVariable
通过NetworkVariable
创建两个网络同步的字段,一个同步position
,另一个同步rotation
private NetworkVariable<Vector3> _syncPos = new();
private NetworkVariable<Quaternion> _syncRota = new();
然后本地玩家通过UploadTransform
方法,上传自己的位置信息,这里又有两种情况
- 为主机时
- 为客户时
UploadTransform
为主机时,直接同步Transform
即可
为客户时,向服务器发送信息,请求同步Transform
private void UploadTransform()
{if (IsServer){_syncPos.Value = transform.position;_syncRota.Value = transform.rotation;}else{UploadTransformServerRpc(transform.position, transform.rotation);}
}[ServerRpc]
private void UploadTransformServerRpc(Vector3 position, Quaternion rotation)
{_syncPos.Value = position;_syncRota.Value = rotation;
}
SyncTransform
我自己上传位置就是在同步,别人就把位置信息同步到transform。
在Update()
与FixedUpdate()
可以确保优先级,先同步自己的,在同步他人的。
private void Update()
{if (IsLocalPlayer){UploadTransform();}
}private void FixedUpdate()
{if (!IsLocalPlayer){SyncTransform();}
}private void SyncTransform()
{transform.position = _syncPos.Value;transform.rotation = _syncRota.Value;
}
后话
这次的位置同步教程是服务端
与客户端
直接的基础通信,需要知道用法,还有搞清楚服务端与客户端之间的逻辑才是本次教程最大的重点。
NetworkVariable
这个用于字段网络同步有很大帮助,[ServerRpc]
与[ClientRpc]
之间的通信以后会经常用到,后面博文会继续深入讲解。