用于ASP.net的MVC模块

下载MVCBricks_SRC - 492.58 KB 表的内容 介绍系统要求游戏闪屏的最终考虑历史 介绍 自从我写上一篇关于ASP的文章以来,已经有很长时间了。净的话题。这次我决定写一些关于它的东西,更具体地说,关于ASP。净MVC。 到目前为止,MVC方法是我在ASP.Net中最喜欢的方法。您没有背后的代码,也没有视图状态这一简单事实值得我考虑。 不用说,你不关心post - back。 此外,该应用程序使用了强大的jQuery库,使我们的生活更加轻松。正如读者将看到的,我尝试在任何可能的地方使用jQuery。这个库 对于我们这些需要在web应用程序上工作的开发人员来说,这真是一件幸事。 系统需求 要使用本文提供的MVC砖块游戏,如果您已经拥有Visual Studio 2010,也可以。如果你没有,你可以下载以下100%免费的开发工具直接从微软: Visual Web Developer 2010 Express 此外,您必须在启用CSS3的浏览器中打开它,因为这个应用程序使用CSS3 Webkit梯度。 闪屏 当游戏开始时,有一个简单的jQuery动画,它使“MVC砖块”的标题上升到屏幕中央。这是由 动画顶部的css属性的标题div: 隐藏,复制Code

function showSplashScreen() {
    $('.subTitle').css('visibility', 'hidden');
    $('.press').css('visibility', 'hidden');
    $('.title').animate({
        top: 200
    }, 1000, 'swing', function () {
        // Animation complete.
        $('.subTitle').css('visibility', 'visible');
        $('.press').css('visibility', 'visible');
    });
}

从上面的代码可以看出,当动画结束时,标题会出现在屏幕的中间,并会出现字幕。 游戏控制 游戏全部由键盘控制。当游戏处于介绍模式时,可以使用空格键开始一个新的游戏。当 游戏正在进行中,如果您想暂停游戏,请再次使用空格键。然后再用空格键重新开始游戏。 当游戏结束时,您再次按空格键打开游戏介绍屏幕。 使用左箭头键和右箭头键将掉落的棋子向左或向右移动。按向上箭头键 将工件旋转90度。最后,按下箭头键来加速棋子的下落。 命令空格键开始游戏/暂停游戏/恢复游戏/重启游戏左箭头键移动掉落的棋子到左箭头键移动掉落的棋子到右箭头键向下箭头键加速掉落的棋子 游戏规则 你对这类游戏一定很熟悉,不过我还是得解释一下游戏规则。 这里我们有一个空的10×16的板,包含160个空的位置。游戏一开始, 游戏引擎会随机生成一个新的片段,它会从顶部掉下来 板,以每秒1平方的速度下落。当落下的碎片发现一个障碍物(也就是, 另一部分固定在板的底部)然后它不能再掉下去,所以 掉落的碎片卡住了。然后游戏引擎会产生新的随机碎片,它们被堆积起来 直到堆到棋盘的顶端,此时游戏结束。用户必须这样做 控制每一块落下的碎片,向左、向右移动,或旋转它,放置新的碎片 块在最低可能空的地方,在董事会新块适合,以一种方式避免 堆起的棋子到达棋盘的顶部。此外,当用户填充任何板子行时,这些 行被清除,从而提供了一些额外的空间并延长了游戏时间。 “我”形状的“L”形“J”形“O”形“T”形的“S”形“Z”形状 游戏引擎可以随机生成上述任何形状。正如我们所看到的,每个形状都与一个与它相似的字母相联系。 对于每清一条线,用户的分数是10乘以游戏等级。也就是说,在第一级中每完成一行将得到10分。第二个层次 将给每清除行20分,以此类推。 当用户清除了10行数据时,就完成了每个级别。也就是说,要达到第5层,用户必须清除了40行。 当游戏结束时,将游戏分数与之前的最高分进行比较,如果有新的记录,则将其替换。 下一块让用户有机会以一种更容易容纳下一块落下的方式放置当前一块。 模型-视图-控制器 在我看来,MVC的美妙之处在于它坚持了关注点分离的原则。 与“经典的”(非mvc) ASP.net不同,您不需要在视图中放置业务逻辑。相反,视图只用于表示逻辑(例如解析和呈现原始数据) 数据或输入验证),以及表示本身。另一方面,为业务规则保留控制器(或另一层,如服务层)。 如果您查看应用程序中的javascript,您不会找到业务逻辑。相反,你会发现它真的又薄又轻。幸运的是, 由于我已经拥有了来自以前项目的brick游戏逻辑,所以我能够在服务器端几乎完整地维护托管代码, 并让视图通过我专门为这个MVC项目设计的新视图模型与它通信。 该模型 模型是由BoardViewModel和BrickViewModel类定义的,它包含了所有需要的信息 通过视图渲染游戏板、记分板并知道游戏是否结束。正如我们在下面看到的,大多数属性 的BoardViewModel类是原生类型,砖块和Next属性除外,它们是 BrickViewModel的二维数组,并保存构成当前快照的数据块和空白空间的数据 游戏板和砖块对应的下一块将从游戏板的顶部落下。 低级别的BrickViewModel类拥有关于每个单独砖块的信息:行、列和颜色名称。这些值 将被视图用来查找相应的div并相应地更新它们的背景颜色。 隐藏,复制Code

    public class BrickViewModel
    {
        public int Row { get; set; }
        public int Col { get; set; }
        public string Color { get; set; }
    }

    public class BoardViewModel
    {
        public BoardViewModel()
        {
            IsGameOver = false;
        }

        public BrickViewModel[] Bricks { get; set; }
        public int Score { get; set; }
        public int HiScore { get; set; }
        public int Lines { get; set; }
        public int Level { get; set; }
        public BrickViewModel[] Next { get; set; }
        public bool IsGameOver { get; set; }
    }
}

视图 该视图实际上不包含任何业务逻辑(在我们的示例中,没有游戏逻辑)。从这里我们可以看到它的基本目标: 设置一个计时器,它每200毫秒调用控制器以获得一个包含更新的游戏板快照(序列化为JSON)的ViewModel。解析返回的JSON以呈现板、分数、高分、水平和下一块。设置另一个计时器,每1000毫秒调用控制器,请求一个新的移动为落下的碎片。听键盘事件,并调用控制器开始一个新的游戏,暂停或恢复游戏,移动或旋转落下的棋子,或重新开始游戏。 这就是视图的作用。请注意,在传统的(非mvc) ASP.net应用程序中,类后面的代码将会 可能有一些业务逻辑。多亏了MVC,保持了关注点分离的原则,我们可以将业务逻辑从视图中移开。 我们使用jQuery语法在页面文档的按键事件上附加一个函数来处理用户的手势: 隐藏,收缩,复制Code

	$(document).keydown(function (event) {
	switch (event.keyCode) {
		case 32: //space
			if (gameState.current_state == 'intro')
				gameState.process('play');
			else if (gameState.current_state == 'paused')
				gameState.process('continue');
			else if (gameState.current_state == 'gameOver')
				gameState.process('showIntro');
			else
				gameState.process('pause');
			break;
		case 37: //left
			if (gameState.current_state == 'playing')
				moveLeft();
			break;
		case 38: //up
			if (gameState.current_state == 'playing')
				moveUp();
			break;
		case 39: //right
			if (gameState.current_state == 'playing')
				moveRight();
			break;
		case 40: //down
			if (gameState.current_state == 'playing')
				moveDown();
			break;
	}
});

