MVC在很多地方都涉及过,因此在这里不必重复太多。本质上,您希望对象图,助手和逻辑包含在模型层中。视图将是被弹出以填充页面的动态部分的屏幕(并且可能包含少量逻辑和帮助器)。控制器是一种轻量级的实现,可以根据对象图,助手和逻辑中的可用内容为屏幕提供服务。
模型
这应该是应用程序所在的位置。可以将其分为服务层,逻辑层和实体层。这对您的示例意味着什么?
实体层
这应该包含游戏模型和内部行为的定义。例如,如果您有一个扫雷游戏,那么这将是木板和正方形定义以及它们如何更改其内部状态的地方。
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;
};
因此,MineTile将知道其内部状态,例如它是否正在显示或已检查(this.pristine
),如果它是具有地雷的图块之一(this.hasMine
),但不会确定它是否应该具有地雷。这将取决于逻辑层。(要进一步进入OOP,MineTile可以从通用Tile中继承)。
逻辑层
这应该包含应用程序与更改模式交互,保持状态等的复杂方式。因此,将在这里实现调解器模式以保持当前游戏的状态。例如,这就是游戏逻辑所在的位置,用于确定游戏期间发生的情况,或设置哪个MineTiles拥有地雷。它将调用实体层,以基于逻辑确定的参数获取实例化的级别。
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];
}
};
服务层
这将是控制器可以访问的位置。它将有权访问用于构建游戏的逻辑层。可以在服务层中进行高层调用,以检索完全实例化的游戏或修改后的游戏状态。
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();
}
控制者
控制器应该轻巧,从本质上讲,这就是模型的客户端所暴露的东西。将有许多控制器,因此构造它们将变得很重要。控制器函数调用将是基于UI事件的javascript调用命中的对象。这些应该公开服务层中可用的行为,然后填充或在这种情况下修改客户端的视图。
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());
};
视图
视图应相对于控制器的行为进行组织。它们可能是应用程序中最密集的部分,因为它处理画布。
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();
}
因此,现在您已经为这一游戏设置了完整的MVC。或者至少是一个空洞的例子,把整个游戏写出来是多余的。
一旦完成所有这些操作,就需要在某个地方为应用程序提供全局作用域。这将保留当前控制器的生命周期,该控制器是此方案中所有MVC堆栈的网关。
var currentGame;
var context = document.getElementById("masterCanvas").getContext('2d');
startMineSweeper.click = function(){
currentGame = new MineSweeperController(context);
currentGame.Start(25,25,"expert");
};
使用MVC模式非常强大,但是不必太担心遵循它们的每一个细微差别。最后,游戏体验将决定应用程序是否成功:)
考虑:不要让建筑宇航员吓到您了Joel Spolsky