A* Pathfinding utilizing banks
Blitz3D Forums/Blitz3D Tutorials/A* Pathfinding utilizing banks
| ||
Advanced A* Pathfinding utilizing banks --------------------------------------- Programm: B3D and BP State: intermediate and advanced users This Tutorial does not explain A* itself! For an excelent A* Pathfinding Tutorial check http://www.policyalmanac.org/games/aStarTutorial.htm You should fully understand A* before continuing this tutorial. This tutorial explains how to avoid arrays and types while pathfinding. Why not Arrays? - arrays are not dynamic and consume lots of memory - arrays cannot be created on runtime - arrays are always global Why not types? - since types cannot be nested into eachother its hard to parallel process pathfindings. This would take some managment and its hard to deal with. - types are always global so my choice is to entirely use banks for pathfinding Why banks? - there fast! - they are small in memory - they can be nested (will cover this later) - they are not global - they can even be stored easily or send through networks Sorry, but i havent any webspace so i cant include the media. First you should draw a small image (about 32x32) the background should be black and then paint some walls with pure white(255,255,255). Save it as Bitmap or PNG. We will call the file "map.png" in the following code Now we will initialise the graphics Graphics 1024,768,32,2 SetBuffer(BackBuffer()) We need some globals ;map width and height Global as_dim_x Global as_dim_y ;this is the variable to store the image ... see below Global as_map_bank ;stores how many pathfindings are in progress Global as_count ;the treshhold when tiles are unwalkable Global as_unwalk = 120 ;some constants i will explain later Const DEF_AS_MANHATTEN = 10 Const DEF_AS_OPEN = 1 Const DEF_AS_CLOSED = 2 Const DEF_OFF_F = 0 Const DEF_OFF_G = 4 Const DEF_OFF_H = 8 Const DEF_OFF_X = 12 Const DEF_OFF_Y = 16 Const DEF_OFF_PARENT = 20 Because accessing images with ReadPixel is much slower than reading in memorybanks we will convert our "map.png" to a bank with this function Function img2bank(img) ;store the image dimension as global map width and height as_dim_x = ImageWidth(map) as_dim_y = ImageHeight(map) mx = as_dim_x my = as_dim_y ;now we create a bank that has as much bytes as pixels in the image bank = CreateBank(mx * my) buffer = ImageBuffer(img) LockBuffer(buffer) ;step through each pixel For y=0 To my-1 For x=0 To mx-1 ;we use the blue channel to determ the brightness b = ReadPixelFast(x,y,buffer) And $FF PokeByte(bank,(x + mx * y),b) Next Next UnlockBuffer(buffer) Return bank End Function This one is quit simple. We create a bank thats imagewidth x imageheight and store the bluevalue row by row, column by column in the bank Okay now lets convert our image map = LoadImage("map.png") as_map_bank = img2bank(map) At this point you should have read the A* Tutorial as mentioned above (or know how A* works), or you wont get it !!! I've said no types ... i was lying ;-) I will use types to store the running pathfinding processes So here we go! Type as_path Field info_bank Field node_bank Field sort_bank Field temp_bank Field result_bank Field start_x,start_y Field end_x,end_y Field data_size Field status Field current_node Field time End Type So whats that all about?: info_bank - this bank will be link between the map and the "A* node's". Each entry in there contains the current status of the node(open, closed, untouched) and the "handle" to the nodes data.(i will explain that later) node_bank - this one stores all the nodes data sort_bank - this bank contains the sorted "open" list. Each entry consists of the F-value and the node "handle" temp_bank - this bank is for swaping memory blocks. you will see later result_bank - this one will containe the resulting path in reverse order. Why reverse? Because A* traces back the best way and you dont know how many steps you need. It can only be "stored" in reverse order from targetpoint to startpoint. start_x,start_y end_x,end_y - that not too hard to guess data_size - this one stores how much data is stored per node. this could be different for some reasons (i will cover this later) status - stores if the process of pathfinding is still running or cant be solved etc. current_node - the current node. Thats importent to continue the search after a break time - stores how much time the pathfind can consume Okay,now we've got all we need to get started. now we need a function to create and initialise our "as_path"-object Function as_createPath.as_path() a.as_path = New as_path a\info_bank = CreateBank(as_dim_x * as_dim_y * 5) a\node_Bank = CreateBank() a\sort_Bank = CreateBank() a\result_Bank = CreateBank() a\temp_bank = CreateBank(1024*4) as_count = as_count + 1 Return a End Function the function is declerated as returntype ".as_path". !!! To all that dont know about types as returns you should check the forum about that !!! so what do we do here: 1. we create the info_bank -------------------------- each entry in this bank consits of 5 byte 1 Byte - status of the tile (open, closed, untouched) 4 Byte Int - this will containe the handle of the node in the node_bank. Its not truely a handle its the offset to the nodes data. so we got (mapwidth * mapheight * 5) 2.create the node_bank ---------------------- this is left blank cause we dont have any nodes now 3.create the sort_bank ---------------------- this is left blank cause we dont have any nodes to sort now 4.create the result_bank ---------------------- yep, no results till now 5.create the temp_bank ---------------------- The size of the tempbank results from the size of the map and should be guessed at startup. I will cover this later in detail 6. increment the as_count ------------------------- its always good to know how many threads we got Now we need a function to start the process Function as_pathfind(a.as_path,start_x,start_y,end_x,end_y) size = BankSize(a\info_bank)-1 For i=0 To size Step 5 PokeByte(a\info_bank,i,0) Next ResizeBank(a\node_bank,0) ResizeBank(a\sort_bank,0) a\start_x = start_x a\start_y = start_y a\end_x = end_x a\end_y = end_y a\data_size = 4 * 6 a\status = 1 End Function The whole thing is setup that "entitys" dont have to delete there as_path object. They use always there personal pathfinding object. But this depents on the game and can be changed easily. 1.cleaning up ------------- step through all the enrys in the info-bank and set the status-byte to zero(untouched). We dont clear the handles cause they will be overwritten. Reseting this would cosume time and time is short! 2. reset node_bank and sort_bank -------------------------------- no comment 3. store start and endpoint --------------------------- no comment 4. setting datasize ------------------- now its time to explain the nodes data: each node consits of 4 byte int - the F value 4 byte int - the G value 4 byte int - the H value 4 byte int - the Xposition 4 byte int - the Yposition 4 byte int - handle to the PARENT node check the constants from the start .. that are the offsets in the nodes data 5. set status ------------- set to 1 ... initialised and ready to go Switching status ---------------- Now we we should switch each status in the pathfinding process Function as_calc(a.as_path,time = 99999999) a\time = time map_bank = as_map_bank mx = as_dim_x my = as_dim_y Select a\status Case 1 ;initialise If a\start_x => mx Or a\start_x < 0 Or a\start_y => my Or a\start_y < 0 Then ;the startpoint is off the map a\status = 4 Return False EndIf If a\end_x => mx Or a\end_x < 0 Or a\end_y => my Or a\end_y < 0 Then ;the endpoint is off the map a\status = 4 Return False EndIf ;start oder endpunkt ist nicht begehbar If PeekByte(map_bank,a\start_x + mx * a\start_y) => as_unwalk Then ;the startpoint is not walkable a\status = 4 Return False EndIf If PeekByte(map_bank,a\end_x + mx * a\end_y) => as_unwalk Then ;the endpoint is not walkable a\status = 4 Return False EndIf ;open the startnode a\current_node = as_openNode(a,f,g,h,a\start_x,a\start_y,parent) a\status = 2 ;start the search Return as_path(a.as_path) Case 2 ;calculating ;continue the search Return as_path(a.as_path) Case 3 ;finished Return 3 Case 4 ;cannot calculate Return 4 End Select End Function so each time we run through the list of as_path objects we will call "as_calc" on status 1 we first check if the search can be made. If the start or end tile is off the map or not walkable the search cannot be successfull so the status is set to 4(cannot calculate) if the search can be successfull the status is set to 2(calculating) and the startpoint will be stored in the "openlist" if status is 2 the search is still in progress and will be continued now status 3 means the path is allready calcualated and the result can be found in the result_bank status 4 means there is no wy from start to endpoint Storing nodes in the "openlist" ------------------------------- now stuff gets serious and we should take a detailed look on the node_bank the nodebank is a list of all known nodes it looks like this: Offset Datablock +---+---+---+---+---+------+ 0 | F | G | H | X | Y |PARENT| +---+---+---+---+---+------+ 24 | F | G | H | X | Y |PARENT| +---+---+---+---+---+------+ 48 | F | G | H | X | Y |PARENT| +---+---+---+---+---+------+ . . . +---+---+---+---+---+------+ n*24 | F | G | H | X | Y |PARENT| +---+---+---+---+---+------+ the handle to a node is the offset in the bank to store an additional node we increase the size of the bank by the datamount per node (24 bytes) +---+---+---+---+---+------+ 0 | F | G | H | X | Y |PARENT| +---+---+---+---+---+------+ 24 | F | G | H | X | Y |PARENT| +---+---+---+---+---+------+ 48 | F | G | H | X | Y |PARENT| +---+---+---+---+---+------+ 72 | | | | | | | +---+---+---+---+---+------+ then we store the nodes data into the last block +---+---+---+---+---+------+ 0 | F | G | H | X | Y |PARENT| +---+---+---+---+---+------+ 24 | F | G | H | X | Y |PARENT| +---+---+---+---+---+------+ 48 | F | G | H | X | Y |PARENT| +---+---+---+---+---+------+ 72 | | | | | | | <<< Poke new values +---+---+---+---+---+------+ since we need to access the node with the lowest f score we will always sort the newly added nodes into rthe sortbank now lets take a closer look into the sortbank: Offset Datablock +---+--------+ 0 | F | HANDLE | +---+--------+ 5 | F | HANDLE | +---+--------+ 10 | F | HANDLE | +---+--------+ . . . +---+--------+ n*5 | F | HANDLE | +---+--------+ and we want to be F sorted descending so that the last entry is the lowest F now we have to ad one new +---+--------+ new | 25| HANDLE | +---+--------+ +---+--------+ 0 | 30| HANDLE | +---+--------+ 5 | 20| HANDLE | +---+--------+ 10 | 10| HANDLE | +---+--------+ first we run through from bottom to top until the new f-value is smaler or equal the f value size = BankSize(a\sort_bank) offset = size While offset > 0 other_f = PeekInt(a\sort_bank,offset - 8) If f <= other_f Exit EndIf offset = offset - 8 Wend We got offset 0 cause the new F=25 is smaler then the F=30 Now we copy everything belowe this entry to the tempbank count = size - offset nOffset = offset + 8 CopyBank(a\sort_bank,offset,a\temp_bank,0,count) +---+--------+ 0 | 30| HANDLE | +---+--------+ +------------+ 5 | 20| HANDLE | >>> | TEMP | +---+--------+ +------------+ 10 | 10| HANDLE | >>> | TEMP | +---+--------+ +------------+ now increase the size of the bank size = BankSize(a\sort_bank) + 8 +---+--------+ 0 | 30| HANDLE | +---+--------+ 5 | 20| HANDLE | +---+--------+ 10 | 10| HANDLE | +---+--------+ 15 | | | +---+--------+ now we have to write back the value ResizeBank(a\sort_bank,size) CopyBank(a\temp_bank,0,a\sort_bank,nOffset,count) +---+--------+ 0 | 30| HANDLE | +---+--------+ 5 | 20| HANDLE | +---+--------+ +------------+ 10 | 20| HANDLE | <<< | TEMP | +---+--------+ +------------+ 15 | 10| HANDLE | <<< | TEMP | +---+--------+ +------------+ now there is space for our new entry PokeInt(a\sort_bank,offset,f) PokeInt(a\sort_bank,offset+4,hoffset) +---+--------+ 0 | 30| HANDLE | +---+--------+ 5 | 25| HANDLE | <<< Poke new values +---+--------+ 10 | 20| HANDLE | +---+--------+ 15 | 10| HANDLE | +---+--------+ now we wil take a closer look into the info_bank this is a representation of the known map: S = status of the Tile (opend, closed or untouched) H = handle -> Offset to the nodes data in the node_bank Offset DATA(5 byte per tile) +-------+-------+-------+ +-------+ 0*width*5 | S & H | S & H | S & H | | S & H | +-------+-------+-------+ +-------+ 1*width*5 | S & H | S & H | S & H | ... | S & H | +-------+-------+-------+ +-------+ 2*width*5 | S & H | S & H | S & H | | S & H | +-------+-------+-------+ +-------+ . . . +-------+-------+-------+ +-------+ n*width*5 | S & H | S & H | S & H | ... | S & H | +-------+-------+-------+ +-------+ here we will now mark the node as opend offset = (x + (y * as_dim_x)) * 5 PokeByte(a\info_bank,offset,DEF_AS_OPEN) PokeInt(a\info_bank,offset+1,hOffset) Function as_openNode(a.as_path,f,g,h,x,y,parent) ;increase the bank to one more entry hOffset = BankSize(a\node_bank) ResizeBank(a\node_bank,hOffset + a\data_size) size = BankSize(a\sort_bank) offset = size While offset > 0 other_f = PeekInt(a\sort_bank,offset - 8) If f <= other_f Exit EndIf offset = offset - 8 Wend count = size - offset nOffset = offset + 8 CopyBank(a\sort_bank,offset,a\temp_bank,0,count) size = BankSize(a\sort_bank) + 8 ResizeBank(a\sort_bank,size) CopyBank(a\temp_bank,0,a\sort_bank,nOffset,count) ;write the f and the handle in the sort_bank PokeInt(a\sort_bank,offset,f) PokeInt(a\sort_bank,offset+4,hoffset) ;write the data into the new node space PokeInt(a\node_bank,hOffset + DEF_OFF_F,f) PokeInt(a\node_bank,hOffset + DEF_OFF_G,g) PokeInt(a\node_bank,hOffset + DEF_OFF_H,h) PokeInt(a\node_bank,hOffset + DEF_OFF_X,x) PokeInt(a\node_bank,hOffset + DEF_OFF_Y,y) PokeInt(a\node_bank,hOffset + DEF_OFF_PARENT,parent) ;mark the tile as opend offset = (x + (y * as_dim_x)) * 5 PokeByte(a\info_bank,offset,DEF_AS_OPEN) PokeInt(a\info_bank,offset+1,hOffset) ;return the handle Return hoffset End Function that was tough ... :-) A* main ------- now here comes the big block of A* Function as_path(a.as_path) map_bank = as_map_bank mx = as_dim_x my = as_dim_y begin = MilliSecs() ;as long we are in time ... While (MilliSecs() - begin) < a\time ;close the node ;the below for this function as_closeNode(a,a\current_node) current_x = PeekInt(a\node_bank,a\current_node + DEF_OFF_X) current_y = PeekInt(a\node_bank,a\current_node + DEF_OFF_Y) current_g = PeekInt(a\node_bank,a\current_node + DEF_OFF_G) ;check to see if the endpoint has been reached If current_x = a\end_x And current_y = a\end_y Then ;the endpoint has been reach ;create and fill the resultbank ; i will explain this at the end of this tutorial .. so leave this now a\result_bank = CreateBank(8) size = 8 current_node = a\current_node x = PeekInt(a\node_bank,current_node + DEF_OFF_X) y = PeekInt(a\node_bank,current_node + DEF_OFF_Y) PokeInt(a\result_bank,0,x) PokeInt(a\result_bank,4,y) scale = 55 While current_node current_node = PeekInt(a\node_bank,current_node + DEF_OFF_PARENT) x = PeekInt(a\node_bank,current_node + DEF_OFF_X) y = PeekInt(a\node_bank,current_node + DEF_OFF_Y) size = size + 8 ResizeBank(a\result_bank,size) PokeInt(a\result_bank,size - 8,x) PokeInt(a\result_bank,size - 4,y) Wend a\status = 3 Return True EndIf ;open all surrounding nodes For y = current_y - 1 To current_y + 1 For x = current_x - 1 To current_x + 1 ;if its not the center ... If x <> current_x Or y <> current_y ;.. and not off the map ... If x < mx And x >= 0 And y < my And y >= 0 Then offset = (x + mx * y) offset2 = offset * 5 new_status = PeekByte(a\info_bank,offset2) b = PeekByte(map_bank,offset) ;... and not allready closed or unwalkable If new_status <> DEF_AS_CLOSED And b < as_unwalk g = 14 If x = current_x Or y = current_y Then g = 10 g = current_g + g; + (b / 10) Select new_status Case DEF_AS_OPEN ;this one is allready open check if this one should be edited new_node = PeekInt(a\info_bank,offset2+1) new_g = PeekInt(a\node_bank,new_node + DEF_OFF_G) new_h = PeekInt(a\node_bank,new_node + DEF_OFF_H) If g < new_g ;this node must be edited ... as_updateNode will sort this into the sort_bank ; will explain this later f = g + new_h ;update the values in the nodebank PokeInt(a\node_bank,new_node + DEF_OFF_PARENT,a\current_node) PokeInt(a\node_bank,new_node + DEF_OFF_G,g) PokeInt(a\node_bank,new_node + DEF_OFF_F,f) ;resort the sortlist as_updateNode(a.as_path,new_node,f) EndIf Default ;this tile is still untouched ... add this to the openlist h = (Abs(a\end_x - x) + Abs(a\end_y - y)) * DEF_AS_MANHATTEN f = g + h parent = a\current_node as_openNode(a.as_path,f,g,h,x,y,parent) End Select EndIf EndIf EndIf Next Next ;this one is for debbuging and uses the draw function(see below) draw(a) WaitKey ; get the next node with the lowest F score size = BankSize(a\sort_bank) If size = 0 Then ;oops .. no more nodes .. there is no path to the endpoint ;set status to 4 (cannot calculate) a\status = 4 Return False Else ;get next node for next loop a\current_node = PeekInt(a\sort_bank,BankSize(a\sort_bank)-4) EndIf Wend Return True End Function i will not get into this to deep the nodes data can be accessed by there handle + the offset of the data like: node_g = PeekInt(a\node_bank,node_handle + DEF_OFF_G) Closing nodes ------------- okay ... now lets check what happens if a node gets closed Function as_closeNode(a.as_path,node) size = BankSize(a\sort_bank) ResizeBank(a\sort_bank,size - 8) x = PeekInt(a\node_bank,node + DEF_OFF_X) y = PeekInt(a\node_bank,node + DEF_OFF_Y) offset = (x + y * as_dim_x) * 5 PokeByte(a\info_bank,offset,DEF_AS_CLOSED) Return End Function thats easy ... first we reduce the sortbank by one set of data cause the node to be closed is allways the last one in the list then we mark the tile in the infobank as closed now lets see how we can resort the list of handles if we edited one of the nodes Updating nodes and sort them ---------------------------- This one gave me some headache and i dont know if this is the optimal approach to do this! first we will search the node to be updated offset = size While offset > 0 offset = offset - 8 other_node = PeekInt(a\sort_bank,offset + 4) If node = other_node Exit EndIf Wend then we will erase the node +---+--------+ 0 | 30| HANDLE | +---+--------+ 5 | 20| HANDLE | <<< should be erased +---+--------+ 10 | 10| HANDLE | +---+--------+ 15 | 5 | HANDLE | +---+--------+ copy everything below +---+--------+ 0 | 30| HANDLE | +---+--------+ 5 | 20| HANDLE | +---+--------+ +------------+ 10 | 10| HANDLE | >>> | | +---+--------+ +---TEMP-----+ 15 | 5 | HANDLE | >>> | | +---+--------+ +------------+ write the data back on the position of the node to be erased +---+--------+ 0 | 30| HANDLE | +---+--------+ +------------+ 5 | 10| HANDLE | <<< | | +---+--------+ +---TEMP-----+ 10 | 5 | HANDLE | <<< | | +---+--------+ +------------+ 15 | 5 | HANDLE | +---+--------+ then do the same as descriped when adding a node to the openlist (see above) but dont increase the size of the bank ... we allready got one free entry Function as_updateNode(a.as_path,node,f) size = BankSize(a\sort_bank) ;find the node to update offset = size While offset > 0 offset = offset - 8 other_node = PeekInt(a\sort_bank,offset + 4) If node = other_node Exit EndIf Wend ;erase the node count = size - offset CopyBank(a\sort_bank,offset,a\temp_bank,0,count) CopyBank(a\temp_bank,8,a\sort_bank,offset,count-8) offset = size - 8 While offset > 0 other_f = PeekInt(a\sort_bank,offset - 8) If f <= other_f Exit EndIf offset = offset - 8 Wend count = size - offset - 8 CopyBank(a\sort_bank,offset,a\temp_bank,0,count) nOffset = offset + 8 CopyBank(a\temp_bank,0,a\sort_bank,nOffset,count) ;write the f and the handle in the sort_bank PokeInt(a\sort_bank,offset,f) PokeInt(a\sort_bank,offset+4,node) End Function Tracing back the path --------------------- at this point we have found the endpoint in our openlist now lets resize our resultbank back to 8 then step from parent to parent and add the X and Y value to the resultbank until there is no more parent node this will lead from endpoint to the startpoint and the resultbank contains the X,Y Positions leading to the target in reverse order. Offset Datablock +---+---+ 0 | X | Y | +---+---+ 8 | X | Y | +---+---+ 16 | X | Y | +---+---+ . . . +---+---+ n*8 | X | Y | +---+---+ ;create and fill the resultbank resizeBank(a\result_bank,8) size = 8 current_node = a\current_node x = PeekInt(a\node_bank,current_node + DEF_OFF_X) y = PeekInt(a\node_bank,current_node + DEF_OFF_Y) PokeInt(a\result_bank,0,x) PokeInt(a\result_bank,4,y) scale = 55 While current_node current_node = PeekInt(a\node_bank,current_node + DEF_OFF_PARENT) x = PeekInt(a\node_bank,current_node + DEF_OFF_X) y = PeekInt(a\node_bank,current_node + DEF_OFF_Y) size = size + 8 ResizeBank(a\result_bank,size) PokeInt(a\result_bank,size - 8,x) PokeInt(a\result_bank,size - 4,y) Wend a\status = 3 Return True Debugging and drawing --------------------- this is a small function to draw the result this is for very small maps only (13 x 13) [code] Function draw(a.as_path) Cls() scale = 55 If a\result_bank Then Color(100,0,20) size = BankSize(a\result_bank) For i=size To 0 Step -8 xs = PeekInt(a\result_bank,i - 8) * scale ys = PeekInt(a\result_bank,i - 4) * scale Rect(xs,ys,scale,scale,1) Next End If For y=0 To as_dim_y-1 For x=0 To as_dim_x-1 xs = x * scale ys = y * scale offset = (x + y * as_dim_x) b = PeekByte(as_map_bank,offset) Color(b,b,b) Rect(xs,ys,scale,scale,0) offset = (x + y * as_dim_x) * 5 status = PeekByte(a\info_bank,offset) node = PeekInt(a\info_bank,offset + 1) Select status Case DEF_AS_OPEN Color(20,250,20) Rect(xs,ys,scale,scale,0) Color(200,250,200) f = PeekInt(a\node_bank,node + DEF_OFF_F) g = PeekInt(a\node_bank,node + DEF_OFF_G) h = PeekInt(a\node_bank,node + DEF_OFF_H) parent = PeekInt(a\node_bank,node + DEF_OFF_PARENT) px = PeekInt(a\node_bank,parent + DEF_OFF_X) py = PeekInt(a\node_bank,parent + DEF_OFF_Y) Text xs + 1,ys + 1,f Text xs , ys + scale - 1 - (FontHeight()),g Text xs + scale - StringWidth(h) - 1 , ys + scale - 1 - FontHeight(),h Line(xs+scale/2,ys+scale/2,px*scale+scale/2,py*scale+scale/2) Case DEF_AS_CLOSED Color(20,20,250) Rect(x*scale,y*scale,scale,scale,0) Color(200,200,250) f = PeekInt(a\node_bank,node + DEF_OFF_F) g = PeekInt(a\node_bank,node + DEF_OFF_G) h = PeekInt(a\node_bank,node + DEF_OFF_H) parent = PeekInt(a\node_bank,node + DEF_OFF_PARENT) px = PeekInt(a\node_bank,parent + DEF_OFF_X) py = PeekInt(a\node_bank,parent + DEF_OFF_Y) Text xs + 1,ys + 1,f Text xs , ys + scale - 1 - (FontHeight()),g Text xs + scale - StringWidth(h) - 1 , ys + scale - 1 - FontHeight(),h Line(xs+scale/2,ys+scale/2,px*scale+scale/2,py*scale+scale/2) End Select Next Next x = PeekInt(a\node_bank,a\current_node + DEF_OFF_X) y = PeekInt(a\node_bank,a\current_node + DEF_OFF_y) Color(250,20,20) Rect(x*scale,y*scale,scale,scale,0) Color(250,20,20) Rect(a\end_x*scale,a\end_y*scale,scale,scale,1) Color(20,250,20) Rect(a\start_x*scale,a\start_y*scale,scale,scale,1) size = BankSize(a\sort_bank)-1 While i < size count = count + 1 Text 800,count * 17, i + " -> f:" + PeekInt(a\sort_bank,i) +" - node:"+ PeekInt(a\sort_bank,i+4) i = i + 8 Wend Flip() End Function [\code] so, thats it for now I hope you find this usefull. My english isnt that good so if anyone could correct this tutorial to be more understandable please mail the corrected thread to: rayzor-s-edge@... i will then update this post. thx in advance. If you got some improvements or questions ... please drop a line. |
| ||
Very detailed tutorial! And the english is just fine. Thanks! |
| ||
Why not Arrays? - accessing arrays in Blitzbasic is slow *WRONG* - arrays are not dynamic and consume lots of memory *WRONG* - arrays are always global *WRONG* Why not types? - since types cannot be nested into eachother *WRONG* - types are always global *SO WHAT* |
| ||
@GW - accessing arrays in Blitzbasic is slow *WRONG* i've benchmarked arrays and banks ... accessing arrays was about 1/2 that fast [edit]my mistake ... they are not slow[/edit] - arrays are not dynamic and consume lots of memory *WRONG* arrays can be redimed - YES .. but you cannot redim without losing all values in the array, this means you have to make the array as big as the highest possible nodecount therefor you consum lots of extra memory without need! - arrays are always global *WRONG* arrays are always global ... except Blitzarrays and those are always constant - since types cannot be nested into eachother *WRONG* Types cannot be nested for t.type = each type would allways get ALL existing entries of "type" for t.type = each parent\type cannot be done - types are always global *SO WHAT* calculating pathes paralel would result in a mess in the typelist. you allways have to sort the types by there "task" |
| ||
- accessing arrays in Blitzbasic is slow *WRONG* i've benchmarked arrays and banks ... accessing arrays was about 1/2 that fast erm...okay...thats very doubtful, but if you say so. - since types cannot be nested into eachother *WRONG* Types cannot be nested for t.type = each type would allways get ALL existing entries of "type" for t.type = each parent\type cannot be done erm...I hope you realise that there are other ways to perform loops accessing types... "For t.type = each type" is but ONE way Blitz allows you to loop through types...course the other ways require a little bit more work from the programmer, but then again C/C++ users are used to that sort of thing and they have no problems with haveing to implament thier own parent/child data nesting schemes. Anyway, very good tutorial overall. |
| ||
okay, its true that it can be done ... i've wrote: "since types cannot be nested into eachother its hard to parallel process pathfindings. This would take some managment and its hard to deal with." i wanted to say that doing this with types would have some managment overhead that you do not have when using banks. I correct the statements in the tutorial soon to avoid confusion about that. |
| ||
Banks in this case are a =lot= more useful than arrays, if for no other reason than individual a* banks can be attached to individual pathfinding entities via their handle in an appropriate type field. In theory you could do the same with BlitzArrays but then you'd need to futz around with constant and inflexible dim values, so in the end I think Klaas has the most flexible and best overall method here. |