开启辅助访问
 找回密码
 注册帐号

扫一扫,访问微社区

开发者专栏

关注:1904

当前位置:游戏蛮牛 技术专区 开发者专栏

__________________________________________________________________________________
开发者干货区版块规则:

  1、文章必须是图文形式。(至少2幅图)
      2、文章字数必须保持在1500字节以上。(编辑器右下角有字数检查)
      3、本版块只支持在游戏蛮牛原创首发,不支持转载。
      4、本版块回复不得无意义,如:顶、呵呵、不错......【真的会扣分的哦】
      5、......
__________________________________________________________________________________
查看: 2108|回复: 19

[碧俐千仞] 《球球大作战》源码解析——(6)碰撞处理

[复制链接]  [移动端链接]
排名
17311
昨日变化
11

27

主题

182

帖子

700

积分

Rank: 9Rank: 9Rank: 9

UID
53741
好友
31
蛮牛币
1958
威望
0
注册时间
2014-11-6
在线时间
111 小时
最后登录
2017-10-18

专栏作家

发表于 2017-7-25 22:16:54 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?注册帐号

x


知乎@罗培羽

小球移动过程中,可能会碰到食物、其他玩家和病毒,如果碰到食物,则吞食食物,质量增加;如果碰到其他玩家,体积大的吃掉体积小的,如果吞食病毒,分身解体。tickPlayer中有一段遍历所有cell的代码,它处理了游戏中的碰撞事件。

[AppleScript] 纯文本查看 复制代码
for(var z=0; z<currentPlayer.cells.length; z++) {
    ……
}


代码中定义了一个SAT.Circle类型的playerCircle,它指的是以currentCell.x和currentCell.y为圆心,currentCell.radius为半径的圆。后续将会用这个圆形去和场景中的物体做碰撞检测。

[AppleScript] 纯文本查看 复制代码
var V = SAT.Vector; //一开始定义
var C = SAT.Circle;


var playerCircle = new C(
            new V(currentCell.x, currentCell.y),
            currentCell.radius
        );


吞食食物
吞食食物的代码如下所示,foodEaten表示被吃掉的食物列表,程序对food列表的所有食物执行funcFood方法,即是使用 SAT.pointInCircle看看食物是不是被包含在玩家的面积之内。然后再对每个foodEaten执行deleteFood方法,即删除掉这个食物。food.map(funcFood)表示对food数组的每个元素传递给指定的函数,并返回一个数组,该数组由函数的返回值构成。funcFood返回的是玩家是否吞食了食物,形成true/false的列表。reduce() 方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终为一个值,是ES5中新增的一个数组逐项处理方法。针对map(funcFood)返回的true/false列表,如果该食物被包含(为true),则将它添加到返回值中。


[AppleScript] 纯文本查看 复制代码
        var foodEaten = food.map(funcFood)
            .reduce( function(a, b, c) { return b ? a.concat(c) : a; }, []);

        foodEaten.forEach(deleteFood);

    function funcFood(f) {
        return SAT.pointInCircle(new V(f.x, f.y), playerCircle);
    }

    function deleteFood(f) {
        food[f] = {};
        food.splice(f, 1);
    }


看到这里作者还是比较失望的,因为本来期待有更好的方法,减少计算量。像这样两两判断谁不会啊!

吞食massFood
massFood是玩家喷射出的“质量”处理过程与吞食食物类似,获取被吃掉的mass的列表massEaten,然后从massFood列表中删掉它。

[AppleScript] 纯文本查看 复制代码
        var massEaten = massFood.map(eatMass)
            .reduce(function(a, b, c) {return b ? a.concat(c) : a; }, []);

     ……

        var masaGanada = 0;
        for(var m=0; m<massEaten.length; m++) {
            masaGanada += massFood[massEaten[m]].masa;
            massFood[massEaten[m]] = {};
            massFood.splice(massEaten[m],1);
            for(var n=0; n<massEaten.length; n++) {
                if(massEaten[m] < massEaten[n]) {
                    massEaten[n]--;
                }
            }
        }


吞食病毒
如果不小心吞食了病毒,玩家会被迫分身,代码如下所示。

