功能说明:
键盘AD按键控制场景中spine对象(士兵),左右移动
鼠标左键点击屏幕,士兵攻击
士兵隔一段时间会眨一眨眼
制作流程:
hierarchy中 新建 SkeletonAnimation
建好后将其命名为 FootSoldier
将资源 FootSoldier_SkeletonData.asset 拖到 FootSoldier 的SkeletonAnimation组件中
FootSoldier 的SkeletonAnimation组件中的 skin 调成White
这时能看到屏幕上有个spine对象了,此时是没有剑的,运行后也没有剑
增加手中的剑
将脚本 AtlasRegionAttacher 挂到FootSoldier上
将spineAtlasAsset Equipment_Atlas 挂在 AtlasRegionAttacher 上
增加 AtlasRegionAttacher 上的Attachment 如图
这时,运行后手中是有剑的了
代码:这里展示的是 从资源库里找到新的资源放到某个地方
using UnityEngine;
using System.Collections.Generic;
using Spine;
using Spine.Unity.AttachmentTools;
namespace Spine.Unity.Examples {
/// <summary>
/// Example code for a component that replaces the default attachment of a slot with an image from a Spine atlas.</summary>
public class AtlasRegionAttacher : MonoBehaviour {
[System.Serializable]
public class SlotRegionPair {
[SpineSlot] public string slot;
[SpineAtlasRegion] public string region;
}
[SerializeField] protected SpineAtlasAsset atlasAsset;
[SerializeField] protected bool inheritProperties = true;
[SerializeField] protected List<SlotRegionPair> attachments = new List<SlotRegionPair>();
Atlas atlas;
void Awake () {
var skeletonRenderer = GetComponent<SkeletonRenderer>();
skeletonRenderer.OnRebuild += Apply; //这句不写不行,出不来武器
if (skeletonRenderer.valid) Apply(skeletonRenderer); //这句不写可以,正常出现武器,valid 字面意思有效的
}
void Apply (SkeletonRenderer skeletonRenderer) {
if (!this.enabled) return;
//检查atlas资源怎么样,有没有。把SkeletonRenderer的Scale找到。
atlas = atlasAsset.GetAtlas();
if (atlas == null) return;
float scale = skeletonRenderer.skeletonDataAsset.scale;
// attachments:插槽名+资源名 数组
foreach (var entry in attachments) {
Slot slot = skeletonRenderer.Skeleton.FindSlot(entry.slot); //插槽
Attachment originalAttachment = slot.Attachment;//插槽下面的附件
AtlasRegion region = atlas.FindRegion(entry.region);//资源
//检查后 插槽的附件替换成某资源:比如说 插槽的附件换成 Equipment/sword1
//检查:先看看 资源有没有
if (region == null) {
slot.Attachment = null;
}
//检查:有资源+让换 且 插槽下面附件不为空(插槽有附件)
//操作:原来的附件更新一下,替换成新资源
else if (inheritProperties && originalAttachment != null) {
slot.Attachment = originalAttachment.GetRemappedClone(region, true, true, scale);
}
//检查:有资源+其它情况
//情况1: 让换 且 附件空
//情况2: 不让换 且 附件不为空
//情况3: 不让换 且 附件空
//操作: 资源格式化,赋给附件
else
{
var newRegionAttachment = region.ToRegionAttachment(region.name, scale);
slot.Attachment = newRegionAttachment;
}
}
}
}
}
控制
脚本 FootSoldierExample 挂在 FootSoldier上
调整如下
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
// Contributed by: Mitch Thompson
using UnityEngine;
using System.Collections;
using Spine.Unity;
namespace Spine.Unity.Examples {
public class FootSoldierExample : MonoBehaviour {
[SpineAnimation("Idle")]
public string idleAnimation;
[SpineAnimation]
public string attackAnimation;
[SpineAnimation]
public string moveAnimation;
[SpineSlot]
public string eyesSlot;
[SpineAttachment(currentSkinOnly: true, slotField: "eyesSlot")]
public string eyesOpenAttachment;
[SpineAttachment(currentSkinOnly: true, slotField: "eyesSlot")]
public string blinkAttachment;
[Range(0, 0.2f)]
public float blinkDuration = 0.05f;
public KeyCode attackKey = KeyCode.Mouse0;
public KeyCode rightKey = KeyCode.D;
public KeyCode leftKey = KeyCode.A;
public float moveSpeed = 3;
SkeletonAnimation skeletonAnimation;
void Awake () {
skeletonAnimation = GetComponent<SkeletonAnimation>();
skeletonAnimation.OnRebuild += Apply; //委托:在骨架成功初始化后,开始有节奏的眨眼睛
}
//骨架初始化成功后 调用的函数
void Apply (SkeletonRenderer skeletonRenderer) {
//调用协程函数,协程函数就是眨眼睛
StartCoroutine(Blink());
}
//update中主要 用来控制移动
void Update () {
//鼠标左键 播放打击动画
if (Input.GetKey(attackKey)) {
skeletonAnimation.AnimationName = attackAnimation;
} else {
//如果 键盘按键 D 就面向右向右走
if (Input.GetKey(rightKey)) {
skeletonAnimation.AnimationName = moveAnimation;
skeletonAnimation.Skeleton.ScaleX = 1;
transform.Translate(moveSpeed * Time.deltaTime, 0, 0);
}
//如果 键盘按键 A 就面向左向左走
else if (Input.GetKey(leftKey)) {
skeletonAnimation.AnimationName = moveAnimation;
skeletonAnimation.Skeleton.ScaleX = -1;
transform.Translate(-moveSpeed * Time.deltaTime, 0, 0);
}
//如果没有以上的操作,就播放idle动画
else {
skeletonAnimation.AnimationName = idleAnimation;
}
}
}
//眨眼睛
IEnumerator Blink() {
while (true) {
yield return new WaitForSeconds(Random.Range(0.25f, 3f));
skeletonAnimation.Skeleton.SetAttachment(eyesSlot, blinkAttachment);
yield return new WaitForSeconds(blinkDuration);
skeletonAnimation.Skeleton.SetAttachment(eyesSlot, eyesOpenAttachment);
}
}
}
}
总结和疑问
替换插槽上的图片:或隐或现
这里的情况是:插槽原来就有两张图,只不过一显一隐
skeletonAnimation.Skeleton.SetAttachment(插槽名, 图片名);
增加手里的剑:无中生有
在资源库里找到新的资源,放到某个位置,这里指原来可能没有
首先要有一个SpineAtlasAsset
将下面这俩武器的资源文件放进某文件夹中自动生成SpineAtlasAsset
Equipment.png
Equipment.atlas.txt
播放动画,这俩种方式在使用上效果的区别
skeletonAnimation.AnimationName = “idle”;
skeletonAnimation..AnimationState.SetAnimation(0, "idle", false);
需要注意的是,例子里动画的loop是勾选的
LOOP勾选
第一种API:AnimationName = “idle”;
点击按钮就会不断播放的挥砍(攻击动画),再点击或者憋着也不会受影响
第二种API:AnimationState.SetAnimation(0, "idle", false);
点击可以正常播放
憋着按钮,动画会卡主住 ,松开后会继续播放完,再点击继续播放一次
LOOP取消
第一种API,播放一次后,不管怎么按都不播了
第二种API:AnimationState.SetAnimation(0, "idle", false); 和之前一样
点击可以正常播放
憋着按钮,动画会卡主住 ,松开后会继续播放完,再点击继续播放一次
所以,这个例子里的控制设计为,如果没有其它操作(挥砍或移动),就切回idle动画
初始化
初始化就调用,每隔一段时间执行一次效果
//委托:在骨架成功初始化后,开始有节奏的眨眼睛
skeletonAnimation.OnRebuild += Apply;
//骨架初始化成功后 调用的函数
void Apply (SkeletonRenderer skeletonRenderer) {
StartCoroutine(Blink());
}
//调用协程函数,协程函数就是眨眼睛
IEnumerator Blink() {
while (true) {
yield return new WaitForSeconds(Random.Range(0.25f, 3f));
skeletonAnimation.Skeleton.SetAttachment(eyesSlot, blinkAttachment);
yield return new WaitForSeconds(blinkDuration);
skeletonAnimation.Skeleton.SetAttachment(eyesSlot, eyesOpenAttachment);
}
}
skeletonAnimation.OnRebuild += Apply; //委托:在骨架成功初始化后,开始有节奏的眨眼睛