如何使用Unity3d实现多人对战联机游戏

news2024/11/16 23:35:30

所需资源

课程来源(请支持正版课程)
安装Unity Hub
安装Visual Studio
角色模型

环境准备

①Unity设置

不设置的话编写有些代码没有自动补全
点开 Preferences
在这里插入图片描述
选择 visual studio
在这里插入图片描述

②角色导入

点击 windows—>Package Manager
在这里插入图片描述
左上角 My Assets
在这里插入图片描述
找到该角色模型导入
在这里插入图片描述

角色预制件配置

1、动画控制器配置
①创建一个 Animation 文件夹,以便存放动画相关文件,在 Animation 中创建一个 Animatior Controller
在这里插入图片描述
②双击打开Animator Controller
SciFiWarriorPBRHPPolyart(下载的人物模型)中的 Animation 中的 Idle_guard_ar(角色的站立动作)拖入状态图,将该状态更名为idle。
Run_guard_AR(角色的跑步动作)拖入状态图,连接两状态,即:
在这里插入图片描述
③在 Parameters 中添加 Bool 参数 isRun ,用来控制从站立到跑步的状态转换
在这里插入图片描述
④选中 Idle—>Run_guard_AR
在这里插入图片描述
取消勾选 has exit time (有退出时间),取消勾选即达到 Transition 过度条件,那么会立马过度,不会等待动画结束,Conditions 选择刚刚创建的参数,当 isRuntrue 时,从站立变为跑步
在这里插入图片描述
⑤同理选中 Run_guard_AR—>idle ,取消 has exit time , ConditionsisRunfalse
在这里插入图片描述
2、角色预制件配置
SciFiWarriorPBRHPPolyart—>Mesh->Polyart_Mesh 拖入场景
①添加 Animatior 组件,将刚才配置好的 Animator Controller(动画控制器)拖入相应位置
在这里插入图片描述
②添加 Capsule Conllider(胶囊碰撞体)组件,用于碰撞检测
在这里插入图片描述
③添加 Character Controller(角色控制器)组件,用于控制角色,并将 center 中的 y 改为1
在这里插入图片描述

角色控制

关于单件模式:请看这篇博客
注: 原视频实现的是第一人称,由于本例加入了动画动作,摄像机放入头部中,会穿模,暂改为第三人称
①角色移动:
getAxisRaw 用于接收接受键盘输入,例如 float xMov = Input.GetAxisRaw(“Horizontal”);
其中 Horizontal 为虚拟轴中的水平轴,其已经规定好了按键输入
如何查看虚拟轴?
Edit —> project settings —> Input Manager
下拉 Axis
在这里插入图片描述


input.GetAxis()与input.GetAxisRaw()的区别:
参考博客
input.GetAxis() 的返回值是[-1, 1],拥有平滑过渡功能

input.GetAxisRaw() 的返回值是{-1,0,1}


对于Horizontal:
按键 A 返回-1,不按键返回0,按键 D 返回1
对于Vertical:
按键 S 返回-1,不按键返回0,按键 W 返回1
对于transform.right: 世界变换的红色轴
对于transform.forward: 世界变换的蓝色轴
对于transform.right * xMov: 通过返回的{-1,0,1}决定是朝红色轴的反向,不变,或者正向移动,对于 transform.forward * yMov 同理
两者相加即向量的合成,通过normalized将向量归一,将该方向向量×一个速度值,变为带有方向的速度
Time.deltaTime: 按秒为单位,完成上一帧所用的时间。
位移 = 速度 × 时间
PlayerController:

②角色旋转
鼠标横向移动即角色旋转,鼠标纵向移动即摄像机旋转
关于角度限制:
Mathf.Clamp(value,min, max);
限制 value 的值在 minmax 之间, 如果 value 小于 min,返回 min。 如果 value 大于 max ,返回 max ,否则返回 value

using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;

public class PlayerController : MonoBehaviour
{
    public float Speed = 5f;
    public float LookSensitivity = 20f;
    public Camera cam;
    private float cameraRotationTotal = 0f;  // 累计转了多少度
    [SerializeField]
    private float cameraRotationLimit = 85f;
    static PlayerController instance;
    public static PlayerController Instance()
    {
        return instance;
    }
    private void Awake()
    {
        instance = this;
    }

    private Animator animator;
    private CharacterController cc;
    // Start is called before the first frame update
    void Start()
    {
        Cursor.lockState = CursorLockMode.Locked;//将鼠标隐藏
        animator = GetComponent<Animator>(); //获取动画组件
        cc = GetComponent<CharacterController>();//获取角色控制器组件
    }

    //实现移动功能
    void move()
    {
        float xMov = Input.GetAxisRaw("Horizontal");//获取水平输入
        float yMov = Input.GetAxisRaw("Vertical");//获取垂直输入

        Vector3 velocity = (transform.right* xMov +transform.forward * yMov).normalized* Speed;//速度向量
        if(velocity != Vector3.zero )//如果速度不为0,则移动
        {
            cc.Move(velocity * Time.deltaTime);
            animator.SetBool("isRun", true);//动画组件参数isRun设为true,控制角色移动动作
        }
        else
        {
            animator.SetBool("isRun", false);
        }
    }
    //实现旋转功能
    void rotate()
    {
        float xMse = Input.GetAxisRaw("Mouse X");
        float yMse = Input.GetAxisRaw("Mouse Y");

        Vector3 yRotation = new Vector3(0f, xMse, 0f) * LookSensitivity;//鼠标横向移动即绕y轴旋转
        Vector3 xRotation = new Vector3(yMse,0f,0f) * LookSensitivity;//鼠标纵向移动即绕x轴旋转

        if (yRotation != Vector3.zero)//对y轴旋转即角色旋转
        {
            transform.Rotate(yRotation);
        }
        if (xRotation != Vector3.zero)//对x轴旋转即相机旋转(第一人称)
        {
            cameraRotationTotal += xRotation.x;
            cameraRotationTotal = Mathf.Clamp(cameraRotationTotal, -cameraRotationLimit, cameraRotationLimit);
            cam.transform.localEulerAngles = new Vector3(cameraRotationTotal, 0f, 0f);
            
        }
    }
    // Update is called once per frame
    void Update()
    {
        move();   
        rotate();
    }
}

联机功能

①下载包
Netcode for GameObjects
Multiplayer Tools
②创建NetworkManager
创建空对象(更名为 NetworkManager),选中,添加一个 transport
在这里插入图片描述
给同步物体加唯一编号,选中 Player,添加组件 Network Object
利用 NetworkManager 管理 Player
在这里插入图片描述
③给Player添加同步信息
添加组件 Network Transform
同步信息勾选:

  • 位置:全部更新
  • 角度:只围绕y轴旋转
  • 大小:不更新
    在这里插入图片描述
    ④给Player的跟随相机添加同步信息
    只同步 x
    在这里插入图片描述
    ⑤实现开局创建角色
    NetworkManager
    在这里插入图片描述
    ⑥创建简单UI界面以便调试
    创建 UI—>Canvas ,更名为 MenuUI ,添加 Button
    在这里插入图片描述
    在这里插入图片描述
    NetworkManager 添加UI脚本 NetworkManagerUI ,将 MenuUI 中的 HostBtnServerBtn、ClientBtn 分别拖入
    在这里插入图片描述

NetworkManagerUI:

using System.Collections;
using System.Collections.Generic;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.UI;

public class NetworkManagerUI : MonoBehaviour
{
    [SerializeField]
    private Button HostBtn;
    [SerializeField]
    private Button ServerBtn;
    [SerializeField]
    private Button ClientBtn;
    // Start is called before the first frame update
    void Start()
    {
    	//对按键进行监听
        HostBtn.onClick.AddListener(()=>
        {
            NetworkManager.Singleton.StartHost();
        });

        ServerBtn.onClick.AddListener(() =>
        {
            NetworkManager.Singleton.StartServer();
        });

        ClientBtn.onClick.AddListener(() =>
        {
            NetworkManager.Singleton.StartClient();
        });
    }
}

⑦ 处理冲突
如果这时运行,我们打开两个窗口,运行一个窗口,则两个窗口中的角色都会动,由于共同接收输入,所以这时我们需要禁用不是本地玩家的一些功能。
Player 添加脚本 PlayerSetup
添加不是本地玩家需要被禁用的行为
在这里插入图片描述
注:该类继承自NetworkBehaviour

PlayerSetup:

using System.Collections;
using System.Collections.Generic;
using Unity.Netcode;
using UnityEngine;

//该类继承自NetworkBehaviour
public class PlayerSetup : NetworkBehaviour
{
    [SerializeField]
    private Behaviour[] componentsToDisable;
    private Camera senceCam;
    // Start is called before the first frame update
    void Start()
    {
        if (!IsLocalPlayer)//若不是本地玩家
        {
            for(int i = 0; i < componentsToDisable.Length; i++)
            {
                componentsToDisable[i].enabled = false;
            }
        }
        else//若是本地玩家
        {
            senceCam = Camera.main;//调用主摄像机
            if(senceCam != null)
            {
                senceCam.gameObject.SetActive(false);//将主摄像机关闭
            }
        }
    }

    //当该脚本挂载的物体不被激活执行代码
    private void OnDisable()
    {
        senceCam.gameObject.SetActive(true);//主摄像机打开
    }
}

⑧解决客户端Client权限问题
原视频是不带动画效果,对于不带动画效果出现的问题是:
客户端 Client 无法操作
1)无动画效果解决方法:
创建 ClientNetworkTransform 脚本,添加如下代码,该 ClientNetworkTransform 类继承自 NetworkTransform 类,重写函数 bool OnIsServerAuthoritative(),将该函数返回 false,然后将 Player 中和 Player 中的摄像机的 NetworkTransform 替换为该脚本,就解决了不带动画的,Client 端无法操作的问题
2)带动画效果解决方法:
当我用带动画的人物做出联机功能是,出现的问题是:
首先解决了上述问题后出现的情况:角色在各自的世界都可以移动,但是在别的世界却无法移动,且都没有动画动作
①解决动画动作:
Player 中添加 Network Animator 脚本,将 Player 中的Animator 组件拖入相应位置,即可解决

  • 这时会发现 client 端可以看到 host 端的角色动作,但 host 端无法看到 client 端的角色动作,且两个窗口皆无法看到别窗口的角色移动

②解决角色移动
Player 中的 Animator 组件中的 Apply Root Motion 取消勾选
在这里插入图片描述

  • 这时 client 端可以看到 host 端的角色动作和移动,而 host 端只能看到 client 端的移动,却看不到角色的动作。

对于该问题我上网搜索好久没找到答案,于是便自己思考,考虑到是 hostclient 端的关系,且对于无动画效果时,由于 client 没有权限, client 端物体不能移动,创建一个新类去继承NetworkTransform 类,而现在是添加了动画之后, Host 端看不到 Client 端的动画,于是能不能创建一个新类去继承 NetworkAnimator ,将该类的函数 OnIsServerAuthoritative() 重写,返回 false ,答案是可行的!
③解决动画效果client权限问题:
创建 ClientNetworkAnimator 脚本,将 Player 中的 Animator 拖到相应位置
在这里插入图片描述

ClientNetworkTransform:

using Unity.Netcode.Components;
using UnityEngine;

namespace Unity.Multiplayer.Samples.Utilities.ClientAuthority
{
    [DisallowMultipleComponent]
    public class ClientNetworkTransform : NetworkTransform
    {
        protected override bool OnIsServerAuthoritative()
        {
            return false;
        }
    }
}

ClientNetworkAnimator:

using Unity.Netcode.Components;
using UnityEngine;

namespace Unity.Multiplayer.Samples.Utilities.ClientAuthority
{
    [DisallowMultipleComponent]
    public class ClientNetworkAnimator : NetworkAnimator
    {
        protected override bool OnIsServerAuthoritative()
        {
            return false;
        }
    }

}


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

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

相关文章

数据结构与算法(七):排序算法