[AppleScript] 纯文本查看 复制代码
        var virusCollision = virus.map(funcFood)
           .reduce( function(a, b, c) { return b ? a.concat(c) : a; }, []);

        if(virusCollision > 0 && currentCell.mass > virus[virusCollision].mass) {
          sockets[currentPlayer.id].emit('virusSplit', z);
        }


下图为吞食病毒导致的分身前后,绿色圆形为病毒,大球aa吞食病毒后,立即分解为两个小球。
球球大作战 源码解析62213.png

球球大作战 源码解析62215.png



增加质量

如果玩家吞食了食物或massfood,小球会变大,相关代码如下。

[AppleScript] 纯文本查看 复制代码
        if(typeof(currentCell.speed) == "undefined")
            currentCell.speed = 6.25;
        masaGanada += (foodEaten.length * c.foodMass);
        currentCell.mass += masaGanada;
        currentPlayer.massTotal += masaGanada;
        currentCell.radius = util.massToRadius(currentCell.mass);
        playerCircle.r = currentCell.radius;



吞食其他玩家
   接下来是使用四叉树计算玩家之间的碰撞,笔者就在想,前面都用了那么多个for循环了,这可是每个玩家都对food,massfood,病毒都for一次啊。这里用四叉树意义很大么?为什么不一开始就都用呢?

先使用tree.put构建四叉树,四叉树可以把判断的范围变小,把每个玩家都放进去,然后通过tree.get(currentPlayer, check)获取发生碰撞的玩家。最后再对每个可能发生碰撞的玩家执行
[AppleScript] 纯文本查看 复制代码
collisionCheck。
        tree.clear();
        users.forEach(tree.put);
        var playerCollisions = [];

        var otherUsers =  tree.get(currentPlayer, check);

        playerCollisions.forEach(collisionCheck);


接下来看看check,它遍历玩家身上每个cells,然后使用SAT.testCircleCircle测试是否圆在圆内,如果是的话返回一个response结构,该结构里面包含对方玩家的id、name、坐标等信息。然后构建playerCollisions数组。

[AppleScript] 纯文本查看 复制代码
   function check(user) {
        for(var i=0; i<user.cells.length; i++) {
            if(user.cells[i].mass > 10 && user.id !== currentPlayer.id) {
                var response = new SAT.Response();
                var collided = SAT.testCircleCircle(playerCircle,
                    new C(new V(user.cells[i].x, user.cells[i].y), user.cells[i].radius),
                    response);
                if (collided) {
                    response.aUser = currentCell;
                    response.bUser = {
                        id: user.id,
                        name: user.name,
                        x: user.cells[i].x,
                        y: user.cells[i].y,
                        num: i,
                        mass: user.cells[i].mass
                    };
                    playerCollisions.push(response);
                }
            }
        }
        return true;
    }


然后是对发生碰撞的玩家执行逻辑,把它吃掉。

[AppleScript] 纯文本查看 复制代码
   function collisionCheck(collision) {
        if (collision.aUser.mass > collision.bUser.mass * 1.1  && collision.aUser.radius > Math.sqrt(Math.pow(collision.aUser.x - collision.bUser.x, 2) + Math.pow(collision.aUser.y - collision.bUser.y, 2))*1.75) {
            console.log('[DEBUG] Killing user: ' + collision.bUser.id);
            console.log('[DEBUG] Collision info:');
            console.log(collision);

            var numUser = util.findIndex(users, collision.bUser.id);
            if (numUser > -1) {
                if(users[numUser].cells.length > 1) {
                    users[numUser].massTotal -= collision.bUser.mass;
                    users[numUser].cells.splice(collision.bUser.num, 1);
                } else {
                    users.splice(numUser, 1);
                    io.emit('playerDied', { name: collision.bUser.name });
                    sockets[collision.bUser.id].emit('RIP');
                }
            }
            currentPlayer.massTotal += collision.bUser.mass;
            collision.aUser.mass += collision.bUser.mass;
        }
    }


这里是笔者看不懂还是四叉树没啥作用呢?在这里用四叉树和直接两次循环有区别么?check是固定返回true的啊!!!!!


四叉树
http://blog.csdn.net/zhouxuguang236/article/details/12312099

