SkyrimMOD作成wiki

ゲームとUI間でデータをやり取りする

最終更新:

匿名ユーザー

- view
だれでも歓迎! 編集

ゲームとUI間でデータをやり取りする

このチュートリアルではSkyrimでのプレイヤー名をUI上で表示し、その名前を別のものに変更して承認ボタンを押したら、ゲーム内のプレイヤー名が変わるものを作ります。

ここでは以下の内容ができるようになります。
  • CLIKコンポーネントを利用する
  • ゲームからUIにデータを送る
  • UIからゲームにデータを送る

+ Flash関連ファイル
本チュートリアルのFlashに関連するファイルを置いておきます。Adobe Flashを所持している方はこれ抜きでチュートリアルを進めても問題ありません。
ソフトを持ってなかったりするけど動作は見てみたい、というような方はご利用ください。
ゲームとUI間でデータをやり取りする
ファイル名 説明
02test.fla Adobe Flashで開く編集ファイルです。
02test.swf コンパイルして作成したデータやり取りのインターフェースです。CKのスクリプトで読み込むことで開くことができます。
MainPanel.as ゲームとUIのやり取りのためのASソースです。

必要なオブジェクトを作る


Flashを用いて名前の入力域、名前変更を承認する「Submit」ボタン、名前の変更をキャンセルする「Cancel」ボタンを作ります。


画面では、黒い領域に「New name is ...」と表示し、下の「name」の所にプレイヤー名を表示します。
この「name」の領域はこちらの入力を受け付けるようにし、ここに名前を入力して「Submit」ボタンを押すことでプレイヤー名が変更できるという構造です。
ファイル・フォルダ構造について、制作者の環境ではプロジェクトのファイル名は02test.flaとし、「UIを開く・閉じる」のプロジェクトファイルと同じ場所に新しく02testフォルダを作成し、そこに保存しております。

各オブジェクトの作成

UIを開く・閉じるで行ったように、名前を表示する領域、承認ボタン、キャンセルボタンの3つのボタンを作成します。
必要なオブジェクトとテキストを配置します。
各設定パラメータは以下のようにしています。

  • 名前領域・ボタンの位置/大きさ/色
名前領域 承認ボタン キャンセルボタン
X 440 340 680
Y 200 400 400
W 400 260 260
H 160 120 120
塗りつぶし色 #000000 #CCCCCC #CCCCCC

  • テキストのフォント/サイズ/入力可否
New name is... name Submit Cancel
フォントファミリー MS UI Gothic
サイズ 48.0 64.0 32.0 32.0
選択可否 false true false

テキストエリアについて、選択可能かどうかは以下の画像の部分で設定します。グレーの状態が選択できる状態です。
名前を表示する場所(「name」としているテキストボックス)はユーザーが変更できるようにするため、選択可能な状態にしておきます。


名前を表示するテキストボックスは、<インスタンス名>の部分を「textField」にします。
ここのインスタンス名は絶対にこの名前である必要があります(理由は後述)。大文字小文字にも注意しましょう。


[埋め込み...]というボタンからフォントの埋め込みを行います。
Flash上で変更の可能性があるテキストボックスはswf自体にフォントを埋め込む必要があるようです。
文字の範囲について、「大文字」~「日本語 漢字」あたりまでチェックが入ればいいと思います。


ムービークリップへの変換

①名前領域と②承認ボタンと③キャンセルボタンをそれぞれ別々のムービークリップとして変換します。
ここでは名前、識別子、クラスを以下のように設定します。

名前領域 承認ボタン キャンセルボタン
名前 NameArea SubmitButton CancelButton
識別子
クラス gfx.controls.TextInput gfx.controls.Button

クラスの所がそれぞれ謎のテキストになっていますが、これはskyuiリソースに入っているCLIKフォルダ以下のパスを示しています。
名前領域はCLIK\gfx\controls\TextInput.as、ボタン二つはCLIK\gfx\controls\Button.asが適用されます。


+ CLIKについて
SkyUIの中に入っているCLIKフォルダには、Scaleformと呼ばれる「ゲーム内でFlashを用いたUIを運用するためのコンポーネント」に関するソースが入っています。

元々Flashはアニメーションを作るためのものであり、ゲーム用のUIとして活用するには色々と問題がありました。
そのためFlashのムービークリップを拡張してゲーム内でボタンやテキスト、スクロールバーのような構造を簡単に作ることができるようにしたのがScaleformのCLIKコンポーネントと呼ばれるものです。

また、SkyUIチームはこれらを拡張してSkyrim専用にしたソースを作成しており、それらはCommonフォルダに入っています。

ライブラリの項目に3つのムービークリップシンボルができていたらOKです。


すべてをまとめて1つのムービークリップにする

ステージ上の3つのムービークリップについて、インスタンス名を以下のように設定します。

名前領域 nameArea
承認ボタン submitButton
キャンセルボタン cancelButton

この3つのムービークリップをすべて選択し、[シンボルへ変換]を実行して1つのムービークリップにします。
シンボルに変換ウィンドウの名前、識別子、クラスはすべてMainPanelにします。
また、これによって作成したムービークリップのインスタンス名はmainPanelにします。


こうする理由は、以降のスクリプトファイルの編集時に、MainPanel.asだけを用いて名前領域、承認ボタン、キャンセルボタンの3つを設定できるようにする。

スクリプトファイルの作成

スクリプトファイル「MainPanel.as」を作成し、以下の内容を記述します。

import gfx.controls.TextInput;
import gfx.controls.Button;
import Shared.GlobalFunc;

class MainPanel extends MovieClip 
{
	/* PRIVATE Variables */
	private var MENU_NAME :String;

	/* SHORTEND Variables */
	public var _optionText: TextField;
	
	/* STAGE ELEMENTS */
	public var nameArea: TextInput;
	public var submitButton: Button;	
	public var cancelButton: Button;
	
	public function MainPanel()
	{
		super();
		
		MENU_NAME = "CustomMenu";
		_optionText = nameArea.textField;
	}
	
	// @override MovieClip
	private function onLoad()
	{
		super.onLoad();
		
		nameArea.addEventListener("focusIn", this, "startInput");
		nameArea.addEventListener("focusOut", this, "endInput");

		submitButton.addEventListener("click", this, "onSubmitPress");
		cancelButton.addEventListener("click", this, "onCancelPress");
	}
	
	private function startInput()
	{
		_optionText.type = "input";
		Selection.setFocus(_optionText);
		Selection.setSelection(0,0);
		skse.AllowTextInput(true);
	}
	
	private function endInput()
	{
		_optionText.type = "dynamic";
		skse.AllowTextInput(false);
	}
	
	private function onCancelPress()
	{
		skse.CloseMenu(MENU_NAME);
	}
	
	private function onSubmitPress()
	{
		if (_optionText.text != ""){
			skse.SendModEvent("TEST_setName", _optionText.text);
			skse.CloseMenu(MENU_NAME);
		}
	}
	
	public function startMenu(name: String)
	{
		GlobalFunc.MaintainTextFormat()
		_optionText.SetText(name); 
	}
}

+ ソースコードの説明

nameAreaとsubmitButtonとcancelButton

初見では混乱するかもしれませんが、コンストラクタやonLoadにおいて、nameAreaやsubmitButton、cancelButtonのオブジェクトを何の初期化もなしに扱っています。
これはFlashでステージ上に設置したオブジェクトに対して設定した<インスタンス名>が関わっており、あそこで設定した名称と同じ名前の変数はそのオブジェクトのインスタンス化が予め行われている扱いになります。

MainPanelはNameAreaとSubmitButtonとCancelButtonの3つのムービークリップで構成されます。先ほどの設定にてこれらの3つのムービークリップはそれぞれnameArea、submitButton、cancelButtonというインスタンス名を付けました。

なのでMainPanel.as上では、nameAreaという変数は最初からNameAreaムービークリップのインスタンス化が済んでおり、これを用いることでステージ上に配置されたNameAreaを操作することができるのです。
同様にSubmitButtonとCancelButtonもすでにインスタンス化が済んだ状態でsubmitButton、cancelButtonという変数名で扱えます。


