ページ 11

【解決済み】戦闘開始時に現在の戦闘背景のファイル名を取得したい

Posted: 2021年6月07日(月) 10:42
by ドラムカン
いつもお世話になります。

戦闘開始時に現在設定されている戦闘背景のファイル名(地面用と背景用の両方)を取得して
条件式を作りたいと考えております。

調べましたら、rpg_sprites.js の Spriteset_Battleで管理しているようで
以下のクラスがファイル名を返すということは分かりましたが

コード: 全て選択

Spriteset_Battle.prototype.battleback1Bitmap = function() {
    return ImageManager.loadBattleback1(this.battleback1Name());
};

Spriteset_Battle.prototype.battleback2Bitmap = function() {
    return ImageManager.loadBattleback2(this.battleback2Name());
};
戦闘開始時の取得がどうしても解決できません。

コード: 全て選択

console.log(this.battleback1Bitmap());
//または
console.log(battleback1Bitmap());
//どちらもエラー
ご教示いただけますと幸いです。

Re: 戦闘開始時に現在の戦闘背景のファイル名を取得したい

Posted: 2021年6月07日(月) 14:06
by Plasma Dark
調べましたら、rpg_sprites.js の Spriteset_Battleで管理しているようで
以下のクラスがファイル名を返すということは分かりましたが
battleback1Bitmap, battleback2Bitmap はクラスではなく関数(あるいはメソッド)と呼ばれます。
(JavaScriptにおいては、クラスは特別な関数ではあるのでややこしいですが、 Spriteset_Battle.prototype.battleback1Bitmap を指してクラスである、という言い方はしません)

ところで、 battleback1Bitmap メソッドが返すのは Bitmap クラスのインスタンスであり、戦闘背景のファイル名ではありません。
戦闘背景のファイル名を返すメソッドは battleback1Name のほうです。
戦闘開始時の取得がどうしても解決できません。
thisの理解を放棄したままでは解決は難しいでしょう。
thisは実行される文脈によって内容が変わります。
試しに、下記のコードをプラグインとして導入し、タイトル画面とメニュー画面に入った際に表示される内容を確認してみてください。

コード: 全て選択

(function () {
  'use strict';

  const _Scene_Title_start = Scene_Title.prototype.start;
  Scene_Title.prototype.start = function () {
    _Scene_Title_start.call(this);
    console.log(this);
  };

  const _Scene_Menu_start = Scene_Menu.prototype.start;
  Scene_Menu.prototype.start = function () {
    _Scene_Menu_start.call(this);
    console.log(this);
  };
})();
タイトルで表示される内容は Scene_Title クラスのインスタンスであり、メニューで表示される内容は Scene_Menu のインスタンスです。
this.battleback1Name() は Spriteset_Battle クラスのインスタンスが this である場合に呼び出すことができます。

Re: 戦闘開始時に現在の戦闘背景のファイル名を取得したい

Posted: 2021年6月07日(月) 18:06
by ドラムカン
Plasma Dark 様

いつも大変お世話になっております。

いただいたコードを導入しましたら
コンソールにScene_Titleの配列?が表示されました。

すみません、 せっかくPlasma Dark 様がヒントを与えてくださっているのに
その思いをなかなかくみ取れずにおります。

Spriteset_Battle クラスのインスタンスを this にしてからthis.battleback1Name() を参照すればエラーが出ない、ということでしょうか?
しかし、Spriteset_Battle クラスのインスタンスを thisにするとは一体……
理解できず申し訳ございません。

Re: 戦闘開始時に現在の戦闘背景のファイル名を取得したい

Posted: 2021年6月07日(月) 21:31
by WTR
出しゃばらなくてもいいかな、と思いつつ
自分の言葉で整理してみたくもあったので挑戦してみます。

まず…
Spriteset_Battle クラスのインスタンスを this にする
とは言われていないと思います。

this の中身を好き勝手に設定できるという話ではなく
this は書く場所によって中身が変わるということを理解しよう、というお話でしょう。

Spriteset_Battle の中で this と書かれていればそれは Spriteset_Battle のインスタンスです。
だから Spriteset_Battle の中に限っては this.battleback1Name() と書ける。

Spriteset_Battle の適当な必ず呼ばれるメソッド…とりあえず createBattleback() に処理を追加して
ゲーム変数に戦闘背景のファイル名を取得する場合
(create... なのに名前取得処理を追加するセンスはおいといて)

コード: 全て選択

const _Spriteset_Battle_createBattleback = Spriteset_Battle.prototype.createBattleback;
Spriteset_Battle.prototype.createBattleback = function() {
	_Spriteset_Battle_createBattleback.call(this);
    $gameVariables.setValue(1, this.battleback1Name()); // 戦闘背景1のファイル名を変数1に取得
    $gameVariables.setValue(2, this.battleback2Name()); // 戦闘背景2のファイル名を変数2に取得
};
こんな風に出来るかと思います。これもひとつの手段です。
が、別の方法も考えられます。Scene_Battle の処理でやる場合。

