[H5游戏引擎]第 1 篇: 搭建开发环境

257 阅读5分钟

前言

本文内容包含以下几个部分:

  • 游戏引擎的运行逻辑
  • 创建 HTML 页面:包含一个居中显示的 canvas 元素
  • canvas 坐标系统:包括逻辑宽高与屏幕显示宽高的区别
  • 监听窗口的 resize 事件:调整 canvas 的逻辑宽高
  • canvas 的两种上下文:2D 和 WebGL

代码托管在 Github


0. 游戏引擎的运行逻辑

先从动画片说起。

早期的动画片制作,先绘制出一张张图片(称为),每一帧略有差别。然后,快速播放这些帧(例如一秒钟播放 24 帧),就形成了动画效果。

游戏引擎的运行逻辑与此类似,只是游戏的每一帧不是事先画好的图片,而是需要实时计算。另外,游戏需要达到 60 帧/秒,才能流畅运行。

每一帧,引擎需要完成以下工作:

  • 逻辑层:负责更新游戏状态,例如角色位置、动作等,然后生成一系列渲染指令
  • 渲染层:负责绘制游戏画面,根据渲染指令,调用图形库 API 绘制到屏幕上

其中,第二步渲染层是基于图形库的,例如 Windows 的 DirectX、Mac 的 Metal、跨平台的 OpenGL、移动平台的的 OpenGL ES、H5 的 WebGL 等。而逻辑层可以独立于渲染层,只需要针对不同的图形库,生成不同的渲染指令即可。

接下来,我们为游戏引擎创建一个基础的运行环境。这个环境是一个 HTML 页面,包含一个 canvas 元素、一段 CSS 代码和一段 JavaScript 代码。

1. 创建 HTML 页面

首先,我们创建一个 HTML 页面,其中包含一个 canvas 元素。这个是 Web 前端开发基础,各位掘金大佬比我熟悉,我就不多说了。

方便起见,将 CSS 和 JavaScript 代码都放在 HTML 文件中。CSS 代码因为很少,会一直放在 HTML 文件中。JavaScript 代码后续会移到单独的文件中,通过 script 标签引入。

该代码做了两件事:

  • 创建一个居中显示的深蓝色背景的 canvas
  • 输出一句 Hello NovaEngine!

HTML 文件:index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Lesson_01</title>
  <style>
    html, body {
      margin: 0;
      padding: 0;
      overflow: hidden;
      height: 100%;
      display: flex;
      flex-direction: column;
      justify-content: center;
      align-items: center;
    }
    
    canvas {
      display: block;
      width: 50vw;          /* CSS 宽度为屏幕宽度的一半 */
      height: 50vh;         /* CSS 高度为屏幕高度的一半 */
      background-color: darkblue;
    }
  </style>
</head>
<body>
  <canvas id="main-canvas"></canvas>
  <script type="text/javascript">
    console.log("Hello NovaEngine!");
  </script>
</body>
</html>

0.png


2. Canvas 坐标系统

canvas 是 H5 游戏引擎渲染的核心,在开始设计一款游戏引擎之前,先熟悉下 canvas 的坐标系统。因为 canvas 是 HTML 元素,所以它的坐标系统与 HTML 类似,以左上角为原点,X 轴向右为正,Y 轴向下为正。

这与其他的游戏引擎略有差别,一般游戏引擎的坐标系统以左下角为原点,X 轴向右为正,Y 轴向上为正。这个影响不大,涉及到坐标计算的数学原理是相同的。

2.1 坐标原点与轴方向
  • 坐标原点位于 canvas 的左上角 (0, 0)
  • X 轴从左到右,值逐渐增大
  • Y 轴从上到下,值逐渐增大

1.png


2.2 屏幕显示宽高与逻辑宽高

canvas 的两种宽高:屏幕显示宽高逻辑宽高

  • 屏幕显示宽高
    • 在 Web 开发中使用,通过 CSS 控制 canvas 在网页中的显示大小
    • 在上面代码中,canvas 的屏幕显示宽高为 50vw50vh,即父元素的一半
  • 逻辑宽高
    • canvas 内部绘制时使用,通过 canvas.widthcanvas.height 设置
    • 在坐标 (canvas.width, 0) 绘制一张图片,那么这张图片就位于 canvas右上角
    • 在坐标 (canvas.width, canvas.height) 绘制一张图片,那么这张图片就位于 canvas右下角

通常,我们要保证逻辑宽高比与屏幕显示宽高比一致,否则会造成图片拉伸变形。简单情况,就是设置逻辑宽高与屏幕显示宽高相同。

/************************************************************
 * 设置 canvas 的逻辑宽高
 ************************************************************/
const canvas = document.getElementById('main-canvas');
const canvasRect = canvas.getBoundingClientRect();
canvas.width = canvasRect.width;
canvas.height = canvasRect.height;

3. 监听窗口的 resize 事件

与手机游戏不同,H5 游戏的窗口大小经常发生变化,因此我们需要监听窗口的 resize 事件,并重新设置 canvas 的逻辑宽高。

/************************************************************
 * 窗口大小变化
 ************************************************************/
window.addEventListener('resize', () => {
  /************************************************************
   * 设置 canvas 的逻辑宽高
   ************************************************************/
  const canvas = document.getElementById('main-canvas');
  const canvasRect = canvas.getBoundingClientRect();
  canvas.width = canvasRect.width;
  canvas.height = canvasRect.height;
})

4. canvas 的两种上下文

在图形渲染层,canvas 的两种上下文:2DWebGL

2D 上下文是 canvas 最常用的上下文,用于绘制 2D 图形。使用 CPU 进行计算,性能较低,但可以满足大部分需求。

WebGL 上下文是 canvas 的另一种上下文,基于 OpenGL ES 2.0 的标准。使用 GPU 进行计算,性能高,功能强大,适合绘制 3D 图形或高性能 2D 图形。

特性2D 上下文WebGL 上下文
方式canvas.getContext("2d")canvas.getContext("webgl")
用途2D 图形和简单动画绘制3D 图形和高性能渲染
性能使用 CPU 绘制,性能较低使用 GPU 硬件加速,性能优越
易用性简单易用,适合初学者复杂,需要学习着色器和渲染管线
编程模型基于 Canvas API基于 OpenGL 的图形 API

显然,WebGL 上下文更适合游戏引擎。但 WebGL 基于 OpenGL,学习曲线较高,所以在项目前期为了避免陷入图形学的细节,我选择使用 2D 上下文。

等引擎主体功能(即逻辑层)基本完成,再将渲染层切换到 WebGL 上下文。

下面,执行代码在 canvas 上绘制一个浅蓝色的背景,并绘制一个红色的矩形。

/************************************************************
 * 绘制内容
 ************************************************************/
const context = canvas.getContext('2d');
context.fillStyle = 'lightblue';
context.fillRect(0, 0, canvas.width, canvas.height);

context.fillStyle = 'red';
context.fillRect(0, 0, canvas.width / 2, canvas.height / 2);

2.png


5. 总结

本篇文章先创建了一个基础的 HTML 页面,并绘制了一个浅蓝色的背景。接着介绍了 canvas 的坐标系统,最后介绍了两种绘制上下文,并绘制了一个浅蓝色的背景。

通过本篇文章,我们完成了以下内容:

  1. 介绍游戏引擎的运行逻辑
  2. 创建了一个基础的 HTML 页面和 CSS 文件。
  3. 设置了 canvas 的逻辑宽高与显示宽高,并理解了它们的区别。
  4. 学习了如何适配高 DPI 屏幕和绘制简单内容。

下一篇文章,我们将实现游戏逻辑层的帧循环。