タスク管理システムを作ってみる

※この記事は2016年8月1日に投稿した記事をソースコード等が見やすいよう修正し、投稿し直した物です。

タスク管理システムとは。

タスク管理システムとはその名の通り、オブジェクトごとのタスクを管理するクラスです。

例えば、2Dのシューティングゲームを作る際には「自機」「敵」「双方の弾」「背景の動き」など、ゲームを構成するオブジェクトがありますが、そのオブジェクトの更新の順番や、描画の順番を動的に変更することにより、列挙型などで処理順番が決められて更新、描画し忘れの心配もなくなり、作業の効率を上げることが出来ます。(そもそも忘れなければいいd)

ちなみに、タスクの処理順の管理に「連結リスト」を使い、今回はその中で一番簡単な片方向リストを紹介します。

連結リスト - Wikipedia

とりあえず実装してみます。

まず最初に次のタスクと仕事の中身を呼び出すクラスを作ります

Task.h

class CTask
{
 //仕事のポインタ
 CToDo *node;

 //次のタスクのポインタ
 CTask *next;

 //この仕事の認識番号
 int id;

public:
 //コンストラクタ
 CTask();
 //タスクを前に追加する
 void SetTask(CToDo *pNode, int a_ID);

 //仕事の更新
 void Update()
 {
  node->Update();
 }
 //仕事の描画
 void Render()
 {
  node->Render();
 }
 //仕事を取得
 void SetNode(CToDo *pNode){ node = pNode; }
 //IDを取得
 void SetId(int tmp){ id = tmp; }
 //IDを返す
 int GetId(){ return id; }
 //次のタスクのポインタを返す
 CTask *GetNext(){ return next; }
 //次のタスクのポインタを取得
 void SetNext(CTask *tmp){ next = tmp; }

};

この「CToDo *node」が仕事内容のクラスです。

ではついでに仕事内容も作ります。
(CTaskクラスでCToDoのインスタンスを呼び出している部分があるため、CToDoはCTaskの上で定義しましょう。)


//仕事の中身を管理するクラス
Task.h

class CToDo
{
 //継承先でも使えるようにする
protected:
 //仮のデータ(ここでゲームオブジェクトを入れる)
 int num;

public:
 //コンストラクタ
 CToDo();
 //デストラクタ
 virtual ~CToDo(){};
 //更新
 virtual void Update();
 //描画
 virtual void Render();
 void SetNum(int tmp){ num = tmp; }

};

次にクラスで定義した関数の中身を作っていきます。

Task.cpp

#include"Task.h"
#include<iostream>
using namespace std;

//コンストラクタ
CToDo::CToDo()
{
 //番号初期化
 num = 0;
}

//更新
void CToDo::Update()
{
 cout << num << "番更新" << endl;
}
//描画
void CToDo::Render()
{
 cout << num << "番描画" << endl;
}
//コンストラクタ
CTask::CTask()
{
 //初期化
 node = NULL;
 next = NULL;
 id = 0;
}

//仕事を前に追加する
void CTask::SetTask(CToDo *pNode, int a_ID)
{
 //CTaskを動的確保
 CTask *task = new CTask();
 //IDを取得
 task->SetId(a_ID);
 //仕事を取得
 task->SetNode(pNode);
 /*元々次にあった仕事を生成した仕事の次にセットして生成したものを次の仕事にする*/
 task->SetNext(this->GetNext());
 this->SetNext(task);
}

これでタスク管理システムが完成しました。

ではちゃんと動くかの確認のため、実際に使用してみます。



main.cpp

 #define TODO_NUM 10
int main()
{
 //仕事の始まり
 CTask loopStart;
 //仕事の終わり
 CTask loopEnd;
 //終わりのIDを0にする
 loopEnd.SetId(0);
 //終わりを最初の次の仕事にセットする
 loopStart.SetNext(&loopEnd);
 //1~10の仕事を生成
 CToDo todo[TODO_NUM+1];
 for (int num = 1; num <= TODO_NUM; num++)
 {
  todo[num].SetNum(num);
  //仕事を入れていく
  loopStart.SetTask(&todo[num], num);
 }
 //更新
 for (//スタートの次の仕事のポインタを取得
  CTask *task = loopStart.GetNext();
  //loopEndと同じIDで無いかチェック
  task->GetId() != loopEnd.GetId();
  //次の仕事のポインタ取得
  task = task->GetNext())
 {
  task->Update();
 }
 //描画
 for (//スタートの次の仕事のポインタを取得
  CTask *task = loopStart.GetNext();
  //loopEndと同じIDで無いかチェック
  task->GetId() != loopEnd.GetId();
  //次の仕事のポインタ取得
  task = task->GetNext())
 {
  task->Render();
 }
 //削除
 //削除するためのポインタ宣言
 CTask *deleteTask = NULL;
 for (//スタートの次の仕事のポインタを取得
  CTask *task = loopStart.GetNext();
  //loopEndと同じIDで無いかチェック
  task->GetId() != loopEnd.GetId();
  //次の仕事のポインタ取得
  task = task->GetNext())
 {
  delete deleteTask;
  deleteTask = task;
 }
 delete deleteTask;
 return 0;
}


実行すると10~1番の更新描画処理がされます。

これをループさせるとゲームループの完成です。

IDを取得する際に一緒に優先度も決めて後でソートすることにより動的な処理順番の管理が出来ます。