排序算法是《数据结构与算法》中最基本的算法之一&#xff0c;排序算法可以分为内部和外部排序。 内部排序&#xff1a;数据记录在内存中进行排序。 外部排序&#xff1a;因排序的数据很大&#xff0c;一次不能容纳全部的排序记录&#xff0c;在排序过程中需要访问外存。 常…

xgboost:分割Sparsity-aware Split Finding

Sparsity-aware Split Finding1 在许多现实问题中&#xff0c;输入xxx是稀疏的是很常见的。造成稀疏性的可能原因有很多: 1)数据中存在缺失值&#xff1b; 2)统计中频繁出现零项&#xff1b; 3)特征工程的处理结果&#xff0c;如独热编码。 重要的是使算法意识到数据中的稀…

RocketMQ5.1.0单机安装与启动

RocketMQ单机安装与启动系统要求下载地址安装步骤RocketMq启动NameServer查看是否启动成功启动BrokerProxy查看是否启动成功修改tool.sh测试消息产生消息的消费关闭服务器系统要求 下载地址 官网下载地址 二进制包是已经编译完成后可以直接运行的&#xff0c;源码包是需要编译…

javaWeb核心02-RequestResponse

文章目录Request&Response1&#xff0c;Request和Response的概述2&#xff0c;Request对象2.1 Request继承体系2.2 Request获取请求数据2.2.1 获取请求行数据2.2.2 获取请求头数据2.2.3 获取请求体数据2.2.4 获取请求参数的通用方式基于上述理论&#xff0c;request对象为我…

python:使用 Jupyter notebook(测试 matplotlib 和 opencv)

环境&#xff1a; window1python 3.10.6 参考&#xff1a; https://jupyter.org/https://opencv.org/ 一、创建虚拟环境 这个步骤可以跳过&#xff08;因为笔者不喜欢在全局环境安装任何东西&#xff0c;所以搞一个新环境&#xff09;。 先选中一个目录&#xff1a;D:\jackl…

论文笔记 | Conducting research in marketing with quasi-experiments

这篇论文是Journal of Marketing上的论文&#xff0c;讲了使用准实验来进行论文研究的一些事项。外生性识别的来源、几种准实验方法的注意点还有内生性的解决。 这篇论文对于准实验或者是平常论文的展开有一个非常友善的指导功能&#xff0c;可以阅读~ 摘要&#xff1a;本文旨…

多线程实现的三种方法、线程名称的获取

文章目录多线程实现的三种方法1、通过继承Thread&#xff0c;并重写里面的run()方法2、实现Runnable接口&#xff0c;并重写其中run()方法将runnable类传递给Thread类中3、实现Callable接口&#xff0c;重写其中的call()方法Callable接口时有泛型的&#xff0c;该泛型值call()方…

字符串模式匹配,经典KMP算法你还不会?我可不允许你不会!

文章目录重点1. 简单模式匹配算法2. 部分匹配值PM的算法&#xff08;Move j-1 PM[j-1]&#xff09;3. 部分匹配值PM的两次改进&#xff08;Move j-next[j]&#xff09;4. 快速得到next数组5. KMP匹配算法重点 童鞋们看网上讲解的时候一定要分清楚序列是从0开始还是从1开始&…

大数据框架之Hive:第1章 Hive入门

1.1 什么是Hive 1&#xff09;Hive简介 Hive是由Facebook开源&#xff0c;基于Hadoop的一个数据仓库工具&#xff0c;可以将结构化的数据文件映射为一张表&#xff0c;并提供类SQL查询功能。 那为什么会有Hive呢&#xff1f;它是为了解决什么问题而诞生的呢&#xff1f; 下…

性能优化|记一次线上OOM问题处理

概述最近线上监控发现 OOM 涨幅较大&#xff0c;因此去尝试定位和修复这个问题&#xff0c;在修复了一些内存泄漏和大对象占用问题后, OOM 依旧未达到正常标准&#xff0c;在这些新上报的 hprof 文件中&#xff0c;发现几乎所有 case 中都有个叫 FinalizerReference 的对象&…

集合体系概述以及Collection集合常用API

