クラスまみれのゲームプログラミング入門

11:タスク処理を実装する(1)

 ゲームでは一連の処理をまとめて行うためのシステム開発が不可欠です。プレイヤーの処理にしても、背景の処理にしても、処理を行うための関数を同じ名称で統一しておけば、ループに組み込ませることができ、コーディングが簡潔になるばかりでなく、見た目もさっぱりして便利だと思いませんか?


これらはすべてCGameObjectの派生クラスなので……

このようにfor文などを使って一気に処理を行うことができるはず。

 まずはCGameObjectに処理を実行させるための共通関数「Exec()」を宣言させましょう。ただし、CGameObjectクラス自体に何らかの処理をしてもらうことはないので、空の処理内容を入れておきます(9行目)。同様にオブジェクト生成時ではなく、タスク追加時に初期化が同時にできるよう「Init()」関数も用意しておきましょう。

GameObject4.h
class CGameObject
{
	/* 省略 */
	
protected:
	/* 省略 */
	
	void Init(){}
	void Exec(){}
};

 CGameObjectの派生クラスに、Exec()とInit()を宣言して、関数を上書きすれば完成です(下は一例)。

exectest.cpp
class CPlayer : public CGameObject
{
private:
	int x, y;
protected:
	void Init()
	{
		// タスク追加時に初期化
		x = 320;
		y = 0;
	}
	void Exec()
	{
		// 下方向へ移動
		y++;
	}
};

 とはいってみたものの、実はこの方法のままだと結構不便なのです。なぜかというと、一度タスク処理を行うリストへは、共通のクラスである「CGameObject」型に変換してから登録しなければならないのは当然としても、Exec()を実行するときには、本来のクラスである「CPlayer」型に戻してやらないと、CGameObject::Exec()の記述内容が実行されてしまう、つまり、プレイヤーの移動処理が無視されてしまうので、結局何も処理していないのと同じになってしまうからです。


転職、求人情報ならリクルートの転職サイト【リクナビNEXT】

 わざわざクラスを元に戻さなくても、CGameObject::Exec()でCPlayer::Exec()を実行させたい……そんなわがままをかなえてくれる機能がC++には備わっています。それが仮想関数です。仮想関数化されれば、基幹クラスの型のまま関数を実行しても、そのクラスが派生クラスを持っているのであれば、派生クラスの同名関数を優先して実行してくれます。

 関数の仮想化は、対象となる関数の先頭にvirtualを付け加えるだけ。ちなみに派生クラスからさらに派生したクラスの同名関数を優先的に実行させたい場合は、派生クラス内の関数にもvirtualを付け加えておく必要があります。

 仮想関数が存在する基底クラスにはvirtualを付け加えたデストラクタ(仮想デストラクタ)も作成しておいた方が無難です。仮想デストラクタがないと、基底クラスのデストラクタが優先して実行され、派生クラスのデストラクタは機能せず、やるべき後処理が行われない可能性があるからです。

GameObject4_2.h
#pragma once

/* 省略 */

class CGameObject
{
	/* 省略 */

public:
	virtual ~CGameObject(){}
	
	/* 省略 */

protected:
	/* 省略 */
	
	virtual void Init(){}
	virtual void Exec(){}
};

 次回はCGameObjectの派生クラスを一元管理するためのリストを作成します。