一起来读开源项目的代码-Agar.io为例
读开源项目的代码可以分为三层: 1,弄清代码创作者目的,初衷,分析架构,框架 2,分析代码的接口分析代码的框架组织 3,根据功能模块,学习代码细节
image.png
怎么玩
游戏基础
1,在屏幕上移动鼠标以单元格移动。 2,吃食物和其他玩家以增强自己的外形(玩家每次吃东西,食物都会重生)。 3,球球的体重是所吃食物颗粒的数量。 目标:尝试变得尽可能大并吃掉其他玩家。 对战规则 1,尚未进食的玩家,别人也不能吃你,被视为“宽限期”。 2, 一旦他们获得了质量,这种无敌就消失了。 3,每次玩家加入游戏时,都会产生3个食物粒子。 4,玩家每次食用食物颗粒时,都会重新产生一个新的食物颗粒。 5,您吃的食物越多,移动速度就越慢,以使游戏对所有人都更公平。
架构
image.png
技术栈:
NodeJS NPM Bower Socket.IO Express
该游戏在使用Socket.IO的NodeJS环境上运行,以创建侦听端口3000的WebSocket服务器(默认情况下)。
还有一个ExpressJS安装程序,它提供显示index.html的简单HTTP服务,该服务具有用于渲染游戏的Canvas元素和一些与WebSocket服务器通信的客户端Javascript。
项目结构
该项目包含3个主要部分: 配置文件(bower.json,package.json等) 客户端 服务器端 配置文件列出了运行游戏所需的库,程序包等。您可以使用以下命令安装所有依赖项:
npm install
它将安装package.json和bower.json中列出的所有库。
游戏客户端
客户端文件夹包含游戏客户端中使用的代码。这只是一个简单的HTML文件,可创建画布来渲染游戏以及聊天框的一些HTML元素。
js / app.js中的游戏客户端逻辑。它包含渲染游戏,检查ping /等待时间,切换黑暗模式,发送聊天消息,处理游戏输入以及一些套接字事件侦听器以与服务器进行通信的功能。
客户端未处理任何游戏逻辑。客户端上与游戏性相关的唯一事情是处理游戏输入(将鼠标位置发送到服务器)。
游戏的渲染循环使用requestAnimationFrame而不是setInterval,这使画布具有更好的绘制性能。
为了进行比较,您可以更改代码块:
(函数animloop(){
requestAnimFrame(animloop);
gameLoop();
})();
to
setInterval(gameLoop,16);
并看到糟糕的滞后。。
游戏服务器
server / server.js上的服务器代码包含与游戏逻辑相关的所有配置/信息和功能,例如:食物的质量,移动速度,可食用的最小质量差,随机颜色,命中测试,过程玩家移动,等等
所有游戏逻辑都在服务器端处理。服务器和客户端之间的通信将在以下部分中说明。
播放器列表是在服务器端的users数组中处理的。食物清单在食物数组内。还有一个套接字数组,用于存储来自已连接播放器的所有套接字连接。
最初,在服务器端运行了一个带有setInterval的简单循环以每秒随机生成食物,但是在服务器端运行一个循环是一个坏主意,因为它会严重降低服务器的运行速度,即使在客户端运行时也会造成延迟仅连接2位玩家。
这就是我们更改为新的(当前)方式的原因:当玩家连接到游戏时,服务器将生成30个新的随机食物(请注意,可以在newFoodPerPlayer变量处更改此数字)。当玩家吃食物时,将产生1种新食物,可以在respawnFoodPerPlayer变量中更改此数字。如果游戏场所中的食物总数大于50(请参阅maxFoodCount),则服务器将停止提供新食物。
客户端服务端通信
客户端和服务器端之间的通信可以分为两个阶段:身份验证和游戏中通信
身份验证
image.png
连接新玩家时,将显示一个弹出窗口,询问他们的名字。然后,将打开一个新的套接字连接。服务器接收到此新连接,并接受带有此客户端的UserID的欢迎消息。
当客户收到该欢迎消息时,它将回复一条getit消息,并附带播放器的名称。
服务器收到该getit时,会将其广播给某人已通过playerJoin消息加入游戏的每个连接的玩家(当前玩家除外)。连接到游戏的每个玩家都将收到此消息并更新其玩家列表(在屏幕上绘制新敌人等)
游戏开始后,共有3种通讯类型:游戏逻辑,聊天和Ping(检查延迟)
游戏逻辑
我们根据玩家的行为设计了游戏逻辑。基本上,我们有3个玩家行为:移动,进食和进食其他玩家。
所有游戏逻辑都应在服务器端进行处理,并且仅将可见结果返回给客户端。
运动
image.png
当玩家想要移动时,他会将鼠标移动到新位置。 客户端将向服务器发送此新位置附带的playerSendTarget消息。 然后,服务器接收到该消息并在其一侧处理玩家的移动。 完成后,它将使用消息serverTellPlayerMove回复此客户端,并同时将消息serverUpdateAllPlayers发送给其他人,以更新每个人在他们身边的位置。
在玩家移动期间,服务器还会检查饮食和彼此饮食行为
吃食物
image.png
如果玩家击中食物。 服务器将增加该玩家的体重并删除所吃的食物。 产生新食物。 一切将在服务器端的用户和食物阵列中完成。 然后,它将通过两条消息serverUpdateAllPlayers和serverUpdateAllFoods回复所有玩家。
互相吃
image.png
如果玩家击中某人。 服务器将比较他的质量(包括eatableMassDistance)与该敌人的质量。 如果敌人的质量更大,玩家将死亡。
服务器将向他发送RIP消息并关闭其连接。 从用户阵列中删除他,并通过serverUpdateAllPlayers消息将此阵列发送给其他玩家。
聊天室
使用下图实现聊天:
image.png
当玩家发送新消息并按Enter时,新消息将作为玩家聊天消息发送到服务器。 然后,服务器接收到该消息,并使用serverSendPlayerChat将其广播给其他播放器。
当玩家收到serverSendPlayerChat消息时,它将解析该聊天消息并将其放入他们的聊天框中。
ping(延迟)的情况
每个游戏都有-ping命令来检查与服务器的连接延迟。 实现此延迟检查命令非常容易:
image.png
在检查开始时,我们保存开始时间。 然后向服务器发送一条消息,我们称其为ping。 当服务器收到该ping消息时,它将以pong消息进行回复。 当乒乓球到达客户端时,我们可以计算开始时间和结束时间之间的差。 就如此容易!
多服务器
从主分支发生了什么变化? 将不活动的超时从5000毫秒增加到...我不记得了,只是将其设置得尽可能大。。 添加gateway.js和npm run cluster命令以将服务器启动为集群 添加Redis以在服务器之间共享数据 服务器的外观如何? 我们正在使用4个Agar.IO服务器实例和1个Redis服务器实例运行群集,以在实例之间传输消息。
现在使用Redis的发布和订阅进行实例之间的通信
image.png
群集将以在 http:// localhost:3000, http:// localhost:3001, http:// localhost:3002和 http:// localhost:3003上运行的4个服务器启动
步骤3:测试多服务器功能
转到服务器1: http:// localhost:3000,使用您想要的任何名称登录 转到服务器2: http:// localhost:3001,以您想要的任何名称登录 在任何客户端中,留下一些聊天消息 转到另一个客户端以查看显示的消息!
这样,我们可以从多个位置运行多个服务器,但仍然能够在它们之间共享数据(食物,播放器,聊天消息等)。
- DEDECMS自定义表单unix时间戳转换成常规时间方法及增加表单添加时间方法
- dedecms自定义表单发布成功后返回当前页面
- 前端构建工具 Gulp.js 上手实例
- dedecms数据库内容替换安全确认码不显示怎么解决
- 利用宏避免发送确认邮件时忘记添加附件
- dateDiff在Objective-C中的实现
- 禁用Firefox自带的元素查看工具
- 容易被误解的overflow:hidden
- dedecms调用全站相关文章怎么设置
- dedecms自定义表单提交成功后提示信息修改和跳转链接修改
- dede:arclist orderby=weight dedecms列表页文章按权重排序无效问题
- Golang语言社区--Go语言基础第二节变量
- 如何让帝国CMS7.2搜索模板支持动态标签调用
- 数据视觉盛宴—数据可视化实践之美
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Python实现封装打包自己写的代码,被python import
- 创建一个 Serverless 应用,真的没有这么难!
- PHP使用mongoclient简单操作mongodb数据库示例
- 基于TensorFlow的CNN实现Mnist手写数字识别
- django rest framework 自定义返回方式
- PHP+Ajax实现的检测用户名功能简单示例
- Yii框架学习笔记之session与cookie简单操作示例
- Ajax+Jpgraph实现的动态折线图功能示例
- Python闭包及装饰器运行原理解析
- Django中Q查询及Q()对象 F查询及F()对象用法
- keras.layer.input()用法说明
- python入门:argparse浅析 nargs='+'作用
- PHP7导出Excel报ERR_EMPTY_RESPONSE解决方法
- YII框架行为behaviors用法示例
- 浅谈Python里面None True False之间的区别