四叉树索引的基本思想是将地理空间递归划分为不同层次的树结构。它将已知范围的空间等分成四个相等的子空间,如此递归下去,直至树的层次达到一定深度或者满足某种要求后停止分割。四叉树的结构比较简单,并且当空间数据对象分布比较均匀时,具有比较高的空间数据插入和查询效率,因此四叉树是GIS中常用的空间索引之一。常规四叉树的结构如图所示,地理空间对象都存储在叶子节点上,中间节点以及根节点不存储地理空间对象。
球球大作战 源码解析65808.png


四叉树对于区域查询,效率比较高。但如果空间对象分布不均匀,随着地理空间对象的不断插入,四叉树的层次会不断地加深,将形成一棵严重不平衡的四叉树,那么每次查询的深度将大大的增多,从而导致查询效率的急剧下降。

nodejs的 simple-quadtree介绍
代码中的tree.get、tree.put等方法用到了nodejs的simple-quadtree库,这里做个简单介绍。
https://www.npmjs.com/package/simple-quadtree

simple-quadtree是一套小型的四叉树实现,每棵树支持 put、 get、remove 和 clear四种操作。四叉树的节点对象必须包含x,y坐标,以及长度宽度w、h。

Put方法
Put方法可以将节点放入四叉树里面,例如:
qt.put({x: 5, y: 5, w: 0, h: 0, string: 'test'});

Get方法
Get方法会迭代取出四叉树节点,然后调用回调函数,如下所示。
qt.get({x:0, y: 0, w: 10, h: 10}, function(obj) {
    // obj == {x: 5, y: 5, w: 0, h: 0, string: 'test'}
});
如果回调函数返回true,迭代会一直进行下去,如果回调函数返回false,则迭代停止。由于源码中的check方法总是返回true,所以这里使用四叉树并没能减少计算量,相反比for循环多了构建树的计算。没什么用!



还是放个广告吧,笔者出版的一本书《网络游戏实战》充分的讲解怎样开发一款网络游戏,特别对网络框架设计、网络协议、数据处理等方面都有详细的描述,相信会是一本好书的。
球球大作战 源码解析66626.png




回复

使用道具 举报

6蛮牛粉丝
1133/1500
排名
2532
昨日变化
15

15

主题

208

帖子

1133

积分

Rank: 6Rank: 6Rank: 6

UID
12594
好友
1
蛮牛币
4243
威望
0
注册时间
2014-1-15
在线时间
628 小时
最后登录
2017-10-18
QQ
发表于 2017-7-27 09:36:44 | 显示全部楼层
好东西,谢谢谢谢
[发帖际遇]: 尹华南 发帖时在路边捡到 2 蛮牛币,偷偷放进了口袋. 幸运榜 / 衰神榜

回复

使用道具 举报

3偶尔光临
190/300
排名
8757
昨日变化
99

0

主题

31

帖子

190

积分

Rank: 3Rank: 3Rank: 3

UID
130351
好友
0
蛮牛币
379
威望
0
注册时间
2015-11-29
在线时间
63 小时
最后登录
2017-10-18
发表于 2017-7-27 09:55:54 | 显示全部楼层
好东西,谢谢分享

回复

使用道具 举报

5熟悉之中
610/1000
排名
5496
昨日变化
32

1

主题

259

帖子

610

积分

Rank: 5Rank: 5

UID
216583
好友
1
蛮牛币
867
威望
0
注册时间
2017-4-7
在线时间
160 小时
最后登录
2017-10-18
发表于 2017-7-27 10:12:27 | 显示全部楼层
好东西,谢谢分享

回复

使用道具 举报

2初来乍到
125/150
排名
17311
昨日变化
11

0

主题

87

帖子

125

积分

Rank: 2Rank: 2

UID
200328
好友
0
蛮牛币
10
威望
0
注册时间
2017-1-9
在线时间
14 小时
最后登录
2017-9-28
发表于 2017-7-27 11:18:38 | 显示全部楼层
好东西,学习饿啦

回复

使用道具 举报

7日久生情
1847/5000
排名
17311
昨日变化
11

2

主题

1553

帖子

1847

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
185807
好友
0
蛮牛币
2483
威望
0
注册时间
2016-11-22
在线时间
268 小时
最后登录
2017-10-18
发表于 2017-7-27 13:29:29 | 显示全部楼层
好东西,谢谢分享
[发帖际遇]: 东坡肘子2010 在论坛发帖时没有注意,被小偷偷去了 2 蛮牛币. 幸运榜 / 衰神榜

回复

使用道具 举报

3偶尔光临
206/300
排名
9459
昨日变化
132

0

主题

56

帖子

206

积分

Rank: 3Rank: 3Rank: 3

UID
223562
好友
0
蛮牛币
160
威望
0
注册时间
2017-5-24
在线时间
68 小时
最后登录
2017-10-18
发表于 2017-7-27 17:41:56 | 显示全部楼层
6666666666666

回复

使用道具 举报

5熟悉之中
673/1000
排名
3325
昨日变化
6

0

主题

120

帖子

673

积分

Rank: 5Rank: 5

UID
57980
好友
0
蛮牛币
482
威望
0
注册时间
2014-11-27
在线时间
213 小时
最后登录
2017-10-9
QQ
发表于 2017-7-28 10:05:36 | 显示全部楼层
刚刚买了这本书

回复

使用道具 举报

4四处流浪
335/500
排名
7273
昨日变化
2

0

主题

117

帖子

335

积分

Rank: 4

UID
227843
好友
0
蛮牛币
955
威望
0
注册时间
2017-6-20
在线时间
90 小时
最后登录
2017-10-18
发表于 2017-7-28 14:50:17 | 显示全部楼层
66666666666666

回复

使用道具 举报

排名
28188
昨日变化
82

0

主题

5

帖子

18

积分

Rank: 1

UID
165289
好友
0
蛮牛币
7
威望
0
注册时间
2016-9-1
在线时间
5 小时
最后登录
2017-9-6
发表于 2017-7-28 15:05:32 | 显示全部楼层
不错。。。。。

回复

使用道具 举报

2初来乍到
127/150
排名
11744
昨日变化
10

0

主题

28

帖子

127

积分

Rank: 2Rank: 2

UID
233332
好友
0
蛮牛币
130
威望
0
注册时间
2017-7-20
在线时间
43 小时
最后登录
2017-10-16
发表于 2017-7-28 21:35:51 | 显示全部楼层
讲的非常好

回复

使用道具 举报

5熟悉之中
565/1000
排名
5181
昨日变化
2

5

主题

199

帖子

565

积分

Rank: 5Rank: 5

UID
187870
好友
0
蛮牛币
902
威望
0
注册时间
2016-11-29
在线时间
155 小时
最后登录
2017-10-1
发表于 2017-7-29 14:05:56 | 显示全部楼层
好人一生平安

回复

使用道具 举报

5熟悉之中
527/1000
排名
4378
昨日变化
21

3

主题

79

帖子

527

积分

Rank: 5Rank: 5

UID
169460
好友
4
蛮牛币
534
威望
0
注册时间
2016-9-20
在线时间
193 小时
最后登录
2017-10-18
发表于 2017-7-31 16:40:47 | 显示全部楼层
你好,请问球球大作战里面的,LBS功能,可以讲解一下吗?
我最近在坐一个LBS的游戏,但是不知道怎么在接入的地图上面加入模型.

回复 支持 反对

使用道具 举报

4四处流浪
386/500
排名
9337
昨日变化
5

0

主题

213

帖子

386

积分

Rank: 4

UID
146677
好友
7
蛮牛币
1950
威望
0
注册时间
2016-4-25
在线时间
87 小时
最后登录
2017-9-29
QQ
发表于 2017-7-31 19:51:34 | 显示全部楼层
很棒哦,加油聆听大神讲解

回复 支持 反对

使用道具 举报

4四处流浪
368/500
排名
7045
昨日变化
65

1

主题

117

帖子

368

积分

Rank: 4

UID
233944
好友
0
蛮牛币
768
威望
0
注册时间
2017-7-24
在线时间
114 小时
最后登录
2017-10-19
发表于 2017-8-8 08:08:26 | 显示全部楼层

好东西,学习饿啦

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册帐号

本版积分规则

快速回复 返回顶部 返回列表