OOP Advice

BlitzMax Forums/BlitzMax Programming/OOP Advice

Steve Elliott(Posted 2006) [#1]
I initially started with a general TGameObject which used a GameObjectList. So every GameObject that inherits the TGameObject is added to the TGameObjectList upon creation.

To the general TGameObject I added UpdateAll() and DrawAll() functions. Now this is great for updating and drawing the whole game in the main loop - TGameObject.UpdateAll() and TGameObjectDrawAll().

But when updating and drawing you don't always want to go through the whole GameObjectList - when updating just your group of 'Aliens' for example. So do you keep 2 lists? Keeping 2 lists works, but you're using twice the memory aren't you? Is there a better way?


767pilot(Posted 2006) [#2]
I have a different list for every type of object

ie Alienlist, Weaponlist, Asteriodlist etc

then I just go through each list to update


Steve Elliott(Posted 2006) [#3]
So do I at the moment - but after the convienience of using TGameObject.UpdateAll() and TGameObjectDrawAll() in the main loop it doesn't seem such a good solution - remember if you have lots of different characters you will have to add an update and draw for each one.


Gabriel(Posted 2006) [#4]
Why do you need two lists?

All objects :

For O:TGameObject=EachIn GameObjectList
Next


All SpaceShips

For S:TSpaceShip=EachIn GameObjectList
Next


Anyway, I see no harm in having two or three lists, if it gives you extra convenience or speed. You're talking about tiny amounts of memory to maintain a list of objects. I'm guessing three or four integers per entry, and in the overall scheme of 512meg machines, that's not going to add up to a hill of beans.


Perturbatio(Posted 2006) [#5]
why not make your GameObjectList contain lists of the various objects?

That way if you want to do updateAll, you just call the GameObjectList.UpdateAll which then invokes each list's UpdateAll.


H&K(Posted 2006) [#6]
The second list can be a tlist of only Tlinks, wereas the first list is a list of objects.

So no it doesnt need to double the memory


Sean Doherty(Posted 2006) [#7]
One list would probably work. My concern would be in areas like collision detection. Lets assume objects collide differently depend on the types.

For example, maybe asteroids are industructable.

Rather than keeping track of the kind of object, not object oriented, you could have an object for the physical properties of an object. i.e. m_bIsDestoyable

So TStarship has a TPhysicalPropertie object passed as one of the arguments during contruction.

I wish I had of done it this way. :) Of course, I needed at least one list per sector.


JoshK(Posted 2006) [#8]
My root class has a list of objects:

Type TEngineNode

Global list:TList=New TList

Field link:TLink
Field handle

Method New()
list.addfirst self
handle=HandleFromObject(self)
EndMethod

EndType


And all derivatives have their own list:
Type TEntity Extends TEngineNode

Global list:TList=New TList

Field link:TLink
Field handle

Method New()
list.addfirst self
handle=HandleFromObject(self)
EndMethod

EndType


So I can go through all objects in the program, or go through any class of objects.


Steve Elliott(Posted 2006) [#9]
Thanks guys got it working properly with one list now.

Gabe I didn't think of it like that - I tested your code by printing the total number of a particular type that was being accessed. And yes it gave me the correct number of objects for that type and no more.


Sean Doherty(Posted 2006) [#10]
I would recommend that you don't use a global variable for your list. If you ever need to make a second list, it will cause you a lot of rework.


Grey Alien(Posted 2006) [#11]
Sean: How else can you do it without a global variable? Sure the list should be an instance of a type that you can alter, but having the instance global makes perfect sense...or are you saying pass in the global as a parameter to any functions that use it? If so, I agree, but if the various functions that use it are in fact methods of the special list type, it'll be fine anyway.

Gabriel: Neat tip, saves having an a objecttype field and checking it as you loop through the main list.


Sean Doherty(Posted 2006) [#12]
I'll give you an example, off the top of my head (code is not exactly correct):


'Declare Local Variables.
local pTStarshipList:TStarshipList = Null
local pTStarship:TStarship = Null

'Create is a function, so it is static.
pTStarshipList = TStarshipList.Create()

'Create the Starship(pTStarshipList, dX, dY)
pTStarship = TStarship.Create(pTStarshipList, 100, 100)

'In the Create Function, the starship will add itself to the list.

'Game loop.
pTStarshipList.Update(dDeltaTime)
pTStarshipList.Render()


'Destroy the Starship
pTStarship.Destroy()

'In the destroy section the starship will remove itself from the list.

pTStarshipList.Destroy()


You don't use any globals if you can help it.


Sean Doherty(Posted 2006) [#13]
@Gabriel,

Are you saying that you can use a single list and it will only iterate through the type in your loop; dispite the list having mixed type? Please don't say that? I wish I knew it did that...

Thanks


Grey Alien(Posted 2006) [#14]
@Sean. What is the advantage of declaring them as local in the main program scope instead of global? Sure it forces you not to use them in functions, so you have to pass them in as variables, but I doubt there's any major speed bost or anything by not having them as Global.


Gabriel(Posted 2006) [#15]
Are you saying that you can use a single list and it will only iterate through the type in your loop; dispite the list having mixed type? Please don't say that? I wish I knew it did that...

Sorry Sean, that's what I'm saying.


SuperStrict

Type Thing
	Global List:TList
End Type

Type ThingA Extends Thing
	Method New()
		If List=Null
			List=New TList
		End If
		List.AddLast(Self)
	End Method
End Type

Type ThingB Extends Thing
	Method New()
		If List=Null
			List=New TList
		End If
		List.AddLast(Self)
	End Method
End Type

Local Counter:Int

For Counter=1 To 5
	Local AThing:ThingA=New ThingA
Next

For Counter=1 To 5
	Local BThing:ThingB=New ThingB
Next


For Local AnotherThingA:ThingA=EachIn Thing.List
	Print "HERE'S A THING A"
Next

For Local AnotherThingB:ThingB=EachIn Thing.List
	Print "HERE'S A THING B"
Next




Sean Doherty(Posted 2006) [#16]
Its not for speed! Its for design!

Not using global variables increases the cohesion and reduces the coupling of the design. With Global variables, you only get one instance ever. The Swift sprite library uses global variables and now it is a difficult to use it in ways that are unplanned.

There is a place for global variables, but generaly if your going to use them, I would recommend a singleton design pattern instead.


dmaz(Posted 2006) [#17]
Gabriel actually you don't need the "if list=null then list=new TList" in the extended types.

just do
Type Thing
Global List:TList = New TList
End Type


Sean Doherty(Posted 2006) [#18]
@Gabriel,

Cool! To bad you could get a count of a certain type without iterating through the loop. I'll give this a try when I have a chance!


dmaz(Posted 2006) [#19]
Sean, I don't understand what you're saying... the global mentioned here is a "type global", they're not standard "globals".


dmaz(Posted 2006) [#20]
To bad you could get a count of a certain type without iterating through the loop

I find it most helpful to save 2 things when dealing with lists. count and links. the count because it's easy to add, and links because saving those allows removing an object and movement through the list without iterating the whole thing.


Steve Elliott(Posted 2006) [#21]
Lol - looks as if I'm not the only one who didn't realise you could do it that way. It gives the best of both worlds it seems - quick access to update and draw the whole game - but also with a slight change you can access only the type required within the list of game objects.

Thanks for that.


dmaz(Posted 2006) [#22]
one other thing... Gabriel's tip is not for performace if you have a lot of objects. this is because you have to iterate through the entire list each time. You're not looking trough just ThingB or just ThingA's objects, you are iterating through all of them. Basically it just skips the objects that can't be cast.

So, if you have a lot of objects it's better to go with multiple lists, one in each extended type.


Grey Alien(Posted 2006) [#23]
sure, but I bet Gabriels tip is faster than implementing the loop yourself and doing If thistype then ...

Sean: OK, sure that's what I thought you meant.


SculptureOfSoul(Posted 2006) [#24]
sure, but I bet Gabriels tip is faster than implementing the loop yourself and doing If thistype then ...


I did a quick test, iterating over the list 10,000 times and found that Gabriels method to be about 5-10 ms faster then manually casting and checking each object.


Not a huge difference, but the test was on a small list. I'm sure the difference would be noticeable on a huge list that is getting iterated over every frame or two.

Of course, multiple smaller lists would be faster.


Grey Alien(Posted 2006) [#25]
hmm, that's not a big a difference as I'd have thought.


dmaz(Posted 2006) [#26]
no it's not but that's not what I was referring to. Say you have 3 different objects, 3300 of each. if they are all in the same list accessing an object means possibly iterating 9900 objects every time. While obviously, if in different lists, accessing all objects means iterating only 3300 each time.

it's a great trick if you don't have a ton of objects.


Gabriel(Posted 2006) [#27]
Gabriel actually you don't need the "if list=null then list=new TList" in the extended types.

It's just a code example to show what I meant. I mean if we're gonna get technical, it's probably not a good practice to call your types ThingA and ThingB either ;)

one other thing... Gabriel's tip is not for performace if you have a lot of objects. this is because you have to iterate through the entire list each time. You're not looking trough just ThingB or just ThingA's objects, you are iterating through all of them. Basically it just skips the objects that can't be cast.

Indeed, and I said this earlier when I suggested that Steve might want to have a list of all of them and separate lists as well to get the best speed and the most convenience.


Steve Elliott(Posted 2006) [#28]
Well my game is currently running at 5100 FPS so I guess I must be ok. ;-)

But there again it's a very simple game and isn't finished. It'll be interesting to try Gabe's suggestion when it does start to slow down as I add things.


Grey Alien(Posted 2006) [#29]
I actually do have separate lists and it works well for me.