系列文章
《球球大作战》源码解析——(1)运行起来
《球球大作战》源码解析:服务器与客户端架构
《球球大作战》源码解析:移动算法
《球球大作战》源码解析(6):碰撞处理
《球球大作战》源码解析(7):游戏循环
《球球大作战》源码解析(8):消息广播
服务端程序中有3个定时器函数,前几篇分别解析了处理玩家移动的moveloop方法、以及处理排行榜、食物生成等游戏逻辑的gameloop。最后一个定时器函数为sendUpdates,调用语句是:setInterval(sendUpdates, 1000 / c.networkUpdateFactor),其中networkUpdateFactor默认值为40,即是每秒执行40次sendUpdates,处理消息的广播。sendUpdates会对每个玩家都做操作,具体的结构如下。
- function sendUpdates() {
- users.forEach( function(u) {
- ……
- }
- }
复制代码
同步食物列表
那么看看foreach里面的内容,先获取对该用户可见的食物信息,这里通过范围判断,获取该用户视野范围内的食物,然后组合成visibleFood列表。后面只要把visibleFood的信息发送给该用户就好了,其他看不见的就不管了。
- var visibleFood = food
- .map(function(f) {
- if ( f.x > u.x - u.screenWidth/2 - 20 &&
- f.x < u.x + u.screenWidth/2 + 20 &&
- f.y > u.y - u.screenHeight/2 - 20 &&
- f.y < u.y + u.screenHeight/2 + 20) {
- return f;
- }
- })
- .filter(function(f) { return f; });
复制代码
同步病毒列表
使用同样的方法计算可见的病毒,产生同步列表visibleVirus。
- var visibleVirus = virus
- .map(function(f) {
- if ( f.x > u.x - u.screenWidth/2 - f.radius &&
- f.x < u.x + u.screenWidth/2 + f.radius &&
- f.y > u.y - u.screenHeight/2 - f.radius &&
- f.y < u.y + u.screenHeight/2 + f.radius) {
- return f;
- }
- })
- .filter(function(f) { return f; });
复制代码
同步massFood列表
使用同样的方法计算可见的massFood,产生同步列表visibleMass。
- var visibleMass = massFood
- .map(function(f) {
- if ( f.x+f.radius > u.x - u.screenWidth/2 - 20 &&
- f.x-f.radius < u.x + u.screenWidth/2 + 20 &&
- f.y+f.radius > u.y - u.screenHeight/2 - 20 &&
- f.y-f.radius < u.y + u.screenHeight/2 + 20) {
- return f;
- }
- })
- .filter(function(f) { return f; });
复制代码
同步其他玩家
用类似的方法计算玩家可以看到的其他玩家,因为其他玩家可能有多个分身,这里对每个分身做处理。如果分身在可见范围内,就把相关的信息放到visibleCells 列表里面。列表里包括玩家自己的信息和其他玩家的信息,如果是其他玩家,还要加上他们的名字和id。
- var visibleCells = users
- .map(function(f) {
- for(var z=0; z<f.cells.length; z++)
- {
- if ( f.cells[z].x+f.cells[z].radius > u.x - u.screenWidth/2 - 20 &&
- f.cells[z].x-f.cells[z].radius < u.x + u.screenWidth/2 + 20 &&
- f.cells[z].y+f.cells[z].radius > u.y - u.screenHeight/2 - 20 &&
- f.cells[z].y-f.cells[z].radius < u.y + u.screenHeight/2 + 20) {
- z = f.cells.lenth;
- if(f.id !== U.ID) {
- return {
- id: f.id,
- x: f.x,
- y: f.y,
- cells: f.cells,
- massTotal: Math.round(f.massTotal),
- hue: f.hue,
- name: f.name
- };
- } else {
- //console.log("Nombre: " + f.name + " Es Usuario");
- return {
- x: f.x,
- y: f.y,
- cells: f.cells,
- massTotal: Math.round(f.massTotal),
- hue: f.hue,
- };
- }
- }
- }
- })
- .filter(function(f) { return f; });
复制代码
发送数据
最后就是发送数据了,服务端发送serverTellPlayerMove协议,并且把可见食物、可见其他玩家部位、可见的mass和病毒发过去。如果排行榜发生了变化,还通过leaderboard协议把排行榜数据发送出去。客户端收到协议后,更新画面。
- sockets[U.ID].emit('serverTellPlayerMove', visibleCells, visibleFood, visibleMass, visibleVirus);
- if (leaderboardChanged) {
- sockets[U.ID].emit('leaderboard', {
- players: users.length,
- leaderboard: leaderboard
- });
- }
- });
- leaderboardChanged = false;
复制代码
还是放个广告吧,笔者出版的一本书《Unity3D网络游戏实战》充分的讲解怎样开发一款网络游戏,特别对网络框架设计、网络协议、数据处理等方面都有详细的描述,相信会是一本好书的。
作者:罗培羽
原地址:https://zhuanlan.zhihu.com/p/29733789