【phina.js】tmlib.jsで書いたゲームをphina.jsで書き直す


本記事はphina.js Advent Calendar 2015の18日目の記事です。

← 17日目 phina.js を使って様々な図形を表示してみよう --- 国産ゲームライブラリ「phina.js」で3Dゲーム作ろうぜ! 19日目 →

2日遅れですいません…(18日が終わる2時間前にエントリーしたので許してください…)

概要

tmlib.jsの後継のライブラリであるphina.jsがリリースされたということで昔書いたゲームをphina.jsで書き直してみました(書き直すといってもそこまで大きく変える必要はないです)
自分はtmlibのバージョンを気にせずに調べまくって作ったので古いものと新しいものが混ざってたりします…(新しいtmlib.jsはphina.jsにかなり近いと思います)
tmlib.jsにはなかったとか言いながらあったりするかもしれないですが、そこはご愛敬ということで…

起動処理(tm.main -> phina.main)

tmlib.jsでは(tm.game内のtmlib.globalize()で)一部の変数はグローバルに展開されていましたが、phina.jsでは展開するためにphina.globalize()を使用します

tm.main -> phina.main

tm.mainでは

  app = tm.display.CanvasApp("#app")
  app.resize(width, height)
  app.fitWindow()
  app.background = "#DDDDDD"
  app.fps = 60

  loadScene = LoadScene({
    assets: ASSETS
    # ~略~
  })
  loadScene.onload = ->
    app.replaceScene(TitleScene())
    return
  app.run()

と挿入位置などの設定、ロードシーンに記述などをしていましたが、phina.mainでは、GameApp()を使用してオブジェクトで設定を渡すことができるようになりました(tmlibのtm.gameあたりで既にできてたかもしれないです)

また、GameAppではTitleSceneやLoadSceneも入っているので独自のものを使わないのであれば実装が不要です

core.coffee#17-42
  app = GameApp({
    assets: ASSETS
    startLabel: "title"
    # ~略~
  })
  app.backgroundColor = "#DDDDDD"
  app.fps = 60
  app.run()

ただbackgroundColorやfpsなどはオブジェクトで渡せないみたいです

クラス (tm.define -> phina.define)

tmlib.jsではすべてのクラスに必要はなかったのですが、phina.jsではすべてのクラスにはthis.superInit()が必要になりましたtmlib.jsと同様に継承するクラスに関してはthis.superInit()が必要です(12/20 16:19修正)

Assets

{
  "shooter": "img/shooter.png"
}

と指定するのではなくそれぞれ種類を指定するようになりました

core.coffee#23-37
  sound: {
    "start": "sound/btn09.#{SUPPORT_EXT}"
  }
  image: {
    "shooter": "img/shooter.png"
  }

※SUPPORT_EXTの話はすぐしたのSoundに

Sound

Sound.SUPPORT_EXTがなくなりました
自分はChromium系ブラウザを使っているので.mp3指定だけでは音楽が流れないので、下のように実装してます

util.coffee
SUPPORT_EXT = do ->
 ext = ""
  audio = new Audio()

  if audio.canPlayType("audio/mp3") is "maybe"
    ext = "mp3"
  else if audio.canPlayType("audio/ogg") is "maybe"
    ext = "ogg"
  else if audio.canPlayType("audio/wav") is "maybe"
    ext = "wav"
  return ext

mp3 -> ogg -> wavという順番なのはサイズの小さいのを優先させたいからです(tmlib.jsだとwav -> mp3 -> oggでしたが…)

SoundManager

mute()がトグルではなくミュートにするになりました
よってこれまでのトグルをするには

  if SoundManager.isMute()
    SoundManager.unmute()
  else
    SoundManager.mute()

とする必要があります
また、tmlib.jsでは実装されていたSoundManager.play()Sound.play()へ移動されました(SoundManager.playMusic()に関しては利用できます)
AssetsManager.get("sound", "").play()と使用できます
SoundManager.pause()は一部実装されていないので今のところ動きません

Scene (Scene -> CanvasScene)

