个人技术总结——Spine的使用

222200424 赵伟豪 2024-12-20 13:38:25
这个作业属于哪个课程FZU_SE_teacherW_4
这个作业要求在哪里软件工程实践总结&个人技术博客
学号222200424赵伟豪
这个作业的目标个人技术总结
其他参考文献Spine 相关的基本知识点和术语Spine在Unity中的应用Spine 实用技巧大全spine用户指南
#个人技术总结——Spine的使用

1.技术概述

1.1spine是什么

Spine是一款专为游戏开发而设计的2D骨骼动画编辑工具,它允许开发者通过将图片绑定到骨骼上,然后控制骨骼来实现动画效果。(骨骼动画:通过骨骼和绑定的图片来创建动画,相较于传统的逐帧动画,Spine动画在资源大小和流畅性方面具有显著优势。)

1.2spine的使用场景

Spine可以为游戏角色创建各种动作和表情,如行走、跑步、攻击等,使角色更加生动逼真。例如,在角色扮演游戏中,开发者可以利用Spine制作角色的战斗动作和日常活动动画。spine可以渲染游戏场景中的各种特效和背景动画,如树叶摇曳、水流波动等,增强游戏的视觉效果和氛围感。在多媒体制作领域,Spine可以用于制作各种动画视频、动画广告等,提供丰富的动画效果和创意表现。(近些年大火的虚拟主播)

1.3学习该技术的原因:

1.我们的项目是用unity开发一款塔防游戏,那么游戏中就会出现大量的人物、动物等等。借由spine制作这些人物的动作可以展现出更好的动画效果。

2.此外Spine是专注于游戏的2D动画软件,可以创建骨骼动画,并将其整合到游戏当中,提供了一套高效的工作流程。

3.代码通过API可以直接访问骨骼、附件、皮肤以及动画等数据。使用API可以在游戏中操作骨骼、组合动画、创造淡入淡出等效果。轻松实现代码和动画之间的组合。

1.4主要的技术难点:

动画细节把控:骨骼动画涉及到大量的细节处理,如骨骼的设置、权重的分配、动画帧的创建与编辑等,稍有不慎就会导致动画效果失真。

复杂性:骨骼动画系统本身较为复杂,涉及到大量的参数调整,如骨骼的层级关系、约束关系、动画的过渡和混合等。

动画控制的复杂性:虽然Spine提供了丰富的动画控制功能,但如何灵活运用这些功能来实现复杂的动画逻辑和交互效果,对开发者来说是一个挑战.

跨平台兼容性:不同平台和设备的性能差异可能导致骨骼动画在导出和运行时出现兼容性问题,需要确保动画在各种环境下都能保持良好的表现。

2.技术详述

2.1项目背景与需求

我们团队的目标是使用unity引擎开发一款游戏,游戏中登场的人物和动物等等自然会有很多的动画需求,而其中spine的动画效果好,且便于整合到unity中。

2.2技术的实现以及使用

2.2.1基本概念

Skeleton(骨架)
数据的集合,包含构成此骨架的所有骨骼、插槽、附件及其他信息。

bones(骨骼)
骨骼是动画的基本结构,每个骨骼都有自己的位置、旋转、缩放和父子关系,所以可以通过控制骨骼的这些属性可以实现动画效果。同时骨骼之间存在父子关系,父骨骼的变化会传递到子骨骼,即父骨骼能影响子骨骼变化。

slots(槽位)
插槽是必须要在骨骼下,一个骨骼可以包含多个插槽。这也意味着我们可以对插槽进行切换(例如武器的更换)。插槽可以控制附件的一些渲染属性例如:颜色、混合、alpha等,会影响附件的渲染。

Attachments(附件)
附件通常是绑定到骨骼上,但是一般都附加到插槽上。附件可以是图片,也可以是检测碰撞的边界框(例如是武器的碰撞体)等。插槽可以加多个附件,而附件的可见与不可见由骨骼控制,在运行时,只能显示一个附件。

skin(皮肤)
skin可以看做是attachment的集合,或者可以认为是attachment的一个映射查询表,一个人物可以由多套skin,通过切换skin的方式去查询不同的附件映射表,便可以变相的实现人物的全身换装。

2.2.2json的数据结构

spine资源有altas、png、json。其中json主要包含动画的变换信息。

skins:记录了所有的皮肤信息。每条信息一般的结构如下:

"skins": {
    "newSkin2": {
        "slotName2": {
            "attachmentName3": {
                "type": "mesh",
                "uvs": [0.1, 0.1, 0.9, 0.1, 0.1, 0.9, 0.9, 0.9],
                "triangles": [0, 1, 2, 2, 3, 1],
                "vertices": [x1, y1, x2, y2, x3, y3, x4, y4],
                "edges": [0, 1, 1, 2, 2, 3, 3, 0],
                "width": 200,
                "height": 100
            },
            "attachmentName4": {
                "type": "region",
                "x": 5,
                "y": 10,
                "rotation": 30,
                "width": 80,
                "height": 60,
                "scaleX": 0.8,
                "scaleY": 1.2,
                "color": "ff00ff"
            }
        },
        "slotName3": {
            "attachmentName5": {
                "type": "region",
                "x": 0,
                "y": 0,
                "rotation": 0,
                "width": 150,
                "height": 150,
                "scaleX": 1.0,
                "scaleY": 1.0,
                "color": "00ff00"
            },
            "attachmentName6": {
                "type": "mesh",
                "uvs": [0.2, 0.2, 0.8, 0.2, 0.2, 0.8, 0.8, 0.8],
                "triangles": [0, 1, 2, 2, 3, 1],
                "vertices": [x1, y1, x2, y2, x3, y3, x4, y4],
                "edges": [0, 1, 1, 2, 2, 3, 3, 0],
                "width": 250,
                "height": 200
            }
        }
    }
}

事件(Events):主要用于在特定的帧上触发特定的操作,可以是触发脚本中的函数(可携带参数)、音效、粒子效果等.

 
  "events": [
    {
      "name": "event_name",  // 事件的名称
      "int": 34,  // 事件的整数参数
      "float": 4.32,  // 事件的浮点参数
      "string": "event_description",  // 事件的描述
      "audio": "event_sound.ogg",  // 与事件关联的音频文件
      "volume": 0.6,  // 音频音量
      "balance": 0.0  // 音频平衡(左右声道)
    },
    // 其他事件...
  ]

动画(animations):主要是记录了动作的时间轴列表。每个动作中包含插槽时间轴列表,指定了时间轴所使用的附件。骨骼的时间轴列表,指定了所使用到的骨骼在时间轴上的变化情况,例如:旋转、缩放、位移等。

