https://linen.dev logo
車両のスクリプトを軽量化したい
# forum
u
以前から課題になっていた自車の軽量化に関してですが、いよいよ煮詰まってきたので皆様のご意見を頂戴したくすべての情報を公開します。 車両に搭載されているもの ・特殊発光 ・車掌スクリプト ・車内LCD ・現在駅表示 ・パンタ描画 ・走行音スクリプト ・方向幕スクリプト 重い原因? ・描画関係を見直したら重くなった ・材質をまとめてモデルを改変した ・一部描画処理をポリ描画処理に変更した 主な動作 ・サーバーだと深刻なTPS低下を招く ・シングルで運転していても数分に一回FPSが極端に低下する(ほぼカクつく) 原因とされているものと最近変更したもの(自分調べ) 基本的に同じこと書いてあるのでCrail369.jsを例に ・方向幕及び現在駅表示のテッセレーターを2点から推測して面を作る方式に変更した renderRollsign(entity)内のrenderVartexListWithUV処理 ・renderPanelrighting(entity)内の速度のデジタル数字表示も同じく2点から推測して面を作る表示に変更した //sokudokei_mater付近のrenderVartexListWithUV処理 ・renderPanelrighting(entity)内のマスコン表示をオブジェクト処理に変更した masconpot付近 Crail369.jsスクリプト内の構成は以下の通りになっています 最初に各種前提条件の読み込み //include ←車両に共通するスクリプト類を外部にまとめる //include ←LCD表示 function init(par1, par2)←最初のオブジェクト登録 function render(entity, pass, par3)←発光、非発光や表示するfunctionの車種確認 function renderUndais(entity) ←運転室内の非発光部分の動作処理(マスコンなど) function renderPanelrighting(entity) ←運転室内の発光部分の動作処理(速度計や運転パネルやそのほか発光機器類) function renderRollsign(entity)←方向幕及び現在駅表示の処理 これらを踏まえて以下の疑問が生まれました ・最適なテッセレーター処理構文とオブジェクト分けして指定するのはどっちが軽いかor最適化はどうすべきか ・//include方式で複数車両で共通する処理部分(ドアやパンタ、車掌スクリプトなど)を一つの別のファイルにしてインポートする方式は悪手なのか? ・構文の書き方の中でGL11.glPushMatrix();~GL11.glPopMatrix();でくくる方法はなるべく避けたほうがいいのか ・getintなどをいっぱいするとサーバー環境で負荷をかけるのか ・renderスクリプトでも鯖の動作に影響を与えるとのことだが具体的にどういった処理を避けるべきなのか ちょっと煮詰まってよくわからなくなってきたのでいったんここで書き出してみたいと思います。 何かこれを見ていただいてちょっとでも思う節があればどんどん反応いただきたいと思います https://cdn.discordapp.com/attachments/1553829228993323048/1553829229311819897/crail365-kako.zip?ex=6abaabbd&is=6ab95a3d&hm=4ce63bff03a340da4eccb808cfd8baaf06d93ca51afa8e5d3466c1ba4694ca11& https://cdn.discordapp.com/attachments/1553829228993323048/1553829229848825856/Crail369.js?ex=6abaabbd&is=6ab95a3d&hm=cf25d783533a8354fa2de2d385f37a55a7dd5f1a69fa648a6c63f5ecd2aa6ef1&
renderVartexListWithUV処理について言及しているのにもかかわらずその処理が共通ファイルのほうにあったのでこっちを至急あげます。 CrailcostomLibsのスクリプトの内容は以下の通りです。 function hi03_render_conductorSystem(entity, pass, par3) →車掌スクリプトとATO出発機構 function headlights(entity, isOn) →ポリ分けした前照灯尾灯の表示処理 function renderDoors(entity, isInner) →ドアカット機構と特殊発光に対応した列車のドア制御 renderDoors(entity, 1);という形で車両スクリプトから渡されておりこれが結構重さに拍車かけてる可能性は全然ありそうです。 function render_doorLamp(entity) →ドアランプ getDoorMovementR(entity) getDoorMovementL(entity) function sigmoid(par1) →ドア処理に必要なもの function renderVartexListWithUV(pos, uv) →2点から推測して面を作る処理 function render_panta(entity) →パンタのポリ分け処理 このスクリプトはパック内搭載車両すべての共通する部分を抽出しておりここが全体に影響を与えている可能性は結構高そうです。 https://cdn.discordapp.com/attachments/1553829228993323048/1553830027047731200/CrailcostomLibs.js?ex=6abaac7b&is=6ab95afb&hm=1bcef87d0c51ebf43ffb7f072886d9745b776b4fa3fbe20af248b619cb7e08cb&
h
ぱっと流し見した感じですが、描画スクリプト側のDataMap.set~系で同期がONのとき(フラグが1 or 3のとき)、ifで条件を付けずに垂れ流すと、すべてのPCからサーバーに向けて毎フレームパケットを送信する(60fpsなら1プレイヤーあたり1秒に600回※送信している)ことになるので、そこがちょっと気になるところですね ※1秒あたりフレーム60回 x 材質2枚 x pass0~4 これは ・renderスクリプトでも鯖の動作に影響を与えるとのことだが具体的にどういった処理を避けるべきなのか に関わるところかなと思います render_doorLampでは
Copy code
javascript
        //ドア検知発車標時
        if (doorL == 0&&doorR == 0) {
            dataMap.setInt("doorclosing", 1, 1);
        }else{
            dataMap.setInt("doorclosing", 2, 1);
        }
