Skyrim Mod ファイルフォーマット解説 - 3
はじめに
この記事ではSkyrimのModのファイルフォーマットについて解説しています。
SkyrimのModのファイルフォーマットについてはTes5Mod:Mod File Format - The Unofficial Elder Scrolls Pages (UESP)が詳しいですが、ここでは、これまで自分なりに解析してきた内容や、必要な部分なども踏まえて解説していきます。
また、同時にプログラミングで利用する情報なども合わせて解説したいと思いますが、プログラミングについての主な情報は別サイトで記事にいていきます。
前回はグループについて記載しましたので、今回は主にレコードについてです。
レコード
武器データのレコードを見てみることにします。武器の情報はWEAP
です。
選択部分はグループヘッダーです。
次の、範囲がレコードヘッダーです。
レコードヘッダーの情報は次のようになっています。
項目名 | 型 | byte数 | byte数合計 | 説明 |
---|---|---|---|---|
Signature | string | 4 | 4 | どのような情報を扱うかを識別する記号。 |
DataSize | uint | 4 | 8 | ヘッダーサイズを含まないレコードの全体サイズ。 |
RecordFlags | uint | 4 | 12 | あまり気にしない。詳細はUESPのRecordsのflags参照。 |
FormID | uint | 4 | 16 | mod内でデータを一意に識別するためのID。 |
VersionControlInfo1 | uint | 4 | 20 | あまり気にしない。詳細はUESPのRecordsのrevision参照。 |
FormVersion | ushot | 2 | 22 | あまり気にしない。詳細はUESPのRecordsのversion参照。 |
VersionControlInfo2 | ushot | 2 | 24 | あまり気にしない。詳細はUESPのRecordsのdata参照。 |
※項目名はSSEEditでの表示名称を使用
では、実際のデータに当てはめて見て見ます。
項目名 | 値 | バイナリ上での表示 |
---|---|---|
Signature | WEAP | |
DataSize | 422 | |
RecordFlags | 0 | |
FormID | 00017288 | |
VersionControlInfo1 | 10 2C 1E 00 | |
FormVersion | 44 | |
VersionControlInfo2 | 03 00 |
SSEEditで見ると次のようになっています。
ちなみに、最初のファイルヘッダーにあたるTES4
はグループヘッダーが無く、このレコードヘッダーの部分からはじまっています。
以降のDataSize
分のデータがフィールドのデータになります。
以上、今回はここまで。
Skyrim Mod 製作日記 - 20 - ダンジョンを自動生成する (5)
ウィザードリィ#1のマップを利用したダンジョン生成について、前回は敵の配置について記載しました。
前回まではただ配置していただけで、最初から敵対状態としてありましたのでダンジョンに入った瞬間、敵が動き出してうるさかったです。
そこで、近付いたら敵対するように変更を加えます。
近付いたら敵対するようにする
色々と方法があるのかもしれませんが、配置時はAI DataのAggressionをUnaggressiveにして、TriggerでのScriptで、Very Aggressiveにするという方法をとりました。
Aggression (攻撃性)
Faction Relationships (派閥関係)と関連付け、 Actor がいつ戦闘を開始するのかを決定します。
タイプ | 値 | 説明 |
---|---|---|
Unaggressive(平和的) | 0 | 戦闘に入りません。 |
Aggressive(攻撃的) | 1 | 敵を見つけると攻撃します。 |
Very Aggressive(非常に攻撃的) | 2 | 敵対者及び中立関係の者を見つけると、攻撃します。 |
Frenzied(凶暴) | 3 | 視界に入った者は誰でも攻撃します。 Actor は、標準で Frenzied 設定になっていることは希です。通常、魔法や特殊効果(例えば激昂の呪文など)の結果でしかこの状態にはなりません。 |
※以下のサイトより抜粋
AI Data Tab/ja - Creation Kit
敵配置用のActorの設定を変更します。
[Template Data]の[Use AI Data]のチェックを外して、変更できるようにします。
[AI Data]タブより、[Aggression]のコンボボックスから一番上の"Unaggressive"を選択します。
Scriptは以下のdefaultSetMultiAVTriggerScript
を利用することにしました。
ScriptName defaultSetMultiAVTriggerScript extends ObjectReference {Sets the specified actor value on the actor(s) to the specified value when the player enters the trigger.} import game import debug ObjectReference property Actor1 Auto ObjectReference property Actor2 Auto ObjectReference property Actor3 Auto ObjectReference property Actor4 Auto ObjectReference property Actor5 Auto String property ActorValueName Auto int property ActorValueValue Auto bool property onlyOnce = True Auto bool property evalPackageAfterwards = True Auto bool property onlyPlayer = True Auto auto State Waiting Event onTriggerEnter(ObjectReference obj) if (!onlyPlayer || obj == GetPlayer()) if (Actor1 != None) (Actor1 as Actor).SetAV(ActorValueName, ActorValueValue) EndIf if (Actor2 != None) (Actor2 as Actor).SetAV(ActorValueName, ActorValueValue) EndIf if (Actor3 != None) (Actor3 as Actor).SetAV(ActorValueName, ActorValueValue) EndIf if (Actor4 != None) (Actor4 as Actor).SetAV(ActorValueName, ActorValueValue) EndIf if (Actor5 != None) (Actor5 as Actor).SetAV(ActorValueName, ActorValueValue) EndIf if (evalPackageAfterwards) if (Actor1 != None) (Actor1 as Actor).EvaluatePackage() EndIf if (Actor2 != None) (Actor2 as Actor).EvaluatePackage() EndIf if (Actor3 != None) (Actor3 as Actor).EvaluatePackage() EndIf if (Actor4 != None) (Actor4 as Actor).EvaluatePackage() EndIf if (Actor5 != None) (Actor5 as Actor).EvaluatePackage() EndIf EndIf if (onlyOnce) GoToState("AllDone") EndIf EndIf EndEvent EndState State AllDone Event OnTriggerEnter(ObjectReference obj) ;Do nothing. EndEvent EndState
アクター5人まで、任意のアクターバリューの設定を変更できるので、中々に汎用的なスクリプトではあると思います。
こうして使えそうなスクリプトを駆使するのも面白みはありますが、やりたい事がすんなり出来ないのでもどかしいですね。
次に、このスクリプトを利用するActivatorを登録します。
プロパティはデフォルトで次の設定をしておきます。
後は、マップ設定で、Triggerとして配置する位置と関連付けする敵の設定をします。
これまでは、ただ単に向きを数値指定していただけでしたが、Triggerの配置は先頭に"T"を付けて、後は連番で、配置する向きは":"で区切って向きの数値。
敵の配置はそのまま向きの数値の後に、":"で区切って、Triggerの配置の"T"を除いた数値。
同じ場所に複数配置する場合、"/"で区切るとしました。
わかりづらいなぁと思いますが、まぁおいおいと最適化していきます。
この例だと、右下の部屋は部屋のドアの前に"T1"で1番目のTriggerを北側(1)に配置。
部屋にいる敵は南側(4)を向き、1番目のTriggerに関連付けています。
CKで見ると次のような感じです。
スクリプトのプロパティは次のように配置した敵のFormIDを設定してあります。
これで、扉に近付いた際に敵が敵対して動き出すようにできました。
本当は扉を開いた瞬間に動かすようにしたかったのですが、うまくできそうなスクリプトを発見できませんでした。
また、他の仕組みもまだよくわかっていません。
ちなみに、公開した後に気づきましたが、次のように結構Triggerの幅が狭いため、ギリギリ扉を開ける位置で開くと、感知しないため、遠距離での先制攻撃が出来てしまいます。
今回の対応で、大体やりたいことは出来ました。
PS4版でフォロワーがうまくテレポートできなかったり、敵の反応方法を改善したりなど、そもそも敵の配置や種類の調整などやった方がいい事は多々ありますが、自動生成の仕組み作りとしてはほぼOKかなと思います。
今後改善するなら、よりウィザードリィらしい要素を加えたりなどで、より遊べるModになるでしょうか。
もっともソースコードはかなり汚くなったので、もしツールとして公開するなら、マップの仕組みなども含めて全体的に見直していかなければいけません。
そう考えるとまだまだ未完成ですね・・・
以上。
【2017/04/22 追記】
Mod製作についての動画を作成しました。
www.nicovideo.jp
Skyrim Mod 製作日記 - 19 - ダンジョンを自動生成する (4)
ウィザードリィ#1のマップを利用したダンジョン生成について、前回はテレポートの実現方法について記載しました。
このModで使用しているスクリプトはその辺りのものだけなので、後は基本的なことになるかもしれません。
敵の配置
まずは敵の内容について。
現在、このModはVersion 0.9.3として公開しています。
当初は的はドラウグルのレベルドリストのみでしたが、いくつかの敵パターンを用意するようにしました。
まず、レベルドリストですが、B1~B10までのレベルドリストを作成し、その中にそれっぽい敵配置となるようなレベルドリストを含めるようにしました。
次の内容はB1に配置する敵のレベルドリストです。
リストの内容はざっくりと次の通りです。
- 近接戦闘(片手・両手それぞれ)を行う山賊のレベルドリスト
- 動物のレベルドリスト
- スケルトンのレベルドリスト
- ドラウグルのレベルドリスト
プレイヤーのレベルが高ければ相応に高いレベルの敵が出現します。
ただし、この内スケルトンは大して強い内容になりません。
レベルドリストだけでは配置できませんので、このレベルドリストをテンプレートとして使用するアクターを作成します。
テンプレートを使用する際、そのまま配置すると、関係性の無い敵は同士討ちをはじめてしまうため、Factions
を追加するようにしています。
ただ、自分はまだこの辺の適切な設定をわかっていないため、敵の種別について一通り登録してあります。
ユニークな敵など、個別の設定を行う場合、テンプレートのチェックを外して任意の設定を行うようにします。
例えば、ラスボスのワードナは特別強くなるようにステータスなどを底上げしています。
敵の配置は、マップ上で次のように配置する位置と向きを指定します。
今のところただ配置しているだけのため、そのセルに侵入すると全員がアクティブ状態となります。
このため、レベルが高いと敵も強いのはいいのですが、ドラウグルはやたらとシャウトを使い、動物は熊がやたらと吼えてうるさいったらないです。
本来なら適切な位置などでアクティブになるようにしたいのですが、やり方をよくわかっていません。
以上
Skyrim Mod ファイルフォーマット解説 - 2
はじめに
この記事ではSkyrimのModのファイルフォーマットについて解説しています。
SkyrimのModのファイルフォーマットについてはTes5Mod:Mod File Format - The Unofficial Elder Scrolls Pages (UESP)が詳しいですが、ここでは、これまで自分なりに解析してきた内容や、必要な部分について解説していきます。
また、同時にプログラミングで利用する情報なども合わせて解説したいと思いますが、プログラミングについての主な情報は別サイトで記事にいていきます。
前回はModの基本構成や、ファイルの先頭部分のファイルヘッダーについて記載しました。
今回は主にグループについてです。
グループ
Modファイルの中身をみると、最初には必ずTES4
からはじまるファイルヘッダーがあり、その後はグループの固まりの繰り返しが基本となります。
Skyrim.esm
を見てみます。
選択した範囲がファイルヘッダーです。その後に続くGRUP
からが、グループのはじまりです。
グループの先頭24byteはグループヘッダーです。
ここではとりあえず次の点を把握できればOKです。
名称 | データ型(サイズ) | 説明 |
---|---|---|
Group | string(4) | GRUP 固定グループの始まりを示す |
DataSize | uint(4) | グループヘッダーを含むグループ全体のサイズ |
Signature | string(4) | そのグループで扱う情報を識別する記号 |
実際のデータを見てみます。
名称 | 内容 | バイナリ上での表示 |
---|---|---|
Group | GRUP | |
DataSize | 96,862 | |
Signature | GMST |
前回特に言及しませんでしたが、DataSize
は内容の所には10進数で書いていますが、バイト上での表示は16進数で、バイトオーダーはリトルエンディアンです。
バイトオーダーなどについての詳細はGoogleなどで"バイトオーダー"で検索してみてください。
10進数と16進数の確認は例えば、Windows標準の計算機などで確認する方法があります。
qiita.com
Signature
はGMST
とあります。UESPによるとGame Setting
とのこと。
Game Setting
ということでしょうか。
とりあえず、ここでは詳細は気にしません。
ここまでわかれば、知りたい情報のSignature
のある位置まで、DataSize
分読み飛ばしていけばよいことになります。
プログラミングでグループのデータを扱う方法は次の記事にまとめました。
qiita.com
ブログのテーマを変更しました
これまで、次のテーマを使っていました。
クリスタブルー~CrystalBlue~ - テーマ ストア
直前の記事がこんな感じの表示ですね。
自分でテーマを作ってみようと思いましたが、中々手がつかず、次のような表示にもなってしまうため、変更しました。
現在使用中のテーマは次のテーマです。
このテーマだと、画面いっぱいに広げれば、その分広がるので見やすくなったと思います。
なお、実際にテーマを知りたい場合は、画面最上部のHatena Blogのメニューである、そのブログタイトルのドロップダウンリストをクリックして、「このブログのテーマを見る」をクリックすればわかります。
Skyrim Mod 製作日記 - 18 - ダンジョンを自動生成する (3)
ウィザードリィ#1のマップを利用したダンジョン生成ですが、一応一通りの内容が出来たので、以前に公開したバージョンを更新し、日本語版も用意し、Nexusの方へも登録してみました。
ただ、PS4版でフォロワーの追従がうまくいかず、その点は不完全なままです。
また、出現する敵と敵の強さなどの調整が不十分なので、今後修正していきたいと思っています。
以下、このModでのポイントなど。特にPS4基準で作成している点について。
テレポートについて
※書いてみたものの、わかりづらい内容ですみません
ウィザードリィでは元々トラップの一種としてテレポートがありますが、それとは別にSkyrimでウィザードリィのダンジョンを再現するのに次の手段をとっています。
- ダークゾーンはLightの設定を真っ暗にして、別セルとして用意し、その場所へMoveTo(テレポート)させる
- マップの端から端へのループはダミーの見た目を用意して、端から端の場所へMoveTo(テレポート)させる
- 各階への移動は、各階は別セルとして、通常のDoorでのTeleportで移動させる
- エレベータでの移動は、エレベータ用の部屋を用意してDoorでつないで、Teleportで移動させる
まず、ダークゾーンについてです。
通常のダンジョンは次のように設定して、ライトを設置せずに明るくさせています。
一方ダークゾーンは次のように設定して、ライトがないと真っ暗になります。
"Directional Ambient Lighting"とは日本語で、指向性周囲照明となるようです。
壁などを設置した際にその向きの周辺の照明ということなのでしょう。
例えば、色を赤に設定すれば赤色の照明になります。
セルへの移動は、アクティベーターを次のように作成しています。
ここで重要なのは次の2つのスクリプトです。
まず1つ目。プレイヤーを移動させるのに使用します。
Scriptname DA16DreamwarpTriggerScript extends ObjectReference Conditional ObjectReference Property pDA16DreamwarpReturnMarker Auto Message Property pDA16DreamwarpMessage Auto Event OnTriggerEnter(ObjectReference akActionRef) if akActionRef == Game.GetPlayer() Game.GetPlayer().MoveTo(pDA16DreamwarpReturnMarker) pDA16DreamwarpMessage.Show() endif endEvent
続いて、2つ目はフォロワーを移動させるのに使用しています。
Scriptname C06TombEntranceCheatScript extends ObjectReference Location Property TombLoc auto ReferenceAlias Property Aela auto ReferenceAlias Property Farkas auto ReferenceAlias Property Vilkas auto ObjectReference Property AelaSpot auto ObjectReference Property FarkasSpot auto ObjectReference Property VilkasSpot auto Quest Property C06 auto Event OnTriggerEnter(ObjectReference akActivator) if (akActivator == Game.GetPlayer()) if (C06.GetStage() >= 30 && C06.GetStage() < 40) ; if (Aela.GetReference().GetCurrentLocation() != TombLoc) Aela.GetReference().MoveTo(AelaSpot) ; endif ; if (Farkas.GetReference().GetCurrentLocation() != TombLoc) Farkas.GetReference().MoveTo(FarkasSpot) ; endif ; if (Vilkas.GetReference().GetCurrentLocation() != TombLoc) Vilkas.GetReference().MoveTo(VilkasSpot) ; endif endif endif EndEvent
よくよく見ると、2つ目のスクリプトでプレイヤーもフォロワーも移動できますが、当初は1つ目のスクリプトのみ利用していたのと、メッセージ表示に利用していたためです。
今後修正してもよいかもしれません。
プレイヤー用のスクリプトは、プログラムで生成する際に移動先のマーカーを設定するようにしています。
フォロワー用のスクリプトは次のようにプロパティ設定をしてあります。
クエストと、クエストステージが条件として必要となるため、次のようにクエストを作ってあります。
クエストはゲーム開始時にクエストが開始されるようにしているので、スクリプトの条件が成立します。
フォロワーはクエストの"DialogueFollower"にあるエイリアス"Follower"を参照すればよいようで、プロパティに設定しています。(※しかしながら、PS4版ではコレが有効でない?のか、フォロワーが移動しません、この辺はまだ自分が知識不足で原因がわかっていません)
このアクティベーターをセルへトリガーとして次のように配置します。
※XMarkerHeadingに重なっている四角のトリガーはメッセージ表示に使用しているもので、別途説明します
ダークゾーン側には次のように配置します。
トリガーに触れると、矢印の先のXMarkerHeadingへ移動します。
マップで見ると次の部分です。
セルを赤色で塗っている部分がダークゾーンです。それぞれの入り口にトリガーを置き、出口にマーカーを配置して、スクリプトのMoveToで移動しています。
マップの端から端への移動もこのトリガーとマーカーを配置させて移動させます。
ただし、表示領域を作らないと真っ暗な表示になるため、ダミー用の領域を作成します。
次のマップはB3階のマップです。
セルを黄色で塗っている部分がダミー用の表示領域で、その他前の位置で、逆方向へテレポートさせます。
ただ、ドアの開閉などの状態はリンクしないため、見た目が異なるなどの難点がありますが、まぁしょうがないでしょう。
DoorをActivateした際に、別のドアを開閉するスクリプトがあれば、見た目をリンクさせることができるかもしれません。ありそうな気はしますが、今のところ探していません。
各階やエレベータについては、DoorのTeleportをそのまま利用します。
※こちらも、PS4版だとナビメッシュでフォロワーの配置場所を作っていても、移動してこない場合があります。基本的には別のセル間への移動はできて、同一セル間の移動は出来ないようですが、別のセル間でも移動しない場合もあり、いまひとつ原因がわかっていません
とりあえず、今回はこの辺で。