物理エンジンp2を使ったジャンプゲームの公式サンプル"p2-jump-game"を読む (1) 起動処理

# デモ

[Egret Developer Center(白鹭 开发者 中心) > Example(示例) > Demo(功能演示) > 2D](http://developer.egret.com/cn/list/example/id/13)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/302078/3038a443-7ab1-7244-3dd6-a068257975ea.png)


![image.png](https://qiita-image-store.s3.amazonaws.com/0/302078/058b6e1a-1f77-0e8c-8253-94f403e188dc.png)

# 注意事項
古いサンプルなので、エンジンは2.5.7が必要です。現在の最新版は5.2.12なので、新しくEgret環境をインストールすると、2.5.7はインストールされていません。Egret LauncherのEnginesタブからインストールしてください。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/302078/44507c72-b06c-f66f-9e22-2ca658c8470b.png)

# 読みました

## 構造
srcディレクトリ内にgameとcityディレクトリがあります。ゲームの実装はgameディレクトリ、ライブラリ的な汎用コードがcityディレクトリ内にあります。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/302078/12811ef8-c4a6-c5f9-d27b-0524d2169273.png)

`Bootstart.ts`と`GameCore.ts`の二つが肝です。

## 起動は？
いままでソースコードをみてきた中では `class Main extends egret.DisplayObjectContainer`が起動クラスでした。ところが今回は`class Bootstrap extends egret.DisplayObjectContainer`というクラスがどうもそのようです。

ルートディレクトリの`index.html`を確認してみます。

```html:index.html
    <div style="margin: auto;width: 100%;height: 100%;" class="egret-player"
         data-entry-class="game.jumper.Bootstrap"
         data-orientation="auto"
         data-scale-mode="showAll"
         data-resolution-mode="retina"
         data-frame-rate="30"
         data-content-width="480"
         data-content-height="800"
         data-show-paint-rect="false"
         data-multi-fingered="2"
         data-show-fps="false" data-show-log="false"
         data-log-filter="" data-show-fps-style="x:0,y:0,size:30">
    </div>
    <script>
        egret.runEgret();
    </script>
```

`data-entry-class="game.jumper.Bootstrap"`で画面のクラスを指定するようです。



よく見るとここで、フレームレート、コンテンツサイズや解像度も設定できるようです。

DeveloperCenterを検索してみた感じでは[このページ](http://developer.egret.com/cn/github/egret-docs/Engine2D/projectConfig/indexFile/index.html)が`index.html`の仕様のようです。

## 起動処理

ここは定番処理です。

```ts:Bootstart.ts
        public constructor() {
            super();
            this.addEventListener(egret.Event.ADDED_TO_STAGE, this.onAddToStage, this);
        }

        private onAddToStage(event:egret.Event) {
```

> [egret.Event.ADDED_TO_STAGE](http://developer.egret.com/cn/apidoc/index/name/egret.Event)
> [static] 表示オブジェクト(DisplayObject)がステージ表示リストに直接追加されたとき、または表示オブジェクト(DisplayObject)を含むサブツリーがステージ表示リストに追加されたときに発生するイベント

この場合だと、Bootstrapクラスのインスタンスがステージ表示リストに追加された時に発生するイベントです。
HTMLでいうとloadイベントみたいな使い方ですかね。

なお、最新のサンプルでは`addEventListener`と`removeEventListener`の組み合わせではなく、`once`を使っているようです。こうすると呼び出された後にremoveする必要がありません。

```ts
        this.once(egret.Event.ADDED_TO_STAGE, this.onAddToStage, this);
```

## リソースの読み込み

ローディング画面を表示して、その間に`resource/resource.json`に記載されているリソース一覧を事前に読み込むための定番コードがこちら。

### 読み込み開始
```ts
        private onAddToStage(event:egret.Event) {
            // 読み込み画面の表示
            this.loadingView = new city.disp.LoadingUI(this.stage.stageWidth, this.stage.stageHeight);
            this.stage.addChild(this.loadingView);

            // リソース定義ファイルの読み込みの開始
            RES.addEventListener(RES.ResourceEvent.CONFIG_COMPLETE, this.onConfigComplete, this);
            RES.loadConfig("resource/resource.json", "resource/");
```

リソース定義ファイルの読み込み後に、リソースを事前ロードする

```ts

        private onConfigComplete(event:RES.ResourceEvent):void {
            // こことかもともとのaddEventListnerをonceにすれば不要になる
            RES.removeEventListener(RES.ResourceEvent.CONFIG_COMPLETE, this.onConfigComplete, this);

            // リソース読み込み完了ハンドラーを設定する
            // ここもonceでいい気がする
            RES.addEventListener(RES.ResourceEvent.GROUP_COMPLETE, this.onResourceLoadComplete, this);
            // リソースの読み込み進捗を受け取るハンドラー
            // ここは複数回呼ばれるのでonceはなし
            RES.addEventListener(RES.ResourceEvent.GROUP_PROGRESS, this.onResourceProgress, this);
            RES.loadGroup("preload");
        }
```

### 読み込みの終了

リソースの読み込みが完了したら、`onConfigCompilete`で設定した`onResourceLoadComplete`が呼び出されます。
ローディング画面を消去し、不要なハンドラーを削除して、ゲームの開始メソッド`createGameScene`を呼び出します。

```ts
        private onResourceLoadComplete(event:RES.ResourceEvent):void {
            if (event.groupName == "preload") {
                // ローディング画面の消去
                this.stage.removeChild(this.loadingView);
                // 不要なハンドラーの削除
                RES.removeEventListener(RES.ResourceEvent.GROUP_COMPLETE, this.onResourceLoadComplete, this);
                RES.removeEventListener(RES.ResourceEvent.GROUP_PROGRESS, this.onResourceProgress, this);
                // ゲームの開始メソッドを呼ぶ
                this.createGameScene();
            }
        }
```

### リソースの定義

リソース定義ファイルの説明は[こちら](http://developer.egret.com/cn/github/egret-docs/extension/RES/loadConfig/index.html)から

```json:resource/resource.json
{
"resources":
	[
       {"name":"kuchipatchi1.png","type":"image","url":"assets/kuchipatchi1.png"}
		,{"name":"rects","type":"sheet","url":"assets/color-rectangles.json"}
	],

"groups":
	[
        {"name":"preload","keys":"rects,kuchipatchi1.png"}
	]
}
```

### 追記

[リソース管理フレームワーク(资源管理框架)](http://developer.egret.com/cn/github/egret-docs/extension/RES/newres/index.html)によると、イベントを使わずに`Promise`または`async/await`でも記載できるようです。超便利ですね。

```ts
RES.loadConfig().then(()=>{
  RES.getResAsync("assets/bg.jpg");
}).then(()=>{
  RES.getResAsync("assets/icon.jpg");
});

await RES.loadConfig()
await RES.getResAsync("assets/bg.jpg");
await RES.getResAsync("assets/icon.jpg");
```