Scene_Battle の中で this とあればそれは Scene_Battle のインスタンスです。
Spriteset_Battle のインスタンスではないので、this.battleback1Name() とは書けません。
ではどうするかというと
Scene_Battle の中で Spriteset_Battle のインスタンスを作成している箇所があるのでそれを探します。

コード: 全て選択

Scene_Battle.prototype.createSpriteset = function() {
    this._spriteset = new Spriteset_Battle();
    this.addChild(this._spriteset);
};
これです。
Spriteset_Battle のインスタンスを new で作成し、this._spriteset という名前で保持しています。
したがって Scene_Battle の中に書くなら
this.battleback1Name() ではなく
this._spriteset.battleback1Name() になります。

最初の例と同じく
Scene_Battle の適当なメソッド…とりあえず createSpriteset() に処理を追加
(create... なのに())

コード: 全て選択

const _Scene_Battle_createSpriteset = Scene_Battle.prototype.createSpriteset;
Scene_Battle.prototype.createSpriteset = function() {
    _Scene_Battle_createSpriteset.call(this);
    $gameVariables.setValue(1, this._spriteset.battleback1Name()); // 戦闘背景1のファイル名を変数1に取得
    $gameVariables.setValue(2, this._spriteset.battleback2Name()); // 戦闘背景2のファイル名を変数2に取得
};
これでも出来るでしょう。
ではイベントコマンドのスクリプトやコンソールに直に書く場合はどうか。

イベントコマンドのスクリプトにおける this と
デバッグコンソールにおける this もまた別物なんですけど
めんどくさいのでおいといて
Scene_Battle が Spriteset_Battle のインスタンスを持っていることは前述のとおりですので
Scene_Battle のインスタンスを見つけられれば解決しそうです。

Scene_Battle._spriteset と書きたいところですが
シーンの外からシーンのインスタンスを直接参照することはできないらしいので
SceneManager を通して現在のシーンを参照します。

コード: 全て選択

SceneManager._scene; // 現在のシーンのインスタンス(戦闘中なら Scene_Battle)
SceneManager._scene._spriteset; // Scene_Battle がもつ Spriteset_Battle のインスタンス
SceneManager._scene._spriteset.battleback1Name(); // 戦闘背景1のファイル名
Scene_Battle の中に書いた this._spriteset と
戦闘中に SceneManager._scene._spriteset で参照できるものは同一だというお話でした。
言葉で説明するのはなかなか難儀ですね…

余計にややこしくなったカモシレナーイ

Re: 戦闘開始時に現在の戦闘背景のファイル名を取得したい

Posted: 2021年6月07日(月) 22:48
by ドラムカン
WTR様

はぁー! WTR様 仏様ー!
15時頃からずっと頭を抱えていて
thisとは何ぞやと理解に励むも抜け出せない迷路にハマっておりました。

Plasma Dark 様からいただいたコードと睨めっこして
以下のように書いてみたのですが、

コード: 全て選択

const _Scene_Battle_start = Scene_Battle.prototype.start;
Scene_Battle.prototype.start = function () {
 _Scene_Battle_start.call(this);
    console.log(this.battleback1Name());
};
エラーになるので
console.log(this.battleback1Name()) を console.log(this)のみにして、
中の_urlというファイル名付きの相対パスが入った要素から
ファイル名だけを抜き出せないのかなあと試行錯誤しておりました。

そんな中で、WTR様から頂いたcreateBattleback() のコードで一発解決しました!

Spriteset_Battleのインスタンスでのみ参照できるbattleback1Name()を
Scene_Battle内で呼び出すには、Spriteset_Battleのインスタンスを作成して継承する、ということで合っていますか?
それと、this._spriteset = new Spriteset_Battle() のインスタンスを作成しているメソッドは
コアスクリプトにあるのでしょうか?

すみません、あと一点、初歩的な質問が続きますが
createBattleback というのは任意の名前、ではないですよね?
頭の悪い質問で申し訳ございません。

解決していただいただけでも本当に有難いことなのですが、厚かましくも伺ってしまいました。
このまま、突き詰めないまま終わりますと、これまでと何も変わらないので
今回せっかく少し踏み込んだところを教えていただけたこともありますし
この機会に是非、不明な点は晴らしておきたい気持ちでおります。

許されるのでしたら、教えていただけますと幸いです。
どうかよろしくお願いいたします。

Re: 戦闘開始時に現在の戦闘背景のファイル名を取得したい

