[SOLVED] Script logic seems to be dependent on the entity hierarchy

I have created a custom script that fades a popup in and out on a button click. When I add this script to more than 1 button in my scene the fading animation only works on the entity that is lowest in the hierarch.

I have 2 buttons; b1 and b2, where b1 is higher in the hierarchy, and 2 modals; m1 and m2. When I click b1 I expect m1 to fade in/out and when I click b2 I expect m2 to fade in/out. However b1 will just enable m1 and not do the fade action.

**To note this is all created on a 2D screen.

here is the script code:

var ModalAnimation = pc.createScript('modalAnimation');

ModalAnimation.attributes.add('Modal', {type: 'entity'});
ModalAnimation.attributes.add('BasicFade', {type: 'entity', array: true});
ModalAnimation.attributes.add('CloseButton', {type: 'entity'});
ModalAnimation.attributes.add('OpacityMultiplier', {type: 'number', default: 1.2});
ModalAnimation.attributes.add('LowestOpacity', {type: 'number', default: 0.1});
ModalAnimation.attributes.add('HighOpacity', {type: 'number', default: 0.97});

var fadeOutModalFlag = false;
var fadeInModalFlag = false;

var basicFade;
var modal;
var opacityMultiplier;
var lowestOpacity;
var highOpacity;

// initialize code called once per entity
//TODO: lock all buttons once popups are open
ModalAnimation.prototype.initialize = function() 
{
    basicFade = this.BasicFade;
    modal = this.Modal;
    opacityMultiplier = this.OpacityMultiplier;
    lowestOpacity = this.LowestOpacity;
    highOpacity = this.HighOpacity;

    this.entity.button.on('click', function()
    {
        if (this.Modal.enabled)
        {
            fadeOutModalFlag = true;
        }

        if(!this.Modal.enabled)
        {
            for(let i = 0; i < basicFade.length; i++)
            {
                basicFade[i].element.opacity = this.LowestOpacity;
            }
            this.Modal.enabled = true;
            fadeInModalFlag = true;
        }
    }, this);

    this.CloseButton.button.on('click', function()
    {
        this.Modal.enabled = false;
        
    }, this);
};


ModalAnimation.prototype.DetermineFade = function()
{
    if (this.Modal.enabled)
    {
        fadeOutModalFlag = true;
    }

    if(!this.Modal.enabled)
    {
        for(let i = 0; i < basicFade.length; i++)
        {
            basicFade[i].element.opacity = lowestOpacity;
        }
        this.Modal.enabled = true;
        fadeInModalFlag = true;
    }
};

ModalAnimation.prototype.FadeOutModal = setInterval(function()
{
    if(fadeOutModalFlag && !fadeInModalFlag)
    {
        var tempOpacity;
        for(let i = 0; i < basicFade.length; i++)
        {
            basicFade[i].element.opacity /= opacityMultiplier; 
            tempOpacity = basicFade[i].element.opacity;
        }

        if(tempOpacity < lowestOpacity)
        {
            modal.enabled = false;
            fadeOutModalFlag = false;
        }
    }
}, 10);

ModalAnimation.prototype.FadeInModal = setInterval(function()
{
    console.log()
    if(fadeInModalFlag && !fadeOutModalFlag)
    {
        var tempOpacity;
        for(let i = 0; i < basicFade.length; i++)
        {
            basicFade[i].element.opacity *= opacityMultiplier; 
            tempOpacity = basicFade[i].element.opacity;
        }

        if(tempOpacity >= highOpacity)
        {
            fadeInModalFlag = false;
            //reset
        }
    }
}, 10);

Hi @DeAunJohnson and welcome!

I suggest to debug your script with the console of your browser to find out what’s wrong. Maybe your global variables are not used correctly, since all buttons use the same variables.

console.log(fadeInModal);

I am a little confused as to how the script would still be working overall and show the correct modal, but not doing the animations?

I am not familiar with how PlayCanvas sets up it’s entities but why would a script object on 2 different entities have references that are scrambled? I am expressly setting each individual entity that I want to fade (stored in the basicFade attribute).

Since your fade variables are global, the same variables will be used by all buttons. Is this as intended?

lol nope, just started js a few weeks ago. I need them scoped to the file not global.

In that case you need to move them to the initialize function and use them like below.

this.fadeInModal = true;
if (this.fadeInModal) {

}

But the this keyword does not work on setInterval functions

ModalAnimation.prototype.FadeInModal = setInterval(function() {

}.bind(this), 10);

Ok I have them initialized like so in the initialize function

this.fadeInModalFlag = false;
    this.fadeOutModalFlag = false;

Then my fade function like so:

ModalAnimation.prototype.FadeInModal = setInterval(function()
{
    if(this.fadeInModalFlag && !this.fadeOutModalFlag)
    {
        var tempOpacity;
        for(let i = 0; i < this.basicFade.length; i++)
        {
            this.basicFade[i].element.opacity *= opacityMultiplier; 
            tempOpacity = this.basicFade[i].element.opacity;
        }

        if(tempOpacity >= this.highOpacity)
        {
            this.fadeInModalFlag = false;
            //reset
        }
    }
}.bind(this), 10);

but the flags in the fade function are undefined

Looks correct to me. Can you share the error or a link of your project please?

Do I need to publish the project to share it?

No, you can share the editor link of your project.

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

Here is a mock project with the issue

I don’t get any errors.

Its not giving errors but if you put breakpoints in at line 59 or 78 and push one of the buttons, it wont execute the logic in the if statement because the this.flag is undefined

On button click the modal ui should fade in to at least .97 opacity, it is not

Sorry, I’m not familiar with how you created the interval function, so I’m not sure why it’s not defined there either.

Maybe the topic below can help you.

Alternatively, you can use a tween to fade in and out your elements. (For this, you need to add the tween.js script to your project).

https://developer.playcanvas.com/en/tutorials/tweening/

Below example code from one of my own projects.

var FadeIn = pc.createScript('fadeIn');

FadeIn.prototype.initialize = function() {
	this.entity.enabled = true;

	this.elements = this.entity.findComponents("element");

	this.elements.forEach(function(element) {
		element.opacity = 0;
	}.bind(this));

	this.opacity = {value: 0};

	this.opacityTween = this.entity.tween(this.opacity).to({value: 1}, 3, pc.Linear).on('update', this.onFade, this);

    this.opacityTween.start();
};

FadeIn.prototype.onFade = function() {
	this.elements.forEach(function(element) {
		element.opacity = this.opacity.value;
	}.bind(this));

	if (this.opacity.value == 1) {
		this.app.timeScale = 0;
	}
};
1 Like

Changing the interval notation worked. Thank you

For future ref:

this.myInterval = setInterval(this.FadeInModal.bind(this),500);

...---

ModalAnimation.prototype.FadeInModal = function(){
    var tempOpacity;
        for(let i = 0; i < this.basicFade.length; i++)
        {
            this.basicFade[i].element.opacity *= this.opacityMultiplier; 
            tempOpacity = this.basicFade[i].element.opacity;
        }

        if(tempOpacity >= this.highOpacity)
        {
            this.fadeInModalFlag = false;
            clearInterval(this.myInterval);
            //reset
        }
};
1 Like