"animations": {
"jump": {
 "slots": {
   "slot1": {
     "0": [{ "attachment": "attachment1" }],
     "30": [{ "attachment": "attachment2" }],
     "60": [{ "attachment": "attachment1" }]
   },
   "slot2": {
     "0": [{ "attachment": "attachment6" }],
     "45": [{ "attachment": "attachment7" }],
     "90": [{ "attachment": "attachment6" }]
   }
 },
 "bones": {
   "bone1": {
     "0": { "rotation": 0, "x": 0, "y": 0 },
     "45": { "rotation": 90, "x": 0, "y": 10 },
     "90": { "rotation": 0, "x": 0, "y": 0 }
   },
   "bone2": {
     "0": { "rotation": 0, "x": 0, "y": 0 },
     "60": { "rotation": -45, "x": 5, "y": 5 },
     "120": { "rotation": 0, "x": 0, "y": 0 }
   }
 }
}

2.2.3创建骨骼与绑定骨骼

创建骨骼:在spine中创建角色或对象的骨骼结构。骨骼是动画的基础,需要根据角色的解剖结构或对象的运动逻辑来设置骨骼的层级关系和连接方式。为每个骨骼设置属性,如长度、旋转、缩放等,确保骨骼能够正确地模拟角色或对象的运动。

绑定骨骼:将准备好的图像素材绑定到相应的骨骼上。这个过程称为“蒙皮”或“绑定”,需要确保图像与骨骼的对应关系准确,以便在骨骼移动时,图像能够跟随骨骼一起运动。在绑定过程中,可能需要调整图像的位置和旋转角度,使其与骨骼的运动更加协调。

img

2.2.4实现动作与关键帧

根据角色或对象的动作需求,创建动画帧。动画帧是动画的基本单位,每个动画帧代表角色或对象在某一时刻的状态。在动画帧中设置关键帧,关键帧定义了骨骼在特定时间点的位置、旋转、缩放等属性。通过关键帧之间的插值,可以生成平滑的动画过渡。此外,为了使动画更加自然和流畅,可以调整关键帧之间的插值曲线,如贝塞尔曲线等,控制动画的加速度和减速度。

img

2.2.5spine动画的导出与应用

导出动画资源:将制作好的动画导出为适合游戏或应用使用的格式,如JSON、二进制文件等。导出过程中需要注意资源的压缩和优化,以减少文件大小和提高加载速度。

应用到项目中:将导出的动画资源导入到游戏或应用项目中,并在相应的场景或对象上应用动画。通过编程控制动画的播放、暂停、循环等,实现动画的交互和展示效果。

{"skeleton":{"hash":"lEVp+N1IgmEQu3VHkdjeoUBdEo4","spine":"3.8","x":-339,"y":32.56,"width":695,"height":520.44,"images":"./images2/","audio":"C:/Users/Administrator/Desktop/狼spine"},"bones":[{"name":"root"},{"name":"bone","parent":"root","length":207.68,"rotation":-0.5,"x":-3.99,"y":-0.71},{"name":"bone2","parent":"bone","length":108.83,"rotation":172.9,"x":183.1,"y":345.92},{"name":"bone3","parent":"bone2","length":131.25,"rotation":7.59,"x":108.83},{"name":"bone4","parent":"bone3","length":118.83,"rotation":-14.46,"x":131.25},{"name":"bone5","parent":"bone4","length":38.16,"rotation":-28.63,"x":118.83},{"name":"bone6","parent":"bone5","length":104.61,"rotation":-22.55,"x":39.48,"y":1.23},{"name":"bone15","parent":"bone2","length":82.96,"rotation":127.49,"x":-21.68,"y":6.18},{"name":"bone16","parent":"bone15","length":64.08,"rotation":4.24,"x":82.96},{"name":"bone17","parent":"bone16","length":58.63,"rotation":20.22,"x":64.08},{"name":"bone18","parent":"bone4","length":107.31,"rotation":113.13,"x":42.54,"y":115.88},{"name":"bone19","parent":"bone18","length":76.55,"rotation":-21.57,"x":107.31},{"name":"bone20","parent":"bone19","length":23.03,"rotation":-25.77,"x":76.55},{"name":"bone21","parent":"bone4","length":99.48,"rotation":110.68,"x":100.19,"y":133.52},{"name":"bone22","parent":"bone21","length":79.84,"rotation":-13.99,"x":99.48},{"name":"bone23","parent":"bone22","length":37.07,"rotation":-31.33,"x":79.84},{"name":"bone24","parent":"bone","length":54.02,"rotation":178.29,"x":-184.4,"y":75.36,"color":"ff3f00ff"},{"name":"bone25","parent":"bone","length":53.98,"rotation":-178.77,"x":-126.26,"y":74.48,"color":"ff3f00ff"},{"name":"bone7","parent":"bone2","length":149.42,"rotation":129.15,"x":43.21,"y":88.86},{"name":"bone8","parent":"bone7","length":72.44,"rotation":-50.51,"x":149.42},{"name":"bone9","parent":"bone8","length":50.38,"rotation":-55.1,"x":72.44},{"name":"bone10","parent":"bone2","length":142.43,"rotation":122.29,"x":109.47,"y":99.79},{"name":"bone11","parent":"bone10","length":59.58,"rotation":-55.43,"x":142.43},{"name":"bone12","parent":"bone11","length":50.76,"rotation":-33.4,"x":59.58},{"name":"bone13","parent":"bone","length":63.7,"rotation":-177.64,"x":91.37,"y":82.82,"color":"ff3f00ff"},{"name":"bone14","parent":"bone","length":61.62,"rotation":178.57,"x":184.22,"y":68.4,"color":"ff3f00ff"}],"slots":[{"name":"图层 1","bone":"root","attachment":"图层 1"},{"name":"图层 2","bone":"root","attachment":"图层 2"},{"name":"图层 3","bone":"root","attachment":"图层 3"},{"name":"图层 4","bone":"root","attachment":"图层 4"},{"name":"图层 5","bone":"root","attachment":"图层 5"},{"name":"图层 6","bone":"root","attachment":"图层 6"},{"name":"图层 7","bone":"root","attachment":"图层 7"}],"ik":[{"name":"bone13","order":1,"bones":["bone10","bone11"],"target":"bone13","bendPositive":false},{"name":"bone14","bones":["bone7","bone8"],"target":"bone14","bendPositive":false},{"name":"bone24","order":3,"bones":["bone21","bone22"],"target":"bone24","bendPositive":false},{"name":"bone25","order":2,"bones":["bone18","bone19"],"target":"bone25","bendPositive":false}],"skins":[{"name":"default","attachments":{"图层 1":{"图层 1":{"type":"mesh","uvs":[0.26892,0,0.4198,0.13433,0.61392,0.13925,0.80805,0.14418,0.82987,0.16369,0.93768,0.31813,1,0.52207,1,0.52486,0.94591,0.73588,0.89181,0.94689,0.84993,0.94118,0.71836,0.95588,0.5868,0.97059,0.32366,1,0.19892,1,0.09944,0.95404,0.05037,0.80665,0.00131,0.65926,0,0.49175,0.02142,0.33717,0.22848,0.26524,0.24517,0.13262,0.26186,0,0.46,0.56706,0.75904,0.39961,0.16788,0.65933,0.75378,0.70638,0.63062,0.48666,0.29983,0.71801,0.418,0.81435,0.36255,0.33494,0.49752,0.33129},{"time":0.3333,"vertices":[-20.25177,20.41175,-11.1243,11.21221,-1.99664,2.01263,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-19.63763,19.79268,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2.75314,2.77491,0,0,0,0,0,0,-10.40002,10.48221]}]}}},"drawOrder":[{"offsets":[{"slot":"图层 5","offset":-4}]}]}}}
狼.png
size: 1281,241
format: RGBA8888
filter: Linear,Linear
repeat: none
图层 1
  rotate: false
  xy: 2, 2
  size: 527, 237
  orig: 527, 237
  offset: 0, 0
  index: -1
