【pixi.js】イベントシステムの構築:イベント衝突判定編
こんにちは。継続の錬金術士なおキーヌです。
ブログ毎日更新は124日目になります。
【pixi.js】イベントシステムの構築:描写編の続きですね。
イベントの表示とイベントレイヤーの塗り替えが出来たので今度は決定キーを押して目の前のイベントを調べられるようにしてみましょう。
これが出来れば結構RPGぽくなりますね。
PCゲームでの決定ボタンってエンターかzキーが多いので今回はzキーにします。
zキーとZキーは別物なので注意してください。
前回のダウンロードしてもらったモノの中に、コントローラークラスにzキーを押せるように変更したものを入れているので、
早速プレイヤー操作の部分にzキーの分岐を作りましょう。
キー入力でイベントを調べる
上下左右の条件分岐をしているところを見てください。
// プレイヤー操作関数
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 if(Controller_Base.keyAry.indexOf("z") >= 0) {
console.log("z押された");
this.getGridEvent(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; }
}
増えたのは「Controller_Base.keyAry.indexOf("z")」のところだけですが省略しちゃうとわかり辛くなるので、
一旦関数全部載せておきます。
zが押されたかどうかが分かりやすいようにconsole.logで押したことが分かるようにしています。
上下左右キーの場合は「getGridCollision」メソッドで衝突判定を調べていますが、
zキーは「getGridEvent」を呼び出しています。
その際に現在向いている方向を渡しているのに注目してください。
それでは「getGridEvent」を見てみましょう。
イベントを調べる関数
イベントを調べるには目の前のイベントの数値を知る必要があります。
zキーを押したときにプレイヤーの向きを渡しているので、
その数値によってどこの数値を参照するかわかればいいですね。
これは衝突判定を取るときにも使っているので省略します。
衝突判定の時とは違って「getDirectionGridNumber」の第二引数に渡しているのは、
マップ配列の数値はイベントレイヤーの数値になります。
これはSYSTEMモジュールで定義しています。
まだテキスト表示の仕組みは作っていないので一旦console.logで代用しておきましょう。
ここで調べられるシステムを作っておけば後はここをテキストに置き換えたりすればいいです。
// 向いている方向にイベントがあるかどうか調べる
private getGridEvent(direction:number) {
// 判定マップ参照
let gridNo:number | undefined = this.getDirectionGridNumber(direction,SYSTEM.mapLayer.event);
switch (gridNo) {
case 0:
break;
case 1:
console.log("パカパカ宝箱");
break;
case 2:
console.log("透明イベントだよ");
break;
case 3:
break;
default :
return 0;
}
}
とりあえずイベントは2つでid1と2しかいないので調べられたときにそれぞれのidを元にイベントが発動するようになっています。
現時点ではキー操作の制御をしていないので、長押ししているとすさまじい速さでイベントが発動してしまいます。
キーを押したときに1回だけ発動するようにして、キーが離されるまではそのキーを無効にする等の処理を作らなければいけません。
次回までにコントローラークラスの修正を行っておきます。
出来るなら自力で実装してもらっても構いません。
ここまで出来たら一度コンパイルして宝箱の前でzキーを押してコンソールログに「パカパカ宝箱」と表示されたら完璧です。
1歩下に行ってもう一度zキーを押して透明イベントのことも確認できればOKです。
イベントの衝突判定について
現時点ではプレイヤーは衝突判定しかみていないので、宝箱もすり抜けてしまいます。
プレイヤーの方が後に追加しているので宝箱の上に乗ってしまいますね。
ファミコン時代のドラクエ1~4であれば上に乗って調べるをすることで宝箱を開いていましたが、
5以降では前で調べるに変化していますね。
FFでは1からやっていましたがドラクエでは4までずっとその仕様でやってた理由は
ドラクエだからってことなのでしょうかね。
現在作っている者はドラクエ1と動きが一緒なのでこのまま足元に来てから調べて取るタイプにしてもいいですが、
今後のことも考えて宝箱は上に乗れないようにしておきましょう。
実装方法は複数あります。
イベントスプライト生成時にイベントレイヤーの数値を書き換えたのを覚えているでしょうか?
1つはその方法で衝突判定のレイヤーを書き換えてしまうことです。
しかしこれは予想外のバグを引き起こしてしまう可能性が非常に高いです。
イベントが移動した時にもしちゃんと元の数値に書き換えられなかった場合、移動できなかった部分が出来てしまったり
最悪ゲームが止まってしまう可能性もあります。
もう1つの方法は、プレイヤーが移動時に衝突判定レイヤーだけじゃなくイベントレイヤーをみることでしょうか。
イベントレイヤーはイベントが移動したらいないところは0にして現在いるところだけ数値を変化させるので、
間違って衝突判定レイヤーを書き換えてしまうことはありません。
今回は後者で行こうと思います。
なので衝突判定を行っている箇所でやりたいのですが、これがまた結構面倒くさいのです。
イベントはそのマップで何個作るかわかりません。
作るたびに分岐処理を書いていてはすさまじいコード量になりますしミスも出てきます。
なので考えられるフローチャートとしては以下になりそうです。
- 衝突判定レイヤーをみて移動可能かどうかを調べる
- 移動可能であれば次にイベントレイヤーの数値を取得する
- マップイベント情報を元に取得したイベントidの情報を見る
- イベントがめり込み可能かどうかのフラグを設けておく
- めり込み不可だった場合、移動させない
こんな感じでしょうか?
マップ型のRPGであれば現時点に居るマップ情報は常に保持されていると思うので
アクセスしやすいように情報を保持しておくのがベストですね。
違うマップに行ったりシーンが変わった場合、中身をクリアして次のマップイベント情報を埋め込むといった感じです。
これでイベントを増やすごとに処理を書き加えていく必要がないので、
とりあえずイベントマップ情報のjsonに判定をするためのフラグを設けてみましょう。
{
"event" :
[
{
"id":"1",
"move": "static_move",
"collision":"ng",
"image": "actor01.png,",
"json" : "/actor/chest01.json",
"defaultSpriteName": "chest01_c0.png",
"animateSprites": "chest01_c0.png,chest01_c1.png",
"xPos":"1",
"yPos":"5"
},
{
"id":"2",
"move": "static",
"collision":"ok",
"image": "actor01.png",
"json" : "/actor/chest01.json",
"animateSprites": "none",
"xPos":"1",
"yPos":"6"
}
]
}
moveの下にcollisionという項目を設けてみました。
trueかfalseにしようと思ったのですがここはすべて文字列にしておきたいのと、
別の判定をしたいときに別の文字列をいれられるようにしました。
例えばぶつかったときに跳ね返されたりするオブジェクトの場合、
特定の文字列にしておけば判定が簡単になります。
ついでにそのまま判定を作ってしまうとプレイヤーが全く動けないので少し下にずらしました。
それでは衝突判定にイベントレイヤーも見るように分岐処理を作ってみましょう。
// 移動する先のグリッドの情報を取得する
private getGridCollision(direction:number) {
// 判定マップ参照
let collisionLayerNo:number | undefined = this.getDirectionGridNumber(direction,SYSTEM.mapLayer.collision);
let eventLayerNo:number | undefined = this.getDirectionGridNumber(direction,SYSTEM.mapLayer.event);
// イベントレイヤーデータ格納用変数定義
let eventCollision:string = "ok";
// 衝突判定マップレイヤーチェック
switch (collisionLayerNo) {
case 9:
// 衝突判定レイヤーがNGであれば強制的に移動不可判定
return false
// 条件に何も満たなければ移動可能と判定する
default :
break;
}
// 衝突判定をクリアしたらイベントデータの取得
if (eventLayerNo === 0) {
return true
}
else if( eventLayerNo !== undefined) {
// idは1からだが添え字は0から始まるので-1しておく
eventCollision = this.PIXI_resources[__dirname + "/src/json/event/map/map001.json"].data.event[eventLayerNo-1].collision;
} else {
// undefined対策でngを入れておく
eventCollision = "ng";
}
// イベントマップレイヤーチェック
switch (eventCollision) {
case "ng":
return false
// 条件に何も満たなければ移動可能と判定する
default :
return true;
}
}
衝突判定レイヤーに加えてイベントレイヤーも見るようにしました。
最初は衝突レイヤー判定を見て、通行可能であればイベントレイヤーの判定に入ります。
衝突レイヤーを見た時に通行不可能であれば即刻falseを返します。
こうすることでイベントレイヤー判定をしなくなるので多少なりと処理が軽くなります。
微々たる程度の処理負荷ですが、プログラミングはこういうのが重なりに重なることで
重たいゲームになってしまう可能性が高いです。
実際、市販で売っているゲームもFPS15も出ていないゲーム(某PS2の牧場生活ゲーム)があって即売りしたことがあります。
あれはオブジェクト多すぎて処理しきれなかったのもありますがゲームキューブ版では問題なく動いていたので
単純に移植したプログラマーの腕が低かったのでしょう。
もしくは3Dモデルのサイズを圧縮しなかったかですね。
処理を軽くするのはプログラマの役目なので少しでも軽くできるのであれば軽くするに越したことはありません。
話を戻すと、気を付けてほしいのがイベントナンバーは1からで取得した配列は0から始まるので、
参照する際はイベントナンバー-1にしておきましょう。
0の時にマイナスにしてしまうと-1の添え字を参照してundefinedになってしまうので
0の時は何もないという意味にしているので強制的に通行可能なtrueをreturnしてしまいましょう。
取得した文字列がngであればfalseを返して通行できないようにしましょう。
そうでなければtrueを返せばイベントも衝突判定としてみることが出来ます。
もう一度コンパイルして動かしてみましょう。
……宝箱にぶつかることができましたか?
ぶつかってエラーもなにもでなければ完璧です。
──いよいよゲームらしくなってきましたね。
次回はテキストの実装をしてみます。
確かPIXI.JSはテキスト描写をできる機能を持っていたので結構楽に実装出来そうです。
使い方はCSSを使うような感じみたいな記憶があるのですが、ちょっと予習してきます。
最後に恒例のソースコードを張り付けておきますね。
それでは。
import * as fs from 'fs';
import * as PIXI from 'pixi.js'
import { SYSTEM } from '../../config/system';
import Controller_Base from './Controller/Controller_Base';
interface EventQueue {
// イベントタイプ
type:number,
// イベントナンバー
no:number,
}
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 = 3;
// イベント用変数
private EventSprites:Array<PIXI.extras.AnimatedSprite> = [];
// マップ用変数
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,
],
[
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
],
];
//--------------------------------------------
// コンストラクタ
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.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")
.add(__dirname + "/src/json/spritesheets/actor/chest01.json")
.add(__dirname + "/src/json/event/map/map001.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.EventsSpriteInit();
// console.log(this.PIXI_resources[__dirname + "/src/json/event/map/map001.json"].data.event[0]);
// オブジェクトをステージに追加
this.addStageInit();
}
// イベント配列数値書き換え
private eventMapLayerReWrite(event:PIXI.extras.AnimatedSprite,eventNo:number) {
let pos = this.getEventPosition(event);
this.MapData[SYSTEM.mapLayer.event][pos] = eventNo;
}
// イベントオブジェクト配列生成
private EventsSpriteInit() {
// イベントデータの取得
let eventData:Array<{ [key: string]: string; }> = this.PIXI_resources[__dirname + "/src/json/event/map/map001.json"].data.event;
// 取得したイベントデータをmapで回し、返り値をAnimatedSprite配列にする
this.EventSprites = eventData.map( (item,key) => {
// テクスチャの空配列を準備
let textureEvents:Array<PIXI.Texture> = [];
// 取得したイベントデータの保持しているアニメーション文字列をカンマ区切りで配列化
let textureStrings:Array<string> = item.animateSprites.split(",");
// 取得したイベントデータの保持しているjson文字列でテクスチャを取得
let textureGraphics:PIXI.Spritesheet | undefined = this.PIXI_resources[__dirname + "/src/json/spritesheets" + item.json].spritesheet;
// 取得したアニメーション文字列からアニメーションテクスチャ配列を作る
textureStrings.map( (name) => {
// json文字列からテクスチャを取得出来ているかチェック
if(textureGraphics !== undefined) {
// アニメーション文字列がnoneであれば
if(name === "none") {
// 空のテクスチャを入れる(透明扱い)
textureEvents.push(PIXI.Texture.EMPTY);
} else {
// そうでなければテクスチャ名を指定して配列に組み込む
textureEvents.push(textureGraphics.textures[name]);
}
}
});
// テクスチャ配列を元にアニメーションスプライトを作る
let sprite:PIXI.extras.AnimatedSprite = new PIXI.extras.AnimatedSprite(textureEvents);
// 取得したイベントデータをもとに座標を決める
sprite.setTransform(SYSTEM.guiChipSize*2 + ( Number(item.xPos) * SYSTEM.chipSize) ,SYSTEM.guiChipSize*2 + ( Number(item.yPos) * SYSTEM.chipSize));
// 取得したデータからアニメーションをするかどうかをチェック
if (item.move !== "static") {
sprite.animationSpeed = 0.05;
sprite.play();
}
// マップ配列のイベントレイヤーを自身のidで数値を塗り替える
this.eventMapLayerReWrite(sprite,Number(item.id));
// 形成したスプライトを返す
return sprite;
});
}
// プレイヤー初期化
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 if(Controller_Base.keyAry.indexOf("z") >= 0) {
console.log("z押された");
this.getGridEvent(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; }
}
// 引数1つ目の座標を調べる
private getEventPosition(event:PIXI.extras.AnimatedSprite) {
// プレイヤーの現在の座標を取得(GUI位置補正もする)
let pos: { [key: string]: number; } = {
x:( event.x - (SYSTEM.guiChipSize*2)) / SYSTEM.chipSize ,
y:( event.y - (SYSTEM.guiChipSize*2)) / SYSTEM.chipSize
}
// 一次元配列の現在値を取得
return pos.x + ( pos.y * SYSTEM.gridX );
}
// 向いている先のグリッドの数値を取得する
private getDirectionGridNumber(direction:number,mapLayer:number) {
// プレイヤーが居る1次元配列の数値を取得
let nowPos:number = this.getEventPosition(this.PlayerSprite);
// 向いている方向の先にあるグリッドの数値を返す
switch (direction) {
case 0:
return this.MapData[mapLayer][nowPos - 1];
case 1:
return this.MapData[mapLayer][nowPos + 1];
case 2:
return this.MapData[mapLayer][nowPos - SYSTEM.gridX];
case 3:
return this.MapData[mapLayer][nowPos + SYSTEM.gridX];
}
}
// 向いている方向にイベントがあるかどうか調べる
private getGridEvent(direction:number) {
// 判定マップ参照
let gridNo:number | undefined = this.getDirectionGridNumber(direction,SYSTEM.mapLayer.event);
switch (gridNo) {
case 0:
break;
case 1:
console.log("パカパカ宝箱");
break;
case 2:
console.log("透明イベントだよ");
break;
case 3:
break;
default :
return 0;
}
}
// 移動する先のグリッドの情報を取得する
private getGridCollision(direction:number) {
// 判定マップ参照
let collisionLayerNo:number | undefined = this.getDirectionGridNumber(direction,SYSTEM.mapLayer.collision);
let eventLayerNo:number | undefined = this.getDirectionGridNumber(direction,SYSTEM.mapLayer.event);
// イベントレイヤーデータ格納用変数定義
let eventCollision:string = "ok";
// 衝突判定マップレイヤーチェック
switch (collisionLayerNo) {
case 9:
// 衝突判定レイヤーがNGであれば強制的に移動不可判定
return false
// 条件に何も満たなければ移動可能と判定する
default :
break;
}
// 衝突判定をクリアしたらイベントデータの取得
if (eventLayerNo === 0) {
return true
}
else if( eventLayerNo !== undefined) {
// idは1からだが添え字は0から始まるので-1しておく
eventCollision = this.PIXI_resources[__dirname + "/src/json/event/map/map001.json"].data.event[eventLayerNo-1].collision;
} else {
// undefined対策でngを入れておく
eventCollision = "ng";
}
// イベントマップレイヤーチェック
switch (eventCollision) {
case "ng":
return false
// 条件に何も満たなければ移動可能と判定する
default :
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.addStageArray(this.EventSprites);
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;