MVC has been covered in so many places so there shouldn't be much to re-iterate here. Essentially you want your object graph, helpers, and logic to be contained in the model tier. The views will be the screens that get pushed out to fill the dynamic part of the page (and may contain a light amount of logic and helpers). And the controller, which be a lightweight implementation to serve the screens based on what was available from the object graphs, helpers, and logic.
Model
This should be where the meat of the application sits. It can be tiered out into a service layer, a logic layer, and an entity layer. What does this mean for your example?
Entity layer
This should house the definitions of your game's models and internal behaviors. For example, if you had a game for minesweeper, this would be where the board and square definitions were along with how they change their internal state.
function Location(x,y){
this.x = x;
this.y = y;
}
function MineTile(x,y){
this.flagged = false;
this.hasMine = false;
this.pristine = true;
this.location = new Location(x,y);
}
MineTile.prototype.expose = function(){
if( this.hasMine ) return false;
this.pristine = false;
return this.location;
};
So the MineTile will know its internal state, such as if it is showing or was examined (this.pristine
), if it was one of the tiles that has a mine (this.hasMine
) but will not determine if it was supposed to have a mine. That will be up to the logic layer. (To go even further into OOP, MineTile could inherit from a generic Tile).
Logic layer
This should house the complex ways that the application will interact with changing modes, keeping state, etc. So this would be where a mediator pattern would be implemented in order to maintain the state of the current game. This would be where the game logic resided for determining what happens during a game over for example, or for setting up which MineTiles will have a mine. It would make calls into the Entity layer to get instantiated levels based on logically determined parameters.
var MineSweeperLogic = {
construct: function(x,y,difficulty){
var mineSet = [];
var bombs = 7;
if( difficulty === "expert" ) bombs = 15;
for( var i = 0; i < x; i++ ){
for( var j = 0; i j < y; j++ ){
var mineTile = new MineTile(i,j);
mineTile.hasMine = bombs-- > 0;
mineSet.push(mineTile);
}
}
return mineSet;
},
mineAt: function(x,y,mineSet){
for( var i = 0; i < mineSet.length; i++ )
if( mineSet[i].x === x && mineSet[i].y === y ) return mineSet[i];
}
};
Service layer
This will be where the controller has access to. It will have access to the logic layer for building the games. A high level call may be made into the service layer in order to retrieve a fully instantiated game or a modified game state.
function MineSweeper(x,y,difficulty){
this.x = x;
thix.y = y;
this.difficulty = difficulty;
this.mineSet = MineSweeperLogic.construct(x,y,difficulty);
}
MineSweeper.prototype.expose = function(x,y){
return MineSweeperLogic.mineAt(x,y,this.mineSet).expose();
}
Controller
Controllers should be light weight, essentially this is what is exposed as the client to the model. There will be many controllers, so structuring them will become important. Controller function calls will be what the javascript calls hit based on UI events. These should expose the behaviors available in the service layer and then populate or in this case modify views for the client.
function MineSweeperController(ctx){
var this.context = ctx;
}
MineSweeperController.prototype.Start = function(x,y,difficulty){
this.game = new MineSweeper(x,y,difficulty);
this.view = new MineSweeperGameView(this.context,this.game.x,this.game.y,this.game.mineSet);
this.view.Update();
};
MineSweeperController.prototype.Select = function(x,y){
var result = this.game.expose(x,y);
if( result === false ) this.GameOver();
this.view.Select(result);
};
MineSweeperController.prototype.GameOver = function(){
this.view.Summary(this.game.FinalScore());
};
View
The views should be organized relative to the controller's behaviors. They will probably be the most intensive part of your application since it deals with canvasing.
function MineSweeperGameView(ctx,x,y,mineSet){
this.x = x;
this.y = y;
this.mineSet = mineSet;
this.context = ctx;
}
MineSweeperGameView.prototype.Update = function(){
//todo: heavy canvas modification
for(var mine in this.mineSet){}
this.context.fill();
}
So now you have your entire MVC setup for this one game. Or at least, a bare bones example, writing the whole game out would have been excessive.
Once this is all done, there will need to be a global scope for the application somewhere. This will hold the lifetime of your current controller, which is the gateway to all of the MVC stack in this scenario.
var currentGame;
var context = document.getElementById("masterCanvas").getContext('2d');
startMineSweeper.click = function(){
currentGame = new MineSweeperController(context);
currentGame.Start(25,25,"expert");
};
Using MVC patterns are very powerful, but do not worry too much about adhering to every nuance of them. In the end, it is the game experience that will determine if the application is a success :)
For consideration: Don't Let Architecture Astronauts Scare You by Joel Spolsky