【pixi.js】ドラクエ1式の移動と衝突判定を作ってみよう【蟹歩き】

javascript

RPG 蟹歩き

こんにちは。継続の錬金術士なおキーヌです。

ブログ毎日更新は122日目になります。

ついに令和元年ですね。

私は特に気にせず淡々とゲーム作りをしています。

【pixi.js】プレイヤーを描写して操作してみよう:ドット移動編の続きですね。

ドット移動は前回に実装しましたが、そのままでは衝突判定が面倒くさいのでグリッド移動に切り替えます。

前回の最後にも言いましたが、グリッド移動は割った余りを使って実装出来ます。

まずは移動中かどうかのフラグと向いている方向を保持するクラス変数を作りましょう。

グリッド移動の準備

    private moving:boolean = false;
    private direction:number = 0;

本来はプレイヤークラスを作ってそこに値を保持したほうがいいのですが、
今回はそこまで大きくないゲームなのでコントローラークラスを除いて1つのスクリプトファイルにまとめたいと思います。

グリッド移動の実装

次に、ドット移動していた部分をグリッド移動に変更してみましょう。

    // プレイヤー操作関数
    private PlayerController(delta:number) {
        if(!this.moving) {
            if(Controller_Base.keyAry.indexOf("ArrowLeft") >= 0) {
                this.moving = true;
                this.direction = 0;
            }
            else if(Controller_Base.keyAry.indexOf("ArrowRight") >= 0) {
                this.moving = true;
                this.direction = 1;
            }        
            else if(Controller_Base.keyAry.indexOf("ArrowUp") >= 0) {
                this.moving = true;
                this.direction = 2;
            }
            else if(Controller_Base.keyAry.indexOf("ArrowDown") >= 0) {
                this.moving = true;
                this.direction = 3;
            }
        } else {
            switch(this.direction) {
                case 0:
                    this.PlayerSprite.x += -1;
                    break;
                case 1:
                    this.PlayerSprite.x += 1;
                    break;
                case 2:
                    this.PlayerSprite.y += -1;
                    break;
                case 3:
                    this.PlayerSprite.y += 1;
                    break;
            }
            // GUI分ズレたグリッド位置補正をする
            let playerX = this.PlayerSprite.x - (SYSTEM.guiChipSize*2);
            let playerY = this.PlayerSprite.y - (SYSTEM.guiChipSize*2);
            if(playerX%SYSTEM.chipSize === 0 && playerY%SYSTEM.chipSize === 0 ) {
                this.moving = false;
            }
        }
    }

説明すると、まず移動中かどうかのフラグ(this.moving)で処理を切り替えます。

falseの時はキー入力受付をしていて、キーが押されたらtrueになって移動処理に切り替えます。
移動が完了したらフラグを元に戻してキー入力を受け付けるようにすれば完成ですね。

フラグがtrueになっている間、移動後にxとyともに割った余りが0であれば移動完了とみなします。

チップサイズの分8pxズレているので、チップサイズx2を引いた数値を割った余りで0になるかチェックしています。

ここの計算がおかしいとちゃんとグリッド移動にならないので気を付けましょう。

それではちゃんとグリッド移動できるかコンパイルして実行してみましょう。

……やりました!ドラクエ1ぽい蟹歩きの成功です!

が、今のままでは衝突判定がないのでグリッド移動が出来ても画面外やGUIのところまで移動できてしまいます。

グリッド移動が完成したので、グリッド移動用の衝突判定を実装しましょう。

グリッド移動の衝突判定を作るには

マップの値を1次元配列にしたのは覚えていますでしょうか?

マップ配列と同じ配列をもう1つ作れば、表示用とは別に衝突判定用の配列を作れば判定がすごく楽になります。

そのままマップ配列を判定に使うことも出来るのですが、壁のようで実はすり抜けることが出来るという
ギミックを作る場合は少々面倒くさいことになるので初めのうちに分けておいた方が無難でしょう。

