Problem with Fog-of-War

Game development specific discussions.
ITomi
Posts: 154
Joined: Jul 31, 2015 11:23
Location: Hungary

Problem with Fog-of-War

Post by ITomi »

Hello!

I found a big problem in my RTS game:
To create fog-of-war, I cover the whole game field with 128*128 black images. These images represented by type with X and Y place:

Code: Select all

type fogtiles
	as integer xplace,yplace
end type

dim shared numoffogtiles as ushort=0
redim shared afogtile(numoffogtiles) as fogtiles
When I create instances based on this type, everything will be black on the screen. It is OK for me.
But I can't delete these instances with my code:

Code: Select all

for t as integer=0 to numoffogtiles-1
    if distancetotile(afogtile(t),object)<=128 then
	for c as integer=t to numoffogtiles-1
            afogtile(c)=afogtile(c+1)
        next c
        numoffogtiles-=1
        redim preserve afogtile(numoffogtiles)
    end if
next t
But it seems, nothing happen, although I would like overwrite the datas of the tile instance with the next instance in the array and draw out tiles only the existing coordinates:

Code: Select all

for i as integer=0 to numoffogtiles-1
	put (afogtile(i).xplace,afogtile(i).yplace),tileimage,pset
next i
I would not like add "if tileexists..." or similar expressions, because I want totally delete that tile.
dafhi
Posts: 1641
Joined: Jun 04, 2005 9:51

Re: Problem with Fog-of-War

Post by dafhi »

how i would do it

swap tile(new visible tile), tile(numoffogtiles)
numof.. -= 1

redim preserve seems unnecessary
dodicat
Posts: 7983
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: Problem with Fog-of-War

Post by dodicat »

. . .
Last edited by dodicat on Nov 25, 2020 17:13, edited 1 time in total.
paul doe
Moderator
Posts: 1733
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: Problem with Fog-of-War

Post by paul doe »

@ITomi: Indeed, both dafhi and dodicat's observations are noteworthy; it's better (and easier) to preallocate your array of tiles, and you can't modify the end of loop counter in a for.

You'll have to use a do-loop:

Code: Select all

/' ... '/
type fogtiles
   as integer xplace,yplace
end type

dim shared numoffogtiles as integer = 0 '' SIGNED

/'
  The max number of FOW tiles is always going to be
  mapWidth * mapHeight, so it makes sense (from a
  performance standpoint) to just preallocate it and
  work on it directly, overwriting old values as
  required.
'/
dim shared as fogtiles afogtile( 0 to ( mapWidth * mapHeight ) - 1 )

/' ... '/
dim as ulong tile = 0

do while( tile <= numoffogtiles - 1 )
  if( distancetotile( afogtile( tile ), object ) <= 128 ) then
    afogtile( tile ) = afogtile( numoffogtiles - 1 )
    numoffogtiles - 1
    continue do
  end if
  
  tile += 1
loop
Here is a bit of working code showing the technique, so you can test/examine it in isolation to better adapt it to your needs:

Code: Select all

'' The array of values (these are your fog tiles)
dim as integer q( ... ) = { 2, 3, 8, 7, 1, 4, 9, 6 }
'' Make this var (it would be 'numoffogtiles') SIGNED
dim as integer count = ubound( q ) + 1

? "Count: " & count

'' This one's sign is unimportant (it's just the iterator)
dim as integer tile = 0

'' Prune all values >= 5
do while( tile <= count - 1 )
  if( q( tile ) >= 5 ) then
    q( tile ) = q( count - 1 )
    count -= 1
    continue do
  end if
  
  tile += 1
loop

'' Show results
for i as integer = 0 to count - 1
  ? q( i )
next

? "Count: " & count

sleep()
Another optimization that costs nothing and may be significative: if you're just comparing distances, use the squared distance (ie no square root), and check against the square of the other distance, like this:

Code: Select all

/' ... '/
if( distancetotilesquared( afogtile( tile ), object ) <= 128 ^ 2 ) then
  '' ...
Or perhaps you're already doing it. I can't tell from the code you posted...
ITomi
Posts: 154
Joined: Jul 31, 2015 11:23
Location: Hungary

Re: Problem with Fog-of-War

Post by ITomi »

I wrote a small code based on my program. It works well in this separated code, but not in my whole game. :-(

Code: Select all

#include "fbgfx.bi"
Using FB

dim shared as any ptr background,player,tileimage
dim shared as integer playerx=400,playery=300

type fogtiles
    as integer xplace,yplace
end type

dim shared numoffogtiles as ushort=0
redim shared afogtile(numoffogtiles) as fogtiles

const sw=800 : const sh=600

screenres sw,sh,32

sub tiledelete()
    for t as integer=0 to numoffogtiles-1
        if afogtile(t).xplace>=playerx-64 and afogtile(t).xplace<=playerx+8+64 and afogtile(t).yplace>=playery-64 and afogtile(t).yplace<=playery+8+64 then
            for c as integer=t to numoffogtiles-1
                afogtile(c)=afogtile(c+1)
            next c
            numoffogtiles-=1
            redim preserve afogtile(numoffogtiles)
        end if
    next t
end sub

background=imagecreate(800,600,rgb(0,255,0))
player=imagecreate(8,8,rgb(255,0,0))
tileimage=imagecreate(16,16,rgb(0,0,0))

for w as ushort=0 to 800-16 step 16
    for h as ushort=0 to 600-16 step 16
        afogtile(numoffogtiles).xplace=w : afogtile(numoffogtiles).yplace=h : numoffogtiles+=1
        redim preserve afogtile(numoffogtiles)
    next h
next w

dim k as string

do
    k = Inkey$
    Select Case k
    case "W","w"
        if playery-2>0 then playery-=2
    case "S","s"
        if playery+2<600 then playery+=2
    case "A","a"
        if playerx-2>0 then playerx-=2
    case "D","d"
        if playerx+2<800 then playerx+=2
    end select
    tiledelete()
    screenlock
    cls
    put (0,0),background,pset
    put (playerx,playery),player,pset
    for i as integer=0 to numoffogtiles-1
       put (afogtile(i).xplace,afogtile(i).yplace),tileimage,pset
    next i
    screenunlock
loop until multikey(sc_q)

imagedestroy background
imagedestroy player
imagedestroy tileimage
fxm
Moderator
Posts: 12107
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Problem with Fog-of-War

Post by fxm »

paul doe wrote:@ITomi: you can't modify the end of loop counter in a for

Code: Select all

sub tiledelete()
    for t as integer=0 to numoffogtiles-1
        print t: sleep 10
        if afogtile(t).xplace>=playerx-64 and afogtile(t).xplace<=playerx+8+64 and afogtile(t).yplace>=playery-64 and afogtile(t).yplace<=playery+8+64 then
            for c as integer=t to numoffogtiles-1
                afogtile(c)=afogtile(c+1)
            next c
            numoffogtiles-=1
            redim preserve afogtile(numoffogtiles)
        end if
    next t
end sub
In 'for t as integer = 0 to numoffogtiles-1', 'numoffogtiles-1' is evaluated at the beginning of the loop only (it is not reevaluated at each loop).
If in the loop 'numoffogtiles' is decremented latter and 'afogtile()' resized smaller accordingly, this will induce an out of bound access of the array.
paul doe
Moderator
Posts: 1733
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: Problem with Fog-of-War

Post by paul doe »

fxm wrote:...
In 'for t as integer = 0 to numoffogtiles-1', 'numoffogtiles-1' is evaluated at the beginning of the loop only (it is not reevaluated at each loop).
If in the loop 'numoffogtiles' is decremented latter and 'afogtile()' resized smaller accordingly, this will induce an out of bound access of the array.
'Esactly'.
I was too ambiguous I guess, so clarifying/correcting never hurts. Thanks.

@ITomi: do you understand what we are trying to tell you here? Your 'working code' crashes with an 'Array out of bounds access' error when you compile it with the -exx compiler switch, for the reasons explained by fxm above...

Modified snippet:

Code: Select all

#include "fbgfx.bi"

Using FB

dim shared as any ptr background,player,tileimage
dim shared as integer playerx=400,playery=300

type fogtiles
    as integer xplace,yplace
end type

dim shared numoffogtiles as integer = 0
redim shared afogtile(numoffogtiles) as fogtiles

const sw=800 : const sh=600

screenres sw,sh,32

sub tiledelete()
  dim as integer t = 0
  
  do while( t <= numoffogtiles - 1 )
    if afogtile(t).xplace>=playerx-64 and afogtile(t).xplace<=playerx+8+64 and afogtile(t).yplace>=playery-64 and afogtile(t).yplace<=playery+8+64 then
      afogtile( t ) = afogtile( numoffogtiles - 1 )
      numoffogtiles -= 1
      continue do
    end if
   
    t += 1
  loop
end sub

background=imagecreate(800,600,rgb(0,255,0))
player=imagecreate(8,8,rgb(255,0,0))
tileimage=imagecreate(16,16,rgb(0,0,0))

for w as ushort=0 to 800-16 step 16
    for h as ushort=0 to 600-16 step 16
        afogtile(numoffogtiles).xplace=w : afogtile(numoffogtiles).yplace=h : numoffogtiles+=1
        redim preserve afogtile(numoffogtiles)
    next h
next w

dim k as string

do
  if( multiKey( SC_UP ) ) then
    if playery-2>0 then playery-=2
  end if
  
  if( multiKey( SC_DOWN ) ) then
    if playery+2<600 then playery+=2
  end if
  
  if( multiKey( SC_LEFT ) ) then
    if playerx-2>0 then playerx-=2
  end if
  
  if( multiKey( SC_RIGHT ) ) then
    if playerx+2<800 then playerx+=2
  end if
  
  tiledelete()
  
  screenlock
    cls
    put (0,0),background,pset
    put (playerx,playery),player,pset
    for i as integer=0 to numoffogtiles-1
       put (afogtile(i).xplace,afogtile(i).yplace),tileimage,pset
    next i
  screenunlock
  
  sleep( 1, 1 )
loop until multikey( SC_ESCAPE )

imagedestroy background
imagedestroy player
imagedestroy tileimage
ITomi
Posts: 154
Joined: Jul 31, 2015 11:23
Location: Hungary

Re: Problem with Fog-of-War

Post by ITomi »

Hello Paul Doe!

Okay, I corrected it, although the "Array out of bounds access" error didn't come up at me. :-/
But I found an interesting thing: in my game the whole playfield is greater than the 640*480 view. But when I move away the view in the playfield, the program shows me the same fog-of-war tiles.
So, the game contains 205 tiles in the first level, but it always shows me those ones, which cover the 640*480 view and the coordinates of these tiles are the same, whereas if the view move away, it ought to show the other tiles with those own coordinates.
Maybe am I on wrong way with drawing out the tiles? I don't understand it...
I complemented your code, Paul, and although it can delete some tiles, it is not the real solution for me yet:

Code: Select all

#include "fbgfx.bi"

Using FB

dim shared as any ptr background,player,tileimage,ground
dim shared as integer playerx=400,playery=300,viewx=0,viewy=0

type fogtiles
    as integer xplace,yplace
end type

dim shared numoffogtiles as integer = 0
redim shared afogtile(numoffogtiles) as fogtiles

const sw=800 : const sh=600
const pfwidth=2000 : const pfheight=1000

screenres sw,sh,32

sub tiledelete()
  dim as integer t = 0
 
  do while( t <= numoffogtiles - 1 )
    if afogtile(t).xplace>=playerx-64 and afogtile(t).xplace<=playerx+8+64 and afogtile(t).yplace>=playery-64 and afogtile(t).yplace<=playery+8+64 then
      afogtile( t ) = afogtile( numoffogtiles - 1 )
      numoffogtiles -= 1
      continue do
    end if
    t += 1
  loop
end sub

background=imagecreate(pfwidth,pfheight,rgb(0,255,0))
ground=imagecreate(pfwidth,pfheight,rgb(0,255,0))
player=imagecreate(8,8,rgb(255,0,0))
tileimage=imagecreate(64,64,rgb(0,0,0))

for i as ubyte=0 to 20
    circle background,(int(rnd()*pfwidth),int(rnd()*pfheight)),int(rnd()*50)+1,color rgb(0,0,255)
next i

for w as ushort=0 to pfwidth-64 step 64
    for h as ushort=0 to pfheight-64 step 64
        afogtile(numoffogtiles).xplace=w : afogtile(numoffogtiles).yplace=h : numoffogtiles+=1
        redim preserve afogtile(numoffogtiles)
    next h
next w

dim k as string

do
  if( multiKey( SC_UP ) ) then
    if playery-2>0 then playery-=2
    if viewy-2>0 then viewy-=2
  end if
 
  if( multiKey( SC_DOWN ) ) then
    if playery+2<600 then playery+=2
    if viewy+sh+2<pfheight then viewy+=2
  end if
 
  if( multiKey( SC_LEFT ) ) then
    if playerx-2>0 then playerx-=2
    if viewx-2>0 then viewx-=2
  end if
 
  if( multiKey( SC_RIGHT ) ) then
    if playerx+2<800 then playerx+=2
    if viewx+sw+2<pfwidth then viewx+=2
  end if
 
  tiledelete()
 
  screenlock
    cls
    put ground,(0,0),background,pset
    put (0,0),ground,(viewx,viewy)-(viewx+800,viewy+600),pset
    put (playerx,playery),player,pset
    for i as integer=0 to numoffogtiles-1
        put (afogtile(i).xplace,afogtile(i).yplace),tileimage,pset
        color rgb(255,255,255)
        draw string (afogtile(i).xplace,afogtile(i).yplace),str(afogtile(i).xplace)+"-"+str(afogtile(i).yplace)
        draw string (afogtile(i).xplace,afogtile(i).yplace+16),str(i)+"/"+str(numoffogtiles-1)
    next i
  screenunlock
  sleep( 1, 1 )
loop until multikey( SC_ESCAPE )

imagedestroy background
imagedestroy player
imagedestroy tileimage
imagedestroy ground
paul doe
Moderator
Posts: 1733
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: Problem with Fog-of-War

Post by paul doe »

ITomi wrote: Okay, I corrected it, although the "Array out of bounds access" error didn't come up at me. :-/
...
You need to compile with the -exx compiler switch. See here for details on switches you can use: https://www.freebasic.net/wiki/CatPgCompOpt
...
But I found an interesting thing: in my game the whole playfield is greater than the 640*480 view. But when I move away the view in the playfield, the program shows me the same fog-of-war tiles.
So, the game contains 205 tiles in the first level, but it always shows me those ones, which cover the 640*480 view and the coordinates of these tiles are the same, whereas if the view move away, it ought to show the other tiles with those own coordinates.
Maybe am I on wrong way with drawing out the tiles? I don't understand it...
...
It should come as no surprise, due to the way you're handling FOW: you change the view coordinates but you don't relate the change to the FOW tiles, so the view always display the same FOW.
Anyway, why are you doing it this way? It's woefully inefficient and unnecessary. You'll do better (and it's easier to implement) simply by keeping a separate FOW map for each player, and use bitflags to control visibility (as in, fully obscured, partially obscured or fully visible). Then, all you have to do is update this map for each unit (since each player always has only one FOW map). It'll also simplify the calculations when the AI uses it...
Last edited by paul doe on Nov 21, 2020 23:11, edited 1 time in total.
dafhi
Posts: 1641
Joined: Jun 04, 2005 9:51

Re: Problem with Fog-of-War

Post by dafhi »

is this what you want?

Code: Select all

sub tiledelete()
  dim as integer t = 0
  
  while t <= numoffogtiles
    if afogtile(t).xplace>=playerx-64 and afogtile(t).xplace<=playerx+8+64 and afogtile(t).yplace>=playery-64 and afogtile(t).yplace<=playery+8+64 then
      
      '' fog for this position is no longer needed
      afogtile(t) = afogtile(numoffogtiles)
      
      numoffogtiles -= 1
    EndIf
    t += 1
  wend
ITomi
Posts: 154
Joined: Jul 31, 2015 11:23
Location: Hungary

Re: Problem with Fog-of-War

Post by ITomi »

"why are you doing it this way? It's woefully inefficient and unnecessary. You'll do better (and it's easier to implement) simply by keeping a separate FOW map for each player, and use bitflags to control visibility (as in, fully obscured, partially obscured or fully visible)."

Unfortunately I don't understand about your method, because I'm only a beginner FB programmer.
I gave up using tiles in this RTS game, but instead of this I built in an other method to make invisible the enemy units: each units controls whether the player's units are enough closer, and if so, they are visible else not.
Maybe this is a similar method to your advice.
BasicCoder2
Posts: 3906
Joined: Jan 01, 2009 7:03
Location: Australia

Re: Problem with Fog-of-War

Post by BasicCoder2 »

Maybe you are just over complicating things? I cannot actually visualize what it is you are trying to do. I didn't know what "fog of war" meant but after a google it appears it means only showing a fixed area around a player?
paul doe
Moderator
Posts: 1733
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: Problem with Fog-of-War

Post by paul doe »

ITomi wrote: ...
I gave up using tiles in this RTS game...
Is there any particular reason? Implementing a simple, smooth scrolling tile engine is relatively straightforward:

Code: Select all

#include once "fbgfx.bi"

type as ulong color_t

enum MapTerrains
  TERRAIN_WATER
  TERRAIN_LAND
  TERRAIN_WOODS
  TERRAIN_MOUNTAINS
end enum

private function max overload( a as integer, b as integer ) as integer
  return( iif( a > b, a, b ) )
end function

private function min overload( a as integer, b as integer ) as integer
  return( iif( a < b, a, b ) )
end function

private function fClamp( v as single, a as single, b as single ) as single
  return( iif( a > v, a, iif( v < b, v, b ) ) )
end function

private function noise2( x as integer, y as integer ) as single
  dim as integer _
    n = x + y * 57
    n = ( n shl 13 ) xor n
  
  return( ( 1.0f - ( ( n * ( n * n * 15731 + 789221 ) + _
    1376312589 ) and &h7fffffff ) / 1073741824.0f ) )   
end function

private function remap overload( v as single ) as single
  return( fClamp( v * 0.5f + 0.5f, 0.0f, 1.0f ) )
end function

private function remap( _
  x as single, start1 as single, end1 as single, start2 as single, end2 as single ) as single
  
  return( ( x - start1 ) * ( end2 - start2 ) / ( end1 - start1 ) + start2 )
end function

private function smoothedNoise( x as single, y as single ) as single
  dim as single _
    fractX = x - int( x ), _
    fractY = y - int( y )
  
  dim as integer _
    x1 = int( x ), _
    y1 = int( y ), _
    x2 = int( x - 1.0f ), _
    y2 = int( y - 1.0f )
  
  return( _
    fractX * fractY * noise2( x1, y1 ) + _
    fractX * ( 1.0f - fractY ) * noise2( x1, y2 ) + _
    ( 1.0f - fractX ) * fractY * noise2( x2, y1 ) + _
    ( 1.0f - fractX ) * ( 1.0f - fractY ) * noise2( x2, y2 ) )
end function

private function turbulence( x as single, y as single, size as single ) as single
  dim as single _
    value = 0.0f, _
    initialSize = size
  
  do while( size >= 1.0f )
    value += smoothedNoise( x / size, y / size ) * size
    size *= 0.5f
  loop
  
  value /= initialSize
  
  return( fClamp( value, -1.0, 1.0 ) )
end function

type Vec2
  declare constructor()
  declare constructor( as single, as single )
  
  as single x, y
end type

constructor Vec2() : end constructor

constructor Vec2( nX as single, ny as single )
  x = nX : y = nY
end constructor

type Region
  declare constructor()
  declare constructor( _
    as single, as single, as single, as single )
  declare destructor()
  
  declare function centerAt( as single, as single ) byref as Region
  declare function outsideAmount( byref as Region ) as Vec2
  
  as single x, y, w, h
end type

constructor Region() : end constructor

constructor Region( aX as single, aY as single, aW as single, aH as single )
  x = aX : y = aY
  w = aW : h = aH
end constructor

destructor Region() : end destructor

function Region.centerAt( aX as single, aY as single ) byref as Region
  dim as single _
    hw = w * 0.5f, hh = h * 0.5f
  
  x = aX - hw : y = aY - hh
  
  return( this )
end function

private function Region.outsideAmount( byref r as Region ) as Vec2
  return( Vec2( _
    iif( x < r.x, r.x - x, iif( x + w - 1 > r.x + r.w - 1, _
      ( r.x + r.w - 1 ) - ( x + w - 1 ), 0 ) ), _
    iif( y < r.y, r.y - y, iif( y + h - 1 > r.y + r.h - 1, _
      ( r.y + r.h - 1 ) - ( y + h - 1 ), 0 ) ) ) )
end function

type MapCell
  as long terrain
end type

type Map
  declare constructor()
  declare constructor( as long, as long )
  declare destructor()
  
  declare function getRegionFor( as long ) as Region
  
  as MapCell cell( any, any )
  as long w, h
end type

constructor Map()
  constructor( 128, 128 )
end constructor

constructor Map( aW as long, aH as long )
  w = iif( aW < 1, 1, aW )
  h = iif( aH < 1, 1, aH )
  
  redim cell( 0 to w - 1, 0 to h - 1 )
end constructor

destructor Map() : end destructor

'' Gets a region that describes the map size in pixels
private function Map.getRegionFor( tileSize as long ) as Region
  return( Region( 0, 0, w * tileSize, h * tileSize ) )
end function

type Resolution
  as long w, h
end type

type GameAssets
  as long ptr terrains
  as long terrainCount
  as Fb.Image ptr _miniMap
  as long tileSize
end type

type GameCamera
  as Region reg
  as single speed
end type

type Unit
  as Vec2 pos
  as long vision
end type

type PlayerState
  as Unit units( any )
  as long unitCount
  as GameCamera cam
  as color_t color
end type

type GameState
  as GameAssets ptr assets
  as Resolution ptr _res
  as Map ptr _map
end type

'' Creates a random map
sub createMap_noise( _
  byref gs as GameState, threshold as single = 0.3f, seed as ulong = 0 )
  
  for y as integer = 0 to gs._map->h - 1
    for x as integer = 0 to gs._map->w - 1
      dim as single t = remap( turbulence( x + seed, y - seed, 64 ) )
      
      with gs._map->cell( x, y )
        if( t > threshold ) then
          .terrain = gs.assets->terrains[ int( remap( _
            t, threshold, 1.0f, 1, gs.assets->terrainCount ) ) ]
        else
          .terrain = gs.assets->terrains[ 0 ] 
        end if
      end with
    next
  next
end sub

function init( w as integer, h as integer ) as Resolution
  screenRes( w, h, 32, 2, , Fb.GFX_ALPHA_PRIMITIVES )
  screenSet( 0, 1 )
  
  return( type <Resolution>( w, h ) )
end function

'' Gets which tile the render function has to draw. In a real implementation,
'' you'll look up which tile corresponds to the specified cell coordinates
'' and return that. Here, I just return a different color for each terrain.
function getTile( byref m as Map, x as long, y as long ) as color_t
  select case as const( m.cell( x, y ).terrain )
    case TERRAIN_WATER
      return( rgb( 128, 128, 255 ) )
    case TERRAIN_LAND
      return( rgb( 0, 164, 0 ) )
    case TERRAIN_WOODS
      return( rgb( 0, 128, 0 ) )
    case TERRAIN_MOUNTAINS
      return( rgb( 214, 214, 214 ) )
  end select
  
  '' For 'tile not found'; it should not happen
  return( rgb( 255, 0, 255 ) )
end function

'' Updates the minimap with the map contents. In a real implementation,
'' you wouldn't be using 'pset' since it's slow.
sub updateMinimap( mm as Fb.Image ptr, byref m as Map )
  for y as integer = 0 to m.h - 1
    for x as integer = 0 to m.w - 1
      pset mm, ( x, y ), getTile( m, x, y )
    next
  next
end sub

/'
  Main update function. Make it return false if you want to end the
  outer loop.
'/
function update( _
  dt as double, byref gs as GameState, byref cam as GameCamera ) as boolean
  
  dim as string k = lcase( inkey() )
  
  with cam.reg
    '' Move the camera
    if( multiKey( Fb.SC_UP ) ) then
      .y -= cam.speed * dt
    end if
    
    if( multiKey( Fb.SC_DOWN ) ) then
      .y += cam.speed * dt
    end if
    
    if( multiKey( Fb.SC_LEFT ) ) then
      .x -= cam.speed * dt
    end if
    
    if( multiKey( Fb.SC_RIGHT ) ) then
      .x += cam.speed * dt
    end if
    
    '' Keep the camera on bounds
    with cam.reg.outsideAmount( gs._map->getRegionFor( gs.assets->tileSize ) )
      cam.reg.x += .x
      cam.reg.y += .y
    end with
  end with
  
  '' End by pressing the 'ESC' key
  if( multiKey( Fb.SC_ESCAPE ) ) then
    return( true )
  end if
  
  '' Generates another map
  if( k = "r" ) then
    createMap_noise( gs, 0.3f, rnd() * 12345 )
    updateMinimap( gs.assets->_miniMap, *gs._map )
  end if
  
  return( false )
end function

'' Renders the minimap at the specified coordinates
sub renderMinimap( _
  gs as GameState, byref cam as GameCamera, x as long, y as long )
  
  put( x, y ), gs.assets->_miniMap, alpha, 128
  
  '' Map width and height, in pixels
  dim as long _
    mw = gs._map->w * gs.assets->tileSize, _
    mh = gs._map->h * gs.assets->tileSize
  
  '' Compute the corners of the view rectangle
  with cam.reg
    dim as long _
      x1 = remap( .x, 0, mw, 0, gs.assets->_miniMap->width ), _
      y1 = remap( .y, 0, mh, 0, gs.assets->_miniMap->height ), _
      x2 = remap( .x + .w, 0, mw, 0, gs.assets->_miniMap->width ), _
      y2 = remap( .y + .h, 0, mh, 0, gs.assets->_miniMap->height )
    
    '' And render it over the minimap
    line( x + x1, y + y1 ) - ( x + x2, y + y2 ), rgb( 255, 0, 0 ), b
  end with
  
end sub

/'
  Main render function
'/
sub render overload( _
  dt as double, byref gs as GameState, byref cam as GameCamera )
  
  cls()
  
  '' Shorthand for the tile size
  dim as long ts = gs.assets->tileSize
  
  '' Compute how many rows and columns we need to display,
  '' based on the tile size and the camera extents.
  dim as long _
    cols = ( cam.reg.w \ ts ) + abs( cam.reg.w mod ts <> 0 ), _
    rows = ( cam.reg.h \ ts ) + abs( cam.reg.h mod ts <> 0 )
  
  '' Compute the tile (in map cell coordinates) for the
  '' upper left corner of the view. This will be the
  '' starting tile from where we need to start drawing.
  dim as long _
    startX = cam.reg.x \ ts, startY = cam.reg.y \ ts
  
  '' Compute how many pixels we need to offset the starting
  '' tile for smooth scrolling.
  dim as long _
    offsetX = cam.reg.x mod ts, offsetY = cam.reg.y mod ts
  
  '' Then we render. Careful not to try and access a tile outside
  '' the map boundaries.
  with *gs._map
    for y as integer = 0 to min( rows, .h - startY - 1 )
      for x as integer = 0 to min( cols, .w - startX - 1 )
        '' Select each tile appropriately. Here I just assign a color to
        '' each terrain index.
        dim as color_t clr = getTile( *gs._map, startX + x, startY + y )
        
        '' Tile position, in screen coordinates
        dim as long _
          tx = x * ts, ty = y * ts
        
        '' Render the 'tile'
        line( tx - offsetX, ty - offsetY ) - _
          ( tx - offsetX + ts - 1, ty - offsetY + ts - 1 ), clr, bf
      next
    next
  end with
  
  renderMinimap( gs, cam, 10, gs._res->h - gs.assets->_miniMap->height - 10 )
  
  sleep( 1, 1 )
  flip()
end sub

/'
  Main code
'/
var res = init( 800, 600 )


'' Four types of terrain
dim as long terrains( ... ) = { _
  TERRAIN_WATER, TERRAIN_LAND, TERRAIN_WOODS, TERRAIN_MOUNTAINS }

randomize()

'' Create a 256x256 map
var m = Map( 256, 256 )
var miniMap = imageCreate( m.w, m.h )

'' Create and initialize the global state and assets. Global is to be
'' understood here as 'state common to all functions', not *shared* state.
dim as GameState gs

'' Create assets
dim assets as GameAssets

with assets
  .terrains = @terrains( 0 )
  .terrainCount = ubound( terrains ) + 1
  ._miniMap = minimap
  .tileSize = 32
end with

with gs
  ._res = @res
  ._map = @m
  .assets = @assets
end with

createMap_noise( gs, 0.3f, rnd() * 12345 )
updateMinimap( miniMap, m )

'' The camera will be centered in the middle of the map at startup
dim as GameCamera cam 

with cam
  .reg = Region( 0, 0, res.w, res.h ).centerAt( _
    ( m.w * gs.assets->tileSize ) / 2, ( m.h * gs.assets->tileSize ) / 2 )
  .speed = 500.0f
end with

dim as boolean finished = false
dim as double dt

do
  finished = update( dt, gs, cam )
  
  dt = timer()
  render( dt, gs, cam )
  dt = timer() - dt
loop until( finished )

imageDestroy( miniMap )
Image
The code generates a random map and then lets you navigate it with the arrow keys. If you press 'R', a new map will be generated. The scroll is a bit slow, but that's kind of the point so you see how you can implement a tile based, smooth scrolling engine...
Last edited by paul doe on Nov 23, 2020 4:03, edited 3 times in total.
dodicat
Posts: 7983
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: Problem with Fog-of-War

Post by dodicat »

. . .
Last edited by dodicat on Nov 25, 2020 17:13, edited 1 time in total.
BasicCoder2
Posts: 3906
Joined: Jan 01, 2009 7:03
Location: Australia

Re: Problem with Fog-of-War

Post by BasicCoder2 »

dodicat wrote:Basiccoder2 - you have done this stuff in the past.

Code: Select all

[/quote]

Indeed I have however iTomi's question was in the context of object orientated programming which I haven't actually used in any experimental tile game engines where objects are created and deleted.

iTomi also says he has given up on tiles and instead is just using the distance between units to determine if they are visible or not.
Post Reply