2016年12月に書いた記事から転載・一部編集しています

死は敗北ではない(あいさつ)

こんにちは。おれです。

phina.jsを使って「寺井、キミってやつは…」というゲームを作りました。

こちらから遊べます。
http://cachacacha.com/GAME/Teraikimi/

この記事ではゲームの紹介がてらどんな感じで制作したか書いていこうと思います。
趣味で作ってる程度のものですが、これからゲームを作りたいという人向けに参考ぐらいになれば幸いです。

というわけでこのゲームの制作過程ですが、だいたい3つに分けられます

  • 企画
  • プログラミング

です。

企画

通勤中電車を待ってる時に「会社行きたくない。誰か電車止めてくれ」と思って、誰かが電車を止めるゲームを作ることにしました。
テリーマンみたいに走ってくる電車から子犬を守る設定にすれば分かりやすくなる気がしたので主人公の名前を「寺井」にして、せっかくなんで電車だけじゃなくて色々襲い掛かってきたほうがおもしろいなと思ったので陸上部とか怪獣も出すことにしました。
ついでに止めるだけじゃなくてぶん殴ってぶっ飛ばしたほうが気持ちいいので、ぶんなぐってぶっ飛ばすことにしました。
そんな感じで固まったので、作ります。

絵についてはドット絵でやることにしました。
ドット絵は根気があればなんとかなる気がするので絵心がない人におすすめです。

使ったツールは次の3つです。全部無料です。

EDGE
EDGEはドット絵を描くためのエディタです。

こんな感じでドット絵を描いて出力できます。

ちなみにドット絵の描き方とEDGEの使い方についてマジで死ぬほどタメになるサイトがあるので紹介します。
ドット絵描こうZ
アニメーションの講座とか超ありがたいです。

TexturePacker
TexturePackerはスプライトシートを作成するためのツールです。
スプライトシートというのは画像が繋がってる画像で、こんなやつです。

phina.jsはこのスプライトシートを読み込むことでアニメーションを作ることが出来ます。

EDGEで描いた画像をつなげてスプライトシートを作るわけですが、Gimpなどの画像編集ソフトでいちいち自分で画像をつなげるのは非常に面倒です。
それがTexturePackerを使うと超ラクになります。

使い方は簡単で、画像をドラッグアンドドロップするだけです。
有料版と無料版があるのですが、自分は無料版で事足りてるので無料版を使ってます。が、注意点があります。
まず無料版では大きいサイズの画像は作れないというのと、無料版では使えない機能があるので、それを全部オフにするのがやや面倒です。
具体的には

  • AlgorithmをBasicにする
  • Png Opt Levelを0にする
  • Detect identical spritesのチェックを外す(Layoutのshow advancedを押すと出てくる)
  • Trim mode をnoneにする

こんな感じで設定します。面倒ですが自分で画像くっつけるよりマシです。

PhotoScape
画像サイズを一括で拡大するときに使います。
EDGEで出力した画像はちっちゃいので、これで良い感じの大きさにします。
あってもなくてもいいですが。あると便利です。

プログラミング

最低限の絵が用意できたら、だいたいのゲームの流れを網羅したプロトタイプを作って、後から絵を描いて追加したり、プログラムを追加したりしてゲームを作っていきます。

