【完了】プラグインのレビュー依頼:敵グループのランダム化

アバター
トリアコンタン
記事: 2311
登録日時: 2015年11月10日(火) 21:13
お住まい: きのこ王国
連絡を取る:

Re: プラグインのレビュー依頼:敵グループのランダム化

投稿記事by トリアコンタン » 2020年3月29日(日) 11:21

こんにちは!
まず、while文の仕様について確認しましょう。
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/while

while文は『判定式がtrueの間、処理を繰り返す』です。
判定式がfalseを返すとループを中断します。

問題は以下の通りです。
1. 判定式で代入『condition = true』している。
2. 本来ループを止めたいとき(hideでないとき)に逆にtrueを設定している。

修正案です。
仕様を完全に理解していないので要件通りに動作するとは限りませんが、無限ループは解消されると思います。

コード: 全て選択

var condition = true;
while (condition) {
    this.clear();
    this._troopId = troopId;
    this._enemies = [];
    condition = true;

    this.troop().members.forEach(function(member) {
        if ($dataEnemies[member.enemyId]) {
            var randomEnemyId = [];
            randomEnemyId = selectEnemyId($dataEnemies[member.enemyId]);
            var enemyId = member.enemyId;
            var x = member.x;
            var y = member.y;
            if (randomEnemyId === 0) {
                var enemy = new Game_Enemy(enemyId, x, y);
                enemy.hide();
            } else if (randomEnemyId) {
                var enemy = new Game_Enemy(randomEnemyId, x, y);
                condition = false;
            } else {
                var enemy = new Game_Enemy(enemyId, x, y);
                condition = false;
            };
            if (member.hidden) {
                enemy.hide();
            };
            this._enemies.push(enemy);
        };
    }, this);
    this.makeUniqueNames();
}

プラグイン関連のトラブルが発生した際の切り分けと報告の方法です。
http://qiita.com/triacontane/items/2e227e5b5ce9503a2c30

[Blog] : http://triacontane.blogspot.jp/
[Twitter]: https://twitter.com/triacontane/
[GitHub] : https://github.com/triacontane/
アバター
トリアコンタン
記事: 2311
登録日時: 2015年11月10日(火) 21:13
お住まい: きのこ王国
連絡を取る:

Re: プラグインのレビュー依頼:敵グループのランダム化

投稿記事by トリアコンタン » 2020年3月29日(日) 11:26

ただし、上記コードでも無限ループするパターンがあります。

それは、メモ欄に0だけ指定された場合です。

コード: 全て選択

<RandomEnemy:0>


おそらくこれは作者としては想定していない書き方だと思いますが、どのように扱うかは実装と言うよりは仕様決めの問題になると思います。
プラグイン関連のトラブルが発生した際の切り分けと報告の方法です。
http://qiita.com/triacontane/items/2e227e5b5ce9503a2c30

[Blog] : http://triacontane.blogspot.jp/
[Twitter]: https://twitter.com/triacontane/
[GitHub] : https://github.com/triacontane/
アバター
ムノクラ
記事: 2011
登録日時: 2018年2月23日(金) 11:41
連絡を取る:

Re: プラグインのレビュー依頼:敵グループのランダム化

投稿記事by ムノクラ » 2020年3月29日(日) 11:41

トリアコンタン さんが書きました:こんにちは!
まず、while文の仕様について確認しましょう。
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/while

while文は『判定式がtrueの間、処理を繰り返す』です。
判定式がfalseを返すとループを中断します。

問題は以下の通りです。
1. 判定式で代入『condition = true』している。
2. 本来ループを止めたいとき(hideでないとき)に逆にtrueを設定している。

修正案です。
仕様を完全に理解していないので要件通りに動作するとは限りませんが、無限ループは解消されると思います。

コード: 全て選択

var condition = true;
while (condition) {
    this.clear();
    this._troopId = troopId;
    this._enemies = [];
    condition = true;

    this.troop().members.forEach(function(member) {
        if ($dataEnemies[member.enemyId]) {
            var randomEnemyId = [];
            randomEnemyId = selectEnemyId($dataEnemies[member.enemyId]);
            var enemyId = member.enemyId;
            var x = member.x;
            var y = member.y;
            if (randomEnemyId === 0) {
                var enemy = new Game_Enemy(enemyId, x, y);
                enemy.hide();
            } else if (randomEnemyId) {
                var enemy = new Game_Enemy(randomEnemyId, x, y);
                condition = false;
            } else {
                var enemy = new Game_Enemy(enemyId, x, y);
                condition = false;
            };
            if (member.hidden) {
                enemy.hide();
            };
            this._enemies.push(enemy);
        };
    }, this);
    this.makeUniqueNames();
}


