MVC is a useful way to develop games in a way that keeps the code portable and organized.
Lets define the components to the MVC design pattern with respect to games:
Model : the state of an object in the game
View : the visible representation of the model
Controller : the behavior of a model, modifies the state of an object
Simple enough!
So why is this cool? Your Model classes are completely portable, decoupled from a graphics library. The Views are the only classes aware of the graphics library, so for each graphics library you have View implementations for every Model type in your game. The portability of controllers is entirely dependent on the purpose of the controller. A controller could be for AI (portable), or could be for user input (not so portable).
So, how can we represent these as interfaces?
public interface Model {
public void update( UpdateState state );
public void draw( DrawState state );
public int getController();
public int getView();
}
public interface View<M> {
public void draw( M model, DrawState state );
}
public interface Controller<M> {
public void update( M model, UpdateState state );
}
And the all important registries:
public class ViewRegistry {
private static int nextId = 0;
private static View[] views = new View[128];
public static int newView() {
return nextId++;
}
public static void setView(int id, View view) {
views[id] = view;
}
public <M extends Model> static View<M> getView(int id) {
return (View<M>)views[id];
}
}
public class ControllerRegistry {
private static int nextId = 0;
private static Controller[] controllers = new Controller[128];
public static int newController() {
return nextId++;
}
public static void setController(int id, Controller controller) {
controllers[id] = controller;
}
public <M extends Model> static Controller<M> getController(int id) {
return (Controller<M>)controllers[id];
}
}
Not too bad, lets create the BaseModel class to tie some things together before further explanation
public class BaseModel implements Model
{
public void update( UpdateState state ) {
ControllerRegistry.getController( getController() ).update( this, state );
}
public void draw( DrawState state ) {
ViewRegistry.getView( getView() ).draw( this, state );
}
}
Cool! Now lets explain…
Essentially when a model is being drawn, it gets the View from the ViewRegistry, and the view actually handles the drawing. So why have the draw method in the Model code? In case the implementing class has custom drawing code!
This logic also applies to the controller.
So when you start your game, you register all your views so your models can be drawn and register your controllers so your models can be updated.
Lets look at an example:
public class Sprite extends BaseModel {
public static final int VIEW = ViewRegistry.newView();
public static final int CONTROLLER = ControllerRegistry.newController();
public float x, y, velx, vely;
public float w, h;
public int getView() {
return VIEW;
}
public int getController() {
return CONTROLLER;
}
}
public class SpriteView implements View<Sprite> {
public void draw( Sprite model, DrawState state ) {
// draw sprite here
}
}
public class SpriteController implements Controller<Sprite> {
public void update( Sprite model, UpdateState state ) {
model.x += model.vx * state.dt;
model.y += model.vy * state.dt;
}
}
// Before the game starts
ViewRegistry.setView( Sprite.VIEW, new SpriteView() );
ControllerRegistry.setController( Sprite.CONTROLLER, new SpriteController() );
Well look at that!
Now you can create a SpriteView for LWJGL, JOGL, Java2D, Java3D, etc.
A few things you could do:
- The model implementation could return a class level variable view/controller so sprites could have different views depending on their state or purpose.
- Create a generic PhysicsModel with position, velocity, and acceleration getters, and all you need is one PhysicsController and anything that implements PhysicsModel could use it! (Sprite, ParticleSystem, etc)
A note: Controllers could be morphed into components, so Models can have multiple components opposed to one (a controller for physics, a controller for spatial database, a controller for steering behaviors, etc).
I hope this pattern encourages you to design your game for portability whether you plan on porting or not!