这是我参与「第四届青训营 」笔记创作活动的第 6 天。
本篇笔记记录了小游戏开发的相关知识。主要包含 Web 游戏引擎的介绍,及 Pixi 开发示例简述。
小游戏开发
01. 前端场景下的游戏开发
开发链路和角色
组建一个最小但最完整的游戏开发团队只需要 3 个人:策划、程序、美术。
- 策划案
- 立项阶段:创意 市场
- 游戏Demo
- 原型阶段:定下核心玩法,确定开发的可行性
- 体验版本
- Alpha 阶段:宏观设计(世界观),剧情/角色设计,音效 / UI 设计,体验流程设计(可以体验游戏全流程)
- Beta 阶段:测试,迭代
- 运营阶段
为什么要用游戏引擎
游戏引擎最大的优势:渲染。
游戏引擎更像一套解决方案,在制作某一类产品时使用一些可复用逻辑 / 代码,提高开发效率。
- 多平台移植
- React Native、 Weex、 Cordova
- 物理效果
- MatterJS、ammo.js
- 动画
- CSS、封装一个动画库.
这些效果通过某几个库结合都可以实现,那为什么要用游戏引擎呢?
游戏引擎能够提供一套完整的实现方案,无需再自己去拼凑、封装。可以让你花更少的时间做出更好的效果,特别是关于渲染效率和性能优化。
- 提供游戏开发时需要的常见功能。
- 引擎会提供许多组件。
- 专业引擎会表现出更好的性能。
游戏引擎通常会包括渲染器, 2D / 3D 图形元素,碰撞检测,物理引擎,声音,控制器支持,动画等部分。
02. 游戏引擎
特定类型的客户端游戏引擎
- The NVL Maker 文字冒险游戏制作器
- No Code 形式开发,只需要写文字脚本加上一定的配置就可以生成一个文字冒险游戏
- AVG.js 适用于前端的库,内核是 PixiJS 作为渲染引擎
- RPG Maker 可以 Low Code 搭建一个关卡类型的游戏,适合代码能力不强但是想发挥自己的创意的开发者。
Web 游戏引擎
利用 Canvas 和 WebGL 为底层技术抽象的图像绘制库(往往还附带一些其他的功能)
Web 游戏引擎的通用能力:
预加载:
- 游戏中往往存在大量的静态素材,包括场景、元素、声音、动画、模型、贴图等,如果以原生 JS 进行请求,并统筹请求时间和加载时机,将会非常麻烦。
- 游戏引擎中的预加载引擎将加载时机、加载过程加以抽象,解决加载编码中的效率问题。
展示与图层、组合系统:
- 对于 Web 游戏编程而言,往往选择
Canvas或WebGL作为渲染方式。 - Canvas 和 WebGL 作为底层的 API,接口非常基础, 需要用大量的编码来编写简单的展示。
- 而且图形之间没有组合和图层, 很难处理元素组合和图层问题。渲染引擎和图层、组合系统应运而生。
动画系统:
- 动画往往被分为 缓动动画 和 逐帧动画。这里讨论缓动动画系统。
- 缓动动画系统在原生 JS 中需要搭配帧渲染进行考量而进行书写,代码量和思考量巨大,抽象程度低,因此需要游戏引擎动画系统。
音效和声音系统:
- 游戏相较于普通的 Web 前端而言需要更加立体、及时的反馈,声音和音效是反馈的重要组成部分。
- 因此声音和音效系统往往包含了声音的播放、音量、截止、暂停等功能的集成。
Web 游戏引擎 Cocos
- 优势:
- 平台支持能力好
- 善的游戏功能支持
- 缺点:
- 3D 能力仍在建设中
- 版本迭代过快
Web 游戏引擎 Laya
- 优势:
- 3D 能力比较成熟,号称市场占有率 90%
- 支持 JS、TS、AS
- 引擎体积小
- 缺点:
- 界面能力不友好
- 生态很差
Web 游戏引擎 Egret
- 优势:
- 工具链比较完善
- 第三方库支持好
- 企业定制能力强
- 缺点:
- 更新迭代遭瓶颈
- 生态较差
Web 游戏引擎 CreateJS & Phaser
这两个游戏引擎没有可视化界面。
CreateJS
它是多个库的集合,通过预加载后的素材展示、动画、声音构成游戏。
EASELJS:控制素材展示和组合TWEENJS:控制素材缓动动画SOUNDJS:控制声音PRELOADJS:控制加载
PhaserJS
除了 CreateJS 为基础的的展示、声音、动画、加载系统,还设计了摄像机、物理引擎、内置浏览器、插件系统等高级功能。
功能引擎
大型游戏引擎往往是由小的功能引擎组装成的。包含:渲染引擎、物理引擎、UI 系统、声音系统、动画系统、粒子系统、骨骼系统、网络系统等组合而成。
其中最重要的便是 渲染引擎 和 物理引擎。
功能引擎是专注于某个方向能力的引擎,其特点是体积小、功能完善。
2D 游戏引擎的技术架构
以 Cocos 的引擎架构为例子:
Web 游戏引擎的渲染原理
以 Pixi 的渲染流程为例子:
- 创建一个
Renderer渲染器,获取它的view(一个canvas对象),添加到DOM Tree中。 (或者指定 Dom Tree 中已经存在的 canvas 对象作为 view) - 在
MainLoop主循环中调用Reanderer.render()并传入一个DisplayObject作为根节点开发渲染。- 主循环每帧调用一次,可以进行一个帧渲染。
- 从场景树的根节点开始,以
zIndex为序从小到大进行深度优先遍历,对每个节点进行渲染操作,由后往前把整个场景绘制一次。 WebGL的render方法执行过程。Canvas的render方法执行过程。
03. 游戏开发技能树
游戏前端开发入门技能树
为什么使用 Canvas 进行渲染而不使用 DOM?
- 由渲染效率决定,DOM 渲染较慢
- 浏览器的渲染模式分为两种:
- 保留模式:向浏览器的 API 发送渲染命令,由浏览器帮我们渲染
- 快速模式:直接将指令发送给 CPU,直接执行指令
- DOM 渲染采用保留模式,Canvas 渲染采用快速模式。
04. PixiJS + Web 开发
Web 项目中加载一个游戏玩法
-
安装与引入
- npm 安装
- script 引入
-
创建 Pixi 应用和舞台(Stage)
- 舞台:展示所有精灵对象的主要容器。
// 1. 创建 Pixi 应用,会自动创建一个 canvas 并计算出怎么让你的图片在 canvas 中显示。 // app.stage 就是一个舞台,包含了需要显示的图片 // 计算了应该使用 Canvas 还是 WebGL 去渲染图片,取决于当前浏览器支持哪一个,一般优先使用 WebGL let app = new PIXI.Application({width:250, height:250}) // 2. 将 canvas 添加到 DOM Tree 中 document.body.appendChild(app.view) // 3. 设置 Canvas 是透明的,或者强制使用 Canvas 模式 let app = new PIXI.Application({ width:250, height:250, transparent:true, forceCanvas:true }) -
显示一张图片
-
Sprite:承载图像的对象。不同于 CSS 中的雪碧图 / 精灵图。- 可以控制它的大小、位置等属性来产生交互、动画
- 创建和控制
Sprite是学习Pixi很重要的部分,而创建一个Sprite需要了解图片怎么加载到Pixi中。
-
纹理缓存:指可以被 GPU 处理的图像。Pixi使用纹理缓存来存储和引用Sprite所需要的纹理。- 纹理的名称字符串就是图像的地址。
- 如
"images/cat.png",就可以使用PIXI.utils.TextureCache["image/cat.png"]来在纹理缓存中找到它。
-
使用前当然要先把它转化为纹理存储在纹理缓存中,这时候可以使用
PIXI.loader加载进来。PIXI.loader.add("images/cat.png").load(res => { let sprite = new PIXI.Sprite( PIXI.loader.resources["images/cat.png"].texture ) }) app.stage.addChild(sprite)
-
-
让图片动起来
-
设置
Sprite的位置和大小:sprite.width = 80 sprite.height = 120 sprite.position.set(10, 10) sprite.scale.set(2, 2) sprite.retation = 0.5 -
每帧移动一个像素,需要用到游戏循环。
-
app.tiker.add(delta => { sprite.x += 1 })
-
-
05. Cocos Creator 编辑器开发
Cocos Creator 介绍
是一个完整的游戏开发解决方案,包含了轻量高效的跨平台游戏引擎,以及能更快速开发游戏所需要的各种图形界面工具。
编辑器开发集成的能力
创建项目
搭建场景
Cocos 的工作流——数据驱动和场景为核心、组件式开发为核心。
节点 cc.Node 是承载组件的实体,我们通过将具有各种功能的组件(如 Sprite、Spine、Label)挂载到节点上,来让节点具有各式各样的表现和功能。
导入资源 + 显示资源
从操作系统中拖拽文件到 Cocos Creator 中的资源管理器面板上,该操作会自动复制资源文件到项目资源文件夹下,并完成导入操作。
将图片拖动到层级管理器即可生成一个 cc.Sprite。
脚本挂载
const {property, ccclass} = cc._decorator
@ccclass
export default class TestComp extends cc.Component {
onLoad() {
console.log('TestComp onload')
}
start(){}
update(dt) {}
onDestroy() {}
}
在 Cocos Creator 中对应的结点将脚本挂载上去。
运行调试