Ocult stg タスクシステムについて

タスクシステムとは

複数の処理を同時に実行しているように見せかける方法であり、シューティングゲームに限らず様々なプログラムで応用できます。処理A、処理B、処理Cがあったとすると、処理A→処理B→処理C→処理A→・・・と順番に切り替えながら処理を行うことで並列に処理しているように見せかけます。この方法を用いる利点はいくつかあります。

タスクシステムにはさまざまな実装方法があり、今回使用したタスクシステムについて C++のオブジェクト指向(?)な点を生かして、タスクシステムを構築します。(したがって、Cではまったく同じようなことはできません)

このソースを用いたタスクシステムは、メモリの配置は比較的OSに任せる部分が多いです。(より、メモリ効率やスピードを重んじるならメモリ管理まで自分で行う必要があります)

定義:タスクシステム


//tasks.h
///
///タスク(タスクシステムのひとつづつの単位)
class TASK{
public:
    ///処理用仮想関数()
    virtual const int main_func(){return 0;};

    virtual ~TASK(void){return;};
    TASK* next;
    TASK* previous;
    ///
    ///pData用のスペース
    unsigned char workspace[1024];
    //const int main_func();
};

///
///削除予定リスト用構造体
struct del_list{
    TASK* del;
    del_list* previous;
};
class tasks
{
public:
    tasks(void);
    ~tasks(void);

    ///
    ///新たにタスクを一つ加える
    int AddTask(TASK* add);
    ///
    ///指定したタスクをひとつ取り除く
    ///タスクの中に含まれていないときは何もしない
    int DelTask(TASK* del);
    ///
    ///ひとつ何もしないタスクを作る(基本的に使わない)
    int MakeTask(void* data);
    ///
    ///現在のタスクを削除する
    ///自動的に次のタスクに移動する
    ///注:自分自身でこれを呼び出してはいけない
    int DelMainTask();
    ///
    ///次のタスクに移動する
    int TaskNext();
    ///
    ///タスクの削除予定リストに一つタスクを追加する
    int preDelTask(TASK* del);
    ///
    ///タスクの削除予定リストをすべて削除する
    int ClearTask();
    ///
    ///次のタスクに移動する
    int TaskCicle();
    int DelAllTask();
    //int SendTasksMessage(int message,int w,int l);

    ///タスクの削除予定リスト
    del_list* del_old;
    ///最初のタスク何もしない(これは必ず削除されない)
    TASK dammy;
    ///現在のタスク
    TASK* Main_task;
private:
    ///
    ///現在のタスクの処理を実行する。
    int TaskFetch();
    ///
    ///タスクの数の合計。
    int Task_cnt;

};

実装例

#include "tasks.h"

tasks::tasks(void)
{
    Task_cnt=0;
    Main_task=&dammy;
    Main_task->next=Main_task;
    Main_task->previous=Main_task;
    del_old=NULL;

}

tasks::~tasks(void)
{
    while(Task_cnt>0){
        if(Main_task==&dammy){
            TaskNext();
        }
        DelMainTask();
    }
}
int tasks::TaskFetch(){
    if(Main_task==&dammy){
    return 0;
    }

    Main_task->main_func();
    return 1;
}
int tasks::TaskCicle(){
    int cnt;
    TaskNext();
    cnt=Task_cnt+1;
    for(;cnt>0;cnt--){
        TaskFetch();
        ClearTask();
        TaskNext();
    }
    return 0;
}
int tasks::TaskNext(){

    Main_task=Main_task->next;
    return 1;
}
int tasks::AddTask(TASK* add){

    add->next=Main_task;
    add->previous=Main_task->previous;
    Main_task->previous->next=add;
    Main_task->previous=add;
    Task_cnt++;
    return 1;

}
int tasks::DelMainTask(){
    TASK* del;
    if(Main_task==&dammy){
        if(Main_task!=Main_task->next){
            Main_task=Main_task->next;
        }else{
            return -1;
        }
    }
    del=Main_task;
    Main_task=Main_task->next;
    return DelTask(del);
}
int tasks::DelTask(TASK* del){
    if(del==&dammy){
        return -1;
    }
    if(del==Main_task){
        Main_task=Main_task->next;
    }
    del->next->previous=del->previous;
    del->previous->next=del->next;


    delete del;

    Task_cnt--;
    return Task_cnt;
}
int tasks::MakeTask(void* data){
    TASK *n;
    n= new TASK;
    data=(n->workspace);
    AddTask(n);
    return 1;
}

int tasks::preDelTask(TASK* del){
    del_list* n;
    if(del==NULL){
        return 0;
    }
    if(del_old!=NULL&&del==del_old->del){
        return 0;
    }
    n=new del_list;
    n->del=del;
    n->previous=del_old;
    del_old=n;
    return 1;
}
int tasks::ClearTask(){
    del_list* n;
    n=del_old;
    while(del_old!=NULL){

        DelTask(del_old->del);
        n=del_old;


         del_old=del_old->previous;
        free(n);
    }

    del_old=NULL;
    return Task_cnt+1;//ダミーを含むタスク数
}
int tasks::DelAllTask(){
    int ret;
    ret=ClearTask();
    while(ret>0){
        ret=DelMainTask();
    }
    Task_cnt=0;
    return 1;
}

使い方

このクラスの使い方はまず処理させたいクラスを作り、TASKクラスから継承させる。(クラスには必ずmain_funcメソッドを持たせる)

最後にaddTask()で登録する。
あとは、必要な時にTaskCicle()を呼び出せばタスクに入っている処理が一気に実行される。

#include "tasks.h"
#include <stdio.h>
class Test :
    public TASK
{
public:
    Test(void);
    ~Test(void);
    const int main_func(){
        printf("this is test");
        return 0;
    };
};
int main(){
    class tasks task_sys;

    task_sys.addTask();

    //ゲーム中のループ
    for(int i=0;i<10;i++){
        task_sys.TaskCicle();
    }
    return 0;
}

さらに、今回のシューティングゲームでは汎用性のため、クラスに持たせたいデータがあればworkspaceに登録しておくことでクラスサイズの統一化を図ることができる。
また、実際のシューティングゲームではこのTASKクラスをさらにrelationクラスを間にはさんでからそれぞれの処理用クラス(弾や敵)を継承させることで、当たり判定などもここで計算できるようにした。

まあ、詳しくは後々

<戻る  n研究所トップへ    Ocult〜stgのページへ