开发实战Part 3:为Quest 2构建多人游戏应用体验
构建多人游戏
(映维网Nweon 2022年07月06日)多人游戏体验已经成为VR生态系统中越来越重要的一环。多人游戏功能允许你轻松地在各种游戏和应用中查找和邀请好友。
在这个“Building Your Multiplayer VR Experience(构建你的多人VR体验)”博文系列中,Meta将通过SharedSpaces示例来讨论Quest中的Platform SDK多人游戏功能,并向你展示如何利用所述示例来构建你的多人游戏应用。
延伸阅读:开发实战Part 1:为Quest 2构建多人游戏应用体验
延伸阅读:开发实战Part 2:为Quest 2构建多人游戏应用体验
在新一篇的博文中,Meta将介绍根据SharedSpaces Unity示例制作一个简单游戏的步骤。下面是映维网的具体整理:
在你的示例Unity项目中,创建一个名为“GameAssets”的空文件夹。你将用它来为游戏所需的所有asset创建文件夹。所有游戏脚本将位于主SharedSpaces Scripts之下的“GameScripts”文件夹。
1. 理解Startup场景
SharedSpaces示例从启动场景开始,并创建你在整个体验过程中可能需要的游戏对象和prefabs。然后是大厅,在这里你可以选择停留或进入另一个房间。在开始创建自定义房间之前,请打开Starup场景以熟悉游戏对象和脚本。
在这个场景中,你会注意到一定的主要对象:
-
SharedSpacesApplication:这个游戏对象包含SharedSpace应用脚本,它引用了网络层、场景加载程序、生成程序和VoIP。所述脚本由各种回调函数组成,用于建立、断开或恢复主机或客户端连接。它同时包括允许通过邀请面板发送邀请、记录错误、加入和加载正确房间等功能的函数。
-
SharedSpacesNetworkLayer:这个游戏对象引用了Photon Realtime传输脚本。它充当网络管理器,支持SharedSpaces的玩家和网络层之间的通信。网络管理器脚本是一个Unity netcode脚本,用于处理所有与networking相关的设置,例如允许你启动或停止networking,允许你提供网络预制和注册场景名称。在SharedSpaces示例中,团队提供了对玩家prefab和会话的参考,如下所示:
-
SharedSpacesSpawner:这个对象包含脚本SharespacesPawner,它具有生成网络prefab的函数。所述函数返回NetworkObject。
2. 创建你的环境
为了便于演示,本文使用的是公共Purple Room,但你可以在任何喜欢的任何房间中实现,并且可以根据游戏需要进行改动。请注意,其他房间属于私密,用于邀请朋友。
相信你现在已经对Startup场景中的函数有了基本的认知,所以下面我们是时候进入下一个场景。由于是在Purple Room中创建游戏,请从Unity Assets中打开Purple Room场景。你会看到已经设置了一定的元素。使用这一设置来构建世界。
在本例中,你可以将Unity的Asset Store作为所有game asset的来源地。下面提供了用于游戏的免费asset:
请注意,asset的使用应符合许可条款。
在深入了解asset之前,我们先了解更多关于游戏的信息。在这个示例中,构建的游戏将是一个双人游戏。其中,两名玩家竞争收集逃到森林的鸡。率先收集30只鸡的人获胜。通过这个简单的游戏,你将学习到如何使用SharedSpace示例轻松创建多人游戏。我们将讨论网络代码基础知识,邀请朋友一起玩,以及多人游戏功能的其他方面。
回到场景,导入以下asset作为prefab:鸡、农民和环境的asset。首先,将环境prefab拖到场景中。你的环境是低多边形森林场景。
接下来,添加生成鸡的目标位置,并令它们在区域内随机移动。为此,创建环境的子对象并将其称为“Positions”。这将包含生成鸡的所有位置。
要控制的鸡生成和游戏记分,请添加一个名为GameController的游戏对象,并附带GameController脚本。这将是你的主控制器,它添加了与鸡实例化相关的逻辑。
GameController脚本包含控制鸡的功能,鸡在哪里生成,启用和禁用触发器和生成点,显示谁获胜,以及在收集鸡时播放独特声音的逻辑。
接下来,你希望鸡在GameController设置的随机位置中实例化。为了实现这一点,添加一个名为PopulateChickens的协同程序,它控制鸡实例化的位置和频率。为了在鸡实例化后为其启用随机移动,请在鸡prefab中添加一个脚本,并将其称为RandomMovement。所述脚本令鸡向区域内的随机点移动,并控制其动画。给鸡加上一个名为“Chicken”的标签,这样你就可以识别它们。确保你的鸡有一个连接到prefab的collider,并启用“IsTrigger” flag。
回到场景,添加在收集鸡时触发的音频源。添加两个音效:一个GameController,当捕捉到鸡时触发“Collect”声音;另一个SfxAmbience,用于“Background”声音。
现在已经有了基本的场景设置。接下来,我们来看看如何设置你的玩家。
3. 设置玩家prefab
在游戏中,主玩家是一个农民prefab。打开SharedSpacesPlayer pfrefab并检查其包含的元素。然后你就会了解玩家对象需要什么组件,以及如何自定义它。
在玩家prefab中,你会注意到一定的组件:
-
NetworkObject:由于你希望玩家prefab在网络持久化,因此需要将网络对象组件附加到它。
-
SharedSpacesPlayerColor:用于通过网络更新玩家的颜色。
-
SharedSpacesPlayerName:添加它是为了在角色网格显示玩家的名称。
要更新这个prefab以使用农民模型,请首先更新Animatorx下的Avatar以使用农民网格。接下来,在 Geometry选项卡下添加农民网格。确保在SharedSpacesPlayerColor脚本下更新Mesh Renderer的参考,从而匹配玩家的衣服。现在,玩家prefab已准备好在场景中引用。你可以替换现有prefab,也可以选择将其另存为新prefab。
现在你的玩家prefab已经准备好,下面是时候更新引用来实例化你的农民,并将其作为玩家操纵的对象。
为此,请打开Startup场景并选择SharedSpacesNetworkLayer。在这里,你将确保网络prefab在NetworkManager脚本中引用农民模型。
更新SharedSpacesSpawner脚本下的引用以反映这一prefab。
你同时要确保所有SharedSpaces Launch Triggers检查“Chicken” flag,并且如果游戏对象是鸡时不会触发。这可以通过添加这个检查来实现:
if (other.tag == "Chicken") return;
现在当启动示例时,你的播放器将更新为农民。如果你进入Purple Room,你会看到鸡正在生成。下一步是添加游戏和networking逻辑。
4. 添加networking逻辑
你需要将游戏成,当一名玩家收集鸡时,分数通过网络正确传播,并对两名玩家可见。为此,你需要使用Unity Netcode的Network Variables和Remote Procedure Calls (RPCs)。
NetworkVariable的值定期复制到网络中的其他节点。当客户端最初连接到主机时,NetworkVariable的最新值将复制到新客户端。在SharedSpace中,用于添加NetworkVariable的逻辑位于SharedSpacesPlayerState类中。请注意,这个脚本是从NetworkBehavior扩展而来,NetworkBehavior是从MonoBehavior派生的抽象类,是所有网络脚本应派生的基类。
在这个脚本中,添加两个新的NetworkVariable,一个用于第一名玩家的分数,另一个用于第二明玩家的分数。
public NetworkVariablefirstPlayerScore = new NetworkVariable (); public NetworkVariable secondPlayerScore = new NetworkVariable ();
指定第一个进入游戏Purple Room的人作为玩家1。接下来,创建一个每次收集鸡时都会调用的函数,并将其命名为SetScore。
public void SetScore() { if (!LocalPlayerState) return; if (IsServer) { Debug.Log("First Player value changed"); FirstPlayerCatchChickenServerRpc(); } else { Debug.Log("Second Player value changed"); SecondPlayerCatchChickenServerRpc(); } }
在这个函数中,根据它是服务器还是客户端,你将使用RPC更新第一个玩家的分数或第二个玩家的分数。为此添加两个服务器RPC。
[ServerRpc] private void FirstPlayerCatchChickenServerRpc() { firstPlayerScore.Value++; } [ServerRpc] private void SecondPlayerCatchChickenServerRpc() { secondPlayerScore.Value++; }
设置好后,确保在玩家收集鸡时调用SetScore函数。要检测玩家和鸡之间的collision,请添加一个名为DetectCollision的新脚本,并将其附加到农民玩家。这个脚本在收集鸡时将调用SetScore函数。类似于:
private void OnTriggerEnter(Collider other) { if (other.gameObject.tag == "Chicken") { Destroy(other.gameObject); GameController.chickenCollected = true; playerState.SetScore(); } }
5. 设置记分牌
现在已经设置好了玩家、鸡和网络代码,接下来你需要一个地方来显示分数以及获胜者是谁。你同时希望两名玩家能够同时查看分数。
在Purple Room中添加两个记分牌,一个用于玩家1,另一个用于玩家2。接下来,添加第三个记分牌用于显示获胜者的状态。由于第三个记分牌不需要通过网络发送或更新任何值,请将其作为普通游戏对象添加到场景中。下面的示例显示了Unity Asset Store中用于创建低多边形记分牌的可用asset。
就像你的玩家一样,这两个记分牌同样需要通过网络更新。两个记分牌都应该附接一个NetworkObject组件,如下所示。
现在,确保spawner在主机连接时生成新创建的记分牌,并且它们仅在玩家进入Purple Room时出现。要实现这一点,请添加对记分牌的引用,并在SharedSpacesSpawner脚本中添加两个新的spawn函数,同时从SharedSpacesApplication脚本中调用它们:
SharedSpacesSpawner:
public NetworkObject firstPlayerScorePrefab; public NetworkObject secondPlayerScorePrefab; public NetworkObject SpawnFirstPlayerScoreBoard() { NetworkObject score = Instantiate(firstPlayerScorePrefab); score.Spawn(); return score; } public NetworkObject SpawnSecondPlayerScoreBoard() { NetworkObject score = Instantiate(secondPlayerScorePrefab); score.Spawn(); return score; }
SharedSpacesApplication:
public NetworkObject scoreBoardFirstPlayer; public NetworkObject scoreBoardSecondPlayer; private void OnHostStarted() { … scoreBoardFirstPlayer = spawner.SpawnFirstPlayerScoreBoard(); scoreBoardSecondPlayer = spawner.SpawnSecondPlayerScoreBoard(); } private void OnHostRestored() { … scoreBoardFirstPlayer = spawner.SpawnFirstPlayerScoreBoard(); scoreBoardSecondPlayer = spawner.SpawnSecondPlayerScoreBoard(); }
更新spawner程序和应用脚本中的引用,然后将新的分数prefab添加到NetworkManager中的网络prefab列表中。
设置记分牌的最后一步是在收集鸡时启用分数更新。为此,添加两个脚本,每个记分牌一个。所述脚本提供了SharedspacesPlayerState脚本中相应回调的更新分数。
ScoreControllers:
两个新脚本ScoreControllerFirstPlayer和ScoreControllerSecondPlayer控制分数的更新和显示方式。下面显示了当第一个玩家的分数更改时调用的函数:
public static void UpdateScore(int newScore) { firstPlayerCurrentScore = newScore; }
SharedSpacesPlayerState:
确保添加OnValueChanged订阅,从而在分数更改时通知记分牌。
OnEnable:
firstPlayerScore.OnValueChanged += OnFirstPlayerScoreChanged; secondPlayerScore.OnValueChanged += OnSecondPlayerScoreChanged;
OnDisable:
firstPlayerScore.OnValueChanged -= OnFirstPlayerScoreChanged; secondPlayerScore.OnValueChanged -= OnSecondPlayerScoreChanged; private void OnFirstPlayerScoreChanged(int oldScore, int newScore) { ScoreControllerFirstPlayer.UpdateScore(newScore); } private void OnSecondPlayerScoreChanged(int oldScore, int newScore) { ScoreControllerSecondPlayer.UpdateScore(newScore);
6. 最终设置、构建和运行游戏
最后,回到GameController,确保记分牌正确显示谁是赢家。在这里,你同时必须确保在游戏开始时禁用所有面板,并且只有当两名玩家都在竞技场时才填充鸡。
现在,你已经准备好构建游戏。单击“Build”,确保已设置密钥库,并且平台已设置为Android。APK准备好后,使用ODH在Quest头显安装并运行,如上一篇博文所示。
这关于博文快速过了一遍如何使用Unity SharedSpace示例构建简单的游戏。你学习了如何设置玩家,如何使用NetworkVariables和RPC来确保数据在网络复制,以及如何只需数个步骤就可以构建一个简单的VR多人游戏。如有兴趣,你可以参阅YouTube视频介绍。在下一篇博文中,Meta将介绍更多的多人游戏功能。