2Dのアクションゲームにおける自機のジャンププログラムを作る。ジャンプは固定長ジャンプと可変長ジャンプの2種類。固定長ジャンプはどんなボタンの押し方をしても、ジャンプ高に違いがない。可変長ジャンプはボタンを押す長さでジャンプ高が変化する。
固定長ジャンプ
以下のスクリプトはPCの矢印キーの左右で横移動。上で固定長ジャンプする。
まず、猫画像を読み込み表示する簡素なCatクラス。
/**
* 猫画像を表示(cat.js)
*/
export class Cat {
constructor(parent = undefined, x = 0, y = 0) {
if (parent !== undefined) {
this._parent = parent;
}
this._x = x;
this._y = y;
this._rotation = 0;
this._alpha = 1.0;
this._img;
this._width;
this._height;
this._init();
}
//////////////////////////////////
// Private and protected
//////////////////////////////////
_init() {
if (this._parent !== undefined) {
this._ctx = this._parent;
}
this._img = new Image();
this._img.src = "./cat.png";
this._img.onload = () => {
this._width = this._img.width;
this._height = this._img.height;
};
this.draw();
}
//////////////////////////////////
// Public
//////////////////////////////////
draw() {
if (!this._img.complete) {
return;
}
// 現在の描画状態を保存
this._ctx.save();
// コンテキストの座標を変更し、キャンバス中央に移動
this._ctx.translate(this._x, this._y);
// コンテキストの角度をラジアン値で指定
this._ctx.rotate(this._rotation);
this._ctx.globalAlpha = this._alpha;
this._ctx.drawImage(this._img, -(this._img.width / 2), -(this._img.height / 2));
// save()で保存した描画状態を復元
this._ctx.restore();
}
//////////////////////////////////
// Getters/Setters
//////////////////////////////////
get x() {
return this._x;
}
set x(x) {
this._x = x;
}
get y() {
return this._y;
}
set y(y) {
this._y = y;
}
get width() {
return this._width;
}
get height() {
return this._height;
}
}
次に、固定長ジャンプを担当するfixed_length_jumpクラス。
import { Cat } from "./cat.js";
/**
* 固定長ジャンプクラス(fixed_length_jump.js)
*/
export class FixedLengthJump {
constructor() {
this.cvs = document.getElementById('canvas');
this.ctx = this.cvs.getContext('2d');
this.cat;
this.CAT_HEIHGT = 43.5;
this.jumpSpeed = -40; // ジャンプの初速度
this.vx = 0;
this.vy = 0;
this.ay = 1.5;
this.isJump = false;
this.cvs.setAttribute('tabindex', 0); // tabindexの指定でキーイベントを受け取れるようにする
this.cvs.addEventListener('keydown', this._onKeyDown.bind(this));
this.cvs.addEventListener('keyup', this._onKeyUp.bind(this));
// タイマー関連
this.animID;
this.isAnim = 0;
this.FPS = 60;
this.frame = 0;
this.startTime;
this.nowTime;
this._init();
}
//////////////////////////////////
// Private and protected
//////////////////////////////////
_init() {
this.cvs.style.backgroundColor = "#eeeeee";
this.cat = new Cat(this.ctx, this.cvs.width / 2, this.cvs.height - this.CAT_HEIHGT);
this.startTime = performance.now();
this._mainLoop();
}
_mainLoop() {
this.nowTime = performance.now();
let elapsedTime = this.nowTime - this.startTime;
let idealTime = this.frame * (1000 / this.FPS);
if (idealTime < elapsedTime) {
this.ctx.clearRect(0, 0, this.cvs.width, this.cvs.height);
// ①ジャンプ処理
if (this.isJump) {
// y座標の更新
this.vy += this.ay;
this.cat.y += this.vy;
// x座標の更新
this.cat.x += this.vx;
// ②着地判定
if (this.cat.y + this.CAT_HEIHGT >= this.cvs.height) {
this.isJump = false;
this.cat.y = this.cvs.height - this.CAT_HEIHGT;
this.vx = 0;
}
} else {
this.cat.x += this.vx;
}
this.cat.draw();
this.frame++;
if (elapsedTime >= 1000) {
this.startTime = this.nowTime;
this.frame = 0;
}
}
this.animID = requestAnimationFrame(this._mainLoop.bind(this));
}
//////////////////////////////////
// Handlers
//////////////////////////////////
_onKeyDown(e) {
if (this.isJump) { return; }
switch(e.keyCode) {
case 37: // 左矢印キー
this.vx = -10;
break;
case 39: // 右矢印キー
this.vx = 10;
break;
case 38: // 上矢印キー
this.isJump = true;
this.vy = this.jumpSpeed;
break;
}
}
_onKeyUp(e) {
switch(e.keyCode) {
case 37: // 左矢印キー
case 39: // 右矢印キー
if (!this.isJump) {
this.vx = 0;
}
break;
}
}
}
requestAnimationFrameを使ったゲームループの実装は、「【JavaScript】requestAnimationFrameでゲームループを作る」を参照。
上キーを押すとフラグ変数のthis.isJumpにtrueが入り、this.vyにジャンプ初速度を代入。メインループの中で①this.vyに加速度this.ayを加算し、その値を猫画像のyに代入する。最初は-の値が、加速度(+の値)が加算されていくことで負数から正数になる。そこから落下が始まり、②猫画像が地面に着地したらthis.isJumpにfalseを代入してジャンプ処理終了。
可変長ジャンプ
以下は可変長ジャンプするスクリプト。操作は固定長ジャンプのものと同じ。
以下が可変長ジャンプを担当するvariable_length_jumpクラス。
import { Cat } from "./cat.js";
/**
* 可変長ジャンプクラス(variable_length_jump.js)
*/
export class VariableLengthJump {
constructor() {
this.cvs = document.getElementById('canvas');
this.ctx = this.cvs.getContext('2d');
this.cat;
this.CAT_HEIHGT = 43.5;
this.jumpSpeed = -40; // ジャンプの初速度
this.vx = 0;
this.vy = 0;
this.ay = 1.5;
this.isJump = false;
this.cvs.setAttribute('tabindex', 0); // tabindexの指定でキーイベントを受け取れるようにする
this.cvs.addEventListener('keydown', this._onKeyDown.bind(this));
this.cvs.addEventListener('keyup', this._onKeyUp.bind(this));
// タイマー関連
this.animID;
this.isAnim = 0;
this.FPS = 60;
this.frame = 0;
this.startTime;
this.nowTime;
this._init();
}
//////////////////////////////////
// Private and protected
//////////////////////////////////
_init() {
this.cvs.style.backgroundColor = "#eeeeee";
this.cat = new Cat(this.ctx, this.cvs.width / 2, this.cvs.height - this.CAT_HEIHGT);
this.startTime = performance.now();
this._mainLoop();
}
_mainLoop() {
this.nowTime = performance.now();
let elapsedTime = this.nowTime - this.startTime;
let idealTime = this.frame * (1000 / this.FPS);
if (idealTime < elapsedTime) {
this.ctx.clearRect(0, 0, this.cvs.width, this.cvs.height);
// ジャンプ処理
if (this.isJump) {
// y座標の更新
this.vy += this.ay;
this.cat.y += this.vy;
// x座標の更新
this.cat.x += this.vx;
// 着地判定
if (this.cat.y + this.CAT_HEIHGT >= this.cvs.height) {
this.isJump = false;
this.cat.y = this.cvs.height - this.CAT_HEIHGT;
this.vx = 0;
}
} else {
this.cat.x += this.vx;
}
this.cat.draw();
this.frame++;
if (elapsedTime >= 1000) {
this.startTime = this.nowTime;
this.frame = 0;
}
}
this.animID = requestAnimationFrame(this._mainLoop.bind(this));
}
//////////////////////////////////
// Handlers
//////////////////////////////////
_onKeyDown(e) {
if (this.isJump) { return; }
switch(e.keyCode) {
case 37: // 左矢印キー
this.vx = -10;
break;
case 39: // 右矢印キー
this.vx = 10;
break;
case 38: // 上矢印キー
this.isJump = true;
this.vy = this.jumpSpeed;
//this.ay = 1.5;
break;
}
}
_onKeyUp(e) {
switch(e.keyCode) {
case 37: // 左矢印キー
case 39: // 右矢印キー
if (!this.isJump) {
this.vx = 0;
}
break;
case 38: // 上矢印キー
// ①ジャンプが上昇中のみ実行
if (0 > this.vy) {
this.vy = 0;
}
break;
}
}
}
固定長ジャンプのコードとの違いは110行目の部分だけ。①ジャンプの上昇中に上キーが離されたらthis.vyに0を代入してすぐに落下させている。
参考図書
リンク
コメント