ご指摘の点(whileの仕様)の勘違いに気づいて修正してみたのですが、どうしても上手く動きませんでした。

そこで、トリアコンタンさんのコードをそのまま使用したら動いたので、コードを比較したところ…


var condition = false;

修正
condition = false;

の違いに気づきました。

このcondition は変数で、varで代入するのではない…らしい、というのは分かったのですが、どういう時に使い分けるのかが理解できていません。
この点につきまして、参考になりそうなページを教えていただければ幸いです。
---
JavaScriptの基本を学習せずにツクールのプラグインやスクリプトを使って横着してゲームを作ろうとしている人間です。
そのような者なので、適当な投稿をするかも知れません。
他の方の投稿を信用してください。
アバター
ムノクラ
記事: 2011
登録日時: 2018年2月23日(金) 11:41
連絡を取る:

Re: プラグインのレビュー依頼:敵グループのランダム化

投稿記事by ムノクラ » 2020年3月29日(日) 11:42

トリアコンタン さんが書きました:ただし、上記コードでも無限ループするパターンがあります。

それは、メモ欄に0だけ指定された場合です。

コード: 全て選択

<RandomEnemy:0>


おそらくこれは作者としては想定していない書き方だと思いますが、どのように扱うかは実装と言うよりは仕様決めの問題になると思います。


はい、想定していませんでした。

これは…ヘルプに「絶対使うなよ!無限ループするからな!」と書いて済ませようかと思います。
---
JavaScriptの基本を学習せずにツクールのプラグインやスクリプトを使って横着してゲームを作ろうとしている人間です。
そのような者なので、適当な投稿をするかも知れません。
他の方の投稿を信用してください。
アバター
トリアコンタン
記事: 2311
登録日時: 2015年11月10日(火) 21:13
お住まい: きのこ王国
連絡を取る:

Re: プラグインのレビュー依頼:敵グループのランダム化

投稿記事by トリアコンタン » 2020年3月29日(日) 12:10

すみません、そこを問題点にあげるのを忘れてました。

var(constやletも)は『変数の宣言』です。つまり、conditionという名前の変数を使いますと宣言しています。
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/var

コード: 全て選択

var condition = true;


宣言は一番最初に使うときに一度だけすればOKです。
では、何度も宣言するとエラーになるのかというと、本来なら無視されるだけです。

今回のケースがなぜうまく動かないのか、それはローカル変数のスコープ(有効範囲)に関わる問題ですが、正しく理解するにはJavaScriptの仕様に関する知識が不可欠です。

下記で説明しますが、どうしても難しければ混乱を避けるためにも『変数の宣言は最初に使うときに一度だけ』と覚えておければOKです。
----------------------------------------------------------

簡単に説明すると、varで宣言したローカル変数のスコープは、宣言した関数の中(関数の中に関数が入っていればその中も含む)になります。
(ちなみにlet、constはさらに異なる仕様なのですが話が複雑になり過ぎるので割愛します)

実は、今回再定義した関数『setup』には入れ子になっている内部関数があります。
それはforEachの中です。

コード: 全て選択