となっていますが、
Copy code
javascript
        //ドア検知発車標時
        var doorclosing = dataMap.getInt("doorclosing");
        if (doorL == 0 && doorR == 0) {
            if (doorclosing !== 1) dataMap.setInt("doorclosing", 1, 1);//doorclosingが1じゃないときにsetIntを実行する → 1のときは送信しない
        } else {
            if (doorclosing !== 2) dataMap.setInt("doorclosing", 2, 1);//doorclosingが2じゃないときにsetIntを実行する → 2のときは送信しない
        }
とし、dataMapの同期送信を常に送り続けないようにして症状が改善するか試してほしいです
・最適なテッセレーター処理構文とオブジェクト分けして指定するのはどっちが軽いかor最適化はどうすべきか モデル上に実装→描画はキャッシュ化が効くので、モデル形状変更や動的UVをしない(テッセレーターなしで表現できる)のであればモデル上で実装したほうが軽いと思います(テッセレーターは毎回頂点を組むため)。 テッセレーター後に形状固定して描画するならコンパイルして描画する(=テッセレーターのキャッシュ化)という方法もあります ・//include方式で複数車両で共通する処理部分(ドアやパンタ、車掌スクリプトなど)を一つの別のファイルにしてインポートする方式は悪手なのか? //includeは読み込み時にスクリプトを結合する処理が走るぐらいの違いしかないので、通常の描画中に影響はないです(マイクラ起動後初めて車両を出す瞬間のロードが重たくなるという報告はある) あとからコード修正したいときにすべてのスクリプトを書き換えなくても一元的に修正できるという保守性の観点では優れています ・構文の書き方の中でGL11.glPushMatrix();~GL11.glPopMatrix();でくくる方法はなるべく避けたほうがいいのか オブジェクトの移動処理を挟むなら必須です。逆に言えばオブジェクトを動かさない描画(GL11.~等を使わない描画)では不要なのでそこは削除しても大丈夫ですが、その修正の効果はほとんどないと思われます。
Copy code
javascript
GL11.glPushMatrix();
body.render(renderer);
GL11.glPopMatrix();

↓

