CreateJS 新司机开车指南

Categories: Study

拍于沙巴红树林萤火虫

TL,TR

最近在对前端互动游戏技术一些学习和探索,恰逢上一个小游戏使用了 CreateJS 技术,借此文对其使用做一次备忘,同时让想用 CreateJS 写小游戏的同学可以快速上手。

笔记基于最新1.0版本,有不少地方和网上老教程不一致,同时也建使用最新版本,主要内容如下:

概况

简介

CreateJS是基于HTML5开发的一套模块化的库和工具,基于这些库,可以非常快捷地开发出基于HTML5的游戏、动画和交互应用。

历史可以追溯到7年前 flash 使用慢慢变少那个时代,gskinner 开发它作为“下一代”的富交互的工具库,Adobbe、微软、火狐官方资助的一个项目,API 在很多地方和 Flash 是很像,同时可以通过Adobe Animate CC(替代 flash)直接导出Canvas在Web中使用。

在 18年 终于发布了 1.0 版本,同时官方正在开发 2.0 版本,相信不就会有一个更现代化的 Createjs 呈现给开发者。

模块组成

CreateJS 提供的 4 个模块可以帮助处理游戏开发中常见操作:

  • EaselJS:用于位图、图形、Sprites、文本的绘制,还包含 Ticker 定时器
  • TweenJS:用来创建补间动画效果
  • PreloadJS:游戏资源的预加载和管理,包括图片、音频、json等资源
  • SoundJS:播放和处理音频

更好使用

目前官方版本的 createjs 不支持通过 npm 方式的使用,导致在 ES6 开发中,需要在 html 中手动引入一个 <script> 标签才可以使用,对模块化开发不方便。

createjs-npm 通过 imports-loader、exports-loader 对官方库加了一层胶水,让大家可以向使用 npm 包方式一样使用,具体为:

// 安装后和官方 createjs 1.0 使用一致
npm install createjs-npm -S

// 引入全部模块
import createjs from 'createjs-npm';

// 只引入单个模块(xx 可以是 easel、preload、tween、sound)
import createjs from 'createjs-npm/lib/xx';

EaselJS 的使用

EaselJS 在 Createjs 中承担 的能力,比如说你要画图片、画图形、画帧动画、画文字可以使用它的 API 实现。

步骤

比如说你需要在画一个红色的圆,必含步骤为创建舞台->创建对象->设置对象属性->添加对象到舞台->更新舞台呈现下一帧

  //创建一个舞台,得到一个参考的画布
  const stage = new createjs.Stage("myCanvas");
  //创建一个形状的显示对象
  const circle = new createjs.Shape();
  circle.graphics.beginFill("red").drawCircle(0, 0, 40);
  //形状实例的设置位置
  circle.x = circle.y = 50;
  //添加形状实例到舞台显示列表
  stage.addChild(circle);
  //更新舞台将呈现下一帧
  stage.update();

上述代码的效果为:

See the Pen Demo by Tw93 (@tw93) on CodePen.

Graphics 类

在上面Demo中,我们创建了一个 Shape,这里其实是创建了一块画布,同时假如需要在画布上面画东西,就需要使用Graphics类了。

可以在 Graphics 上面设置很多样式,或者调用绘图方法来画其他的图形,同时Graphics类里的所有绘图方法都会返回一个graphics实例,这里使用链式操作会很方便,使用具体可见【EaselJS API】

EaselJS 元素

上面说过,EaselJS 可以用来画图片、图形、动画帧、文字这类型的元素,对应是 Bitmap、Shape、Sprite、Text 这几个。 具体使用为:

// 使用 Bitmap 来表示图片
const bitmap = new createjs.Bitmap("imagePath.jpg");

// 使用 Shape 结合 Graphics 来表示图形
 const shape = new createjs.Shape();
 shape.graphics.beginFill("#ff0000").drawRect(0, 0, 100, 100);
 
// 使用 Sprite 来表示精灵动画(后面详细介绍)
const instance = new createjs.Sprite(spriteSheet);
instance.gotoAndStop("frameName"); 

// 使用 Text 来表示文字
const text = new createjs.Text("How are you", "20px Arial", "#000000");

精灵动画

这个特性会让第一次接触的同学高兴起来,很方便使用,同时实现效果很棒,比如说需要实现一个绅士走路的样子:

See the Pen Sprite by Tw93 (@tw93) on CodePen.

【技巧】此处雪碧图的生成可以借助 TexturePacker,其中有一个导出为 EaselJS 的选项,将值直接替换 SpriteSheet即可。

上述代码应该能够很好的表达意思,可以试试animations中多设置几个动作,让其更有趣,更详细的使用可以参考 SpriteSheet Class

Ticker 刷新

主要作用

