なんか,結構な期間このサイトを見てきた感じ,
- Scene とかいう謎の基底
- Task とかいう(同上)
- XXXManager とかいうクソみてぇな名称の何か
何故彼らはこれらを好むのだろうか?
もちろん,それで何かが楽になるのであれば良いのだが,楽になってない方々は何なの?っていう.
意味わかんねぇ謎の実装形態を(どこかから持ってきて)採用したら直後に実装作業に行き詰るであろうことは明白であると思うのだが.
マゾなのか?
GameLogic::GameScene::Request GameLogic::GameScene::Update( const Input_b &rInput, RndGenerator &Rnd )
{
Request Ret( false, false );
{
//これは「避ける者」の移動処理ですわね.
Ret.NeedToRedraw |= MoveAvoider( rInput );
//以下が増えましてよ.
//これらのメソッドは何かデータに変化があればtrueを返してくるというルールですわ.
//ランダムに「敵」を生成して,メンバ変数のコンテナ m_Enemies に追加する処理ですわ.
Ret.NeedToRedraw |= CreateNewEnemyAtRandom( Rnd );
//「敵」の移動と破棄,敵が生成した「弾」をメンバ変数のコンテナ m_Bullets に追加する処理ですわ.
Ret.NeedToRedraw |= UpdateEnemies( Rnd );
//「弾」の移動と破棄ですわね.
Ret.NeedToRedraw |= MoveBullets( Rnd );
//「弾」と「避ける者」との当たり判定ですことよ.
//当たった場合は,ゲームオーバーフラグ m_bGameOver がtrueに変更されるだけのことですわね.
Ret.NeedToRedraw |= HitCheck();
}
if( m_bGameOver )
{
Ret.ResetRequested = ( (rInput.KeyStateHist( VK_LBUTTON ) & 0x03) == 0x01 );
}
return Ret;
}
class SimpleBullet : public Bullet
{
private:
Vec2f m_CenterPos; //中心座標
float m_Radius; //半径
float m_SqRadius; //半径の2乗
Vec2f m_Velocity; //速度
public:
SimpleBullet( const Vec2f &InitCenterPos, float Radius, const Vec2f &Velocity )
: m_CenterPos{ InitCenterPos }, m_Radius{ Radius }, m_SqRadius{ Radius*Radius }, m_Velocity{ Velocity }
{}
virtual bool Move(int GameFieldW, int GameFieldH, const Vec2f & AvoiderPos, RndGenerator & Rnd) override
{
m_CenterPos += m_Velocity;
//寿命判定も,いわゆる「画面外に出たら」という話でしかありませんわ.
if( m_CenterPos[0] < -m_Radius || m_CenterPos[0] >= GameFieldW+m_Radius )return false;
if( m_CenterPos[1] < -m_Radius || m_CenterPos[1] >= GameFieldH+m_Radius )return false;
return true;
}
//当たり判定は簡素に「円 vs 点」で済ましていましてよ.
virtual const bool HitCheck(const Vec2f & CheckTgtPos) const override
{ return ( (CheckTgtPos - m_CenterPos).SqL2Norm() < m_SqRadius ); }
virtual void Paint(HDC hdc) const override { ここは円を描画するだけですから割愛ですわ }
};
//要は,Enemy::Fire() の移譲先ですわね
class Weapon
{
public:
virtual ~Weapon(){}
//Returns a newly fired bullet or nullptr.
virtual std::unique_ptr<Bullet> Fire( const Vec2f &OwnerPos, const Vec2f &TargetPos, RndGenerator &Rnd ) = 0;
};
//小さな弾をばらまく武器ができましたわ!
class MG : public Weapon
{
private:
int m_FirePercentage;
public:
MG( int FirePercentage ) : m_FirePercentage(FirePercentage) {}
virtual std::unique_ptr<Bullet> Fire( const Vec2f &OwnerPos, const Vec2f &TargetPos, RndGenerator &Rnd ) override
{
if( Rnd.Int( 1, 100 ) > m_FirePercentage )return nullptr;
Vec2f V;
{//弾の向かう先をランダムに少しばらけさせているだけのことですわ
auto D = GetUnit(TargetPos - OwnerPos);
float theta = Rnd.Float( -PI, PI );
D += Rnd.Float( 0.0f, D.L2Norm()*0.25f ) * Vec2f( cos(theta), sin(theta) );
if( D.SqL2Norm() < 0.01f )return nullptr;
V = GetUnit(D) * Rnd.Float( 3.0f, 5.0f );
}
return std::make_unique<SimpleBullet>( OwnerPos, 3.0f, V );
}
};
//「ゲーム中シーン」の敵生成処理で実施していましてよ.
bool GameLogic::GameScene::CreateNewEnemyAtRandom( RndGenerator &Rnd )
{
if( m_bGameOver )return false;
if( Rnd.Int( 1, 100 ) <= 5 )
{ //「敵」の具体クラス SimpleEnemy に武器 MG を提供していますわ.
//これで弾丸の雨あられが期待できますわね! うふふっ!
m_Enemies.push_back(
std::make_unique<SimpleEnemy>(
std::make_unique<MG>( Rnd.Int(3,18) ), GameFieldW(), GameFieldH(), Rnd
)
);
return true;
}
return false;
}
//これが「敵」の具体実装ですわ
class SimpleEnemy : public Enemy
{
public:
//武器の受け取りの型がこれで妥当なのかはちょっとわかりませんの.勉強不足が露呈ですわね…
SimpleEnemy( std::unique_ptr<Weapon> &&Weapon, int GameFieldW, int GameFieldH, RndGenerator &Rnd )
: m_upWeapon{ std::move(Weapon) }
{
//適当に画面外上側から降りてきて,弾をばらまいた後,上側に去っていく,という動きにしていますわ.
m_Pos.Assign( Rnd.Float( ms_HalfSize, GameFieldW-ms_HalfSize ), -ms_HalfSize );
float y = Rnd.Float( GameFieldH*0.1f, GameFieldH*0.5f ); //どこまで降りてくるかに個体差を持たせてみましてよ.
m_Schedule.push( std::make_unique<StraightMove>( Vec2f(0,2), [y]( const Vec2f &P )->bool{ return P[1]>=y; } ) );
m_Schedule.push( std::make_unique<Stay>( Rnd.Int( 32, 128 ) ) ); //留まる期間も個体ごとにランダムですわ
m_Schedule.push( std::make_unique<StraightMove>( Vec2f(0,-5), []( const Vec2f &P )->bool{ return P[1] < -ms_HalfSize; } ) );
}
virtual bool Move( int GameFieldW, int GameFieldH, const Vec2f &AvoiderPos, RndGenerator &Rnd ) override
{//コンストラクタで決めた動作を再生していくだけのことですわね.
if( !m_Schedule.empty() && !m_Schedule.front()->Control( m_Pos ) )
{ m_Schedule.pop(); }
return !m_Schedule.empty();
}
virtual std::shared_ptr<Bullet> Fire( const Vec2f &AvoiderPos, RndGenerator &Rnd ) override
{
if( m_Schedule.empty() || !m_Schedule.front()->CanFire() )return nullptr;
return m_upWeapon->Fire( m_Pos, AvoiderPos, Rnd ); //ここは武器に処理移譲するだけで解決ですわね
}
virtual void Paint( HDC hdc ) const override { ここは割愛ですわね.単に四角形で描画しているだけのことですもの. }
private:
//コンストラクタで動作をスケジューリングするのに使っている型ですわ.
class State //型名はちょっと…微妙かもしれませんわね.
{
public:
virtual ~State(){}
//位置を更新するメソッドですわ.このステートが終わった場合にはfalseを返しますの.
virtual bool Control( Vec2f &Pos ) = 0;
//このステートで発砲するか否か,ですわね.
virtual bool CanFire() const { return false; }
};
//降りてくるところと,帰っていくところの実装ですわ
class StraightMove : public State
{
private:
Vec2f m_Velocity;
std::function<bool(const Vec2f &)> m_TargetCondition;
public:
StraightMove( const Vec2f &V, std::function<bool(const Vec2f &)> TgtCondition )
: m_Velocity{V}, m_TargetCondition{TgtCondition}
{}
virtual bool Control( Vec2f &Pos ) override { Pos += m_Velocity; return !m_TargetCondition(Pos); }
};
//その場に留まって発砲する期間の実装ですことよ.
class Stay : public State
{
private:
int m_Counter;
public:
Stay( int WaitCount ) : m_Counter{WaitCount} {}
virtual bool Control( Vec2f &Pos ) override { return ( --m_Counter > 0 ); }
virtual bool CanFire() const override { return true; }
};
private:
Vec2f m_Pos;
std::queue< std::unique_ptr<State> > m_Schedule;
std::unique_ptr< Weapon > m_upWeapon;
private:
static const float ms_HalfSize; //この敵のサイズ(面倒なのでstaticな定数)ですわ.
};
const float SimpleEnemy::ms_HalfSize = 7.0f;
ああ、一言二言多いのが玉に瑕なお嬢様ですこと。usao さんが書きました:3年前…ですが,わたくし… 正直,これの何が面白いのかちっともわかりませんわ.
ひらすら「かわす」だけの内容では 5 秒で飽きてしまいましてよ!
(これでは,「敵」や「弾」の具体実装を増やす意欲もわきませんわね.)
private: //「シーン」に関する保持データ
enum class SceneIndex{ Title=0, GamePlay=1 } m_CurrSceneIndex;
std::unique_ptr<Scene> m_Scenes[2];
private: //「カレントシーン」
std::unique_ptr<Scene> &CurrScene(){ return m_Scenes[ (int)m_CurrSceneIndex ]; }
UpdateResult GameLogic::Update( const Input_b &rInput, RndGenerator &Rnd )
{
if( rInput.KeyStateHist( 'Q' ) & 0x01 )
{ return UpdateResult(false,true); }
return UpdateResult( CurrScene()->Update( *this, rInput, Rnd ) );
}
bool GameLogic::Paint(HDC hdc, int W, int H)
{ CurrScene()->Paint( hdc, *this ); return true; }