下表显示了事件(例如计时器计时或按键)、BricksView端上类似于jquery的ajax调用以及BricksController端上调用的方法(动作)。 事件/键视图代码控制器代码每1000毫秒隐藏复制代码美元(文档)。everyTime(1000, function (i) { . ajax({美元 类型:“获得”, url:“滴答”, 缓存:假的, 数据类型:“json”, 函数(xhr,状态,错误){ / /警报(xhr.status); }, 成功:函数(json) { } }); }); 隐藏,复制Codepublic ActionResult () { BricksManager.Instance.Presenter.Tick (); 返回新JsonResult () {Data = "", JsonRequestBehavior = JsonRequestBehavior。AllowGet}; } 每隔200毫秒隐藏一次复制代码美元(文档)。function (i) { 如果(gameState。current_state == 'playing') { . ajax({美元 类型:“获得”, url:“GetBoard”, 缓存:假的, / /数据:{}, 数据类型:“json”, 函数(xhr,状态,错误){ / /警报(xhr.status); / /警报(错误); }, 成功:函数(json) { … } }); } }); 隐藏,复制Codepublic ActionResult GetBoard() { 返回new JsonResult() {Data = BricksManager.Instance.CurrentBoard, JsonRequestBehavior = JsonRequestBehavior。AllowGet}; } 隐藏,复制代码函数initializeBoard() { . ajax({美元 类型:“获得”, url:“InitializeBoard”, 缓存:假的, 数据类型:“json”, 函数(xhr,状态,错误){ 警报(xhr.status); }, 成功:函数(json) { } }); } 隐藏,复制Codepublic ActionResult InitializeBoard() { BricksManager.Instance.InitializeBoard (); 返回new JsonResult() {Data = "", JsonRequestBehavior = JsonRequestBehavior。AllowGet}; } 隐藏,复制代码函数moveLeft() { . ajax({美元 类型:“获得”, url:“MoveLeft”, 缓存:假的, 数据类型:“json”, 函数(xhr,状态,错误){ / /警报(xhr.status); }, 成功:函数(json) { } }); } 隐藏,MoveLeft() { BricksManager.Instance.Presenter.MoveLeft (); 返回new JsonResult() {Data = "", JsonRequestBehavior = JsonRequestBehavior。AllowGet}; } 隐藏,moveRight() { . ajax({美元 类型:“获得”, url:“MoveRight”, 缓存:假的, 数据类型:“json”, 函数(xhr,状态,错误){ / /警报(xhr.status); }, 成功:函数(json) { } }); } 隐藏,MoveRight() { BricksManager.Instance.Presenter.MoveRight (); 返回new JsonResult() {Data = "", JsonRequestBehavior J = sonRequestBehavior。AllowGet}; } 隐藏,moveDown() { . ajax({美元 类型:“获得”, url:“MoveDown”, 缓存:假的, 数据类型:“json”, 函数(xhr,状态,错误){ / /警报(xhr.status); }, 成功:函数(json) { } }); } 隐藏,MoveDown() { BricksManager.Instance.Presenter.MoveDown (); 返回new JsonResult() {Data = "", JsonRequestBehavior = JsonRequestBehavior。AllowGet}; } 隐藏,复制代码函数moveUp() { . ajax({美元 类型:“获得”, url:“MoveUp”, 缓存:假的, 数据类型:“json”, 函数(xhr,状态,错误){ / /警报(xhr.status); }, 成功:函数(json) { } }); } 隐藏,复制Codepublic ActionResult MoveUp() { BricksManager.Instance.Presenter.Rotate90 (); 返回new JsonResult() {Data = "", JsonRequestBehavior = JsonRequestBehavior。AllowGet}; } 这里基本上是我们视图所需的所有html。注意你在游戏屏幕上看到的所有元素都在那里, 除了砖块: 隐藏,收缩,复制Code

<body>
    <br/>
    <divclass="screen">
        <divid="title"class="title">
            <imgsrc="../../Content/images/Title.png"/>
            <divclass="subTitle">©2011 Marcelo Ricardo de Oliveira<br/>
            Made for The Code Project<imgsrc="../../Content/images/Bob.png"class="bob"/></div>
            <br/>
            <divclass="press">Press SPACE to start game!</div>
        </div>
        <divclass="centerPanel">
            <divclass="board">
            </div>
            <divclass="scorePanel">
                <div>
                    Score</div>
                    <divid="divScore"class="scoreText">000000</div>
                    <br/>
                <div>
                    HiScore</div>
                    <divid="divHiScore"class="scoreText">000000</div>
                    <br/>
                <div>
                    Lines</div>
                    <divid="divLines"class="scoreText">0</div>
                    <br/>
                <div>
                    Level</div>
                    <divid="divLevel"class="scoreText">0</div>
                    <br/>
                <div>
                    Next</div>
                    <divid="divNext"class="scoreText"></div>

            </div>
        </div>
        <divid="gamePaused">
            GAME PAUSED<br/>Press SPACE to continue!</div>
        </div>
        <divid="gameOver">
            GAME OVER<br/>Press SPACE to restart!</div>
        </div>
    </div>
