18日目で関数SP_NEWCLASSを作成し、クラスを生成すると同時にインスタンスのリソースも確保しました。
今回は、インスタンスの管理(生成と廃棄)を扱います。
インスタンスの個数が状況に応じて増えたり減ったりする場合、確保したリソースを上限として、インスタンスを増やしたり減らしたりする処理が必要です。
たとえばシューティングゲームにおいて、自機は通常画面内に1機ですが、敵は新しく登場したりプレイヤーの攻撃によって消滅したりするため、数が変動します。
新しく敵を登場させるには、リソースに空きがあるかどうか確認が必要ですし、敵が消滅したらそのリソースは返却しなければなりません。
オブジェクト指向言語ではインスタンスを生成する処理をコンストラクタ、廃棄する処理をデストラクタと呼びます。
今回は、このコンストラクタとデストラクタを作ります。
といっても、実質的にはオブジェクト=リソース=スプライトですので、
・未使用のスプライトと使用中のリスプライトを、スプライト変数1番の値で区別する。
・新しくインスタンスを生成するときは、未使用のスプライトから割り当てる。
・インスタンスを廃棄する時は、割り当てていたスプライトを未使用状態に戻す。
という処理をするだけです。
スプライト変数0番はクラスに割り当てましたので、スプライト変数の利用状態は下図のようになります。
1ビットの情報を表現するのに、残り7つしかない変数を一つ使ってしまうのはちょっともったいない気もしますので、スプライト変数1番は、広く「オブジェクトの状態」を表す変数とします。
そして、「未使用」はその状態の1つで値は0とし、状態が0以外の値なら使用中と見なすことにします。
そのほか、スプライトは廃棄時にSPHIDEで不可視にし、生成時にSPSHOWで可視にします。
ただしクラスオブジェクトのスプライトはクラス生成時にSPHIDEします。
コンストラクタ・デストラクタの実装は以下のようになっています。
VAR _S_UNUSED:_S_UNUSED = 0 VAR _S_ACTIVE:_S_ACTIVE = 1 DEF SP_NEWOBJECT(CLASS) VAR I IF __I1ST[CLASS] != _S_NULL THEN FOR I=__I1ST[CLASS] TO __ILAST[CLASS] IF SP_GETSTATE(I)==_S_UNUSED THEN SP_SETSTATE I, _S_ACTIVE SPSHOW I RETURN I ENDIF NEXT ENDIF RETURN _S_NULL END DEF SP_FREE SELF SPHIDE SELF SP_SETSTATE SELF,_S_UNUSED END
_S_UNUSEDはリソースが未使用である状態を示す定数です。
_S_ACTIVEは、状態が_S_UNUSEDではないことを表すために便宜上定義した定数です。
SP_NEWOBJECT(CLASS)は、CLASSの新しいインスタンスを生成して返します。
リソース不足で生成できなかった場合には、_S_NULLを返します。
呼び出し側では、返り値が_S_NULLかどうかを必ずチェックしなければなりません。
SP_FREEは、オブジェクトを廃棄します。引数は廃棄するオブジェクト自身で、返り値はありません。
また、オブジェクトの状態に関して以下のようなユーティリティ関数を作成しました。
DEF SP_SETSTATE SELF,STATE _SPVAR SELF,1,STATE END DEF SP_GETSTATE(SELF) RETURN _SPVAR_(SELF,1) END DEF SP_ISACTIVE(SELF) RETURN (SP_GETSTATE(SELF) != _S_UNUSED) END DEF SP_COUNT_INSTANCE(CLASS,STATE) VAR I,C C=0 IF __I1ST[CLASS] != _S_NULL THEN FOR I=__I1ST[CLASS] TO __ILAST[CLASS] IF SP_GETSTATE(I)==STATE THEN INC C NEXT ENDIF RETURN C END
SP_GETSTATEとSP_SETSTATEはオブジェクトの状態を取得・設定します。
SP_ISACTIVEはオブジェクトが未使用ならFALSE、使用中ならTRUEを返します。
SP_COUNT_INSTANCE(CLASS,STATE)は、CLASSのインスタンスで状態がSTATEであるものの個数を返します。
SP_COUNT_INSTANCE(CLASS,_S_UNUSED)で、あと何個インスタンスを作成可能かが分かります。
スプライト変数1番にアプリケーション本体からアクセスするのではなく、これらの関数を介してアクセスすることで、オブジェクトの実装を将来変更した場合の影響を小さくします。
例えば、現在クラスは512個しか作成できないのですから、スプライト変数0番の上位ビットに状態を表す数を設定することで、スプライト変数1番を使わないようにすることができます。
これらの関数を使っていれば、上のような変更をした場合でも、これらの関数の実装を変更するだけで対応できます。
このほか、関数SP_NEWCLASSにクラス作成時点でのオブジェクトの状態を設定するためのコードを追加しました。
赤が追加部分です。
クラスオブジェクトは使用中、インスタンスオブジェクトは全て未使用に設定します。
DEF SP_NEWCLASS(CNAME,N_INSTANCES) VAR CLASS,OBJ CLASS=__MAXOBJ OBJ=SP_SETCLASS(CLASS,CLASS) SP_SETSTATE CLASS, _S_ACTIVE SPHIDE CLASS INC __MAXOBJ FOR I=1 TO N_INSTANCES OBJ=SP_SETCLASS(__MAXOBJ,CLASS) SP_SETSTATE OBJ, _S_UNUSED SPHIDE OBJ INC __MAXOBJ NEXT __CNAME$[CLASS]=CNAME IF N_INSTANCES > 0 THEN __I1ST[CLASS]=CLASS+1 __ILAST[CLASS]=CLASS+N_INSTANCES ELSE __I1ST[CLASS]=_S_NULL __ILAST[CLASS]=_S_NULL ENDIF RETURN CLASS END
コメント
それって、正しくはデストラクタ(destructor)ではありませんか?
そうでした!ツッコミありがとうございます。修正しました。
OOPの記事をまとめたプログラムの公開キーとかありますか?