版本: 3.4.0
参考:
Spine 骨骼动画资源
Spine Skeleton组件
cocosLua 之 骨骼动画
简介
使用spine动画,cocosCreator目前支持的版本:
creator版本 | spine版本 |
---|---|
V3.0 及以上 | v3.8(原生平台不支持特定版本 v3.8.75) |
v2.3 及以上 | v3.8 |
v2.2 | v3.7 |
v2.0.8~v2.1 | v3.6 |
v2.0.7 及以下 | v2.5 |
spine骨骼动画所需资源主要有如下三种:
.png
动画图片相关.txt/.atlas
图集配置数据相关.json/.skel
骨骼数据配置相关
spine骨骼动画主要组成部分:
bones
骨骼相关,基本组成元素,存在父子关系;每个骨骼可关联多个slot
相关slots
插槽相关, 主要用于关联多个attachment
相关attachment
附件相关, 有Mesh, BoundingBox, SkinnedMesh等不同类型skins
皮肤相关animations
动画相关ik
在cocosCreator中使用spine动画,官方封装的组件是sp.Skeleton
。
sp.Skeleton组件的使用
通过编译器添加spine动画,大概步骤:
- 在层级管理器创建一个空节点,假设命名为
spine_node
- 在
spine_node
的属性检查器中,添加组件,搜索skeleton
添加进来 - 可把
UITransform
的锚点修改为(0.5, 0)
如果在脚本中使用,注意类型:
- 如果声明为
sp.Skeleton
类型,将编译器的节点spine_node
放进去,在脚本中就可以直接对骨骼动画进行处理 - 如果声明为
Node
类型,将编译器的节点spine_node
放进去,则需要在脚本中通过getComponent(sp.Skeleton)
来骨骼动画组件
export class DebugLayer extends Component {
@property(Node)
spineNode: Node = null;
private _spine: sp.Skeleton = null;
onLoad() {
this._spine = this.spineNode.getComponet(sp.Skeleton);
}
onEnable() {
// 使用Node类型,可以添加对spine动画的触摸事件
this.spineNode.on(Node.EventType.TOUCH_END, this._touchSpineEvent, this);
}
private _touchSpineEvent() {
console.log("you touch spine");
}
}
cocosCreator通过Skeleton对spine骨骼动画封装了很多方法:
export class Skeleton extends Renderable2D {
// 是否循环播放
loop: boolean;
// 当前骨骼动画是否暂停
get paused(): boolean;
// 当前播放的动画名称
get animation(): string;
// 设置动画播放速度,数值越大,播放速度越快
get timeScale(): number;
// 是否显示slot的测试信息
get debugSlots(): boolean;
// 是否显示bone的测试信息
get debugBones(): boolean;
// 是否显示mesh的测试信息
get debugMesh(): boolean;
// 是否启用染色效果
get useTint(): boolean;
// 获取动画状态
// 设置皮肤,比如切换衣服
setSkin(skinName: string): void;
// 设置附件,比如切换武器
setAttachment(slotName: string, attachmentName: string): void;
// 设置两个动画之间的混合时间,可以优化两个动画之间切换不连贯
setMix(fromAnimation: string, toAnimation: string, duration: number): void;
// 设置当前动画。队列中的任何的动画将被清除
// 可以理解:如果前面有动画正在播放,且不管是否循环,会直接停止,然后播放设置的动画
setAnimation(trackIndex:number, name:string, loop:boolean):spine.TrackEntry;
// 添加一个动画到动画队列尾部,还可以延迟指定的秒数
// 可以理解:该动画会等待当前播放的动画在单次循环结束后,开始播放
addAnimation(trackIndex, name, loop, delay:number): spine.TrackEntry;
// 还原到起始动作, 它主要用于处理上个动作留下的残影效果
setToSetupPose(): void;
setBonesToSetupPose(): void;
setSlotsToSetupPose(): void;
// 根据骨骼名字获取骨骼信息
findBone(boneName: string): spine.Bone | null;
// 根据关节名字获取关节信息
findSlot(slotName: string): spine.Slot | null;
//
get sockets(): SpineSocket[];
get socketNodes(): Map<number, Node>;
/*
* 若想切换渲染模式,最好在设置'dragonAsset'之前,先设置好渲染模式,否则有运行时开销。
* 若在编辑中设置渲染模式,则无需担心设置次序的问题。
*/
setAnimationCacheMode(cacheMode: AnimationCacheMode): void;
// 当前是否处于缓存模式
isAnimationCached(): boolean;
// 通过 track 索引获取 TrackEntry
getCurrent(trackIndex: number): spine.TrackEntry | null;
// 清除所有 track 的动画状态
clearTracks(): void;
/*
spine事件回调相关,它们主要用于对应:
Start: 用来设置开始播放动画的事件监听
Interrupt: 用来设置动画被打断的事件监听
End: 用来设置动画播放完后的事件监听
Dispose: 用来设置动画将被销毁的事件监听
Complete: 用来设置动画播放一次循环结束后的事件监听
Event: 用来设置动画播放过程中帧事件的监听
*/
setStartListener(listener: TrackListener): void;
setInterruptListener(listener: TrackListener): void;
setEndListener(listener: TrackListener): void;
setDisposeListener(listener: TrackListener): void;
setCompleteListener(listener: TrackListener): void;
setEventListener(listener: TrackListener2): void;
// 增加了trackIndex相关
setTrackStartListener(entry: spine.TrackEntry, listener: TrackListener): void;
setTrackInterruptListener(entry, listener: TrackListener): void;
setTrackEndListener(entry, listener: TrackListener): void;
setTrackDisposeListener(entry, listener: TrackListener): void;
setTrackCompleteListener(entry, listener: TrackListener2): void;
setTrackEventListener(entry, listener: TrackListener | TrackListener2): void;
}
关于trackIndex
一般情况下默认为0,如果要获取当前动画的索引,可以这样:
// 设置开始播放动画的事件监听
this.monsterSpine.setStartListener(trackEntry => {
this._curTrackIndex = trackEntry.trackIndex;
});
简单的实例
export class UI_PillageLayer extends Component {
@property(sp.Skeleton)
spine: sp.Skeleton = null; // skeleton组件
onLoad() {
// 设置播放速度
this.spine.timeScale = 0.6;
// 设置两个动画之间的混合时间
this.spine.setMix("walk", "run", 0.2);
// 设置当前动画播放
this.spine.setAnimation(0, "walk", true);
// 设置当前皮肤
this.spine.setSkin("boy");
// spine动画单次循环结束回调相关
this.spine.setCompleteListener((trackEntry) => {
let track = trackEntry.trackIndex;
let aniName = trackEntry.animation ? trackEntry.animation.name : "";
let loopCount = Math.floor(trackEntry.trackTime/trackEntry.animationEnd);
if (aniName === "walk") {
this.spine.timeScale = 1;
this.spine.setToSetupPose();
this.spine.setAnimation(trackIndex, "run", true);
}
});
}
// 更换皮肤
public ChangeSkinEvent() {
this.spine.setSkin("girl");
}
}