C++のクラスについて2

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
クールアイス
記事: 34
登録日時: 12年前

C++のクラスについて2

#1

投稿記事 by クールアイス » 12年前

こんにちは。前回に引き続きクラスの勉強中です。
今回は、前回の最後の問題点「弾がまとめて出てしまう」という点についての質問です。

現在、自機の弾であるCPlayerShotクラスを、

コード:

CPlayerShot playerShot[PLAYER_SHOT_MAX];	//	プレイヤーショット
のように配列で用意しています。

このクラスは「移動」「描画」「呼び出し」などを含んでいるのですが、どうやらすべてが同じ動作をしてしまいます。

原因として、クラスの設計及びループの回し方に問題があるのではと考えています。
しかしそれ以外に複数の弾を管理する方法が分からず困っています。

以下コードを載せますので、「クラスのここがおかしい」「ループの回し方が変だ」など、ご指摘があったらお願いいたします。

main.cpp

コード:

#include"DxLib.h"
#include"Const.h"
#include"key.h"
#include"player.h"
#include"playerShot.h"

//	プロトタイプ宣言=========================================================
void WindowModeCheck();	//	ウィンドウモードorフルスクリーンチェック

//===========================================================================
//	メイン
//===========================================================================
int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int)
{
	//===========================================================================
	//	三大初期化
	//===========================================================================
	//WindowModeCheck();				//	ウィンドウモードorフルスクリーンチェック			
	ChangeWindowMode(true);			//	ウィンドウモード
	DxLib_Init();					//	DXライブラリ初期化
	SetDrawScreen(DX_SCREEN_BACK);	//	裏画面設定
	//===========================================================================
	
	CPlayer player;								//	プレイヤー
	CPlayerShot playerShot[PLAYER_SHOT_MAX];	//	プレイヤーショット

	for(int i = 0; i < PLAYER_SHOT_MAX; ++i)
	{
		playerShot[i].Load();		//	プレイヤーショット読み込み
		playerShot[i].Initialize();	//	プレイヤーショット初期化
	}

	player.Load();		//	プレイヤー読み込み
	player.Initialize();//	プレイヤー初期化

	//===========================================================================
	//	メインループ
	//===========================================================================
    while(ScreenFlip() == 0 && ProcessMessage() == 0 && ClearDrawScreen() == 0)
	{
		CKey::UpdateKey();									//	キー処理
		if(CKey::KeyState(KEY_INPUT_ESCAPE) == 1)	break;	//	エスケープで終了

		player.Move();				//	プレイヤー移動
		player.Draw();				//	プレイヤー描画

		for(int i = 0; i < PLAYER_SHOT_MAX; ++i)
		{
			playerShot[i].Enter(player.GetPosX(), player.GetPosY());	//	プレイヤーショットデータ登録
			playerShot[i].Move();										//	プレイヤーショット移動
			playerShot[i].Draw();										//	プレイヤーショット描画
		}
	}

	DxLib_End();// DXライブラリ終了

	return 0;	// ソフトの終了
}
playerShot.h

コード:

#pragma once

//	プレイヤーショットクラス
class CPlayerShot
{
private:
	int img;	//	画像
	int flag;	//	フラグ
	double x,y;	//	座標
	double sp;	//	移動速度

public:
	void Load();						//	読み込み
	void Initialize();					//	初期化
	void Move();						//	移動
	void Draw();						//	描画
	void Enter(double px, double py);	//	データ登録
};
playerShot.cpp

コード:

#include"DxLib.h"
#include"Const.h"
#include"key.h"
#include"playerShot.h"

//----------------------------------------------
//	プレイヤーショット読み込み
//----------------------------------------------
void CPlayerShot::Load()
{
	//	プレイヤーショット画像
	img = LoadGraph("画像/p_bullet.png");
}

//----------------------------------------------
//	プレイヤーショット初期化
//----------------------------------------------
void CPlayerShot::Initialize()
{
	x = 0.0;	//	座標x
	y = 0.0;	//	座標y
	flag = 0;	//	フラグ
	sp = 7.0;	//	移動速度
}

//----------------------------------------------
//	プレイヤーショット描画
//----------------------------------------------
void CPlayerShot::Draw()
{
	//	フラグが立っていたら
	if(flag == 1)
	{
		//	プレイヤーショット(座標は画像の中心)
		DrawRotaGraphF(x, y, 1.0f, 0.0f, img, true);
	}
}

//----------------------------------------------
//	プレイヤーショット移動
//----------------------------------------------
void CPlayerShot::Move()
{
	//	フラグが立っていたら
	if(flag == 1)
	{
		//	上へ
		y -= sp;

		//	画面外
		if(y < (0 - PLAYER_SHOT_SIZE / 2))
			flag = 0;//	フラグ初期化
	}
}

//----------------------------------------------
//	プレイヤーショットデータ登録
//----------------------------------------------
void CPlayerShot::Enter(double px, double py)
{
	//	Zキーが押された
	if(CKey::KeyState(KEY_INPUT_Z) == 1)
	{
		//	フラグが立っていなかったら
		if(flag == 0)
		{
			flag = 1;					//	フラグを立てる
			x = px;						//	プレイヤーの座標xに合わせる
			y = py - PLAYER_SHOT_SIZE;	//	プレイヤーの座標y(弾サイズ分上)に合わせる
		}
	}
}

jay
記事: 314
登録日時: 14年前
住所: 大阪市
連絡を取る:

Re: C++のクラスについて2

#2

投稿記事 by jay » 12年前

for(int i = 0; i < PLAYER_SHOT_MAX; ++i)
{
playerShot.Enter(player.GetPosX(), player.GetPosY()); // プレイヤーショットデータ登録
playerShot.Move(); // プレイヤーショット移動
playerShot.Draw(); // プレイヤーショット描画
}

ここでクラス配列の最初(i=0)から最後(i < PLAYER_SHOT_MAX)までループした上で
playerShot.Enter ってやっちゃってますよね
だから配列の最初から最後まで全部登録されてしまうのでしょう

解決策としては
1、フラグが立っていない弾の番号を探し、その番号返す関数を作り、返って来た番号の弾だけを登録するようにする

2、CPlayerShot::Enter関数に、登録が成功したかどうかを判断する戻り値を設定する
  登録が成功した場合は、その時点でメインループの弾の登録関数から抜けるようにする(これで1Fにつき一つだけ登録される仕様になる)

の2つが考えられますが、後々にアレコレと仕様を追加することを考えたら1の方がいいかと思われます。


そもそも毎フレームに全ての弾において”Zキーが押されたか”とか判断していたら、処理にとても無駄が多いので
Zキーが押されている間だけ登録関数を呼び出す、という形にした方がいいかと思われます
最後に編集したユーザー jay on 2012年9月29日(土) 18:36 [ 編集 1 回目 ]
♪僕たちは まだ森の中 抜け出そう 陽のあたる場所へ

クールアイス
記事: 34
登録日時: 12年前

Re: C++のクラスについて2

#3

投稿記事 by クールアイス » 12年前

>>jayさん

ご回答ありがとうございます。

1の方法については、龍神録での

コード:

//自機ショットの登録可能番号を返す
int search_cshot(){
        for(int i=0;i<CSHOT_MAX;i++){
                if(cshot[i].flag==0)
                        return i;
        }
        return -1;
}
これのようなもの、ということでよろしいのでしょうか?
また、その場合CPlayerShotクラスでメンバ関数として使用出来るものでしょうか?


>>そもそも毎フレームに全ての弾において”Zキーが押されたか”とか判断していたら、処理にとても無駄が多いので
Zキーが押されている間だけ登録関数を呼び出す、という形にした方がいいかと思われます

たしかによく考えたら無駄が多いですね。修正してみます。

jay
記事: 314
登録日時: 14年前
住所: 大阪市
連絡を取る:

Re: C++のクラスについて2

#4

投稿記事 by jay » 12年前

はい、そんな感じでいいと思いますよ
ただご察しの通り、そのままの形で実装するのは面倒な事になりそうなのでちょっとやり方を工夫する必要がありますね

CPlayerShotにこんな感じの関数を作ってやるといいでしょう

コード:

//flagが経っているかどうかを判定する
bool CPlayerShot::Flag_Search()
{
    //  フラグが立っていたら
    if(flag == 1)
    {
        //  trueを返す
        return true;
    }

    // 立っていなければfalseを返す
    return false;
}

これで”この弾”が登録されているかどうかを判断することが出来ます
後はクラスの外でこれを最初から順番にtrueが返ってくるまで繰り返せば…、察しのいいクールアイスさんなら後は分かると思います…w
分からなければまたお教えしますので、頑張ってみてください~
最後に編集したユーザー jay on 2012年9月29日(土) 19:39 [ 編集 1 回目 ]
♪僕たちは まだ森の中 抜け出そう 陽のあたる場所へ

クールアイス
記事: 34
登録日時: 12年前

Re: C++のクラスについて2

#5

投稿記事 by クールアイス » 12年前

>> jay さん

例のようにフラグを管理する関数を作り、条件でそのフラグだけ判断することが出来ました。
以下修正したプログラムです。

mainの修正部分

コード:

//	Zキーが押された
if(CKey::KeyState(KEY_INPUT_Z) == 1)
{
	for(int i = 0; i < PLAYER_SHOT_MAX; ++i)
	{
		//	フラグがまだ立っていなければ
		if(!playerShot[i].SearchEnter())
		{
			playerShot[i].Enter(player.GetPosX(), player.GetPosY());	//	プレイヤーショットデータ登録
			break;	//	抜ける
		}
	}
}

だんだんクラスの使い方、ループの使い方等々がわかってきました。

非常に分かりやすい解説をありがとうございました。

閉鎖

“C言語何でも質問掲示板” へ戻る