ページ 11

[C++]クラスの使い方について(STG)

Posted: 2013年5月12日(日) 08:04
by マシュマロモンスター
以前「Cでアニメーションの質問」でお世話になりましたマシュマロです。
usao氏に提示して頂いたコードを、今回クラスで扱いたいのですが、肝心なアニメーション部分がプログラム内で反映されません。
http://dixq.net/forum/viewtopic.php?f=3&t=13026
クラスなど使わずにすべてメイン関数で実行すると思い通りに動くので、クラスの書き方(使い方)が間違っているかと思います。現在のクラス宣言とメンバー関数・変数の定義はこの通りです

プレーヤークラス宣言

コード:

class PLAYER{
private:
	double x, y;//自機x、y座標
	int tilt;//傾き状態(自機のアニメーション)
	int width, height;//自機各イメージの幅、高さ
	int gh[5];//自機フレーム画像のハンドル
	double speed;//自機の速さ
	
protected:
	//移動計算処理
	void Move(double&,double&,double&,char&,char&,char&,char&);
	//描画処理
	void Draw();
	
public:
	PLAYER();
	
	void All();//上記Move関数とDraw関数の呼び出し
};
次に定義部分です。

コード:

#include "player.h"
#include "DxLib.h"
#include <math.h>

extern char Key[256];

int roll(char& left, char&right)
{//usao氏から頂いたコードを関数にまとめて見たけど正しいかどうか不安・・・

	const int MaxAbsCount=8;  //一方へのキー押し続けを数える最大数
	const int TiltTable[2*MaxAbsCount+1] = { 0,0,0,0,1,1,1,2, 2, 2,3,3,3,4,4,4,4 };
	int ShiftedTableIndex = 0;

    if( !left && !right )
    {  ShiftedTableIndex /= 2 ; } //てきとー.ShiftedTableIndex=0;とかで一気に戻すのとどっちが良いか?
    else if(right)
	{
        ShiftedTableIndex = ( ShiftedTableIndex>=0 ? ShiftedTableIndex+1 : 1 );
        if( ShiftedTableIndex>MaxAbsCount )ShiftedTableIndex=MaxAbsCount;
	}
    else if(left)
	{
        ShiftedTableIndex = ( ShiftedTableIndex<=0 ? ShiftedTableIndex-1 : -1 );
        if( ShiftedTableIndex<-MaxAbsCount )ShiftedTableIndex=-MaxAbsCount;
	}	
	//0~4までの結果値
    return TiltTable[ShiftedTableIndex+MaxAbsCount];
}

//コンストラクタ
PLAYER::PLAYER()
{
	width=320, height=240;

	LoadDivGraph("playerframeset.png",5,5,1,width,height,gh);//フレーム画像の分割ロード
        //上のroll関数の戻り値をtilt変数に格納
	tilt = roll(Key[KEY_INPUT_LEFT],Key[KEY_INPUT_RIGHT]);
	

	speed=6;

	x=300, y=400;

	
}

void PLAYER::Move(//移動処理 あまり関係ないですが・・
	double& x, double& y,
	double& speed,
	char& up, char& down,
	char& left, char& right)
{
		double s=speed/sqrt(2.0);

		if(up && left) x-=s,y-=s; else
		if(up && right)x+=s,y-=s; else
		if(down && left)x-=s,y+=s; else
		if(down && right)x+=s,y+=s;else

		if(up && !down) y-=speed; else
		if(down && !up)	 y+=speed; else
		if(left && !right)	 x-=speed; else
		if(right&& !left)	 x+=speed;	

		if(x>600){
			x=600;
		}else if(x<0){
			x=0;
		}
		if(y>800){
			y=800;
		}else if (y<0){
			y=0;
		}
    }

void PLAYER::Draw()
{
    //roll関数で得た値を描画・・・のつもり
	DrawRotaGraph((int)x,(int)y, 0.5,0.0,gh[tilt],TRUE);
}
	
void PLAYER::All()
{
	Move(x,y,speed,Key[KEY_INPUT_UP],Key[KEY_INPUT_DOWN],Key[KEY_INPUT_LEFT],Key[KEY_INPUT_RIGHT]);
	Draw();
}
関係ない部分もありますが無視してください。
メイン関数ではもちろん上のAll関数を呼び出しMove()とDraw()を順に呼んでます。
このまま実行するとエラー無しで画面表示、描画、移動処理は成功、しかし肝心なアニメーション処理がPLAYER::DrawのDrawRotaGraph関数で反映されません。
つまりgh[2]固定のままで左右キー入力しても変化(アニメーション)が起きない状態です。
やはりroll()の書き方・使い方が間違っているのでしょうか?

Re: [C++]クラスの使い方について(STG)

Posted: 2013年5月12日(日) 10:02
by softya(ソフト屋)
コンストラクタに tilt = roll(Key[KEY_INPUT_LEFT],Key[KEY_INPUT_RIGHT]);があるのが意味不明です。
これだとインスタンス生成時にしか反映されないと思いますけど。

Re: [C++]クラスの使い方について(STG)

Posted: 2013年5月12日(日) 12:49
by usao
roll() の役割は,各フレームにおけるキー入力に基づき
ShiftedTableIndexの値の状態を変更し,その値からtiltの値を決めること です.

なので,
・roll()関数はゲーム中毎フレーム呼ばれないといけないと思います.
・roll()内で ShiftedTableIndexが毎回0に初期化されてしまうのはまずいです(12行目)

Re: [C++]クラスの使い方について(STG)

Posted: 2013年5月12日(日) 22:34
by マシュマロモンスター
softyaさん、usaoさん、ご回答どうもありがとうございます。
コンストラクタに tilt = roll(Key[KEY_INPUT_LEFT],Key[KEY_INPUT_RIGHT]);を置いたのは流石にアホでしたね、訂正します。
やっぱりroll()の結果を毎フレーム更新させないと駄目ですよね。色々試してみます。
提示したコードはこちらの旧サイトhttp://etu.bituse.info/game/shot/3に載っているコードを参考にさせてもらっています。自分のコードとの決定的な違いが分かれば前に進めると思うのですが、どなたか教えて頂けないでしょうか?

Re: [C++]クラスの使い方について(STG)

Posted: 2013年5月12日(日) 22:43
by softya(ソフト屋)
元のコードは、コンストラクタ・Move()・Draw()の役割分担がちゃんと出来ています。それを見習ってください。

それとマシュマロモンスターさんのAll()メソッドが折角のMove()・Draw()の分離の意味をなくしています。
それにMove()の引数を増やす意味も分かりません。利用するキーが切り替わるなら今の仕組みだと意味が無いので別の仕組みが必要だと思います。
rollもPLAYERクラスのparaive関数で良いのでは?

こんな所だと思うのですが、質問の意図と違ったらどう違うのか説明をお願いします。

Re: [C++]クラスの使い方について(STG)

Posted: 2013年5月13日(月) 00:31
by マシュマロモンスター
アドバイスどうも有難うございます。softyaさんが仰ったようにコンストラクタ・Move()・Draw()の役割分担を見直してやったらちゃんと思い通りに動きました。

アニメーション処理はroll() という関数にまとめても、Move()内で直截処理しても同じですね(当たり前ですか・・)。ポイントはShiftedTableIndexとMaxAbsCountをメンバー変数にして、前者はコンストラクタで初期化、後者の場合staticで宣言・初期化されなければならなかったんですね。非常に勉強になりました。

書き直したコードを再度提示します。もし他にアドバイスが御座いましたら是非。

コード:

//クラス宣言
class PLAYER{
private:
	double x, y;//自機x、y座標
	int tilt;
	int width, height;//自機各イメージの幅、高さ
	int gh[5];//自機フレーム画像のハンドル
	double speed;//自機の速さ
	bool life;
	static const int MaxAbsCount=8;  //一方へのキー押し続けを数える最大数
	int ShiftedTableIndex;
private:
	//描画処理
	void Draw();
	//移動計算処理
	void Move();
	int roll();
	
	
public:
	PLAYER();
	void All();//上記Move関数とDraw関数の呼び出し
};
//定義
#include "player.h"
#include "DxLib.h"
#include <math.h>

extern char Key[256];

//コンストラクタ
PLAYER::PLAYER()
{
	width=320, height=240;
	LoadDivGraph("playerframeset.png",5,5,1,width,height,gh);//フレーム画像の分割ロード
	tilt= 2;
	speed=6;
	ShiftedTableIndex = 0;
	x=300, y=400;
	life=true;
}
void PLAYER::Move()
{
	double s=speed/sqrt(2.0);

	if(Key[KEY_INPUT_UP] && Key[KEY_INPUT_LEFT]) x-=s, y-=s; else
	if(Key[KEY_INPUT_UP] && Key[KEY_INPUT_RIGHT]) x+=s, y-=s; else
	if(Key[KEY_INPUT_DOWN] && Key[KEY_INPUT_LEFT]) x-=s, y+=s;else
	if(Key[KEY_INPUT_DOWN] && Key[KEY_INPUT_RIGHT]) x+=s, y+=s; else

	if(Key[KEY_INPUT_UP] && !Key[KEY_INPUT_DOWN]) y-=speed; else
	if(Key[KEY_INPUT_DOWN] && !Key[KEY_INPUT_UP]) y+=speed; else
	if(Key[KEY_INPUT_LEFT] && !Key[KEY_INPUT_RIGHT]) x-=speed; else
	if(Key[KEY_INPUT_RIGHT] && !Key[KEY_INPUT_LEFT]) x+=speed;

	if(x>600){
			x=600;
		}else if(x<0){
			x=0;
		}
		if(y>800){
			y=800;
		}else if (y<0){
			y=0;
		}
}
int PLAYER::roll()
{
    const int TiltTable[2*MaxAbsCount+1] = { 0,0,0,0,1,1,1,2, 2, 2,3,3,3,4,4,4,4 };

    if( !Key[KEY_INPUT_LEFT] && !Key[KEY_INPUT_RIGHT] )
    {  ShiftedTableIndex /= 2 ; } //てきとー.ShiftedTableIndex=0;とかで一気に戻すのとどっちが良いか?
    else if(Key[KEY_INPUT_RIGHT])
	{
        ShiftedTableIndex = ( ShiftedTableIndex>=0 ? ShiftedTableIndex+1 : 1 );
        if( ShiftedTableIndex>MaxAbsCount )ShiftedTableIndex=MaxAbsCount;
	}
    else if(Key[KEY_INPUT_LEFT])
	{
        ShiftedTableIndex = ( ShiftedTableIndex<=0 ? ShiftedTableIndex-1 : -1 );
        if( ShiftedTableIndex<-MaxAbsCount )ShiftedTableIndex=-MaxAbsCount;
	}	
	//0~4までの結果値
    tilt = TiltTable[ShiftedTableIndex+MaxAbsCount];

	return tilt;
}


void PLAYER::Draw()
{	
	if(life){
	DrawRotaGraph((int)x,(int)y, 0.5,0.0,gh[tilt],TRUE);
	}
}	
void PLAYER::All()
{
	roll();
	Move();	
	Draw();
}

Re: [C++]クラスの使い方について(STG)

Posted: 2013年5月13日(月) 00:38
by softya(ソフト屋)
All()は前にも書いた通り止めたほうが良いですね。 Move(); とDraw();の分離の意義を無くします。あとご自身で書いている通りroll();はMove(); の一部だと思います。
あとstaticが登場しないといけないとしたらインスタンスの管理が間違っています。
訂正。定数なんですね。

【補足】
staticにしなくても次のような方法があります。

初期化子リストを使う。

コード:

class test{
	const int a;
public:
	test() : a(3) {};
};

Re: [C++]クラスの使い方について(STG)

Posted: 2013年5月13日(月) 01:36
by マシュマロモンスター
softyaさん、どうも有難うございます。そちらの方法もメモっておきます。すごく勉強になりました!

Re: [C++]クラスの使い方について(STG)

Posted: 2013年5月13日(月) 16:49
by usao
解決になっていますが,MaxAbsCountはクラスメンバにする理由が何か特別にあるのでなければ

コード:

int PLAYER::roll()
{
    const int MaxAbsCount=8;  //一方へのキー押し続けを数える最大数
    const int TiltTable[2*MaxAbsCount+1] = { 0,0,0,0,1,1,1,2, 2, 2,3,3,3,4,4,4,4 };
    ....
のようにしておく方が自然だと思います.
この値は,roll()内の処理でしか用いられないので.
(特に,Tilttable[]の宣言と離れたところにあると見通しが悪いと思う.)