views:

1082

answers:

4

I'm trying to write a simple RPG. So far, each time I try to start it instantly becomes a mess and I don't know how to organize anything. So I'm starting over, trying to prototype a new structure that is basically the MVC framework. My app starts execution in the Controller, where it will create the View and Model. Then it will enter the game loop, and the first step in the game loop is to collect user input.

User input will be collected by a part of the View, because it can vary (a 3D View will directly poll user input, whereas maybe a remote View will receive it over a telnet connection, or a command-line view would use System.in). The input will be translated into messages, and each message will be given to Controller (by a method call) which can then interpret the message to modify Model data, or send data over the network (as I am hoping to have a networking option).

This message handling technique can also be used, in the event of a networked game, to process network messages. Am I keeping the spirit of the MVC so far?

Anyway my question is, what is the best way to represent these messages?

Here is a use case, with each message in italics: Let's say the user starts the game and chooses character 2. Then the user moves to coordinates (5,2). Then he says to public chat, "hi!". Then he chooses to save and quit.

How should the view wrap up these messages into something the controller can understand? Or do you think I should have separate controller methods like chooseCharacter(), moveCharacterTo(), publicChat()? I'm not sure that such simple implementation would work when I move to a networked game. But at the other end of the extreme, I don't want to just send strings to the Controller. It's just tough because the choose-character action takes one integer, the move-to takes two integers, and the chat takes a string (and a scope (public private global) and in the case of private, a destination user); there's no real set data type to it all.

Also any general suggestions are very welcome; am I worrying about this at the right time? Am I headed down the right path to a well-laid-out MVC app? Is there anything I've forgotten?

Thanks!

A: 

While I'm not completely convinced that MVC lends itself well to game design, there are some articles out there that go over the basics of where to put different bits of game logic using an MVC architecture. Here's a quick reference that answers quite a few of your questions:

Game Architecture: Model-View-Controller

Justin Niessner
+2  A: 

I'm not so sure an MVC framework is correct for a game, but I'll assume you're creating a game server for, e.g., a MUD or simple MMPROGOOGPRG, and that code readability and upgradeability is more important for you than raw performance.

It depends on how many users you want to support at the same time, and the capabilities of your game server. You could start off with a text based I/O, and then move to a binary or XML representation as your project matured.

I would certainly have different actions, with a different class doing each possible command.

Your front-end parser would create UserAction objects (actually subclasses, T extends UserAction) from the network/view->controller layer. This allows you to change how your networking operates down the line without tearing your core application apart. You're probably already thinking that you could use custom serialisation or similar for the messages with these UserAction objects. This UserAction would be passed onto its UserActionHandler (Command) implementation via a Factory or just checking a CommandEnum field within in a switch. Said Handler will then do the necessary magic on the model, and the controller would notice model state changes and send notifications to other players/views, and so on and so forth.

JeeBee
Voted up for "Your front-end parser would create UserAction objects (actually subclasses, T extends UserAction) from the network/view->controller layer"
Aaron
+24  A: 

(Disclaimer: I never programmed games in Java, only in C++. But the general idea should be applicable in Java too. The ideas I present are not my own, but a mash-up of solutions I found in books or "on the internet", see references section. I employ all this myself and so far it results in a clean design where I know exactly where to put new features I add.)

I am afraid this will be a long answer, it might not be clear when reading for the first time, as I can't describe it just top-down very well, so there will be references back and forth, this is due to my lacking explaining skill, not because the design is flawed. In hindsight I overreached and may even be off-topic. But now that I have written all this, I can't bring myself to just throw it away. Just ask if something is unclear.

Before starting to design any of the packages and classes, start with an analysis. What are the features you want to have in the game. Don't plan for a "maybe I'll add this later", because almost certainly the design decisions you make up-front before you start to add this feature in earnest, the stub you planned for it will be insufficient.

And for motivation, I speak from experience here, don't think of your task as writing a game engine, write a game! Whatever you ponder about what would be cool to have for a future project, reject it unless you put it in the game you are writing right now. No untested dead code, no motivation problems due to not being able to solve a problem that isn't even an issue for the immediate project ahead. There is no perfect design, but there is one good enough. Worth keeping this in mind.

As said above, I don't believe that MVC is of any use when designing a game. Model/View separation is not an issue, and the controller stuff is pretty complicated, too much so as to be just called "controller". If you want to have subpackages named model, view, control, go ahead. The following can be integrated into this packaging scheme, though others are at least as sensible.

It is hard to find a starting point into my solution, so I just start top-most:

In the main program, I just create the Application object, init it and start it. The application's init() will create the feature servers (see below) and inits them. Also the first game state is created and pushed on top. (also see below)

Feature servers encapsulate orthogonal game features. These can be implemented independently and are loosely coupled by messages. Example features: Sound, visual representation, collision detection, artificial intelligence/decision making, physics, and so on. How the features themselves are organized is described below.

