ゲームエンジンやライブラリを使わずに、HTMLとJavaScriptだけでゲームを作ります。
以下のような人のためにあります。
ゲームを作ってみたいけど、何をどうしていいか分からない。
単純なゲームを作りたいだけなのに、UnityとかUnreal Engineは面倒くさい。
ある程度プログラミングの知識がある人を対象にしています。
動作デモ
これから、マリオブラザーズのようなジャンプ・アクションゲームを作っていきたいと思いますが、今後似たようなゲームを作るときに、すぐに始められるようにテンプレートを用意しておきたいと思います。
用意したのは以下のデモです。キャラクターがフィールドを左右に走って軽快にジャンプします。
j … 左に移動, l … 右に移動, c … ジャンプ
ジョイパッドでも操作できます。
ソースコード
ソースコードのファイルの構成は以下のとおりです。
.
├── Chara.js
├── SpriteSheet.js
├── UserInput.js
├── World.js
├── assets
│ ├── chara_spritesheet.png
│ ├── stages.json
│ ├── style.css
│ └── tilesheet.png
├── index.html
└── index.js
先に読んでおく記事
ドット絵キャラクターをアニメーションさせながら動かす方法は、下記の記事を読んでください。
この記事で解説する内容
今回用意したゲームのテンプレートのポイントになる部分を解説します。
- スプライトシートを複数種類持つ
- ジャンプ動作の実現方法
スプライトシートを複数種類持つ
これまで作成したゲームは、8×8ドットのキャラクターのみで構成され、背景となるマップデータもひとマスが8×8のみでした。アニメーションのパターンも少なく、背景のタイルパターン含めて、スプライトシートは1種類で事足りていました。
今回のゲームはマリオブラザーズを想定しており、ドット絵のキャラクターは16×24、16×16と2種類あり、背景は8×8のマスで構成されます。また、キャラクターのアニメーションのパターンも多くなるので、キャラクターごとに別々のスプライトシートを持つようにした方が管理がしやすくなります。
そのための仕込みはすでに以前の記事で行なっています。下記記事で解説したSpriteSheet.jsがそれです。
今回のゲーム・テンプレートでは、スプライトシートは2種類用意しています。ひとつは、画面上を動き回るキャラクターのアニメーション用、もうひとつはブロックなどの背景の配置用です。
├── assets
│ ├── chara_spritesheet.png
│ └── tilesheet.png
それぞれのスプライトシートを扱えるように、JavaScrip内では下記のように読み込みます。
const CHARA_WIDTH = 16;
const CHARA_HEIGHT = 24;
・・・
const spsheet = new SpriteSheet(CHARA_WIDTH, CHARA_HEIGHT, "./assets/chara_spritesheet.png");
const spsheet = new SpriteSheet(8, 8, "./assets/tilesheet.png");
スプライトシートはキャラクターを描画する際に使用します。Chara.jsでは下記です。
draw() {
drawSprite(spsheet, this.sprite, this.x, this.y, this.flip);
}
drawSprite()はindex.js内で定義された関数です。以前との変化点は、第一引数にspsheetを与えられるようになっています。スプライトの描画はdocumentから作成したcontext情報が必要ですが、これはindex.jsで生成管理するようにしています。
drawSprite()関数の定義は下記のとおりです。
export function drawSprite(spsheet, sprite_no, x, y, flip) {
spsheet.drawSprite(context_bg, sprite_no, x, y, flip);
}
ジャンプ動作の実現方法
今回のテンプレートのポイントはキャラクターのジャンプアクションです。ジャンプが成り立つためには、地面との当たり判定もキモになってきます。また、ジャンプ中のアニメーション動作への切り替えも必要です。
関連するパラメータは下図のとおりです。
落下を実装する(下方向への加速度計算)
画面上のキャラクターには常に重力が働いています。といっても擬似的なもので、ちゃんとした物理演算をやるわけではありません。
画面の上から下の方向で、キャラクターに対して一定の加速度が働くようにします。今回の設計のポイントは以下のとおりです。
- Charaオブジェクトは、自身の座標(x, y) の他に移動速度のパラメータを持つ (vx, vy)
- オブジェクトに対して落下にかかる加速度の処理は、外の世界 World クラスで行う
World.js内のWorldクラス内の実装は下記の部分です。
・・・
const GRAVITY = 0.2;
・・・
do_fall(o) {
・・・
o.vy += GRAVITY;
o.y = Math.round(o.y+o.vy);
}
update() {
・・・
this.do_fall(this.player);
・・・・
}
Worldクラスのupdate()関数は一定周期で呼ばれます。一定周期ごとにdo_fall()関数が呼ばれ、引数で渡されたオブジェクトの vy (速度)y 座標に加算することで、落下を実現します。この際に、毎サイクルvyに定数GRAVITYを加えることで、落下速度がどんどの加速されるようになります。
加速度は定数GRAVITYで0.2としています。この値は実際の動作を見て調整した結果です。実際のゲーム制作では自分の好みで調整してください。
下方向への加速だけだと、キャラクターはすぐに画面の外に消えてしまいますが、ブロックを置いて落下できない場所を用意しておきます。落下できないと判断したときに、vy をゼロにすることで落下させないようにします。do_fall() の実装の前半部分でそれを行っています。
can_not_go_through(o, dy) {
return !this.canGoThrough(o.x+2, o.y + dy) || !this.canGoThrough(o.x+o.w-2, o.y + dy);
}
do_fall(o) {
if (o.vy == 0 && this.can_not_go_through(o, o.h)) {
return;
}
if (o.vy > 0 && this.can_not_go_through(o, o.h + o.vy)) {
o.vy = 0;
return;
}
・・・
}
ジャンプを実装する(上方向への加速度計算)
前述の通り、キャラクターには常に下方向への加速度がかかっている状態です。ジャンプの実行は単純で、上方向の加速度を加えるだけです。
Chara.jsの下記の実装です。
const JUMP_VY = -5;
・・・
do_jump() {
・・・
this.vy = JUMP_VY;
this.change_state(State.JUMP);
}
定数JUMP_VYが上方向にかかる加速度です。-5 としています。この数値もGRAVITYと同様、実際の動作を見て調整した結果です。
なお、加速度GRAVITYはCharaオブジェクトの外で処理しているのに対し、JUMP_VYはCharaオブジェクト自身が処理しています。これはなぜかというと、GRAVITYはゲーム上のどの物体に対しても同一にかかるもので、JUMP_VYの方はゲームの登場キャラクタによっては数値が異なるように設定したいからです。
ジャンプの状態切り替え
ジャンプの処理と状態切り替えの流れは下記図に示すとおりです。
ジャンプの入力トリガーは、このデモでは’c’キー(Aボタン)です。’c’キーが押されたら、vyにJUMP_VYを代入して、stateをJUMP_UPに切り替えます。
JUMP_VYは-5 なので、次のフレームは -5 + 0.2 の座標移動がされてキャラクターは上に上がります。フレームが進むごとにvyは +0.2加算されるので、上がる速度はどんどん落ちてきます。
vy > 0 になった時は落下しているので、state をJUMP_DOWNにします。stateがJUMP_DOWN時に、vy == 0 になったら、stateをSTOPにします。
ジャンプの頂点のときにも vy == 0 になる瞬間があるので、JUMP_DOWN状態を設けずに、JUMP_UPだけで着地を判断することはできません。空中でSTOP状態に一瞬切り替わってしまうからです。
なお、このデモの実装は、あくまでも一例です。絶対こうでなくてもいけないというものではありません。参考にしてみて、自分の理解しやすいように実装するのが良いと思います。
まとめ
さて、この記事では、マリオブラザーズのようなジャンプアクションゲームを作るためのテンプレート(動くデモ)を用意しました。
主なポイントとして、ひとつは、スプライトシートを複数持てるようにしました。これによって、キャラクターのサイズと背景のタイルのサイズを一緒にしなくてもよくデータの管理しやすくなりました。
もうひとつは、ジャンプアクションを実現したことです。考え方も実装もシンプルだと思います。それでいて実際の動きは十分にマリオっぽいです。
次回は、キャラクターの動きを拡充するとともに、マリオの主要なゲームギミックである、床の突き上げを実現したいと思います。
コメント