废话不多说看看效果:
借鉴的四叉树github地址:https://github.com/timohausmann/quadtree-js 个人感觉写的还不错
先说一下四叉树的四个象限:
首先需要有四叉树类:QT.ts
export interface Bounds {
x : number,
y : number,
width : number,
height: number
}
export default class QT {
/** 四叉树的最大层数 */
public maxLevel: number = 5;
/** 该四叉树的范围 */
public bounds: Bounds;
/** 该节点所容纳的最多的对象个数 */
public maxobjects: number;
/** 当前节点的层级 */
public level: number;
/** 当前节点所拥有的四个象限的子节点数组 */
public nodes: QT[];
/** 当前节点所存储的节点对象数组 */
public objects: any[];
constructor(bounds: Bounds,maxObjects?: number,maxLevel?: number,level?: number) {
this.maxLevel = maxLevel || 4;
this.bounds = bounds;
this.maxobjects = maxObjects || 10;
this.level = level || 0;
this.objects = [];
this.nodes = [];
}
/** 将该节点分割为4个子节点 */
split() {
let nextLevel = this.level + 1,
subWidth = this.bounds.width / 2,
subHeight = this.bounds.height / 2,
x = this.bounds.x,
y = this.bounds.y;
this.nodes[0] = new QT({
x: x + subWidth,
y: y + subHeight,
width: subWidth,
height: subHeight
},this.maxobjects,this.maxLevel,nextLevel);
this.nodes[1] = new QT({
x: x,
y: y + subHeight,
width: subWidth,
height: subHeight
},this.maxobjects,this.maxLevel,nextLevel);
this.nodes[2] = new QT({
x: x,
y: y,
width: subWidth,
height: subHeight
},this.maxobjects,this.maxLevel,nextLevel);
this.nodes[3] = new QT({
x: x + subWidth,
y: y,
width: subWidth,
height: subHeight
},this.maxobjects,this.maxLevel,nextLevel);
}
getIndex(obj: Bounds): number[] {
let indexs: number[] = [];
let verticalMidPoint = this.bounds.x + this.bounds.width / 2;
let horizonMidPoint = this.bounds.y + this.bounds.height / 2;
/** 第一象限 */
let isOne = obj.x > verticalMidPoint && obj.y > horizonMidPoint;
let isTwo = obj.x < verticalMidPoint && obj.y > horizonMidPoint;
let isThird = obj.x < verticalMidPoint && obj.y < horizonMidPoint;
let isFour = obj.x > verticalMidPoint && obj.y < horizonMidPoint;
if(isOne) {
indexs.push(0);
}
if(isTwo) {
indexs.push(1);
}
if(isThird) {
indexs.push(2);
}
if(isFour) {
indexs.push(3);
} else {
indexs.push(-1);
}
return indexs;
}
/** 插入 */
insert(obj) {
if(!obj) {
return;
}
let i = 0,
indexes;
/** 如果该节点已经分裂了就将对象放到对应的子节点里面 */
if(this.nodes.length) {
indexes = this.getIndex(obj);
indexes.forEach(element => {
if(element !== -1) {
this.nodes[element].insert(obj);
}
});
return;
}
this.objects.push(obj);
if(this.objects.length > this.maxobjects && this.level < this.maxLevel) {
/** 如果超过该节点所能容纳的最多节点就分裂并且将该节点下的所有对象都放到分裂的子节点下 */
if(!this.nodes.length) {
/** 分裂 */
this.split();
}
for(let i = 0; i < this.objects.length; i++) {
indexes = this.getIndex(this.objects[i]);
indexes.forEach(element => {
if(element !== -1) {
this.nodes[element].insert(this.objects[i]);
}
});
}
/** 清空该节点下的所有对象 */
this.objects = [];
}
}
/**
*
* return all objects that could collide with the given object
*
*/
retrieve(obj) {
let indexes = this.getIndex(obj),
returnObjects = this.objects;
if(this.nodes.length) {
indexes.forEach((ele) => {
if(ele !== -1) {
returnObjects = returnObjects.concat(this.nodes[ele].retrieve(obj));
}
})
}
/** 去重 */
returnObjects = returnObjects.filter((ele,index) => {
return returnObjects.indexOf(ele) >= index;
});
return returnObjects;
}
clear() {
this.objects = [];
for(let i = 0; i < this.nodes.length; i++) {
this.nodes[i].clear();
}
this.nodes = [];
}
}
细节的地方应该写的还算明白了
下面是QuadTree.ts
import QT, { Bounds } from "./QT";
import Util from "../utils/Util";
import TreeObj from "./TreeObj";
const {ccclass, property} = cc._decorator;
@ccclass
export default class NewClass extends cc.Component {
@property({
type: cc.Prefab
})
testNode: cc.Prefab = null;
// LIFE-CYCLE CALLBACKS:
private draw: cc.Graphics;
private drawNode: cc.Node;
private w: number = 0;
private h: number = 0;
private qtRoot: QT;
private objs = [];
private isMouseMoving: boolean = false;
private mouseNode: Bounds = null;
private waitCheckNodeQue: cc.Node[] = [];
onLoad () {
this.drawNode = new cc.Node();
this.node.addChild(this.drawNode);
this.drawNode.addComponent(cc.Graphics);
this.draw = this.drawNode.getComponent(cc.Graphics);
this.draw.lineWidth = 2;
this.draw.lineCap = cc.Graphics.LineCap.SQUARE;
this.draw.lineJoin = cc.Graphics.LineJoin.MITER;
this.draw.strokeColor = new cc.Color(255,0,0,255);
this.node.on(cc.Node.EventType.MOUSE_MOVE,this.mouseMove,this);
this.node.on(cc.Node.EventType.MOUSE_LEAVE,this.mouseLeave,this);
this.w = this.node.width;
this.h = this.node.height;
// this.createRange(this.w,this.h);
this.qtRoot = new QT({
x: 0,
y: 0,
width: 1000,
height: 750
},10,5);
this.mouseNode = {
x: 0,
y: 0,
width: 20,
height: 20
}
}
/** 鼠标移动 */
mouseMove(event: cc.Event.EventMouse): void {
this.resetTargetsColor();
this.waitCheckNodeQue = [];
this.isMouseMoving = true;
let worldLocation = event.getLocation();
let mouseLocation = this.node.convertToNodeSpace(worldLocation);
this.mouseNode.x = mouseLocation.x;
this.mouseNode.y = mouseLocation.y;
let targets = this.qtRoot.retrieve(this.mouseNode);
this.updateTargetsInfo(targets);
}
updateTargetsInfo(targets: cc.Node[]) {
for(let item of targets) {
if(this.waitCheckNodeQue.indexOf(item) === -1) {
this.waitCheckNodeQue.push(item);
}
let itemCom: TreeObj = item.getComponent(TreeObj);
itemCom.check = true;
let itemXMin = item.x - item.width / 2;
let itemXMax = item.x + item.width / 2;
let itemYMin = item.y - item.height / 2;
let itemYMax = item.y + item.height / 2;
/** 检测是否碰撞 */
if(itemXMin < this.mouseNode.x && this.mouseNode.x < itemXMax &&
itemYMin < this.mouseNode.y && this.mouseNode.y < itemYMax) {
item.color = cc.Color.ORANGE;
itemCom.isCollision = true;
}
}
}
resetTargetsColor() {
this.waitCheckNodeQue.forEach((item) => {
let itemCom: TreeObj = item.getComponent(TreeObj);
itemCom.isCollision = false;
item.color = cc.Color.BLACK;
})
}
/** 鼠标离开 */
mouseLeave(event): void {
this.isMouseMoving = false;
}
createRange(w: number,h: number) {
if(this.draw) {
this.draw.moveTo(-this.w / 2,0);
this.draw.lineTo(this.w / 2,0);
this.draw.moveTo(0,this.h / 2);
this.draw.lineTo(0,-this.h / 2);
this.draw.stroke();
}
}
addObj() {
let rect: cc.Node = cc.instantiate(this.testNode);
rect.x = Util.getRandom(rect.width / 2,this.qtRoot.bounds.width - rect.width / 2);
rect.y = Util.getRandom(rect.height / 2,this.qtRoot.bounds.height - rect.height / 2);
this.node.addChild(rect);
this.objs.push(rect);
this.qtRoot.insert(rect);
}
drawQT(qt: QT) {
let bounds = qt.bounds;
if(qt.nodes.length) {
qt.nodes.forEach((item) => {
this.drawQT(item);
})
} else {
// this.draw.clear();
this.draw.rect(bounds.x,bounds.y,bounds.width,bounds.height);
this.draw.strokeColor = cc.Color.RED;
this.draw.stroke();
}
}
start () {
}
update (dt) {
if(this.qtRoot) {
// this.draw.clear();
this.drawQT(this.qtRoot);
}
}
}