四方向A星寻路

步骤解释可参考http://blog.csdn.net/agroupofruffian/article/details/77700338,本示例即按照该步骤而来;
主场景皮肤:
<?xml version="1.0" encoding="utf-8"?>
<e:Skin class="aStarWin" width="800" height="600" xmlns:e="http://ns.egret.com/eui" xmlns:w="http://ns.egret.com/wing">
	<e:Rect width="800" height="600"/>
	<e:Rect width="600" height="600" horizontalCenter="0" verticalCenter="0" fillColor="0x53ea09"/>
	<e:ToggleButton id="btnSetTag" label="设置障碍" x="700" y="0"/>
	<e:ToggleButton id="btnStartPos" label="设置起点" x="700" y="60"/>
	<e:ToggleButton id="btnSetEndPos" label="设置终点" x="700" y="120"/>
	<e:ToggleButton id="btnFindWay" label="开始寻路" x="700" y="180"/>
	<e:ToggleButton id="btnReset" label="重置" x="700" y="240"/>
	<e:Group id="blockGroup" width="600" height="600" horizontalCenter="0" verticalCenter="0">
		<e:layout>
			<e:TileLayout requestedRowCount="10" requestedColumnCount="10" horizontalGap="0" verticalGap="0"/>
		</e:layout>
	</e:Group>
</e:Skin>
-------------------------------分割线-------------------------------
单块皮肤
<?xml version="1.0" encoding="utf-8"?>
<e:Skin class="block" width="60" height="60" xmlns:e="http://ns.egret.com/eui" xmlns:w="http://ns.egret.com/wing">
	<e:Rect id="rectBG" width="50" height="50" x="5" y="5" fillColor="0x096BE8"/>
	<e:Label id="lbX" text="Label" size="15" horizontalCenter="0.5" verticalCenter="-16.5" touchEnabled="false"/>
	<e:Label id="lbY" text="Label" size="15" horizontalCenter="0.5" verticalCenter="3.5" touchEnabled="false"/>
	<e:Label id="lbCode" text="Label" y="41" size="15" horizontalCenter="0"/>
</e:Skin>

-------------------------------分割线-------------------------------
块类

module ui {
    export class Block extends eui.Component {
        private rectBG: eui.Rect;
        private lbX: eui.Label;
        private lbY: eui.Label;
        private lbCode: eui.Label;
        public data: BlockData = null;
        public lastBlcok: ui.Block = null;
        constructor(data: ui.BlockData) {
            super();
            this.data = data;
            this.addEventListener(eui.UIEvent.COMPLETE, this.uiComplete, this);
            this.addEventListener(egret.Event.ADDED_TO_STAGE, this.addToStage, this);
            this.skinName = "resource/ui/block.exml";
        }
        private uiComplete(): void {
            this.lbX.text = "x:" + this.data.x;
            this.lbY.text = "y:" + this.data.y;
            this.lbCode.text = this.hashCode + "";
        }
        public setTagStatus(): void {
            this.rectBG.fillColor = (this.data.tag) ? 0x000000 : 0x096BE8;
        }
        private addToStage(): void {
            this.rectBG.addEventListener(egret.TouchEvent.TOUCH_TAP, this.onBtnRectBG, this);
        }
        private onBtnRectBG(): void {
            if (AStarUI.step == Step.settag) {
                this.data.tag = 1;
                this.setTagStatus();
            } else if (AStarUI.step == Step.setStar) {
                if (AStarUI.startBlock != null) {
                    AStarUI.startBlock.setTagStatus();
                }
                this.data.tag = 0;
                this.rectBG.fillColor = 0xE5094B;
                AStarUI.startBlock = this;
            } else if (AStarUI.step == Step.setEnd) {
                if (AStarUI.endBlock != null) {
                    AStarUI.endBlock.setTagStatus();
                }
                this.data.tag = 0;
                this.rectBG.fillColor = 0xF72E04;
                AStarUI.endBlock = this;
            }
        }
        public showWay(): void {
            this.rectBG.fillColor = 0xFFFFFF;
        }
    }
}

-------------------------------分割线-------------------------------

主场景类

module ui {
    export interface BlockData {
        x: number,/**横向第几位 */
        y: number,/**竖向第几位 */
        tag: number,/**能否通过,0可以,1不行 */
    }
    export enum Direction {
        up = 1,
        down = 2,
        left = 3,
        right = 4
    }
    export class Step {
        public static settag = "settag";
        public static setStar = "setStar";
        public static setEnd = "setEnd";
    }
    export class AStarUI extends eui.Component {
        private blockGroup: eui.Group;
        private btnSetTag: eui.ToggleButton;
        private btnStartPos: eui.ToggleButton;
        private btnSetEndPos: eui.ToggleButton;
        private btnFindWay: eui.ToggleButton;
        private btnReset: eui.ToggleButton;
        private lastBtn: eui.ToggleButton = null;
        public static step: string = "";
        public static startBlock: ui.Block = null;
        public static endBlock: ui.Block = null;
        private blockMap: { [ind: number]: ui.Block } = {};
        private openList: ui.Block[] = [];
        private closeList: ui.Block[] = [];
        constructor() {
            super();
            this.addEventListener(eui.UIEvent.COMPLETE, this.uiComplete, this);
            this.addEventListener(egret.Event.ADDED_TO_STAGE, this.addToStage, this);
            this.skinName = "resource/ui/aStarWin.exml";
        }
        private uiComplete(): void {
            let x1 = 1,
                y1 = 1;
            for (let i = 0; i < 100; i++) {/**设置一10x10地图块 */
                let data: ui.BlockData = { x: x1, y: y1, tag: 0 };
                let block: ui.Block = new ui.Block(data);
                this.blockGroup.addChild(block);
                this.blockMap[x1 + "" + y1] = block;/**块对应键存入 */
                x1++;
                if (x1 == 11) {
                    x1 = 1;
                    y1++;
                }
            }
        }
        private addToStage(): void {
            this.btnSetTag.addEventListener(egret.TouchEvent.CHANGE, this.onTouchBtn, this);
            this.btnStartPos.addEventListener(egret.TouchEvent.CHANGE, this.onTouchBtn, this);
            this.btnSetEndPos.addEventListener(egret.TouchEvent.CHANGE, this.onTouchBtn, this);
            this.btnFindWay.addEventListener(egret.TouchEvent.CHANGE, this.onTouchBtn, this);
            this.btnReset.addEventListener(egret.TouchEvent.CHANGE, this.onTouchBtn, this);
        }
        private onTouchBtn(e: egret.TouchEvent): void {
            let btn: eui.ToggleButton = e.target;
            if (btn == this.btnSetTag) {/**设置障碍 */
                AStarUI.step = Step.settag;
            } else if (btn == this.btnStartPos) {/**设置起点 */
                AStarUI.step = Step.setStar;
            } else if (btn == this.btnSetEndPos) {/**设置终点 */
                AStarUI.step = Step.setEnd;
            } else if (btn == this.btnFindWay) {/**开始寻路 */
                AStarUI.step = "";
                this.findWay();
            } else if (btn == this.btnReset) {/**重置地图 */
                AStarUI.step = "";
                AStarUI.startBlock = null;
                AStarUI.endBlock = null;
                this.openList = [];
                this.closeList = [];
                for (let obj in this.blockMap) {
                    this.blockMap[obj].data.tag = 0;
                    this.blockMap[obj].lastBlcok = null;
                    this.blockMap[obj].setTagStatus();
                }
            }
            if (this.lastBtn != null) {
                this.lastBtn.selected = false;
            }
            this.lastBtn = btn;
        }
        private findWay(): void {
            if (!AStarUI.startBlock) {
                console.log("未设置起点");
                return
            }
            if (!AStarUI.endBlock) {
                console.log("未设置终点");
                return
            }
            let beginBlock: ui.Block = AStarUI.startBlock;
            this.count = 0;
            this.searchBlock(beginBlock);
        }
        private count: number = 0;
        private searchBlock(beginBlock: ui.Block): void {/**解释参照 http://blog.csdn.net/agroupofruffian/article/details/77700338 */
            /**寻找当前起点块上下左右四个块 */
            this.count++;
            let blockArr: ui.Block[] = [];
            let upBlock = this.findBlock(Direction.up, beginBlock);
            if (upBlock) {
                blockArr.push(upBlock);
            }
            let downBlock = this.findBlock(Direction.down, beginBlock);
            if (downBlock) {
                blockArr.push(downBlock);
            }
            let leftBlock = this.findBlock(Direction.left, beginBlock);
            if (leftBlock) {
                blockArr.push(leftBlock);
            }
            let rightBlock = this.findBlock(Direction.right, beginBlock);
            if (rightBlock) {
                blockArr.push(rightBlock);
            }
            /**新块数组的前一个块设置为当前起点块 */
            blockArr.forEach(block => {
                block.lastBlcok = beginBlock;
            });
            /**新块里有终点块,即为找到,结束 */
            if (blockArr.indexOf(AStarUI.endBlock) != -1) {
                console.log(this.count);
                this.showWay();
                return;
            }
            /**新块数组追加到开放数组 */
            this.openList = this.openList.concat(blockArr);
            /**当前起点块未在关闭数组中,加入关闭数组 */
            if (this.closeList.indexOf(beginBlock) == -1) {
                this.closeList.push(beginBlock)
            }
            let that = this;
            /**开放数组排序,F = G + H,g为最初起点到当前块的步数,H为当前块到终点的步数(定值)   */
            this.openList.sort((a, b) => {
                let a_last = a.lastBlcok,
                    b_last = b.lastBlcok;
                let Ga = that.getGValue(a_last),
                    Gb = that.getGValue(b_last);
                let Ha = Math.abs(a.data.x - AStarUI.endBlock.data.x) + Math.abs(a.data.y - AStarUI.endBlock.data.y),
                    Hb = Math.abs(b.data.x - AStarUI.endBlock.data.x) + Math.abs(b.data.y - AStarUI.endBlock.data.y);
                return (Ga + Ha - Gb - Hb);
            });
            /**开放列表中选取F最小(即步数最少)块作为新起点 */
            let bestBlock = this.openList.shift();
            /**新起点不存在,即未寻得路,结束 */
            if (!bestBlock) {
                console.log("路线未寻得");
                console.log(this.count);
                return;
            }
            /**新起点做参数继续寻找 */
            this.searchBlock(bestBlock);
        }
        private findBlock(dir: Direction, curBlock: ui.Block): ui.Block {
            let newInd: string = "";
            if (dir == Direction.up) {
                newInd = (curBlock.data.x) + "" + (curBlock.data.y - 1);
            } else if (dir == Direction.down) {
                newInd = (curBlock.data.x) + "" + (curBlock.data.y + 1);
            } else if (dir == Direction.left) {
                newInd = (curBlock.data.x - 1) + "" + (curBlock.data.y);
            } else if (dir == Direction.right) {
                newInd = (curBlock.data.x + 1) + "" + (curBlock.data.y);
            }
            let block: ui.Block = this.blockMap[newInd];
            if (block && block.data.tag == 0 && this.closeList.indexOf(block) == -1) {/**寻得的新块存在并且不是障碍块且不在关闭数组中 */
                if (this.openList.indexOf(block) != -1) {/**新块在开启数组中 */
                    let oldGa = 0, newGa = 0;
                    let bl = block.lastBlcok;
                    oldGa = this.getGValue(block.lastBlcok);/**最初起点到当前新块步数 */
                    newGa = this.getGValue(curBlock);/**最初起点块到参数块再到当前新块的步数 */
                    if (oldGa > newGa) {/**步数少的,设置为新块的前一块 */
                        block.lastBlcok = curBlock;
                    }
                    return null;
                } else {
                    return block;
                }
            } else {
                return null;
            }
        }
        private getGValue(block: ui.Block): number {
            let value = 0;
            while (block) {
                value++;
                block = block.lastBlcok;
            }
            return value;
        }
        /**显示路线 */
        private showWay(): void {
            let block: ui.Block = AStarUI.endBlock.lastBlcok;
            while (block != null) {
                if (block != AStarUI.startBlock) {
                    block.showWay();
                }
                block = block.lastBlcok;
            }
        }
    }
}

-------------------------------分割线-------------------------------

附:可能还有些不完善的地方,有时候寻路会多一两步(问题已修改,原因是在寻找新块时遗漏了判断新块在不在关闭数组和开启数组的条件)

猜你喜欢

转载自blog.csdn.net/qq_39194398/article/details/79481181