[SOLVED] UI Button to manipulate animation

I have been pouring through tutorials and example scenes but still can’t get my 2D button to work. I do however have my keyboard working.

I have an animation that plays in reverse and I want to be able to hit a UI button and have it play forward (I got it to work using the ‘space’ button)

I created a 2D UI button but can’t seem to replicate the same functionality as my keyboard press. Any help would be appreciated.


I attempted this script for my button ( I wasn’t sure if I had to add my animation to this script - I have another script for my animation/keyboard interaction - or do I attach my other script to the UI button entity?)

var BtnClick = pc.createScript('btnClick');

BtnClick.attributes.add('buttonEntity', {type: 'entity'});
// BtnClick.attributes.add('animationEntity', {type: 'entity'});

// initialize code called once per entity
BtnClick.prototype.initialize = function() {
    this.button = this.buttonEntity.button;
    // this.anim = this.entity.animation.getAnimation('CapstoneAnimation2.json');
    this.buttonPressed = false;
    this.button.on('mousedown', this.onMouseDown, this);
    this.button.on('mouseup', this.onMouseUp, this);
    this.entity.on('destroy', function() {
        this.button.off('mousedown', this.onMouseDown, this);
        this.button.off('mouseup', this.onMouseUp, this);
    // Register the mouse down and touch start event so we know when the user has clicked
    this.app.mouse.on(pc.EVENT_MOUSEDOWN, this.onMouseDown, this);
    if (this.app.touch) {
        this.app.touch.on(pc.EVENT_TOUCHSTART, this.onTouchStart, this);

// update code called every frame
BtnClick.prototype.update = function(dt) {
    if (this.buttonPressed) {
        this.entity.animation.speed = 0.7;
        alert("this is working");

BtnClick.prototype.onMouseDown = function(event) {
    if (this.buttonPressed) {
        this.entity.animation.speed = 0.7;
        alert("this is working");

BtnClick.prototype.onMouseDown = function() {
    this.buttonPressed = true;
    alert("this is working");

BtnClick.prototype.onMouseUp = function() {
    this.buttonPressed = false;

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

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

Looks like you were pretty close. Here is a fixed version using the pressedStart and pressedEnd events: https://playcanvas.com/editor/scene/851686

1 Like

That is so awesome, thanks yaustar! I’m still wrapping my head around Javascript, but what does the destroy function accomplish exactly?

If I want for text to be triggered at certain points of the animation do you recommend I write a separate script for that? It would be different text in the same point of the animation depending on whether it’s playing backwards or forwards (button being pressed or not).

I would think I would use the btn_clicked script and just add an switch statement with the cases being
the point of the animation…ie

this.entity.animation.currentTime = this.anim.duration * 0.6; would show text “hello”
this.entity.animation.currentTime = this.anim.duration * 0.65; would show text “world”

am I on the right path?

Since you’ll probably be checking this in the update() portion of your script, you may need to bracket your target time with >= and <= than with an === (note: you want to use == or ===, not =). There are discrete intervals between each execution of update() and it is not likely that the anim.duration will be exactly the value you are looking for at that time.

this.entity.animation.currentTime >= this.anim.duration * 0.59 && this.entity.animation.currentTime <= this.anim.duration * 0.649 || ; would show text “world”

You’d have to do some testing (console.log) to see what values are necessary in order to trap the right animation times.

1 Like

Listeners aren’t automatically cleaned up so destroying an entity without unsubscribing to events can lead to crashes as the object no longer exists when the callback is called.

Therefore, we listen to the destroy event on the entity and remove the subscribers on the button events.

That’s a tricky one as the animation system in PlayCanvas doesn’t support animation events yet so you probably have to do it be checking the currentTime on the animation component and triggering events/calling when it has reached that point: https://developer.playcanvas.com/en/api/pc.AnimationComponent.html#currentTime

Since you are going backwards as well, you need check for currentTime going forward pass the boundary and back as well.

1 Like

So, to better explain my previous post I made a modification to one of my animation test projects.

Click on the Forward box to run the timer forward and the Reverse box to run the animation in reverse. More clicks will increase the speed incrementally.

Anim Time shows the actual value for the animation time each time it is checked in the update() function. These values are seldom, if ever, a simple whole number.

The counter deals with this by being updated at discrete intervals using if/else if statements and the >= and < tests I mentioned previously.

I suppose you could also try using Math.round() as well.

The pertinent code is in pulse.js in this project.


1 Like

I really appreciate the help guys. I hope to be scripting ninjas like you someday.

I used @yaustar’s code on my original scene and it didn’t work. The only difference I see between the files is that my JS script has a dot next to the name which makes me think that my script is not connected to my scene which would explain why no matter what I would do I could not get my script to affect my scene.

Is there a step I’m missing to connect the two?


Did you add the script to your Ch36_nonPBR entity?


Yup, rookie mistake. Thanks @wturber. It’s working now. Time to start experimenting with my text appearances.

Cool. I just got into your project and found it working so was curious what was up.

I think it takes a while to get “ninja” status. I’m happy if I’m just a good student at this point.

1 Like

Was thinking about this more last night and got to fiddling. I updated my test project to include the select() method and to use Math.ceil() and/or Math.floor() instead of greater than and less than. Same scripts, same links, same results … just different ways to get there. Probably boring for those more experienced, but hopefully useful for some of us newbs. Pertinent stuff in pulse.js.


1 Like

great, thanks @wturber. I’ll play with this this upcoming week