Here's an example using the method of overdrawing the map with a completely separate sprite containing 'the fog':
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
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.