A_Pathfinder By Mince by PALMER19th Feb 2008 10:13
|
---|
Summary Raster map Path Finder / AI / NPC Finds a path from Pointa(startx,starty) to Pointb(destx,desty) Description I use this in my rts and it works. Code ` This code was downloaded from The Game Creators ` It is reproduced here with full permission ` http://www.thegamecreators.com gosub advanced_pathfinder_setup` Call this at start. `---------------------------------------------------------- `EXAMPLE unit (type) var `---------------------------------------------------------- `type u_nit_path_type `x as integer `y as integer `endtype `max_units = 1000 `max_indexsize = 7000`The tile search limit `dim unit_path(max_units,max_indexsize) as u_nit_path_type `--------------------------------------------------------- set display mode 1024,768 `-------------------------------- `More INFO/Example; `eg: Add Object to the map Array. `Object..(make sure the vars for thease objects are global so the function has access) `wall = 1(map_object_data) `tree = 2(map_object_data) `Array map(x,y).mtype = wall `---------------------------- do inc stack_turn,1 if stack_turn > stack_run `(stack_run = the amount of tiles to check per game loop cycle) A_pathfinder_work_stack()`works out paths for units on pathfinder stack. stack_turn = 0 endif `Your code `To add a path, to be searched for a unit > `Call A_pathfinder_add_to_stack(startx,starty,destinationx,destinationy,Unit_number) loop `should never get to here! end. advanced_pathfinder_setup: `Types type squaretype fscore as integer gscore as integer hscore as integer mtype as integer px as integer py as integer endtype type pointtype x as integer y as integer endtype type stack_info`Stack Store unit as integer finished as integer started as integer spx as integer spy as integer dpx as integer dpy as integer index as integer endtype `Vars global A_pathfinder_runtime = 5 `Time in ms. global A_pathfinder_max_steps = 150*6 `Tiles to check before moving on to next unit in stack. (N)tiles *(6) a must! global A_pathfinder_max_stacks = 1000 `Sets the limit for unit paths to work out. global A_pathfinder_max_index = 50000 `The maximum Size of search. global A_pathfinder_time_started as integer `timer() global index_pos as integer global A_pathfinder_waiting as integer A_pathfinder_waiting = 1 global false = 0 global true = 1 `Controls the intesity of pathfinding global stack_run as integer stack_run = 10`0 is intence and 1000 is never ish. `Stack intensity counter global stack_turn as integer stack_turn = 0 `Constants `Search logic global ground_grass = 1`global constants global hill = 8`global constants global crops = 2`global constants global water = 3`global constants global wall = 4`global constants global corncost = 2`global constants global groundcost = 1`global constants `sizewidth = 1024 `sizeheight = 1024 `Arrays global dim A_pathfinder_stack() as stack_info`A_pathfinder_max_stacks global dim map(1024,1024) as squaretype `Make Walls around map to stop searching out of bounds for x = 1 to 1024 for y = 1 to 1024 map(x,y).mtype = tilelogicarray(x,y)`need to add walls all around map to stop out of bound searches! if x = 1 then map(x,y).mtype = 4 if y = 1 then map(x,y).mtype = 4 if x = 1024 then map(x,y).mtype = 4 if y = 1024 then map(x,y).mtype = 4 next y next x return function A_pathfinder_add_to_stack(startx,starty,dpx,dpy,UNIT) `Check to see if this unit is already in stack, if so then remove that stack then add this new one. stacks = array count(A_pathfinder_stack(0)) for look_instack = 0 to stacks stacks_now = array count(A_pathfinder_stack(0)) if stacks_now => look_instack`der! if A_pathfinder_stack(look_instack).unit = UNIT `Remove this stack A_pathfinder_stack(look_instack).started = 0 A_pathfinder_remove_from_stack(look_instack) `if it was the working path remove arrays if look_instack = 0 undim A_pathfinder_openlist(0) undim A_pathfinder_closedlist(0) undim A_pathfinder_path(0) endif`look_instack endif`=unit else`stacks_now = -1 exit endif`stacks_now next look_instack if array count(A_pathfinder_stack(0)) < A_pathfinder_max_stacks` if a stack is availible `Add to stack array insert at bottom A_pathfinder_stack(0) position = array count(A_pathfinder_stack(0)) `at new blank stack A_pathfinder_stack(position).unit = UNIT A_pathfinder_stack(position).spx = startx A_pathfinder_stack(position).spy = starty A_pathfinder_stack(position).dpx = dpx A_pathfinder_stack(position).dpy = dpy A_pathfinder_stack(position).started = 0 A_pathfinder_stack(position).finished = 0 A_pathfinder_stack(position).index = 0 unit(UNIT).waiting_stack = 0 unit(UNIT).has_path = 0 unit(UNIT).need_a_Waypoint = 0 else `Flag unit to> waiting to be placed on stack. unit(UNIT).waiting_stack = 1 unit(UNIT).has_path = 0 unit(UNIT).need_a_Waypoint = 0 endif`space on stack? endfunction function A_pathfinder_remove_from_stack(stack_number) array delete element A_pathfinder_stack(0),stack_number endfunction function A_pathfinder_work_stack() stacks = array count(A_pathfinder_stack(0)) if stacks => 0 A_pathfinder_find_path(0) endif endfunction `Function finds path from spx,spy to dpx,dpy and adds this path to Unit_Path(unit) `The Path Engine ¬Mince 08 function A_pathfinder_find_path(stack_num) `Temp Vars tempg as integer x as integer y as integer fcx as integer fcy as integer path_posible as integer tile_score as integer steps as integer index_pos as integer `Get Time Stamp A_pathfinder_time_started = timer() A_pathfinder_stop_time = A_pathfinder_time_started + A_pathfinder_runtime `Pull stack info spx = A_pathfinder_stack(stack_num).spx spy = A_pathfinder_stack(stack_num).spy dpx = A_pathfinder_stack(stack_num).dpx dpy = A_pathfinder_stack(stack_num).dpy UNIT = A_pathfinder_stack(stack_num).unit`unit data `Check if this is a new search or if to continue a search. if A_pathfinder_stack(stack_num).started = 1 `Search through the open list to find lowest f score tile_score = 90000`reset high path_posible = 0 for i = 1 to array count(A_pathfinder_openlist(0)) if A_pathfinder_isonclosed((A_pathfinder_openlist(i).x),(A_pathfinder_openlist(i).y)) = 0 if map((A_pathfinder_openlist(i).x),(A_pathfinder_openlist(i).y)).fscore =< tile_score c.x = A_pathfinder_openlist(i).x c.y = A_pathfinder_openlist(i).y tile_score = map((A_pathfinder_openlist(i).x),(A_pathfinder_openlist(i).y)).fscore path_posible = 1 endif endif next i `-------------------------------------------------------------------------------- else`New search or Continue search.>NEW SEARCH index_pos = 0 `Set up Arrays each time function is called global dim A_pathfinder_openlist() as pointtype global dim A_pathfinder_closedlist() as pointtype global dim A_pathfinder_path() as pointtype global mstart as pointtype`Global? global mfinish as pointtype`Global? `global dim comp() as pointtype`????? fix global c as pointtype`Global? global dim onfinish() as integer`??? A_pathfinder_stack(stack_num).started = 1`Flag Now Started! endif`End of continue check steps = 0 mstart.x = spx mstart.y = spy mfinish.x = dpx mfinish.y = dpy `Search logic `ground = 1`global constants `hill = 8`global constants `crops = 2`global constants `water= 3`global constants `wall = 4`global constants `corncost = 2`global constants `groundcost = 1`global constants `sizewidth = 1024 `sizeheight = 1024 `Also if A_pathfinder_stack(stack_num).started = 1 c.x = mstart.x c.y = mstart.y endif A_pathfinder_addtoopen(c.x,c.y)`Start/Continue Search Location. onfinish = 0 while onfinish = 0`----------------------------------------------------------------------------¬ A_pathfinder_addtoclosed(c.x,c.y) for y = -1 to 1 for x = -1 to 1 fcx = c.x + x fcy = c.y + y if a_pathfinder_inbounds(fcx,fcy) = 1 if map(fcx,fcy).mtype <> 4`not = 4 wall if map(fcx,fcy).mtype <> water`not water if A_pathfinder_isonclosed(fcx,fcy) = false`not on closed list if A_pathfinder_isonopen(fcx,fcy) = false`not on open list `Caculate tile scores `G first------------------------------------¬ if map(fcx,fcy).mtype = ground_grass map(fcx,fcy).gscore = map(c.x,c.y).gscore + groundcost`echo else map(fcx,fcy).gscore = map(c.x,c.y).gscore + corncost`not water or wall so must be corn! endif `------------------------------------------- `Now H--------------------------------------¬ `Using whats known as Manhatten distance sys `hx = 10 * (abs( fcx - mfinish.x) ) `hy = 10 * (abs( fcy - mfinish.y) ) `Now using whats known as Mince distance hx = 20 * (abs( fcx - mfinish.x) ) hy = 20 * (abs( fcy - mfinish.y) ) map(fcx,fcy).hscore = (hx + hy) `Finally the F score = G + H map(fcx,fcy).fscore = map(fcx,fcy).gscore + map(fcx,fcy).hscore `Make the current square (c.x,c.y) the parent of this square (c.x+x,c.y+y) map(fcx,fcy).px = c.x map(fcx,fcy).py = c.y `Add this square to the open list A_pathfinder_addtoopen(fcx,fcy) `If its the finish square we have completed the search! when added to close!! if fcx = mfinish.x and fcy = mfinish.y then onfinish = 1 else`on open list `check g scores if map(fcx,fcy).mtype = ground_grass tempg = map(fcx,fcy).gscore + groundcost else`on closed list `if water `if hill tempg = map(c.x,c.y).gscore + corncost`corn endif`map = ground if tempg < map(fcx,fcy).gscore `make the current square the parent of this square map(fcx,fcy).px = c.x map(fcx,fcy).py = c.y `re calculate scores map(fcx,fcy).gscore = tempg map(fcx,fcy).fscore = map(fcx,fcy).gscore + map(fcx,fcy).hscore endif`tempg<map.gscore switch endif`open list endif`closed=false endif`<>water endif`<>wall endif`inbounds() = 1 next x next y `Search through the open list to find lowest f score tile_score = 99999`reset high path_posible = 0 for i = 1 to array count(A_pathfinder_openlist(0)) if A_pathfinder_isonclosed((A_pathfinder_openlist(i).x),(A_pathfinder_openlist(i).y)) = 0 if map((A_pathfinder_openlist(i).x),(A_pathfinder_openlist(i).y)).fscore =< tile_score c.x = A_pathfinder_openlist(i).x c.y = A_pathfinder_openlist(i).y tile_score = map((A_pathfinder_openlist(i).x),(A_pathfinder_openlist(i).y)).fscore path_posible = 1 endif endif next i `IF No Path open then quit current search. if path_posible = 0 then exit `Check A_pathfinder runnung time. `if timer() > A_pathfinder_stop_time then exit inc steps,1 inc index_pos,1 if steps > A_pathfinder_max_steps then exit if index_pos => A_pathfinder_max_index path_posible = 0 `OH DEAR! endif endwhile `Exit or endwhile¬ `-Path Found Store Working Path¬----------------------------------------------------------------------------------------- if onfinish = 1`Via Endwhile `Work backwards to get path! c.x = mfinish.x c.y = mfinish.y i = 0 onstart = 0 `Store path either to A_pathfinder_path().x/y or to your array like mine >unit_path(unit).x/y while onstart = 0 ` unit_path(UNIT,i).x = c.x ` unit_path(UNIT,i).y = c.y `OR A_pathfinder_addtopath(x,y) can store one path at a time. `Check to see if at start. if c.x = mstart.x and c.y = mstart.y then exit`onstart = 1 x = c.x y = c.y `Make the current square the parent square. c.x = map(x,y).px c.y = map(x,y).py inc i,1 endwhile `This path is created from parented tiles,working from destination to start. `Rather than reversing it just set your unit to start at the end. unit(unit).index = i `Each step taking 1 from unit(unit).index until unit(unit).index = 0. `Also you know how many steps are left unit(unit).index = steps left. `Here you would place the search result into a array like below. `unit_path(UNIT,0).x = mfinish.x `unit_path(UNIT,0).y = mfinish.y`Update Unit Path. `you then can flag your unit to has path = true, in your function/sub controlling your `units only control units with paths. `unit(UNIT).has_path = 1 A_pathfinder_stack(stack_num).finished = 1 A_pathfinder_stack(stack_num).started = 0 ` unit(UNIT).waiting_stack = 0 ` waypoint(UNIT).path_step_counter = i`start at end and dec till 0! `remove from stack A_pathfinder_remove_from_stack(stack_num) undim A_pathfinder_openlist(0) undim A_pathfinder_closedlist(0) undim A_pathfinder_path(0) `Happy Dayz!! else`No Path posible or Ran out of time!¬ if path_posible = 0 `No Path posible A_pathfinder_stack(stack_num).finished = 1 A_pathfinder_stack(stack_num).started = 0 `remove from stack A_pathfinder_remove_from_stack(stack_num) undim A_pathfinder_openlist(0) undim A_pathfinder_closedlist(0) undim A_pathfinder_path(0) `set unit to stop `unit(UNIT).action = stopped else `Ran out of time.. endif`path_posible = 1? endif`onfinish? endfunction function a_pathfinder_inbounds(x,y) ret_bounds = 0 if x > 0 and x <1024 if y > 0 and y <1024 ret_bounds = 1 endif endif endfunction ret_bounds function A_pathfinder_addtoopen(x,y) array insert at bottom A_pathfinder_openlist(0) A_pathfinder_openlist(array count(A_pathfinder_openlist(0))).x = x A_pathfinder_openlist(array count(A_pathfinder_openlist(0))).y = y endfunction function A_pathfinder_addtoclosed(x,y) array insert at bottom A_pathfinder_closedlist(0) A_pathfinder_closedlist(array count(A_pathfinder_closedlist(0))).x = x A_pathfinder_closedlist(array count(A_pathfinder_closedlist(0))).y = y endfunction function A_pathfinder_addtopath(x,y) array insert at bottom A_pathfinder_path(0) A_pathfinder_path(array count(A_pathfinder_path(0))).x = x A_pathfinder_path(array count(A_pathfinder_path(0))).y = y endfunction function A_pathfinder_isonclosed(x,y) onclosed = 0 for i = 1 to array count(A_pathfinder_closedlist(0)) if A_pathfinder_closedlist(i).x = x and A_pathfinder_closedlist(i).y = y onclosed = 1 exit endif next i endfunction onclosed function A_pathfinder_isonopen(x,y) onopen = 0 for i = 1 to array count(A_pathfinder_openlist(0)) if A_pathfinder_openlist(i).x = x and A_pathfinder_openlist(i).y = y onopen = 1 exit endif next i endfunction onopen |