Fog of war
Fog of war
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 :
without fog of war
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.
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 :
without fog of war
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.
Re: Fog of war
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.
Re: Fog of war
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.
Re: Fog of war
Nice idea !#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.
No need of complicated mathematics
Re: Fog of war
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
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
Re: Fog of war
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.
Re: Fog of war
Here's an example using the method of overdrawing the map with a completely separate sprite containing 'the fog':
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.
Code: Select all
;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
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.
Last edited by Demivec on Fri May 20, 2016 1:10 am, edited 1 time in total.
-
- Addict
- Posts: 4519
- Joined: Thu Jun 07, 2007 3:25 pm
- Location: Berlin, Germany
- electrochrisso
- Addict
- Posts: 980
- Joined: Mon May 14, 2007 2:13 am
- Location: Darling River
Re: Fog of war
Thank you Demivec. Cool effect +++
➽ Windows 11 64-bit - PB 6.0 x64 - AMD Ryzen 7 - NVIDIA GeForce GTX 1650 Ti
Sorry for my bad english and the Dunning–Kruger effect.
Re: Fog of war
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.
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.