通过示例在 Unity 和 NodeJS 上的游戏中创建安全、快速的多人游戏

千雪酱_1320

千雪酱_1320

2024-09-01

991人浏览

转载

介绍

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

通过示例在 Unity 和 NodeJS 上的游戏中创建安全、快速的多人游戏

所以,让我们定义我们的主要标准:

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

通过示例在 Unity 和 NodeJS 上的游戏中创建安全、快速的多人游戏

架构的主要组成部分

  1. 客户端(unity):客户端负责显示游戏状态,将玩家操作发送到服务器并从服务器接收更新。这里还使用反应字段来动态更新玩家位置。
  2. 服务器端(node.js):服务器处理关键数据(例如,移动、碰撞和玩家动作)并将更新发送到所有连接的客户端。非关键数据可以在客户端上处理并使用服务器转发到其他客户端。
  3. 二进制协议:二进制数据序列化用于减少传输的数据量并提高性能。
  4. 同步:提供客户端之间数据的快速同步,以最大程度地减少延迟并确保流畅的游戏体验。
  5. 客户端反作弊:它用于我们可以在客户端上更改并发送给其他客户端的数据。

第 1 步:在 node.js 中实现服务器

首先,您需要在 node.js 上设置一个服务器。服务器将负责所有关键计算并将更新的数据传输给玩家。

安装环境
要在 node.js 上创建服务器,请安装必要的依赖项:

mkdir multiplayer-game-server
cd multiplayer-game-server
npm init -y
npm install socket.io

socket.io可以轻松地使用web套接字实现客户端和服务器之间的实时双向通信。

基本服务器实现
让我们创建一个简单的服务器,它将处理客户端连接、检索数据、计算关键状态并在所有客户端之间同步它们。

// create a simple socket io server
const io = require('socket.io')(3000, {
    cors: {
        origin: '*'
    }
});

// simple example of game states
let gamestate = {};
let playerspeedconfig = {
    maxx: 1,
    maxy: 1,
    maxz: 1
};

// work with new connection
io.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 data
function 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 data
function 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 protocol
function 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);
}

此服务器执行以下操作:

  1. 处理客户端连接。
  2. 接收二进制格式的玩家移动数据,验证它,更新服务器上的状态并将其发送到所有客户端。
  3. 以最小延迟同步游戏状态,使用二进制格式来减少数据量。
  4. 简单地转发来自客户端的不安全数据。

要点:

  1. 服务器权限:所有重要数据均在服务器上处理和存储。客户端仅发送操作命令(例如,位置变化增量)。
  2. 二进制数据传输:使用二进制协议可以节省流量并提高网络性能,特别是对于频繁的实时数据交换。

第2步:在unity上实现客户端部分

现在让我们在 unity 上创建一个与服务器交互的客户端部分。

要将 unity 连接到 socket.io 上的服务器,您需要连接专为 unity 设计的库。 在这种情况下,我们不受任何特定实现的约束(事实上它们都是相似的),而只是使用一个抽象示例。

使用反应字段进行同步
我们将使用反应字段来更新玩家位置。这将使我们能够更新状态,而无需通过 update() 方法检查每个帧中的数据。当数据状态发生变化时,反应字段会自动更新游戏中对象的视觉表示。

要获得反应性属性功能,您可以使用 unirx。

unity 上的客户端代码
让我们创建一个脚本来连接到服务器、发送数据并通过反应字段接收更新。

using UnityEngine;
using SocketIOClient;
using UniRx;
using System;
using System.Text;

// Basic Game Client Implementation
public class GameClient : MonoBehaviour
{
    // SocketIO Based Client
    private SocketIO client;

