WoWS - UI Mod作成の基本 1: UnboundとDataHub

はじめに

World of Warshipsは複数のプログラミング言語で構成されており、それぞれ以下のような役割を担っています

  1. ゲームの中核(オブジェクトのレンダリングやダメージ処理など): Python, C++
  2. UI(ダメージポップアップや艦艇名表示など): ActionScript3, Unbound, Unbound2, Gameface

ゲーム中核とUIの間でデータを橋渡しするのがDataHubというオブジェクトです。

詳細は公式ドキュメントにもありませんが、UI系Modの作成ではほぼ必須の知識になります。
今までのModderは公式のマークアップや他人のModを見て独学してます。この狂った連鎖を断ち切りたい
具体例なんかは次回以降(SoonTM)にします

必要なもの

  • やる気と時間: 作ったModが思い通りに動かないこと多数。公式ドキュメントに記載されている手法も特定条件では動かなかったり(しかも条件書いてない)。Modテストのためにクライアント複数回起動するだけで1時間失ったりします。これはマジです
  • FlashDevelop: .swcファイルを読み込めるFlash IDEなら他のものでもOKです
  • SWCファイル: 下記リンクから最新バージョンのフォルダを選択、as3_library.zipをダウンロードしてください

drive.google.com

ダウンロードした.swcファイルのパスをIDEに通してください。
FlashDevelopであれば、画面上部のProject→Properties...→Compiler Options→SWC Librariesから設定できます

SWC Libraries

この手順が完了すると、FlashDevelopのProjectカラムにあるRefences→Classesより、各Component(後述)の持つプロパティやイベントが確認できるようになります。

例: AccountNameコンポーネントの持つ要素

DataHubのデータ構造

全体図(要拡大)

定数について

Unbound系に共通する定数としてCC(ComponentClass)が存在し、Component(後述)のクラスIDが保存されています。
例として、AccountNameコンポーネント(プレイヤーの名前が保持されている)のクラスIDはCC.accountNameのようにアクセスできます。

Collectionsについて

Collectionを格納するオブジェクトです。クラスID(CC.something)を使用して特定のCollectionを取得できます。

Collectionについて

特定のComponentを持ったEntityへの参照を格納する配列です。
配列インデックスを指定して任意のEntityを取り出せます。
また、コレクションから要素を選択し、新たなコレクションを作成することも可能です。
例えばAccountNameコレクションには、AccountNameコンポーネントを含むEntityが全て保存されています。

Entityについて

Componentを格納するコンテナです。
全てのEntityはユニークなIDを持ち、これを用いて直接参照することができます。

Componentについて

数値、文字列、イベントなど、実態のあるデータを保持するオブジェクトです。
前出のAccountNameコンポーネントであれば

  • name: プレイヤー名(IGN)
  • nickName: ニックネーム(クランタグを含むIGN)
  • evChanged: 上記どちらか変更された際トリガーされるイベント

が保存されています。
また、primaryKeyメソッドが存在する場合はユニークIDを持ちます。
ユニークIDは1つまたは複数の要素で構成されており、前者の場合は数値や文字列、後者の場合は'element1_element2'のようにアンダーバーで各要素を結合した文字列がキーとなります。

オブジェクトの取得方法

Collection

variableNameCC.somethingのコレクションを代入します。
variableName[index]で任意のインデックスにあるEntityを取り出すことができます

Unbound <bind name="collectionDH" value="CC.something; 'variableName'"/>
Unbound2 (var variableName:gfx = "$datahub.getCollection(CC.something)")

Entity

variableNameに任意のEntityを代入します。

EntityのユニークID(entity.id)から
Unbound <bind name="entityDH" value="'variableName'; entityId"/>
Unbound2 (var variableName:gfx = "$datahub.getEntity(entityId)")
ComponentのユニークIDから

複数の要素からなるIDの場合、自分で文字列を作成してgetPrimaryEntityを使用するか、getPrimaryCompositeEntityの引数に結合前の要素を与えることでEntityを得ることができます。
例1: ownSquadronのユニークIDは、飛行中隊のインデックス(ロケット:0, 雷撃機:1, 爆撃機:2等)です。getPrimaryEntity(CC.ownSquadron, index)
例2: battleConsumableのユニークIDは、消耗品タイプと飛行中隊インデックス(艦消耗品の場合-1)の結合文字列type_squadronIdです。
getPrimaryCompositeEntity(CC.battleConsumable, consumableType, squadronId)getPrimaryEntity(CC.battleConsumable, 'consumableType_squadronId')と等価です。

Unbound <bind name="primaryEntityDH" value="'variableName'; CC.something; index"/>
Unbound2 (var variableName:gfx = "$datahub.getPrimaryEntity(CC.something, index)")
(var variableName:gfx = "$datahub.getPrimaryCompositeEntity(CC.something, *args)")
コレクションの最初のEntity

collections[CC.something][0]と等価です。

Unbound <bind name="firstEntityDH" value="'variableName'; CC.something"/>
Unbound2 (var variableName:gfx = "$datahub.getSingleEntity(CC.something)")

Component

コレクション最初のEntityにあるComponent

collections[CC.something][0].somethingまたはgetSingleEntity(CC.something).somethingと等価です。

Unbound2 (var variableName:gfx = "$datahub.getSingleComponent(CC.something)")