Hi! I'm making an HTML5 game with two.js and I want to order the sprites by their Y-Index: for instance, a sprite at (x:10,y:40) would be in front of a sprite just behind it at (x:10,y:41). I've seen #146, but I don't find that to be helpful, since I am going to have n layers, not a fixed amount (3, in the given example).
Is there any way, no matter how hacky or ridiculous, that I could re-order the sprites on each game tick? The preferred way for this to happen in my mind is to reach in to an internal list and give it a _.orderBy(...)
Thank you in advance.
I've tried variations of this, but to no avail: in fact, any call to two.add() or two.scene.add() has never seemed to properly add a sprite to the scene.
function orderScene(two) {
var children_copy = two.scene.children.slice();
_.forEach(two.scene.children, function(child) {
two.remove(child);
);
children_copy = _.sortBy(children_copy, function (child) {
return child.translation.y;
})
_.forEach(children_copy, function(child) {
two.scene.add(child);
});
}
There was an issue in the SVGRenderer that is now fixed. Here's an example of how to do this, albeit quite memory intensive. It works with all renderers: https://jsfiddle.net/jonobr1/c1e28cp2/5/
@jonobr1 I'm having a hard time mapping your solution to my problem. My problem has the added complexity of the shapes-to-be-sorted being those that are already in the scene, which seems to make your posted solution no longer work (all I see is a blank screen).
Here's my sort function, called inside a ".play()" loop, using the knowledge gained from your fiddle:
function orderScene(two) {
var shapes = two.scene.children;
var comparator = function(shape) {
return shape.translation.y;
};
var add = function(elem) {
two.scene.add(elem);
};
_.each(_.sortBy(shapes, comparator), add);
}
I omitted the Two.Group that isn't used for anything (that I can see), because when I removed group = makeGroup() and group.add(shapes) from your solution, it didn't change anything.
Thanks again for your help, I hope what I am trying to achieve is possible with the functionalities of the framework.
Sorry for the terse initial response! There was a bug in the SVGRenderer so I had to update the code.
master?I'm sure there's a way to workaround your issue, but I need some help in order to help you out 👍
@jonobr1 Sorry for the delay, but I've now tried out the master branch and have found that add works as intended. Thanks so much for doing that.
However, the performance of the example you showed in the JSFiddle is much too slow when applied to the (relatively simple) SVG files that I'm loading, instead of the stars in the example. That's when I'm doing a loop like this:
function orderScene(two, svgMapIds) {
// order only children that are not the reference SVGs in the SVGMap
var shapes = _.filter(two.scene.children, function(s) {
return $.inArray(s.id, svgMapIds) === -1;
});
var comparator = function(shape) {
return shape.translation.y;
};
sorted = _.sortBy(shapes, comparator);
_.forEach(sorted, function(s) {
// two.scene.remove(s); *
two.scene.add(s);
});
}
So essentially in my mind this is doing what your Fiddle was doing, but it runs at about 0.5 FPS and chews up tons of memory. Now, about that commented-out "two.scene.remove(s)"... in my mind, this seems like maybe the solution to the problem -- aren't I just endlessly adding sprites to the same scene? Wouldn't removing the old ones first be a better idea? But when I put that line there, I can never add the sprite back -- it will run with no errors, but the screen will be blank.
If nothing about that seems fishy, then maybe it's just that moving 19-part SVG files around in two.js 60 times a second is too intense for the framework and I should check out a more game-oriented framework like Phaser, but I wanted to run it by you first.
Here is the JSFiddle of a simplified, stripped-of-websockets-and-mouse-handlers version of my code, demonstrating the issue by switching every frame between two states. We can see that the depth now works, but also that the performance is terrifying.
PLEASE VIEW IN CHROME, see comments in "additionally" on the SVG interpreter to understand why not to open in Firefox.
https://jsfiddle.net/04L34dn4/3/
Here is the involved SVG.
first-creature.zip
Your SVG interpreter does not work correctly for certain types of SVG documents. It's out of scope for this ticket, but my partner and I may give you a pull request if we can figure out a nice way to fix it. The current changes we've made to fix things on Chrome and Firefox are the following:
(sorry about this being in image form, couldn't find a good online diff util for this purpose)
http://imgur.com/a/5VATV
I'm mentioning this because that is the reason that the SVG character's legs are pushed to the side in Chrome and the reason I don't recommend using Firefox (the problems in Firefox were worse)
Thanks so much for sharing! I've made another JSFiddle which performs better than the one you described and has imported your creatures: https://jsfiddle.net/jonobr1/xdb13bos/
I couldn't really get yours to run because, as you mentioned, runs painfully slow and freezes up my browser (Chrome). However, looking at your code at a cursory level and your asset there are a few things that jump out at me:
comparator function in modulo to only do the comparison at certain intervals: e.g: if (!(frameCount % 10)) _.each(_.sortBy(shapes, comparator), add); does the comparator every 10th frame instead of _every_ frame. Simple hack.Two.Path via "Create Compound Path" functionality in Adobe Illustrator. Use this instead of Groups. The more nesting, the more hairy things get.
Lastly, I'm happy to help as much as I can for you to make your project with Two.js. I'm glad you guys are using it and trying it! It's just me, so things can get left behind. I try to make tests where I can to make sure things are bulletproof, but as you can see there's a long way to go. Ultimately, the introduction of Two.Texture will really help your project and I'm hoping to get that wrapped up by the end of the year. It'll make Two.js _more like_ — though not exactly — a game engine...
That being said I totally understand if you have to abandon Two.js for something like Phaser. Phaser is meant for games, it's based on performance. Two.js is more about an accessible API to manipulate Adobe Illustrator-like (i.e Vector Shapes) objects with code.
Hope this helps you out!
@jonobr1 Thanks so much for all the help, I really appreciate it! The performance of the game has significantly improved, I have however noticed that even in your own Fiddle, performance degrades over time (down to 10 FPS from 30 FPS on my Surface Pro 4), which might point to some kind of memory leak. I tested this by doing console.log(1000/timeDelta) each frame in the update loop.
I totally understand if my use case is too performance-intensive, I know that two.js definitely does not advertise itself as a game engine. I might check out the Javascript Game Engine landscape a bit, but I'll keep checking back for updates as I really like the Two.js API as whole. Thanks again.
I'll look into the degradation over time. That definitely shouldn't happen.
As for the performance issues, I'm sure there are ways to increase performance. Especially if you are _not_ using the SVGRenderer, but it may be more effort than switching to a game engine like Phaser. All in all, having Two.Textures will vastly improve performance and allow for what you'd like and a potential layer of abstraction to make Two.js comparable to something like Phaser. Thanks for posting and happy coding!
Relates to Issue 245 and should be much more straightforward with an ordering API.