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

现代化您的HTML5 Canvas游戏硬件缩放和CSS3

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.80/5 (5投票s)

2012年6月8日

CPOL

7分钟阅读

viewsIcon

35674

在这篇分为两部分的文章中,我将展示如何使用这些新功能来现代化我之前的 HTML5 游戏,“HTML5 Platformer”。

30 天开发一个 Windows 8 应用

Internet Explorer 10 这样的现代浏览器正在实现一些有趣的 HTML5 功能的稳定版本,包括离线应用程序编程接口 (API)、拖放文件 API。这些功能正在将我们带入一个全新的 Web 应用程序时代和新鲜、快速兴起的游戏场景。

在这篇分为两部分的文章中,我将展示如何使用这些新功能来现代化我之前的 HTML5 游戏,HTML5 Platformer。希望您能为自己的游戏获得一些新的灵感!

  • 第一部分:硬件缩放和 CSS3(本文)
  • 第二部分:离线、文件和拖放 API(下篇文章)

注意:演示地址在本篇文章的末尾。欢迎使用您喜欢的浏览器进行体验,并观看 IE10 的游戏视频。源代码将在第二部分提供下载。

跨设备缩放

如果您正在开发一款 HTML5 游戏,您可能对这种标准编程语言的跨平台特性很感兴趣。但是,与各种设备兼容意味着您必须考虑大量的分辨率。与 SVG 相比,Canvas—起初—似乎不适合处理这个问题。

然而,对于基于精灵图的休闲游戏,有一个简单的解决方案可以实现。David Catuhe 在他的博客 "Unleash the power of HTML 5 Canvas for gaming – Part 1" 上对此进行了精彩的描述(请参阅“使用硬件缩放功能”部分了解具体细节)。

这个想法既简单又聪明。您在固定、可预测的分辨率下工作于 canvas 中,然后使用 canvas.style 属性将其拉伸到当前显示的分辨率。

步骤 1:拉伸

在我这款小小的 HTML5 Platformer 游戏中,资源和关卡逻辑都设置为 800x480。因此,如果我想填充一个 1080p 屏幕或一个 1366x768 的平板电脑,我需要创建更高分辨率的资源来匹配这些规格。

在创建所有这些资源之前,我可以尝试进行缩放操作—以及适当的抗锯齿—来提高图像质量。让我们试试看。

缩放操作只需要这段代码

window.addEventListener("resize", OnResizeCalled, false);

function OnResizeCalled() {
    canvas.style.width = window.innerWidth + 'px';
    canvas.style.height = window.innerHeight + 'px';
}

就这样!

在支持硬件加速的浏览器中,此操作将由您的 GPU 完成,无需任何额外成本。甚至可以启用抗锯齿。这就是为什么 David Catuhe 认为在 他的 Canvas 性能文章中提及它的原因。

顺便说一句,这个技巧并非 HTML5 特有。大多数现代主机游戏并非内部计算为 720p 或 1080p;几乎所有游戏都以较低分辨率(如 1024x600)渲染,然后让 GPU 处理缩放/抗锯齿过程。在大多数情况下,这里描述的方法可以帮助您提高每秒帧数 (FPS)。

但是,此操作本身会引发比例问题。事实上,当 canvas 的固定尺寸为 800x480 时,比例是可控的。现在,当我调整浏览器窗口大小时,会出现这种奇怪的结果

游戏仍然可以玩,但也明显偏离了最佳状态。

步骤 2:控制您的比例

这里的想法是控制浏览器窗口调整大小时屏幕的填充方式。与其进一步拉伸任何东西,不如在窗口过大时在右侧添加一些空白区域,或者在窗口过高时在底部添加一些空白区域。这是那段代码

var gameWidth = window.innerWidth;
var gameHeight = window.innerHeight;
var scaleToFitX = gameWidth / 800;
var scaleToFitY = gameHeight / 480;

var currentScreenRatio = gameWidth / gameHeight;
var optimalRatio = Math.min(scaleToFitX, scaleToFitY);

if (currentScreenRatio >= 1.77 && currentScreenRatio <= 1.79) {
    canvas.style.width = gameWidth + "px";
    canvas.style.height = gameHeight + "px";
}
else {
    canvas.style.width = 800 * optimalRatio + "px";
    canvas.style.height = 480 * optimalRatio + "px";
}

