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

  在上一篇《Chrome自带恐龙小游戏的源码研究(三)》中实现了让游戏昼夜交替,这一篇主要研究如何绘制障碍物。

  障碍物有两种:仙人掌和翼龙。仙人掌有大小两种类型,可以同时并列多个;翼龙按高、中、低的随机飞行高度出现,不可并行。仙人掌和地面有着相同的速度向左移动,翼龙则快一些或慢一些,因为添加了随机的速度修正。我们使用一个障碍物列表管理它们,当它们移出屏幕外时则将其从列表中移除。同时再用一个列表记录它们的类型:

1 Obstacle.obstacles = [];    //存储障碍物的数组
2 Obstacle.obstacleHistory = [];  //记录障碍物数组中障碍物的类型

障碍物的出现不能太频繁,也不能太稀少,太频繁立刻就gameover了,太稀少则没有挑战性,因此需要一定的规则来生成障碍物。每组障碍物之间应该有一段间隔作为落脚点,新生成的障碍物在这个间隔之外生成。如示意图所示:

因此,先定义一个最大间距系数,下面会用这个系数生成随机间距:

Obstacle.MAX_GAP_COEFFICIENT = 1.5; //障碍物最大间距系数

另外,还需要对障碍物进行一些约束及配置:

 1 //每组障碍物的最大数量
 2 Obstacle.MAX_OBSTACLE_LENGTH = 3;
 3 //相邻的障碍物类型的最大重复数
 4 Obstacle.MAX_OBSTACLE_DUPLICATION = 2;
 5
 6 Obstacle.types = [
 7     {
 8         type: ‘CACTUS_SMALL‘, //小仙人掌
 9         width: 17,  //宽
10         height: 35, //高
11         yPos: 105,  //在画布上的y坐标
12         multipleSpeed: 4,
13         minGap: 120,    //最小间距
14         minSpeed: 0    //最低速度
15     },
16     {
17         type: ‘CACTUS_LARGE‘,   //大仙人掌
18         width: 25,
19         height: 50,
20         yPos: 90,
21         multipleSpeed: 7,
22         minGap: 120,
23         minSpeed: 0
24     },
25     {
26         type: ‘PTERODACTYL‘,    //翼龙
27         width: 46,
28         height: 40,
29         yPos: [ 100, 75, 50 ], //有高、中、低三种高度
30         multipleSpeed: 999,
31         minSpeed: 8.5,
32         minGap: 150,
33         numFrames: 2,   //有两个动画帧
34         frameRate: 1000/6,  //动画帧的切换速率,这里为一秒6帧
35         speedOffset: .8 //速度修正
36     }
37 ];

障碍物的所有实现由构造函数Obstacle完成,下面是它的实现代码:

  1 /**
  2  * 绘制障碍物构造函数
  3  * @param canvas
  4  * @param type 障碍物的类型
  5  * @param spriteImgPos 雪碧图坐标
  6  * @param dimensions 屏幕尺寸
  7  * @param gapCoefficient 障碍物间隙
  8  * @param speed 障碍物移动速度
  9  * @param opt_xOffset 障碍物水平偏移量
 10  * @constructor
 11  */
 12 function Obstacle(canvas,type,spriteImgPos,dimensions,gapCoefficient,speed,opt_xOffset) {
 13     this.ctx = canvas.getContext(‘2d‘);
 14     this.spritePos = spriteImgPos;
 15     //障碍物类型(仙人掌、翼龙)
 16     this.typeConfig = type;
 17     this.gapCoefficient = gapCoefficient;
 18     //每个障碍物的数量(1~3)
 19     this.size = getRandomNum(1,Obstacle.MAX_OBSTACLE_LENGTH);
 20     this.dimensions = dimensions;
 21     //表示该障碍物是否可以被移除
 22     this.remove = false;
 23     //水平坐标
 24     this.xPos = dimensions.WIDTH + (opt_xOffset || 0);
 25     this.yPos = 0;
 26     this.width = 0;
 27     this.gap = 0;
 28     this.speedOffset = 0;   //速度修正
 29
 30     //障碍物的动画帧
 31     this.currentFrame = 0;
 32     //动画帧切换的计时器
 33     this.timer = 0;
 34
 35     this.init(speed);
 36 }
 37 ```
 38
 39 实例方法:
 40 ```javascript
 41 Obstacle.prototype = {
 42     init:function(speed) {
 43         //如果随机障碍物是翼龙,则只出现一只
 44         //翼龙的multipleSpeed是999,远大于speed
 45         if (this.size > 1 && this.typeConfig.multipleSpeed > speed) {
 46             this.size = 1;
 47         }
 48         //障碍物的总宽度等于单个障碍物的宽度乘以个数
 49         this.width = this.typeConfig.width * this.size;
 50
 51         //若障碍物的纵坐标是一个数组
 52         //则随机选取一个
 53         if (Array.isArray(this.typeConfig.yPos))  {
 54             var yPosConfig = this.typeConfig.yPos;
 55             this.yPos = yPosConfig[getRandomNum(0, yPosConfig.length - 1)];
 56         } else {
 57             this.yPos = this.typeConfig.yPos;
 58         }
 59
 60         this.draw();
 61
 62         //对翼龙的速度进行修正,让它看起来有的飞得快一些,有些飞得慢一些
 63         if (this.typeConfig.speedOffset) {
 64             this.speedOffset = Math.random() > 0.5 ? this.typeConfig.speedOffset :
 65                 -this.typeConfig.speedOffset;
 66         }
 67
 68         //障碍物之间的间隙,与游戏速度有关
 69         this.gap = this.getGap(this.gapCoefficient, speed);
 70     },
 71     //障碍物之间的间隔,gapCoefficient为间隔系数
 72     getGap: function(gapCoefficient, speed) {
 73         var minGap = Math.round(this.width * speed +
 74             this.typeConfig.minGap * gapCoefficient);
 75         var maxGap = Math.round(minGap * Obstacle.MAX_GAP_COEFFICIENT);
 76         return getRandomNum(minGap, maxGap);
 77     },
 78     //判断障碍物是否移出屏幕外
 79     isVisible: function() {
 80         return this.xPos + this.width > 0;
 81     },
 82     draw:function() {
 83         //障碍物宽高
 84         var sourceWidth = this.typeConfig.width;
 85         var sourceHeight = this.typeConfig.height;
 86
 87         //根据障碍物数量计算障碍物在雪碧图上的x坐标
 88         //this.size的取值范围是1~3
 89         var sourceX = (sourceWidth * this.size) * (0.5 * (this.size - 1)) +
 90             this.spritePos.x;
 91
 92         // 如果当前动画帧大于0,说明障碍物类型是翼龙
 93         // 更新翼龙的雪碧图x坐标使其匹配第二帧动画
 94         if (this.currentFrame > 0) {
 95             sourceX += sourceWidth * this.currentFrame;
 96         }
 97         this.ctx.drawImage(imgSprite,
 98             sourceX, this.spritePos.y,
 99             sourceWidth * this.size, sourceHeight,
100             this.xPos, this.yPos,
101             sourceWidth * this.size, sourceHeight);
102     },
103     //单个障碍物的移动
104     update:function(deltaTime, speed) {
105         //如果障碍物还没有移出屏幕外
106         if (!this.remove) {
107             //如果有速度修正则修正速度
108             if (this.typeConfig.speedOffset) {
109                 speed += this.speedOffset;
110             }
111             //更新x坐标
112             this.xPos -= Math.floor((speed * FPS / 1000) * deltaTime);
113
114             // Update frame
115             if (this.typeConfig.numFrames) {
116                 this.timer += deltaTime;
117                 if (this.timer >= this.typeConfig.frameRate) {
118                     //在两个动画帧之间来回切换以达到动画效果
119                     this.currentFrame =
120                         this.currentFrame == this.typeConfig.numFrames - 1 ?
121                             0 : this.currentFrame + 1;
122                     this.timer = 0;
123                 }
124             }
125             this.draw();
126
127             if (!this.isVisible()) {
128                 this.remove = true;
129             }
130         }
131     },
132     //管理多个障碍物移动
133     updateObstacles: function(deltaTime, currentSpeed) {
134         //保存一个障碍物列表的副本
135         var updatedObstacles = Obstacle.obstacles.slice(0);
136
137         for (var i = 0; i < Obstacle.obstacles.length; i++) {
138             var obstacle = Obstacle.obstacles[i];
139             obstacle.update(deltaTime, currentSpeed);
140
141             //移除被标记为删除的障碍物
142             if (obstacle.remove) {
143                 updatedObstacles.shift();
144             }
145         }
146         Obstacle.obstacles = updatedObstacles;
147
148         if(Obstacle.obstacles.length > 0) {
149             //获取障碍物列表中的最后一个障碍物
150             var lastObstacle = Obstacle.obstacles[Obstacle.obstacles.length - 1];
151
152             //若满足条件则添加障碍物
153             if (lastObstacle &&
154                 lastObstacle.isVisible() &&
155                 (lastObstacle.xPos + lastObstacle.width + lastObstacle.gap) <
156                 this.dimensions.WIDTH) {
157                 this.addNewObstacle(currentSpeed);
158             }
159         } else {//若障碍物列表中没有障碍物则立即添加
160             this.addNewObstacle(currentSpeed);
161         }
162     },
163     //随机添加障碍
164     addNewObstacle:function (currentSpeed) {
165         //随机选取一种类型的障碍
166         var obstacleTypeIndex = getRandomNum(0,Obstacle.types.length - 1);
167         var obstacleType = Obstacle.types[obstacleTypeIndex];
168
169         //检查随机取到的障碍物类型是否与前两个重复
170         //或者检查其速度是否合法,这样可以保证游戏在低速时不出现翼龙
171         //如果检查不通过,则重新再选一次直到通过为止
172         if(this.duplicateObstacleCheck(obstacleType.type) || currentSpeed < obstacleType.minSpeed) {
173             this.addNewObstacle(currentSpeed);
174         } else {
175             //检查通过后,获取其雪碧图中的坐标
176             var obstacleSpritePos = this.spritePos[obstacleType.type];
177             //生成新的障碍物并存入数组
178             Obstacle.obstacles.push(new Obstacle(c,obstacleType,obstacleSpritePos,this.dimensions,
179                 this.gapCoefficient,currentSpeed,obstacleType.width));
180             //同时将障碍物的类型存入history数组
181             Obstacle.obstacleHistory.unshift(obstacleType.type);
182         }
183
184         //若history数组的长度大于1,则清空最前面的两个
185         if (Obstacle.obstacleHistory.length > 1) {
186             Obstacle.obstacleHistory.splice(Obstacle.MAX_OBSTACLE_DUPLICATION);
187         }
188     },
189     //检查障碍物是否超过允许的最大重复数
190     duplicateObstacleCheck:function(nextObstacleType) {
191         var duplicateCount = 0;
192         //与history数组中的障碍物类型比较,最大只允许重得两次
193         for(var i = 0; i < Obstacle.obstacleHistory.length; i++) {
194             duplicateCount = Obstacle.obstacleHistory[i] === nextObstacleType ? duplicateCount + 1 : 0;
195         }
196         return duplicateCount >= Obstacle.MAX_OBSTACLE_DUPLICATION;
197     }
198 };

