ZOrdering Question

BlitzMax Forums/BlitzMax Beginners Area/ZOrdering Question

QuickSilva(Posted 2009) [#1]
Hello, hopefully someone can help me with this small problem that I am trying to implement in my game.

I have a base type, in this case TEntity, which all other game objects are extended from. I have TPlayers, Enemies and so on. When a new instance of one of these types is created it is added to a single, global, list inside of TEntity.

OK, now on to the problem. I have a field inside TEntity that allows me to set a ZOrder for my extended types and I want to be able to draw them in the correct order based on those ZOrder values which will change dynamically throughout the game. Normally I would just do a simple,

...
TPlayer.Draw
TEnemy.Draw
...


in my main drawing loop in the order that I want them drawn but this will not work in this case. Can anyone see a simple way that I can get the code below to draw my objcts in their correct ZOrder positions?

Thanks for any help,
Jason.

SuperStrict

Type TEntity
	Global List:TList=New TList
	
	Field XPosition:Int
	Field YPosition:Int
	Field ZOrder:Int
	
	Method New()
		ListAddLast(List,Self)
	End Method
	
	Method Draw() Abstract
		
	Function DrawAll()
		Local Entity:TEntity
		
		For Entity=EachIn TEntity.List
			Entity.Draw
			
			DrawText Entity.ZOrder,Entity.XPosition+20,Entity.YPosition+18
		Next
	End Function
End Type

Type TPlayer Extends TEntity
	Method Draw()
		SetColor 255,0,0
			DrawRect XPosition,YPosition,50,50
		SetColor 255,255,255
	End Method
End Type

Type TEnemy Extends TEntity
	Method Draw()
		SetColor 255,255,0
			DrawRect XPosition,YPosition,50,50
		SetColor 255,255,255
		
		SetColor 0,0,255
			DrawRect XPosition+5,YPosition+5,50-10,50-10
		SetColor 255,255,255
	End Method
End Type

Graphics(640,480,0,60)

Local Player:TPlayer=New TPlayer
Player.XPosition=50
Player.YPosition=50
Player.ZOrder=1

Local Enemy1:TEnemy=New TEnemy
Enemy1.XPosition=80
Enemy1.YPosition=80
Enemy1.ZOrder=2

Local Enemy2:TEnemy=New TEnemy
Enemy2.XPosition=110
Enemy2.YPosition=110
Enemy2.ZOrder=3

While Not KeyHit(KEY_ESCAPE)
	Cls
	
	If KeyHit(KEY_SPACE)
		Player.ZOrder=Rand(1,3)
		Enemy1.ZOrder=Rand(1,3)
		Enemy2.ZOrder=Rand(1,3)
	EndIf
	
	TEntity.DrawAll()
	
	Flip
Wend
End



Warpy(Posted 2009) [#2]
You can make your own comparison function for the TList.Sort method:

	Function DrawAll()
		Local Entity:TEntity
		
		Function zordercmp:Int(o1:Object,o2:Object)
			Local e1:tentity,e2:tentity
			e1:tentity=tentity(o1)
			e2:tentity=tentity(o2)
			If e1.zorder<e2.zorder
				Return 1
			Else
				Return -1
			EndIf
		End Function
		
		Tentity.List.Sort True,zordercmp
		
		For Entity=EachIn TEntity.List
			Entity.Draw
			
			DrawText Entity.ZOrder,Entity.XPosition+20,Entity.YPosition+18
		Next
	End Function



Htbaa(Posted 2009) [#3]
You need to create a sort function.

Function SortEntityList_Z:Int(p1:TEntity, p2:TEntity)
	If p1 And p2
		Return p1.ZOrder - p2.ZOrder
	End If
End Function


And just before you start drawing:

TEntity.List.Sort(True, SortEntityList_Z)


Edit: Ah, Warpy beat me to it. Must say that in my original code my SortEntityList_Z function also only accepted Object types. I'm not sure anymore if it's required, but it think so. Couldn't test it.


QuickSilva(Posted 2009) [#4]
Thanks guys,

I was doing something like,

Select Entity.ZOrder
     Case 1
         Entity.DrawAll
     Case 2
         Entity.DrawAll
     Case 3
         Entity.DrawAll
End Select


Which I assumed would work and I couldn`t see why it didn`t. Is there a flaw in my logic?

Actually, are there any other ways to do this without using this list sorting method as I`m finding it a little hard to understand in practice.

I`ve implemented it and it works but I`d rather have a way that I can understand. The BMax docs aren`t helping as they offer no real explanation to the technique that you are both using. The parts that I do not understand are the (o1:object,o2:object) part, what are objects in this case exactly? and the e1:tentity=tentity(o1) part. We have already declared e1 as a tentity type so why do we then go on to say =tentity(o1)? Why is the (o1) in brackets here?

Sorry if this sounds stupid but I haven`t encountered this before.

Thanks for any further help.
Jason.

Function zordercmp:Int(o1:Object,o2:Object)
    Local e1:tentity,e2:tentity
    e1:tentity=tentity(o1)
    e2:tentity=tentity(o2)
   
    If e1.zorder<e2.zorder
	Return 1
    Else
 	Return -1
    EndIf
End Function



Nate the Great(Posted 2009) [#5]
changing your draw function in your first post to this will help

Function DrawAll(zmax:Int)
		Local Entity:TEntity
		
		For Local i:Int = 1 To zmax
			For Entity=EachIn TEntity.List
				If entity.zorder = i Then 
					Entity.Draw
					DrawText Entity.ZOrder,Entity.XPosition+20,Entity.YPosition+18
				EndIf
			Next
		Next
	End Function


now when you call drawall you must include the maximum z order and the zorder variable cannot be 0 or it won't be drawn


Gabriel(Posted 2009) [#6]
I was doing something like,

Select Entity.ZOrder
Case 1
Entity.DrawAll
Case 2
Entity.DrawAll
Case 3
Entity.DrawAll
End Select



Shouldn't that be Entity.Draw() in each case instead of DrawAll()

From the looks of your code above, you're drawing every entity multiple times on multiple layers.

EDIT: Didn't see Nate's reply above. He kinda already covered this.


Jesse(Posted 2009) [#7]
I do it kind of a lame way but it works for me:

	Method Sortz()
			list.clear()
			Local link:TLink
			List.addfirst(point[0])
			For Local i:Int = 1 Until 13 
				Local p:t3point = point[i]
				link = list.FirstLink()
				Repeat
					If p.sc <= t3point(link.value()).sc Then  'sc is the z component in my point
						List.insertbeforelink(p,link)
						Exit
					EndIf
					link = link.nextlink()
					If link = Null Then
						list.addlast(p)
						Exit
					EndIf
				Forever 
			Next
	End Method

I don't need it to be too fast so its fine.


Warpy(Posted 2009) [#8]
So, to explain the List Sort method:

Every TList has a built-in method which sorts the objects. Normally, it sorts based on the memory address, I think, but you can define your own comparison function to do what you want.

The comparison function has to take two Objects as its arguments. An 'Object' is like the base class in blitzmax - every data type is derived from the Object type. What this means is that once the entities have been passed into our comparison function, they need to be 'cast' back to the type TEntity so that the program can have access to the entity's fields and methods. You can cast something by using the form 'userobject:UserType = UserType( baseobject )'. It's a bit like a function that turns the given object into the right type.

(by the way, the 'True' argument in the TList.Sort line just means that you want things in ascending order, not descending)


QuickSilva(Posted 2009) [#9]
That`s much clearer thanks. I wish the docs included clear explanations like yours. Thanks to everyone else as well for your examples, each of them has helped me to understand the concept in different ways.

Jason.