Dynamic lighting
From VbGORE Visual Basic Online RPG Engine
[edit] Introduction
This tutorial will teach you how to add dynamic circular lights. These lights are calculated by a few variables, such as the size and intensity of the light, and modify the map's light values directly to present the light, allowing for "moving" lights such as a lantern for characters, or a fireball that emits a light.
[edit] Adding the code
First create a new module in GameClient.vbp by clicking "Project->Add Module->OK" in the Menu. Name the module "Lighting". Add the following code to the module:
Option Explicit Public LightX As Long Public LightY As Long Public Const LightSize As Single = 320 Public Const LightStrength As Single = 1 Public LightAddX As Long Public LightAddY As Long Public MapLight() As Long Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long) Private Function CalcLight(ByVal X As Long, ByVal Y As Long, ByVal Light As Long) As Long Dim ARGB(3) As Byte Dim Dist As Long Dim i As Long Dim k As Long Dim a As Long 'Find the distance of the verticy and light Dist = Distance(X, Y, LightX, LightY) 'Confirm the distance is valid If LightSize > Dist Then 'Find the ARGB value of the light CopyMemory ARGB(0), Light, 4 'Calculate the modifier value to the light once instead of for each color a = ((LightSize - Dist) * LightStrength) 'Update the RGB values For i = 0 To 2 'Calculate the new light value k = CLng(ARGB(i)) + a If k > 255 Then k = 255 If k < 0 Then k = 0 'Put the variable back now that we know it is in byte range ARGB(i) = k Next i 'Store the new light value CalcLight = D3DColorARGB(ARGB(3), ARGB(2), ARGB(1), ARGB(0)) Else 'Return the original value since the light is too far away from the location to affect it CalcLight = Light End If End Function Public Sub UpdateLight() Dim X As Long Dim Y As Long Dim i As Long Dim R As Long '*** Tiles *** 'Calculate each visual verticy once (to prevent duplicate calculations) For X = 1 To MapInfo.Width For Y = 1 To MapInfo.Height If X = MapInfo.Width And Y = MapInfo.Height Then R = MapData(X - 1, Y - 1).DefaultLight(4) ElseIf X = MapInfo.Width Then R = MapData(X - 1, Y).DefaultLight(2) ElseIf Y = MapInfo.Height Then R = MapData(X, Y - 1).DefaultLight(3) Else R = MapData(X, Y).DefaultLight(1) End If MapLight(X, Y) = CalcLight((X - LightAddX) * 32, (Y - LightAddY) * 32, R) Next Y Next X 'Apply the lights to the tiles For X = 1 To MapInfo.Width - 1 For Y = 1 To MapInfo.Height - 1 For i = 1 To 24 With MapData(X, Y) Select Case i Case 1: .Light(i) = MapLight(X, Y) Case 2: .Light(i) = MapLight(X + 1, Y) Case 3: .Light(i) = MapLight(X, Y + 1) Case 4: .Light(i) = MapLight(X + 1, Y + 1) Case 5: .Light(i) = MapLight(X, Y) Case 6: .Light(i) = MapLight(X + 1, Y) Case 7: .Light(i) = MapLight(X, Y + 1) Case 8: .Light(i) = MapLight(X + 1, Y + 1) Case 9: .Light(i) = MapLight(X, Y) Case 10: .Light(i) = MapLight(X + 1, Y) Case 11: .Light(i) = MapLight(X, Y + 1) Case 12: .Light(i) = MapLight(X + 1, Y + 1) Case 13: .Light(i) = MapLight(X, Y) Case 14: .Light(i) = MapLight(X + 1, Y) Case 15: .Light(i) = MapLight(X, Y + 1) Case 16: .Light(i) = MapLight(X + 1, Y + 1) Case 17: .Light(i) = MapLight(X, Y) Case 18: .Light(i) = MapLight(X + 1, Y) Case 19: .Light(i) = MapLight(X, Y + 1) Case 20: .Light(i) = MapLight(X + 1, Y + 1) Case 21: .Light(i) = MapLight(X, Y) Case 22: .Light(i) = MapLight(X + 1, Y) Case 23: .Light(i) = MapLight(X, Y + 1) Case 24: .Light(i) = MapLight(X + 1, Y + 1) Case Else: .Light(i) = MapLight(X, Y) End Select End With Next i Next Y Next X End Sub Public Sub SetAmbientLight(ByVal Color As Long) Dim X As Long Dim Y As Long Dim i As Long On Error GoTo 0 'Set the default light For X = 1 To MapInfo.Width For Y = 1 To MapInfo.Height With MapData(X, Y) For i = 1 To 24 .DefaultLight(i) = Color Next i End With Next Y Next X End Sub Private Function Distance(ByVal X1 As Long, ByVal Y1 As Long, ByVal X2 As Long, ByVal Y2 As Long) As Single Distance = Sqr(((X2 - X1) * (X2 - X1)) + ((Y2 - Y1) * (Y2 - Y1))) End Function
In frmMain's code, find:
'Move on X axis Case DIMOFS_X If Windowed Then OldMousePos = MousePos GetCursorPos MousePos MousePos.X = MousePos.X - (Me.Left \ Screen.TwipsPerPixelX) MousePos.Y = MousePos.Y - (Me.Top \ Screen.TwipsPerPixelY) MousePosAdd.X = -(OldMousePos.X - MousePos.X) MousePosAdd.Y = -(OldMousePos.Y - MousePos.Y) Else MousePosAdd.X = (DevData(LoopC).lData * MouseSpeed) MousePos.X = MousePos.X + MousePosAdd.X If MousePos.X < 0 Then MousePos.X = 0 If MousePos.X > frmMain.ScaleWidth Then MousePos.X = frmMain.ScaleWidth End If Moved = 1
After, add:
LightX = MousePos.X
Find:
MousePosAdd.Y = (DevData(LoopC).lData * MouseSpeed) MousePos.Y = MousePos.Y + MousePosAdd.Y If MousePos.Y < 0 Then MousePos.Y = 0 If MousePos.Y > ScreenHeight Then MousePos.Y = ScreenHeight End If Moved = 1
After, add:
LightY = MousePos.Y
In module "TileEngine", find:
'************** Update weather **************
Before, add:
'************** Dynamic lighting ************
UpdateLight
Find:
ReDim MapData(1 To MapInfo.Width, 1 To MapInfo.Height) As MapBlock
After, add:
ReDim MapLight(1 To MapInfo.Width, 1 To MapInfo.Height) As Long
Find:
'****** Update screen ******
After, add:
'Fixes problem with Lightcircle/Mousepos If UserPos.X > 14 Then LightAddX = UserPos.X - 14 Else LightAddX = 0 End If If UserPos.Y > 10 Then LightAddY = UserPos.Y - 10 Else LightAddY = 0 End If
Find:
'Cache the TileBufferOffset value to prevent always having to calculate it on the fly TileBufferOffset = ((10 - TileBufferSize) * 32)
After, add:
'Set the ambient light - you will probably want to set this in the map's file instead of directly in the code SetAmbientLight D3DColorARGB(255, 50, 50, 50)
Last thing would be to change some declarations. Find:
Public Type MapBlock
In that type, find:
Light(1 To 24) As Long
After, add:
DefaultLight(1 To 24) As Long
[edit] To-do
The following are additions that would be highly beneficial to this custom feature:
- Smarter updating: Only update the dynamic lights when they change, not every frame.
- Map-based ambient: The ambient value is saved in the map file, not set from a constant in the code.
- Map editor ambient support: The ambient light shown in the map editor, allowing mappers to see what the lighting looks like while developing.
- Map Specific: The lightening would not be shown on specific maps.



