Dynamic lighting

From VbGORE Visual Basic Online RPG Engine

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.

Lightcircle.jpg

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:

<vb> 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 </vb>

In frmMain's code, find:

<vb> '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

</vb>

After, add:

<vb> LightX = MousePos.X </vb>

Find:

<vb> 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

</vb>

After, add:

<vb> LightY = MousePos.Y </vb>

In module "TileEngine", find:

<vb> '************** Update weather ************** </vb>

Before, add:

<vb> '************** Dynamic lighting ************

   UpdateLight

</vb>

Find:

<vb> ReDim MapData(1 To MapInfo.Width, 1 To MapInfo.Height) As MapBlock </vb>

After, add:

<vb> ReDim MapLight(1 To MapInfo.Width, 1 To MapInfo.Height) As Long </vb>

Find:

<vb> '****** Update screen ****** </vb>

After, add:

<vb> '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

</vb>

Find:

<vb>

   'Cache the TileBufferOffset value to prevent always having to calculate it on the fly
   TileBufferOffset = ((10 - TileBufferSize) * 32)

</vb>

After, add:

<vb>

   '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)

</vb>

Last thing would be to change some declarations. Find:

<vb> Public Type MapBlock </vb>

In that type, find:

<vb>

   Light(1 To 24) As Long

</vb>

After, add:

<vb>

   DefaultLight(1 To 24) As Long

</vb>

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.
Personal tools