Making heatMaps more Dynamic

Game development specific discussions.
leopardpm
Posts: 1795
Joined: Feb 28, 2009 20:58

Making heatMaps more Dynamic

Post by leopardpm »

I am trying to make Heatmaps more dynamic. Usually they are created once, at the beginning, and accessed throughout the gameplay for a variety of reasons (pathfinding mostly). They have several advantages of being EXTREMELY fast (trivial) for pathfinding, allowing 1,000's of agents to pathfind at once, and being relatively easy to generate/setup and use. The downside is that this initial creation is not trivial and so they are not used generally for dynamic objects. They also have a memory requirement as each HeatMap (array?) would have to have the same dimensions as the general map.
So, for example, if a Heatmap was generated for all 'Trees' in a grid map 1,000 x 1,000 large, the Heatmap would also be that size AND would be static. So, if a Tree was cut down, an entirely new Heatmap would need to be generated... this could take some time depending on the size of the map and how often it needed to be re-generated.

My thinking, which is perhaps the same as the 'Brushfire Algorithm' suggested by Paul Doe (but I can't find any reference to it thru google...), is instead of re-generating the entire heatmap (since most of it will not change...), only re-generate the part directly related to the object that has changed. For adding a new object to the map, this is relatively easy (just do a djistra's fill starting with only that object instead of all similar objects) , BUT, when removing/deleting an object, it is possible but I am still figuring out the logic of the routine.

Reverse Dijkstra: Basically, you would remove all the nodes of the Heatmap which are closest to the deleted object (they should increase in value as the distance from the object grows) until the nodes start decreasing in value (indicating they are now 'pointing' to a different, closer object). Every node encountered that starts decreasing in value is then added to the frontier and once all nodes related to the deleted node are cleared, regenerate the heatmap using the frontier with all the active nodes. Pretty simple, but....

Here is my program so far, just your down n dirty brute force generation of heatmaps.... no addition or deletion routines yet. Notice the darker lines between the objects (especially noticeable on the green 'Tree' heatmap), this is representative of the frontier that will have to be re-generated when deleting. I do also notice that the heatmaps tend to form polygonal, apparently convex, shapes which makes me think this feature can possibly be used to further speed up some of the processes - but unknown.

Code: Select all

const TILEW = 5
const TILEH = 5

const SCRW = 1280 '100 * TILEW
const SCRH = 600 '100 * TILEH

const Red as ulong = rgb(255,0,0)
const Green as ulong = rgb(0,255,0)
const Blue as ulong = rgb(0,0,255)
const Yellow as ulong = rgb(255,255,0)
const Black as ulong = rgb(0,0,0)
const White as ulong = rgb(255,255,255)
const Gray as ulong = rgb(200,200,200)

const Trees as integer = 1
const IronOre as integer = 2
const BerryBushes as integer = 3
const Mushrooms as integer = 4

dim shared as integer map(100,100,5)
''''''' Map variable explanation
' 0 = object at location (9 = Blocker)
' 5 = quantity of object
'
'  the following are for cheap, fast pathfinding routine - heat/influence/flow map
'
' 1 = distance to nearest tree object (#1)
' 2 = distance to nearest iron ore object (#2)
' 3 = distance to nearest Berry Bush Object (#3)
' 4 = distance to nearest Mushroom Object (#4)


dim shared as integer DirInfo(7,2) ' for movement & pathfinding
' directions 0-7
' 7 0 1
' 6 x 2
' 5 4 3
'
' info 0-2: 0 = xAdj, 1 = yAdj, 2 = movecost
data 0,-1,10
data 1,-1,14
data 1,0,10
data 1,1,14
data 0,1,10
data -1,1,14
data -1,0,10
data -1,-1,14

for i as integer = 0 to 7
    for j as integer = 0 to 2
        read DirInfo(i,j)
    next j
next i

dim shared as integer frontier(9000,2)
' frontier for pathfinding (#, cost=0 x=1 y=2 )????????
dim shared as integer frontpointer

' some subroutines....
declare function FrontierAdd(ByVal frontX as integer, ByVal frontY as integer, ByVal frontCost as integer, byval ot as integer) as integer

function FrontierAdd(ByVal frontX as integer, ByVal frontY as integer, ByVal frontCost as integer, byval ot as integer) as integer
    ' this function uses and alters the shared variables: frontier(9000,2) & frontpointer

    '  ... add it to the end then bubble sort it down...
    dim as integer bub, frontHere
    frontpointer = frontpointer + 1
    frontHere = frontpointer
    frontier(frontpointer,0) = frontCost
    frontier(frontpointer,1) = frontX
    frontier(frontpointer,2) = frontY
    if frontpointer > 1 then
        bub = frontpointer
        do
            if frontier(bub,0) > frontier(bub-1,0) then
                swap frontier(bub,0) , frontier(bub-1,0)
                swap frontier(bub,1) , frontier(bub-1,1)
                swap frontier(bub,2) , frontier(bub-1,2)
                frontHere = bub - 1
            else
                bub = 2 ' early exit
            end if
            bub = bub - 1
        loop until bub < 2
    end if
    return frontHere
end function

declare function FrontierDel(ByVal thisOne as integer) as integer

function FrontierDel(ByVal thisOne as integer) as integer
    select case thisOne
        case is < frontpointer
            for i as integer = thisOne to (frontpointer-1)
                frontier(i,0) = frontier(i+1,0)
                frontier(i,1) = frontier(i+1,1)
                frontier(i,2) = frontier(i+1,2)
            next i
            frontpointer = frontpointer - 1
        case is = frontpointer
            frontpointer = frontpointer - 1
    end select
    return thisOne
end function

declare sub PutBlocker(ByVal cnt as integer) 

sub PutBlocker(ByVal cnt as integer)
    dim as integer x, y
    for i as integer = 1 to cnt
        x = rnd * 92 + 4 : y = rnd * 92 + 4
        while map(x,y,0) > 0
            x = rnd * 92 + 4 : y = rnd * 92 + 4
        wend
        map(x,y,5) = 1 : map(x,y,0) = 9 ' put 1 blocker there

        ' plot it on map - not needed....
        line(x*TILEW,y*TILEH)- step(TILEW-2,TILEH-2),rgb(0,0,0),BF
    next i
    ' force an actual walls around middle...
    for x = 40 to 60
        map(x,40,5) = 1 : map(x,40,0) = 9 ' put 1 blocker there
        map(x,60,5) = 1 : map(x,60,0) = 9 ' put 1 blocker there
        line(x*TILEW,40*TILEH)- step(TILEW-2,TILEH-2),rgb(0,0,0),BF
        line(x*TILEW,60*TILEH)- step(TILEW-2,TILEH-2),rgb(0,0,0),BF
    next x
end sub

declare sub PutMapPF(ByVal ot as integer, ByVal cnt as integer) 

sub PutMapPF(ByVal ot as integer, ByVal cnt as integer)
    dim as integer x, y
    for i as integer = 1 to cnt
        x = rnd * 92 + 4 : y = rnd * 92 + 4
        while map(x,y,0) > 0
            x = rnd * 92 + 4 : y = rnd * 92 + 4
        wend
        map(x,y,5) = 1 : map(x,y,0) = ot ' put 1 object(ot) there
        map(X,Y,ot) = 0 ' zero out the distance for that object
'        'add it to the 'frontier' for pathfinding
        FrontierAdd(x,y,0, ot)
        ' plot it on map
        line(x*TILEW,y*TILEH)- step(TILEW-2,TILEH-2),rgb(255,255,255),BF
    next i
end sub


declare sub MakeHeatMap(ByVal ot as integer)

sub MakeHeatMap(ByVal ot as integer)
    ' a basic Djistra's Floodfill routine, not optimized at all...
    dim as integer x,y,i,j,cost,costDir,costNew, oldPoint
    dim as integer x1, y1, c1, clrADJ, clrNew
    do
        oldPoint = frontpointer
        x = frontier(frontpointer,1)
        y = frontier(frontpointer,2)
        cost = frontier(frontpointer,0)
        ' remove point from frontier
        FrontierDel(oldPoint) 'remove current point from frontier
        ' error check
 '       if cost <> map(x,y,ot) then 'ERROR
'            beep
'            sleep
'            end
'        end if
        ' check all 8 directions, if cost to move there is less then their current cost then
        ' change their cost and add to frontier
        for direct as integer = 0 to 7
            i = x+DirInfo(direct,0) 'x Adj
            j = y+DirInfo(direct,1) 'y Adj
            if ((i > 0) and (i < 101)) and ((j > 0) and (j < 101)) then
                if (map(i,j,0)<>9) then
                    costDir = DirInfo(direct,2) ' movecost
                    costNew = cost + costDir
                    if map(i,j,ot) > costNew then
                        if map(i,j,ot)= 999 then
                            FrontierAdd(i,j,costNew,ot)
                        end if
                        map(i,j,ot) = costNew
                        'plot it
                        x1 = i*TILEW
                        y1 = j*TILEH
                        c1 = rgb(200,200,200) ' degfault grey background
                        clrNew = 255 - (costNew/1)
                        if clrNew < 0 then clrNew = 0
                        select case ot
                            case 1 ' 1 = distance to nearest tree object (#1)
                                c1 = rgb(0,clrNew,0)
                            case 2 ' 2 = distance to nearest iron ore object (#2)
                                c1 = rgb(clrNew,0,0)
                            case 3 ' 3 = distance to nearest Berry Bush Object (#3)
                                c1 = rgb(0,0,clrNew)
                            case 4 ' 4 = distance to nearest Mushroom Object (#4)
                                c1 = rgb(clrNew,clrNew,0)
                        end select
                        line(x1,y1)- step(TILEW-2,TILEH-2),c1,BF
                    end if
                end if
            end if
        next direct
    loop until frontpointer = 0
end sub


' MAIN

screenres SCRW,SCRH,32
    
    cls
    dim as integer x1, y1, c1
    
    ' draw grid
    for i as integer = 1 to 100
        x1 = i*TILEW
        for j as integer = 1 to 100
            y1 = j*TILEH
            c1 = rgb(200,200,200)
            select case map(i,j,0)
                case 1
                    c1 = rgb(0,255,0)
                case 2
                    c1 = rgb(255,0,0)
                case 3
                    c1 = rgb(0,0,255)
                case 4
                    c1 = rgb(255,255,0)
            end select
            line(x1,y1)- step(TILEW-2,TILEH-2),c1,BF
        next j
    next i
    
    randomize
    ' fill map with random objects, and init pathfinding heatmaps for each object
    
    ' initialize map distances for pathfinding....
    for i as integer = 0 to 100
        for j as integer = 0 to 100
            map(i,j,0) = 0
            map(i,j,1) = 999
            map(i,j,2) = 999
            map(i,j,3) = 999
            map(i,j,4) = 999
        next j
    next i
    
    ' Put in some blocking tiles
    PutBlocker(100) ' Blocker Tiles
        
    PutMapPF(1,40) ' Trees
    MakeHeatMap(1)
    locate 3,70 : Print "Heatmap for all 40 Trees"
    locate 5,70 : Print "Hit <anykey> to continue"
    sleep

    PutMapPF(2,5) ' Iron Ore
    MakeHeatMap(2)
    locate 3,70 : Print "Heatmap for all 5 Iron Ores"
    locate 5,70 : Print "Hit <anykey> to continue"
    sleep
    
    PutMapPF(3,10) ' Berry Bushes
    MakeHeatMap(3)
    locate 3,70 : Print "Heatmap for all 10 Berry Bushes"
    locate 5,70 : Print "Hit <anykey> to continue"
    sleep
    
    PutMapPF(4,5) ' Mushrooms
    MakeHeatMap(4)
    locate 3,70 : Print "Heatmap for all 5 Mushrooms"
    locate 5,70 : Print "Hit <anykey> to continue"
    sleep

    'draw map
    for i as integer = 1 to 100
        x1 = i*TILEW
        for j as integer = 1 to 100
            y1 = j*TILEH
            c1 = rgb(50,50,50)
            select case map(i,j,0)
                case 1
                    c1 = rgb(0,255,0)
                case 2
                    c1 = rgb(255,0,0)
                case 3
                    c1 = rgb(0,0,255)
                case 4
                    c1 = rgb(255,255,0)
                case 9
                    c1 = rgb(0,0,0)
            end select
            line(x1,y1)- step(TILEW-2,TILEH-2),c1,BF
        next j
    next i
    sleep
BTW: I use the terms Heat map, Influence Map, Potential Field Maps, etc as interchangeable because they seem to be all generated by the same method - if I am misunderstanding these terms, please let me know! Basically, do a Dijkstra Floodfill of the map, starting with all the 'destination objects' in the frontier, then fill the map with either the distance value to the closest object or a vector pointing to the closest object. Then, to use the map:
To get distance to nearest object = directly access the map
To figure out a path to the object, check all surrounding distances and follow the smallest one - rinse and repeat for the next step until have reached the object.

EDIT: updated program to have Blocking tiles as well, and a few forced 'walls' and fixed Dodicats Error
Last edited by leopardpm on Dec 11, 2018 1:13, edited 4 times in total.
leopardpm
Posts: 1795
Joined: Feb 28, 2009 20:58

Re: Making heatMaps more Dynamic

Post by leopardpm »

an alternate, more memory intensive, but easier way to re-generate heatmap for a specific object is to first store the closest object reference within the heatmap itself - so each node (or grid location) in the map would have both the distance and the grid coordinates of the closest object - this adds the memory cost of the coordinates, perhaps 2 singles?

Then, when that object is deleted, only need to clear all nodes with that object as their pointer..... a time savings I think, and less complicated than a reverse djistra routine of some sort...
leopardpm
Posts: 1795
Joined: Feb 28, 2009 20:58

Re: Making heatMaps more Dynamic

Post by leopardpm »

Here is an example using Goal-Based Vector Field Pathfinding, which is basically the same thing as I am talking about... BUT, notice that the Potential Field is being ENTIRELY regenerated in real-time.. very slow. Also, the example has only ONE goal, not multiple.

https://gamedevelopment.tutsplus.com/tu ... medev-9007
Tourist Trap
Posts: 2958
Joined: Jun 02, 2015 16:24

Re: Making heatMaps more Dynamic

Post by Tourist Trap »

Very interesting topic. Just to be sure, with your coloured maps, the darker tiles are those that are close to obstacles, while the lighter ones are those free of obstacles, true?
Edit: ok it's the contrary :)

So in my opinion, what is needed is to override (invalidate) a squared zone of n x n tiles centered on the old position of the obstacle that has just moved. Then override, a same squared zone centered on the new position of the obstacle. Then redo the map on those two squares... First the oldest, then the newest with the newest overwritting the oldest zone if overlapping.

I don't know if you can compute your maps in a defined sub-zone easily from your code, I never played with this stuff. Until now I must admit it scared me a lot!
Last edited by Tourist Trap on Dec 11, 2018 1:01, edited 2 times in total.
leopardpm
Posts: 1795
Joined: Feb 28, 2009 20:58

Re: Making heatMaps more Dynamic

Post by leopardpm »

Tourist Trap wrote:Very interesting topic. Just to be sure, with your coloured maps, the darker tiles are those that are close to obstacles, while the lighter ones are those free of obstacles, true?
the darker means the farther away from the object type... so, on the green map, the brightest green is right next to a 'tree' and as you move away from this tree it gets darker...until becoming closer to a different tree at which point the cell will start getting brighter....

NOTE: these are not necessarily 'obstacles'! The only tiles/nodes/grid locations that will have values in a Heatmap are those that are 'walkable' so obstacles are basically ignored, but also taken into account.... if that makes sense! lol

ALSO NOTE: this example is NOT using any sort of Terrain based movement costs, but it is easy to make it do so. So every up/down/left/right step is 1 unit and a diagonal step is 1.41 units in movement costs...
Last edited by leopardpm on Dec 11, 2018 1:03, edited 1 time in total.
leopardpm
Posts: 1795
Joined: Feb 28, 2009 20:58

Re: Making heatMaps more Dynamic

Post by leopardpm »

also, in re-reading the above example Flow Field link, that author is calling the Dijkstra Algo = Brushfire Algo.... strange.... I like the term Brushfire for my idea because it spreads out like a brushfire until it hits a 'fire line' (the frontier of the next object...)... Drats, using a good name for something that already has a name!
Tourist Trap
Posts: 2958
Joined: Jun 02, 2015 16:24

Re: Making heatMaps more Dynamic

Post by Tourist Trap »

leopardpm wrote:also, in re-reading the above example Flow Field link, that author is calling the Dijkstra Algo = Brushfire Algo.... strange.... I like the term Brushfire for my idea because it spreads out like a brushfire until it hits a 'fire line' (the frontier of the next object...)... Drats, using a good name for something that already has a name!
What happens if you compute the map locally over a subzone, like a square of say 20 x 20 pixels, rather than the entire map in a whole? Does it show some disconnectivity with the whole gradient? Or does it insert well and smooth?
leopardpm
Posts: 1795
Joined: Feb 28, 2009 20:58

Re: Making heatMaps more Dynamic

Post by leopardpm »

if you only do a section of the map, then the heatmaps on either side will be incorrect in most cases

but your idea is along the lines of what I suggested above....

... and looking at the polygon shapes generated by the green map, I am really thinking that its possible to use these polygons to store the heatmap and possibly re-generate it... it would definitely take up less memory, would be speedy but slower than normal to access it (possibly, depends on how complicated it is to determine which polygon has a certain location in it), and might be faster to re-generate the dynamic part (unsure...)
Last edited by leopardpm on Dec 11, 2018 1:10, edited 1 time in total.
dodicat
Posts: 7976
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: Making heatMaps more Dynamic

Post by dodicat »

I get
Aborting due to runtime error 6 (out of bounds array access) at line 172 of ... your last code.

reminds me of bullets:
viewtopic.php?f=7&t=23685&p=208771&hili ... 2A#p208771
Tourist Trap
Posts: 2958
Joined: Jun 02, 2015 16:24

Re: Making heatMaps more Dynamic

Post by Tourist Trap »

leopardpm wrote:if you only do a section of the map, then the heatmaps on either side will be incorrect in most cases
Is it easy to adapt your code in order to give it a try? If you confirm , I'll give it a try. Otherwise I may ask you what that or that function does in order to make this first step that seems useful to learn your code, even if unfortunately it doesn't solve the issue.
leopardpm
Posts: 1795
Joined: Feb 28, 2009 20:58

Re: Making heatMaps more Dynamic

Post by leopardpm »

dodicat wrote:I get
Aborting due to runtime error 6 (out of bounds array access) at line 172 of ... your last code.
fixed... had to separate the last test in the IF THEN statement....
leopardpm
Posts: 1795
Joined: Feb 28, 2009 20:58

Re: Making heatMaps more Dynamic

Post by leopardpm »

Tourist Trap wrote:
leopardpm wrote:if you only do a section of the map, then the heatmaps on either side will be incorrect in most cases
Is it easy to adapt your code in order to give it a try? If you confirm , I'll give it a try. Otherwise I may ask you what that or that function does in order to make this first step that seems useful to learn your code, even if unfortunately it doesn't solve the issue.
there really is no need because its an easy thought exercise... imagine if you had 1 tree in the topleft quadrant of the map and generated a heatmap - the gradient would spread across the entire map, right?.... now generate the heatmap for any other quadrant which does not have that 1 tree.... the 'heat' from that topleft tree would be gone and just be all black....

I will see how hard this would be to show... BRB...nope, dont want to do it... would have to make a new, separate heat map generation routine for the quadrant, fill the frontier with only things from that quadrant... me too lazy to show what I know wont work.... sorry Mr. Trap... but, if you want to give it a go I will explain every function and tidbit if the current program to help
Last edited by leopardpm on Dec 11, 2018 1:27, edited 2 times in total.
Tourist Trap
Posts: 2958
Joined: Jun 02, 2015 16:24

Re: Making heatMaps more Dynamic

Post by Tourist Trap »

leopardpm wrote:there really is no need because its an easy thought exercise... imagine if you had 1 tree in the topleft quadrant of the map and generated a heatmap - the gradient would spread across the entire map, right?.... now generate the heatmap for any other quadrant which does not have that 1 tree.... the 'heat' from that topleft tree would be gone and just be all black....
Yes but in another hand, as you pointed out or suggested, there are maybe some nodes to take into account that would be sufficient to ensure a correct patch connectivity. I mean if you have a tree, and you suppress it, if you rebuild the map including all the closest surrounding trees, this may give something better. Not perfect but not necessarily too bad. Worth testing.
leopardpm
Posts: 1795
Joined: Feb 28, 2009 20:58

Re: Making heatMaps more Dynamic

Post by leopardpm »

Tourist Trap wrote:
leopardpm wrote:there really is no need because its an easy thought exercise... imagine if you had 1 tree in the topleft quadrant of the map and generated a heatmap - the gradient would spread across the entire map, right?.... now generate the heatmap for any other quadrant which does not have that 1 tree.... the 'heat' from that topleft tree would be gone and just be all black....
Yes but in another hand, as you pointed out or suggested, there are maybe some nodes to take into account that would be sufficient to ensure a correct patch connectivity. I mean if you have a tree, and you suppress it, if you rebuild the map including all the closest surrounding trees, this may give something better. Not perfect but not necessarily too bad. Worth testing.
hmmm...thinking... well, you would need ALL the nodes whose heatmaps touch the heatmap of the node you are deleting.... which is pretty much what I was suggesting in first post

If you look at the Tree (green) heatmap, and notice the darker 'lines' between the trees that seem to form the polygonal shapes I was referring to, these lines surrounding each node define the EXACT area that would need to be recomputed and all the rest of the heatmap will be unchanged. These areas are not square regions at all and are entirely based on the locations of all the current trees .... so if you could easily determine all the trees that have heatmaps that 'touch' the recomputed area, then did the djikstra thing on them, it would work... but determining those particular trees is not easy, as far as I can see because they are not necessarily only the 'closest' - there is only ONE 'closest' tree... how do you determine the cutoff for which ones are 'close' enough where they influence the heatmap? Only by looking at the actual heatmap, I fear.

I really do appreciate your thoughts on this and I am not trying to be obstinate, I just don't see that method working without some sort of 'tricks' that I am not aware of....
Last edited by leopardpm on Dec 11, 2018 1:41, edited 1 time in total.
Tourist Trap
Posts: 2958
Joined: Jun 02, 2015 16:24

Re: Making heatMaps more Dynamic

Post by Tourist Trap »

leopardpm wrote: hmmm...thinking... well, you would need ALL the nodes whose heatmaps touch the heatmap of the node you are deleting.... which is pretty much what I was suggesting in first post
Yes, but also there may be some distance beyond which it becomes useless to get one or many of the surrounding trees. Because if the area is empty, or if the trees are far enough, it's the same to some extent. The need for some testing is all about the need of gathering some observations in order to fix the ideas.
What are the variable that defines the explored pixels, does it consist on the whole array defined first containing the obstacles? Or is it defined in the function you name Heatmap? As far as I can judge for now, it's all defined at start when you provide the array?

Code: Select all

I really do appreciate your thoughts on this and I am not trying to be obstinate, I just don't see that method working without some sort of 'tricks' that I am not aware of....
Just tell me where are defined the boundaries of the exploration of the MakeHeatmap function, I'm not sure I can find it by myself easily enough ! ;)

In my opinion, you simply exhaust the whole array firstly defined when the program starts, but I'm not sure due to the things hidden in the frontiers stuff functions:

Code: Select all

sub MakeHeatMap(ByVal ot as integer)
    ' a basic Djistra's Floodfill routine, not optimized at all...
    dim as integer x,y,i,j,cost,costDir,costNew, oldPoint
    dim as integer x1, y1, c1, clrADJ, clrNew
    do
        oldPoint = frontpointer
        x = frontier(frontpointer,1)
        y = frontier(frontpointer,2)
        cost = frontier(frontpointer,0)
        ' remove point from frontier
        FrontierDel(oldPoint) 'remove current point from frontier
        ' error check
 '       if cost <> map(x,y,ot) then 'ERROR
'            beep
'            sleep
'            end
'        end if
        ' check all 8 directions, if cost to move there is less then their current cost then
        ' change their cost and add to frontier
        for direct as integer = 0 to 7
            i = x+DirInfo(direct,0) 'x Adj
            j = y+DirInfo(direct,1) 'y Adj
            if ((i > 0) and (i < 101)) and ((j > 0) and (j < 101)) then
                if (map(i,j,0)<>9) then
                    costDir = DirInfo(direct,2) ' movecost
                    costNew = cost + costDir
                    if map(i,j,ot) > costNew then
                        if map(i,j,ot)= 999 then
                            FrontierAdd(i,j,costNew,ot)
                        end if
                        map(i,j,ot) = costNew
                        'plot it
                        x1 = i*TILEW
                        y1 = j*TILEH
                        c1 = rgb(200,200,200) ' degfault grey background
                        clrNew = 255 - (costNew/1)
                        if clrNew < 0 then clrNew = 0
                        select case ot
                            case 1 ' 1 = distance to nearest tree object (#1)
                                c1 = rgb(0,clrNew,0)
                            case 2 ' 2 = distance to nearest iron ore object (#2)
                                c1 = rgb(clrNew,0,0)
                            case 3 ' 3 = distance to nearest Berry Bush Object (#3)
                                c1 = rgb(0,0,clrNew)
                            case 4 ' 4 = distance to nearest Mushroom Object (#4)
                                c1 = rgb(clrNew,clrNew,0)
                        end select
                        line(x1,y1)- step(TILEW-2,TILEH-2),c1,BF
                    end if
                end if
            end if
        next direct
    loop until frontpointer = 0
end sub
Post Reply