トップ > 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の項目でライブラリを公開するので、利用するといいだろう。
|