Chrome自带恐龙小游戏的源码研究(六)
在上一篇《Chrome自带恐龙小游戏的源码研究(五)》中实现了眨眼睛的恐龙,这一篇主要研究恐龙的跳跃。
恐龙的跳跃
游戏通过敲击键盘的Spacebar
或者Up
来实现恐龙的跳跃。先用一张图来表示整个跳跃的过程:
- 首先规定向下为正方向,即重力加速度(g)为正,起跳的速度(v)为负,恐龙距离画布上方的距离为yPos;
- 每一帧动画中,速度都会与重力加速度相加得到新的速度,再用新的速度与yPos相加得到新的yPos,改变恐龙的位置为新的yPos,表现出来为yPos不断减小;
- 当恐龙升至最高点,此时速度为0,并且仍具有向下的重力加速度。
- 速度仍与重力加速度相加得到新的速度,此时速度方向向下,为正值,表现为yPos逐渐增加;
- 落地,并使yPos不超过地面的高度,将速度重置为0,更新状态jumping为false。
下面通过代码来实现。首先注册键盘事件:
document.addEventListener('keydown',onKeyDown);
document.addEventListener('keyup',onKeyUp);
function onKeyDown(e) {
if(keycode.JUMP[e.keyCode]) {
if(!trex.jumping) {
trex.startJump(6);
}
}
}
按下跳跃键后,执行startJump方法:
startJump: function(speed) {
if (!this.jumping) {
//切换到jump状态
this.update(0, Trex.status.JUMPING);
//设置跳跃速度
this.jumpVelocity = this.config.INIITAL_JUMP_VELOCITY - (speed / 10);
this.jumping = true;
this.reachedMinHeight = false;
}
}
之后在每次GameLoop中更新状态:
if (trex.jumping) {
ctx.clearRect(0, 0, 600, 150);
trex.updateJump(deltaTime);
}
updateJump: function(deltaTime) {
//帧切换速率
var msPerFrame = Trex.animFrames[this.status].msPerFrame;
//经过的帧数
var framesElapsed = deltaTime / msPerFrame;
//更新y轴坐标
this.yPos += Math.round(this.jumpVelocity * framesElapsed);
//由于速度受重力影响,需要对速度进行修正
this.jumpVelocity += this.config.GRAVITY * framesElapsed; //达到最小跳跃高度
if (this.yPos < this.minJumpHeight) {
this.reachedMinHeight = true;
}
//达到最大高度后停止跳跃
if (this.yPos < this.config.MAX_JUMP_HEIGHT) {
this.endJump();
}
if (this.yPos > this.groundYPos) {
this.reset();
this.jumpCount++;
}
this.update(deltaTime);
}, update: function(deltaTime, opt_status) {
this.timer += deltaTime; if (opt_status) {
this.status = opt_status;
this.currentFrame = 0;
//得到对应状态的帧率 e.g. WAITING 1000ms / 3fps = 333ms/fps
this.msPerFrame = Trex.animFrames[opt_status].msPerFrame;
//对应状态的动画帧 e.g. WAITING [44,0]
this.currentAnimFrames = Trex.animFrames[opt_status].frames; if (opt_status === Trex.status.WAITING) {
//开始计时
this.animStartTime = getTimeStamp();
//设置延时
this.setBlinkDelay();
}
} //计时器超过一帧的运行时间,切换到下一帧
if (this.timer >= this.msPerFrame) {
this.currentFrame = this.currentFrame === this.currentAnimFrames.length - 1 ? 0 : this.currentFrame + 1;
this.timer = 0;
} //待机状态
if (this.status === Trex.status.WAITING) {
//执行眨眼动作
this.blink(getTimeStamp());
} else {
this.draw(this.currentAnimFrames[this.currentFrame], 0);
}
}
这样就实现了跳跃的过程。
轻跳
如果持续按住Spacebar
或者Up
不放,跳跃总是能达到最大高度的,但很多情况下我们只是轻轻敲击一下键盘然后就放手了,这时的游戏表现为恐龙只跳起一个很低的高度,然后开始下落,一般称之为“轻跳”、“小跳”。这看起来是根据按键时长来决定跳跃高度,实现起来有一定的难度,但实际情况却比较简单,只监听键盘的onkeyup事件即可。
function onKeyUp(e) {
if (keycode.JUMP[e.keyCode]) {
trex.endJump();
}
}
当键盘抬起时,执行endJump方法,而endJump方法也十分简单:
endJump: function() {
if (this.reachedMinHeight && this.jumpVelocity < this.config.DROP_VELOCITY) {
this.jumpVelocity = this.config.DROP_VELOCITY;
}
}
首先要判断是否达到了最小跳跃高度,this.reachedMinHeight
这个变量非常有用,它避免了游戏角色只跳起数像素然后落地这样的无意义跳跃。此时如果向上的速度仍比较大的话,则强制减小为this.config.DROP_VELOCITY
以便能够更快地下落。
下图分别是“大跳”和“小跳”的区别:
快速落地
在跳跃过程中如果按下了Down
键,恐龙会加速下降。
function onKeyDown(e) {
//......
if(keycode.DUCK[e.keyCode]) {//Down
if(trex.jumping) {
trex.setSpeedDrop(); //加速下降
}
}
}
松开键位时取消加速:
function onKeyUp(e) {
//......
if (keycode.DUCK[e.keyCode]) {
trex.speedDrop = false;
}
}
在构造函数中添加setSpeedDrop方法:
setSpeedDrop: function() {
this.speedDrop = true;
this.jumpVelocity = 1; //将速度设置为1,正方向(向下为正方向)
}
还需要对updateJump方法做一些更新:
updateJump:function (deltaTime) {
//...... //更新y轴坐标
if (this.speedDrop) {
//SPEED_DROP_COEFFICIENT为加速倍数,初始设定为3
this.yPos += Math.round(this.jumpVelocity * this.config.SPEED_DROP_COEFFICIENT * framesElapsed);
} else {
this.yPos += Math.round(this.jumpVelocity * framesElapsed);
} //达到最小跳跃高度
//speedDrop也能触发reachedMinHeight
if (this.yPos < this.minJumpHeight || this.speedDrop) {
this.reachedMinHeight = true;
} //达到最大高度后停止跳跃
//speedDrop也能触发endJump
if (this.yPos < this.config.MAX_JUMP_HEIGHT || this.speedDrop) {
this.endJump();
}
//...... }
效果如下图所示,在跳跃过程中按住Down
,可以发现下落速度比平时快:
闪避
在地面上按住Down
键,恐龙会进入闪避状态。首先还是从keydown方法入手:
if (keycode.DUCK[e.keyCode]) {
e.preventDefault();
if (trex.jumping) {
trex.setSpeedDrop();
} else if (!trex.jumping && !trex.ducking) {
trex.setDuck(true); //闪避
}
}
keyup方法取消闪避:
function onKeyUp(e) {
if (keycode.JUMP[e.keyCode]) {
trex.endJump();
}
if (keycode.DUCK[e.keyCode]) {
trex.speedDrop = false;
trex.setDuck(false); //取消闪避
}
}
setDuck方法:
setDuck: function(isDucking) {
if (isDucking && this.status !== Trex.status.DUCKING) {
this.update(0, Trex.status.DUCKING);
this.ducking = true;
} else if (this.status === Trex.status.DUCKING) {
this.update(0, Trex.status.RUNNING);
this.ducking = false;
}
}
最终效果如下(Spacebar
或Up
跳跃;Down
快速下降/闪避):
// this.bumpThreshold ? this.dimensions.WIDTH : 0;
},
draw:function() {
this.ctx.drawImage(imgSprite,
this.sourceXPos[0], this.spritePos.y,
this.dimensions.WIDTH, this.dimensions.HEIGHT,
this.xPos[0],this.yPos,
this.dimensions.WIDTH,this.dimensions.HEIGHT);
this.ctx.drawImage(imgSprite,
this.sourceXPos[1], this.spritePos.y,
this.dimensions.WIDTH, this.dimensions.HEIGHT,
this.xPos[1],this.yPos,
this.dimensions.WIDTH,this.dimensions.HEIGHT);
},
updateXPos:function(pos,increment) {
var line1 = pos,
line2 = pos === 0 ? 1 : 0;
this.xPos[line1] -= increment;
this.xPos[line2] = this.xPos[line1] + this.dimensions.WIDTH;
if(this.xPos[line1] = this.msPerFrame) {
this.currentFrame = this.currentFrame === this.currentAnimFrames.length - 1 ?
0 : this.currentFrame + 1;
this.timer = 0;
}
if (this.speedDrop && this.yPos === this.groundYPos) {
this.speedDrop = false;
this.setDuck(true);
}
},
//开始跳跃
startJump:function (speed) {
if(!this.jumping) {
//切换到jump状态
this.update(0,Trex.status.JUMPING);
//设置跳跃速度
this.jumpVelocity = this.config.INIITAL_JUMP_VELOCITY - (speed / 10);
this.jumping = true;
this.reachedMinHeight = false;
this.speedDrop = false;
}
},
updateJump:function (deltaTime, speed) {
//帧切换速率
var msPerFrame = Trex.animFrames[this.status].msPerFrame;
//经过的帧数
var framesElapsed = deltaTime / msPerFrame;
//更新y轴坐标
if(this.speedDrop) {
this.yPos += Math.round(this.jumpVelocity *
this.config.SPEED_DROP_COEFFICIENT * framesElapsed);
} else {
this.yPos += Math.round(this.jumpVelocity * framesElapsed);
}
//由于速度受重力影响,需要对速度进行修正
this.jumpVelocity += this.config.GRAVITY * framesElapsed;
//达到最小跳跃高度
if (this.yPos this.groundYPos) {
this.reset();
this.jumpCount++;
}
this.update(deltaTime);
},
endJump: function() {
if (this.reachedMinHeight && this.jumpVelocity = this.blinkDelay) {
this.draw(this.currentAnimFrames[this.currentFrame],0);
if (this.currentFrame === 1) {//0闭眼 1开眼
//设置新的眨眼间隔时间
this.setBlinkDelay();
this.animStartTime = time;
}
}
},
draw:function (x,y) {
var sourceX = x;
var sourceY = y;
var sourceWidth = this.ducking && this.status != Trex.status.CRASHED ?
this.config.WIDTH_DUCK : this.config.WIDTH;
var sourceHeight = this.config.HEIGHT;
sourceX += this.spritePos.x;
sourceY += this.spritePos.y;
this.ctx.drawImage(imgSprite,
sourceX, sourceY,
sourceWidth, sourceHeight,
this.xPos, this.yPos,
this.ducking ? this.config.WIDTH_DUCK : this.config.WIDTH, this.config.HEIGHT);
}
};
window.onload = function () {
var h = new HorizonLine(c,spriteDefinition.HORIZON);
trex = new Trex(c,spriteDefinition.TREX);
var startTime = 0;
var deltaTime;
var speed = 3;
(function draw(time) {
gameFrame++;
time = time || 0;
deltaTime = time - startTime;
if(trex.jumping) {
ctx.clearRect(0,0,600,150);
trex.updateJump(deltaTime);
h.update(deltaTime,speed);
} else {
ctx.clearRect(0,0,600,150);
h.update(deltaTime,speed);
trex.update(deltaTime);
}
startTime = time;
requestAnimationFrame(draw,c);
})();
};
// ]]>
Chrome自带恐龙小游戏的源码研究(六)的更多相关文章
- Chrome自带恐龙小游戏的源码研究(七)
在上一篇<Chrome自带恐龙小游戏的源码研究(六)>中研究了恐龙的跳跃过程,这一篇研究恐龙与障碍物之间的碰撞检测. 碰撞盒子 游戏中采用的是矩形(非旋转矩形)碰撞.这类碰撞优点是计算比较 ...
- Chrome自带恐龙小游戏的源码研究(一)
目录 Chrome自带恐龙小游戏的源码研究(一)——绘制地面 Chrome自带恐龙小游戏的源码研究(二)——绘制云朵 Chrome自带恐龙小游戏的源码研究(三)——昼夜交替 Chrome自带恐龙小游戏 ...
- Chrome自带恐龙小游戏的源码研究(完)
在上一篇<Chrome自带恐龙小游戏的源码研究(七)>中研究了恐龙与障碍物的碰撞检测,这一篇主要研究组成游戏的其它要素. 游戏分数记录 如图所示,分数及最高分记录显示在游戏界面的右上角,每 ...
- Chrome自带恐龙小游戏的源码研究(五)
在上一篇<Chrome自带恐龙小游戏的源码研究(四)>中实现了障碍物的绘制及移动,从这一篇开始主要研究恐龙的绘制及一系列键盘动作的实现. 会眨眼睛的恐龙 在游戏开始前的待机界面,如果仔细观 ...
- Chrome自带恐龙小游戏的源码研究(四)
在上一篇<Chrome自带恐龙小游戏的源码研究(三)>中实现了让游戏昼夜交替,这一篇主要研究如何绘制障碍物. 障碍物有两种:仙人掌和翼龙.仙人掌有大小两种类型,可以同时并列多个:翼龙按高. ...
- Chrome自带恐龙小游戏的源码研究(三)
在上一篇<Chrome自带恐龙小游戏的源码研究(二)>中实现了云朵的绘制和移动,这一篇主要研究如何让游戏实现昼夜交替. 昼夜交替的效果主要是通过样式来完成,但改变样式的时机则由脚本控制. ...
- Chrome自带恐龙小游戏的源码研究(二)
在上一篇<Chrome自带恐龙小游戏的源码研究(一)>中实现了地面的绘制和运动,这一篇主要研究云朵的绘制. 云朵的绘制通过Cloud构造函数完成.Cloud实现代码如下: Cloud.co ...
- WinFom中经典小游戏(含源码)
最近整理了若干经典的小游戏,无聊时可以打发时间.程序本身不大,练手非常不错,主要是GDI编程,主界面地址如下图所示 源码下载方式 1,关注微信公众号:小特工作室(也可直接扫描签名处二维码) 2,发送: ...
- github下载下来的C#控制台小游戏[含源码]
早就听说了github是世界最大的源码库,但自己却不是很懂,今天去研究了下,注册了一个帐号,然后在上面搜索了一下C# game,然后发现有许多的游戏. 随意地选择了一个,感觉比较简单,于是就下载了下来 ...
随机推荐
- Spring MVC @PathVariable 特殊字符
1.问题 SpringMVC项目中通过下面的URL进行GET请求.当version有多个小数点的时候.如version为1.0.1008.后台通过@PathVariable来获取version等于1. ...
- pat 甲级 1010. Radix (25)
1010. Radix (25) 时间限制 400 ms 内存限制 65536 kB 代码长度限制 16000 B 判题程序 Standard 作者 CHEN, Yue Given a pair of ...
- 创建外网 ext
虽然外部网络是已经存在的网络,但我们还是需要在 Neutron 中定义外部网络的对象,这样 router 才知道如何将租户网络和外部网络连接起来. 上一节我们已经为创建外部网络配置了ML2,本节将通过 ...
- 【CF1073A】Diverse Substring(签到)
题意:给定一个由小写字母组成的串,要求找出一个子串使得其中出现最多的字母不超过它长度的一半下取整 n<=1e3 思路: #include<cstdio> #include<cs ...
- Codeforces 475D CGCDSSQ 区间gcd值
题目链接 题意 给定一个长度为 \(n\) 的数列 \(a_1,...,a_n\) 与 \(q\) 个询问 \(x_1,...,x_q\),对于每个 \(x_i\) 回答有多少对 \((l,r)\) ...
- hdu 4525(数学)
威威猫系列故事——吃鸡腿 Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 65535/32768 K (Java/Others)Tota ...
- js链式调用 柯里化
var d = 1; d.add(2).add(3).add(4) //输出10 写出这个add函数 Number.prototype.add = function(x){ return this + ...
- SDOI2017round1酱油记day0
嗯... 现在是21:12...准备睡了. 睡前写下day0一天如何过的: 早上5点起床到教室早自习,迷迷糊糊的宣誓,背东西,英语听写: 都停课了为什么还要上早自习! 我!想!去!机!房! OI才是我 ...
- visual studio 插件 resharper 使用指南
vs虽然号称是宇宙第一ide,但在智能提示和代码分析方面还是要略逊于jetbrains系列的ide.如果将jetbrains系列ide的智能提示和代码分析集成到vs中,对vs来说无异于如虎添翼.res ...
- Delphi Integer 转成单字节
整形不能超过256 b:=Byte(StrToInt(n)); var s: string; b: Byte; begin s := Edit1.Text; b := Byte(Str ...