それではマップ配列を多次元配列にしてしまいましょう。

マップを多次元配列にする

多次配列化にともない、大部屋からダンジョンぽくしてみました。

    // マップデータ
    private MapData:number[][] = [
        [
            2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
            2,1,2,1,1,1,1,1,1,2,1,1,1,1,2,1,1,1,2,
            2,1,2,1,1,1,1,1,1,2,1,1,1,1,2,1,1,1,2,
            2,1,2,1,1,1,1,1,1,2,1,1,1,1,2,1,1,1,2,
            2,1,2,1,1,1,1,1,1,2,2,2,2,1,2,2,1,2,2,
            2,1,2,2,1,2,2,1,2,2,1,1,1,1,1,2,1,2,2,
            2,1,1,1,1,2,1,1,2,1,1,1,1,1,1,2,1,2,2,
            2,2,2,2,2,2,1,1,2,1,1,1,1,1,1,1,1,1,2,
            2,1,1,1,1,1,1,1,2,1,1,2,2,2,2,2,2,1,2,
            2,1,2,2,2,2,2,2,2,2,1,2,1,1,1,1,1,1,2,
            2,1,2,1,1,1,1,1,1,2,1,2,1,1,1,1,1,1,2,
            2,1,2,2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2,
            2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,
            2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
        ],
        [
            9,8,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
            9,0,9,0,0,0,0,0,0,9,0,0,0,0,9,0,0,0,9,
            9,0,9,0,0,0,0,0,0,9,0,0,0,0,9,0,0,0,9,
            9,0,9,0,0,0,0,0,0,9,0,0,0,0,9,0,0,0,9,
            9,0,9,0,0,0,0,0,0,9,9,9,9,0,9,9,0,9,9,
            9,0,9,9,0,9,9,0,9,9,0,0,0,0,0,9,0,9,9,
            9,0,0,0,0,9,0,0,9,0,0,0,0,0,0,9,0,9,9,
            9,9,9,9,9,9,0,0,9,0,0,0,0,0,0,0,0,0,9,
            9,0,0,0,0,0,0,0,9,0,0,9,9,9,9,9,9,0,9,
            9,0,9,9,9,9,9,9,9,9,0,9,0,0,0,0,0,0,9,
            9,0,9,0,0,0,0,0,0,9,0,9,0,0,0,0,0,0,9,
            9,0,9,9,9,9,9,9,9,9,0,9,9,9,9,9,9,9,9,
            9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,
            9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
        ],

    ];

手うち面倒くさいですね……

このゲームが完成したらこれ用のマップエディタでも作ろうと思います。

話を戻すと、変数の型がArrayからnumberの多次元配列に型に変えました。

Webプログラミングではあまり多次元配列を使おうと思わないので、型情報が分からずに戸惑ってましたが何とか解決です。

次にマップ生成のメソッドを修正しましょう。

    // マップチップ初期化
    private MapChipsInit(textureData:PIXI.Spritesheet) {
        // y座標折り返しカウント
        let yCount:number = 0;
        // 画面サイズ分のマップチップ配列生成
        for (let i=0; i<this.MapData[0].length; i++ ) {
            // 長いのでテクスチャ名のエイリアスを作っておく
            let textureName = this.getTextureName(textureData.data.meta.name,this.MapData[0][i]);
            // i番目の配列にnewする
            this.MapChips[i] = new PIXI.Sprite(textureData.textures[textureName]);
            // i番目のx座標設定
            this.MapChips[i].x = ((i%SYSTEM.gridX) * SYSTEM.chipSize) + SYSTEM.fixMapPosition;
            // X座標が端っこまで行ったら改行する
            if(i !== 0 && i%SYSTEM.gridX === 0) {
              // y座標折り返しカウントをインクリメント
              yCount++;
            } 
            // i番目のy座標設定
            this.MapChips[i].y = yCount * SYSTEM.chipSize + SYSTEM.fixMapPosition;
            // i番目の横幅設定
            this.MapChips[i].width = SYSTEM.chipSize;
            // i番目の縦幅設定
            this.MapChips[i].height = SYSTEM.chipSize;
        }
    }

変更点は繰り返しの条件式をマップ配列0個めという風に指定してるのと、
getTextureNameの部分はマップチップの描写をしたいので1つ目の添え字は0で2つ目の添え字はループのiにして取り出しています。

この時点では見た目が壁になりましたがまだ衝突判定をしていないのでめり込めます。

多次元配列の2つ目を見て衝突判定をする

それでは衝突判定を作っていきましょう。

マップ描写は多次元配列の1つ目を使いましたが、衝突判定は2つ目の配列を参照します。

    // 移動する先のグリッドの情報を取得する
    private getGridCollision(direction:number) {
        // プレイヤーの現在の座標を取得(GUI位置補正もする)
        let pos: { [key: string]: number; }  = {
            x:( this.PlayerSprite.x - (SYSTEM.guiChipSize*2)) / SYSTEM.chipSize ,
            y:( this.PlayerSprite.y - (SYSTEM.guiChipSize*2)) / SYSTEM.chipSize  

        }
        // 一次元配列の現在値を取得
        let nowPos = pos.x + ( pos.y * SYSTEM.gridX );
        // 判定マップ参照
        let mapLayer = 1;

        switch (direction) {
            case 0:
                if(this.MapData[mapLayer][nowPos - 1] === 9) {
                    return false;
                }
                break;
            case 1:
                if(this.MapData[mapLayer][nowPos + 1] === 9) {
                    return false;
                }
                break;
            case 2:
                if(this.MapData[mapLayer][nowPos - SYSTEM.gridX] === 9) {
                    return false;
                }
                break;
            case 3:
                if(this.MapData[mapLayer][nowPos + SYSTEM.gridX] === 9) {
                    return false;
                }
                break;
        }
        // 条件に何も満たなければ移動可能と判定する
        return true
    }

移動先のグリッドの数値が9であれば移動できないという仕組みになっています。

関数冒頭で計算式を作っていますが、単純にGUI分のズレを修正したプレイヤーの座標をですね。

本来はこういうのもプレイヤークラスで計算したものを保持しておくべきなのですが今回はここに記載しています。

その後、計算した値とグリッドサイズを計算して1次元配列の、
どの添え字にプレイヤーがいるのかをnowPos変数に取得します。

ついでにマップ配列のどのレイヤーを使うかを変数に入れておきましょう。

0から数えるので配列2つ目は1ですね。

これも予めわかっている数値なのでSYSTEMモジュールに組み込んでおいてもいいかもしれません。

こうやって後から1つにまとめたほうがいいなっていうモノが出てくるので、
プログラミングは考えすぎず取り合えずがむしゃらに書くとどこを最適化したらいいのか明確化するので
考えすぎてコーディングが進まない癖のある人はまずはコードを打ち込みまくることをオススメします。

話を戻すと次は引数で受け取った方向、つまり押されたキーによって渡される数値が変わります。

その数値によってswitchで条件分岐してどの方向のグリッドを調べるのかを判定します。

見た目は縦横グリッドですがプログラム的に1次元配列なので調べ方が上下左右で異なります。

左右を調べる場合は現在地点から1つ前後の数値を見ればいいのでプラスマイナス1でいけますね。

しかし上下の場合は1次元配列で考えると難しいです。

二次元配列で考えると横の数が等しいものが何行もあるので、
単純に1列の数だけプラスマイナスしてやれば現在地点の上か下かの数値を得ることが出来ます。

ちょっと説明が下手なので申し訳ないのですが、SYSTEM.gridXで横幅の数を取得できるので、
現在地点からこれを増減してやれば動こうとしている場所の判定が出来ます。

もし、移動する先の数値が9であればfalseを返してそれ以外であれば条件満たさないので
関数の最後にあるtrueを返すようにしています。