    // Our Player Reactive Position
    public ReactiveProperty<vector3> playerPosition = new ReactiveProperty<vector3>(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<byte>();

        // Work with binary data
        int offset = 0;
        while (offset <h2>
  
  
  第 3 步:优化同步和性能
</h2>
<p><strong>为了确保流畅的游戏体验并最大程度地减少同步期间的延迟,建议:</strong></p>
<ol>
<li>
<strong>使用插值:</strong>客户端可以使用插值来平滑服务器更新之间的移动。这可以补偿较小的网络延迟。</li>
<li>
<strong>批量数据发送:</strong>不要按每一步发送数据,而是使用批量发送。例如,每隔几毫秒发送一次更新,这将减少网络负载。 </li>
<li>
<strong>降低更新频率:</strong>将发送数据的频率降低到合理的最低限度。例如,对于大多数游戏来说,每秒更新 20-30 次可能就足够了。</li>
</ol>
<h2>
  
  
  如何简化二进制协议的使用?
</h2>
<p>为了简化您使用二进制协议的工作 - 创建数据处理的基本原理以及与其交互的方案。</p><div class="aritcle_card flexRow artxards">
											<div class="artcardd flexRow">
												<a class="aritcle_card_img" rel="nofollow" href="/xiazai/skill10804" title="digital-solution-designer"><img
														src="https://img.php.cn/upload/skill/000/000/081/179335626025953.jpg" alt="digital-solution-designer" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
												<div class="aritcle_card_info flexColumn">
													<a rel="nofollow" href="/xiazai/skill10804" title="digital-solution-designer" class="overflowclass">digital-solution-designer</a>
													<p class="overflowclass">系统化设计数字化解决方案,涵盖方案类型识别、政策背景分析、需求分析、建设思路设计、架构设计(业务、功能、数据、技术四维度)、技术选型、具体建设内容、实施规划、风险评估和投资估算的全流程能力。适用于规划类、申报类、可研类、投标类和工作汇报类等多种方案产出场景,覆盖政府数字化转型和企业数字化转型两大核心领域。</p>
												</div>
												<a rel="nofollow" href="/xiazai/skill10804" title="digital-solution-designer" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span>
												</a>
											</div>
										</div>
<p><strong>对于我们的示例,我们可以采用一个基本协议,其中:</strong><br>
1) 前 4 位是用户发出的请求的最大值(例如 0 - 移动玩家,1 - 射击等);<br>
2) 接下来的 16 位是我们客户的 id。<br>
3) 接下来我们填充通过循环传递的数据(一些网络变量),其中存储变量的 id、到下一个变量开头的偏移量(以字节为单位)、变量的类型和它的价值。</p>
<p>为了方便版本和数据控制 - 我们可以以方便的格式(json / xml)创建客户端-服务器通信模式,并从服务器下载一次,以便根据该模式进一步解析我们的二进制数据以获得所需的内容我们的 api 版本。</p>
<h2>
  
  
  客户端反作弊
</h2>
<p>在服务器上处理所有数据是没有意义的,其中一些数据更容易在客户端修改并发送到其他客户端。</p>
<p>为了让你在这个方案中更加安全 - 你可以使用客户端防黑客系统来防止内存黑客 - 例如,我的 gameshield - 一个免费的开源解决方案。</p>
<h2>
  
  
  结论
</h2>
<p>我们举了一个简单的例子,在 unity 上使用 node.js 服务器开发多人游戏,所有关键数据都在服务器上处理,以确保游戏的完整性。使用二进制协议传输数据有助于优化流量,而 unity 中的反应式编程可以轻松同步客户端状态,而无需使用 update() 方法。</p>
<p>这种方法不仅可以提高游戏性能,还可以通过确保所有关键计算都在服务器而不是客户端上执行来增强对作弊的保护。</p>
<p><strong>当然,一如既往地感谢您阅读这篇文章。如果您在组织多人项目架构方面仍有任何疑问或需要帮助 - 我邀请您加入我的 discord</strong></p>
<hr>
<p><strong>您还可以在我的困境中为我提供很多帮助,并支持发布新文章以及为开发人员免费提供的库和资源:</strong></p>
<p><strong>我的不和谐</strong> | <strong>我的博客</strong> | <strong>我的 <a style="color:#f60; text-decoration:underline;" title="git" href="https://m.php.cn/zt/15841.html" target="_blank">git</a>hub</strong></p>
<p><strong>btc:</strong> bc1qef2d34r4xkrm48zknjdjt7c0ea92ay9m2a7q55<br><br><strong>eth:</strong> 0x1112a2ef850711df4de9c432376f255f416ef5d0<br><strong><a style="color:#f60; text-decoration:underline;" title="usdt" href="https://m.php.cn/zt/94398.html" target="_blank">usdt</a> (trc20):</strong>trf7sli6trtnau6k3pvvy61bzqkhxdcrlc</p></byte></vector3></vector3>

