🐄

【phina.js】牛を避けるだけのゲームを作りました(?)

2021/03/05に公開
1

webゲーム「牛を避けるだけのゲーム」を作りました

空から降り注ぐ牛を避け続けるゲームです
github pagesにアップしてあるので気軽に遊んでみてください
https://git-gen.github.io/cow_runner
※ 音が出るので音量注意です!!

こんな感じのクソゲーの作り方を紹介します

ゲームライブラリ

phina.jsというゲームライブラリを使用します

https://phinajs.com

使用例はこちらのサイトに沢山用意されているので、
作りたいゲームの参考にしてもらえると良いと思います

http://runstant.com/projects?tag=phina.js

雛形

https://github.com/git-gen/parcel-phinajs-example

parcelにphina.jsを入れただけのリポジトリです
phina.jsはパッケージではなくCDNで読み込んでます、
パッケージを使わない理由はnpm・yarnのphina.jsは更新されていないのかバグがある為

セットアップ

$ yarn 

$ yarn dev

http://localhost:1234

ざっくり仕組みを説明

雛形の中のindex.jsが本体になります
まずこれがindex.jsの中身で最低限の設定しかされていない状態です

parcel-phinajs-example/src/assets/index.js

// phina.js をグローバル領域に展開
phina.globalize();

// MainScene クラスを定義
phina.define('MainScene', {
  superClass: 'DisplayScene',
  init: function() {
    this.superInit();
    this.backgroundColor = '#fff';
  },
});

// メイン処理
phina.main(function() {
  // アプリケーション生成
  var app = GameApp({
    startLabel: 'title',
    title: 'ゲーム',
  });
  // アプリケーション実行
  app.run();
});

phina.globalize();はphina.jsで用意されているオブジェクトを呼び出しやすくする宣言です
といっても、これを書かなかった場合は原因不明のエラーが発生してゲームが止まる事が多々あったので、ほぼ必須な気がします

後はapp.runしてあげれば自動でDOMにcanvas要素を作成してくれてゲーム画面が作られます

MainSceneというクラスを定義しています
これはゲーム画面はシーンごとに分かれており
phina.jsではデフォルトで用意されているシーン(タイトル、メイン、リザルトなどがある)を上書きしているものです

アセットを用意する

フリー素材を使っても良し・自分で作っても良しです
自機や障害物にアニメーションを付けたければ
スプライトシートを作れば簡単にアニメーションをつけることもできます
ちなみにスプライトシートの作成はPiskelでできます(オススメ)

こんな感じで使います

parcel-phinajs-example/src/assets/index.js

import bgm from './bgm.mp3'
import player from './player.png'

const ASSETS = {
  sound: {
    'bgm': bgm,
  },
  image: {
    'player': player,
  },
}

...省略

phina.main(function () {
  // アプリケーションを生成
  const app = GameApp({
    width: SCREEN_WIDTH,
    height: SCREEN_HEIGHT,
    assets: ASSETS,
  app.run()
})

ゲームを作ってみる

例として敵にぶつかるとゲームオーバーになるだけのゲームを作ってみました
MailSceneで定義しているplayerはクリックした位置に移動して動かないenemyに接触するとゲームオーバーとなります
コード内にコメントを記載しているので参考にしてみてください
基礎としてはこんな感じでガンガンコーディングしてクオリティをあげていく感じです

import bgm from './bgm.mp3'
import effect from './effect.mp3'
import player from './player.png'

// phina.js をグローバル領域に展開
phina.globalize();

const SCREEN_WIDTH = 640
const SCREEN_HEIGHT = 960
const OBJECT_POSITION = 800
const ASSETS = {
  sound: {
    'bgm': bgm,
    'effect': effect,
  },
  image: {
    'player': player,
  },
}

// MainScene クラスを定義
phina.define('MainScene', {
  superClass: 'DisplayScene',
  init: function() {
    this.superInit();
    this.backgroundColor = '#fff';
    SoundManager.playMusic('bgm');

    // labelを表示する
    Label('敵ぶつかるとゲームオーバー').addChildTo(this).setPosition(this.gridX.center(), this.gridY.center());

    // 背景の設定方法(backgroundという背景用の画像をassetsに読み込む)
    // Sprite('background').addChildTo(this).setPosition(this.gridX.center(), this.gridY.center());

    // 自機
    this.player = Sprite('player', 64, 64).addChildTo(this)
    this.player.setPosition(200, OBJECT_POSITION)

    // 敵
    this.enemy = Sprite('player', 64, 64).addChildTo(this)
    this.enemy.setPosition(440, OBJECT_POSITION)
  },

  // 毎フレーム更新処理
  // 1フレームごとにこの処理が実行されます
  // デフォルトは30fpsなので1秒間に30回実行されるという事です
  update(app) {
    const p = app.pointer
    // クリックがあったところにplayerを移動させる
    if (p.getPointing()) {
      this.player.setPosition(p.x, p.y)
    }

    // playerがenemyに接触していたらgemeover()を呼び出す
    if (this.player.hitTestElement(this.enemy)) {
      this.gameover()
    }
  },

  // ゲームオーバー
  gameover() {
    // 効果音を再生する
    SoundManager.play('effect');

    // 次のシーンに遷移する
    this.exit()
  },
});

// メイン処理
phina.main(function() {
  // アプリケーション生成
  var app = GameApp({
    startLabel: 'title',
    title: 'ゲーム',
    width: SCREEN_WIDTH,
    height: SCREEN_HEIGHT,
    assets: ASSETS,
  });
  // アプリケーション実行
  app.run();
});

github

牛を避けるだけのゲームのgithub pagesのリポジトリはこちらになります!
https://github.com/git-gen/git-gen.github.io

Discussion

カルマカルマ

初めまして、カルマと申します。
今、卒業制作でphina.jsを使用したゲーム制作を行いたいと思い、phina.jsを勉強しています。
githubで上げられている牛を避けるだけのゲームのフォルダをダウンロードし、ソースコードを拝見しています。

cow_runner.jsの169行目からの毎フレーム更新処理について、質問させて頂きます。
updateメソッドの引数にappを格納。変数pにapp.pointerを代入。最初のif文で、クリックかタッチの入力を、p.getPointing()で取得のところまでは分かるのですが、172行目からが分からないです。

ご教授、お願いいたします。