気ままなタンス*プログラミングなどのノートブック

プログラミングやRPGツクール、DTM、VOCALOIDについてのんびり書きます。

【ツクールMV】ゲーム開始~Scene_Title呼び出しまでの流れ(一人輪読会)

ツクールMVで、各種内部処理を理解するため、ソースコードを読み、アウトプットした。

「ここはどういう意図?あーでもないこうでもない」と意見を思考しながらやっていたため、適当に「一人輪読会」とつける。

今回の目的は
ゲーム開始~Scene_Title呼び出しまでにどういう処理が動いているのかを理解すること

www.slideshare.net

わかったこと

  • Scene_XXXX.initializeは、SceneManager.goto内で、シーンオブジェクトが生成されたタイミングで呼ばれる
  • Scene_XXXX.createは、SceneManager.changeSceneで、シーンが切り替わる時に呼ばれる
  • Scene_XXXX.startは、SceneManager.updateSceneで、シーンが開始されておらず、準備完了となった時点で呼ばれる
    • 1.initialize, 2.create, 3.start

ゲーム開始~Scene_Titleまでの流れ(ソースコード)

  • js/main.js
// 5行目
PluginManager.setup($plugins);

// 7行目
window.onload = function() {
    SceneManager.run(Scene_Boot);
};
  • js/rgs_managers.js
// 1470行目
SceneManager.run = function(sceneClass) {
    try {
        this.initialize();     // ★A
        this.goto(sceneClass); // ★B
        this.requestUpdate(); // ★C
    } catch (e) {
        this.catchException(e);
    }
};

/*
★A
// 1480行目
SceneManager.initialize = function() {
    this.initGraphics();    
    this.checkFileAccess();
    this.initAudio();
    this.initInput();
    this.initNwjs();
    this.checkPluginErrors();
    this.setupErrorHandlers();
};

// this.initGraphics(): Graphicsの初期化
// this.checkFileAccess(): index.htmlにおける、js/libs/pixi.jsから、js/main.jsまでをgetElementsByTagNameで取得し、XMLHttpRequestで取得確認をする
// this.initAudio(): Web Audio APIがブラウザで使えるかどうか判断
// this.initInput(): 入力関係の初期化(Input、TouchInput)
// this.initNwjs(): Nw.jsの初期化
// this.checkPluginErrors(): プラグインのエラーチェック。PluginManagerでエラーが発生した時点で、_errorUrls配列に対象スクリプトのURLを格納している
// this.setupErrorHandlers(): errorイベントが発生したら、SceneMangaer.onErrorが実行されるように設定
//                            keydownイベントが発生したら、SceneManager.onKeyDownが実行されるように設定
*/

/* ★B
// 1718行目
SceneManager.goto = function(sceneClass) {
    if (sceneClass) {
        this._nextScene = new sceneClass();
    }
    if (this._scene) {
        this._scene.stop();
    }
};

渡されたSceneクラス(Scene_Boot)のオブジェクトを生成

*/
  • js/rgs_scenes.js
// 155行目

Scene_Boot.prototype.initialize = function() {
    Scene_Base.prototype.initialize.call(this);
    this._startDate = Date.now();
};

// Scene_Bootの初期化と開始日付の設定
  • js/rpg_managers.js
/* ★C
// 1566行目
SceneManager.requestUpdate = function() {
    if (!this._stopped) {
        requestAnimationFrame(this.update.bind(this));
    }
};
*/

// SceneManager.updateをコールバックとして、requestAnimationFrameに渡す
// これで定期的にupdateが実行されるようになる
// 1572行目
SceneManager.update = function() {
    try {
        this.tickStart();
        this.updateInputData();
        this.updateMain(); // ★D
        this.tickEnd();
    } catch (e) {
        this.catchException(e);
    }
};

// this.updateMainの呼び出し

/* ★D
// 1639行目
SceneManager.updateMain = function() {
    this.changeScene(); // ★E
    this.updateScene();
    this.renderScene();
    this.requestUpdate();
};
*/