</body>
</html>

这是因为砖是在应用程序启动时以dinamically方式生成的div。而不是硬编码这个div html, dinamic生成使得控制砖块如何生成,它们如何渲染等变得更加容易。 下面是生成所有砖块的代码,包括得分板上“Next”部分中的砖块。请注意jQuery是多么优雅 附加html到现有html元素的语法: 隐藏,复制Code

function createCells() {
    for (var row = 0; row < 16; row++) {
        for (var col = 0; col < 10; col++) {
            var divId = 'cell_' + row + '_' + col;
            var imgId = 'img_' + row + '_' + col;
            var divTag = '<div id="' + divId + '" name="brick" class="colorChip clearfix"></div>';
            $(divTag).appendTo('.board');
        }
        $('<div class="clear">').appendTo('.board');
        $('</div>').appendTo('.board');
    }

    for (var row = 0; row < 2; row++) {
        for (var col = 0; col < 4; col++) {
            var divId = 'next_' + row + '_' + col;
            var imgId = 'nextImg_' + row + '_' + col;
            var divTag = '<div id="' + divId + '" name="brick" class="colorChip clearfix"></div>';
            $(divTag).appendTo('#divNext');
        }
        $('<div class="clear">').appendTo('#divNext');
        $('</div>').appendTo('#divNext');
    }
}

BricksView方面最重要的部分之一是游戏板渲染。注意,我们不用 为砖造的像;相反,我们使用CSS3 Webkit梯度生成器,它只在启用CSS3的浏览器上工作: 隐藏,收缩,复制Code

$('#divScore').text(json.Score);
$('#divHiScore').text(json.HiScore);
$('#divLines').text(json.Lines);
$('#divLevel').text(json.Level);

$.each(json.Bricks, function (i, val) {
    $('#cell_' + val.Row + '_' + val.Col).css('background-image',
    '-webkit-gradient(linear, left top, right bottom, color-stop(0.0, ' + val.Color + '),
    color-stop(1.0, rgba(0, 0, 0, 0.0)))');
    $('#cell_' + val.Row + '_' + val.Col).css('border-color', val.Color);
});

for (var row = 0; row < 2; row++) {
    for (var col = 0; col < 4; col++) {
        $('#next_' + row + '_' + col).css('background-image',
        '-webkit-gradient(linear, left top, right bottom, color-stop(0.0, #000), color-stop(1.0, #000))');
        $('#next_' + row + '_' + col).css('border-color', '#333');
    }
}

$.each(json.Next, function (i, val) {
    $('#next_' + val.Row + '_' + val.Col).css('background-image',
    '-webkit-gradient(linear, left top, right bottom, color-stop(0.0, ' + val.Color + '),
    color-stop(1.0, rgba(0, 0, 0, 0.0)))');
    $('#next_' + val.Row + '_' + val.Col).css('border-color', val.Color);
});

这是Json查看器显示的Json快照。注意Json表示的红砖: 以下是视图中呈现的Score、High Score、line、Level信息的Json数据: 控制器 下面我们可以看到,控制器甚至比视图更笨。它的目标仅仅是暴露行为 调用辛苦工作的GameManager端方法,并返回(或不返回)json序列化的视图模型。 隐藏,收缩,复制Code

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using MVCBricks.Core;

namespace MVCBricks.Controllers
{
    [System.Web.Mvc.OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")]
    public class BricksController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }

        public ActionResult GetBoard()
        {
            return new JsonResult() { Data = BricksManager.Instance.CurrentBoard, 
                JsonRequestBehavior = JsonRequestBehavior.AllowGet };
        }

        public ActionResult Tick()
        {
            BricksManager.Instance.Presenter.Tick();
            return new JsonResult() { Data = "", JsonRequestBehavior = JsonRequestBehavior.AllowGet };
        }

