Invader 02
Player クラスを作ろう
準備
- フォルダの中に
src
フォルダを作成し、その中にPlayer.js
ファイルを作成しましょう。 - フォルダの中に
assets
フォルダを作成し、ships.png
を入れましょう。 index.js
ファイルの一番上に下記のように書きましょう。globalThis.player = new Player()
ではグローバル変数にPlayer
クラスのインスタンスを設定しています。
import { Player } from "./src/Player.js"
...
// -------------------------------------------
// VARIABLES
// -------------------------------------------
// ゲームで使われる変数をここで設定
globalThis.player = new Player()
import
,export
とは?
import
とは、他のファイルからコードを読み込むことです。export
とは、他のファイルにコードを読み込めるようにすることです。import
,export
とは新しいjavascript
の書き方です。<script type="module">
にし、サーバを立ち上げると使用できます。
globalThis
とは?
globalThis
はwindow
と同じようなグローバルオブジェクトです。グローバルオブジェクトというのは、プログラムのどこからでも呼び出せる変数のことです。この変数(オブジェクト)に様々な値を割り当てることができます。
index.js
のポイント
animate()
関数の確認
animate()
関数は 1 フレームごとに実行される関数です。animate()
関数の宣言の上にglobalThis.frames = 0
でグローバル変数にframes
が設定されていることを確認しましょう。これが現在のフレーム数を格納します。- 関数の中に
globalThis.requestAnimationFrame(animate)
という行に注目をしましょう。これはanimate()
関数の中に同じanimate
関数を実行しています。つまり自分自身の中で自分を呼び出しています。これがゲームのループを表現しています。 animate()
の中にplayer
のupdate()
が呼び出され、1 フレームごとの変化を計算しています。- キーが押された時の挙動もここで管理をしています。
- 関数の最後の行に
globalThis.frames++
でframes
に 1 を足し、再び関数の上に戻って実行が始まります。
.addEventListener()
の確認
addEventListener
で設定されているkeyup
,keydown
がconsole.log()
で表示されるのを確認しましょう。a
,d
,SPACE
を押すとconsole.log()
に表示されます。
index.js
//-------------------------------------------
// IMPORTS
//-------------------------------------------
import { Player } from "./src/Player.js"
// -------------------------------------------
// SETUP CANVAS
// -------------------------------------------
// globalThisはどのファイルでも共有できるグローバル変数
// どのファイルでもその変数を使いたい時に追加する
// canvasとcは
globalThis.canvas = document.querySelector("canvas")
globalThis.ctx = globalThis.canvas.getContext("2d")
// ゲーム画面のサイズを固定
globalThis.canvas.width = 1024
globalThis.canvas.height = 576
// -------------------------------------------
// VARIABLES
// -------------------------------------------
// ゲームで使われる変数をここで設定
globalThis.player = new Player()
// 各キーの押されている(pressed)状態を保管する
globalThis.keys = {
a: {
pressed: false,
},
d: {
pressed: false,
},
space: {
pressed: false,
},
}
// フレームを記録
globalThis.frames = 0
// -------------------------------------------
// ANIMATE FUNCTION
// -------------------------------------------
// 1フレームごとに呼び出す関数・メソッドをここでまとめて呼ぶ
function animate() {
// GAME LOOP -------------------------
// ゲームをループする
globalThis.requestAnimationFrame(animate)
// 背景をblackに設定する(すべてを黒で塗りつぶす)
globalThis.ctx.fillStyle = "black"
globalThis.ctx.fillRect(
0,
0,
globalThis.canvas.width,
globalThis.canvas.height
)
// playerのupdateメソッドを呼び出す
globalThis.player.update()
// キーが押された場合の挙動 ------------------------------
// Aが押された時
if (globalThis.keys.a.pressed && globalThis.player.left >= 0) {
globalThis.player.velocity.x = -7
globalThis.player.rotation = -0.15
} else if (
// Dが押された時
globalThis.keys.d.pressed &&
globalThis.player.right <= globalThis.canvas.width
) {
globalThis.player.rotation = 0.15
globalThis.player.velocity.x = 7
} else {
// 何も押されていなければ各数値を0に戻す
globalThis.player.rotation = 0
globalThis.player.velocity.x = 0
}
// フレームを1増加
globalThis.frames++
}
// アニメーションファンクションを走らせる
animate()
// -------------------------------------------
// EVENT LISTENERS
// -------------------------------------------
// KEYDOWN -------------------------
globalThis.addEventListener("keydown", ({ key }) => {
switch (key) {
case "a": {
globalThis.keys.a.pressed = true
break
}
case "d": {
globalThis.keys.d.pressed = true
break
}
case " ": {
globalThis.keys.space.pressed = true
}
}
})
// KEYUP -------------------------
globalThis.addEventListener("keyup", ({ key }) => {
switch (key) {
case "a": {
globalThis.keys.a.pressed = false
break
}
case "d": {
globalThis.keys.d.pressed = false
break
}
case " ": {
globalThis.keys.space.pressed = false
break
}
}
})
Player.js
のポイント
export
の理解
Player.js
ファイルからは下記のPlayer
クラスがexport
されます。
export class Player {}
constructor
の理解
class
はあくまでも「青写真」です。クラスを実体化させるにはnew Player()
とクラスのインスタンスを作成しなければなりません。インスタンスを作成するときに呼び出されるのがconstructor
メソッドです。
new Image()
new Image()
クラスのインスタンスを作成し、その中にassets
フォルダに入れた画像(ship.png
)を読み込みます。image.onload
で画像のロードが終わった時に実行するコードを書きます。
class Player
のdraw()
メソッド
draw()
メソッドはcanvas
上でどのように描画するかを表すコードです。
class Player
のupdate()
メソッド
update()
メソッドは 1 フレームごとにどのように数値が変化するかを設定するメソッドです。
// -------------------------------------------
// PLAYER CLASS
// -------------------------------------------
// Player(飛行機)
export class Player {
// コンストラクタ:Playerインスタンスをはじめて作成される時に呼び出される
constructor() {
// velocityはフレームごとに動く距離
this.velocity = {
x: 0,
y: 0,
}
// 飛行機の傾きの数値
this.rotation = 0
// 透明度を設定(0になると透明)
this.opacity = 1
// new Image()で画像を入れる「箱」を用意し、assetsフォルダにあるship.pngをImageインスタンスの.src属性に設定する
const image = new Image()
image.src = "./assets/ship.png"
// Imageインスタンスに画像が読み込まれた(loadされた)後に呼ぶファンクション
image.onload = () => {
const scale = 1.15
this.image = image
// scale変数をもとにimageのサイズを変更する
this.width = image.width * scale
this.height = image.height * scale
// canvasの横真ん中と画面の下の方に置く
this.position = {
x: globalThis.canvas.width / 2 - this.width / 2,
y: globalThis.canvas.height - this.height - 20,
}
}
}
// DRAW METHOD -------------------------
// Classをどのように画面上に描かれるかを定義する
draw() {
// globalThis.ctx.fillStyle = "red"
// globalThis.ctx.fillRect(this.position.x, this.position.y, this.width, this.height)
// playerの傾き(rotate)を設定するためのコード。まずはもとの状態を保存する
globalThis.ctx.save()
// playerのopacity(透明度)を設定
globalThis.ctx.globalAlpha = this.opacity
// 回転の起点をplayerの真ん中の位置に設定する
globalThis.ctx.translate(
this.position.x + this.width / 2,
this.position.y + this.height / 2
)
// this.rotationで設定した傾き分を回転させる
globalThis.ctx.rotate(this.rotation)
// 回転の起点をplayerからもとにあった場所に戻す
globalThis.ctx.translate(
-this.position.x - this.width / 2,
-this.position.y - this.height / 2
)
// 画像を描く
globalThis.ctx.drawImage(
this.image,
this.position.x,
this.position.y,
this.width,
this.height
)
// 最初に保存したもとの状態に戻す
globalThis.ctx.restore()
}
// UPDATE METHOD -------------------------
// draw()を呼び、1フレームごとにどのように数値が変化するかを設定するメソッド
update() {
// this.imageがある場合のみ描く(draw)する
if (!this.image) return
// x位置に常にvelocity.xを足す
this.draw()
this.position.x += this.velocity.x
// positionの変化に合わせて数値を更新
// 真ん中の位置(x,y)
this.center = {
x: this.position.x + this.width / 2,
y: this.position.y + this.height / 2,
}
// top, bottom, left, rightの位置を設定・更新
this.top = this.position.y
this.bottom = this.position.y + this.height
this.left = this.position.x
this.right = this.position.x + this.width
// 透明度が1出ない場合はここでreturnをする(return以下のコードを実行しない)
if (this.opacity !== 1) return
}
}
確認
a
で飛行機が左に、d
を押すと飛行機が右に移動することを確認しましょう。- 飛行機が移動方向に合わせて傾くこと(rotation)を確認しましょう。