Unity Spine动画事件 SkeletonGraphic
文章目录
- 前言
- 一、什么是 Unity SkeletonGraphic?
- 二、代码部分
- 1.核心脚本
- 2.简单使用
- 总结
前言
不少人在Unity中刚开始用到Spine的时候添加事件不会做,这边直接提供了一个脚本,挂载到SkeletonGraphic组件的物体上直接可以代码进行添加事件。
提示:以下是本篇文章正文内容,下面案例可供参考
一、什么是 Unity SkeletonGraphic?
- SkeletonGraphic 是 Unity 中 Spine 插件(用于 2D 骨骼动画)提供的核心组件之一,专门用于在 Unity 的 UI 系统(UGUI)中渲染 Spine 骨骼动画。简单来说:
它是 Spine 动画与 UGUI 结合的 “桥梁”,让你可以把 Spine 制作的骨骼动画(比如角色、特效)直接放到 UI 层级(如 Canvas 下)显示。
区别于 SkeletonAnimation(用于世界空间的 3D/2D 渲染),SkeletonGraphic 完全适配 UGUI 的渲染逻辑,支持 UI 排序、遮罩、层级叠加等特性。 - 核心用途:
UI 中的骨骼动画:比如游戏的 UI 按钮动效、弹窗角色展示、血条旁的角色头像动画等,这些需要和 UI 元素(按钮、文本)同层级显示的骨骼动画,都可以用 SkeletonGraphic 实现。
适配 UGUI 特性:支持 UGUI 的 Canvas 渲染模式(屏幕空间、世界空间)、UI 遮罩(Mask)、Raycast 射线检测(比如点击动画区域触发事件)。
高性能渲染:基于 UGUI 的批处理机制,多个 SkeletonGraphic 可以合批渲染,减少 DrawCall,提升性能。
二、代码部分
1.核心脚本
代码如下(示例):
using System.Collections.Generic;
using UnityEngine;
using Spine.Unity;
using Spine;
using System;
using UnityEngine.Events;
public class SpineEventHelper : MonoBehaviour
{
[Serializable]
public class SpineFrameKey
{
[SpineEvent] public string key;
public UnityEvent handler;
}
[SerializeField] private List<SpineFrameKey> handlers;
private Dictionary<string, UnityEvent> keyFrameEventDict = new Dictionary<string, UnityEvent>();
public const string START_EVENT = "START_EVENT", END_EVENT = "END_EVENT";
private SkeletonGraphic skeletonAnimation;
private void Start()
{
Bind();
}
private void Bind()
{
skeletonAnimation = GetComponent<SkeletonGraphic>();
if (skeletonAnimation == null) return;
skeletonAnimation.AnimationState.Event += HandleEvent;
skeletonAnimation.AnimationState.Start += delegate (TrackEntry trackEntry)
{
if (keyFrameEventDict.TryGetValue(START_EVENT, out UnityEvent @event))
{
@event.Invoke();
}
};
skeletonAnimation.AnimationState.Complete += delegate
{
if (keyFrameEventDict.TryGetValue(END_EVENT, out UnityEvent @event))
{
@event.Invoke();
}
};
foreach (var handler in handlers)
{
keyFrameEventDict[handler.key] = handler.handler;
}
}
///
/// 添加自定义的帧事件
///
/// 帧名称
/// 处理的方法
public void AddCustomEventHandler(string key, UnityAction handler)
{
if (keyFrameEventDict.TryGetValue(key, out UnityEvent ev))
{
ev.AddListener(handler);
}
else
{
var ue = new UnityEvent();
ue.AddListener(handler);
keyFrameEventDict.Add(key, ue);
}
}
/// 移除自定义的帧事件
///
/// 指定帧名称
/// 处理的方法 如果忽略则移除key的所有帧事件
public void RemoveCustomEventHandler(string key, UnityAction handler = null)
{
if (keyFrameEventDict.TryGetValue(key, out UnityEvent ev))
{
if (handler == null)
{
ev.RemoveAllListeners();
}
else
{
ev.RemoveListener(handler);
}
}
}
///
/// 添加动画开始事件
///
/// 开始处理事件的方法
public void AddStartEventHandler(UnityAction handler)
{
if (keyFrameEventDict.TryGetValue(START_EVENT, out UnityEvent ev))
{
ev.AddListener(handler);
}
else
{
var ue = new UnityEvent();
ue.AddListener(handler);
keyFrameEventDict.Add(START_EVENT, ue);
}
}
///
/// 移除开始帧事件
///
///
public void RemoveStartEventHandler(UnityAction handler)
{
if (keyFrameEventDict.TryGetValue(START_EVENT, out UnityEvent ev))
{
ev.RemoveListener(handler);
}
}
///
/// 添加结束帧事件
///
///
public void AddEndEventHandler(UnityAction handler)
{
if (keyFrameEventDict.TryGetValue(END_EVENT, out UnityEvent ev))
{
ev.AddListener(handler);
}
else
{
var ue = new UnityEvent();
ue.AddListener(handler);
keyFrameEventDict.Add(END_EVENT, ue);
}
}
///
/// 移除结束帧事件
///
///
public void RemoveEndEventHandler(UnityAction handler)
{
if (keyFrameEventDict.TryGetValue(END_EVENT, out UnityEvent ev))
{
ev.RemoveListener(handler);
}
}
private void HandleEvent(TrackEntry trackEntry, Spine.Event e)
{
if (keyFrameEventDict.TryGetValue(e.Data.Name, out UnityEvent @event))
{
@event.Invoke();
}
}
///
/// 清理所有帧事件
///
public void Clear()
{
keyFrameEventDict.Clear();
}
private void OnDestroy()
{
Clear();
}
}
2.简单使用
代码如下(示例):
using Spine;
using Spine.Unity;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class GameSpineCtrl : MonoBehaviour
{
public SkeletonGraphic skeletonGraphic;
private TrackEntry trackEntry;
public string animationName;
EffectAudioPoint effectAudioPoint;
void Start()
{
skeletonGraphic.transform.GetComponent<SpineEventHelper>().AddCustomEventHandler("Atk", AtkHadler);
skeletonGraphic.transform.GetComponent<SpineEventHelper>().AddEndEventHandler(EndHadler);
skeletonGraphic.transform.GetComponent<SpineEventHelper>().AddEndEventHandler(StartHadler);
}
void Update()
{
}
private void OnDestroy()
{
skeletonGraphic.transform.GetComponent<SpineEventHelper>().RemoveCustomEventHandler("Atk", AtkHadler);
skeletonGraphic.transform.GetComponent<SpineEventHelper>().RemoveCustomEventHandler(EndHadler);
skeletonGraphic.transform.GetComponent<SpineEventHelper>().RemoveCustomEventHandler(StartHadler);
}
public void SetupSkeleton()
{
// 设置皮肤
skeletonGraphic.Skeleton.SetSkin("default");
// 设置初始动画
trackEntry = skeletonGraphic.AnimationState.SetAnimation(0, animationName, false);
// 调整颜色
skeletonGraphic.color = new Color(1, 1, 1, 1f);
skeletonGraphic.Initialize(true); // 重新初始化骨骼动画
// 更新骨骼
skeletonGraphic.Update(0);
}
///
/// 播放指定动画
///
/// 动画名称
/// 是否循环
public void PlayAnimation(string animationName, bool loop = false, float TimeScale = 1)
{
if (skeletonGraphic == null)
{
SetupSkeleton();
}
this.animationName = animationName;
// 停止当前动画
skeletonGraphic.Skeleton.SetToSetupPose();
skeletonGraphic.AnimationState.ClearTrack(0);
// 播放新动画
trackEntry = skeletonGraphic.AnimationState.SetAnimation(0, animationName, loop);
// 可以设置动画速度
trackEntry.TimeScale = TimeScale;
}
public void AtkHadler()
{
Debug.Log("AtkHadler");
}
public void StartHadler()
{
Debug.Log("StartHadler");
}
public void EndHadler()
{
Debug.Log("EndHadler");
}
}
总结
核心定位:这是一个高复用性的 Spine UI 动画事件管理工具,统一处理 SkeletonGraphic 的自定义帧事件、动画开始 / 结束事件。
核心优势:兼顾编辑器可视化配置和运行时动态管理,解耦事件逻辑,提升代码可维护性。
使用要点:需挂载在含 SkeletonGraphic 的对象上,确保事件名与 Spine 配置一致,销毁时清理事件回调避免内存泄漏。