プレイヤー移動関数を修正しよう

グリッド衝突判定が完成したので、プレイヤーの移動関数を修正してみましょう。

押されたキーのところが先ほど作ったgetGridCollision関数にしているので、
そこに押された時にdirection変数に値を保持して衝突判定関数に渡しています。


    // プレイヤー操作関数
    private PlayerController(delta:number) {
        let moveFlag:boolean = false;
        if(!this.moving) {
            if(Controller_Base.keyAry.indexOf("ArrowLeft") >= 0) {
                this.direction = 0;
                moveFlag = this.getGridCollision(this.direction);
            }
            else if(Controller_Base.keyAry.indexOf("ArrowRight") >= 0) {
                this.direction = 1;
                moveFlag = this.getGridCollision(this.direction);
            }        
            else if(Controller_Base.keyAry.indexOf("ArrowUp") >= 0) {
                this.direction = 2;
                moveFlag = this.getGridCollision(this.direction);
            }
            else if(Controller_Base.keyAry.indexOf("ArrowDown") >= 0) {
                this.direction = 3;
                moveFlag = this.getGridCollision(this.direction);
            }
        } else {
            switch(this.direction) {
                case 0:
                    this.PlayerSprite.x += -1;
                    break;
                case 1:
                    this.PlayerSprite.x += 1;
                    break;
                case 2:
                    this.PlayerSprite.y += -1;
                    break;
                case 3:
                    this.PlayerSprite.y += 1;
                    break;
            }
            // GUI分ズレたグリッド位置補正をする
            let playerX = this.PlayerSprite.x - (SYSTEM.guiChipSize*2);
            let playerY = this.PlayerSprite.y - (SYSTEM.guiChipSize*2);
            if(playerX%SYSTEM.chipSize === 0 && playerY%SYSTEM.chipSize === 0 ) {
                this.moving = false;
            }
        }

        // ぶつかってなかったら移動フラグをONにする
        if (moveFlag) { this.moving = true; }

    }

もし移動先が移動可能であればtrueが返ってくるので、関数の最後にある条件式が満たされるので
移動開始フラグをONにします。

次のフレームにまたこの関数が呼び出されるのですが、関数冒頭でmoveFlagにfalseを入れていることと
前フレームでmoving変数にtrueが入っているので移動が終わるまではキー操作が出来ない状態になります。

ここまで来たら一度コンパイルして実行して歩いてみましょう。

問題なく壁にぶつかって道が歩ける状態であれば衝突判定は完璧です!

pixi.js コンパイル結果

余談:イベントを作るためにやること

このままだとダンジョンを歩くだけでゲームですらないため、触れたり話しかけたりすると何かが起こるイベントを実装しなければいけません。

勘の鋭い人であれば、この衝突判定を学んだ時にもうイベントの実装方法はわかったと思います。

答えを書くと、多次元配列のマップをもう1つ増やせばいいだけです。

もう少し深堀すると、今回マップ描写用配列・衝突判定用配列を用意しました。

今後それぞれをレイヤーと読んでいこうと思います。

描写用レイヤー、衝突用レイヤーとつくったのですから同じ形のイベント用レイヤーを作れば良いのです。

衝突判定は移動する前に判定しました。

イベントの場合は同じく先に何があるのかを判定して、例えば落とし穴とか毒の沼とかだった場合
移動完了後にイベントを発動させたいですが、宝箱やNPCだったりすると移動させたくないのです。

移動先のイベントを判定してswitchで処理を分岐してどうするかを記述すればイベントの完成ですね。

最後に今回のソースコードを記載しておきます。

それでは。

import * as PIXI from 'pixi.js'
import { SYSTEM } from '../../config/system';

import Controller_Base from './Controller/Controller_Base';

class PIXI_MainProcess {

