[ECS框架系列]-与四叉树的一次结合(2)

news/2023/11/30 21:13:43

前言

大家好,我IT侠又双叒叕来了,上一篇文章说了下ECS框架在H5下是如何使用的。本篇文章就来聊聊主角-四叉树。

还是在开头说一下:

  • 不了解四叉树的可以自行查阅一下相关资料。因为篇幅问题,IT侠会下方简单的介绍下。

  • 因为demo是基于ECS框架,所以如果不了解ECS框架的话,可以看看我的ECS框架系列文章,获取我已经开源的ECS框架。或者扫文末的二维码联系我,给我“加个鸡腿”支持一下!19.9 交个朋友。

正文

最终效果

先给大家看看demo运行起来的效果,看看四叉树在对象很多的情况下的威力

  • 每一个"田字"矩形是一个4叉树节点,方便调试的使用
  • 高亮黄色的就是需要检查是否与主角发生碰撞的块
  • 高亮紫色的就是当前和主角发生碰撞的块

四叉树

生成了超级多块后四叉树的表现

四叉树牛逼

最后是关闭了调试线后的效果

最终效果

四叉树

四叉树(quad-tree)是一种数据结构,是一种每个节点最多有四个子树的数据结构。四叉树常应用于二维空间数据的分析与分类。所以把四叉树应用在二维的有效率之碰撞侦测上是非常适合的。

代码实现

源码里的方法我就不一一列举出来了,因为四叉树的实现不是本文的重点,只介绍下四叉树构造函数,还有包含哪些方法,以及方法的简单解释。github上开源很多四叉树的实现,如果想深入了解可以自行查阅学习。

    constructor(bounds: IBounds, maxObjects?: number, maxLevels?: number, level?: number,location?:number,parent?:QuadTree) {// 树节点的边界(包围盒)this.bounds = bounds; // 节点上最多存放物体的数量,为了测试默认设置为3,具体设定开发者可以自行决定this.maxObjects = maxObjects || 3; // 4叉树的最大层级this.maxLevels = maxLevels || 4; // 当前节点在树中的层级this.level = level || 0;// 当前节点上所有对象this.objs = []; //4个象限(节点),如果该节点上的对象超过设定的最大值后就进行分裂this.nodes = []; //标记一下当前节点是那个象限this.location = location;// 当前节点的父节点,方便后续删除子树this.parent = parent || null;}

四叉树类有以下成员方法组成:

  • getIndex 返回当前对象在哪些象限上
  • insert 将一个对象加入到当前节点上
  • clear 清空树上所有节点和节点上的对象
  • recovery 如果一个子节点上没有任何对象,那么进行一次初始化
  • split分裂子树节点,生成4个叶子节点(象限)
  • retrieve 获取树上可能与指定对象发生碰撞的对象
  • getBounds 获取所有节点的包围盒,方便调试
  • toJSON 转成json格式,方便调试输出树信息

ECS中使用四叉树

当有Canvas组件的实体产生时候,捕获该实体,创建一个四叉树,根节点包围盒就用canvas节点的位置和宽高。然后将四叉树对象保存到ecs对象上方便其他系统访问。

     _canvas.on("Canvas", function (e) {//获取Canvas组件上的canvas属性//该属性 保存的就是canvas对象let canvas = e.Canvas.canvas;// 创建一个四叉树toy.qt = new toy.QuadTree({x: canvas.width / 2,y: canvas.height / 2,width: canvas.width,height: canvas.height});});

接下来写一个系统"关心一下"带有 “Mob”,“Appearance”,“Props” 这3个组件的实体的产生。因为有这3个组件的实体我们认定是一个怪物,怪物会和主角发生碰撞,所以需要放进四叉树中等待提取。

调用四叉树实例上的 insert 方法把怪物的包围盒和怪物实体一起打包到一个对象上保存进四叉树上的节点中。

     _character.on(["Mob","Appearance","Props"],function(ent){//调用四叉树实例上的 insert 方法toy.qt.insert({_node:ent,width:ent.Props.props.width,height:ent.Props.props.height,x:ent.Props.props.x,y:ent.Props.props.y});});

现在怪物都被放进了四叉树上,接着我们需要写一个用户输入系统,监听玩家控制主角移动等事件。这里用的是 hammer.js库来实现。

定义一个用户输入系统,该系统关心"LocalControl",“Props”,“Node”,“Character” 组件。有这4个组件的实体就是我们的主角实体。之后分别监听 panstart(开始触摸屏幕) ,panmove(移动手指), panend (手抬起) 这3个事件。

            let _userInput = toy.system("userInputSystem",101);_userInput.on(["LocalControl","Props","Character","Node"],function(ent){let props = ent.Props.props;let canvasEnt = toy.getFirstEnt("Canvas");if(!canvasEnt){return;}let mc = new Hammer.Manager(canvasEnt.Canvas.canvas)// pan的距离判断为0 手指不限定mc.add(new Hammer.Pan({threshold:0,pointers:0}))mc.on("panstart",function(ev){props.isTouch = true;let rect = canvasEnt.Canvas.canvas.getBoundingClientRect();props.x = props.lastX = ev.center.x - rect.left;props.y = props.lastY = ev.center.y - rect.top;toy.__output.innerHTML = JSON.stringify(props, null, 4);});mc.on("panmove", function onPanMove(ev) {toy.comOnce("RecoverColor");props.lastX = props.lastX;props.lastY = props.lastY;props.x = props.lastX + ev.deltaX;props.y = props.lastY + ev.deltaY;ent.Node.node.x =  props.x;ent.Node.node.y =  props.y;props.isTouch = true;toy.__output.innerHTML = JSON.stringify(props, null, 4);           //尝试获取带有"HighLight"组件的实体let e = toy.getFirstEnt("HighLight");// 保证世界上只有一个实体带有"HighLight"if(!e){//创建一个带有"HighLight"组件的实体。//之后湖北关心该组件的系统捕获toy.com("HighLight")}});mc.on("panend", function onPanEnd(ev){props.isTouch = false;props.lastX = props.x;props.lastY = props.y;toy.__output.innerHTML = JSON.stringify(props, null, 4);          let e = toy.getFirstEnt("HighLight");//这边主角停止移动 我就删除了带有"HighLight"组件的实体.//其实也可以不删除,我这边删除该实体那么在主角停止移动的时候负责高亮的系统就不会工作了。e && toy.removeCom(e,"HighLight")});});  

可以看到IT侠在"panmvoe"事件回调中创建了一个"HighLight"组件的实体,目的就是为了告知(world)引擎我需要高亮,如果有关心"HighLight"的系统存在的话,那么该系统就会工作。那么接下来就是定义一个关心该组件的系统。

      // 改系统是一个update方法 每一帧都会执行_sketch.onUpdate("HighLight",function(dt,es){//如果没有所关心的实体那么直接返回if(!es[0]){return;}//拿到转化为json的四叉树数据let data = toy.qt.toJSON()// 拿到主角实体let selfEnt = toy.getFirstEnt("LocalControl")if(!selfEnt){return;}//获取所有怪物实体let foods = toy.getAllEnt(["Mob","Health"])  // 获取可能与主角发生碰撞的怪物let hlFood = toy.qt.retrieve(selfEnt.Node.node);if( !hlFood[0]){return;}hlFood.forEach((food)=>{//检查碰撞 if(food._node.OldColor){return;}//判断2个物体是否将相交let isIntersects = toy.intersects(selfEnt.Node.node,food._node.Node.node);//在自己身上挂一个组件来保存自己原来的颜色,方便后面还原toy.com("OldColor",{color:toy.deepClone(food._node.Props.props.color)},food._node);   if(false === isIntersects){food._node.Props.props.color = {r:219,g:100,b:0}}else{food._node.Props.props.color = {r:106,g:9,b:125}                  //告诉关心带有"Collisioned"组件实体的系统 来处理碰撞toy.comOnce("Collisioned",{character:selfEnt,target: food._node});}});});

碰撞逻辑就不属于demo功能了。开发者可以自行处理

       let _collision = toy.system("CollisionSystem",100);_collision.on("Collisioned",function(ent){let character = ent.Collisioned.character;let target = ent.Collisioned.target;//todo 写自定义的碰撞需求});

最后就是怪物消失,我们需要更新一下树节点状态。写一个死亡系统关心带有"Mob",“Dead” 组件的实体,这些实体被定义为了已经死亡的实体。

         let _dead = toy.system("DeadSystem",100);_dead.on(["Mob","Dead"],function(ent){// 从实体的Tree组件上获取保存在树节点上的引用let treeNodes = ent.Tree.node;if(!treeNodes[0]){return;}//检查是否该实体所在的子节点是空闲的//如果空闲则从四叉树上删除该子节点treeNodes.forEach((node)=>{node.remove(ent);});// 从world中删除该实体toy.destroy(ent);});

结束语

现在有了 创建四叉树,将对象加入树,检查是否和与主角发生碰撞,还有删除树上空闲的子节点。等功能,相信大家已经大概了解 ECS框架是如何与四叉树结合来做碰撞检测的了。

如果需要源码的可以微信和我联系哈。支持一下 交个朋友。

谢谢看到最后(ps: 点赞 支持一下呗)

上篇回顾

[ECS框架系列]-与四叉树的一次结合(1)

ECS系列文章

[ECS系列] 框架中必不可少的工具类

[ECS系列] World的设计思路

[ECS系列] 实体,组件,系统的设计思路

[ECS系列] 欢迎来到ECS的世界

更多精彩

CocosCreator 教学系列-对象池

无限嵌套的富文本打字机

CocosCreator 教学系列-打字机效果(1)

CocosCreator 教学系列-打字机效果(2)

公众号

我是IT侠来了,下面是我的公众号,专注于分享IT圈内各种技术干货,内容涉及后端技术,前端技术等等,希望大家喜欢。再次感谢关注。

IT侠来了

有什么问题可以加本人微信,一起交流。

本人微信


http://www.ppmy.cn/news/318630.html

相关文章

和女生的聊天禁忌

首先跟大家纠正一个认知:聊天是病症不是病因。 我们可以把遇到的情感问题比喻成一个病,在追求女生过程中出现了问题,就当于生病了。 但是很多学员会讲:我的聊天不好女生不回我,女生回复的反应不好,这句我…

国产_系统_加油 by tmddebaba

去年一年没登陆csdn blog发文章,因为刚接触国产系统UOS(基于Debian10),没啥Linux的经验,一直在debug。后面这几个月我想多登陆csdn,记录一下平时遇到的问题。从2021年4月份接触UOS专业版(龙芯mips64el&…

用Python写个自动批改作业系统

开发者(KaiFaX) 面向全栈工程师的开发者 专注于前端、Java/Python/Go/PHP的技术社区 来源:juejin.cn/user/615370768790158 作者:TF男孩 一、亮出效果 最近一些软件的搜题、智能批改类的功能要下线。 退1024步讲,要不要…

只有契合用户心智模型的产品,才可能成功

提到矿泉水,我们脑子里冒出来的就是农夫山泉;提起方便面,就想到康师傅;提起凉茶,不自觉地就想到了王老吉或者加多宝。 但为什么键盘的排列顺序是QWERTY而不是ABCDE? 本文从用户心智模型来解读其中的奥秘。 …

python读取word文档结构图_Word 有什么技巧,让你相见恨晚?

Word作为日常办公最常用的软件之一,其实真没你想得那么简单! 你不知道的每一个技巧,都会让你相见恨晚! 每当身边的小伙伴询问这些疑难杂症时,我都会抛出这张图… 真的没骗你,我们遇到的 99% 的Word难题&…

JAVA基础 多线程技术学习笔记(V1.0)

目录 一、多线程介绍 1.1 多线程中的基本概念 1.1.1多线程与进程 1.1.2 进程、线程的区别和联系 1.1.3 并发和并行的区别 1.1.4 线程的执行特点 1.1.5 主线程与子线程 二、线程的创建及生命周期 2.1 通过继承Thread类实现多线程 2.1.1 继承Thread类实现多线程的步…

可用!三行代码高仿高德地图三段式抽屉效果

三行代码 废话不在前面说&#xff0c;直接上代码&#xff01; 将 XML 根布局设置为 CoordinatorLayout <android.support.design.widget.CoordinatorLayout 将要要弹出的三段式抽屉根布局设置 layout_behavior 属性 app:layout_behavior"android.support.design.wi…

微信简单之美

[ BadTudou同学尊重知识产权与创新&#xff0c;若本文侵犯到任何人的相关权益&#xff0c;请与我联系。 Email: BadTudouYeah.Net ] 本文由和菜头整理&#xff08;http://www.hecaitou.net/?p7259&#xff09;&#xff0c;版权归作者及张小龙所有&#xff0c;转载请注明。 …

苹果笔记本电脑我的计算机在哪里设置密码,Mac小教程:设置苹果Mac电脑的开机密码...

原标题&#xff1a;Mac小教程&#xff1a;设置苹果Mac电脑的开机密码 有很多用户刚从windows系统转过来使用mac系统&#xff0c;可能有很多东西都不知道在哪里&#xff0c;不知道怎么去设置&#xff1f;下面我们就来看下mac是怎样设置开机密码的。非常简单&#xff0c;来跟小编…

TX2使用

查看Jetson Tx2 JetPack版本信息 $ git clone https://github.com/jetsonhacks/jetsonUtilities $ cd jetsonUtilities $ python jetsonInfo.py可得到 NVIDIA Jetson TX2L4T 28.1.0 [ JetPack UNKNOWN ]Board: t186refUbuntu 16.04.5 LTSKernel Version: 4.4.38-tegraCUDA 8.0…

Anntec ZKUXFT XT2 FGPA卡DPDK使用方法

1. 建议环境 CPU Architecture x86_64、aarch64 CPU MHz&#xff1a; 2000以上 Memory 每个node空闲内存超过2G 硬盘 剩余空间大于100M OS Ubuntu,centos,银河麒麟&#xff0c;UOS等Linux …

树莓派和机器人有啥关系(也有jetson tx2的知识主要)

个人是这样理解的&#xff0c;master computer属于全嵌入式开发&#xff0c;slave computer属于半嵌入式开发 这个划断依据是根据是否运行了独立的os来划分的&#xff0c;不是指能不能&#xff0c;而是指实际应用中是不是 slave更好的翻译是从动而不是其字面含义 个人的基础开发…

MSP430f149使用XT2的8M晶振输出PWM(PWM输出总结)

由于需要将PWM保持在20khz来控制BUCK电路降压&#xff0c;同时又需要步进改动占空比&#xff0c;故需要TA利用8M晶振输出PWM&#xff0c;经测试&#xff0c;最大占空比频率可达4M&#xff0c;将频率定为20khz时&#xff0c;有200个步进点&#xff0c;BUCK输入电压为18V&#xf…

联想天逸F41M无线网卡搜索不到无线信号

联想天逸F41M无线网卡搜索不到无线信号 无线开关已经开启&#xff0c;驱动也正常&#xff0c;就是无法搜索到信号&#xff0c;通过网上搜索&#xff0c;解决方法如下&#xff1a; 1、确保无线开关开启 2、键盘上按功能键FnF5&#xff08;下方有无线标示&#xff09;&#xff0c…

联想天逸F41a重装win7 x64

联想天逸F41a重装win7 x64 原装操作系统是xp&#xff0c;但是必须要用IE11&#xff0c;所以必须重装为win7&#xff0c;为了多用内存&#xff0c;选64位。 安装前&#xff0c;先提前下载了: - 网卡和无线网卡的驱动 以防刚装好操作系统时&#xff0c;网卡及无线网卡不工作。…

联想天逸笔记本关闭Fn功能,直接使用F1~12键

【前言】 联想的 Thinkpad 和 ideapad 系列笔记本有个很反人类的设计就是按 F1~12 时必须同时按下 Fn 键&#xff0c;否则实际效果是打开或关闭那些图标对应的快捷功能。 这非常不方便&#xff0c;所以我们需要手动修改一下配置。 【操作步骤】 我手上的笔记本型号是 Lenovo Ti…

K8s in Action 阅读笔记——【13】Securing cluster nodes and the network

K8s in Action 阅读笔记——【13】Securing cluster nodes and the network 13.1 Using the host node’s namespaces in a pod Pod中的容器通常在不同的Linux名称空间下运行&#xff0c;这使得它们的进程与其他容器或节点默认名称空间下运行的进程隔离开来。 例如&#xff…

没联网 电脑对时 校时 XJ-T100

本文介绍个孤岛(没联网)电脑时间的校准办法

实验二、设置1ms定时器

/***************************************************************************//**文件: main.c版本: V1.0.0时间: 202101201平台:MINI-GD32F103C8T6开发板*******************************************************************************/ #include "gd32f10x.h&qu…

在电脑上开启网络校时服务

在电脑上开启网络校时服务的方法&#xff1a; 1、通过开始菜单&#xff0c;输入regedit命令后打开注册表设定画面&#xff0c;此时请一定备份注册表文件。 2、修改以下选项的键值 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\NtpServer内的「…
最新文章