/* ★E
SceneManager.changeScene = function() {
    if (this.isSceneChanging() && !this.isCurrentSceneBusy()) {
        if (this._scene) {
            this._scene.terminate();
            this._previousClass = this._scene.constructor;
        }
        this._scene = this._nextScene;
        if (this._scene) {
            this._scene.create();
            this._nextScene = null;
            this._sceneStarted = false;
            this.onSceneCreate();
        }
        if (this._exiting) {
            this.terminate();
        }
    }
};

// this._sceneには何も入っていない。Sceneも変更中ではないので、if文はスルー
// this._nextSceneにはScene_Baseのオブジェクトが入っている
// ここでようやく、Scene_Baseのcreateメソッドが呼ばれる
*/
  • js/rgs_scenes.js
// 160行目

Scene_Boot.prototype.create = function() {
    Scene_Base.prototype.create.call(this);
    DataManager.loadDatabase(); // ★F
    ConfigManager.load(); // ★G
    this.loadSystemImages(); // ★H
};
  • js/rgs_managers.js
/*
★F
// 65行目
DataManager.loadDatabase = function() {
    var test = this.isBattleTest() || this.isEventTest();
    var prefix = test ? 'Test_' : '';
    for (var i = 0; i < this._databaseFiles.length; i++) {
        var name = this._databaseFiles[i].name;
        var src = this._databaseFiles[i].src;
        this.loadDataFile(name, prefix + src);
    }
    if (this.isEventTest()) {
        this.loadDataFile('$testEvent', prefix + 'Event.json');
    }
};

// 78行目
DataManager.loadDataFile = function(name, src) {
    var xhr = new XMLHttpRequest();
    var url = 'data/' + src;
    xhr.open('GET', url);
    xhr.overrideMimeType('application/json');
    xhr.onload = function() {
        if (xhr.status < 400) {
            window[name] = JSON.parse(xhr.responseText);
            DataManager.onLoad(window[name]);
        }
    };
    xhr.onerror = function() {
        DataManager._errorUrl = DataManager._errorUrl || url;
    };
    window[name] = null;
    xhr.send();
};

// jsonファイルの読み込み、Gameオブジェクトを作成
// 例によって、エラーが発生したjsonは、_errorUrls配列に格納される
*/


/*
★G
// 503行目
ConfigManager.load = function() {
    var json;
    var config = {};
    try {
        json = StorageManager.load(-1);
    } catch (e) {
        console.error(e);
    }
    if (json) {
        config = JSON.parse(json);
    }
    this.applyData(config);
};

// config.rpgsaveの存在確認
// ローカルファイルか、Webストレージかによってパラメーターが異なる

*/

/*
// ★H
//  167行目
Scene_Boot.prototype.loadSystemImages = function() {
    ImageManager.loadSystem('Window');
    ImageManager.loadSystem('IconSet');
    ImageManager.loadSystem('Balloon');
    ImageManager.loadSystem('Shadow1');
    ImageManager.loadSystem('Shadow2');
    ImageManager.loadSystem('Damage');
    ImageManager.loadSystem('States');
    ImageManager.loadSystem('Weapons1');
    ImageManager.loadSystem('Weapons2');
    ImageManager.loadSystem('Weapons3');
    ImageManager.loadSystem('ButtonSet');
};

// 各アイコンファイルを取得

*/
  • js/rpg_managers.js
// 1665行目
SceneManager.updateScene = function() {
    if (this._scene) {
        if (!this._sceneStarted && this._scene.isReady()) {
            this._scene.start();
            this._sceneStarted = true;
            this.onSceneStart();
        }
        if (this.isCurrentSceneStarted()) {
            this._scene.update();
        }
    }
};

// this._sceneがnullではなく、まだ開始されていないので、startする(Scene_Boot)
  • js/rgs_scenes.js
// 200行目
Scene_Boot.prototype.start = function() {
    Scene_Base.prototype.start.call(this);
    SoundManager.preloadImportantSounds();
    if (DataManager.isBattleTest()) {
        DataManager.setupBattleTest();
        SceneManager.goto(Scene_Battle);
    } else if (DataManager.isEventTest()) {
        DataManager.setupEventTest();
        SceneManager.goto(Scene_Map);
    } else {
        this.checkPlayerLocation();
        DataManager.setupNewGame();
        SceneManager.goto(Scene_Title);
        Window_TitleCommand.initCommandPosition();
    }
    this.updateDocumentTitle();
};

// バトルテスト、イベントテストではないので、
// プレイヤーの初期位置を確認し、Scene_Titleを呼び出す