Posted at

EgretEngineでRPGマップはどう実現するか?(3) 表示処理

前回の記事「EgretEngineでRPGマップはどう実現するか?(2) 表示処理が呼び出されるまで」の続きです


RpgGameView

前回の調査した通り下記の手順で呼び出されます


  • loadResource

  • initUI

  • initData

  • open


loadResource

このメソッドはRpgGameViewでは実装されておらず親クラスのBaseSpriteViewで実装されています


BaseSpriteView.ts

    public loadResource(loadComplete:Function, initComplete:Function):void {

if (this._resources && this._resources.length > 0) {
App.ResourceUtils.loadResource(this._resources, [], function ():void {
loadComplete();
initComplete();
}, null, this);
}
else {
loadComplete();
initComplete();
}
}

this._resourcesは、setResourcesで設定されますが、実際にはsetResourcesは呼び出されていないので、loadResources処理は何もせずにloadCompleteinitCompleteを呼び出します。


BaseSpriteView.ts

    public setResources(resources:string[]):void {

this._resources = resources;
}


initUIとinitData

initUIinitDataをみるとRpgBackgroundがくさいですが後にまわします。


RpgGameView.ts

    public initUI(): void {

super.initUI();

this.background = new RpgBackground();
this.addChild(this.background);

this.gameObjcetLayer = new egret.DisplayObjectContainer();
this.addChild(this.gameObjcetLayer);

this.gameEffectLayer = new egret.DisplayObjectContainer();
this.addChild(this.gameEffectLayer);
}

public initData(): void {
super.initData();

this.monsters = [];
}



open

gameModelは、RpgGameControllerthis.gameModel.mapId = mapId;のように初期化しています。

initBackgroundinitBlocksmapIdが引き渡されているのでここをさらに追いかけます。


RpgGameView.ts


public open(...param: any[]): void {
super.open(param);

var gameModel: RpgGameModel = param[0];

this.initBackground(gameModel.mapId);
this.initBlocks(gameModel.mapId);
this.createPlayer(gameModel.playerData);
this.createMonsters(gameModel.monsterNum);
}



initBackground と initBlocks

RpgBackground.initメソッドで背景を初期化しつつ、initBlocksでマップデータを読み込んでいます。


RpgGameView.ts

    private initBackground(mapId: number): void {

this.background.init(mapId);
}

private initBlocks(mapId: number): void {
var mapData: any = RES.getRes("map_" + mapId + "_data.json");
this.blocksData = mapData.blocks;
}


先ほどのリソース"map_" + mapId + "_data.json"は、前回おっかけたRpgTestクラスで動的に定義したものです。


