Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

In WEBGL renderer, the mask on a group does not work if group.getBounds() is invoked #334

Closed
mindcity opened this issue Aug 27, 2017 · 8 comments
Labels
Milestone

Comments

@mindcity
Copy link

mindcity commented Aug 27, 2017

This Issue is about A bug in the API PIXI.DisplayObjectContainer.prototype.getBounds()

In Phaser.WEBGL renderer, the mask on a group does not work if group.getBounds() invoked
but it still works if you use Phaser.CANVAS renderer

please see online example:
https://phaser.io/sandbox/edit/ogYiKNXF

FYI: This issue is found when I try to use alignIn() to align a group with a mask inside, but the mask does not work. (because the method alignIn() invokes the getBounds() )

@samme samme changed the title There is probably an issue in getBounds() with WebGL In WEBGL renderer, the mask on a group does not work if group.getBounds() is invoked Aug 27, 2017
@jbpuryear
Copy link
Contributor

I found a couple ways to fix this. I'm not sure why they work, but here's my guess.

If you add group.mask.dirty = true after the call to mask.getBounds the mask works again. I think this is because you're not supposed to add the mask to the group that it acts on.

In the example the mask is created with group.mask = game.add.graphics(0, 0, group), which makes it a mask, but also adds it as a child of the group. Because of this, when you call getBounds on the group it also calls getBounds on the mask, which is setting the mask's dirty flag to false and keeping it from working like it's supposed to.

So the better fix is changing game.add.graphics(0, 0, group) to game.add.graphics(0, 0).

I don't think this is a bug. Masks just aren't meant to go on the display list.

@mindcity
Copy link
Author

It works, thanks, @jbpuryear

@samme
Copy link
Collaborator

samme commented Dec 18, 2017

Didn't notice that, thanks.

@sigseg1v
Copy link

sigseg1v commented Dec 30, 2017

So I have a mask which I want to position at the same place as a sprite. I call the following:

game.world.add(sprite);
game.world.add(mask);
sprite.mask = mask;

and it works fine, until anywhere else in code calls game.stage.getBounds(), at which point the mask stops working.

I think it's the same issue as expressed here.

Given that (correct me if I'm wrong), anything I want to display on the screen is going to be a child of the stage, and calling .getBounds() on something will cause masks on it's children to stop displaying, doesn't this mean that you can technically never mask anything if you also want to check the bounds of the stage with game.stage.getBounds()? Is that still working as intended?

Any ideas? Thanks!

Update:

My workaround for this was the following:

  1. Set mask.renderable = false;
  2. Change the code in Phaser.Graphics.prototype.getBounds
    from the existing:
//  Return an empty object if the item is a mask!
if (!this.renderable)
{
    return Phaser.EmptyRectangle;
}

to the following:

//  Return an empty object if the item is a mask!
if (!this.renderable)
{
    return PIXI.EmptyRectangle;
}

as it appears that Phaser.EmptyRectangle doesn't exist.
This method of setting renderable as false on the mask has some side effects however (it changes what happens when you change mask.scale). If you don't want those side effects, then the workaround supplied by @jbpuryear works as well (although not sure if at a performance cost?).

@samme
Copy link
Collaborator

samme commented Jan 2, 2018

It looks like masks are supposed to be in the display list, probably in game.world, as in sprites/mask.

If you have to use Stage#getBounds(), I guess you could mark the mask dirty each time.

I'd guess the

Return an empty object if the item is a mask!

block is never run because AFAIK Phaser never marks masks as non-renderable.

@griever989 I opened sprites/mask and ran game.stage.getBounds() in the console, and it didn't seem to break the mask. Maybe that's not exactly the same as your situation, though.

@Robominister
Copy link

I am encountering a version of this issue myself (with webgl of course)

I have a complicated animated sequence, with a lot of animation going on relating to group scale / width / height properties. Amongst this, is a group containing a mask. As the masked group is positioned dynamically (and we often want to animate it and move it around), using the display list as a simple way to scale the mask (as part of the group that it is masking) makes sense (we are making slot games, and also animating the content within the masked area).

The confusing thing is, it seems to be nothing to do with any scaling of the masked group (which also contains the mask) that breaks masking for webgl: its all the other content being rendered. When i remove all calls to setting scale, reading or writing to width / height properties, the mask started to work again. Its making it hard for me to find a working solution, as i have not yet been able to simplify my example down to a single line of code that would make it work / not work.

If this isn't classified as a bug, then the implication is you cannot use masks in phaser (or maybe pixi?) very flexibly, ie: you aren't able to add them as part of a group which is scaled or moved (not if you want to use webgl - it works fine for canvas, which adds to the confusion). This is a shame: its not clear to me from the documentation that this would be the case, and its different to every other display list library I have used. I also didn't find anything in the documentation (yet?) which explained whether i should or should not be adding masks manually to the display list. (the Phaser examples for masking are quite simple use cases). So i think both the documentation and examples could be much clearer about what are the supported uses of masks in Phaser.

@samme samme added this to the 2.11.0 milestone Apr 17, 2018
@samme
Copy link
Collaborator

samme commented Apr 17, 2018

I would favor making the change in #334 (comment) for v2.11.0 (https://codepen.io/samme/pen/aGoKpK). It may change the effects of some existing code, since masks will no longer be included in bounds calculations. Oddly, renderable doesn't seem to affect Graphics objects at all, so maybe that should change too.

@Robominister did you try setting mask.dirty after any bounds calculations (https://codepen.io/samme/pen/KRPRwL)? Reading the width or height of any Display Container (stage/world/group) implies a getBounds() call.

@samme samme reopened this Apr 17, 2018
@Robominister
Copy link

@samme
Thank you for the feedback and example. I will take a In the end, i removed all calls to width / height, and did some scaling manually, which solved the issue i was having. I will look at the mask.dirty option in the future.

We were trying to add tweens of the scale (or width / height) properties on the masked object, so if doing that, it looks like we might have to set dirty manually in an update function (as in your example) ? (Although we are using TimelineMax, i dont know off the top of my head if it actually reads the properties that it is tweening on an object during the tweens update).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants