动画编辑器使用手册

[复制链接]
722 |3
哭唧唧的细狗 发表于 2024-7-5 19:53:43 | 显示全部楼层 |阅读模式
本帖最后由 哭唧唧的细狗 于 2024-7-5 20:08 编辑

功能描述
为对象通过可视化工具,添加关键帧,实现移动、旋转、缩放、显隐等等设置预览,并在游戏中通过脚本应用这些设定

安装前请先安装商店的lighter工具

效果演示


Editor 编辑使用说明


打开窗口导入动画工具后,在视图中会出现名为 Animator 的入口
image.png

打开动画工具,其直接出现在工程内容并行的位置
image.png

UI 窗口中点击视图找到 Animator 入口
image.png

首次点击打开 Animator 窗口会悬浮在中间,将其拖入到 UI 窗口布局中后,该 UI 窗口会记住此布局
image.png

界面介绍
image.png

左侧工具栏
image.png

红点 1 从左至右
  • 录制工具(暂时没用)
  • 选中第一个关键帧
  • 选中上一个关键帧
  • 播放
  • 选中下一个关键帧
  • 选中最后一个关键帧
  • 时间轴选中时间对应的帧数
红点 2 从左至右
  • 现有动画 clip 选择
  • 新建事件
  • 新建关键帧
红点 3 - 添加属性

右侧时间轴
image.png

红点 1 - 关键帧选中位置拖动轴
红点 2 - 当前可调节的关键帧属性
红点 3
  • close - 重置选中状态,清空 animator 界面
  • save - 编辑了关键帧和事件的动画 clip 需要通过该按钮进行保存
红点 4 - 时间轴操作
  • seconds -  以时间显示轴单位
  • frames - 以帧显示轴单位
  • frameRate - 时间轴里显示的帧数比例:包含30fps、60fps、120fps可选
  • playMode - 分为stop、continue、loop三种
    • stop 则在播放完成最后一帧停下来
    • continue 则在播放完成后一直往下播,即使后边什么也没有
    • loop 在播放完成后从头播放

显示帧数
image.png

播放模式
image.png

可以通过鼠标滚轮达成缩放时间轴的效果

创建动画在主视口或对象管理器选中任意对象:
该对象没有动画时,动画工具右侧 UI 界面显示创建按钮



已有动画时,则会直接显示动画时间轴
创建状态点击 create 按钮后弹出 create new clip 界面,可以修改创建的动画 clip 名字
image.png

点击确认按钮后会弹出路径选择框,当前必须选择将其放在 JavaScripts\lighter\assets\mwanim 路径下
image.png

编辑动画新增属性如下图展示了一个空的剪辑,左侧未显示任何属性。
image.png

点击 addproperty 按钮,弹出的 AddTrackPopupWindow 窗口里,能够选择想要增加的属性值,选中右侧“+号”即可新增节点
image.png

新增属性节点成功后,会在右侧时间轴里看到初始位置添加了对应的关键帧
image.png

编辑属性右键选中已有的属性,此时会弹出操作框,当前能进行移除属性的操作,其它功能待添加
image.png

添加关键帧方法 1:双击时间轴


方法 2:在时间轴上右键 add key
image.png

方法 3:属性修改后确定


删除关键帧时间轴选中关键帧右键进行删除,如果删除没成功,可以尝试通过鼠标滚轮方法时间轴操作
image.png

录制关键帧


添加事件动画编辑器的案例中,天空盒开始的右下角名字显示,都是通过事件来做到的,事件能进行的操作包括:
  • 新建编辑可在弹窗中设置事件名字及加入参数
  • 双击已有事件可以进行编辑名字及参数



常用曲线右键点击一个关键帧(xyz 节点)弹出窗口会包含 easing 曲线选择以及 cons、tangent、linear 选项。
  • 默认选择是 linear,即一条斜向直线(当前没有默认选择上表现)

image.png

  • tangent 是第二期将支持的自定义曲线
  • const 是当前帧与下一帧之间效果不会缓动,在最后一帧瞬间切换
  • easing 则是曲线集合,可与下下图效果对应(名字去掉 ease 和下划线)

image.png

https://easings.net/

image.png

插件运行时使用说明环境注入请将插件runtime文件zip,放入项目场景的根目录下解压即可
生命周期初始化

export default class GameStart extends Script {    /** 当脚本被实例后,会在第一帧更新前调用此函数 */    protected onStart(): void {        if (SystemUtil.isClient()) {            // 需要设置update为true            this.useUpdate = true;                        // 初始化            lighter.asset.startLoad();            // 按键F1执行逻辑            InputUtil.onKeyDown(mw.Keys.F1, () => {                // 播放UI的逻辑,具体看UI动画案例部分                this.playUIAnimate();            })        }    }}


更新周期
update 可以传 dt,也可以固定帧率更新动画。
为了让动画不跳帧,推荐使用固定帧率播放

/*** 周期函数 每帧执行* 此函数执行需要将this.useUpdate赋值为true* @param dt 当前帧与上一帧的延迟 / 秒*/protected onUpdate(dt: number): void {    lighter.animation.getGlobalAnimationManager().update(1 / 60);}



销毁动画播放完毕后,调用destroy来回收Animator,以供下次复用

// 通过获取持有animatorUI的rootCanvas// 再传入RootCanvas以获取Animatorconst animator = lighter.animation.Animator.getAnimator(rootCanvas);// 销毁animator.destroy()


监听动画事件当前有两种事件可监听
1.使用 Animator.onAnimationLifeEvent监听动画生命周期事件
2.使用Aniamtor.onAnimationEvent 监听用户在动画编辑器中配置的事件
动画生命周期包含:
Play - 开始播放时触发
Stop - 停止播放时触发
Pause - 暂停播放时触发
  • Resume - 恢复播放时触发

Lastframe - 当播放循环次数大于1时会在当次播放最后一帧触发
Finished - 动画完成播放时触发

image.png

控制动画的 API
参数释义
AnimationClip : 保存基于关键帧的动画,即文档里提到的动画片段,用于动画播放
AnimationState : 动画混合状态,用于衔接两段动画片段

export class Animator {    static pools: Animator[];    static getAnimator(target: unknown): Animator;    onAnimationEvent: mw.Action2<string, string[]>;    onAnimationLifeEvent: mw.Action2<EventType, AnimationState>;    /**     * 播放动画片段     * @param animationClip 动画片段 | 动画片段的名字     */    play(animationClip: AnimationClip | string): void;    // 暂停动画播放    pause(): void;    // 恢复动画播放    resume(): void;    /**     * 动画混合     * 在一段动画播放过程中根据参数2的时间,间隔切换到名为name的动画混合     * @param name 动画混合的名字     * @param duration 间隔时间     */    crossFade(name: string, duration?: number): void;    /**     * 根据名字返回动画混合     * @param name 动画混合的名字     * @returns 动画混合     */    getAnimationState(name: string): AnimationState;    /**     * 通过动画片段创建一个动画混合     * 与addClip接口效果类似,但该接口能多个AnimationClip创建AnimationState     * @param clip 动画片段     * @param name 动画混合的名字     * @returns 动画混合     */    createState(clip: AnimationClip, name?: string): AnimationState;    /**     * 通过名字移除一个动画混合     * @param clip 动画片段     */    removeState(name: string): void;    /**     * 给底层AnimationState容器添加一个动画片段     * 与createState接口效果类似,但该接口会对AnimationClip进行去重     * 可用于播放连着播放多段动画     * @param clip 动画片段     * @param name 动画混合的名字     * @returns 动画混合     */    addClip(clip: AnimationClip, name?: string): AnimationState;    /**     * 通过动画片段移除一个动画混合     * 当动画播放中时,强制停止会使得当前动画立刻停止,否则无效     * @param clip 动画片段     * @param force 强制停止     */    removeClip(clip: AnimationClip, force?: boolean): void;        // 销毁    destroy(): void;}


UI 动画案例保证 UI 文件夹下有相应名称的 UI 文件
image.png

创建及播放完整代码

private playUIAnimate() {// 代码创建对象并获取其RootCanvas    const widget = createUIByPath('UI/ExampleAnimate.ui');    const rootCanvas = widget.findChildByPath("RootCanvas");    UIService.canvas.addChild(rootCanvas);    // 通过传入RootCanvas获取Animator    const animator = lighter.animation.Animator.getAnimator(rootCanvas);    // 监听动画生命周期事件    animator.onAnimationLifeEvent.add(this.onAnimationLifeEvent, this);    // 监听动画事件    animator.onAnimationEvent.add(this. , this);    // 获取想要播放的动画片段。这里传入的是动画.ts文件的guid    const clip = AnimationAssetMgr.getAnimationClipWithUUID('0761464D4C972C5B6E8A81BD96093C53');    // 播放加载的动画    animator.play(clip);}


1.创建 UI 并获取RootCanvas
2.通过给lighter.animation.Animator.getAnimator传入RootCanvas获取Animator
3.通过 AnimationAssetMgr 和动画 guid 获取创建好的动画片段
所有动画文件都存放在 JavaScripts/lighter/asssets/mwanim 目录下,文件中存放了 guid,该 guid 一般等于编辑器中脚本的 guid ,两种方式都可以获取 clip
方法 1:右键对应动画复制资源 ID

image.png

方法 2:打开对应目录下。ts 动画信息文件后找到 guid 一行
image.png

4.传入动画片段到animator.play(clip)播放动画

完整项目 Demo
AnimDemo.rar (24.89 MB, 下载次数: 55)
回复

使用道具 举报

窜稀大仙 发表于 2024-7-5 21:43:42 | 显示全部楼层
本帖最后由 窜稀大仙 于 2024-7-5 21:44 编辑

高情商:这个动画编辑器真不错,以后应用到骨骼动画就更厉害了!
低情商:我一直想要的是骨骼动画编辑啊喂!!
回复

使用道具 举报

哭唧唧的细狗楼主 发表于 2024-7-8 10:23:57 | 显示全部楼层

动画编辑器使用手册

本帖最后由 哭唧唧的细狗 于 2024-7-9 14:35 编辑

功能描述
为对象通过可视化工具,添加关键帧,实现移动、旋转、缩放、显隐等等设置预览,并在游戏中通过脚本应用这些设定

安装前请先安装商店的lighter工具

效果演示


Editor 编辑使用说明


打开窗口
导入动画工具后,在视图中会出现名为 Animator 的入口


打开动画工具,其直接出现在工程内容并行的位置


UI 窗口中点击视图找到 Animator 入口


首次点击打开 Animator 窗口会悬浮在中间,将其拖入到 UI 窗口布局中后,该 UI 窗口会记住此布局


界面介绍


左侧工具栏


红点 1 从左至右
  • 录制工具
  • 选中第一个关键帧
  • 选中上一个关键帧
  • 播放
  • 选中下一个关键帧
  • 选中最后一个关键帧
  • 时间轴选中时间对应的帧数
红点 2 从左至右
  • 现有动画 clip 选择
  • 新建事件
  • 新建关键帧
红点 3 - 添加属性

右侧时间轴


红点 1 - 关键帧选中位置拖动轴
红点 2 - 当前可调节的关键帧属性
红点 3
  • close - 重置选中状态,清空 animator 界面
  • save - 编辑了关键帧和事件的动画 clip 需要通过该按钮进行保存
红点 4 - 时间轴操作
  • seconds -  以时间显示轴单位
  • frames - 以帧显示轴单位
  • frameRate - 时间轴里显示的帧数比例:包含30fps、60fps、120fps可选
  • playMode - 分为stop、continue、loop三种
    • stop 则在播放完成最后一帧停下来
    • continue 则在播放完成后一直往下播,即使后边什么也没有
    • loop 在播放完成后从头播放

显示帧数


播放模式


可以通过鼠标滚轮达成缩放时间轴的效果

创建动画
在主视口或对象管理器选中任意对象:
该对象没有动画时,动画工具右侧 UI 界面显示创建按钮



已有动画时,则会直接显示动画时间轴
创建状态点击 create 按钮后弹出 create new clip 界面,可以修改创建的动画 clip 名字


点击确认按钮后会弹出路径选择框,当前必须选择将其放在 JavaScripts\lighter\assets\mwanim 路径下


编辑动画
新增属性
如下图展示了一个空的剪辑,左侧未显示任何属性。


点击 addproperty 按钮,弹出的 AddTrackPopupWindow 窗口里,能够选择想要增加的属性值,选中右侧“+号”即可新增节点


新增属性节点成功后,会在右侧时间轴里看到初始位置添加了对应的关键帧


编辑属性
右键选中已有的属性,此时会弹出操作框,当前能进行移除属性的操作,其它功能待添加


添加关键帧
方法 1:双击时间轴


方法 2:在时间轴上右键 add key


方法 3:属性修改后确定


删除关键帧
时间轴选中关键帧右键进行删除,如果删除没成功,可以尝试通过鼠标滚轮方法时间轴操作


录制关键帧


调整时间轴长短
通过鼠标滚轮滑动,能调节时间轴的长短,方便把握设置的整体和细节


添加事件
动画编辑器的案例中,天空盒开始的右下角名字显示,都是通过事件来做到的,事件能进行的操作包括:
  • 新建编辑可在弹窗中设置事件名字及加入参数
  • 双击已有事件可以进行编辑名字及参数



常用曲线
右键点击一个关键帧(xyz 节点)弹出窗口会包含 easing 曲线选择以及 cons、tangent、linear 选项。
  • 默认选择是 linear,即一条斜向直线(当前没有默认选择上表现)



  • tangent 是第二期将支持的自定义曲线
  • const 是当前帧与下一帧之间效果不会缓动,在最后一帧瞬间切换
  • easing 则是曲线集合,可与下下图效果对应(名字去掉 ease 和下划线)



https://easings.net/



插件运行时使用说明
环境注入
请将插件runtime文件zip,放入项目场景的根目录下解压即可
生命周期
初始化

export default class GameStart extends Script {    /** 当脚本被实例后,会在第一帧更新前调用此函数 */    protected onStart(): void {        if (SystemUtil.isClient()) {            // 需要设置update为true            this.useUpdate = true;                        // 初始化            lighter.asset.startLoad();            // 按键F1执行逻辑            InputUtil.onKeyDown(mw.Keys.F1, () => {                // 播放UI的逻辑,具体看UI动画案例部分                this.playUIAnimate();            })        }    }}


更新周期
update 可以传 dt,也可以固定帧率更新动画。
为了让动画不跳帧,推荐使用固定帧率播放

/*** 周期函数 每帧执行* 此函数执行需要将this.useUpdate赋值为true* @param dt 当前帧与上一帧的延迟 / 秒*/protected onUpdate(dt: number): void {    lighter.animation.getGlobalAnimationManager().update(1 / 60);}



销毁
动画播放完毕后,调用destroy来回收Animator,以供下次复用

// 通过获取持有animatorUI的rootCanvas// 再传入RootCanvas以获取Animatorconst animator = lighter.animation.Animator.getAnimator(rootCanvas);// 销毁animator.destroy()


监听动画事件
当前有两种事件可监听
1.使用 Animator.onAnimationLifeEvent监听动画生命周期事件
2.使用Aniamtor.onAnimationEvent 监听用户在动画编辑器中配置的事件
动画生命周期包含:
Play - 开始播放时触发
Stop - 停止播放时触发
Pause - 暂停播放时触发
  • Resume - 恢复播放时触发

Lastframe - 当播放循环次数大于1时会在当次播放最后一帧触发
Finished - 动画完成播放时触发



控制动画的 API
参数释义
AnimationClip : 保存基于关键帧的动画,即文档里提到的动画片段,用于动画播放
AnimationState : 动画混合状态,用于衔接两段动画片段

export class Animator {    static pools: Animator[];    static getAnimator(target: unknown): Animator;    onAnimationEvent: mw.Action2<string, string[]>;    onAnimationLifeEvent: mw.Action2<EventType, AnimationState>;    /**     * 播放动画片段     * @param animationClip 动画片段 | 动画片段的名字     */    play(animationClip: AnimationClip | string): void;    // 暂停动画播放    pause(): void;    // 恢复动画播放    resume(): void;    /**     * 动画混合     * 在一段动画播放过程中根据参数2的时间,间隔切换到名为name的动画混合     * @param name 动画混合的名字     * @param duration 间隔时间     */    crossFade(name: string, duration?: number): void;    /**     * 根据名字返回动画混合     * @param name 动画混合的名字     * @returns 动画混合     */    getAnimationState(name: string): AnimationState;    /**     * 通过动画片段创建一个动画混合     * 与addClip接口效果类似,但该接口能多个AnimationClip创建AnimationState     * @param clip 动画片段     * @param name 动画混合的名字     * @returns 动画混合     */    createState(clip: AnimationClip, name?: string): AnimationState;    /**     * 通过名字移除一个动画混合     * @param clip 动画片段     */    removeState(name: string): void;    /**     * 给底层AnimationState容器添加一个动画片段     * 与createState接口效果类似,但该接口会对AnimationClip进行去重     * 可用于播放连着播放多段动画     * @param clip 动画片段     * @param name 动画混合的名字     * @returns 动画混合     */    addClip(clip: AnimationClip, name?: string): AnimationState;    /**     * 通过动画片段移除一个动画混合     * 当动画播放中时,强制停止会使得当前动画立刻停止,否则无效     * @param clip 动画片段     * @param force 强制停止     */    removeClip(clip: AnimationClip, force?: boolean): void;        // 销毁    destroy(): void;}


UI 动画案例
保证 UI 文件夹下有相应名称的 UI 文件


创建及播放完整代码

private playUIAnimate() {// 代码创建对象并获取其RootCanvas    const widget = createUIByPath('UI/ExampleAnimate.ui');    const rootCanvas = widget.findChildByPath("RootCanvas");    UIService.canvas.addChild(rootCanvas);    // 通过传入RootCanvas获取Animator    const animator = lighter.animation.Animator.getAnimator(rootCanvas);    // 监听动画生命周期事件    animator.onAnimationLifeEvent.add(this.onAnimationLifeEvent, this);    // 监听动画事件    animator.onAnimationEvent.add(this. , this);    // 获取想要播放的动画片段。这里传入的是动画.ts文件的guid    const clip = AnimationAssetMgr.getAnimationClipWithUUID('0761464D4C972C5B6E8A81BD96093C53');    // 播放加载的动画    animator.play(clip);}


1.创建 UI 并获取RootCanvas
2.通过给lighter.animation.Animator.getAnimator传入RootCanvas获取Animator
3.通过 AnimationAssetMgr 和动画 guid 获取创建好的动画片段
所有动画文件都存放在 JavaScripts/lighter/asssets/mwanim 目录下,文件中存放了 guid,该 guid 一般等于编辑器中脚本的 guid ,两种方式都可以获取 clip
方法 1:右键对应动画复制资源 ID



方法 2:打开对应目录下。ts 动画信息文件后找到 guid 一行


4.传入动画片段到animator.play(clip)播放动画

完整项目 Demo






回复

使用道具 举报

怜toki 发表于 2024-10-8 16:43:36 | 显示全部楼层
提个优化建议,特效能否增加控制开启关闭,现在只能改显隐,使用有些受限
回复

使用道具 举报

奇妙造物大全
2
Timeline插件使用手册
奇妙造物大全(内含bug)
56人已阅读
3
配置表工具使用手册
奇妙造物大全(内含bug)
514人已阅读
4
动画编辑器使用手册
奇妙造物大全(内含bug)
722人已阅读
5
插件分享:圆规工具使用手册
奇妙造物大全(内含bug)
186人已阅读
6
导出UI使用手册
奇妙造物大全(内含bug)
185人已阅读
7
“为啥你不想用水体区域”“请看VCR!”
奇妙造物大全(内含bug)
283人已阅读
8
AI助手MetaGPT使用手册
奇妙造物大全(内含bug)
682人已阅读
9
天空盒预览工具使用手册
奇妙造物大全(内含bug)
287人已阅读
10
生成图集工具使用手册
奇妙造物大全(内含bug)
289人已阅读
11
距离测量及寻路检测工具使用手册
奇妙造物大全(内含bug)
320人已阅读
12
简单属性面板工具使用手册
奇妙造物大全(内含bug)
388人已阅读
13
资源强化浏览使用手册
奇妙造物大全(内含bug)
318人已阅读
14
多语言工具使用手册
奇妙造物大全(内含bug)
369人已阅读
15
交互物预览工具使用手册
奇妙造物大全(内含bug)
362人已阅读
快速回复 返回顶部 返回列表