RpgTest.ts

    private initMapResource(): void {

var mapResPath: string = "resource/assets/rpgGame/map/" + this.mapId + "/";
:
var mapRes: any[] = [
{
name: "data.json",
type: "json"
},
{
name: "mini.jpg",
type: "image"
}
];


RpgBackground.init

RpgBackgroundの初期化処理は、マップデータmap_<mapId>_data.jsonと、ミニマップ画像map_<mapId>_mini.pngを読み込んでいます。

そしてRpgTilesクラスが実際にタイルを読み込み管理する処理のようです。


RpgBackground.ts

    public init(mapId: number) {

this.mapId = mapId;

var mapData: any = RES.getRes("map_" + mapId + "_data.json");
this.mapWidth = mapData.width;
this.mapHeight = mapData.height;

this.miniBg = new egret.Bitmap();
this.miniBg.texture = RES.getRes("map_" + mapId + "_mini.jpg");
this.miniBg.width = this.mapWidth;
this.miniBg.height = this.mapHeight;
this.addChild(this.miniBg);

this.tiles = new RpgTiles();
this.tiles.init(mapId);
this.addChild(this.tiles);
}



RpgTiles

RpgTiles.initではマップ定義ファイル(data.json)から縦と横のタイルの数を計算しています。


RpgTiles.ts

    public init(mapId: number): void {

this.mapId = mapId;
var mapData: any = RES.getRes("map_" + mapId + "_data.json");

this.cols = Math.floor(mapData.width / RpgGameData.GameTileWidth);
this.rows = Math.floor(mapData.height / RpgGameData.GameTileHeight);
}



RpgTiles.updateCameraPos

そしてRpgTiles.updateCameraPosが、お目当ての処理のようです。

updateCameraPos($x: number, $y: number)xyが移動後のカメラの位置ですね。


RpgTiles.ts

class RpgTiles extends egret.DisplayObjectContainer {

:
public updateCameraPos($x: number, $y: number): void {

カメラ位置に該当するタイルの行と列を求めます。

        var currCol: number = Math.round($x / RpgGameData.GameTileWidth);

var currRow: number = Math.round($y / RpgGameData.GameTileHeight);

画面に表示されるタイルの行数と列数をもとめます

        var screenCols: number = Math.ceil(App.StageUtils.getWidth() / RpgGameData.GameTileWidth) + 1;

var screenRows: number = Math.ceil(App.StageUtils.getHeight() / RpgGameData.GameTileHeight) + 1;

var halfScreenCols: number = Math.ceil(screenCols / 2);
var halfScreenRows: number = Math.ceil(screenRows / 2);

次に、

画面の左端minColと右端maxCol

画面の上端minRowと下端maxRow

を求めます

        var minCol: number = currCol - halfScreenCols;

var maxCol: number = currCol + halfScreenCols;
:
var minRow: number = currRow - halfScreenRows;
var maxRow: number = currRow + halfScreenRows;

画面のタイル1つはRpgTileというクラスで実装されています。

画面に表示されるべきすべてのタイルのRpgTileが生成されていなければ新規に作成します

        var screenTiles = [];

for (var i = minCol; i <= maxCol; i++) {
for (var j = minRow; j <= maxRow; j++) {
var tileKey: string = i + "_" + j;
var tile: RpgTile = this.tiles[tileKey];
if (!tile) {
tile = new RpgTile();
tile.init(this.mapId, i, j);
this.tiles[tileKey] = tile;
}
if (!tile.parent) {
this.addChild(tile);
}
screenTiles.push(tileKey);
}
}

カメラの移動前に表示していたタイルのうち表示領域外にでたタイルを削除します

        this.screenTiles.forEach(function (tileKey: string) {

if (screenTiles.indexOf(tileKey) == -1) {
var tile: RpgTile = this.tiles[tileKey];
tile && App.DisplayUtils.removeFromParent(tile);
}
}.bind(this));

updateCameraPosの全体像


RpgTiles.ts

class RpgTiles extends egret.DisplayObjectContainer {

:
public updateCameraPos($x: number, $y: number): void {
var currCol: number = Math.round($x / RpgGameData.GameTileWidth);
var currRow: number = Math.round($y / RpgGameData.GameTileHeight);

var screenCols: number = Math.ceil(App.StageUtils.getWidth() / RpgGameData.GameTileWidth) + 1;
var screenRows: number = Math.ceil(App.StageUtils.getHeight() / RpgGameData.GameTileHeight) + 1;

var halfScreenCols: number = Math.ceil(screenCols / 2);
var halfScreenRows: number = Math.ceil(screenRows / 2);

var minCol: number = currCol - halfScreenCols;
var maxCol: number = currCol + halfScreenCols;
if (minCol < 0) {
maxCol += -minCol;
minCol = 0;
}
if (maxCol > this.cols) {
minCol -= (maxCol - this.cols);
maxCol = this.cols;
}
var minRow: number = currRow - halfScreenRows;
var maxRow: number = currRow + halfScreenRows;
if (minRow < 0) {
maxRow += -minRow;
minRow = 0;
}
if (maxRow > this.rows) {
minRow -= (maxRow - this.rows);
maxRow = this.rows;
}

var screenTiles = [];
for (var i = minCol; i <= maxCol; i++) {
for (var j = minRow; j <= maxRow; j++) {
var tileKey: string = i + "_" + j;
var tile: RpgTile = this.tiles[tileKey];
if (!tile) {
tile = new RpgTile();
tile.init(this.mapId, i, j);
this.tiles[tileKey] = tile;
}
if (!tile.parent) {
this.addChild(tile);
}
screenTiles.push(tileKey);
}
}

//移除不在屏幕内的格子
this.screenTiles.forEach(function (tileKey: string) {
if (screenTiles.indexOf(tileKey) == -1) {
var tile: RpgTile = this.tiles[tileKey];
tile && App.DisplayUtils.removeFromParent(tile);
}
}.bind(this));
this.screenTiles = screenTiles;
}
:
}



まとめ

次回は updateCameraPosが、どう呼ばれるのかについて確認したいと思います。