相关文章

在线游戏
在线游戏

海量精品小游戏合集,无需安装即点即玩,休闲益智、动作闯关应有尽有,秒开即玩,轻松解压,快乐停不下来

下载

相关标签:

nodejs git usdt

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
json数据格式
json数据格式

JSON是一种轻量级的数据交换格式。本专题为大家带来json数据格式相关文章,帮助大家解决问题。

2023.08.07

2215

5

json是什么
json是什么

JSON是一种轻量级的数据交换格式,具有简洁、易读、跨平台和语言的特点,JSON数据是通过键值对的方式进行组织,其中键是字符串,值可以是字符串、数值、布尔值、数组、对象或者null,在Web开发、数据交换和配置文件等方面得到广泛应用。本专题为大家提供json相关的文章、下载、课程内容,供大家免费下载体验。

2023.08.23

3522

1

jquery怎么操作json
jquery怎么操作json

操作的方法有:1、“$.parseJSON(jsonString)”2、“$.getJSON(url, data, success)”;3、“$.each(obj, callback)”;4、“$.ajax()”。更多jquery怎么操作json的详细内容,可以访问本专题下面的文章。

2023.10.13

1176

3

go语言处理json数据方法
go语言处理json数据方法

本专题整合了go语言中处理json数据方法,阅读专题下面的文章了解更多详细内容。

2025.09.10

4059

7

pdf怎么转换成xml格式
pdf怎么转换成xml格式

将 pdf 转换为 xml 的方法:1. 使用在线转换器;2. 使用桌面软件(如 adobe acrobat、itext);3. 使用命令行工具(如 pdftoxml)。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2024.04.01

4824

5

xml怎么变成word
xml怎么变成word

步骤:1. 导入 xml 文件;2. 选择 xml 结构;3. 映射 xml 元素到 word 元素;4. 生成 word 文档。提示:确保 xml 文件结构良好,并预览 word 文档以验证转换是否成功。想了解更多xml的相关内容,可以阅读本专题下面的文章。

2024.08.01

6277

7

xml是什么格式的文件
xml是什么格式的文件

xml是一种纯文本格式的文件。xml指的是可扩展标记语言,标准通用标记语言的子集,是一种用于标记电子文件使其具有结构性的标记语言。想了解更多相关的内容,可阅读本专题下面的相关文章。

2024.11.28

2542

7

js正则表达式
js正则表达式

php中文网为大家提供各种js正则表达式语法大全以及各种js正则表达式使用的方法,还有更多js正则表达式的相关文章、相关下载、相关课程,供大家免费下载体验。

2023.06.20

4656

8

js获取当前时间
js获取当前时间

JS全称JavaScript,是一种具有函数优先的轻量级,解释型或即时编译型的编程语言;它是一种属于网络的高级脚本语言,主要用于Web,常用来为网页添加各式各样的动态功能。js怎么获取当前时间呢?php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

2023.07.28

1395

5

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
快速入门Node.JS全套完整版
快速入门Node.JS全套完整版

共83课时 | 13.6万人学习

nodejs开发基础教程
nodejs开发基础教程

共15课时 | 4.9万人学习

JavaScript设计模式视频教程
JavaScript设计模式视频教程

共28课时 | 6.3万人学习