“if”语句创建了一个例外:如果您在浏览器中按 F11 进入全屏视图,并且您拥有一个 16:9 的屏幕(例如我的 1920x1080 Sony VAIO Z 屏幕或 1366x768 的 Samsung BUILD 平板电脑),游戏将被完全拉伸。这种体验非常棒。

如果没有这个例外,您将看到以下类型的输出

 

请注意游戏下方和右侧的黑色区域,它们控制着比例。

如果将游戏居中,赋予其宽屏电影效果,那会更好,对吧?让我们来实现它。

步骤 3:使用 CSS3 Grid Layout 居中游戏

居中 HTML 元素有时可能会很麻烦。有几种方法可以做到这一点—而且网络上有许多资源可以帮助您。

我喜欢使用一个名为 CSS Grid Layout 的新规范(目前仅得到 IE10 支持),这是 Windows 8 中 Metro 风格布局的基础。

使用 CSS Grid Layout 居中元素非常简单

  • 将容器的 display 设置为 display:grid。
  • 定义 1 列和 1 行。
  • 使用 column-align 和 row-align 属性居中内部元素。

以下是我在此情况下使用的 CSS

.canvasHolder {
    width: 100%;
    height: 100%;
    display: -ms-grid;
    -ms-grid-columns: 1fr;
    -ms-grid-rows: 1fr;
}

#platformerCanvas {
    -ms-grid-column: 1;
    -ms-grid-row: 1;
    -ms-grid-column-align: center;
    -ms-grid-row-align: center;
}</</

您会注意到 IE10 的前缀是“-ms”。Mozilla 最近宣布他们也将支持 Firefox 的 CSS Grid Layout 规范(在 2012 年),这是个好消息。与此同时,这个居中技巧仅适用于 IE10。下面是它的样子

IE10 窗口将显示垂直或水平黑条,类似于您在电视屏幕上可能看到的情况。在其他浏览器中,结果将与步骤 2 中的情况相同,因为 CSS3 Grid Layout 规范将被忽略。

使用流畅的动画

现在我们已经使用简单的缩放操作处理了多分辨率问题,当用户调整窗口大小时,能够播放流畅的过渡效果会很棒。此外,在加载每个关卡时播放酷炫的动画也会很好。为此,我们将使用 CSS3 Transitions 和 CSS3 3D Transforms 工具。在大多数平台上,硬件加速由 GPU 提供。

为 Canvas 样式属性的每次更改添加动画

CSS3 Transitions 易于使用,可产生流畅、高效的动画。要了解如何使用它们,您可以阅读我同事的优秀文章 "Introduction to CSS3 Transitions",或者在我们 Internet Explorer 的试用网站 "Hands On: transitions" 上进行尝试。

我通过以下规则为所有 canvas 属性设置了全局过渡

#platformerCanvas {
    -ms-grid-column: 1;
    -ms-grid-row: 1;
    -ms-grid-column-align: center;
    -ms-grid-row-align: center;

    -ms-transition-property: all;
    -ms-transition-duration: 1s;
    -ms-transition-timing-function: ease;
}</</

ID 为“platformerCanvas”的 canvas 现在将以一个由缓动函数生成的、持续一秒的动画自动反映其样式属性的任何更改。

thanks to this new rule, resizing the browser window reduces/increases the size of the canvas with a smooth animation. I love the effect it produces—all with only 3 lines of CSS.

注意:为了兼容性,我还添加了不同的前缀(-moz、-webkit 和 -o 分别代表 Mozilla、WebKit 和 Opera)。您需要记住也这样做。

在每个关卡之间构建一个酷炫的动画

现在我想使用 CSS3 3D Transforms 来暂时隐藏 canvas。这将通过在 Y 轴上进行 90 度的动画旋转来完成。一旦动画旋转 90 度并回到初始位置(旋转零度),我将加载下一个关卡。为了更好地理解效果,您可以在 Internet Explorer 试用网站上使用我们的 "Hands On: 3D Transforms" 进行尝试。

我们还将使用 scale 和 rotateY 属性来构建一个有趣的动画。为此,我添加了两个 CSS 规则并定位了两个类