    /**
     * ----------------------------------------------------
     * エイリアス作成
     * ----------------------------------------------------
     *  */ 
    // pixi.jsアプリケーション
    private PIXI_Application = PIXI.Application
    // ローダー
    private PIXI_loader = PIXI.loader
    // リソースローダー
    private PIXI_resources= PIXI.loader.resources

    /**
     * ----------------------------------------------------
     * クラス変数
     * ----------------------------------------------------
     *  */ 
    // pixiアプリケーション生成
    private App:PIXI.Application = this.pixiApplicationCreate(SYSTEM.width,SYSTEM.height);

    //--------------------------------------------

    // プレイヤー用変数
    private PlayerSprite:PIXI.extras.AnimatedSprite = new PIXI.extras.AnimatedSprite([PIXI.Texture.EMPTY]);
    private moving:boolean = false;
    private direction:number = 0;

    // マップ用変数
    private MapChips:Array<PIXI.Sprite> = [];

    // Window背景変数
    private GuiFrameBacks:Array<PIXI.Graphics> = [];

    // GUI用変数
    private GuiFrame:Array<PIXI.Sprite> = [];

    // マップデータ
    private MapData:number[][] = [
        [
            2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
            2,1,2,1,1,1,1,1,1,2,1,1,1,1,2,1,1,1,2,
            2,1,2,1,1,1,1,1,1,2,1,1,1,1,2,1,1,1,2,
            2,1,2,1,1,1,1,1,1,2,1,1,1,1,2,1,1,1,2,
            2,1,2,1,1,1,1,1,1,2,2,2,2,1,2,2,1,2,2,
            2,1,2,2,1,2,2,1,2,2,1,1,1,1,1,2,1,2,2,
            2,1,1,1,1,2,1,1,2,1,1,1,1,1,1,2,1,2,2,
            2,2,2,2,2,2,1,1,2,1,1,1,1,1,1,1,1,1,2,
            2,1,1,1,1,1,1,1,2,1,1,2,2,2,2,2,2,1,2,
            2,1,2,2,2,2,2,2,2,2,1,2,1,1,1,1,1,1,2,
            2,1,2,1,1,1,1,1,1,2,1,2,1,1,1,1,1,1,2,
            2,1,2,2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2,
            2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,
            2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
        ],
        [
            9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
            9,0,9,0,0,0,0,0,0,9,0,0,0,0,9,0,0,0,9,
            9,0,9,0,0,0,0,0,0,9,0,0,0,0,9,0,0,0,9,
            9,0,9,0,0,0,0,0,0,9,0,0,0,0,9,0,0,0,9,
            9,0,9,0,0,0,0,0,0,9,9,9,9,0,9,9,0,9,9,
            9,0,9,9,0,9,9,0,9,9,0,0,0,0,0,9,0,9,9,
            9,0,0,0,0,9,0,0,9,0,0,0,0,0,0,9,0,9,9,
            9,9,9,9,9,9,0,0,9,0,0,0,0,0,0,0,0,0,9,
            9,0,0,0,0,0,0,0,9,0,0,9,9,9,9,9,9,0,9,
            9,0,9,9,9,9,9,9,9,9,0,9,0,0,0,0,0,0,9,
            9,0,9,0,0,0,0,0,0,9,0,9,0,0,0,0,0,0,9,
            9,0,9,9,9,9,9,9,9,9,0,9,9,9,9,9,9,9,9,
            9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,
            9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
        ],

    ];


    //--------------------------------------------

    // コンストラクタ
    constructor() {

        // 初期化プロセス
        this.initializePIXI();
    }

    // 初期化プロセス
    private initializePIXI() {
        // ピクセル倍率変更
        this.pixelScaleUP();

        // htmlのbodyにPIXIAPPを追加
        document.body.appendChild(this.App.view);

        // Sprite初期化
        this.loadingResrouces();

    }

    // PIXIアプリケーションオブジェクト作成
    private pixiApplicationCreate(screenWidth:number,screenHeight:number) {
        return new this.PIXI_Application({
            width:screenWidth,
            height:screenHeight,
            antialias: false,
            transparent: false,
            resolution: 2
            }
        );
    }

