Chrome自带恐龙小游戏的源码研究(七)

  在上一篇《Chrome自带恐龙小游戏的源码研究(六)》中研究了恐龙的跳跃过程,这一篇研究恐龙与障碍物之间的碰撞检测。

碰撞盒子

  游戏中采用的是矩形(非旋转矩形)碰撞。这类碰撞优点是计算比较简单,缺点是对不规则物体的检测不够精确。如果不做更为精细的处理,结果会像下图:

如图所示,两个盒子虽然有重叠部分,但实际情况是恐龙和仙人掌之间并未发生碰撞。为了解决这个问题,需要建立多个碰撞盒子:

不过这样还是有问题,观察图片,恐龙和仙人掌都有四个碰撞盒子,如果每次Game Loop里都对这些盒子进行碰撞检测,那么结果是每次需要进行4X4=16次计算,如果物体或者盒子很多,就会导致运算量大大增加,造成严重的性能问题。为改进这一点,只需要先检测两个大盒子之间是否碰撞,如果没有,则略去里面小盒子的碰撞检测。反之则对里面的小盒子做碰撞检测。游戏中使用CollisionBox构造函数创建碰撞盒子:

/**
* 碰撞盒子
* @param x    {number} 盒子x坐标
* @param y    {number} 盒子y坐标
* @param w    {number} 盒子宽度
* @param h    {number} 盒子高度
*/
function CollisionBox(x, y, w, h) {
    this.x = x;
    this.y = y;
    this.width = w;
    this.height = h;
}

使用boxCompare方法检测两个盒子是否发生碰撞:

 1 /**
 2 * 碰撞检测
 3 * @param tRexBox {Object} 霸王龙的碰撞盒子
 4 * @param obstacleBox {Object} 障碍物的碰撞盒子
 5 */
 6 function boxCompare(tRexBox, obstacleBox) {
 7     var tRexBoxX = tRexBox.x,
 8         tRexBoxY = tRexBox.y,
 9         obstacleBoxX = obstacleBox.x,
10         obstacleBoxY = obstacleBox.y;
11
12     return tRexBoxX < obstacleBoxX + obstacleBox.width && tRexBoxX + tRexBox.width > obstacleBoxX && tRexBoxY < obstacleBoxY + obstacleBox.height && tRexBox.height + tRexBoxY > obstacleBoxY;
13 }

建立碰撞盒子

  接下来要为恐龙和障碍物建立碰撞盒子。游戏中为恐龙建立了6个碰撞盒子,分布在头、躯干和脚,同时它还有闪避状态:

        Trex.collisionBoxes = {
            DUCKING:[
                new CollisionBox(1,18,55,25)
            ],
            RUNNING: [
                new CollisionBox(22, 0, 17, 16),
                new CollisionBox(1, 18, 30, 9),
                new CollisionBox(10, 35, 14, 8),
                new CollisionBox(1, 24, 29, 5),
                new CollisionBox(5, 30, 21, 4),
                new CollisionBox(9, 34, 15, 4)
            ]
        };

障碍物的碰撞盒子定义在Obstacle.types中:

 1 Obstacle.types = [{
 2     type: ‘CACTUS_SMALL‘,
 3     width: 17,
 4     height: 35,
 5     yPos: 105,
 6     multipleSpeed: 4,
 7     minGap: 120,
 8     minSpeed: 0,
 9     collisionBoxes: [new CollisionBox(0, 7, 5, 27), new CollisionBox(4, 0, 6, 34), new CollisionBox(10, 4, 7, 14)]
10 },
11 {
12     type: ‘CACTUS_LARGE‘,
13     width: 25,
14     height: 50,
15     yPos: 90,
16     multipleSpeed: 7,
17     minGap: 120,
18     minSpeed: 0,
19     collisionBoxes: [new CollisionBox(0, 12, 7, 38), new CollisionBox(8, 0, 7, 49), new CollisionBox(13, 10, 10, 38)]
20 },
21 {
22     type: ‘PTERODACTYL‘,
23     width: 46,
24     height: 40,
25     yPos: [100, 75, 50],
26     // Variable height mobile.
27     multipleSpeed: 999,
28     minSpeed: 8.5,
29     minGap: 150,
30     collisionBoxes: [new CollisionBox(15, 15, 16, 5), new CollisionBox(18, 21, 24, 6), new CollisionBox(2, 14, 4, 3), new CollisionBox(6, 10, 4, 7), new CollisionBox(10, 8, 6, 9)],
31     numFrames: 2,
32     frameRate: 1000 / 6,
33     speedOffset: .8
34 }];

