OOP Advice
BlitzMax Forums/BlitzMax Programming/OOP Advice
| ||
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? |
| ||
I have a different list for every type of object ie Alienlist, Weaponlist, Asteriodlist etc then I just go through each list to update |
| ||
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. |
| ||
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. |
| ||
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. |
| ||
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 |
| ||
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. |
| ||
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. |
| ||
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. |
| ||
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. |
| ||
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. |
| ||
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. |
| ||
@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 |
| ||
@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. |
| ||
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 |
| ||
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. |
| ||
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 |
| ||
@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! |
| ||
Sean, I don't understand what you're saying... the global mentioned here is a "type global", they're not standard "globals". |
| ||
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. |
| ||
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. |
| ||
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. |
| ||
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. |
| ||
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. |
| ||
hmm, that's not a big a difference as I'd have thought. |
| ||
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 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. |
| ||
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. |
| ||
I actually do have separate lists and it works well for me. |