BabylonJS and Blazor - Lanterns
For this article we will go over Lanterns implemented in C#, here we load in the lanterns models and place them around the map, this step did not have any major differences so checkout the Demo and Source Code below!
Next Article in the works, follow me on Twitter @CodyAnhorn to get notified when I publish it!
Demo and Source Code
Below is a demo of the Blazor application after Step 8 is implemented, you can open the demo in a new tab by going to BabylonJS Blazor Step 08.
Use the Arrow keys to move the around the map. Space key to Jump and the Shift key to Dash while in the Air. Jump into the Lanterns to see them light up!
You can see the full source code in GitHub: canhorn/BabylonJS.Blazor.Game.Tutorial at step/08_Lanterns
If you want to see the original step in the Series: Lanterns | Babylon.js Documentation (babylonjs.com)
Implementation Overview
As part of the GameEnvironment.Load
the Lantern mesh is now loaded, there are 22 lanterns spread across the map. Using the Mesh.clone function we are able take the loaded Lantern mesh we are able to easily create copies that can be be placed around the map. Using the loaded Environment we look for a TransformNode with the name of "lantern {i}"
, we can then use this Node to set the Lanterns Position.
To give the Lantern a unique paper like look we are using the PBRMetallicRoughnessMaterial
, we give this material a Texture
and emissiveColor
adding to the Lanterns aesthetics. We pass this Light Material into each lanterns encapsulated instance, when the Lantern is lit it will set its Mesh.material
property. When the Lantern is lit it will create a PointLight that will only illuminate any meshes in an area around it based on a Mesh created during the instantiation of the Lantern.
To light a Lantern the Player needs to intersect with the Lantern Mesh, this is done by attaching Code Actions to the Player.Mesh.actionManager
. The trigger for the action is ActionManager.OnInstersectionEnterTrigger
and the parameter is the lantern.Mesh
. During the intersection the player will have two distinct action taken, checking the lantern is lit or not. The first action checked against the Player is the lantern lit state and the Players sparkler state, if both are true the Players.LanterLit
will go up by one, the lantern will be lit, and the Sparkler timer will be reset. If the first action does not pass the next action will check if the lantern intersected with was lit, if it was then the Player Sparkler timer will be reset.
Source Code Example(s)
With this code sample we are importing the Lanterns and placing them on the level.
// --- Part of GameEnvironment.Load Method
// Load Lanterns Mesh
var lanternResult = await SceneLoader.ImportMeshAsync(
"",
"./models/",
"lantern.glb",
_scene
);
// Extract the actual lanterns mesh from the root of the mesh that is imported
var lantern = lanternResult.meshes[0].getChildren()[0];
// Removing the parent helps with positioning clones
lantern.parent = null;
// --- Part of GameEnvironment.LoadAsset Method
// Original mesh is not visible
assets.Lantern.isVisible = false;
// A transform node to hold all lanterns
var lanternHolder = new TransformNode(
"lanternHolder",
_scene,
true
);
// We have 22 lanterns on the map, this creates those Lanterns.
for (int i = 0; i < 22; i++)
{
// We clone the lantern mesh
var lanternInstance = assets.Lantern.clone(
$"lantern{i}",
lanternHolder
);
lanternInstance.isVisible = true;
lanternInstance.setParent(lanternHolder);
// Create new Lantern
var newLantern = new Lantern(
_lightMaterial,
lanternInstance,
_scene,
// Position the Lantern based on a node loaded by the Environment
assets.Env.getChildTransformNodes(false)
.FirstOrDefault(a => a.name == $"lantern {i}")
.getAbsolutePosition()
);
_lanternObjs.Add(
newLantern
);
}
Here we are registering the IntersectionEnterTrigger
action on the lantern, so when the player interacts with a lantern and it is not lit it will lite the lantern.
player.Mesh.actionManager.registerAction(
new ExecuteCodeAction(
new
{
trigger = ActionManager.OnIntersectionEnterTrigger,
parameter = lantern.Mesh
},
new EventHorizon.Blazor.Interop.Callbacks.ActionCallback<ActionEvent>(
_ =>
{
if (!lantern.IsLit
&& player.SparkLit
)
{
// Increment the lantern count
player.LanternsLit += 1;
// Light up the lantern
lantern.SetEmissiveTexture();
// reset the Sparkler
player.SparkRest = true;
player.SparkLit = true;
}
// If the lantern is lit already, reset the Sparkler
else if (lantern.IsLit)
{
player.SparkRest = true;
player.SparkLit = true;
}
return Task.CompletedTask;
}
)
)
);
This code creates a PBRMetallicRoughnessMaterial
assigning it a Texture
and emissiveColor
for our Lanterns to give them a really fancy look.
// Create emissive material for when a Lantern is lit
var lightMaterial = new PBRMetallicRoughnessMaterial(
"lantern-mesh-light",
_scene
);
lightMaterial.emissiveTexture = new Texture(
_scene,
"/textures/litLantern.png",
true,
false
);
lightMaterial.emissiveColor = new Color3(
0.8784313725490196m,
0.7568627450980392m,
0.6235294117647059m
);