pixijs / layers

Separate the z-hierarchy of your scene tree from its canonical structure.
https://pixijs.io/layers/docs/
MIT License
220 stars 57 forks source link

I've read through all the documentation / examples and still don't understand how to use this plugin. #79

Closed jt0dd closed 2 years ago

jt0dd commented 2 years ago

Maybe it's just me but I don't find that the Usage section or documentation is very intuitive. Perhaps it needs clarification or I'm just dense. I'm using PIXIJS 6.2.1.

Here's the specific things I found confusing:

1) Readme: "Nothing will work if you dont create Stage and set it as root. Please do it or read full explanation."

My thought: Ok but doesn't the standard app (const app = new PIXI.Application();) have a Stage attached as app.stage by default? So does this mean I need to create a new one, like app.stage = new PIXI.display.Stage() or what? This doesn't even work, I get the error: Uncaught TypeError: Cannot read properties of undefined (reading 'Stage')

2) Readme:

import { Layer } from '@pixi/layers'

let layer = new Layer();

A DisplayObject/Container can be rendered inside its layer instead of direct parent

bunnySprite.parentLayer = layer;

My thought: Ok then all I have to do is create a single global layer and assign a reference to it on every sprite in my scene like sprite.parentLayer = layer. Then, according to the readme I should set layer.group.enableSort = true; and then I can just do:

layer.group.on('sort', function onWillSort(sprite) {
    sprite.zOrder = sprite.y 
});

So I tried that and nothing happened. My app didn't break, but onWillSort didn't get triggered (I added a console log inside to check).

3) So at this point I'm looking through all the examples for references to a complete set of working code, so I open up https://pixijs.io/examples/#/plugin-layers/zorder.js

My thought: But wait, none of the code in here references anything the Readme's Usage section showed. new Layer() isn't called anywhere, there's not even an import of '@pixi/layers', so how is the plugin attached? Is there some external thing I have to do to initiate the plugin or what? This example left me confused.

4) So I went back to the Readme to re-read and see if I missed something. I notice "{@link Layer} extends {@link PIXI.Container}:".

My thought: Oh so I use this just like a Container instance, so maybe I need to do something like:

const globalLayer = new Layer();
const app = new PIXI.Application();
app.stage.addChild(globalLayer)
globalLayer.group.on('sort', function onWillSort(sprite) {
    sprite.zOrder = sprite.y 
});

... and assign globalLayer (like sprite.parentLayer = globalLayer) to any sprites that get added to any child of globalLayer or its children. And then any Container instances I add as a child of globalLayer will have support for layers. Nope. Now nothing is rendering, and no errors either.

I don't know about everyone else, but I am seriously missing something here. I wish there was a single simple example that shows from start to finish how to use the plugin. Or maybe there is and I just don't get it or missed something.

domis86 commented 2 years ago

For point 3:

But wait, none of the code in here references anything the Readme's Usage section showed. new Layer() isn't called anywhere, there's not even an import of '@pixi/layers', so how is the plugin attached?

I see (search for keyword "layer" there) :

Example Code (plugins used: pixi-layers)

...

// This is demo of pixi-layers.js, https://github.com/pixijs/pixi-layers
// Drag the rabbits to understand what's going on

...

// Drag is the best layer, dragged element is above everything else
const dragGroup = new PIXI.display.Group(2, false);

...

// PixiJS v5 sorting - works on zIndex - and layer gets its zIndex from a group!
app.stage.sortableChildren = true;
// sorry, group cant exist without layer yet :(;
app.stage.addChild(new PIXI.display.Layer(greenGroup));
app.stage.addChild(new PIXI.display.Layer(blueGroup));
app.stage.addChild(new PIXI.display.Layer(dragGroup));
app.stage.addChild(new PIXI.display.Layer(shadowGroup));

so instead of new Layer() there is: new PIXI.display.Layer(greenGroup) etc

domis86 commented 2 years ago

Also about: how is the plugin attached? - see:

https://github.com/pixijs/examples/blob/main/examples/manifest.json#L519-L525 https://github.com/pixijs/examples/blob/main/src/js/pixi-examples.js#L66

so is a logic of this "Pixi examples" website which loads plugins required for specific examples.

Of course for your project you can do it differently and simpler. Here is some random working example i found - it has "import" usage etc. It was written for pixi v4 but i converted it to pixi v6: https://codesandbox.io/s/stupefied-marco-cwjji?file=/src/index.js

Some notes:

Last thing: please read this useful article about layers by @ShukantPal : https://www.shukantpal.com/blog/pixijs/pixijs-layers-kit/

ivanpopelyshev commented 2 years ago

If you create a layer, and assign element.parentLayer = layer - element will be rendered the same time as layer. That's it, nothing more. Everything else is optional.

Group is only needed if you have multiple stages and you want to make global variable, you dont need it.

Sorting is needed only if you dont sort characters yourself or through pixi mechanism.

useRenderTexture is needed if you understand how layering, renderTextures and filters work.

jt0dd commented 2 years ago

If you create a layer, and assign element.parentLayer = layer - element will be rendered the same time as layer. That's it, nothing more. Everything else is optional.

Group is only needed if you have multiple stages and you want to make global variable, you dont need it.

Sorting is needed only if you dont sort characters yourself or through pixi mechanism.

useRenderTexture is needed if you understand how layering, renderTextures and filters work.

I'm following but it doesn't seem to be working for me, what stupid mistake am I making?

https://i.gyazo.com/4a1357bdab6729f3d3062e2ff5ac421c.png

What should happen here is sprite1 should be sorted between sprite2 and sprite3.

You'll notice I use require rather than import (it's an Electron.js application) but I logged the classes out so you could see they're imported correctly.

That screenshot should show the problem along side the code, but here's the code separately:

//import { Layer, Group, Stage } from "@pixi/layers";

const PIXI = require('pixi.js')
const Layer = require('@pixi/layers').Layer
const Group = require('@pixi/layers').Group
const Stage = require('@pixi/layers').Stage

console.log('Layer', Layer )
console.log('Group', Group )
console.log('Stage', Stage )

const app = new PIXI.Application();
document.body.appendChild(app.view);
app.stage = new Stage();
const layer = new Layer()
layer.group.enableSort = true;

const image = "https://wsop-poker-stage1.s3.amazonaws.com/web/game/layouts/tableResource/Regular/cards/cardBack.png"

PIXI.Loader.shared.add(image).load(() => {
    const texture = PIXI.Loader.shared.resources[image].texture
    const container = new PIXI.Container();

    // my goal is to have sprite2 and sprite3 in a container and sprite1 outside of the container, but 
    // sort sprite1 to render between the two
    const sprite1 = new PIXI.Sprite(texture);
    const sprite2 = new PIXI.Sprite(texture);
    const sprite3 = new PIXI.Sprite(texture);
    sprite1.parentLayer = layer
    sprite1.zOrder = 2
    sprite2.parentLayer = layer
    sprite2.zOrder = 1
    sprite3.parentLayer = layer
    sprite3.zOrder = 3

    sprite1.position.set(10,10)
    sprite3.position.set(20,20)
    container.position.set(0,0)

    app.stage.addChild(sprite1)
    container.addChild(sprite2, sprite3)
    app.stage.addChild(container)

    app.stage.updateStage();
    console.log(layer._sortedChildren);
})
ivanpopelyshev commented 2 years ago

you didnt add layer here. The whole point is "element is rendered like its a child of layer". If layer is not in stage, well, when do you want to render this thing?

jt0dd commented 2 years ago

you didnt add layer here. The whole point is "element is rendered like its a child of layer". If layer is not in stage, well, when do you want to render this thing?

I see my mistake. Now it works perfectly. I missed that in the examples because I thought PIXI.display.Layer was a native PIXI construct rather than a construct of your plugin. I would still suggest that a minimal working example would be appropriate on the Usage section of the Readme. It might help others not have the same confusion. Perhaps everyone else understood it and I'm just slow.

Anyhow, thanks for your help. @domis86 Thank you too, it was your example that cleared up some things for me.

jt0dd commented 2 years ago

For the record, this barely impacts my performance at all, and after spending 12 hours straight trying to achieve the same outcome, resulting in terrible performance, I have incredible appreciation and respect for what you guys have done with this plugin.

ivanpopelyshev commented 2 years ago

OK, so now performance is ok?

jt0dd commented 2 years ago

OK, so now performance is ok?

Yes, however you guys did it, it seems to be efficient.

ivanpopelyshev commented 2 years ago

Pixi layers work the same way people usually do it. It has one linear visitor, so it can be slow at 10000 elements or so. If you dont sort stuff - its efficient :)

Also, if you have 100 characters , each has one shadow, one hpbar, one body, e.t.c. , all you have to do is enable pixi sort with zIndex, and contents of characters will be distributed to layers according to that sort - only 100 elements instead of 300

jt0dd commented 2 years ago

Pixi layers work the same way people usually do it. It has one linear visitor, so it can be slow at 10000 elements or so. If you dont sort stuff - its efficient :)

I thought the main point of the project was to be able to sort things (with a container independent zIndex that PIXI wouldn't normally allow you to)?

Also, if you have 100 characters , each has one shadow, one hpbar, one body, e.t.c. , all you have to do is enable pixi sort with zIndex, and contents of characters will be distributed to layers according to that sort - only 100 elements instead of 300

Are you basically saying that when the contents of a container doesn't require global zIndexing, and could be handled with normal PIXI zIndex sorting, I can enable standard PIXI zIndexing for that container? Like if I set container.sortableChildren = true on a container, this plugin can handle the container more efficiently?

ivanpopelyshev commented 2 years ago

structure: container-> char1 -> hpbar1, shadow1, body1 char2 -> hpbar2, shadow2, body2

you can enable sort on container with zIndex, for example char1 and char2 will switch places.

If you enable layers, layer Shadow will contain shadow2, shadow1 layer hpbar - hpbar2 , hpbar1 layer body - body2, body1 because container has order of children char2, char1, because pixi sorted it

jt0dd commented 2 years ago

structure: container-> char1 -> hpbar1, shadow1, body1 char2 -> hpbar2, shadow2, body2

you can enable sort on container with zIndex, for example char1 and char2 will switch places.

If you enable layers, layer Shadow will contain shadow2, shadow1 layer hpbar - hpbar2 , hpbar1 layer body - body2, body1 because container has order of children char2, char1, because pixi sorted it

I think I understand.

But if I'm close to publishing my game a year from now and have any performance issues how about I just hire you for a week or two to make PIXI layer optimizations lol.