ゲームプログラミングの骨組み:その2 前編

アバター
a5ua
記事: 199
登録日時: 14年前

ゲームプログラミングの骨組み:その2 前編

投稿記事 by a5ua » 14年前

続いて、「2.画像や音楽などのリソース管理」について考えていきます。

まず、画像などを一元管理するために、次のような形式のテキストファイルを考えます。(内容に意味は特にありません。)

【image_list.txt】

background001:bg001.png
character001:char001.png,12,3,4,32,32

「画像ID:パラメータ」という並びになっています。
まずは、ファイルから文字列を読み込んで、文字列を分割するために、以下のような関数を作っておきます。

【utility.hpp】

CODE:

#pragma once

#include 

namespace a5ua
{

	//// sourceをdelimiterごとに区切り、destを通して出力する
	template 
	void tokenize(const StrT &source, const StrT &delimiter, OutIt dest)
	{
		// delimiterが空ならsourceそのものを返して終了
		if (delimiter.empty()) {
			*dest = source;
			return;
		}

		StrT::size_type offset= 0;	// 探索位置
		StrT::size_type pos;		// delimiterの位置

		while ((pos = source.find(delimiter, offset)) != StrT::npos) {
			// delimiterが見つかったら、文字を区切って出力
			*dest = source.substr(offset, pos - offset);
			// 探索位置を更新
			offset = pos + delimiter.length();
		}
		// delimiterが見つからなかったら、残り全部を出力
		*dest = source.substr(offset);
	}

	//// delimiterが文字列リテラルの場合
	template 
	void tokenize(const std::basic_string &source, const CharT *delimiter, OutIt dest)
	{
		tokenize(source, std::basic_string(delimiter), dest);
	}

}

次にオブジェクトを作るためのFactoryクラステンプレートを考えます。このクラスでは、コンストラクタの引数に上記のようなテキストファイルから、IDとパラメータの対応関係を読み込み、保持します。
なぜ、このようなクラスを作るかというと、どんなオブジェクトも、IDを表す文字列から、生成できるようになるからです。

【Factory.hpp】

CODE:

#pragma once

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include "utility.hpp"

namespace a5ua
{
	//// ファクトリの基底クラス
	template 
	class BasicFactory
	{
	protected:
		typedef std::basic_string tstring;
		typedef std::map database_type;

	public:
		// 使用するデータベースファイル名を指定
		// データベースの形式は、「id:value」を想定
		BasicFactory(const TCHAR *database_name)
		{
			std::basic_ifstream ifs(database_name);

			if (!ifs.is_open()) {
				assert(false);
			}

			tstring line;
			while (std::getline(ifs, line)) {
				std::vector v;
				// 「:」で区切る
				a5ua::tokenize(line, _T(":"), std::back_inserter(v));
				assert(v.size() == 2); 

				// ID二重登録のチェックをすること!
				m_database.insert(std::make_pair(v[0], v[1]));
			}
		}

		virtual ~BasicFactory()
		{

		}

		// idを指定して新しいオブジェクトを作る
		virtual T *create(const TCHAR *name) const = 0;

	protected:

		database_type m_database;

	};

}
さて、最後にオブジェクトを一元管理するクラスを考えます。前述のFactoryクラスは、「ID:パラメータ」を文字列の形で保持していました。以下のStoreクラステンプレートは、「ID:オブジェクト」という具合に、実際のオブジェクトを保持します。

【Store.hpp】

CODE:

#pragma once

#include 
#include 
#include 
#include 

#include "Factory.hpp"

namespace a5ua
{
	// 実体のポインタをdeleteできないように
	// Handleを介したポインタへのアクセス機能だけを提供する
	template 
	class Handle
	{
	public:
		Handle(T *body = 0) : m_body(body)
		{

		}

		T *operator->()
		{
			return m_body;
		}

		const T *operator->() const
		{
			return m_body;
		}

	private:
		T *m_body;
	};

	// オブジェクトの倉庫
	template 
	class Store
	{
	private:
		typedef std::map map_type;

	public:

		// Tをcreateするファクトリを指定
		Store(std::auto_ptr> factory) : m_factory(factory)
		{

		}

		// 保管オブジェクト全削除
		~Store()
		{
			for (map_type::const_iterator itr = m_map.begin(); itr != m_map.end(); ++itr) {
				delete itr->second;
			}
		}

		// 名前を指定して削除
		void release(const TCHAR *name)
		{
			map_type::const_iterator pos = m_map.find(name);

			if (pos != m_map.end()) {
				delete pos->second;
				m_map.erase(pos);
			}
		}

		// 指定したIDのハンドルを取得
		// もし対応するオブジェクトがない場合は、新たにオブジェクトを生成して、そのハンドルを返す
		Handle get(const TCHAR *name)
		{
			map_type::const_iterator pos = m_map.find(name);

			if (pos != m_map.end()) {
				return pos->second;
			}

			T *new_object = m_factory->create(name);
			m_map[name] = new_object;

			return  Handle(new_object);
		}

	private:
		Store(const Store &);
		Store &operator=(const Store &);

		map_type m_map;
		std::auto_ptr> m_factory;

	};

}
このクラスは、ユーザからの要求によって、対応するオブジェクトを返すわけですが、もしオブジェクトが登録されていなかった場合は、あらかじめ指定したFactoryクラスで新たにオブジェクトを生成します。また、生成・保持するオブジェクトの型は「T *」ですが、実際にユーザに返す型は「Handle」となっています。このHandleクラステンプレートは、内部にポインタだけをもち、operator->のみを提供しています。なぜ、このようなクラスを用意したかというと、T *を直接ユーザに返してしまうと、そのポインタを勝手にdeleteされる可能性があるからです。これをHandleに包んで、Handleの値を返せば、ユーザは、オブジェクトの寿命を気にする必要はありません。

今回は、具体的なサンプルソースはなしです。後編へ続く。
最後に編集したユーザー a5ua on 2010年11月05日(金) 02:06 [ 編集 1 回目 ]

コメントはまだありません。