this.troop().members.forEach(function(member) {


前述のとおり、varで宣言した変数のスコープは『宣言した関数の中』だけなので、forEachの中で宣言した変数は、forEachの中でだけ有効です。

簡略化するとこうなります。

コード: 全て選択

var condition = true;

this.troop().members.forEach(function(member) {
    var condition = false;
}, this);

console.log(condition); // trueが出力される。


----------------------------------------------------------
プラグイン関連のトラブルが発生した際の切り分けと報告の方法です。
http://qiita.com/triacontane/items/2e227e5b5ce9503a2c30

[Blog] : http://triacontane.blogspot.jp/
[Twitter]: https://twitter.com/triacontane/
[GitHub] : https://github.com/triacontane/
アバター
ムノクラ
記事: 2011
登録日時: 2018年2月23日(金) 11:41
連絡を取る:

Re: プラグインのレビュー依頼:敵グループのランダム化

投稿記事by ムノクラ » 2020年3月29日(日) 12:20

トリアコンタン さんが書きました:すみません、そこを問題点にあげるのを忘れてました。

var(constやletも)は『変数の宣言』です。つまり、conditionという名前の変数を使いますと宣言しています。
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/var

コード: 全て選択

var condition = true;


宣言は一番最初に使うときに一度だけすればOKです。
では、何度も宣言するとエラーになるのかというと、本来なら無視されるだけです。

今回のケースがなぜうまく動かないのか、それはローカル変数のスコープ(有効範囲)に関わる問題ですが、正しく理解するにはJavaScriptの仕様に関する知識が不可欠です。

下記で説明しますが、どうしても難しければ混乱を避けるためにも『変数の宣言は最初に使うときに一度だけ』と覚えておければOKです。
----------------------------------------------------------

簡単に説明すると、varで宣言したローカル変数のスコープは、宣言した関数の中(関数の中に関数が入っていればその中も含む)になります。
(ちなみにlet、constはさらに異なる仕様なのですが話が複雑になり過ぎるので割愛します)

実は、今回再定義した関数『setup』には入れ子になっている内部関数があります。
それはforEachの中です。

コード: 全て選択

this.troop().members.forEach(function(member) {


前述のとおり、varで宣言した変数のスコープは『宣言した関数の中』だけなので、forEachの中で宣言した変数は、forEachの中でだけ有効です。

簡略化するとこうなります。

コード: 全て選択

var condition = true;

this.troop().members.forEach(function(member) {
    var condition = false;
}, this);

console.log(condition); // trueが出力される。


----------------------------------------------------------


完全に理解した!とは言えませんが、かなり理解が進んだと思います。
大きな勘違い「varは変数に値を代入する処理」が解消されたことで、今後の理解が進みそうです。

ご指導いただき、ありがとうございました!
---
JavaScriptの基本を学習せずにツクールのプラグインやスクリプトを使って横着してゲームを作ろうとしている人間です。
そのような者なので、適当な投稿をするかも知れません。
他の方の投稿を信用してください。
アバター
ムノクラ
記事: 2011
登録日時: 2018年2月23日(金) 11:41
連絡を取る:

Re: プラグインのレビュー依頼:敵グループのランダム化

投稿記事by ムノクラ » 2020年3月29日(日) 12:35

「途中から出現」の敵を指定するとエラーになります。
SyntaxError: Unexpected token u in JSON at position 1

どうも、最初からこの問題はあったようです。
添付ファイル
MNKR_RandomEnemies7.js
(2.28 KiB) ダウンロード数: 2 回
最後に編集したユーザー ムノクラ on 2020年3月29日(日) 13:08 [ 編集 2 回目 ]
---
JavaScriptの基本を学習せずにツクールのプラグインやスクリプトを使って横着してゲームを作ろうとしている人間です。
そのような者なので、適当な投稿をするかも知れません。
他の方の投稿を信用してください。
アバター
Plasma Dark
記事: 669
登録日時: 2020年2月08日(土) 02:29
連絡を取る:

Re: プラグインのレビュー依頼:敵グループのランダム化

投稿記事by Plasma Dark » 2020年3月29日(日) 13:08

正直に告白しますと、まだ変数とクラスの違いさえ、読んでいてあやふやな感覚でいます。


なるほど、大変失礼しました。
コードとしてぱっと見きれいな形になっていたものですから、質問者の理解度を取り違えてしまったようですね。

randomEnemyIdは代入先と同じ名前ですが、これも別にすべきなのでしょうか?


すでに修正されているようなのでもう必要ない説明かもしれませんが、念の為。
変数の名前の衝突を気にするべき範囲は、その変数のスコープと呼ばれる有効範囲の中だけです。
トリアコンタンさんのご説明にもある通り、 var で宣言された変数のスコープは、宣言された関数の中です。
forEach に渡している関数の中で宣言した randomEnemyId と selectEnemyId 関数の中で宣言した randomEnemyId の名前の衝突は気にしなくても大丈夫です。

var ではなく let を使うと、スコープは関数以上に狭くなります。
その変数が宣言された場所と同じ一番内側の {} 内(ブロックスコープと呼ばれる範囲内)の、宣言された行より後でしか有効になりません。
具体例をあげると以下のようになります。

コード: 全て選択

(function() {
  //console.log(x); // 宣言前に表示しようとするとエラー
  let x = 100;
  console.log(y);  // undefined と表示される
  if (true) {
    let x = 300;
    console.log(x); // 300 と表示される
    var y = 50;
    console.log(y); // 50 と表示される
  }
  console.log(x); // 100 と表示される
  console.log(y); // 50 と表示される
})();


変数の有効範囲をより狭く絞り、必要な範囲でのみ利用できるようにしておくとコードの見通しがよくなる……というのは慣れた立場での意見ですので、あまりピンとこないかもしれませんが。
ブロック内で宣言された変数がブロックの外で使われたり、宣言前に変数が操作される、なんていうお行儀の悪いコードをエラーで咎めてくれるのも let の良いところです。
こういった利点があるので、 var ではなく let を用いるべき、と主張されるのです。
もちろん、再代入の必要がないならばしぐれんさんの仰る通り、 const を利用すべきではあります。
アバター
Plasma Dark
記事: 669
登録日時: 2020年2月08日(土) 02:29
連絡を取る:

Re: プラグインのレビュー依頼:敵グループのランダム化

投稿記事by Plasma Dark » 2020年3月29日(日) 13:16

「途中から出現」の敵を指定するとエラーになります。


これはエラーメッセージから判断して、 RandomEnemy が指定されていない敵を出現させようとしていませんか。
つまり、 $dataEnemies[enemyId].meta.RandomEnemy が undefined になっているのだと思います。
JSON.parseは [undefined] をパースできずエラーになるので、 undefined の場合(つまり、ランダムに変化させたくない場合)のみ特別扱いしてあげましょう。
selectEnemyId 関数を以下のように修正することで治るんじゃないかと思います。

コード: 全て選択

function selectEnemyId(arrayData) {
+  if (!arrayData.meta.RandomEnemy) {
+    return null;
+  }
  var pool = JsonEx.parse( `[${arrayData.meta.RandomEnemy}]` );
  var randomEnemyId = Number(pool[Math.randomInt(pool.length)]);
  return randomEnemyId;
};


+と書いた行を追加するだけです。(もちろん、行頭の+は消してください)
アバター
ムノクラ
記事: 2011
登録日時: 2018年2月23日(金) 11:41
連絡を取る:

Re: プラグインのレビュー依頼:敵グループのランダム化

投稿記事by ムノクラ » 2020年3月29日(日) 14:08

Plasma Dark さんが書きました:
「途中から出現」の敵を指定するとエラーになります。


これはエラーメッセージから判断して、 RandomEnemy が指定されていない敵を出現させようとしていませんか。
つまり、 $dataEnemies[enemyId].meta.RandomEnemy が undefined になっているのだと思います。
JSON.parseは [undefined] をパースできずエラーになるので、 undefined の場合(つまり、ランダムに変化させたくない場合)のみ特別扱いしてあげましょう。
selectEnemyId 関数を以下のように修正することで治るんじゃないかと思います。

コード: 全て選択

function selectEnemyId(arrayData) {
+  if (!arrayData.meta.RandomEnemy) {
+    return null;
+  }
  var pool = JsonEx.parse( `[${arrayData.meta.RandomEnemy}]` );
  var randomEnemyId = Number(pool[Math.randomInt(pool.length)]);
  return randomEnemyId;
};


+と書いた行を追加するだけです。(もちろん、行頭の+は消してください)


状況は、お察しのとおりです。
今は
ランダム2体、メモなし1体(途中から出現)のグループでテストしています。
ランダム2体、メモなし1体のグループでも、同様の状況だと分かりました。

頂いたコードを追加したところ、全ての敵が出なくなり、自動勝利となります。
下記のコードに書き換えるなど試みましたが、いずれも自動勝利となってしまいます。

コード: 全て選択

        if (!arrayData.meta.RandomEnemy) {
            return randomEnemyId;
        };


コード: 全て選択

        if (!arrayData.meta.RandomEnemy) {
            var randomEnemyId = null;
            return randomEnemyId;
        };


コード: 全て選択

        if (!arrayData.meta.RandomEnemy) {
            var randomEnemyId = Number(1);
            return randomEnemyId;
        };
添付ファイル
MNKR_RandomEnemies71.js
(2.35 KiB) ダウンロード数: 1 回
---
JavaScriptの基本を学習せずにツクールのプラグインやスクリプトを使って横着してゲームを作ろうとしている人間です。
そのような者なので、適当な投稿をするかも知れません。
他の方の投稿を信用してください。

“MV:質問” へ戻る