    // ピクセル倍率を2倍に変更
    private pixelScaleUP() {
        this.App.renderer.roundPixels = true;
        this.App.renderer.resize(SYSTEM.width, SYSTEM.height);
        this.App.stage.scale.set(1,1);
        this.App.stage.interactive = true;
        PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
    }


    // // セットアップ関数
    private setup() {
        this.App.ticker.add(delta => this.gameloop(delta));
    }

    // メインループ
    private gameloop(delta:number) {
        // this.GuiFrame[6].rotation += 0.01;
        // console.log(this.GuiFrame[6].rotation);
        // console.log(this.pressedKeyCount(Controller_Base.keyAry));
        this.PlayerController(delta);

    }

    // PIXIローダー
    private loadingResrouces() {
        // エイリアスで設定したローダーの呼び出し
        this.PIXI_loader
        // addメソッドの引数にロードしたい画像ファイルパスを渡す
        .add(__dirname + "/src/assets/images/games/mapchip/testmap.png")
        .add(__dirname + "/src/assets/images/games/system/system.png")
        .add(__dirname + "/src/assets/images/games/actor/actor01.png")
        .add(__dirname + "/src/json/spritesheets/mapchip/testmap.json")
        .add(__dirname + "/src/json/spritesheets/system/system.json")
        .add(__dirname + "/src/json/spritesheets/actor/actor01.json")
        // チェーンメソッドでプログレス機能を稼働
        .on("progress", (loader, resource)=>{
            console.log("progress: " + loader.progress + "%"); 
        })
        // ロード終了後に実行する関数を指定する
        .load(() => {
            this.spriteSetup();
            //  ループ関数起動
            this.setup();
        })
    }

    // テクスチャネームを返す
    private getTextureName(str:string, i:number) {
        return `${str}${i}.png`;
    }


    // セットアップ関数
    private spriteSetup() {
        // テクスチャを準備する
        let mapchips:PIXI.Spritesheet | undefined = this.PIXI_resources[__dirname + "/src/json/spritesheets/mapchip/testmap.json"].spritesheet;
        let systemGraphics:PIXI.Spritesheet | undefined = this.PIXI_resources[__dirname + "/src/json/spritesheets/system/system.json"].spritesheet;
        let actorGraphics:PIXI.Spritesheet | undefined = this.PIXI_resources[__dirname + "/src/json/spritesheets/actor/actor01.json"].spritesheet;
        if(mapchips !== undefined && systemGraphics !== undefined && actorGraphics !== undefined ) {
            this.MapChipsInit(mapchips);
            this.GuiFrameInit(systemGraphics);
            this.ActorSpriteInit(actorGraphics);
        }
        // オブジェクトをステージに追加
        this.addStageInit();
    }

    // プレイヤー初期化
    private ActorSpriteInit(textureData:PIXI.Spritesheet) {
        let textureActors:Array<PIXI.Texture> = [];
        textureActors.push(textureData.textures["actor01_c0.png"]);
        textureActors.push(textureData.textures["actor01_c1.png"]);
        this.PlayerSprite = new PIXI.extras.AnimatedSprite(textureActors);
        this.PlayerSprite.setTransform(SYSTEM.guiChipSize*2 + SYSTEM.chipSize,SYSTEM.guiChipSize*2 + SYSTEM.chipSize);
        this.PlayerSprite.animationSpeed = 0.05;
        this.PlayerSprite.play();
    }

