Enable and disable entities with a button

Hello friends, I need to make some buttons work that allow me to enable and disable entities. I share the project, I have not yet implemented any code on the buttons. I don’t even know where to start. Someone could help me or share a project from which to inspire me. Thanks for your help.

https://playcanvas.com/editor/scene/965478


There can be different approaches to do this. Let me tell you a simpler one. Firstly find reference of the button you need to act on in initialize function. In this case:

this.layoutABtn= this.app.root.findByName("Layout A");

Now in the initialize add the event to the button and now when button will be pressed, the attached function will be called

this.layoutABtn.element.on('mouseup', this.onLayoutABtnPressed, this);

So, now you have entered into the function, just get reference of the box you want to disable and simply disable it.

1 Like

Hello @saif , Thanks. Something like that? Sorry for my no coding ability:

var ChangeLayouts = pc.createScript(‘changeLayouts’);

// initialize code called once per entity
this.layoutABtn= this.app.root.findByName(“Layout A”);

// update code called every frame
this.layoutABtn.element.on(‘mouseup’, this.onLayoutABtnPressed, this);

// swap method called for script hot-reloading
// inherit your script state here
// ChangeLayouts.prototype.swap = function(old) { };

// to learn more about script anatomy, please read:
// http://developer.playcanvas.com/en/user-manual/scripting/

I add to proyect saif

First you need to create a new script and attach it to any object in the editor. After creating the script, there will be two functions pre defined by the name initiliaze and update. Add the relevant code in initilaize function and make a new function with named “onLayoutABtnPressed” with same way as update function is written.
You need to see some tutorial to have more idea about scripting.
Here is an example
https://developer.playcanvas.com/en/tutorials/using-events-with-scripts/

1 Like

I’ve created something like this and applied it to the object but it doesn’t seem to work:

// More information about events can be found here
// http://developer.playcanvas.com/en/api/pc.events.html
var EventReceiver = pc.createScript(‘changeLayouts’);

// initialize code called once per entity
EventReceiver.prototype.initialize = function() {
// Listen for the ‘changePosition’ event so when it is fired, call
// the function onChangePositionEvent
this.layoutABtn= this.app.root.findByName(“Layout A”);
};

EventReceiver.prototype.onChangePositionEvent = function() {
this.layoutABtn.element.on(‘mouseup’, this.onLayoutABtnPressed, this);
};

Try using code highlighter option so that we can understand and differentiate between the code easily.
You need to attach event to the function in initialize like this

EventReceiver.prototype.initialize = function() {


this.layoutABtn= this.app.root.findByName(“Layout A”);
this.entityToDisable = this.app.root.findByName(“entityToDisable”);

this.layoutABtn.element.on(‘mouseup’, this.onLayoutABtnPressed, this);
};

Now as you have attach an event to “onLayoutABtnPressed” function so it must be there in your code. Now add the function with the name attach.

EventReceiver.prototype.onLayoutABtnPressed= function() {
this.entityToDisable.enabled = false;
};

Here “entityToDisable” is a entity you want to disable. In your case it will be a box. Just write its exact name and it should work.

Hi @saif I can’t get the code highlighter option to work, sorry
I am trying to understand the tutorial that you have recommended, but I can’t seem to combine what you explain to me and what the tutorial teaches, I am frustrated.

,

//

var EventReceiver = pc.createScript(‘changeLayouts’);

EventReceiver.prototype.initialize = function() {

this.layoutABtn= this.app.root.findByName(“Layout A”);

this.entityToDisable = this.app.root.findByName(“entityToDisable”);

this.layoutABtn.element.on(‘mouseup’, this.onLayoutABtnPressed, this);

};

EventReceiver.prototype.onLayoutABtnPressed= function() {
this.Layout A_One Box.enabled = false;
};

,

Your names can’t have spaces.

Also, your citation marks are not the default and are not working. The text should turn orange when they work.

Not sure why it is giving the first error though.

Ok, thanks @guzu I will try to fix this.

Now it seems to have improved, but I’m still confused with the last mistake, I’m not sure what’s happening

click captura 03

// More information about events can be found here
// http://developer.playcanvas.com/en/api/pc.events.html
var EventReceiver = pc.createScript(‘changeLayouts’);

