Jump to content
XCOMUFO & Xenocide

Model And Components


rincewind

Recommended Posts

I thought I would open a new thread in order to discuss the concerns about the role of Model in the gamecomponents branch.

 

As dteviot pointed out, having all model-parts be a GameComponent is not the right way of doing things as it makes it all a very heavy component. On the other hand, just having everything hanging as a hierarchical tree off a static reference in the Xenocide-class is probably not the best idea either.

 

So maybe we can use this thread to find some solution.

 

Here's what I have for now in terms of thinking (the points aren't particularly ordered, I just put them here to provide some food for though).

 

* Model currently serves two purposes. It provides a storage container for GameState and thus effectivly everything that needs to be persisted in a savegame. On the other hand, it provides methods and logic to actually update this GameState. I think GeoTime provides a very good example for this.

 

* The Model somehow needs to be able to notify the View/UI of events. This is partly implemented in the trunk through use of GeoEvent. What makes me feel a little uncomfortable with it, is that it basicly reinvents the old-school C-style windows-event loop (ok, we are using Command-Objects, but still, it's a queue that is manually queried, etc). I think dteviot mentioned somewhere that we need it this way (and can't use delegates/.NEt-style events) because of the GUI requires them to be used asynchonously. Maybe we can dig into this a little deeper and see if we might not come up with a more elegant design.

 

* When we load a savegame (and stick to the thought that the Model-Classes actually ARE the gamestate and don't keep their identity when we load a new Game), we have to make sure, that the UI-Components update their references to the GameState-objects (this currently works since they access it by following the tree from the static reference in Xenocide.

 

* If we want to make use of the Services-structure inside the XNA-Framework, we need to give the Model-Objects access to the Game-Object. I tried to make this functionality available in the form of a baseclass GameStateComponent (it's basicly a very stripped down version of GameComponent that just supports the storage of a Game-reference. No Update() or Draw() methods). It has the advantage of being very lightweight and being serializable (which GameComponent is not). the notion of Services leads also to the question whether we can't just make the Model's available as Services to the UI-Components. Which in turn leads back to the question of updating the references when we load a game.

 

* Load/Save in general. We somehow have to serialize the GameState. Red knight hinted at a scheme he'd thought about in that msn discussion I posted. Maybe you can enlarge on it a little?

 

As a final note, what I think is not good about the static approach.

 

First, I think it makes the desing very rigid as I can't move something around inside the hierarchy of GameState without changing code in all places that access that particular object. Take research for example. It's currently in GeoData, what if we need to check from battlescape to see if we can actually use the picked-up plasma gun? Of course, you can argue that then we should have put it into the global state already now, but what about other stuff that might now be quite as easily predicted? As a service, we'd just change it's lifetime to also be available in battlescape and be done with it.

 

Second, I think most of XNA-Framework's idea of having independent GameComponents (whose update-methods should work independently), of separating Update/Draw and of decoupling components through services is related to the fact that the future in performance-demanding computing lies in multicore. I can imagine the framework running several threads that walk through the update-methods, etc. If we have a single entry point to all things GameState (the static reference in Xenocide, again), we have to serialize all access to this object. If we offer the functionality through different services, we can just serialize access per-service. As we are all aware that xeno still has a long way to got, I think this should be put into consideration as well.

 

Ok, I hope we can get some fruitful discussion here.

 

Rincewind

Link to comment
Share on other sites

Ok, maybe one "solution" to one of the issues I brought up:

 

The updating of references to GameState. Currently I tried to counter this in putting a thin wrapper on top of the actual GameState-Object which acts as the actual service and in turn holds the reference.

 

When the load-method deserializes a GameStateComponent, it injects the Game-object and the Component (GameSTATEComponent, not GameComponent) registers itself with the service, basicly as a datastore and functionality backend.

The service-wrapper currently only forwards to the registered component. If you want an example of this, take a look at ResearchService or GeoTimeService (This could probably even be semi-automated using Attributes for all methods that should be exposed in the service from a GameStateComponent and the service reflecting on the GameStateComponent and providing the appropriate wrappers).

class GeoTime : GameStateComponent
{
[ServiceMethod]
Time getGeoTime()
{
...
}
}

Game.Services.Add(typeof(XenoService<GeoTime>), new XenoService<GeoTime>());

 

Tell me what you think.

Edited by rincewind
Link to comment
Share on other sites

Model currently serves two purposes. It provides a storage container for GameState and thus effectivly everything that needs to be persisted in a savegame. On the other hand, it provides methods and logic to actually update this GameState. I think GeoTime provides a very good example for this.

Actually, the model represents the model part of a Model-View pattern. As such, model doesn?t just have the Game?s State (which obviously needs to be saved to the save game file.) And of course, if you?re doing OOP, you bundle the data and the methods that manipulate the data together into classes. :)

 

The Model somehow needs to be able to notify the View/UI of events. This is partly implemented in the trunk through use of GeoEvent. What makes me feel a little uncomfortable with it, is that it basically reinvents the old-school C-style windows-event loop (ok, we are using Command-Objects, but still, it's a queue that is manually queried, etc). I think dteviot mentioned somewhere that we need it this way (and can't use delegates/.NEt-style events) because of the GUI requires them to be used asynchronously. Maybe we can dig into this a little deeper and see if we might not come up with a more elegant design.

OK, the reason for GeoEvents is described right at the bottom of http://svn.projectxenocide.com/xenocide/xn...alogsToWork.htm. If you don?t understand the problem after reading the document, please let me know and I?ll try to explain better.

 

When we load a savegame (and stick to the thought that the Model-Classes actually ARE the gamestate and don't keep their identity when we load a new Game), we have to make sure, that the UI-Components update their references to the GameState-objects (this currently works since they access it by following the tree from the static reference in Xenocide.

You do realize that the Xenocide static members and Services are two not that different approaches to the same problem.

That is, for the game to work there?s got to be a number of objects. (e.g. Human Bases, UFO, Aircraft, and Research Tree.) These objects need to live somewhere, and components need to be able to find each other. E.g. Human bases need to be able to find their Aircraft, and vice versa.

One solution is to have a single, global, root node that everything can find (Xenocide), and then hang the rest of the game objects off it. I?ve tried to hang the objects of Xenocide in a logical hierarchy, but I could have made each game object a member of Xenocide. That is, flatten the hierarchy to a single level.

The Services model takes this to the next step, and instead of explicitly declaring each object as a member of the root node, the root has an associative container, that stores the objects and an identifier for each object. Then components can get a reference to other objects by supplying the identifier for the wanted object to the root node.

A point to note, the Xenocide class inherits from the Game class. And in the services model, the Game class is the root node holding the associative container.

 

As a final note, what I think is not good about the static approach.

 

First, I think it makes the design very rigid as I can't move something around inside the hierarchy of GameState without changing code in all places that access that particular object. Take research for example. It's currently in GeoData, what if we need to check from battlescape to see if we can actually use the picked-up plasma gun? Of course, you can argue that then we should have put it into the global state already now, but what about other stuff that might now be quite as easily predicted? As a service, we'd just change it's lifetime to also be available in battlescape and be done with it.

OK, I will concede that for some objects it?s not obvious where they should go in the hierarchy. However, I don?t see that as a big issue.

I?d also point out that I think the current design of Research (a single monolithic piece) is a mistake. It?s got three parts.

  • The Research Tree (the encoding of the XML file)
  • A project manager. (That keeps track of which projects are currently being worked on, updates their progress, allows projects to be started, stopped, and have scientists allocated to them.
  • A technology manager. Really, just a big set that tracks the technologies that have been achieved.

Reasons for this design.

  • It should not be necessary to pack the research tree into the save game.
  • Possibility of reusing the research project manager for production projects.

Second, I think most of XNA-Framework's idea of having independent GameComponents (whose update-methods should work independently), of separating Update/Draw and of decoupling components through services is related to the fact that the future in performance-demanding computing lies in multicore. I can imagine the framework running several threads that walk through the update-methods, etc. If we have a single entry point to all things GameState (the static reference in Xenocide, again), we have to serialize all access to this object. If we offer the functionality through different services, we can just serialize access per-service. As we are all aware that xeno still has a long way to got, I think this should be put into consideration as well.

Following links in hierarchy to find objects isn?t a problem in multithreading. Provided nothing changes. And if it does change, you?re going to have serialization problems whatever you do.

Link to comment
Share on other sites

×
×
  • Create New...