65.9K
CodeProject 正在变化。 阅读更多。
Home

Coding4Fun:使用 CSS Grid Layout & Blend 5 构建下落式方块游戏

starIconstarIconstarIconstarIconstarIcon

5.00/5 (2投票s)

2013年5月17日

CPOL

8分钟阅读

viewsIcon

16973

Coding4Fun:使用 CSS Grid Layout & Blend 5 构建下落式方块游戏

我想与大家分享一个微软内部长期保守的秘密。这是关于微软在 IE10 和 Windows 应用商店应用中构想的 CSS 网格布局 概念的真实故事。你们中的许多人可能认为这个规范是为了帮助开发者获得更好的网站和应用程序布局引擎。但最初的动机完全不同。最根本的目标是为了能够轻松创建下落式方块游戏!但我确信你们现在还不信服。因此,我将使用 Blend 5 作为辅助工具来证明这一点。好吧,我们开始吧!

先决条件:要学习本教程,您需要首先

  1. 在您的机器上下载/购买并安装 Windows 8 RTMhttp://msdn.microsoft.com/en-US/windows/apps/br229516.aspx
  2. 下载并安装适用于 Windows 8 的 Visual Studio 2012 Express RTM 的免费版本: http://msdn.microsoft.com/en-US/windows/apps/br229516.aspx,其中包含 Expression Blend 5 for Visual Studio,或者使用更高版本。

第 1 步:通过 Blend 5 发现 CSS 网格布局背后的秘密

启动 Expression Blend 5,创建一个名为“TheRealCSSGridStory ”的 HTML(Windows 应用商店)空白应用程序项目。

image

替换

<p>Content goes here</p>

有了

<div class="mainGrid">
</div>

让我们使用分数单位创建一个包含 10 列和 20 行的网格,无论屏幕分辨率如何。为此,请添加此 CSS 规则

.mainGrid {
    display<span class="code-none">: -ms-grid<span class="code-none">;
    width<span class="code-none">: 100%<span class="code-none">;
    height<span class="code-none">: 100%<span class="code-none">;
    -ms-grid-columns<span class="code-none">: (1fr)[10]<span class="code-none">;
    -ms-grid-rows<span class="code-none">: (1fr)[20]<span class="code-none">;
<span class="code-none">}</span></span></

在 Live DOM 中选择 <div> mainGrid 元素,您应该会看到这个

image

让我们在这个漂亮的网格中绘制一个形状。将此 HTML 块添加到 main grid 中

<div class="shape1">
</div>

并插入与之关联的 CSS

.shape1 {
    -ms-grid-column<span class="code-none">: 4<span class="code-none">;
    -ms-grid-row<span class="code-none">: 3<span class="code-none">;
    -ms-grid-column-span<span class="code-none">: 3<span class="code-none">;
    -ms-grid-row-span<span class="code-none">: 2<span class="code-none">;
    background-color<span class="code-none">: red<span class="code-none">;
<span class="code-none">}</span></span></

您现在应该在 Blend 5 中看到这个

image

很酷,但还没有任何东西看起来像下落式方块游戏的棋子。让我们来处理这个问题。在 shape1 中添加这两个 DIV

<div class="line1shape1"></div>
<div class="line2shape1"></div>

并将之前的 .shape1 规则替换为此 CSS 块

.shape1 {
    -ms-grid-column<span class="code-none">: 4<span class="code-none">;
    -ms-grid-row<span class="code-none">: 3<span class="code-none">;
    -ms-grid-column-span<span class="code-none">: 3<span class="code-none">;
    -ms-grid-row-span<span class="code-none">: 2<span class="code-none">;
    display<span class="code-none">: -ms-grid<span class="code-none">;
    -ms-grid-columns<span class="code-none">: 1fr 1fr 1fr<span class="code-none">;
    -ms-grid-rows<span class="code-none">: 1fr 1fr<span class="code-none">;
    width<span class="code-none">: 100%<span class="code-none">;
    height<span class="code-none">: 100%<span class="code-none">;
<span class="code-none">}
.line1shape1 <span class="code-none">{
    -ms-grid-column-span<span class="code-none">: 2<span class="code-none">;
    background-color<span class="code-none">: red<span class="code-none">;
    
<span class="code-none">}

.line2shape1 <span class="code-none">{
    -ms-grid-column<span class="code-none">: 2<span class="code-none">;
    -ms-grid-row<span class="code-none">: 2<span class="code-none">;
    -ms-grid-column-span<span class="code-none">: 2<span class="code-none">;
    background-color<span class="code-none">: red<span class="code-none">;
<span class="code-none">}</span></span></span></span></span></span></

shape1 当前跨越 3 列和 2 行。然后,我将在此区域内创建一个新网格,该网格包含 3 列和 2 行,以便单元格的大小与主网格的单元格完全相同,无论分辨率如何!

完成后,我将创建 2 行来模拟下落式方块游戏的 Z 形。您现在应该看到这个结果

z-shape in Tetris

更好的是,尝试使用设备选项卡中提供的各种视图,您会发现我们的游戏已经在实现 响应式设计!这真是太酷了,不是吗? Smile | <img src= " />

例如,这是快照视图和纵向视图的输出


z-shape in landscape view

现在让我们解决另一个问题。下落式方块游戏的网格由正方形组成。我们当前的响应式设计将宽度拉伸到 100%。为 Windows 应用商店构建 Windows 8 应用程序,大多数情况下会遇到宽屏显示器(当前的平板电脑为 1366x768 或 1920x1080,大多数台式机为 16:9)。因此,假设针对宽屏比例可以满足几乎所有情况。要计算正确的响应式宽度,您需要执行:9/16 * 10/20(主游戏网格的比例)等于:28.125%

然后添加此规则以定位全屏横向模式下的主网格

@media screen and (-ms-view-state: fullscreen-landscape) {
    .mainGrid {
        width<span class="code-none">: 28.125%<span class="code-none">;
        <span class="code-none">}
}</span></span></

现在让我们使用…… CSS 网格布局再次居中游戏网格!(您现在应该开始相信它是真正为下落式方块游戏设计的!)。

body 元素切换为 –ms-grid,由 1 列和 1 行组成

body {
    display<span class="code-none">: -ms-grid<span class="code-none">;
    -ms-grid-columns<span class="code-none">: 1fr<span class="code-none">;
    -ms-grid-rows<span class="code-none">: 1fr<span class="code-none">;
    width<span class="code-none">: 100%<span class="code-none">;
    height<span class="code-none">: 100%<span class="code-none">;
<span class="code-none">}</span></span></

现在只需将此属性添加到与主网格关联的 CSS 中

-ms-grid-column-align: center;

网格现在已居中

此时,您可能已经震惊了。“*我怎么会错过这个不可思议的秘密?*”您一定在想。深呼吸。现在您知道了秘密,让我们一起继续这个教程,探索 CSS 规范组合在一起提供的其他绝佳可能性。

第 2 步:移动或旋转形状

我的第一个想法是尝试通过尽可能多地使用 CSS 来避免 JavaScript。我最初尝试使用 CSS3 动画 来在各种行/列中移动和动画化形状。但坏消息是,您无法通过 CSS3 动画更改 –ms-grid-column–ms-grid-row 的值。因此,这将由一些 JavaScript 代码来完成。

然后我开始思考如何旋转形状。 CSS 变换 似乎非常适合。让我们通过做一些实验来检查一下。Blend 5 在这方面非常出色,因为您可以直接实时看到更改的结果。

通过将此类添加到 shape1 的 DIV 元素中,添加 90 度旋转

.shape1rotated {
    transform<span class="code-none">: rotate(90deg)<span class="code-none">;
<span class="code-none">}</span></span></

我确信您没有预料到这一点

Rotated shape

问题:它没有正确对齐游戏网格。为了将我们的形状对齐到网格,我们需要进行一些小调整

.shape1rotated {
    transform-origin<span class="code-none">: 33% 50%<span class="code-none">;
    transform<span class="code-none">: rotate(90deg) translateX(-33%)<span class="code-none">;
<span class="code-none">}</span></span></

现在,我们获得了与下落式方块游戏相同的旋转效果。以下是旋转前后的两张截图

z-shape before rotation

z-shape after rotation

通过在此设置应用于 shape1 的过渡,我们甚至可以做得更进一步

transition: all 1s ease-out;

现在,移除/添加 shape1 DIV 上的 .shape1rotated 类将触发平滑的旋转动画!

此时,我们可能会认为这种方法是构建游戏的正确方法。但不幸的是,目前还不是。原因如下。尝试通过简单地更改其 –ms-grid-column 属性来移动形状。Blend 5 将直接反映更改。未旋转时,形状最多可以移动到第 8 列

z-shape moved to the 8th column

到目前为止一切顺利。但当您通过向 DIV 添加 .shape1rotated 类来旋转它时

z-shape rotated to the 8th column

您会看到右侧仍有一个可用行用于形状移动。如果您认为我们只需要将其移动到第 9 行,那就错了!实际上,我们在第 9 行会得到以下结果

z-shape gets compressed when moved to the 9th row

您可能忘记了,我们目前正在移动一个显示 3 列和 2 行网格布局的 DIV 元素,它与底层游戏网格完全匹配。移动它时,我们真的感觉它是一个属于主网格的方块。但要使这个技巧奏效,我们需要至少 3 列可用空间来容纳我们的形状元素。如果它被包含在 2 列(设置为第 9 列)或更少列中,它将被“压缩”,就像屏幕截图一样。