プログラミングについて全部解説すると長くなるし他の記事読んだ方がいいと思うので、ポイントだけ書いていこうと思います。

  onpointstart: function(e){
    switch (this.Player.MoveMode) {
      case "Normal":
        this.Player.CatchAction();
 
        break;
 
      case "Kamae":
        this.Player.RendaStart();
 
        this.bg.tweener
        .clear()
        .to({y:this.gridY.center()}, 50)
        .to({y:this.gridY.center(0.2)}, 50)
        .to({y:this.gridY.center(-0.2)}, 50)
        .to({y:this.gridY.center()}, 50)
        var bokoboko = BokoBokoEffect().addChildTo(EffectGroup);
        var toucheffect = TouchEffect(e.pointer.x,e.pointer.y,2).addChildTo(EffectGroup);
 
        break;
 
 
      case "Renda":
 
        this.bg.tweener
        .clear()
        .to({y:this.gridY.center()}, 50)
        .to({y:this.gridY.center(0.2)}, 50)
        .to({y:this.gridY.center(-0.2)}, 50)
        .to({y:this.gridY.center()}, 50)
        var bokoboko = BokoBokoEffect().addChildTo(EffectGroup);
        var toucheffect = TouchEffect(e.pointer.x,e.pointer.y,2).addChildTo(EffectGroup);
 
        this.Player.RendaAdd();
 
        break;
 
 
      case "GameOver":
 
        break;
 
      default:
 
    }
 

onpointstartに書いた処理は画面をタッチしたときに動きます。

主人公の状態によって処理を分けています。

普通に立ってるときにタッチすると、主人公は敵をキャッチするアクションをします。

構えてるときにタッチすると連打を開始、連打してるときにタッチすると、連打回数を加算したりヒットエフェクトを出す処理に飛びます。

同時に、背景画像をtweenerを使って一瞬移動させることで画面が揺れるエフェクトをかけます。

更にタッチした場所にタッチエフェクトを生成します。

あと画面全体にまんべんなく「ドカ」とか「バキ」とか文字を出すボコボコエフェクトというのを生成します。
ボコボコエフェクトは乱数を使うことで画面上のランダムな場所に文字を生成します。

連打

主人公が敵をキャッチしたあとは、一定時間内に目標値まで連打します。
以下は連打の受付時間をカウントしている部分です。

app.deltaTimeを使うと正確な時間を計れます。
処理が重くてカクカク動いてる時でも1フレームにかかった時間を加算するのでfpsに影響されず時間を計れる感じです。
これを使わないと、カクカク動くほど連打受け付け時間が長くなってゲームが楽になってしまうので、ちゃんと作ってユーザーを苦しめましょう。

連打時間を超えると、主人公は弾き飛ばされ、敵が走り抜け、連打エフェクトが消えます。

必殺アクション

連打に成功すると必殺アクションに移行します。
以下は必殺アクションに移行する時のコードです。

  HissatuMove: function(){
    this.BlackBG = RectangleShape().addChildTo(BackGroup);
    this.BlackBG.width = SCREEN_WIDTH * 2;
    this.BlackBG.height = SCREEN_HEIGHT * 2;
    this.BlackBG.fill = "black";
 
    var Flash;
 
    var self = this;
 
 
    this.tweener
      .clear()
      .call(function(){
        Flash = Sprite("Flash").addChildTo(BackGroup);
        Flash.setSize(SCREEN_WIDTH,SCREEN_HEIGHT)
        Flash.setPosition(self.gridX.center(),self.gridY.center(1));
        Flash.tweener
          .clear()
          .wait(300)
          .to({alpha:0}, 1000)
          .wait(300)
 
 
      })
      .wait(1400)
      .call(function(){
 
        self.TutorialText.text = "押せ!";
        self.TutorialText.scaleX = 1.2;
        self.TutorialText.scaleY = 1.2;
        self.TutorialText.tweener
          .clear()
          .to({scaleX:1,scaleY:1}, 50)
        self.TutorialText.rotation =-10;
        self.CreateHissatuButton();
 
      })
      .to({alpha:0}, 1000)
      .wait(300)
      .call(function(){
        Flash.remove();
 
      })
  },

だいたいtweenerを使ってやってます。手軽に動きをつけられるので使い倒しています。
まず最初に背景を真っ黒にします。これは黒くて大きい四角を置いているだけです。
そして、なんかそれっぽい雷を出しておきます。

雷が徐々に消えるので、消えたタイミングぐらいで必殺ボタンを生成します。

必殺ボタンが押されたとき、必殺ボタンのonpointstartが動き、そこから以下の処理が呼ばれます。

  PushHissatuButton: function(){
 
    this.PushHissatuCount++;
    if(this.PushHissatuCount >= this.HissatuButtonMax){
      this.KnockOut();
    }
 
  },
 

ボタンを押した回数をカウントして、全部押したらノックアウトする処理を走らせます。
コードは省略します。K.O.って文字を出して、主人公が昇竜拳出して、敵をぶっ飛ばします。

エフェクト

自分なりにエフェクトをつけてみました。
エフェクトがあるとプレイヤーがした操作が視覚的にわかりやすくなります。

タッチエフェクト

自分なりにエフェクトをつけてみました。
エフェクトがあるとプレイヤーがした操作が視覚的にわかりやすくなります。

タッチエフェクトについては、画像を使わずにphina.jsの図形描画機能で作っています。
中を透明にした白いボーダーラインの円の図形と
角を増やした星型の図形を一緒に出すことで、それっぽく見せています。

 
phina.define("TouchEffect", {
    superClass: "DisplayElement",
    init: function(X,Y,scale) {
      this.superInit({
        width: 550,
        height: 550,
        fill: "green",
        stroke: null,
 
      });
 
      var shape = CircleShape().addChildTo(this);
      // 位置を指定
      shape.setPosition(X, Y);
      shape.fill = 'rgba(0,0,0,0)';
      shape.stroke = 'white';
      shape.strokeWidth = 2 * scale;
      shape.radius  = 180;
      shape.tweener
      .clear()
      .to({alpha:0,scaleX:scale,scaleY:scale}, 500,"easeOutCubic")
      .to({alpha:0}, 200,"easeOutQuint")
      .call(function(){
        shape.remove();
      })
 
      // 図形をシーンに追加
      var star = StarShape().addChildTo(this);
      // 位置を指定
      star.stroke = 'red';
      star.fill = 'yellow';
      star.setPosition(X, Y);
      star.sides = 11;
      star.sideIndent = 0.6;
      star.strokeWidth = 11;
      star.radius  = 90;
      star.tweener
      .clear()
      .to({alpha:0,scaleX:scale,scaleY:scale}, 300)
      .call(function(){
        star.remove();
      })
 
    },
 
    update: function(app) {
 
 
    },
 
});
 

図形を生成したあとtweenerを使って、徐々に広がりつつ透明にしています。
scaleX,scaleYの値を変えてサイズを大きくしています。
radiusの値を変えて大きくすることもできますが、処理が重くなるので、scale使ったほうがいいです。
一回それで大量に生成しておしまいになりました。

キャッチエフェクト

インパクトの瞬間に円を出しています。格ゲーでガードした時とかを参考にしました。

タッチエフェクトと同じ方法で出しています。
タッチした瞬間に主人公から出る円と、敵をキャッチした瞬間に出る円があり、重なると良い感じになります。

あとキャッチした瞬間に画面が一瞬白くなります。白い大きい四角を一瞬生成して消してるだけです。

炎エフェクト

phina.jsのパーティクルのサンプルをほぼそのまんま使っています。
http://phiary.me/phina-js-tips-fire-effect-particle/

煙エフェクト

敵を止めたときに足元から煙が出てます。
これは炎エフェクトをちょっと変えたら作れます。
パーティクルの図形を四角にして、色をクッキリさせて、サイズと速度の調整をするだけです。

おわり

こんな感じでゲームが完成したらサイトなりにあげてツイッターで宣伝とかして終わりです。

トータルの作業時間でだいたい1~2週間ぐらいでした。
簡単なゲームなら1日で作れてしまうので、興味もった方はつくってみてください。

あとサイトの宣伝させてください。作ったゲームが置いてあります。

かちゃコム

それでは、これで失礼します
読んでくださった方ありがとうございました。

「ええ。勉強させていただきましたよ」

なっ・・・!お、おまえは・・・!

太公望・・・!!!

おわり

phina.jsでゲームをつくったので制作過程を公開(バラ)します Qiita