当前位置:   article > 正文

零基础学CocosCreator·第六季-常用编程框架和算法_cocos creator发布订阅模式

cocos creator发布订阅模式

01.MVC架构

是一种设计程序的思路/套路.

MVC的含义

  1. M-Model模型(数据)
    FlappyBird中小鸟的位置,当前激活的节点等等
  2. V-View视觉层
    展示出来的界面,通常是引擎进行处理
  3. C-Controller控制器(逻辑)
    写下的代码,小鸟碰到管子判断游戏结束

总结内涵:拿数据-根据逻辑-刷新界面

02.单例模式

单例(instance)的特点

  1. 单例类,全局只有一个对象,不可能产生多个
  2. 在代码任意位置容易获取这个对象

作用

防止一个全局使用的类频繁的实现与销毁
控制实例数目,节省系统资源

实现

/**单例类 */
export default class InstanceDemo {
    /**全局唯一的对象 */
    private static _instance: InstanceDemo = null; //private的变量名前一般加个_

    /**获取单例对象 */
    public static getInstance(): InstanceDemo{
        if (InstanceDemo._instance == null){
            InstanceDemo._instance = new InstanceDemo();
        }
        return InstanceDemo._instance;
    }

    /**防止创建第二个对象 */
    constructor(){
        if (InstanceDemo._instance != null) {
            throw Error("Already has InstanceDemo._instance");
        }
    }

    num: number = 0;