        public ActionResult MoveLeft()
        {
            BricksManager.Instance.Presenter.MoveLeft();
            return new JsonResult() { Data = "", JsonRequestBehavior = JsonRequestBehavior.AllowGet };
        }

        public ActionResult MoveUp()
        {
            BricksManager.Instance.Presenter.Rotate90();
            return new JsonResult() { Data = "", JsonRequestBehavior = JsonRequestBehavior.AllowGet };
        }

        public ActionResult MoveRight()
        {
            BricksManager.Instance.Presenter.MoveRight();
            return new JsonResult() { Data = "", JsonRequestBehavior = JsonRequestBehavior.AllowGet };
        }

        public ActionResult MoveDown()
        {
            BricksManager.Instance.Presenter.MoveDown();
            return new JsonResult() { Data = "", JsonRequestBehavior = JsonRequestBehavior.AllowGet };
        }

        public ActionResult InitializeBoard()
        {
            BricksManager.Instance.InitializeBoard();
            
            return new JsonResult() { Data = "", JsonRequestBehavior = JsonRequestBehavior.AllowGet };
        }
    }
}

有限状态机 正如你所期望的,游戏必须有控制状态的方法,这样在游戏开始之前你就不会有掉落的碎片, 或者在比赛结束后。此外,分数不应该显示在介绍模式。 首先,我在javascript端使用布尔变量来处理游戏状态。“isPlaying”、“ispause”、“isGameOver”等等), 但这看起来既不优雅也不有效。因此,在开发的过程中,我注意到应用程序必须使用更好的状态管理, 比如有限状态机(FSM)。所以我搜索了一下,最后找到了一个简单易用的 JavaScript的有限状态机,由Anthony Blackshaw编写。 Blackshaw的FSM的实现非常简单。首先,声明一个状态机对象(在我们的例子中是gameState)。然后添加过渡 到状态机实例。每个转换定义: FSM.prototype。add_transition = function (action, state, callback, next_state) 操作名,它被调用来处理转换。转换工作时机器必须保持的初始状态。当转换发生时调用的回调函数。在转换结束时状态机切换到的下一个状态。 这是真正的实现: 隐藏,复制Code

<script type="text/javascript">

    //Finite State Machine for JavaScript
    //by Anthony Blackshaw
    //http: //antsdev.wordpress.com/2008/06/18/a-simple-js-finite-state-machine/

    var gameState = new FSM("intro");

    gameState.add_transition("play", "intro", changeIntroToPlaying, "playing");
    gameState.add_transition("pause", "playing", changePlayingToPaused, "paused");
    gameState.add_transition("continue", "paused", changePausedToPlaying, "playing");
    gameState.add_transition("end", "playing", changePlayingToGameOver, "gameOver");
    gameState.add_transition("showIntro", "gameOver", changeGameOverToIntro, "intro");

play动作隐藏副标题,并将标题的顶部动画到位置0(屏幕的顶部)。除此之外, 计分板和野猪都是可见的多亏了一个淡入效果的动画 他们的不透明度css属性.. 隐藏,复制Code

function changeIntroToPlaying() {
    initializeBoard();
    $('.subTitle').css('visibility', 'hidden');
    $('.press').css('visibility', 'hidden');

    $('.title').animate({
        top: 0
    }, 1000, 'swing', function () {
        // Animation complete.
        $('.scorePanel').animate({
            opacity: 1.0
        }, 1000, 'swing', function () {
            $('.scorePanel').css('visibility', 'visible');
        });

        $('.board').animate({
            opacity: 1.0
        }, 1000, 'swing', function () {
            $('.board').css('visibility', 'visible');
        });
    });
}

pause操作只是在游戏板上显示gamePaused div。 隐藏,复制Code

function changePlayingToPaused () {
    $('#gamePaused').css('visibility', 'visible');
}

暂停本身在游戏中发生,因为状态切换为“暂停”和 下面我们可以看到,除非游戏状态机处于“playing”状态,否则控制器的动作不会被调用: 隐藏,复制Code

...
$(document).everyTime(200, function (i) {

    if (gameState.current_state == 'playing') {

        $.ajax({
            type: "GET",
            url: "GetBoard",
...

continue操作只是在游戏板上隐藏gamePaused div。 隐藏,复制Code

function changePausedToPlaying() {
    $('#gamePaused').css('visibility', 'hidden');
}

由于状态切换回“玩”,游戏继续,因为控制器的动作 现在可以调用。 end操作只是在游戏板上显示gameOver div,表示堆 一块块砖长到了木板的顶端。 隐藏,复制Code

function changePlayingToGameOver () {
    $('#gameOver').css('visibility', 'visible');
}

showIntro动作使得分面板和游戏板都淡出,并将标题动画化回到屏幕中央。此外, 字幕演职员表n。 隐藏,复制Code

function changeGameOverToIntro() {
    $('#gameOver').css('visibility', 'hidden');

    $('.scorePanel').animate({
        opacity: 0.0
    }, 1000, 'swing', function () {
        $('.scorePanel').css('visibility', 'hidden');
    });

    $('.board').animate({
        opacity: 0.0
    }, 1000, 'swing', function () {
        // Animation complete.
        $('.board').css('visibility', 'hidden');
        $('.title').animate({
            top: 200
        }, 1000, 'swing', function () {
            // Animation complete.
            $('.subTitle').css('visibility', 'visible');
            $('.press').css('visibility', 'visible');
        });
    });
}

游戏管理器 GameManager是一个包含BricksController所需的所有方法的类 这样BricksView请求就可以被传递给游戏引擎,并且响应可以被返回 BricksView。 隐藏,复制Code

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MVCBricks.Core
{
    public class GameManager : MVCBricks.Core.IView
    {
        private static GameManager instance = null;
        private static BricksPresenter presenter = null;
        private static BoardViewModel currentBoard = null;

        private GameManager()
        {
            currentBoard = new BoardViewModel();
            currentBoard.Bricks = new BrickViewModel[] { };

            presenter = new BricksPresenter(this);
            presenter.InitializeBoard();
            presenter.Tick();
        }

当调用DisplayScore时,将收集所有的记分牌数据,以便通过来自控制器的单个调用,视图可以使用这些数据。 隐藏,复制Code

public void DisplayScore(int score, int hiScore, int lines,
int level, MVCBricks.Core.Shapes.IShape next)
{
    currentBoard.Score = score;
    currentBoard.HiScore = hiScore;
    currentBoard.Lines = lines;
    currentBoard.Level = level;
    currentBoard.Next = GetBricksArray(next.ShapeArray.GetUpperBound(1) + 1,
    next.ShapeArray.GetUpperBound(0) + 1, next.ShapeArray);
}

GetBricksArray方法将游戏板砖数组和下一个形状数组转换为一个颜色系统,该颜色系统将被转换为一个颜色系统 视图可以理解。 隐藏,收缩,复制Code

private BrickViewModel[] GetBricksArray(int rowCount, int colCount, IBrick[,] array)
{
    var bricksList = new List<BrickViewModel>();

    for (var row = 0; row < rowCount; row++)
    {
        for (var col = 0; col < colCount; col++)
        {
            var b = array[col, row];
            if (b != null)
            {
                bricksList.Add(new BrickViewModel()
                {
                    Row = row,
                    Col = col,
                    Color = b.Color.ToString().Replace("Color [", "").Replace("]", "")
                });
            }
            else
            {
                bricksList.Add(new BrickViewModel()
                {
                    Row = row,
                    Col = col,
                    Color = "rgba(0, 0, 0, 1.0)"
                });
            }
        }
    }
    return bricksList.ToArray();
}

最后考虑 非常感谢您阅读我的MVC砖块文章。我希望它在某种程度上对您有用,无论是通过这里介绍的MVC概念,还是通过jQuery语法 javascript部分轻薄而优雅,甚至因为游戏本身的乐趣。欢迎在下方评论。请分享你的想法,抱怨,建议,以便 接下来的文章会越来越好。 历史 2011-04-23:初始版本。2011-04-29:图片更正。2011-05-03:Json浏览器图片附件。2011-05-05:游戏经理解释。 本文转载于:http://www.diyabc.com/frontweb/news19294.html

posted @ 2020-08-14 01:26  DiyAbc-Eleven  阅读(149)  评论(0)    收藏  举报