m4uro Posted January 14, 2014 Share Posted January 14, 2014 I have two groups of sprites, one containing trees, and another one containing characters. I want to sort them with the group.sort() function, but in order to do that I would need to put them in other, bigger group. However, I can't do that with the approach taken in the example of "sub groups", because when I do it that way, the _container has other containers as its children, instead of the actual sprites. What is the best/easiest way to accomplish this, without losing the flexibility of having separate groups for trees and characters? Link to comment Share on other sites More sharing options...
Secretmapper Posted February 19, 2014 Share Posted February 19, 2014 Bump, seems to be an interesting question. Link to comment Share on other sites More sharing options...
castiboy Posted February 26, 2014 Share Posted February 26, 2014 That's a very good question and I was looking for an answer to it as well.I did the very same thing not long ago in Flixel, where I needed to sort objects of different kinds for display (in a display group) and manage their behavior separately (in two logic groups.)Right now I'm trying to do something similar with Phaser and noticed that adding an object to two groups is not working as I expected. The example does show you how to add the _container to another group but as you mentioned, we actually want the sprites to be part of both groups, not to have groups within groups. Maybe there's a simple solution around this problem that we're not seeing... EDIT: thanks for the answer rich, that makes perfect sens. I managed to reorganize my game logic in an efficient way that doesn't require multiple object lists. Link to comment Share on other sites More sharing options...
rich Posted February 26, 2014 Share Posted February 26, 2014 You can't have a Sprite in more than one Group. Phaser Groups are part of the Display List, and in a display list (as in Flash) everything exists in just one place at once. You'll need to manage your own lists of objects with a custom sort to handle what you need I'm afraid. Link to comment Share on other sites More sharing options...
Krummelz Posted March 30, 2014 Share Posted March 30, 2014 So, judging from this thread, http://www.html5gamedevs.com/topic/3995-is-it-possible-to-sort-nested-groups/, this "problem" should not be a problem any more since it's fixed in v2, right? Link to comment Share on other sites More sharing options...
rich Posted April 1, 2014 Share Posted April 1, 2014 You can properly nest and sort Groups within Groups now. However you still can't mix the z-depth of objects spread across 2 Groups. I.e. the original question was asking how to depth blend a trees Group with a characters Group. You still can't do that without putting both types of object in the same Group, because of the way display lists work. Link to comment Share on other sites More sharing options...
Krummelz Posted April 5, 2014 Share Posted April 5, 2014 I understand now. I also had a look at the source and I see what you mean. This will really be very very handy if it can be done. When I saw what's possible in Phaser with groups, I was amazed and immediately started re-writing my game in Phaser. Not being able to sort multiple groups together kinda makes a lot of the awesome group features redundant in my case. I'd be happy to try and code something into Phaser or Pixi for this, but I don't understand the implications around the changes I have in mind.. Here's what I've been thinking: If I add a property to groups that, when set to true for instance, would make the system copy all the child sprites of all the child groups into one single array in the top-level group, sort them in the given order, and only draw sprites from that collection, once every frame. So updates will happen to the individual children as they normally do, but everything is copied to a separate array just for the sake of sorting and drawing at the correct depth. Would this work? I'm not sure how efficient this might be in the end, or if it's just a simple as adding another array, sorting it, and drawing from said array. Should I attempt this? Is there anything in particular that I should be mindful of that might be easily overlooked? Link to comment Share on other sites More sharing options...
rich Posted April 8, 2014 Share Posted April 8, 2014 The issue is that Groups are basically display object containers, and in a display list structure (such as Phaser / Pixi / Flash uses) you can't have an item exist across multiple containers. What you need is a single container for all objects that need depth sorting, but I suspect you want to use the benefit of Groups for pooling items, collision, etc. There's no reason why you couldn't take the Group class and customise it so it doesn't extend a display object container, but instead is just a data bucket of objects - the objects would still need to exist in a single container for display purposes - but with these AbstractGroups you could at least 'talk to' and maintain similar types of object, regardless where they are on the display list. It's definitely not a small change, and would require quite some work, but I'm not sure I see another approach really. Link to comment Share on other sites More sharing options...
Krummelz Posted April 21, 2014 Share Posted April 21, 2014 Ok, so I've managed to solve this little problem. Here's a screenshot for proof.. https://twitter.com/Krummelz/status/458307201430347778 This currently doesn't work for the canvas renderer, as the code below only illustrates the changes made to Pixi's _renderWebGL function. Fixing this is really very easy. Here are the files I've touched and the changes I've made: Phaser / src / pixi / display / DisplayObjectContainer.jsIn the constructor function, I've added the drawCache array to keep the cached children of Phaser Group objects, as well as the isSingleParentGroup property which, if set, will cause caching to happen.PIXI.DisplayObjectContainer = function(){ PIXI.DisplayObject.call( this ); /** * [read-only] The array of children of this container. * * @property children * @type Array<DisplayObject> * @readOnly */ this.children = []; this.drawCache = []; this.isSingleParentGroup = false;};Then, I've updated the _renderWebGL function to check the isSingleParentGroup property, and if it's true, will render the sprites from the drawCache instead of the regular children array.PIXI.DisplayObjectContainer.prototype._renderWebGL = function(renderSession){ if(!this.visible || this.alpha <= 0)return; if(this._cacheAsBitmap) { this._renderCachedSprite(renderSession); return; } var i,j; if(this._mask || this._filters) { if(this._mask) { renderSession.spriteBatch.stop(); renderSession.maskManager.pushMask(this.mask, renderSession); renderSession.spriteBatch.start(); } if(this._filters) { renderSession.spriteBatch.flush(); renderSession.filterManager.pushFilter(this._filterBlock); } // for multi-group depth sort if (this.isSingleParentGroup === true) { // render the drawCache for (i = 0, j = this.drawCache.length; i < j; i++) { this.drawCache[i]._renderWebGL(renderSession); } } else { // simple render children! for(i = 0,j = this.children.length; i<j; i++) { this.children[i]._renderWebGL(renderSession); } } renderSession.spriteBatch.stop(); if(this._filters)renderSession.filterManager.popFilter(); if(this._mask)renderSession.maskManager.popMask(renderSession); renderSession.spriteBatch.start(); } else { // for multi-group depth sort if (this.isSingleParentGroup === true) { // render the drawCache for (i = 0, j = this.drawCache.length; i < j; i++) { this.drawCache[i]._renderWebGL(renderSession); } } else { // simple render children! for (i = 0, j = this.children.length; i < j; i++) { this.children[i]._renderWebGL(renderSession); } } }};Phaser / src / core / Group.jsI've added two functions as below. These will use the Pixi drawCache array as seen above, and add every sprite of every child Group object to it. Phaser.Group.prototype._recursiveCache = function (arrayToSort) { // recursive function that will stick all nested children sprites in the drawCache so they're on the same level for (var i = 0; i < arrayToSort.length; i++) { if (arrayToSort[i].children && arrayToSort[i] instanceof Phaser.Group) { this._recursiveCache(arrayToSort[i].children); } else { this.drawCache.push(arrayToSort[i]); } }};Phaser.Group.prototype._CacheObjectsForDraw = function () { //clear the cache this.drawCache.length = 0; //cache all children this._recursiveCache(this.children);}; Then I've updated the Group.js sort function as below. This will now check if the isSingleParentGroup property is true, and will then call the _CacheObjectsForDraw function I created, and set the drawCache as the array to sort; or otherwise the regular children array will be sorted as normal. Phaser.Group.prototype.sort = function (index, order) { if (this.children.length < 2) { // Nothing to swap return; } if (typeof index === 'undefined') { index = 'z'; } if (typeof order === 'undefined') { order = Phaser.Group.SORT_ASCENDING; } this._sortProperty = index; // determine the array to sort var arrToSort; if (this.isSingleParentGroup === true) { // cache all children this._CacheObjectsForDraw(); arrToSort = this.drawCache; } else { arrToSort = this.children; } // sort the appropriate array in the appropriate way if (order === Phaser.Group.SORT_ASCENDING) { arrToSort.sort(this.ascendingSortHandler.bind(this)); } else { arrToSort.sort(this.descendingSortHandler.bind(this)); } this.updateZ();};Simple stuff, really Here's some sample code from my tower defence game, to show how you would use this:var game = new Phaser.Game(800, 600, Phaser.AUTO, 'phaser-example', { preload: preload, create: create, update: update });var mainGroup;var enemyGroup;var towerGroup;function preLoad(){ // your preload logic}function create(){ // create your groups mainGroup = game.add.group(); enemyGroup = game.add.group(); towerGroup = game.add.group(); // nest your enemy and tower groups under the main group mainGroup.add(enemyGroup); mainGroup.add(towerGroup); // this will make the magic happen mainGroup.isSingleParentGroup = true; // go ahead and add your sprites to your groups as you normally would // for example: // var enemy = game.add.sprite([blah blah]); // enemyGroup.add(enemy);}function update(){ // your update logic goes here // as per the examples, we are told to call sort last in the update function.. // this works as you would expect.. mainGroup.sort("y", Phaser.Group.SORT_ASCENDING);} Link to comment Share on other sites More sharing options...
aberrantmind Posted May 5, 2014 Share Posted May 5, 2014 Is this solution going into the next Phaser release? Also wanted to add I just tried this out and it works like a champ so far! spent all yesterday trying to get nested groups and depth sorting to work, couldn't figure out what I was doing wrong and finally hit up the forums this morning. Link to comment Share on other sites More sharing options...
aberrantmind Posted May 6, 2014 Share Posted May 6, 2014 Ah, just tested it in Safari on both the iPad mini, and the last generation of iPad(not sure what it's called) and depth sorting + nested groups breaks on there. Not sure why? Also tested in IE11: doesn't workIn Firefox it works but is slooow slow.In Chrome all is good Link to comment Share on other sites More sharing options...
Krummelz Posted May 6, 2014 Share Posted May 6, 2014 The only reason I can think of that it would break is if the iPads and IE use the canvas for rendering instead of WebGL - In the code above, I only changed the internal Pixi "_renderWebGL" function, and not "_renderCanvas". I still have to do the canvas function. I can test IE, but I'm afraid I don't own an iPad to test on. I'll look at doing the canvas and then a pull request. Link to comment Share on other sites More sharing options...
lewster32 Posted May 6, 2014 Share Posted May 6, 2014 Phaser runs in canvas on iOS devices, so this confirms your suspicion Krummelz. Link to comment Share on other sites More sharing options...
Krummelz Posted May 6, 2014 Share Posted May 6, 2014 Thanks! That helps.. I'll put up the canvas code as soon as I can, then you guys can try it out for me Link to comment Share on other sites More sharing options...
Nikomaru Posted May 7, 2014 Share Posted May 7, 2014 I found an easier work-around, quite by accident and quick theorizing. It does require creating all your objects in one group, but when actually assign the object's data, simply add a custom variable that indicates if it is to be affected by your game functions. It's a slow work-around, but it worked in my code.function create() { obj = game.add.group(); obj.add(pc); obj.add(exit); pc = game.add.sprite(blah,blah,blah); pc.objState = false; // identifiable custom variable exit = game.add.sprite(blah,blah,blah); exit.objState = true; // identifiable custom variable for (var i=0;i<x;i++) { npc = game.add.sprite(blah,blah,blah); npc.objState = true; // identifiable custom variable obj.add(npc); }; obj.sort();}function update() { obj.forEach(updateObj, this); obj.sort('y', Phaser.Group.SORT_ASCENDING);}function updateObj(z) { if(z.objState) { // processes here only affect objects with the objState set to true z.<game object property> <argument>; }; // processes here affect every object}I have yet to figure out how to effectively use protoypes, so all my stuff is from a basic programmer's perspective. I'd apreciate any assistance in speeding up this routine, but until I tested it, I didn't actually expect it to work. I expected the interpreter to say ".objState is not a " whatever. Link to comment Share on other sites More sharing options...
jouniii Posted May 8, 2014 Share Posted May 8, 2014 I was working with dynamic z-indexing (ie. bringing stuff to front and returning back to same position after some action) but I stumbled to an issue with the updateZ() function in the group sorting. Is it required that the Z index is gapless or any other reason why the Z values are being reset after sort? For now I was able to fix this with custom sort property to my sprites (called 'zindex') and sort based on that and not to update the actual z value manually, but this has it's problems with Typescript (nothing what makes things stop working, but you have to resort either 'any' type or [] accessor). Problem was a bit similar as this thread was asking. Object of multiple parts and each part could build up from sprites which need to overlap on different z index relative to other parts. Plus I wanted to "lift" one part above others temporarily. Link to comment Share on other sites More sharing options...
jouniii Posted May 8, 2014 Share Posted May 8, 2014 Small additional question.. anyone tested more with Nokia N9 native browser? Seems things work fairly well, but initial testing shows the z sorting is reversed on the N9. Not sure if webGL is used or is it just canvas mode, but same code works fine on Chrome and iOS. Not going to inspect this more unless it appears again on the devices we're really targetting. Link to comment Share on other sites More sharing options...
Owumaro Posted September 20, 2015 Share Posted September 20, 2015 Bump I'm running into the same issue but can't find an easy solution. Anything new since 2014 ? Link to comment Share on other sites More sharing options...
Tom Atom Posted September 21, 2015 Share Posted September 21, 2015 Hi, I wrote small tutorial how to simply do it: http://sbcgamesdev.blogspot.cz/2015/09/phaser-tutorial-breaking-z-order-law.html Trick is to have one group with all items, but transform is done with different parent group. staff0rd and NateTheGreatt 2 Link to comment Share on other sites More sharing options...
NateTheGreatt Posted September 21, 2015 Share Posted September 21, 2015 Hi, I wrote small tutorial how to simply do it: http://sbcgamesdev.blogspot.cz/2015/09/phaser-tutorial-breaking-z-order-law.html Trick is to have one group with all items, but transform is done with different parent group. Very elegant solution! Well done, and thank you for sharing. This will be helpful to me Link to comment Share on other sites More sharing options...
Owumaro Posted September 22, 2015 Share Posted September 22, 2015 Many thanks for this example. That's interesting. However I'm not sure if I can use this to solve my problem since I must keep my 2 sprites in different groups (from Phaser point of view) to use collisions. If I understand right, your sprites (MySprite class) have 2 groups : the original one managed by Phaser (which can be used for collisions and stuff), and another one that is used in the updateTransform method. I didn't find any documentation on updateTransform so I'm not sure how this works but anyway. In your example you're putting your sprites in the same group (I mean the "real" phaser group) "spritesGroup", so you can't manage collisions anymore using their groups. Link to comment Share on other sites More sharing options...
Tom Atom Posted September 23, 2015 Share Posted September 23, 2015 Hi, you understand it well. The second group is only reference to different parent. When updateTransform on sprite is called, it is given parent and it is transformed as its child (position relative to parent, angle relative to parent, ...). What you do is, that you give it different parent from its actual in scene graph. I do not have enough experience with Phaser physics engines. But P2 collision groups are not Phaser.Group, but Phaser.Physics.P2.CollisionGroup. So, you can have sprites from different Phaser.Groups in one Phaser.Physics.P2.CollisionGroup. Link to comment Share on other sites More sharing options...
Owumaro Posted September 27, 2015 Share Posted September 27, 2015 Ok cool. I'm using Arcade Physics which doesn't have CollisionGroups So I guess the cleanest solution would be to introduce CollisionGroups to Arcade Physics... Well for now I'll manage collision with each sprite of my groups using loops Link to comment Share on other sites More sharing options...
staff0rd Posted February 16, 2016 Share Posted February 16, 2016 On 9/20/2015 at 7:39 AM, Tom Atom said: Hi, I wrote small tutorial how to simply do it: http://sbcgamesdev.blogspot.cz/2015/09/phaser-tutorial-breaking-z-order-law.html Trick is to have one group with all items, but transform is done with different parent group. I had a different scenario where I needed to keep multiple sprites in different groups by have them all affected by one tween. The above link solved it for me. Thanks @Tom Atom! Link to comment Share on other sites More sharing options...
Crisu83 Posted February 17, 2016 Share Posted February 17, 2016 Hello everyone, I wrote an ES6 RenderGroup class based on the research by @Krummelz (thanks) that allows sorting of all children recursively, simply create a new RenderGroup as your root group and set it as the parent to each of your groups and it will apply the sort to the group and all of its child groups. https://gist.github.com/crisu83/f13ec7ad0de9273480de Hopefully you find it helpful. Link to comment Share on other sites More sharing options...
Recommended Posts