programming practice: where do you manage lists?
BlitzMax Forums/BlitzMax Programming/programming practice: where do you manage lists?
| ||
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) |
| ||
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. |
| ||
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" |
| ||
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! |
| ||
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 :) |
| ||
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? |
| ||
ah, yeh of course. meant to have *field* there.Type TApp field characterCollection:TCharacterCollection ...etc. end type |
| ||
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. |
| ||
my pithy contribution:Type characterCollection Extends TList Method update() For character:TCharacter=EachIn Self character.update Next End Method End Type |
| ||
"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! |
| ||
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. |
| ||
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'. |
| ||
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. |
| ||
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.) |
| ||
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 :). |
| ||
"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. :) |
| ||
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. |
| ||
Btw, found this: http://gamearchitect.net/2008/09/13/an-anatomy-of-despair-managers-and-contexts/ Very helpful reading to anyone pondering this problem... |
| ||
I do something like thistype 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 |
| ||
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(); |