场景管理:四叉树算法C++实现

简单实现了游戏中场景管理用到的四叉树算法

代码结构:

  • object.h,object.cpp被管理的对象类
  • quad_tree_node.h,quad_tree_node.cpp四叉树类
  • main.cpp程序入口

object.h
/*
//被管理的对象类
*/
#pragma once
class Object
{
public:
	Object(float _x,float _y,float _width,float _height);
	~Object();
public:
	//对象的属性,例如坐标和长宽,以左上角为锚点
	float x;
	float y;
	float width;
	float height;
};

object.cpp
#include "object.h"


Object::Object(float _x,float _y,float _width,float _height):
	x(_x),
	y(_y),
	width(_width),
	height(_height)
{
}
Object::~Object()
{
}

quad_tree_node.h
/*
//四叉树节点类,用头节点代表四叉树
//坐标系坐上角为原点,左往右为x轴递增,上往下y轴递增
//本四叉树的策略是:1,插入时动态分配节点和删除节点,不是满树;2,当矩形区域完全包含某个节点时才获取或剔除;3,对象放在完全包含它的区域节点内,非根节点也存储对象
*/
#pragma once
#include <list>

//四叉树类型枚举
enum QuadType
{
	ROOT,         //根
	UP_RIGHT,     //象限Ⅰ
	UP_LEFT,      //象限Ⅱ
	BOTTOM_LEFT,  //象限Ⅲ
	BOTTOM_RIGHT  //象限Ⅳ
};

template <typename T>
class QuadTreeNode
{
public:
	QuadTreeNode(float _x,float _y,float _width,float _height,int _level,int _maxLevel,QuadType _quadType,QuadTreeNode *_parent);
	~QuadTreeNode();
public:
	void InsertObject(T *object); //插入对象
	std::list<T *> GetObjectsAt(float px,float py,float w,float h); //查询对象,获得一片区域里的对象链表,此处只考虑完全包含的
	void RemoveObjectsAt(float px,float py,float w,float h); //删除对象,删除一片区域里的对象和节点,此处只考虑完全包含的

private:
	bool IsContain(float px,float py,float w,float h,T *object) const; //判断某个区域是否包含某对象
	bool IsContain(float px,float py,float w,float h,QuadTreeNode<T> *quadTreeNode) const; //重载,判断某个区域是否包含某个节点
private:
	std::list<T *> objects; //节点数据队列
	//父、子节点,分四个象限
	QuadTreeNode *parent;
	QuadTreeNode *upRightNode;
	QuadTreeNode *upLeftNode;
	QuadTreeNode *bottomLeftNode;
	QuadTreeNode *bottomRightNode;
	//节点类型
	QuadType quadType;
	//坐标和长宽属性,左上角为锚点
	float x;
	float y;
	float width;
	float height;

	int level; //当前深度
	int maxLevel; //最大深度
};



quad_tree_node.cpp
#include "quad_tree_node.h"

template <typename T>
QuadTreeNode<T>::QuadTreeNode(
	float _x,float _y,float _width,float _height,
	int _level,int _maxLevel,
	QuadType _quadType,
	QuadTreeNode *_parent):
	x(_x),
	y(_y),
	width(_width),
	height(_height),
	level(_level),
	maxLevel(_maxLevel),
	quadType(_quadType)
{
	parent=_parent;
	upRightNode=nullptr;
	bottomLeftNode=nullptr;
	bottomRightNode=nullptr;
}

template <typename T>
QuadTreeNode<T>::~QuadTreeNode()
{
	if(level==maxLevel)
		return;
	//如果不是叶子节点,就销毁子节点
	parent=nullptr;

}

template <typename T>
bool QuadTreeNode<T>::IsContain(float px,float py,float w,float h,T *object) const 
{
	if(object->x>=px
		&&object->x+object->width<=px+w
		&&object->y>=py
		&&object->y+object->height<=py+h)
		return true;
	return false;
}

template <typename T>
bool QuadTreeNode<T>::IsContain(float px,float py,float w,float h,QuadTreeNode<T> *quadTreeNode) const
{
	if(quadTreeNode->x>=px
		&&quadTreeNode->x+quadTreeNode->width<=px+w
		&&quadTreeNode->y>=py
		&&quadTreeNode->y+quadTreeNode->height<=py+h)
		return true;
	return false;
}

template <typename T>
void QuadTreeNode<T>::InsertObject(T *object)
{
	//如果是叶子节点,则存在叶子节点
	if(level==maxLevel)
	{
		objects.push_back(object);
		return;
	}
	
	//非叶子节点,如果下层节点可以包含该对象,则递归构建子节点并插入对象,边构建边插入
	if(IsContain(x+width/2,y,width/2,height/2,object))
	{
		if(!upRightNode) //避免重复创建覆盖掉原来的节点
			upRightNode=new QuadTreeNode(x+width/2,y,width/2,height/2,level+1,maxLevel,UP_RIGHT,this);//如果没有子节点就创建子节点,parent节点是当前节点
		upRightNode->InsertObject(object);
		return;
	}
	else if(IsContain(x,y,width/2,height/2,object))
	{
		if(!upLeftNode)
			upLeftNode=new QuadTreeNode(x,y,width/2,height/2,level+1,maxLevel,UP_LEFT,this);
		upLeftNode->InsertObject(object);
		return;
	}
	else if(IsContain(x,y+height/2,width/2,height/2,object))
	{
		if(!bottomLeftNode)
			bottomLeftNode=new QuadTreeNode(x,y+height/2,width/2,height/2,level+1,maxLevel,BOTTOM_LEFT,this);
		bottomLeftNode->InsertObject(object);
		return;
	}
	else if(IsContain(x+width/2,y+height/2,width/2,height/2,object))
	{
		if(!bottomRightNode)
			bottomRightNode=new QuadTreeNode(x+width/2,y+height/2,width/2,height/2,level+1,maxLevel,BOTTOM_RIGHT,this);
		bottomRightNode->InsertObject(object);
		return;
	}
	//下层节点不能完全包含改对象,则插入到当前非叶子节点
	//这个判断也可以省去
	if(IsContain(x,y,width,height,object))
		objects.push_back(object);
}

