PlayCanvasでサンディちゃんを歩かせるゲームを作るぞ☆（第4回）

# こんなん作ってます。
https://playcanv.as/b/iAPwWXqY/
※本解説よりも開発が進んでいることもございますのでご了承ください。

# これはまだゲームではない。
サンディちゃんを操作できるところまで来ましたが、そろそろゲームっぽくしないといけません。
とりあえず、迷路を作ってみましょうか。（テキトー）
壁・スタート・ゴールの配置をJSONファイルで定義、ゲーム開始時にプログラムにファイルを読み込ませて迷路を作らせます。


# 迷路データ（JSON）
JSONファイルで迷路を定義します。
12×12マスの方眼紙上に正方体を置いていくのをイメージしてください。

JSONファイルには

- ワールド座標上の壁の設置をはじめる位置
- マスの数（縦横、今回は12×12）
- 1マスのサイズ
- マス上の配置（壁・スタート・ゴール・なにもなし）
- 行列

```js
{
    "startPosX":1,
    "startPosZ":1,
    "offsetX":-24,//マスを置き始めるワールドX座標
    "offsetZ":-24,//マスを置き始めるワールドZ座標
    "unitX":12,//マスの数（X軸方向）
    "unitZ":12,//マスの数（Z軸方向）
    "sizeX":4,//マスの大きさ（X軸方向）
    "sizeZ":4,//マスの大きさ（Z軸方向）
    "matrix":"999999999999910000000009900999999009909900000009900099999999909000000009909099099999909090099009909090000009909999009909900000900929999999999999"//行列
}
```

「行列」っていうのはマスの配置そのもので、もとは

```js
999999999999
910000000009
900999999009
909900000009
900099999999
909000000009
909099099999
909090099009
909090000009
909999009909
900000900929
999999999999
```
という数字の並びを1行に直しただけのものです。
9が壁、1がスタート地点、2がゴール地点、0がなにもなしです。

ASSETSのsrcディレクトリ内にいくつか作っておきます。
![s4_0５.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110583/90bf0871-c5c3-b87c-ebef-16c5d09e5c02.png)

これらの設定をプログラム起動時に読み込ませて迷路を作成させます。


# エディタで素材を用意する
プログラムで迷路を作成する際に使う素材をエディタで作っておきます。

templatesディレクトリを作成し、そのなかに「wall」「start」「goal」を作成しておきます。
![templates.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110583/2c39fed7-9dc4-3b5c-b214-11482bb9a898.jpeg)

ライトグリーンの円柱がスタート地点のオブジェクト、赤のコーンがゴール、グレーの立方体は壁に使います。

templates直下のエンティティはプログラムが使うコピー元としてしか使わないので、templatesのEnabledをオフにして隠してしまいましょ。


# 生成プログラムを作成
RootでScriptコンポーネントを追加します。
![s4_02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110583/010a51a4-318e-f43b-6ce8-82d8c1c390d0.png)

「<>Root」という表示になると思います。

次にインスペクターで「createMaze」という名前のScriptを作成します。
![s4_04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110583/fe1bd57e-ea9f-e299-b689-abc1f98115fd.png)
![s4_03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110583/fbbd00e6-a8ef-cbf3-2f27-2e9b03d24f94.png)

「createMaze.js」をコーディングします。

まずスクリプト属性「mazeData」を作成しましょう。

```createMaze.js
CreateMaze.attributes.add('mazeData', {
    type: 'asset',
    assetType: 'json'
});
```

属性を作成したあとエディタ上でParseをするとmazeDataの項目が出てくるので、そこにASSETS上のJSONファイルをドラッグ＆ドロップします。
![s4_06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110583/c395fe35-0c9a-b158-20dc-6eb50594dc1c.png)

ここでセットされたJSONファイルがゲーム起動時にプログラムに読み込まれます。

次に迷路生成部分。

```createMaze.js
// initialize code called once per entity
CreateMaze.prototype.initialize = function() {
    // JSONデータ取得
    var mazeData = this.mazeData.resource;
    
    // templates以下の素材（非表示）のインスタンスを取得しておく
    var templates = this.app.root.findByName('templates');
    var wall = templates.findByName('wall');
    var start = templates.findByName('start');
    var goal = templates.findByName('goal');

    //スタート位置を格納
    var startPos = new pc.Vec3();

    //マスの数だけループして素材を配置していく
    for (var z = 0; z < mazeData.unitZ; z++) {
        for (var x = 0; x < mazeData.unitX; x++) {
            switch(mazeData.matrix.charAt(mazeData.unitZ * z + x)){//if(Math.random() > 0.5){
                case "9":
                    // wallをクローンします
                    var w = wall.clone();
                    w.setPosition(
                        mazeData.offsetX + x * mazeData.sizeX + mazeData.sizeX/2, 
                        0, 
                        mazeData.offsetZ + z * mazeData.sizeZ + mazeData.sizeZ/2);
                    this.app.root.addChild(w);
                    break;
                case "2":
                    // goalをクローンします
                    var g = goal.clone();
                    g.setPosition(
                        mazeData.offsetX + x * mazeData.sizeX + mazeData.sizeX/2, 
                        0, 
                        mazeData.offsetZ + z * mazeData.sizeZ + mazeData.sizeZ/2);
                    this.app.root.addChild(g);
                    break;
                case "1":
                    // startをクローンします
                    var s = start.clone();
                    s.setPosition(
                        mazeData.offsetX + x * mazeData.sizeX + mazeData.sizeX/2, 
                        0, 
                        mazeData.offsetZ + z * mazeData.sizeZ + mazeData.sizeZ/2);

                    // シーンの階層にタイルを追加します。
                    this.app.root.addChild(s);
                    
                    //プレイヤー用にスタート位置を取得しておく
                    startPos = s.getPosition();

                    break;
                
            }
        }
    }

    //スタート位置にサンディちゃんを置く
    var mainCharacter = this.app.root.findByName('Player');
    startPos.y = startPos.y+1;
    mainCharacter.rigidbody.teleport(startPos);
};
```

ついでにスタート地点のオブジェクトの位置にサンディちゃんを連れてきておきます。

どや！
![s4_play00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110583/fb226aa1-cfe5-7dba-16d7-dc03a0b8214e.png)

左上がスタート、右下がゴールとなります。


# 画面の二分割（プレイヤーの尻を追うカメラと俯瞰カメラ）
このままでは見下ろし型の2Dゲームと変わりません。
3Dやってんだぜっっってなことでカメラを追加してTPS視点も追加してみましょう。

エディタから、カメラエンティティをもう1個追加します。
![s4_09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110583/a1a62305-49c4-827c-740c-f45e82cb7f6e.png)

ポイントはサンディちゃんの子エンティティとして、後ろに配置することです。
カメラをサンディちゃんの後ろに固定することで、サンディちゃんの尻を常におっかけることになります。（このド変態☆）
Positionは[X:0, Y:2.5, Z:-5]、Rotationが[X:-10, Y:180, Z:0]です。

二つのカメラのViewportをいじります。
デフォのカメラが[X:0.5, Y:0, W:0.5, H:1]、新カメラが[X:0, Y:0, W:0.5, H:1]として水平方向に二つ並べるようにします。

![s4_play01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110583/c99ae88b-6c09-987b-e9c4-d3baf725dafe.png)

少々間抜けな光景ですが、画面の二分割ができました。

お尻は・・・見えませんね・・・
続きはご自分でコーディングしてください（笑）