一. 集合 集合与数组类似&#xff0c;都是一种容器。集合是Java中存储对象数据的一种容器。集合也被称为对象容器。 数组的特点 集合的特点 集合的大小不固定&#xff0c;启动后可以动态变化&#xff0c;类型也可以选择不固定。集合更像气球&#xff0c;可大可小。集合非常适合…

python与pycharm从零安装

python&#xff08;解释器&#xff09;下载地址&#xff1a;Welcome to Python.orgpycharm&#xff08;编译器&#xff09;下载地址&#xff1a;PyCharm: the Python IDE for Professional Developers by JetBrains一、python的下载与安装到官网后根据步骤下载安装包后&#xf…

xgboost:分割查找:Weighted Quantile Sketch

Weighted Quantile Sketch 专门处理流式和分布式加权数据集的一种分桶的方法 近似算法的一个重要步骤是提出候选分裂点。通常使用特征的百分位数来使候选数据均匀分布。形式上&#xff0c;设Dk(x1k,h1)&#xff0c;(x2k,h2)⋅⋅⋅(xnk,hn)D_k {(x_{1k}, h_1)&#xff0c;(x_…

Redis持久化:RDB、AOF

Redis持久化一. RDB(1) save(2) bgsave(3) 总结二. AOF(1) 重写优化(2) RDB和AOF的区别引入&#xff1a;Redis用内存存储数据&#xff0c;有数据丢失的问题&#xff1b; 一. RDB RDB&#xff08;Redis Database Bcakup file&#xff09;即Redis数据备份文件&#xff0c;或Red…

如何用 Python采集 <豆某yin片>并作词云图分析 ?

嗨害大家好鸭&#xff01;我是小熊猫~ 总有那么一句银幕台词能打动人心 总有那么一幕名导名作念念不忘 不知道大家有多久没有放松一下了呢&#xff1f; 本次就来给大家采集一下某瓣电影并做词云分析 康康哪一部才是大家心中的经典呢&#xff1f; 最近又有哪一部可能会成为…

拉链表详解

目录 一、拉链表概念 二、拉链表对应的业务需求 三、代码实现 3.1 数据初始化&#xff1a; 3.2 创建ods层增量表&#xff1a; 3.3 创建dwd层拉链表 3.4 数据更新 &#xff0c;将数据日期为2023-3-4的日期添加到拉链表中 3.4.1 先追加数据到ods层表 3.4.2 更新dwd层表数据 …

【SpringCloud】SpringCloud详解之Ribbon实战

目录前言SpringCloud Ribbon 负载均衡一需求二.RestTemplate远程调用配置负载均衡(order服务内修改)三.Ribbon实现负载均衡的原理四.Ribbon负载均衡策略1.负载均衡种类2.配置负载均衡(order服务中配置)五.Ribbon的饥饿加载配置(在order服务配置)前言 微服务中比如用户服务部署…

sklearn使用入门

文章目录1.机器学习1.1 机器学习简介1.2 有监督学习(supervised learning)1.3 无监督学习(unsupervised learning)1.4 半监督学习2. 机器学习工具SKlearn2.1 sklearn2.2 sklearn常用模块2.2.1 分类2.2.2 回归2.2.3 聚类2.2.4 降维2.2.5 模型选择2.2.6 数据预处理2.3 sklearn使用…

Android startActivityForResult()废弃了,代替方案案例

安卓项目compileSdk为32&#xff0c;在使用startActivityForResult()方法时发现Android studio提示此方法已经废弃了。 目前的代替方案案例。 // 确保 app 的 build.gradle中已经引入了androidx.appcompat:appcompat dependencies {implementation androidx.appcompat:appcomp…

操作系统之进程管理---每天一点点(春招加油呀)--知识点回顾(自问自答版本总结)

1.什么是进程&#xff1f;什么是线程&#xff1f;进程和线程的区别&#xff1f; 进程&#xff1a;资源分配和管理的基本单位 线程&#xff1a;程序执行的最小单位。 区别&#xff1a; 地址空间&#xff1a; 同一进程的所有线程共享本进程的地址空间&#xff0c;而不同的进程之间…