最后在此前的基础上添加一段测试代码:

 1 window.onload = function () {
 2             var h = new HorizonLine(c,spriteDefinition.HORIZON);
 3             var cloud = new Cloud(c,spriteDefinition.CLOUD,DEFAULT_WIDTH);
 4             var night = new NightMode(c,spriteDefinition.MOON,DEFAULT_WIDTH);
 5             var obstacle = new Obstacle(c,Obstacle.types[0],spriteDefinition,{WIDTH:600},0.6,1);
 6             var startTime = 0;
 7             var deltaTime;
 8             var speed = 3;
 9             (function draw(time) {
10                 gameFrame++;
11                 if(speed < 13.5) {
12                     speed += 0.01;
13                 }
14                 ctx.clearRect(0,0,600,150);
15                 time = time || 0;
16                 deltaTime = time - startTime;
17                 h.update(deltaTime,speed);
18                 cloud.updateClouds(0.2);
19                 night.invert(deltaTime);
20                 obstacle.updateObstacles(deltaTime,speed);
21                 startTime = time;
22                 window.requestAnimationFrame(draw,c);
23             })();
24         };

最终得到的效果:

时间: 2024-10-16 10:34:49

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

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

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

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

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

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

在上一篇<Chrome自带恐龙小游戏的源码研究(六)>中研究了恐龙的跳跃过程,这一篇研究恐龙与障碍物之间的碰撞检测. 碰撞盒子 游戏中采用的是矩形(非旋转矩形)碰撞.这类碰撞优点是计算比较简单,缺点是对不规则物体的检测不够精确.如果不做更为精细的处理,结果会像下图: 如图所示,两个盒子虽然有重叠部分,但实际情况是恐龙和仙人掌之间并未发生碰撞.为了解决这个问题,需要建立多个碰撞盒子: 不过这样还是有问题,观察图片,恐龙和仙人掌都有四个碰撞盒子,如果每次Game Loop里都对这些盒子进行碰撞检测

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

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

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("准备好

[小游戏] 微信小游戏开发源码_教程_工具_资源最新集合

[小游戏资源] 微信小游戏开发资源目录 一.微信官方游戏教程 小游戏简易教程 小游戏API大全 小游戏开发工具 二.微信小游戏图标资源 Game-icons.net 三.微信小游戏图片资源 Super Game Asset GameDev Market envato market Game Art Partners KENNEY 四.微信小游戏音频资源 工具类 Audacity 9 款音频压缩软件推荐 7 款混音软件推荐 7 款降噪软件推荐 资源类 爱给音效库 freesound Soundim