Warcraft3のMOD総合案内
- Jass Script/モジュール、クラス の編集
[
ファイルを添付する
] [
このページの名前を変更する
] [
このページを編集する
]
サイトメニュー
ページ一覧
最近の更新ページ
使い方
トップへ戻る
Jass講座
[[トップ>FrontPage]] > [[MAP作成]] > [[トリガーエディタ]] > [[Jass Script]] > [[Jass講座]] > モジュール、クラス ~ ~ Jass NewGenの最大のウリのひとつ。Jss NewGenを使うと、関数郡を ''library'' や ''scope'' にまとめたり、''structure'' を使い変数をまとめたりできる。 各ページのコンパイル順序が前後してしまうトリガーエディタにおいて、どこからでも参照できるlibraryは非常に重要な機能。structureも、要素を簡潔にまとめてくれるので便利。 &color(red){''Jass NewGenの導入が前提''};です。 ---- #contents ---- *globals - グローバル [#g5d9a8f0] グローバル変数を定義できる。 -例 globals constant real g = 9.8 integer val = 0 endglobals ''globals'' は、どこにでも、いくつでも設定できる。トリガーエディタ付属の変数エディタよりもいろいろな面で優れていて、使いやすい。 定数も宣言できる。 *library - ライブラリ [#b59b184f] ライブラリは、中に関数をいれて利用する。 -文法 library ライブラリ名 [initializer 初期化関数] [requires 必要なライブラリ1, 必要なライブラリ2, ...] endlibrary -例 library mylib function libfunc takes nothing returns nothing endfunction endlibrary コンパイル時、ライブラリ内の関数は、グローバル変数定義の後〜他の関数の前に読み込まれる。いろいろな場所で何度も呼ばれる関数をいれておくと、便利。 **requires - ライブラリの読み込み順序を指定 [#i7c43ecc] あるライブラリから別のライブラリ内の関数を呼び出したいときは、''requires'' パラメータを使う。 -例 library liba requires libb function libaf takes nothing returns nothing call libbf() endfunction endlibrary library libb function libbf takes nothing returns nothing endfunction endlibrary 上の例の場合、コンパイル時に、liba の中身より先に libb の中身が、読み込まれる。 複数のライブラリを ''requires'' に指定したいときは、'','' 記号で区切る。 library liba requires libb, libc, libd また、二つのライブラリがお互いを ''requires'' に設定することはできない。 **initializer - ライブラリの初期化 [#c87a1541] ライブラリを読み込む時(MAPロード時)、そのまえに関数を実行しておきたい場合は、''initializer'' パラメータを使う。 -例 library liba initializer init_liba function run takes nothing returns nothing endfunction function init_liba takes nothing returns nothing endfunction endlibrary ''initializer'' に指定する関数は、ライブラリ内に書かなければならない(''private'' も可)。また、''initializer'' は、引数を取らない形( takes nothing 〜)でなければならない。 **private - プライベート関数 [#t74e7605] ''library'' 内の ''function'' を、 ''private function'' 〜 とすると、その関数は同じライブラリの中からしか呼び出せなくなる。そのかわりに、ライブラリの外にも同名の関数を定義できるようになる。 -例 library whee private function test takes nothing returns nothing endfunction endlibrary function test takes nothing returns nothing endfunction ライブラリの中だけでしか使わない関数は、できるだけ ''private'' で宣言しよう。似たような名前の関数がある時など、ミス防止に役に立つ。 *scope - スコープ [#eb6b273c] スコープ は、中に関数をいれて利用する。ライブラリと同じような機能をもつが、コンパイルしても先に読み込まれるといったことはない。 -定義 scope スコープ名 endscope -例 scope myscope private function scopetest2 takes nothing returns nothing endfunction function scopetest takes nothing returns nothing call scopetest2() endfunction endscope 関数をまとめるのに便利。''library'' 同様、''private'' が使える。 *structure - 構造体 [#t8d9fcb4] 複数のデータを持つ要素をまとめるのに、非常に便利。[[GameCache>Jass Script/GameCache]]よりも&color(red){''処理が軽い''};ので、とても役に立つ。 構造体は、変数型のひとつのように振舞う(正体はinteger)。 -例 struct somestruct string message = "Hello!" endstruct function showmsg takes nothing returns nothing local somestruct s=somestruct.create() call BJDebugMsg(s.message) call s.destroy() endfunction 上の例では、画面に『Hello!』と表示される。 -定義 struct 名前 endstruct -構造体の作成 local 構造体の名前 変数名=構造体の名前.create() -構造体の破棄 call 変数名.destroy() 構造体は破棄しなくても、メモリリークの原因になることはない。ただし、一度にあつかえる構造体の数は、最大8190 個までと決まっているので、使い終わった構造体は必ず破棄しよう。 -構造体の値を参照 構造体の変数名.構造体内の変数名 -例 struct test real one=1 real two=2 real three=3 real sum endstruct function add takes nothing returns nothing local test blah=test.create() set blah.sum=blah.one+blah.two+blah.three call BJDebugMsg(R2S(blah.sum)) call blah.destroy() endfunction 上の例の場合、画面に "6.000" が表示される。 -構造体の変数は、[[null化>Jass Script/メモリリーク]]する必要はない(正体はinteger)。 -構造体はそれ自身がひとつのインスタンスなので、普通の変数型と同じように、何個でも(最大8190個)宣言できる。 -構造体の中に配列を宣言するときは、大きさを明示する必要がある。 struct SampleStruct real array sample[2] endstruct ~ 上の例の場合、''sample[0]'' と ''sample[1]'' が利用可能。''インデックスではなく、長さを記す''点に注意。~ ~ 配列を宣言すると、その構造体のインスタンスを同時に利用できる最大数が、[構造体内の配列の、最大の長さ]で割った数まで減る。上の例の場合、同時に利用できるインスタンスの最大数は 8190/2 = 4095個。 **static - スタティック [#ud7fc480] ''static'' とは、構造体の中での、 ''グローバル変数/定数'' の宣言方法。 通常の構造体内の変数とは違い、全ての構造体で共有する変数になる。 -参照方法 構造体名.staticな変数名 // もしくは 構造体のインスタンス名.staticな変数名 どちらの場合でも、同じ変数を指す。 -例 struct we string msg = "Hello!" static integer X = 3 endstruct function SetX takes nothing returns nothing local we NewStruct1 = we.create() local we NewStruct2 = we.create() // NewStruct1の、msg を編集 // これはインスタンスそれぞれが持つ変数なので、NewStruct2.msg は変更されない。 set NewStruct1.msg = "Goodbye!" // we構造体の、X を編集 // これは、全てのwe構造体のインスタンスで共通して参照する値なので、 // NewStruct1.X と NewStruct1.X は、常に同じ値 = 10 を返すようになる。 set we.X = 10 // set NewStruct1.X = 10 等、we構造体のインスタンスから参照しても同じ。 // 値を表示 call BJDebugMsg("=== Common(Static) ===") call BJDebugMsg("X = " + I2S(we.X)) call BJDebugMsg("=== NewStruct1 ===") call BJDebugMsg("msg = " + NewStruct1.msg) call BJDebugMsg("X = " + I2S(NewStruct1.X)) call BJDebugMsg("=== NewStruct2 ===") call BJDebugMsg("msg = " + NewStruct2.msg) call BJDebugMsg("X = " + I2S(NewStruct2.X)) endfunction これを実行すると、以下のような出力が得られる。 === Common(Static) === X = 10 === NewStruct1 === msg = Goodbye! X = 10 === NewStruct2 === msg = Hello! X = 10 つまり、''globals'' 内で定義した変数と同じように扱える。 また、''static'' な定数を宣言する方法は、以下のとおり。 static constant real e = 2.71828 つまり、''globals'' 内で定義した定数と同じように扱える。 **method - メソッド [#r6d93307] ''method'' とは、構造体の中で定義できる ''function'' のこと。 -例 struct we string msg="Hello!" method sayit takes nothing returns nothing call BJDebugMsg(this.msg) endmethod endstruct function test takes nothing returns nothing local we s=we.create() call s.sayit() call s.destroy() endfunction -''method'' を呼び出す方法 call 変数名.''method''名(引数) ''method''内から、同じ構造体に含まれる変数を呼び出したいときは、''this.変数名'' を使う。 また、 ''static'' な ''method'' も宣言できる。 -注意点 --''method''内で、&color(red){''GetTriggerUnit()''};を使ってはいけない --''method''内で、&color(red){''TriggerSleepAction''};/&color(red){''PolledWait''};を使ってはいけない **構造体の利点 [#structprofit] 通常、値をオブジェクトに関連付けたい場合は、[[GameCache>Jass Script/GameCache]]を使う。 -構造体を&color(red){使わない};例 //よくない例 function AdddUnitInfo takes unit whichUnit, integer A, real B, unit C returns nothing call SetHandleInteger(whichUnit,"A",A) call SetHandleReal (whichUnit,"B",B) call SetHandleHandle (whichUnit,"C",C) endfunction //ユニットの値を取得&編集、更新 function ShowUnitInfo takes unit whichUnit returns nothing local integer A = GetHandleInteger(whichUnit,"A") local real B = GetHandleReal (whichUnit,"B") local unit C = GetHandleUnit (whichUnit,"C") //たとえば、AとBに1たして更新する。 //GameCacheの中身と変数の中身は別なので、登録しなおす必要がある。 call SetHandleInteger(whichUnit,"A",A+1) call SetHandleReal (whichUnit,"B",B+1) //再びこの変数郡を使わないなら、GameCacheから削除すること。 //call SetHandleInteger(whichUnit,"A",0) //call SetHandleReal(whichUnit,"B",0) //call SetHandleHandle(whichUnit,"C",null) //ユニット型変数を、開放 set C = null endfunction [[GameCache>Jass Script/GameCache]]の処理は、構造体を使った処理よりも遅い。しかも複数のパラメータがあるとき、それぞれを呼び出さねばならず、更新の処理も必要だ。 構造体を使うと、下のようになる。 -構造体を&color(green){使った};例 //よい例 //構造体を定義する struct UnitInfo integer A = 0 real B = 0 unit C = null endstruct function AdddUnitInfo takes unit whichUnit, integer A, real B, unit C returns nothing local UnitInfo UI = UnitInfo.create() set UI.A = A set UI.B = B set UI.C = C //構造体(integer扱い)を、SetHandleIntegerを使って格納。 call SetHandleInteger(whichUnit,"UI",UI) endfunction //ユニットの値を取得&編集、更新 function ShowUnitInfo takes unit whichUnit returns nothing local UnitInfo UI = UnitInfo(GetHandleInteger(whichUnit,"UI")) //これだけで、 UI.A、UI.B、UI.C 全てにアクセスできる。 //たとえば、AとBに1たして更新する。 set UI.A = UI.A+1 set UI.A = UI.A+2 //GameCacheの手法とは違い、UI に格納してあるのは構造体の識別番号のみ。 //従って、GameCacheを更新する必要はない。 //再びこのインスタンスを使わないなら、destroy()すること。 //call UI.destroy() //call SetHandleInteger(whichUnit,"UI",0) //よくない例と違い、ダイレクトに構造体のインスタンスに格納するので、 //ローカル変数の開放をしなくてよい endfunction [[GameCache>Jass Script/GameCache]]を呼び出す回数が圧倒的に減るので、処理も軽快になる。また、スクリプトを書きながら要素を増やした時なども、非常に対応がしやすい。 **利用例 [#q99e1a65] [[ノックバック>Jass Script/サンプル集#Knockback]] のサンプルをどうぞ。 **注意点 [#bd583f79] 構造体は、.destroy()しても、''当分は使えてしまう''。 -使えてしまう例 //のちのち不具合がでる、悪い例 struct abuseStruct integer A = 0 endstruct function abusesample takes nothing returns nothing local abuseStruct S = abuseStruct.create() //変数を設定 set S.A = 100 //構造体を破棄 call S.destroy() //構造体.A が、参照できてしまう call BJDebugMsg(I2S(S.A)) endfunction この場合、画面には正常に 100 という数字が表示される。しかし、大量の、同じ構造体が作られた場合、このデータは上書きされ、違う数値が出力される。 これはとても不具合の原因になりやすいので、十分に注意が必要だ。なまじ参照できてしまうだけに気づきにくいが、十分に注意すること。 ***Q&A [#l4bcb52f] -構造体にhandle型変数(たとえばユニット)を指定した場合、.destroy()時に破棄しなくていいのか? --おまけコラムに記すが、構造体の中身は全てグローバル変数に定義してある。領域は確保されたままにはなるが、ローカル変数とは違い再利用される領域なので、''破棄する必要はない。'' ***おまけコラム [#e38ef574] 構造体の正体は、&color(red){''グローバル変数(配列)の集まり''};です。たとえば struct test integer a unit b endstruct このような構造体があった場合、以下のようなグローバル変数が定義されます。 //構造体内の、最大の配列の長さ constant integer si__test=1 //インスタンス別に、インデックスを操作する変数 integer si__test_F=0 integer si__test_I=0 integer array si__test_V //それぞれのインスタンスの変数を格納する配列 integer array s__test_a unit array s__test_b まず、インスタンスが作成されると、そのインスタンスに特定のインデックス(integer)が割り当てられます。 そして、そのインスタンスの変数が扱われるときは、グローバル変数の配列の特定のインデックスに対して値が代入されるという処理が行われます。 -コンパイル前 struct test integer a = 1 unit b = null endstruct function sample takes unit unitA, unit unitB returns nothing local test T1 = test.create() local test T2 = test.create() set T1.a = 100 set T1.b = unitA set T2.a = 300 set T2.b = unitB call T1.destroy() endfunction -コンパイル後 globals //構造体内の、最大の配列の長さ constant integer si__test=1 //インスタンス別に、インデックスを操作する変数 integer si__test_F=0 integer si__test_I=0 integer array si__test_V //それぞれのインスタンスの変数を格納する配列 integer array s__test_a unit array s__test_b endglobals //test.create() に対応。構造体のインスタンスを作る処理。 //まだ使っていないインデックスを割り当てる。 function s__test__allocate takes nothing returns integer local integer this=si__test_F if (this!=0) then set si__test_F=si__test_V[this] else set si__test_I=si__test_I+1 set this=si__test_I endif if (this>8190) then return 0 endif //値を初期化 set s__test_a[this]= 1 set s__test_b[this]= null set si__test_V[this]=-1 return this endfunction //test.destroy() に対応。構造体のインスタンスを削除する処理。 //まだ使っていないインデックスを割り当てる。 function s__test_destroy takes integer this returns nothing if this==null then return elseif (si__test_V[this]!=-1) then return endif set si__test_V[this]=si__test_F set si__test_F=this endfunction function sample takes unit unitA, unit unitB returns nothing local integer T1 = s__test__allocate() local integer T2 = s__test__allocate() set s__test_a[T1] = 100 set s__test_b[T1] = unitA set s__test_a[T2] = 300 set s__test_b[T2] = unitB call s__test_destroy(T1) endfunction このような処理に変換されます。グローバル変数の操作は[[GameCache>Jass Script/GameCache]]の操作より遥かに早いので、より効率のいいスクリプトとなるわけです。 また、構造体の仕様も、上のソースから説明できます。 -構造体をnull化しなくていい理由 --構造体のインスタンスの正体は、integerだから -『構造体の利点』の項目にある、『''set UI.A = UI.A+1''』だけで、[[GameCache>Jass Script/GameCache]]に入っている構造体のインスタンスそのものが変更できる理由 --[[GameCache>Jass Script/GameCache]]に入っているのはインデックスだけで、実データはグローバル変数の配列。それを直接操作しているだけだから。 -『注意点』の項目にある、.destroy() 後もしばらく使えてしまう理由 --配列のインデックスを再利用可能にするだけなので、データ自体は消失していないため。なので、同じインデックス番号を指定すれば、上書きされるまでは使えてしまう。 ---.create() 時にはきちんと初期化されるので、別のインスタンスに引き継がれてしまうということはない。 要は、グローバル変数の配列を利用しているから、ゲームキャッシュを介すよりも、ずっと早いということ。
タイムスタンプを変更しない
[[トップ>FrontPage]] > [[MAP作成]] > [[トリガーエディタ]] > [[Jass Script]] > [[Jass講座]] > モジュール、クラス ~ ~ Jass NewGenの最大のウリのひとつ。Jss NewGenを使うと、関数郡を ''library'' や ''scope'' にまとめたり、''structure'' を使い変数をまとめたりできる。 各ページのコンパイル順序が前後してしまうトリガーエディタにおいて、どこからでも参照できるlibraryは非常に重要な機能。structureも、要素を簡潔にまとめてくれるので便利。 &color(red){''Jass NewGenの導入が前提''};です。 ---- #contents ---- *globals - グローバル [#g5d9a8f0] グローバル変数を定義できる。 -例 globals constant real g = 9.8 integer val = 0 endglobals ''globals'' は、どこにでも、いくつでも設定できる。トリガーエディタ付属の変数エディタよりもいろいろな面で優れていて、使いやすい。 定数も宣言できる。 *library - ライブラリ [#b59b184f] ライブラリは、中に関数をいれて利用する。 -文法 library ライブラリ名 [initializer 初期化関数] [requires 必要なライブラリ1, 必要なライブラリ2, ...] endlibrary -例 library mylib function libfunc takes nothing returns nothing endfunction endlibrary コンパイル時、ライブラリ内の関数は、グローバル変数定義の後〜他の関数の前に読み込まれる。いろいろな場所で何度も呼ばれる関数をいれておくと、便利。 **requires - ライブラリの読み込み順序を指定 [#i7c43ecc] あるライブラリから別のライブラリ内の関数を呼び出したいときは、''requires'' パラメータを使う。 -例 library liba requires libb function libaf takes nothing returns nothing call libbf() endfunction endlibrary library libb function libbf takes nothing returns nothing endfunction endlibrary 上の例の場合、コンパイル時に、liba の中身より先に libb の中身が、読み込まれる。 複数のライブラリを ''requires'' に指定したいときは、'','' 記号で区切る。 library liba requires libb, libc, libd また、二つのライブラリがお互いを ''requires'' に設定することはできない。 **initializer - ライブラリの初期化 [#c87a1541] ライブラリを読み込む時(MAPロード時)、そのまえに関数を実行しておきたい場合は、''initializer'' パラメータを使う。 -例 library liba initializer init_liba function run takes nothing returns nothing endfunction function init_liba takes nothing returns nothing endfunction endlibrary ''initializer'' に指定する関数は、ライブラリ内に書かなければならない(''private'' も可)。また、''initializer'' は、引数を取らない形( takes nothing 〜)でなければならない。 **private - プライベート関数 [#t74e7605] ''library'' 内の ''function'' を、 ''private function'' 〜 とすると、その関数は同じライブラリの中からしか呼び出せなくなる。そのかわりに、ライブラリの外にも同名の関数を定義できるようになる。 -例 library whee private function test takes nothing returns nothing endfunction endlibrary function test takes nothing returns nothing endfunction ライブラリの中だけでしか使わない関数は、できるだけ ''private'' で宣言しよう。似たような名前の関数がある時など、ミス防止に役に立つ。 *scope - スコープ [#eb6b273c] スコープ は、中に関数をいれて利用する。ライブラリと同じような機能をもつが、コンパイルしても先に読み込まれるといったことはない。 -定義 scope スコープ名 endscope -例 scope myscope private function scopetest2 takes nothing returns nothing endfunction function scopetest takes nothing returns nothing call scopetest2() endfunction endscope 関数をまとめるのに便利。''library'' 同様、''private'' が使える。 *structure - 構造体 [#t8d9fcb4] 複数のデータを持つ要素をまとめるのに、非常に便利。[[GameCache>Jass Script/GameCache]]よりも&color(red){''処理が軽い''};ので、とても役に立つ。 構造体は、変数型のひとつのように振舞う(正体はinteger)。 -例 struct somestruct string message = "Hello!" endstruct function showmsg takes nothing returns nothing local somestruct s=somestruct.create() call BJDebugMsg(s.message) call s.destroy() endfunction 上の例では、画面に『Hello!』と表示される。 -定義 struct 名前 endstruct -構造体の作成 local 構造体の名前 変数名=構造体の名前.create() -構造体の破棄 call 変数名.destroy() 構造体は破棄しなくても、メモリリークの原因になることはない。ただし、一度にあつかえる構造体の数は、最大8190 個までと決まっているので、使い終わった構造体は必ず破棄しよう。 -構造体の値を参照 構造体の変数名.構造体内の変数名 -例 struct test real one=1 real two=2 real three=3 real sum endstruct function add takes nothing returns nothing local test blah=test.create() set blah.sum=blah.one+blah.two+blah.three call BJDebugMsg(R2S(blah.sum)) call blah.destroy() endfunction 上の例の場合、画面に "6.000" が表示される。 -構造体の変数は、[[null化>Jass Script/メモリリーク]]する必要はない(正体はinteger)。 -構造体はそれ自身がひとつのインスタンスなので、普通の変数型と同じように、何個でも(最大8190個)宣言できる。 -構造体の中に配列を宣言するときは、大きさを明示する必要がある。 struct SampleStruct real array sample[2] endstruct ~ 上の例の場合、''sample[0]'' と ''sample[1]'' が利用可能。''インデックスではなく、長さを記す''点に注意。~ ~ 配列を宣言すると、その構造体のインスタンスを同時に利用できる最大数が、[構造体内の配列の、最大の長さ]で割った数まで減る。上の例の場合、同時に利用できるインスタンスの最大数は 8190/2 = 4095個。 **static - スタティック [#ud7fc480] ''static'' とは、構造体の中での、 ''グローバル変数/定数'' の宣言方法。 通常の構造体内の変数とは違い、全ての構造体で共有する変数になる。 -参照方法 構造体名.staticな変数名 // もしくは 構造体のインスタンス名.staticな変数名 どちらの場合でも、同じ変数を指す。 -例 struct we string msg = "Hello!" static integer X = 3 endstruct function SetX takes nothing returns nothing local we NewStruct1 = we.create() local we NewStruct2 = we.create() // NewStruct1の、msg を編集 // これはインスタンスそれぞれが持つ変数なので、NewStruct2.msg は変更されない。 set NewStruct1.msg = "Goodbye!" // we構造体の、X を編集 // これは、全てのwe構造体のインスタンスで共通して参照する値なので、 // NewStruct1.X と NewStruct1.X は、常に同じ値 = 10 を返すようになる。 set we.X = 10 // set NewStruct1.X = 10 等、we構造体のインスタンスから参照しても同じ。 // 値を表示 call BJDebugMsg("=== Common(Static) ===") call BJDebugMsg("X = " + I2S(we.X)) call BJDebugMsg("=== NewStruct1 ===") call BJDebugMsg("msg = " + NewStruct1.msg) call BJDebugMsg("X = " + I2S(NewStruct1.X)) call BJDebugMsg("=== NewStruct2 ===") call BJDebugMsg("msg = " + NewStruct2.msg) call BJDebugMsg("X = " + I2S(NewStruct2.X)) endfunction これを実行すると、以下のような出力が得られる。 === Common(Static) === X = 10 === NewStruct1 === msg = Goodbye! X = 10 === NewStruct2 === msg = Hello! X = 10 つまり、''globals'' 内で定義した変数と同じように扱える。 また、''static'' な定数を宣言する方法は、以下のとおり。 static constant real e = 2.71828 つまり、''globals'' 内で定義した定数と同じように扱える。 **method - メソッド [#r6d93307] ''method'' とは、構造体の中で定義できる ''function'' のこと。 -例 struct we string msg="Hello!" method sayit takes nothing returns nothing call BJDebugMsg(this.msg) endmethod endstruct function test takes nothing returns nothing local we s=we.create() call s.sayit() call s.destroy() endfunction -''method'' を呼び出す方法 call 変数名.''method''名(引数) ''method''内から、同じ構造体に含まれる変数を呼び出したいときは、''this.変数名'' を使う。 また、 ''static'' な ''method'' も宣言できる。 -注意点 --''method''内で、&color(red){''GetTriggerUnit()''};を使ってはいけない --''method''内で、&color(red){''TriggerSleepAction''};/&color(red){''PolledWait''};を使ってはいけない **構造体の利点 [#structprofit] 通常、値をオブジェクトに関連付けたい場合は、[[GameCache>Jass Script/GameCache]]を使う。 -構造体を&color(red){使わない};例 //よくない例 function AdddUnitInfo takes unit whichUnit, integer A, real B, unit C returns nothing call SetHandleInteger(whichUnit,"A",A) call SetHandleReal (whichUnit,"B",B) call SetHandleHandle (whichUnit,"C",C) endfunction //ユニットの値を取得&編集、更新 function ShowUnitInfo takes unit whichUnit returns nothing local integer A = GetHandleInteger(whichUnit,"A") local real B = GetHandleReal (whichUnit,"B") local unit C = GetHandleUnit (whichUnit,"C") //たとえば、AとBに1たして更新する。 //GameCacheの中身と変数の中身は別なので、登録しなおす必要がある。 call SetHandleInteger(whichUnit,"A",A+1) call SetHandleReal (whichUnit,"B",B+1) //再びこの変数郡を使わないなら、GameCacheから削除すること。 //call SetHandleInteger(whichUnit,"A",0) //call SetHandleReal(whichUnit,"B",0) //call SetHandleHandle(whichUnit,"C",null) //ユニット型変数を、開放 set C = null endfunction [[GameCache>Jass Script/GameCache]]の処理は、構造体を使った処理よりも遅い。しかも複数のパラメータがあるとき、それぞれを呼び出さねばならず、更新の処理も必要だ。 構造体を使うと、下のようになる。 -構造体を&color(green){使った};例 //よい例 //構造体を定義する struct UnitInfo integer A = 0 real B = 0 unit C = null endstruct function AdddUnitInfo takes unit whichUnit, integer A, real B, unit C returns nothing local UnitInfo UI = UnitInfo.create() set UI.A = A set UI.B = B set UI.C = C //構造体(integer扱い)を、SetHandleIntegerを使って格納。 call SetHandleInteger(whichUnit,"UI",UI) endfunction //ユニットの値を取得&編集、更新 function ShowUnitInfo takes unit whichUnit returns nothing local UnitInfo UI = UnitInfo(GetHandleInteger(whichUnit,"UI")) //これだけで、 UI.A、UI.B、UI.C 全てにアクセスできる。 //たとえば、AとBに1たして更新する。 set UI.A = UI.A+1 set UI.A = UI.A+2 //GameCacheの手法とは違い、UI に格納してあるのは構造体の識別番号のみ。 //従って、GameCacheを更新する必要はない。 //再びこのインスタンスを使わないなら、destroy()すること。 //call UI.destroy() //call SetHandleInteger(whichUnit,"UI",0) //よくない例と違い、ダイレクトに構造体のインスタンスに格納するので、 //ローカル変数の開放をしなくてよい endfunction [[GameCache>Jass Script/GameCache]]を呼び出す回数が圧倒的に減るので、処理も軽快になる。また、スクリプトを書きながら要素を増やした時なども、非常に対応がしやすい。 **利用例 [#q99e1a65] [[ノックバック>Jass Script/サンプル集#Knockback]] のサンプルをどうぞ。 **注意点 [#bd583f79] 構造体は、.destroy()しても、''当分は使えてしまう''。 -使えてしまう例 //のちのち不具合がでる、悪い例 struct abuseStruct integer A = 0 endstruct function abusesample takes nothing returns nothing local abuseStruct S = abuseStruct.create() //変数を設定 set S.A = 100 //構造体を破棄 call S.destroy() //構造体.A が、参照できてしまう call BJDebugMsg(I2S(S.A)) endfunction この場合、画面には正常に 100 という数字が表示される。しかし、大量の、同じ構造体が作られた場合、このデータは上書きされ、違う数値が出力される。 これはとても不具合の原因になりやすいので、十分に注意が必要だ。なまじ参照できてしまうだけに気づきにくいが、十分に注意すること。 ***Q&A [#l4bcb52f] -構造体にhandle型変数(たとえばユニット)を指定した場合、.destroy()時に破棄しなくていいのか? --おまけコラムに記すが、構造体の中身は全てグローバル変数に定義してある。領域は確保されたままにはなるが、ローカル変数とは違い再利用される領域なので、''破棄する必要はない。'' ***おまけコラム [#e38ef574] 構造体の正体は、&color(red){''グローバル変数(配列)の集まり''};です。たとえば struct test integer a unit b endstruct このような構造体があった場合、以下のようなグローバル変数が定義されます。 //構造体内の、最大の配列の長さ constant integer si__test=1 //インスタンス別に、インデックスを操作する変数 integer si__test_F=0 integer si__test_I=0 integer array si__test_V //それぞれのインスタンスの変数を格納する配列 integer array s__test_a unit array s__test_b まず、インスタンスが作成されると、そのインスタンスに特定のインデックス(integer)が割り当てられます。 そして、そのインスタンスの変数が扱われるときは、グローバル変数の配列の特定のインデックスに対して値が代入されるという処理が行われます。 -コンパイル前 struct test integer a = 1 unit b = null endstruct function sample takes unit unitA, unit unitB returns nothing local test T1 = test.create() local test T2 = test.create() set T1.a = 100 set T1.b = unitA set T2.a = 300 set T2.b = unitB call T1.destroy() endfunction -コンパイル後 globals //構造体内の、最大の配列の長さ constant integer si__test=1 //インスタンス別に、インデックスを操作する変数 integer si__test_F=0 integer si__test_I=0 integer array si__test_V //それぞれのインスタンスの変数を格納する配列 integer array s__test_a unit array s__test_b endglobals //test.create() に対応。構造体のインスタンスを作る処理。 //まだ使っていないインデックスを割り当てる。 function s__test__allocate takes nothing returns integer local integer this=si__test_F if (this!=0) then set si__test_F=si__test_V[this] else set si__test_I=si__test_I+1 set this=si__test_I endif if (this>8190) then return 0 endif //値を初期化 set s__test_a[this]= 1 set s__test_b[this]= null set si__test_V[this]=-1 return this endfunction //test.destroy() に対応。構造体のインスタンスを削除する処理。 //まだ使っていないインデックスを割り当てる。 function s__test_destroy takes integer this returns nothing if this==null then return elseif (si__test_V[this]!=-1) then return endif set si__test_V[this]=si__test_F set si__test_F=this endfunction function sample takes unit unitA, unit unitB returns nothing local integer T1 = s__test__allocate() local integer T2 = s__test__allocate() set s__test_a[T1] = 100 set s__test_b[T1] = unitA set s__test_a[T2] = 300 set s__test_b[T2] = unitB call s__test_destroy(T1) endfunction このような処理に変換されます。グローバル変数の操作は[[GameCache>Jass Script/GameCache]]の操作より遥かに早いので、より効率のいいスクリプトとなるわけです。 また、構造体の仕様も、上のソースから説明できます。 -構造体をnull化しなくていい理由 --構造体のインスタンスの正体は、integerだから -『構造体の利点』の項目にある、『''set UI.A = UI.A+1''』だけで、[[GameCache>Jass Script/GameCache]]に入っている構造体のインスタンスそのものが変更できる理由 --[[GameCache>Jass Script/GameCache]]に入っているのはインデックスだけで、実データはグローバル変数の配列。それを直接操作しているだけだから。 -『注意点』の項目にある、.destroy() 後もしばらく使えてしまう理由 --配列のインデックスを再利用可能にするだけなので、データ自体は消失していないため。なので、同じインデックス番号を指定すれば、上書きされるまでは使えてしまう。 ---.create() 時にはきちんと初期化されるので、別のインスタンスに引き継がれてしまうということはない。 要は、グローバル変数の配列を利用しているから、ゲームキャッシュを介すよりも、ずっと早いということ。
テキスト整形のルールを表示する