NameAreaのオブジェクト内で名前を表示する場所をtextFiledというインスタンス名にしたのもこれが関係しており、CLIK\gfx\controls\TextInput.asファイルではテキストを記述する領域を「textFiled」という変数名で取り扱っているため、名前を揃える必要がありました。

MainPanel.as内では、今後はnameArea.textFiledという形でプレイヤー名を表示する領域を参照したり代入したりすることができるようになります。
ただ、いちいち長い文章を書くのは面倒なので、コンストラクタで_optionTextという短い名前で扱うようにしています。

イベントの設定

nameArea、submitButton、cancelButtonはそれぞれaddEventListenerを実行していますが、これは「第一引数の名称のイベントが発生した場合、第三引数の関数を実行する」という仕組みを登録するためのCLIKコンポーネントが使えるメソッドです。
focusIn/focusOutは「テキストが入力状態になった/入力状態が解除された」、clickは「クリックされた」というイベントに対応しています。
どのようなイベントがあるかはこちらの「Topics in this section」から進んだところにある「Events」の項目で確認できます。

CLIKコンポーネントを利用する場合、onPressなどのAction Scriptのデフォルトのイベントハンドラは使わず、すべてaddEventListenerを使用します。

そのほかの説明

startInputとendInputは名前入力の開始、終わりに起動します。
テキストフィールドのtypeプロパティは「入力可能(input)」か「入力不可能(dynamic)」かを示します。

Selectionは「今選んているもの」に対してなんやかんやできるオブジェクトです。
setFocusはそのオブジェクトにフォーカスを当てます。テキストフィールドなら入力状態になります。
setSelectionは選択範囲のはじめと終わりを設定するものです。テキストフィールドなら「第一引数の文字目から第二引数の文字目までを選択する」という感じです。

skse.AllowTextInputはキーボードの状態をテキスト入力に対応させるかどうかです。これがtrueのままUIを終わらせた場合、ゲーム中でも「テキスト入力中」という扱いになり、キーボードによる行動が制限されます。
なのでフォーカスが外れた(入力が終了した)際にはAllowTextInputをfalseにする必要があります。

onSubmitPressとstartMenuはゲームとのデータのやり取りを行うための関数になっています。
Papyrusの構造も含めて説明が必要なため、これらの関数の役割は後述します。


コンパイル

Flashからコンパイルを行い、.swfファイルを作成します。

Mod環境の構築

CKを用いてModを作成します。

CKからUIを起動するMod環境を作る

UIを開く・閉じると同じように、新規mod環境を作成し、適当なQuestを作り、スクリプトとして以下の内容を追加します。

;CONSTANTS
string property MENU_NAME = "CustomMenu" AutoReadonly
int property MENU_KEY = 210 AutoReadonly
string property MENU_ROOT = "_root.mainPanel" AutoReadonly

Event OnInit()
	RegisterKey()
EndEvent

Function OnGameReload()
	RegisterForModEvent("TEST_setName", "OnSetName")
EndFunction

Function RegisterKey()
	RegisterForKey(MENU_KEY)
EndFunction

Event OnKeyDown(int KeyCode)
	if (KeyCode == MENU_KEY && !UI.IsMenuOpen(MENU_NAME))
		UI.OpenCustomMenu("02test", 0)
		UI.InvokeString(MENU_NAME, MENU_ROOT + ".startMenu", Game.GetPlayer().GetBaseObject().GetName())
	endif
EndEvent

Event OnSetName(string eventName, string strArg, float numArg, Form sender)
	Game.GetPlayer().GetBaseObject().SetName(strArg)
EndEvent

また、Aliasをプレイヤーで設定し、Aliasのスクリプトとして以下の内容を追加します。

aaaMyQuest Property QuestScript Auto ;「aaaMyQuest」はQuestのScriptとして追加したスクリプト名を使います。

Event OnInit()
	QuestScript.OnGameReload()
EndEvent

Event OnPlayerLoadGame()
	QuestScript.OnGameReload()
EndEvent

ここで注目するのはOnGameReloadで実行している「RegisterForModEvent」と、OnKeyDown内で実行している「UI.InvokeString」、そして「OnSetName」です。
ソースコードを見れば大体わかると思いますが、以下のような構造でゲームとUI上でデータのやりとりをしています。


