ステージ間ワープを作りましたが、自信がないので見てください。

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

ステージ間ワープを作りましたが、自信がないので見てください。

#1

投稿記事 by イマダニ » 4年前

お久しぶりです。

開発環境:DXライブラリ、 VisualStudio Express 2013、 言語はC++

で、デモンズソウルのようなステージ間ワープを作りました。
2Dの横スクロールアクションゲームです。

具体的にどのような感じかというと、まずワープには
ワープ先を選べる「拠点からのワープ」と、拠点へただ帰る「各ステージからのワープ」の二つあり、

「拠点からのワープ」

①ワープポイントでメニューを開いて、ワープしたいステージを選択。

②エフェクトとかアニメーションしながら、画面がフェードアウト。

③ステージロード。

④フェードインして、エフェクトとかアニメーションしながら、ステージに到着。

⑤アクション


「各ステージからのワープ」

①ワープポイントでメニューを開いて、「拠点に帰る」項目を選択。

②エフェクトとかアニメーションしながら、画面がフェードアウト。

③ステージロード。

④フェードインして、エフェクトとかアニメーションしながら、拠点に到着。

⑤アクション

まあ大体こんな感じです。
で、その処理をコードに書き表したのがこちら。


※以下のコードは簡単にまとめた疑似コードです。ありのままのコードを書くと、とんでもない量になってしまうので、そうしました。


GameMng.h(ゲームメインの管理クラスの宣言が書かれたヘッダ)

コード:

#pragma once
#include "Singleton.h"
#include "StateMng.h"
#include "Stage.h"
#include "WarpPoint.h"
#include "Player.h"

//ゲームメインの管理クラスのシングルトンクラス(これを介してプレイヤーのあたり判定やら敵のデータやらをやり取りする)
class GameMng : public Singleton<GameMng>{
	friend Singleton<GameMng>;
public:
	//ゲームの状態
	enum{
		GAMESTATE_STAGEWARP_OUT,  //ステージワープ移動
		GAMESTATE_STAGEWARP_IN,   //ステージワープ到着
		GAMESTATE_STAGELOAD,	  //ステージロード処理
		GAMESTATE_ACTION,		  //アクション
		GAMESTATE_WARPMENU,		  //ワープメニューオープン

		GAMESTATE_MAX,			  //ゲーム本編の状態の数
	};
	//ステージの種類
	enum{
		STAGE_BASE,				  //拠点
		STAGE_1,
		STAGE_2,

		STAGETYPE_MAX,			  //ステージの数
	};

private:
	//=========
	//各クラス(ここに敵とかもある感じ)
	//=========
	Stage *stage;   //ステージ
	WarpPoint *warp;//ワープポイント
	Player *player; //プレイヤー

private:
	//=========
	//各状態
	//=========
	StateMng *GameState;//ゲーム本体の状態
	StateMng *NowStage; //今いるステージ
	StateMng *NextStage;//次のステージ

private:
	//===========================
	//画面効果
	//===========================
	//暗転
	int Black_Alpha;  //フェード用の黒画面の透明度
	bool FadeIn_Flag; //フェードインするよー
	bool FadeOut_Flag;//フェードアウトするよーフラグ
	void DisplayEffect_FadeInBlack(); //実際の処理
	void DisplayEffect_FadeOutBlack();

public:
	void Set_FadeInFlag(); //フェードイン
	void Set_FadeOutFlag();//フェードアウト

private:
	GameMng(); //初期化
	~GameMng();

	void GameMng_StageLoad();    //ステージロードパート
	void GameMng_StageWarp_Out();//ワープ開始
	void GameMng_StageWarp_In(); //ワープ到着
	void GameMng_Action();		 //アクションパート(プレイヤーがステージ攻略とか)
	void GameMng_WarpMenuOpen(); //ワープポイントでのメニューオープンパート(メニュー項目のワープでステージ間ワープ)

public:
	//===========================
	//ステージのゲッター
	//===========================
	bool StageHitX(int state, Rect rx, bool way);//あたり判定
	bool StageHitY(int state, Rect ry);

	bool SlopeHitY_RDOWN(int sx, int sy);		 //坂とのあたり判定
	bool SlopeHitY_LDOWN(int sx, int sy);
	double Get_SlopeNowY(int x);			     //乗ってる坂のY座標
public:
	//===========================
	//ワープポイントのゲッター
	//===========================
	int WarpPointGetPos(int type);//ワープポイントの位置
public:
	//===========================
	//プレイヤーのゲッター
	//===========================
	int Player_GetState();		   //プレイヤーの状態
	int Player_GetPos(int type);   //プレイヤーの位置
	int Player_GetHPFuel();        //HP残量
	Rect Player_GetHit(int type);  //あたり判定
	//===========================
	//プレイヤーのセッター
	//===========================
	void Player_SetHPPlus(int point); //HP足します
	void Player_SetHPMinus(int point);//HP引きます
public:
	//===========================
	//ゲームメインのゲッター
	//===========================
	int Get_StageType();//現在のステージ
	//===========================
	//ゲームメインのセッター
	//===========================
	void Set_Change_GAMEMAIN_STATE(int sstate);//ゲームの状態
	void Set_Change_GAMEMAIN_NOWSTAGE(int sstate);//今のステージ
	void Set_Change_GAMEMAIN_NEXTSTAGE(int sstate);//次のステージ

	void Main();//メイン
};


GameMng.cpp(ゲームメインの管理クラスの処理が書かれたcpp)

コード:

#include <DxLib.h>
#include "Camera.h"
#include "SystemMng.h"
#include "GameMng.h"

//==================
//コンストラクタ
//==================
GameMng::GameMng(){
	//ゲームの状態初期化
	GameState = DEBUG_NEW StateMng(GAMESTATE_MAX);
	//今のステージがどこか初期化
	NowStage  = DEBUG_NEW StateMng(STAGETYPE_MAX);
	//次のステージがどこか初期化
	NextStage = DEBUG_NEW StateMng(STAGETYPE_MAX);

	//ステージ初期化
	stage = DEBUG_NEW Stage(STAGE_BASE);/*DEBUG_NEWはnewです。メモリリーク確認のためこうしてます。*/
	//ワープポイント初期化
	warp  = DEBUG_NEW WarpPoint("data/StageData/WarpPoint/BaseStage.csv");

	//今現在のステージを拠点に
	NowStage->ChangeState(STAGE_BASE);
	NextStage->ChangeState(STAGE_BASE);

	//プレイヤー初期化(ワープポイントの位置を初期位置に)
	player = DEBUG_NEW Player(warp->GetPos(Light::X) - 38, warp->GetPos(Light::Y, i));

	//フェードインとか
	Black_Alpha = 0;
	FadeIn_Flag = false;
	FadeOut_Flag = false;

	//ゲームの状態を"アクションパート"に
	GameState->ChangeState(GAMESTATE_ACTION);
}

//==================
//デストラクタ
//==================
GameMng::~GameMng(){
	if (GameState != nullptr){
		delete GameState;
		GameState = nullptr;
	}
	if (NowStage != nullptr){
		delete NowStage;
		NowStage = nullptr;
	}
	if (NextStage != nullptr){
		delete NextStage;
		NextStage = nullptr;
	}

	if (stage != nullptr){
		delete stage;
		stage = nullptr;
	}
	if (warp != nullptr){
		delete warp;
		warp = nullptr;
	}
	if (player != nullptr){
		delete player;
		player = nullptr;
	}
}

//==================
//状態セッター
//==================
//ゲームの状態
void GameMng::Set_Change_GAMEMAIN_STATE(int sstate){
	GameState->ChangeState(sstate);
}
//現在のステージ
void GameMng::Set_Change_GAMEMAIN_NOWSTAGE(int sstate){
	NowStage->ChangeState(sstate);
}
//次のステージ
void GameMng::Set_Change_GAMEMAIN_NEXTSTAGE(int sstate){
	NextStage->ChangeState(sstate);
}


//==================
//ステージロード中
//==================
void GameMng::GameMng_StageLoad(){
	//ステージ破棄
	delete stage;
	stage = nullptr;
	//ワープポイント破棄
	delete warp;
	warp = nullptr;

	//ワープ先、次のステージに合わせて各オブジェクトを確保
	switch (NextStage->GetNowState()){
	case STAGE_BASE://拠点
		//拠点ステージロード
		stage = new Stage(STAGE_BASE);
		//ワープポイントロード
		warp  = new WarpPoint("data/StageData/WarpPoint/BaseStage.csv");

		//プレイヤーの位置変更(ワープポイントの位置を初期位置に)
		player->Reload(warp->GetPos(Warp::X) - 38, warp->GetPos(Warp::Y) + 96);

		//フェードイン開始
		FadeIn_Flag = true;
		//ワープ到着に
		GameState->ChangeState(GAMESTATE_STAGEWARP_IN);
		break;
	case STAGE_1:
		//---省略(上とおんなじ---
		break;
	case STAGE_2:
		//---省略---
		break;
	}

	//===================
	//フェードイン描画
	//===================
	SetDrawBlendMode(DX_BLENDMODE_ALPHA, Black_Alpha);
	DrawBox(0, 0, SCREEN_W, SCREEN_H, GetColor(0, 0, 0), TRUE);
	SetDrawBlendMode(DX_BLENDMODE_NOBLEND, 0);
}

//================================================
//フェードイン/フェードアウトの開始フラグセット
//================================================
void GameMng::Set_FadeInFlag(){
	FadeIn_Flag = true;
}
void GameMng::Set_FadeOutFlag(){
	FadeOut_Flag = true;
}

//================================
//フェードイン/フェードアウト動作
//================================
//フェードイン
void GameMng::DisplayEffect_FadeInBlack(){
	if (FadeIn_Flag){
		if (Black_Alpha > 0){
			Black_Alpha -= 10;
		}
		if (Black_Alpha < 0 || Black_Alpha == 0){
			FadeIn_Flag = false;
			Black_Alpha = 0;
		}
	}
}
//フェードアウト
void GameMng::DisplayEffect_FadeOutBlack(){
	if (FadeOut_Flag){
		if (Black_Alpha < 255){
			Black_Alpha += 10;
		}
		if (Black_Alpha > 255 || Black_Alpha == 255){
			FadeIn_Flag = false;
			Black_Alpha = 255;
		}
	}
}

void GameMng::GameMng_Respawn(){
}

//===================
//ワープ開始
//===================
void GameMng::GameMng_StageWarp_Out(){
	//================================
	//フェードイン/フェードアウト動作
	//================================
	DisplayEffect_FadeInBlack();
	DisplayEffect_FadeOutBlack();

	//=================
	//各動作
	//=================
	warp->Update();
	player->Warp_Out();
	//=================
	//ステージ切り替え
	//=================
	if (Black_Alpha == 255){//画面が真っ黒になったら
		FadeOut_Flag = false;//フェードアウト終わったよ
		switch (NextStage->GetNowState()){//次のステージを
		//今現在いるステージにする
		case STAGE_BASE:
			Set_Change_GAMEMAIN_NOWSTAGE(STAGE_BASE);
			break;
		case STAGE_1:
			Set_Change_GAMEMAIN_NOWSTAGE(STAGE_1);
			break;
		case STAGE_2:
			Set_Change_GAMEMAIN_NOWSTAGE(STAGE_2);
			break;
		}
		//ゲームの状態をステージロード中に
		GameState->ChangeState(GAMESTATE_STAGELOAD);
	}

	//=================
	//各描画
	//=================
	stage->Draw();
	warp->Draw();
	player->Draw();
	stage->PlayerFrontDraw();

	//===================
	//フェードアウト描画
	//===================
	SetDrawBlendMode(DX_BLENDMODE_ALPHA, Black_Alpha);
	DrawBox(0, 0, SCREEN_W, SCREEN_H, GetColor(0, 0, 0), TRUE);
	SetDrawBlendMode(DX_BLENDMODE_NOBLEND, 0);
}

//===================
//ワープ到着
//===================
void GameMng::GameMng_StageWarp_In(){
	//================================
	//フェードイン/フェードアウト動作
	//================================
	DisplayEffect_FadeInBlack();
	DisplayEffect_FadeOutBlack();

	//=================
	//各動作
	//=================
	warp->Update();
	player->WarpIn_Move();

	//=================
	//フェードイン動作
	//=================
	if (Black_Alpha == 0){
		FadeIn_Flag = false;
		player->Warp_In();
	}

	//===========
	//ゲーム描画
	//===========
	stage->Draw();
	warp->Draw();
	player->Draw();
	stage->PlayerFrontDraw();

	//=================
	//フェードイン描画
	//=================
	SetDrawBlendMode(DX_BLENDMODE_ALPHA, Black_Alpha);
	DrawBox(0, 0, SCREEN_W, SCREEN_H, GetColor(0, 0, 0), TRUE);
	SetDrawBlendMode(DX_BLENDMODE_NOBLEND, 0);
}

//===================
//アクションパート
//===================
void GameMng::GameMng_Action(){
	//===========
	//動作
	//===========
	warp->Update();
	player->Move();

	//===========
	//ゲーム描画
	//===========
	stage->Draw();
	warp->Draw();
	player->Draw();
	stage->PlayerFrontDraw();

	//==========================================
	//HPゲージやメニューなどインタ―フェース描画
	//==========================================
	player->GaugeDraw();
}

//===================
//ワープメニューオープン(プレイヤーキャラはこの時動かないので描画だけ)
//===================
//基盤
void GameMng::GameMng_WarpMenuOpen(){
	//ワープポイント更新
	warp->Update();

	//===========
	//ゲーム描画
	//===========
	stage->Draw();
	warp->Draw();
	player->Draw();/*描画だけする。キャラは動かない*/
	stage->PlayerFrontDraw();

	//メニュー描画
	warp->MenuMove();/*ここでワープ先を決める処理(Set_Change_GAMEMAIN_NEXTSTAGE(int sstate)とかを使ってる)*/
	warp->MenuDraw();
}

//===================
//メイン
//===================
void GameMng::Main(){
	//=============
	//状態の更新
	//=============
	GameState->UpdateState();
	NowStage->UpdateState();
	NextStage->UpdateState();

	//===============
	//各状態での動作
	//===============
	switch (GameState->GetNowState()){
	case GAMESTATE_STAGEWARP_OUT://ワープ開始
		GameMng_StageWarp_Out();
		DrawFormatString(0, 15, GetColor(255, 255, 255), "STAGEWARP_OUT");
		break;
	case GAMESTATE_STAGEWARP_IN://ワープ到着
		GameMng_StageWarp_In();
		DrawFormatString(0, 15, GetColor(255, 255, 255), "STAGEWARP_In");
		break;
	case GAMESTATE_STAGELOAD://ステージロード
		GameMng_StageLoad();
		DrawFormatString(0, 15, GetColor(255, 255, 255), "STAGELOAD");
		break;
	case GAMESTATE_ACTION://アクション
		GameMng_Action();
		DrawFormatString(0, 15, GetColor(255, 255, 255), "GAMESTATE_ACTION");
		break;
	case GAMESTATE_WARPMENU://ワープメニューオープン時
		GameMng_WarpMenuOpen();
		DrawFormatString(0, 15, GetColor(255, 255, 255), "GAMESTATE_WARPMENU");
		break;
	}
}
/*ゲッター*/
//===========================
//ステージ
//===========================
int GameMng::Get_StageType(){
	return NowStage->GetNowState();
}
bool GameMng::StageHitX(int state, Rect rx, bool way){
	return stage->StageHitX(state, rx, way);
}
bool GameMng::StageHitY(int state, Rect ry){
	return stage->StageHitY(state, ry);
}

bool GameMng::SlopeHitY_RDOWN(int sx, int sy){
	return stage->SlopeHitY_RDOWN(sx, sy);
}
bool GameMng::SlopeHitY_LDOWN(int sx, int sy){
	return stage->SlopeHitY_LDOWN(sx, sy);
}
double GameMng::Get_SlopeNowY(int x){
	return stage->Get_SlopeNowY(x);
}

//===========================
//ワープポイント
//===========================
int GameMng::WarpPointGetPos(int type, int i){
	return warp->GetPos(type, i);
}

//===========================
//プレイヤー
//===========================
int GameMng::Player_GetState(int type){
	return player->GetState(type);
}
int GameMng::Player_GetPos(int type){
	return player->GetPos(type);
}
int GameMng::Player_GetHPFuel(){
	return player->Get_HPFuel();
}
Rect GameMng::Player_GetHit(int type){
	return player->GetHit(type);
}

void GameMng::Player_SetHPMinus(int point){
	player->Set_HPMinus(point);
}
void GameMng::Player_SetHPPlus(int point){
	player->Set_HPPlus(point);
}

ざっとこんな感じなのですが、どうでしょうか?
ステージファイル読み込み部分(C++なのにfopenでtxtファイルを読み込んでる)だとか、見てほしい場所はまだあるのですが、
一気に張るのもあれなので、ひとまず全てのベースとなるこれから。
一番気になるというか自信のない部位が

//==================
//ステージロード中
//==================
GameMng_StageLoad()

の部分で、このステージロード方法がほんとうにこれでいいのかいまいち自信が持てません。
今のところ問題なく動作はしてはいますが、
これからステージを追加していく前に、一度本当に問題ないかを確認したいと思い、質問しに来ました。
全部追加してから問題に気づいた、となったら地獄を見る羽目になると思うので……

俺ならこうするとか、これじゃロード長くなるぞとか、なんでもいいので、気軽に書き込んでください。

よろしくお願いします。

アバター
h2so5
副管理人
記事: 2212
登録日時: 8年前
住所: 東京
連絡を取る:

Re: ステージ間ワープを作りましたが、自信がないので見てください。

#2

投稿記事 by h2so5 » 4年前

ステージロード方法についてはステージが少ないなら一応これでもいいんじゃないでしょうか。
ステージが多いならハードコードではダメですね。

どちらかというと他の部分が気になります。
  • new/deleteではなくスマートポインタを使ってください。
  • Facadeのつもりなのか知れませんが、GameMngクラスが大きすぎます。これでは大量のグローバル関数を定義しているのと同じでクラスの意味がまるでないですよね。
  • 描画処理を分離しましょう。フェードイン処理がGameMng_StageLoadにあるのはおかしいです。

イマダニ
記事: 145
登録日時: 7年前

Re: ステージ間ワープを作りましたが、自信がないので見てください。

#3

投稿記事 by イマダニ » 4年前

new/deleteではなくスマートポインタを使ってください。
ひとまずunique_ptrをつかってみました。とても便利ですね。
で、使ってみて、思ったのが、これをロードの部分に用いる場合。

※下のコードは実際に試したものではなく軽い例です。

コード:

