Skip to content

Conversation

@Electr0Gunner
Copy link

Haxeflixel has lacked an easy way to layer objects in the groups when drawing.
The current way was to use add() if you never moved it or insert() but this required knowing what object was where in the group.

By using a renderOrder variable, now added into FlxBasic, you can sort the draw call order.
In FlxGroup.draw() i created an array of indices for each member, and sort them based on the member's renderObject value.
This is the fastest and least expensive method i could think of, i made tje index array local which might've been a bad idea.
It also has a new renderOrderMode public variable, that uses bitwise operators to determine what type of sorting should happen. (Currently only renderOrder based is implemented)

Example:

var sprite1 = new FlxSprite(0,0).makeGraphic(30, 30);
sprite1.renderOrder = 3;
var sprite2 = new FlxSprite(0,0).makeGraphic(30, 30);
sprite2.renderOrder = 1;
add(sprite1);
add(sprite2);

In this example, sprite1 will always draw over sprite2

I also added a FlxDefine called FLX_NO_RENDER_ORDER to disable the render ordering incase projects don't need it.

I don't expect this to be approved, but i hope this feature would be put on some todo list for the future, as this is a pretty helpful feature.

TESTS

I have tested the code on Windows 11 cpp target.

@moxie-coder
Copy link
Contributor

Absolutely love this, the fact that HaxeFlixel had no Z index and relied on this wasn’t ideal, because you had no good way to change the draw order besides insert as far as I’m aware

@Geokureli
Copy link
Member

Geokureli commented Oct 2, 2025

TBH I've never been a fan of this method of sorting, this is already something that can be achieved a dozen different ways, and IMO this is the most messy. I really dislike the idea of all FlxBasics having a field that controls the behavior of the containing group, rather than groups simply having an ordered list of members.

I also don't think it's a good idea to sort the entire unsorted display list on every single draw call, compared to iteratively sorting the actual members, like in this example. The method I recommend will only need to reorder a small portion of the sprites that moved in the last frame, where yours may have to reorder every single sprite, every single frame (often resulting in the same order as the previous frame), and the number of comparisons grow exponentially with the size of the members.

All in all there's far more efficient ways to control render order, with sub-groups, tools like FlxArrayUtil.swap, or general array utils like sorting. If you had a use-case for a generic render-order sorting, I recommend adding that field to a specific FlxSprite extending class, and sorting the group every frame by that field. I've done this in the past, my game's SpriteBase class extended FlxSprite with a zOrder and SortedContainer<T:BaseSprite> extends FlxTypedContainer<T> that sorts the members each frame and my main state has foreground and background containers, where the foreground is sorted. There's a decent tutorial here, too.

I'm not gonna close this yet, if people really want this I'll consider it, but I do have issues with this specific implementation, on top of general concerns for the feature as a whole. If you want this to happen, please provide examples that you think are cumbersome to achieve without this feature. Try to make sure that these are practical examples that you have come across in your projects, rather than theoretical ones that you've never actually needed

For instance:

var sprite1 = new FlxSprite(0,0).makeGraphic(30, 30);
sprite1.renderOrder = 3;
var sprite2 = new FlxSprite(0,0).makeGraphic(30, 30);
sprite2.renderOrder = 1;
add(sprite1);
add(sprite2);

Can easily be

var sprite1 = new FlxSprite(0,0).makeGraphic(30, 30);
var sprite2 = new FlxSprite(0,0).makeGraphic(30, 30);
// OPTION 1
add(sprite2);
add(sprite1);
// OPTION 2
add(sprite1);
insert(0, sprite2);
// OPTION 3 (with `using flixel.util.FlxArrayUtil;` in imports
add(sprite1);
add(sprite2);
members.swap(sprite1, sprite2);

@DetectiveBaldi
Copy link
Contributor

DetectiveBaldi commented Oct 2, 2025

I've personally never been a fan of a core z-index field like this, insert has always been more than enough for every usecase for me. I've never considered using swap, but that seems like it could get the job done in an even cleaner way. That's just my 2 cents, though

Also for this specific pr, creating an array on each draw call most likely isn't very healthy, and would probably get exponentially worse with large amounts of groups

@ACrazyTown
Copy link
Contributor

I think ideally it would be nice to have both. While not often, I've personally added a similar "zIndex" field on certain occasions because it was a bit easier to work with.

Aside from that, I think it would be better if the renderOrder was a float and not an int. If you have a complex scene and want to add something in between two pre-existing elements you'd have to change the renderOrder of a bunch of elements above the new one. With a float you could just do something like 3.1 to place it between the elements with id 3 and 4

@Electr0Gunner
Copy link
Author

I have redid the sorting a little, now floats are used as @ACrazyTown town suggested, (i will be honest, it might be a good decision) added _drawMemberIndices to FlxGroup to avoid making a new array every draw call, now it does a new array only if the member length != indices array length.

i don't expect this to be approved at all.
But i do want a feature like this to be considered
as extending classes, adding macros, are not such easy ways to add sorting like this.
With a define like i added this can be disabled like other features in flixel.

If it's considered then it can be even more efficient. And help HaxeFlixel games a ton

@Geokureli
Copy link
Member

Geokureli commented Oct 3, 2025

Even with these changes, I'm still in camp "no".

I don't think we should gate features like this behind compiler-flags either. This will increase complexity of testing and maintaining the codebase, and it will make it harder to communicate issues to one another. If you think its handy to hide a feature behind a compile flag, thats a good sign we probably shouldn't add it at all.

With a define like i added this can be disabled like other features in flixel.

The other defines are mainly to allow flixel to work on different hardware, or for debug features that decrease performance, the only other feature like this behind a flag is NO_HEALTH which is used to help deprecate an old feature that is about to be removed

i don't expect this to be approved at all. But i do want a feature like this to be considered as extending classes, adding macros, are not such easy ways to add sorting like this.

Make an issue stating the problem you are having, give actual snippets of code from actual projects, describing what you think is cumbersome, and why. Once we identify an actual problem can start talking about solutions. Side note, but extending classes is actually a decent solution here, and I would like to know why you don't think so.

@Geokureli Geokureli closed this Oct 3, 2025
@Electr0Gunner
Copy link
Author

Compiler flags features are mostly an accidental fall through from my c++ methods, sorry bout that

For the extending part
Well
I am usually using alot of classes from base haxeflixel

Considering renderOrder is applied to flxbasic
This would allow all objects to be ordered with little to no issues

I guess using an interface could work

Thank you for looking into this.

@Geokureli
Copy link
Member

Geokureli commented Oct 4, 2025

I feel like you're not fully reading my replies

Make an issue stating the problem you are having, give actual snippets of code from actual projects, describing what you think is cumbersome, and why. Once we identify an actual problem can start talking about solutions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants