Unity之NetCode多人网络游戏联机对战教程(8)--玩家位置同步

前言

承接上篇,在Player上添加了移动脚本之后,还得同步每个玩家的位置。


添加相机

  • Main Camera上添加一个CinemachineBrain组件
  • 新建一个空物体Camera,添加CinemachineVirtualCameraCinemachineCollider这两个组件

先不手动绑定玩家,等后面在脚本控制摄像机的跟随对象。


玩家添加对应组件

  • 确保玩家的预制体添加了碰撞体与其他两个Network组件

NetworkTransform组件上选择要同步的轴,这里我只选择同步PositionRotationXYZ


服务端权威(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_MANAGER
        Vector3 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 jump
        if (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();
#else
        InputSystemHelper.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]之间的通信以后会经常用到,后面博文会继续深入讲解。

猜你喜欢

转载自blog.csdn.net/a924282761/article/details/134298180