# Drawing game objects using their Z-order property

I am creating a game, with Ruby scripting.

Sprite and Label objects can be drawn on the screen. Depending on the order you draw them, one will overlap the other.

To make things easier, I want to implement a property in my drawable objects: Z order. So the higher the Z order of one object, the closer it will be to the player so to speak.

For this, I decided to create a class that will be in charge of containing my Sprite and Label objects, and depending on their Z property, draw them in order.

When a Sprite or Label object is created, they are automatically assigned to my global Renderer class.

Here is my Renderer class:

class Renderer
EXPECTED_CLASSES = [Sprite,Label]

# ----------------------------------------------------------------------------------------------------
# * Constructor
# ----------------------------------------------------------------------------------------------------
def initialize
@sprite_batch = Sprite_Batch.new
@stack = {}
@assigned_items = []
@sorted_keys = []
end

# ----------------------------------------------------------------------------------------------------
# * Add a drawable element with a Z order value
# ----------------------------------------------------------------------------------------------------
if (assignable?(item))
@assigned_items.push(item)
if (@stack.has_key?(z))
@stack[z].push(item)
else
@stack[z] = [item]
end
@sorted_keys = @stack.keys.sort
end
end

# ----------------------------------------------------------------------------------------------------
# * Remove an item from this manager
# ----------------------------------------------------------------------------------------------------
def remove(item)
if (removable?(item))
key = item.z
@stack[key].delete(item)
@assigned_items.delete(item)
if (@stack[key].empty?)
@stack.delete(key)
@sorted_keys.delete(key)
end
end
end

# ----------------------------------------------------------------------------------------------------
# * Reorder item
# ----------------------------------------------------------------------------------------------------
def reorder(item,z)
remove(item)
end

# ----------------------------------------------------------------------------------------------------
# * Render items in order
# ----------------------------------------------------------------------------------------------------
def render
@sprite_batch.begin
for key in @sorted_keys
for item in @stack[key]
@sprite_batch.draw(item)
end
end
@sprite_batch.end
end

# ----------------------------------------------------------------------------------------------------
# * Determine if an item can be added
# ----------------------------------------------------------------------------------------------------
def assignable?(item)
if (removable?(item) || !EXPECTED_CLASSES.include?(item.class))
return false
end
return true
end

# ----------------------------------------------------------------------------------------------------
# * Determine if an item can be removed
# ----------------------------------------------------------------------------------------------------
def removable?(item)
if (!@assigned_items.include?(item))
return false
end
return true
end

end


You probably noticed Sprite_Batch there. Don't pay it attention - it is just where the actual drawing occurs, but it is beyond the purpose of this question.

What I want to know - how can I further improve this class to be able to draw the objects in the right order depending on their Z property?

This class works alright, but I'm afraid it could get a bit slow when I have lots of objects for drawing.

Notes

• The render method is called whenever the window wants to update its contents. So it is called a lot of times in a short interval.
• The reorder method is used rarely, so I guess it is fine.
• The add method is not called very often throughout gameplay, but at startup of a specific scene (like a new game level), a large batch of new Sprite and Label objects are to be expected.
• I've considered removing the validation when adding/removing items and instead let the game crash if I did a mistake in a script.

how can I further improve this class to be able to draw the objects in the right order depending on their Z property?

The abstraction for the task is a priority queue, that's it, a dynamic structure that keeps elements sorted by a defined order (here the z-index). A priority queue is efficiently implemented with a heap, which has O(log n) find/insertion/deletion, which is pretty cool, and can be traversed in O(n). A Ruby gem that implements a priority queue: pqueue. Of course, if you have few sprites it won't improve much and it's not worth the hassle, but it's good to know about these algorithms. In your case:

require 'pqueue'
pq = PQueue.new(initial_sprites) { |s1, s2| s1.z <=> s2.z }
pq.push(new_sprite)


You use an imperative approach throughout the code. I won't argue that a game has extremely fast dynamic changes and it may be the only reasonable choice (at least in an imperative language like Ruby), but note that every time that you change some variable in-place, you're making the code harder to understand (because potentially everything can be changed everywhere, functions are no longer black boxes that get things and return things). Try to minimize in-place updates and side-effects. If you are curious: Functional Programming and Ruby.

for key in @sorted_keys
for item in @stack[key]
@sprite_batch.draw(item)
end
end


This is more idiomatic:

@sorted_keys.each do |key|
@stack[key].each do |item|
@sprite_batch.draw(item)
end
end


def assignable?(item)
if (removable?(item) || !EXPECTED_CLASSES.include?(item.class))
return false
end
return true
end


First, it's not idiomatic to write explicit returns. Second, it's more clear if you write a full if/else, the branches of the conditional (along with its indendation) conveys a lot of information to the reader. Also, you have a boolean already, if boolean then false else true -> !boolean, so the branches are unnecessary. You can simply write:

def assignable?(item)
!(removable?(item) || !EXPECTED_CLASSES.include?(item.class))
end


Or using De Morgan law, in possitive form:

def assignable?(item)
!removable?(item) && EXPECTED_CLASSES.include?(item.class)
end


More on idiomatic Ruby.