A*算法网上很多介绍了,在此就不详细介绍了。
首先是计算一个格子的(start)的相邻4个方向上的G,F,H值等
每个格子有一个index属性来标识在cells数组中的位置
target表示要到达的目标格子
opened是要将要搜寻的数组
closed是已经搜寻了的数组
snakes看着障碍物
function SnakeGame:calculateCell(start, dir, target, opend, closed, snakes)
local cell = nil
local index = start.index
local b = false
if dir == SnakeGame.Dir.LEFT then
cell = index - 1
elseif dir == SnakeGame.Dir.RIGHT then
cell = index + 1
elseif dir == SnakeGame.Dir.UP then
cell = index + self.column_num
elseif dir == SnakeGame.Dir.DOWN then
cell = index - self.column_num
end
if self.cells[cell] then
for k, v in ipairs(closed) do
if cell == v.index then
-- 已经在搜寻过了的路径里则不作处理
b = true
break
end
end
if not b then
for k, v in ipairs(snakes) do
if cell == v.index and v ~= target then
-- 障碍物
b = true
break
end
end
end
if not b then
-- 跳行的判断
if dir == SnakeGame.Dir.LEFT then
b = self.cells[index].x > self.cells[cell].x
elseif dir == SnakeGame.Dir.RIGHT then
b = self.cells[index].x < self.cells[cell].x
elseif dir == SnakeGame.Dir.UP then
b = self.cells[index].y < self.cells[cell].y
elseif dir == SnakeGame.Dir.DOWN then
b = self.cells[index].y > self.cells[cell].y
end
if b then
local G = start.G + 1 --G值是上一个的G+1
local H = math.abs(self.cells[target.index].x - self.cells[cell].x) / self.cell_width + math.abs(self.cells[target.index].y - self.cells[cell].y) / self.cell_width
-- H是当前到目标的最短距离(其实就是横向和竖向的直线距离和)
local F = G + H
for k, v in ipairs(opend) do
if v.index == cell then
if F < v.F then
-- 如果在待搜索的里面并且F值更小则替换F值和parent
v.F = F
v.parent = start
end
return nil
end
end
-- 返回dir方向上的搜寻
return {index = cell, G = G, H = H, F = F, parent = start, dir = dir}
end
end
end
return nil
end
在opend里找到最小F值的
function SnakeGame:findMinF(opend)
local key = #opend
for k, v in ipairs(opend) do
if k < #opend then
if v.F <= opend[key].F then
key = k
end
end
end
return key
end
搜寻一条最短的路径
-- 返回蛇的头部到达target的最短路径或者不存在
function SnakeGame:checkAWay(snakes, target)
local opend = {}
local closed = {}
-- 用蛇的头部作为搜寻的起点
local start = {index = snakes[1].index, G = 0}
table.insert(closed, start)
local left = self:calculateCell(start, SnakeGame.Dir.LEFT, target, opend, closed, snakes)
local right = self:calculateCell(start, SnakeGame.Dir.RIGHT, target, opend, closed, snakes)
local up = self:calculateCell(start, SnakeGame.Dir.UP, target, opend, closed, snakes)
local down = self:calculateCell(start, SnakeGame.Dir.DOWN, target, opend, closed, snakes)
-- 存在则插入到opend数组里面
if left then
table.insert(opend, left)
end
if right then
table.insert(opend, right)
end
if up then
table.insert(opend, up)
end
if down then
table.insert(opend, down)
end
local find_a_way = false
while opend[1] do
local key = self:findMinF(opend)
-- 最小F值的放入closed里面
table.insert(closed, opend[key])
table.remove(opend, key)
-- 到达目标则找到最短路径
if closed[#closed].index == target.index then
find_a_way = true
break
end
local left = self:calculateCell(closed[#closed], SnakeGame.Dir.LEFT, target, opend, closed, snakes)
local right = self:calculateCell(closed[#closed], SnakeGame.Dir.RIGHT, target, opend, closed, snakes)
local up = self:calculateCell(closed[#closed], SnakeGame.Dir.UP, target, opend, closed, snakes)
local down = self:calculateCell(closed[#closed], SnakeGame.Dir.DOWN, target, opend, closed, snakes)
if left then
table.insert(opend, left)
end
if right then
table.insert(opend, right)
end
if up then
table.insert(opend, up)
end
if down then
table.insert(opend, down)
end
end
if find_a_way then
return closed
else
print("not find a way!")
return nil
end
end
在ctor里面初始化一个数组self.targetLines = {} 用于放置最短路径每一步的方向,即蛇移动的方向
function SnakeGame:doAI(target)
if #self.targetLines > 0 then
return
end
target = target or self.tail
local closed = self:checkAWay(self.snakes, target)
if closed then
self.targetLines = {}
table.insert(self.targetLines, closed[#closed].dir)
local parent = closed[#closed].parent
local ii = 1
while parent and parent ~= closed[1] do
local index = parent.index
local x = self.cells[index]
table.insert(self.targetLines, parent.dir)
parent = parent.parent
end
else
print("not find a way!")
end
end
在generateBody末尾增加一个self:doAI()的调用,让每次生成目标tail的时候生成最短路径!
最后修改onEnter函数中的update让它沿AI最短路径跑起来
function SnakeGame:onEnter()
local time1 = tsixi.THelper:currentMilliseconds()
local update = function(dt)
if not self.is_failed then
local now = tsixi.THelper:currentMilliseconds()
if not self.parse and (now - time1 >= 50 or self.must) then
self.must = false
time1 = now
self.direction = self.targetLines[#self.targetLines]
table.remove(self.targetLines)
self:doMove()
end
end
end
self:scheduleUpdateWithPriorityLua(update, 0)
end
现在蛇可以自己去吃目标物了
现在这个AI还有很多问题,如果生成的红块在蛇本身的身体里时就找不到,下一节再优化这个问题