enchant.jsとtmlib.jsを使ったゲーム作成フレームワーク「enforce」チュートリアル（８）弾の数を制限する

ここでは、「**game01**」というディレクトリ内で作業しているという前提で説明しています。

# 撃てる弾の数を制限する
今の状態だと、弾の数は無制限（実際は制限はあります）ですが、これではゲームになりません。
なので撃てる弾の数を制御してみます。

まずは、「**bearChara.coffee**」の「**constructor**」を下記のように修正してください。

```coffeescript
    constructor:(initparam)->
        super(initparam)
        @scaleX = 1.5 
        @scaleY = 1.5 
        # 以下追加分
        @bulletcontrolobj = addObject
            motionObj: bulletControl
            type: CONTROL
        , @ 
        GLOBAL['bulletcontrol'] = @bulletcontrolobj
        @bullettime = 0
```
追加分を説明します。
撃った弾を制御するオブジェクトを「**bulletControl**」というクラスから生成します（後ほど新しいクラスファイルを生成します）。
ここで、「**type**」として新しく「**CONTROL**」というのが出てきます。
これは画面に出てくるキャラクタとしては使わずに、ゲーム内で制御などの動きを定義するオブジェクトとして宣言しています。
なので、渡すパラメータはオブジェクト生成に使うクラス名だけです。
画面には表示されませんが、クラスの中身はまったく同じなので、動作の記述方法もまったく同じになります。
さらに「**addObject**」の引数で、パラメータの後に、

```coffeescript
        , @
```
があります。
これは、生成したオブジェクトのインスタンス変数「**@parent**」に、自分自身を設定するパラメータになります。
生成したオブジェクトをどのオブジェクトからも参照出来るように「**GLOBAL**」に格納しています。
その後に、弾の発射間隔調整用のインスタンス変数「**@bullettime**」に0を格納しています。

そして、「**behavior**」を下記のように修正してください。

```coffeescript
    behavior:-> 
        super()
        switch @_processnumber
            when 0
                axes = PADAXES[0]
                @x += axes[HORIZONTAL] * 4
                @y += axes[VERTICAL] * 4
                buttons = PADBUTTONS[0]

                if (@y > SCREEN_HEIGHT - @height)
                    @y = SCREEN_HEIGHT - @height
                if (buttons[1])
                    @ys = -20

                # 以下修正箇所
                if (!buttons[0])
                    @bullettime = 0
                if (buttons[0] && LAPSEDTIME > @bullettime)
                    @bulletcontrolobj.fire()
                    @bullettime = LAPSEDTIME + 0.4
```
「**buttons[0]**」がtrueの場合（キーボードの「z」かスペースバーかジョイパッドのボタン0が押された）に問答無用で弾を発射させていましたが、グローバル変数「**LAPSEDTIME**」が「**@bullettime + 0.4**」を越えていた場合に弾を発射するようにします。
グローバル変数「**LAPSEDTIME**」はシステムで用意されている「ゲームが起動されてからの秒数」が小数点以下第二位までのfloatとして格納されています。
これで押しっぱなしでは0.4秒間隔で弾が発射されるようになります。
もし「**buttons[0]**」がfalseになったら間隔制御用の変数「**@bullettime**」を0にするので、連打した場合は連射出来ます。
そして、直接「**bullet**」オブジェクトを生成していた処理を、「**constructor**」で生成した「**@bulletControl**」オブジェクトに弾を発射するメッセージを送る処理に変更します。

次に、「**bearChara.coffee**」でオブジェクトを生成するのに使った、弾を制御するクラスを生成します。
ゲームのトップディレクトリ（**game01**）で、

$ **enforce derive bulletControl**

と実行し、「**src**」ディレクトリに「**bulletControl.coffee**」が生成されていることを確認してください。
監視コマンドが実行されている場合は生成と同時にコンパイルされます。
生成されているのが確認出来ましたら、「**constructor**」メソッドに下記を追記してください。

```coffeescript
        @bulletlist = []
```
これは生成した弾のオブジェクトを管理するための配列です。

次に、下記のメソッドを追記してください。

```coffeescript
    fire:->
        if (@bulletlist.length < 3)
            bulletobj = addObject
                image: 'bear'
                motionObj: bullet
                x: @parent.x
                y: @parent.y
                ys: -16 
                width: 32
                height: 32
                animlist: [
                    [100, [0, 1, 0, 2]] 
                ]   
            @bulletlist.push(bulletobj)

    removeBullet:(obj)->
        for num in [0...@bulletlist.length]
            obj2 = @bulletlist[num]
            if (!obj2?)
                continue
            if (obj._uniqueID == obj2._uniqueID)
                removeObject(obj2)
                @bulletlist.splice(num, 1)
```
「**fire**」メソッドは弾オブジェクトを生成します。
もし配列「**bulletlist**」の要素数が3より少なかった場合は、弾オブジェクトを生成します。
弾オブジェクトを生成する際の座標値として、このオブジェクトが生成された時に設定された「@parent」（中には**bearChara.coffee**から生成されたオブジェクトが格納されています）の座標をそのまま使っています。
生成した後に配列「**@bulletlist**」に追加します。

「**removeBullet**」メソッドは渡されてきたオブジェクトが「**@bulletlist**」にあった場合は、配列から削除しオブジェクト自体を削除します（スプライトも削除されるので画面からも消えます）。
ここの処理で、オブジェクトの比較に「**_uniqueID**」というインスタンス変数が使われていますが、これはシステムで用意されている変数で、オブジェクト生成時にユニークなIDが生成されて格納されています。

最後に、「**bullet.coffee**」を修正します。
「**constructor**」に、

```coffeescript
        @bulletcontrolobj = GLOBAL['bulletcontrol']
```
を追記し、「**bulletControl.coffee**」から生成したオブジェクトを取得します。
そして「**behavior**」を下記のように修正します。

```coffeescript
    behavior:->
        super()
        switch @_processnumber
            when 0
                if (@y < 0)
                    @bulletcontrolobj.removeBullet(@)

                enemy = GLOBAL['enemy']
                if (@isIntersect(enemy))
                    enemy.burn()
                    @bulletcontrolobj.removeBullet(@)
```
弾が画面最上段より上に行った場合に単純に削除していた処理を、「**bulletControl.coffee**」で定義されているメソッド「**removeBullet**」を自分のオブジェクトを引数にして呼び出す処理に変更しています。
自分（弾）が敵に当たった場合の自分を削除する処理も同じように「**removeBullet**」を呼び出す処理に変更しています。

すべての修正が終わりましたら、保存しコンパイルし、ブラウザをリロードしてください。
弾が0.4秒間隔で発射され、画面上には3発しか撃てないようになっているはずです。


[チュートリアル（７）](http://qiita.com/digitarhythm/items/58422db419d00bd6ae74) <--- ⬛︎ ---> [チュートリアル（９）](http://qiita.com/digitarhythm/items/1cd10eb34b69d585992f)

---

[enforceチュートリアル一覧](http://qiita.com/digitarhythm/items/9e75c871f5ce582a4d01)