    trace(){
        this.num ++;
        console.log("trace",this.num);
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

调用

import InstanceDemo from "./instanceDemo";

const {ccclass, property} = cc._decorator;

@ccclass
export default class HelloWorld extends cc.Component {

    start () {
        /**获取单例对象 */
        let instance = InstanceDemo.getInstance();
        /**调用单例方法 */
        instance.trace();

        //创建对象
        new InstanceDemo();
    }

    // update (dt) {}
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

结果

在这里插入图片描述

补充

实现单例模式,将构造函数私有化就好了

/**私有化构造函数*/
private constructor(){}
  • 1
  • 2

03.观察者模式-订阅发布模式

流程

  1. A订阅了开始游戏事件
  2. B抛出(发布)了开始游戏事件
  3. A响应事件

特点:A不管什么时候出发,只负责触发时接受,B不知道谁注册了事件,只负责触发.

实现

将WebStorm改为ES6:File→Setting→Languages&Framework→JavaScript→选择ES6
还要将typescriptconfig.json里的“ES5”改为“ES6”
在这里插入图片描述

EventCenter.ts 事件控制中心
EventHandler 记录事件信息

/**观察者模式 */
export default class EventCenter {
    //事件数据 存放事件名和注册该事件的注册信息们
    private static events: Map<string,Array<EventHandler>> = new Map<string,Array<EventHandler>>();

    /**注册事件
     * eventName: string 事件名
     * target: object 谁注册的事件 用于回调绑定
     * callBack: Function 回调
     */
    static registerEvent(eventName: string,target: object,callBack: Function): void {
        if (eventName == undefined || target == undefined || callBack == undefined) {
            throw Error("regsiter event error");
        }
        /**判断是否已经有该事件被注册过 */
        if (EventCenter.events[eventName] == undefined){
            EventCenter.events[eventName] = new Array<EventHandler>();
        }

        /**将此次注册事件的信息存入Map中 */
        let handler = new EventHandler(target,callBack);
        EventCenter.events[eventName].push(handler);
    }

    /**触发事件
     * eventName: string 事件名
     * param?: any 回调参数
     */
    static postEvent(eventName: string,param?: any): void {
        let handlers = EventCenter.events[eventName];
        if (handlers == undefined){
            return;
        }

        //遍历所有注册了该事件的eventHandler
        for (let i = 0; i < handlers.length; i++){
            let handler = handlers[i];
            if (handler){
                //调用事件回调
                //使用try-catch防止回调有报错但没有信息
                try {
                    //.call(绑定的this,参数) 调用方法
                    handler.function.call(handler.target,param);
                }catch (e){
                    console.log(e.message);
                    console.log(e.stack.toString()); //输出堆栈信息
                }
            }
        }
    }

    /**移除注册事件
     * eventName: string 事件名
     * target: object 谁注册的事件 用于回调绑定
     * callBack: Function 注册事件回调
     */
    static removeEvent(eventName: string,target: object,callBack: Function): void {
        if (eventName == undefined || target == undefined || callBack == undefined) {
            throw Error("destory event failed");
        }
        let handlers = EventCenter.events[eventName];
        if (handlers){
            for (let i = 0; i < handlers.length; i++){
                let handler = handlers[i];
                if (handler && target == handler.target && callBack == handler.function){
                    //有两种移除方法 "= undefined"性能要好 "splice"要省内存空间
                    handlers[i] = undefined;
                    // handlers.splice(i,1);
                    break;
                }
            }
        }
    }

}

/**注册信息类 */
class EventHandler {
    /**记录谁注册了事件 */
    target: object;
    /**记录事件触发时调用的方法 */
    function: Function;

    constructor(target: object,func: Function){
        this.target = target;
        this.function = func;
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89

Panel.ts 用来注册事件

import EventCenter from "./EventCenter";

/**用来注册事件 检验观察者模式 */
const {ccclass, property} = cc._decorator;

@ccclass
export default class Panel extends cc.Component {

    @property(cc.Label)
    label: cc.Label = null;

    onLoad () {
        EventCenter.registerEvent("gameStart",this,this.onGameStart);
        //5s后移除事件注册
        this.scheduleOnce(function() {
            this.onDestroy();
        }.bind(this),5)
    }

    /**注册事件回调 */
    onGameStart(str: string){
        console.log("event callBack");
        this.label.string = str;
    }

    onDestroy(){
        //移除gameStart事件
        console.log("remove event gameStart");
        EventCenter.removeEvent("gameStart",this,this.onGameStart);
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

触发事件

EventCenter.postEvent("gameStart","game is start!");
  • 1

结果

在这里插入图片描述
点击按钮

在这里插入图片描述
输出
在这里插入图片描述

注意:target节点destory后,一定要记得注销其注册的事件。否则callBack会出错

04.工厂模式

特点和作用

操作的对象本身只实现功能方法,具体的操作由工厂实现
这样不会暴露对象及创建逻辑

实现

/**工厂模式 */

//c: {new ():T} 告诉ide这个T类型的c是可以被实例化的
export function createAttack<T extends IActor>(c: {new (): T},life: number): T {
    let object = new c();
    object.attack();
    object.life = life;
    return object;
}

export function createDie<T extends IActor>(c: {new (): T}): T {
    let object = new c();
    object.die();
    return object;
}

/**角色接口 */
interface IActor {
    attack: Function;
    die: Function;
    life: number;
}

/**盗贼 */
export class Thief implements IActor {
    life: number;
    attack() {
        console.log("thief attack");
    }
    die() {
        console.log("thief is die");
    }
}

/**战士 */
export class Warrior implements IActor {
    life: number;
    attack() {
        console.log("warrior attack");
    }
    die() {
        console.log("warrior is die");
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

调用

	createAttack<Thief>(Thief,10);
    createDie<Warrior>(Warrior);
  • 1
  • 2

05.代理模式

代理 (额外的控制器)

  1. 单一原则,不给一个类太多功能.
    自己的功能留在类里面,将一些逻辑控制放到外面.
    高内聚,低耦合.
  2. 不方便访问一个类的时候,给个代理
    例如:明星和经纪人的关系;
    快递中发货人-快递-收货人;
    cc.loader.load(…)内部非常复杂,但对于调用的人来说并不关 心内部逻辑.

06.递归寻路

题目

需要从①走到②,要怎么走(可以斜着走,褐色是不能走的)
在这里插入图片描述

步骤

  1. 把起点当作当前节点
  2. 重复以下步骤
    a. 把当前节点加入openList,标记Open
    b.查找可以走的下一步节点
    c.把下一步可以走的节点排序
    d.把下一步可以走的离终点最近的点,当做当前的寻路节点
    e.把走过的节点标记为Close
  3. 直到查找到目标节点

实战

先制作格子 NodeGrid.ts

import FindPath from "./FindPath";

/**寻路地图格子 */
const {ccclass, property} = cc._decorator;
/**格子 显示层 */
@ccclass
export default class NodeGrid extends cc.Component {
    dataGrid: DataGrid = null;
    findPathController: FindPath;

    onLoad () {
        this.node.on(cc.Node.EventType.TOUCH_END,this.onBtnGrid,this);
    }

    /**点击格子 确定起点终点 生成路线 */
    onBtnGrid(){
        this.findPathController.onTouch(this);
    }

    /**刷新格子颜色 */
    updateGridColor(){
        if (this.dataGrid.type == GrideType.Normal){
            this.node.color = new cc.Color().fromHEX("#fffff9");
        } else if (this.dataGrid.type == GrideType.Wall){
            this.node.color = new cc.Color().fromHEX("#151513");
        } else if (this.dataGrid.type == GrideType.Road){
            this.node.color = new cc.Color().fromHEX("#41ff0b");
        } else {
            this.node.color = new cc.Color().fromHEX("#fff42d");
        } 
    }
}

/**格子数据 数据层 */
export class DataGrid {
    type: GrideType;
    //坐标
    x: number;
    y: number;
    /**是否为当前节点 */
    inOpenList: boolean = false;
    /**路径节点标记 */
    inCloseList: boolean = false;
}

/**格子类型枚举 */
export enum GrideType {
    Normal, //普通
    Wall, //墙
    Start, //起点,当前节点
    End, //终点
    Road, //路线
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54

在场景下创建一个40x40的格子,并挂载NodeGrid.ts
在这里插入图片描述

制作地图

/**随机生成8x8地图 */
    generateMap () {
        for (let x = 0;x < 8;x ++){
            this.dataGrids[x] = [];
            this.nodeGrids[x] = [];
            for (let y = 0;y < 8;y ++){
                let rand = Math.random();
                let grideType: GrideType = GrideType.Normal;
                if (rand < 0.2) { //1/5的概率生成墙
                    grideType = GrideType.Wall;
                }
                //数据层
                let grid: DataGrid = new DataGrid();
                grid.x = x;
                grid.y = y;
                grid.type = grideType;
                this.dataGrids[x][y] = grid;

                //视图层
                let gridNode: NodeGrid = cc.instantiate(this.nodeGridPrefab).getComponent(NodeGrid);
                gridNode.node.position = cc.v3(50 * (x - 4),50 * (y - 4),0);
                this.nodeGrids[x][y] = gridNode;
                gridNode.dataGrid = grid;
                gridNode.findPathController = this;
                gridNode.updateGridColor();
                gridNode.node.parent = this.node;
            }
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

寻路(完整代码 FindPath.ts)

import NodeGrid, { DataGrid, GrideType } from "./NodeGrid";

/**递归寻路 */
const {ccclass, property} = cc._decorator;

@ccclass
export default class NewClass extends cc.Component {
    /**格子节点 */
    @property(cc.Node)
    nodeGridPrefab: cc.Node = null;

    dataGrids: DataGrid[][] = [];
    nodeGrids: NodeGrid[][] = [];

    /**记录起点 */
    startGrid: DataGrid = null;
    /**记录终点 */
    endGrid: DataGrid = null;

    onLoad () {
        this.generateMap();
    }

    /**随机生成8x8地图 */
    generateMap () {
        for (let x = 0;x < 8;x ++){
            this.dataGrids[x] = [];
            this.nodeGrids[x] = [];
            for (let y = 0;y < 8;y ++){
                let rand = Math.random();
                let grideType: GrideType = GrideType.Normal;
                if (rand < 0.2) { //1/5的概率生成墙
                    grideType = GrideType.Wall;
                }
                //数据层
                let grid: DataGrid = new DataGrid();
                grid.x = x;
                grid.y = y;
                grid.type = grideType;
                this.dataGrids[x][y] = grid;

                //视图层
                let gridNode: NodeGrid = cc.instantiate(this.nodeGridPrefab).getComponent(NodeGrid);
                gridNode.node.position = cc.v3(50 * (x - 4),50 * (y - 4),0);
                this.nodeGrids[x][y] = gridNode;
                gridNode.dataGrid = grid;
                gridNode.findPathController = this;
                gridNode.updateGridColor();
                gridNode.node.parent = this.node;
            }
        }
    }

    /**点击格子 */
    onTouch(nodeGrid: NodeGrid){
        if (!this.startGrid) { //设置起点
            this.startGrid = nodeGrid.dataGrid;
            this.startGrid.type = GrideType.Start;
            nodeGrid.updateGridColor();
        }else if (!this.endGrid) { //设置终点
            this.endGrid = nodeGrid.dataGrid;
            this.endGrid.type = GrideType.End;
            nodeGrid.updateGridColor();

            //寻路
            this.startFindPath();
        }
    }

    openPath: DataGrid[] = [];

    /**寻路 */
    startFindPath(){
        if (this.find(this.startGrid)) {
            for (let i = 0; i < this.openPath.length; i++) {
                let path = this.openPath[i];
                path.type = GrideType.Road;
                this.nodeGrids[path.x][path.y].updateGridColor();
            }
        }else {
            console.log("无法走到终点");
        }
    }

    find(base: DataGrid) {
        this.openPath.push(base);
        base.inOpenList = true;
        if (base == this.endGrid){ //寻路结束
            return true;
        }
        let round = this.getRoundGrid(base);
        for (let i = 0;i < round.length;i ++) {
            let nextBaseGride = round[i];
            if (this.find(nextBaseGride)) {
                return true;
            }
        }

        base.inCloseList = true;
        this.openPath.splice(this.openPath.length - 1,1);
        return false;
    }

    /**获取当前节点周围可走的节点 */
    getRoundGrid(grid: DataGrid): DataGrid[] {
        let arr: DataGrid[] = [];
        //周围的格子
        this.addToRoundIfNeed(arr,this.getGrid(grid.x,grid.y + 1));
        this.addToRoundIfNeed(arr,this.getGrid(grid.x - 1,grid.y));
        this.addToRoundIfNeed(arr,this.getGrid(grid.x,grid.y - 1));
        this.addToRoundIfNeed(arr,this.getGrid(grid.x + 1,grid.y));
        this.addToRoundIfNeed(arr,this.getGrid(grid.x - 1,grid.y - 1));
        this.addToRoundIfNeed(arr,this.getGrid(grid.x + 1,grid.y + 1));
        this.addToRoundIfNeed(arr,this.getGrid(grid.x + 1,grid.y - 1));
        this.addToRoundIfNeed(arr,this.getGrid(grid.x - 1,grid.y + 1));

        //会将数组里元素两两进行比较,自定义方法里返回-1就不交换位置 返回1交换位置
        arr.sort(this.compareGrids.bind(this));

        return arr;
    }

    /**将格子放到数组里 */
    addToRoundIfNeed(arr: DataGrid[],roundGrid: DataGrid) {
        //当前节点和路径节点都不计入
        if (!roundGrid || roundGrid.type == GrideType.Wall || roundGrid.inCloseList || roundGrid.inOpenList){
            return;
        }
        if (roundGrid) {
            arr.push(roundGrid);
        }
    }

    /**根据坐标获取格子数据 */
    getGrid(x: number,y: number): DataGrid {
        //边界判断
        if (x < 0 || x >= 8 || y < 0 || y >=8){
            return null;
        }
        return this.dataGrids[x][y];
    }

    /**格子比较和终点的距离
     * 距离小的放在前面
     */
    compareGrids(grid0: DataGrid,grid1: DataGrid): number{
        let grid0Dis = this.getDistance(grid0);
        let grid1Dis = this.getDistance(grid1);
        if (grid0Dis > grid1Dis) {
            return 1;
        }else{
            return -1;
        }
    }

    /**获取节点到终点距离 */
    getDistance(grid: DataGrid){
        return Math.abs(grid.x - this.endGrid.x) + Math.abs(grid.y - this.endGrid.y);
    }

    /**点击重新开始*/
    onBtnRestart(){
        for (let x = 0;x < this.dataGrids.length;x ++){
            for (let y = 0;y < this.dataGrids[x].length;y ++){
                let dataGrid = this.dataGrids[x][y];
                dataGrid.inOpenList = false;
                dataGrid.inCloseList = false;
                if (dataGrid.type != GrideType.Wall){
                    dataGrid.type = GrideType.Normal;
                }
                this.nodeGrids[x][y].updateGridColor();
            }
        }
        this.startGrid = null;
        this.endGrid = null;
        this.openPath = [];
    }

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180

运行结果

在这里插入图片描述

不足

当前算法只是寻找下一步的最优解,并不是全局的最优解,有时并不是最优路径。

07.A星寻路

可以看下大神写的A星寻路算法原理

步骤

  1. 把起点当做当前节点
  2. 重复以下步骤
    a.把当前节点加入openList,标记Open
    b.查找可以走的下一步节点
    c.把下一步可以走的节点的父节点,设置为当前节点
    d.把下一步可以走的节点加入到openList,排序
    e.把openList中的第一个节点,当做当前节点
    f.把走过的节点标记为Close
  3. 直到查找到目标节点

实现

DataGrid添加一个字段

/**父节点 用于A星寻路 */
fatherGrid: DataGrid = null;
  • 1
  • 2

FindPathAX.ts完整代码

/**A星寻路 */
import NodeGrid, { DataGrid, GrideType } from "./NodeGrid";

const {ccclass, property} = cc._decorator;

@ccclass
export default class FindPathAX extends cc.Component {
    /**格子节点 */
    @property(cc.Node)
    nodeGridPrefab: cc.Node = null;

    dataGrids: DataGrid[][] = [];
    nodeGrids: NodeGrid[][] = [];

    /**记录起点 */
    startGrid: DataGrid = null;
    /**记录终点 */
    endGrid: DataGrid = null;

    onLoad () {
        this.generateMap();
    }

    /**随机生成8x8地图 */
    generateMap () {
        for (let x = 0;x < 8;x ++){
            this.dataGrids[x] = [];
            this.nodeGrids[x] = [];
            for (let y = 0;y < 8;y ++){
                let rand = Math.random();
                let grideType: GrideType = GrideType.Normal;
                if (rand < 0.2) { //1/5的概率生成墙
                    grideType = GrideType.Wall;
                }
                //数据层
                let grid: DataGrid = new DataGrid();
                grid.x = x;
                grid.y = y;
                grid.type = grideType;
                this.dataGrids[x][y] = grid;

                //视图层
                let gridNode: NodeGrid = cc.instantiate(this.nodeGridPrefab).getComponent(NodeGrid);
                gridNode.node.position = cc.v3(50 * (x - 4),50 * (y - 4),0);
                this.nodeGrids[x][y] = gridNode;
                gridNode.dataGrid = grid;
                gridNode.findPathController = this;
                gridNode.updateGridColor();
                gridNode.node.parent = this.node;
            }
        }
    }

    /**点击格子 */
    onTouch(nodeGrid: NodeGrid){
        if (!this.startGrid) { //设置起点
            this.startGrid = nodeGrid.dataGrid;
            this.startGrid.type = GrideType.Start;
            nodeGrid.updateGridColor();
        }else if (!this.endGrid) { //设置终点
            this.endGrid = nodeGrid.dataGrid;
            this.endGrid.type = GrideType.End;
            nodeGrid.updateGridColor();

            //寻路
            this.startFindPathAStar();
        }
    }

    /**待考虑的节点列表 */
    openPath: DataGrid[] = [];

    /**A星寻路 */
    startFindPathAStar(){
        this.openPath.push(this.startGrid);
        this.startGrid.inOpenList = true;

        while (this.openPath.length > 0) {
            let current = this.openPath.shift(); //shift--取出数组中首个元素

            if (current == this.endGrid) {
                break;
            }
            let round = this.getRoundGrid(current);

            for (let i = 0;i < round.length;i ++) {
                let r = round[i];
                r.fatherGrid = current;
                r.inOpenList = true;
            }

            this.openPath = this.openPath.concat(round); //拼接数组
            this.openPath.sort(this.compareGridsAStar.bind(this));

            current.inCloseList = true;
        }

        if (this.endGrid.fatherGrid) {
            let pathGrid = this.endGrid;

            while (pathGrid) {
                pathGrid.type == GrideType.Road;
                this.nodeGrids[pathGrid.x][pathGrid.y].updateGridColor();
                pathGrid = pathGrid.fatherGrid;
            }
        }else {
            console.log("没有路径可走");
        }
    }

    /**获取当前节点周围可走的节点 */
    getRoundGrid(grid: DataGrid): DataGrid[] {
        let arr: DataGrid[] = [];
        //周围的格子
        this.addToRoundIfNeed(arr,this.getGrid(grid.x,grid.y + 1));
        this.addToRoundIfNeed(arr,this.getGrid(grid.x - 1,grid.y));
        this.addToRoundIfNeed(arr,this.getGrid(grid.x,grid.y - 1));
        this.addToRoundIfNeed(arr,this.getGrid(grid.x + 1,grid.y));
        this.addToRoundIfNeed(arr,this.getGrid(grid.x - 1,grid.y - 1));
        this.addToRoundIfNeed(arr,this.getGrid(grid.x + 1,grid.y + 1));
        this.addToRoundIfNeed(arr,this.getGrid(grid.x + 1,grid.y - 1));
        this.addToRoundIfNeed(arr,this.getGrid(grid.x - 1,grid.y + 1));

        //会将数组里元素两两进行比较,自定义方法里返回-1就不交换位置 返回1交换位置
        arr.sort(this.compareGridsAStar.bind(this));

        return arr;
    }

    /**将格子放到数组里 */
    addToRoundIfNeed(arr: DataGrid[],roundGrid: DataGrid) {
        //当前节点和路径节点都不计入
        if (!roundGrid || roundGrid.type == GrideType.Wall || roundGrid.inCloseList || roundGrid.inOpenList){
            return;
        }
        if (roundGrid) {
            arr.push(roundGrid);
        }
    }

    /**根据坐标获取格子数据 */
    getGrid(x: number,y: number): DataGrid {
        //边界判断
        if (x < 0 || x >= 8 || y < 0 || y >=8){
            return null;
        }
        return this.dataGrids[x][y];
    }

    /**格子排序 优化
     * 距离小的放在前面
     */
     compareGridsAStar(grid0: DataGrid,grid1: DataGrid): number{
        let grid0Dis = this.getDistanceAStar(grid0,this.startGrid,this.endGrid);
        let grid1Dis = this.getDistanceAStar(grid1,this.startGrid,this.endGrid);
        if (grid0Dis > grid1Dis) {
            return 1;
        }else{
            return -1;
        }
    }

    /**获取综合距离 优化
     * grid 当前节点
     * start 起始节点
     * end 目标节点
    */
    getDistanceAStar(grid: DataGrid,start: DataGrid,end: DataGrid) {
        let endDis = Math.abs(grid.x - end.x) + Math.abs(grid.y - end.y);
        let startDis = Math.abs(grid.x - start.x) + Math.abs(grid.y - start.y);
        return endDis + startDis;
    }

    /**点击重新开始*/
    onBtnRestart(){
        for (let x = 0;x < this.dataGrids.length;x ++){
            for (let y = 0;y < this.dataGrids[x].length;y ++){
                let dataGrid = this.dataGrids[x][y];
                dataGrid.inOpenList = false;
                dataGrid.inCloseList = false;
                if (dataGrid.type != GrideType.Wall){
                    dataGrid.type = GrideType.Normal;
                }
                this.nodeGrids[x][y].updateGridColor();
            }
        }
        this.startGrid = null;
        this.endGrid = null;
        this.openPath = [];
    }

}


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194

08.对象池模式

意义

用于解决当需要创建大量相同对象的时候,避免重复创建,节能.

流程图

在这里插入图片描述

直接创建对象

/**对象池模式 */
const {ccclass, property} = cc._decorator;

@ccclass
export default class PoolDemo extends cc.Component {

    @property(cc.Node)
    nodeIcon: cc.Node = null;

    onLoad () {

    }

    shoot() {
        let node = cc.instantiate(this.nodeIcon);
        //创建完节点还要从父节点上移出,太费事了
        node.runAction(cc.sequence(cc.moveBy(1,0,300),cc.removeSelf()));
        node.parent = this.node;
    }

    update() {
        this.shoot();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

会不断创建节点,并将节点从父节点移出,耗费性能,内存.

使用对象池

/**对象池模式 */
const {ccclass, property} = cc._decorator;

@ccclass
export default class PoolDemo extends cc.Component {

    @property(cc.Node)
    nodeIcon: cc.Node = null;

    /**对象池 */
    pool: cc.Node[] = [];

    onLoad () {

    }

    shoot() {
        let node = this.getNode();
        //创建完节点还要从父节点上移出,太费事了
        node.runAction(cc.sequence(cc.moveBy(1,0,300),cc.removeSelf(),cc.callFunc(function () {
            node.position = cc.Vec2.ZERO; //节点位置重置
            //用完后将节点放回对象池
            this.pool.push(node);
        }.bind(this))));
        node.parent = this.node;
    }

    /**获取节点
     * 如果对象池里有节点的话就取出来用
     * 没有的话就实例化一个
     */
    getNode(): cc.Node {
        if (this.pool.length > 0) {
            return this.pool.shift();
        }else {
            console.log("创建了一个节点");
            return cc.instantiate(this.nodeIcon);
        }
    }

    update() {
        this.shoot();
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

运行效果

只需创建了62个节点
在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/菜鸟追梦旅行/article/detail/152466
推荐阅读
相关标签
  

闽ICP备14008679号