レーザーと爆発のエフェクトをつかってゲームをつくる(1) の続きです。今回は自機クラスと敵クラスを定義します。
Contents
Playerクラスの定義
自機の状態を更新し描画するためにPlayerクラスを定義します。
コンストラクタ
最初にコンストラクタだけ示します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
class Player { constructor(){ this.Size = PLAYER_SIZE; // 自機の中心座標 this.CenterX = CANVAS_WIDTH / 2; this.CenterY = CANVAS_HEIGHT - PLAYER_SIZE * 2; // 移動キーは押下されているか? this.MoveUp = false; this.MoveDown = false; this.MoveLeft = false; this.MoveRight = false; // 自機の回転角と現在どちら向きに回転中か? this.Angle = 0; this.RotateRight = true; // 現在レーザーは発射できるか?(連射制限) this.AllowShot = true; this.Life = MAX_PLAYER_LIFE; this.IsDead = false; this.IsMuteki = false; // 敵弾に触れたらLifeを減らすとともに一時的に無敵状態にする this.Score = 0; this.Gameovered = false; this.UpdateCount = 0; } } |
更新処理
更新時におこなわれる処理を示します。
キーが押下されていたら自機をその方向に移動させますが、canvasの外に出てしまう場合はcanvas内に収まるように移動させます。また時間の経過とともに自機を左右にゆらゆら回転させます。回転角が30度を超えたら反対方向を反転させます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
class Player { Update(){ this.UpdateCount++; // キーが押下されていたらその方向に移動させる if(this.MoveLeft) this.CenterX -= PLAYER_SPEED; if(this.MoveRight) this.CenterX += PLAYER_SPEED; if(this.MoveUp) this.CenterY -= PLAYER_SPEED; if(this.MoveDown) this.CenterY += PLAYER_SPEED; // canvasの外に出てしまう場合はcanvas内に収まるように移動させる if(this.CenterX < this.Size / 2) this.CenterX = this.Size / 2; if(this.CenterX > CANVAS_WIDTH - this.Size / 2) this.CenterX = CANVAS_WIDTH - this.Size / 2; if(this.CenterY < this.Size / 2) this.CenterY = this.Size / 2; if(this.CenterY > CANVAS_HEIGHT - this.Size / 2) this.CenterY = CANVAS_HEIGHT - this.Size / 2; // 左右にゆらゆら回転させる this.Angle += this.RotateRight ? 0.01 : -0.01; this.RotateRight = this.Angle > Math.PI / 6 ? false : this.RotateRight; this.RotateRight = this.Angle < -Math.PI / 6 ? true : this.RotateRight; } } |
描画の処理
描画の処理を示します。
自機死亡の場合は描画しません。また無敵状態のときは点滅させます。UpdateCountを4で割ったときの剰余が2より小さいときは描画しません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
class Player { Draw(){ if(player.IsDead) return; if(this.IsMuteki && this.UpdateCount % 4 < 2) return; // 回転した状態で描画する ctx.save(); ctx.translate(this.CenterX, this.CenterY); ctx.rotate(this.Angle); ctx.translate(-this.CenterX, -this.CenterY); ctx.drawImage(playerImage, this.CenterX - this.Size / 2, this.CenterY - this.Size / 2, this.Size, this.Size); ctx.restore(); } } |
レーザーの発射
レーザーを発射する処理を示します。中心が(150, 150)になるようにキャラクタを描画したとき、レーザーの発射地点(x0, y0)とその通過点(x1, y1)はleftsとrightsのようになります。これを(150, 150)を中心にしてthis.Angleだけ回転させます。これでレーザーの発射地点がわかります。
またMath.atan2関数にy1 – y0 と x1 – x0 を渡せば回転していない状態の発射角度がわかります。回転している状態での発射角度であればMath.atan2関数が返した値にthis.Angleを加えることで求めることができます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
class Player { Shot(){ // (x, y)を(150, 150)を中心に rad だけ回転させたときのx座標を求める function getConvertedX(x, y, rad, addX){ return (x - 150) * Math.cos(rad) - (y - 150) * Math.sin(rad) + addX; } // (x, y)を(150, 150)を中心に rad だけ回転させたときのy座標を求める function getConvertedY(x, y, rad, addY){ return (x - 150) * Math.sin(rad) + (y - 150) * Math.cos(rad) + addY; } if(!this.AllowShot || this.IsDead) return; this.AllowShot = false; setTimeout(() => { this.AllowShot = true; }, 240); const lefts = [ {x0: 129, y0: 145, x1: 97, y1: 74}, {x0: 129, y0: 145, x1: 65, y1: 103}, {x0: 129, y0: 145, x1: 35, y1: 145}, ] for(let i = 0; i < 3; i++){ const x = getConvertedX(lefts[i].x0, lefts[i].y0, this.Angle, this.CenterX); const y = getConvertedY(lefts[i].x0, lefts[i].y0, this.Angle, this.CenterY); const angle = Math.atan2(lefts[i].y1 - lefts[i].y0, lefts[i].x1 - lefts[i].x0) + this.Angle; lasers.push(new Laser(x, y, angle, 0)); } const rights = [ {x0: 173, y0: 138, x1: 200, y1: 80}, {x0: 173, y0: 138, x1: 235, y1: 112}, {x0: 173, y0: 138, x1: 229, y1: 169}, ] for(let i = 0; i < 3; i++){ const x = getConvertedX(rights[i].x0, rights[i].y0, this.Angle, this.CenterX); const y = getConvertedY(rights[i].x0, rights[i].y0, this.Angle, this.CenterY); const angle = Math.atan2(rights[i].y1 - rights[i].y0, rights[i].x1 - rights[i].x0) + this.Angle; lasers.push(new Laser(x, y, angle, 1)); } sound.PlayShotSound(); } } |
敵クラスの定義
敵の状態を更新して描画するために敵クラスを定義します。敵は2種類生成します。ブログ1000回更新記念企画で定義したEnemy0クラスとEnemy1クラスを少しだけ手直ししたものを使います。
EnemyBaseクラスの定義
EnemyBaseクラスはEnemy0クラスとEnemy1クラスの基底クラスです。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class EnemyBase{ constructor(delay){ this.CenterX = 0; this.CenterY = 0; this.Size = ENEMY_SIZE; this.Time = -delay; this.IsDead = false; } Update(){ } Draw(){ } } |
Enemy0クラスの定義
Enemy0クラスは上から急降下してきてそのあと上方に撤収するタイプの敵を動作させるためのものです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
class Enemy0 extends EnemyBase{ constructor(initX, delay){ super(delay); this.InitX = initX; } Update(){ const max = CANVAS_HEIGHT - 150; // 下から150pxまで接近しそのあと撤収 const vx = this.InitX < CANVAS_WIDTH / 2 ? 2 : -2; // 1更新あたりのXY方向の移動量 const vy = 3; this.Time++; this.CenterX = this.InitX + this.Time * vx; if(this.Time < max / vy) // 下に移動中 this.CenterY = this.Time * vy; else{ // 上に撤収中 this.Y = max - (this.Time * vy - max); if(this.CenterY < -this.Size) // canvasの外に待避したので死亡フラグをセットする this.IsDead = true; } } Draw(){ if(!this.IsDead) ctx.drawImage(enemyImage0, this.CenterX - this.Size / 2, this.CenterY - this.Size / 2, this.Size, this.Size); } } |
Enemy1クラスの定義
Enemy1クラスはジグザグ移動するタイプの敵を動作させるためのものです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
class Enemy1 extends EnemyBase{ constructor(minX, delay){ super(delay); this.minX = minX; } Update(){ // 幅200でジグザグ移動 // this.minXが一番左のX座標 const moveWidth = 200; const min = this.minX; const vx = 3; const vy = 1; this.Time++; const x = Math.abs(this.Time * vx); if(Math.floor(x / moveWidth) % 2 == 0) this.CenterX = x % moveWidth + min; else this.CenterX = moveWidth - x % moveWidth + min; this.CenterY = this.Time * vy; if(this.CenterY > CANVAS_HEIGHT) this.IsDead = true; } Draw(){ if(!this.IsDead) ctx.drawImage(enemyImage1, this.CenterX - this.Size / 2, this.CenterY - this.Size / 2, this.Size, this.Size); } } |
EnemyBulletクラスの定義
敵の弾丸を更新して描画するためにEnemyBulletクラスを定義します。
コンストラクタに初期の中心座標と移動量を渡し、更新時に移動量だけ中心座標を移動させます。移動することでcanvasの外に出てしまった場合は死亡フラグをセットします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
class EnemyBullet{ constructor(x, y, vx, vy){ this.CenterX = x; this.CenterY = y; this.VX = vx; this.VY = vy; this.IsDead = false; this.Size = ENEMY_BULLET_SIZE; } Update(){ this.CenterX += this.VX; this.CenterY += this.VY; const radius = this.Size / 2; if(this.CenterX > CANVAS_WIDTH + radius || this.CenterX < - radius || this.CenterY > CANVAS_HEIGHT + radius || this.CenterY < - radius) this.IsDead = true; } Draw(){ if(!this.IsDead) ctx.drawImage(enemyBullet, this.CenterX - this.Size / 2, this.CenterY - this.Size / 2, this.Size, this.Size); } } |