Input, control flow and the game loop

Game states present a way to organize input control. I usually have a single class that collects input events or capture input state and poll it later (InputServer/InputManager) . If using the event based approach the events are given to the single one registered active game state.

When starting the game this will be the main menu game state. A game state has init/destroy and resume/suspend function. Init() will initialize the game state, in case of the main menu it will show the top most menu level. Resume() will give control to this state, it now takes the input from the InputServer. Suspend() will clear the menu view from the screen and destroy() will free any resources the main menu needs.

GameStates can be stacked, when a user starts the game using the "new game" option, then the MainMenu game state gets suspended and the PlayerControlGameState will be put onto the stack and now receives the input events. This way you can handle input depending on the state of your game. With only one controller active at any given time you simplify control flow enormously.

Input collection is triggered by the game loop. The game loop basically determines the frame time for the current loop, updates feature servers, collects input and updates the game state. The frame time is either given to an update function of each of these or is provided by a Timer singleton. This is the canonical time used to determine time duration since last update call.

Game objects and features

The heart of this design is interaction of game objects and features. As shown above a feature in this sense is a piece of game functionality that can be implemented independently of each other. A game object is anything that interacts with the player or any other game objects in any way. Examples: The player avatar itself is a game object. A torch is a game object, NPCs are game objects as are lighting zones and sound sources or any combination of these.

Traditionally RPG game objects are the top class of some sophisticated class hierarchy, but really this approach is just wrong. Many orthogonal aspects can't be put into a hierarchy and even using interfaces in the end you have to have concrete classes. An item is a game object, a pick-able item is a game object a chest is a container is an item, but making a chest pick-able or not is an either or decision with this approach, as you have to have a single hierarchy. And it gets more complicated when you want to have a talking magic riddle chest that only opens when a riddle is answered. There just is no one all fitting hierarchy.

A better approach is to have just a single game object class and put each orthogonal aspect, which usually is expressed in the class hierarchy, into its own component/feature class. Can the game object hold other items? Then add the ContainerFeature to it, can it talk, add the TalkTargetFeature to it and so on.

In my design a GameObject only has an intrinsic unique id, name and location property, everything else is added as a feature component. Components can be added at run-time through the GameObject interface by calling addComponent(), removeComponent(). So to make it visible add a VisibleComponent, make it make sounds, add an AudableComponent, make it a container, add a ContainerComponent.

The VisibleComponent is important for your question, as this is the class that provides the link between model and view. Not everything needs a view in the classical sense. A trigger zone will not be visible, an ambient sound zone won't either. Only game objects having the VisibleComponent will be visible. The visual representation is updated in the main loop, when the VisibleFeatureServer is updated. It then updates the view according to the VisibleComponents registered to it. Whether it queries the state of each or just queues messages received from them depends on your application and the underlying visualization library.

In my case I use Ogre3D. Here, when a VisibleComponent is attached to a game object it creates a SceneNode that is attached to the scene graph and to the scene node an Entity (representation of a 3d mesh). Every TransformMessage (see below) is processed immediately. The VisibleFeatureServer then makes Ogre3d redraw the scene to the RenderWindow (In essence, details are more complicated, as always)

Messages

So how do these features and game states and game objects communicate with each other? Via messages. A Message in this design is simply any subclass of the Message class. Each concrete Message can have its own interface that is convenient for its task.

Messages can be sent from one GameObject to other GameObjects, from a GameObject to its components and from FeatureServers to the components they are responsible for.

When a FeatureComponent is created and added to a game object it registers itself to the game object by calling myGameObject.registerMessageHandler(this, MessageID) for every message it wants to receive. It also registers itself to its feature server for every message it wants to receive from there.

If the player tries to talk to a character it has in its focus, then the user will somehow trigger the talk action. E.g.: If the char in focus is a friendly NPC, then by pressing the mouse button the standard interaction is triggered. The target game objects standard action is queried by sending it a GetStandardActionMessage. The target game object receives the message and, starting with first registered one, notifies its feature components that want to know about the message. The first component for this message will then set the standard action to the one that will trigger itself (TalkTargetComponent will set standard action to Talk, which it will receive too first.) and then mark message as consumed. The GameObject will test for consumption and see that it is indeed consumed and return to caller. The now modified message is then evaluated and the resulting action invoked

Yes this example seems complicated but it already is one of the more complicated ones. Others like TransformMessage for notifying about position and orientation change are easier to process. A TransformMassage is interesting to many feature servers. VisualisationServer needs it to update GameObject's visual representation on screen. SoundServer to update 3d sound position and so on.

