|
トップ > MAP作成 > トリガーエディタ > Jass Script > メモリリーク メモリリークのないMAP作りを行う方法。 Jass NewGenの導入が前提です。導入せずとも利用できる内容も一部ありますが、あったほうが断然有利です。 メモリリークとは?
ゲームを続けていくと、だんだんと処理が重くなるようなMAPはありませんか?それはPCのメモリ不足が原因です。World Editorでは、一部の変数をのぞいて、変数等は正しく破棄しないと、処理が終了してもPC上のメモリに残り続け、メモリ容量を圧迫->メモリリークを起こします。 メモリ不足は、ユニット数が異常に多いとき、スペルエフェクト等が激しすぎる時などに、一時的にみられる場合もあります。これはメモリリークとは関係なく、それらが消え去れば症状も改善されます。 メモリリークがあるMAPの場合、プレイを続けていくにつれて徐々に処理が重くなってしまい、ゲームの快適性を大きく損ねます。 メモリリークを防ぐにはcall Destroy〜 を必ず呼ぶ real, integer, boolean以外は、使い終わったら set ○○ = null を徹底する 〜BJ な、Blizzard.j で定義されている関数は、安全が確認されているもの以外使わない。 具体的な対策handle型変数のnull化handle型の変数はすべて、利用後に null を代入することで、メモリからデータを消去する必要がある。handle型の変数は、変数型 のページにあるように、integer, real, boolean 型以外の全ての変数。 具体的には、以下のようになる。 function Sample takes player whichPlayer returns nothing
local item whichItem = GetManipulatedItem()
local real whichReal = GetRandomReal(0.00,1.11)
local string whichString = "SampleString"
local unit whichUnit = GetManipulatingUnit()
//ここに何かしら処理がある
//処理終了。以下、handle型変数のnull化
// item 型とunit型は、ともにwidget型の派生。
//widget型は、handle型の派生であるから、null化する必要がある。
// player 型も handle 型の派生だが、
//takes 〜 で渡された数字は、null化してはいけない。
//※引数をnull化すると、関数をcallしたときに設定した変数がnull化されてしまう。
set whichItem = null
set i = null
endfunction
オブジェクトの破棄以下のオブジェクトは、利用後にそれぞれ専用の関数で破棄しなければならない。また、これは必ずnull化の前に行うこと。
製作のヒントhandle型変数の戻り値を返す以上のことを理解できたら、以下の関数はメモリリークが発生してしまうことがわかるだろう。 //メモリリークを起こす例
function CreateUnitLocust takes player whichPlayer returns unit
local unit newUnit = CreateUnit(whichPlayer,'hpea',0,0,0)
//作成したユニットを、Locustに設定する
call UnitAddAbility(newUnit,'Aloc')
return newUnit
endfunction
どこでメモリリークを起こすかわかるだろうか。そう、ローカル変数 newUnit がnull化されていない点だ。ではどうすればよいか。まず、ダメな回答例をあげる。 //メモリリークを起こさない例
function CreateUnitLocust takes player whichPlayer returns unit
//Blizzard.j で定義されているグローバル変数、bj_lastCreatedUnit を使う
set bj_lastCreatedUnit = CreateUnit(whichPlayer,'hpea',0,0,0)
//作成したユニットを、Locustに設定する
call UnitAddAbility(bj_lastCreatedUnit,'Aloc')
return bj_lastCreatedUnit
endfunction
最初からローカル変数を定義しなければよいのだが、これは例としてあまりに不適切なウンコ回答なのでスルーしてもらいたい。誤作動の元になりうる。*1上の例は無視しよう。 では、どうすればよいか。以下が正式な回答だ。 //handle型変数を、Integerに変換
function H2I takes handle h returns integer
return h
return 0
endfunction
//メモリリークを起こさない例
function CreateUnitLocust takes player whichPlayer returns unit
local unit newUnit = CreateUnit(whichPlayer,'hpea',0,0,0)
//ユニットのhandleを、Integer型で格納する。
local integer newUnitHandle = H2I(newUnit)
//作成したユニットを、Locustに設定する
call UnitAddAbility(newUnit,'Aloc')
//メモリリーク対策。newUnit変数を開放
set newUnit = null
// integer型を返す操作になるが、
//integer型とhandle型は等価であるからエラーは発生しない。
// また、この関数の戻り値は unit型に設定されているので、
//戻り値のintegerは、この関数の呼び出し元では
//handle型->widget型->unit型として処理される。
return newUnitHandle
// 形式上はユニット型を返さないとコンパイルエラーが起こるので、
//ここではreturn null を末尾に記入。"ただし、実際には実行されない。"
return null
endfunction
まず、integer型でユニットのhandleを格納し、その後にユニットを格納していた変数を開放。最後に、ユニット型変数のかわりに、handleと同じ値を持つinteger型変数を返す。このinteger値は、関数の呼び出し元では本来の戻り値であるはずのunit型変数(=widget=handle)と解釈されるため、正常に動作する。また最終行で、コンパイラのエラーを回避するために null を返す。 ここで利用されている H2I 関数は、GameCacheなどあらゆるシステムで非常に便利。GameCacheの項目でライブラリを公開するので、利用するといいだろう。
|