图层 2
  rotate: true
  xy: 531, 36
  size: 203, 172
  orig: 203, 172
  offset: 0, 0
  index: -1
图层 3
  rotate: false
  xy: 705, 30
  size: 167, 209
  orig: 167, 209
  offset: 0, 0
  index: -1
图层 4
  rotate: false
  xy: 874, 20
  size: 129, 219
  orig: 129, 219
  offset: 0, 0
  index: -1
图层 5
  rotate: false
  xy: 1005, 32
  size: 115, 207
  orig: 115, 207
  offset: 0, 0
  index: -1
图层 6
  rotate: false
  xy: 1204, 41
  size: 75, 198
  orig: 75, 198
  offset: 0, 0
  index: -1
图层 7
  rotate: false
  xy: 1122, 23
  size: 80, 216
  orig: 80, 216
  offset: 0, 0
  index: -1

3遇到的问题和解决过程

3.1问题1:动画跳帧

描述:有的时候需要在动画播放完毕时新引入一个动画效果,直接引入就会出现跳帧的情况。

解决方案:调用AnimationState.AddAnimation这个方法

SkeletonGraphic Graphic;   //定义Spine类型

Graphic = Spine.gameObject.GetComponent<SkeletonGraphic>(); //找到Spine骨架

var spAnim = Graphic.Skeleton.Data.FindAnimation("Idle");
 if (null != spAnim)
 {
               Graphic.AnimationState.SetAnimation(0, "Idle", true);//播放Spine动画。
 }
public SkeletonAnimation sa;
sa.AnimationState.AddAnimation(层级(int),要播放的动画名(string),是否循环(bool),延迟多久播放(float));

3.2问题2:切换动画的bug

描述::spine在切换动画的时候自动补偿,用于动画的平稳过度,所以可能会出现残影等bug

解决方案:

using UnityEngine;
using Spine.Unity;
 
public class TestSpine : MonoBehaviour
{
     SkeletonAnimation ska;   
    void Start()
    {
        ska = gameObject.GetComponent<SkeletonAnimation>();
        ska.timeScale = 1.5f;
        ska.loop = true;
        ska.AnimationName = "animation";
        //参数 1.层级, 2.要播放的动画名 , 3.是否循环        
        ska?.AnimationState.SetAnimation(0, "animation", true);
    }
}

获取 SkeletonAnimation 这个核心控制组件,去设置一些运行参数,需要在SetAnimation前调用
skeletonAnimation.skeleton.SetToSetupPose ();
spineAnimationState.ClearTracks ();
来消除前一个动画的影响。

3.3问题3:设置人物手臂跟随鼠标移动时出现问题

描述:在一些场景中,我们需要实现人物的手臂跟随鼠标移动的动作。

解决方案:

public Bone bone;
SkeletonAnimation ska;
void Start()
    {
        ska = gameObject.GetComponent<SkeletonAnimation>();
        bone = ska?.skeleton.FindBone("target1");
    }
bone.Skeleton.FindIkConstraint(IK骨骼的名字(string)).Mix = 1;

引入命名空间获取IK骨骼,可以通过这个方法来操控IK,实现手臂跟随鼠标的效果。

4.总结

Spine是专为Unity游戏引擎设计的一款2D骨骼动画工具,它基于Esoteric Software的Spine动画系统。Spine插件极大地简化了在Unity中创建、编辑和播放高质量2D骨骼动画的过程,提供了丰富的功能和高效的性能。Spine为2D游戏开发者提供了一套完整的骨骼动画解决方案,从创建到导出,再到在Unity中播放和控制,整个流程都非常流畅,大大提升了2D游戏的视觉质量和开发效率。未来,我会更深层次的学习unity以及spine,争取在游戏制作领域深耕,

5.参考文献

Spine用户指南

Spine 相关的基本知识点和术语

Spine在Unity中的应用

Spine 实用技巧大全

...全文
2325 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

239

社区成员

发帖
与我相关
我的任务
社区管理员
  • FZU_SE_teacherW
  • 助教赖晋松
  • D's Honey
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