有两种方法可以解决这个问题。

  1. 停止使用 CSS 变换,而是使用另一个网格定义来绘制旋转后的形状。例如,使用 3 个 div 代替 2 个。但使用这种方法将无法实现漂亮的 CSS 动画。
  2. 将主网格重新定义为使用 12 列而不是 10 列,我们将只使用从 2 到 11 的列(如果您愿意,可以看作是一种裁剪区域)。这将解决我们的“溢出”问题。

让我们实现第二种解决方案。

使用此代码重新定义主网格

.mainGrid {
    display<span class="code-none">: -ms-grid<span class="code-none">;
    -ms-grid-columns<span class="code-none">: (1fr)[12]<span class="code-none">;
    -ms-grid-rows<span class="code-none">: (1fr)[20]<span class="code-none">;
    -ms-grid-column-align<span class="code-none">: center<span class="code-none">;    
    width<span class="code-none">: 100%<span class="code-none">;
    height<span class="code-none">: 100%<span class="code-none">;
<span class="code-none">}</span></span></

为了获得正确的比例,您需要更新关联的媒体查询

@media screen and (-ms-view-state: fullscreen-landscape) {
    .mainGrid {
        width<span class="code-none">: 33.75%<span class="code-none">;
        <span class="code-none">}
}</span></span></

33.75% = 9/16 *12/20

我们还可以添加一个“虚拟网格”来界定我们可以移动形状的空间。在 main grid DIV 中,插入以下内容

<div class="virtualGrid">
</div>

与此 CSS 块关联

.virtualGrid {
    -ms-grid-column<span class="code-none">: 2<span class="code-none">;
    -ms-grid-column-span<span class="code-none">: 10<span class="code-none">;
    -ms-grid-row-span<span class="code-none">: 20<span class="code-none">;
    border-right-style<span class="code-none">: dashed<span class="code-none">;
    border-left-style<span class="code-none">: dashed<span class="code-none">;
    background-color<span class="code-none">: #505A5A<span class="code-none">;
<span class="code-none">}</span></span></

它将有助于用灰色背景和虚线边框来界定游戏区域。

现在,如果我将 Z 形移动到第 9 列、第 2 行,结果如下

z-shape at column 9

如果我使用 CSS 变换旋转它,我可以正确地将其移动到第 10 列

Rotated z-shape moved correctly to column 9

奖励 - 处理纵向模式

如果您想支持纵向模式(这对于下落式方块游戏更好),请添加此 CSS 媒体查询定义

@media screen and (-ms-view-state: fullscreen-portrait) {
        .mainGrid {
        width<span class="code-none">: 106.66%<span class="code-none">;
        <span class="code-none">}
}</span></span></

因为比例需要计算为 = 16/9 * 12/20 = 106.66%。

Tetris grid in landscape

第 3 步:添加一些代码来处理部分游戏逻辑

现在我们已经使用纯 CSS 和 HTML 代码解决了游戏的图形部分,我们需要 JavaScript 的帮助来在游戏区域中移动/旋转形状。我们将通过一个 JavaScript 对象重新实现 CSS 逻辑,该对象将使用 WinJS.Class 来定义。

在 Visual Studio 2012 中打开“TheRealCSSGridStory”。

在 JS 目录中创建一个 TetrisShapeZ.js 文件,并将此代码复制/粘贴

(function () {
    "use strict";

    var ShapeZ = WinJS.Class.define(
    /// Constructor - columnIndex is optional. If provided, will define in which column the shape will start
        function (columnIndex) {
            // We're creating the equivalent of this HTML block :
            // <div class="shape1 ">
            //     <div class="line1shape1"></div>
            //     <div class="line2shape1"></div>
            // </div>
            this._shape1 = document.createElement("div");
            var line1 = document.createElement("div");
            var line2 = document.createElement("div");
            this._shape1.className = "shape1";
            line1.className = "line1shape1";
            line2.className = "line2shape1";
            this._shape1.appendChild(line1);
            this._shape1.appendChild(line2);
            // Boolean to indicate if the shape is in its default orientation mode or not
            // True means not rotated, false means rotated
            this._defaultOrientation = true;
            // Setting the column position in the main grid
            if (columnIndex) {
                this._currentColPos = columnIndex;
                this._shape1.style.msGridColumn = this._currentColPos;
            }
            else {
                this._currentColPos = 1;
            }
            // We always start at line 1
            this._currentLinePos = 1;
            // Boolean to know if the shape can be move/rotate or not
            // If true, this means we've reached the last line possible
            this._fixed = false;
        },
        {
            /// Specify in which HTML element displayed in CSS Grid you'd like to work with
            /// width is the number of columns of the grid & height is the number of lines
            insertIntoGrid: function (element, width, height) {
                element.appendChild(this._shape1);
                this._gridWidth = width;
                this._gridHeight = height;
                // These are the left & bottom max limit for this shape
                // when displayed in default orientation mode
                this._maxLeft = width - 3;
                this._maxBottom = height - 1;
            },
            /// Rotate the Z shape 90 degrees anti/clockwise using CSS Transforms
            /// by simply removing/adding the shape1rotated class
            rotate: function () {
                if (!this._fixed && this._defaultOrientation) {
                    // rotating 90 degrees clockwise, it will trigger also the CSS Transition
                    WinJS.Utilities.addClass(this._shape1, "shape1rotated");
                    this._defaultOrientation = false;
                    // the left limit is now +1 vs the default orientation
                    this._maxLeft = this._gridWidth - 2;
                }
                else {
                    if (!this._fixed && this._currentColPos < this._maxLeft) {
                        // removing the shape1rotated will automatically reset the shape
                        // to its initial matrix and again the CSS Transition will do the animation for you
                        WinJS.Utilities.removeClass(this._shape1, "shape1rotated");
                        this._defaultOrientation = true;
                        this._maxLeft = this._gridWidth - 3;
                    }
                }
            },
            // Internal function called by public moveLeft/moveRight
            _moveHorizontally: function (direction) {
                if (!this._fixed && (this._currentColPos < this._maxLeft || direction === -1) && (this._currentColPos > 2 || direction === 1)) {
                    this._currentColPos = this._currentColPos + direction;
                    this._shape1.style.msGridColumn = this._currentColPos;
                }
            },
            /// Move the shape on the immediate left column
            /// Test if you've reached the left limit or not
            moveLeft: function () {
                this._moveHorizontally(-1);
            },
            /// Move the shape on the immediate right column
            /// Test if you've reached the right limit or not
            moveRight: function () {
                this._moveHorizontally(1);
            },
            /// Move the shape down on the immediate below line
            /// Test if you've reached the bottom limit or not
            moveDown: function () {
                if (!this._fixed) {
                    this._currentLinePos = this._currentLinePos + 1;
                    this._shape1.style.msGridRow = this._currentLinePos;
                    if (this._currentLinePos === this._maxBottom) {
                        this._fixed = true;
                    }
                }
            }
        }
    );

    WinJS.Namespace.define("CSSTetris", { ShapeZ: ShapeZ });
} ());

只需阅读代码即可理解它的作用。它应该有足够的注释来解释。

default.html 中添加对此脚本文件的引用,并在 body 中只保留此 HTML 块

<div class="mainGrid">
    <div class="virtualGrid">
    </div>
</div>

跳到 default.js

拥有文档齐全的代码的好处是,我们现在可以获得有趣的 IntelliSense 详细信息,例如 **构造函数**

Intellisense for ShapeZ(columnIndex) constructor

rotate 函数

Intellisense for the rotate function

为了正确使用此代码,请将此 JS 块添加到 processAll 调用之后

document.addEventListener("keydown", OnKeyDown, false);
mainGrid = document.getElementsByClassName("mainGrid")[0];
myShape = new CSSTetris.ShapeZ(4);
myShape.insertIntoGrid(mainGrid, 12, 20);
init();

并添加这两个函数

function init() {
    setInterval(function () {
        myShape.moveDown();
    }, 1000);
}

function OnKeyDown(event) {
    switch (event.keyCode) {
        case KEYCODE_X:
            myShape.rotate();
            break;
        case KEYCODE_LEFT:
            myShape.moveLeft();
            break;
        case KEYCODE_RIGHT:
            myShape.moveRight();
            break;
        case KEYCODE_DOWN:
            myShape.moveDown();
            break;
    }
}

完成了!我们现在拥有一个非常基础的游戏,它结合了 CSS 网格布局、CSS 变换和动画用于图形部分,以及几行 JS 代码,用于实现下落式方块游戏基础的开端。

您可以在此处下载对应本教程三个步骤的最终 Visual Studio 解决方案: http://david.blob.core.windows.net/win8/TheRealCSSGridStory.zip 

那么,您现在相信 CSS 网格布局是为了简化下落式方块游戏的创建而设计的吗? Smile | <img src= " />

本文是 Internet Explorer 团队 HTML5 技术系列的一部分。通过 @ http://modern.IE 免费获得 3 个月的 BrowserStack 跨浏览器测试,亲身体验本文中的概念。

David Rousset 是微软的开发布道师,专注于 HTML5 和 Web 开发。本文最初发布在他的 MSDN 博客 Coding4Fun 上,日期为 2013 年 2 月 12 日。您可以在 Twitter 上关注他:@davrous

© . All rights reserved.