スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

スカイリム/CKのスクリプトPapyrusその2 (最終更新 2012/3/25)

前の記事が5項目になったので、新しい記事を起こしました。

※基本的に「オブリでスクリプトをいじったことがある方」に向けた内容となっています
※「それ違ってるよ」みたいなのがありましたら、生暖かく教えてください!
 


・2012/2/22 複数スクリプトの関連付け

スカイリムではオブリと同じように、アクターやアイテムやクエストなどにスクリプトを関連付けることが可能ですが、「複数のスクリプトを関連付けできるようになった」のは大きな変更点だと思います。
今回はその点に関していろいろと検証してみました。

まず試してみたのは「クエスト」に対する複数スクリプトの関連付け。
ひとつのクエストに対して
 Script1 (extends Quest)
 Script2 (extends Quest)
の2つのスクリプトを関連付けてみました。

OnInitイベント処理をそれぞれに作り、Script1では「1」と表示し、Script2では「2」と表示するようにしました。
すると「1」と「2」が表示されました。
以上の点から、複数スクリプトを関連付けた場合でも、イベントは正しくすべてのスクリプトに対して発生しているようです。

…ん?となると、ちょっと気になることが…
RegisterForUpdateを使ってOnUpdateイベントを発生させた時はどうなるんだろう?

検証内容)

Script1で「RegisterForUpdate(1.0)」を行う。
Script2では行わない。

1秒後

Script1で「OnUpdateイベント」が発生しました。
Script2でも「OnUpdateイベント」が発生しました。 ← \(^o^)/

どうやらイベント発生は適切な判断により対象となるスクリプトに対して発生させているわけではなく、「問答無用で全てのスクリプトに対してイベントを発生させている」ようです。

次の検証です。
クエストに関連付けしてあるスクリプトのpropertyは外部からの参照が可能です。
ではもし複数のスクリプトに同名propertyが存在していた場合は…?
実はこれに関しては何の問題もありません。
外部からスクリプトのpropertyにアクセスする場合は明確なcastを行う必要があるからです。

例)castを使う方法
Quest property TargetQuest auto ;対象クエストを設定しておく

(TargetQuest as Script1).TestInt = 123 ;対象クエストのScript1にアクセス
(TargetQuest as Script2).TestInt = 456 ;対象クエストのScript2にアクセス
;TargetQuest.TestInt = 0 ;castしていないのでQuestスクリプトクラス内のTestIntを探しますが、存在しないのでコンパイルエラー

例)castを使わず、最初からどのクエストのどのスクリプトを扱うかを設定しておく方法
Script1 property TargetScript1 auto ;対象クエストを設定しておく
Script2 property TargetScript2 auto ;対象クエストを設定しておく

TargetScript1.TestInt = 123 ;対象クエストのScript1にアクセス
TargetScript2.TestInt = 456 ;対象クエストのScript2にアクセス



・2012/3/4 複数スクリプトによるマルチスレッド処理

拙作自動採集MOD「rbAutoHarvest」において、バニラスクリプトの「Find~系メソッド」を多用しているのですが、このメソッドをコールしてから完了して返ってくるまでが非常に遅い!
そこで「複数のスクリプトからマルチスレッド的に並列処理を行うとどうなるか」を実験してみました。

1)シングルスレッド処理
単一スクリプトでFindClosestReferenceOfAnyTypeInListFromRef
を繰り返し実行し続け、合計100回の処理が完了するまでにかかる時間を測定。

2)マルチスレッド処理
スクリプト1でFindClosestReferenceOfAnyTypeInListFromRef
スクリプト2でFindClosestReferenceOfAnyTypeInListFromRef
を繰り返し実行し続け、合計100回の処理が完了するまでにかかる時間を測定。

~結果~

1)シングルスレッド処理 → 所要時間(5回平均) 8.889秒

2)マルチスレッド処理 → 所要時間(5回平均) 7.1632秒 (-24%)

さすがに倍(-50%)の速さにはなりませんでしたが、処理速度を3/4程度まで最適化することができました。
ただ複数スクリプトによるマルチスレッド化を実装することで全体の管理やフローが複雑化するので、なんでもかんでも複数スクリプトで並列化すればいい…というわけでもない気がします。



・2012/3/23 CK上の扱いとPapyrusスクリプト型との違い

