简单实现了游戏中场景管理用到的四叉树算法
代码结构:
/* //被管理的对象类 */ #pragma once class Object { public: Object(float _x,float _y,float _width,float _height); ~Object(); public: //对象的属性,例如坐标和长宽,以左上角为锚点 float x; float y; float width; float height; };
#include "object.h" Object::Object(float _x,float _y,float _width,float _height): x(_x), y(_y), width(_width), height(_height) { } Object::~Object() { }
/* //四叉树节点类,用头节点代表四叉树 //坐标系坐上角为原点,左往右为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; //最大深度 };
#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; } }
#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; }