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

news2024/11/18 15:27:07

文章目录

    • 前言
    • 添加相机
    • 玩家添加对应组件
    • 服务端权威(server authoritative)
    • 客户端权威(client authoritative)
    • 服务端同步位置
    • 阅读与理解`PlayerTransformSync.cs`
      • NetworkVariable
      • UploadTransform
      • SyncTransform
    • 后话

前言

承接上篇,在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]之间的通信以后会经常用到,后面博文会继续深入讲解。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1200192.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vColorPicker与vue3-colorPicker——基于 Vue 的颜色选择器插件

文章目录 前言样例特点 一、使用步骤&#xff1f;1. 安装2.引入3.在项目中使用 vcolorpicker 二、选项三、事件四、问题反馈问题所在安装引入例子效果图 前言 vColorPicker——官网 vColorPicker——GitHub 样例 vColorPicker是基于 Vue 的一款颜色选择器插件&#xff0c;仿照…

主题模型LDA教程:主题数选取 困惑度perplexing

文章目录 LDA主题数困惑度1.概率分布的困惑度2.概率模型的困惑度3.每个分词的困惑度 LDA主题数 LDA作为一种无监督学习方法&#xff0c;类似于k-means聚类算法&#xff0c;需要给定超参数主题数K&#xff0c;但如何评价主题数的优劣并无定论&#xff0c;一般采取人为干预、主题…

电子工程师的焊接技法总结

基础学习视频如下&#xff1a; 1 老司机焊接纯干货分享&#xff0c;让你焊接不迷路&#xff0c;很适合零基础小白_哔哩哔哩_bilibili 焊接常用工具 1 焊锡丝 按照粗细来分的话&#xff0c;有粗焊锡&#xff0c;有细焊锡&#xff0c;细焊锡一般适合比较精细的焊接。 按照是否含铅…

吃透 Spring 系列—Web部分

目录 ◆ Spring整合web环境 - Javaweb三大组件及环境特点 - Spring整合web环境的思路及实现 - Spring的web开发组件spring-web ◆ web层MVC框架思想与设计思路 ◆ Spring整合web环境 - Javaweb三大组件及环境特点 在Java语言范畴内&#xff0c;web层框架都是基于J…

win环境Jenkins部署前端项目

今天分享win环境Jenkins部署前端vue项目&#xff0c;使用的版本jenkins版本Jenkins 2.406版本。 前提是jenkins安装好了&#xff0c;通用配置已经配置好了&#xff0c;可以参考上两篇博客。 1、前端项目依赖nodejs&#xff0c;需要安装相关插件 点击进入 安装成功标准 jenki…

Home Assistant使用ios主题更换背景

Home Assistant使用ios主题、更换背景 lovelace-ios-dark-mode-theme 默认前置情况&#xff0c;1、已安转HACS插件2、搜索安装 IOS Dark Mode Theme1&#xff09;第一、二步应该很容易实现&#xff0c;configuration.yaml文件很容易被找到2&#xff09;而本人在进行第三步操作时…

在vue3中使用Element-plus的图标

首先安装Element-Plus-icon # 选择一个你喜欢的包管理器# NPM $ npm install element-plus/icons-vue # Yarn $ yarn add element-plus/icons-vue # pnpm $ pnpm install element-plus/icons-vue 如何使用 Element-Plus-icon官方文档链接Icon 图标 | Element Plus (element-…

Zyxel NBG2105 身份验证绕过

直接访问如下payload则会以管理员身份跳转到 home.htm页面 ​​/login_ok.htm漏洞证明 查看本页面的cookie&#xff0c;login为1 文笔生疏&#xff0c;措辞浅薄&#xff0c;望各位大佬不吝赐教&#xff0c;万分感谢。 免责声明&#xff1a;由于传播或利用此文所提供的信息、…

antlr4踩坑记录

一. syntax error: ‘<’ came as a complete surprise to me while matching alternative 参考这个issue&#xff0c;antlr版本必须得是4.6 下载链接&#xff1a;http://www.antlr.org/download/antlr-4.6-complete.jar 二.org.antlr.v4.analysis.LeftRecursiveRuleTrans…

如何在ModelScope社区魔搭下载所需的模型

