なんか,結構な期間このサイトを見てきた感じ,
- Scene とかいう謎の基底
- Task とかいう(同上)
- XXXManager とかいうクソみてぇな名称の何か
何故彼らはこれらを好むのだろうか?
もちろん,それで何かが楽になるのであれば良いのだが,楽になってない方々は何なの?っていう.
意味わかんねぇ謎の実装形態を(どこかから持ってきて)採用したら直後に実装作業に行き詰るであろうことは明白であると思うのだが.
マゾなのか?
///<summary>
///ゲームの実装をこの中に書いていく的なやつ:
///これよりも外側にメインループがあって,そこから Update(), Paint() が呼ばれる想定ですわよ.
///</summary>
class GameLogic : public Toyger::Painter
{
public: //※このゲームの表示サイズ.(ウィンドウ作る側がクライアント領域サイズを決める用)
static int ViewW(){ return 400; }
static int ViewH(){ return 300; }
public:
GameLogic();
~GameLogic();
GameLogic( const GameLogic & ) = delete;
GameLogic &operator=( const GameLogic & ) = delete;
public: //Update() と Paint()
UpdateResult Update( const Input_b &rInput, RndGenerator &Rnd );
virtual bool Paint( HDC hdc, int W, int H ) override;
private: //「シーン」関連Data
enum class SceneIndex{ Title, GamePlay } m_CurrScene;
class TitleScene;
class GameScene;
std::unique_ptr< TitleScene > m_upTitleScene;
std::unique_ptr< GameScene > m_upGameScene;
private:
void TransToTitleScene();
void TransToGamePlayScene();
};
///<summary>
/// <see cref="GameLogic::Update"/>の戻り値用.
/// 呼び出し側(メインループ側)に対して必要事項を伝達する.
///</summary>
struct UpdateResult
{
bool NeedToRedraw; //再描画が必要か?(Update()で表示内容を変えるべき変化が起きたかどうか)
bool AppQuitRequested; //「APP自体を終了させたい」ということになったか?
UpdateResult( bool Redraw, bool Quit=false ) : NeedToRedraw(Redraw), AppQuitRequested(Quit) {}
};
GameLogic::GameLogic(){ TransToTitleScene(); } //最初は「タイトルシーン」にしておくのですわ
GameLogic::~GameLogic(){}
void GameLogic::TransToTitleScene() //「タイトルシーン」への遷移処理ですわ
{
m_CurrScene = SceneIndex::Title;
m_upTitleScene = std::make_unique< TitleScene >();
}
void GameLogic::TransToGamePlayScene() //「ゲーム中シーン」への遷移処理ですわ
{
m_CurrScene = SceneIndex::GamePlay;
if( !m_upGameScene ){ m_upGameScene = std::make_unique< GameScene >(); }
else { m_upGameScene->InitializeGameState(); }
}
UpdateResult GameLogic::Update( const Input_b &rInput, RndGenerator &Rnd )
{
if( rInput.KeyStateHist( 'Q' ) & 0x01 ) //シーンに依らず,Qキーで終了するための処理ですわ.
{ return UpdateResult(false,true); }
switch( m_CurrScene ) //「シーン」毎の処理をするための分岐ですわね
{
case SceneIndex::Title:
{
if( m_upTitleScene->Update( rInput ) ) //「ゲームを開始したい」場合にはtrueが返される想定としておりますの.
{
TransToGamePlayScene();
return UpdateResult( true );
}
}
break;
case SceneIndex::GamePlay:
{
auto Req = m_upGameScene->Update( rInput, Rnd ); //こちらの戻り値は構造体な想定ですわ.
if( Req.ResetRequested )
{//「リセット」が要求された場合には「タイトルシーン」に戻すこととしていますわ
TransToTitleScene();
return UpdateResult( true );
}
else
{ return UpdateResult( Req.NeedToRedraw ); }
}
break;
default:
break;
}
return UpdateResult( false );
}
//※引数(W,H) は hdc に Select されている BMP のサイズということにしていますわ.
// (=クライアント領域のサイズ という感じでしてよ)
bool GameLogic::Paint(HDC hdc, int W, int H)
{
RECT ViewRect;
ViewRect.left = ViewRect.top = 0;
ViewRect.right = W; ViewRect.bottom = H;
//とりあえず常に全面Clearしていますわ.
//でも,これは「シーン」に任せるべき仕事かもしれませんわね.
::FillRect( hdc, &ViewRect, (HBRUSH)::GetStockObject(BLACK_BRUSH) );
switch( m_CurrScene ) //ここは「シーン」に描画を任せるだけですわね
{
case SceneIndex::Title:
m_upTitleScene->Paint( hdc, ViewRect ); break;
case SceneIndex::GamePlay:
m_upGameScene->Paint( hdc, ViewRect ); break;
default:
break;
}
return true;
}
class GameLogic::TitleScene
{
public:
//ゲーム開始するならtrueを返す
bool Update( const Input_b &rInput )
{ return ( (rInput.KeyStateHist( VK_LBUTTON ) & 0x03) == 0x01 ); }
void Paint( HDC hdc, const RECT &ViewRect ) const
{
::SetTextColor( hdc, RGB(220,230,255) );
::SetBkMode( hdc, TRANSPARENT );
RECT r = ViewRect;
::DrawText( hdc, _T("= L-Click to Start ="), -1, &r, DT_CENTER|DT_SINGLELINE|DT_VCENTER );
}
};
//-------------------------------------------
//これらは現状,不明ですわ.
class Bullet{}; //「弾」
class Enemy{}; //「敵」
//-------------------------------------------
//GameLogic::GameScene
class GameLogic::GameScene
{
public:
GameScene(){ InitializeGameState(); }
public:
//ゲーム状態を初期状態にするメソッドですわ.
void InitializeGameState()
{
m_bGameOver = false;
m_AvoiderPos.Assign( ViewW()*0.5f, ViewH()*0.75f );
m_Enemies.clear();
m_Bullets.clear();
}
//Updateの戻り値:呼び出し側への要求を返すものですわね.
struct Request
{
bool NeedToRedraw; //再描画が必要か?(Update()で表示内容を変えるべき変化が起きたかどうか)
bool ResetRequested; //「リセット」したいということになったか?
Request( bool Redraw, bool Reset=false ) : NeedToRedraw(Redraw), ResetRequested(Reset) {}
};
Request Update( const Input_b &rInput, RndGenerator &Rnd );
void Paint( HDC hdc, const RECT &ViewRect ) const;
private: //Data
bool m_bGameOver; //GameOverフラグ
Vec2f m_AvoiderPos; //「避ける者」の位置
std::vector< std::shared_ptr<Enemy> > m_Enemies; //敵
std::vector< std::shared_ptr<Bullet> > m_Bullets; //弾
private:
bool MoveAvoider( const Input_b &rInput ); //「避ける者」の移動処理
};
GameLogic::GameScene::Request GameLogic::GameScene::Update( const Input_b &rInput, RndGenerator &Rnd )
{
Request Ret( false, false );
//「避ける者」の移動処理ですわ.
if( !m_bGameOver )
{ Ret.NeedToRedraw |= MoveAvoider( rInput ); }
{//※これはゲームオーバー状態にするためのダミー処理ですわ
if( !m_bGameOver && (rInput.KeyStateHist( VK_MBUTTON )) )
{ m_bGameOver=true; Ret.NeedToRedraw=true; }
}
//【ゲームオーバーになっているときに,マウスクリック】で「リセット」ということにいたしますわね.
if( m_bGameOver )
{
Ret.ResetRequested = ( (rInput.KeyStateHist( VK_LBUTTON ) & 0x03) == 0x01 );
}
return Ret;
}
class Bullet
{
public:
virtual ~Bullet(){}
//Returns false if this bullet must be disabled (removed from GameField).
virtual bool Move( int GameFieldW, int GameFieldH, const Vec2f &AvoiderPos, RndGenerator &Rnd ) = 0;
virtual const bool HitCheck( const Vec2f &CheckTgtPos ) const = 0;
virtual void Paint( HDC hdc ) const = 0;
};
class Enemy
{
public:
virtual ~Enemy(){}
//Returns false if this enemy must be disabled (removed from GameField).
virtual bool Move( int GameFieldW, int GameFieldH, const Vec2f &AvoiderPos, RndGenerator &Rnd ) = 0;
//Returns a newly fired bullet or nullptr.
virtual std::shared_ptr<Bullet> Fire( const Vec2f &AvoiderPos, RndGenerator &Rnd ) = 0;
virtual void Paint( HDC hdc ) const = 0;
};