使用socket.io制作帧同步游戏(思路)

阅读: 评论:0

使用socket.io制作帧同步游戏(思路)

使用socket.io制作帧同步游戏(思路)

前言

一直想做一个联机的游戏,之前也用socket.io做了几个demo,不过那个时候不知道帧同步这回事,所以那时我就是通过将所有玩家的数据(位置啊,血量啊),还有子弹的所有数据转发给所有的玩家(除了自己),然后其他的玩家通过判断是否有这个数据,如果没有就生成一个,有的话就将覆盖掉。

不过上面的这种做法超级卡,无比的卡,异常的卡,迫不得已,百度了一下怎么做联机游戏。

网络上,有两种做联机游戏的方式,一种是状态同步,一种是帧同步。

下面就简单的介绍一下两种方法的区别,不过就不过多的说了。(因为我还是一知半解2333333)

联机游戏最重要的就是所有客户端显示统一。

状态同步

这个做法是服务器为主,服务器将所有的计算处理好,然后返回统一的数据给所有的玩家,玩家通过这个数据渲染出游戏的界面。(这个方法和我之前做的有点类似= = )

这种做法很安全,因为所有的数据在服务器中,客户端不管怎么改数据,最后执行的依旧是服务器中的数据。

帧同步

这个就是本文重点讲的,在百度上怎么搜都没有看到js/node/socket.io的联机游戏教程,所以只能自己硬着头皮学(瞎写)。

目前我实现的帧同步做法是,客户端发出的操作并不会在本地处理,而是会上传到服务器,让服务器保存所有玩家的操作,然后在固定时间发送给所有的客户端。然后客户端就会在固定的频率上处理这些操作,以到达一个同步的效果。

并且帧同步会保存玩家的操作,所以很容易做回放&观战很简单。

下面我简单说明一下做的两个demo。(代码过于烂,可以学习思路,但是不能抄,会死。)

demo1 - 小球画图

这个demo主要实现了中途加入游戏的玩家可以看到已经在游戏中的玩家、游戏回放、所有玩家操作统一。

demo2 - 球球大作战

这个主要是实现了一整个的房间系统,创建私密房间、加入房间、房间列表、踢出房间。游戏就写了一个移动(后面不想写了…)

还做了一个简单的聊天室,两个频道,一个世界频道(所有人都看到),一个房间频道(房间内看到)。

思路

我这里的做法是这样的, 通过前后端两个action.js来分别解析发送给双方的动作:

服务器:

const actions = {// 玩家加入游戏的操作'player.add': (player, package) => {},// 创建房间&#ate': (player, package) => {},// 加入房间'room.add': (player, package) => {},// 离开房间'room.leave': (player, package) => {},// 踢出房间'room.shit': (player, package) => {},// 房间列表'room.list': (player, package) => {},// 房间中准备游戏&#ady': (player, package) => {},// 创建一个游戏&#ate': (player, package) => {},// 游戏的操作'game.action': (player, package) => {},// 系统信息分发'message': (player, package) => {}
}// 处理数据包
ports = function (data) {if (!data.action) {console.warn('无法处理的数据包', data);return;}let action = actions[data.action];if (!action) {console.warn('无法处理的动作: ' + data.action);return;}actions[data.action](this, data);
}// 然后如果有玩家链接上了服务,所以的数据都是通过使用on('all'), emit('all')这个方法来接受和发送的
('all', data => {action.call(this, data)
});

然后前端发送数据包是这样发送的:

on(name, fn) {if (!this.io) {('not connect socket server.');return;}(name, fn);
},
emit(name, data) {if (!this.io) {('not connect socket server.');return;}it(name, data);
},
// 在这里发送数据包
action(name, data) {it('all', {data,action: name,time: new Date().getTime()});
},

客户端也有一个action来解析后端发送的数据包,这里我就不粘贴代码了(因为都一样)

接下来讲一讲,游戏中玩家的操作

目前我的做法是,服务器固定一个频率将接受到的所有玩家操作发送给所有的客户端。

class G{constructor(room){ = room;// 这个是保存整个游戏所有的操作this.frames = [];// 这个保存每帧的客户端操作this.actions = {};// 频率,也就是帧,在每一帧发送保存客户端的所有操作this.packageNum = global.option.gameFrame;this.interval = null;this.start();}start(){this.interval = setInterval(() => {// 将房间中所有玩家的动作统一发送给房间内所有人global.it('game.action', this.actions, null, key);// 将这一帧的操作保存起来,后面就可以通过这个来制作游戏回放了。this.frames.push(this.actions);// 将操作清空playerList.forEach(item => {this.actions[item.id] = [];});}, 1000 / this.packageNum);}
}

然后客户端就可以通过解析game.action

// action.js
'game.action': package => {gameplite(package.data);
},// game.js
complite(action) {// 将动作拿出来给玩家的实例处理Object.keys(action).forEach(key => {action[key].forEach(ac => {this.playerList[key].action(ac);})});// 接收到后端发过来的动作帧才会让每个玩家实例移动,这样就可以达到所有客户端显示统一Object.keys(this.playerList).forEach(key => {this.playerList[key].move();})
}

客户端发送游戏操作是这样的:

// 这些代码很简单= = ,我就懒得写注释了2333.
let prev = null;function action(key, flag) {let action = key + (flag ? '_up' : '');if (action == prev) return;app.action('game.action', {action});prev = action;
}let event = {'0'(flag) {action('left', flag);},'1'(flag) {action('top', flag);},'2'(flag) {action('right', flag);},'3'(flag) {action('bottom', flag);},'-5'(flag){action('speed', flag);}
}keydown = ev => {if (!this.playStatus) return;let code = ev.keyCode - 37;event[code] && event[code]();
}
keyup = ev => {if (!this.playStatus) return;let code = ev.keyCode - 37;event[code] && event[code](true);
}

游戏回放

在之前game.js的注释我就写了,通过保存每一动作帧(我瞎编的名词,就是服务器每帧保存玩家的动作)玩家的操作,如果某个玩家需要看回放,就可以接受这个集合,然后遍历执行完所有的动作 = =(是不是很简单)

('getGameLog', data => {this.logs = data;this.play();
})play() {// 如果看完了所有动作就退出if (this.playLog >= this.logs.length) {this.playStatus = false;return;}this.playStatus = true;let item = this.logs[this.playLog];gameplete(item);// 解析每一帧this.playLog++;// 这里是播放的倍率setTimeout(this.play.bind(this), 1000 / (20 * this.playX));
}

代码地址

码云

最后

因为本人文笔有限,可能会有错别字、思路没讲清的内容,还请多多担待= =(可能会看不懂我写的是什么个鬼东西)

还有,就是这个思路可能不是很行…,因为这是我自己想的,会有很多的问题,这里就当做抛砖引玉了。

本文发布于:2024-02-02 19:35:58,感谢您对本站的认可!

本文链接:https://www.4u4v.net/it/170687375645976.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:思路   游戏   socket   io
留言与评论(共有 0 条评论)
   
验证码:

Copyright ©2019-2022 Comsenz Inc.Powered by ©

网站地图1 网站地图2 网站地图3 网站地图4 网站地图5 网站地图6 网站地图7 网站地图8 网站地图9 网站地图10 网站地图11 网站地图12 网站地图13 网站地图14 网站地图15 网站地图16 网站地图17 网站地图18 网站地图19 网站地图20 网站地图21 网站地图22/a> 网站地图23