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:
- Make the button send an event.
- Listen for it in our script.
- 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
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?
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.