上述人物的走动,就是靠 Ticker 来实时刷新舞台的,同时可以通过addEventListener来监听tick的刷新,并在里面”做些事情”。

createjs.Ticker.framerate = 60;
createjs.Ticker.timingMode = createjs.Ticker.RAF_SYNCHED;
sprite.addEventListener("tick", ()=>{console.log("doing something"});

关注点

createjs.Ticker.framerate 表示 Ticker 帧频率,理想下是60FPS,这里在游戏体验优化时候可以设置到60。

createjs.Ticker.timingMode 表示 Ticker的渲染模式(setTimeout、requestAnimationFrame),可以这样选择:

  • TIMEOUT:默认模式,这种模式通过setTimeout方式来更新下一次,虽然提供了可预期的、灵活的帧率,但是在性能体验上比requestAnimationFrame方式要差很多,不建议使用
  • RAF:只单纯使用RAF,会将 framerate 设置的帧速率等值忽略,频率取决于当时运行环境和浏览器性能
  • RAF_SYNCHED:它会尝试去协调RAF和设置的framerate频率,相当于结合了setTimeout 和 RAF 的优点,也即这里可以设置framerate=60,优化让CreateJS 帮忙处理,建议使用这个模式

优化点

同时这里可以和Ticker.paused结合起来做性能优化,比如说我们在游戏暂停时候,可以这样防止不停刷新,类似这样:

createjs.Ticker.addEventListener('tick', (e) => {
      !e.paused && stage.update();
});
createjs.Ticker.paused = true;  //在任何地方调用这个,则会暂停tick里面的处理
createjs.Ticker.paused = false;  //在任何地方调用这个,则会恢复tick里面的处理

在性能调优过程中,可以通过 createjs.Ticker.getMeasuredFPS() 将实时的帧率输出到屏幕,当这个值和设置的 framerate 很接近,说明性能良好

TweenJS 的使用

从上面可以看到,EaselJS 其实可以处理 CreateJS 开发中很多的事情,其他三项可以当做辅助工具,让开发更便捷。

TweenJS 在这里充当着“动”的能力,同时它也可以单独使用,支持数字对象属性和CSS样式属性,是一个很强大的补间动画功能库。

案例

比如说以下这个很经典的动画例子:

See the Pen TweenJS by Tw93 (@tw93) on CodePen.

使用

可以看到链式处理让很多操作使用是很简单的,也即可以获取目标元素、设置等待时间、执行下一步动画、动画结束事件;

一般场景用上面完全够用,还有如下高级特性,可以通过【文档】来查询:

  • createjs.Ease:设置动画缓动效果,包含众多的动画Ease效果可用,如linear/bounceIn/circInOut/getBackIn/quadInOut
  • ColorPlugin:色值处理插件,可以处理任何 CSS 可支持的颜色
  • CSSPlugin:使用前需要createjs.CSSPlugin.install();注册,可以处理几大部分 CSS 中的样式
  • RotationPlugin:通过RotationPlugin.install(props);,用在处理角度问题
  • MotionGuidePlugin:引导动画效果,使用前需要createjs.MotionGuidePlugin.install(createjs.Tween); ,通过设置一系列path来实现路径动画效果
  • Timeline: 用来处理多组动画效果,让他们可以一组一组地被控制

PreloadJS 的使用

PreloadJS是一个用来管理和协调相关资源加载的类库,它可以方便的帮助你预先加载相关资源。

它主要是使用 LoadQueue 类来管理加载内容的,在游戏中很常见的场景是预加载图片资源和游戏音乐。

案例

屏幕中可以看到处理后的图片加载出来,同时在播放着背景音乐:

See the Pen PreloadJS by Tw93 (@tw93) on CodePen.

使用

加载方式

const queue = new createjs.LoadQueue(false);

里面传入布尔值用来表明是用XHR还是用HTML标签来加载,默认为true,就是使用 XHR 来加载,但是XHR 加载 cdn 常常会报一些错误,也即推荐使用false类型,通过标签来加载。

资源加载和使用

通过 loadManifest 类可以加载一组游戏中用到的所有资源,包括 图片、音频、json、svg 等文件,id 用来加载完使用的标识,同时通过queue.getResult(id)获取。

资源加载过程中,有complete、error、progress、fileload供订阅,完成后才可以对资源进行使用,progress和complete结合来显示进度条,fileload表示当个文件加载完毕

加载音乐

加载音乐时候需要注册 Sound 插件,也即queue.installPlugin(createjs.Sound);,同时指明音乐文件格式,比如说createjs.Sound.alternateExtensions = ["mp3"];

这里还有一个小坑,对于低版本的浏览器,可能会出现音乐不能播放情况,可以引入 HTMLAudioPlugin 解决:createjs.Sound.registerPlugins([createjs.HTMLAudioPlugin])

对于音乐播放,使用createjs.Sound.play(id);即可,详细使用后面 SoundJS 介绍。

SoundJS 的使用

一个小游戏要想互动性比较好,背景音乐是必不可少少的,可以选择一些轻松活泼的,需要使用SoundJS来“放音乐”,同时具备完善的兼容处理。

Sound 类

音频管理主要使用 Sound 类,有如下功能:

  • Sound.registerSound:注册播放的音频,registerSounds 支持多个音频,同时在音频播放前的需要提前注册(此处的加载其实也可用preload弄),
  • Sound.registerPlugins:注册音频播放插件,一般用于不兼容的低浏览器场景,可以注册来兼容 WebAudioPlugin 或者 HTMLAudioPlugin
  • Sound.play: 播放音频,一般是createjs.Sound.play(id)

AbstractSoundInstance 播放实例类

当我们调用 Sound.play API时候其实就创建了一个AbstractSoundInstance 类,创建好后,我们可以通过它来直接控制音频的播放或者其他操作,同时可以监听其播放状态:

const myInstance = createjs.Sound.play("myAssetPath/mySrcFile.mp3");

// 属性控制,实际中选择需要的属性设置就好
// 属性同时以对象方式可以加到play第二个参数
myInstance.volume = 0.5;
myInstance.paused = false; // 控制音乐暂停否
myInstance.loop = true;  // 循环播放
myInstance.duration = 30;  //播放时长

// 监听状态,常用于错误时候,重新播放
myInstance.on("complete", handleComplete);
myInstance.on("loop", handleLoop);
myInstance.on("failed", handleFailed);

性能体验优化

图片优化

  • 图片大小和正常 H5 相比更加影响性能,尽量使用合适尺寸和通过 tinypng 压缩后的图片
  • 对于精灵图或者很多小图场景,可用 TexturePacker工具将他们合成一张雪碧图
  • 对于帧动画的场景,可用gka 工具对图片进行压缩和直接生成动画文件

善用回收机制

  • 对于屏幕外或者不在舞台里面的需及时销毁
  • 对于复杂的对象使用cache来增加性能,简单的尽量就不要用了,会有反作用
  • SpriteSheetBuilder 的方式可以代替 cache,而且效果更好

多用位图少用矢量

  • 在 CreateJS 中矢量运算比较影响性能,奇怪是 Text shape都算矢量,建议此处直接将字和矢量图直接通过工具转成位图
  • 如果某些项目必须用矢量,滤镜,叠加模式,或者含有非常多子元件,可以通过使用SpriteSheetBuilder进行位图渲染,从而优化
  • animateCC 中的遮罩相当于把整个动画序列帧化,矢量化,所以也尽量少用,如果实在要用,操作方法跟处理上面矢量一样

滤镜和动画场景优化

  • 不要使用滤镜特效比如(阴影滤镜和发光滤镜)来做动画,因为这样会非常耗费性能,在移动端上性能不可控
  • 可以使用逐帧图片来代替相关滤镜特效来实现动画效果

减少嵌套和叠加

  • 在做动画或者其他场景时候,尽量不要有太多容器的嵌套,尽量打平层级(有点点像写 Weex,讲究平和薄)
  • 有些叠加的场景,可以直接用图片代替

帧率优化

  • 不用的帧率监听就及时取消,比如说 tick
  • 使用 createjs.Ticker.timingMode = createjs.Ticker.RAF_SYNCHED
  • createjs.Ticker.framerate 设置比预想高一点
  • 通过createjs.Ticker.getMeasuredFPS() 输出实时FPS到屏幕,进行优化

试试 StageGL 神器

  • StageGL 在目前压缩版本没有引入,需要额外引入
  • 使用起来和 Stage 差不多,相当于将原有的 Stage 改成 StageGL,但是相当于使用WebGl能力,性能会比原有的好太多,更多详细可见GETTING STARTED WITH STAGEGL
 // 常规的2D场景
  const stage = new createjs.Stage("canvasId");
   
  // 使用 WebGl
  const stage = new createjs.StageGL("canvasId");

移动端点击事件优化

在我们游戏初始化时候,一般需要为之绑定点击事件,但是对于移动端来说加上这一句效果会好很多

var stage = new createjs.Stage("canvasId");
createjs.Touch.enable(stage);

结束

CreateJS 的特性应该不止上述这些,更多的需要在实际项目中使用,通过各种游戏场景可以发掘很多有趣的使用,假如你也对 H5 互动小游戏开发有见解,欢迎一起交流。

Read More

《黑客与画家》读书笔记

【2018-07-08】最近在看《黑客与画家》这一本书,主要介绍黑客即优秀程序员的爱好和动机,讨论黑客成长、黑客对世界的贡献以及编程语言和黑客工作方法等所有对计算机时代感兴趣的人的一些话题,本文用于记录读书过程中的一些笔记和思考。