JavaScript(React)でのアクションゲーム制作の手順

2019年4月7日日曜日

プログラミング

こんにちは、財前です。

この記事では、JavaScriptでのゲーム作りについて書いてみたいと思います。

自分は今、ReactというJavaScriptのライブラリを使っているのですが、Reactだけの話だと、使っている人がそこまで多くないと思うので、できる限りJavaScriptで動きのあるゲームを作る際に全般的に言えることを書いていけたらと思います。

何故そんなテーマで記事を書こうと思ったのかというと、趣味で作っていたブラウザアプリを、つい昨日リリースしたからです。

Lingual Ninja!

Game Screen


忍者の画像が動いて、ちょっとした謎を解いていくシンプルなゲームです。
上記のリンクから遊んで下されば分かると思いますが、まぁ大したゲームではありません 笑


ただ、これから素のJavaScriptやReactなどのフロントエンドフレームワーク(ライブラリ)を用いて、ゲームを作りたい方等の参考になれば幸いです。

僕は仕事で使うための練習も兼ねてReactを使いましたが、ゲーム用のJavaScriptライブラリも多くあるので、自分に合ったものを探して使ってみるのもいいかも知れません。



ちなみに私は、仕事では人事系のWebサービスを作らせて頂いており、ゲーム作りに関しては全然プロではありません。
ですので、ゲーム畑でバリバリ働かれている方に向けた記事というよりは、Webプログラミングは多少やったことがあって、趣味や自己研鑽も含めてゲーム作りにも手を出してみたい方向けの内容になると思います。

要素を動かす基本的な仕組み

要素を動かす基本的な仕組み

前提として、上記のゲームではcanvasなどは使っておらず、DOM要素の位置を動かすことで動きを出しています。

JavaScriptからのDOM操作の概要に関しては、以前に記事を書いているので、興味がある方はそちらも参照して頂くと理解しやすいのではないかと思います:
 JavaScriptからのDOM操作 >>


「DOM」等の言葉に親しみがない方に向けて噛み砕いてご説明すると、
「単純にWebサイト内の画像の位置が、動いているだけのもの」と考えてください。

Webサイト作成をされたことがある方は、<img>というHTMLタグを使われたことがあるのではないかと思います。

そのHTMLタグの位置を、少しずつずらすことによって、動いているように見せています。

位置をずらす方法としては、スタイルの値を、一定時間(0.1秒)毎にずらしています。

CSSをいじられたことがあれば、以下のような記述を見たことがある方もいらっしゃるのではないでしょうか。

セレクタ {
  position: absolute;
  top: 20px;
  left: 35px;
}

HTML要素の座標を指定して、画面上の任意の場所に要素を描画できるような、CSSの指定ですね。

この「top」と「left」の値をJavaScriptから操作することで、HTML要素が画面上で動いているように見せることができます。




僕が作った上記のゲームでは、基本的にすべての物がJavaScriptのオブジェクトとして管理されています。

忍者のキャラクターはもちろんですが、他のキャラクターに触れたときに表示されるセリフが書かれた巻物や、地面、左右の壁、木、川の水、等々…すべてがJavaScriptのオブジェクトです。

プログラミングにおける「オブジェクト」という概念がフワフワしている方は、以下の記事を読んでいただくと、イメージがしやすいかも知れません:
 オブジェクト指向とは >>


たとえば、岩のオブジェクトは、以下のようなプログラムで指定しています。

rock1: {
    size: 17,
    posX: 90,  //X座標
    posY: 65,  //Y座標
    zIndex: 20,
    img: imgRock,  //描画する画像
    onTouch: onTouchBlock,
},

こういうオブジェクト達をいくつか配置して、岩や木、キャラクターや巻物などを表現しています。

上記のrock1オブジェクトに書かれているのは、基本的には位置や、表示する画像データの名前等なのですが、
「onTouch: onTouchBlock」
という行は、少し分かりにくいかもしれません。


プログラムの中で、キャラクター(忍者)が、各要素に触れると、onTouchという関数が呼ばれるようにしています。
ここではrock1というオブジェクトに対して主人公の忍者が触った時に、onTouchBlockという関数が呼ばれるようにしています。

このonTouchBlockとは、以下のような関数です。

//=======================================
// 貫通不可能ブロックに、忍者が触った際の処理
//=======================================
function onTouchBlock(ninja, from) {
    if (from === "upper") {
        //忍者が上から
        ninja.posY = this.posY - ninja.size;
        ninja.speedY = 0;

    } else if (from === "right") {
        //忍者が右から
        ninja.posX = this.posX + this.size;
        ninja.speedX = 0;

    } else if (from === "lower") {
        //忍者が下から
        ninja.posY = this.posY + this.size;
        ninja.speedY = 0;

    } else if (from === "left") {
        //忍者が左から
        ninja.posX = this.posX - ninja.size;
        ninja.speedX = 0;
    }
}

