1. このサイトではcookie (クッキー) を使用しています。サイトの利用を継続した場合、cookieの使用に同意したものとみなさせていただきます。 詳しくはこちらをご覧ください。

コミュニティ版コアスクリプト議論場

くらむぼん2018-08-24に開始した「プラグイン制作・技術」の中の討論

  1. くらむぼん

    くらむぼん モデレーター スタッフ モデレーター

    コミュニティ版コアスクリプト(http://blog.nicovideo.jp/atsumaru/atsumaru-corescript.html)について
    「誰でも見られる」「日本語で話せる」「長文が書ける」をすべて満たす場がなかったので立てました。

    公式な場ではありません、ご了承ください。
     
    #1
    メルサイア, 神無月サスケ, BIT/O他1人 がいいね!しています
  2. サンシロ

    サンシロ ユーザー

    まずはこの場を設けてくださったくらむぼんさんに感謝します。
    ありがとうございます!

    ここはコミュニティ版コアスクリプト全般の話題を扱うスレッドであって
    特定の話題専門のスレッドではないと認識しています。
    しかしこのスレッドが開設された直接の発端となった話題は確かに存在します。

    まずは最初の話題としてこのスレッドが開かれるに至った経緯について
    記録を残すためリンクを張っておきます。

    • 直接の経緯はこちら
    https://twitter.com/rev2nym/status/1032723317923270656

    • さらに発端となった GitHub の issue ※ がこちら
    https://github.com/rpgtkoolmv/corescript/issues/175


    ・GitHub : ソースコード共有場所、開発場所。
    ・issue : 問題提起のこと。バグ報告や要望、改善案の提案など。
     
    #2
    メルサイアくらむぼん がいいね!しています
  3. サンシロ

    サンシロ ユーザー

    issueの本文はリンク先を見てもらいたいのですが
    一応、以下和訳(と補足)します。()内は補足。

    ■問題提起
    コミュニティ版コアスクリプトと
    初期搭載の公式プラグインの「MadeWithMv.js」が競合している。
    原因は「$gameSystem」が生成される前に「$gameSystem.onFrameUpdate()」が
    Scene_Baseから呼ばれていることだ。
    「$gameSystem.onFrameUpdate()」は「$gameSystem._frameCount」変数を
    数え上げ更新するメソッドだ。
    「$gameSystemはScene_Boot.start()」によって生成される。
    ゆえに「Scene_Boot.start()」の実行前に「$gameSystem」にアクセスしてはならない。


    ここでいくつかの問題がある。

    • 「MadeWithMv.js」との競合を解決すること
    しかしながら、これはプラグイン側の不具合でもあるので優先度は低い。
    (コミュニティ版コアスクリプトに問題がないとは言っていない)​

    • 「Scene_Base」からゲームオブジェクトにアクセスしてはならない
    (ゲームオブジェクトとは「$game〇〇」のこと)
    シーンベースの状態ではゲームオブジェクトが生成されている保証はない。
    これは(絶対に)修正されなければならない。​

    • 「Scene_Boot」で「setupNewGame()」を呼ぶことは適切だろうか?
    ブートシーンではニューゲームが始まっていないにもかかわらず
    「DataManager.setupNewGame()」がブートシーンで呼び出される。
    私はこれが最も重要な(難しい)問題だと考えている。​

    • 「$gameSystem.update()」メソッドとして定義すべきではないか
    1フレームに必ず1回呼ばれるメソッドならフレーム更新メソッドとするのが適切だ。​

    • 「$gameSystem._framesOnSave」の用途が不明瞭
    「$gameSystem._frameCount」はセーブファイルコンテンツに含まれる。
    そのため今となっては「_framesOnSave」は用を為さないはずだ。
    (「_framesOnSave」はもともと
    プレイ時間としてセーブファイルに記録されていた変数で
    その用途を今は「_frameCount」が担っているが消されずに残っている。)​

    • そもそもなぜ「Graphics.frameCount」を使うのをやめたか?
    そして逆に他の個所では「Graphics.frameCount」を使い続けてる理由は何か?​


    ■解決アイデア
    • 「DataManager.setupNewGame()」はどこから呼ばれるべきか
    (ニューゲームのセットアップ用でゲームオブジェクトを生成するメソッド)​

    ・プランA
    (「setupNewGame()」が呼ばれるべきタイミングは)
    タイトルシーンからマップシーンに遷移する直前が適切だが、
    現在すでにもとからそのタイミングで「setupNewGame()」が
    呼ばれるようになっている。
    (つまりゲーム起動からニューゲーム開始の間に2回実行されている。)​

    ・プランB
    「$gameSystem」だけタイトルシーンの開始時に生成する。
    そのほかのゲームオブジェクトについては
    専用の新しいシーンクラス
    (例えば「Scene_Setup」や「Scene_NewGame」)
    を定義してすべてのゲームオブジェクトを
    そのシーン(ニューゲームシーン)で生成する。
    その後に初期マップのマップシーンへ移動する。​

    私としてはプランBがより良いと思う。
    この方法ならニューゲームを明示することができる。​

    • 「$gameSystem._frameCount」にどのようにアクセスするか?
    新たにベース的なクラス(例えばScene_GameBase)を定義する。
    ゲームタイトル以降のシーンはこの新しいベースシーンを継承するようにする。

    それと…
    「$gameSystem._framesOnSave」と「Graphics.frameCount」の扱いについては
    後で考えましょう。

    和訳は以上です。
     
    最後に編集: 2018-08-25
    #3
    くらむぼん がいいね!しました
  4. サンシロ

    サンシロ ユーザー

    また別の Github の issue に
    「Graphics.frameCount」に代わって
    「$gameSystem._frameCount」を使った理由が提示されていました。

    https://github.com/rpgtkoolmv/corescript/pull/171

    前者はレンダー回数を計るため
    リフレッシュレートが高い環境では
    正確に時間を計ることができないためとのことでした。
    「$gameSystem._framesOnSave」はセーブファイルの互換性のために
    残しているとのことでした。

    それに対して、私は「Date.now()」による時間計測を提案しています。
    その方が正確に時間を計ることができるからです。
    (あと追加ですが「$gameSystem」に全シーンが毎フレームアクセスする
    なんてこともせずに済むようになります。)

    これに対して、くらむぼんさんから
    いくつかの反論をツイッターDMでいただいていますが
    この場で私が代わりにコピペするのは気が引けるため
    (コピペをする許可は得ていますが)
    なるべくご本人に書いてもらうのが望ましいのではないか
    と考えています。
     
    #4
    くらむぼん がいいね!しました
  5. サンシロ

    サンシロ ユーザー

    ■あとだし補足
    既にGitHubたツイッターなどに書いている内容ではありますが
    あとだしで補足することをどうかお許しください><

    英語も下手ですし
    うまく伝えられないところがあるのですが
    絶対に修正しなければならない問題は
    すべてのシーンの親クラスである「Scene_Base」が
    「$gameSystem」をはじめとするゲームオブジェクトに
    アクセスすることを改めることだと考えています。

    「MadeWithMv.js」とのクラッシュの直接の原因は
    それをやってしまったコードでした。

    「MadeWithMv.js」のケースを無視するにしても
    問題のコードは子クラスが生成する仮想でもないオブジェクト
    (つまり存在していることが保証されないオブジェクト)に
    親クラスがアクセスするというコードで
    これはクラス継承の基本を無視した
    非常に良くないコードだと考えています。

    今回私の issue と commit は
    上記の課題を適切に処理するためのもの
    (とついでに周辺整理)でした。

    すべての commit が適用されなければ
    気が済まないとまでは言いませんが
    「Scene_Base」によるゲームオブジェクトへの
    アクセスを禁止することは必要だと考えていますし
    そこだけは譲れません。

    ひとまず以上です。
     
    #5
    くらむぼん がいいね!しました
  6. くらむぼん

    くらむぼん モデレーター スタッフ モデレーター

    サンシロさん、今までの分まとめありがとうございます!
    以下、まずはこれに対する私の最初の返答DMをコピーします。



    まずScene_GameBaseを用いた大改変ですが、これは今回適応するのは難しいかなと考えています。
    理由はやはり、改変の大きさと影響の読めなさです。

    既存のどんなプラグインもScene_GameBaseの存在を想定していませんし、
    BootとGameBaseが切り離されたことでいくつかのメソッドの呼び出しタイミングが変わっていますから、
    ここにフックしたプラグインが破壊される可能性も新たに発生してしまいます。

    確かに現状MadeWithMvが動かないのは心苦しいですが、
    正直コアスクリプトでの$game系の生成をプラグインが先送りするというのはかなり良くない振る舞いで、
    こういったプラグインをケアするためにコアスクリプト側が対応して
    他のお行儀の良いプラグインが壊れるかもしれない機会を作ってしまうのは、ちょっとバランスが悪いかな、と考えています。
    なのでこの件についてはMadeWithMvの方になんとか適応してほしいところです><


    また、「framesOnSaveからframeCountへ代入してるのはよくない」も
    「Date.now()使ったほうがスッキリする」も正しいのですが、ここも互換性との折り合いを考えてのことです。

    まず、前者の微妙な代入ですがこれはセーブデータ互換性維持のためです。
    確かにGraphics.frameCountのカウントアップが良くなくてframeCountを新設したわけですが、
    これを新設する前のセーブデータを新設した後にロードしてしまった場合、
    中身がundefinedなのでカウントアップを継続できなくなります。

    framesOnSaveは不正確な値ですが、
    何らかの値で代用しないとプレイ時間が0からのスタートになってしまいますから、
    セーブデータが新設の前後をまたいだ時だけ特別に代入しているわけです。

    また、「Date.now()を使え」も実にわかりやすいご提案で、
    「もし新作が出るなら、こうしてほしい」という要望としてなら大変有望だと思うのですが、
    実際は現在のカウントアップ仕様(例えば、シーン間のローディング中はカウントアップしない仕様)との互換性を守ることが先決になってくるわけです。

    それを言うと厳密にはそもそもここに修正を加えたこと事態が互換性破壊なのですが、
    これは「240FPSの高品質モニターでプレイ時間が4倍早く過ぎ去る」という明らかなバグの修正が目的でしたから、
    最小限そのバグを修正するという変更に留めるように互換性に配慮しつつ修正したのです。
     
    #6
    サンシロ がいいね!しました
  7. くらむぼん

    くらむぼん モデレーター スタッフ モデレーター

    以上がDMです。
    それに付け加えて、あともう少し。


    確かに、現在のScene_Baseはゲームオブジェクトへのアクセスについて不安全なようですね。
    ただご提案のようなScene_GameBaseを用いた変更でプラグインが壊れる可能性もあります。
    例えば「Scene_Baseを継承して自作したシーンで、checkGameoverを使用するプラグイン」が存在すれば、
    今回のサンシロさんの変更を適応すると、その自作シーンはcheckGameoverを失ってしまいますから、動作不可能になります。

    このような改変は既存のプラグインへの新たな不利益の可能性もありますから、
    構造的により正しさのある提案でも採用するのが難しいということになってしまうわけです。


    ただし、サンシロさんの意見で私の考えが変わったところもあります。
    私は上の書き込みではMadeWithMvの方に構造の改善を期待していましたが、
    互換性をより広い範囲で維持することを考えれば、ここは対策が必要そうです。
    ただし大きな改変は避ける必要がありますので、単純ではありますが
    エラーが発生する箇所にif ($gameSystem) を加えてエラーを回避しようと考えています。

    この解決策はその場しのぎに近いものですし、
    「Scene_Splash中はカウントアップしないのは一貫性の観点からどうなのか」
    など新たな問題も抱えています。
    しかし「より多くのプラグインを守るためにはどうするか」という点を大切に考えていくと、
    構造の整合性がなくて一貫性もないような解決策を、それでも採用せざるを得ない場合が
    往々にしてあるのかな、と思います。
     
    #7
    サンシロ がいいね!しました
  8. サンシロ

    サンシロ ユーザー

    時間を計る他の方法はないのですか?
    フレーム数を数えるにしても別の手段はないのですか?
    例えば「Scene_Base」ではなく「SceneManager._frameCount」で計測するとか
    あるいは個別の「Scene_○○」で計測するとか
    微妙な構造ですが、より安全なコードになりますし
    「MadeWithMV」の互換も復活します。

    (私個人は「Scene_GameBase」新設案を推していますが…
    「Date.now()」でもよいです。)

    セーブファイル互換を保つためなら
    今まで通りにフレーム数を「_framesOnSave」に
    記録すればよかったのではないですか?

    (とはいえ私個人としてはセーブファイル互換は
    むしろ考慮してはいけないのではと考えています。
    スクリプト開発の手足を過度に縛るだけですし
    今回以前にもセーブファイル互換は破壊されています。)

    あと、くらむぼんさんも問題をご指摘されていますが
    「Scene_Base.checkGameover()」も
    維持すべき箇所ではなく、直すべき箇所です。

    それによってプラグインの互換性が失われたなら
    プラグイン作者さんが対応すれば済むことだと思います。

    追記
    (「Scene_Base.checkGameover()」も
    「Scene_GameBase」に移動させてたつもりでしたが
    今確認したところやっていませんでした…)
     
    最後に編集: 2018-08-26
    #8
  9. サンシロ

    サンシロ ユーザー

    互換性について私の立場を説明したいです。
    互換性には重要なものから並べて以下の項目があると思っています。

    ・プロジェクトデータ互換性
    これは必ず守られるべきものだと思います。
    RPGツクールMVエディタから生成された
    プロジェクトデータを問題なく読み込めることです。
    これが破壊されてしまったらそれはもはやRPGツクールMVでは
    なくなってしまうでしょう。

    ・スクリプト機能互換性
    よほどの特別な理由がない限り守られるべきものだと思います。
    「あるメソッドの内部仕様が変わっても外部仕様が変わらない」だとか
    「あるメソッドが廃止になっても代替の機能が提供される」とかのことです。

    ・プラグイン完全互換性
    ある程度考慮されるべきですが
    他に理由があるなら破壊されてもかまわないものだと思います。
    プラグインの互換性が完全にはなくなったとしても
    上記の互換性があればプラグイン作者の
    メンテナンスによって回復できます。

    ・セーブファイルデータ互換性
    守られる優先度の低い互換性で
    むしろ守らないほうが良い、まであると思います。
    これを守ると保守の手間にキリがなくなります。
    回避コードが増えてゆくくらいなら破壊すべきです。
    (わざと破壊すべきではないですが。)

    上2つはほぼどんな時も守られるべきですが
    下2つはコアスクリプトの改善
    (例えばパフォーマンス改善やモジュール結合度を疎にするとか)が
    見込めるならある程度は軽視されてもよい互換性だと思います。
     
    最後に編集: 2018-08-26
    #9
  10. サンシロ

    サンシロ ユーザー

    ただ、コアスクリプトへの大規模な変更には難しさがある
    ということは私も理解できます。

    なるべく小さな変更で妥協できる案としては次のあたりだと思います。

    ・「Scene_Base」から「$gameSystem」に触れないようにする

    ・「$gameSystem._frameCount」でフレームを数えず
    「SceneManager._frameCount」でフレームを数えるようにする

    ・セーブ時は「SceneManager._frameCount」を
    もともとの「$gameSystem._framesOnSave」に代入して保存し
    「$gameSystem._framesCount」は使わないようにする
     
    #10
  11. くらむぼん

    くらむぼん モデレーター スタッフ モデレーター

    折り合いのつく着地点を考えてくださったのですね、ありがとうございます!
    確かに、フレームのカウント役をSceneManagerに移行すれば$gameSystemに触る必要はなくなりそうです。

    ただ3点目については互換性の問題がありますので、それは反映できないと思います。
    現状の$gameSystem._framesOnSaveはGraphics.frameCountを復元する役目を持っています。
    ということはframesOnSaveの役目を変更して今までと違う数値を代入してしまうと、
    これまでに書かれたGraphics.frameCountを読み取っているプラグインの挙動が変化してしまいます。

    1点目・2点目については互換性への配慮が行き届いていると思いますので、
    この内容でプルリクエストを出していただければ、コミュニティ版に受け入れ可能だと思います!


    あ、それから互換性をいくつかの種類に分けて、重視の度合いを分ける考え方は大変興味深いと思いました。
    むしろ個人的には大きなメリットを手に入れるために小さな互換切りをするという選択はありだと思っています。
    ただ、コミュニティ版コアスクリプトは改善を確実に「本家」RPGツクールMVに受け入れてもらうことを目的としており、
    それを達成するためには互換性の堅持が必要だ、という事情が存在しています。
    そんなわけですので、色々とお互い苦労しますね…><
    どうぞよろしくお願いします。
     
    #11
    サンシロ がいいね!しました
  12. サンシロ

    サンシロ ユーザー

    承知いたしました。
    事情があるとはいえフレームカウントが
    2重管理になってしまう点で残念な感は残りますが
    「Scene_Base」の「$gameSystem」アクセスを消せる点で
    一定の前進があったいうことでこちらからは区切りにしたいと思います。

    近いうちにプルリクエストも用意したいと思います。
    ご対応ありがとうございました。
     
    #12
    くらむぼん がいいね!しました
  13. くらむぼん

    くらむぼん モデレーター スタッフ モデレーター

    プルリクエストありがとうございます!
    …あ、大変申し訳ないのですが、Github Issueに「こっちの議論を見て」と書くのは勘弁してください><
    ここは公開ですが日本語なので、グローバルにいうと「密室での議論」になってしまいます。
    「公式な場ではありません、ご了承ください」とはそういうことです。
    互換性に配慮したPRを出しました、など一行でもいいので、リンクを貼るのでなくて説明でお願いします~
     
    #13
  14. くらむぼん

    くらむぼん モデレーター スタッフ モデレーター

    上記お願いへのご対応、ありがとうございました!
     
    #14
  15. しぐれん

    しぐれん ユーザー

    Game_Troop.expTotal()なんですが、撃破した上で蘇生し、さらに逃げ出してhidden()すると経験値などがもらえない問題があります。
    これ、修正するべきなんでしょうか?
    仲間呼びプラグインを作る上で気になったので。
     
    #15
    ネコタ がいいね!しました
  16. くらむぼん

    くらむぼん モデレーター スタッフ モデレーター

    お?
    うーん、違和感があると言われればありますが「最後に逃げたんだから撃破はしてないだろ」と言われればそんな気もするし…

    つまり仕様かバグかわからないので、手を付けるのが難しいですね><
    …最初に開発した時そんなレアなパターン想定してたんだろうか…わからぬ
     
    #16
  17. しぐれん

    しぐれん ユーザー

    となると、これは手出しができませんね。
    うーん、困った。
    修正案はあるんですが、コアスクリプトのバージョン差によるバグの温床になるので難しいですね。
    (Game_TroopにGame_Enemy[]を返すrewardEnemys()を追加するのを検討していました)
     
    #17

このページを共有