It is currently Sat Oct 19, 2019 2:11 pm

All times are UTC + 1 hour




Post new topic Reply to topic  [ 11 posts ] 
Author Message
 Post subject: Fog of war
PostPosted: Sat May 14, 2016 9:09 am 
Offline
Enthusiast
Enthusiast

Joined: Wed Sep 18, 2013 11:54 am
Posts: 387
Location: France
Hello,

I'd like to implement a fog of war on a big sprite (hand drawn map) that I clipped to the screen size.

The idea would be to do that :

Image
without fog of war


Image
with fog of war


But I have no idea how to do that. Note that it's not a light: when the player moves, the revealed hallway should stay visible.


Top
 Profile  
Reply with quote  
 Post subject: Re: Fog of war
PostPosted: Sat May 14, 2016 12:19 pm 
Offline
Addict
Addict

Joined: Thu Aug 30, 2007 11:54 pm
Posts: 1111
Location: right here
you could draw multiple semitransparent black spites (or with a radiant alpha gradient) all over the scene. maybe in a tiled manner or also possibly overlapping. then you store if the player has entered the acording area and simply skip the fog drawing for that region. but there could be limits to the performance of this due to many sprites displayed but there are also ways to reduce this problem.


Top
 Profile  
Reply with quote  
 Post subject: Re: Fog of war
PostPosted: Sat May 14, 2016 12:27 pm 
Offline
Addict
Addict

Joined: Thu Aug 30, 2007 11:54 pm
Posts: 1111
Location: right here
actually you could also overdraw your scene with one black opaque sprite and draw some transparency to regions of that sprite while the player is moving, so your fog sprite gets more and more transparent in the known regions while playing.


Top
 Profile  
Reply with quote  
 Post subject: Re: Fog of war
PostPosted: Sat May 14, 2016 1:35 pm 
Offline
Addict
Addict
User avatar

Joined: Tue Oct 09, 2007 2:15 am
Posts: 1084
#NULL wrote:
actually you could also overdraw your scene with one black opaque sprite and draw some transparency to regions of that sprite while the player is moving, so your fog sprite gets more and more transparent in the known regions while playing.


Nice idea ! Image
No need of complicated mathematics ;)

_________________
PureBasic 5.71 LTS (Windows x86/x64) | Windows10 Pro x64 | Z370 Extreme4 | i7 8770k | 32GB RAM | iChill GeForce GTX 980 X4 Ultra | HAF XF Evo​​
English is not my native language... (I often use DeepL to translate my texts.)


Top
 Profile  
Reply with quote  
 Post subject: Re: Fog of war
PostPosted: Sat May 14, 2016 3:53 pm 
Offline
Enthusiast
Enthusiast

Joined: Wed Sep 18, 2013 11:54 am
Posts: 387
Location: France
That was my thought too, but I can think of two problems :

1/ Performances : I'd have to redraw in real time everytime the player moves into an unrevealed territory.
2/ The hard transition between revealed an non-revealed portions of the map would probably look a bit strange.

Anyway, I want to avoid complicated math :)


Top
 Profile  
Reply with quote  
 Post subject: Re: Fog of war
PostPosted: Sat May 14, 2016 7:56 pm 
Offline
Addict
Addict

Joined: Thu Aug 30, 2007 11:54 pm
Posts: 1111
Location: right here
i would actually try a mix of those two, something like updating the drawing every some frames/time according to the current player position and only if the player moved since last time. only as often enough as to have the exploration somewhat smooth. and i would use a 'static' image as an alpha mask to be drawn on the sprite at those times.


Top
 Profile  
Reply with quote  
 Post subject: Re: Fog of war
PostPosted: Tue May 17, 2016 11:54 pm 
Offline
Addict
Addict
User avatar

Joined: Mon Jul 25, 2005 3:51 pm
Posts: 3585
Location: Utah, USA
Here's an example using the method of overdrawing the map with a completely separate sprite containing 'the fog':