EventReceiver.prototype.initialize = function() {

this.layoutABtn = this.app.root.findByName(‘Layout A’);

this.entityToDisable = this.app.root.findByName(‘entityToDisable’);

this.layoutABtn.element.on(‘mouseup’, this.onLayoutABtnPressed, this);

};

EventReceiver.prototype.onLayoutABtnPressed= function() {
this.Layout A_One Box.enabled = false;
};

To create a variable in Javascript, you use a keyword var before the variable name:

var myVariable = 0;

Here you created a variable named myVariable and assigned a zero to it. Without going into details, variables will get destroyed after function exits. As you might have noticed, Playcanvas scripts have methods, which are simple functions:

EventReceiver.prototype.initialize = function() {};
EventReceiver.prototype.myMethod = function() {};
EventReceiver.prototype.myOtherOne = function() {};
EventReceiver.prototype.update = function(dt) {};

The initialize() and update() methods are special in a way that Playcanvas will execute them automatically at the appropriate times. You can safely remove them, or write your own contents inside.

  • .initialize() method will be executed by Playcanvas engine once, when it loads the script. You want to put here all your initialization stuff, like finding and creating entities, ui elements, etc. and storing them in variables, so the script can use them later.
  • .update(dt) method will be run by Playcanvas every time it wants to draw a new frame on the screen. If you have something that moves, you want to update its positions iniside this method.

Based on this knowledge, you should know where to create your variable. However, the other methods of the script will not be able to use it, because once the function ends, the variable is destroyed:

EventReceiver.prototype.initialize = function() {
  var myVariable = 0;
};
EventReceiver.prototype.myMethod = function() {
  console.log(myVariable);  // undefined
};

This is where the keyword this comes to the resque. I won’t go into details for what it actually means, but to make it simple, you use it to “attach” a variable to the script.

this.myVariable = 0;  // no var keyword

Now, you can access myVariable from any other method in that script:

EventReceiver.prototype.initialize = function() {
  this.myVariable = 0;
};
EventReceiver.prototype.myMethod = function() {
  console.log(this.myVariable);  // 0
};

So, when you write the following:
this.Layout A_One Box.enabled = false;
I read it as you mean "I want to disable the entity that has a name Layout A_One Box". Break your task into smaller steps:

  1. Find the entity and store it in a variable for future operations on it.
  2. Disable it when the conditions are met.

To solve the first one, you should already have the knowledge where it should take place, but here is how:

EventReceiver.prototype.initialize = function() {
  this.myLayoutA = this.app.root.findByName('Layout A_One Box');
};

You should know what this.myLayoutA means now. It simply creates a variable and attaches it to the script. Whats the value that it assigns?

this.app.root.findByName('Layout A_One Box');

When your application starts, the Playcanvas engine will create a variable app and store itself in it. Then when you create a script, will automatically attach it to your script, so your scripts can have a convenient access to it via this.app. The app has a graph node called root, which apart from other things allows to search for your scene entities via its method findByName(). You need to pass the entity name to it, so the engine can run through your scene and find a match. Once it does, it stores it in your variable myLayoutA.

And now the second part of the puzzle should be easier:

EventReceiver.prototype.onLayoutABtnPressed = function() {
  this.myLayoutA.enabled = false;
};

You access the entity that is stored in the variable this.myLayoutA, and use its property enabled to disable it.

Another thing, make sure the single and double quotation marks used in the script: ` and ". First one is usually in the top left of your keyboard, left from 1 key, the other one is near the Enter. Only those will tell a compiler that you are giving it a String type. There are different quotation mark symbols, based on the language and region settings of your computer. A good indication that the string didn’t convert into String type, is when you can visually see it didn’t change its color, and there is a yellow warning on the side of the editor.

3 Likes

Fantastic explanation @LeXXik, I will read everything carefully and try to apply what you explain. Thank you very much.

1 Like

Hello @LeXXik, I’m trying to get the script to work, I’ve seen your guide and created a new script but it doesn’t seem to work.
I’m also not sure where I should add the script: to the object? to the entity? to the button? it doesn’t seem to work on either.
Excuse my poor ability, I am taking my first steps.

click captura 04

// More information about events can be found here
// http://developer.playcanvas.com/en/api/pc.events.html
var EventReceiver = pc.createScript(‘changeLayouts’);

EventReceiver.prototype.initialize = function() {
this.myLayoutA = this.app.root.findByName(‘Layout A_One Box’);
};

EventReceiver.prototype.onLayoutABtnPressed = function() {
this.myLayoutA.enabled = false;
};

Your code is fine. You followed my guide correctly, young obi-wan.

Now, your script has 2 methods initialize() and onLayoutABtnPressed(). As I mentioned earlier, when your application runs, the engine will automatically execute the initialize() method when it loads your script. But when will the other method be executed? The engine has no idea of it and just leaves it up to you, a developer, who knows better when it should be run.

You already know the answer to that - it should be run when the button is pressed, obviously. We just need to tell the engine that, so it knows it too. How do we do this? How do we tell the engine to do something when a button is clicked?

Playcavas has a powerful event system, which allows different scripts and entities to send events. Any other script that listens for that event will catch it. There are different ways to do this, as @saif mentioned, but we can use his proposed method, to make it simple.

To use an event system, you need two parties - one that sends an event, and the other that listens for it. Like a dialogue between two people. There could actually be many listeners, like a teacher in an auditorium full of students. In our case the first party is a button, and the second one is our script. When the button sends an event, our script listens for it, and when hears it, does something. Se, let’s break it into simple tasks:

  1. Make the button send an event.
  2. Listen for it in our script.
  3. Run our method when we hear the event.

Starting with the first one. Well, we actually don’t need to do anything for this step! Because we have chosen a simplified method, we will just rely on the default button behavior. When a button recognizes that it has been clicked, it will fire related events automatically. It will call them like mouseup, mousedown, etc, depending on the event. You can find other events in the API docs.

Great. We now know that the button will fire an event for us, that we can use. @saif proposed to use mouseup event, which will be sent (or fired) when the mouse button is released after the press. Now, we get to our second task - that is to tell our script to listen for it. The syntax is following:

myButton.element.on(name, function, scope);

Let’s break down:

  • myButton is the button element we want to listen to
  • .element is the screen compoenent of that button. Pretty much all your 2D Screen elements have it
  • .on this is the actual event system we hook into (listen to)

What are those arguments?

  • name - this is a string name of the event we want to listen for
  • function is the name of the function we want to run when this event happens
  • scope just put this here, it should be fine in most cases, you can learn more about javascript classes and scopes on internet

Can you now identify in @saif post above where he does this? It’s simple really:

myButton.element.on(`mouseup`, onLayoutABtnPressed, this);

We want our script to start listening for the button press from the moment our script starts. Can you guess where should we put this line then? I’m sure you guessed right - in the initialize() method. Since Playcanvas will execute this method once it loads the script, then the script will immediately start listening for the event.

EventReceiver.prototype.initialize = function() {
    myButton.element.on(`mouseup`, onLayoutABtnPressed, this);
    
    // your existing code here or before the event hook, doesn't matter
};

Can you see something wrong in the code? The engine doesn’t know what myButton is. We need to declare it and assign it our 2D Screen button. You should have a knowledge how to do it by now. I will leave it to you as an excercise.

EventReceiver.prototype.initialize = function() {
    var myButton = ?;
    myButton.element.on(`mouseup`, onLayoutABtnPressed, this);
    
    // your existing code here or before the event hook, doesn't matter
};

If you did it right, then it still won’t work :slight_smile: Why? Look at the event hook, you might see the issue. We tell it that we want to run onLayoutABtnPressed function, but there is no function with such name inside initialize() method. To allow methods within script to access each other, you should prefix them with this. You can learn about javascript classes on the internet. I also encourage you to go through some basic tutorials on Javascript to brush up your skills. Now, with this in mind, the initialize() will look something like this:

EventReceiver.prototype.initialize = function() {
    var myButton = ?;
    myButton.element.on(`mouseup`, this.onLayoutABtnPressed, this);
    
    // your existing code here or before the event hook, doesn't matter
};

You might ask, if you need to attach button to the script using this.button? It will depend on whether you want to access it from other methods or not. In our case we only need it temporarily to hook the event, and have no need in it afterwards, so there is no need.

The last task was to run our method when we hear the event. Which we actually already covered. We simply pass the name of the function to the event hook. Now, we have a script that will listen for the button event and do something when it happens. The last point is probably where to attach the script?

When your application starts, the engine will look into your scene graph (that is your tree of entities and elements on the left side of the Editor), and start traversing it from top to bottom. It will skip the ones that are disabled. If it sees that an entity has a script attached, it will search for its initialize() method and if finds one, executes it. Then moves on.

Giving that logic - it doesn’t matter where you attach your script, as long as you attach to something that is enabled from the beginning of your app life. You can even attach it to your light. The script is self-contained. What does it mean for script being self-contained? Is that even English? :slight_smile: I don’t know, I just came up with the term. Let me try to explain what it means:

When you attach the script to some entity, the engine will automatically add a reference to that entity inside the script in a variable called entity. For example, here is a pseudo-code that should be familiar to you:

var layout = this.app.root.findByName('myLayout');

When the script is run, it will search for an entity called myLayout and assign it to variable layout (some terminology sidestep: after you attach a variable to script with this it becomes a “property”, not “variable”). However, if you add this script to myLayout entity, then you can change the code to following:

var layout = this.entity;

Since engine automatically added that entity property for you to reference to the entity it is attached to, you can use it in your script. The good thing about this method is that no matter which entity you attach the script to, it will always refer to the one it is attached to. No searching needed. Neat. The downside though is that you tightly couple the entity and the script together. If you attach that script to another entity, it might do things with it you didn’t expect it to.

This is what I meant by script being self-contained. Our script searches all the entities itself, and does not depend on the one it is attached to. It is usually a good habbit to organize your scripts, however. You can add them to the entities that make sense to you or the design of your app.

As an example, you can create an empty entity in the root of your scene, call it “Logic” or something and attach all scripts to it. Once the engine gets to it while traversing the scene graph tree, it will go through all the scripts in it and run them. Then, if that entity has children with scripts, will run them too.

2 Likes

Hello @LeXXik, I still can’t get the script to work, I think I’m the worst student in the Jedi school, I think I’ve followed the steps well but it doesn’t seem to work.
The script I have applied to the 2d screen button, is this correct?

var EventReceiver = pc.createScript(‘changeLayouts’);

EventReceiver.prototype.initialize = function() {
var myLayoutA = 0;
myLayoutA.element.on(‘mouseup’, onLayoutABtnPressed, this);
this.myLayoutA = this.app.root.findByName(‘Layout A_One Box’);
};

EventReceiver.prototype.onLayoutABtnPressed = function() {
this.myLayoutA.enabled = false;
};

I hope I didn’t miss anything from your formidable explanation.

Nope, your homework didn’t go well :slight_smile: Here is the answer:

EventReceiver.prototype.initialize = function() {
    var myLayoutA = this.app.root.findByName('Layout A');
    myLayoutA.element.on(`mouseup`, this.onLayoutABtnPressed, this);
    
    // your existing code here or before the event hook, doesn't matter
};

You need to search for your button and save it to your myLayoutA variable. Only then, you can access its element property. What happens in your code now:

var myLayoutA = 0;
// remember school math? when you can substitute knowns in your equation?
myLayoutA.element
// we replace myLayoutA with 0
0.element....
// but 0 is just a primitive number, it has no properties so it won't work

Ok, @LeXXik The zero value is wrong, basically I replace the zero by “this.app.root.findByName (‘Layout A’);” with this I am assigning a function to the variable? It is right?

This is how the script runs, with errors

I expect to be close to success :rofl: :rofl:

Right, it just says that null has no element property, e.g. null.element. Remember the substitution? It means myLayoutA is null, which means it didn’t find a button with a name Layout A.

I just checked your project, the name is LayoutA, without space in it.

Is working @LeXXik !! Now I need the script to do other things, can you help me with this?

1 - I want the script to enable and disable the entity, now it only disables it but does not show it again when I press the button again.
2 - I want that when I press the button with two cubes, the single cube disappears and the two cubes appear.

My plan for the first point:

Should I create a function “this.myLayoutA.enabled = true;” ???

My plan for the second point:

I have to create a new variable “var myLayoutB = this.app.root.findByName (‘LayoutB’);” that tells the script to disable “Layout A_One Box”

What do you think of this, would it be the right way?

My next challenge, how do I tell the script that when there is a second click it changes to “true” and so on consecutively “true / false / true / false … to infinity”