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

介绍

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

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

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

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

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

架构的主要组成部分

客户端(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 次可能就足够了。

如何简化二进制协议的使用?

为了简化您使用二进制协议的工作 – 创建数据处理的基本原理以及与其交互的方案。

对于我们的示例,我们可以采用一个基本协议,其中:
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/1491049.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
Golang如何处理多文件上传_Golang 文件上传批量处理示例
上一篇 2026年5月10日 11:19:21
如何高效处理20万张图片并清除冗余数据?
下一篇 2026年5月10日 11:19:24

相关推荐

  • 稳定币交易平台 稳定币app交易所有哪些

    稳定币交易平台 稳定币app交易所有哪些稳定币交易平台 稳定币app交易所有哪些稳定币交易平台 稳定币app交易所有哪些稳定币交易平台 稳定币app交易所有哪些

    当前主流的稳定币交易平台排名依次为Binance、OKX、gate.io和火币。Binance是全球交易量最大的平台,支持多种稳定币交易对,并提供现货、合约及杠杆交易,手续费竞争力强;OKX以创新产品著称,支持稳定币跨链兑换,提供专业API接口及高安全性;gate.io上线稳定币种类齐全,并提供理财…

    2026年5月10日 用户投稿
    100
  • 在Ubuntu 20.04上安装最新版Golang的最佳实践是什么

    最直接且推荐的做法是从官网下载最新Go二进制包并手动配置。首先下载%ignore_a_1%1.22.3.linux-amd64.tar.gz,解压至/usr/local,再将/usr/local/go/bin加入PATH环境变量,并设置GOPATH为$HOME/go,最后通过go version和g…

    2026年5月10日
    000
  • 以太坊ETH交易平台币安官网地址入口

    以太坊ETH交易平台币安官网地址入口以太坊ETH交易平台币安官网地址入口以太坊ETH交易平台币安官网地址入口以太坊ETH交易平台币安官网地址入口

    在当下的加密市场中,选择一款安全可靠的交易平台是每一位新手的第一步。本文将为大家详细介绍币安binance交易所的官网入口、最新版app获取方式、注册操作流程,并额外讲解如何通过c2c买币功能快速完成资产购买,让用户能够顺利开启以太坊(eth)等主流币的交易体验。 一、币安官网入口: 用户如需进入币…

    2026年5月10日 用户投稿
    000
  • Golang初学者如何用flag包开发一个功能完整的命令行工具

    Go语言flag包可用于解析命令行参数,支持布尔、字符串、整数等类型,通过flag.Type或flag.TypeVar定义参数,结合flag.Parse实现输入解析。示例中定义了-name和-v参数,运行时输出问候语和详细信息。支持多种定义方式:flag.Type返回指针,flag.TypeVar绑…

    2026年5月10日
    000
  • Golang在DevOps流水线中的依赖管理技巧 对比Go Modules与传统包管理

    Golang在DevOps流水线中的依赖管理技巧 对比Go Modules与传统包管理Golang在DevOps流水线中的依赖管理技巧 对比Go Modules与传统包管理Golang在DevOps流水线中的依赖管理技巧 对比Go Modules与传统包管理Golang在DevOps流水线中的依赖管理技巧 对比Go Modules与传统包管理

    go modules 是 golang 在 devops 流水线中实现稳定构建与部署的关键依赖管理工具。1. 它通过语义化版本控制确保每次构建使用一致的依赖版本;2. go.mod 和 go.sum 文件分别记录依赖及其哈希值,保障依赖可追溯与安全性;3. 支持 goproxy 和缓存机制提升 ci…

    2026年5月10日 用户投稿
    100
  • 如何自学HTML5_HTML5自学技巧【指南】

    掌握HTML5需系统学规范、用平台实操、拆解开源项目、做功能练习、测无障碍性。重点包括语义标签、表单控件、localStorage、Canvas及ARIA等,结合W3C文档、CodePen、GitHub与校验工具实践。 如果您希望掌握HTML5的基础知识并独立构建网页,可以从理解其核心语法和语义化结…

    2026年5月10日
    000
  • 使用PHP实现图片相似度比对:基于感知哈希的目录图像查找与展示教程

    本教程详细介绍了如何在PHP中实现图片相似度比对,以解决传统MD5哈希无法识别相似图片的问题。通过引入第三方感知哈希库,我们能够计算上传图片与目标目录下所有图片的相似度,并根据设定的阈值筛选并展示相似图片。教程涵盖了从HTML表单到PHP处理逻辑、代码示例、关键注意事项及性能优化建议,帮助开发者构建…

    2026年5月10日
    000
  • 为什么使用subprocess.open执行Git命令会报错“git: command not found”?

    subprocess.open难以识别git命令的原因 使用python的subprocess.open函数执行shell脚本时,遇到git命令执行失败(git: command not found),而npm命令却成功执行。修改git命令为绝对路径后,git命令执行成功。 原因分析: git命令识…

    2026年5月10日
    000
  • Golang微服务版本管理与灰度发布方法

    Golang微服务通过语义化版本、Git分支策略、Docker镜像标签和API版本控制实现规范版本管理,并借助服务网格或注册中心实现灰度发布,结合监控与回滚机制确保上线稳定。 微服务在现代架构中广泛应用,Golang因其高性能和简洁语法成为微服务开发的热门选择。随着服务数量增长,版本管理和灰度发布变…

    2026年5月10日
    000
  • PHP源码命令行工具开发_PHP源码命令行工具开发教程

    答案是使用PHP开发命令行工具需依托CLI SAPI,结合Composer管理依赖,并推荐采用Symfony Console等组件库来构建。首先确保PHP支持CLI模式,通过编写基础脚本并利用$argv和getopt()处理参数,但更优方式是引入Symfony Console组件进行命令定义与输入输…

    2026年5月10日
    000
  • 专攻php辅助有哪些

    PHP 辅助工具专门为 PHP 开发人员打造,旨在提升他们的工作效率和开发体验。这些工具的功能包括:调试工具:Xdebug、PHP Debug Bar 和 PHPUnit代码分析工具:PHPStan、Psalm 和 Symfony Debug Toolbar版本控制工具:Git、GitHub 和 B…

    2026年5月10日
    300
  • 别人的php源码怎么用_别人的php源码用部署与合规法【指南】

    拿到PHP源码后需先确认授权、搭建环境、审查代码并合规使用,确保来源合法、配置正确、无安全风险后方可运行。 拿到别人的PHP源码后,不能直接运行或上线使用。正确的方式是理解代码结构、部署到合适环境,并确保使用过程合法合规。以下是具体操作步骤和注意事项。 一、确认源码来源与授权 在使用任何第三方PHP…

    2026年5月10日
    000
  • 使用 tox 管理多 Python 版本测试环境

    tox是一个自动化测试工具,用于在多个Python版本中验证代码兼容性。它基于virtualenv和pip创建隔离环境,通过tox.ini配置文件定义测试环境,支持跨版本测试、条件依赖安装及与CI/CD集成。示例配置包括指定Python版本列表(envlist)、测试依赖(deps)和执行命令(co…

    2026年5月10日
    000
  • 加密货币排行前十有哪些 加密货币排行榜

    比特币(BTC)凭借稀缺性和机构资金流入稳居榜首;2. 以太坊(ETH)作为智能合约平台引领DeFi与NFT生态;3. 泰达币(USDT)以1:1美元锚定提供市场流动性;4. 瑞波币(XRP)推动跨境支付并拓展国际合作;5. 币安币(BNB)依托币安生态与通缩机制保持竞争力;6. 索拉纳(SOL)以…

    2026年5月10日
    000
  • Golang如何管理跨项目模块依赖

    合理配置go.mod、规范版本发布与使用replace调试是管理Go跨项目依赖的核心。首先通过go mod init定义模块路径,确保与代码仓库一致,如github.com/yourorg/projectA,便于其他项目导入;接着通过git tag发布语义化版本(如v1.0.0),使依赖可追踪;在开…

    2026年5月10日
    000
  • Python实现TXT文本数据转Excel:数值类型转换与平均值计算教程

    本教程详细指导如何使用Python和openpyxl库将TXT文本文件中的数据读取并写入Excel文件。内容涵盖了从文本数据中提取数值、将其转换为整数类型、在Excel中创建新工作表、逐行写入数据,以及动态计算并添加平均值列的全过程,确保数据类型准确无误。 1. 引言 在数据处理的日常工作中,我们经…

    2026年5月10日
    000
  • 如何正确初始化Golang模块 详解go mod init的使用场景

    如何正确初始化Golang模块 详解go mod init的使用场景如何正确初始化Golang模块 详解go mod init的使用场景如何正确初始化Golang模块 详解go mod init的使用场景如何正确初始化Golang模块 详解go mod init的使用场景

    初始化 golang 模块需使用 go mod init 命令。1. 打开终端并进入项目根目录;2. 运行 go mod init ,指定代码仓库地址作为模块名;3. 检查生成的 go.mod 文件是否包含模块名。此举可使 go 编译器识别项目为模块,正确管理依赖,确保团队协作时依赖环境一致。后续通…

    2026年5月10日 用户投稿
    000
  • 优化字符串查找:内存映射 vs. 数据库查询

    在Go服务器应用开发中,经常会遇到需要对接收到的字符串进行验证的场景,例如验证字符串是否存在于数据库中。针对高并发的HTTP请求,如何高效地进行字符串查找是一个关键问题。通常有两种策略:一是每次请求都执行SQL查询;二是将所有字符串预先加载到内存中的Map,然后通过Map进行快速查找。选择哪种策略取…

    2026年5月10日
    000
  • C++ Boost库怎么安装使用_C++准标准库核心功能解析

    Boost库是C++中功能强大的“准标准库”,提供智能指针、正则表达式、文件系统、多线程等丰富功能,提升开发效率。安装方式因平台而异:Windows可使用vcpkg或预编译包,Linux(如Ubuntu)通过sudo apt install libboost-all-dev安装,macOS用Home…

    2026年5月10日
    000
  • Anchorage Digital关联地址向币安充值500万枚ENA,价值337万美元

    近日,链上数据显示,anchorage digital 关联地址向币安(binance)充值了约500万枚ena,按当前价格估值约为337万美元。此类大额转账通常反映机构资金调配或市场布局,投资者应关注潜在影响。 ENA大额转账的市场意义 ENA作为新兴加密资产,其大额入金交易可能对市场流动性产生一…

    2026年5月10日
    000

发表回复

登录后才能评论
关注微信