Posted: 2021年6月07日(月) 23:00
by Plasma Dark
いただいたコードを導入しましたら
コンソールにScene_Titleの配列?が表示されました。
配列ではなく、オブジェクトです。
Scene_Title クラスのインスタンス と言います。
this の中身を好き勝手に設定できるという話ではなく
this は書く場所によって中身が変わるということを理解しよう、というお話でしょう。
WTRさんの仰る通りの意図ではありますが、thisを任意のオブジェクトに設定する方法も存在はします。正確な表現にしようとして、わかりにくくなってしまいましたね。

私が書いたコードについて補足するなら、 Scene_Title インスタンスから見たthisは Scene_Title インスタンスそのもので、 Scene_Menu インスタンスから見たthisは Scene_Menu インスタンスそのものです。
特殊な書き方をしない限り、thisはそう書いたインスタンス自身を指す、ということを説明するためのものでした。
エラーになると仰っていた console.log のコードには文脈(どのクラスのインスタンスの中で実行されているか)が明示されていなかったので、thisとは実行時の文脈で内容が変わるものである、ということを示したつもりでした。
こんな風に出来るかと思います。これもひとつの手段です。
ゲーム変数にセットすると、戦闘背景の名前が空文字列である場合に直感に反する挙動になることに注意してください。
Scene_Battle の中に書いた this._spriteset と
戦闘中に SceneManager._scene._spriteset で参照できるものは同一だというお話でした。
コンソール等、制約のある環境から参照する場合には仕方がありませんが、プラグインを書いて対応する場合にアンダースコアで始まる変数にクラスの外からアクセスすべきでないことは書いておきます。
https://elleonard.github.io/nplus_doc/2 ... m-outside/
(WTRさんが イベントコマンドのスクリプトやコンソールに直に書く場合 と限定されているのはこのためでしょう)
Spriteset_Battleのインスタンスでのみ参照できるbattleback1Name()を
Scene_Battle内で呼び出すには、Spriteset_Battleのインスタンスを作成して継承する、ということで合っていますか?
継承するというと別の意味を指す言葉になってしまいますね。
ツクールMVでは元々 Scene_Battle.prototype.createSpriteset において、 Spriteset_Battle クラスのインスタンスを生成しています。
(WTRさんが提示されているコードが rpg_scenes.js 内にあります)
createBattleback というのは任意の名前、ではないですよね?
Spriteset_Battle.prototype.createBattleback は rpg_sprites.js 内に定義されています。
気になる名前が出てきた場合には、コード内をテキスト検索で探してみると良いかと思います。

Re: 【解決済み】戦闘開始時に現在の戦闘背景のファイル名を取得したい

Posted: 2021年6月07日(月) 23:44
by ドラムカン
Plasma Dark 様

丁寧にご回答くださり、ありがとうございます!
前半部分はレベルが高すぎて、まだ私には時期尚早の内容でしたが
アンダーバーの記事はとても参考になりました。
以前、「戦闘行動の強制」をスクリプトで実行した際、MPが -1 になったことを思い出しました。
外部からアクセスしたことによる弊害とは違うかもしれませんが……。
エラーになると仰っていた console.log のコードには文脈(どのクラスのインスタンスの中で実行されているか)が明示されていなかったので、thisとは実行時の文脈で内容が変わるものである、ということを示したつもりでした。
WTR様の情報と合わせて、ようやく少しだけ理解できました。
最初は何が何やら分からず、くみ取れなくて申し訳ございません。
ツクールMVでは元々 Scene_Battle.prototype.createSpriteset において、 Spriteset_Battle クラスのインスタンスを生成しています。
元々、インスタンスが生成されていたのですね。
rpg_scenes.js ということで、早速、文字列検索を活用して見つけました。
私はAtomで.jsファイルを開いております。
個人的な意見で恐縮ですが、ツクール用jsの補完機能がついたエディタがあれば
とても理解しやすいなのになあと感じました。

諸々、貴重な情報をご提供くださり、
本当にありがとうございました!
またご教示いただけますと幸いです。

Re: 【解決済み】戦闘開始時に現在の戦闘背景のファイル名を取得したい

Posted: 2021年6月08日(火) 00:31
by WTR
だいたい補足してもらったからいいのかな…
Plasma Dark さんが書きました:thisを任意のオブジェクトに設定する方法も存在はします。
bind(this) とかいうやつですかね。
ときどき見かけはするけどそういうのもあるんだなー くらいにしか思ってなかったヤツでした。
Plasma Dark さんが書きました:ゲーム変数にセットすると、戦闘背景の名前が空文字列である場合に直感に反する挙動になることに注意してください。
本当だ…
空文字とか null を入れると $gameVariables.value(id) の戻り値は 0 になってしまうらしいです。
わかっていればその先の処理で対処できるとは思いますが。
一応 $gameVariables._data[id] だと元の型のまま参照できますが
ちょうどアンダースコア付きは外から参照しないという話題も出てることだし…