+ ソースコードの説明

UI.InvokeString

UI.InvokeStringは、開いているUIに関係する.asに定義されている関数を、文字列を引数として実行することができます。
第一引数としてメニュー名、第二引数としてどの関数か、第三引数として送信する文字列を指定します。
第二引数で指定する関数はフルパスで指定する必要があります。
Flashにおけるオブジェクトは「_root」を基本とした階層構造になっており、Flashのステージ上に配置されたオブジェクトのインスタンス名を辿ることで目的の関数を指定します。


OpenCustomMenuにてUIの表示が完了した後(UIのインスタンスが作成し終えた後)、この関数を実行してStartMenuを起動、プレイヤーの名前を渡しているという構図です。

補足:Invoke系の関数の種類と使い方

InvokeStringの他にどのようなものがあるかは、ここで確認できます。
Invokeの後に型名があるものはその型を送信します。
末尾にAとついているものはその型の配列を送信します。

Invokeによって送信した値を.as上の関数で扱うには、関数の括弧内に適当に引数名を書けばその名前で受け取れます。
今回のケースではInvoke先のstartMenu関数は「name」という名前の引数としていますが、ここは別名でも問題ありません。
ただ、型を指定する(:Stringなど)場合はInvoke〇〇関数で送信するデータの型と受け取るデータの型は一致させる必要があります
Invoke関数で送信するデータ型に対応する.asでの型は以下の通りです。

使う関数 対応するActionScriptの型
InvokeBool Boolean
InvokeInt Number
InvokeFloat Number
InvokeNumber Number
InvokeString String
Invoke〇〇A Array
InvokeForm 不明

また、何も書かないということもできます。この場合、「arguments」という配列型に値が入り、以下のような感じで使用できます。

public function startMenu()
{
	GlobalFunc.MaintainTextFormat()
	_optionText.SetText(arguments[0]); //Invokeで投げられた値はargumentsという配列型で使用
}

RegisterForModEventとOnSetName

RegisterForModEventはイベントによるコールバックを自分で登録することができる関数です。
第一引数としてイベント名を設定し、その名前のイベントが発生した場合に、第二引数の関数を実行します(関数も文字列として登録している点に注意しましょう)。
第二引数にて登録する関数の引数は、必ず「string、string、float、form」の引数を持っておく必要があります。
ここで実際に登録しているOnSetNameはこの4つの引数で動いていますね。

そして、「イベントの発生」をどう行うかというと、SendModEventという関数を使ってイベント実行を行います。
SendModEventは以下のように、イベント名とコールバック関数(登録した関数)に投げる引数を指定します。

SendModEvent("TEST_setName", strArg, numArg)

さてこのSendModEventですが、これをまさにFlash側のMainPanel.asのonSubmitPressで使っている構図となっています。
Flash側にて承認ボタンが押された場合、onSubmitPress内のSendModEventが実行され、「TEST_setName」イベントを発生させつつコールバック関数に名前欄の記述内容を送信します。
そうしたらPapyrus側にてOnSetNameイベントが実行され、プレイヤーの名前に受け取った引数がセットされる、という形です。

ちなみにSendModEventで送信できる引数は文字列と数字だけです(配列も不可)。これはそれぞれコールバック用関数(今回はOnSetName)の第二引数と第三引数に格納されます。
文字列だけ送信したい場合やfloatだけ送信したい場合など、以下のような記述方法で対応できます。

SendModEvent("eventName", strArg) ;文字列のみ送信。numArgは省略可能
SendModEvent("eventName", NULL, numArg) ;数字のみ送信。文字列の送信はないのでNULLを指定


動作確認

作成したModを導入し、swfファイルも配置して動作を確認します。
UI表示時にプレイヤーの名前が出ており、そこを編集してSubmitボタンを押したらプレイヤー名が変更されていれば成功です。

タグ:

+ タグ編集
  • タグ:

このサイトはreCAPTCHAによって保護されており、Googleの プライバシーポリシー利用規約 が適用されます。

目安箱バナー