In my last post I was talking about the difference between using Lumen or traditional lighting and why I'm interested in the traditional option.
In addition, I created a config scene Blueprint to control the "PostProcessVolume" and switch between Lumen and traditional lighting using Unreal Engine editor and in game.
Now I will go deeper in my lights Blueprints to take some important notes about inheritance and component instances because there are some confusing concepts that I want to clarify.
In software development this is a very important concept in Object Oriented Programming and Unreal Engine can apply it in Blueprints which it's very useful.
Imagine that you want to make ground vehicles of different types, for example a motorbike, a car and a bus, and you want to make their logic and properties. You could make the code for each vehicle type, but your program will become a hell because you are triplicating the necessary work to make them (and the necessary work to make improvements and changes later on these logics...)
Here is where you need inheritance. With inheritance you can specify a base object, for example a "ground vehicle", and derive from it all variations that you will need: the motorbike, the car and the bus.
In this base object "ground vehicle" we can specify some basic logics and variables, for example each ground vehicle can carry people, have a motor, can move around, has lights, etc. But each vehicle type can have some special variables and logics that wouldn't apply on the other vehicle types: for example, to ride a motorbike you must wear a helmet, a car and a bus has doors, the bus has a tickets validator machine, etc.
Then we have something like this:
- Ground vehicle -> can carry people, has a motor and lights that can turn on or off, can move around, etc.
Derived child objects:
- Motorbike -> must wear a helmet, can have a wheel lock, etc.
- Car -> has doors, has windows, etc.
- Bus -> has doors, has windows, has a tickets validator machine, etc.
How it works in Unreal Engine? Let's see an example with my lights system.
DIFFERENT TYPES OF LAMPS
I want a Blueprint to control a lamp in the scenery, but later I will need different kinds of lights. For now, I have three: a round lamp and a long lamp. The green lamp is the same than round one but changing its color, we will see it later.
The logic is the same for each one: I need to be able to switch the light and configure it to use Lumen or not.
Then I must be able to change the chassis of the lamp and its color and intensity to customize its light. In a future I will add options to make them blink and throw sparks.
As you can see in this diagram, I will have a Blueprint with the base lamp and two derived ones:
In "BP_Base_Lamp" will be almost all logic and properties and then in "BP_Protoype_LongLamp" and "BP_Prototype_PointLamp" there only will be some config logics and the mesh.
Every lamp will have:
- One "LightComponent" that will be the main light that has the lamp.
- One or more "LightComponents" that will be the ambient lights that projects every lamp light for the traditional lighting mode (Lumen will not need them).
- A "Mesh" that will be the light bulb with a special material that glows in the color of the main light.
To achieve this, it will also have these public properties:
- LightColor: a "LinearColor" variable that will have the color of the light.
- LightOnIntensity: a "Float" variable with the intensity that will have the light.
- LightOffIntensity: another "Float" variable with the intensity when the light is switched off. Usually, will be zero but maybe I will need some lamps that still working with low intensity when they are switched off to illuminate a bit a dark scenery.
- IsActive: a "Boolean" variable to know if this light is switched on or off. On the editor we can turn or not the light using this variable and see the result.
- HideAmbientLight: this is another "Boolean" and it will be useful only for testing purposes. It will hide all ambient lights on the traditional lighting mode to help me in the scenery illumination process.
Also, there will be some private variables that will be useful on the internal logic of this Blueprint, but we will take a look on them meanwhile we explore its logic.
BASE LAMP INITIALIZATION
First, we need the base lamp that will hold the main logic and variables: our base Blueprint called "BP_Base_Lamp".
Inside we won't make anything on the Viewport as this Blueprint don't need any mesh.
We need all these variables:
It's important to make them public or private as we will only see the public ones on the child Blueprint and in our case, we only need to make public the config variables to customize the child Blueprints that we will use in the level.
Then we need some functions that we will call inside our lamp logic. First of all, the "InitializeLamp" function:
This function receives three parameters:
- A "Mesh" called bulb that will be the light bulb where to find the special glowing material.
- A main light component called "MainLight"
- A collection of lights components called "AmbientLights".
We need this function to initialize the child Blueprint. The first is to set these parameters into private variables as we will need to retrieve them in some points inside the logic of this lamp.
It's important to clarify that these parameters will receive a reference to objects that exists in game, but we are working with an abstract element that really don't have them. We need living instances of this bulb and light components to play with its properties affecting the world around them, to achieve it we need to initialize them from outside in the child Blueprint constructor because is where these elements get alive, and we can assign them inside the base Blueprint using this function.
Then we get the bulb mesh and from its material we make a "DynamicMaterialInstance" and save it in a private variable to use later, this material has two properties which we can play with its color and glow.
Next step is to set the lamp properties, this is a function that we will develop later in which we set the color and intensity of the light of the lamp. If the lamp is now active, we set the "LightOnIntensity", elsewhere we will set "LightOffIntensity".
And the final step is to get the "BP_Scene_Config" from the level and config Lumen usage in this lamp. To config Lumen we use another function that you can see below called "ConfigLumen":
"ConfigLumen" receives a Boolean to know if its active or not. Then there is a "ForEachLoop" in which we deactivate every ambient light of the lamp if Lumen is active or vice versa.
Now we have the lamp configured. This logic works every time that we change a variable in Unreal Engine editor or at the game startup. Later we will see where to launch the "InitializeLamp" function.
One more thing about initialization: on the "BP_Scene_Config" we have two events to set Lumen on or off, and in the "EventGraph" of the "BP_Base_Lamp" we use them to update the Lumen configuration, it helps to update the Lumen state foreach lamp in game.
SET LAMP AND LIGHT PROPERTIES
To set the lamp properties we have a function called "SetLampProperties".
First, we set some local variables to modify this light. A local variable is a variable that only exists inside a function. In this case we set "ActualColor" and "ActualIntensity" to work with them easily inside this function.
We set the light properties of the "MainLight" using the private variable that we set up earlier in the "InitializationLamp". Here we use another custom function called "SetLightProperties" where we set the light color and intensity, in this case our actual color and intensity from our local variable.
Then we set the bulb material to glow with our actual color and a little fraction of our actual light intensity. To achieve it we use the "SetVectorParameterValue" for the light color and "SetScalarParameterValue" for the intensity over our "LightBulbMaterial" private variable that we set earlier in our "InitializeLamp" function. This requires a special material with parameters to set the color and intensity of its glow.
Then we apply the light properties over all ambient lights using the same "SetLightProperties" function but dividing the intensity because the ambient light glows less than the main light. In case that we are using Lumen or we want to hide these lights we set the intensity to zero. We have all ambient lights in an array in our private variables thanks to the "InitializationLamp" function.
The "SetLightProperties" function is this one which you can see below:
It's simpler than it appears. If the light intensity is greater than zero then we set the color and intensity and we set this light as visible, elsewhere we hide this light, so we don't want to make work Unreal showing lights that are useless.
Earlier in this post we have talked about a special material that glows. This is it: the "M_BulbLight" material.
It's pretty simple. We make a material with two parameters called "LightColor" and "LightIntensity" which configures the glowing effect dynamically in game and in the Unreal Engine editor. From this material we make an instance material which is the one that we will use on our bulb mesh in our lamps.
Finally, we can do different lamps using our base "BP_Base_Lamp". In the Blueprints editor we can setup here the base class to inherit all the stuff: functions, variables, meshes, etc:
So, in the child lamp Blueprints we will set the same "BP_Base_Lamp" and then we will use the magic of inheritance to set up two different lamps.
In the "BP_Prototype_LongLamp" I have done a cube mesh with a rectangular form and another one littler inside with the bulb material "M_BulbLight". To make the ambient lights I put three points lights and for the main light I put a rectangular light. Then I could setup the variables for the color and intensity, but I let them in their default values.
The important thing is that we use the constructor to initialize the lamp, here is where we call the famous "InitializeLamp" function.
As you can see, we set the lights and the bulb through the "InitializeLamp" function because here we have the instances of these elements.
And for the round lamp something similar but with a different mesh and less ambient lights:
I can make more variations, this one is just changing the light color when we instantiates a "BP_Prototype_PointLamp" in the level:
And the result is what you see at the top of this post: