SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

ゲームプログラミング入門

本格的なシューティングゲームを実現するタスクシステム

複数の処理を管理・実行する仕組み


  • X ポスト
  • このエントリーをはてなブックマークに追加

拡張タスクシステムの定義

 本稿のサンプルゲームに依存する処理をまとめたTaskExクラスの宣言を見てください。多くのタスクに必要と思われるメンバ変数、DirectXに依存する描画関数、そしてグループ分けの機能を追加しています。

 グループ分けのカラクリは簡単で、全てのタスクにグループ識別子を持たせるだけです。グループ分けの機能は必須であり、Taskクラスのメンバ変数にしようか迷ったのですが、最初の直感を信じてTaskExクラスのメンバ変数としました。好みによってTaskクラスのメンバ変数にしても問題ありません。

#pragma once

#include"TaskSystem.h"
#include"main.h"        // for DirectX

#include<math.h>
#include<time.h>

extern IDirect3DDevice9 *g_pD3DDevice;  // main.cpp

struct DATA
{
    int id;
    float cx,cy;
    float hit_cw,hit_ch;
    float speed;
    DWORD life,max_life;
    float rot;
    BYTE alpha;
};

class TaskEx : public Task
{
protected:
    int m_id;           // グループ識別子
    float m_cx,m_cy;
    float m_hit_cw,m_hit_ch;
    float m_speed;
    DWORD m_life,m_max_life;
    IDirect3DTexture9 *m_pTexture;
    float m_rot;
    BYTE m_alpha;
public:
    TaskEx(void);

    static void InitTaskList(void);

    void Delete(int id);

    static void DrawTask(ID3DXSprite *pSprite);
    static void DrawTask(ID3DXSprite *pSprite,int id);

    static DWORD GetCount(int id);
    static void* GetTask(int id,DWORD number);
    void GetData(DATA *pData);

    virtual void Draw(ID3DXSprite *pSprite)=0;  // 描画関数
};

enum  // グループ識別子
{
    PLAYER,ENEMY,MYSHOT,ENEMYSHOT,EXPLOSION,CONTROLPLAYER,
    CONTROLENEMY,STAGE,LIFEBAR,SCORE,TITLE,GAMEOVER
};

タスクリストとゲームの初期化

 TaskExクラスのInitTaskListメンバ関数は、TaskクラスのInitTaskListメンバ関数を呼び出した後に、ゲームの初期化をします。ここでは、乱数の初期化、およびTitleタスクの生成・追加をすることにしました。

#include"TaskSystemEx.h"
#include"task.h"  // for Title

// タスクリストとゲームの初期化
void TaskEx::InitTaskList(void)
{
    Task::InitTaskList();

    srand((unsigned)time(NULL));

    new Title();
}

全てのタスクを描画

 タスクは、描画関数であるDrawを呼び出すことにより描画します。描画関数の内部でタスクの追加や削除が行われることはありませんが(可能ですが、自主的に禁止しましょう)、処理関数に似せたプログラムとしました。

// 全てのタスクを描画
void TaskEx::DrawTask(ID3DXSprite *pSprite)
{
    if(pSprite==NULL) return;

    TaskEx *task,*next;
    for(task=(TaskEx*)m_active; m_active; task=next)
    {
        task->Draw(pSprite);
        next=(TaskEx*)task->m_next;
        if(next==(TaskEx*)m_active) break;
    }
}

指定したグループのタスクだけを描画

 一部のグループのタスクを描画しない、などの用途に使います。

// 指定したグループのタスクだけを描画
void TaskEx::DrawTask(ID3DXSprite *pSprite,int id)
{
    if(pSprite==NULL) return;

    TaskEx *task,*next;
    for(task=(TaskEx*)m_active; m_active; task=next)
    {
        if(task->m_id == id) task->Draw(pSprite);
        next=(TaskEx*)task->m_next;
        if(next==(TaskEx*)m_active) break;
    }
}

自分以外の指定したグループのタスクだけを削除

 ボスの敗北に同期してザコを一掃する、特殊攻撃により弾を一掃する、などの用途に使います。

// 自分以外の指定したグループのタスクだけを削除
void TaskEx::Delete(int id)
{
    TaskEx *task,*next;
    for(task=(TaskEx*)this->m_next; task!=this; task=next)
    {
        if(task->m_id == id)
        {
            delete task;
        }
        next=(TaskEx*)task->m_next;
    }
}

指定したグループのタスク数を返す

 主にGetTaskメンバ関数と組み合わせて使います。

// 指定したグループのタスク数を返す
DWORD TaskEx::GetCount(int id)
{
    DWORD count=0;

    TaskEx *task,*next;
    for(task=(TaskEx*)m_active; m_active; task=next)
    {
        if(task->m_id == id) count++;
        next=(TaskEx*)task->m_next;
        if(next==(TaskEx*)m_active) break;
    }

    return count;
}

指定したグループの指定した順番のタスクを返す

 グループ識別子、および先頭を0とする順番によりタスクを指定し、取得するために使います。

// 指定したグループの指定した順番のタスクを返す
// タスクは優先度の値が小さい順につながっている
// number:先頭を 0 とする順番
void* TaskEx::GetTask(int id,DWORD number)
{
    DWORD count=0;

    TaskEx *task,*next;
    for(task=(TaskEx*)m_active; m_active; task=next)
    {
        if(task->m_id == id && count++ == number) return task;
        next=(TaskEx*)task->m_next;
        if(next==(TaskEx*)m_active) break;
    }

    return NULL;
}

自分のデータを取得

 自分以外のタスクを取得した場合に、アクセス制限に阻まれて参照できないメンバ変数たちを読み取るために使います。参照されては困るもの(例えばポインタ)は渡さないようにしましょう。

// 自分のデータを取得
void TaskEx::GetData(DATA *pData)
{
    if(pData==NULL) return;

    pData->id       = m_id;
    pData->cx       = m_cx;
    pData->cy       = m_cy;
    pData->hit_cw   = m_hit_cw;
    pData->hit_ch   = m_hit_ch;
    pData->speed    = m_speed;
    pData->life     = m_life;
    pData->max_life = m_max_life;
    pData->rot      = m_rot;
    pData->alpha    = m_alpha;
}

コンストラクタ

 メンバ変数の初期化を行います。

// コンストラクタ
TaskEx::TaskEx(void)
{
    m_id=0;
    m_cx=0; m_cy=0;
    m_hit_cw=0; m_hit_ch=0;
    m_speed=0;
    m_life=0; m_max_life=0;
    m_pTexture=NULL;
    m_rot=0;
    m_alpha=0xff;
}

次のページ
タスクシステムを動かす

修正履歴

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
ゲームプログラミング入門連載記事一覧

もっと読む

この記事の著者

ひよこ(ヒヨコ)

職業ゲームプログラマーを志す学生です。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/297 2006/07/24 19:47

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング