EgretEngineでRPGマップはどう実現するか？(4) カメラ回り


`updateCameraPos`を呼び出しているのは`CameraComponent .dealBgCamera`メソッドです

```ts:CameraComponent.ts
class CameraComponent extends Component {
:
    public dealBgCamera(): void {
        this.background.updateCameraPos(this.playerX, this.playerY);
    }
}
```

`CameraComponent.dealBgCamera`を呼び出しているのは、同じくCameraComponentの`update`メソッドです。


```ts:CameraComponent.ts
class CameraComponent {
:
    public update(advancedTime: number): void {
        super.update(advancedTime);

        if (this.playerPosChange()) {
            this.playerX = this.entity.x;
            this.playerY = this.entity.y;

            this.dealMoveObjs();
        }

        if (this.playerCellChange()) {
            this.playerCol = this.entity.col;
            this.playerRow = this.entity.row;

            this.dealBgCamera();
        }
    }
```

では、`CameraComponent`はどこで生成されて、だれが`CameraComponent.update`を呼び出しているのかを調べていきます。

結論から言うと`RpgPlayer`クラスの初期化時(`init`メソッド)の`addComponent`です。

```ts:RpgPlayer.ts
class RpgPlayer extends RpgGameObject {
:
    public init(data: any): void {
:
        this.addComponent(ComponentType.Camera);
```

`addComponent`が定義されているのは親クラスの`RpgGameObject`です。

下記のように、まずオブジェクトプール`ObjectPool`からすでに存在している場合はそのオブジェクト取得して、存在していない場合は新規作成をします。

そのオブジェクトをコンポーネントの管理クラス`ComponentSystem`に`addComponent`で管理対象として追加されます。

```ts:RpgGameObject.ts
    public addComponent(componentName: string): void {
        if (this._components[componentName]) {
            return;
        }

        var component: Component = ObjectPool.pop(componentName);
        component.type = componentName;
        component.entity = this;
        component.start();

        ComponentSystem.addComponent(component);

        this._components[componentName] = component;
    }
```

`ComponentSystem.addComponent`では、コンポネント種別毎に生成されたコンポーネントが管理されます。

```ts:ComponentSystem.ts
    public static addComponent(component: Component): void {
        if (!this._Components[component.type]) {
            this._Components[component.type] = [];
        }
        this._Components[component.type].push(component);
    }
```

そしてフレーム毎に呼び出される`ComponentSystem.onEnterFrame`で各コンポーネントの`update`メソッドが呼び出されます。

```ts:ComponentSystem.ts
    private static onEnterFrame(advancedTime: number): void {
:
        this.dealComponents(this._Components[ComponentType.Camera], advancedTime);
:
    }

    private static dealComponents(arr: Component[], advancedTime: number): void {
        if (!arr) {
            return;
        }
        arr.forEach(function (component: Component) {
            if (!component.isRuning) {
                return;
            }

            component.dealTime += advancedTime;
            if (component.dealTime >= component.dealInterval) {
                component.dealTime = 0;
                component.update(advancedTime);
            }
        })
    }
```

つまり
1. 各フレーム毎に、`ComponentSystem.onEnterFrame`が呼ばれ
2. すべてのコンポーネントの`update`が呼びされた結果`CameraComponent.update`が呼ばれ
3. `CameraComponent.dealMoveObjs`でカメラ位置に応じてすべてのオブジェクトの位置がされて
4. `CameraComponent.dealBgObjs`でカメラの可視範囲に応じてマップタイルが破棄されたり生成されたりします

```ts:CameraComponent:dealMoveObjs
    public dealMoveObjs(): void {
        var left: number = Math.max(this.playerX - App.StageUtils.getWidth() * 0.5, 0);
        var top: number = Math.max(this.playerY - App.StageUtils.getHeight() * 0.5, 0);

        left = Math.min(this.background.mapWidth - App.StageUtils.getWidth(), left);
        top = Math.min(this.background.mapHeight - App.StageUtils.getHeight(), top);

        this.moveObjs.forEach(function (obj: egret.DisplayObject) {
            obj.x = -left;
            obj.y = -top;
        })
    }
```

このように`Background`は、moveObjsでもありbackgroundでもあります。

```ts:CameraComponent
    public start(): void {
:
        this.moveObjs.push(this.entity.gameView.getBackground());

        this.background = this.entity.gameView.getBackground();
    }
```