body.render(renderer);//移動処理をしていないためglPushMatrix、glPopMatrixは不要
・getintなどをいっぱいするとサーバー環境で負荷をかけるのか get系はクライアント←→サーバーの通信が発生しないため多用しても重くなりません(getは描画スクリプトなら描画スクリプト側、サーバースクリプトならサーバースクリプト側に保存された値を読む) set系は同期フラグを立てるなら通信が発生するので大量に実行すると重くなります(前の話につながる)
u
ご丁寧にありがとうございます。 いったん咀嚼してみます
いったんサーバー側の負荷の問題を念頭に置きつつ車両のスクリプトを一巡してみた結果、大量のDataMapが生成されていることに気づきました。 先ほどの話にもあるとおりサーバーへの負荷の問題でいうとクライアント←→サーバーの通信がどれだけ起きるのかという点が重要なようで、以下の点が疑問点として生まれました。 1. DataMapの数そのものはサーバー負荷や通信量にどの程度影響するのでしょうか? ・DataMapの保持数が多いだけで重くなるのか ・ 実際に負荷となるのは値の変更・同期時なのか 2. 運転会など大量の列車が走る際のラグ対策の基本的な指針としては、サーバーとの 「値変更時のみ同期を徹底」と「datamap生成を抑える」 のどちらがより効果的になるでしょうか? 3. DataMapには ・スクリプトで生成したもの ・ カスタムボタンで生成されたもの ・ IFTTT地上子で生成されたもの がありますが、サーバーとの同期有無や同期方法を細かく制御できるのはスクリプト方式のみでしょうか? 4. IFTTT地上子で編成全車にDataMapを書き込む場合、 ・IFTTT側で全車へ直接設定する ・先頭車のみ変更し、スクリプトで他車へ展開する のどちらが通信量や負荷の面で有利でしょうか? 5. ServerScriptとRenderScriptの使い分けについても確認したいです。 描画以外は基本的にServerScriptへ書き、RenderScriptは表示処理中心と考えていますが、この認識で問題ないでしょうか? setIntをRenderScript側でも使用したことがあるので改めてこの2つの認識を確認したいです。 そのほか構文的なアドバイスなどあればご協力をお願いします。 長文失礼しました。 https://cdn.discordapp.com/attachments/1553829228993323048/1554853667369263166/image.png?backend=b2&ex=6abe65d2&is=6abd1452&hm=a5eb43a2ffba2594db4f766f4c3dc8d5dc44114f6eb0890bfcba2e4f253d5653& https://cdn.discordapp.com/attachments/1553829228993323048/1554853667956461578/crailserverScript.js?backend=b2&ex=6abe65d2&is=6abd1452&hm=f11d1396343aff029ce84da41cda44b7db67776afb770230649d1b5acba63d2c&
u
大域的な意見ですが、描画スクリプトはそれそのものが「FPSに連動して動いている巨大なFor文制御ロジック」と私は捉えていて 「Tickで間引いて良いもの」「そもそも更新を監視しなくて良いもの」「絶対に今のフレームでわかってるべきもの」と、時間軸を切り分けるのも良いかもしれません(TickUpdateが走った時だけ処理に入り、処理が終わればフラグを下ろす、だけでも実行回数減らせるので)
おおむね音声スクリプトについてはfpsをティックに置き換えて考えているが、あちらはむしろ更新速度が足りない。。。 かといって、描画スクリプトで無理やり音声は鳴らしたくないな。。。
※For文との違いは、一度走ったスクリプトの内容は、メモリに格納しない限り次の実行では揮発していること ↑ DataMapなどによる保存が必須
h
まず前提の知識として、DataMapはクライアント側とサーバー側で分かれて保有されており、サーバーとクライアントで違う値を保有することができます。同期付きでsetすることで通信が発生し、両者に同じ値を設定することができます。 ・DataMapの保持数が多いだけで重くなるのか DataMapの中身はHashMapで、値を拾ってくるときの検索の計算量はほぼO(1)です。保有数がgetやsetに与える影響はほとんどないです。 ・実際に負荷となるのは値の変更・同期時なのか その認識であってます。同期なしでも内部的にはデータ生成処理などが走っているので、一度に大量のsetをするとクライアント側が重くなると思います。大量のgetはほとんど影響はないと思います。 ・ラグ対策の基本的な指針は「値変更時のみ同期を徹底」と「datamap生成を抑える」のどちらが有効か 値変更時のみ同期を徹底することでラグを抑えることができます(通信が発生する処理はラグに直接関わってくるため) DataMapの大量保有はメモリ消費量の方に関連するので、大量保有がラグの直接的な原因になる可能性は低いかなと思います(メモリ逼迫でサーバー側が重くなるケースはありそうです) ・同期方法を細かく制御できるのはスクリプト方式のみか その認識であってます。カスタムボタンは同期+保存フラグ、IFTTTブロックは同期フラグつきです。 ・IFTTTで全車へ配信する場合と先頭車→他車両への配信はどちらが重いか 同期フラグを立てるのであればどちらの方法をとっても合計の通信量は同じになると思います。 同期フラグを使わずに各車へ配信する(サーバースクリプト側だけで値が必要などの状況)のであれば先頭車→他車両への配信が軽いかなと思います。 ・ServerScriptとRenderScriptの使い分け クライアント側でしか取れない処理(キー入力など)を同期する場合はRenderScriptから送信する必要がありますが、基本的にはServerScriptから送信する形式が理想的です。
u
したら先頭車である時データ配信元となり、先頭車以外はあんぐりと口を開けて待つマスタースレーブの関係が発生し。。。(職業病)
サーバースクリプトがいわゆるオーケストレータ(まとめ役)になって、Renderでどうしても拾わなきゃいけない値はTickごとにサーバスクリプトに渡してやるとか良さそう むかしトラえもん先生がそれ全部Renderで済ませててバケモンだったな
u
おお、ご丁寧にありがとうございます。 やっぱり同期の仕組みを考えながらやったほうが効果的なようですね 描画スクリプトを巨大なfor文制御ということで要素ごとにTickを間引くというのは新しい知見でした。 いまスクリプトの改修を行ってるのでラグ対策とTick間引きの観点も取り入れてみたいと思います
4 Views