アクターコマンドのサブウィンドウの作成

にしゆう2017-12-06に開始した「プラグイン制作・技術」の中の討論

  1. にしゆう

    にしゆう ユーザー

    お世話になっております。

    当方、プラグイン制作初心者です。
    以下のような仕様で、アクターコマンドから派生する、オリジナルのコマンドウィンドウのようなものを作ろうとしています。

    ------------------------------------------------------------------
    [アクターコマンドウィンドウ]
    攻撃 ほげ(オリジナルのコマンド) スキル 防御 アイテム
    -「ほげ」を選択

    [ほげコマンドウィンドウ]
    攻撃 装備変更(未実装)
    ------------------------------------------------------------------

    以下のような問題点、疑問点があります。

    1. [ほげコマンドウィンドウ]から攻撃を選択すると、敵の選択までは進むが、敵を選択すると処理が止まってしまう。
    2. [ほげコマンドウィンドウ]が開いた後も[アクターコマンドウィンドウ]がアクティブのままで、他のアクターコマンドを選択できてしまうが、これを非アクティブ化する方法がわからない。

    プラグインのソースは以下の通りです。
    丸投げのようで恐縮ですが、アドバイスのほどよろしくお願いいたします。

    コード:
    //==============================================================================
    // NS_test_plugin.js
    // Version : 1.00
    //------------------------------------------------------------------------------
    // 作者:nishi
    //
    //==============================================================================
    
    /*:
    * @plugindesc テストプラグイン
    *
    * @author nishi
    *
    * @help
    * テスト
    *
    */
    
    (function(){
        // Scene_Battle /////////////////
    
        var _Scene_Battle_createActorCommandWindow_TestPlugin = Scene_Battle.prototype.createActorCommandWindow;
        Scene_Battle.prototype.createActorCommandWindow = function() {
            _Scene_Battle_createActorCommandWindow_TestPlugin.call(this);
            this._actorCommandWindow.setHandler('hoge',  this.commandHoge.bind(this));     
            $gameMessage.add( "プラグインが呼び出された" );
        };
      
        var _Scene_Battle_createAllWindows_TestPlugin = Scene_Battle.prototype.createAllWindows;
        Scene_Battle.prototype.createAllWindows = function() {
            _Scene_Battle_createAllWindows_TestPlugin.call(this);
            this.createSubAttackCommandWindow();
        }; 
      
        Scene_Battle.prototype.createSubAttackCommandWindow = function() {
            this._subAttackCommandWindow = new Window_SubAttackCommand();
            this._subAttackCommandWindow.setHandler('attack', this.commandAttack.bind(this));
            this._subAttackCommandWindow.setHandler('cancel', this.selectPreviousCommand.bind(this));
            this.addWindow(this._subAttackCommandWindow);
        };
      
        Scene_Battle.prototype.commandHoge = function() {
            this._subAttackCommandWindow._actor = BattleManager.actor();
            this._subAttackCommandWindow.makeCommandList();
            this._subAttackCommandWindow.refresh();
            this._subAttackCommandWindow.activate();
            this._subAttackCommandWindow.open();
    };
    
        // Window_ActorCommand ////////////////////
    
        var _Window_ActorCommand_makeCommandList_NS_testplugin = Window_ActorCommand.prototype.makeCommandList;
        Window_ActorCommand.prototype.makeCommandList = function() {
            //_Window_ActorCommand_makeCommandList_NS_testplugin.call(this); // 元処理
            if (this._actor) {
                this.addAttackCommand();
                this.addHogeCommand();
                this.addSkillCommands();
                this.addGuardCommand();
                this.addItemCommand();
            }
        };
    
        Window_ActorCommand.prototype.addHogeCommand = function() {
            this.addCommand('ほげ', 'hoge', true);
        };
      
        // Window_SubAttackCommand(オリジナルウィンドウ) ////////////////
    
        function Window_SubAttackCommand() {
            this.initialize.apply(this, arguments);
        }
      
        Window_SubAttackCommand.prototype = Object.create(Window_Command.prototype);
        Window_SubAttackCommand.prototype.constructor = Window_SubAttackCommand;
    
        Window_SubAttackCommand.prototype.initialize = function() {
            Window_Command.prototype.initialize.call(this, 50, 50);
            this.openness = 0;
            this.deactivate();
            this._actor = null;
        };
    
        Window_SubAttackCommand.prototype.makeCommandList = function() {
            if (this._actor) {
                this.addAttackCommand();
            }
        };
      
        Window_SubAttackCommand.prototype.addAttackCommand = function() {
            this.addCommand(TextManager.attack, 'attack', this._actor.canAttack());
        };
    
        Window_SubAttackCommand.prototype.windowWidth = function() {
            return 192;
        };
    
        Window_SubAttackCommand.prototype.numVisibleRows = function() {
            return 2
        };
    })();
     
    最後に編集: 2017-12-06
    #1
  2. フトコロ

    フトコロ ユーザー

    こんにちは。
    プラグイン制作ということですので、アドバイスを。

    「処理が止まる」とは、どのような状態でしょうか。
    エラーがでて、ゲームがフリーズすることでしょうか?
    それとも、フリーズはしないが、操作できなくなって先に進まなくなったことでしょうか?
    エラーのことを書いていませんので、後者の方だと思いますが、では、画面はなにが表示されていますか?
    余計なものは、表示していませんか?
    本来表示するものは、何でしょうか?

    問題解決のためには、なにが起きているのかを、正確に把握することが大切です。


    もしかしたら知らずに使っているのかもしれませんが、deactivate()がそうですよ。
    なお、ウィンドウのアクティブ化と非アクティブ化は、ウィンドウ毎に個別に行わなくてはいけません。
    つまり、何かのウィンドウをアクティブ化したら、もともとアクティブ状態だったウィンドウを、非アクティブ化してあげないと、両方アクティブ状態になってしまいます。

    なお、類似で表示化非表示化のコードがありますが、これは見えるか見えないかだけで、アクティブ状態は変わりませんので注意してください。


    ところで、表示化アクティブ化したサブウィンドウは、どこで非表示化非アクティブ化しているのでしょうか?
    コードの中に見当たらなかったので、気になりました。
     
    #2
    にしゆう がいいね!しました
  3. にしゆう

    にしゆう ユーザー

    フトコロ様

    ご返信ありがとうございます。

    2017-12-07_164718.jpg

    このような感じで、ステータスウィンドウとサブウィンドウが表示された状態のままになります。入力は受け付けません。
    本来表示すべきものは、バトルログや攻撃のエフェクトです。

    BGMは鳴っているので、完全にハングしているわけではありません。
    VSCodeのデバッグモード(?)で一時停止すると、SceneManager.updateというメソッドで止まります。
    ステップアウトで進めていくと、このupdateをひたすらループします(当然?)。
    ステップインで深入りしていくと、どういう処理を経ているのか自分にはよくわからないので、きちんと見ていませんが、Scene_Battleをループしているようです。

    サブウィンドウがアクティブのままで、表示されたままなことが悪さをしているのでしょうか。
    非表示化と非アクティブ化の記述は、後述のようにやり方がわからないので、とりあえず後回しにしようと考えました。

    deactivate()かなとは、名前から推測していたのですが、どちらかと言うとどこに記述をしていいかわからなかった感じです。
    コアスクリプトを検索して参考にしようと思ったのですが、わかりませんでした。
    例えば、アクターコマンドウィンドウで「攻撃」を選択したときに、どのタイミングで当該ウィンドウをdeactivateしているのか自分なりに追いかけたのですが、自分の見た限りではdeactivate()が見当たらなかったです。
     

    添付ファイル:

    #3
  4. フトコロ

    フトコロ ユーザー

    画面添付ありがとうございます。
    キャラは1人なので、行動選択後は、実際に行動する状態にならないといけない、というわけですね。

    はい、これは正常です。
    SceneManager.updateが大本で、これがループすることでゲームが動いています。

    これも正常です。
    SceneManager.updateの中で this.updateMain()が実行されます。
    SceneManager.updateMain()を見ると、いろいろと書いていますが、その中でthis.updateScene()が実行されます。
    SceneManager.updateScene()を見ると、これもいろいろと書いていますが、中でthis._scene.update()が実行されます。
    ここで、this._sceneは、現在のシーンのオプジェクトが格納されています。戦闘画面ならScene_Battleですね。
    つまり、Scene_Battle.update()が実行されるということです。

    SceneManager.update()がループしているので、その中で実行されるScene_Battle.update()もループするわけです。

    もう少しScene_Battle.update()の中身を見ていきましょうか。
    Scene_Battle.prototype.update()の中を見ると、this.updateBattleProcess()がありますね。
    Scene_Battle.prototype.updateBattleProcess()は、中でBattleManager.update()を実行しています。

    このBattleManager.update を見ると、switch文を使って、turnやaction、turnEndなどの文字列で分岐していますね。
    これがバトルシーンのメインとなるプロセスです。
    startでターン開始時の処理と各アクターのコマンド入力処理を、
    その後にturnになりエネミー含む全キャラの行動処理を進めます。(turnの中でactionをキャラごとに実行)
    actionで設定された行動にしたがいアクターとエネミーを動かしてアニメーションを表示させたり、ダメージポップアップをさせる
    turnEndになりターン終了時の処理を行い、
    戦闘終了の条件を満たすとbattleEndで戦闘終了処理を行うといった流れです。
    このBattleManager.updateが実行されることで、戦闘のプロセスが進むわけです。

    つまり、戦闘が先に進まないということは、この処理が実行されていないと考えられます。

    updateBattleprocess()や、BattleManager.update()、およびその中のswitch文はどれもif文で囲われて
    実行する条件が規定されています。

    これらの条件のうち、どれかに該当していないことが考えられますので、
    どの条件を満たしていないのかを調べてみると良いと思います。


    コアスクリプトでは、processOk() やprocessCancel()の中で非アクティブ化を行っています。
    なので、コマンドを選択すると自動的に非アクティブ化するようになっています。

    しかし、この場合は非アクティブ化していませんので、つまりどこかで再度アクターコマンドウィンドウを
    アクティブ化しているところがあるということです。

    アクターコマンドウィンドウをアクティブ化する処理はそうありませんので
    Window_ActorCommandクラスか、Scene_Battleクラスで調べてみてください。

    ///////////////////////////////////

    なお、一度新規プロジェクトでプラグインを実行することをお勧めします。
    こちらの環境で、サンプルプラグインを実行しましたが、サブウィンドウは表示したままでしたが
    特に戦闘が止まらず攻撃が実行されましたよ。
    戦闘が先に進まないのは、何か他のプラグインか何かで止まっている可能性が高いです。

    >追記
    バトルプロセスの説明に間違いがあったため修正。
     
    最後に編集: 2017-12-20
    #4
    にしゆう がいいね!しました
  5. にしゆう

    にしゆう ユーザー

    フトコロ様

    お世話になっております。
    詳細な解説とアドバイス、ありがとうございます。非常に参考になりました。
    特に、このアドバイスが決め手になりました。

    Window_ActorCommand.prototype.setupで再アクティブ化が行われていたので、この直前のif文で、サブウィンドウがアクティブの際はこのsetupを呼び出さないように書き加えましたら、上手くいきました。

    その他、細かい点を修正した結果、望むような挙動を実現できました。
    (ただ、戦闘がストップする件に関しては、いつの間にか直っていて結局原因がよくわかりませんでした)

    この度は的確なアドバイスと解説ありがとうございました。
    また機会がありましたら、よろしくお願いいたします。
     
    最後に編集: 2017-12-08
    #5
    フトコロ がいいね!しました

このページを共有