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

本記事は[phina.js Advent Calendar 2015](http://qiita.com/advent-calendar/2015/phinajs)の18日目の記事です。

← 17日目 [phina.js を使って様々な図形を表示してみよう](http://phiary.me/phinajs-shape-collection/)  ---   [国産ゲームライブラリ「phina.js」で3Dゲーム作ろうぜ!](http://qiita.com/emadurandal/items/93223877936b5dc767f5) 19日目 →

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

# 概要
tmlib.jsの後継のライブラリである[phina.jsがリリースされた](http://phiary.me/phinajs-release/)ということで昔書いたゲームを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では

```coffeescript
  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も入っているので独自のものを使わないのであれば実装が不要です

```coffeescript: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
```coffeescript
{
  "shooter": "img/shooter.png"
}
```

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

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

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

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

```coffeescript: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()`がトグルではなくミュートにするになりました
よってこれまでのトグルをするには

```coffeescript
  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が配列で渡せなくなりました
これまでは

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

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

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

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

```coffeescript
  {
    type: "FlatButton"
    name: "muteButton"
    init: [
      {
        text: ""
      }
    ]
  }
```

```coffeescript:scene.coffee#91-93
  "muteButton": {
     className: "Button"
     text: ""
  }
```

## 衝突判定
`isHitElement()`が`hitTestElement()`に変わりました

# Sprite (Sprite/AnimationSprite -> Sprite/FrameAnimation)
また、`AnimationSprite`がなくなり、`Sprite`を継承して`FrameAnimation`を`attachTo(this)`することでSpriteSheetを利用するようになりました

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

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

```coffeescript
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
サイズの指定が`width`、`height`から`radius`に変わりました
ここでそのままさらっと`width`の値を`radius`にすると倍の大きさになってしまうので(当たり前ですが)注意が必要です

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

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

```coffeescript:entity.coffee#16-21
  @rotate = {}
  window.addEventListener("deviceorientation", @catchOrientation, false
  )
```

```coffeescript:entity.coffee#20-22
  catchOrientation: (e) ->
    @rotate.beta = e.beta
    @rotate.gamma = e.gamma
    return
```

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

```coffeescript: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()`に変わりました

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

[実際の今回書き直したゲームはこちら](http://s--minecraft.github.io/shooting/)
[ソースコードはこちら](https://github.com/S--Minecraft/shooting)(汚いですが…)

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

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