    // プレイヤー操作関数
    private PlayerController(delta:number) {
        let moveFlag:boolean = false;
        if(!this.moving) {
            if(Controller_Base.keyAry.indexOf("ArrowLeft") >= 0) {
                this.direction = 0;
                moveFlag = this.getGridCollision(this.direction);
            }
            else if(Controller_Base.keyAry.indexOf("ArrowRight") >= 0) {
                this.direction = 1;
                moveFlag = this.getGridCollision(this.direction);
            }        
            else if(Controller_Base.keyAry.indexOf("ArrowUp") >= 0) {
                this.direction = 2;
                moveFlag = this.getGridCollision(this.direction);
            }
            else if(Controller_Base.keyAry.indexOf("ArrowDown") >= 0) {
                this.direction = 3;
                moveFlag = this.getGridCollision(this.direction);
            }
        } else {
            switch(this.direction) {
                case 0:
                    this.PlayerSprite.x += -1;
                    break;
                case 1:
                    this.PlayerSprite.x += 1;
                    break;
                case 2:
                    this.PlayerSprite.y += -1;
                    break;
                case 3:
                    this.PlayerSprite.y += 1;
                    break;
            }
            // GUI分ズレたグリッド位置補正をする
            let playerX = this.PlayerSprite.x - (SYSTEM.guiChipSize*2);
            let playerY = this.PlayerSprite.y - (SYSTEM.guiChipSize*2);
            if(playerX%SYSTEM.chipSize === 0 && playerY%SYSTEM.chipSize === 0 ) {
                this.moving = false;
            }
        }

        // ぶつかってなかったら移動フラグをONにする
        if (moveFlag) { this.moving = true; }

    }

    // 移動する先のグリッドの情報を取得する
    private getGridCollision(direction:number) {
        // プレイヤーの現在の座標を取得(GUI位置補正もする)
        let pos: { [key: string]: number; }  = {
            x:( this.PlayerSprite.x - (SYSTEM.guiChipSize*2)) / SYSTEM.chipSize ,
            y:( this.PlayerSprite.y - (SYSTEM.guiChipSize*2)) / SYSTEM.chipSize  

        }
        // 一次元配列の現在値を取得
        let nowPos = pos.x + ( pos.y * SYSTEM.gridX );
        // 判定マップ参照
        let mapLayer = 1;

        switch (direction) {
            case 0:
                if(this.MapData[mapLayer][nowPos - 1] === 9) {
                    return false;
                }
                break;
            case 1:
                if(this.MapData[mapLayer][nowPos + 1] === 9) {
                    return false;
                }
                break;
            case 2:
                if(this.MapData[mapLayer][nowPos - SYSTEM.gridX] === 9) {
                    return false;
                }
                break;
            case 3:
                if(this.MapData[mapLayer][nowPos + SYSTEM.gridX] === 9) {
                    return false;
                }
                break;
        }
        // 条件に何も満たなければ移動可能と判定する
        return true
    }



    // マップチップ初期化
    private MapChipsInit(textureData:PIXI.Spritesheet) {
        // y座標折り返しカウント
        let yCount:number = 0;
        // 画面サイズ分のマップチップ配列生成
        for (let i=0; i<this.MapData[0].length; i++ ) {
            // 長いのでテクスチャ名のエイリアスを作っておく
            let textureName = this.getTextureName(textureData.data.meta.name,this.MapData[0][i]);
            // i番目の配列にnewする
            this.MapChips[i] = new PIXI.Sprite(textureData.textures[textureName]);
            // i番目のx座標設定
            this.MapChips[i].x = ((i%SYSTEM.gridX) * SYSTEM.chipSize) + SYSTEM.fixMapPosition;
            // X座標が端っこまで行ったら改行する
            if(i !== 0 && i%SYSTEM.gridX === 0) {
              // y座標折り返しカウントをインクリメント
              yCount++;
            } 
            // i番目のy座標設定
            this.MapChips[i].y = yCount * SYSTEM.chipSize + SYSTEM.fixMapPosition;
            // i番目の横幅設定
            this.MapChips[i].width = SYSTEM.chipSize;
            // i番目の縦幅設定
            this.MapChips[i].height = SYSTEM.chipSize;
        }
    }

