スポンサーサイト

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

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

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


2012/2/9 Papyrus言語

事前情報では「RubyやPythonに似ている」とのことでしたが、
私はどちらも触ったことがない為か「C++/Javaに似ているなぁ」と感じました。

・スクリプト≒クラスという感じ?
・extendsで親スクリプトを継承したスクリプトを作れる
・配列の確保は int[] a = int[10] という感じでそのまま
・C++/Javaでいうthisと似たようなselfがある(厳密にいうと違うかも)
・Javaでいうsuperと同等のparentがある
・名前空間を扱うimportがある
・文字列型であるstringがある(Javaっぽい)
・関数にデフォルト引数を設定できる(C++っぽい)
・propertyに対するget/setメソッドを自分で実装できる(C++っぽい)
・インスタンスなしで呼び出せるglobal関数を実装できる(C++でいうstaticメンバ関数)
…などなど。

オブリの(OBSEなし)スクリプトは「単純な簡易スクリプト」という感じのものでしたが、
スカイリムのPapyrusは「言語」として成立している感じです。恐ろしい進化 ((( ;゚Д゚)))



2012/2/14 非同期処理とマルチスレッド

スカイリムのスクリプトはそれぞれが別々のスレッドで動作しています。
オブリの場合、特定スクリプトの特定箇所が処理されている時には
「その箇所のその処理だけ」が行われていて、ゲーム進行も止まっていますし、
ましてや他のスクリプトの処理が走るようなこともありませんでした。
しかしスカイリムではゲーム進行を妨げることもなければ、
他のスクリプトとは完全に別(非同期)で動作し続けます。

例えばスクリプト内のフローで…

1行目:10秒間 待つ
2行目:何らかの処理を行う

こんな書き方をした場合、オブリでは画面が完全に停止して
ハングアップ状態で10秒間待つことになります。
スカイリムの場合は、あくまでそのスレッドだけが10秒間停止するので
ゲーム進行にも他のスクリプトにも影響を与えません。

先ほど「スクリプトはそれぞれが別々のスレッド」と言いましたが、
正確に言うと「発生するイベントも別々のスレッド」です。

例えばアクティベートが行われて「OnActivateイベント」が発生して、
スクリプト内でそのイベント処理を行っている最中に
ゲーム側で再びアクティベートが行われ、新しいスレッドで
「OnActivateイベント」が発生したりします。

コーディングを行う際、常に
この処理は複数のスレッドから同時に呼び出されている可能性もある
と意識をしておかないと、バグだらけになると思います。
というか、バニラがあれだけバグだらけなのも少し納得できます (´Д`;;



2012/2/14 イベント

オブリの頃からスクリプトには「OnActivate」や「OnEquip」など
「イベントトリガーに対するイベント処理部分」を実装することができました。
もちろんスカイリムでもできます。…というか、それしかできません(多分)。

スカイリムではオブリにあった「Begin GameMode」や「Begin Menumode」のように
「毎フレーム常に呼び出され続ける部分」が存在しません(私が調べた限りでは)。
全ては「イベント発生」をトリガーとした「イベント駆動型プログラミング」となっています。

冷静に考えてみるとそれもそのはずで、上でも書いたように
スクリプト処理はそれぞれが別々のスレッドで動作している
のですから、もしオブリのように毎フレーム自動的にスクリプトが呼び出されたとしたら、
毎フレーム新しいスレッドが生まれてスクリプト処理を呼び続ける…という
とんでもないことになってしまいますね ((( ;゚Д゚)))

「じゃあ毎フレーム処理したい場合はどうするんだよっ!」となります。
これには代替となるイベントがあります。

Function RegisterForUpdate(float afInterval) native ←こういう関数があります。
RegisterForUpdate( 0.5 )とすれば、0.5秒ごとにOnUpdateイベントが発生します。
これをすさまじく小さいインターバル値に設定すれば、
毎フレーム処理が呼び出されるのと同じ状態になると思われます。
ただし「前の処理が終わっていないのに次のOnUpdateイベントが発生してしまった時」
の処理をちゃんと実装しておかないと大変なことになると思います。

で、このOnUpdateイベントにはとんでもない罠があって
アイテム類にAddしたスクリプト内ではOnUpdateイベントが発生しない
この現象(不具合?バグ?仕様?何なの?)に気付くまで数時間を無駄にしました…。
アイテムスクリプトによる管理でOnUpdateイベントを使いたい場合は
別途クエスト+クエストスクリプトを用意して、そっちでやるしかないのかもしれません。
(拙作rbAutoHarvestはそういう仕組みになっています)




2012/2/17 処理時間

スクリプトは非同期で動いている為、処理に時間がかかっていてもゲーム進行が止まらないので
「スクリプトがとても重くなっている状況」を把握しづらいです。

試しにプレイヤーに対してGetPositionX()を25回ほど行っただけで、
そのスクリプト処理が完了するまで1秒ほどかかってしまいました (´Д`;

上記以外にも異常に処理時間がかかる関数はたくさんあるようです。
スカイリムでMODを組む際は「どの関数を使えば何ができるのか?」だけではなく
「その関数を使うとどれくらい処理時間がかかるのか?」も把握しておかないと
とんでもないことになりそうです… orz

・Find~系の関数
当方の実験では呼び出し1回で0.7~0.8秒ほどかかりました…。ひどすぎる (´Д`;
試作したMOD「rbAutoHarvest」ではこの関数を何度も呼び出しているのですが、
1回呼び出す度に0.7~0.8秒ほど処理が停止状態となり、
とてもじゃありませんが快適な動作を行わせることができず、使い物になりませんでした…。



2012/2/19 配列

Papyrusでは標準で配列を扱えます。 その配列を扱っていて気付いたことをメモします。

・確保できる配列サイズは1~128に限定されている
 例)int[] testInt = new int[0] ;NG
 例)int[] testInt = new int[129] ;NG
・一度決めた配列サイズを動的に変更することはできないようです
 OBSEではできたのになぁ (´・ω・`)
・一度確保した配列を純粋に解放させる方法はないようです
 ただし新しく確保した配列を割り当てると、古い配列は解放されるようです。
 この辺はいわゆる「参照カウンタ」が機能しているからだと思われます。

以下、詳しい検証内容を書いておきます。

1) Noneを使って配列を解放することはできないのか?
 int[] testInt = new Int[ 10 ] ;配列確保
 testInt = None ;配列解放したいのでこうしてみた