template <typename T>
std::list<T *> QuadTreeNode<T>::GetObjectsAt(float px,float py,float w,float h)
{
	std::list<T *> resObjects;
	//如果当前节点完全被包含,把当前节点存的对象放到列表末尾,空链表也行
	if(IsContain(px,py,w,h,this))
	{
		resObjects.insert(resObjects.end(),objects.begin(),objects.end());
		//最后一层
		if(level==maxLevel)
			return resObjects;
	}

	//如果有下层节点就把下层节点包含的对象加进来
	if(upRightNode)
	{
		std::list<T *> upRightChild;
		upRightChild=upRightNode->GetObjectsAt(px,py,w,h);
		resObjects.insert(resObjects.end(),upRightChild.begin(),upRightChild.end());
	}		
	if(upLeftNode)
	{
		std::list<T *> upLeftChild;
		upLeftChild=upLeftNode->GetObjectsAt(px,py,w,h);
		resObjects.insert(resObjects.end(),upLeftChild.begin(),upLeftChild.end());
	}	
	if(bottomLeftNode)
	{
		std::list<T *> bottomLeftChild;
		bottomLeftChild=bottomLeftNode->GetObjectsAt(px,py,w,h);
		resObjects.insert(resObjects.end(),bottomLeftChild.begin(),bottomLeftChild.end());
	}
	if(bottomRightNode)
	{
		std::list<T *> bottomRightChild;
		bottomRightChild=bottomRightNode->GetObjectsAt(px,py,w,h);
		resObjects.insert(resObjects.end(),bottomRightChild.begin(),bottomRightChild.end());
	}
	return resObjects;
}

template <typename T>
void QuadTreeNode<T>::RemoveObjectsAt(float px,float py,float w,float h)
{
	//如果本层节点被包含则删除本层节点的对象
	//这个判断主要是对根节点起作用,其他子节点实际在上层都做了判断
	if(IsContain(px,py,w,h,this))
	{
		//清除本节点层的对象
		objects.clear();
		//最后一层
		if(level==maxLevel)
			return;

	}
	//如果有子节点且被包含就销毁子节点,注意别产生野指针
	//其实只要上层被包含了,下层肯定被包含,代码还需改进
	if(upRightNode&&IsContain(px,py,w,h,upRightNode))
	{
		upRightNode->RemoveObjectsAt(px,py,w,h);
		delete upRightNode;
		upRightNode=nullptr;

	}
	if(upLeftNode&&IsContain(px,py,w,h,upLeftNode))
	{
		upLeftNode->RemoveObjectsAt(px,py,w,h);
		delete upLeftNode;
		upLeftNode=nullptr;

	}
	if(bottomLeftNode&&IsContain(px,py,w,h,bottomLeftNode))
	{
		bottomLeftNode->RemoveObjectsAt(px,py,w,h);
		delete bottomLeftNode;
		bottomLeftNode=nullptr;

	}
	if(bottomRightNode&&IsContain(px,py,w,h,bottomRightNode))
	{
		bottomRightNode->RemoveObjectsAt(px,py,w,h);
		delete bottomRightNode;
		bottomRightNode=nullptr;
	}
}

main.cpp
#include <iostream>
#include <queue>
#include "object.h"
#include "quad_tree_node.h"
#include "quad_tree_node.cpp"

using namespace std;


int main(int argc,char *argv[])
{
	QuadTreeNode<Object> *quadTree=new QuadTreeNode<Object>(0,0,200,200,1,3,ROOT,nullptr);

	quadTree->InsertObject(new Object(50,50,100,100));
	quadTree->InsertObject(new Object(25,25,50,50));
	quadTree->InsertObject(new Object(62.5,12.5,25,25));
	quadTree->InsertObject(new Object(62.5,62.5,25,25));
	quadTree->InsertObject(new Object(63.5,63.6,25,25));
	quadTree->InsertObject(new Object(125,25,50,50));
	quadTree->InsertObject(new Object(112.5,62.5,25,25));

	quadTree->RemoveObjectsAt(100,0,110,110);
	list<Object *> resObjects=quadTree->GetObjectsAt(0,0,200,200);
	cout<<resObjects.size()<<endl;
	for(auto &t:resObjects)
		cout<<t->x<<' '<<t->y<<' '<<t->width<<' '<<t->height<<endl;
	delete quadTree;
	system("pause");
	return 0;
}


你可能感兴趣的:(游戏)