场景管理:四叉树算法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;
}


以下是一个简单的四叉树算法实现,包括增删改查等基本功能。这里我们假设四叉树中存储的是二维平面点,每个点有一个唯一的ID和坐标。同时,我们使用文件读写来演示如何导入数据。 ```c++ #include <iostream> #include <fstream> #include <vector> #include <cmath> using namespace std; // 定义二维平面上的点 struct Point { int id; double x, y; Point(int id_, double x_, double y_) : id(id_), x(x_), y(y_) {} }; // 定义四叉树节点 class QuadTreeNode { public: QuadTreeNode(double x0_, double y0_, double x1_, double y1_) : x0(x0_), y0(y0_), x1(x1_), y1(y1_), isLeaf(true) {} ~QuadTreeNode() { for (auto& child : children) { delete child; } } // 插入一个点 bool insert(Point* p) { if (!contains(p->x, p->y)) { return false; } if (isLeaf) { points.push_back(p); if (points.size() > maxPointsPerNode) { subdivide(); } return true; } for (auto& child : children) { if (child->insert(p)) { return true; } } return false; } // 删除一个点 bool remove(Point* p) { if (!contains(p->x, p->y)) { return false; } if (isLeaf) { for (auto it = points.begin(); it != points.end(); ++it) { if ((*it)->id == p->id) { points.erase(it); return true; } } return false; } for (auto& child : children) { if (child->remove(p)) { return true; } } return false; } // 更新一个点 bool update(Point* p) { if (remove(p)) { return insert(p); } return false; } // 查找包含某个点的所有节点 void search(double x, double y, vector<Point*>& result) { if (!contains(x, y)) { return; } for (auto& p : points) { result.push_back(p); } if (!isLeaf) { for (auto& child : children) { child->search(x, y, result); } } } private: double x0, y0, x1, y1; bool isLeaf; vector<Point*> points; vector<QuadTreeNode*> children; static const int maxPointsPerNode = 4; // 每个节点最多存储的点数 // 判断一个点是否在范围内 bool contains(double x, double y) const { return x >= x0 && x < x1 && y >= y0 && y < y1; } // 分裂一个节点 void subdivide() { isLeaf = false; double xm = (x0 + x1) / 2, ym = (y0 + y1) / 2; children.push_back(new QuadTreeNode(x0, y0, xm, ym)); children.push_back(new QuadTreeNode(xm, y0, x1, ym)); children.push_back(new QuadTreeNode(x0, ym, xm, y1)); children.push_back(new QuadTreeNode(xm, ym, x1, y1)); for (auto& p : points) { for (auto& child : children) { if (child->insert(p)) { break; } } } points.clear(); } }; int main() { // 从文件中读取数据 vector<Point> data; ifstream fin("data.txt"); int id; double x, y; while (fin >> id >> x >> y) { data.emplace_back(id, x, y); } fin.close(); // 构建四叉树 QuadTreeNode root(0, 0, 100, 100); for (auto& p : data) { root.insert(&p); } // 测试增删改查功能 Point p1(1, 10, 10); root.insert(&p1); Point p2(2, 20, 20); root.update(&p2); root.remove(&p1); vector<Point*> result; root.search(30, 30, result); for (auto& p : result) { cout << p->id << " " << p->x << " " << p->y << endl; } return 0; } ``` 在这个示例中,我们首先从文件中读取了一些点的数据,然后用这些点构建了一个四叉树。接着,我们对四叉树进行了一些增删改查的操作,并最终输出了包含特定点的所有节点。在实际使用中,你可以根据需要调整节点的最大点数以及节点的范围等参数,以适应不同的场景
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值