在上一篇《Chrome自带恐龙小游戏的源码研究(五)》中实现了眨眼睛的恐龙,这一篇主要研究恐龙的跳跃。

恐龙的跳跃

  游戏通过敲击键盘的Spacebar或者Up来实现恐龙的跳跃。先用一张图来表示整个跳跃的过程:

  1. 首先规定向下为正方向,即重力加速度(g)为正,起跳的速度(v)为负,恐龙距离画布上方的距离为yPos
  2. 每一帧动画中,速度都会与重力加速度相加得到新的速度,再用新的速度与yPos相加得到新的yPos,改变恐龙的位置为新的yPos,表现出来为yPos不断减小;
  3. 当恐龙升至最高点,此时速度为0,并且仍具有向下的重力加速度。
  4. 速度仍与重力加速度相加得到新的速度,此时速度方向向下,为正值,表现为yPos逐渐增加;
  5. 落地,并使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;
}
}

最终效果如下(SpacebarUp跳跃;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自带恐龙小游戏的源码研究(六)的更多相关文章

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

    在上一篇<Chrome自带恐龙小游戏的源码研究(六)>中研究了恐龙的跳跃过程,这一篇研究恐龙与障碍物之间的碰撞检测. 碰撞盒子 游戏中采用的是矩形(非旋转矩形)碰撞.这类碰撞优点是计算比较 ...

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

    目录 Chrome自带恐龙小游戏的源码研究(一)——绘制地面 Chrome自带恐龙小游戏的源码研究(二)——绘制云朵 Chrome自带恐龙小游戏的源码研究(三)——昼夜交替 Chrome自带恐龙小游戏 ...

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

    在上一篇<Chrome自带恐龙小游戏的源码研究(七)>中研究了恐龙与障碍物的碰撞检测,这一篇主要研究组成游戏的其它要素. 游戏分数记录 如图所示,分数及最高分记录显示在游戏界面的右上角,每 ...

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

    在上一篇<Chrome自带恐龙小游戏的源码研究(四)>中实现了障碍物的绘制及移动,从这一篇开始主要研究恐龙的绘制及一系列键盘动作的实现. 会眨眼睛的恐龙 在游戏开始前的待机界面,如果仔细观 ...

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

    在上一篇<Chrome自带恐龙小游戏的源码研究(三)>中实现了让游戏昼夜交替,这一篇主要研究如何绘制障碍物. 障碍物有两种:仙人掌和翼龙.仙人掌有大小两种类型,可以同时并列多个:翼龙按高. ...

  6. Chrome自带恐龙小游戏的源码研究(三)

    在上一篇<Chrome自带恐龙小游戏的源码研究(二)>中实现了云朵的绘制和移动,这一篇主要研究如何让游戏实现昼夜交替. 昼夜交替的效果主要是通过样式来完成,但改变样式的时机则由脚本控制. ...

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

    在上一篇<Chrome自带恐龙小游戏的源码研究(一)>中实现了地面的绘制和运动,这一篇主要研究云朵的绘制. 云朵的绘制通过Cloud构造函数完成.Cloud实现代码如下: Cloud.co ...

  8. WinFom中经典小游戏(含源码)

    最近整理了若干经典的小游戏,无聊时可以打发时间.程序本身不大,练手非常不错,主要是GDI编程,主界面地址如下图所示 源码下载方式 1,关注微信公众号:小特工作室(也可直接扫描签名处二维码) 2,发送: ...

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

    早就听说了github是世界最大的源码库,但自己却不是很懂,今天去研究了下,注册了一个帐号,然后在上面搜索了一下C# game,然后发现有许多的游戏. 随意地选择了一个,感觉比较简单,于是就下载了下来 ...

随机推荐

  1. 小M的作物 最大权闭合子图

    题目大意 bzoj 3438 两个田\(A,B\) \(n\le 1000\)种作物的种子 第\(i\)个种子,种\(A\)价值\(a[i]\),种\(B\)价值\(b[i]\) 再给出\(m\)个子 ...

  2. SPOJ HIGH Highways

    In some countries building highways takes a lot of time... Maybe that's because there are many possi ...

  3. AtCoder Regular Contest 075 C D E (暂时)

    C - Bugged 题意 给\(n\)个数,找其中的一个子集,使得其和最大,且不是\(10\)的整数倍. 思路 先对\(n\)个数求和, 如果本身即不被\(10\)整除,则即为答案. 否则,如果本身 ...

  4. Error-invalid project description(转)

    导入android工程时有时会出现下面错误: 按照提示,说是当前的工作空间内已经有同名的工程了,但实际是没有的. 多次碰到这种问题后,无意间找到解决办法.导入工程时,不要选择Android,而是选择G ...

  5. Selenium2+python自动化(学习笔记3)

    1.加载firefox配置 参考代码: # coding=utf-8from selenium import webdriver# 配置文件地址,打开Firefox点右上角设置--帮助--故障排除信息 ...

  6. python print的参数介绍

    参考print的官方文档 print(...) print(value, ..., sep=' ', end='\n', file=sys.stdout, flush=False) Prints th ...

  7. C#图解教程学习笔记——委托

    一.委托概述委托和类一样,是用户自定义类型,也是引用类型.但类表示的是数据和方法的集合,而委托持有一个或多个方法,以及一系列预定义操作. 可以通过以下操作步骤来使用委托:(1)声明一个委托类型.委托声 ...

  8. unittest框架及自动化测试

    之前在公司做过自动化测试的知识分享,现在把它记录下来.   •一.如何更好的编写测试用例 •1.模块化:将一些基础的.共有的步骤代码独立为单独的模块,使用时再调用.好处:可以使代码复用,减少代码编写, ...

  9. (1)jquery基本用法

    引入jquery 本地引用 <script src="jquery-3.2.1.js"></script> 网络引用 谷歌CDN <script sr ...

  10. Unity -- 材质-Material和预设体-Prefabs

    材质(Materials)用来把网格(Mesh)或粒子渲染器(Particle Renderers)贴到游戏对象上.他们在定义对象怎么被显示发挥重要组成部分.材质包括用于呈现网状或颗粒着色器的参考,所 ...