Code:
;Author: Demivec
;Created: 5/17/2016
;Updated: 5/18/2016 (minor adjustments and a new feature added)
;OS: Tested with Windows
;Compiler: PureBasic v5.41 x64
;Description: A sample test of showing the 'Fog of War' with sprites.

EnableExplicit

Enumeration
  #map_spr
  #fog_spr
  #mouse_spr
EndEnumeration

#fogClearWidth = 80
#fogClearHeight = #fogClearWidth

#mapWidth = 700
#mapHeight = 300

#mouseWidth = 15
#mouseHeight = #mouseWidth

Declare customFogClear(x, y)
Declare customFogSet(tlx, tly, brx, bry)

InitSprite()
InitKeyboard()

OpenWindow(0, 0, 0, #mapWidth, #mapHeight, "Fog of War Test (use arrows to move around, 'R' resets fog)", #PB_Window_SystemMenu)
OpenWindowedScreen(WindowID(0), 0, 0, #mapWidth, #mapHeight)

Define i, x, y, fogClear_img

;create sprites
CreateSprite(#map_spr, #mapWidth, #mapHeight)
StartDrawing(SpriteOutput(#map_spr))
  ;make random map
  For i = 1 To 1000
    If Random(1): DrawingMode(#PB_2DDrawing_Outlined): Else: DrawingMode(#PB_2DDrawing_Default): EndIf
    Box(Random(#mapWidth), Random(#mapHeight), Random(100), Random(100), RGB(Random(200, 40), Random(200, 40), Random(200, 40)))
  Next
StopDrawing()
 
CreateSprite(#mouse_spr, #mouseWidth, #mouseHeight)
StartDrawing(SpriteOutput(#mouse_spr))
  DrawingMode(#PB_2DDrawing_Outlined)
  Circle(#mouseWidth / 2, #mouseHeight / 2, #mouseHeight / 2 - 1, RGB(255, 255, 255))
  Circle(#mouseWidth / 2, #mouseHeight / 2, #mouseHeight / 2 - 2, RGB(0, 0, 1))
  Circle(#mouseWidth / 2, #mouseHeight / 2, #mouseHeight / 2    , RGB(0, 0, 1))
  Line(0, 0, #mouseWidth, #mouseHeight, RGB(255, 0, 0))
  Line(#mouseWidth - 1, 0, -#mouseWidth, #mouseHeight, RGB(255, 0, 0))
StopDrawing()
 
CreateSprite(#fog_spr, #mapWidth, #mapHeight, #PB_Sprite_AlphaBlending)
StartDrawing(SpriteOutput(#fog_spr))
  Box(0, 0, OutputWidth(), OutputHeight(), RGB(0, 0, 200))
  customFogSet(0, 0, #mapWidth - 1, #mapHeight - 1)
StopDrawing()

Dim fogClear(#fogClearWidth - 1, #fogClearHeight - 1) ;stores alpha adjustment values (towards transparency)
;use an image with gradient values to generate our values for us
fogClear_img = CreateImage(#PB_Any, #fogClearWidth, #fogClearHeight, 32)
StartDrawing(ImageOutput(fogClear_img))
  DrawingMode(#PB_2DDrawing_Gradient)
 
  BackColor(RGB(64, 64, 64))
  GradientColor(0.60, RGB(20, 20, 20))
  GradientColor(0.9, RGB(4, 4, 4))
  FrontColor(RGB(0, 0, 0)) ;max level of alpha adjustment, it should be low if it will overlap when drawn
 
  ;An example is provided for both rectangular and circular shape for fog clearing.
  ;BoxedGradient(0, 0, #fogClearWidth, #fogClearHeight)
  ;Box(0, 0,  #fogClearWidth, #fogClearHeight)
  CircularGradient(#fogClearWidth / 2, #fogClearHeight / 2, #fogClearWidth / 2)
  Circle(#fogClearWidth / 2, #fogClearHeight / 2, #fogClearWidth / 2)
 
  For x = 0 To #fogClearWidth - 1
    For y = 0 To #fogClearHeight - 1
      fogClear(x, y) = Red(Point(x, y))
    Next
  Next
StopDrawing()
FreeImage(fogClear_img) ;don't need the work image anymore 


x = 0 ;x = #mapWidth / 2
y = #mapHeight / 2

For i = 1 To 20: customFogClear(x, y): Next ;repeatedly draw the initial position to 'clear' the fog completely

Define event, quit, updateFog
Repeat
  ClearScreen(0)
  Repeat
    event = WindowEvent()
    If event = #PB_Event_CloseWindow
      quit = 1
    EndIf
  Until event = 0
 
  updateFog = #False
  ExamineKeyboard()
  If KeyboardPushed(#PB_Key_Left) And x > 1: x - 3: updateFog = #True: EndIf
  If KeyboardPushed(#PB_Key_Right) And x < #mapWidth: x + 3: updateFog = #True: EndIf
  If KeyboardPushed(#PB_Key_Up) And y > 1: y - 3: updateFog = #True: EndIf
  If KeyboardPushed(#PB_Key_Down) And y < #mapHeight: y + 3: updateFog = #True: EndIf
  If KeyboardPushed(#PB_Key_R)
    StartDrawing(SpriteOutput(#fog_spr))
      customFogSet(0, 0, #mapWidth - 1, #mapHeight - 1)
    StopDrawing()
    For i = 1 To 20: customFogClear(x, y): Next
  EndIf
 
  If updateFog
    customFogClear(x, y)
  EndIf
 
  DisplaySprite(#map_spr, 0, 0)
  DisplayTransparentSprite(#fog_spr, 0, 0) ;The fog level can also be tinted and have its intensity set.
  DisplayTransparentSprite(#mouse_spr, x - #mouseWidth / 2, y - #mouseHeight / 2, 125)

 
  FlipBuffers()
  Delay(10)
Until quit = 1

End

;procedures
Procedure customFogClear(x, y)
  ;Manually change alpha settings by replacing each pixel with its corrected values.
  ;We need to manually change the alpha because the 2D Drawing commands reset the alpha for
  ;sprites in all modes of drawing including those specifically for changing the alpha levels.
  Shared fogClear()
  Protected i, j, ofx, ofy, tlx, tly, brx, bry, fogPoint
  Protected *bufferLine.Long, *bufferPixel.Long, pitch
 
  tlx = x - #fogClearWidth / 2
  tly = y - #fogClearHeight / 2
  brx = tlx + #fogClearWidth
  bry = tly + #fogClearHeight
  If tlx < 0: ofx = -tlx: tlx = 0: EndIf
  If tly < 0: ofy = -tly: tly = 0: EndIf
  If brx > #mapWidth: brx = #mapWidth: EndIf
  If bry > #mapHeight: bry = #mapHeight: EndIf
 
  StartDrawing(SpriteOutput(#fog_spr))
    DrawingMode(#PB_2DDrawing_AlphaBlend)
    *bufferLine.Long = DrawingBuffer()
    pitch = DrawingBufferPitch()
   
    *bufferLine + pitch * tly
    For j = ofy To ofy + bry - tly - 1
      *bufferPixel = *bufferLine + tlx * SizeOf(long)
      For i = ofx To ofx + brx - tlx - 1
        fogPoint = Alpha(*bufferPixel\l) - fogClear(i, j)
        If fogPoint < 0: fogPoint = 0: EndIf
        ;We assume the pixel format is #PB_PixelFormat_32Bits_BGR : 4 bytes per pixel (BBGGRR) + AA for alpha
        ;We could also provide separate routines to deal with other DrawingBufferPixelFormat() values.
        *bufferPixel\l = RGBA(0, 0, 0, fogPoint) ;Memory color values here are actually in reverse but alpha is still last
        *bufferPixel + SizeOf(Long)
      Next
      *bufferLine + pitch
    Next
  StopDrawing()
EndProcedure

Procedure customFogSet(tlx, tly, brx, bry) ;sets values within a a box
  ;Called between StartDrawing(SpriteOutput(spriteID)) and StopDrawing().
  ;resets color and alpha settings manually by replacing each pixel with its correct values
  Protected i, j
  Protected pitch = DrawingBufferPitch(), *bufferPixel.Long
  Protected *bufferLine.Long = DrawingBuffer() + tly * pitch + tlx * SizeOf(Long)
   
 
  For j = tly To bry
    *bufferPixel = *bufferLine + tlx * SizeOf(Long)
    For i = tlx To brx
      ;We assume the pixel format is #PB_PixelFormat_32Bits_BGR : 4 bytes per pixel (BBGGRR) + AA for alpha
      *bufferPixel\l = RGBA(0, 0, 0, 255) ;memory color values here are actually in reverse but alpha is still last
      *bufferPixel + SizeOf(Long)
    Next
    *bufferLine + pitch
  Next
EndProcedure


There are a couple of special tricks to make everything possible.

The first is using an image to generate the alpha gradients that will be applied to the sprite.
The Second is applying the alpha changes to the 'fog' sprite manually because drawing them would destroy the sprite's alpha layer.


@Edit: made some minor corrections and documentation updates to code. Added the ability to reset the fog. Added the code to use a box shape instead of a circle area to clear the fog but it must be commented/uncommented in the code to work.

_________________
Image


Last edited by Demivec on Fri May 20, 2016 1:10 am, edited 1 time in total.

Top
 Profile  
Reply with quote  
 Post subject: Re: Fog of war
PostPosted: Wed May 18, 2016 10:24 am 
Offline
Addict
Addict
User avatar

Joined: Thu Jun 07, 2007 3:25 pm
Posts: 3678
Location: Berlin, Germany
Hi Demivec,

that's a cool effect, thank you!

_________________
Please excuse my flawed English. My native language is PureBasic.
Search
RSBasic's backups


Top
 Profile  
Reply with quote  
 Post subject: Re: Fog of war
PostPosted: Thu May 19, 2016 3:25 am 
Offline
Addict
Addict
User avatar

Joined: Mon May 14, 2007 2:13 am
Posts: 979
Location: Darling River
Quote:
that's a cool effect, thank you!

I like too. :wink:

_________________
PureBasic Rocks! Even More! And More!
PureBasic 5, Now We're Really Rockin!


Top
 Profile  
Reply with quote  
 Post subject: Re: Fog of war
PostPosted: Thu May 19, 2016 1:33 pm 
Offline
Enthusiast
Enthusiast
User avatar

Joined: Wed Sep 21, 2011 9:11 am
Posts: 606
Location: France
Thank you Demivec. Cool effect +++

_________________

➽ Windows 10 - PB 5.62 & PB 5.70 LTS

Sorry for my bad english and the Dunning–Kruger effect.


Top
 Profile  
Reply with quote  
 Post subject: Re: Fog of war
PostPosted: Fri May 20, 2016 1:12 am 
Offline
Addict
Addict
User avatar

Joined: Mon Jul 25, 2005 3:51 pm
Posts: 3585
Location: Utah, USA
Thanks for the positive comments regarding the Fog Test code. :)

I made some minor corrections and documentation updates to code.

Added the ability to reset the fog and I also added code to use a box shape instead of a circle area to clear the fog. Two lines have to be commented/uncommented in the code to make the change.

_________________
Image


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 11 posts ] 

All times are UTC + 1 hour


Who is online

Users browsing this forum: No registered users and 1 guest


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
Jump to:  

 


Powered by phpBB © 2008 phpBB Group
subSilver+ theme by Canver Software, sponsor Sanal Modifiye