RTS製作日記。その8

アバター
MNS
記事: 35
登録日時: 15年前

RTS製作日記。その8

投稿記事 by MNS » 14年前

*マップの作成 - グラフ

本格的に、マップというものを定義します。
それにあたっては、グラフというものを使用します。
ここでいうグラフとは、私たちが日頃みてる統計図としてのグラフや、
関数などを表現するためのグラフなどを理論化したもので、
頂点(ノード)と連結(リンク)によって構成されます。
(らしいです。詳しいことは私も知りません-_-;)

数学の分野ではグラフ理論とよばれ、
木構造などのデータ構造として使われることもありますが、
今回は”経路”を表すものとして使用します。
graph.png
graph.png (27.39 KiB) 閲覧数: 85 回
●ノード

CODE:

class	Node
{
	//ノードは独自のIDを持つ
	int		m_ID;

	Vec2D	m_Pos;


	//_IDの操作
	static int nextID;

public:

	Node(Vec2D	pos)
		:m_Pos(pos)
	{
		m_ID = nextID;
		++nextID;
	}


	
	int		ID()const{ return m_ID; }
	Vec2D	Pos()const{ return m_Pos; }
};
ノードは座標とIDをもちます。
このIDはゲームエンティティのIDとは区別される、独自のIDです。

●リンク

CODE:

class	Link
{
	//IDで定める
	int		m_From;
	int		m_To;

	double	m_Cost;

public:

	Link(int from, int to, int cost)
		:m_From(from),
		 m_To(to),
		 m_Cost(cost)
	{}


	int		From()const{ return m_From; }
	int		To()const{ return m_To; }
};
リンクは、始点・終点・コストを持ちます。
コストは、ノード間の重み付けで、
最短距離の探索などに使用します。


●グラフ

CODE:

class	Graph
{
	
	//すべてのノード
	std::vector	m_Nodes;

	//リンク(隣接リスト)
	std::vector
				>		m_Links;



	//ノードを4方向に展開していく
	void	SeedNode(Vec2D to, Node& ori);

	//指定された座標にノードがあるか
	bool	AnyNodeThere(Vec2D pos);

public:

	Graph(){ MakeGraph(Vec2D(320,240)); }


	void	MakeGraph(Vec2D Origin);

	void	Render();
};
グラフクラスは、今のところ、
全ノードと全リンクの実体をもち、
グラフを生成するための関数と、
それを補助する関数をもちます。

リンクは「隣接リスト」というもので管理され、
(参照:http://ja.wikipedia.org/wiki/%E9%9A%A3% ... 9%E3%83%88
頂点のIDからO(1)でアクセスできるよう、
"リンクを値としてもつリスト、を値としてもつベクター”
で表されます。


次に、グラフを自動的に作成する関数をかきます。

CODE:

//ノード間の最小距離
const double NODE_minDist = 20.0;

//リンクを作成するにあたる最小距離の平方数
const double LINK_NODE_minDist
	= (NODE_minDist+NODE_minDist)*(NODE_minDist+NODE_minDist);

void	Graph::MakeGraph(Vec2D Origin)
{
	//Originの位置にノードを作成
	Node newNode(Origin);
	m_Nodes.push_back(newNode);

	Vec2D To;

	//ノードを敷き詰める
	To = Origin + Vec2D(-NODE_minDist, 0);
	SeedNode(To, newNode);
	To = Origin + Vec2D(+NODE_minDist, 0);
	SeedNode(To, newNode);
	To = Origin + Vec2D(0, -NODE_minDist);
	SeedNode(To, newNode);
	To = Origin + Vec2D(0, +NODE_minDist);
	SeedNode(To, newNode);

	//最小距離内にあるノードを隣接リストに追加する
	m_Links.resize(m_Nodes.size());
	for(int i = 0; i < m_Nodes.size(); ++i)
	{
		for(int k = 0; k < m_Nodes.size(); ++k)
		{
			double e = 0.0001;
			double dist = (m_Nodes[k].Pos() - m_Nodes[i].Pos()).LengthSQ();
			if( dist < LINK_NODE_minDist + e)
			{
				//間に障害物がないか調べる
				if(WallManager::Instance().
					IntersectWalls(m_Nodes[i].Pos(),
								   m_Nodes[k].Pos()))
				{
					continue;
				}

				//リンクを作成
				Link newLink(m_Nodes[i].ID(), m_Nodes[k].ID(), dist);
				m_Links[m_Nodes[i].ID()].push_back(newLink);
			}
		}
	}
}


void	Graph::SeedNode(Vec2D to, Node& ori)
{
	//ノード作成

	//置こうとしているところに、
	//すでにノードがないか確かめる
	if( AnyNodeThere(to) )
		return;

	//間に遮るものがないか確かめる
	if(WallManager::Instance().IntersectWalls(ori.Pos(), to))
	{
		return;
	}

	//ノード・リンクを作成
	Node newNode(to);
	m_Nodes.push_back(newNode);

	//拡張
	Vec2D To;
	To = to + Vec2D(-NODE_minDist, 0);
	SeedNode(To, newNode);
	To = to + Vec2D(+NODE_minDist, 0);
	SeedNode(To, newNode);
	To = to + Vec2D(0, -NODE_minDist);
	SeedNode(To, newNode);
	To = to + Vec2D(0, +NODE_minDist);
	SeedNode(To, newNode);
}


bool	Graph::AnyNodeThere(Vec2D pos)
{
	for(int i = 0; i < m_Nodes.size(); ++i)
	{
		if(m_Nodes.at(i).Pos() == pos)
			return true;
	}
	return false;
}
SeedNode関数は、いわゆる再帰関数で、
自分の周りの四方向にノードを作ります。
(その際、壁を越えてノードを置くことはない)
最後に、あるノードに関して、隣接するノードへのリンクを作成し、
それを隣接リストに登録します。

これで、グラフの作成は完了です。

実行結果:
sono3.png
sono3.png (35.86 KiB) 閲覧数: 76 回

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