Jump to content

How does Pixi deal with items positioned off the screen?


Recommended Posts

I have a DisplayObjectContainer full of tiles that the user can pan around and explore.


At the moment all the tiles are in a DisplayObjectContainer at all times, but I'm wondering whether Pixi does any optimisation around not rendering items off the screen. Should I should be more aggressive about adding & removing items from the stage when they're out of view?

Link to comment
Share on other sites

I was working with this this issue for quite a while. 


From my experience if you have a lot of tiles that are the same (I mean exactly the same texture) you could yous sprite batch and don`m bother to do some funky addChild Remove child code.


If your tiles are quite different but again you can divide them into some groups you can create different spriteBatches and still forget about removing elements that are outside the viewport.




Managing your scene and dynamically adding or removing the sprites can give you some serious performance boost.


I had a huge tile map that the player could move around and explore. Parts of the tilemap were added dynamically so while exploring the stage started to contain even more elements and at some point the framerate dropped to 30fps (as pixi is blazing fast that was unacceptable)!!!!


What I did is that I rendered parts of the tiles  (for example 9*9 tiles) into a single texture with the use of renderTexture.


Additionally you can divide you world into something that I call the cameras. Cameras are display object containers that you move when you player moves.


If a camera is outside the view (which is usually a simple logic to check) you remove it from your stage. For additional performance you can perform checks from time to time  inside the camera and remove or add the element that should not be there.


the cool thing about it is that you don`t have to do it every frame just every  15-20 frames.


for instance.