不过这只是定义了障碍物数量为1的情况,复数的障碍物需要在创建时修正碰撞盒子:

1 if (this.size > 1) {//只针对仙人掌
2     this.collisionBoxes[1].width = this.width - this.collisionBoxes[0].width - this.collisionBoxes[2].width;
3     this.collisionBoxes[2].x = this.width - this.collisionBoxes[2].width;
4 }

下图分别为单数和复数的盒子。

最后执行碰撞检测:

 1 function checkForCollision(obstacle, tRex) {
 2         //创建最外层的大盒子
 3     var tRexBox = new CollisionBox(tRex.xPos + 1, tRex.yPos + 1, tRex.config.WIDTH - 2, tRex.config.HEIGHT - 2);
 4     var obstacleBox = new CollisionBox(obstacle.xPos + 1, obstacle.yPos + 1, obstacle.typeConfig.width * obstacle.size - 2, obstacle.typeConfig.height - 2);
 5
 6     }
 7     if (boxCompare(tRexBox, obstacleBox)) {
 8         var collisionBoxes = obstacle.collisionBoxes;
 9         var tRexCollisionBoxes = tRex.ducking ? Trex.collisionBoxes.DUCKING: Trex.collisionBoxes.RUNNING;
10
11         for (var t = 0; t < tRexCollisionBoxes.length; t++) {
12             for (var i = 0; i < collisionBoxes.length; i++) {
13                 //修正盒子
14                 var adjTrexBox = createAdjustedCollisionBox(tRexCollisionBoxes[t], tRexBox);
15                 var adjObstacleBox = createAdjustedCollisionBox(collisionBoxes[i], obstacleBox);
16                 var crashed = boxCompare(adjTrexBox, adjObstacleBox);
17
18                 if (crashed) {
19                     return [adjTrexBox, adjObstacleBox];
20                 }
21             }
22         }
23     }
24     return false;
25 }

//修正盒子,将相对坐标转为画布坐标
function createAdjustedCollisionBox(box, adjustment) {
    return new CollisionBox(box.x + adjustment.x, box.y + adjustment.y, box.width, box.height);
}

以下是最终运行效果,打开控制台就能看到碰撞输出:

后记

  通过建立碰撞盒子进行碰撞检测在应用上非常广泛,著名的街机游戏《街霸》和《拳皇》就是采用了这种方式:

可以看到游戏中对人物建立了多个碰撞盒子,红色代表攻击区域,蓝色代表可以被攻击的区域,绿色区域之间不能重叠,用来推挤对手。

时间: 2024-10-22 22:12:43

Chrome自带恐龙小游戏的源码研究(七)的相关文章

Chrome自带恐龙小游戏的源码研究(完)

在上一篇<Chrome自带恐龙小游戏的源码研究(七)>中研究了恐龙与障碍物的碰撞检测,这一篇主要研究组成游戏的其它要素. 游戏分数记录 如图所示,分数及最高分记录显示在游戏界面的右上角,每达到100分就会出现闪烁特效,游戏第一次gameover时显示历史最高分.分数记录器由DistanceMeter构造函数实现,以下是它的全部代码: 1 DistanceMeter.dimensions = { 2 WIDTH: 10, //每个字符的宽度 3 HEIGHT: 13, //每个字符的高 4 DE

Chrome自带恐龙小游戏的源码研究(五)

在上一篇<Chrome自带恐龙小游戏的源码研究(四)>中实现了障碍物的绘制及移动,从这一篇开始主要研究恐龙的绘制及一系列键盘动作的实现. 会眨眼睛的恐龙 在游戏开始前的待机界面,如果仔细观察会发现恐龙会时不时地眨眼睛.这是通过交替绘制这两个图像实现的: 可以通过一张图片来了解这个过程: 为实现图片的切换,需要一个计时器timer,并且需要知道两张图片切换的时间间隔msPerFrame.当计时器timer的时间大于切换的时间间隔msPerFrame时,将图片切换到下一张,到达最后一张时又从第一张

Chrome自带恐龙小游戏的源码研究(六)

在上一篇<Chrome自带恐龙小游戏的源码研究(五)>中实现了眨眼睛的恐龙,这一篇主要研究恐龙的跳跃. 恐龙的跳跃 游戏通过敲击键盘的Spacebar或者Up来实现恐龙的跳跃.先用一张图来表示整个跳跃的过程: 首先规定向下为正方向,即重力加速度(g)为正,起跳的速度(v)为负,恐龙距离画布上方的距离为yPos: 每一帧动画中,速度都会与重力加速度相加得到新的速度,再用新的速度与yPos相加得到新的yPos,改变恐龙的位置为新的yPos,表现出来为yPos不断减小: 当恐龙升至最高点,此时速度为

Chrome自带恐龙小游戏的源码研究(四)

在上一篇<Chrome自带恐龙小游戏的源码研究(三)>中实现了让游戏昼夜交替,这一篇主要研究如何绘制障碍物. 障碍物有两种:仙人掌和翼龙.仙人掌有大小两种类型,可以同时并列多个:翼龙按高.中.低的随机飞行高度出现,不可并行.仙人掌和地面有着相同的速度向左移动,翼龙则快一些或慢一些,因为添加了随机的速度修正.我们使用一个障碍物列表管理它们,当它们移出屏幕外时则将其从列表中移除.同时再用一个列表记录它们的类型: 1 Obstacle.obstacles = []; //存储障碍物的数组 2 Obs

Chrome自带恐龙小游戏的源码研究(二)

在上一篇<Chrome自带恐龙小游戏的源码研究(一)>中实现了地面的绘制和运动,这一篇主要研究云朵的绘制. 云朵的绘制通过Cloud构造函数完成.Cloud实现代码如下: 1 Cloud.config = { 2 HEIGHT:14, //云朵sprite的高度 3 MAX_CLOUD_GAP:400, //两朵云之间的最大间隙 4 MAX_SKY_LEVEL:30, //云朵的最大高度 5 MIN_CLOUD_GAP:100, //两朵云之间的最小间隙 6 MIN_SKY_LEVEL:71,

Chrome自带恐龙小游戏的源码研究(一)

众所周知,Chrome浏览器在网络不通的情况下,会出现一个霸王龙翻越障碍的小游戏:  这个游戏做得小巧精致,于是探究了一下它的源码,发现代码写得相当严谨并且富有技巧性,用来学习再好不过了. 游戏虽然看起来简单,但也有几千行的代码量.主要包括五个构造函数: 游戏逻辑控制函数Runner 背景管理函数Horizon 地面 (HorizonLine) 云朵 (Cloud) 昼夜更替 (NightMode) 障碍物 (Obstacle) 霸王龙函数Trex 分数记录函数DistanceMeter 游戏结

github下载下来的C#控制台小游戏[含源码]

早就听说了github是世界最大的源码库,但自己却不是很懂,今天去研究了下,注册了一个帐号,然后在上面搜索了一下C# game,然后发现有许多的游戏. 随意地选择了一个,感觉比较简单,于是就下载了下来.这个解决方案包含了5个项目,每个项目都是一个小的控制台游戏. 我打开运行了了下,有2个项目报错,但是汽车和乒乓可以运行. 看了下代码,感觉还不错,有许多值得学习的地方. 这个代码库是一个美国人提供的,瞬间感觉自己也变得洋气了起来! 每个项目都只有一个文件,真是够简单. 贴出乒乓的代码看看 usin

c#实现简单金山打字小游戏(源码)

using GameDemo.Utils;using System;using System.Collections.Generic;using System.Linq;using System.Text; namespace GameDemo{ class Program { static void Main(string[] args) { int total=0;//计时 Console.WriteLine("开始游戏"); Console.WriteLine("准备好

Nginx源码研究七:ngx_http_core_main_conf_t

typedef struct { ngx_array_t servers; /* ngx_http_core_srv_conf_t */ ngx_http_phase_engine_t phase_engine; ngx_hash_t headers_in_hash; ngx_hash_t variables_hash; ngx_array_t variables; /* ngx_http_variable_t */ ngx_uint_t ncaptures; ngx_uint_t server