本篇文章介绍如何在ModelScope社区下载所需的模型。 若您需要在ModelScope平台上有感兴趣的模型并希望能下载至本地&#xff0c;则ModelScope提供了多种下载模型的方式。 使用Library下载模型 若该模型已集成至ModelScope的Library中&#xff0c;则您只需要几行代码即可加载…

【Vue3】scoped 和样式穿透

我们使用很多 vue 的组件库&#xff08;element-plus、vant&#xff09;&#xff0c;在修改样式的时候需要进行其他操作才能成功更改样式&#xff0c;此时就用到了样式穿透。 而不能正常更改样式的原因就是 scoped 标记。 scoped 的渲染规则&#xff1a; <template>&l…

Ubuntu查看Python某个包的具体路径

使用命令&#xff1a; python(版本号) -m pip show (包)这里的Location就是这个包所在的路径。同时它还列出了这个包的版本的信息。

吴恩达《机器学习》8-3->8-4:模型表示I、模型表示II

8.3、模型表示I 一、大脑神经网络的基本原理 为了构建神经网络模型&#xff0c;首先需要理解大脑中的神经网络是如何运作的。每个神经元都可以被看作是一个处理单元或神经核&#xff0c;它包含多个输入&#xff08;树突&#xff09;和一个输出&#xff08;轴突&#xff09;。…

【见缝插针】射击类游戏-微信小程序项目开发流程详解

还记得小时候玩过的见缝插针游戏吗&#xff0c;比一比看谁插得针比较多&#xff0c;可有趣了&#xff0c;当然了&#xff0c;通过它可以训练自己的手速反应&#xff0c;以及射击水平&#xff0c;把握时机&#xff0c;得分越高就越有成就感&#xff0c;相信小朋友们会喜欢它的&a…

pointnetgpd复现

参考&#xff1a; Installation Instructions — Dex-Net 0.2.0 documentation Install git clone https://github.com/lianghongzhuo/PointNetGPD.git 添加环境变量 gedit ~/.bashrc #添加下面这一行 export PointNetGPD_FOLDER$HOME/code/PointNetGPD #然后source source…

k8s 1.28.3 使用containerd

文章目录 环境说明最终结果环境配置时钟同步 主机名称配置主机名解析关闭swap安装ipvs 安装containerd安装containerd生成配置修改配置开启containerd服务 安装runc安装k8s安装kubelet kubeadm kubectl获取kubernetes 1.28组件容器镜像 拉取镜像初始化集群方法一&#xff08;不…

【4】Gradle-快速入门使用【Gradle多模块项目详解】

目录 【4】Gradle-快速入门使用【Gradle多模块项目详解】创建多项目构建添加子项目命名建议 项目依赖项项目路径不同模块的build.gradle配置 子项目之间共享构建逻辑公约插件跨项目配置buildSrc开发公约插件 调整多模块项目配置修改项目树的元素 了解Gralde配置时间和执行时间并…

API 集成测试工具Hitchhiker 0.1.1 正式发布

Hitchhiker 是一款开源的 Restful Api 集成测试工具&#xff0c;你可以在轻松部署到本地&#xff0c;和你的 team 成员一起管理 Api。 能做什么 * Team 协作开发 Api * Api 历史修改记录及支持 diff 展示 * 支持多环境变量及运行时变量 * 支持 Schedule 及批量 run * 不同…

Ubuntu诞生已经19年了

导读2004 年 10 月 20 日&#xff0c;Ubuntu 4.10 正式发布&#xff0c;代号‘Warty Warthog’。 2004 年 10 月 20 日&#xff0c;Ubuntu 4.10 正式发布&#xff0c;代号‘Warty Warthog’。 ▲ Ubuntu 4.10 与最新版 Ubuntu 23.10 的对比 作为 Ubuntu 第一个版本&#xff0…

[mysql]索引优化-2

目录 一、分页查询优化1.根据自增且连续的主键排序的分页查询2.根据非主键字段排序的分页查询 二、Join关联查询优化1.嵌套循环连接 Nested-Loop Join(NLJ) 算法2.基于块的嵌套循环连接 Block Nested-Loop Join(BNL)算法 三、count(*)查询优化1.查询mysql自己维护的总行数2.sho…