在创建微信小程序源码及h5小游戏源码过程中,我们将创建我们的游戏应用程序(使用 Unity 3d),我们将连接到我们的WebSocket服务器。议使用 Unity Hub 来管理您的 Unity 下载,因为它允许您拥有多个版本的 Unity,如果您愿意的话。在撰写本文时,我使用的是 Unity 版本2020.3.23f1。在大多数情况下,我们今天将介绍的内容应该适用于所有 2018、2019、2020 版本的 Unity。我建议使用 Unity 的 LTS(长期支持版本)版本之一,您可以从LTS Releases Unity Page下载它们。在我们构建游戏应用程序时,使用其中一个版本可确保更高的稳定性和支持。
仓库源码:y.wxlbyx.icu
所以,现在我们已经在我们的机器上安装了 Unity3D 引擎,让我们创建我们的 Unity 项目。
创建我们的 Unity 项目
首先,让我们打开 Unity Hub 并确保我们在应用程序的项目部分如下图所示:
进入 Unity Hub 后,选择“新建”按钮旁边的向下插入符号,然后选择您想要创建应用程序的 Unity 版本。
您现在应该看到一个标题为“创建新项目…”的对话框,并确保您在“模板”部分中选择了“3D”。在“设置”部分,选择项目名称(我将使用“有史以来最好的游戏 – 多人游戏”)并选择您希望创建项目的位置。准备就绪后,选择“创建”按钮,如下图所示:
选择“创建”后,您将看到 Unity 开始工作,因为它创建了我们将基于“3D”项目模板使用的基础项目。
创建我们的玩家移动脚本
创建玩家角色 player 后,让我们通过创建移动脚本来为其添加在场景中移动的能力。为了让事情井井有条,让我们快速创建一个“Scripts”文件夹来保存我们所有的脚本,并在该文件夹中创建一个名为“Player”的文件夹来保存播放器特定的文件夹。
因此,在“Project”窗口的 Assets 文件夹中,让我们创建一个名为“Scripts”的文件夹。创建后,让我们进入该文件夹并创建一个名为“Player”的文件夹。创建文件夹后,让我们创建一个名为“Movement”的新 C# 脚本。
注意:当我们创建脚本时,请确保在取消选择文件之前更改文件的名称。否则你将不得不做额外的工作来确保你的脚本被正确命名。我不会详细说明这意味着什么,因为需要一些时间来解释,但请注意这一点。
在我们的 Player 文件夹中,右键单击空白区域并选择“创建 > C# 脚本”。一旦出现脚本图标,我们将命名我们的脚本运动,如下所示:
现在我们的“运动”脚本已经创建,让我们打开它和我们的代码。所以继续双击该文件并在您首选的编辑器中打开我们的脚本(默认情况下,Unity 使用 Visual Studio)并删除所有默认代码,直到您的文件如下所示:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Movement : MonoBehaviour
{
}
现在我们的文件已经很清楚了,让我们在文件中的 Movement 公共类中添加这行代码:
float movementSpeed = 5.0f;
这个浮点变量将用于确定我们播放器的速度。从理论上讲,您可以将此值设置为您想要的任何值,但在这种情况下,我们将其设置为 5.0f。
添加第一个变量后,让我们添加实际移动玩家角色的功能。在我们的新变量下面,让我们添加以下代码:
void Update()
{
Vector3 moveDir = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
Vector3 newMoveDir = moveDir * movementSpeed;
transform.position += newMoveDir * Time.deltaTime;
}
此代码利用 Unity 引擎每帧调用的 Update 函数,并根据播放器的移动输入(WASD 和箭头键)更改播放器位置。moveDir变量创建一个 Vector3 对象,该对象使用 Input.GetAxis 方法存储玩家的输入。GetAxis根据按下的方向输出一个从 -1 到 1 的值,传入Input.GetAxis方法的 Horizo ntal和Vertical参数对应于键输入:
水平:A/D/左箭头/右箭头
垂直:W/S/向上箭头/向下箭头
一旦这个 Vector3 对象被创建,它就会被添加到玩家的对象变换位置,它决定了玩家在世界中的位置。
现在我们已经创建了脚本,我们需要将它添加到我们的玩家对象中,以便我们的玩家角色移动。让我们导航回 Unity 编辑器,然后选择我们的 Cube。
在 Inspector 窗口中,导航到底部并单击“添加组件”按钮,如下所示:
创建多人游戏套接字连接
在花了一些时间构建我们非常简单的游戏之后,是时候将我们的游戏连接到我们之前制作的多人游戏服务器了。为此,我们需要在项目中添加两个新脚本:PlayerData和SocketManager。
这两个脚本对于使我们的游戏与我们的 WebSocket 服务器有效通信至关重要。
因此,让我们从 PlayerData 脚本开始。
在我们项目的 Scripts 文件夹中,让我们创建一个名为“Multiplayer”的新文件夹。这个文件夹将包含我们游戏的多人游戏方面的所有代码。打开我们新建的“Multiplayer”文件夹并创建一个新脚本(右键单击> Create > C# Script)并命名为“PlayerData”。在我们的 PlayerData 脚本文件中,将其全部内容替换为以下代码:
using System;
[Serializable]
public struct PlayerData
{
public string id;
public float xPos;
public float yPos;
public float zPos;
public double timestamp;
}
这个脚本本质上是为我们将传递给我们的服务器的数据设置结构,其中将包含有关我们玩家角色当前位置的信息。我们创建这个脚本的原因是因为它可以让我们轻松地将我们的玩家数据转换为我们可以轻松发送到我们的服务器的 JSON 对象/字符串。
如果您查看代码,您可能会注意到[Serializable]行。这实质上使我们创建的这个数据结构成为可序列化的,这意味着我们可以使用一些基本的 Unity 实用程序轻松地将这些数据转换为其他形式。
您可能还注意到这个 PlayerData 对象是一个struct。这允许我们将此 PlayerData 对象用作“结构”,作为我们的数据对象将遵循的形式,并与说一个类相比保持该数据非常轻量级。所以这可能是描述这一点的最糟糕的方式,但如果您有兴趣进一步了解这一点,我鼓励您查看“结构和类之间的差异”。我保证你会学到很多东西。
当我们继续我们的 SocketManager 脚本时,您将看到我们使用 PlayerData 结构来快速存储我们的数据并将其转换为 JSON,然后再将其发送到服务器。
现在我们已经创建了 PlayerData 脚本(或者更好地描述为 PlayerData 结构),让我们创建 SocketManager 脚本,我们将使用它来管理我们的连接、向游戏服务器发送数据和从游戏服务器接收数据。
在 Unity 编辑器的 Project 区域的 Script 文件夹中,让我们在“Multiplayer”文件夹中添加一个名为“SocketManager”的新脚本。我们现在应该在 Multiplayer 文件夹中看到两个脚本 PlayerData 和 SocketManager,如下所示:
16-Multiplayer-scripts-in-folder
创建该文件后,让我们打开该文件。进入文件后,删除为您预先制作的所有代码并添加以下代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using WebSocketSharp;
using Newtonsoft.Json.Linq;
public class SocketManager : MonoBehaviour
{
WebSocket socket;
public GameObject player;
public PlayerData playerData;
// Start is called before the first frame update
void Start()
{
socket = new WebSocket("ws://localhost:8080");
//socket = new WebSocket("ws://websocket-starter-code-multiplayer-websocket-app.bsh-serverconnect-b3c-4x1-162e406f043e20da9b0ef0731954a894-0000.us-south.containers.appdomain.cloud/");
socket.Connect();
//WebSocket onMessage function
socket.OnMessage += (sender, e) =>
{
//If received data is type text...
if (e.IsText)
{
//Debug.Log("IsText");
//Debug.Log(e.Data);
JObject jsonObj = JObject.Parse(e.Data);
//Get Initial Data server ID data (From intial serverhandshake
if (jsonObj["id"] != null)
{
//Convert Intial player data Json (from server) to Player data object
PlayerData tempPlayerData = JsonUtility.FromJson<PlayerData>(e.Data);
playerData = tempPlayerData;
Debug.Log("player ID is " + playerData.id);
return;
}
}
};
//If server connection closes (not client originated)
socket.OnClose += (sender, e) =>
{
Debug.Log(e.Code);
Debug.Log(e.Reason);
Debug.Log("Connection Closed!");
};
}
// Update is called once per frame
void Update()
{
//Debug.Log(player.transform.position);
if (socket == null)
{
return;
}
//If player is correctly configured, begin sending player data to server
if (player != null && playerData.id != "")
{
//Grab player current position and rotation data
playerData.xPos = player.transform.position.x;
playerData.yPos = player.transform.position.y;
playerData.zPos = player.transform.position.z;
System.DateTime epochStart = new System.DateTime(1970, 1, 1, 8, 0, 0, System.DateTimeKind.Utc);
double timestamp = (System.DateTime.UtcNow - epochStart).TotalSeconds;
//Debug.Log(timestamp);
playerData.timestamp = timestamp;
string playerDataJSON = JsonUtility.ToJson(playerData);
socket.Send(playerDataJSON);
}
if (Input.GetKeyDown(KeyCode.M))
{
string messageJSON = "{\"message\": \"Some Message From Client\"}";
socket.Send(messageJSON);
}
}
private void OnDestroy()
{
//Close socket when exiting application
socket.Close();
}
}
现在这是一大段代码。所以我要做的就是把它分成 6 个部分并总结每个部分发生的事情。一旦我们理解了这段代码,我们就可以设置我们的 Unity 场景来使用它并连接到我们之前创建的服务器。但在我们继续之前,您需要做一些非常重要的事情来使 SocketManager 脚本工作
现在所有必要的文件都准备好了,让我们继续。正如我所提到的,我现在将套接字管理器分解为单独的部分。
1. 进口
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using WebSocketSharp;
using Newtonsoft.Json.Linq;
本节非常简单,因为我们只是导入了运行代码所需的所有必要框架。需要注意的最重要的事情是我们正在导入WebSocketSharp(从我们的 .dll 文件)和Newtonsoft.Json.Linq。注意:在 Unity 2020 之前,您需要手动将 Newtonsoft 添加到您的项目中。因此,如果您使用的是 2020 年之前的版本,您可以访问 [此页面] 以了解有关将 Newtonsoft 添加到您的项目的更多信息。
2.变量
WebSocket socket;
public GameObject player;
public PlayerData playerData;
这部分代码声明了我们将在整个脚本中使用的变量,以使我们的 SocketManager 工作。socket变量是我们将用来连接、发送和接收来自服务器的消息的变量。player游戏对象只是代表我们游戏中的玩家,而playerData对象是我们将发送到套接字服务器的数据。这些将是我们的经理工作所需的唯一变量。
3. 创建我们的 WebSocket 连接
因此,在我们的 Start 函数(启动此脚本时触发的函数)中,您将首先看到以下代码行:
socket = new WebSocket("ws://localhost:8080");
socket.Connect();
第一行代码指定了我们的 websocket 游戏服务器的位置以及连接的位置。这可以在我们的本地机器上或 Red Hat OpenShift 上的远程服务器上。唯一需要注意的是,由于我们使用的是 WebSocket 协议,所以我们的网址需要以ws://开头
第二行代码实际上会将我们连接到前行指定的游戏服务器。这应该会启动客户端(Unity 游戏)和服务器之间的“握手”。
4. 接收消息
//WebSocket onMessage function
socket.OnMessage += (sender, e) =>
{
//If received data is type text...
if (e.IsText)
{
//Debug.Log("IsText");
//Debug.Log(e.Data);
JObject jsonObj = JObject.Parse(e.Data);
//Get Initial Data server ID data (From intial serverhandshake
if (jsonObj["id"] != null)
{
//Convert Intial player data Json (from server) to Player data object
PlayerData tempPlayerData = JsonUtility.FromJson<PlayerData>(e.Data);
playerData = tempPlayerData;
Debug.Log("player ID is " + playerData.id);
return;
}
}
};
//If server connection closes (not client originated)
socket.OnClose += (sender, e) =>
{
Debug.Log(e.Code);
Debug.Log(e.Reason);
Debug.Log("Connection Closed!");
};
所以这部分代码应该看起来与我们在服务器上创建的代码非常相似。本质上,这段代码中有两个监听器在运行;一个 OnMessage 侦听器和一个 OnClose 侦听器。与我们的服务器类似,这两个侦听器正在侦听传入消息以及何时从服务器触发关闭方法(也就是服务器出于某种原因关闭它与我们的游戏的连接)。
在我们的 OnMessage 代码部分中,您将看到:
if (e.IsText)…
这只是一个健全性检查,因为我们的服务器理论上可以向我们发送二进制或文本数据。在我们的例子中,我们只想处理文本数据。
JObject jsonObj = JObject.Parse(e.Data);
//Get Initial Data server ID data (From initial server handshake
if (jsonObj["id"] != null)
{
//Convert Initial player data Json (from server) to Player data object
PlayerData tempPlayerData = JsonUtility.FromJson<PlayerData>(e.Data);
playerData = tempPlayerData;
Debug.Log("player ID is " + playerData.id);
return;
}
此代码解析从服务器发送的 JSON 字符串数据,然后检查 JSON 字符串是否采用我们希望使用的格式。在这种情况下,它专门查看 JSON 数据是否具有id字段。这样做是因为我们将服务器设计为向客户端发送一组特定的数据,其中包含玩家 ID 以供以后存储。您可以使用这样的逻辑来发送具有特定字段的数据,并根据数据可能具有的字段对数据执行不同的操作。
一旦我们确认了该字段,我们就会将我们收到的 JSON 数据转换为一个 PlayerData 对象,然后我们可以在我们的代码中使用该对象。这非常方便,因为它通过自动将 JSON 字段链接到 PlayerData 对象中的数据字段来为我们节省大量时间,然后我们可以在 Unity 代码中使用这些字段。它不会包含 PlayerData 对象所期望的所有数据,但 Unity 能够实现这一点,并且只是根据属性类型将不存在的字段设置为默认值。
5. 向游戏服务器发送数据
//Debug.Log(player.transform.position);
if (socket == null)
{
return;
}
//If player is correctly configured, begin sending player data to server
if (player != null && playerData.id != "")
{
//Grab player current position and rotation data
playerData.xPos = player.transform.position.x;
playerData.yPos = player.transform.position.y;
playerData.zPos = player.transform.position.z;
System.DateTime epochStart = new System.DateTime(1970, 1, 1, 8, 0, 0, System.DateTimeKind.Utc);
double timestamp = (System.DateTime.UtcNow - epochStart).TotalSeconds;
//Debug.Log(timestamp);
playerData.timestamp = timestamp;
string playerDataJSON = JsonUtility.ToJson(playerData);
socket.Send(playerDataJSON);
}
在我们的更新函数中,我们做了很多事情。我们要做的第一件事就是检查我们的套接字变量是否有效。如果不是,我们只是短路/停止我们的代码,因为它下面的所有代码都需要套接字对象有效。
如果套接字对象有效,我们有一个“IF 语句”来检查我们的玩家游戏对象是否有效,以及我们的 playerData id 字段是否有一个非空的字符串值。这个检查做了两件重要的事情:
它确保我们有一个与该脚本关联的有效播放器。这很重要,因为我们的 IF 语句中的所有代码都需要有效的玩家数据(例如位置)才能发送到服务器。
它确保玩家有一个ID。这很重要,因为在我们向服务器发送数据之前,我们需要一个标识符让服务器知道哪个客户端发送了数据。这只是确保我们的游戏在我们正确配置与服务器的连接之前不会发送数据。
在我们确保我们的玩家和玩家 ID 是有效的之后,剩下的代码就是获取关于玩家当前位置的信息,并设置一个时间戳,在这个时间戳上我们捕获了关于玩家的这些信息。这些都存储在我们的 PlayerData 对象中,然后由 Unity 的 JSONUtility 转换为 JSON 字符串并发送到我们的服务器以供其检索。
就像我们的游戏正在向我们的服务器发送关于我们的玩家以及玩家在任何特定时刻的位置的数据一样。
5.关闭游戏服务器连接
private void OnDestroy()
{
//Close socket when exiting application
socket.Close();
}
最后,我们有一小部分代码在脚本本身被销毁(也就是游戏应用程序因任何原因关闭时)被触发。此代码只是向我们的服务器发送一条“关闭”消息,这将触发服务器上的 OnClose 侦听器。这是一组重要的代码,因为服务器必须知道玩家何时离开游戏,这样它才能将该玩家从服务器中移除并通知所有其他玩家。
服务器启动并运行后,返回 Unity 编辑器并按下播放按钮运行我们的游戏。游戏开始后,返回终端窗口,您现在应该会看到终端窗口打印有关我们的播放器的信息,如下所示:
Client 50c7f039-1d1a-45d5-a695-a8656c6a4ba8 Connected!
Player Message
{
id: '50c7f039-1d1a-45d5-a695-a8656c6a4ba8',
xPos: -0.7148541808128357,
yPos: 0.5,
zPos: 0.6946321129798889,
timestamp: 1638454171.455932
}
Player Message
{
id: '50c7f039-1d1a-45d5-a695-a8656c6a4ba8',
xPos: -0.7148541808128357,
yPos: 0.5,
zPos: 0.6946321129798889,
timestamp: 1638454171.488816
}
今天的文章微信小程序源码及H5小游戏源码内核构建方法分享到此就结束了,感谢您的阅读,如果确实帮到您,您可以动动手指转发给其他人。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/26157.html