版本记录
版本号 | 时间 |
---|---|
V1.0 | 2019.07.16 星期二 |
前言
Unity是由Unity Technologies开发的一个让玩家轻松创建诸如三维视频游戏、建筑可视化、实时三维动画等类型互动内容的多平台的综合型游戏开发工具,是一个全面整合的专业游戏引擎。Unity类似于Director,Blender game engine, Virtools 或 Torque Game Builder等利用交互的图型化开发环境为首要方式的软件。其编辑器运行在Windows 和Mac OS X下,可发布游戏至Windows、Mac、Wii、iPhone、WebGL(需要HTML5)、Windows phone 8和Android平台。也可以利用Unity web player插件发布网页游戏,支持Mac和Windows的网页浏览。它的网页播放器也被Mac 所支持。网页游戏 坦克英雄和手机游戏王者荣耀都是基于它的开发。感兴趣的看下面几篇文章。
1. Unity强化篇(一) —— 如何使用Vuforia制作AR游戏(一)
2. Unity强化篇(二) —— 适用于Unity的HTC Vive教程(一)
3. Unity强化篇(三) —— 适用于Unity的HTC Vive教程(二)
4. Unity强化篇(四) —— Unity 和 Ethereum(一)
5. Unity强化篇(五) —— Unity 和 Ethereum(二)
Photon vs Unity — Which is better?
首先看下写作内容
了解如何使用
Unity
和Photon Unity Networking(PUN)
库制作自己的多人游戏。
接着看下写作环境
C# 6, Unity 2018.3, Unity
如果你已经用Unity制作游戏已经有一段时间了,你知道创造游戏需要付出很多努力。诸如关卡设计,游戏机制和进展之类的事情需要经过大量的试验和错误以及精心设计才能做到正确。即便如此,只有少数人能够完成一场完整的比赛。
与此相比,Fortnite
和PUBG
等多人游戏风靡全球。这些易于学习但难以掌握的游戏随着破纪录的人数不断变得越来越受欢迎。有些人甚至将这些游戏作为职业生涯,这对于单人游戏非常困难。
在本教程中,您将学习如何使用Unity
和Photon Unity Networking
库(简称PUN
)制作自己的多人游戏。
具体来说,你将学到:
-
Unity Networking
和PUN
之间的主要区别。 - 如何创建玩家可以加入的大厅
(Lobby)
场景。 - 如何加载游戏场景并同步玩家的变换
(Transform)
值。
让我们开始吧。
在开始学习本教程之前,让我们先看看Unity Networking
和Photon Unity Networking
之间的主要区别。
1. Exploring the Architecture
Unity
和PUN
都有类似的底层API。 但是这些库使用这些API所需的架构是它们之间的关键区别因素。
参考 https://www.youtube.com/watch?v=xLECRl1eyGk
上图描述了如何在Unity
和PUN
中的网络中的节点之间传输消息。
Unity Networking
支持服务器/客户端(server/client)
体系结构。所有消息都必须通过主机客户端(Host client)
,不能直接在节点之间发送。例如,基于上图,消息使用以下路径从客户端B(client B)
传输到客户端C(client C)
:Client B ▸ Relay Server ▸ Host A ▸ Relay Server ▸ Client C
。
如您所见,该消息从源到目的地总共需要4个跃点。除此之外,如果主机Host
与网络断开连接,游戏将停止。
PUN
具有类似的服务器/客户端(server/client)
体系结构,但也支持消息的对等(peer-to-peer)
发送。例如,基于上图,消息使用以下路径从Client B
传输到Client C
:Client B ▸ Relay Server ▸ Client C
。
对于两个节点之间相同的消息传输,与Unity中的4个相比,总共有2个跃点。除此之外,PUN
可能完全绕过Relay Server
,Client B
可以直接与Client C
通信,从而将跳数减少到1。
因此,PUN
比Unity
快。
2. Pricing
Unity
和PUN
之间的另一个关键区别是定价模型(Pricing model)
。
Unity
为每个许可提供免费数量的Concurrent Users (CCU)
。
- Personal:20个并发用户
- Plus:50个并发用户
- Professional:200个并发用户
如果您需要增加游戏支持的CCU
数量,则必须为您使用的额外带宽付费。您将通过Unity infrastructure(Matchmaker and Relay Server)
收取0.49美元/ GB
的流量。
来源 - https://support.unity3d.com/hc/en-us/articles/209604483-How-much-does-Unity-Multiplayer-cost-
PUN
还提供每个房间最多20
个CCU,8000
个月活跃和500
个免费消息。除了免费计划,它提供了一个非常好的95
美元一次性付款选项60
个月,其中包括100
个CCU,每月40k活跃,每个房间500条消息。此选项非常适合预算有限的小型独立开发人员。
相对更快的性能,精心编写的教程和文档以及健康的定价计划选择使PUN成为开发人员构建多人游戏的一个非常好的选择。
定价 - https://www.photonengine.com/en-US/PUN/pricing
开始
您需要Unity版本2018.3
或更高版本才能成功加载启动项目。 如果您没有在系统上安装它,可以从unity3d.com下载。
1. Project Overview
看一下Project
窗口中的文件夹结构:
这是每个文件夹包含的内容:
- LogViewer:
LogViewer
资源所需的文件。 - Materials:本教程所需的材料。
- Models:本教程所需的模型。
- Photon:
Photon Library
所需的文件。 - PhysicsMaterial:项目中使用的
Physics
材料。 - Prefabs:本教程的预制件。
- Resources:必须由
Photon
同步的预制件。 - Scenes:游戏的主菜单和竞技场场景。
- Scripts:项目所需的脚本。
从Assets / RW / Scenes
打开Launcher
场景。
如果您之前玩过多人游戏,您会知道在您和您的朋友一起开始玩游戏之前,您首先需要创建或加入大厅(或游戏室),然后您将连接所有游戏,然后大厅领导者启动游戏。
在这个场景中,您将使用Photon Unity Networking
制作一个大厅。您将创建一个具有特定名称的房间,然后您的朋友可以通过在他们的游戏实例中输入相同的房间“name”
来加入您的大厅。
一旦您和您的朋友加入同一个房间,大厅领导者就可以加载MainArena
场景,您可以在这里一起玩游戏。
Creating a Photon Account
在开始构建大厅之前,您需要通过访问 https://dashboard.photonengine.com/en-us/account/SignUp在Photon Engine
的官方网站上创建一个帐户。
- 1) 注册成功后,您将被重定向到您的帐户信息中心。
- 2) 在同一页面上,单击
Create a new app
按钮。 输入应用程序的名称,例如“SampleApp”
,然后单击表单底部的Create
按钮。 - 3) 最后,在
Dashboard
页面上,您将看到一个包含“SampleApp”
详细信息的框。 复制AppId
并将其存储在某个地方,稍后您将在教程中使用它进行测试。
注意:
Photon Unity Networking
库已经存在于您在本教程开头下载的入门项目中,但您也可以通过从Asset store
中下载unitypackage
在现有项目中使用它。
返回Unity
编辑器,通过选择Window ▸ Photon Unity Networking ▸ PUN Wizard
打开PUN Wizard
。
在PUN Wizard
窗口中,单击Setup Project
,然后输入在上一节中设置光子引擎帐户时保存的AppId
。单击Setup Project
按钮。
现在您已经设置了Photon
,让我们开始构建大厅。
1. Creating the Lobby
以下是Launcher.cs
脚本将按顺序进行的总结:
- 1) 连接到
Photon Network
。 - 2) 连接后,从用户处获取两个输入:他们想要使用的
Player Name
,以及他们想要创建或加入的Room Name
。 - 3) 如果输入名称的房间不存在,请创建一个具有该名称的房间,并将当前玩家器设为
Lobby Leader
。如果房间存在,玩家将加入房间。 - 4) 一旦两个玩家都连接到同一个房间,
Lobby Leader
就可以加载MainArena
场景。
在Assets / RW / Scripts
中打开Launcher.cs
脚本。
在注释// Start Method
之后,在Launcher.cs
中添加以下代码行。
添加代码时不要担心中间错误。所有必要的代码将在下面的章节中解释。
// Start Method
void Start()
{
//1
PlayerPrefs.DeleteAll();
Debug.Log("Connecting to Photon Network");
//2
roomJoinUI.SetActive(false);
buttonLoadArena.SetActive(false);
//3
ConnectToPhoton();
}
void Awake()
{
//4
PhotonNetwork.AutomaticallySyncScene = true;
}
这是代码的简要说明。
- 1) 连接到服务器时,
PUN ping
所有可用服务器,并将具有最低ping
的服务器的IP地址存储为PlayerPrefs
键值对。 这可能会在连接阶段导致意外行为。 为避免任何异常,Launcher
场景启动时会调用DeleteAll
。 - 2) UI元素默认隐藏,并在建立与
Photon
服务器的连接后激活。 - 3) 调用
ConnectToPhoton
连接到Photon
网络。 - 4)
AutomaticallySyncScene
的值设置为true
。 这用于在一个房间中的所有连接的玩家之间同步场景。
2. Loading the MainArena Scene
要从TextField UI
元素获取Input
,您需要一个公共方法来将值存储在TextField
中。 在// Helper Methods
方法之后添加以下代码:
// Helper Methods
public void SetPlayerName(string name)
{
playerName = name;
}
public void SetRoomName(string name)
{
roomName = name;
}
在注释// Tutorial Methods
之后添加如下代码:
// Tutorial Methods
void ConnectToPhoton()
{
connectionStatus.text = "Connecting...";
PhotonNetwork.GameVersion = gameVersion; //1
PhotonNetwork.ConnectUsingSettings(); //2
}
这段代码的工作原理:
- 1)
GameVersion
参数已设置。 这是构建的版本字符串,可用于分隔不兼容的客户端。 对于本教程,它将设置为1
(在声明gameVersion
字段时设置)。 - 2) 调用
ConnectUsingSettings
,用于连接到编辑器中配置的Photon
。 您可以在文档the docs中阅读更多内容。
接下来,添加以下代码行:
public void JoinRoom()
{
if (PhotonNetwork.IsConnected)
{
PhotonNetwork.LocalPlayer.NickName = playerName; //1
Debug.Log("PhotonNetwork.IsConnected! | Trying to Create/Join Room " +
roomNameField.text);
RoomOptions roomOptions = new RoomOptions(); //2
TypedLobby typedLobby = new TypedLobby(roomName, LobbyType.Default); //3
PhotonNetwork.JoinOrCreateRoom(roomName, roomOptions, typedLobby); //4
}
}
public void LoadArena()
{
// 5
if (PhotonNetwork.CurrentRoom.PlayerCount > 1)
{
PhotonNetwork.LoadLevel("MainArena");
}
else
{
playerStatus.text = "Minimum 2 Players required to Load Arena!";
}
}
查看每行代码注释评论:
- 1)
LocalPlayer
的NickName
参数是从私有变量playerName
设置的。这是您在Photon
网络上玩的所有人都可以使用的名称,并用作唯一标识符。 - 2) 声明了
RoomOptions
类的对象。这包含了创建房间时所需的常用房间属性。它可以用来让用户控制房间的各种特征,例如可以加入的最大玩家数量,PlayerTtl
(玩家生存时间)等等。(Docs) - 3) 声明了类
TypedLobby
的对象。这是指Photon
服务器上的特定大厅类型。名称和大厅类型用作唯一标识符。Room
名称由私有变量roomName
设置,Lobby
类型设置为Default
。 (Docs) - 4) 最后,使用前面设置的参数 -
roomName,roomOptions和typedLobby
调用PhotonNetwork
类的JoinOrCreateRoom
方法。如果具有新房间名称但尚不存在的新用户调用该方法,则会创建一个房间并将该用户设置为Lobby Leader
。否则,其他玩家就加入了房间。 - 5) 一旦
Lobby Leader
创建并加入了一个房间,LoadArena
按钮将被设置为活动状态。在加载Arena
之前设置一个检查,以确保仅在两个玩家都加入房间时才加载MainArena
场景。
3. PUN Callback Methods
现在您已经添加了Joining
和Creating a Room
的基本构建块,剩下要做的就是添加PUN Callback
方法来处理异常处理。
在注释// Photon Methods
之后添加以下代码:
// Photon Methods
public override void OnConnected()
{
// 1
base.OnConnected();
// 2
connectionStatus.text = "Connected to Photon!";
connectionStatus.color = Color.green;
roomJoinUI.SetActive(true);
buttonLoadArena.SetActive(false);
}
public override void OnDisconnected(DisconnectCause cause)
{
// 3
isConnecting = false;
controlPanel.SetActive(true);
Debug.LogError("Disconnected. Please check your Internet connection.");
}
public override void OnJoinedRoom()
{
// 4
if (PhotonNetwork.IsMasterClient)
{
buttonLoadArena.SetActive(true);
buttonJoinRoom.SetActive(false);
playerStatus.text = "You are Lobby Leader";
}
else
{
playerStatus.text = "Connected to Lobby";
}
}
让我们看看每段代码的作用:
- 1) 顾名思义,当用户连接到
Photon Network
时,会调用OnConnected
。这里,该方法调用基本方法onConnected()
。需要执行的任何其他代码都是在此方法调用之后写入的。 - 2) 这些方法向用户提供反馈。当用户成功连接到
Photon Network
时,将设置UI Text connectionStatus
,并将roomJoinUI GameObject
设置为visible
。 - 3) 如果用户从
Photon Network
断开连接,则会调用OnDisconnected
。在这种情况下,controlPanel GameObject
设置为false
,并将错误类型消息记录到Unity
。 - 4) 最后,当用户加入房间时调用
OnJoinedRoom
。在这里,它检查用户是否是Master Client
(第一个加入房间的用户)。如果是这样,则将用户设置为lobby leader
并且显示消息以指示这一点。lobby leader
有能力加载MainArena
场景,这是在大多数流行的多人游戏中创建房间的常用方式。否则,如果用户不是第一个加入房间,则会显示一条消息,告诉该用户他们已成功加入房间。
保存Launcher.cs
脚本并返回Launcher
场景,然后单击Play
。
如您所见,当场景开始时,ConnectToPhoton
被调用,连接状态UI文本显示Connecting…
。 成功连接后,文本将更改为“Connected”
,并且roomJoinUI GameObject
的可见性将设置为true
。
接下来,用户可以通过单击Join Room
按钮输入他们的Name
以及他们要创建或加入的房间的名称。
最后,如果用户是Master Client
,则playerStatus Text
设置为“You are now the Lobby Leader!”
并且Load Arena
按钮被设置为活动状态。 否则,显示大厅加入成功的指示。
此时,您可以通过选择File ▸ Build and Run
来构建操作系统项目的可执行文件来测试加入房间。 您应该能够使用新构建的可执行文件和连接到同一房间的Unity Editor
加载MainArena
场景。
但是,你只能看到没有玩家的空竞技场。在下一节中,您将学习如何为每个客户端添加玩家和球预制件到场景中。您还可以在Photon Network
中同步其位置,旋转等。
4. Using the Photon Transform View Component
随着房间加入代码的完成,您需要知道的下一个重要的Photon Unity Networking
概念是Photon View Component
。
使用Photon View Component
进行多人游戏时,PUN
可以很容易地制作一个预制件,其属性(位置,旋转等)必须在网络中同步。
理解使用PUN
的一个重要概念是,应该通过网络实例化的预制件必须位于名为Resources
的文件夹中。
在Resources
文件夹中使用Prefabs
的一个重要副作用是您需要查看其名称。您的Assets
资源路径下不应该有两个预制件名称相同,因为Unity
只会选择它找到的第一个预制件。
有了这个,让我们开始构建游戏管理器。
在Assets / RW / Scripts
中打开GameManager.cs
脚本。
以下是GameManager.cs
脚本的概述:
- 1) 加载
MainArena
场景时,请检查客户端是否为Master
。如果是,则使用PhotonNetwork.Instantiate
实例化Car GameObject
,更改player1 GameObject
的名称,并实例化ball GameObject
。否则只需实例化player2 GameObject
并更改其名称。 - 2) 添加
PUN
回调方法,该方法将根据网络条件和事件处理各种用例。 - 3) 辅助方法将禁用UI,退出房间等。
在注释// Start Method
之后添加以下代码:
// Start Method
void Start()
{
if (!PhotonNetwork.IsConnected) // 1
{
SceneManager.LoadScene("Launcher");
return;
}
if (PlayerManager.LocalPlayerInstance == null)
{
if (PhotonNetwork.IsMasterClient) // 2
{
Debug.Log("Instantiating Player 1");
// 3
player1 = PhotonNetwork.Instantiate("Car",
player1SpawnPosition.transform.position,
player1SpawnPosition.transform.rotation, 0);
// 4
ball = PhotonNetwork.Instantiate("Ball",
ballSpawnTransform.transform.position,
ballSpawnTransform.transform.rotation, 0);
ball.name = "Ball";
}
else // 5
{
player2 = PhotonNetwork.Instantiate("Car",
player2SpawnPosition.transform.position,
player2SpawnPosition.transform.rotation, 0);
}
}
}
下面是工作原理介绍:
- 1) 检查客户端是否连接到
Photon Network
。如果网络存在一些问题,则应加载Launcher
场景,以便客户端可以尝试再次连接。 - 2) 获取对本地玩家(控制客户端的玩家)的引用,并检查它是否是主客户端
(Master client)
。 - 3) 如果是,则使用
PhotonNetwork.Instantiate
从Resources
文件夹(您将在下一步中执行)实例化Player GameObject
,并在player1 GameObject
中保存对它的引用。 - 4) 类似地,实例化
Ball GameObject
,使其与连接到当前房间的所有客户端上加载的Ball GameObject
相同。 - 5) 如果客户端不是
Master
,则从Resources
文件夹加载Player GameObject
,并在player2 GameObject
中保存对它的引用。
保存文件并返回编辑器。
现在您已准备好实例化Player
和Ball GameObjects
的逻辑,下一步是添加所需的组件,以便可以使用PhotonNetwork.Instantiate
方法对它们进行实例化。
在Project
窗口中,双击Assets / RW / Prefabs
中的Car prefab
,在Prefab Editing
模式下将其打开。
在Inspector
中,您应该能够看到Car GameObject(Rigidbody,Collider,Movements等)
的一些基本组件已经是预制件的一部分。
由于只需要在网络上同步GameObject
的Transform
属性,因此将Photon Transform View
组件添加到Car GameObject
。
你会注意到还添加了一个Photon View
组件,因为Photon Transform View
组件从它继承了很多属性。
除了同步GameObject
的位置,旋转和缩放之外,Photon Transform View
还为您提供了许多不同的选项,使得即使每秒仅接收数据几次,同步值也会显得平滑。
在Inspector
中,在Photon View Component
中将Observe option
设置为Unreliable on change
。这将确保Car Transform
值之间的平滑过渡。
此外,将Car
预制件添加到Photon View Component
中的Observed Components
列表中,以便同步其选定的Transform
属性(在Photon View Component
组件中视为已选择)。
保存Car
预制件。
接下来,在Prefab Editing
模式下从Assets / RW / Prefabs
打开Ball
预制件并重复上述步骤:
- 1) 添加
Photon Transform View component
。 - 2) 将
Observe Option
设置为更改时不可靠Unreliable
。 - 3) 在
Photon View component
的Observed Components list
列表中添加Ball prefab
。
最后,将Car and Ball
预制件从Assets / RW / Prefabs
移动到Assets / RW / Resources
,以便它们可以通过PhotonNetwork.Instantiate
方法加载。
是时候测试了!
选择File ▸ Build and Run
以构建操作系统的可执行二进制文件。
这次:
- 在两个客户端中输入不同的玩家名称。
- 输入相同的房间名称。
- 单击主客户端
Master client
中的“Load Arena”
按钮。
你应该看到MainArena
场景加载了2个玩家(汽车)和一个球。 您可以使用键盘上的WASD
或箭头键在竞技场中移动汽车。
请注意,只有1辆车在两个客户端(属于客户的汽车)中移动。
您还可以看到玩家和球的位置在两个客户端都是同步的。
所有工作都由您添加到Car and Ball
预制件中的Photon Transform View Component
完成。
但是,如果关闭其中一个客户端,则另一个客户端将保持不稳定状态。 要处理这样的情况,您将添加回调方法,以根据客户端的当前情况(例如网络丢失或其他玩家离开)执行必要的操作。
5. Adding PUN Callback Methods
在注释// Update Method
方法之后,在GameManager.cs
中添加以下代码:
// Update Method
void Update()
{
if (Input.GetKeyDown(KeyCode.Escape)) //1
{
Application.Quit();
}
}
这非常简单。
在游戏中的任何一点,如果按下Escape
按钮,请调用Application.Quit
。
接下来,在注释// Photon Methods
之后添加以下代码:
// Photon Methods
public override void OnPlayerLeftRoom(Player other)
{
Debug.Log("OnPlayerLeftRoom() " + other.NickName); // seen when other disconnects
if (PhotonNetwork.IsMasterClient)
{
PhotonNetwork.LoadLevel("Launcher");
}
}
OnPlayerLeftRoom
是一种PUN
回调方法,只要玩家离开房间,就可以通过关闭客户端或断开与网络的连接来调用它。
最后,在注释// Helper Methods
之后添加以下代码:
// Helper Methods
public void QuitRoom()
{
Application.Quit();
}
单击Canvas / Top Menu Panel / Quit Room
按钮时,将调用此方法。
最后将Quit Room Button
的On Click()
事件设置为GameManager.QuitRoom
。
就是这样!
您可以为您的操作系统构建最终二进制文件并开始玩!
本教程的目的是向您介绍构建多人游戏的基本概念。 您可以使用这些相同的原则将现有的单人游戏变成多人游戏!
您可以在官方网站 the official website上阅读有关Photon Unity Networking
库的更多信息。
后记
本篇主要讲述了使用Unity和Photon进行多人游戏简介,感兴趣的给个赞或者关注~~~