【Unity2022】Unity多人游戏开发教程 | 您所在的位置:网站首页 › webgl游戏开发教程 › 【Unity2022】Unity多人游戏开发教程 |
文章目录
官方文档前言教程的开发环境预备知识
1 简介1.1 Netcode for GameObjects1.2 NGO支持的Unity版本1.3NGO支持的平台
2 开始旅程2.1 安装NGO2.2 运行项目2.2.1 Unity基础获取命令行参数判断当前是否在编辑器中运行发布版本的Log日志输出
2.2.2 C#基础判断字符串前缀2.2.1.2 空值合并操作符2.2.1.3 获取字典中的值
2.2.3 创建命令行测试助手
简单的RPCsRPC
持续更新中,由于笔者水平有限,如有错误,请在评论区指正
官方文档
首先亮出文档,可以直接去看官方文档。 本文章大部分内容来源于官方文档,另一部分为笔者讲解的教程。 如果英语不好,或看不懂文档的人,可以阅读本文章。 官方文档 官方文档的中文翻译: 中文翻译 前言 教程的开发环境本教程使用的开发环境如下: Windows10Unity 2022.3.0f1c1Netcode for GameObjects 1.5.2 预备知识本教程需要具备以下预备知识: C#编程语言Unity基础知识教程内也会讲解一些C#编程的知识,包括部分Unity的知识,不过并不会全面讲解。 1 简介 1.1 Netcode for GameObjects以前的多人游戏开发,有些项目或者教程可能使用的是UNet,当然也有别的方案,但是UNet已经被Unity官方弃用了,也就是说UNet是一个过时的方案,目前正在开发一种新的多人游戏和网络解决方案,名字叫做Netcode for GameObjects。 本篇文章的多人游戏解决方案采用的就是Netcode for GameObjects。 Netcode for GameObjects(简称Netcode或NGO)是一个为Unity构建的高级网络库,可用于抽象化网络逻辑,抽象化网络逻辑是指将网络通信的复杂性和细节隐藏在一个高级接口之后,使开发者能够更专注于构建游戏,而无需深入了解底层的网络协议和通信机制。 Netcode提供了简单的网络操作,让我们能够更方便的将GameObject和世界数据通过网络会话发送给多个玩家或接收,并在多个玩家之间同步数据。 1.2 NGO支持的Unity版本使用Netcode,我们的Unity需要是2021.3或者更高的版本。并且脚本后端是Mono和IL2CPP。 Unity有两种脚本后端:Mono和IL2CPP(Intermediate Language To C++),它们使用不同的编译技术,Mono使用即时(JIT)编译,在运行时按需编译代码。而IL2CPP使用提前(AOT)编译,在运行应用程序之前对整个应用程序进行编译。 Mono是一种开源的跨平台的.NET实现,允许开发者在不同的操作系统上运行.NET应用程序。它提供了一系列工具和库,使开发者能够使用C#等.NET编程语言来创建和运行应用程序。 1.3NGO支持的平台NGO支持如下平台: Windows、MacOS和Linux iOS和Android 运行在Windows、Android和iOS操作系统上的XR平台 大多数封闭平台,如游戏主机。 WebGL(需要NGO 1.2.0+和UTP 2.0.0+)。注意:尽管NGO 1.2.0引入了WebGL支持,但NGO 1.2.0中存在影响WebGL兼容性的错误,因此建议使用NGO 1.3.0+。 2 开始旅程 2.1 安装NGO首先我们需要新建一个项目,当然如果你也可以打开已有项目。 进入项目后打开Package Manager,在编辑器的菜单栏选择“Window > Package Manager”,即可打开Package Manager。然后点击左上角的加号“+”,选择“Add package by name…”。然后在包名称的输入框中输入“com.unity.netcode.gameobjects”,然后选择“Add”,这样就为你的项目导入了NGO。 运行多人游戏,那就需要启动多个游戏实例,将多个游戏实例以不同端来启动,比如主机端或者客户端,启动方法也有很多,例如可以在程序中通过制作网络连接的UI界面选择启动端。也可以通过命令行启动,获取命令行参数来选择对应端。 这里先介绍第二种方法,也就是通过命令行来启动多端。 2.2.1 Unity基础 获取命令行参数通过命令行启动unity程序的时候,我们可以获取其命令行参数。创建一个Unity项目,然后创建一个Text,用于一会显示我们获取到的参数,然后创建一个脚本,该脚本内容如下: using UnityEngine; using TMPro; public class GetArgs: MonoBehaviour { public TextMeshProUGUI text; // Start is called before the first frame update void Start() { var args = System.Environment.GetCommandLineArgs(); for(int i=0; i i}]: "+args[i]+"\n"; } } }在代码中,我们通过System.Environment.GetCommandLineArgs()方法来获取命令行参数,该方法返回一个字符串数组。 现在我们保存代码和场景,然后构建程序,并通过命令行的方式执行。如图所示,直接通过指定路径的方式执行程序,Learn_2D是你程序的名字,前面的路径就是该程序所在的路径。然后,在执行程序的命令后面,我们跟上命令行参数,输入什么都可以,随便写点字符串。 在Unity中,我们可以通过Application类的isEditor字段来判断当前运行的游戏是否是在编辑器中运行的,Application.isEditor是一个静态只读的布尔值,定义如下: // 摘要: // Are we running inside the Unity editor? (Read Only) public static bool isEditor => true;当我们在 Unity 编辑器中运行游戏时,Application.isEditor的值将为 true。 当我们在游戏的构建版本(即发布版本)中运行游戏时,Application.isEditor的值将为 false。 Application.isEditor在某些时候非常有用。通过它,可以根据我们在编辑器还是构建版本中,来执行不同的逻辑和功能。这对于在开发过程中进行调试、测试和实现编辑器专用功能有一定的帮助。 这里我们写一个测试代码如下: if(Application.isEditor) { text.text = "此时正在编辑器中运行"; } else { text.text = "此时正在发布版本中运行"; }运行效果如下,当我们在编辑中运行时: 其实当我们将项目Build后,运行的程序,也会输出日志文件,没错就是你在代码中使用Debug.Log方法输出的日志文件,那么问题来了,我们的发布版本又没有Console窗口,怎么输出日志文件?其实,日志文件被存储在了一个名叫Player.log的文件中。 Player.log 文件是用来记录详细的日志信息的。无论是在编辑器中还是在发布版本中,Debug.Log 输出的内容都会被记录在 Player.log 文件中。 Player.log文件默认存放在如下路径中: C:\Users\username\AppData\LocalLow\CompanyName\ProductName\Player.log具体来说,username 是指当前计算机上登录的用户名,而 CompanyName 和 ProductName 是指 Unity 项目中的公司名称和项目名称。如果你没有明确填写公司名称,那么这里的CompanyName就是“DefaultCompany”。 例如如下路径: C:\Users\Zhi\AppData\LocalLow\DefaultCompany\Learn_2D\Player.log想必你已经知道日志文件默认在什么位置了,接下来我们尝试输出一些日志看看。 编写如下代码: void Start() { Debug.Log("此处为输出的日志"); }代码中,我们简单的打印了一行日志。 接下来构建项目并运行,然后去前面说的路径中,找到Player.log文件,打开该文件,可以看到如下内容: 现在,让我们实践一下。构建一个程序,然后通过命令提示符来运行他,为其传递命令行参数 -logfile weLog.txt。 那么我们如何改变当前命令提示符所在的目录,让其生成到指定的位置呢? 接下来我们需要了解一下命令提示符窗口的cd命令,cd(change directory)是用于在命令行中切换当前工作目录的命令。 默认情况下,打开cmd进入的是当前windows用户的文件夹下。 我们刚刚学习了如何获取命令行参数,获取数据后,接下来我们就要对其进行处理了,在对命令行参数进行处理之前,我们先来学习一些C#的知识。 首先就是一个字符串处理函数,StartsWith(),这个函数我们在学习C#的时候都接触过,它的作用是用来判断字符串开头的字符,具体来讲,就是判断字符串的开头是否为指定的字符串,如果是,就返回True,如果不是就返回False。 这是一个实例方法,也就是说,我们需要在一个字符串的实例上使用这个方法。 具体来举个例子: string str = "Hello, world!"; bool startsWithHello = str.StartsWith("Hello"); Console.WriteLine(startsWithHello);我们声明了一个字符串,里面是Hello,world,通过StartWIth方法, 判断其开头是否为"Hello"。 运行结果如图所示: 此时运行结果为False。 但是我们可以通过一个枚举值,来使其不区分大小写。将StringComparison.OrdinalIgnoreCase枚举值作为第二个参数,StartWith就不会区分大小写了。 string str = "Hello, world!"; bool startsWithHello = str.StartsWith("hello", StringComparison.OrdinalIgnoreCase); Console.WriteLine(startsWithHello);此时运行结果为:True。 2.2.1.2 空值合并操作符空值合并操作符是由两个问号组成的“??”。 expression1 ?? expression2 它的作用是用来检查问号左侧的表达式是否为null,如果为null,就返回右侧表达式的值,如果不为null,就返回左侧表达式的值。 举个例子: string name = null; string result = name ?? "无名"; Console.WriteLine(result);运行结果显示为:无名。 这里我们用了一个name变量,给其null值,然后通过空值合并操作符,来判断其是否为null,如果此时name不是null值,就会返回name ,把name的值,赋值给result,如果name为null,就会返回右侧表达式的值,也就是"无名"二字。 由于这里我们给name赋值为null,理所当然的就会返回右侧表达式的值了。 2.2.1.3 获取字典中的值复习一下,在学习C#的时候,我们应该学习过TryGetValue方法,我们可以通过TryGetValue来从字典中获取值。这个方法是字典类的一个成员方法(比如Dictionary)。这是一个可以安全的获取字典中的值的方法,因为它避免了在字典中查找键时引发的异常。 方法的定义如下: public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value);该方法有两个参数,第一个参数是键。 第二个参数是值,当找到匹配的键时,会通过该参数将值返回出来。如果没找到对应的键值对,就会将值类型的默认值赋给这个变量,并返回false。 下面是一个示例程序: Dictionary dict = new Dictionary(); dict.Add(1, "Unity"); dict.Add(2, "UE5"); string result; if (dict.TryGetValue(1, out result)) { Console.WriteLine("找到键 '1' 的值: " + result); } else { Console.WriteLine("未找到键 '1'"); } if (dict.TryGetValue(3, out result)) { Console.WriteLine("找到键 '3' 的值: " + result); } else { Console.WriteLine("未找到键 '3'"); }运行结果如下所示: 使用TryGetValue()方法时,可以避免查找字典中不存在的键时引发的异常,非常滴好用。 2.2.3 创建命令行测试助手接下来,我们就要使用命令行来启动多端了,首先进入到你的项目,新建一个UI-Text-TextMeshPro。调整其位置为左上角,设置Anchor Presets为left top。创建这个UI的目的是为了显示当前实例是客户端还是服务器端、主机端。 然后在游戏的场景中新建一个空物体,命名为NetworkManager。我们点击该物体的Add Component,搜索NetworkManager,为其挂载该组件,后续我们会通过该组件来启动不同的端。该组件是整个NGO中最为重要的组件,包含了你项目中所有与网络代码相关的设置,可以说netcode的中心。 然后再右键单击NetworkManger,为其创建子物体,同样也是空物体,并将该物体命名为NetworkCommandLine。 |
CopyRight 2018-2019 实验室设备网 版权所有 |