在上一篇《Chrome自帶恐龍小游戲的源碼研究(五)》中實現了眨眼睛的恐龍,這一篇主要研究恐龍的跳躍。
恐龍的跳躍
游戲通過敲擊鍵盤的Spacebar
或者Up
來實現恐龍的跳躍。先用一張圖來表示整個跳躍的過程:
- 首先規定向下為正方向,即重力加速度(g)為正,起跳的速度(v)為負,恐龍距離畫布上方的距離為yPos;
- 每一幀動畫中,速度都會與重力加速度相加得到新的速度,再用新的速度與yPos相加得到新的yPos,改變恐龍的位置為新的yPos,表現出來為yPos不斷減小;
- 當恐龍升至最高點,此時速度為0,並且仍具有向下的重力加速度。
- 速度仍與重力加速度相加得到新的速度,此時速度方向向下,為正值,表現為yPos逐漸增加;
- 落地,並使yPos不超過地面的高度,將速度重置為0,更新狀態jumping為false。
下面通過代碼來實現。首先注冊鍵盤事件:
1 document.addEventListener('keydown',onKeyDown); 2 document.addEventListener('keyup',onKeyUp);
1 function onKeyDown(e) { 2 if(keycode.JUMP[e.keyCode]) { 3 if(!trex.jumping) { 4 trex.startJump(6); 5 } 6 } 7 }
按下跳躍鍵后,執行startJump方法:

1 startJump: function(speed) { 2 if (!this.jumping) { 3 //切換到jump狀態 4 this.update(0, Trex.status.JUMPING); 5 //設置跳躍速度 6 this.jumpVelocity = this.config.INIITAL_JUMP_VELOCITY - (speed / 10); 7 this.jumping = true; 8 this.reachedMinHeight = false; 9 } 10 }
之后在每次GameLoop中更新狀態:

1 if (trex.jumping) { 2 ctx.clearRect(0, 0, 600, 150); 3 trex.updateJump(deltaTime); 4 }

1 updateJump: function(deltaTime) { 2 //幀切換速率 3 var msPerFrame = Trex.animFrames[this.status].msPerFrame; 4 //經過的幀數 5 var framesElapsed = deltaTime / msPerFrame; 6 //更新y軸坐標 7 this.yPos += Math.round(this.jumpVelocity * framesElapsed); 8 //由於速度受重力影響,需要對速度進行修正 9 this.jumpVelocity += this.config.GRAVITY * framesElapsed; 10 11 //達到最小跳躍高度 12 if (this.yPos < this.minJumpHeight) { 13 this.reachedMinHeight = true; 14 } 15 //達到最大高度后停止跳躍 16 if (this.yPos < this.config.MAX_JUMP_HEIGHT) { 17 this.endJump(); 18 } 19 if (this.yPos > this.groundYPos) { 20 this.reset(); 21 this.jumpCount++; 22 } 23 this.update(deltaTime); 24 }, 25 26 update: function(deltaTime, opt_status) { 27 this.timer += deltaTime; 28 29 if (opt_status) { 30 this.status = opt_status; 31 this.currentFrame = 0; 32 //得到對應狀態的幀率 e.g. WAITING 1000ms / 3fps = 333ms/fps 33 this.msPerFrame = Trex.animFrames[opt_status].msPerFrame; 34 //對應狀態的動畫幀 e.g. WAITING [44,0] 35 this.currentAnimFrames = Trex.animFrames[opt_status].frames; 36 37 if (opt_status === Trex.status.WAITING) { 38 //開始計時 39 this.animStartTime = getTimeStamp(); 40 //設置延時 41 this.setBlinkDelay(); 42 } 43 } 44 45 //計時器超過一幀的運行時間,切換到下一幀 46 if (this.timer >= this.msPerFrame) { 47 this.currentFrame = this.currentFrame === this.currentAnimFrames.length - 1 ? 0 : this.currentFrame + 1; 48 this.timer = 0; 49 } 50 51 //待機狀態 52 if (this.status === Trex.status.WAITING) { 53 //執行眨眼動作 54 this.blink(getTimeStamp()); 55 } else { 56 this.draw(this.currentAnimFrames[this.currentFrame], 0); 57 } 58 }
這樣就實現了跳躍的過程。
輕跳
如果持續按住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
鍵,恐龍會加速下降。
1 function onKeyDown(e) { 2 //...... 3 if(keycode.DUCK[e.keyCode]) {//Down 4 if(trex.jumping) { 5 trex.setSpeedDrop(); //加速下降 6 } 7 } 8 }
松開鍵位時取消加速:
1 function onKeyUp(e) { 2 //...... 3 if (keycode.DUCK[e.keyCode]) { 4 trex.speedDrop = false; 5 } 6 }
在構造函數中添加setSpeedDrop方法:
1 setSpeedDrop: function() { 2 this.speedDrop = true; 3 this.jumpVelocity = 1; //將速度設置為1,正方向(向下為正方向) 4 }
還需要對updateJump方法做一些更新:

1 updateJump:function (deltaTime) { 2 //...... 3 4 //更新y軸坐標 5 if (this.speedDrop) { 6 //SPEED_DROP_COEFFICIENT為加速倍數,初始設定為3 7 this.yPos += Math.round(this.jumpVelocity * this.config.SPEED_DROP_COEFFICIENT * framesElapsed); 8 } else { 9 this.yPos += Math.round(this.jumpVelocity * framesElapsed); 10 } 11 12 13 //達到最小跳躍高度 14 //speedDrop也能觸發reachedMinHeight 15 if (this.yPos < this.minJumpHeight || this.speedDrop) { 16 this.reachedMinHeight = true; 17 } 18 19 //達到最大高度后停止跳躍 20 //speedDrop也能觸發endJump 21 if (this.yPos < this.config.MAX_JUMP_HEIGHT || this.speedDrop) { 22 this.endJump(); 23 } 24 //...... 25 26 }
效果如下圖所示,在跳躍過程中按住Down
,可以發現下落速度比平時快:
閃避
在地面上按住Down
鍵,恐龍會進入閃避狀態。首先還是從keydown方法入手:
1 if (keycode.DUCK[e.keyCode]) { 2 e.preventDefault(); 3 if (trex.jumping) { 4 trex.setSpeedDrop(); 5 } else if (!trex.jumping && !trex.ducking) { 6 trex.setDuck(true); //閃避 7 } 8 }
keyup方法取消閃避:
1 function onKeyUp(e) { 2 if (keycode.JUMP[e.keyCode]) { 3 trex.endJump(); 4 } 5 if (keycode.DUCK[e.keyCode]) { 6 trex.speedDrop = false; 7 trex.setDuck(false); //取消閃避 8 } 9 }
setDuck方法:
1 setDuck: function(isDucking) { 2 if (isDucking && this.status !== Trex.status.DUCKING) { 3 this.update(0, Trex.status.DUCKING); 4 this.ducking = true; 5 } else if (this.status === Trex.status.DUCKING) { 6 this.update(0, Trex.status.RUNNING); 7 this.ducking = false; 8 } 9 }
最終效果如下(Spacebar
或Up
跳躍;Down
快速下降/閃避):