Ocult stg タスクシステムについて
タスクシステムとは
複数の処理を同時に実行しているように見せかける方法であり、シューティングゲームに限らず様々なプログラムで応用できます。処理A、処理B、処理Cがあったとすると、処理A→処理B→処理C→処理A→・・・と順番に切り替えながら処理を行うことで並列に処理しているように見せかけます。この方法を用いる利点はいくつかあります。
- ゲーム全体を細かいいくつかのオブジェクトに分けて書くことができる。
- 並列処理でよく問題になる変数への同時アクセス等の問題がない。
- 同期が完全にとれている
タスクシステムにはさまざまな実装方法があり、今回使用したタスクシステムについて
C++のオブジェクト指向(?)な点を生かして、タスクシステムを構築します。(したがって、Cではまったく同じようなことはできません)
このソースを用いたタスクシステムは、メモリの配置は比較的OSに任せる部分が多いです。(より、メモリ効率やスピードを重んじるならメモリ管理まで自分で行う必要があります)
定義:タスクシステム
class TASK{
public:
virtual const int main_func(){return 0;};
virtual ~TASK(void){return;};
TASK* next;
TASK* previous;
unsigned char workspace[1024];
};
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();
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のページへ