RTS游戏开发:基于四叉树的平面管理系统【加源码】

2024-02-28 22:40

本文主要是介绍RTS游戏开发:基于四叉树的平面管理系统【加源码】,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

为什么要有四叉树?

四叉树的思路。

四叉树源码。


为什么要有四叉树?

有一个算法名为flocking算法,作用是让一群单位模拟群行为的,这意味着每一个单位都需要获取周围邻居的信息,这里我们可以很容易地想到两重for循环来遍历,但是这样的时间复杂度是o(n^2),是我们接受不了的,所以我们就迫切需要一种数据结构来优化这一情景,很显然四叉树可以胜任,他可以用o(2*logn)的时间复杂度来寻找邻居对象和删除指定对象,用o(2*n*logn)的时间复杂度来为一群对象构建树,用o(n)的时间复杂度clear;

四叉树的思路。

 我们可以看上面这副图,可以发现,我们将一个区域划分4等分,并对其中一部分划分出来的区间再进行划分,形成一个递归过程。

我们将一个区域划分成多个子区域,并在子区域中存储对象,一个区域的对象只与存储该对象的长辈区域【包含该区域的区域被称为该区域的长辈区域】和本区域以及晚辈区域【被该区域包含的区域被成为该区域的晚辈区域】的对象进行距离比较,如果距离满足,那么我们就视为其为邻居对象;

经过这么一个区域划分,我们可以忽略掉大部分不可能成为该对象的邻居对象的对象,因为是采用了二分的思路,所以查询效率是非常高的;

四叉树在游戏中的运用主要是获取某一片区域中的对象,比如RTS游戏中的框选单位;

四叉树的源码解析。

实现我们先定义节点

template<class T>
class QTreeNode {
public:QTreeNode<T>* son[4];//该节点的4个孩子节点QTreeNode<T>* Father;//该节点的父节点std::list<T*> Data;//该节点存储对象的容器SquareArea<float> Bound;//区域QTreeNode(SquareArea<float> bound):Bound(bound) {for (int i = 0; i < 4; i++) {son[i] = nullptr;}}QTreeNode(){}/*Quadrant3 20 1*/void InstantiateNode() {//将区级分裂Pair<float> P1(Bound.BottomLeft.X, Bound.BottomLeft.Y + Bound.H / 2);Pair<float> P2(Bound.BottomLeft.X + Bound.W / 2, Bound.BottomLeft.Y);Pair<float> P3(Bound.Center.X, Bound.Center.Y + Bound.H / 2);Pair<float> P4(Bound.Center.X + Bound.W / 2, Bound.Center.Y);son[0] = new QTreeNode(SquareArea<float>(P1, P2));son[1] = new QTreeNode(SquareArea<float>(Bound.Center, Bound.BottomRight));son[2] = new QTreeNode(SquareArea<float>(P3, P4));son[3] = new QTreeNode(SquareArea<float>(Bound.TopLeft, Bound.Center));}~QTreeNode() {}};

其中SquareArea是已经写好的一个矩形区域类,主要作用是描绘平面空间中的一个矩形。

然后是四茶树管理类

template<class T>
class QTree {QTreeNode<T>* Head;int MaxData;//默认为1,为1时效率高int PretreatmentDeep;//预处理深度,这里可以选择性地调用,作用是先将区间划分n次,而且这些区间不存储对象,这种行为在某些情况下可以增加效率,在某些情况下会降低效率,这里本菜没有过多的研究SquareArea<float> TreeBound;//这个四叉树所覆盖的区域,也就是头节点所代表的矩形void pushBody(QTreeNode<T>* Node, T* Data, int Deep = 0);void extractBody(QTreeNode<T>* Node,SquareArea<float>* Bound, std::list<T*>* List);void extractBody(QTreeNode<T>* Node, CircularArea<float>* Bound, std::list<T*>* List);void clearBody(QTreeNode<T>* Node);
public:void Pretreatment(int Deep = 0) {//预处理if (Deep < PretreatmentDeep && Nodes->son[0] == nullptr) {Nodes->InstantiateNode();for (int i = 0; i < 4; i++) {Pretreatment(Deep + 1);}}}QTree(SquareArea<float> Bound,int Deep);void push(T* Data) { pushBody(Head, Data, 0); }//insert operatestd::list<T*> extract(SquareArea<float>* Bound){//提取一个矩形区间的对象std::list<T*> *List = new std::list<T*>();extractBody(Head, Bound, List);return List;}std::list<T*> extract(CircularArea<float>* Bound) {//提取一个圆形区域的对象std::list<T*> *List = new std::list<T*>();extractBody(Head, Bound, List);return List;}void clear() {//清除操作clearBody(Head);Head = new QTreeNode<T>(TreeBound);}void SetTreeBound(SquareArea<float> Bound) {//设置这个树所覆盖的区间Head->Bound = Bound;TreeBound = Bound;}~QTree() { clear(); }
};

下面是具体的代码实现;

插入代码的思路如下【这里考虑的是每个节点只存储一个对象】:

1、2、3ifelse语句

1:区间的孩子节点是否是空