ちょっと前まで私が勘違いしていて、コメントにて指摘を頂くまで混同していた内容です。

CKのObject Windowのツリーに各種要素があり、
それらと一致する同名のPapyrusスクリプト型もあります。

例えば…

 CK / Papyrus
 Ammo → Ammo(ベースオブジェクト)
 Armor → Armor(ベースオブジェクト)
 Weapon → Weapon(ベースオブジェクト)
 Flora → Flora(ベースオブジェクト)

などなど。

じゃあこれ↓は?

 CK / Papyrus
 Actor → Actor

違います。

 CK / Papyrus
 Actor → ActorBase(ベースオブジェクト)

これ↑が正解です。

PapyrusのActor型は、ActorBaseをベースオブジェクトとして作成され
リファレンスとして世界に配置されたObjectReference(を継承した型)である。

わかりづらっ! これだけ名前が一致しなくて、わかりづらっ!



・2012/2/25 処理速度の検証

検証方法:
int型のカウンタ変数とwhile~endWhileを使って特定の処理を1000回行い、
処理にかかった時間を1000で割って「1回当たりの処理時間」を算出する。
時間はUtility.GetCurrentRealTime()で取得する。

※あくまで個人が個人の環境で測定した結果です、保証値ではありません
※以下の検証で扱った変数「DoOnce」はbool型で、値はtrueです

if ( DoOnce )
endif
0.000017秒

if ( DoOnce || DoOnce || DoOnce || DoOnce || DoOnce || DoOnce || DoOnce || DoOnce || DoOnce || DoOnce )
endif
0.000017秒

if ( DoOnce && DoOnce && DoOnce && DoOnce && DoOnce && DoOnce && DoOnce && DoOnce && DoOnce && DoOnce )
endif
0.000034秒 ←他より遅い

if ( DoOnce )
 if ( DoOnce )
  if ( DoOnce )
   if ( DoOnce )
    if ( DoOnce )
     if ( DoOnce )
      if ( DoOnce )
       if ( DoOnce )
        if ( DoOnce )
         if ( DoOnce )
         endif
        endif
       endif
      endif
     endif
    endif
   endif
  endif
 endif
endif
0.000017秒

if文に関して、個人的な感想としては…
・if文で変数を扱う限りは大きな遅延にはならないと思われる
・ただし&&で判定するのであれば複数のif文に分解した方が速くなる

そしてif文とは関係ありませんが、MODを作っていて遅いことが判明したネイティブ関数…

Utility.IsInMenuMode()
0.016666秒

Game.IsActivateControlsEnabled()
0.016660秒

Game.IsCamSwitchControlsEnabled()
0.016572秒

Game.IsMovementControlsEnabled()
0.016630秒

おっそ! (´Д`;
単純if文の約1,000回分の処理の遅さです。
私の場合、上記の関数をif文の中で&&判定していたので、さらに遅くなり大変なことに…

しかしネイティブ関数はどれもこれも遅すぎて本当にひどい…
マルチスレッド化なんてしなくてもよかったから、オブリ並みの処理速度で動いて欲しかった。
今後SKSEが拡張コマンドを実装したとしても、常に状態を監視し続けて
変化があったら即レスポンスを返すようなシステム型MODを作るのは困難な気がする…
スポンサーサイト

コメント

非公開コメント

No title

rbautohervestは事前のif処理が多いのも遅い原因かもしれないですね
doonceとか削れるものは削ったり
そもそも事前処理で省くんじゃなくて事後で省くとかw

コメントありがとうございます

tyさん、コメントありがとうございます。

おっしゃる通り、不要なif文はあるよりもない方がいいと思います。
ただこちらで処理速度を検証した限り、rbAutoHarvestで行っている
FindClosestReferenceOfAnyTypeInListFromRefの呼び出し一回にかかる処理時間は
if ( DoOnce )/endifにかかる処理時間の約530回分に相当します。
なので、例えif文を100個削って最適化できたとしても、
特定関数における処理の遅さをカバーできるほどの効果には繋がらないと思います。
プロフィール

r_basilico

バジリコ風味 (r_basilico)
Twitter: r_basilico
Steam: r_basilico
艦これ: 嫁艦は祥鳳

リンク
最新記事
最新コメント
月別アーカイブ
カテゴリ
検索フォーム
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。