上記のプログラムを理解する必要はないのですが、雰囲気だけでも掴んで頂けたら幸いです。

忍者が上から岩に触れると、岩が忍者を上向きに押し返して、忍者が岩の上に乗れるイメージです。
忍者が横から岩に触れても、岩を貫通することはできなくなっています。



プログラムの大まかな流れ


このゲームのプログラムの大まかな流れは、以下のようなものです。

  1. 各要素の初期位置などを設定
  2. 各要素の位置や状態(表示・非表示 等)を計算
  3. 計算した内容を元に、要素を画面に描画する
  4. 「2」と「3」を0.1秒ごとに繰り返す

基本的にはこれだけです。

この中で一番多くプログラムを書くことになるのが、「2. 各要素の位置や状態を計算」の部分だと思います。

重力加速度を表現するために下向きに速度を追加したり、速度から位置を計算したりします。

右向きに忍者が進むためのボタンが押下されているのであれば、それに応じて、右向きの速度を忍者に与える必要もあります。

一定条件を満たさないと表示されないような要素があるのであれば、その条件が満たされているかどうかを計算し、表示・非表示の判定をしておきます。


その後、「3. 計算した内容を元に、要素を画面に描画する」という処理をやります。

素のJavaScriptやjQueryでDOM操作を行うのであれば、ここでまとめて行うと良いのではないでしょうか。(上記の計算中に直接DOMをいじっていると処理が遅くなるので)

(Reactを使っているのであれば、setState関数を呼び出して、render処理が走るようにします。)


上記の流れを0.1秒間隔で繰り返すことにより、忍者の画像を少しずつ動かし、パラパラ漫画のように動きを表現しています。


Reactは何をやっているの?


上記のようにDOMを操作するゲームであれば、大抵の場合はReactを使うことによってパフォーマンスが向上すると思うので、オススメです。

これを詳しく説明しようとすると、Reactについての理解が多少必要になるため、大まかな概念だけでも雰囲気を掴んでもらえると幸いです。

Reactは「仮想DOM」というものを用いて、描画するオブジェクトを管理します。

「仮想DOM」などというとすごく難しそうに聞こえますが、要は、「getElementByIdなどで取得したDOM要素を、僕たちは直接いじらない」ということです。

僕たちは、ただのJavaScriptのオブジェクト(仮想DOM)を操作するだけで、実際のDOMには触れません。
そこはReactが「描画のタイミングで」イイ感じにやってくれます。

たとえば、上記では「rock1 (岩)」のオブジェクトを例に出していますが、これはあくまでただのJavaScriptのオブジェクトであり、DOM要素ではありません

rock1の値はReactの仮想DOMにセットされ、実際のDOM操作は、Reactが裏でやってくれます。



なぜそんなことをするのかというと、何も考えずに素のJavaScriptで直接DOM操作を行うと、非常に処理が遅くなってしまうことが多いからです。

自分も上記のようなゲームを、素のJavaScriptで作ろうとしたことがあるのですが、DOMにアクセスしすぎて、要素数が増えたときに、非常に処理が遅くなってしまいました。

なぜ遅くなるのかというと、DOMを操作するということは、その都度、対応するHTML要素の描画が行われるということです。
この描画というのが、非常に処理に時間がかかるのです。

JavaScript内の計算で、オブジェクトの値が少し更新される度に、毎回描画を行っていたら、「0.1秒ごとに呼び出される処理が0.1秒以内に終わらない」という事態になってしまいかねません。

描画処理は、0.1秒ごとの処理の最後に1回だけ、Reactを通して行うようにすることで、「描画」の負担を大幅に減らして、素のJavaScriptでDOMを直接いじりまくるよりも、スムーズに忍者が動くようにしてくれています。


まとめ:
JavaScriptでゲームを作ろう

JavaScriptでゲームを作ろう

この記事では、ざっくり概念的な説明しかできませんでしたが、なんとなく雰囲気は掴んで頂けましたら幸いです。

まぁ自分のゲームは、見ての通り大したゲームではないのですが、JavaScriptを練習中の方などには、ゲーム作りはとても良い練習になると思うのでオススメです。
オブジェクト指向でプログラムを書く、良い訓練になると思います。

この記事が少しでも、
「何か作りたいけど、何作ろうかなぁ~」
と悩んでいらっしゃる方々へのヒントになりましたら、幸いです。