The advantage of using messages rather than invoking methods should be clear. There is lower coupling between components. When invoking a method the caller needs to know the callee. But by using messages this is completely decoupled. If there is no receiver, then it doesn't matter. Also how the receiver processes the message if at all is not a concern of the caller. Maybe delegates are a good choice here, but Java misses a clean implementation for these and in case of the network game, you need to use some kind of RPC, which has a rather high latency. And low latency is crucial for interactive games.

Persistence and marshalling

This brings us to how to pass messages over the network. By encapsulating GameObject/Feature interaction to messages, we only have to worry about how to pass messages over the network. Ideally you bring messages into a universal form and put them into a UDP package and send it. Receiver unpacks message to a instance of the proper class and channels it to the receiver or broadcasts it, depending on the message. I don't know whether Java's built-in serialization is up to the task. But even if not, there are lots of libs that can do this.

GameObjects and components make their persistent state available via properties (C++ doesn't have Serialization built-in.) They have an interface similar to a PropertyBag in Java with which their state can be retrieved and restored.

References

  • The Brain Dump: The blog of a professional game developer. Also authors of the open source Nebula engine, a game engine used in commercially successful games. Most of the design I presented here is taken from Nebula's application layer.
  • Noteworthy article on above blog, it lays out the application layer of the engine. Another angle to what I tried to describe above.
  • A lengthy discussion on how to lay out game architecture. Mostly Ogre specific, but general enough to be useful for others too.
  • Another argument for component based designs, with useful references at the bottom.
haffax
Wow. That is a lot to think about. And more confusion for me, yay! Just when I thought I was settled on the MVC pattern... Lol. But it's good for me. I've got 3 years of college left to figure all this stuff out. :)
Ricket
Extremely helpful answer (I'm currently designing the message passing system for a game engine)Just wanted to add a thanks to Haffax for that one... :D
Ryan Rohrer
This great! Although I would very much appreciate a class diagram or quick tutorial on how the design patterns you mentioned are implemented. Thanks heaps!
Louis
A: 

Make mine another answer for "MVC considered potentially harmful in games". If your 3D rendering is 'a view' and your network traffic is 'a view', then don't you end up with remote clients end up essentially treating a view as a model? (Network traffic may look like just another view mechanism when you're sending it, but at the receiving end that's your definitive model upon which your game is based.) Keep MVC where it belongs - separation of visual presentation from logic.

Generally, you want to work by sending a message to the server, and waiting until you get a response. Whether that server is on another continent or within the same process doesn't matter if you handle it the same way.

Let's say the user starts the game and chooses character 2. Then the user moves to coordinates (5,2). Then he says to public chat, "hi!". Then he chooses to save and quit.

Keep it simple. MUDs used to simply send the command in plain text (eg. "SELECT character2", "MOVE TO 5,2", "SAY Hi") and there's little reason why you couldn't do that, if you're comfortable writing the text parser.

A more structured alternative would be to send a simple XML object, since I know you Java guys love XML ;)

<message>
    <choose-character number='2'/>
</message>


<message>
    <move-character x='5' y='2'/>
</message>

<!--- etc --->

In commercial games we tend to have a binary structure which contains a message-type-id and then an arbitrary payload, with serialisation to pack and unpack such messages at each end. You wouldn't need that sort of efficiency here however.

Kylotan
Well the network is special because it both sends and receives. It is treated like an input device, in that network messages are translated to game messages and sent to the controller. But then it also needs to listen to game messages generated by the actual input devices and relay them over the network, so that the remote server can broadcast them and reply back. There is actually no sending and waiting... It all needs to be asynchronous. Plain text is great over a network connection but not within the app, it makes no sense to convert a button press to a string,
Ricket
hand it to the controller, parse it back to something and then send it to the model. It's akin to taking a number, multiplying it by 2, then dividing it by 2, then using the number. As far as XML, there's no need to look down on Java, not that I even know what you mean. I am very against XML. Regardless, you and I both know it wasn't really a good suggestion.
Ricket
I think what I'm more going for, is Separated Presentation. http://martinfowler.com/eaaDev/SeparatedPresentation.html - if you're still against that for a game then I dunno what to tell you. But if you're only against MVC for some other reason then, I still don't understand.
Ricket
I'm afraid my suggestion was entirely serious from start to finish. Plain text is a perfectly reasonable approach if you know for sure that you have to support network input later. There's no point writing 2 systems when 1 will do. You don't need the most efficient system, you need the most reliable and flexible.As for MVC/separated presentation, that's fine for your graphics, but shoehorning non-visual aspects into that such as networking (and later perhaps AI) is just going to cause you trouble. That's why pretty much nobody here is recommending MVC for this.
Kylotan
As for the Java comment, it was deliberately flippant and was just restating the belief of programmers of other languages that Java uses XML in far more places than it is actually suited for. However this example is one where it can work well, simplifying your message parsing and providing a tested serializable format.
Kylotan