Change the draw order of a sprite in SpriteBatch?

Hi How do you change the zindex or the order the sprite is drawn in a sprite batch?

Not sure if there’s a better solution but I’ve created a solution where I’ve gone in and changed the sorting of the batch element based on the z-index.

Basically it adds BatchElement to the right sorted location with addbyZIndex and/or when you change the ZBatchElement.zIndex value.


import h2d.SpriteBatch.BatchElement; 
import h2d.SpriteBatch;

/**
 * Batch Element with a zIndex value.
 */
class ZBatchElement extends BatchElement {
	var _zIndex = 0.0;

	public var zIndex(get, set):Float;

	function get_zIndex() {
		return _zIndex;
	}

   /**
    * Remove from the list and re-add by sorted z-index
    */
	function set_zIndex(v) {
		this._zIndex = v;
		if (this.batch == null)
			return v;

		var b:ZSpriteBatch = cast this.batch;
		this.batch.delete(this);
		b.addbyZIndex(this);

		return v;
	}
}

/**
 * Sprite Batch with Z-Index functionality
 */
class ZSpriteBatch extends SpriteBatch {
	/**
	 * Add element by their z-index
	 * @param e 
	 */
	public function addbyZIndex(e:ZBatchElement) {
		var inserted = false;

		for (elements in this.getElementsReversed()) {
			var e1:ZBatchElement = cast elements;
			if (e1.zIndex <= e.zIndex) {
				inserted = true;
				this.insertAfter(e, e1);
			}
		}

		if (inserted == false) {
			this.add(e, true);
		}
	}


   /**
    * Reverse iterator since we want to insert elements at the end.
    */
	public function getElementsReversed() {
		return new ReversedElementsIterator(this.last);
	}

   /**
    * Function to insert before an element
    */
	public function insertBefore(e:ZBatchElement, before:ZBatchElement) {
		e.batch = this;

		if (first == null) { // List is likely empty
			first = last = e;
			e.prev = e.next = null;
		} else if (before.prev == null) { // First element
			before.prev = e;
			first = e;
		} else { // Insert between 'before' and 'before.prev'
			var tmp = before.prev;
			e.prev = tmp;
			tmp.next = e;

			e.next = before;
			before.prev = e;
		}
		return e;
	}
   /**
    * Function to insert before an element
    */

	public function insertAfter(e:ZBatchElement, after:ZBatchElement) {
		e.batch = this;
		if (first == null) { // List is likely empty
			first = last = e;
			e.prev = e.next = null;
		} else if (after.next == null) { // Last element
			after.next = e;
			last = e;
		} else { // Insert between 'after' and 'after.next'
			var tmp = after.next;
			e.next = tmp;
			tmp.prev = e;

			e.prev = after;
			after.next = e;
		}
		return e;
	}

}

/**
 * Iterate backwards from the end
 */

private class ReversedElementsIterator {
	var e:BatchElement;

	public inline function new(e) {
		this.e = e;
	}

	public inline function hasNext() {
		return e != null;
	}

	public inline function next() {
		var n = e;
		e = @:privateAccess e.prev;
		return n;
	}
}

The code is a bit rough and there might be a better solution. But it works for now.

Good question. I think you cannot do that manually, because SpriteBatch and its BasicElement are based on a “dynamic list”. So you must clear and “rebuild” your spritebatch if you really need control over the index. What you can do however is to save a reference to one BasicElement within the SpriteBatch and replace it by another. Then you just replace var weapon : BasicElement by another.

Anyway, yes, the class is a bit barebones and as soon as you really need this kind of class you probably needed more in the first place. I can see just from your code how this is actually an essential feature, it really should be there by default. :confused:

(I’m thinking this could actually be a contribution directly to the classes themselves without inheritance/extends? :thinking:
By the way, why did you choose a Float for index? Just curious.)

I’m wondering, maybe Yanris (check :white_check_mark:, hes has) or Deepnight (he has, too! :white_check_mark: ) have an alternative solution to the default Heaps SpriteBatch in their projects.

Yes I’m surprised… It says on the docs that to be careful of anims/bitmaps because it creates a draw call each. But it looks the tools for SpriteBatches are quite sparse. (Layers uses objects, bitmaps, anims and drawables, so it kinda encourages having hundreds of draw calls??)

I took a peek at deepnights code and I think he doesn’t use SpriteBatches except for particle effects. Which unless I’m mistaken you can only have a few hundred moving sprites on screen at once with a decent desktop computer. Which to be fair is good enough for most PC games. I didn’t look too deeply though he might be using SpriteBatches in other key places.

Edit: Oh looks like he does have it. I’ll check it out

Btw who is Yanris?

The code I’ve written is kinda jank so I think a contribution as it currently stands might not be too good. I think it might need to be rewritten to have it’s own class and to take it’s unique BatchElement.

Currently I feel it kinda muddles the original class a bit so maybe a separate class where you can have your own custom sorting? :thinking: (so you can have ysorting or something like that).

Not sure, do they take contributions like these?

Well, ideally I was thinking of a contribution, but in reality this sometimes can take a while until, if even, it is accepted/included. But when the classes can have an index implemented without affecting the other functionality and performance, then personally I’d say this is so basic it just should be there.

Ah and for the float for zIndex, I’ve just seen it like this in other places and it’s just for precision. There are some people who just set the zindex/zorder to equal the y value to quickly add ysorting (to make the objects lower in the screen in front of everything, like in zelda or in pokemon).

And I agree it does seem like an essential feature. It should encourage the use of SpriteBatches for pretty much majority of the sprite rendering I think…

I’ve opened a pull request here: