3d a* pathfinding
Blitz3D Forums/Blitz3D Programming/3d a* pathfinding
| ||
| how do you tackle pathfinding in 3d? should you use types or arrays? |
| ||
| if it is really 3D you wouldn't use a* but given pathing graphs. if it is a 3D representation of a 2D map then a 2D array. types become extremely slow if you need many of them |
| ||
| skyfire1 You can use both. You can use banks as well. It really does depend on how your implementing it. I used a types to manage priority queues for open/closed waypoint nodes. I've seen others use a Binary Heap Array. You may be interested in the Bot System I propose here. |
| ||
| dang it! pathfinding is hard! is there any a* pathfinding library that's easy to implement? |
| ||
| Nope but a waypoint based pathfinding library :-) I think its even in the toolbox. |
| ||
| 3D games usually use 2D pathfinding because at the end of the day, tanks dont fly, if you see what i'm getting at. The point is doing a game in 3D need not complicate your path finding, you can treat it just like 2D. You're better off writting the pathfinding yourself so that you understand it enough to make it work well for your game, but there's no harm in doing so by learning from existing code examples/libraries. Path finding is one of the harder things to implement in a game which is why I try to solve it relatively early in the programs development cycle. Better to grin and bear it early than to put it off and never face it. I usually make my path finding functions in a seperate program using the games actual data but shown onscreen as a 2D map - it helps me tweak the algorythm. This is a modified A* routine which is quite good for searching routes out of a large heightmap based terrain. You can make extra "blocked" areas for trees & houses simply by painting the colour red(255). You'll need to change the heightmap file loaded before this sample will run - I last used this for my Paradise Island project so it's trying to load the heightmap from that...
Graphics 512,512,0,2
AppTitle("A*")
Dim mapData(512,512)
Global treeMap=LoadImage("C:\BeckysGames\Paradise Island\Gfx\Terrain\map2.png")
SetBuffer ImageBuffer(treeMap)
For x=0 To 511
For z=0 To 511
GetColor(x,z)
mapData(x,511-z)=ColorRed()
If mapData(x,511-z)=255 Then mapData(x,511-z)=999
Next
Next
SetBuffer BackBuffer()
Type openList
Field x,z
Field parentX,parentZ
Field fSum
Field gCost
Field hGuess
Field mapHeight
End Type
Type closedList
Field x,z
Field parentX,parentZ
End Type
Type AIpath
Field x,z
End Type
sx=256
sz=256
tx=256
tz=256
SeedRnd MilliSecs()
Repeat
strt=MilliSecs()
unit=Rnd(1,5)
route(sx,sz,tx,tz,unit)
time=MilliSecs()-strt
Cls
DrawImage treeMap,0,0
Color 255,255,0
Oval sx,511-sz,3,3,0
Color 0,255,0
Oval tx,511-tz,5,5,0
Color 0,0,255
For n=1 To 5
id=0
For p.AIpath=Each AIpath
id=id+1
If id>1
o.AIpath=Before p
Line p\x,512-p\z, o\x,512-o\z
EndIf
Next
Next
Color 255,255,0
Text 10,10,time+"ms for "+id+" steps"
Flip 0
Repeat
If MouseDown(1)
sX=MouseX()
sZ=512-MouseY()
EndIf
If MouseDown(2)
tX=MouseX()
tZ=512-MouseY()
EndIf
Until KeyDown(17)
;Stop
For clearPath.AIpath=Each AIpath
Delete clearPath
Next
Forever
Function route(sx,sz,tx,tz,id)
node.openList=New openList
node\x=sx : node\z=sz
node\fSum=0 : node\gCost=0
node\hGuess=0
node\mapHeight=mapData(node\x,node\z)
maxNodeRange=(Abs(sx-tx)+Abs(sz-tz))*1.1
searchTime=MilliSecs()+333
Repeat
lowestScore=99999999
nodes=0
For checkNode.openList=Each openList
nodes=nodes+1
If checkNode\fSum<lowestScore
lowestScore=checkNode\fSum
node.openList=checkNode.openList
EndIf
Next
;DebugLog(node\x+"x"+node\z+" = "+node\gCost+" ("+tX+"x"+tZ+") ["+nodes+"]")
;we now have the node to search
If lowestScore=99999999; Or MilliSecs()>searchTime
buildPath=2
Else
If Abs(node\x-tx)<=3 And Abs(node\z-tz)<=3
buildPath=1
Else
For bearing=0 To 359 Step 40
rX#=node\x : rZ#=node\z
range=0
ht=mapData(node\x,node\z)
While range<=5
rX=rX+Cos(bearing)
rZ=rZ+Sin(bearing)
If rX=>0 And rX<=511 And rZ=>0 And rZ<=511
If Int(rX)=tX And Int(rZ)=tZ
range=10
Else
If mapData(rX,rZ)-ht<=4
ht=mapdata(rX,rZ)
range=range+1
Else
range=10
EndIf
EndIf
Else
range=10
EndIf
Wend
sX=rX : sZ=rZ
;*** CORE SYSTEM
If sX=>0 And sX<=511 And sZ=>0 And sZ<=511
If sX<>node\X Or sZ<>node\Z
onClosedList=0
For checkCNode.closedList=Each closedList
If Abs(checkCNode\x-sX)<=1
If Abs(checkCNode\z-sZ)<=1
onClosedList=1
checkCNode.closedList=Last closedList
EndIf
EndIf
Next
If Not onClosedList
onOpenList=0
For checkONode.openList=Each openList
If Abs(checkONode\x-sX)<=1
If Abs(checkONode\z-sZ)<=1
onOpenList=1
If node\gCost+1<checkONode\gCost
If checkONode\mapheight-node\mapHeight<=4
If checkOnode\mapHeight<8 Then cost=10 Else cost=1
checkONode\parentX=node\X
checkONode\parentZ=node\Z
checkONode\gCost=node\gCost+cost
checkONode\fSum=checkONode\gCost+checkONode\hGuess
EndIf
EndIf
checkONode.openList=Last openList
EndIf
EndIf
Next
If Not onOpenList
If mapData(sX,sZ)-node\mapHeight<=4
If node\gCost<maxNodeRange
If mapData(sX,sZ)<8 Then cost=10 Else cost=1
newNode.openList=New openList
newNode\x=sX : newNode\z=sZ
newNode\parentX=node\x : newNode\parentZ=node\z
newNode\gCost=node\gCost+cost
newNode\hGuess=moveHeuristic(sX,sZ,tX,tZ)
newNode\fSum=newNode\gCost+newNode\hGuess
newNode\mapHeight=mapData(sX,sZ)
EndIf
EndIf
EndIf
EndIf
EndIf
EndIf
;*** END CORE SYSTEM
Next
finished.closedList=New closedList
finished\x=node\x : finished\z=node\z
finished\parentX=node\parentX : finished\parentZ=node\parentZ
Delete node.openList
EndIf
EndIf
up=up+1
ms=MilliSecs()
If up=>lastTime-ms
lastTime=ms
up=0
Color 255,255,0
DrawImage treeMap,0,0
For showO.openList=Each openList
Plot showO\x,512-showO\z
Next
Color 0,255,255
For showC.closedList=Each closedList
Plot showC\x,512-showC\z
Next
Color 0,255,0
Oval tx,512-tz,3,3,0
Flip 0
Color 0,0,255
Oval ox,512-oz,5,5,0
EndIf
Until buildPath>0
If buildPath=2
For checkOnode.openList=Each openList
finished.closedList=New closedList
finished\x=checkOnode\x
finished\z=checkOnode\z
finished\parentX=checkOnode\parentX
finished\parentZ=checkOnode\parentZ
Delete checkOnode
Next
nearest=99999999
newTx=tx : newTz=tz
For CheckCnode.closedList=Each closedList
dx#=checkCnode\x-tx
dz#=checkCnode\z-tz
rng=Sqr((dx*dx)+(dz*dz))
If rng<nearest
newTx=checkCnode\x
newTz=checkCnode\z
thisNode.closedList=checkCnode.closedList
nearest=rng
EndIf
Next
path.AIpath=New AIpath
path\x=thisNode\x
path\z=thisNode\z
x=thisNode\parentX
z=thisNode\parentZ
EndIf
If buildPath=1
path.AIpath=New AIpath
path\x=node\x : path\z=node\z
x=node\parentX : z=node\parentZ
Delete node
EndIf
Repeat
For pathnode.closedList=Each closedList
If x=pathnode\x And z=pathnode\z
path.AIPath=New AIPath
path\x=x
path\z=z
x=pathnode\parentX
z=pathnode\parentZ
Delete pathNode
EndIf
Next
Until x=0 And z=0
For clearOList.openList=Each openList
Delete clearOList
Next
For clearCList.closedList=Each closedList
Delete clearCList
Next
End Function
;Function moveHeuristic(sX,sZ,tX,tZ)
; difX=Abs(sX-tX)
; difZ=Abs(sZ-tZ)
; If difX>difZ
; Return difZ+(difX*3)
; Else
; Return difX+(difZ*3)
; EndIf
;End Function
;Function moveHeuristic(sX,sZ,tX,tZ)
; dx#=sX-tX
; dz#=sZ-tZ
; Return Sqr((dx*dx)+(dz*dz))
;End Function
Function moveHeuristic(sX,sZ,tX,tZ)
Return (Abs(sX-tX)+Abs(sZ-tZ))*2
End Function
|
| ||
| Becky, Very nice looking A* code you got there. |
| ||
| And use heaps... you can really optimize a* with it. |
| ||
Here's my a* function ... might give you ideas how to construct yours. It's a node graph based and it uses heap to really speed it up.Function P_ASTAR(node1%,GOAL%,ply%=0) Local CLOSED%[P_MAXNODES] Local PARENT%[P_MAXNODES] Local G#[P_MAXNODES] ;GENERATED COST Local H#[P_MAXNODES] ;ESTIMATED COST (to target) goal_found=0 If ply=0 Then P_GOAL_NODE(ply) = 0 If NODE1 <> GOAL CLEAR_HEAP() HEAP_INSERT(node1,0) ;P_ELAPSEDTIME=MilliSecs() Repeat ;GET THE MOST LOWEST COST NODE FROM THE HEAP current = HEAP_REMOVELOWEST() ;PUT IT TO THE CLOSED LIST CLOSED[current] = 1 ;CHECK ALL THE NODES CONNECTED TO CURRENT NODE For i=1 To P_NODE_CONNECTIONS(current) nn = P_NODE_CON(current,i) ;IF NOT ON THE CLOSED LIST PROCEED If CLOSED[nn]=0 ;SEARCH THE OPEN LIST -> PUT THE HEAP POS IN found ELSE found = 0 found=0 For ii=1 To HEAPSIZE If HEAPITEM(ii) = nn Then found=ii:Exit Next ;NOT ON OPENLIST ---> CALCULATE COST AND PUT IT IN! If found=0 PARENT[nn] = current H[nn] = P_MANHATTAN_DISTANCE(nn,GOAL) ;ESTIMATE THE DISTANCE TO GOAL G[nn] = P_NODE_ARCLENGTH(current,i) + G[current] ;G = distance to this node + G of the parent node F# = G[nn] + H[nn] ;CALCULATE FINAL COST ;PUT TO OPENLIST HEAP_INSERT(nn,F) End If ;IS ON THE OPEN LIST --- > CHECK IF THIS IS BETTER PATH TO IT If found>0 NG# = P_NODE_ARCLENGTH(current,i) + G[current] ;IF THIS PATH*S G SCORE = LOWER THAN OLD G --> UPDATE If NG<G[nn] PARENT[nn] = current G[nn] = NG ;REPLACE THE OLD G WITH THE CURRENT G F# = G[nn] + H[nn] ;CALCULATE FINAL COST UPDATE_HEAPITEM(nn,F,found) ;UPDATE THIS NODE End If End If End If Next If current=GOAL Then goal_found=1 Until HEAP_EMPTY()=1 Or goal_found=1 End If P_PATHNODES(ply)=0 P_PATHCOST(ply)=0 If P_GOAL_NODE(ply)>0 P_PATHNODES(ply) = P_PATHNODES(ply) + 1 P_PATHNODE(ply,P_PATHNODES(ply)) = P_GOAL_NODE(ply) End If ;TRACE THE PATH! If goal_found=1 current=GOAL P_PATHCOST(ply) = G[current] Repeat P_PATHNODES(ply) = P_PATHNODES(ply)+1 P_PATHNODE(ply,P_PATHNODES(ply)) = current current = PARENT[current] Until current=node1 P_PATHNODES(ply) = P_PATHNODES(ply)+1 P_PATHNODE(ply,P_PATHNODES(ply)) = current End If ;P_ELAPSEDTIME=MilliSecs()-P_ELAPSEDTIME Return P_PATHNODES(ply) End Function |
| ||
| Bouncer, Very nice compact code. Thanks for sharing. |
| ||
And while I'm at it... Here's some ready to use heap function.s
;HEAP FUNCTIONS
Global HEAPSIZE=0
Global HMAXSIZE=P_MAXNODES
Dim HEAPITEM%(HMAXSIZE)
Dim HEAPKEY#(HMAXSIZE)
Function HEAP_INSERT(n,key#)
HEAPSIZE=HEAPSIZE+1
HEAPITEM(HEAPSIZE) = n
m = HEAPSIZE
HEAPKEY(HEAPITEM(m))=key
While m <> 1 ;While item hasn't bubbled to the top (m=1)
;Check if child is <= parent. If so, swap them.
If HEAPKEY(HEAPITEM(m)) <= HEAPKEY(HEAPITEM(m/2)) Then
temp = HEAPITEM(m/2)
HEAPITEM(m/2) = HEAPITEM(m)
HEAPITEM(m) = temp
m = m/2
Else
Exit ;exit the while/wend loop
End If
Wend
End Function
Function HEAP_REMOVELOWEST()
;If HEAP_EMPTY()=0
L = HEAPITEM(1)
;GET THE LAST ITEM AND PUT IT IN THE FIRST POSITION
;(FIRST GET*S DELETED)
HEAPITEM(1) = HEAPITEM(HEAPSIZE)
HEAPSIZE = HEAPSIZE - 1
v = 1
;Repeat the following until the item sinks to its proper spot in the binary heap.
Repeat
u = v
If 2*u+1 <= HEAPSIZE;if both children exist
;Select the lowest of the two children.
If HEAPKEY(HEAPITEM(u)) >= HEAPKEY(HEAPITEM(2*u)) Then v = 2*u
If HEAPKEY(HEAPITEM(v)) >= HEAPKEY(HEAPITEM(2*u+1)) Then v = 2*u+1
Else If 2*u <= HEAPSIZE ;if only child #1 exists
;Check if the F cost is greater than the child
If HEAPKEY(HEAPITEM(u)) >= HEAPKEY(HEAPITEM(2*u))Then v = 2*u
End If
If u <> v Then ; If parent's F > one or both of its children, swap them
temp = HEAPITEM(u)
HEAPITEM(u) = HEAPITEM(v)
HEAPITEM(v) = temp
Else
Exit ;if item <= both children, exit repeat/forever loop
End If
Forever
Return L
;End If
End Function
Function UPDATE_HEAPITEM(item,newkey#,HEAPPOS=0)
If HEAPPOS=0
For i=1 To HEAPSIZE
If HEAPITEM(i) = item Then m=i:Exit
Next
Else
m=HEAPPOS
End If
HEAPKEY(HEAPITEM(m)) = newkey
While m <> 1 ;While item hasn't bubbled to the top (m=1)
;Check if child is <= parent. If so, swap them.
If HEAPKEY(HEAPITEM(m)) <= HEAPKEY(HEAPITEM(m/2)) Then
temp = HEAPITEM(m/2)
HEAPITEM(m/2) = HEAPITEM(m)
HEAPITEM(m) = temp
m = m/2
Else
Exit ;exit the while/wend loop
End If
Wend
End Function
Function HEAP_EMPTY()
If HEAPSIZE>0 Return 0 Else Return 1
End Function
Function CLEAR_HEAP()
Dim HEAPITEM%(HMAXSIZE)
Dim HEAPKEY#(HMAXSIZE)
HEAPSIZE=0
End Function
|