var CNTR = 0;var loop = function(){CNTR++;// move the world// perform collision// do some other stuffif(CNTR % 15 == 0){ check the stage every 15 frames if there are things that you could remove from the stage to east the renderer}if(CNTR == 100){CNTR = 0;}}

Additionally this structure of the loop can help you perform other checks with different frequency.


you can add

if(CNTR % 50 == 0){    // verify if you are close to another part of the world to pre-load some data to local storage!}

You get a firm grip over your loop.


Another thing is that before you add your sprites to the stage you can give them classes to control your sprites by groups etc.

var spr = new PIXI.sprite(myTexture);spr._class = 'robots'stage.addChild(spr);//And whit this you can remove elements from stage easilyfor(var obj in stage){if(stage[obj]._class === 'robots'){stage.removeChild()stage[obj];}}



instead of iterating through all children in stage and using method stage.remove child you can do stage.children = [ ];


But I am not sure if this is a valid way to manipulate pixi stage.




Link to comment
Share on other sites

Thank you for posting that!


Dynamically adding/removing children seems like a lot of trouble so I wonder there's an easier or more performant way.


Did you try setting the `visible` property to `false` if sprites are not within the camera's field of view?

Or did you try setting the `renderable` property to `false`?


I'm going to be dealing with this in my own project very soon so it's good to have these advance reports from the trenches :)

Link to comment
Share on other sites

Oh yeas I did! :D I have a property inSight on my camera containers for managing them. It is easier to move the whole camera container than each element inside. Plus like I mentioned before you can make a snapshot of the tiles and render them into one image with renderTexture. TRUST ME ! SNAPSHOTING THE TILES GIVES YOU SERIOUS BOOST since you move only one image instead of 30 each after another or a container holding 30 tiles (30 sprites)


I never used renderable property of a sprite but that might be a very good idea to use for the tiles that are inside the camera or any other elements like entities etc. but I cant give you any reasonable answer about that!


Well i wanted the engine to be as many false hits as possible.


In other words I structured the code to perform fewer checks.

if(_camera[1].position.y + (_camera[1].height)  >= 0 && _camera[1].inSight !== true){                _renderer.stage.addChildAt(_camera[1],  0 );              _camera[1].inSight = true;             }else if(_camera[1].position.y + (_camera[1].height)  < 0 && _camera[1].inSight === true){                                for(var cam in _renderer.stage.children){                                        if(_renderer.stage.children[cam]._class === 'tileHolder'                        && _renderer.stage.children[cam]._id === 1){                                                _camera[1].inSight = false;                        _renderer.stage.removeChild(_renderer.stage.children[cam]);                        break;                                            }                 }             }

This is a short part of the mapWatcher class that i have created.


What you should think about is to figure out how to call this as few times as possible.


For example if you are sure that the size of each map container ( I my case camera) is bigger than the viewport ( part of the visible screen )  you can add a logic that excludes test.




if camera height is bigger than the height of the viewport you can`t see the north camera ( even a tiny part of it ) at the same time having the sout camera in view.


so a simple logical structure can be created

if( viewport.y < (cameraNorth.y + cameraNorth.height) ){// viewprt.y => top of the canvas. position = 0;// cameraNorth.y - cameraNorth.height bottom of the north camera// please note that at this time the camera is outside the view so// cameraNorth.y is negative and the height is positivecode that adds the camera to stage }else if(check if south camera is in the view){code that adds the south camera to stage }

The same thing can be done with east and west.


But bare in mind that having many cameras can give you huge performance boost, but when it comes to scaling can be very problematic to make a responsive viewport and make the game graphics responsive. (this is what I am struggling right now with... :/ )



additional simple improvement is to add a break statement with logical condition in the remove testing loop, saves you some precious cpu.



and last but not lest is the structure of your if else statements ( I prefer if else than switch case since nothing falls through)


always put the most probable element at the top and then descend the logic based on lower probablity. makes you engine do fewer checks in the same loop => again better cpu usage




Link to comment
Share on other sites

That's an amazing reply, thanks so much. I've marked the other one as the answer because, well, it is, but I really appreciate your insights.


In reply to your comment about manipulating the stage.children array directly, I believe this is allowed because it's the only way (afaik) to adjust the z-index/render order of your sprites. I've seen it mentioned elsewhere as an appropriate practice in any case.

Link to comment
Share on other sites

OK so I did some more digging and testing  and it seems that


Renderable property is read only and does not affect the visibility of the sprite. Even if you do

mySprite.renderable = false;

Nothing really happens.


But you can manipulate visible property.

mySprite.visible = false;

Makes the sprite disappear from the stage.


So it seems that you can use this property in order to let the rendering engine rest a little bit. I lloked up how the pixi engine works inside and there are many check performed by the rendering functions just on the beginning of the function.


A LOT... I MEAN A LOT!!! of them check if element is visible

PSEUDO CODE!!! not actual pixi code!!!var somePixiRenderingFunction() {if(!this.visible){return}//rest of the code for rendering ...}var somePixiRenderingFunction() {   for(var element in elementsForRendering){      if(!this.visible){continue};      //rest of the code for rendering ...   }}

So it is possible to use this property to give your engine some additional performance boost.

What is even better is that you don`t have to create any other object (var nonRenderedHolder = {}) for the sprites to be hold until you would have to insert them again to the stage so effectively you save the memory (which is so important on mobile)  and you don`t have to create any function that would manage some sort of cleaning of nonRenderedHolder from objects that you will not have to add again to the stage in the future.


Then again all the manipulation of the position etc will be performed so setting the visible property to false only lets the pixi renderer omit only part of the operations.



element.visible = false

is the fastest way to make the element/sprite not rendered on the stage and should be used for elements that show and disappear from the viewport quite frequently.


Removing elements from stage should be used in case you have elements that will be shown from time to time on a lower frequency as the engine will not have to iterate through the whole stage filled with "invisible" elements.


This question actually helped me with my game to dig deeper into pixi! :D


Additionally to supplement the problems with scaling I mentioned before. If you put all of your displayObjectContainers, sprites etc. into one displayObjectContainers  which will act as a wrapper all the scaling is done automatically by pixi without any hustle! :D



Link to comment
Share on other sites

sure! :D


As I`m digging into my project why not to share the knowledge! :D


There is another property called worldVisible but it only checks if all the sprite parent containers are visible. but that still has nothing to do with actually being visible in the viewport.




Additionally I created a function that automatically sets property visible = true to all my tileHolders that are visible and false for everything else.


!!! I use different cameras so you have to take in account that the position of x and y of an object inside displayObjectContainer are relative so in this model so have to take that into account and modify it for your own project.

for(var cam in _viewport.children){                                if(_viewport.children[cam]._class === 'tileHolder'){                    for(var bck in _viewport.children[cam].children){                                                                        if(                                                                (_viewport.children[cam].children[bck].position.x + _viewport.children[cam].position.x >= 0                                 &&                                 (_viewport.children[cam].children[bck].position.x + _viewport.children[cam].position.x  < gameCanvas.width))                                 ||                                (_viewport.children[cam].children[bck].position.x + _viewport.children[cam].children[0].width/3 >= 0)                                &&                                _viewport.children[cam].children[bck].position.x + _viewport.children[cam].position.x < 0                                                                                          ){                                                        if(                                                                        (_viewport.children[cam].children[bck].position.y + _viewport.children[cam].position.y >= 0                                     &&                                     (_viewport.children[cam].children[bck].position.y + _viewport.children[cam].position.y < gameCanvas.height))                                     ||                                    (_viewport.children[cam].children[bck].position.y + _viewport.children[cam].children[0].height/3 >= 0)                                    &&                                    _viewport.children[cam].children[bck].position.y + _viewport.children[cam].position.y < 0                                                                  ){                                                                _viewport.children[cam].children[bck].visible = true;                                                            }else{                                                                _viewport.children[cam].children[bck].visible = false;                                                            };                                                                                }else{                                                        _viewport.children[cam].children[bck].visible = false;                                                    };                    };                };                            };

Hope this part of code will help you with your games!



Link to comment
Share on other sites

  • 11 months later...

Haha, got it, thanks for clarifying. When I reread the above, I see Hubert is just calling his container 'camera'.  I just wondered if it had a camera whether it would help me. I'm trying to figure out how flashvhtml.com generated their scrollmap. http://flashvhtml.com/js/site/ScrollMap.js (but I asked about that somewhere else on this forum I think). Beyond the simple examples, some aspects of Pixi, or what people have done with it, are a lot to get my head around. o.O! 

Link to comment
Share on other sites

  • 3 months later...

Haha, got it, thanks for clarifying. When I reread the above, I see Hubert is just calling his container 'camera'.  I just wondered if it had a camera whether it would help me. I'm trying to figure out how flashvhtml.com generated their scrollmap. http://flashvhtml.com/js/site/ScrollMap.js (but I asked about that somewhere else on this forum I think). Beyond the simple examples, some aspects of Pixi, or what people have done with it, are a lot to get my head around. o.O! 


Already you know how to do? I want to do the same that http://flashvhtml.com/ but using Phaser. Any tuto?


Regards and thanks

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


  • Recently Browsing   0 members

    • No registered users viewing this page.
  • Create New...