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 互动小游戏开发有见解,欢迎一起交流。