Sceneという名前からCanvasSceneという名前になってCanvasに特化したシーンになりました

fromJSON()

これに渡すchildrenが配列で渡せなくなりました
これまでは

  children: [
    {
      type: "Label"
      name: "titleLabel"
      text: "Shooting"
    }
  ]

のように指定できたのですが
phina.jsでは

scene.coffee#48-51
  children: {
    "titleLabel": {
      className: "Label"
      text: "Shooting!"
    }
  }

と指定します
これまでのnameがキー名に、typeclassNameに変わりました
また、init: []arguments: []に変更されました(12/20 16:35修正)
また、一部でinit: []と指定していた場所もinit: []に入れる必要がなくなりそのまま指定できるようになりました(FlatButtonのLabelがButton自体になったからかもしれないです)

  {
    type: "FlatButton"
    name: "muteButton"
    init: [
      {
        text: ""
      }
    ]
  }
scene.coffee#91-93
  "muteButton": {
     className: "Button"
     text: ""
  }

衝突判定

isHitElement()hitTestElement()に変わりました

Sprite (Sprite/AnimationSprite -> Sprite/FrameAnimation)

また、AnimationSpriteがなくなり、Spriteを継承してFrameAnimationattachTo(this)することでSpriteSheetを利用するようになりました

Sprite("参照名", width, height)でサイズが変更できるのですが、自分のほうではうまく変更できませんでした

setImage()がなくなったので下のように画像を変更できなくなりました

a = Sprite()
a.setImage()

変更をするにはFrameAnimationを使ってgotoAndPlay()で切り替えるという方法があります

display

CircleShapeやLabelなどが入っていますが、
これまで色を変更するのにfillStyleを使っていましたがfillになりました
それとrgba(,,,)はtmlib.jsではつかえたのですが、phina.jsでは今のところつかえないみたいです
自分のtypoだったみたいです…すいません(12/20 16:39修正)

tmlib.jsでは何もせずともクリックを感知していましたが、phina.jsではクリックを感知するために.setInteractive(true)が必要になります
ないとonpointstart()などがきかないです
また、onpointingstart()onpointstart()に変わりました

tmlib.jsではposition.set()で位置を設定できましたが、phina.jsではsetPosition()を使います
こっちも自分のtypoだったみたいです…(12/20 16:41修正)

CircleShape

サイズの指定がwidthheightからradiusに変わりました
ここでそのままさらっとwidthの値をradiusにすると倍の大きさになってしまうので(当たり前ですが)注意が必要です

FlatButton

おそらく今までのFlatButtonButtonを乗っ取りました
また、Button内にLabelが入っている構造ではなくそのままButtonLabelがあるのでbutton.label.tweenerなどのようにできなくなりました(方法があれば教えてください)

Input (Accelerometer -> ×)

Accelerometerがなくなりました(今後実装予定?)
といっても実装はeventつければいいだけなのでさらっと

entity.coffee#16-21
  @rotate = {}
  window.addEventListener("deviceorientation", @catchOrientation, false
  )
entity.coffee#20-22
  catchOrientation: (e) ->
    @rotate.beta = e.beta
    @rotate.gamma = e.gamma
    return

SceneやEntityなどの中でeventを生成するときはそのSceneやEntityがなくなるときにremoveしましょう…(ここで少し引っかかりました)

scene.coffee#106-108
  onDied: ->
    window.removeEventListener("deviceorientation", @catchOrientation, false)
    return

v2.0で実装予定だそうです!(12/20 16:41修正)

Geom

Vector2

random(min, max, len)がうまく動いてません
v2で修正予定です
とりあえずのところは
random(min, max).mul(len)
と書くと動きます

Math

Math.rand()Math.randint()
Math.randf()Math.randfloat()に変わりました

最後に

自分が関係した変更はこんな感じです

実際の今回書き直したゲームはこちら
ソースコードはこちら(汚いですが…)

御覧いただきありがとうございました

いろいろと躓いた際に助けていただいたphiさんやsimiraaaaさんやdaishiさんにも感謝の意を…