  • 是,进入2
  • 不是,检测该区间的子区间是否与该对象有交集
    • 有交集,那么我们进入该区间所代表的节点,进行1操作 
    • 无交集,查询下一个子区间,如果查询完毕,返回上一层节点

2:该区间是否满了

  •  满了,将该区间进行分割,并查询子区域是否与对象有交集;
    • 有交集,那么我们进入该区间所代表的节点,进行1操作 
    • 无交集,查询下一个子区间,如果查询完毕,返回上一层节点
  • 没有满,进行3

3:将对象插入该节点,并返回上一个节点

提取的思路如下【这里考虑的是每个节点只存储一个对象】;

1:将该节点的对象放入到容器中,进行2

2:该节点的子节点是否为空

  • 是,返回上一个节点
  • 不是,进行3

3:查询该区域的子区域是否与该对象有交集

  • 有,进入该节点,进行1
  • 没有,查询下一个节点,如果查询完毕,返回上一层节点

clear操作的话,就不多介绍了,就是一个dfs进行后序遍历

template<class T>
void QTree<T>::pushBody(QTreeNode<T>* Node,T* Data,int Deep) {//When the MaxDeep is reached,insert directly//这里我选择不设置深度限制,并且不考虑预处理深度if (Node->Son[i]!=nullptr) {for (int i = 0; i < 4; i++) {if (IsInterSectSS(Node->Son[i]->Bound, Data.Bound)) {pushBody(Node->Son[i], Data, Deep + 1);}}}else if (Node->Data.size() >= MaxData) {Node->InstantiateNode();for (int i = 0; i < 4; i++) {if (IsInterSectSS(Node->Son[i]->Bound, Data.Bound)) {pushBody(Node->Son[i], Data, Deep + 1);}}}else if (Node->Data.size() < MaxData) {Node->Data.push_back(Data);}}template<class T>
void QTree<T>::extractBody(QTreeNode<T>* Node,SquareArea<float>* Bound, std::list<T*>* List) {for (auto t = Node->Data.begin(); t != Node->Data.end(); t++) {List->push_back(*t);}//如果子节点是if (Node->Son[0] == nullptr) return;//看4个象限是否与之有交集,如果有交集,那么我们就进入for (int i = 0; i < 4; i++) {if (IsInterSectSS(Node->Son[i]->Bound, Bound))//如果有交集extractBody(Node->Son[i], Bound, List);}}template<class T>
void QTree<T>::extractBody(QTreeNode<T>* Node, CircularArea<float>* Bound, std::list<T*>* List) {for (auto t = Node->Data.begin(); t != Node->Data.end(); t++) {List->push_back(*t);}//如果子节点是if (Node->Son[0] == nullptr) return;//看4个象限是否与之有交集,如果有交集,那么我们就进入for (int i = 0; i < 4; i++) {if (IsInterSectSC(Node->Son[i]->Bound, Bound))//如果有交集extractBody(Node->Son[i], Bound, List);}}template<class T>
void QTree<T>::clearBody(QTreeNode<T>* Node) {if (Node == nullptr) return;for (int i = 0; i < 4; i++) {clearBody(Node->Son[i]);}delete Node;return;
}template<class T>
QTree<T>::QTree(SquareArea<float> Bound,int Deep=3) {//预处理深度,默认5TreeBound = Bound;Head = new QTreeNode<T>(Bound);MaxData = 1;PretreatmentDeep = 3;
}

这篇关于RTS游戏开发:基于四叉树的平面管理系统【加源码】的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/756840

相关文章

Java中的Opencv简介与开发环境部署方法

《Java中的Opencv简介与开发环境部署方法》OpenCV是一个开源的计算机视觉和图像处理库,提供了丰富的图像处理算法和工具,它支持多种图像处理和计算机视觉算法,可以用于物体识别与跟踪、图像分割与... 目录1.Opencv简介Opencv的应用2.Java使用OpenCV进行图像操作opencv安装j

基于Qt开发一个简单的OFD阅读器

《基于Qt开发一个简单的OFD阅读器》这篇文章主要为大家详细介绍了如何使用Qt框架开发一个功能强大且性能优异的OFD阅读器,文中的示例代码讲解详细,有需要的小伙伴可以参考一下... 目录摘要引言一、OFD文件格式解析二、文档结构解析三、页面渲染四、用户交互五、性能优化六、示例代码七、未来发展方向八、结论摘要

Java汇编源码如何查看环境搭建

《Java汇编源码如何查看环境搭建》:本文主要介绍如何在IntelliJIDEA开发环境中搭建字节码和汇编环境,以便更好地进行代码调优和JVM学习,首先,介绍了如何配置IntelliJIDEA以方... 目录一、简介二、在IDEA开发环境中搭建汇编环境2.1 在IDEA中搭建字节码查看环境2.1.1 搭建步

在 VSCode 中配置 C++ 开发环境的详细教程

《在VSCode中配置C++开发环境的详细教程》本文详细介绍了如何在VisualStudioCode(VSCode)中配置C++开发环境,包括安装必要的工具、配置编译器、设置调试环境等步骤,通... 目录如何在 VSCode 中配置 C++ 开发环境:详细教程1. 什么是 VSCode?2. 安装 VSCo

C#图表开发之Chart详解

《C#图表开发之Chart详解》C#中的Chart控件用于开发图表功能,具有Series和ChartArea两个重要属性,Series属性是SeriesCollection类型,包含多个Series对... 目录OverviChina编程ewSeries类总结OverviewC#中,开发图表功能的控件是Char

鸿蒙开发搭建flutter适配的开发环境

《鸿蒙开发搭建flutter适配的开发环境》文章详细介绍了在Windows系统上如何创建和运行鸿蒙Flutter项目,包括使用flutterdoctor检测环境、创建项目、编译HAP包以及在真机上运... 目录环境搭建创建运行项目打包项目总结环境搭建1.安装 DevEco Studio NEXT IDE

Python开发围棋游戏的实例代码(实现全部功能)

《Python开发围棋游戏的实例代码(实现全部功能)》围棋是一种古老而复杂的策略棋类游戏,起源于中国,已有超过2500年的历史,本文介绍了如何用Python开发一个简单的围棋游戏,实例代码涵盖了游戏的... 目录1. 围棋游戏概述1.1 游戏规则1.2 游戏设计思路2. 环境准备3. 创建棋盘3.1 棋盘类

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

Hadoop企业开发案例调优场景

需求 (1)需求:从1G数据中,统计每个单词出现次数。服务器3台,每台配置4G内存,4核CPU,4线程。 (2)需求分析: 1G / 128m = 8个MapTask;1个ReduceTask;1个mrAppMaster 平均每个节点运行10个 / 3台 ≈ 3个任务(4    3    3) HDFS参数调优 (1)修改:hadoop-env.sh export HDFS_NAMENOD

嵌入式QT开发:构建高效智能的嵌入式系统

摘要: 本文深入探讨了嵌入式 QT 相关的各个方面。从 QT 框架的基础架构和核心概念出发,详细阐述了其在嵌入式环境中的优势与特点。文中分析了嵌入式 QT 的开发环境搭建过程,包括交叉编译工具链的配置等关键步骤。进一步探讨了嵌入式 QT 的界面设计与开发,涵盖了从基本控件的使用到复杂界面布局的构建。同时也深入研究了信号与槽机制在嵌入式系统中的应用,以及嵌入式 QT 与硬件设备的交互,包括输入输出设