How to create different "Pop-Ups" in Decentraland using a single Canvas
The mission
How to create different "Pop-Ups" in Decentraland using a single Canvas. This involves developing a system that enables easy control over, preventing them overlap or imbalance.
We'll asume that you have some basic knowledge of creating a basic Decentraland game in TypeScript and understanding how its engine and entities function. If you need more information about Decentraland, please refer to the following documentation.
We will use the basic Decentraland scene as an example and add cubes to it as launchers for our interface.
Box as activator for the UI
// create the entity
const cubeClic = new Entity()
// add a transform to the entity
cubeClic.addComponent(new Transform({ position: new Vector3(1, 1, 1) }))
// add a shape to the entity
cubeClic.addComponent(new BoxShape())
// add the entity to the engine
engine.addEntity(cubeClic)
// add click component to the box
cubeClick.addComponent(
new OnPointerDown(
(e) => {
//We will insert here the function "Show UI POP-UP"
log("Box clicked", e)
},
{
button: ActionButton.PRIMARY,
showFeedback: true,
hoverText: "Open UI",
}
)
)
Now that we have an activator, we can create our canvas. To demonstrate one of the benefits of having a Manager, we will display a simple welcome pop-up that we will hide. Then, when clicking on the box, we will show a second pop-up that will create a monitoring interface. When it reaches 10, it will hide and show us another pop-up indicating completion. For all of this, we will only use a single canvas, allowing us to conserve resources and control the flow of these messages in a simple manner.
Create the HUD Manager
HUD class for initialize all UI classes and control the canvas:
import { UIMessagge as MessageTest, UICounter} from "./uiTest"
var hud: HUD
//Create 1 HUD,
export function getHUD(){
if (!hud) {
spawnHUD()
}
return hud
}
function spawnHUD(){
hud = new HUD()
}
//Declare all UIs we will use here
export class HUD {
canvas: UICanvas
uiCounter: UICounter
messageTest: MessageTest
constructor(){
this.canvas = new UICanvas()
this.canvas.visible = true
this.uiCounter = new UICounter (this.canvas)
this.messageTest= new MessageTest (this.canvas)
}
hideAll(){
this.canvas.visible=false
}
}
To complete the process, we need to populate the empty functions that we created in our two UIs. Through the HUD Manager, we’ll have greater control over their status:
//Set confirmFunction to show counter
hud_test.messageTest.confirmFunction=()=>{
hud_test.uiCounter.show(true);
log("Clic Yes!");
}
//Set cancelFunction to show counter
hud_test.messageTest.cancelFunction=()=>{
log("Clic No!");
hud_test.uiCounter.show(false)
}
We will also require a second cube as an actuator for our counter:
//Material Red
const mRed = new Material()
mRed.albedoColor = Color3.Red()
//Create a new cube for inc Count
const cubeIncCount = new Entity();
cubeIncCount.addComponent(new Transform({ position: new Vector3(6, 1, 6) }));
cubeIncCount.addComponent(new BoxShape());
cubeIncCount.addComponent(mRed)
engine.addEntity(cubeIncCount);
cubeIncCount.addComponent(
new OnPointerDown(
() => {
hud_test.uiCounter.incCount()
log("Counter Box clicked");
},
{
button: ActionButton.PRIMARY,
showFeedback: true,
hoverText: "Inc counter UI",
}
)
);
As a result of all this, we will achieve a simple UI that displays a message asking whether or not we want to show a counter. All of this will be managed from a single canvas.
Results
As a result, we have obtained 4 different UI states:
Conclusion
To maintain order among the different UIs that may appear in a scene and to display them through a single canvas, it is necessary to create an intermediate class, such as the HUD class in our case.
Moreover, this class will enable us to interact with all of them simultaneously or independently, offering several advantages when developing a complex UI.