//==================
//ステージロード中
//==================
void GameMng::GameMng_StageLoad(){
/*この部分が消えて
    //ステージ破棄
    delete stage;
    stage = nullptr;
    //ワープポイント破棄
    delete warp;
    warp = nullptr;
*/
 
void GameMng::GameMng_StageLoad(){
    //ワープ先、次のステージに合わせて各オブジェクトを確保
    switch (NextStage->GetNowState()){
    case STAGE_BASE://拠点
        //拠点ステージロード
        stage.reset(new Stage(STAGE_BASE));/*deleteせずにいきなり確保しておk*/
こういう風にできるという解釈であってるんですよね?

GameMngクラスが大きすぎます。
これ最近の悩みでして……
小さいゲーム、というかまだ1ステージだけの頃、オブジェクトがまだ少ない頃はこれでよかったんですが(よくないけど)、
最近、ゲームのベースが出来上がったおかげで、オブジェクトが当時より増え、
このGameMngクラスを介してやり取りするものがものすごく多くなって、とても困ってるんです。

白状すると、最初に書き込んだ疑似コードの 倍 はあります。ぶくぶく太っちゃって、ものすごいデブです。

敵、プレイヤー、ワープポイント、ステージ、ステージギミックなどなど、
ゲーム上の全てのオブジェクトが、お互いの情報をここでやり取りしちゃってます。

GameMngクラスというデブ駅を、各オブジェクトのゲッターセッターがぞろぞろと行き来してる感じで、朝の通勤ラッシュみたいになっちゃってます。

で、これからちょっと大掃除しようと
「この際だからいったん全部書き直してみよう」、と思ったのですが、、、、

掃除するものが多いうえに、設計の知識がほとんどないため、どこから手を付けたらいいか分からず、ものすごい戸惑っています。

なのでよろしければ、アドバイスをいただけないでしょうか。
いったん、全体の設計を見直したいです。
よろしくお願いします。

ステージが多いならハードコードではダメですね。
ステージは拠点も含めて9ステージあります。

sp6章 メニュー画面の作り方(C++編)
http://dixq.net/g/sp_06.html

ここを参考にちょっと書き換えてみようかと思いましたが、ゲームパートのベースであるGameMngクラスを書き直したい。
というか設計自体を見直したいので、いったん後回しにします。

描画処理を分離しましょう。フェードイン処理がGameMng_StageLoadにあるのはおかしいです。
ごめんなさい。適当やってました。

イマダニ
記事: 145
登録日時: 7年前

Re: ステージ間ワープを作りましたが、自信がないので見てください。

#4

投稿記事 by イマダニ » 4年前

なのでよろしければ、アドバイスをいただけないでしょうか。
すいません、やっぱりいいです。
なんだか自力でできそうです。
できたら貼ります。

イマダニ
記事: 145
登録日時: 7年前

Re: ステージ間ワープを作りましたが、自信がないので見てください。

#5

投稿記事 by イマダニ » 4年前

行き詰ったので質問します

コード:

class Player{
private:
	int m_x, m_y;		//位置
	double m_vx, m_vy;  //加速度
	int m_state;		//状態
	Rect m_Hit;		//あたり判定
}
class Stage{
private:
	StageData m_Data; //ステージデータ
}
この二つのクラスのprivateメンバを


①Stageクラスの『m_Data』とPlayerクラスの『m_Hit』を使って、ステージとのあたり判定を確認する。

②上記の判定で、当たってる場合は、Playerクラスの『m_x』『m_y』の位置と、『m_vx』『m_vy』の加速度と、『m_state』の状態を、変更する。


上記の流れで各クラスのメンバをやり取りする場合、


①Stageクラスのほうで、『StageHit(Rect Hit)』というあたり判定を引数とし、その引数『Rect Hit』とメンバの『m_Data』を基に当たっているかどうかを戻り値で返す関数を作り、それをPlayerクラスの中で使い、上記の処理を行う

②どちらのクラスのメンバにアクセスできるクラス内で、上記の処理を行う


どちらがいいのでしょうか?
今回の質問をする前は①の方法でやっていました。
『StageHit(Rect Hit)』をGameMngを通して、Playerに取得させ、上記の処理を行っていました。
この方法だとGameMngがどんどん大きくなってしまうので、
PlayerクラスとStageクラス、両方のメンバにアクセスでき、その二つを用いた上記処理だけを行うクラス
『PlayerStage_Collisionクラス』を作ろうと思ったのですが、

PlayerクラスとStageクラスのメンバにどうアクセスするのか?

『PlayerStage_Collisionクラス』をPlayerクラスとStageクラスのフレンドクラスにすればいいのか?
それとも自分が知らない別の方法があるのか?
ゲッターとセッターなんか使ったらprivateにした意味ないし。。。
そうなると、ゲッターとセッターはそもそも使っちゃいけない禁忌なんじゃね?
じゃあそれを頻繁に使ってた俺はいったい。。。
ああもう、どうメンバをやり取りしたらいいんだ……

と、ひどく悩み、なかなか方針が決まりません。
アドバイスお願いします。

Rittai_3D
記事: 522
登録日時: 6年前

Re: ステージ間ワープを作りましたが、自信がないので見てください。

#6

投稿記事 by Rittai_3D » 4年前

class Stage内にclass Playerのインスタンスを作ってインターフェースクラスを
使ってやり取りするのはどうでしょう。

コード:

class Interface
{
public :
    // いろいろ省略
    virtual bool IsHit() const = 0;
}

class Player
{
    Interface* m_pInter;
    Rect m_Rect;
public:
    void hoge()
    {
        if( m_pInter->IsHit() ) // 当たった時の処理
    }
    Rect getRect() const { return m_Rect; }
};

class StageMgr : public Interface
{
    Player* m_pPlayer;
public:

    virtual bool IsHit() const override
    {
        // 実装
        m_pPlayer->getRect(); // プレイヤーのrectがもらえる
    }
};
こんな感じです。コンパイルはしていません。
最後に編集したユーザー Rittai_3D on 2015年6月13日(土) 09:55 [ 編集 1 回目 ]
初心者です

イマダニ
記事: 145
登録日時: 7年前

Re: ステージ間ワープを作りましたが、自信がないので見てください。

#7

投稿記事 by イマダニ » 4年前

3Dさん。返信ありがとうございます。
ひとつわからないので質問させてください。

コード:

class Player
{
    Interface* m_pInter;/*ここ*/
    Rect m_Rect;
public:
    void hoge()
    {
        if( m_pInter->IsHit() ) // 当たった時の処理/*ここ*/
    }
    Rect getRect() const { return m_Rect; }
};
/*ここ*/の部分どういう仕組みになっているんでしょうか?
どうやってインターフェースクラスの純粋仮想関数を使ってるんですか?

イマダニ
記事: 145
登録日時: 7年前

Re: ステージ間ワープを作りましたが、自信がないので見てください。

#8

投稿記事 by イマダニ » 4年前

ひとまず

コード:

/*インターフェースクラス*/
/*PlayerStage_InterFace.h*/
#pragma once

//プレイヤーとステージのやり取りをするためのインターフェースクラス
class PlayerStage_InterFace{
public:
	//あたり判定
	enum{
		e_HITTYPE_NONE,				//なんもねえよ
		e_HITTYPE_GROUND,			//ただの地面
		e_HITTYPE_SLOPE_R_DOWN,		//右坂道地上
		e_HITTYPE_SLOPE_L_DOWN,		//左坂道地上
		e_HITTYPE_SLOPE_R_UP,		//右坂道天井
		e_HITTYPE_SLOPE_L_UP,		//左坂道天井

		e_HITTYPE_MAX,
	};
	enum{
		e_HITWAY_LEFT,
		e_HITWAY_RIGHT,
		e_HITWAY_UP,
		e_HITWAY_DOWN,

		e_HITWAY_MAX,
	};
public:
	//あたり判定
	virtual bool GroundHit_RX(int type) const = 0;
	virtual bool GroundHit_LX(int type) const = 0;
	virtual bool GroundHit_UY(int type) const = 0;
	virtual bool GroundHit_DY(int type) const = 0;
};

コード:

/*Stage.h*/
//ステージ管理クラス
class Stage_Mng : public Stage_Changer, public PlayerStage_InterFace{
private:
	Player *m_player;//プレイヤーのポインタ
public:
	Stage_Mng();

	void Initialize(Player *player);
	void Update();
	void Draw();

	void ChangeScene(e_StageScene NextScene) override;
public:
//
	virtual bool GroundHit_RX(int type) const override;
	virtual bool GroundHit_LX(int type) const override;
	virtual bool GroundHit_UY(int type) const override;
	virtual bool GroundHit_DY(int type) const override;
};

コード:

/*Stage.Cpp*/
//ステージ管理クラス
class Stage_Mng : public Stage_Changer, public PlayerStage_InterFace{
private:
	Player *m_player;//プレイヤーのポインタ
[省略]
}

コード:

/*Player.Cpp*/

//初期化
void Player::Initialize(PlayerStage_InterFace *stageinter){//インターフェースクラスのポインタ引数
	m_stageInter = stageinter;//いれる
}
//ここであたり判定関数を使う
void Player::Hoge(){
        m_stageInter ->GroundHit_DY(PlayerStage_InterFace::e_HITTYPE_GROUND);
}

コード:

/*GameScene_Base.Cpp*/

/*GameMngクラスを新C言語のシーン管理を参考に書き換え。*/
/*ゲームの状態遷移をシーンとして管理。そのシーンの基底クラス。これ継承する*/
/*アクションシーンだとかステージロードシーンとかの基底クラスのコンストラクタ*/
GameScene_Base::GameScene_Base(GameScene_Changer *changer){
	m_stage.reset(new Stage_Mng());//ステージ確保
	m_player.reset(new Player());//プレイヤー確保

       /*初期化。いずれGameScene_Baseクラスにも*/
       /*初期化関数、Initialize()を作り、そこに書く予定*/
	m_player->Initialize(m_stage.get());/*インターフェースクラスを継承したStageMngのポインタを引数として受け取る*/
	m_stage->Initialize(m_player.get());/*プレイヤーを受け取る*/
}

イマダニ
記事: 145
登録日時: 7年前

Re: ステージ間ワープを作りましたが、自信がないので見てください。

#9

投稿記事 by イマダニ » 4年前

ひとまず

コード:

/*インターフェースクラス*/
/*PlayerStage_InterFace.h*/
#pragma once

//プレイヤーとステージのやり取りをするためのインターフェースクラス
class PlayerStage_InterFace{
public:
	//あたり判定
	enum{
		e_HITTYPE_NONE,				//なんもねえよ
		e_HITTYPE_GROUND,			//ただの地面
		e_HITTYPE_SLOPE_R_DOWN,		//右坂道地上
		e_HITTYPE_SLOPE_L_DOWN,		//左坂道地上
		e_HITTYPE_SLOPE_R_UP,		//右坂道天井
		e_HITTYPE_SLOPE_L_UP,		//左坂道天井

		e_HITTYPE_MAX,
	};
	enum{
		e_HITWAY_LEFT,
		e_HITWAY_RIGHT,
		e_HITWAY_UP,
		e_HITWAY_DOWN,

		e_HITWAY_MAX,
	};
public:
	//あたり判定
	virtual bool GroundHit_RX(int type) const = 0;
	virtual bool GroundHit_LX(int type) const = 0;
	virtual bool GroundHit_UY(int type) const = 0;
	virtual bool GroundHit_DY(int type) const = 0;
};

コード:

/*Stage.h*/
//ステージ管理クラス
class Stage_Mng : public Stage_Changer, public PlayerStage_InterFace{
private:
	Player *m_player;//プレイヤーのポインタ
public:
	Stage_Mng();

	void Initialize(Player *player);
	void Update();
	void Draw();

	void ChangeScene(e_StageScene NextScene) override;
public:
//
	virtual bool GroundHit_RX(int type) const override;
	virtual bool GroundHit_LX(int type) const override;
	virtual bool GroundHit_UY(int type) const override;
	virtual bool GroundHit_DY(int type) const override;
};

コード:

/*Stage.Cpp*/
//ステージ管理クラス
class Stage_Mng : public Stage_Changer, public PlayerStage_InterFace{
private:
	Player *m_player;//プレイヤーのポインタ
[省略]
}

コード:

/*Player.Cpp*/

//初期化
void Player::Initialize(PlayerStage_InterFace *stageinter){//インターフェースクラスのポインタ引数
	m_stageInter = stageinter;//いれる
}
//ここであたり判定関数を使う
void Player::Hoge(){
        m_stageInter ->GroundHit_DY(PlayerStage_InterFace::e_HITTYPE_GROUND);
}

コード:

/*GameScene_Base.Cpp*/

/*GameMngクラスを新C言語のシーン管理を参考に書き換え。*/
/*ゲームの状態遷移をシーンとして管理。そのシーンの基底クラス。これ継承する*/
/*アクションシーンだとかステージロードシーンとかの基底クラスのコンストラクタ*/
GameScene_Base::GameScene_Base(GameScene_Changer *changer){
	m_stage.reset(new Stage_Mng());//ステージ確保
	m_player.reset(new Player());//プレイヤー確保

       /*初期化。いずれGameScene_Baseクラスにも*/
       /*初期化関数、Initialize()を作り、そこに書く予定*/
	m_player->Initialize(m_stage.get());/*インターフェースクラスを継承したStageMngのポインタを引数として受け取る*/
	m_stage->Initialize(m_player.get());/*プレイヤーを受け取る*/
}

イマダニ
記事: 145
登録日時: 7年前

Re: ステージ間ワープを作りましたが、自信がないので見てください。

#10

投稿記事 by イマダニ » 4年前

途中で二度も送信してしまいました!すいません!
大体こんな感じでいいんでしょうか?

Rittai_3D
記事: 522
登録日時: 6年前

Re: ステージ間ワープを作りましたが、自信がないので見てください。

#11

投稿記事 by Rittai_3D » 4年前

http://melpon.org/wandbox/permlink/NLtG2eMDfGNd3YES
汚いですがコンパイルが通る形まで持って行きました。(wandboxでコンパイルが通ることを確認済みです)
コードを一応spoilタグでくくっておきます。
► スポイラーを表示
イマダニ さんが書きました:3Dさん。返信ありがとうございます。
ひとつわからないので質問させてください。

コード:

class Player
{
    Interface* m_pInter;/*ここ*/
    Rect m_Rect;
public:
    void hoge()
    {
        if( m_pInter->IsHit() ) // 当たった時の処理/*ここ*/
    }
    Rect getRect() const { return m_Rect; }
};
/*ここ*/の部分どういう仕組みになっているんでしょうか?
どうやってインターフェースクラスの純粋仮想関数を使ってるんですか?
すいません、スマホから頑張って省略しながら入力したせいか、そのあたりがまるっと抜けていますね。
(上に張ったコードでいうと)class Player のコンストラクタの引数で渡しています。
class Interface を継承したクラスならどれでも渡せるようになっています。

純粋仮想関数を持ったクラスはそのままではインスタンスが作成できません。関数の中身が定義されていませんから、どう振るまえばよいのか分からないのです。
純粋仮想関数をオーバーライドすれば中身が定義されているのでインスタンスを作ることができます。

コード:

class A
{
public:
    virtual void Hoge() = 0;
};

// Error! Hoge の振る舞いが分からない
A a;

class B : public A
{
public:
    // 何かの行動をするように定義する
    virtual void Hoge() override { some_action1(); }
};

// OK! Hogeの中身が分かる
B b;

class C : public A
{
public:
    // 何かの行動をするように定義する
    virtual void Hoge() override { some_action2(); }
};

// OK!
C c;
これをポインタで宣言し、アップキャスト(参考)を使うとこういうことができます。

コード:

// OK
A* pB = new B;
A* pC = new C; 
これと同じことをしているわけです。
初心者です

Rittai_3D
記事: 522
登録日時: 6年前

Re: ステージ間ワープを作りましたが、自信がないので見てください。

#12

投稿記事 by Rittai_3D » 4年前

イマダニ さんが書きました:途中で二度も送信してしまいました!すいません!
大体こんな感じでいいんでしょうか?
Stage.cppがどうなっているか分かりません。
また、Stage_Mgrですべての当り判定をするのではなく、ステージクラスごとに当り判定を持たせる方がよいのではないでしょうか。(ステージごとにクラスを作っている場合)
ステージクラスをステージ分用意しているのか、一つのステージクラスを用意してインスタンスを使いまわしているかで変わると思いますので、わたしは何とも言えません。
初心者です

イマダニ
記事: 145
登録日時: 7年前

Re: ステージ間ワープを作りましたが、自信がないので見てください。

#13

投稿記事 by イマダニ » 4年前

3Dさん さんが書きました: Stage.cppがどうなっているか分かりません。
以下の六つのクラスで構成されてます。
地面クラス:Ground
地面、ワープポイント、二つのオブジェクトを保持する基底クラス:Stage_Base
それを継承した各ステージクラス:Stage_1, Stage_2, Stage_Center//拠点ステージ
ステージ変更のためのインターフェースクラス:StageChanger
ステージの画像を管理するクラス:StageImg_Mng
そしてそれらを管理するクラス:Stage_Mng

このクラスたちを以下に貼ります。

地面クラス:Ground

コード:

/*Ground.h*/
//=========
//地面クラス
//=========
class Ground{
private:
        //描画位置
	std::unique_ptr<Stage_Pos> Pos;
	//現在のステージ
	e_StageScene NowScene;
	//画像
	std::unique_ptr<StageImg_Mng> Img;
	//マップチップの数
	int ChipX_Num, ChipY_Num;
private:
	//データ
	std::unique_ptr<StageData[]>  Data;
	std::unique_ptr<SlopeData[]>  SlopeData;
private:
	//読み込み
	int Load(const char *dataname);

	//各ステージ描画
	void StageCenter_Draw(e_StageIMGORDER Order);
	void Stage_1_Draw(e_StageIMGORDER Order);
	void Stage_2_Draw(e_StageIMGORDER Order);
public:
	Ground(e_StageScene Scene);
	~Ground();

	void Update();

	void Draw(e_StageIMGORDER Order);   //描画
	void PlayerFrontDraw();   //描画
public:
	//=================
	//ステージ
	//=================
	//あたり判定
	//唯のチップ
	bool GroundHit_RX(int state, Rect rx);
	bool GroundHit_LX(int state, Rect rx);
	bool GroundHit_Y(int state, Rect ry);
};

コード:

/*Ground.cpp*/

#include <DxLib.h>
#include "Input.h"
#include "Camera.h"
#include "Ground.h"
#include "STD.h"
#pragma warning(disable:4996)

/*地面データロード*/
int Ground::Load(const char *dataname){
	FILE *fp;
	int FileData = 0;

	fp = fopen(dataname, "r");

	if (fp == NULL){
		return -1;
	}
	else{
		fscanf(fp, "%d,%d", &ChipY_Num, &ChipX_Num);

		//==========================
		//ステージデータ動的確保
		//==========================
		Data.reset(new StageData[ChipY_Num*ChipX_Num]);

		//要素数分だけデータ変数にハンドル内容を代入/*これを各レイヤーごとに実行。*/
		for (int y = 0; y<ChipY_Num; y++){
			for (int x = 0; x<ChipX_Num; x++){
				fscanf(fp, "%d%*c", &FileData);
				//マップデータにファイルデータを渡す
				Data[y*ChipX_Num + x].BackGround = FileData;//背景
			}
		}
                /*省略されてるが、この後、各レイヤーごとにforループが書かれてる。*/
		for (int y = 0; y<ChipY_Num; y++){
			for (int x = 0; x<ChipX_Num; x++){
				fscanf(fp, "%d%*c", &FileData);
				//マップデータにファイルデータを渡す
				Data[y*ChipX_Num + x].各レイヤー = FileData;/*各レイヤー*/
			}
		}
                [省略]
	}
	//読んだのでファイルを閉じる
	fclose(fp);

	return 0;
}

Ground::Ground(e_StageScene Scene){
	ChipX_Num = 0;
	ChipY_Num = 0;

	NowScene = Scene;
	
	Pos.reset(new Stage_Pos);

	switch (NowScene){//ステージごとに
	case e_StageScene_StageCenter://拠点
		Load("data/StageData/Stage/BaseStage.txt");//データ読み込み
		Img.reset(new StageImg_Mng(e_StageType_Center));//画像読み込み
		break;
	case e_StageScene_Stage1://ステージ1
		Load("data/StageData/Stage/Stage1.txt");
		Img.reset(new StageImg_Mng(e_StageType_1));
		break;
	}
}

Ground::~Ground(){
}

/*あたり判定*/
bool Ground::GroundHit_LX(int state, Rect rx){
	if (当たる条件)
	{
		return true;
	}

	return false;
}
bool Ground::GroundHit_RX(int state, Rect rx){
	if (当たる条件)
	{
		return true;
	}

	return false;
}

bool Ground::GroundHit_Y(int state, Rect ry){
	if (当たる条件)
	{
		return true;
	}

	return false;
}

void Ground::Update(){
}
/*びょうが*/
void Ground::Draw(e_StageIMGORDER Order){
	switch (NowScene){
	case e_StageScene_StageCenter://拠点だったら
		StageCenter_Draw(Order);//拠点描画
		break;
	}
}
//拠点描画
void Ground::StageCenter_Draw(e_StageIMGORDER Order){}
地面、ワープポイント、二つのオブジェクトを保持する基底クラス:Stage_Base

コード:

/*Stage_Base.h*/
#pragma once

#include "Ground.h"
/*#include "WarpPoint.h"*/

/*ステージの基底クラス*/
class Stage_Base{
protected:
	//各オブジェクト
	std::unique_ptr<Ground> m_ground;		  //地面
	/*std::unique_ptr<WarpPoint> m_warppoint;//ワープポイント*/
protected:
	Stage_Changer* mStageChanger;    //クラス所有元にシーン切り替えを伝えるインターフェイス
public:
	Stage_Base(Stage_Changer* changer);
	~Stage_Base();

	virtual void Update();//更新
	virtual void Draw();   //描画
public:
	//あたり判定
	//唯のチップ
	bool GroundHit_RX(int state, Rect rx);
	bool GroundHit_LX(int state, Rect rx);
	bool GroundHit_Y(int state, Rect ry);
};
それを継承した各ステージクラス:Stage_1, Stage_2, Stage_Center//拠点ステージ

コード:


#pragma once

/*基底クラスインクルード*/
#include "Stage_Base.h"

/*拠点ステージクラス:ステージ基底クラス継承*/
class Stage_Center : public Stage_Base{
public:
	Stage_Center(Stage_Changer* changer);
	void Update() override;
	void Draw() override;
};

コード:

/*Stage_Center.cpp*/
#include "DxLib.h"
#include "Input.h"
#include "Stage_Center.h"

Stage_Center::Stage_Center(Stage_Changer* changer) : Stage_Base(changer){
	m_ground.reset( new Ground(e_StageScene_StageCenter) );
	/*m_warppoint.reset( new WarpPoint("data/StageData/Light/BaseStage.csv") );*/
}

void Stage_Center::Update(){
	if (Input::Inst()->CheckPad(Input::Square) == 1){//□ボタン押されたらステージチェンジ
		mStageChanger->ChangeScene(e_StageScene_Stage1);
	}
	m_ground->Update();
	/*m_warppoint->Update();*/
}

void Stage_Center::Draw(){
	m_ground->Draw(e_StageIMGORDER_BACKGROUND);
	m_ground->Draw(e_StageIMGORDER_BACK);
	m_ground->Draw(e_StageIMGORDER_REAR);
	m_ground->Draw(e_StageIMGORDER_FRONT);
	/*m_warppoint->Draw();*/
}
ステージ変更のためのインターフェースクラス:StageChanger

コード:

#pragma once

//ステージのタイプ
typedef enum{
	e_StageScene_StageCenter, //拠点
	e_StageScene_Stage1,	  //ステージ1
	e_StageScene_Stage2,	  //ステージ2

	e_StageScene_None,
}e_StageScene;

//ステージ変更のためのインターフェースクラス
class Stage_Changer{
public:
	virtual ~Stage_Changer() = 0;
	virtual void ChangeScene(e_StageScene NextScene) = 0;//ステージ変更
};

ステージの画像を管理するクラス:StageImg_Mng

コード:

/*画像クラス*/
class StageImg_Mng{
private:
	/*画像データ[画像の種類][画像番号] 例:[地面][地面1(お城の床)]*/
	int StageImg[e_StageIMGTYPE_MAX][e_StageIMGNUM_MAX];
private:
        //グラフィックハンドルロード
	void ImgHandle_Load(e_StageType Type);
public:
        //引数ステージの種類によって画像を読み込む
	StageImg_Mng(e_StageType Type);
        //読み込んだ画像をDeleteGraph(StageImg[e_StageIMGTYPE_MAX][e_StageIMGNUM_MAX]);
	~StageImg_Mng();
        //画像取得
	int Get_StageImg(int ImgType, int ImgNum);
};
そしてそれらを管理するクラス:Stage_Mng

コード:

/*StageMng.h*/
#pragma once

#include "Player.h"
#include "Stage_Changer.h"
#include "Stage_Base.h"

/*ステージ管理クラス:ステージ変更インタフェースとプレイヤーとのインターフェース(by Rittai3Dさん)の継承*/
class Stage_Mng : public Stage_Changer, public PlayerStage_InterFace{
private:
	Player *m_player;//プレイヤーのインスタンス
	std::unique_ptr<Stage_Base> mStageScene;//現在のステージ
	e_StageScene mNextScene;//次のステージ
public:
	Stage_Mng();

	void Initialize(Player *player);
	void Update();
	void Draw();

	void ChangeScene(e_StageScene NextScene) override;//ステージ変更インターフェースの関数オーバーライド
public:
        /*あたり判定。インターフェースクラスの仮想関数をオーバーライド*/
	virtual bool GroundHit_RX(int type) const override;
	virtual bool GroundHit_LX(int type) const override;
	virtual bool GroundHit_UY(int type) const override;
	virtual bool GroundHit_DY(int type) const override;
};

コード:

/*StageMng.cpp*/
#include "DxLib.h"
#include "Stage_Center.h"
#include "Stage_1.h"
#include "Stage_2.h"
#include "Stage_Mng.h"

Stage_Mng::Stage_Mng()
{
	mNextScene = e_StageScene_None;
	mStageScene.reset( (Stage_Base*)new Stage_Center(this) );
}

void Stage_Mng::Initialize(Player *player){
	m_player = player;
}

void Stage_Mng::Update(){
	if (mNextScene != e_StageScene_None){
		switch (mNextScene){//ステージによって処理を分岐
		case e_StageScene_StageCenter://次のステージが拠点なら
			mStageScene.reset( (Stage_Base*) new Stage_Center(this) );   //ステージを拠点
			break;//以下略
		case e_StageScene_Stage1:
			mStageScene.reset((Stage_Base*) new Stage_1(this));
			break;
		case e_StageScene_Stage2:
			mStageScene.reset((Stage_Base*) new Stage_2(this));
			break;
		}
		mNextScene = e_StageScene_None;//次のシーン情報をクリア
	}

	mStageScene->Update(); //シーンの更新
}

//描画
void Stage_Mng::Draw(){
	mStageScene->Draw(); //シーンの描画
}

// 引数 nextScene にシーンを変更する
void Stage_Mng::ChangeScene(e_StageScene NextScene){
	mNextScene = NextScene;    //次のシーンをセットする
}

/*あたり判定。3Dさんのインタフェースでやり取りするやつ*/
bool Stage_Mng::GroundHit_RX(int type) const{
	return mStageScene->GroundHit_RX(type, m_player->GetHit(Player::HIT_SIDE));
}
bool Stage_Mng::GroundHit_LX(int type) const{
	return mStageScene->GroundHit_LX(type, m_player->GetHit(Player::HIT_SIDE));
}
bool Stage_Mng::GroundHit_UY(int type) const{
	return mStageScene->GroundHit_Y(type, m_player->GetHit(Player::HIT_UP));
}
bool Stage_Mng::GroundHit_DY(int type) const{
	return mStageScene->GroundHit_Y(type, m_player->GetHit(Player::HIT_DOWN));
}
ステージ部分は大体こんな感じです。

イマダニ
記事: 145
登録日時: 7年前

Re: ステージ間ワープを作りましたが、自信がないので見てください。

#14

投稿記事 by イマダニ » 4年前

ゲーム管理部はこんな感じです。

コード:

/*GameScene_Base.h*/

#pragma once

#include "GameScene_Changer.h"
#include "Camera.h"
#include "Stage_Mng.h"
#include "PlayerStage_InterFace.h"
#include "Player.h"

#define PLAYER_CAMERAPOS_Y 85

class GameScene_Base{
protected:
	//各オブジェクト
	std::unique_ptr<Stage_Mng> m_stage;	//ステージ
	std::unique_ptr<Player> m_player;	//プレイヤー
protected:
	GameScene_Changer* mGameSceneChanger;    //クラス所有元にシーン切り替えを伝えるインターフェイス
public:
	GameScene_Base(GameScene_Changer* changer);
	~GameScene_Base();

	virtual void Update();
	virtual void Draw();
};

コード:

/*GameScene_Base.cpp*/

#include "GameScene_Base.h"

GameScene_Base::GameScene_Base(GameScene_Changer *changer){
	m_stage.reset(new Stage_Mng());//ステージ確保
	 m_player.reset(new Player());//プレイヤー確保
 
	/*初期化。いずれGameScene_Baseクラスにも*/
	/*初期化関数、Initialize()を作り、そこに書く予定*/
	m_player->Initialize(m_stage.get());/*インターフェースクラスを継承したStageMngのポインタを引数として受け取る*/
	m_stage->Initialize(m_player.get());/*プレイヤーを受け取る*/
}
GameScene_Base::~GameScene_Base(){
}

void GameScene_Base::Update(){
}

void GameScene_Base::Draw(){
}

コード:

/*GameScene_Mng.cpp*/

#include "DxLib.h"
#include "Action.h"
#include "StageLoad.h"
#include "GameScene_Mng.h"

GameScene_Mng::GameScene_Mng()
{
	mScene.reset((GameScene_Base*) new Action(this));
	mNextScene = e_GameScene_None;
}
GameScene_Mng::~GameScene_Mng(){}

//更新
void GameScene_Mng::Update(){
	if (mNextScene != e_GameScene_None){    //次のシーンがセットされていたら
		switch (mNextScene){				//シーンによって処理を分岐
		case e_GameScene_StageLoad:/*ステージロード*/
			break;
		case e_GameScene_Action:/*アクションパート*/
			mScene.reset((GameScene_Base*) new Action(this));
			break;
		}
		mNextScene = e_GameScene_None;    //次のシーン情報をクリア
	}
	mScene->Update();
}

//描画
void GameScene_Mng::Draw(){
	mScene->Draw();
}

// 引数 nextScene にシーンを変更する
void GameScene_Mng::ChangeGameScene(e_GameScene NextScene){
	mNextScene = NextScene;    //次のシーンをセットする
}

ISLe()

Re: ステージ間ワープを作りましたが、自信がないので見てください。

#15

投稿記事 by ISLe() » 4年前

わたしは、異なるオブジェクトが干渉する処理は、デザインパターンでいうところのアブストラクトファクトリーパターンで、インターフェースを引っ張ってきて、メソッドを呼び出すようにしています。

pStageContext = pGlobalContext->GetStageContext();
例えばグローバルなコンテキストからステージのコンテキストを取得し
pStageContext->GetAroundInfo(&info);
ステージコンテキストを経由して周辺情報を取得
という具合です。

コンテキストはインターフェースとして定義し、メソッドを呼び出してコンテキストを取得することで、実装を隠せます。
実装が、単体のクラスか複数のクラスか、既存のインスタンスか新たに作られるインスタンスか、といったことは、それを呼び出して利用する側に影響しません。

例えばプレイヤーの行動を決定するのに必要な周辺情報は、それをするために必要十分なものに抽象化します。
異なる同位オブジェクトにアクセスして依存関係が大きくなってしまうのは避けなければいけません。



ステージ間ワープに関しては、ステージが「次のステージ」の情報を持つといったことは避けたほうが良いと思います。
オブジェクト自体を変化を意識するように変えてしまうのはよくありません。
変化を管理する別のオブジェクトを用意しましょう。
フェードイン・アウト中は、それ以外の時間を進めなければ良いのです。
更新・描画のサイクルが正しくできているならば、オブジェクトを現状のまま静止させることは難しくありません。

Rittai_3D
記事: 522
登録日時: 6年前

Re: ステージ間ワープを作りましたが、自信がないので見てください。

#16

投稿記事 by Rittai_3D » 4年前

class Player で当り判定を行うのではなくて、当ったかどうかの結果が分かればよいのではないでしょうか。
わたしのコードは、Player::hit() 内では判定を行わずに、あたったかどうかの結果だけもらって、処理しています。
当り判定を外部に丸投げしてしまい、結果だけをもらう形にした方がコードがすっきりすると思います。

ほかに思ったことも書いておきます。

基底クラスには仮想デストラクタを書きましょう。http://www.yunabe.jp/docs/cpp_virtual_destructor.html

C++ ならば #define ではなくて const で定義しましょう。
また、std::unique_ptr< Some >( new Some ) のような書き方から std::make_unique< Some >() に変えた方がよいかと。

せっかくC++を使用しているなら、ファイルの読み込みには ifstream を使った方がよいと思います。
コンストラクタでは代入ではなくて初期化リストを使いましょう。http://www.geocities.jp/bleis_tift/cpp/init.html

コード:

std::unique_ptr<StageData[]>  Data;
std::vector<> や std::list<> を利用してみてはどうでしょう。
Getterには const を付けてconstメンバ関数にした方がよいです。

GameScene_Baseクラスのコンストラクタ内で行っている

コード:

m_player->Initialize(m_stage.get());/*インターフェースクラスを継承したStageMngのポインタを引数として受け取る*/
m_stage->Initialize(m_player.get());/*プレイヤーを受け取る*/
この辺りは個人的にどうかな、と。get()で生のポインタをInitialize()に渡すのはあまり好きではないというか、なんとなく怖いというか。

class Ground 内にステージごとの描画関数がありますが、この先ステージが増えたことを考えてやめた方がよいと思います。

わたし自身C++を使いこなしているわけではないのでいろいろ間違えたことを言っているかもしれませんが、気になったところはこの辺りです。
初心者です

イマダニ
記事: 145
登録日時: 7年前

Re: ステージ間ワープを作りましたが、自信がないので見てください。

#17

投稿記事 by イマダニ » 4年前

ISLeサン、お久しぶりです
返信ありがとうございます。
今、ISLeさん、3Dさんのアドバイスをもとにもう一度設計を考えてます。
そこで初めてクラス図というのでしょうか?
それをastah communityというクラス図作成ツールを用いて書いてみてます。
大体以下のような感じにしようかなと思っています。
まだ途中なんですが、今の状況とプレイヤーがどうなってるかも見せたいのと、あと書き方あってるか気になったので質問のついでに張っておきます。

それで肝心の質問なんですが
一つわからないことがあります。

それは
pStageContext = pGlobalContext->GetStageContext();
例えばグローバルなコンテキストからステージのコンテキストを取得し
pStageContext->GetAroundInfo(&info);
ステージコンテキストを経由して周辺情報を取得
コンテキスト

コンテキストとはなんですか?初めて聞きました。
検索したところ

http://www.sophia-it.com/content/%E3%82 ... 9%E3%83%88
「「context」(コンテキスト)は、「文脈」、「前後関係」などと訳されるが、IT用語としては意味がイメージしづらく、単にコンテキストとある場合は、何らかの制御情報と考える方がわかりやすいことが多い。」

まあなんとなく制御するためのものなんだな程度の認識を得て、

ISLeさんのこの文を見て、
デザインパターンでいうところのアブストラクトファクトリーパターンで、インターフェースを引っ張ってきて、
アブストラクトファクトリーパターンとあったので、ここを見て、

http://marupeke296.com/DP_AbstractFactory.html

そして、大体イメージし、

コード:

class AbstractContext
{
public:
/*プレイヤーとステージのインターフェース?*/
   virtual PlayerStage_InterFace* GetStageContext()= 0;/*後々ここに敵とのインターフェースとか足してくのかな?*/
   virtual PlayerEnemy_InterFace* GetEnemyContext()= 0;/*こんな感じに*/
};

class pGlobalContext : AbstractContext/*継承したクラスをどっからでもアクセスできるグローバルなものとして宣言し、やり取りするのかな?*/
{
public:
   virtual PlayerStage_InterFace* GetStageContext(){
             return new StageMng;
    }
};
こんな感じなのかなーと、かってに思ってます。

どうでしょうか?
添付ファイル
クラス図.jpg

イマダニ
記事: 145
登録日時: 7年前

Re: ステージ間ワープを作りましたが、自信がないので見てください。

#18

投稿記事 by イマダニ » 4年前

基底クラスには仮想デストラクタを書きましょう。http://www.yunabe.jp/docs/cpp_virtual_destructor.html

C++ ならば #define ではなくて const で定義しましょう。
また、std::unique_ptr< Some >( new Some ) のような書き方から std::make_unique< Some >() に変えた方がよいかと。

せっかくC++を使用しているなら、ファイルの読み込みには ifstream を使った方がよいと思います。
コンストラクタでは代入ではなくて初期化リストを使いましょう。http://www.geocities.jp/bleis_tift/cpp/init.html

コード:

std::unique_ptr<StageData[]>  Data;
std::vector<> や std::list<> を利用してみてはどうでしょう。
Getterには const を付けてconstメンバ関数にした方がよいです。
やはりここで質問して正解でした。
言語に対する知識を得るのは、本やサイトだけでは限界があるし、
コード規約など制約のない状態の上、一人でソースコードを書いていると、何が正しいのかあいまいになるので、こういう指摘は本当に助かります。
僕の知らないことがたくさんあることを自覚する上に、それを知れる機会が得られるのはやはり素晴らしい。
3Dさん。指摘だけでなく、サイトURLの貼り付け、ほんとうにありがとうございます。

では疑問点をいくつか
当り判定を外部に丸投げしてしまい、結果だけをもらう形にした方がコードがすっきりすると思います。
つまりステージの更新、Update内部で、プレイヤーが当たってるかどうかの情報を更新し、
その判定をゲッターでプレイヤーが受け取るだけ。ということでしょうか?

コード:

/*あたり判定。3Dさんのインタフェースでやり取りするやつ*/
bool Stage_Mng::GroundHit(int type) const{
    return mStageScene->GroundHit(type, m_player->GetHit());
}
これを

コード:

void PlayerPosMove::Stage_HitManage(int state){//あたり判定管理
/*インターフェースの関数を使い*/
	if (m_pinter->GroundHit(当たった対象の種類)){//この種類の地面に当たってる?か判断
		//当たったことによるプレイヤーの挙動
	}
}
今こう使ってます。
当たった対象の種類(普通の地面、水)に合わせてプレイヤーの挙動が変わるのでこうしてます。
3Dさんの提案は、m_pinter->GroundHit(当たった対象の種類)がtrue,falseに切り替わる、更新をstageのupdateで行い、
その結果をプレイヤーで受け取るという認識なんですが、
それだと返される結果、ゲッターで受ける戻り値が地面の種類ごとに必要になりませんか?
そんな疑問を抱いています。誤解していたらごめんなさい。

std::make_unique< Some >() に変えた方がよいかと。
autoはあまり使ったことがなく、あまり使い慣れていません。なので書き方について確認します。
このautoは、ヘッダファイルのクラス内のメンバ変数として書くのではなく、コンストラクタで

コード:

GameScene_Base::GameScene_Base(GameScene_Changer *changer){
	/*m_stage = make_shared<Stage_Mng>();*//*変更前*/
	auto m_stage = std::make_unique< Stage_Mng >();/*変更後*/

	m_player = make_shared<Player>(m_stage);

	m_stage->Initialize(m_player);
}
こう書くということでよろしいのでしょうか?
そしてここに書いた場合、このauto m_stageは、クラスのメンバ変数として、
今までと変わらず使用できるという認識でよろしいのでしょうか?
せっかくC++を使用しているなら、ファイルの読み込みには ifstream を使った方がよいと思います。
そう思い、一度修正したのですが、ファイルの読み込みがうまくいかず、
今は全体の設計を見直している最中なので、質問するのもどうかと思い、後にしようということで、
fopenの過去のコードにしています。
ファイルはテキストファイルで、以下のようになっています。

コード:

39,38/*チップ数*/
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
/*スペースであけて*/
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0/*レイヤーごとに*/
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
問題の動かないコードはいかに貼っておきます。
他のデータ、CSVなどはifstreamに書き換え、読み込みができたのですが、
テキストファイルだけできず、困っています。
どう書き換えたらいいんでしょう?

コード:

int Ground::Load(const char *dataname){
	char buf[256];	// データ一時保管用配列
	char comma;						//カンマ用変数
	int data = 0;

	std::ifstream ifs(dataname);

	ifs.getline(buf, sizeof(buf));	// 一行読み込んで
	std::istringstream iss(buf);	//それを文字列として読み込む

	iss >> ChipY_Num >> comma >> ChipX_Num;

	//==========================
	//ステージデータ動的確保
	//==========================
	Data.reset(new StageData[ChipY_Num*ChipX_Num]);

	//要素数分だけデータ変数にハンドル内容を代入
	for (int y = 0; y<ChipY_Num; y++){
		ifs.getline(buf, sizeof(buf));	// 一行読み込んで
		std::istringstream iss(buf);	//それを文字列として読み込む
		for (int x = 0; x<ChipX_Num; x++){
			iss >> data >> comma;

			//マップデータにファイルデータを渡す
			Data[y*ChipX_Num + x].BackGround = data;
		}
	}
	/*これをレイヤーごとに*/
	for (int y = 0; y<ChipY_Num; y++){
		ifs.getline(buf, sizeof(buf));	// 一行読み込んで
		std::istringstream iss(buf);	//それを文字列として読み込む
		for (int x = 0; x<ChipX_Num; x++){
			iss >> data >> comma;

			//マップデータにファイルデータを渡す
			Data[y*ChipX_Num + x].レイヤー = data;
		}
	}
	return 0;
}

コード:

m_player->Initialize(m_stage.get());/*インターフェースクラスを継承したStageMngのポインタを引数として受け取る*/
m_stage->Initialize(m_player.get());/*プレイヤーを受け取る*/
この辺りは個人的にどうかな、と。get()で生のポインタをInitialize()に渡すのはあまり好きではないというか、なんとなく怖いというか。
今回の質問で、h2so5さんのおかげで初めてスマートポインタについて知りました。
なのでまだ探り探り使っている状態です。この機会に知れるのなら使い方を知りたいので、
よければ教えてください。
3Dさん的にはどう渡すのが理想ですか?


C++11の存在は、つい最近知り、autoだけ知ってました。(cocos2d-xの本で読んだ。)
ただよくわからないというのと、ゲームを早く動かしたいという気持ちがあり、使うのを避けていました。
それ以外のスマートポインタなどは今回初めて知りました(shared_ptr以外)。


先に言っておくべきでした。すいません...

Rittai_3D
記事: 522
登録日時: 6年前

Re: ステージ間ワープを作りましたが、自信がないので見てください。

#19

投稿記事 by Rittai_3D » 4年前

イマダニ さんが書きました:
当り判定を外部に丸投げしてしまい、結果だけをもらう形にした方がコードがすっきりすると思います。
つまりステージの更新、Update内部で、プレイヤーが当たってるかどうかの情報を更新し、
その判定をゲッターでプレイヤーが受け取るだけ。ということでしょうか?
そうです。
また、当っているかどうかの判定は当り判定クラスを通して行います。
イマダニ さんが書きました:

コード:

/*あたり判定。3Dさんのインタフェースでやり取りするやつ*/
bool Stage_Mng::GroundHit(int type) const{
    return mStageScene->GroundHit(type, m_player->GetHit());
}
これを

コード:

void PlayerPosMove::Stage_HitManage(int state){//あたり判定管理
/*インターフェースの関数を使い*/
	if (m_pinter->GroundHit(当たった対象の種類)){//この種類の地面に当たってる?か判断
		//当たったことによるプレイヤーの挙動
	}
}
今こう使ってます。
当たった対象の種類(普通の地面、水)に合わせてプレイヤーの挙動が変わるのでこうしてます。
3Dさんの提案は、m_pinter->GroundHit(当たった対象の種類)がtrue,falseに切り替わる、更新をstageのupdateで行い、
その結果をプレイヤーで受け取るという認識なんですが、
それだと返される結果、ゲッターで受ける戻り値が地面の種類ごとに必要になりませんか?
そんな疑問を抱いています。誤解していたらごめんなさい。
そういう仕様だったのですか。行けない場所はみな同じ挙動すると勝手に思い込んでいました。
だからbool型で受け取るようにしていました。
そういう場合、わたしならビット演算を使用し、https://ideone.com/Tqaa0Yのようにします。
コードを貼ると長くなるのでideoneでの実行結果とコードをご覧ください。

このやり方だと、Getterは種類ごとに必要なくなり、一つのGetterで済みます。
結局、何とぶつかったという判定処理は書く必要がありますが・・・。

上の説明では伝割らないかもしれませんので、コードにしてみました。ideoneのコードをもとに書いています。
コンパイルはしていません。あしからず。
► スポイラーを表示
また、わたしはゲームには疎くて、この手のゲームをやったこと、作ったことが無いので、「この処理はないな」と思う部分があるかも知れません。ご了承ください。
イマダニ さんが書きました:
std::make_unique< Some >() に変えた方がよいかと。
autoはあまり使ったことがなく、あまり使い慣れていません。なので書き方について確認します。
このautoは、ヘッダファイルのクラス内のメンバ変数として書くのではなく、コンストラクタで

コード:

GameScene_Base::GameScene_Base(GameScene_Changer *changer){
	/*m_stage = make_shared<Stage_Mng>();*//*変更前*/
	auto m_stage = std::make_unique< Stage_Mng >();/*変更後*/

	m_player = make_shared<Player>(m_stage);

	m_stage->Initialize(m_player);
}
こう書くということでよろしいのでしょうか?
そしてここに書いた場合、このauto m_stageは、クラスのメンバ変数として、
今までと変わらず使用できるという認識でよろしいのでしょうか?
オフトピック
すいません、std::make_unique<>はC++14からでした。
http://en.cppreference.com/w/cpp/memory ... ake_unique
もし、std::make_unique<>が使えない等のエラーが出ましたら、std::make_shared<> を使用してください。
また、make_shared<>を使用した方がよい理由はこちらをご覧ください。
よくないです。スマートポインタはブロックを抜けたらデストラクタが呼び出されてメモリを開放してしまいます。
実際に試せばわかると思いますが、コンストラクタを抜けた時点で無効なポインタになり、セグメンテーション違反を起こします。
わたしもよくやらかしていました。

また、どこからautoが出てきたのかわかりません。使わなくても、

コード:

 std::shared_ptr< Some > pSome; 
と書けばよいのではないでしょうか。
オフトピック
わたしは、std::chronoを使う時は、ヘッダファイルに無名名前空間を書いて、その中で

コード:

namespace
{
    // たとえば、以下の型がほしい時
    auto Type = std::chrono::system_clock::now();
}
// として
class A
{
    decltype( Type ) m_Now;
};
のように使っているのですが、これ、問題ないですかね?
テキストファイルだけできず、困っています。
どう書き換えたらいいんでしょう?
どのような問題があるのでしょうか?
もう少し具体的にお願いします。
今回の質問で、h2so5さんのおかげで初めてスマートポインタについて知りました。
なのでまだ探り探り使っている状態です。この機会に知れるのなら使い方を知りたいので、
よければ教えてください。
3Dさん的にはどう渡すのが理想ですか?
get() を通さないでそのまま渡します。基本的にconstを付けます。
イメージとしてはこんな感じです。
► スポイラーを表示
初心者です

イマダニ
記事: 145
登録日時: 7年前

Re: ステージ間ワープを作りましたが、自信がないので見てください。

#20

投稿記事 by イマダニ » 4年前

どのような問題があるのでしょうか?
もう少し具体的にお願いします。
すいません説明不足でした。
そうですね、読み込み自体はできているのですが、
下の画像のような感じでステージのオブジェクトが下にずれるんですよね。
青いのが本来の位置で、上に重なった赤いのがずれたやつです。
たぶんレイヤーごとにファイルを読む際に、

コード:

//一枚目のレイヤー
00000
00000/*←この辺から二枚目のレイヤーとして読み込んでる?*/
00000

//二枚目のレイヤー
00000
00100
00000
こんな感じでずれちゃってるんじゃないかなと思います

イマダニ
記事: 145
登録日時: 7年前

Re: ステージ間ワープを作りましたが、自信がないので見てください。

#21

投稿記事 by イマダニ » 4年前

画像貼り忘れました
添付ファイル
ずれ.jpg
ずれ.jpg (42.37 KiB) 閲覧数: 6802 回

Rittai_3D
記事: 522
登録日時: 6年前

Re: ステージ間ワープを作りましたが、自信がないので見てください。

#22

投稿記事 by Rittai_3D » 4年前

イマダニ さんが書きました:
どのような問題があるのでしょうか?
もう少し具体的にお願いします。
すいません説明不足でした。
そうですね、読み込み自体はできているのですが、
下の画像のような感じでステージのオブジェクトが下にずれるんですよね。
青いのが本来の位置で、上に重なった赤いのがずれたやつです。
たぶんレイヤーごとにファイルを読む際に、

コード:

//一枚目のレイヤー
00000
00000/*←この辺から二枚目のレイヤーとして読み込んでる?*/
00000

//二枚目のレイヤー
00000
00100
00000
こんな感じでずれちゃってるんじゃないかなと思います
実際に数値を出力しておかしな所があるか確認しましたか?
「思います」との発言から実際にデバッグしていないものと判断しましたが、もしそうならフォーラムルールにある丸投げに当たるのではないでしょうか。
初心者です

ISLe()

Re: ステージ間ワープを作りましたが、自信がないので見てください。

#23

投稿記事 by ISLe() » 4年前

No.15の最初に書いたのですが、コンテキストとしてインターフェースを引っ張ってくるのは、『異なるオブジェクトの干渉』を処理する機能を呼び出すため、です。

エネミーオブジェクトそのものにアクセスしたいなら直接アクセスすれば良いのですから、GetEnemyContextはほとんど意味がないとは思いませんか。
#実装を隠すラッパとしての使い道は同様にありますが、それこそ文脈が違います。

わたしが説明していることは、3Dさんが提案していることを、より柔軟に実装できるようにする方法です。

インターフェースを引っ張ってくるコードがコンテキストを表現します。
コンテキストで「何に対して」を表し、メソッド(メンバ関数)で「何をする」かを表す文章になります。
たとえ同じ名前のメソッドでも、コンテキストによって文脈が変わります。
だから、コンテキストは文脈なのです。
コードとしても読みやすくなります。

ISLe()

Re: ステージ間ワープを作りましたが、自信がないので見てください。

#24

投稿記事 by ISLe() » 4年前

Rittai_3D さんが書きました:
イマダニ さんが書きました:当たった対象の種類(普通の地面、水)に合わせてプレイヤーの挙動が変わるのでこうしてます。
3Dさんの提案は、m_pinter->GroundHit(当たった対象の種類)がtrue,falseに切り替わる、更新をstageのupdateで行い、
その結果をプレイヤーで受け取るという認識なんですが、
それだと返される結果、ゲッターで受ける戻り値が地面の種類ごとに必要になりませんか?
そんな疑問を抱いています。誤解していたらごめんなさい。
そういう仕様だったのですか。行けない場所はみな同じ挙動すると勝手に思い込んでいました。
だからbool型で受け取るようにしていました。
そういう場合、わたしならビット演算を使用し、https://ideone.com/Tqaa0Yのようにします。
コードを貼ると長くなるのでideoneでの実行結果とコードをご覧ください。

このやり方だと、Getterは種類ごとに必要なくなり、一つのGetterで済みます。
結局、何とぶつかったという判定処理は書く必要がありますが・・・。
キャラの振る舞いと世界のルールは分けるべきです。
対象が何であろうが、キャラにとって、進めない場所は進めない場所でしかないのです。

例えば、水中だから浮力が掛かる、という挙動をする場合
「水中だから」と「浮力が掛かる」は分けて考えます。

キャラ自身が、ここは水中だから、という判断はせず
水中かどうかは上位のオブジェクトが判定し
キャラが知るべきなのは浮力が掛かった振る舞いをすべきかどうかだけ。

そうしておけば、キャラの実装には何も手を付けずに、無重力空間を追加することもできます。
スイッチ等を押したら無重力になるといった変化も、キャラが判断する必要ありません。



ステージ間ワープに関しても同様。
ワープポイント以外にも、敵の魔法とかでランダムにワープさせられる、といった演出も、キャラのコードを変更せずに可能です。

イマダニ
記事: 145
登録日時: 7年前

Re: ステージ間ワープを作りましたが、自信がないので見てください。

#25

投稿記事 by イマダニ » 4年前

例えば、水中だから浮力が掛かる、という挙動をする場合
「水中だから」と「浮力が掛かる」は分けて考えます。
今まで

コード:

if (プレイヤーが右キーを押したら){
	状態を『右走り』に;
	加速度をあげる;
}
という処理をプレイヤーの部分で書いていたのですが、
そうではなく

①プレイヤーの状態は上位のオブジェクト、『ステージなどの世界のルール』『ユーザーの操作』で変更させ、

コード:

/*WorldRules.cpp*///プレイヤーのいる世界のルール的な 例としてのコードなので設計は深く考えてません

/*ユーザーの操作による状態遷移*/
if (ユーザーが右キーを押したら){
	Set_PlayerState(右走り);//プレイヤーの状態を『右走り』に
}
if (ユーザーがボタンを押したら){
	Set_PlayerState(浮力がかかってる);//プレイヤーの状態を『浮力がかかってる』に
}
/*ステージなど世界のルールによる状態遷移*/
if (ステージ『水中』に当たっていたら){
	Set_PlayerState(浮力がかかってる);//プレイヤーの状態を『浮力がかかってる』に
}
その状態に合わせてプレイヤーが行動をする。

コード:

/*Player.cpp*/

//状態による行動
if (状態が『右走り』なら){
	加速度をあげる;
}
if (状態が『浮力がかかってる』なら){
	ふわふわさせる;
}
ということですよね?間違ってるなら指摘お願いします。

ISLe()

Re: ステージ間ワープを作りましたが、自信がないので見てください。

#26

投稿記事 by ISLe() » 4年前

イマダニ さんが書きました:ということですよね?間違ってるなら指摘お願いします。
概ね合ってます。
そこらへんをどう設計するかがプログラミングで最も重要なところです。

キーコンフィグがあると右キーの実体は分からないし、キーでもパッドでも操作できるとすると複数対応あるし、リプレイ機能だと実際にキーやボタンは押されないけど押されたことにする。
間接的な繋がりなら、結び付けるのも、切り替えるのも、容易です。

以前3Dさんの立てたトピックでもそんなやりとりがありましたね。

イマダニ
記事: 145
登録日時: 7年前

Re: ステージ間ワープを作りましたが、自信がないので見てください。

#27

投稿記事 by イマダニ » 4年前

キーコンフィグがあると右キーの実体は分からないし、キーでもパッドでも操作できるとすると複数対応あるし、リプレイ機能だと実際にキーやボタンは押されないけど押されたことにする。
ひとまずキーコンフィグを作ってました。
それでこれからISLeさんがおっしゃった仕様に変更しようとしています。

そこで昔から抱えていた悩みがまた顔を出しました。

それは、プレイヤーの状態遷移です。

過去に使ってたやり方がこちら

コード:

	//右キーが押されたら―
	if (Input::Inst()->CheckPad(Input::Right)>0){
		switch (state){
		case R_JUMP://ジャンプの時はこうするよ
		case L_JUMP:
			vx = 6.0;
			state = R_JUMP;
			break;
		default:
			vx = 6.0;
			state = R_RUN;
			break;
		}
	}
	//左キーが押されたら―
	else if (Input::Inst()->CheckPad(Input::Left)>0){
		switch (state){
		case R_JUMP:
		case L_JUMP:
			vx = -6.0;
			state = L_JUMP;
			break;
		default:
			vx = -6.0;
			state = L_RUN;
			break;
		}
	}
	//何も押されてないならー
	else{
		switch (state){
		case R_JUMP:
		case L_JUMP:
			vx = 0;
			break;
		case R_N:
		case R_RUN:
			vx = 0;
			state = R_N;//ただ立ってる状態に
			break;
		case L_N:
		case L_RUN:
			vx = 0;
			state = L_N;
			break;
		}
	}
ジャンプと走りのアクションしかしないゲームならこれでよかったのですが、
状態が増えに増えた今では、この状態の時は走っちゃいかんよなという『走っちゃいけない状態』が増えに増えまして

以下のように膨れ上がりました。

コード:

	switch (state){
	case 走っちゃいけない状態その1:/*これが大量にある*/
	case 走っちゃいけない状態その2:
	case 走っちゃいけない状態その3:
	case 走っちゃいけない状態その4:
	case 走っちゃいけない状態その5:
		break;
	case R_JUMP://ジャンプの時はこうするよ
	case L_JUMP:
		vx = 6.0;
		state = R_JUMP;
		break;
	default:
		vx = 6.0;//普段は走る
		state = R_RUN;
		break;
	}
うわっはあああいやあああああはああああああああああん
といった感じです。

switch~if文まみれはいけない。
状態遷移をググれば必ずと言っていいほど出てくる言葉です。

ではどうするか?
そこで出てきたのがデザインパターンの一つ「STATEパターン」を使おうぜ!という話。
僕が状態遷移について調べた際に出たサイトでは大体そういう話が出てきました。

「状態遷移の壁」
http://sackys-blog-extend.cocolog-nifty ... -61f7.html
「ゲームのキャラクターの状態遷移がしたい」
https://teratail.com/questions/10652

で、Stateパターンが大体何なのかというと

コード:

class State{};//基底クラス

class Run:public State{}//それの継承

class Jump:public State{}//それの継承
このように状態ごとにクラスを作ろうぜって話(あくまで僕の認識)です。

ですが万能というわけでもないんですよね。

「デザインパターン習得編:State」
http://marupeke296.com/DP_State.html

上記のマルペケさんの解説では「派生のしすぎはヤバイ」と言っており、
というのも数が増えれば管理するのは大変なのは当たり前だよねという話で、

実際、

コード:

class State{};//基底クラス

class Run:public State{}//それの継承

class Jump:public State{}//それの継承

class 走っちゃいけない状態その1:public State{}//それの継承

class 走っちゃいけない状態その2:public State{}//それの継承
こういった感じで走っちゃいけない状態を素直に派生していったらと考えたらぞっとします。
それにこのままだと単に各状態をクラス化しただけです。
実際の状態遷移をするのはどこか?それはやはり派生先の各クラスで状態遷移用の関数を基底クラスからオーバライドして、
その派生クラスの種類にあわせて以下のように書き換える

コード:

void Run::Set_ChangeState(int state){//変更したい状態を引数に
	if(state == 変わってやってもいい状態){//それが走りから変われる状態なら
		m_State = state;//状態遷移
	}
}
結局、if構文じゃないですか!!ああああああああもういやあああああああああああ!!!!
以下の質問でもSoftyaさんがおっしゃってます。

見降ろし2Dゲームの操作キャラの状態遷移
http://dixq.net/forum/viewtopic.php?f=3&t=16396
デザインパターンもそうですが、開発性やメンテ性を上げるのが目的ですので、やり過ぎると逆効果になります。
まさに逆効果な状態になってるんじゃないかなと自身で感じます。

じゃあどうしよう?

というところで詰まってます。

みなさんならここからどうされますか?よければ教えてください。

、、、、、、と、漠然としたアドバイス要求ですが、
実際に数値を出力しておかしな所があるか確認しましたか?
「思います」との発言から実際にデバッグしていないものと判断しましたが、もしそうならフォーラムルールにある丸投げに当たるのではないでしょうか。
ほんとうに丸投げにはしたくないのですが、会話の流れでつい甘えが出るのか、ついアホなことを言ってしまいます。
3Dさん、ごめんなさい。

なのでこの質問がいわゆる『丸投げ』になるようでしたら、悩み自体には全く返答しなくても構いません。
返答がなかったら丸投げになってるんだなと判断して、自分でちゃんと考えます。

何でこんなこと言うかというと、今回の悩み自体が難しすぎて、どう質問したらいいのかぶっちゃけよくわからない状態だからです。
どの段階で聞いていいかわからないというか。

『答えてやってもいいけど仕様とか現段階のコードとかいろいろ情報足らんわ』

ということでしたら情報を答えます。

アバター
usao
記事: 1564
登録日時: 6年前

Re: ステージ間ワープを作りましたが、自信がないので見てください。

#28

投稿記事 by usao » 4年前

何をどうしたいのかいまいちわからないのですが.

大量にあるという状態 vs 入力 みたいな表でも作ったらどうです?
例えば,
 今の状態 = 状態遷移表[今の状態][操作入力];
みたいな.

イマダニ
記事: 145
登録日時: 7年前

Re: ステージ間ワープを作りましたが、自信がないので見てください。

#29

投稿記事 by イマダニ » 4年前

何をどうしたいのかいまいちわからないのですが.
すいません……

①入力によってプレイヤーの状態を変えたい。
「右キーを押したら走れ!」という命令を送る感じ

②その入力の際の、命令による状態遷移を、プレイヤー側でどう処理するか?
プレイヤーがジャンプ中に、「右キーを押したら走れ!」と言われても、宙に浮いてるので走れないじゃないですか。
変わるとしてもまあ右向きに寄るぐらい?
だから、その状態にとって変われる状態と、変われない状態があると思うんです。
RUN(走り状態)という状態は、N(立ち状態)とJUMP(ジャンプ状態)、この二つに変われるが、
JUMPは、Nに変われるが(地面に触れた際)、RUNには変われないとか、
でそういった状態遷移を、どう管理するかで悩んでいる状態です。
たとえば

コード:

Set_PlayerState_Run(){
	switch (state){
	case R_JUMP://ジャンプの時はこうするよ
	case L_JUMP:
		vx = 6.0;
		break;
	default:
		vx = 6.0;//普段は走る
		state = R_RUN;
		break;
	}
}

if(右キーが押されたら){
	Set_PlayerState_Run();//走る
}
こう状態遷移命令関数というのでしょうか?
プレイヤーの状態、それのセッターを用意して、
それを各入力時に使用、
その際の状態遷移はセッター内で行うというもの。

でもそれだと状態ごとにcaseを用意することになって、すべての状態分、用意したセッター内が、
switchまみれになって大変にならない?
ということで

③それを解消するためにいろいろ調べた結果、Stateパターンに行きつく。
でもStateパターンを使って、各状態をクラスにしたとしても、状態が多くて管理するの大変だし、もし状態クラス内部で
『どの状態なら遷移できるか?(上記のswitch文)』などの状態遷移処理を書く場合、結局ifとかswitchになりませんか?という疑問が湧く。
タイトル画面の時の状態遷移と違って、状態の数も多く、
それらが常に関わり合ってる(この状態の時はその状態にならない。など)もんだから頭がこんがらがり始めてしまう。

④それで、もっと調べてみるとStateパターンをただ使うだけでは危険という情報も出てくる。
どうしたらいいんだろう……

と④の状態質問した感じです。
大量にあるという状態 vs 入力 みたいな表でも作ったらどうです?
例えば,
 今の状態 = 状態遷移表[今の状態][操作入力];
みたいな.
そうですねいったん整理してみます。

イマダニ
記事: 145
登録日時: 7年前

Re: ステージ間ワープを作りましたが、自信がないので見てください。

#30

投稿記事 by イマダニ » 4年前

すいません。実際の動作と状態遷移は切り離します。

コード:

//走りの状態の時にこれを実行
Player_Run(){
	vx = 6.0;//普段は走る
}
//走り状態に切り替えるセッター
Set_PlayerState_Run(){
	switch (state){
	case R_JUMP://ジャンプの時は変わらない
	case L_JUMP:
		break;
	default:
		state = R_RUN;//それ以外は走りに
		break;
	}
}

if(右キーが押されたら){
	Set_PlayerState_Run();//走る
}

アバター
usao
記事: 1564
登録日時: 6年前

Re: ステージ間ワープを作りましたが、自信がないので見てください。

#31

投稿記事 by usao » 4年前

オフトピック
>走り状態に切り替える
という処理を行う関数の中で
「え?いやです.遷移しません」とかいう話も不思議.

この関数を呼ぶときというのは,
既に何らかの判断の結果として状態遷移先がR_RUNだということが決定しているのではないのだろうか…?

走るべきかどうかわからないけど とりあえず「走り中という状態に遷移しろ」と言ってみる みたいな…?

ISLe()

Re: ステージ間ワープを作りましたが、自信がないので見てください。

#32

投稿記事 by ISLe() » 4年前

usaoさんのおっしゃっている「表」というのは、データテーブル(あるいは単にテーブル)といってとても多用されます。
ゲームプログラムの内部的な動作はテーブルの参照と状態の更新だけと言っても過言ではないです。
テーブルもこの掲示板で話題になったことがあるので過去ログを探せば参考になる情報が見付かるかもしれません。

No.24に書いたように、キャラに与える命令を「ルール」、実際の変化を「振る舞い」として分離させます。
この概念はあらゆる場所に適用できます。
基本的に、命令と状態のインデックスでテーブルを参照し、そこから新しい状態を持ってくるだけです。

キャラオブジェクトに命令を与える(アクションを要求する)メソッドはひとつあれば良い。
「ルール」と「振る舞い」をきちんと整理すればプログラムには基本的にswitchは必要ありませんし、ifすらも必要ありません。

ISLe()

Re: ステージ間ワープを作りましたが、自信がないので見てください。

#33

投稿記事 by ISLe() » 4年前

ステートパターンに関して。

ステートパターンは、継承によりオブジェクト自身がステートとなるパターンです。

現在のキャラのインスタンスに対して「ジャンプしろ」と要求したら、『ジャンプした状態のオブジェクト』が生成されて返ってきて、前のオブジェクトは捨てて次からは新しいオブジェクトを使う、という感じの使い方をします。

ゲームキャラでそんな実装したらプログラマは死ねます。
使いどころとしてはフレームワークの一部とかですかね。

イマダニ
記事: 145
登録日時: 7年前

Re: ステージ間ワープを作りましたが、自信がないので見てください。

#34

投稿記事 by イマダニ » 4年前

基本的に、命令と状態のインデックスでテーブルを参照し、そこから新しい状態を持ってくるだけです。
大体の考え方が分かってきました。
テーブルの概念は、昔ISLeさんにクォーターの八方向の移動を作る際に教わったので大体わかります。
あの時はありがとうございました。

コード:

void Player::InputStateManage(){
	const int keyinput_muki_table[] = {
		Neutral, // nothing
		Left, // L
		Right, // R
		Neutral, // L+R
		Up, // U
		L_Up, // U+L
		R_Up, // U+R
		Up, // U+L+R
		Down, // D
		L_Down, // D+L
		R_Down, // D+R
		Down, // D+L+R
		Neutral, // D+U
		Left, // D+U+L
		Right, // D+U+R
		Neutral, // D+U+L+R
	};

	input_way = 0;

	if (Input::Inst()->CheckPad(Input::Right))
		input_way += 2;
	if (Input::Inst()->CheckPad(Input::Left))
		input_way += 1;
	if (Input::Inst()->CheckPad(Input::Up))
		input_way += 4;
	if (Input::Inst()->CheckPad(Input::Down))
		input_way += 8;

	input_state = keyinput_muki_table[input_way];
}
上記のを参考に書き換えると

コード:

void Player::InputStateManage(){
	int PlayerState;

	const int Player_Input_Table[/*状態*/][/*命令*/4] = {
		//立ち時
		R_N, R_RUN, L_RUN, R_JUMP,	  /*左から*/ /*入力なし、右キー、左キー、上キー*/
		L_N, R_RUN, L_RUN, L_JUMP,
		//走り時
		R_N, R_RUN, L_RUN, R_SIDEJUMP,/*左から*/ /*入力なし、右キー、左キー、上キー*/
		L_N, R_RUN, L_RUN, L_SIDEJUMP,
		//ジャンプ時
		R_JUMP, R_JUMP, L_JUMP, R_JUMP,  /*左から*/ /*入力なし、右キー、左キー、上キー*/
		L_JUMP, R_JUMP, L_JUMP, L_JUMP,
	};

	input_way = 0;

	/*入力に合わせて命令を*/
	if (Input::Inst()->CheckPad(Input::Right))
		input_way += 1;		/*ここで『キャラオブジェクトに命令を与える(アクションを要求する)メソッド』を使う。*/
							/*例:Set_Order(命令);といった感じの命令を引数にとるメソッドをプレイヤー側に用意?*/
	if (Input::Inst()->CheckPad(Input::Left))
		input_way += 2;
	if (Input::Inst()->CheckPad(Input::Up))
		input_way += 3;
	if (Input::Inst()->CheckPad(Input::Down))
		input_way += 0;

	/*現在の状態と、上記のif構文で更新される命令、二つのインデックスをもとに、ここで状態を放り込む*/
	PlayerState = Player_Input_Table[PlayerState][input_way];
}
こんな感じでしょうか?

イマダニ
記事: 145
登録日時: 7年前

Re: ステージ間ワープを作りましたが、自信がないので見てください。

#35

投稿記事 by イマダニ » 4年前

ゲームキャラでそんな実装したらプログラマは死ねます。
使いどころとしてはフレームワークの一部とかですかね。
ひえっ
使いどころをよく考えて使います……
解説ありがとうございます

Rittai_3D
記事: 522
登録日時: 6年前

Re: ステージ間ワープを作りましたが、自信がないので見てください。

#36

投稿記事 by Rittai_3D » 4年前

イマダニ さんが書きました:
実際に数値を出力しておかしな所があるか確認しましたか?
「思います」との発言から実際にデバッグしていないものと判断しましたが、もしそうならフォーラムルールにある丸投げに当たるのではないでしょうか。
ほんとうに丸投げにはしたくないのですが、会話の流れでつい甘えが出るのか、ついアホなことを言ってしまいます。
3Dさん、ごめんなさい。

なのでこの質問がいわゆる『丸投げ』になるようでしたら、悩み自体には全く返答しなくても構いません。
返答がなかったら丸投げになってるんだなと判断して、自分でちゃんと考えます。

何でこんなこと言うかというと、今回の悩み自体が難しすぎて、どう質問したらいいのかぶっちゃけよくわからない状態だからです。
どの段階で聞いていいかわからないというか。

『答えてやってもいいけど仕様とか現段階のコードとかいろいろ情報足らんわ』

ということでしたら情報を答えます。
一応現状のソースコードを貼っていただきたいです。時間が作れましたら動作の確認をします。
ちなみに、イマダニさん自身はデバッグしましたか?デバッグを行ったなら、その結果も教えてくだい。
オフトピック

コード:

std::unique_ptr<StageData[]>  Data;
前にも書きましたが、ここはstd::vector<>やstd::list<>などを使うべきでしょう。
それとも、std::unique_ptr<>で持たなければならない理由でもあるのでしょうか。
オフトピック
今回の悩み自体が難しすぎて、どう質問したらいいのかぶっちゃけよくわからない状態だからです。
「ファイル読み込み処理をfopen()からifstreamに変更したら、自分の意図していない挙動になってしまいました」
「デバッグをした結果、○○行目で入るべき数値と違った数値が代入されているようです」
「なぜ違った数値が入っているのかが分かりません。原因を教えてください」
と聞けばよいのでは・・・?
それとも、わたしが読み違えている・・・?
初心者です

イマダニ
記事: 145
登録日時: 7年前

Re: ステージ間ワープを作りましたが、自信がないので見てください。

#37

投稿記事 by イマダニ » 4年前

一応現状のソースコードを貼っていただきたいです。時間が作れましたら動作の確認をします。
3Dさん。忙しい中、ありがとうございます。
周りにプログラマーの友人もいないので見ていただけるのはほんとうに助かります。
ちなみに、イマダニさん自身はデバッグしましたか?デバッグを行ったなら、その結果も教えてくだい。
今はISLeさんやusaoさんのアドバイスをもとにコードをガンガン書きなおしているので、いったんファイル読み込みのほうは後回しにしている状態です。
それが終わり次第デバッグを行い、結果報告の際に、出来上がったコードをzipファイル形式で貼ろうと思います。

Rittai_3D
記事: 522
登録日時: 6年前

Re: ステージ間ワープを作りましたが、自信がないので見てください。

#38

投稿記事 by Rittai_3D » 4年前

今はISLeさんやusaoさんのアドバイスをもとにコードをガンガン書きなおしているので、いったんファイル読み込みのほうは後回しにしている状態です。
それが終わり次第デバッグを行い、結果報告の際に、出来上がったコードをzipファイル形式で貼ろうと思います。
No18
イマダニ さんが書きました:

コード:

int Ground::Load(const char *dataname){
    char buf[256];  // データ一時保管用配列
    char comma;                     //カンマ用変数
    int data = 0;
 
    std::ifstream ifs(dataname);
 
    ifs.getline(buf, sizeof(buf));  // 一行読み込んで
    std::istringstream iss(buf);    //それを文字列として読み込む
 
    iss >> ChipY_Num >> comma >> ChipX_Num;
 
    //==========================
    //ステージデータ動的確保
    //==========================
    Data.reset(new StageData[ChipY_Num*ChipX_Num]);
 
    //要素数分だけデータ変数にハンドル内容を代入
    for (int y = 0; y<ChipY_Num; y++){
        ifs.getline(buf, sizeof(buf));  // 一行読み込んで
        std::istringstream iss(buf);    //それを文字列として読み込む
        for (int x = 0; x<ChipX_Num; x++){
            iss >> data >> comma;
 
            //マップデータにファイルデータを渡す
            Data[y*ChipX_Num + x].BackGround = data;
        }
    }
    /*これをレイヤーごとに*/
    for (int y = 0; y<ChipY_Num; y++){
        ifs.getline(buf, sizeof(buf));  // 一行読み込んで
        std::istringstream iss(buf);    //それを文字列として読み込む
        for (int x = 0; x<ChipX_Num; x++){
            iss >> data >> comma;
 
            //マップデータにファイルデータを渡す
            Data[y*ChipX_Num + x].レイヤー = data;
        }
    }
    return 0;
}
このコードのまま変わっていないのでしたら、9行目の iss と32行目の iss が被っていて、34行目の

コード:

iss >> data >> comma;
の部分の iss が前者の iss と判断されているのではないでしょうか。
実際に動かしていないので勘です。

・・・と前の投稿で書けばよかったですね、すいません。
初心者です

ISLe()

Re: ステージ間ワープを作りましたが、自信がないので見てください。

#39

投稿記事 by ISLe() » 4年前

イマダニ さんが書きました:こんな感じでしょうか?
それはユーザー入力の変換テーブルですね。

1. 周辺機器からの現実の入力を、アプリケーションで定義する仮想の入力に変換
2. アプリケーションで定義する仮想の入力を、ゲーム内のオブジェクトで定義する仮想のアクションに変換
3. ゲーム内のオブジェクトで定義する仮想のアクションを元に、振る舞いを変化

No.34でやってるのは1です。
やりたいことは1から3の一連の流れ(特に3)なのでは。
わたしは3のつもりでテーブルの話をしてました。

ひとつのテーブルですべて処理できるわけでなく、個々にテーブルを経ていきます。

何度も書いている「ルール」と「振る舞い」の分離で設計や実装に柔軟性が生まれます。
キャラの振る舞いをユーザーの入力(の仕様)に直結すれば、ユーザー入力がないとテストもできなくなります。
開発効率が激しく低下します。


No.29でイマダニさんが書いている「立ち状態」。
文字通り「状態」です。
それはキャラの振る舞いのひとつ。
であれば、「立ち状態」になる外部要因は何か。
それを整理して、アクションをひとつだけ定義してください。

先回りしておくと「地面に触れる」というのは要因であってアクションではありません。
要因がなくてもアクションできるようにするのがゲームプログラムの効率の良い作り方のひとつです。

イマダニ
記事: 145
登録日時: 7年前

Re: ステージ間ワープを作りましたが、自信がないので見てください。

#40

投稿記事 by イマダニ » 4年前

ISLeさん。何度も何度も詳しい説明ありがとうございます。
説明された以上、ちゃんと理解するのが礼儀だと思うので、もう一度、理解できてるかの確認お願いします。
No.29でイマダニさんが書いている「立ち状態」。
文字通り「状態」です。
それはキャラの振る舞いのひとつ。
振る舞い:立ち状態
であれば、「立ち状態」になる外部要因は何か。
先回りしておくと「地面に触れる」というのは要因であってアクションではありません。
外部要因:地面に触れている。入力がない。
それを整理して、アクションをひとつだけ定義してください。
振る舞い:立ち状態
外部要因:地面に触れている。入力がない。

アクション:立つ。


こういうことですか?

振る舞い:右走り
外部要因:地面に触れている。右キー入力がある。

アクション:右へ走る


こんな感じですかね?

コードだと、「地面に触れる」などといった、ステージ要因を抜いてますが(僕の悪い頭だとちょっと混乱しそうなので……

コード:


	/*仮想入力テーブル[現実の入力]*/
	const int VirtualInput[] = {
		Neutral, // nothing
		Left, // L
		Right, // R
		Neutral, // L+R
		Up, // U
		L_Up, // U+L
		R_Up, // U+R
		Up, // U+L+R
		Down, // D
		L_Down, // D+L
		R_Down, // D+R
		Down, // D+L+R
		Neutral, // D+U
		Left, // D+U+L
		Right, // D+U+R
		Neutral, // D+U+L+R
	};

	m_VirtualInputWay = 0;
	
	/*現実の入力*/
	if (Input::Inst()->CheckPad(Input::Inst()->Get_PadHandle(Input::Left)))
		m_VirtualInputWay += 1;
	if (Input::Inst()->CheckPad(Input::Inst()->Get_PadHandle(Input::Right)))
		m_VirtualInputWay += 2;
	if (Input::Inst()->CheckPad(Input::Inst()->Get_PadHandle(Input::Up)))
		m_VirtualInputWay += 4;
	if (Input::Inst()->CheckPad(Input::Inst()->Get_PadHandle(Input::Down)))
		m_VirtualInputWay += 8;

	//仮想入力
	m_VirtualInput_State = VirtualInput[m_VirtualInputWay];


	/*仮想アクションテーブル[仮想入力(外部要因)]*/		/*[ここにステージ要因(地面に触れるとか、触れてないとか)?]*/
	const int VirtualAction[] = {
		STAND,		//インデックスが『入力なし』なら『立ちアクション』
		RUN,		//走り
		RUN,
		JUMP,		//ジャンプ
		SIDEJUMP,	//横ジャンプ
		SIDEJUMP,
		DOWN,		//しゃがみ
		DOWN,
		DOWN,
	};

	//仮想アクション
	m_VirtualAction_State = VirtualAction[m_VirtualInput_State];


	/*振る舞いテーブル*/
	const int PlayerState[/*プレイヤーの状態*/][/*仮想アクション*/] = {
		R_N, R_RUN, L_RUN, R_JUMP, R_SIDEJUMP, L_SIDEJUMP, R_DOWN,		//[右立ち状態の時の][各アクションでの状態]

		R_N, R_RUN, L_RUN, R_SIDEJUMP, R_SIDEJUMP, L_SIDEJUMP, R_DOWN,  //[右走り状態の時の][各アクションでの状態]

		R_JUMP, R_JUMP, L_JUMP, R_JUMP, R_JUMP, L_JUMP, R_JUMP,			//[右ジャンプ状態の時の][各アクションでの状態]
	};

	//プレイヤーの振る舞い(状態)
	m_PlayerState = PlayerState[PlayerState/*プレイヤーの状態*/][m_VirtualAction_State/*仮想アクション*/]

てな感じでしょうか?
何度も何度もすいません……

ISLe()

Re: ステージ間ワープを作りましたが、自信がないので見てください。

#41

投稿記事 by ISLe() » 4年前

イマダニ さんが書きました:振る舞い:立ち状態
外部要因:地面に触れている。入力がない。

アクション:立つ。


こういうことですか?
そういうことです。
まだ外部要因が少ない(あるいは今後増えていく)ですが。

キャラの中で状況判断して、その場で振る舞いを変える、というプログラムが多いですよね。
むしろネットとか書籍とかどこでもそういうコードしか見掛けないというか。

おっしゃるとおり外的要因は、ユーザー操作やステージギミックなど多岐に渡るので、事前にアクションを必要最小限にまとめておくことが重要なのです。
外部要因はいろんなところで使うのでインデックスにも使えるIDとして用意すればよいです。

それぞれ整理してから、外部要因から変化する振る舞いをテーブルにしていきます。
十分整理してからでないと指数関数的に無駄に時間がかかってしまうので注意してください。

閉鎖

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