cococs creator 实现四叉树在游戏开发中的应用

废话不多说看看效果:

借鉴的四叉树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是挂载到一个空节点下的:

下面是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);
        }
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值