この日記はプログラムもオブジェクト指向も初めて間もない人間が
初心者丸出しのことばかり書いてます。
今はオブジェクト指向でシューティングゲーム製作中です。
しかしクラスや仮想関数、演算子オーバーロードなどの
C++の使い方はわかっていてもそれだけではオブジェクト指向を
理解できたわけではない、という結論に(今更ながら)到達…
オブジェクト指向の理念というか思想というか、
考え方といいますか…
これがよくわからなくて苦戦していましたが、
最近自分なりの答えを見つけてきました。
自分自身で考えた末に出た答えとして、
・クラスだけでプログラムを組もうとは考えないこと
というのが一つの答えでした。
学校の授業でやったことのひとつとして
クラスはあくまでも設計図であり、
これでプログラムを動かすわけではない
ということを学んだので、
このように書いてみました
//自機のクラス
//■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
class PlayerClass{
private:
double x; //x座標
double y; //y座標
double speed; //速度
double range; //当たり範囲
int g_dir; //絵の表示番号
int dir; //移動方向
int cnt; //カウンター
int muteki_time; //無敵時間
int flag; //フラグ
int play_times; //残機数
int Handle[24]; //画像ハンドル
public:
//コンストラクタ
PlayerClass( void )
{
x = 200;
y = 200;
g_dir = 0;
dir = 0;
cnt = 0;
range = 5;
muteki_time = 0;
flag = 1;
play_times = 5;
}
void Load( void )
{
LoadDivGraph( "dat/img/1.png" , 24 , 6 , 4 , 384/6 , 254/4 , Handle );
}
void Draw( void )
{
cnt++;
DrawGraphF( (float)x-32 , (float)y-32 , Handle[((cnt%24)/8) + g_dir ] , TRUE );
}
int Graphic( void )
{
if( Key[KEY_INPUT_RIGHT] >= 1 ) { dir = RIGHT; g_dir = 3; }
else if( Key[KEY_INPUT_DOWN ] >= 1 ){ dir = DOWN; g_dir = 6; }
else if( Key[KEY_INPUT_LEFT ] >= 1 ){ dir = LEFT; g_dir = 0; }
else if( Key[KEY_INPUT_UP ] >= 1 ){ dir = UP; g_dir = 9; }
else{ dir = NO_MOVE; g_dir = 3; }
if( Key[KEY_INPUT_RIGHT] >= 1 && Key[KEY_INPUT_UP ] >= 1 ) dir = ( RIGHT^ UP );
if( Key[KEY_INPUT_RIGHT] >= 1 && Key[KEY_INPUT_DOWN ] >= 1 ) dir = ( RIGHT^DOWN );
if( Key[KEY_INPUT_LEFT ] >= 1 && Key[KEY_INPUT_UP ] >= 1 ) dir = ( LEFT ^ UP );
if( Key[KEY_INPUT_LEFT ] >= 1 && Key[KEY_INPUT_DOWN ] >= 1 ) dir = ( LEFT ^DOWN );
return 0;
}
void Moving( void )
{
if( dir == 0xAAAA ){ x += 10 - speed; }
if( dir == 0x55AA ){ x -= 10 - speed; }
if( dir == 0x0050 ){ y += 10 - speed; }
if( dir == 0x0005 ){ y -= 10 - speed; }
if( dir == 0xAAAF ){ x += 10 - speed; y -= 10 - speed; g_dir = 9; }
if( dir == 0xAAFA ){ x += 10 - speed; y += 10 - speed; g_dir = 6; }
if( dir == 0x55AF ){ x -= 10 - speed; y -= 10 - speed; g_dir = 15; }
if( dir == 0x55FA ){ x -= 10 - speed; y += 10 - speed; g_dir = 12; }
x > X_MAX ? x = X_MAX : NULL;
x Y_MAX ? y = Y_MAX : NULL;
y = 60 && muteki_time 120 ){
muteki_time = 0; //タイムは0に
flag = 1; //フラグを1にします。
}
}
そして次にショットのクラス
//ショットクラス
//■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
class shot_class{
double x;
double y;
double r;
double flag;
double ang;
double sp;
public:
shot_class( void )
{
x = 0;
y = 0;
r = 0;
flag = 0;
ang = 0;
sp = 0;
}
void SetData( PlayerClass p , double set_range , double set_speed , double set_angle )
{
x = p.return_data(X);
y = p.return_data(Y);
r = set_range;
sp = set_speed;
ang = get_angle( set_angle );
}
//値を得る関数、外部からメンバ変数に値を入れたい場合はこれを使ってください
double GetData( VARIABLE GetDataNumber , int get )
{
switch( GetDataNumber ){
case X : x = get; break;
case Y : y = get; break;
case RANGE : r = get; break;
case FLAG : flag = get; break;
case ANGLE : ang = get; break;
case SPEED : sp = get; break;
}
return 0;
}
//値を返す関数、外部から値を受け取ったり、値を使った判定を行いたい場合はここで
double return_data( VARIABLE return_number )
{
switch( return_number ){
case X : return x; break;
case Y : return y; break;
case RANGE : return r; break;
case FLAG : return flag; break;
case ANGLE : return ang; break;
case SPEED : return sp; break;
}
return 0;
}
void Draw( void )
{
DrawCircle( x , y , r , GetColor(0,255,0) , TRUE );
}
void Move( void )
{
x += cos( ang ) * sp;
y += sin( ang ) * sp;
}
int CheckWindowOut( void )
{
//画面外へ出たら
if( x >= FIELD_MAX_X || x = FIELD_MAX_Y || y = 1 && Key[KEY_INPUT_Z] % 5 == 0 ){
sc[i].GetData( FLAG , 1 );
}
}
if( sc[i].return_data( FLAG ) == 1 ){
sc[i].SetData( pl , 10 , 25 , 0 );
sc[i].GetData( FLAG , 2 );
break;
}
}
for( int i = 0; i < SHOT_MAX ; i++ ){
if( sc[i].return_data( FLAG ) == 2 ){
sc[i].Draw();
sc[i].Move();
}
if( sc[i].CheckWindowOut() == 1 ){
sc[i].GetData( FLAG , 0 );
}
}
//-------------------------------------------------//
}
それぞれのクラスを使う中で難しいのはプライベートメンバの中身をどうやって持ってくるか、(フラグとか)
そして外部からprivateの変数をいじるにはどうすればいいか
を考えた結果、
値を渡す関数と値を返す関数を作ってみることにしました。
//値渡し、返しの列挙型
enum VARIABLE{
X = 0 ,
Y = 1 ,
SPEED = 2 ,
RANGE = 3 ,
G_DIR = 4 ,
DIR = 5 ,
CNT = 6 ,
MUTEKI_TIME = 7 ,
FLAG = 8 ,
PLAY_TIMES = 9 ,
E_PATTERN = 10 ,
ANGLE = 11 ,
HITFLAG = 12
};
//値を返す関数
double return_data( VARIABLE return_number ){
switch( return_number ){
case X : return x; break;
case Y : return y; break;
case SPEED : return speed; break;
case RANGE : return range; break;
case G_DIR : return g_dir; break;
case DIR : return dir; break;
case CNT : return cnt; break;
case MUTEKI_TIME: return muteki_time; break;
case FLAG : return flag; break;
case PLAY_TIMES : return play_times; break;
}
return 0;
}
double get_Data( VARIABLE GetDataNumber , int get ){
switch( GetDataNumber ){
case X : return x = get; break;
case Y : return y = get; break;
case SPEED : return speed = get; break;
case RANGE : return range = get; break;
case G_DIR : return g_dir = get; break;
case DIR : return dir = get; break;
case CNT : return cnt = get; break;
case MUTEKI_TIME: return muteki_time = get; break;
case FLAG : return flag = get; break;
case PLAY_TIMES : return play_times = get; break;
}
return 0;
}
列挙型で作った番号を引数として渡し、
その番号に合わせて値を返すプログラムです。
これを使っていってメンバ変数の値を取り出したり、
操作したりします。
しかしこれもオブジェクト指向の考え方的な
プログラムといえるのだろうか、個人的にかなり疑問ではあります^^;
当たり判定を行うときとか、
敵のクラスと自機のクラスで分けると
それぞれのデータはprivateに入ってしまうので
どちらからもアクセスすることができない、
でも当たり判定のクラスを作りそこでオブジェクトの中身を操作
したい!
どうすればいいんだぁ!
というときにオブジェクトから値を返す関数を作り、
それを参照で渡せば当たり判定もできるぞヤッター!
という感じでオブジェクト指向でゲーム制作
でもオブジェクトやクラスの設計の仕方が果たしてこんな感じで
いいのだろうかと疑問に思いつつ。