check out my article on Herding AI: http://www.roguebasin.com/index.php/Denizen_Herding_Behavior
---------------sample code:
Here is the code for propogating sounds throughout the gameworld:
Intensity is a variable usally from 1-50
activator is an "object" - whoever is making the sound in the game world.
x,y, and z i s where the sound starts from.
The code works by taking "steps outward" from the center of the sound and making an entry in the "soundscape" with relevant information about the intensity of the sound and who made the sound.
Currently, it also checks the soundactivate function of whatever object it "hits" so a ghoul can "wake up" and start pathfinding towards the object that made the noise.
In the future, this code will also check for any "sound blocking" objects that stop the propogation of the sound to surrounding squares, like walls.
function funcs.makesound(intensity,activator,x,y,z)
-- print("Making a sound .."..intensity.." from "..activator["name"].." X "..x.." Y "..y.." Z "..z)
local objectloc = data.objectloc
local working, future = {}, {{x,y,z}}
local soundscape = data.soundscape
local finished = {}
local insert = table.insert
local tiles = data.tiles
local checkforedgeofmap = funcs.checkforedgeofmap
finished[x] = {}
finished[x][y] = {}
finished[x][y][z] = 1
soundscape[x][y][z][activator] = soundscape[x][y][z][activator] or {} --for moveenemytowards
insert(soundscape[x][y][z][activator],{["strength"] = intensity})
local counter = intensity
while(counter <= intensity and counter >= 1) do
counter = counter - 1
working,future = future, {}
local x,y,z
for k,v in pairs(working) do
for x = v[1]- 1,v[1] + 1 do
finished[x] = finished[x] or {}
for y = v[2] -1,v[2] + 1 do
finished[x][y] = finished[x][y] or {}
for z = v[3] - 1,v[3] + 1 do
if z < data.amountofzlevels and z >= 1 and (not checkforedgeofmap(x,y)) and (not finished[x][y][z]) and (not tiles[x][y][z]["filled"]) and (not tiles[x][y][z]["open"]) then
finished[x][y][z] = 1
insert(future,{x,y,z})
for k2,ob in pairs (objectloc[x][y][z]) do
if ob["soundactivate"] then
for k3,v3 in pairs(ob["soundactivate"]) do
if k3 < counter then v3(ob,activator) end
end
end
end
soundscape[x][y][z][activator] = soundscape[x][y][z][activator] or {} --for moveenemytowards
insert(soundscape[x][y][z][activator],{["strength"] = counter})
-------------------------------------------------------------------------------------------------------
This code needs some cleaning up, it has a few meaningless variables here and there and I wrote it strenuously and with many revisions. It works well, though, and the heuristic can be altered easily enough. When funcs.astarpathfind is called, it returns a table with a chart of what squares are what distance from the destination, so actually, I pathfind backwards, from the destination to the creature who's moving, who just has to move onto the next square with the least distance to get to his destination.
This is my "A-star pathfinding code":
function funcs.checkforblockpassageofpath(x,y,z)
local tiles = data.tiles
if not tiles[x] or not tiles[x][y] or not tiles[x][y][z] then return true end
if tiles[x][y][z]["filled"] or tiles[x][y][z]["open"] then return true end
return false end
function funcs.astarpathfind(startx,starty,startz,endx,endy,endz,ob)
if startx == endx and starty == endy and startz == endz then return false end
local working = {} --node list
-- local closed = {} --node list
local insert = table.insert
local min = math.min
local mapsizex = data.levelsizex
local mapsizey = data.levelsizey
local distances = {}
local future = {}
local getdistance = funcs.getdistance
local blocking = funcs.checkforblockpassageofpath
local impossible = (mapsizex * mapsizey) + 1
--seed the working table
distances[startx] = {}
distances[startx][starty] = {}
distances[startx][starty][startz] = 0
-- print("Startx "..startx)
-- print("Starty "..starty)
-- print("endx "..endx)
-- print("endy "..endy)
-- print("---------")
insert(future,{startx,starty,startz,0,funcs.getastarheuristic(ob,startx,starty,startz,endx,endy,endz)})
while(true) do
for k,v in pairs(future) do/
insert(working,v)
end
future = {}
--Now,get the one with the best heuristic.
local lowestfvalue = impossible --or lowestfvalue (dumb pathfind)
local savedspot
local savedk
-- print("Lowest F value "..lowestfvalue)
for k,v in pairs(working) do --4 is distance, 5 is heuristic
-- print ("WORKING "..working[k][4] + working[k][5])
-- print("LOWEST F "..lowestfvalue)
if working[k][4] + working[k][5] < lowestfvalue then
lowestfvalue = working[k][4] + working[k][5]
savedspot = v
savedk = k
-- print("New lowest f value "..lowestfvalue)
end
end
working[savedk] = nil
--iterate over that one
for x = savedspot[1] - 1,savedspot[1] + 1 do
for y = savedspot[2] -1,savedspot[2]+1 do
for z = savedspot[3] -1,savedspot[3] + 1 do
distances[x] = distances[x] or {}
distances[x][y] = distances[x][y] or {}
distances[x][y][z] = distances[x][y][z] or impossible
local newdistance = min(distances[x][y][z], savedspot[4] + getdistance(x,y,savedspot[1],savedspot[2]))
-- if not closed[savedspot] then
if not blocking(x,y,z) then
if newdistance < distances[x][y][z] then -- adds or readds a spot to the array
-- print("Updating X "..x.." Y "..y)
local myh = funcs.getastarheuristic(ob,x,y,z,endx,endy,endz)
insert(future, {x,y,z,newdistance,myh})
distances[x][y][z] = newdistance
end
end
if x == endx and y == endy and z == endz then
--print("Correct Return")
return distances
end
-- end --ends closedcheck
end--ends for y
end --ends for x
end --ends for z
--The following visualizes the pathfind data
-- print("Path X "..savedspot[1].." Y "..savedspot[2])
-- print("DIST "..savedspot[4])
-- print("HEUR "..savedspot[5])
-- slang.gotorc(savedspot[2]+data.mapoffsety,savedspot[1]+data.mapoffsetx)
-- slang.setcolor(math.random(1,15))
-- slang.writechar("*")
-- slang.refresh()
-- funcs.displaymessage(1,1,"Press a key")
-- funcs.getkey()
end --ends while true
print("shouldnt have gotten to here.")
os.exit()
end
function funcs.getastarheuristic(ob,x,y,z,x2,y2,z2) --was processer heavy possibly.
local distanceguess= 10 * funcs.manhattandistance(x,y,x2,y2)
return distanceguess
end
---------------sample code:
Here is the code for propogating sounds throughout the gameworld:
Intensity is a variable usally from 1-50
activator is an "object" - whoever is making the sound in the game world.
x,y, and z i s where the sound starts from.
The code works by taking "steps outward" from the center of the sound and making an entry in the "soundscape" with relevant information about the intensity of the sound and who made the sound.
Currently, it also checks the soundactivate function of whatever object it "hits" so a ghoul can "wake up" and start pathfinding towards the object that made the noise.
In the future, this code will also check for any "sound blocking" objects that stop the propogation of the sound to surrounding squares, like walls.
function funcs.makesound(intensity,activator,x,y,z)
-- print("Making a sound .."..intensity.." from "..activator["name"].." X "..x.." Y "..y.." Z "..z)
local objectloc = data.objectloc
local working, future = {}, {{x,y,z}}
local soundscape = data.soundscape
local finished = {}
local insert = table.insert
local tiles = data.tiles
local checkforedgeofmap = funcs.checkforedgeofmap
finished[x] = {}
finished[x][y] = {}
finished[x][y][z] = 1
soundscape[x][y][z][activator] = soundscape[x][y][z][activator] or {} --for moveenemytowards
insert(soundscape[x][y][z][activator],{["strength"] = intensity})
local counter = intensity
while(counter <= intensity and counter >= 1) do
counter = counter - 1
working,future = future, {}
local x,y,z
for k,v in pairs(working) do
for x = v[1]- 1,v[1] + 1 do
finished[x] = finished[x] or {}
for y = v[2] -1,v[2] + 1 do
finished[x][y] = finished[x][y] or {}
for z = v[3] - 1,v[3] + 1 do
if z < data.amountofzlevels and z >= 1 and (not checkforedgeofmap(x,y)) and (not finished[x][y][z]) and (not tiles[x][y][z]["filled"]) and (not tiles[x][y][z]["open"]) then
finished[x][y][z] = 1
insert(future,{x,y,z})
for k2,ob in pairs (objectloc[x][y][z]) do
if ob["soundactivate"] then
for k3,v3 in pairs(ob["soundactivate"]) do
if k3 < counter then v3(ob,activator) end
end
end
end
soundscape[x][y][z][activator] = soundscape[x][y][z][activator] or {} --for moveenemytowards
insert(soundscape[x][y][z][activator],{["strength"] = counter})
-------------------------------------------------------------------------------------------------------
This code needs some cleaning up, it has a few meaningless variables here and there and I wrote it strenuously and with many revisions. It works well, though, and the heuristic can be altered easily enough. When funcs.astarpathfind is called, it returns a table with a chart of what squares are what distance from the destination, so actually, I pathfind backwards, from the destination to the creature who's moving, who just has to move onto the next square with the least distance to get to his destination.
This is my "A-star pathfinding code":
function funcs.checkforblockpassageofpath(x,y,z)
local tiles = data.tiles
if not tiles[x] or not tiles[x][y] or not tiles[x][y][z] then return true end
if tiles[x][y][z]["filled"] or tiles[x][y][z]["open"] then return true end
return false end
function funcs.astarpathfind(startx,starty,startz,endx,endy,endz,ob)
if startx == endx and starty == endy and startz == endz then return false end
local working = {} --node list
-- local closed = {} --node list
local insert = table.insert
local min = math.min
local mapsizex = data.levelsizex
local mapsizey = data.levelsizey
local distances = {}
local future = {}
local getdistance = funcs.getdistance
local blocking = funcs.checkforblockpassageofpath
local impossible = (mapsizex * mapsizey) + 1
--seed the working table
distances[startx] = {}
distances[startx][starty] = {}
distances[startx][starty][startz] = 0
-- print("Startx "..startx)
-- print("Starty "..starty)
-- print("endx "..endx)
-- print("endy "..endy)
-- print("---------")
insert(future,{startx,starty,startz,0,funcs.getastarheuristic(ob,startx,starty,startz,endx,endy,endz)})
while(true) do
for k,v in pairs(future) do/
insert(working,v)
end
future = {}
--Now,get the one with the best heuristic.
local lowestfvalue = impossible --or lowestfvalue (dumb pathfind)
local savedspot
local savedk
-- print("Lowest F value "..lowestfvalue)
for k,v in pairs(working) do --4 is distance, 5 is heuristic
-- print ("WORKING "..working[k][4] + working[k][5])
-- print("LOWEST F "..lowestfvalue)
if working[k][4] + working[k][5] < lowestfvalue then
lowestfvalue = working[k][4] + working[k][5]
savedspot = v
savedk = k
-- print("New lowest f value "..lowestfvalue)
end
end
working[savedk] = nil
--iterate over that one
for x = savedspot[1] - 1,savedspot[1] + 1 do
for y = savedspot[2] -1,savedspot[2]+1 do
for z = savedspot[3] -1,savedspot[3] + 1 do
distances[x] = distances[x] or {}
distances[x][y] = distances[x][y] or {}
distances[x][y][z] = distances[x][y][z] or impossible
local newdistance = min(distances[x][y][z], savedspot[4] + getdistance(x,y,savedspot[1],savedspot[2]))
-- if not closed[savedspot] then
if not blocking(x,y,z) then
if newdistance < distances[x][y][z] then -- adds or readds a spot to the array
-- print("Updating X "..x.." Y "..y)
local myh = funcs.getastarheuristic(ob,x,y,z,endx,endy,endz)
insert(future, {x,y,z,newdistance,myh})
distances[x][y][z] = newdistance
end
end
if x == endx and y == endy and z == endz then
--print("Correct Return")
return distances
end
-- end --ends closedcheck
end--ends for y
end --ends for x
end --ends for z
--The following visualizes the pathfind data
-- print("Path X "..savedspot[1].." Y "..savedspot[2])
-- print("DIST "..savedspot[4])
-- print("HEUR "..savedspot[5])
-- slang.gotorc(savedspot[2]+data.mapoffsety,savedspot[1]+data.mapoffsetx)
-- slang.setcolor(math.random(1,15))
-- slang.writechar("*")
-- slang.refresh()
-- funcs.displaymessage(1,1,"Press a key")
-- funcs.getkey()
end --ends while true
print("shouldnt have gotten to here.")
os.exit()
end
function funcs.getastarheuristic(ob,x,y,z,x2,y2,z2) --was processer heavy possibly.
local distanceguess= 10 * funcs.manhattandistance(x,y,x2,y2)
return distanceguess
end
No comments:
Post a Comment