Overiding the "Compare" method for Z ordering

BlitzMax Forums/BlitzMax Programming/Overiding the "Compare" method for Z ordering

altitudems(Posted 2005) [#1]
Overiding the Compare method affects the SortList() command. Which means that you can sort and list of objects by whatever field you like. All you have to do in your main loop is call "SortList(YourList)".

In the example below I've overidden the compare method to z order my sprites sprites:
Strict
'The Sprite Type
Type Sprite
	Field x:Float
	Field y:Float
	Field color:Int [3]

'--- Create a Sprite
	Function Create:Sprite ()
		Local s:Sprite = New Sprite
		s.x = Rand(0,640)
		s.y = Rand(0,480)
		s.color = [Rand(0,255),Rand(0,255),Rand(0,255)]
		SpriteList.AddLast(s)
		Return s		
	End Function
	
'--- Render the Sprite
	Method Render()
		SetColor color[0], color[1], color[2]
		DrawOval x-16,y-16,32,32
	End Method
	
'--- Here we overide the compare method to
'--- look at the "Y" field and compare it with the 
'--- the given object.
	Method Compare( other:Object )
		Return y-Sprite(other).y
	End Method
EndType

Graphics 640,480,0

'Create the Spritelist
Global SpriteList:tList = CreateList()

'--- Create the Player Sprite
Local player:Sprite = Sprite.Create()

'--- Create Some Sprites
For Local i = 1 To 100
	Sprite.Create()
Next 

'MAIN LOOP
While Not KeyHit(KEY_ESCAPE)
	Cls 
'--- Sort the Sprites
	SortList (SpriteList)

'--- Render All Sprites
	For Local s:Sprite = EachIn SpriteList
		s.Render()
	Next

'--- Update Player Position
	player.x :+ (KeyDown(KEY_RIGHT) - KeyDown(KEY_LEFT))*2
	player.y :+ (KeyDown(KEY_DOWN) - KeyDown(KEY_UP))*2

	FlushMem
	Flip
Wend



donduck(Posted 2005) [#2]
Hi,

Nice - but there's a potential bug in there.

Compare is supposed to return an int <0, =0 or >0 depending on whether 'Self' is less than, equal to or greater than 'other'. However, your Compare is only returning 0 or 1.

That fact that it's working is due to the way the sort routine works in TList. However, if this routine changes - or perhaps you have an array of Sprites and use Array.Sort (which uses a different algorithm) - you may have problems.

So, you could go:

If y<s.y
  Return -1
Else if y=s.y
  Return 0
Else
  Return 1
EndIf


However, a more efficient way is:

Return y-s.y


Which, if you think about it, will return the correct value!

It's also probably a good idea to think about whether trying to Compare a Sprite with a non-Sprite should be allowed - currently, your routine allows this and returns 0 for all such comparisons.

But unless you can think of any reason this should be possible/desirable, it's probably best to generate an error, eg:

Assert Sprite(other)
Local s:Sprite=Sprite(other)
Return y-s.y


Which guards against Sprite/non-Sprite comparisons. In fact, you can roll it all up into one statement:

Method Compare( other:Sprite )
  Return y-Sprite(other).y
End Method


This will both generate a runtime error if 'other' is not a Sprite (with 'attempt to access field or method of Null Object') and return the correct range of values.


altitudems(Posted 2005) [#3]
Great!

Your right it could cause problems, thankyou for pointing it out.

I actualy did have it like this originaly:
If y<s.y
  Return -1
Else if y=s.y
  Return 0
Else
  Return 1
EndIf


I turned it into this:
If s.y < y Then Return 1

so it would be more simplified :)
But this is way simpler:
Method Compare( other:Object )
  Return y-Sprite(other).y
End Method
Thanks!


Michael Reitzenstein(Posted 2005) [#4]
The float from y - Sprite( other ).y is being cast to int - keep that in mind.


altitudems(Posted 2005) [#5]
ok? I'm not sure I understand your warning.

I don't think it makes any difference. It's not actualy changing the value of the Y field or even its type, Y will remain the same and still be a float. The returned value will be a Integer, but thats what we want to return. :)


marksibly(Posted 2005) [#6]
Ahh...

The conversion from float to int will drop the fraction, which might give problems, eg:

Int(.75-.5) returns 0.

Probably safest to go:

Return Sgn( y-Sprite(other).y )


Michael Reitzenstein(Posted 2005) [#7]
Yeah, but, the loss of accuracy means an object at 5.9 will have the same Z Order as 5.1. It doesn't really matter in this test program, but you'll want to keep it in mind for anything more complex.