介绍
规划多人游戏开发方法 – 在整个项目的进一步开发中发挥着最重要的作用之一,因为它包含了我们在创建真正高质量的产品时应该考虑的许多标准。在今天的宣言教程中,我们将看一个方法示例,该方法使我们能够创建真正快速的游戏,同时尊重所有安全和反违规规则。

所以,让我们定义我们的主要标准:
多人游戏需要一种特殊的方法来管理网络同步,尤其是在实时情况下。 二进制协议用于加速客户端之间的数据同步,反应字段将有助于以最小的延迟和节省内存来更新玩家位置。服务器权限是一项重要原则,关键数据仅在服务器上处理,确保游戏完整性并防止作弊。然而,为了让我们最大限度地提高性能 – 服务器只进行关键更新,剩下的交给客户端反作弊。实施客户端反投诉,以便在服务器上不增加负载的情况下处理不太关键的数据。

架构的主要组成部分
客户端(unity):客户端负责显示游戏状态,将玩家操作发送到服务器并从服务器接收更新。这里还使用反应字段来动态更新玩家位置。服务器端(node.js):服务器处理关键数据(例如,移动、碰撞和玩家动作)并将更新发送到所有连接的客户端。非关键数据可以在客户端上处理并使用服务器转发到其他客户端。二进制协议:二进制数据序列化用于减少传输的数据量并提高性能。同步:提供客户端之间数据的快速同步,以最大程度地减少延迟并确保流畅的游戏体验。客户端反作弊:它用于我们可以在客户端上更改并发送给其他客户端的数据。
第 1 步:在 node.js 中实现服务器
首先,您需要在 node.js 上设置一个服务器。服务器将负责所有关键计算并将更新的数据传输给玩家。
安装环境
要在 node.js 上创建服务器,请安装必要的依赖项:
mkdir multiplayer-game-servercd multiplayer-game-servernpm init -ynpm install socket.io
socket.io可以轻松地使用web套接字实现客户端和服务器之间的实时双向通信。
基本服务器实现
让我们创建一个简单的服务器,它将处理客户端连接、检索数据、计算关键状态并在所有客户端之间同步它们。
// create a simple socket io serverconst io = require('socket.io')(3000, { cors: { origin: '*' }});// simple example of game stateslet gamestate = {};let playerspeedconfig = { maxx: 1, maxy: 1, maxz: 1};// work with new connectionio.on('connection', (socket) => { console.log('player connected:', socket.id); // initialize player state for socket id gamestate[socket.id] = { x: 0, y: 0, z: 0 }; // work with simple player command for movement socket.on('playermove', (data) => { const { id, dx, dy, dz } = parseplayermove(data); // check maximal values if(dx > playerspeedconfig.maxx) dx = playerspeedconfig.maxx; if(dy > playerspeedconfig.maxy) dx = playerspeedconfig.maxy; if(dz > playerspeedconfig.maxz) dx = playerspeedconfig.maxz; // update game state for current player gamestate[id].x += dx; gamestate[id].y += dy; gamestate[id].z += dz; // send new state for all clients const updateddata = serializegamestate(gamestate); io.emit('gamestateupdate', updateddata); }); // work with unsafe data socket.on('dataupdate', (data) => { const { id, unsafe } = parseplayerunsafe(data); // update game state for current player gamestate[id].unsafevalue += unsafe; // send new state for all clients const updateddata = serializegamestate(gamestate); io.emit('gamestateupdate', updateddata); }); // work with player disconnection socket.on('disconnect', () => { console.log('player disconnected:', socket.id); delete gamestate[socket.id]; });});// simple parse our binary datafunction parseplayermove(buffer) { const id = buffer.tostring('utf8', 0, 16); // player id (16 bit) const dx = buffer.readfloatle(16); // delta x const dy = buffer.readfloatle(20); // delta y const dz = buffer.readfloatle(24); // delta z return { id, dx, dy, dz };}// simple parse of unsafe datafunction parseplayerunsafe(buffer) { const id = buffer.tostring('utf8', 0, 16); // player id (16 bit) const unsafe = buffer.readfloatle(16); // unsafe float return { id, unsafe };}// simple game state serialization for binary protocolfunction serializegamestate(gamestate) { const buffers = []; for (const [id, data] of object.entries(gamestate)) { // player id const idbuffer = buffer.from(id, 'utf8'); // position (critical) buffer const posbuffer = buffer.alloc(12); posbuffer.writefloatle(data.x, 0); posbuffer.writefloatle(data.y, 4); posbuffer.writefloatle(data.z, 8); // unsafe data buffer const unsafebuffer = buffer.alloc(4); unsafebuffer.writefloatle(data.unsafevalue, 0); // join all buffers buffers.push(buffer.concat([idbuffer, posbuffer, unsafebuffer])); } return buffer.concat(buffers);}
此服务器执行以下操作:
处理客户端连接。接收二进制格式的玩家移动数据,验证它,更新服务器上的状态并将其发送到所有客户端。以最小延迟同步游戏状态,使用二进制格式来减少数据量。简单地转发来自客户端的不安全数据。
要点:
服务器权限:所有重要数据均在服务器上处理和存储。客户端仅发送操作命令(例如,位置变化增量)。二进制数据传输:使用二进制协议可以节省流量并提高网络性能,特别是对于频繁的实时数据交换。
第2步:在unity上实现客户端部分
现在让我们在 unity 上创建一个与服务器交互的客户端部分。
要将 unity 连接到 socket.io 上的服务器,您需要连接专为 unity 设计的库。在这种情况下,我们不受任何特定实现的约束(事实上它们都是相似的),而只是使用一个抽象示例。
使用反应字段进行同步
我们将使用反应字段来更新玩家位置。这将使我们能够更新状态,而无需通过 update() 方法检查每个帧中的数据。当数据状态发生变化时,反应字段会自动更新游戏中对象的视觉表示。
要获得反应性属性功能,您可以使用 unirx。
unity 上的客户端代码
让我们创建一个脚本来连接到服务器、发送数据并通过反应字段接收更新。
using UnityEngine;using SocketIOClient;using UniRx;using System;using System.Text;// Basic Game Client Implementationpublic class GameClient : MonoBehaviour{ // SocketIO Based Client private SocketIO client; // Our Player Reactive Position public ReactiveProperty playerPosition = new ReactiveProperty(Vector3.zero); // Client Initialization private void Start() { // Connect to our server client = new SocketIO("http://localhost:3000"); // Add Client Events client.OnConnected += OnConnected; // On Connected client.On("gameStateUpdate", OnGameStateUpdate); // On Game State Changed // Connect to Socket Async client.ConnectAsync(); // Subscribe to our player position changed playerPosition.Subscribe(newPosition => { // Here you can interpolate your position instead // to get smooth movement at large ping transform.position = newPosition; }); // Add Movement Commands Observable.EveryUpdate().Where(_ => Input.GetKey(KeyCode.W)).Subscribe(_ => ProcessInput(true)); Observable.EveryUpdate().Where(_ => Input.GetKey(KeyCode.S)).Subscribe(_ => ProcessInput(false)); } // On Player Connected private async void OnConnected(object sender, EventArgs e) { Debug.Log("Connected to server!"); } // On Game State Update private void OnGameStateUpdate(SocketIOResponse response) { // Get our binary data byte[] data = response.GetValue(); // Work with binary data int offset = 0; while (offset < data.Length) { // Get Player ID string playerId = Encoding.UTF8.GetString(data, offset, 16); offset += 16; // Get Player Position float x = BitConverter.ToSingle(data, offset); float y = BitConverter.ToSingle(data, offset + 4); float z = BitConverter.ToSingle(data, offset + 8); offset += 12; // Get Player unsafe variable float unsafeVariable = BitConverter.ToSingle(data, offset); // Check if it's our player position if (playerId == client.Id) playerPosition.Value = new Vector3(x, y, z); else UpdateOtherPlayerPosition(playerId, new Vector3(x, y, z), unsafeVariable); } } // Process player input private void ProcessInput(bool isForward){ if (isForward) SendMoveData(new Vector3(0, 0, 1)); // Move Forward else SendMoveData(new Vector3(0, 0, -1)); // Move Backward } // Send Movement Data private async void SendMoveData(Vector3 delta) { byte[] data = new byte[28]; Encoding.UTF8.GetBytes(client.Id).CopyTo(data, 0); BitConverter.GetBytes(delta.x).CopyTo(data, 16); BitConverter.GetBytes(delta.y).CopyTo(data, 20); BitConverter.GetBytes(delta.z).CopyTo(data, 24); await client.EmitAsync("playerMove", data); } // Send any unsafe data private async void SendUnsafeData(float unsafeData){ byte[] data = new byte[20]; Encoding.UTF8.GetBytes(client.Id).CopyTo(data, 0); BitConverter.GetBytes(unsafeData).CopyTo(data, 16); await client.EmitAsync("dataUpdate", data); } // Update Other players position private void UpdateOtherPlayerPosition(string playerId, Vector3 newPosition, float unsafeVariable) { // Here we can update other player positions and variables } // On Client Object Destroyed private void OnDestroy() { client.DisconnectAsync(); }}
第 3 步:优化同步和性能
为了确保流畅的游戏体验并最大程度地减少同步期间的延迟,建议:
使用插值:客户端可以使用插值来平滑服务器更新之间的移动。这可以补偿较小的网络延迟。批量数据发送:不要按每一步发送数据,而是使用批量发送。例如,每隔几毫秒发送一次更新,这将减少网络负载。 降低更新频率:将发送数据的频率降低到合理的最低限度。例如,对于大多数游戏来说,每秒更新 20-30 次可能就足够了。
如何简化二进制协议的使用?
为了简化您使用二进制协议的工作 – 创建数据处理的基本原理以及与其交互的方案。
快写红薯通AI
快写红薯通AI,专为小红书而生的AI写作工具
57 查看详情
对于我们的示例,我们可以采用一个基本协议,其中:
1) 前 4 位是用户发出的请求的最大值(例如 0 – 移动玩家,1 – 射击等);
2) 接下来的 16 位是我们客户的 id。
3) 接下来我们填充通过循环传递的数据(一些网络变量),其中存储变量的 id、到下一个变量开头的偏移量(以字节为单位)、变量的类型和它的价值。
为了方便版本和数据控制 – 我们可以以方便的格式(json / xml)创建客户端-服务器通信模式,并从服务器下载一次,以便根据该模式进一步解析我们的二进制数据以获得所需的内容我们的 api 版本。
客户端反作弊
在服务器上处理所有数据是没有意义的,其中一些数据更容易在客户端修改并发送到其他客户端。
为了让你在这个方案中更加安全 – 你可以使用客户端防黑客系统来防止内存黑客 – 例如,我的 gameshield – 一个免费的开源解决方案。
结论
我们举了一个简单的例子,在 unity 上使用 node.js 服务器开发多人游戏,所有关键数据都在服务器上处理,以确保游戏的完整性。使用二进制协议传输数据有助于优化流量,而 unity 中的反应式编程可以轻松同步客户端状态,而无需使用 update() 方法。
这种方法不仅可以提高游戏性能,还可以通过确保所有关键计算都在服务器而不是客户端上执行来增强对作弊的保护。
当然,一如既往地感谢您阅读这篇文章。如果您在组织多人项目架构方面仍有任何疑问或需要帮助 – 我邀请您加入我的 discord
您还可以在我的困境中为我提供很多帮助,并支持发布新文章以及为开发人员免费提供的库和资源:
我的不和谐 | 我的博客 | 我的 github
btc: bc1qef2d34r4xkrm48zknjdjt7c0ea92ay9m2a7q55
eth: 0x1112a2ef850711df4de9c432376f255f416ef5d0
usdt (trc20):trf7sli6trtnau6k3pvvy61bzqkhxdcrlc
以上就是通过示例在 Unity 和 NodeJS 上的游戏中创建安全、快速的多人游戏的详细内容,更多请关注创想鸟其它相关文章!
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/456730.html
微信扫一扫
支付宝扫一扫