一、解决问题:
在Unity3d的一些2d游戏制作上,有时需要敌人在众多箱子(障碍物)中向主角移动,如果是简单的移动ai代码实现,敌人有可能陷入死胡同然后左右移动,卡死在胡同上,为了一个智能的ai实现,比较常用的是A*算法,但这里为了简便,我们使用了深度搜索的方式进行判断。所谓深度搜索,一位博主总结得很好,就是“不见棺材不回头。算法会朝一个方向进发,直到遇到边界或者障碍物,才回溯。一般在实现的时候,我们采用递归的方式来进行,也可以采用模拟压栈的方式来实现。”
在这里的代码中,是利用c#的栈,保存了名为Cell的宫格类,Cell类中包含了周围四个方向是否有路的信息,Maze迷宫类中将对每个能走的方向都进行尝试行走,将能走的路一直走下去,直到走不通为止,此时开始回溯,即使用stack.Pop(),一直退回分支,走下一条路线。举个例子:当前宫格的北方向【有路】,那么尝试向北走一步,走完后把设置此宫格的北方向为【无路】,并把走到的那个宫格的南方向设置为【无路】(都是相对的),就这样一直走下去,当发现无法到达终点时,开始退回,再次判断当前宫格的四个方向是否有路,直到退回到最后的分支上,进行下一条路的尝试行走。
该算法虽然简单,但却在不少地方都需要用到,比如游戏按键刷图脚本的地图判断的处理部分:例如找色判断dnf、手游《永远的7日之都》的万神殿地图部分,只要能直到当前揭示的地图,就可以算出一条通向boss的道路,然后自动刷图就完成了一个核心功能。
二、演示效果:
控制台程序代码Program.cs中输入:
int[,] maps = new int[,] {
{ 0, 0, 0, 0, 0 },
{ 1, 0, 0, 1, 1 },
{ 0, 0, 0, 1, 0 },
{ 0, 1, 1, 1, 1 },
{ 0, 0, 0, 0, 0 } };
注释:0代表可通过,1代表不可通过。
运行程序,输出如下:
2 2 2 0 0
1 2 2 1 1
2 2 2 1 0
2 1 1 1 1
2 2 2 2 2
注释:2代表为走出的路径
三、上代码:
创建控制台程序:
Program.cs:
using System; namespace MazePath { class Program { static void Main(string[] args) { //0表示可走,1表示不可走 int[,] maps = new int[,] { { 0, 0, 0, 0, 0 }, { 1, 0, 0, 1, 1 }, { 0, 0, 0, 1, 0 }, { 0, 1, 1, 1, 1 }, { 0, 0, 0, 0, 0 } }; Maze maze = new Maze(maps); Console.ReadLine(); } } }
Maze.cs:
using System; using System.Collections.Generic; namespace MazePath { class Maze { Cell[,] cells; Stack<Cell> cellStack = new Stack<Cell>(); int[,] maps; int rows; int length; public Maze(int[,] maps) { this.maps = maps; length = maps.GetLength(1); rows = maps.GetLength(0); cells = new Cell[rows, length]; InitCells(); GetValidPath(); ConsoleResult(); } public void InitCells() { for (int i = 0; i < rows; i++) { for (int j = 0; j < length; j++) { //设置每个格子自身的位置属性 Cell cell = new Cell(); cell.SetPos(i, j); cells[i, j] = cell; //-----设置格子的周围方向属性--------------- //西 if ((j - 1) >= 0) { if (maps[i, j - 1] == 0) cells[i, j].SetDirection(Const.WEST, Const.ENABLE); else cells[i, j].SetDirection(Const.WEST, Const.DISABLE); } //东 if ((j + 1) < length) { if (maps[i, j + 1] == 0) cells[i, j].SetDirection(Const.EAST, Const.ENABLE); else cells[i, j].SetDirection(Const.EAST, Const.DISABLE); } //北 if ((i - 1) >= 0) { if (maps[i - 1, j] == 0) cells[i, j].SetDirection(Const.NORTH, Const.ENABLE); else cells[i, j].SetDirection(Const.NORTH, Const.DISABLE); } //南 if ((i + 1) < rows) { if (maps[i + 1, j] == 0) cells[i, j].SetDirection(Const.SOUTH, Const.ENABLE); else cells[i, j].SetDirection(Const.SOUTH, Const.DISABLE); } } } } public void GetValidPath() { int i = 0;int j = 0; cellStack.Push(cells[i, j]); while (true) { i = cellStack.Peek().x; j = cellStack.Peek().y; if (i == (rows - 1) && j == (length - 1)) { return; } //东 if (cellStack.Peek().GetDirection(Const.EAST)==Const.ENABLE) { cells[i, j].SetDirection(Const.EAST, Const.DISABLE); cells[i, j+1].SetDirection(Const.WEST, Const.DISABLE); cellStack.Push(cells[i, j + 1]); continue; } //西 if (cellStack.Peek().GetDirection(Const.WEST) == Const.ENABLE) { cells[i, j].SetDirection(Const.WEST, Const.DISABLE); cells[i, j + 1].SetDirection(Const.EAST, Const.DISABLE); cellStack.Push(cells[i, j - 1]); continue; } //北 if (cellStack.Peek().GetDirection(Const.NORTH) == Const.ENABLE) { cells[i, j].SetDirection(Const.NORTH, Const.DISABLE); cells[i - 1, j].SetDirection(Const.SOUTH, Const.DISABLE); cellStack.Push(cells[i-1, j]); continue; } //南 if (cellStack.Peek().GetDirection(Const.SOUTH) == Const.ENABLE) { cells[i, j].SetDirection(Const.SOUTH, Const.DISABLE); cells[i + 1, j].SetDirection(Const.NORTH, Const.DISABLE); cellStack.Push(cells[i+1, j]); continue; } cellStack.Pop(); if (cellStack.Count == 0) { Console.WriteLine("根本没有这个路径"); return; } } } public void ConsoleResult() { while (cellStack.Count != 0) { int i = cellStack.Peek().x; int j = cellStack.Peek().y; cellStack.Pop(); maps[i, j] = 2; } string s = ""; for(int i = 0; i < maps.GetLength(0); i++) { for(int j = 0; j < maps.GetLength(1); j++) { s += maps[i, j].ToString()+" "; } s += "\r\n"; } Console.WriteLine(s); } } }
Cell.cs:
namespace MazePath { class Cell { public int x; public int y; //directon 索引0-3分别是东西南北,默认为0表示不可走,1则表示可走 private int[] directions = new int[4]; public void SetPos(int x,int y) { this.x = x; this.y = y; } public bool IsEnable(int index) { return directions[index]==1; } public void SetDirection(int index,int result) { directions[index] = result; } public int GetDirection(int index) { return directions[index]; } } }
Const.cs:
namespace MazePath { public static class Const { public const int EAST = 0; //东 public const int WEST = 1; //西 public const int SOUTH = 2; //南 public const int NORTH = 3; //北 public const int ENABLE = 1; public const int DISABLE = 0; } }