programming practice: where do you manage lists?

BlitzMax Forums/BlitzMax Programming/programming practice: where do you manage lists?

gameproducer(Posted 2008) [#1]
I created a 'character' class and put a 'characterList' as a global variable under that class. I was thinking that I could use that list to keep in track about the characters and update them.

I also wrote a class function (not method) "updateAll" where I planned to go through the list of characters and call "character.update" for each of them. Now in the game I would call "TCharacter.updateAll" or something similar to update all characters.

I think this is bit messy way to do this, since now the character class also handles the list.

I thought to create characterHandler (static) class that would contain a list where characters would be put. This way in the game the TCharacter and TCharacterHandler classes would be separate, and I'd call "TCharacterHandler.update", which would loop through its own list and update all the characters.

I'm reading about design patterns and there's so much new stuff that I need a fresh brain (yours :)) here to help me out. How do you handle lists and do you have many classes?

Gimme a better solution, pretty please :)

EDIT: Looks like the handler class is something that I could use (good to have other people using this approach too)


ziggy(Posted 2008) [#2]
Why don't you make a CharacterCollection not static class? You can instantiate once to use 'as if it was static' or if your game grows and you need it at any time, you can have several charactercollection (or managers) at the same time! Each CharacterCollection could have a field of kind TList to store its characters.


gameproducer(Posted 2008) [#3]
ah, Collection - that's a bloody good word! :)
(hmm, at least to some extent...)

I was thinking that I could like having a static class, that way I wouldn't need to store the collection instances anywhere.

Okay, if you instantiate it once, where would you store it? Global variable sounds bit ugly in my opinion, and I would not like to have a "game class" that has a member variable "characterCollection:TCharacterCollection"


ziggy(Posted 2008) [#4]
I was thinking that I could like having a static class, that way I wouldn't need to store the collection instances anywhere.
yes, in the static class! You won't be able to create a static field on a static class unless you declare the field as a global (not really a field then).

Okay, if you instantiate it once, where would you store it? Global variable sounds bit ugly in my opinion, and I would not like to have a "game class" that has a member variable "characterCollection:TCharacterCollection"

If you're going to be OO, the best option IMHO would be:
Local MyApp:TApp = New TApp
App.Start()
Type TApp
   Method Start()
   End Method
End Type


Anything 'Global' should be a field of your TApp class. The 'global' scope of the application, should have only 2 lines of code:
Local MyApp:TApp = New TApp
App.Start()


It helps me to be organized. But this is a very personal way of doing things! there are others!


gameproducer(Posted 2008) [#5]
Yeh, I'm getting into that 2 lines option thingy.

But... regarding instances... this basically means that:

TApp, would also contain:
Type TApp
   global characterCollection:TCharacterCollection

...etc.
end type

Ah... but since everything is inside the "start" method, then I would not need to call "TApp.characterCollection" to get charactercollection - but I could use "characterCollection" (or "self.characterColleciton) to get the stuff I want.

Sneaky, and probably a good idea.

Good thinking Watson :)


ziggy(Posted 2008) [#6]
I would not make it Global, but a field. If at any point you need to launch 2 instances of the app object, you'll be able. I know I know this is not even possible but, who knows?


gameproducer(Posted 2008) [#7]
ah, yeh of course. meant to have *field* there.
Type TApp
   field characterCollection:TCharacterCollection

...etc.
end type



AlexO(Posted 2008) [#8]
I imagine if you're making a game you might have more classes that aren't characters that need updating. The framework I'm currently working on is structured in such a way that I don't have to go to the main loop every time and add another for loop to update that type. Saves on maintenance and adds to flexibility. Below is a very simplified version of what I'm talking about:


the only actual 'executing' code:
Local game:TGame = New TGame
game.Init()
game.Update()


This is the main game class. In fact, it's the _only_ true 'global' in my games. Anything I need to access I go through the global of this class. Init() just adds some characters to the game.
rem
bbdoc: basic game class
end rem
Type TGame
	Field database:TObjectDatabase = Null
	
	Method Init()
		database = New TObjectDatabase
		database.AddObject(New TCharacter)
		database.AddObject(New TCharacter)
	End Method
	
	Method Update()
		database.Update()
	End Method
End Type



this is a generic container class that holds all game objects:
rem
bbdoc: simple collection class.
end rem
Type TObjectDatabase
	Field _objects:TList = CreateList()
	
	Method AddObject(gameObj:TGameObject)
		_objects.AddLast(gameObj)
	End Method
	
	Method Update()
		For Local obj:TGameObject = EachIn _objects
			obj.Update()
		Next
	End Method
End Type


Any object that needs to be 'updated' extends the very basic TGameObject class:
rem
bbdoc: a very thin game object class that provides update() functionality
end rem
Type TGameObject Abstract
	
	Method Update() Abstract
		
End Type


A sample implementation of TGameObject:
rem
bbdoc: a game object that implements TGameObject's update() method
end rem
Type TCharacter Extends TGameObject

	Method Update()
		' w0o!
		Print "updating character!"
	End Method
	
End Type


Slightly more complex than a basic global list and an updateAll() function for each type. In reality, there's a lot more that goes into it, but to get started this works just fine. But what does it buy you? Well, once you've coded these classes you _rarely_ need to go and modify your main game update() loop every time you introduce a new type of object. Anyway, that's my two cents.


Warpy(Posted 2008) [#9]
my pithy contribution:

Type characterCollection Extends TList
   Method update()
      For character:TCharacter=EachIn Self
         character.update
      Next
   End Method
End Type



MGE(Posted 2008) [#10]
"Gimme a better solution, pretty please :) " Better? Different? If it's working, it's probably just fine as it is. :)

In my experience with bmax and oop, the only problem with having everything in one type, your game type tends to get HUGE and it can be difficult to pull something out of the type and use it elsewhere.

So I still like to use individual types as needed with all of their fields included, nothing inherited, so I can pull that type out and easily use it elsewhere. But I'm always coding a zillion things so my framework hasn't stablized yet. But as far as object collection, sure you can have all of your objects in one TList. I didn't realize until a few months ago that TList can contain different object types. :) Very handy!


plash(Posted 2008) [#11]
It's not a good idea to put EVERYTHING in a single list, if you plan on having one specific group of objects updated at a different time or more frequently.


AlexO(Posted 2008) [#12]

In my experience with bmax and oop, the only problem with having everything in one type, your game type tends to get HUGE and it can be difficult to pull something out of the type and use it elsewhere.



putting everything in one type is a bad idea for re-usability I agree. But that's a different problem that requires a different solution.


It's not a good idea to put EVERYTHING in a single list, if you plan on having one specific group of objects updated at a different time or more frequently.



It really depends on how and what you want to achieve. If you approach the idea that you wish to hold a 'database' of objects (ignoring the implementation details such as using Tmaps, lists, etc), you can see that 'querying' the database can become a viable solution to getting different groups out of the collection. Now how frequently or when you choose to update is a different solution also. Keep in mind, there's nothing stopping you from having multiple 'databases'.


Grey Alien(Posted 2008) [#13]
AlexO's solution is neat but I actually use Warpy's. Also for some very specific things I've been using the method detailed in GameProducer's first post i.e. handling the list as part of the actual Type.


ziggy(Posted 2008) [#14]
In my experience with bmax and oop, the only problem with having everything in one type, your game type tends to get HUGE and it can be difficult to pull something out of the type and use it elsewhere.

I supose this is related of how you organize things. I tend to split everything on small classes and I don't get this problem usually, but I get lots of files on a single application, as I also tend to write each class on a different file. I've got BLIde project or solutions with more than 600 or 700 files.

At the end, I think the best approach is the one that makes you feel better if you're coding alone, obviously if it doesn't has an impact on performance, maintainance and performance. When you iterato throug a list with several kind of objects and you're using eachin approach, all items are iterated, even the ones that are avoided becouse doesn't share the same interface as the eachin object, and in very huge lists, this theorically could add a performance impact. (just my two cents.)


AlexO(Posted 2008) [#15]

At the end, I think the best approach is the one that makes you feel better if you're coding alone, obviously if it doesn't has an impact on performance, maintainance and performance.



I concur :).


MGE(Posted 2008) [#16]
"I've got BLIde project or solutions with more than 600 or 700 files."

wow.. and I thought my game with 16 files was big. :)


gameproducer(Posted 2008) [#17]
putting everything in one type is a bad idea for re-usability I agree. But that's a different problem that requires a different solution.

Yes, I can see that putting everything in one object can be a problem, but like AlexO continues: nothing stops from having "several databases". In a way, a "a character collection" is one "database". So, I think this depends on the problem.

I'm also having one file per class, but I'd rather have certain clear modules that would not have dependancies in each other. For example, in your approach "character object" needs to extend game object. I don't want that :) - but for your framework it might be ideal since I believe your framework is one "closed box".

I think the characterCollection approach works best in my situation, although I keep in mind what many of you have said: different situations might require different approach, so even though I use collection here, I might end up using singleton somewhere and even those "loops inside classes" that Grey Alien mentions somewhere else.

Thanks everybody, this has been very helpful.


gameproducer(Posted 2008) [#18]
Btw, found this:
http://gamearchitect.net/2008/09/13/an-anatomy-of-despair-managers-and-contexts/

Very helpful reading to anyone pondering this problem...


deps(Posted 2008) [#19]
I do something like this
type Entity
  global list:tlist = createlist()

  function update_all()
    for local e:Entity = eachin list
      e.update()
    next
  endfunction

  method new()
    list.addlast(self)
  endmethod

  method update()
  endmethod
endtype


type Player extends Entity
  method update()
    ' User input and logic
  endmethod
endtype

type EnemyTank extends Entity
  method update()
  endmethod
endtype



Jim Teeuwen(Posted 2008) [#20]
Oh, how I love my Collections and Managers!
I use them all the time in C# and bmax.

Basically, if I have a class that will be instantiated more than once simultaneously, I make a separate collection class for it.
Preferably with my own custom collection class as it's base, because it contains event handlers for adding/removing and modifying items in the list and this can have a lot of useful purposes.

So in the case of your character type, It'd be something like this:

Type TCharacter
...
End Type

Type TCharacterCollection
Field list:TList; '// list of characters
....
End Type

I don't like making these collections static classes though. Mainly because I often find myself needing more than 1 of them.
If I do need a collection to be available globally, I create a static Global class which contains global fields to all the stuff I need available. This keeps it all much more centralized.

Type Global
Global Characters:TCharacterCollection;
Global Settings:TAppSettings;
Global Localizer:TLocalizer;
Global Plugins:TPlugins;
' etc...
End Type

While globally declared stuff is not generally considered 'best practice' in the OO world, it does occasionally solve a lot of problems.

The tricky part is that it also makes it very easy to just reference these global objects everywhere. Even in places where a slightly different solution would be much more clean and robust. Consequently, this can severely reduce the re-usability and isolation of the rest of your code/classes.

It's a trade-off I suppose. Ease of use over purist OOP-ism.

edit: That 'an-anatomy-of-despair-managers-and-contexts' article brings up the most important issue that exists with global managers/collections and is exactly why I use these things cautiously. As I pointed out earlier in this post, the interdependency of classes can become a real problem if you do not have /very/ strict control and insight into where the global stuff is created and when and who/what needs access to it. This can become a real headache at times. When it does, It is usually time to rethink your strategy and code design.

At least with keeping all the global stuff in a single static 'Global' class, you at least have strict control over what gets created/destroyed in which order. That does require you to know the rest of the code though, as cyclic references are easily made this way.

My Global class ensures this by being the very first and the very last class to be called in my programs.. It has a static Initialize() and a static Dispose() Function which take care of the ordered creation/destruction of the global objects.

Global.Initialize();
'// Entire Program goes here
Global.Dispose();