一週1~2回のペースでやっていこうと思います。
*ユニットの選択を実装する
まず、プレイヤーの行動をクラスとして独立させます。
Worldクラスの肥大化を防ぐ役目もありますが、
将来、敵のプレイヤーを実装する上でも有用だと考えられるからです。
CODE:
class Player
{
public:
Player(){}
virtual ~Player(){}
virtual void Update() = 0;
};
シンプルですね。
自分となるプレイヤーを実装する前に、ユニットマネージャを改変します。
今の構造ではWorldクラスがその実体へのポインタをもつので、
Worldクラスを経由するか、ポインタを譲りうけないかしないと、
ユニットの実体へアクセスすることができませんが、
ゲームオブジェクトは様々な所から参照されることが多いので、
シングルトンパターンを適用することで、グローバルなクラスにします。
CODE:
class Unit;
class UnitManager
{
//IDで管理する
std::map m_Units;
private:
//シングルトン
UnitManager(){}
public:
~UnitManager(){}
static UnitManager& Instance()
{
static UnitManager UM;
return UM;
}
//コンストラクタとデストラクタの代わり
void Init();
void Del();
void Update();
void Render();
//指定IDのユニットのアドレスを返す
Unit* GetUnit(int id);
//ユニットリストを返す
std::map* GetUnitList(){ return &m_Units; }
};
これで、自由にユニットへアクセスできるようになりました。
次に、プレイヤーを実装します。
CODE:
class Unit;
class MyPlayer :public Player
{
//マウスのポジション
Vec2D m_MousePos;
//ドラッグ開始時の座標を記録
Vec2D m_FirstMousePos;
//マウスの状態
bool m_bLeftClick; //左クリック
bool m_bLeftDrag; //左ドラッグ
bool m_bRightClick; //右クリック
//選択しているユニットのリスト
std::list m_SelectedUnitList;
public:
MyPlayer();
~MyPlayer(){}
void Update();
void Render();
};
定義のコードは長いので、スポイラーからご覧ください。
► スポイラーを表示
CODE:
MyPlayer::MyPlayer()
:m_bLeftClick(false),
m_bLeftDrag(false),
m_bRightClick(false)
{}
const int DRAG_TIME = 120;
void MyPlayer::Update()
{
static int preTime = -1;
static bool preLClickFlag = false;
//クリックされ続けている
if(preLClickFlag)
{
//左クリックされた
if(GetMouseInput() & MOUSE_INPUT_LEFT)
{
//ある時間を超えていたら、ドラッグと見なす
m_bLeftDrag = GetNowCount() - preTime > DRAG_TIME;
}
else
{
//クリックされ続けていない
preLClickFlag = false;
//ドラッグならば
if(m_bLeftDrag)
m_bLeftDrag = false;
//ドラッグ時間に満たない時間で離されたのでクリック
else
m_bLeftClick = true;
}
}
else
{
//左クリックされた
if(GetMouseInput() & MOUSE_INPUT_LEFT)
{
preLClickFlag = true;
//座標を記録
int mx, my;
GetMousePoint(&mx, &my);
m_FirstMousePos = Vec2D(mx, my);
//選択ユニットクリア
m_SelectedUnitList.clear();
//時刻を記録
preTime = GetNowCount();
}
else
{
m_bLeftDrag = false;
m_bLeftClick = false;
}
}
//右クリックについては単純
if(GetMouseInput() & MOUSE_INPUT_RIGHT)
{
m_bRightClick = true;
}
//マウス座標の更新
int mx, my;
GetMousePoint(&mx, &my);
m_MousePos = Vec2D(mx, my);
//ユニットを選択する処理
//現段階ではユニットのリストを直接弄り、
//選択範囲にあるか判定するが、
//いずれは別のクラスに処理を任せる予定…
//左上の座標と、右下の座標を取得
Vec2D LT, RB;
if(m_FirstMousePos.x > m_MousePos.x)
{
if(m_FirstMousePos.y > m_MousePos.y)
{
LT = m_MousePos;
RB = m_FirstMousePos;
}
else
{
LT = Vec2D(m_MousePos.x, m_FirstMousePos.y);
RB = Vec2D(m_FirstMousePos.x, m_MousePos.y);
}
}
else
{
if(m_MousePos.y > m_FirstMousePos.y)
{
LT = m_FirstMousePos;
RB = m_MousePos;
}
else
{
LT = Vec2D(m_FirstMousePos.x, m_MousePos.y);
RB = Vec2D(m_MousePos.x, m_FirstMousePos.y);
}
}
//クリックの場合の処理はまた後で改良予定
if(m_bLeftClick)
{
LT = m_MousePos - Vec2D(5,5);
RB = m_MousePos + Vec2D(5,5);
}
//ドラッグ中は常に更新する
if(m_bLeftDrag)
{
m_SelectedUnitList.clear();
}
if(m_bLeftDrag || m_bLeftClick)
{
std::map*
UnitList = UnitManager::Instance().GetUnitList();
std::map::iterator it;
for(it = UnitList->begin();
it != UnitList->end(); ++it)
{
if( LT.x second->Pos().x
&& RB.x > it->second->Pos().x
&& LT.y second->Pos().y
&& RB.y > it->second->Pos().y )
{
m_SelectedUnitList.push_back(it->second);
}
}
}
}
void MyPlayer::Render()
{
if(m_bLeftDrag)
{
//四角形描画
DrawBox((int)m_FirstMousePos.x,
(int)m_FirstMousePos.y,
(int)m_MousePos.x,
(int)m_MousePos.y,
GetColor(255,0,255),
false);
}
std::list::iterator it;
for(it = m_SelectedUnitList.begin();
it != m_SelectedUnitList.end(); ++it)
{
//適当な印
DrawCircle((int)(*it)->Pos().x, (int)(*it)->Pos().y, 3, GetColor(255,0,255), false);
}
}
ドラッグ開始位置→ドラッグ終了位置で四角形を描き、
その範囲にいるユニットを選択ユニットのリストに追加します。
時間的な関係で、今回はここで終了です。
次回は、選択したユニットを動かす、ということをしたいと思いますが、
その実装にあたって、一種のメッセージング機能を追加したいと思っています。
現時点では、ユニットはマウスが押されているか判断をして、
それで自分のステートを変えていますが、
これはユニットの挙動として明らかに自律し過ぎています。
ユニットは自ら動く条件を探索するのではなく、
「動け」という命令が来たときに動くのが理想です。
ですから、この「動け」という命令を、
メッセージというオブジェクトに値として付与します。
ユニットのひとつひとつにはIDが振り分けられているので、
それで送信者・受信者を判別すれば管理しやすいはずです。
これを次回は実装できたらいいな、と思います。では。