.moveRotation
{
    -ms-transform: perspective(500px) rotateY(-90deg) scale(0.1);
    -webkit-transform: perspective(500px) scale(0);
    -moz-transform: perspective(500px) rotateY(-90deg) scale(0.1);
}

.initialRotation
{
    -ms-transform: perspective(500px) rotateY(0deg) scale(1);
    -webkit-transform: perspective(500px) scale(1);
    -moz-transform: perspective(500px) rotateY(0deg) scale(1);
}</</

首先,我的 canvas 设置了 initialRotation

<canvas id="platformerCanvas" width="800"
height="480" class="initialRotation"></canvas>

现在,想法是等待玩家通过当前关卡。一旦完成,我们就将 canvas 的类从 initialRotation 更改为 moveRotation。这将自动触发我们之前设置的 CSS3 Transitions 来生成动画。

为了知道动画何时完成,会触发一个事件。它在每个浏览器中的名称不同。这是我用于注册事件并定位 IE10、Firefox、WebKit 和 Opera 的代码

// Registering to the various browsers vendors transition end event
PlatformerGame.prototype.registerTransitionEndEvents = function () {
    // IE10, Firefox, Chrome & Safari, Opera
    this.platformerGameStage.canvas.addEventListener("MSTransitionEnd", onTransitionEnd(this));
    this.platformerGameStage.canvas.addEventListener("transitionend", onTransitionEnd(this));
    this.platformerGameStage.canvas.addEventListener("webkitTransitionEnd", onTransitionEnd(this));
    this.platformerGameStage.canvas.addEventListener("OTransitionEnd", onTransitionEnd(this));
};

以下是将被调用的回调代码

// Function called when the transition has ended
// We're then loading the next level
function onTransitionEnd(instance) {
    return function () {
        if (instance.loadNextLevel === true) {
            instance.LoadNextLevel();
        }
    }
};

最后,这是我的游戏中将 moveRotation 类设置为 canvas 的代码

// Perform the appropriate action to advance the game and
// to get the player back to playing.
PlatformerGame.prototype.HandleInput = function () {
    if (!this.wasContinuePressed && this.continuePressed) {
        if (!this.level.Hero.IsAlive) {
            this.level.StartNewLife();
        }
        else if (this.level.TimeRemaining == 0) {
            if (this.level.ReachedExit) {
                // If CSS3 Transitions is supported
                // We're using smooth & nice effects between each levels
                if (Modernizr.csstransitions) {
                    this.loadNextLevel = true;
                    // Setting the moveRotation class will trigger the css transition
                    this.platformerGameStage.canvas.className = "moveRotation";
                }
                // If CSS3 Transition is not supported, we're jumping directly
                // to the next level
                else {
                    this.LoadNextLevel();
                }
            }
            else
                this.ReloadCurrentLevel();
        }
        this.platformerGameStage.removeChild(statusBitmap);
        overlayDisplayed = false;
    }

    this.wasContinuePressed = this.continuePressed;
};

请注意,我正在使用 Modernizr 进行 CSS Transitions 的功能检测。我们最后在 LoadNextLevel 函数中设置回 initialRotation

// Loading the next level 
PlatformerGame.prototype.LoadNextLevel = function () {
    this.loadNextLevel = false;
    // Setting back the initialRotation class will trigger the transition
    this.platformerGameStage.canvas.className = "initialRotation";
    // ... loadNextLevel logic stuff...
};

注意:您可能已经注意到,在之前的 CSS 块中,我没有为 WebKit 设置与 IE10 和 Firefox 相同的变换(和动画)。这是因为,在我的例子中,Chrome 的行为与 IE10 和 Firefox 11 不同。我在 jsFiddle 上设置了一个简单的重现案例:http://jsfiddle.net/5H8wg/2/

演示视频和地址

这是一段简短的视频,演示了本文介绍的 IE10 功能

您也可以在这里用 IE10 或您喜欢的浏览器体验此演示:Modern HTML5 Platformer

在本文的下一部分,我将展示如何实现离线 API 使我的游戏无需网络连接即可运行,以及拖放 API 如何创建另一个酷炫的功能。

© . All rights reserved.