JavaScriptでゲーム作ろう

画面をタップするだけで遊べる簡単なゲームの作り方をいくつか紹介します。

 

紹介するサンプルのゲームはJavaScriptのライブラリpixi.jsをベースに作った簡易ゲームエンジン(p94e.js)を使って作っています。

ゲームがさっくり簡単に作れてブラウザですぐ遊べるのでとってもおすすめです。

 

See the Pen
pixi.jsで作った糸通し風ゲーム
by いんわん (@inwan78)
on CodePen.

(↑はCodePenというオンラインエディタで公開している糸通し風ゲームのサンプルです)

 

筆者の私はpixi.jsを使ってゲームを作って自分のサイトで公開してます。

実際に私の作ったゲームを遊んでもらえると記事の信頼性も増すと思うのでぜひ私のゲームサイトに遊びに来てください。

 

この記事ではpixi.jsについて詳しくは解説しません。

pixi.jsの解説とゲームエンジンについては下の2つの記事に書いているのでこっちも見ながら読んでください。

 

タップの基本

pixi.jsでは画面をタップするような処理をイベントというもので処理します。

イベントは以下のように記述します。

screen.on("pointerdown", () => {
  //ここに処理を書く
});

タッチの対象(上の例ではscreen)のonメソッドを使います。

onメソッドの第一引数はイベントの種類(上の例ではpointerdown)、第二引数にコールバック関数を書きます。

ちょっと難しくて何言ってるかわからないかもしれないですが、「何が起こった」時に「何をするか」って書く感じです。

画面のタッチに関するイベントは基本的には「pointerdown(押した) 」「pointerup(離した)」の二つでできます。

他にもマウス専用やタッチ専用のイベントもあるんですが、pointerはマウスにもタッチにも対応してくれるのでこれを使えば記述が一つで済むので楽です。

 

タッチ処理のサンプル

まず簡単なタッチ処理のサンプルプログラムを触ってみましょう。

タッチのサンプルプログラム

画面をタッチすると音とともに白い輪が表示されます。

 

プログラムはこちらからダウンロードできます。

プログラムをダウンロード

 

※プログラムを実行するにはサーバーが必要です。Visual Studio CodeのLive Serverという拡張機能を使うと楽です。

 

サンプルのプログラム内にはコメントをたくさん入れているので読んでもらえれば大体何をやっているかはわかると思います。

 

サラッと簡単に説明するとまずタッチの時に出現するRingというクラスをGraphicsというクラスから拡張して作っています。

最初は透明で見えなくなっていて、画面にタッチをするとその場所に現れて広がりながら消えていきます。

alphaが透明度、radiusが半径で更新処理のupdate()でその値を変えています。

 

今回の一番のポイントのタッチの処理は以下の部分です。

this.on('pointerdown', (e) => {
  const pos = e.data.getLocalPosition(e.currentTarget);//画面の座標を取得
  ring.x = pos.x;
  ring.y = pos.y;
  ring.radius = 8;
  ring.alpha = 1;
  core.resources[Resource.Sound].sound.play();
});

ここでタッチした場所を取得して、それをringの座標に入れて場所を移しています。

 

pointerdownをpointerupに変えるとタッチを離したときに反応するようになります。ずっとタッチしたままだと何も起こらず指を離した瞬間リングが出ます。

 

糸通し風ゲームの作り方

次はちょっとゲームっぽいものを作ります。

タップゲームと言えばやはり糸通しゲーム的なものかと思ったので作ってみました。

糸通し風ゲームのサンプル

 

プログラムはこちらからダウンロードできます。

プログラムをダウンロード

 

今回のプログラムはちょっとゲームっぽくなっているので前のサンプルよりもだいぶ長くなっていますが、その分コメントもたくさん入れているので読めば何となく理解できると思います。

 

プログラムの解説

押し続けている判定はフラグを使う

まず、タップに関しては今回は「押し続ける」という状態の判定が大事になります。

「押し続けている」という状態は画面にタッチしてから指を離すまでですが、イベントとしてはpointerdownとpointerupしかありません。

なのでタッチしてから離すまでをフラグ(isTouching)を使って管理しています。

このフラグで押している状態かそうでないのかを判断しています。

 

スクリーンショットを使う

もう一つ今回のサンプルで特殊なのがMainSceneのupdate内にあるこの部分。

//2つのテクスチャーを交互に使う
if(this.renderFlip = !this.renderFlip){
  core.app.renderer.render(layer, this.renderTexture1);
  pic.texture = this.renderTexture1;
}else{
  core.app.renderer.render(layer, this.renderTexture2);
  pic.texture = this.renderTexture2;
}

実はこの部分はlayerのスクリーンショット(的なもの)を撮ってそれをpicの画像にしています。

何を言っているのかわかりづらいかもしれませんが、この部分をコメントアウト(この部分を/*と*/で挟む)してみてください。

するとゲーム画面には白い点だけが表示されるようになります。

 

これはどうなっているかというと

pic = new PIXI.Sprite();
pic.y = 1;
pic.alpha = 0.95;
layer.addChild(pic);

picを作ったときにpicのY座標を1ドット下にずらしています。

これは撮ったスクリーンショットをpicに入れたとき1ドット下にズレることになります。

そしてlayerはこのpicも含んでいるためそのずれもスクリーンショットで一緒に撮影されます。

なので撮るたびに1ドットずつズレていくように見えるわけです(下の画像みたいなイメージ)。

1ドットずらして貼っていく

それと、このスクリーンショットが2つ交互に行われていますが、これは一つだと処理が終わりきる前に次が始まってエラーになってしまうらしく、そのエラーを起こさないためにやっています。

(このエラーの原因が最初わからなくて結構な時間を無駄にしたんですよね。。)

 

newは重たい

あ、あとこのスクリーンショットを作るのに使われているこの変数

this.renderTexture1 = new PIXI.RenderTexture.create({width: Config.Screen.Width, height: Config.Screen.Height});
this.renderTexture2 = new PIXI.RenderTexture.create({width: Config.Screen.Width, height: Config.Screen.Height});

これをスクリーンショットを撮るときではなく事前に作っているのには訳があります。

 

もしこれをスクリーンショットを撮るとき毎に生成するとすごく負荷がかかります。

最初は普通に動いていますがいずれ処理落ちしはじめて最終的にフリーズする恐れがあります。

 

pixi.jsでSpriteやContainerなど表示に関係する要素をnewで新しく作るときは負荷がかかります。

一つ二つではまったくわからないですが、例えばシューティングゲームなんかで弾を発射するたびにSpriteを作っていたらそれだけで処理落ちしたりします。

 

なので繰り返し使うものは必要な数だけ先に作って使いまわすようにします。

 

シーンの切り替え

あとは、障害物に当たるとGame Overと表示されるシーンがpushScene()でゲーム画面の上に出ます。

pushSceneは一時的に出すシーンでpopSceneで消せます。

今回はゲームを一から再開するのでreplaceSceneで今のシーンを破棄してもう一度MainSceneを作っています。