    // GUIフレーム用意関数
    private GuiFrameInit(textureData:PIXI.Spritesheet) {

        // iを8で割った余りによって生成するものを変化させる
        for (let i=0; i<SYSTEM.guiParams.length; i++) {
            if(i%8 < 4) {
                switch(i%8) {
                    case 0:
                        // フレーム左上
                        this.GuiFrame[i] = new PIXI.Sprite(textureData.textures["windowLeftTop.png"]);
                        break;
                    case 1:
                        // フレーム左下
                        this.GuiFrame[i] = new PIXI.Sprite(textureData.textures["windowLeftBottom.png"]);
                        break;
                    case 2:
                        // フレーム右上
                        this.GuiFrame[i] = new PIXI.Sprite(textureData.textures["windowRightTop.png"]);
                        break;
                    case 3:
                        // フレーム右下
                        this.GuiFrame[i] = new PIXI.Sprite(textureData.textures["windowRightBottom.png"]);
                        break;
                }
            } else {
                switch(i%8) {
                    case 4:
                        // フレーム上部繰り返し用
                        this.GuiFrame[i] = new PIXI.extras.TilingSprite(textureData.textures["windowRepeatFrameTop.png"]);
                        break;
                    case 5:
                        // フレーム下部繰り返し用
                        this.GuiFrame[i] = new PIXI.extras.TilingSprite(textureData.textures["windowRepeatFrameBottom.png"]);
                        break;
                    case 6:
                        // フレーム左部繰り返し用
                        this.GuiFrame[i] = new PIXI.extras.TilingSprite(textureData.textures["windowRepeatFrameTop.png"]);
                        break;
                    case 7:
                        // フレーム右部繰り返し用
                        this.GuiFrame[i] = new PIXI.extras.TilingSprite(textureData.textures["windowRepeatFrameBottom.png"]);
                        break;
                }                
            }
        }

        // ウィンドウ背景スプライト生成
        for (let i=0; i<SYSTEM.windowBack.length; i++) {
            this.GuiFrameBacks[i] = new PIXI.Graphics();
        }

        // ウィンドウ背景設定
        this.GuiFrameBacks.map((item,key) => {
            item.x = SYSTEM.windowBack[key].x;
            item.y = SYSTEM.windowBack[key].y;
            item.width = SYSTEM.windowBack[key].width;
            item.height = SYSTEM.windowBack[key].height;
            item.alpha = SYSTEM.windowBack[key].alpha;
            item.beginFill(SYSTEM.windowBack[key].color);
            item.drawRect(0,0,SYSTEM.windowBack[key].width,SYSTEM.windowBack[key].height);
            item.endFill();
        });

        // 全てのGUIチップサイズを一旦設定
        this.GuiFrame.map( (item,key) => {
            item.x = SYSTEM.guiParams[key].x;
            item.y = SYSTEM.guiParams[key].y;
            item.width = SYSTEM.guiParams[key].width;
            item.height = SYSTEM.guiParams[key].height;
            item.rotation = SYSTEM.guiParams[key].rotation;
            item.anchor.set( SYSTEM.guiParams[key].anchor,SYSTEM.guiParams[key].anchor);
        });
    }

    // 初回ステージ追加
    private addStageInit() {
        this.addStageArray(this.MapChips);
        this.addStageArray(this.GuiFrame);
        this.addStageArrayGraphic(this.GuiFrameBacks);
        this.App.stage.addChild(this.PlayerSprite);
    }

    // 配列ステージ追加メソッド
    private addStageArray(ary:Array<PIXI.Sprite>) {
        ary.map( (item) => {
            this.App.stage.addChild(item);
        });
    }
    // 配列ステージ追加メソッド
    private addStageArrayGraphic(ary:Array<PIXI.Graphics>) {
        ary.map( (item) => {
            this.App.stage.addChild(item);
        });
    }

}

export default PIXI_MainProcess;

const Controller:Controller_Base = new Controller_Base; 
const GameFrame:PIXI_MainProcess = new PIXI_MainProcess;