● Script の種類についてOblivon の Script は3種類あります。
・Quest
・Object
・Magic Effect
3つの違い、特徴について。
Questその名の通り、クエストを管理するのに特化した Script。
長所
ゲーム上のコンソールから 変数 を直接変更することが出来たり、
他の Script から 変数 を参照、代入が容易。
その為、クエストの進行管理以外にも、MOD全体の変数管理としても使用できます。
また、任意に Script を開始、停止出来るため、必要ないときには停止させることで負荷軽減が可能。
ゲーム開始時に1回だけ動作させたいなどに利用できます。(魔法を覚えさせたり、アイテムを入手したりなど)
短所
変数はゲーム上でユニークの為、複数同じ処理が並列で走る可能性があるものには向かない。
開始すると常に動作する為、高負荷の処理をできるだけ避ける工夫が必要。
ObjectObject は物体に貼り付けて動作させるのに特化した Script…
でもあるのですが実は汎用 Script。
長所
Quest と違い、変数は独立している為、処理を複数走らせても平気な設計。
プレイヤーが目標の Object から一定距離離れると Script は停止(不活性)する。
とりあえずこれで作っておけば動作的に問題になることは少ない。
短所
複数走らせることが出来るため Script が複雑だと複数実行されたときに深刻な FPS低下を招きます。
Magic Effect魔法効果として Script を実行するタイプ。
かなり独特の癖があります。
長所
開始処理、メインループ処理、終了処理と3つに分かれている。
開始処理で各種の初期化を行い、メインループで処理を継続、終了処理と分けて作成できる。
Object 同様、複数走らせても平気な設計。
短所
プレイヤーと距離が離れると
"勝手に" 終了処理が走って Script が終了してしまう。
補足
魔法にもいくつか種類があります。
固有能力 (主に種族特性ボーナスとしての能力)
上級パワー (1日一回しか使用できない特殊な魔法)
低級パワー (スキル値に依存せずに使用できる魔法)
魔法
毒
病気
この内、固有能力で作成すれば終了してしまっても NPC に随時動作をしているので
プレイヤーが近づけば、再度魔法の効果(Script)が出ます。
ただし、一度終了処理が走っているので、この Script 内の変数で何かを管理させるのは危険です。
どうしても MagicScript で変数を管理したい場合は、
Flagアイテム(NonPlayable のアイテム)を NPC に持たせて変数と見立てるほうが無難。
(当然変数が多くなればなるほど Flagアイテムが増えるのでケースバイケースですけど)
また、固有能力自体に癖があります。
それは NPC の場合、死んでリスポーンしてもこの固有能力を持ってリスポーンしてしまうこと。
例えば、衛兵を「毒りんご」で毒殺したとします。
この衛兵はリスポーンして生まれ変わっても「毒りんご」の固有能力が付いてしまっている為、
リスポーン⇒毒殺⇒リスポーン...を繰り返してしまいます。
魔法は使用させる方法に癖があります。
固有能力の場合、対象Ref.addspell <付与したい魔法>
でその魔法効果を実行させることが出来ます。
これが
固有能力以外の場合、この手法は
魔法を覚えさせるだけです。
魔法効果を実行させるには対象Refが魔法を唱えないとダメなことに気を付けて下さい。
一般的に
固有能力以外で魔法効果を出させる場合、Activatorを使用する手法があります。
Activetor(魔法をかける物体)を作成。
targetRef(かけたい対象)
SpellID(かけたい魔法、タッチ魔法として作成します)
XMarkerRef(Activetorを戻すホームポイント)
テスト用の Cell を作り、そこに ActivetorRef を配置。(戻れるよう、XMarkerも設置)
script でこんな感じに記載。
ActivetorRef.MoveTo targetRef ;Activetor をかけたい対象の人の場所に飛ばす。
ActivetorRef.Cast SpellID targetRef ;Activetorがかけたい対象にタッチ魔法をかける。
ActivetorRef.MoveTo XMarkerRef ;用が済んだので元の場所に戻してあげる。
● Script の使い分けについて上記で説明した通り、 Script には独自の動作があります。
3つしかないので殆どが Object になってしまうのですが、用途に合わせて使い分けましょう。
上手く使い分けることで Oblivion の安定化、負荷軽減に繋がります。
私の考えではこんな感じに
Quest
クエスト進行を管理。
ini の設定を確保する場所として。
一時的に Script間の変数を代入、参照する場所として。
プレイヤーのみを対象として動作させる Script として。(複数走らないことが前提)
自動でスタートさせることできる唯一の Script。
スタートすると終了させるまで常に動作する為、随時負荷をかける Script を書いてはいけない。
どうしても負荷をかけるものになる場合は高負荷の部分を
別関数 に処理させることで回避するといいです。
※OBSE v0018 から自作の関数を作成することができるようになりました。
関数というのは、まあ決まった書式で実行すると結果を返してくるものみたいなもんです。
Excel で SUM関数ってあるじゃないですか。
あれは SUM(此処から:此処まで)とすると範囲内の和を返しますよね。
後ろの部分を引数といいます。ようするにSUM関数は 引数1から引数2までの和を返す関数ということ。
戻ってくる結果を返り値と呼びます。詳しくはプログラムの話になるので此処までで。
<QuestScript>
GameMode
if 特定条件の時
Call 別関数...
endif
...
end
動作させるのは監視だけで重い処理は必要なときだけ実行するようしています。
これで常に負荷が掛かるのを防ぐことに。
この辺は何れ補完したいかなぁ…
Magic Effect
プレイヤーの近くにいる時且つ、短時間で処理が終わるもの。(要するに距離が離れても処理し続ける必要がない)
瞬間的に動作して勝手に終わって欲しいもの(極端な話、終了処理を気にしなくていいので(固有能力除く))
Object
上記2つでは出来ないこと全部。
● Script の Blocktypes についてBlocktype とは Script がどのような時に動作するかを決める場所です。
この Blocktype は Quest、Object と Magic Effect で違います。
Magic Effect で使用できるものは以下の3つ。
ScriptEffectStart (開始処理)
ScriptEffectUpdate (メインループ)
ScriptEffectFinish (終了処理)
Quest、Object は上3つ以外使用できます。
全部は説明し切れませんので 詳細は CSWiki の此処で ⇒ 【
Blocktypes】
一般的に最も多用されるものは Object + GameMode です。
GameMode とは通常のゲーム画面のことを指します。
その為、此処で監視をしておけばどんな状況でも基本的に感知可能ですが、
当然のことながら、ほぼ随時監視していることになり、負荷が一番高くなります。
● 負荷を減らす方法負荷は Script の処理間隔に大きく左右されます。
Script を作成し、特に指定をしない場合、その Script は 5秒毎に実行されます。
ただし、5秒間隔のチェックではほとんどの場合、チェックが漏れてしまいます。
その場合、間隔を狭めることが出来ますが、狭めると監視漏れが少なくなる代わりに負荷が増大。
この辺のさじ加減は作成する MOD によりますが慎重に設定してください。
この間隔は
float fQuestDelayTime という変数を宣言し
set fQuestDelayTime to <間隔>とすることで変更できます。
何も指定しなければ
set fQuestDelayTime to 5 と同じ意味です。
また、凝った MOD を作ろうとすると Object + GameMode でしか出来ないものが多いのですが、
なるべく 他の Mode で代用できないかを検討した上で使用しましょう。
なお、Quest を併用したり Script を上手く作成することで負荷を減らすことが出来ます。
ダメなほうの例で申し訳ないのですが Hentaiさんの
Hentai Lovely House2 がこれに当たります。
この家 MOD で照明のスイッチはこんな仕組みでした。
例)<Object Script>
scn lightScript
Begin OnActivate ;これはスイッチがアクティベートされた時に動く処理という意味です。
MessageBox "Turn Light?", "On", "Off", "Cancel"
Set stage to 1 ;スイッチをアクティベートしたよフラグ
end
Begin Gamemode ;ゲーム画面での監視処理
set Button to getButtonPressed ;MessageBoxで押したボタンを格納しています
if stage == 1 && button == 0 ;スイッチを押していて、On を選択したなら…
照明を付けるよ処理
set stage to 0 ;フラグ初期化
elseif stage == 1 && button == 1 ;スイッチを押していて、Off を選択したなら…
照明を消すよ処理
set stage to 0 ;フラグ初期化
elseif stage == 1 && button == 2 ;スイッチを押していて、Cancel を選択したなら…
何もしないよ処理
set stage to 0 ;フラグ初期化
endif
end
これで問題なのは常に GameMode で監視してしまっている点。
この家にいる間、常に「このスイッチ押した?」⇒「押してないよ」という問答が繰り返されてるという点です。
正直、この程度の短い script だと影響は0といってもいいんですけどまあ、気になったので…
---
改善例1) GameMode を使用しない
scn lightScript
Begin OnActivate ;これはスイッチがアクティベートされた時に動く処理という意味です。
if stage ;もし照明の状態が 0以外なら...
照明をつけるよ処理
Set stage to 0 ;照明の状態を0とする
else ;照明の状態が0のとき
照明を消すよ処理
Set stage to 1 ;照明の状態を1とする
endif
end
要するに GameMode の監視をやめてアクティベートされた時だけ処理するようにしています。
この方法は単純ですが効果抜群、ただしオリジナルと処理が変わってしまいました。
HentaiLovelyHouse2Fix ではこの方法を用いています。
---
改善例2) QuestScript を併用する
<Object Script>
scn lightScript
Begin OnActivate ;これはスイッチがアクティベートされた時に動く処理という意味です。
startquest lightQuest ;lightQuestを開始させる Set
lightQuest.stage to 1 ;スイッチをアクティベートしたよフラグ(lightQuestの変数を弄ってます)
end
lightQuest という名前のクエストを作り、そこに下の Script を指定。
クエストはゲームスタート時に開始しないにします。
<Quest Script>
scn lightQuestScript
short stage
short Button
Begin Gamemode ;ゲーム画面での監視処理
if stage == 1
MessageBox "Turn Light?", "On", "Off", "Cancel"
set stage to 2
endif
set Button to getButtonPressed ;MessageBoxで押したボタンを格納しています
if stage == 2 && button == 0 ;スイッチを押していて、On を選択したなら…
照明を付けるよ処理
set stage to 3
elseif stage == 2 && button == 1 ;スイッチを押していて、Off を選択したなら…
照明を消すよ処理
set stage to 3
elseif stage == 2 && button == 2 ;スイッチを押していて、Cancel を選択したなら…
何もしないよ処理
set stage to 3
elseif stage == 3
set stage to 0
stopquest ;クエストを停止させる endif
end
こうすることで同じ GameMode の監視処理ですが
必要なときにだけ動かすことで無駄な負荷を回避できます。
この方法も効果抜群、しかもオリジナルと同じ動きです。
ただし、作成するのが面倒なタイプ。
---
改善例3) Return を上手く使い負荷を小さくする
<Object Script>
scn lightScript
Begin OnActivate ;これはスイッチがアクティベートされた時に動く処理という意味です。
MessageBox "Turn Light?", "On", "Off", "Cancel"
Set stage to 1 ;スイッチをアクティベートしたよフラグ
end
Begin Gamemode ;ゲーム画面での監視処理
if stage != 1 ;スイッチを押してないなら
return ;此処から下の処理をスキップ
endif set Button to getButtonPressed ;MessageBoxで押したボタンを格納しています
if stage == 1 && button == 0 ;スイッチを押していて、On を選択したなら…
照明を付けるよ処理
set stage to 0 ;フラグ初期化
elseif stage == 1 && button == 1 ;スイッチを押していて、Off を選択したなら…
照明を消すよ処理
set stage to 0 ;フラグ初期化
elseif stage == 1 && button == 2 ;スイッチを押していて、Cancel を選択したなら…
何もしないよ処理
set stage to 0 ;フラグ初期化
endif
end
この方法は return で下の処理を飛ばすことで負荷を最小限を抑える工夫です。
3つの改善の中では一番負荷が高いのですが、作成中に容易に実装することが可能。
Oblivon の Script は if で条件を区切っても return しないと全部総チェックしてしまうところにあります。
なるべく、負荷を避けるため実行頻度が高いものを上に、頻度が低いものを下に置き、
return で飛ばす仕組みを心がけたほうがいいと思います。
また私は Let をほとんど使ったこと無いのですが Set と比較して Let は2倍以上重いらしいです。
Let 便利なんですけどねぇ…
Set)short Val
set Val to 1 ;Val変数の初期化
set Val to Val + 1 ;Val 変数に1をプラスする方法
---
Letshort Val
Let Val := 1 ;Val変数の初期化
Let Val += 1 ;Val 変数に1をプラスする方法
●おまけ : 重い MOD の特徴Script の仕組み上、以下の特徴を持つものは FPS を大きく下げます。
・GameMode で常に監視するタイプ。上だけでは大きな影響は出ません。下記条件を満たすほど重くなります。
・監視対象が多い。(Object数等)
・実行間隔が狭い。(fQuestDelayTime)
・対象を常に探し続ける。(GetFirstRef)
これらを特徴とするものは総じて重くなります。
具体的なMOD名を上げると...
OblivionXP (レベルシステム変更)
RealisticFatigue (スタミナペナルティー)
Realistic Broken Equipments (防具破損で装備解除)
Break Undies Framework (防具の耐久度で装備を変化)
BetterMusicSystem (状況に応じて曲を再生)
戦闘改善系 MOD全般
・ただし、プレイヤーのみを対象としているものは少し軽め。
Enhanced Economy (商人取引をリアルに)
・fQuestDelayTime は緩め。