これはコンパイル自体は通る、というか、なぜか通ってしまう。
しかし実行時には「エラー:Noneをint[]にcastできねーよ!」と怒られるのでダメ。

2) 配列を何重にも確保するとどうなるのか?
すでに確保した配列に対し、新しい配列を割り当てるとどうなるのか?を検証してみた。
 int[] testInt
このtestIntに対して
 testInt = new int[ 128 ]
を1,000回ほど繰り返してからセーブしてみた。
もし過去の配列データが残ったままであれば、セーブデータファイル(*.ess)は
最低でも128,000バイト以上増加しているはず。
しかし実際にはセーブデータファイルは増加していませんでした。
以上の検証結果から、新しい配列を割り当てると古い配列内容は解放されている…
正確に言うと参照カウンタが正常に機能しており、参照数が0になった時点で解放されるようです。



(次の記事へ)
スポンサーサイト

コメント

非公開コメント

No title

こういう情報助かります
追記だとgoogleりーだーちゃんが認識してくれなくて、さっき気づきました;;
メモみたいに使ってるのだったら良いのですが、情報共有的には記事にしても良いのでは?
自分はブログ放置しちゃったんで偉そうな事は言えませんけどねw

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

tyさん、コメントありがとうございます。
お、Googleリーダーにそんな機能があるんですね!
タイトルも更新するようにしてみましたが、これでもダメでしょうか?

まさしくメモ?独り言?みたいな状態になっていますが、
その真意は「間違っていたら指摘をして欲しい」とか
「わからない部分を教えて欲しい」という他力本願な所にあったりします…w

No title

まだ全然試してもないんですが、今日出た新しいSKSEのリリースノートに
initial papyrus hooks
って書いてありました
これからいよいよ機能追加が始まるんですかね!

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

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

おお、ついにSKSEがスクリプト拡張への一歩を踏み出したのですね!
現状のバニラスクリプトだとできないことだらけなので、
難航することなく開発が進行していくことを心から願います (-人-) オネガイシマス!オネガイシマス!

No title

autohervestだけど多重にscript動かせば解決するかもなーって思ってました
え、邪道ですか?ごめんなさい。

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

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

…鋭いですね!!
「記事その2」でクエストに対して複数スクリプトを関連付ける検証をしたのですが、
あれはrbAutoHarvestで複数スクリプトをマルチスレッド的に動かす為の実験だったのです。

しかし、まだ記事にはしていませんが、追加の実験にて
「同一スクリプトを重複して何個も関連付けることは不可能」だと判明し、
想定していたシンプルな実装方法が実現できず、やや頓挫しています。

スカイリムでは割り当てたスクリプト個別に「プロパティ」を設定できるので、
同一のスクリプトを何個もAddできるのであれば
単一のスクリプトソース使い回し+別々のプロパティ設定値を使うことで
効率よく複数スレッド別々の処理を行えそうだったのですが、それができず (´Д`;

↑自分で補足

よく考えてみたらPapyrusスクリプトはファイル名=スクリプト名(=クラス名)なので、
もし同一スクリプトファイルを複数Addできてしまったら
castしてもどのスクリプトクラスへアクセスすべきか特定が不可能になっちゃいますね。
となると、複数のクエストを並行して走らせればいいのかなぁ… (´Д`;

No title

私が別件で試した時はQuestを別に作る方は出来たので可能だと思いますよ
試したのが左右手持ち装備に変化を加えるものなので条件は違いますけど
プロフィール

r_basilico

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

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