This commit is contained in:
Nim XD
2024-08-27 21:01:33 +05:30
parent 99eaf514fd
commit 121a1b7c73
31803 changed files with 623461 additions and 623399 deletions

View File

@@ -1,72 +1,72 @@
/******************************************************************************
* 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.
*****************************************************************************/
#define AUTOINIT_SPINEREFERENCE
using UnityEngine;
namespace Spine.Unity {
[CreateAssetMenu(menuName = "Spine/Animation Reference Asset", order = 100)]
public class AnimationReferenceAsset : ScriptableObject, IHasSkeletonDataAsset {
const bool QuietSkeletonData = true;
[SerializeField] protected SkeletonDataAsset skeletonDataAsset;
[SerializeField, SpineAnimation] protected string animationName;
private Animation animation;
public SkeletonDataAsset SkeletonDataAsset { get { return skeletonDataAsset; } }
public Animation Animation {
get {
#if AUTOINIT_SPINEREFERENCE
if (animation == null)
Initialize();
#endif
return animation;
}
}
/// <summary>Clears the cached animation corresponding to a loaded SkeletonData object.
/// Use this to force a reload for the next time Animation is called.</summary>
public void Clear () {
animation = null;
}
public void Initialize () {
if (skeletonDataAsset == null) return;
SkeletonData skeletonData = skeletonDataAsset.GetSkeletonData(AnimationReferenceAsset.QuietSkeletonData);
this.animation = skeletonData != null ? skeletonData.FindAnimation(animationName) : null;
if (this.animation == null) Debug.LogWarningFormat("Animation '{0}' not found in SkeletonData : {1}.", animationName, skeletonDataAsset.name);
}
public static implicit operator Animation (AnimationReferenceAsset asset) {
return asset.Animation;
}
}
}
/******************************************************************************
* 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.
*****************************************************************************/
#define AUTOINIT_SPINEREFERENCE
using UnityEngine;
namespace Spine.Unity {
[CreateAssetMenu(menuName = "Spine/Animation Reference Asset", order = 100)]
public class AnimationReferenceAsset : ScriptableObject, IHasSkeletonDataAsset {
const bool QuietSkeletonData = true;
[SerializeField] protected SkeletonDataAsset skeletonDataAsset;
[SerializeField, SpineAnimation] protected string animationName;
private Animation animation;
public SkeletonDataAsset SkeletonDataAsset { get { return skeletonDataAsset; } }
public Animation Animation {
get {
#if AUTOINIT_SPINEREFERENCE
if (animation == null)
Initialize();
#endif
return animation;
}
}
/// <summary>Clears the cached animation corresponding to a loaded SkeletonData object.
/// Use this to force a reload for the next time Animation is called.</summary>
public void Clear () {
animation = null;
}
public void Initialize () {
if (skeletonDataAsset == null) return;
SkeletonData skeletonData = skeletonDataAsset.GetSkeletonData(AnimationReferenceAsset.QuietSkeletonData);
this.animation = skeletonData != null ? skeletonData.FindAnimation(animationName) : null;
if (this.animation == null) Debug.LogWarningFormat("Animation '{0}' not found in SkeletonData : {1}.", animationName, skeletonDataAsset.name);
}
public static implicit operator Animation (AnimationReferenceAsset asset) {
return asset.Animation;
}
}
}

View File

@@ -1,44 +1,44 @@
/******************************************************************************
* 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.
*****************************************************************************/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Spine.Unity {
public abstract class AtlasAssetBase : ScriptableObject {
public abstract Material PrimaryMaterial { get; }
public abstract IEnumerable<Material> Materials { get; }
public abstract int MaterialCount { get; }
public abstract bool IsLoaded { get; }
public abstract void Clear ();
public abstract Atlas GetAtlas (bool onlyMetaData = false);
}
}
/******************************************************************************
* 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.
*****************************************************************************/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Spine.Unity {
public abstract class AtlasAssetBase : ScriptableObject {
public abstract Material PrimaryMaterial { get; }
public abstract IEnumerable<Material> Materials { get; }
public abstract int MaterialCount { get; }
public abstract bool IsLoaded { get; }
public abstract void Clear ();
public abstract Atlas GetAtlas (bool onlyMetaData = false);
}
}

View File

@@ -1,154 +1,154 @@
/******************************************************************************
* 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.
*****************************************************************************/
using Spine;
using System;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
namespace Spine.Unity {
[System.Serializable]
public class BlendModeMaterials {
[System.Serializable]
public class ReplacementMaterial {
public string pageName;
public Material material;
}
[SerializeField, HideInInspector] protected bool requiresBlendModeMaterials = false;
public bool applyAdditiveMaterial = false;
public List<ReplacementMaterial> additiveMaterials = new List<ReplacementMaterial>();
public List<ReplacementMaterial> multiplyMaterials = new List<ReplacementMaterial>();
public List<ReplacementMaterial> screenMaterials = new List<ReplacementMaterial>();
public bool RequiresBlendModeMaterials { get { return requiresBlendModeMaterials; } set { requiresBlendModeMaterials = value; } }
public BlendMode BlendModeForMaterial (Material material) {
foreach (var pair in multiplyMaterials)
if (pair.material == material) return BlendMode.Multiply;
foreach (var pair in additiveMaterials)
if (pair.material == material) return BlendMode.Additive;
foreach (var pair in screenMaterials)
if (pair.material == material) return BlendMode.Screen;
return BlendMode.Normal;
}
#if UNITY_EDITOR
public void TransferSettingsFrom (BlendModeMaterialsAsset modifierAsset) {
applyAdditiveMaterial = modifierAsset.applyAdditiveMaterial;
}
public bool UpdateBlendmodeMaterialsRequiredState (SkeletonData skeletonData) {
requiresBlendModeMaterials = false;
if (skeletonData == null) throw new ArgumentNullException("skeletonData");
var skinEntries = new List<Skin.SkinEntry>();
var slotsItems = skeletonData.Slots.Items;
for (int slotIndex = 0, slotCount = skeletonData.Slots.Count; slotIndex < slotCount; slotIndex++) {
var slot = slotsItems[slotIndex];
if (slot.BlendMode == BlendMode.Normal) continue;
if (!applyAdditiveMaterial && slot.BlendMode == BlendMode.Additive) continue;
skinEntries.Clear();
foreach (var skin in skeletonData.Skins)
skin.GetAttachments(slotIndex, skinEntries);
foreach (var entry in skinEntries) {
if (entry.Attachment is IHasRendererObject) {
requiresBlendModeMaterials = true;
return true;
}
}
}
return false;
}
#endif
public void ApplyMaterials (SkeletonData skeletonData) {
if (skeletonData == null) throw new ArgumentNullException("skeletonData");
if (!requiresBlendModeMaterials)
return;
var skinEntries = new List<Skin.SkinEntry>();
var slotsItems = skeletonData.Slots.Items;
for (int slotIndex = 0, slotCount = skeletonData.Slots.Count; slotIndex < slotCount; slotIndex++) {
var slot = slotsItems[slotIndex];
if (slot.BlendMode == BlendMode.Normal) continue;
if (!applyAdditiveMaterial && slot.BlendMode == BlendMode.Additive) continue;
List<ReplacementMaterial> replacementMaterials = null;
switch (slot.BlendMode) {
case BlendMode.Multiply:
replacementMaterials = multiplyMaterials;
break;
case BlendMode.Screen:
replacementMaterials = screenMaterials;
break;
case BlendMode.Additive:
replacementMaterials = additiveMaterials;
break;
}
if (replacementMaterials == null)
continue;
skinEntries.Clear();
foreach (var skin in skeletonData.Skins)
skin.GetAttachments(slotIndex, skinEntries);
foreach (var entry in skinEntries) {
var renderableAttachment = entry.Attachment as IHasRendererObject;
if (renderableAttachment != null) {
renderableAttachment.RendererObject = CloneAtlasRegionWithMaterial(
(AtlasRegion)renderableAttachment.RendererObject, replacementMaterials);
}
}
}
}
protected AtlasRegion CloneAtlasRegionWithMaterial (AtlasRegion originalRegion, List<ReplacementMaterial> replacementMaterials) {
var newRegion = originalRegion.Clone();
Material material = null;
foreach (var replacement in replacementMaterials) {
if (replacement.pageName == originalRegion.page.name) {
material = replacement.material;
break;
}
}
AtlasPage originalPage = originalRegion.page;
var newPage = originalPage.Clone();
newPage.rendererObject = material;
newRegion.page = newPage;
return newRegion;
}
}
}
/******************************************************************************
* 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.
*****************************************************************************/
using Spine;
using System;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
namespace Spine.Unity {
[System.Serializable]
public class BlendModeMaterials {
[System.Serializable]
public class ReplacementMaterial {
public string pageName;
public Material material;
}
[SerializeField, HideInInspector] protected bool requiresBlendModeMaterials = false;
public bool applyAdditiveMaterial = false;
public List<ReplacementMaterial> additiveMaterials = new List<ReplacementMaterial>();
public List<ReplacementMaterial> multiplyMaterials = new List<ReplacementMaterial>();
public List<ReplacementMaterial> screenMaterials = new List<ReplacementMaterial>();
public bool RequiresBlendModeMaterials { get { return requiresBlendModeMaterials; } set { requiresBlendModeMaterials = value; } }
public BlendMode BlendModeForMaterial (Material material) {
foreach (var pair in multiplyMaterials)
if (pair.material == material) return BlendMode.Multiply;
foreach (var pair in additiveMaterials)
if (pair.material == material) return BlendMode.Additive;
foreach (var pair in screenMaterials)
if (pair.material == material) return BlendMode.Screen;
return BlendMode.Normal;
}
#if UNITY_EDITOR
public void TransferSettingsFrom (BlendModeMaterialsAsset modifierAsset) {
applyAdditiveMaterial = modifierAsset.applyAdditiveMaterial;
}
public bool UpdateBlendmodeMaterialsRequiredState (SkeletonData skeletonData) {
requiresBlendModeMaterials = false;
if (skeletonData == null) throw new ArgumentNullException("skeletonData");
var skinEntries = new List<Skin.SkinEntry>();
var slotsItems = skeletonData.Slots.Items;
for (int slotIndex = 0, slotCount = skeletonData.Slots.Count; slotIndex < slotCount; slotIndex++) {
var slot = slotsItems[slotIndex];
if (slot.BlendMode == BlendMode.Normal) continue;
if (!applyAdditiveMaterial && slot.BlendMode == BlendMode.Additive) continue;
skinEntries.Clear();
foreach (var skin in skeletonData.Skins)
skin.GetAttachments(slotIndex, skinEntries);
foreach (var entry in skinEntries) {
if (entry.Attachment is IHasRendererObject) {
requiresBlendModeMaterials = true;
return true;
}
}
}
return false;
}
#endif
public void ApplyMaterials (SkeletonData skeletonData) {
if (skeletonData == null) throw new ArgumentNullException("skeletonData");
if (!requiresBlendModeMaterials)
return;
var skinEntries = new List<Skin.SkinEntry>();
var slotsItems = skeletonData.Slots.Items;
for (int slotIndex = 0, slotCount = skeletonData.Slots.Count; slotIndex < slotCount; slotIndex++) {
var slot = slotsItems[slotIndex];
if (slot.BlendMode == BlendMode.Normal) continue;
if (!applyAdditiveMaterial && slot.BlendMode == BlendMode.Additive) continue;
List<ReplacementMaterial> replacementMaterials = null;
switch (slot.BlendMode) {
case BlendMode.Multiply:
replacementMaterials = multiplyMaterials;
break;
case BlendMode.Screen:
replacementMaterials = screenMaterials;
break;
case BlendMode.Additive:
replacementMaterials = additiveMaterials;
break;
}
if (replacementMaterials == null)
continue;
skinEntries.Clear();
foreach (var skin in skeletonData.Skins)
skin.GetAttachments(slotIndex, skinEntries);
foreach (var entry in skinEntries) {
var renderableAttachment = entry.Attachment as IHasRendererObject;
if (renderableAttachment != null) {
renderableAttachment.RendererObject = CloneAtlasRegionWithMaterial(
(AtlasRegion)renderableAttachment.RendererObject, replacementMaterials);
}
}
}
}
protected AtlasRegion CloneAtlasRegionWithMaterial (AtlasRegion originalRegion, List<ReplacementMaterial> replacementMaterials) {
var newRegion = originalRegion.Clone();
Material material = null;
foreach (var replacement in replacementMaterials) {
if (replacement.pageName == originalRegion.page.name) {
material = replacement.material;
break;
}
}
AtlasPage originalPage = originalRegion.page;
var newPage = originalPage.Clone();
newPage.rendererObject = material;
newRegion.page = newPage;
return newRegion;
}
}
}

View File

@@ -1,65 +1,65 @@
/******************************************************************************
* 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.
*****************************************************************************/
#define AUTOINIT_SPINEREFERENCE
using UnityEngine;
namespace Spine.Unity {
[CreateAssetMenu(menuName = "Spine/EventData Reference Asset", order = 100)]
public class EventDataReferenceAsset : ScriptableObject {
const bool QuietSkeletonData = true;
[SerializeField] protected SkeletonDataAsset skeletonDataAsset;
[SerializeField, SpineEvent(dataField: "skeletonDataAsset")] protected string eventName;
EventData eventData;
public EventData EventData {
get {
#if AUTOINIT_SPINEREFERENCE
if (eventData == null)
Initialize();
#endif
return eventData;
}
}
public void Initialize () {
if (skeletonDataAsset == null)
return;
this.eventData = skeletonDataAsset.GetSkeletonData(EventDataReferenceAsset.QuietSkeletonData).FindEvent(eventName);
if (this.eventData == null)
Debug.LogWarningFormat("Event Data '{0}' not found in SkeletonData : {1}.", eventName, skeletonDataAsset.name);
}
public static implicit operator EventData (EventDataReferenceAsset asset) {
return asset.EventData;
}
}
}
/******************************************************************************
* 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.
*****************************************************************************/
#define AUTOINIT_SPINEREFERENCE
using UnityEngine;
namespace Spine.Unity {
[CreateAssetMenu(menuName = "Spine/EventData Reference Asset", order = 100)]
public class EventDataReferenceAsset : ScriptableObject {
const bool QuietSkeletonData = true;
[SerializeField] protected SkeletonDataAsset skeletonDataAsset;
[SerializeField, SpineEvent(dataField: "skeletonDataAsset")] protected string eventName;
EventData eventData;
public EventData EventData {
get {
#if AUTOINIT_SPINEREFERENCE
if (eventData == null)
Initialize();
#endif
return eventData;
}
}
public void Initialize () {
if (skeletonDataAsset == null)
return;
this.eventData = skeletonDataAsset.GetSkeletonData(EventDataReferenceAsset.QuietSkeletonData).FindEvent(eventName);
if (this.eventData == null)
Debug.LogWarningFormat("Event Data '{0}' not found in SkeletonData : {1}.", eventName, skeletonDataAsset.name);
}
public static implicit operator EventData (EventDataReferenceAsset asset) {
return asset.EventData;
}
}
}

View File

@@ -1,83 +1,83 @@
/******************************************************************************
* 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.
*****************************************************************************/
using System;
using UnityEngine;
namespace Spine.Unity {
public class RegionlessAttachmentLoader : AttachmentLoader {
static AtlasRegion emptyRegion;
static AtlasRegion EmptyRegion {
get {
if (emptyRegion == null) {
emptyRegion = new AtlasRegion {
name = "Empty AtlasRegion",
page = new AtlasPage {
name = "Empty AtlasPage",
rendererObject = new Material(Shader.Find("Spine/Special/HiddenPass")) { name = "NoRender Material" }
}
};
}
return emptyRegion;
}
}
public RegionAttachment NewRegionAttachment (Skin skin, string name, string path) {
RegionAttachment attachment = new RegionAttachment(name) {
RendererObject = EmptyRegion
};
return attachment;
}
public MeshAttachment NewMeshAttachment (Skin skin, string name, string path) {
MeshAttachment attachment = new MeshAttachment(name) {
RendererObject = EmptyRegion
};
return attachment;
}
public BoundingBoxAttachment NewBoundingBoxAttachment (Skin skin, string name) {
return new BoundingBoxAttachment(name);
}
public PathAttachment NewPathAttachment (Skin skin, string name) {
return new PathAttachment(name);
}
public PointAttachment NewPointAttachment (Skin skin, string name) {
return new PointAttachment(name);
}
public ClippingAttachment NewClippingAttachment (Skin skin, string name) {
return new ClippingAttachment(name);
}
}
}
/******************************************************************************
* 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.
*****************************************************************************/
using System;
using UnityEngine;
namespace Spine.Unity {
public class RegionlessAttachmentLoader : AttachmentLoader {
static AtlasRegion emptyRegion;
static AtlasRegion EmptyRegion {
get {
if (emptyRegion == null) {
emptyRegion = new AtlasRegion {
name = "Empty AtlasRegion",
page = new AtlasPage {
name = "Empty AtlasPage",
rendererObject = new Material(Shader.Find("Spine/Special/HiddenPass")) { name = "NoRender Material" }
}
};
}
return emptyRegion;
}
}
public RegionAttachment NewRegionAttachment (Skin skin, string name, string path) {
RegionAttachment attachment = new RegionAttachment(name) {
RendererObject = EmptyRegion
};
return attachment;
}
public MeshAttachment NewMeshAttachment (Skin skin, string name, string path) {
MeshAttachment attachment = new MeshAttachment(name) {
RendererObject = EmptyRegion
};
return attachment;
}
public BoundingBoxAttachment NewBoundingBoxAttachment (Skin skin, string name) {
return new BoundingBoxAttachment(name);
}
public PathAttachment NewPathAttachment (Skin skin, string name) {
return new PathAttachment(name);
}
public PointAttachment NewPointAttachment (Skin skin, string name) {
return new PointAttachment(name);
}
public ClippingAttachment NewClippingAttachment (Skin skin, string name) {
return new ClippingAttachment(name);
}
}
}

View File

@@ -1,262 +1,262 @@
/******************************************************************************
* 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.
*****************************************************************************/
using System;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using CompatibilityProblemInfo = Spine.Unity.SkeletonDataCompatibility.CompatibilityProblemInfo;
namespace Spine.Unity {
[CreateAssetMenu(fileName = "New SkeletonDataAsset", menuName = "Spine/SkeletonData Asset")]
public class SkeletonDataAsset : ScriptableObject {
#region Inspector
public AtlasAssetBase[] atlasAssets = new AtlasAssetBase[0];
#if SPINE_TK2D
public tk2dSpriteCollectionData spriteCollection;
public float scale = 1f;
#else
public float scale = 0.01f;
#endif
public TextAsset skeletonJSON;
public bool isUpgradingBlendModeMaterials = false;
public BlendModeMaterials blendModeMaterials = new BlendModeMaterials();
[Tooltip("Use SkeletonDataModifierAssets to apply changes to the SkeletonData after being loaded, such as apply blend mode Materials to Attachments under slots with special blend modes.")]
public List<SkeletonDataModifierAsset> skeletonDataModifiers = new List<SkeletonDataModifierAsset>();
[SpineAnimation(includeNone: false)]
public string[] fromAnimation = new string[0];
[SpineAnimation(includeNone: false)]
public string[] toAnimation = new string[0];
public float[] duration = new float[0];
public float defaultMix;
public RuntimeAnimatorController controller;
public bool IsLoaded { get { return this.skeletonData != null; } }
void Reset () {
Clear();
}
#endregion
SkeletonData skeletonData;
AnimationStateData stateData;
#region Runtime Instantiation
/// <summary>
/// Creates a runtime SkeletonDataAsset.</summary>
public static SkeletonDataAsset CreateRuntimeInstance (TextAsset skeletonDataFile, AtlasAssetBase atlasAsset, bool initialize, float scale = 0.01f) {
return CreateRuntimeInstance(skeletonDataFile, new[] { atlasAsset }, initialize, scale);
}
/// <summary>
/// Creates a runtime SkeletonDataAsset.</summary>
public static SkeletonDataAsset CreateRuntimeInstance (TextAsset skeletonDataFile, AtlasAssetBase[] atlasAssets, bool initialize, float scale = 0.01f) {
SkeletonDataAsset skeletonDataAsset = ScriptableObject.CreateInstance<SkeletonDataAsset>();
skeletonDataAsset.Clear();
skeletonDataAsset.skeletonJSON = skeletonDataFile;
skeletonDataAsset.atlasAssets = atlasAssets;
skeletonDataAsset.scale = scale;
if (initialize)
skeletonDataAsset.GetSkeletonData(true);
return skeletonDataAsset;
}
#endregion
/// <summary>Clears the loaded SkeletonData and AnimationStateData. Use this to force a reload for the next time GetSkeletonData is called.</summary>
public void Clear () {
skeletonData = null;
stateData = null;
}
public AnimationStateData GetAnimationStateData () {
if (stateData != null)
return stateData;
GetSkeletonData(false);
return stateData;
}
/// <summary>Loads, caches and returns the SkeletonData from the skeleton data file. Returns the cached SkeletonData after the first time it is called. Pass false to prevent direct errors from being logged.</summary>
public SkeletonData GetSkeletonData (bool quiet) {
if (skeletonJSON == null) {
if (!quiet)
Debug.LogError("Skeleton JSON file not set for SkeletonData asset: " + name, this);
Clear();
return null;
}
// Disabled to support attachmentless/skinless SkeletonData.
// if (atlasAssets == null) {
// atlasAssets = new AtlasAsset[0];
// if (!quiet)
// Debug.LogError("Atlas not set for SkeletonData asset: " + name, this);
// Clear();
// return null;
// }
// #if !SPINE_TK2D
// if (atlasAssets.Length == 0) {
// Clear();
// return null;
// }
// #else
// if (atlasAssets.Length == 0 && spriteCollection == null) {
// Clear();
// return null;
// }
// #endif
if (skeletonData != null)
return skeletonData;
AttachmentLoader attachmentLoader;
float skeletonDataScale;
Atlas[] atlasArray = this.GetAtlasArray();
#if !SPINE_TK2D
attachmentLoader = (atlasArray.Length == 0) ? (AttachmentLoader)new RegionlessAttachmentLoader() : (AttachmentLoader)new AtlasAttachmentLoader(atlasArray);
skeletonDataScale = scale;
#else
if (spriteCollection != null) {
attachmentLoader = new Spine.Unity.TK2D.SpriteCollectionAttachmentLoader(spriteCollection);
skeletonDataScale = (1.0f / (spriteCollection.invOrthoSize * spriteCollection.halfTargetHeight) * scale);
} else {
if (atlasArray.Length == 0) {
Reset();
if (!quiet) Debug.LogError("Atlas not set for SkeletonData asset: " + name, this);
return null;
}
attachmentLoader = new AtlasAttachmentLoader(atlasArray);
skeletonDataScale = scale;
}
#endif
bool hasBinaryExtension = skeletonJSON.name.ToLower().Contains(".skel");
SkeletonData loadedSkeletonData = null;
try {
if (hasBinaryExtension)
loadedSkeletonData = SkeletonDataAsset.ReadSkeletonData(skeletonJSON.bytes, attachmentLoader, skeletonDataScale);
else
loadedSkeletonData = SkeletonDataAsset.ReadSkeletonData(skeletonJSON.text, attachmentLoader, skeletonDataScale);
} catch (Exception ex) {
if (!quiet)
Debug.LogError("Error reading skeleton JSON file for SkeletonData asset: " + name + "\n" + ex.Message + "\n" + ex.StackTrace, skeletonJSON);
}
#if UNITY_EDITOR
if (loadedSkeletonData == null && !quiet && skeletonJSON != null) {
string problemDescription = null;
bool isSpineSkeletonData;
SkeletonDataCompatibility.VersionInfo fileVersion = SkeletonDataCompatibility.GetVersionInfo(skeletonJSON, out isSpineSkeletonData, ref problemDescription);
if (problemDescription != null) {
if (!quiet)
Debug.LogError(problemDescription, skeletonJSON);
return null;
}
CompatibilityProblemInfo compatibilityProblemInfo = SkeletonDataCompatibility.GetCompatibilityProblemInfo(fileVersion);
if (compatibilityProblemInfo != null) {
SkeletonDataCompatibility.DisplayCompatibilityProblem(compatibilityProblemInfo.DescriptionString(), skeletonJSON);
return null;
}
}
#endif
if (loadedSkeletonData == null)
return null;
if (skeletonDataModifiers != null) {
foreach (var modifier in skeletonDataModifiers) {
if (modifier != null && !(isUpgradingBlendModeMaterials && modifier is BlendModeMaterialsAsset)) {
modifier.Apply(loadedSkeletonData);
}
}
}
if (!isUpgradingBlendModeMaterials)
blendModeMaterials.ApplyMaterials(loadedSkeletonData);
this.InitializeWithData(loadedSkeletonData);
return skeletonData;
}
internal void InitializeWithData (SkeletonData sd) {
this.skeletonData = sd;
this.stateData = new AnimationStateData(skeletonData);
FillStateData();
}
public void FillStateData () {
if (stateData != null) {
stateData.DefaultMix = defaultMix;
for (int i = 0, n = fromAnimation.Length; i < n; i++) {
if (fromAnimation[i].Length == 0 || toAnimation[i].Length == 0)
continue;
stateData.SetMix(fromAnimation[i], toAnimation[i], duration[i]);
}
}
}
internal Atlas[] GetAtlasArray () {
var returnList = new System.Collections.Generic.List<Atlas>(atlasAssets.Length);
for (int i = 0; i < atlasAssets.Length; i++) {
var aa = atlasAssets[i];
if (aa == null) continue;
var a = aa.GetAtlas();
if (a == null) continue;
returnList.Add(a);
}
return returnList.ToArray();
}
internal static SkeletonData ReadSkeletonData (byte[] bytes, AttachmentLoader attachmentLoader, float scale) {
using (var input = new MemoryStream(bytes)) {
var binary = new SkeletonBinary(attachmentLoader) {
Scale = scale
};
return binary.ReadSkeletonData(input);
}
}
internal static SkeletonData ReadSkeletonData (string text, AttachmentLoader attachmentLoader, float scale) {
var input = new StringReader(text);
var json = new SkeletonJson(attachmentLoader) {
Scale = scale
};
return json.ReadSkeletonData(input);
}
}
}
/******************************************************************************
* 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.
*****************************************************************************/
using System;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using CompatibilityProblemInfo = Spine.Unity.SkeletonDataCompatibility.CompatibilityProblemInfo;
namespace Spine.Unity {
[CreateAssetMenu(fileName = "New SkeletonDataAsset", menuName = "Spine/SkeletonData Asset")]
public class SkeletonDataAsset : ScriptableObject {
#region Inspector
public AtlasAssetBase[] atlasAssets = new AtlasAssetBase[0];
#if SPINE_TK2D
public tk2dSpriteCollectionData spriteCollection;
public float scale = 1f;
#else
public float scale = 0.01f;
#endif
public TextAsset skeletonJSON;
public bool isUpgradingBlendModeMaterials = false;
public BlendModeMaterials blendModeMaterials = new BlendModeMaterials();
[Tooltip("Use SkeletonDataModifierAssets to apply changes to the SkeletonData after being loaded, such as apply blend mode Materials to Attachments under slots with special blend modes.")]
public List<SkeletonDataModifierAsset> skeletonDataModifiers = new List<SkeletonDataModifierAsset>();
[SpineAnimation(includeNone: false)]
public string[] fromAnimation = new string[0];
[SpineAnimation(includeNone: false)]
public string[] toAnimation = new string[0];
public float[] duration = new float[0];
public float defaultMix;
public RuntimeAnimatorController controller;
public bool IsLoaded { get { return this.skeletonData != null; } }
void Reset () {
Clear();
}
#endregion
SkeletonData skeletonData;
AnimationStateData stateData;
#region Runtime Instantiation
/// <summary>
/// Creates a runtime SkeletonDataAsset.</summary>
public static SkeletonDataAsset CreateRuntimeInstance (TextAsset skeletonDataFile, AtlasAssetBase atlasAsset, bool initialize, float scale = 0.01f) {
return CreateRuntimeInstance(skeletonDataFile, new[] { atlasAsset }, initialize, scale);
}
/// <summary>
/// Creates a runtime SkeletonDataAsset.</summary>
public static SkeletonDataAsset CreateRuntimeInstance (TextAsset skeletonDataFile, AtlasAssetBase[] atlasAssets, bool initialize, float scale = 0.01f) {
SkeletonDataAsset skeletonDataAsset = ScriptableObject.CreateInstance<SkeletonDataAsset>();
skeletonDataAsset.Clear();
skeletonDataAsset.skeletonJSON = skeletonDataFile;
skeletonDataAsset.atlasAssets = atlasAssets;
skeletonDataAsset.scale = scale;
if (initialize)
skeletonDataAsset.GetSkeletonData(true);
return skeletonDataAsset;
}
#endregion
/// <summary>Clears the loaded SkeletonData and AnimationStateData. Use this to force a reload for the next time GetSkeletonData is called.</summary>
public void Clear () {
skeletonData = null;
stateData = null;
}
public AnimationStateData GetAnimationStateData () {
if (stateData != null)
return stateData;
GetSkeletonData(false);
return stateData;
}
/// <summary>Loads, caches and returns the SkeletonData from the skeleton data file. Returns the cached SkeletonData after the first time it is called. Pass false to prevent direct errors from being logged.</summary>
public SkeletonData GetSkeletonData (bool quiet) {
if (skeletonJSON == null) {
if (!quiet)
Debug.LogError("Skeleton JSON file not set for SkeletonData asset: " + name, this);
Clear();
return null;
}
// Disabled to support attachmentless/skinless SkeletonData.
// if (atlasAssets == null) {
// atlasAssets = new AtlasAsset[0];
// if (!quiet)
// Debug.LogError("Atlas not set for SkeletonData asset: " + name, this);
// Clear();
// return null;
// }
// #if !SPINE_TK2D
// if (atlasAssets.Length == 0) {
// Clear();
// return null;
// }
// #else
// if (atlasAssets.Length == 0 && spriteCollection == null) {
// Clear();
// return null;
// }
// #endif
if (skeletonData != null)
return skeletonData;
AttachmentLoader attachmentLoader;
float skeletonDataScale;
Atlas[] atlasArray = this.GetAtlasArray();
#if !SPINE_TK2D
attachmentLoader = (atlasArray.Length == 0) ? (AttachmentLoader)new RegionlessAttachmentLoader() : (AttachmentLoader)new AtlasAttachmentLoader(atlasArray);
skeletonDataScale = scale;
#else
if (spriteCollection != null) {
attachmentLoader = new Spine.Unity.TK2D.SpriteCollectionAttachmentLoader(spriteCollection);
skeletonDataScale = (1.0f / (spriteCollection.invOrthoSize * spriteCollection.halfTargetHeight) * scale);
} else {
if (atlasArray.Length == 0) {
Reset();
if (!quiet) Debug.LogError("Atlas not set for SkeletonData asset: " + name, this);
return null;
}
attachmentLoader = new AtlasAttachmentLoader(atlasArray);
skeletonDataScale = scale;
}
#endif
bool hasBinaryExtension = skeletonJSON.name.ToLower().Contains(".skel");
SkeletonData loadedSkeletonData = null;
try {
if (hasBinaryExtension)
loadedSkeletonData = SkeletonDataAsset.ReadSkeletonData(skeletonJSON.bytes, attachmentLoader, skeletonDataScale);
else
loadedSkeletonData = SkeletonDataAsset.ReadSkeletonData(skeletonJSON.text, attachmentLoader, skeletonDataScale);
} catch (Exception ex) {
if (!quiet)
Debug.LogError("Error reading skeleton JSON file for SkeletonData asset: " + name + "\n" + ex.Message + "\n" + ex.StackTrace, skeletonJSON);
}
#if UNITY_EDITOR
if (loadedSkeletonData == null && !quiet && skeletonJSON != null) {
string problemDescription = null;
bool isSpineSkeletonData;
SkeletonDataCompatibility.VersionInfo fileVersion = SkeletonDataCompatibility.GetVersionInfo(skeletonJSON, out isSpineSkeletonData, ref problemDescription);
if (problemDescription != null) {
if (!quiet)
Debug.LogError(problemDescription, skeletonJSON);
return null;
}
CompatibilityProblemInfo compatibilityProblemInfo = SkeletonDataCompatibility.GetCompatibilityProblemInfo(fileVersion);
if (compatibilityProblemInfo != null) {
SkeletonDataCompatibility.DisplayCompatibilityProblem(compatibilityProblemInfo.DescriptionString(), skeletonJSON);
return null;
}
}
#endif
if (loadedSkeletonData == null)
return null;
if (skeletonDataModifiers != null) {
foreach (var modifier in skeletonDataModifiers) {
if (modifier != null && !(isUpgradingBlendModeMaterials && modifier is BlendModeMaterialsAsset)) {
modifier.Apply(loadedSkeletonData);
}
}
}
if (!isUpgradingBlendModeMaterials)
blendModeMaterials.ApplyMaterials(loadedSkeletonData);
this.InitializeWithData(loadedSkeletonData);
return skeletonData;
}
internal void InitializeWithData (SkeletonData sd) {
this.skeletonData = sd;
this.stateData = new AnimationStateData(skeletonData);
FillStateData();
}
public void FillStateData () {
if (stateData != null) {
stateData.DefaultMix = defaultMix;
for (int i = 0, n = fromAnimation.Length; i < n; i++) {
if (fromAnimation[i].Length == 0 || toAnimation[i].Length == 0)
continue;
stateData.SetMix(fromAnimation[i], toAnimation[i], duration[i]);
}
}
}
internal Atlas[] GetAtlasArray () {
var returnList = new System.Collections.Generic.List<Atlas>(atlasAssets.Length);
for (int i = 0; i < atlasAssets.Length; i++) {
var aa = atlasAssets[i];
if (aa == null) continue;
var a = aa.GetAtlas();
if (a == null) continue;
returnList.Add(a);
}
return returnList.ToArray();
}
internal static SkeletonData ReadSkeletonData (byte[] bytes, AttachmentLoader attachmentLoader, float scale) {
using (var input = new MemoryStream(bytes)) {
var binary = new SkeletonBinary(attachmentLoader) {
Scale = scale
};
return binary.ReadSkeletonData(input);
}
}
internal static SkeletonData ReadSkeletonData (string text, AttachmentLoader attachmentLoader, float scale) {
var input = new StringReader(text);
var json = new SkeletonJson(attachmentLoader) {
Scale = scale
};
return json.ReadSkeletonData(input);
}
}
}

View File

@@ -1,217 +1,217 @@
/******************************************************************************
* 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.
*****************************************************************************/
using System;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
#if UNITY_EDITOR
using System.Globalization;
using System.Text.RegularExpressions;
#endif
namespace Spine.Unity {
public static class SkeletonDataCompatibility {
#if UNITY_EDITOR
static readonly int[][] compatibleBinaryVersions = { new[] { 4, 0, 0 } };
static readonly int[][] compatibleJsonVersions = { new[] { 4, 0, 0 } };
static bool wasVersionDialogShown = false;
static readonly Regex jsonVersionRegex = new Regex(@"""spine""\s*:\s*""([^""]+)""", RegexOptions.CultureInvariant);
#endif
public enum SourceType {
Json,
Binary
}
[System.Serializable]
public class VersionInfo {
public string rawVersion = null;
public int[] version = null;
public SourceType sourceType;
}
[System.Serializable]
public class CompatibilityProblemInfo {
public VersionInfo actualVersion;
public int[][] compatibleVersions;
public string explicitProblemDescription = null;
public string DescriptionString () {
if (!string.IsNullOrEmpty(explicitProblemDescription))
return explicitProblemDescription;
string compatibleVersionString = "";
string optionalOr = null;
foreach (int[] version in compatibleVersions) {
compatibleVersionString += string.Format("{0}{1}.{2}", optionalOr, version[0], version[1]);
optionalOr = " or ";
}
return string.Format("Skeleton data could not be loaded. Data version: {0}. Required version: {1}.\nPlease re-export skeleton data with Spine {1} or change runtime to version {2}.{3}.",
actualVersion.rawVersion, compatibleVersionString, actualVersion.version[0], actualVersion.version[1]);
}
}
#if UNITY_EDITOR
public static VersionInfo GetVersionInfo (TextAsset asset, out bool isSpineSkeletonData, ref string problemDescription) {
isSpineSkeletonData = false;
if (asset == null)
return null;
VersionInfo fileVersion = new VersionInfo();
bool hasBinaryExtension = asset.name.Contains(".skel");
fileVersion.sourceType = hasBinaryExtension ? SourceType.Binary : SourceType.Json;
bool isJsonFileByContent = IsJsonFile(asset);
if (hasBinaryExtension == isJsonFileByContent) {
if (hasBinaryExtension) {
problemDescription = string.Format("Failed to read '{0}'. Extension is '.skel.bytes' but content looks like a '.json' file.\n"
+ "Did you choose the wrong extension upon export?\n", asset.name);
} else {
problemDescription = string.Format("Failed to read '{0}'. Extension is '.json' but content looks like binary 'skel.bytes' file.\n"
+ "Did you choose the wrong extension upon export?\n", asset.name);
}
isSpineSkeletonData = false;
return null;
}
if (fileVersion.sourceType == SourceType.Binary) {
try {
using (var memStream = new MemoryStream(asset.bytes)) {
fileVersion.rawVersion = SkeletonBinary.GetVersionString(memStream);
}
} catch (System.Exception e) {
problemDescription = string.Format("Failed to read '{0}'. It is likely not a binary Spine SkeletonData file.\n{1}", asset.name, e);
isSpineSkeletonData = false;
return null;
}
} else {
Match match = jsonVersionRegex.Match(asset.text);
if (match != null) {
fileVersion.rawVersion = match.Groups[1].Value;
} else {
object obj = Json.Deserialize(new StringReader(asset.text));
if (obj == null) {
problemDescription = string.Format("'{0}' is not valid JSON.", asset.name);
isSpineSkeletonData = false;
return null;
}
var root = obj as Dictionary<string, object>;
if (root == null) {
problemDescription = string.Format("'{0}' is not compatible JSON. Parser returned an incorrect type while parsing version info.", asset.name);
isSpineSkeletonData = false;
return null;
}
if (root.ContainsKey("skeleton")) {
var skeletonInfo = (Dictionary<string, object>)root["skeleton"];
object jv;
skeletonInfo.TryGetValue("spine", out jv);
fileVersion.rawVersion = jv as string;
}
}
}
if (string.IsNullOrEmpty(fileVersion.rawVersion)) {
// very likely not a Spine skeleton json file at all. Could be another valid json file, don't report errors.
isSpineSkeletonData = false;
return null;
}
var versionSplit = fileVersion.rawVersion.Split('.');
try {
fileVersion.version = new[]{ int.Parse(versionSplit[0], CultureInfo.InvariantCulture),
int.Parse(versionSplit[1], CultureInfo.InvariantCulture) };
} catch (System.Exception e) {
problemDescription = string.Format("Failed to read version info at skeleton '{0}'. It is likely not a valid Spine SkeletonData file.\n{1}", asset.name, e);
isSpineSkeletonData = false;
return null;
}
isSpineSkeletonData = true;
return fileVersion;
}
public static bool IsJsonFile (TextAsset file) {
byte[] content = file.bytes;
const int maxCharsToCheck = 256;
int numCharsToCheck = Math.Min(content.Length, maxCharsToCheck);
int i = 0;
if (content.Length >= 3 && content[0] == 0xEF && content[1] == 0xBB && content[2] == 0xBF) // skip potential BOM
i = 3;
bool openingBraceFound = false;
for (; i < numCharsToCheck; ++i) {
char c = (char)content[i];
if (char.IsWhiteSpace(c))
continue;
if (!openingBraceFound) {
if (c == '{' || c == '[') openingBraceFound = true;
else return false;
} else if (c == '{' || c == '[' || c == ']' || c == '}' || c == ',')
continue;
else
return c == '"';
}
return true;
}
public static CompatibilityProblemInfo GetCompatibilityProblemInfo (VersionInfo fileVersion) {
if (fileVersion == null) {
return null; // it's most likely not a Spine skeleton file, e.g. another json file. don't report problems.
}
CompatibilityProblemInfo info = new CompatibilityProblemInfo();
info.actualVersion = fileVersion;
info.compatibleVersions = (fileVersion.sourceType == SourceType.Binary) ? compatibleBinaryVersions
: compatibleJsonVersions;
foreach (var compatibleVersion in info.compatibleVersions) {
bool majorMatch = fileVersion.version[0] == compatibleVersion[0];
bool minorMatch = fileVersion.version[1] == compatibleVersion[1];
if (majorMatch && minorMatch) {
return null; // is compatible, thus no problem info returned
}
}
return info;
}
public static void DisplayCompatibilityProblem (string descriptionString, TextAsset spineJson) {
if (!wasVersionDialogShown) {
wasVersionDialogShown = true;
UnityEditor.EditorUtility.DisplayDialog("Version mismatch!", descriptionString, "OK");
}
Debug.LogError(string.Format("Error importing skeleton '{0}': {1}",
spineJson.name, descriptionString), spineJson);
}
#endif // UNITY_EDITOR
}
}
/******************************************************************************
* 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.
*****************************************************************************/
using System;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
#if UNITY_EDITOR
using System.Globalization;
using System.Text.RegularExpressions;
#endif
namespace Spine.Unity {
public static class SkeletonDataCompatibility {
#if UNITY_EDITOR
static readonly int[][] compatibleBinaryVersions = { new[] { 4, 0, 0 } };
static readonly int[][] compatibleJsonVersions = { new[] { 4, 0, 0 } };
static bool wasVersionDialogShown = false;
static readonly Regex jsonVersionRegex = new Regex(@"""spine""\s*:\s*""([^""]+)""", RegexOptions.CultureInvariant);
#endif
public enum SourceType {
Json,
Binary
}
[System.Serializable]
public class VersionInfo {
public string rawVersion = null;
public int[] version = null;
public SourceType sourceType;
}
[System.Serializable]
public class CompatibilityProblemInfo {
public VersionInfo actualVersion;
public int[][] compatibleVersions;
public string explicitProblemDescription = null;
public string DescriptionString () {
if (!string.IsNullOrEmpty(explicitProblemDescription))
return explicitProblemDescription;
string compatibleVersionString = "";
string optionalOr = null;
foreach (int[] version in compatibleVersions) {
compatibleVersionString += string.Format("{0}{1}.{2}", optionalOr, version[0], version[1]);
optionalOr = " or ";
}
return string.Format("Skeleton data could not be loaded. Data version: {0}. Required version: {1}.\nPlease re-export skeleton data with Spine {1} or change runtime to version {2}.{3}.",
actualVersion.rawVersion, compatibleVersionString, actualVersion.version[0], actualVersion.version[1]);
}
}
#if UNITY_EDITOR
public static VersionInfo GetVersionInfo (TextAsset asset, out bool isSpineSkeletonData, ref string problemDescription) {
isSpineSkeletonData = false;
if (asset == null)
return null;
VersionInfo fileVersion = new VersionInfo();
bool hasBinaryExtension = asset.name.Contains(".skel");
fileVersion.sourceType = hasBinaryExtension ? SourceType.Binary : SourceType.Json;
bool isJsonFileByContent = IsJsonFile(asset);
if (hasBinaryExtension == isJsonFileByContent) {
if (hasBinaryExtension) {
problemDescription = string.Format("Failed to read '{0}'. Extension is '.skel.bytes' but content looks like a '.json' file.\n"
+ "Did you choose the wrong extension upon export?\n", asset.name);
} else {
problemDescription = string.Format("Failed to read '{0}'. Extension is '.json' but content looks like binary 'skel.bytes' file.\n"
+ "Did you choose the wrong extension upon export?\n", asset.name);
}
isSpineSkeletonData = false;
return null;
}
if (fileVersion.sourceType == SourceType.Binary) {
try {
using (var memStream = new MemoryStream(asset.bytes)) {
fileVersion.rawVersion = SkeletonBinary.GetVersionString(memStream);
}
} catch (System.Exception e) {
problemDescription = string.Format("Failed to read '{0}'. It is likely not a binary Spine SkeletonData file.\n{1}", asset.name, e);
isSpineSkeletonData = false;
return null;
}
} else {
Match match = jsonVersionRegex.Match(asset.text);
if (match != null) {
fileVersion.rawVersion = match.Groups[1].Value;
} else {
object obj = Json.Deserialize(new StringReader(asset.text));
if (obj == null) {
problemDescription = string.Format("'{0}' is not valid JSON.", asset.name);
isSpineSkeletonData = false;
return null;
}
var root = obj as Dictionary<string, object>;
if (root == null) {
problemDescription = string.Format("'{0}' is not compatible JSON. Parser returned an incorrect type while parsing version info.", asset.name);
isSpineSkeletonData = false;
return null;
}
if (root.ContainsKey("skeleton")) {
var skeletonInfo = (Dictionary<string, object>)root["skeleton"];
object jv;
skeletonInfo.TryGetValue("spine", out jv);
fileVersion.rawVersion = jv as string;
}
}
}
if (string.IsNullOrEmpty(fileVersion.rawVersion)) {
// very likely not a Spine skeleton json file at all. Could be another valid json file, don't report errors.
isSpineSkeletonData = false;
return null;
}
var versionSplit = fileVersion.rawVersion.Split('.');
try {
fileVersion.version = new[]{ int.Parse(versionSplit[0], CultureInfo.InvariantCulture),
int.Parse(versionSplit[1], CultureInfo.InvariantCulture) };
} catch (System.Exception e) {
problemDescription = string.Format("Failed to read version info at skeleton '{0}'. It is likely not a valid Spine SkeletonData file.\n{1}", asset.name, e);
isSpineSkeletonData = false;
return null;
}
isSpineSkeletonData = true;
return fileVersion;
}
public static bool IsJsonFile (TextAsset file) {
byte[] content = file.bytes;
const int maxCharsToCheck = 256;
int numCharsToCheck = Math.Min(content.Length, maxCharsToCheck);
int i = 0;
if (content.Length >= 3 && content[0] == 0xEF && content[1] == 0xBB && content[2] == 0xBF) // skip potential BOM
i = 3;
bool openingBraceFound = false;
for (; i < numCharsToCheck; ++i) {
char c = (char)content[i];
if (char.IsWhiteSpace(c))
continue;
if (!openingBraceFound) {
if (c == '{' || c == '[') openingBraceFound = true;
else return false;
} else if (c == '{' || c == '[' || c == ']' || c == '}' || c == ',')
continue;
else
return c == '"';
}
return true;
}
public static CompatibilityProblemInfo GetCompatibilityProblemInfo (VersionInfo fileVersion) {
if (fileVersion == null) {
return null; // it's most likely not a Spine skeleton file, e.g. another json file. don't report problems.
}
CompatibilityProblemInfo info = new CompatibilityProblemInfo();
info.actualVersion = fileVersion;
info.compatibleVersions = (fileVersion.sourceType == SourceType.Binary) ? compatibleBinaryVersions
: compatibleJsonVersions;
foreach (var compatibleVersion in info.compatibleVersions) {
bool majorMatch = fileVersion.version[0] == compatibleVersion[0];
bool minorMatch = fileVersion.version[1] == compatibleVersion[1];
if (majorMatch && minorMatch) {
return null; // is compatible, thus no problem info returned
}
}
return info;
}
public static void DisplayCompatibilityProblem (string descriptionString, TextAsset spineJson) {
if (!wasVersionDialogShown) {
wasVersionDialogShown = true;
UnityEditor.EditorUtility.DisplayDialog("Version mismatch!", descriptionString, "OK");
}
Debug.LogError(string.Format("Error importing skeleton '{0}': {1}",
spineJson.name, descriptionString), spineJson);
}
#endif // UNITY_EDITOR
}
}

View File

@@ -1,39 +1,39 @@
/******************************************************************************
* 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.
*****************************************************************************/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Spine.Unity {
/// <summary>Can be stored by SkeletonDataAsset to automatically apply modifications to loaded SkeletonData.</summary>
public abstract class SkeletonDataModifierAsset : ScriptableObject {
public abstract void Apply (SkeletonData skeletonData);
}
}
/******************************************************************************
* 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.
*****************************************************************************/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Spine.Unity {
/// <summary>Can be stored by SkeletonDataAsset to automatically apply modifications to loaded SkeletonData.</summary>
public abstract class SkeletonDataModifierAsset : ScriptableObject {
public abstract void Apply (SkeletonData skeletonData);
}
}

View File

@@ -1,273 +1,273 @@
/******************************************************************************
* 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.
*****************************************************************************/
using Spine;
using System;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
namespace Spine.Unity {
/// <summary>Loads and stores a Spine atlas and list of materials.</summary>
[CreateAssetMenu(fileName = "New Spine Atlas Asset", menuName = "Spine/Spine Atlas Asset")]
public class SpineAtlasAsset : AtlasAssetBase {
public TextAsset atlasFile;
public Material[] materials;
protected Atlas atlas;
public override bool IsLoaded { get { return this.atlas != null; } }
public override IEnumerable<Material> Materials { get { return materials; } }
public override int MaterialCount { get { return materials == null ? 0 : materials.Length; } }
public override Material PrimaryMaterial { get { return materials[0]; } }
#region Runtime Instantiation
/// <summary>
/// Creates a runtime AtlasAsset</summary>
public static SpineAtlasAsset CreateRuntimeInstance (TextAsset atlasText, Material[] materials, bool initialize) {
SpineAtlasAsset atlasAsset = ScriptableObject.CreateInstance<SpineAtlasAsset>();
atlasAsset.Reset();
atlasAsset.atlasFile = atlasText;
atlasAsset.materials = materials;
if (initialize)
atlasAsset.GetAtlas();
return atlasAsset;
}
/// <summary>
/// Creates a runtime AtlasAsset. Only providing the textures is slower
/// because it has to search for atlas page matches.
/// </summary>
/// <param name="textures">An array of all textures referenced in the provided <c>atlasText</c>
/// atlas asset JSON file. When procedurally creating textures, each <c>Texture.name</c>
/// needs to be set to the atlas page texture filename without the .png extension,
/// e.g. 'my_skeleton' if the png filename listed in the atlas asset file is 'my_skeleton.png'.</param>
/// <seealso cref="Spine.Unity.SpineAtlasAsset.CreateRuntimeInstance(TextAsset, Material[], bool)"/>
public static SpineAtlasAsset CreateRuntimeInstance (TextAsset atlasText, Texture2D[] textures, Material materialPropertySource, bool initialize) {
// Get atlas page names.
string atlasString = atlasText.text;
atlasString = atlasString.Replace("\r", "");
string[] atlasLines = atlasString.Split('\n');
var pages = new List<string>();
for (int i = 0; i < atlasLines.Length - 1; i++) {
string line = atlasLines[i].Trim();
if (line.EndsWith(".png"))
pages.Add(line.Replace(".png", ""));
}
// Populate Materials[] by matching texture names with page names.
var materials = new Material[pages.Count];
for (int i = 0, n = pages.Count; i < n; i++) {
Material mat = null;
// Search for a match.
string pageName = pages[i];
for (int j = 0, m = textures.Length; j < m; j++) {
if (string.Equals(pageName, textures[j].name, System.StringComparison.OrdinalIgnoreCase)) {
// Match found.
mat = new Material(materialPropertySource);
mat.mainTexture = textures[j];
break;
}
}
if (mat != null)
materials[i] = mat;
else
throw new ArgumentException("Could not find matching atlas page in the texture array.");
}
// Create AtlasAsset normally
return CreateRuntimeInstance(atlasText, materials, initialize);
}
/// <summary>
/// Creates a runtime AtlasAsset. Only providing the textures is slower because it has to search for atlas page matches.
/// <param name="textures">An array of all textures referenced in the provided <c>atlasText</c>
/// atlas asset JSON file. When procedurally creating textures, each <c>Texture.name</c>
/// needs to be set to the atlas page texture filename without the .png extension,
/// e.g. 'my_skeleton' if the png filename listed in the atlas asset file is 'my_skeleton.png'.</param>
/// <seealso cref="Spine.Unity.AtlasAssetBase.CreateRuntimeInstance(TextAsset, Material[], bool)"/></summary>
public static SpineAtlasAsset CreateRuntimeInstance (TextAsset atlasText, Texture2D[] textures, Shader shader, bool initialize) {
if (shader == null)
shader = Shader.Find("Spine/Skeleton");
Material materialProperySource = new Material(shader);
var oa = CreateRuntimeInstance(atlasText, textures, materialProperySource, initialize);
return oa;
}
#endregion
void Reset () {
Clear();
}
public override void Clear () {
atlas = null;
}
/// <returns>The atlas or null if it could not be loaded.</returns>
public override Atlas GetAtlas (bool onlyMetaData = false) {
if (atlasFile == null) {
Debug.LogError("Atlas file not set for atlas asset: " + name, this);
Clear();
return null;
}
if (!onlyMetaData && (materials == null || materials.Length == 0)) {
Debug.LogError("Materials not set for atlas asset: " + name, this);
Clear();
return null;
}
if (atlas != null) return atlas;
try {
TextureLoader loader;
if (!onlyMetaData)
loader = new MaterialsTextureLoader(this);
else
loader = new NoOpTextureLoader();
atlas = new Atlas(new StringReader(atlasFile.text), "", loader);
atlas.FlipV();
return atlas;
} catch (Exception ex) {
Debug.LogError("Error reading atlas file for atlas asset: " + name + "\n" + ex.Message + "\n" + ex.StackTrace, this);
return null;
}
}
public Mesh GenerateMesh (string name, Mesh mesh, out Material material, float scale = 0.01f) {
AtlasRegion region = atlas.FindRegion(name);
material = null;
if (region != null) {
if (mesh == null) {
mesh = new Mesh();
mesh.name = name;
}
Vector3[] verts = new Vector3[4];
Vector2[] uvs = new Vector2[4];
Color[] colors = { Color.white, Color.white, Color.white, Color.white };
int[] triangles = { 0, 1, 2, 2, 3, 0 };
float left, right, top, bottom;
left = region.width / -2f;
right = left * -1f;
top = region.height / 2f;
bottom = top * -1;
verts[0] = new Vector3(left, bottom, 0) * scale;
verts[1] = new Vector3(left, top, 0) * scale;
verts[2] = new Vector3(right, top, 0) * scale;
verts[3] = new Vector3(right, bottom, 0) * scale;
float u, v, u2, v2;
u = region.u;
v = region.v;
u2 = region.u2;
v2 = region.v2;
if (region.degrees == 90) {
uvs[0] = new Vector2(u2, v2);
uvs[1] = new Vector2(u, v2);
uvs[2] = new Vector2(u, v);
uvs[3] = new Vector2(u2, v);
} else {
uvs[0] = new Vector2(u, v2);
uvs[1] = new Vector2(u, v);
uvs[2] = new Vector2(u2, v);
uvs[3] = new Vector2(u2, v2);
}
mesh.triangles = new int[0];
mesh.vertices = verts;
mesh.uv = uvs;
mesh.colors = colors;
mesh.triangles = triangles;
mesh.RecalculateNormals();
mesh.RecalculateBounds();
material = (Material)region.page.rendererObject;
} else {
mesh = null;
}
return mesh;
}
}
public class NoOpTextureLoader : TextureLoader {
public void Load (AtlasPage page, string path) { }
public void Unload (object texture) { }
}
public class MaterialsTextureLoader : TextureLoader {
SpineAtlasAsset atlasAsset;
public MaterialsTextureLoader (SpineAtlasAsset atlasAsset) {
this.atlasAsset = atlasAsset;
}
public void Load (AtlasPage page, string path) {
#if UNITY_EDITOR
if (BuildUtilities.IsInSkeletonAssetBuildPreProcessing ||
BuildUtilities.IsInSkeletonAssetBuildPostProcessing)
return;
#endif
String name = Path.GetFileNameWithoutExtension(path);
Material material = null;
foreach (Material other in atlasAsset.materials) {
if (other.mainTexture == null) {
Debug.LogError("Material is missing texture: " + other.name, other);
return;
}
if (other.mainTexture.name == name) {
material = other;
break;
}
}
if (material == null) {
Debug.LogError("Material with texture name \"" + name + "\" not found for atlas asset: " + atlasAsset.name, atlasAsset);
return;
}
page.rendererObject = material;
// Very old atlas files expected the texture's actual size to be used at runtime.
if (page.width == 0 || page.height == 0) {
page.width = material.mainTexture.width;
page.height = material.mainTexture.height;
}
}
public void Unload (object texture) { }
}
}
/******************************************************************************
* 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.
*****************************************************************************/
using Spine;
using System;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
namespace Spine.Unity {
/// <summary>Loads and stores a Spine atlas and list of materials.</summary>
[CreateAssetMenu(fileName = "New Spine Atlas Asset", menuName = "Spine/Spine Atlas Asset")]
public class SpineAtlasAsset : AtlasAssetBase {
public TextAsset atlasFile;
public Material[] materials;
protected Atlas atlas;
public override bool IsLoaded { get { return this.atlas != null; } }
public override IEnumerable<Material> Materials { get { return materials; } }
public override int MaterialCount { get { return materials == null ? 0 : materials.Length; } }
public override Material PrimaryMaterial { get { return materials[0]; } }
#region Runtime Instantiation
/// <summary>
/// Creates a runtime AtlasAsset</summary>
public static SpineAtlasAsset CreateRuntimeInstance (TextAsset atlasText, Material[] materials, bool initialize) {
SpineAtlasAsset atlasAsset = ScriptableObject.CreateInstance<SpineAtlasAsset>();
atlasAsset.Reset();
atlasAsset.atlasFile = atlasText;
atlasAsset.materials = materials;
if (initialize)
atlasAsset.GetAtlas();
return atlasAsset;
}
/// <summary>
/// Creates a runtime AtlasAsset. Only providing the textures is slower
/// because it has to search for atlas page matches.
/// </summary>
/// <param name="textures">An array of all textures referenced in the provided <c>atlasText</c>
/// atlas asset JSON file. When procedurally creating textures, each <c>Texture.name</c>
/// needs to be set to the atlas page texture filename without the .png extension,
/// e.g. 'my_skeleton' if the png filename listed in the atlas asset file is 'my_skeleton.png'.</param>
/// <seealso cref="Spine.Unity.SpineAtlasAsset.CreateRuntimeInstance(TextAsset, Material[], bool)"/>
public static SpineAtlasAsset CreateRuntimeInstance (TextAsset atlasText, Texture2D[] textures, Material materialPropertySource, bool initialize) {
// Get atlas page names.
string atlasString = atlasText.text;
atlasString = atlasString.Replace("\r", "");
string[] atlasLines = atlasString.Split('\n');
var pages = new List<string>();
for (int i = 0; i < atlasLines.Length - 1; i++) {
string line = atlasLines[i].Trim();
if (line.EndsWith(".png"))
pages.Add(line.Replace(".png", ""));
}
// Populate Materials[] by matching texture names with page names.
var materials = new Material[pages.Count];
for (int i = 0, n = pages.Count; i < n; i++) {
Material mat = null;
// Search for a match.
string pageName = pages[i];
for (int j = 0, m = textures.Length; j < m; j++) {
if (string.Equals(pageName, textures[j].name, System.StringComparison.OrdinalIgnoreCase)) {
// Match found.
mat = new Material(materialPropertySource);
mat.mainTexture = textures[j];
break;
}
}
if (mat != null)
materials[i] = mat;
else
throw new ArgumentException("Could not find matching atlas page in the texture array.");
}
// Create AtlasAsset normally
return CreateRuntimeInstance(atlasText, materials, initialize);
}
/// <summary>
/// Creates a runtime AtlasAsset. Only providing the textures is slower because it has to search for atlas page matches.
/// <param name="textures">An array of all textures referenced in the provided <c>atlasText</c>
/// atlas asset JSON file. When procedurally creating textures, each <c>Texture.name</c>
/// needs to be set to the atlas page texture filename without the .png extension,
/// e.g. 'my_skeleton' if the png filename listed in the atlas asset file is 'my_skeleton.png'.</param>
/// <seealso cref="Spine.Unity.AtlasAssetBase.CreateRuntimeInstance(TextAsset, Material[], bool)"/></summary>
public static SpineAtlasAsset CreateRuntimeInstance (TextAsset atlasText, Texture2D[] textures, Shader shader, bool initialize) {
if (shader == null)
shader = Shader.Find("Spine/Skeleton");
Material materialProperySource = new Material(shader);
var oa = CreateRuntimeInstance(atlasText, textures, materialProperySource, initialize);
return oa;
}
#endregion
void Reset () {
Clear();
}
public override void Clear () {
atlas = null;
}
/// <returns>The atlas or null if it could not be loaded.</returns>
public override Atlas GetAtlas (bool onlyMetaData = false) {
if (atlasFile == null) {
Debug.LogError("Atlas file not set for atlas asset: " + name, this);
Clear();
return null;
}
if (!onlyMetaData && (materials == null || materials.Length == 0)) {
Debug.LogError("Materials not set for atlas asset: " + name, this);
Clear();
return null;
}
if (atlas != null) return atlas;
try {
TextureLoader loader;
if (!onlyMetaData)
loader = new MaterialsTextureLoader(this);
else
loader = new NoOpTextureLoader();
atlas = new Atlas(new StringReader(atlasFile.text), "", loader);
atlas.FlipV();
return atlas;
} catch (Exception ex) {
Debug.LogError("Error reading atlas file for atlas asset: " + name + "\n" + ex.Message + "\n" + ex.StackTrace, this);
return null;
}
}
public Mesh GenerateMesh (string name, Mesh mesh, out Material material, float scale = 0.01f) {
AtlasRegion region = atlas.FindRegion(name);
material = null;
if (region != null) {
if (mesh == null) {
mesh = new Mesh();
mesh.name = name;
}
Vector3[] verts = new Vector3[4];
Vector2[] uvs = new Vector2[4];
Color[] colors = { Color.white, Color.white, Color.white, Color.white };
int[] triangles = { 0, 1, 2, 2, 3, 0 };
float left, right, top, bottom;
left = region.width / -2f;
right = left * -1f;
top = region.height / 2f;
bottom = top * -1;
verts[0] = new Vector3(left, bottom, 0) * scale;
verts[1] = new Vector3(left, top, 0) * scale;
verts[2] = new Vector3(right, top, 0) * scale;
verts[3] = new Vector3(right, bottom, 0) * scale;
float u, v, u2, v2;
u = region.u;
v = region.v;
u2 = region.u2;
v2 = region.v2;
if (region.degrees == 90) {
uvs[0] = new Vector2(u2, v2);
uvs[1] = new Vector2(u, v2);
uvs[2] = new Vector2(u, v);
uvs[3] = new Vector2(u2, v);
} else {
uvs[0] = new Vector2(u, v2);
uvs[1] = new Vector2(u, v);
uvs[2] = new Vector2(u2, v);
uvs[3] = new Vector2(u2, v2);
}
mesh.triangles = new int[0];
mesh.vertices = verts;
mesh.uv = uvs;
mesh.colors = colors;
mesh.triangles = triangles;
mesh.RecalculateNormals();
mesh.RecalculateBounds();
material = (Material)region.page.rendererObject;
} else {
mesh = null;
}
return mesh;
}
}
public class NoOpTextureLoader : TextureLoader {
public void Load (AtlasPage page, string path) { }
public void Unload (object texture) { }
}
public class MaterialsTextureLoader : TextureLoader {
SpineAtlasAsset atlasAsset;
public MaterialsTextureLoader (SpineAtlasAsset atlasAsset) {
this.atlasAsset = atlasAsset;
}
public void Load (AtlasPage page, string path) {
#if UNITY_EDITOR
if (BuildUtilities.IsInSkeletonAssetBuildPreProcessing ||
BuildUtilities.IsInSkeletonAssetBuildPostProcessing)
return;
#endif
String name = Path.GetFileNameWithoutExtension(path);
Material material = null;
foreach (Material other in atlasAsset.materials) {
if (other.mainTexture == null) {
Debug.LogError("Material is missing texture: " + other.name, other);
return;
}
if (other.mainTexture.name == name) {
material = other;
break;
}
}
if (material == null) {
Debug.LogError("Material with texture name \"" + name + "\" not found for atlas asset: " + atlasAsset.name, atlasAsset);
return;
}
page.rendererObject = material;
// Very old atlas files expected the texture's actual size to be used at runtime.
if (page.width == 0 || page.height == 0) {
page.width = material.mainTexture.width;
page.height = material.mainTexture.height;
}
}
public void Unload (object texture) { }
}
}

View File

@@ -1,394 +1,394 @@
/******************************************************************************
* 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.
*****************************************************************************/
#if UNITY_2018_2_OR_NEWER
#define EXPOSES_SPRITE_ATLAS_UTILITIES
#endif
using Spine;
using System;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.U2D;
#if UNITY_EDITOR
using UnityEditor;
using System.Reflection;
#endif
namespace Spine.Unity {
/// <summary>Loads and stores a Spine atlas and list of materials.</summary>
[CreateAssetMenu(fileName = "New Spine SpriteAtlas Asset", menuName = "Spine/Spine SpriteAtlas Asset")]
public class SpineSpriteAtlasAsset : AtlasAssetBase {
public SpriteAtlas spriteAtlasFile;
public Material[] materials;
protected Atlas atlas;
public bool updateRegionsInPlayMode;
[System.Serializable]
protected class SavedRegionInfo {
public float x, y, width, height;
public SpritePackingRotation packingRotation;
}
[SerializeField] protected SavedRegionInfo[] savedRegions;
public override bool IsLoaded { get { return this.atlas != null; } }
public override IEnumerable<Material> Materials { get { return materials; } }
public override int MaterialCount { get { return materials == null ? 0 : materials.Length; } }
public override Material PrimaryMaterial { get { return materials[0]; } }
#if UNITY_EDITOR
static MethodInfo GetPackedSpritesMethod, GetPreviewTexturesMethod;
#if !EXPOSES_SPRITE_ATLAS_UTILITIES
static MethodInfo PackAtlasesMethod;
#endif
#endif
#region Runtime Instantiation
/// <summary>
/// Creates a runtime AtlasAsset</summary>
public static SpineSpriteAtlasAsset CreateRuntimeInstance (SpriteAtlas spriteAtlasFile, Material[] materials, bool initialize) {
SpineSpriteAtlasAsset atlasAsset = ScriptableObject.CreateInstance<SpineSpriteAtlasAsset>();
atlasAsset.Reset();
atlasAsset.spriteAtlasFile = spriteAtlasFile;
atlasAsset.materials = materials;
if (initialize)
atlasAsset.GetAtlas();
return atlasAsset;
}
#endregion
void Reset () {
Clear();
}
public override void Clear () {
atlas = null;
}
/// <returns>The atlas or null if it could not be loaded.</returns>
public override Atlas GetAtlas (bool onlyMetaData = false) {
if (spriteAtlasFile == null) {
Debug.LogError("SpriteAtlas file not set for SpineSpriteAtlasAsset: " + name, this);
Clear();
return null;
}
if (!onlyMetaData && (materials == null || materials.Length == 0)) {
Debug.LogError("Materials not set for SpineSpriteAtlasAsset: " + name, this);
Clear();
return null;
}
if (atlas != null) return atlas;
try {
atlas = LoadAtlas(spriteAtlasFile);
return atlas;
} catch (Exception ex) {
Debug.LogError("Error analyzing SpriteAtlas for SpineSpriteAtlasAsset: " + name + "\n" + ex.Message + "\n" + ex.StackTrace, this);
return null;
}
}
protected void AssignRegionsFromSavedRegions (Sprite[] sprites, Atlas usedAtlas) {
if (savedRegions == null || savedRegions.Length != sprites.Length)
return;
int i = 0;
foreach (var region in usedAtlas) {
var savedRegion = savedRegions[i];
var page = region.page;
region.degrees = savedRegion.packingRotation == SpritePackingRotation.None ? 0 : 90;
float x = savedRegion.x;
float y = savedRegion.y;
float width = savedRegion.width;
float height = savedRegion.height;
region.u = x / (float)page.width;
region.v = y / (float)page.height;
if (region.degrees == 90) {
region.u2 = (x + height) / (float)page.width;
region.v2 = (y + width) / (float)page.height;
} else {
region.u2 = (x + width) / (float)page.width;
region.v2 = (y + height) / (float)page.height;
}
region.x = (int)x;
region.y = (int)y;
region.width = Math.Abs((int)width);
region.height = Math.Abs((int)height);
// flip upside down
var temp = region.v;
region.v = region.v2;
region.v2 = temp;
region.originalWidth = (int)width;
region.originalHeight = (int)height;
// note: currently sprite pivot offsets are ignored.
// var sprite = sprites[i];
region.offsetX = 0;//sprite.pivot.x;
region.offsetY = 0;//sprite.pivot.y;
++i;
}
}
private Atlas LoadAtlas (UnityEngine.U2D.SpriteAtlas spriteAtlas) {
List<AtlasPage> pages = new List<AtlasPage>();
List<AtlasRegion> regions = new List<AtlasRegion>();
Sprite[] sprites = new UnityEngine.Sprite[spriteAtlas.spriteCount];
spriteAtlas.GetSprites(sprites);
if (sprites.Length == 0)
return new Atlas(pages, regions);
Texture2D texture = null;
#if UNITY_EDITOR
if (!Application.isPlaying)
texture = AccessPackedTextureEditor(spriteAtlas);
else
#endif
texture = AccessPackedTexture(sprites);
Material material = materials[0];
#if !UNITY_EDITOR
material.mainTexture = texture;
#endif
Spine.AtlasPage page = new AtlasPage();
page.name = spriteAtlas.name;
page.width = texture.width;
page.height = texture.height;
page.format = Spine.Format.RGBA8888;
page.minFilter = TextureFilter.Linear;
page.magFilter = TextureFilter.Linear;
page.uWrap = TextureWrap.ClampToEdge;
page.vWrap = TextureWrap.ClampToEdge;
page.rendererObject = material;
pages.Add(page);
sprites = AccessPackedSprites(spriteAtlas);
int i = 0;
for (; i < sprites.Length; ++i) {
var sprite = sprites[i];
AtlasRegion region = new AtlasRegion();
region.name = sprite.name.Replace("(Clone)", "");
region.page = page;
region.degrees = sprite.packingRotation == SpritePackingRotation.None ? 0 : 90;
region.u2 = 1;
region.v2 = 1;
region.width = page.width;
region.height = page.height;
region.originalWidth = page.width;
region.originalHeight = page.height;
region.index = i;
regions.Add(region);
}
var atlas = new Atlas(pages, regions);
AssignRegionsFromSavedRegions(sprites, atlas);
return atlas;
}
#if UNITY_EDITOR
public static void UpdateByStartingEditorPlayMode () {
EditorApplication.isPlaying = true;
}
public static bool AnySpriteAtlasNeedsRegionsLoaded () {
string[] guids = UnityEditor.AssetDatabase.FindAssets("t:SpineSpriteAtlasAsset");
foreach (var guid in guids) {
string path = UnityEditor.AssetDatabase.GUIDToAssetPath(guid);
if (!string.IsNullOrEmpty(path)) {
var atlasAsset = UnityEditor.AssetDatabase.LoadAssetAtPath<SpineSpriteAtlasAsset>(path);
if (atlasAsset) {
if (atlasAsset.RegionsNeedLoading)
return true;
}
}
}
return false;
}
public static void UpdateWhenEditorPlayModeStarted () {
if (!EditorApplication.isPlaying)
return;
EditorApplication.update -= UpdateWhenEditorPlayModeStarted;
string[] guids = UnityEditor.AssetDatabase.FindAssets("t:SpineSpriteAtlasAsset");
if (guids.Length == 0)
return;
Debug.Log("Updating SpineSpriteAtlasAssets");
foreach (var guid in guids) {
string path = UnityEditor.AssetDatabase.GUIDToAssetPath(guid);
if (!string.IsNullOrEmpty(path)) {
var atlasAsset = UnityEditor.AssetDatabase.LoadAssetAtPath<SpineSpriteAtlasAsset>(path);
if (atlasAsset) {
atlasAsset.atlas = atlasAsset.LoadAtlas(atlasAsset.spriteAtlasFile);
atlasAsset.LoadRegionsInEditorPlayMode();
Debug.Log(string.Format("Updated regions of '{0}'", atlasAsset.name), atlasAsset);
}
}
}
EditorApplication.isPlaying = false;
}
public bool RegionsNeedLoading {
get { return savedRegions == null || savedRegions.Length == 0 || updateRegionsInPlayMode; }
}
public void LoadRegionsInEditorPlayMode () {
Sprite[] sprites = null;
System.Type T = Type.GetType("UnityEditor.U2D.SpriteAtlasExtensions,UnityEditor");
var method = T.GetMethod("GetPackedSprites", BindingFlags.NonPublic | BindingFlags.Static);
if (method != null) {
object retval = method.Invoke(null, new object[] { spriteAtlasFile });
var spritesArray = retval as Sprite[];
if (spritesArray != null && spritesArray.Length > 0) {
sprites = spritesArray;
}
}
if (sprites == null) {
sprites = new UnityEngine.Sprite[spriteAtlasFile.spriteCount];
spriteAtlasFile.GetSprites(sprites);
}
if (sprites.Length == 0) {
Debug.LogWarning(string.Format("SpriteAtlas '{0}' contains no sprites. Please make sure all assigned images are set to import type 'Sprite'.", spriteAtlasFile.name), spriteAtlasFile);
return;
}
else if (sprites[0].packingMode == SpritePackingMode.Tight) {
Debug.LogError(string.Format("SpriteAtlas '{0}': Tight packing is not supported. Please disable 'Tight Packing' in the SpriteAtlas Inspector.", spriteAtlasFile.name), spriteAtlasFile);
return;
}
if (savedRegions == null || savedRegions.Length != sprites.Length)
savedRegions = new SavedRegionInfo[sprites.Length];
int i = 0;
foreach (var region in atlas) {
var sprite = sprites[i];
var rect = sprite.textureRect;
float x = rect.min.x;
float y = rect.min.y;
float width = rect.width;
float height = rect.height;
var savedRegion = new SavedRegionInfo();
savedRegion.x = x;
savedRegion.y = y;
savedRegion.width = width;
savedRegion.height = height;
savedRegion.packingRotation = sprite.packingRotation;
savedRegions[i] = savedRegion;
++i;
}
updateRegionsInPlayMode = false;
AssignRegionsFromSavedRegions(sprites, atlas);
EditorUtility.SetDirty(this);
AssetDatabase.SaveAssets();
}
public static Texture2D AccessPackedTextureEditor (SpriteAtlas spriteAtlas) {
#if EXPOSES_SPRITE_ATLAS_UTILITIES
UnityEditor.U2D.SpriteAtlasUtility.PackAtlases(new SpriteAtlas[] { spriteAtlas }, EditorUserBuildSettings.activeBuildTarget);
#else
/*if (PackAtlasesMethod == null) {
System.Type T = Type.GetType("UnityEditor.U2D.SpriteAtlasUtility,UnityEditor");
PackAtlasesMethod = T.GetMethod("PackAtlases", BindingFlags.NonPublic | BindingFlags.Static);
}
if (PackAtlasesMethod != null) {
PackAtlasesMethod.Invoke(null, new object[] { new SpriteAtlas[] { spriteAtlas }, EditorUserBuildSettings.activeBuildTarget });
}*/
#endif
if (GetPreviewTexturesMethod == null) {
System.Type T = Type.GetType("UnityEditor.U2D.SpriteAtlasExtensions,UnityEditor");
GetPreviewTexturesMethod = T.GetMethod("GetPreviewTextures", BindingFlags.NonPublic | BindingFlags.Static);
}
if (GetPreviewTexturesMethod != null) {
object retval = GetPreviewTexturesMethod.Invoke(null, new object[] { spriteAtlas });
var textures = retval as Texture2D[];
if (textures.Length > 0)
return textures[0];
}
return null;
}
#endif
public static Texture2D AccessPackedTexture (Sprite[] sprites) {
return sprites[0].texture;
}
public static Sprite[] AccessPackedSprites (UnityEngine.U2D.SpriteAtlas spriteAtlas) {
Sprite[] sprites = null;
#if UNITY_EDITOR
if (!Application.isPlaying) {
if (GetPackedSpritesMethod == null) {
System.Type T = Type.GetType("UnityEditor.U2D.SpriteAtlasExtensions,UnityEditor");
GetPackedSpritesMethod = T.GetMethod("GetPackedSprites", BindingFlags.NonPublic | BindingFlags.Static);
}
if (GetPackedSpritesMethod != null) {
object retval = GetPackedSpritesMethod.Invoke(null, new object[] { spriteAtlas });
var spritesArray = retval as Sprite[];
if (spritesArray != null && spritesArray.Length > 0) {
sprites = spritesArray;
}
}
}
#endif
if (sprites == null) {
sprites = new UnityEngine.Sprite[spriteAtlas.spriteCount];
spriteAtlas.GetSprites(sprites);
if (sprites.Length == 0)
return null;
}
return sprites;
}
}
}
/******************************************************************************
* 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.
*****************************************************************************/
#if UNITY_2018_2_OR_NEWER
#define EXPOSES_SPRITE_ATLAS_UTILITIES
#endif
using Spine;
using System;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.U2D;
#if UNITY_EDITOR
using UnityEditor;
using System.Reflection;
#endif
namespace Spine.Unity {
/// <summary>Loads and stores a Spine atlas and list of materials.</summary>
[CreateAssetMenu(fileName = "New Spine SpriteAtlas Asset", menuName = "Spine/Spine SpriteAtlas Asset")]
public class SpineSpriteAtlasAsset : AtlasAssetBase {
public SpriteAtlas spriteAtlasFile;
public Material[] materials;
protected Atlas atlas;
public bool updateRegionsInPlayMode;
[System.Serializable]
protected class SavedRegionInfo {
public float x, y, width, height;
public SpritePackingRotation packingRotation;
}
[SerializeField] protected SavedRegionInfo[] savedRegions;
public override bool IsLoaded { get { return this.atlas != null; } }
public override IEnumerable<Material> Materials { get { return materials; } }
public override int MaterialCount { get { return materials == null ? 0 : materials.Length; } }
public override Material PrimaryMaterial { get { return materials[0]; } }
#if UNITY_EDITOR
static MethodInfo GetPackedSpritesMethod, GetPreviewTexturesMethod;
#if !EXPOSES_SPRITE_ATLAS_UTILITIES
static MethodInfo PackAtlasesMethod;
#endif
#endif
#region Runtime Instantiation
/// <summary>
/// Creates a runtime AtlasAsset</summary>
public static SpineSpriteAtlasAsset CreateRuntimeInstance (SpriteAtlas spriteAtlasFile, Material[] materials, bool initialize) {
SpineSpriteAtlasAsset atlasAsset = ScriptableObject.CreateInstance<SpineSpriteAtlasAsset>();
atlasAsset.Reset();
atlasAsset.spriteAtlasFile = spriteAtlasFile;
atlasAsset.materials = materials;
if (initialize)
atlasAsset.GetAtlas();
return atlasAsset;
}
#endregion
void Reset () {
Clear();
}
public override void Clear () {
atlas = null;
}
/// <returns>The atlas or null if it could not be loaded.</returns>
public override Atlas GetAtlas (bool onlyMetaData = false) {
if (spriteAtlasFile == null) {
Debug.LogError("SpriteAtlas file not set for SpineSpriteAtlasAsset: " + name, this);
Clear();
return null;
}
if (!onlyMetaData && (materials == null || materials.Length == 0)) {
Debug.LogError("Materials not set for SpineSpriteAtlasAsset: " + name, this);
Clear();
return null;
}
if (atlas != null) return atlas;
try {
atlas = LoadAtlas(spriteAtlasFile);
return atlas;
} catch (Exception ex) {
Debug.LogError("Error analyzing SpriteAtlas for SpineSpriteAtlasAsset: " + name + "\n" + ex.Message + "\n" + ex.StackTrace, this);
return null;
}
}
protected void AssignRegionsFromSavedRegions (Sprite[] sprites, Atlas usedAtlas) {
if (savedRegions == null || savedRegions.Length != sprites.Length)
return;
int i = 0;
foreach (var region in usedAtlas) {
var savedRegion = savedRegions[i];
var page = region.page;
region.degrees = savedRegion.packingRotation == SpritePackingRotation.None ? 0 : 90;
float x = savedRegion.x;
float y = savedRegion.y;
float width = savedRegion.width;
float height = savedRegion.height;
region.u = x / (float)page.width;
region.v = y / (float)page.height;
if (region.degrees == 90) {
region.u2 = (x + height) / (float)page.width;
region.v2 = (y + width) / (float)page.height;
} else {
region.u2 = (x + width) / (float)page.width;
region.v2 = (y + height) / (float)page.height;
}
region.x = (int)x;
region.y = (int)y;
region.width = Math.Abs((int)width);
region.height = Math.Abs((int)height);
// flip upside down
var temp = region.v;
region.v = region.v2;
region.v2 = temp;
region.originalWidth = (int)width;
region.originalHeight = (int)height;
// note: currently sprite pivot offsets are ignored.
// var sprite = sprites[i];
region.offsetX = 0;//sprite.pivot.x;
region.offsetY = 0;//sprite.pivot.y;
++i;
}
}
private Atlas LoadAtlas (UnityEngine.U2D.SpriteAtlas spriteAtlas) {
List<AtlasPage> pages = new List<AtlasPage>();
List<AtlasRegion> regions = new List<AtlasRegion>();
Sprite[] sprites = new UnityEngine.Sprite[spriteAtlas.spriteCount];
spriteAtlas.GetSprites(sprites);
if (sprites.Length == 0)
return new Atlas(pages, regions);
Texture2D texture = null;
#if UNITY_EDITOR
if (!Application.isPlaying)
texture = AccessPackedTextureEditor(spriteAtlas);
else
#endif
texture = AccessPackedTexture(sprites);
Material material = materials[0];
#if !UNITY_EDITOR
material.mainTexture = texture;
#endif
Spine.AtlasPage page = new AtlasPage();
page.name = spriteAtlas.name;
page.width = texture.width;
page.height = texture.height;
page.format = Spine.Format.RGBA8888;
page.minFilter = TextureFilter.Linear;
page.magFilter = TextureFilter.Linear;
page.uWrap = TextureWrap.ClampToEdge;
page.vWrap = TextureWrap.ClampToEdge;
page.rendererObject = material;
pages.Add(page);
sprites = AccessPackedSprites(spriteAtlas);
int i = 0;
for (; i < sprites.Length; ++i) {
var sprite = sprites[i];
AtlasRegion region = new AtlasRegion();
region.name = sprite.name.Replace("(Clone)", "");
region.page = page;
region.degrees = sprite.packingRotation == SpritePackingRotation.None ? 0 : 90;
region.u2 = 1;
region.v2 = 1;
region.width = page.width;
region.height = page.height;
region.originalWidth = page.width;
region.originalHeight = page.height;
region.index = i;
regions.Add(region);
}
var atlas = new Atlas(pages, regions);
AssignRegionsFromSavedRegions(sprites, atlas);
return atlas;
}
#if UNITY_EDITOR
public static void UpdateByStartingEditorPlayMode () {
EditorApplication.isPlaying = true;
}
public static bool AnySpriteAtlasNeedsRegionsLoaded () {
string[] guids = UnityEditor.AssetDatabase.FindAssets("t:SpineSpriteAtlasAsset");
foreach (var guid in guids) {
string path = UnityEditor.AssetDatabase.GUIDToAssetPath(guid);
if (!string.IsNullOrEmpty(path)) {
var atlasAsset = UnityEditor.AssetDatabase.LoadAssetAtPath<SpineSpriteAtlasAsset>(path);
if (atlasAsset) {
if (atlasAsset.RegionsNeedLoading)
return true;
}
}
}
return false;
}
public static void UpdateWhenEditorPlayModeStarted () {
if (!EditorApplication.isPlaying)
return;
EditorApplication.update -= UpdateWhenEditorPlayModeStarted;
string[] guids = UnityEditor.AssetDatabase.FindAssets("t:SpineSpriteAtlasAsset");
if (guids.Length == 0)
return;
Debug.Log("Updating SpineSpriteAtlasAssets");
foreach (var guid in guids) {
string path = UnityEditor.AssetDatabase.GUIDToAssetPath(guid);
if (!string.IsNullOrEmpty(path)) {
var atlasAsset = UnityEditor.AssetDatabase.LoadAssetAtPath<SpineSpriteAtlasAsset>(path);
if (atlasAsset) {
atlasAsset.atlas = atlasAsset.LoadAtlas(atlasAsset.spriteAtlasFile);
atlasAsset.LoadRegionsInEditorPlayMode();
Debug.Log(string.Format("Updated regions of '{0}'", atlasAsset.name), atlasAsset);
}
}
}
EditorApplication.isPlaying = false;
}
public bool RegionsNeedLoading {
get { return savedRegions == null || savedRegions.Length == 0 || updateRegionsInPlayMode; }
}
public void LoadRegionsInEditorPlayMode () {
Sprite[] sprites = null;
System.Type T = Type.GetType("UnityEditor.U2D.SpriteAtlasExtensions,UnityEditor");
var method = T.GetMethod("GetPackedSprites", BindingFlags.NonPublic | BindingFlags.Static);
if (method != null) {
object retval = method.Invoke(null, new object[] { spriteAtlasFile });
var spritesArray = retval as Sprite[];
if (spritesArray != null && spritesArray.Length > 0) {
sprites = spritesArray;
}
}
if (sprites == null) {
sprites = new UnityEngine.Sprite[spriteAtlasFile.spriteCount];
spriteAtlasFile.GetSprites(sprites);
}
if (sprites.Length == 0) {
Debug.LogWarning(string.Format("SpriteAtlas '{0}' contains no sprites. Please make sure all assigned images are set to import type 'Sprite'.", spriteAtlasFile.name), spriteAtlasFile);
return;
}
else if (sprites[0].packingMode == SpritePackingMode.Tight) {
Debug.LogError(string.Format("SpriteAtlas '{0}': Tight packing is not supported. Please disable 'Tight Packing' in the SpriteAtlas Inspector.", spriteAtlasFile.name), spriteAtlasFile);
return;
}
if (savedRegions == null || savedRegions.Length != sprites.Length)
savedRegions = new SavedRegionInfo[sprites.Length];
int i = 0;
foreach (var region in atlas) {
var sprite = sprites[i];
var rect = sprite.textureRect;
float x = rect.min.x;
float y = rect.min.y;
float width = rect.width;
float height = rect.height;
var savedRegion = new SavedRegionInfo();
savedRegion.x = x;
savedRegion.y = y;
savedRegion.width = width;
savedRegion.height = height;
savedRegion.packingRotation = sprite.packingRotation;
savedRegions[i] = savedRegion;
++i;
}
updateRegionsInPlayMode = false;
AssignRegionsFromSavedRegions(sprites, atlas);
EditorUtility.SetDirty(this);
AssetDatabase.SaveAssets();
}
public static Texture2D AccessPackedTextureEditor (SpriteAtlas spriteAtlas) {
#if EXPOSES_SPRITE_ATLAS_UTILITIES
UnityEditor.U2D.SpriteAtlasUtility.PackAtlases(new SpriteAtlas[] { spriteAtlas }, EditorUserBuildSettings.activeBuildTarget);
#else
/*if (PackAtlasesMethod == null) {
System.Type T = Type.GetType("UnityEditor.U2D.SpriteAtlasUtility,UnityEditor");
PackAtlasesMethod = T.GetMethod("PackAtlases", BindingFlags.NonPublic | BindingFlags.Static);
}
if (PackAtlasesMethod != null) {
PackAtlasesMethod.Invoke(null, new object[] { new SpriteAtlas[] { spriteAtlas }, EditorUserBuildSettings.activeBuildTarget });
}*/
#endif
if (GetPreviewTexturesMethod == null) {
System.Type T = Type.GetType("UnityEditor.U2D.SpriteAtlasExtensions,UnityEditor");
GetPreviewTexturesMethod = T.GetMethod("GetPreviewTextures", BindingFlags.NonPublic | BindingFlags.Static);
}
if (GetPreviewTexturesMethod != null) {
object retval = GetPreviewTexturesMethod.Invoke(null, new object[] { spriteAtlas });
var textures = retval as Texture2D[];
if (textures.Length > 0)
return textures[0];
}
return null;
}
#endif
public static Texture2D AccessPackedTexture (Sprite[] sprites) {
return sprites[0].texture;
}
public static Sprite[] AccessPackedSprites (UnityEngine.U2D.SpriteAtlas spriteAtlas) {
Sprite[] sprites = null;
#if UNITY_EDITOR
if (!Application.isPlaying) {
if (GetPackedSpritesMethod == null) {
System.Type T = Type.GetType("UnityEditor.U2D.SpriteAtlasExtensions,UnityEditor");
GetPackedSpritesMethod = T.GetMethod("GetPackedSprites", BindingFlags.NonPublic | BindingFlags.Static);
}
if (GetPackedSpritesMethod != null) {
object retval = GetPackedSpritesMethod.Invoke(null, new object[] { spriteAtlas });
var spritesArray = retval as Sprite[];
if (spritesArray != null && spritesArray.Length > 0) {
sprites = spritesArray;
}
}
}
#endif
if (sprites == null) {
sprites = new UnityEngine.Sprite[spriteAtlas.spriteCount];
spriteAtlas.GetSprites(sprites);
if (sprites.Length == 0)
return null;
}
return sprites;
}
}
}

View File

@@ -1,223 +1,223 @@
/******************************************************************************
* 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.
*****************************************************************************/
#if UNITY_2018_3 || UNITY_2019 || UNITY_2018_3_OR_NEWER
#define NEW_PREFAB_SYSTEM
#endif
using System;
using UnityEngine;
namespace Spine.Unity {
/// <summary>Sets a GameObject's transform to match a bone on a Spine skeleton.</summary>
#if NEW_PREFAB_SYSTEM
[ExecuteAlways]
#else
[ExecuteInEditMode]
#endif
[AddComponentMenu("Spine/BoneFollower")]
[HelpURL("http://esotericsoftware.com/spine-unity#BoneFollower")]
public class BoneFollower : MonoBehaviour {
#region Inspector
public SkeletonRenderer skeletonRenderer;
public SkeletonRenderer SkeletonRenderer {
get { return skeletonRenderer; }
set {
skeletonRenderer = value;
Initialize();
}
}
/// <summary>If a bone isn't set in code, boneName is used to find the bone at the beginning. For runtime switching by name, use SetBoneByName. You can also set the BoneFollower.bone field directly.</summary>
[SpineBone(dataField: "skeletonRenderer")]
public string boneName;
public bool followXYPosition = true;
public bool followZPosition = true;
public bool followBoneRotation = true;
[Tooltip("Follows the skeleton's flip state by controlling this Transform's local scale.")]
public bool followSkeletonFlip = true;
[Tooltip("Follows the target bone's local scale.")]
[UnityEngine.Serialization.FormerlySerializedAs("followScale")]
public bool followLocalScale = false;
[Tooltip("Includes the parent bone's lossy world scale. BoneFollower cannot inherit rotated/skewed scale because of UnityEngine.Transform property limitations.")]
public bool followParentWorldScale = false;
public enum AxisOrientation {
XAxis = 1,
YAxis
}
[Tooltip("Applies when 'Follow Skeleton Flip' is disabled but 'Follow Bone Rotation' is enabled."
+ " When flipping the skeleton by scaling its Transform, this follower's rotation is adjusted"
+ " instead of its scale to follow the bone orientation. When one of the axes is flipped, "
+ " only one axis can be followed, either the X or the Y axis, which is selected here.")]
public AxisOrientation maintainedAxisOrientation = AxisOrientation.XAxis;
[UnityEngine.Serialization.FormerlySerializedAs("resetOnAwake")]
public bool initializeOnAwake = true;
#endregion
[NonSerialized] public bool valid;
[NonSerialized] public Bone bone;
Transform skeletonTransform;
bool skeletonTransformIsParent;
/// <summary>
/// Sets the target bone by its bone name. Returns false if no bone was found. To set the bone by reference, use BoneFollower.bone directly.</summary>
public bool SetBone (string name) {
bone = skeletonRenderer.skeleton.FindBone(name);
if (bone == null) {
Debug.LogError("Bone not found: " + name, this);
return false;
}
boneName = name;
return true;
}
public void Awake () {
if (initializeOnAwake) Initialize();
}
public void HandleRebuildRenderer (SkeletonRenderer skeletonRenderer) {
Initialize();
}
public void Initialize () {
bone = null;
valid = skeletonRenderer != null && skeletonRenderer.valid;
if (!valid) return;
skeletonTransform = skeletonRenderer.transform;
skeletonRenderer.OnRebuild -= HandleRebuildRenderer;
skeletonRenderer.OnRebuild += HandleRebuildRenderer;
skeletonTransformIsParent = Transform.ReferenceEquals(skeletonTransform, transform.parent);
if (!string.IsNullOrEmpty(boneName))
bone = skeletonRenderer.skeleton.FindBone(boneName);
#if UNITY_EDITOR
if (Application.isEditor)
LateUpdate();
#endif
}
void OnDestroy () {
if (skeletonRenderer != null)
skeletonRenderer.OnRebuild -= HandleRebuildRenderer;
}
public void LateUpdate () {
if (!valid) {
Initialize();
return;
}
#if UNITY_EDITOR
if (!Application.isPlaying)
skeletonTransformIsParent = Transform.ReferenceEquals(skeletonTransform, transform.parent);
#endif
if (bone == null) {
if (string.IsNullOrEmpty(boneName)) return;
bone = skeletonRenderer.skeleton.FindBone(boneName);
if (!SetBone(boneName)) return;
}
Transform thisTransform = this.transform;
float additionalFlipScale = 1;
if (skeletonTransformIsParent) {
// Recommended setup: Use local transform properties if Spine GameObject is the immediate parent
thisTransform.localPosition = new Vector3(followXYPosition ? bone.WorldX : thisTransform.localPosition.x,
followXYPosition ? bone.WorldY : thisTransform.localPosition.y,
followZPosition ? 0f : thisTransform.localPosition.z);
if (followBoneRotation) {
float halfRotation = Mathf.Atan2(bone.C, bone.A) * 0.5f;
if (followLocalScale && bone.ScaleX < 0) // Negate rotation from negative scaleX. Don't use negative determinant. local scaleY doesn't factor into used rotation.
halfRotation += Mathf.PI * 0.5f;
var q = default(Quaternion);
q.z = Mathf.Sin(halfRotation);
q.w = Mathf.Cos(halfRotation);
thisTransform.localRotation = q;
}
} else {
// For special cases: Use transform world properties if transform relationship is complicated
Vector3 targetWorldPosition = skeletonTransform.TransformPoint(new Vector3(bone.WorldX, bone.WorldY, 0f));
if (!followZPosition) targetWorldPosition.z = thisTransform.position.z;
if (!followXYPosition) {
targetWorldPosition.x = thisTransform.position.x;
targetWorldPosition.y = thisTransform.position.y;
}
Vector3 skeletonLossyScale = skeletonTransform.lossyScale;
Transform transformParent = thisTransform.parent;
Vector3 parentLossyScale = transformParent != null ? transformParent.lossyScale : Vector3.one;
if (followBoneRotation) {
float boneWorldRotation = bone.WorldRotationX;
if ((skeletonLossyScale.x * skeletonLossyScale.y) < 0)
boneWorldRotation = -boneWorldRotation;
if (followSkeletonFlip || maintainedAxisOrientation == AxisOrientation.XAxis) {
if ((skeletonLossyScale.x * parentLossyScale.x < 0))
boneWorldRotation += 180f;
} else {
if ((skeletonLossyScale.y * parentLossyScale.y < 0))
boneWorldRotation += 180f;
}
Vector3 worldRotation = skeletonTransform.rotation.eulerAngles;
if (followLocalScale && bone.ScaleX < 0) boneWorldRotation += 180f;
thisTransform.SetPositionAndRotation(targetWorldPosition, Quaternion.Euler(worldRotation.x, worldRotation.y, worldRotation.z + boneWorldRotation));
} else {
thisTransform.position = targetWorldPosition;
}
additionalFlipScale = Mathf.Sign(skeletonLossyScale.x * parentLossyScale.x
* skeletonLossyScale.y * parentLossyScale.y);
}
Bone parentBone = bone.Parent;
Vector3 localScale = new Vector3(1f, 1f, 1f);
if (followParentWorldScale && parentBone != null)
localScale = new Vector3(parentBone.WorldScaleX, parentBone.WorldScaleY, 1f);
if (followLocalScale)
localScale.Scale(new Vector3(bone.ScaleX, bone.ScaleY, 1f));
if (followSkeletonFlip)
localScale.y *= Mathf.Sign(bone.Skeleton.ScaleX * bone.Skeleton.ScaleY) * additionalFlipScale;
thisTransform.localScale = localScale;
}
}
}
/******************************************************************************
* 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.
*****************************************************************************/
#if UNITY_2018_3 || UNITY_2019 || UNITY_2018_3_OR_NEWER
#define NEW_PREFAB_SYSTEM
#endif
using System;
using UnityEngine;
namespace Spine.Unity {
/// <summary>Sets a GameObject's transform to match a bone on a Spine skeleton.</summary>
#if NEW_PREFAB_SYSTEM
[ExecuteAlways]
#else
[ExecuteInEditMode]
#endif
[AddComponentMenu("Spine/BoneFollower")]
[HelpURL("http://esotericsoftware.com/spine-unity#BoneFollower")]
public class BoneFollower : MonoBehaviour {
#region Inspector
public SkeletonRenderer skeletonRenderer;
public SkeletonRenderer SkeletonRenderer {
get { return skeletonRenderer; }
set {
skeletonRenderer = value;
Initialize();
}
}
/// <summary>If a bone isn't set in code, boneName is used to find the bone at the beginning. For runtime switching by name, use SetBoneByName. You can also set the BoneFollower.bone field directly.</summary>
[SpineBone(dataField: "skeletonRenderer")]
public string boneName;
public bool followXYPosition = true;
public bool followZPosition = true;
public bool followBoneRotation = true;
[Tooltip("Follows the skeleton's flip state by controlling this Transform's local scale.")]
public bool followSkeletonFlip = true;
[Tooltip("Follows the target bone's local scale.")]
[UnityEngine.Serialization.FormerlySerializedAs("followScale")]
public bool followLocalScale = false;
[Tooltip("Includes the parent bone's lossy world scale. BoneFollower cannot inherit rotated/skewed scale because of UnityEngine.Transform property limitations.")]
public bool followParentWorldScale = false;
public enum AxisOrientation {
XAxis = 1,
YAxis
}
[Tooltip("Applies when 'Follow Skeleton Flip' is disabled but 'Follow Bone Rotation' is enabled."
+ " When flipping the skeleton by scaling its Transform, this follower's rotation is adjusted"
+ " instead of its scale to follow the bone orientation. When one of the axes is flipped, "
+ " only one axis can be followed, either the X or the Y axis, which is selected here.")]
public AxisOrientation maintainedAxisOrientation = AxisOrientation.XAxis;
[UnityEngine.Serialization.FormerlySerializedAs("resetOnAwake")]
public bool initializeOnAwake = true;
#endregion
[NonSerialized] public bool valid;
[NonSerialized] public Bone bone;
Transform skeletonTransform;
bool skeletonTransformIsParent;
/// <summary>
/// Sets the target bone by its bone name. Returns false if no bone was found. To set the bone by reference, use BoneFollower.bone directly.</summary>
public bool SetBone (string name) {
bone = skeletonRenderer.skeleton.FindBone(name);
if (bone == null) {
Debug.LogError("Bone not found: " + name, this);
return false;
}
boneName = name;
return true;
}
public void Awake () {
if (initializeOnAwake) Initialize();
}
public void HandleRebuildRenderer (SkeletonRenderer skeletonRenderer) {
Initialize();
}
public void Initialize () {
bone = null;
valid = skeletonRenderer != null && skeletonRenderer.valid;
if (!valid) return;
skeletonTransform = skeletonRenderer.transform;
skeletonRenderer.OnRebuild -= HandleRebuildRenderer;
skeletonRenderer.OnRebuild += HandleRebuildRenderer;
skeletonTransformIsParent = Transform.ReferenceEquals(skeletonTransform, transform.parent);
if (!string.IsNullOrEmpty(boneName))
bone = skeletonRenderer.skeleton.FindBone(boneName);
#if UNITY_EDITOR
if (Application.isEditor)
LateUpdate();
#endif
}
void OnDestroy () {
if (skeletonRenderer != null)
skeletonRenderer.OnRebuild -= HandleRebuildRenderer;
}
public void LateUpdate () {
if (!valid) {
Initialize();
return;
}
#if UNITY_EDITOR
if (!Application.isPlaying)
skeletonTransformIsParent = Transform.ReferenceEquals(skeletonTransform, transform.parent);
#endif
if (bone == null) {
if (string.IsNullOrEmpty(boneName)) return;
bone = skeletonRenderer.skeleton.FindBone(boneName);
if (!SetBone(boneName)) return;
}
Transform thisTransform = this.transform;
float additionalFlipScale = 1;
if (skeletonTransformIsParent) {
// Recommended setup: Use local transform properties if Spine GameObject is the immediate parent
thisTransform.localPosition = new Vector3(followXYPosition ? bone.WorldX : thisTransform.localPosition.x,
followXYPosition ? bone.WorldY : thisTransform.localPosition.y,
followZPosition ? 0f : thisTransform.localPosition.z);
if (followBoneRotation) {
float halfRotation = Mathf.Atan2(bone.C, bone.A) * 0.5f;
if (followLocalScale && bone.ScaleX < 0) // Negate rotation from negative scaleX. Don't use negative determinant. local scaleY doesn't factor into used rotation.
halfRotation += Mathf.PI * 0.5f;
var q = default(Quaternion);
q.z = Mathf.Sin(halfRotation);
q.w = Mathf.Cos(halfRotation);
thisTransform.localRotation = q;
}
} else {
// For special cases: Use transform world properties if transform relationship is complicated
Vector3 targetWorldPosition = skeletonTransform.TransformPoint(new Vector3(bone.WorldX, bone.WorldY, 0f));
if (!followZPosition) targetWorldPosition.z = thisTransform.position.z;
if (!followXYPosition) {
targetWorldPosition.x = thisTransform.position.x;
targetWorldPosition.y = thisTransform.position.y;
}
Vector3 skeletonLossyScale = skeletonTransform.lossyScale;
Transform transformParent = thisTransform.parent;
Vector3 parentLossyScale = transformParent != null ? transformParent.lossyScale : Vector3.one;
if (followBoneRotation) {
float boneWorldRotation = bone.WorldRotationX;
if ((skeletonLossyScale.x * skeletonLossyScale.y) < 0)
boneWorldRotation = -boneWorldRotation;
if (followSkeletonFlip || maintainedAxisOrientation == AxisOrientation.XAxis) {
if ((skeletonLossyScale.x * parentLossyScale.x < 0))
boneWorldRotation += 180f;
} else {
if ((skeletonLossyScale.y * parentLossyScale.y < 0))
boneWorldRotation += 180f;
}
Vector3 worldRotation = skeletonTransform.rotation.eulerAngles;
if (followLocalScale && bone.ScaleX < 0) boneWorldRotation += 180f;
thisTransform.SetPositionAndRotation(targetWorldPosition, Quaternion.Euler(worldRotation.x, worldRotation.y, worldRotation.z + boneWorldRotation));
} else {
thisTransform.position = targetWorldPosition;
}
additionalFlipScale = Mathf.Sign(skeletonLossyScale.x * parentLossyScale.x
* skeletonLossyScale.y * parentLossyScale.y);
}
Bone parentBone = bone.Parent;
Vector3 localScale = new Vector3(1f, 1f, 1f);
if (followParentWorldScale && parentBone != null)
localScale = new Vector3(parentBone.WorldScaleX, parentBone.WorldScaleY, 1f);
if (followLocalScale)
localScale.Scale(new Vector3(bone.ScaleX, bone.ScaleY, 1f));
if (followSkeletonFlip)
localScale.y *= Mathf.Sign(bone.Skeleton.ScaleX * bone.Skeleton.ScaleY) * additionalFlipScale;
thisTransform.localScale = localScale;
}
}
}

View File

@@ -1,202 +1,202 @@
/******************************************************************************
* 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.
*****************************************************************************/
#if UNITY_2018_3 || UNITY_2019 || UNITY_2018_3_OR_NEWER
#define NEW_PREFAB_SYSTEM
#endif
using UnityEngine;
namespace Spine.Unity {
using AxisOrientation = BoneFollower.AxisOrientation;
#if NEW_PREFAB_SYSTEM
[ExecuteAlways]
#else
[ExecuteInEditMode]
#endif
[RequireComponent(typeof(RectTransform)), DisallowMultipleComponent]
[AddComponentMenu("Spine/UI/BoneFollowerGraphic")]
[HelpURL("http://esotericsoftware.com/spine-unity#BoneFollowerGraphic")]
public class BoneFollowerGraphic : MonoBehaviour {
public SkeletonGraphic skeletonGraphic;
public SkeletonGraphic SkeletonGraphic {
get { return skeletonGraphic; }
set {
skeletonGraphic = value;
Initialize();
}
}
public bool initializeOnAwake = true;
/// <summary>If a bone isn't set in code, boneName is used to find the bone at the beginning. For runtime switching by name, use SetBoneByName. You can also set the BoneFollower.bone field directly.</summary>
[SpineBone(dataField: "skeletonGraphic")]
public string boneName;
public bool followBoneRotation = true;
[Tooltip("Follows the skeleton's flip state by controlling this Transform's local scale.")]
public bool followSkeletonFlip = true;
[Tooltip("Follows the target bone's local scale.")]
public bool followLocalScale = false;
[Tooltip("Includes the parent bone's lossy world scale. BoneFollower cannot inherit rotated/skewed scale because of UnityEngine.Transform property limitations.")]
public bool followParentWorldScale = false;
public bool followXYPosition = true;
public bool followZPosition = true;
[Tooltip("Applies when 'Follow Skeleton Flip' is disabled but 'Follow Bone Rotation' is enabled."
+ " When flipping the skeleton by scaling its Transform, this follower's rotation is adjusted"
+ " instead of its scale to follow the bone orientation. When one of the axes is flipped, "
+ " only one axis can be followed, either the X or the Y axis, which is selected here.")]
public AxisOrientation maintainedAxisOrientation = AxisOrientation.XAxis;
[System.NonSerialized] public Bone bone;
Transform skeletonTransform;
bool skeletonTransformIsParent;
[System.NonSerialized] public bool valid;
/// <summary>
/// Sets the target bone by its bone name. Returns false if no bone was found.</summary>
public bool SetBone (string name) {
bone = skeletonGraphic.Skeleton.FindBone(name);
if (bone == null) {
Debug.LogError("Bone not found: " + name, this);
return false;
}
boneName = name;
return true;
}
public void Awake () {
if (initializeOnAwake) Initialize();
}
public void Initialize () {
bone = null;
valid = skeletonGraphic != null && skeletonGraphic.IsValid;
if (!valid) return;
skeletonTransform = skeletonGraphic.transform;
// skeletonGraphic.OnRebuild -= HandleRebuildRenderer;
// skeletonGraphic.OnRebuild += HandleRebuildRenderer;
skeletonTransformIsParent = Transform.ReferenceEquals(skeletonTransform, transform.parent);
if (!string.IsNullOrEmpty(boneName))
bone = skeletonGraphic.Skeleton.FindBone(boneName);
#if UNITY_EDITOR
if (Application.isEditor) {
LateUpdate();
}
#endif
}
public void LateUpdate () {
if (!valid) {
Initialize();
return;
}
#if UNITY_EDITOR
if (!Application.isPlaying)
skeletonTransformIsParent = Transform.ReferenceEquals(skeletonTransform, transform.parent);
#endif
if (bone == null) {
if (string.IsNullOrEmpty(boneName)) return;
bone = skeletonGraphic.Skeleton.FindBone(boneName);
if (!SetBone(boneName)) return;
}
var thisTransform = this.transform as RectTransform;
if (thisTransform == null) return;
var canvas = skeletonGraphic.canvas;
if (canvas == null) canvas = skeletonGraphic.GetComponentInParent<Canvas>();
float scale = canvas != null ? canvas.referencePixelsPerUnit : 100.0f;
float additionalFlipScale = 1;
if (skeletonTransformIsParent) {
// Recommended setup: Use local transform properties if Spine GameObject is the immediate parent
thisTransform.localPosition = new Vector3(followXYPosition ? bone.WorldX * scale : thisTransform.localPosition.x,
followXYPosition ? bone.WorldY * scale : thisTransform.localPosition.y,
followZPosition ? 0f : thisTransform.localPosition.z);
if (followBoneRotation) thisTransform.localRotation = bone.GetQuaternion();
} else {
// For special cases: Use transform world properties if transform relationship is complicated
Vector3 targetWorldPosition = skeletonTransform.TransformPoint(new Vector3(bone.WorldX * scale, bone.WorldY * scale, 0f));
if (!followZPosition) targetWorldPosition.z = thisTransform.position.z;
if (!followXYPosition) {
targetWorldPosition.x = thisTransform.position.x;
targetWorldPosition.y = thisTransform.position.y;
}
Vector3 skeletonLossyScale = skeletonTransform.lossyScale;
Transform transformParent = thisTransform.parent;
Vector3 parentLossyScale = transformParent != null ? transformParent.lossyScale : Vector3.one;
if (followBoneRotation) {
float boneWorldRotation = bone.WorldRotationX;
if ((skeletonLossyScale.x * skeletonLossyScale.y) < 0)
boneWorldRotation = -boneWorldRotation;
if (followSkeletonFlip || maintainedAxisOrientation == AxisOrientation.XAxis) {
if ((skeletonLossyScale.x * parentLossyScale.x < 0))
boneWorldRotation += 180f;
} else {
if ((skeletonLossyScale.y * parentLossyScale.y < 0))
boneWorldRotation += 180f;
}
Vector3 worldRotation = skeletonTransform.rotation.eulerAngles;
if (followLocalScale && bone.ScaleX < 0) boneWorldRotation += 180f;
thisTransform.SetPositionAndRotation(targetWorldPosition, Quaternion.Euler(worldRotation.x, worldRotation.y, worldRotation.z + boneWorldRotation));
} else {
thisTransform.position = targetWorldPosition;
}
additionalFlipScale = Mathf.Sign(skeletonLossyScale.x * parentLossyScale.x
* skeletonLossyScale.y * parentLossyScale.y);
}
Bone parentBone = bone.Parent;
Vector3 localScale = new Vector3(1f, 1f, 1f);
if (followParentWorldScale && parentBone != null)
localScale = new Vector3(parentBone.WorldScaleX, parentBone.WorldScaleY, 1f);
if (followLocalScale)
localScale.Scale(new Vector3(bone.ScaleX, bone.ScaleY, 1f));
if (followSkeletonFlip)
localScale.y *= Mathf.Sign(bone.Skeleton.ScaleX * bone.Skeleton.ScaleY) * additionalFlipScale;
thisTransform.localScale = localScale;
}
}
}
/******************************************************************************
* 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.
*****************************************************************************/
#if UNITY_2018_3 || UNITY_2019 || UNITY_2018_3_OR_NEWER
#define NEW_PREFAB_SYSTEM
#endif
using UnityEngine;
namespace Spine.Unity {
using AxisOrientation = BoneFollower.AxisOrientation;
#if NEW_PREFAB_SYSTEM
[ExecuteAlways]
#else
[ExecuteInEditMode]
#endif
[RequireComponent(typeof(RectTransform)), DisallowMultipleComponent]
[AddComponentMenu("Spine/UI/BoneFollowerGraphic")]
[HelpURL("http://esotericsoftware.com/spine-unity#BoneFollowerGraphic")]
public class BoneFollowerGraphic : MonoBehaviour {
public SkeletonGraphic skeletonGraphic;
public SkeletonGraphic SkeletonGraphic {
get { return skeletonGraphic; }
set {
skeletonGraphic = value;
Initialize();
}
}
public bool initializeOnAwake = true;
/// <summary>If a bone isn't set in code, boneName is used to find the bone at the beginning. For runtime switching by name, use SetBoneByName. You can also set the BoneFollower.bone field directly.</summary>
[SpineBone(dataField: "skeletonGraphic")]
public string boneName;
public bool followBoneRotation = true;
[Tooltip("Follows the skeleton's flip state by controlling this Transform's local scale.")]
public bool followSkeletonFlip = true;
[Tooltip("Follows the target bone's local scale.")]
public bool followLocalScale = false;
[Tooltip("Includes the parent bone's lossy world scale. BoneFollower cannot inherit rotated/skewed scale because of UnityEngine.Transform property limitations.")]
public bool followParentWorldScale = false;
public bool followXYPosition = true;
public bool followZPosition = true;
[Tooltip("Applies when 'Follow Skeleton Flip' is disabled but 'Follow Bone Rotation' is enabled."
+ " When flipping the skeleton by scaling its Transform, this follower's rotation is adjusted"
+ " instead of its scale to follow the bone orientation. When one of the axes is flipped, "
+ " only one axis can be followed, either the X or the Y axis, which is selected here.")]
public AxisOrientation maintainedAxisOrientation = AxisOrientation.XAxis;
[System.NonSerialized] public Bone bone;
Transform skeletonTransform;
bool skeletonTransformIsParent;
[System.NonSerialized] public bool valid;
/// <summary>
/// Sets the target bone by its bone name. Returns false if no bone was found.</summary>
public bool SetBone (string name) {
bone = skeletonGraphic.Skeleton.FindBone(name);
if (bone == null) {
Debug.LogError("Bone not found: " + name, this);
return false;
}
boneName = name;
return true;
}
public void Awake () {
if (initializeOnAwake) Initialize();
}
public void Initialize () {
bone = null;
valid = skeletonGraphic != null && skeletonGraphic.IsValid;
if (!valid) return;
skeletonTransform = skeletonGraphic.transform;
// skeletonGraphic.OnRebuild -= HandleRebuildRenderer;
// skeletonGraphic.OnRebuild += HandleRebuildRenderer;
skeletonTransformIsParent = Transform.ReferenceEquals(skeletonTransform, transform.parent);
if (!string.IsNullOrEmpty(boneName))
bone = skeletonGraphic.Skeleton.FindBone(boneName);
#if UNITY_EDITOR
if (Application.isEditor) {
LateUpdate();
}
#endif
}
public void LateUpdate () {
if (!valid) {
Initialize();
return;
}
#if UNITY_EDITOR
if (!Application.isPlaying)
skeletonTransformIsParent = Transform.ReferenceEquals(skeletonTransform, transform.parent);
#endif
if (bone == null) {
if (string.IsNullOrEmpty(boneName)) return;
bone = skeletonGraphic.Skeleton.FindBone(boneName);
if (!SetBone(boneName)) return;
}
var thisTransform = this.transform as RectTransform;
if (thisTransform == null) return;
var canvas = skeletonGraphic.canvas;
if (canvas == null) canvas = skeletonGraphic.GetComponentInParent<Canvas>();
float scale = canvas != null ? canvas.referencePixelsPerUnit : 100.0f;
float additionalFlipScale = 1;
if (skeletonTransformIsParent) {
// Recommended setup: Use local transform properties if Spine GameObject is the immediate parent
thisTransform.localPosition = new Vector3(followXYPosition ? bone.WorldX * scale : thisTransform.localPosition.x,
followXYPosition ? bone.WorldY * scale : thisTransform.localPosition.y,
followZPosition ? 0f : thisTransform.localPosition.z);
if (followBoneRotation) thisTransform.localRotation = bone.GetQuaternion();
} else {
// For special cases: Use transform world properties if transform relationship is complicated
Vector3 targetWorldPosition = skeletonTransform.TransformPoint(new Vector3(bone.WorldX * scale, bone.WorldY * scale, 0f));
if (!followZPosition) targetWorldPosition.z = thisTransform.position.z;
if (!followXYPosition) {
targetWorldPosition.x = thisTransform.position.x;
targetWorldPosition.y = thisTransform.position.y;
}
Vector3 skeletonLossyScale = skeletonTransform.lossyScale;
Transform transformParent = thisTransform.parent;
Vector3 parentLossyScale = transformParent != null ? transformParent.lossyScale : Vector3.one;
if (followBoneRotation) {
float boneWorldRotation = bone.WorldRotationX;
if ((skeletonLossyScale.x * skeletonLossyScale.y) < 0)
boneWorldRotation = -boneWorldRotation;
if (followSkeletonFlip || maintainedAxisOrientation == AxisOrientation.XAxis) {
if ((skeletonLossyScale.x * parentLossyScale.x < 0))
boneWorldRotation += 180f;
} else {
if ((skeletonLossyScale.y * parentLossyScale.y < 0))
boneWorldRotation += 180f;
}
Vector3 worldRotation = skeletonTransform.rotation.eulerAngles;
if (followLocalScale && bone.ScaleX < 0) boneWorldRotation += 180f;
thisTransform.SetPositionAndRotation(targetWorldPosition, Quaternion.Euler(worldRotation.x, worldRotation.y, worldRotation.z + boneWorldRotation));
} else {
thisTransform.position = targetWorldPosition;
}
additionalFlipScale = Mathf.Sign(skeletonLossyScale.x * parentLossyScale.x
* skeletonLossyScale.y * parentLossyScale.y);
}
Bone parentBone = bone.Parent;
Vector3 localScale = new Vector3(1f, 1f, 1f);
if (followParentWorldScale && parentBone != null)
localScale = new Vector3(parentBone.WorldScaleX, parentBone.WorldScaleY, 1f);
if (followLocalScale)
localScale.Scale(new Vector3(bone.ScaleX, bone.ScaleY, 1f));
if (followSkeletonFlip)
localScale.y *= Mathf.Sign(bone.Skeleton.ScaleX * bone.Skeleton.ScaleY) * additionalFlipScale;
thisTransform.localScale = localScale;
}
}
}

View File

@@ -1,250 +1,250 @@
/******************************************************************************
* 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.
*****************************************************************************/
#if UNITY_2018_3 || UNITY_2019 || UNITY_2018_3_OR_NEWER
#define NEW_PREFAB_SYSTEM
#endif
using System.Collections.Generic;
using UnityEngine;
namespace Spine.Unity {
#if NEW_PREFAB_SYSTEM
[ExecuteAlways]
#else
[ExecuteInEditMode]
#endif
[HelpURL("http://esotericsoftware.com/spine-unity#BoundingBoxFollower")]
public class BoundingBoxFollower : MonoBehaviour {
internal static bool DebugMessages = true;
#region Inspector
public SkeletonRenderer skeletonRenderer;
[SpineSlot(dataField: "skeletonRenderer", containsBoundingBoxes: true)]
public string slotName;
public bool isTrigger, usedByEffector, usedByComposite;
public bool clearStateOnDisable = true;
#endregion
Slot slot;
BoundingBoxAttachment currentAttachment;
string currentAttachmentName;
PolygonCollider2D currentCollider;
public readonly Dictionary<BoundingBoxAttachment, PolygonCollider2D> colliderTable = new Dictionary<BoundingBoxAttachment, PolygonCollider2D>();
public readonly Dictionary<BoundingBoxAttachment, string> nameTable = new Dictionary<BoundingBoxAttachment, string>();
public Slot Slot { get { return slot; } }
public BoundingBoxAttachment CurrentAttachment { get { return currentAttachment; } }
public string CurrentAttachmentName { get { return currentAttachmentName; } }
public PolygonCollider2D CurrentCollider { get { return currentCollider; } }
public bool IsTrigger { get { return isTrigger; } }
void Start () {
Initialize();
}
void OnEnable () {
if (skeletonRenderer != null) {
skeletonRenderer.OnRebuild -= HandleRebuild;
skeletonRenderer.OnRebuild += HandleRebuild;
}
Initialize();
}
void HandleRebuild (SkeletonRenderer sr) {
//if (BoundingBoxFollower.DebugMessages) Debug.Log("Skeleton was rebuilt. Repopulating BoundingBoxFollower.");
Initialize();
}
/// <summary>
/// Initialize and instantiate the BoundingBoxFollower colliders. This is method checks if the BoundingBoxFollower has already been initialized for the skeleton instance and slotName and prevents overwriting unless it detects a new setup.</summary>
public void Initialize (bool overwrite = false) {
if (skeletonRenderer == null)
return;
skeletonRenderer.Initialize(false);
if (string.IsNullOrEmpty(slotName))
return;
// Don't reinitialize if the setup did not change.
if (!overwrite &&
colliderTable.Count > 0 && slot != null && // Slot is set and colliders already populated.
skeletonRenderer.skeleton == slot.Skeleton && // Skeleton object did not change.
slotName == slot.Data.Name // Slot object did not change.
)
return;
slot = null;
currentAttachment = null;
currentAttachmentName = null;
currentCollider = null;
colliderTable.Clear();
nameTable.Clear();
var skeleton = skeletonRenderer.skeleton;
if (skeleton == null)
return;
slot = skeleton.FindSlot(slotName);
if (slot == null) {
if (BoundingBoxFollower.DebugMessages)
Debug.LogWarning(string.Format("Slot '{0}' not found for BoundingBoxFollower on '{1}'. (Previous colliders were disposed.)", slotName, this.gameObject.name));
return;
}
int slotIndex = slot.Data.Index;
int requiredCollidersCount = 0;
var colliders = GetComponents<PolygonCollider2D>();
if (this.gameObject.activeInHierarchy) {
foreach (var skin in skeleton.Data.Skins)
AddCollidersForSkin(skin, slotIndex, colliders, ref requiredCollidersCount);
if (skeleton.Skin != null)
AddCollidersForSkin(skeleton.Skin, slotIndex, colliders, ref requiredCollidersCount);
}
DisposeExcessCollidersAfter(requiredCollidersCount);
if (BoundingBoxFollower.DebugMessages) {
bool valid = colliderTable.Count != 0;
if (!valid) {
if (this.gameObject.activeInHierarchy)
Debug.LogWarning("Bounding Box Follower not valid! Slot [" + slotName + "] does not contain any Bounding Box Attachments!");
else
Debug.LogWarning("Bounding Box Follower tried to rebuild as a prefab.");
}
}
}
void AddCollidersForSkin (Skin skin, int slotIndex, PolygonCollider2D[] previousColliders, ref int collidersCount) {
if (skin == null) return;
var skinEntries = new List<Skin.SkinEntry>();
skin.GetAttachments(slotIndex, skinEntries);
foreach (var entry in skinEntries) {
var attachment = skin.GetAttachment(slotIndex, entry.Name);
var boundingBoxAttachment = attachment as BoundingBoxAttachment;
if (BoundingBoxFollower.DebugMessages && attachment != null && boundingBoxAttachment == null)
Debug.Log("BoundingBoxFollower tried to follow a slot that contains non-boundingbox attachments: " + slotName);
if (boundingBoxAttachment != null) {
if (!colliderTable.ContainsKey(boundingBoxAttachment)) {
var bbCollider = collidersCount < previousColliders.Length ?
previousColliders[collidersCount] : gameObject.AddComponent<PolygonCollider2D>();
++collidersCount;
SkeletonUtility.SetColliderPointsLocal(bbCollider, slot, boundingBoxAttachment);
bbCollider.isTrigger = isTrigger;
bbCollider.usedByEffector = usedByEffector;
bbCollider.usedByComposite = usedByComposite;
bbCollider.enabled = false;
bbCollider.hideFlags = HideFlags.NotEditable;
colliderTable.Add(boundingBoxAttachment, bbCollider);
nameTable.Add(boundingBoxAttachment, entry.Name);
}
}
}
}
void OnDisable () {
if (clearStateOnDisable)
ClearState();
if (skeletonRenderer != null)
skeletonRenderer.OnRebuild -= HandleRebuild;
}
public void ClearState () {
if (colliderTable != null)
foreach (var col in colliderTable.Values)
col.enabled = false;
currentAttachment = null;
currentAttachmentName = null;
currentCollider = null;
}
void DisposeExcessCollidersAfter (int requiredCount) {
var colliders = GetComponents<PolygonCollider2D>();
if (colliders.Length == 0) return;
for (int i = requiredCount; i < colliders.Length; ++i) {
var collider = colliders[i];
if (collider != null) {
#if UNITY_EDITOR
if (Application.isEditor && !Application.isPlaying)
DestroyImmediate(collider);
else
#endif
Destroy(collider);
}
}
}
void LateUpdate () {
if (slot != null && slot.Attachment != currentAttachment)
MatchAttachment(slot.Attachment);
}
/// <summary>Sets the current collider to match attachment.</summary>
/// <param name="attachment">If the attachment is not a bounding box, it will be treated as null.</param>
void MatchAttachment (Attachment attachment) {
var bbAttachment = attachment as BoundingBoxAttachment;
if (BoundingBoxFollower.DebugMessages && attachment != null && bbAttachment == null)
Debug.LogWarning("BoundingBoxFollower tried to match a non-boundingbox attachment. It will treat it as null.");
if (currentCollider != null)
currentCollider.enabled = false;
if (bbAttachment == null) {
currentCollider = null;
currentAttachment = null;
currentAttachmentName = null;
} else {
PolygonCollider2D foundCollider;
colliderTable.TryGetValue(bbAttachment, out foundCollider);
if (foundCollider != null) {
currentCollider = foundCollider;
currentCollider.enabled = true;
currentAttachment = bbAttachment;
currentAttachmentName = nameTable[bbAttachment];
} else {
currentCollider = null;
currentAttachment = bbAttachment;
currentAttachmentName = null;
if (BoundingBoxFollower.DebugMessages) Debug.LogFormat("Collider for BoundingBoxAttachment named '{0}' was not initialized. It is possibly from a new skin. currentAttachmentName will be null. You may need to call BoundingBoxFollower.Initialize(overwrite: true);", bbAttachment.Name);
}
}
}
}
}
/******************************************************************************
* 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.
*****************************************************************************/
#if UNITY_2018_3 || UNITY_2019 || UNITY_2018_3_OR_NEWER
#define NEW_PREFAB_SYSTEM
#endif
using System.Collections.Generic;
using UnityEngine;
namespace Spine.Unity {
#if NEW_PREFAB_SYSTEM
[ExecuteAlways]
#else
[ExecuteInEditMode]
#endif
[HelpURL("http://esotericsoftware.com/spine-unity#BoundingBoxFollower")]
public class BoundingBoxFollower : MonoBehaviour {
internal static bool DebugMessages = true;
#region Inspector
public SkeletonRenderer skeletonRenderer;
[SpineSlot(dataField: "skeletonRenderer", containsBoundingBoxes: true)]
public string slotName;
public bool isTrigger, usedByEffector, usedByComposite;
public bool clearStateOnDisable = true;
#endregion
Slot slot;
BoundingBoxAttachment currentAttachment;
string currentAttachmentName;
PolygonCollider2D currentCollider;
public readonly Dictionary<BoundingBoxAttachment, PolygonCollider2D> colliderTable = new Dictionary<BoundingBoxAttachment, PolygonCollider2D>();
public readonly Dictionary<BoundingBoxAttachment, string> nameTable = new Dictionary<BoundingBoxAttachment, string>();
public Slot Slot { get { return slot; } }
public BoundingBoxAttachment CurrentAttachment { get { return currentAttachment; } }
public string CurrentAttachmentName { get { return currentAttachmentName; } }
public PolygonCollider2D CurrentCollider { get { return currentCollider; } }
public bool IsTrigger { get { return isTrigger; } }
void Start () {
Initialize();
}
void OnEnable () {
if (skeletonRenderer != null) {
skeletonRenderer.OnRebuild -= HandleRebuild;
skeletonRenderer.OnRebuild += HandleRebuild;
}
Initialize();
}
void HandleRebuild (SkeletonRenderer sr) {
//if (BoundingBoxFollower.DebugMessages) Debug.Log("Skeleton was rebuilt. Repopulating BoundingBoxFollower.");
Initialize();
}
/// <summary>
/// Initialize and instantiate the BoundingBoxFollower colliders. This is method checks if the BoundingBoxFollower has already been initialized for the skeleton instance and slotName and prevents overwriting unless it detects a new setup.</summary>
public void Initialize (bool overwrite = false) {
if (skeletonRenderer == null)
return;
skeletonRenderer.Initialize(false);
if (string.IsNullOrEmpty(slotName))
return;
// Don't reinitialize if the setup did not change.
if (!overwrite &&
colliderTable.Count > 0 && slot != null && // Slot is set and colliders already populated.
skeletonRenderer.skeleton == slot.Skeleton && // Skeleton object did not change.
slotName == slot.Data.Name // Slot object did not change.
)
return;
slot = null;
currentAttachment = null;
currentAttachmentName = null;
currentCollider = null;
colliderTable.Clear();
nameTable.Clear();
var skeleton = skeletonRenderer.skeleton;
if (skeleton == null)
return;
slot = skeleton.FindSlot(slotName);
if (slot == null) {
if (BoundingBoxFollower.DebugMessages)
Debug.LogWarning(string.Format("Slot '{0}' not found for BoundingBoxFollower on '{1}'. (Previous colliders were disposed.)", slotName, this.gameObject.name));
return;
}
int slotIndex = slot.Data.Index;
int requiredCollidersCount = 0;
var colliders = GetComponents<PolygonCollider2D>();
if (this.gameObject.activeInHierarchy) {
foreach (var skin in skeleton.Data.Skins)
AddCollidersForSkin(skin, slotIndex, colliders, ref requiredCollidersCount);
if (skeleton.Skin != null)
AddCollidersForSkin(skeleton.Skin, slotIndex, colliders, ref requiredCollidersCount);
}
DisposeExcessCollidersAfter(requiredCollidersCount);
if (BoundingBoxFollower.DebugMessages) {
bool valid = colliderTable.Count != 0;
if (!valid) {
if (this.gameObject.activeInHierarchy)
Debug.LogWarning("Bounding Box Follower not valid! Slot [" + slotName + "] does not contain any Bounding Box Attachments!");
else
Debug.LogWarning("Bounding Box Follower tried to rebuild as a prefab.");
}
}
}
void AddCollidersForSkin (Skin skin, int slotIndex, PolygonCollider2D[] previousColliders, ref int collidersCount) {
if (skin == null) return;
var skinEntries = new List<Skin.SkinEntry>();
skin.GetAttachments(slotIndex, skinEntries);
foreach (var entry in skinEntries) {
var attachment = skin.GetAttachment(slotIndex, entry.Name);
var boundingBoxAttachment = attachment as BoundingBoxAttachment;
if (BoundingBoxFollower.DebugMessages && attachment != null && boundingBoxAttachment == null)
Debug.Log("BoundingBoxFollower tried to follow a slot that contains non-boundingbox attachments: " + slotName);
if (boundingBoxAttachment != null) {
if (!colliderTable.ContainsKey(boundingBoxAttachment)) {
var bbCollider = collidersCount < previousColliders.Length ?
previousColliders[collidersCount] : gameObject.AddComponent<PolygonCollider2D>();
++collidersCount;
SkeletonUtility.SetColliderPointsLocal(bbCollider, slot, boundingBoxAttachment);
bbCollider.isTrigger = isTrigger;
bbCollider.usedByEffector = usedByEffector;
bbCollider.usedByComposite = usedByComposite;
bbCollider.enabled = false;
bbCollider.hideFlags = HideFlags.NotEditable;
colliderTable.Add(boundingBoxAttachment, bbCollider);
nameTable.Add(boundingBoxAttachment, entry.Name);
}
}
}
}
void OnDisable () {
if (clearStateOnDisable)
ClearState();
if (skeletonRenderer != null)
skeletonRenderer.OnRebuild -= HandleRebuild;
}
public void ClearState () {
if (colliderTable != null)
foreach (var col in colliderTable.Values)
col.enabled = false;
currentAttachment = null;
currentAttachmentName = null;
currentCollider = null;
}
void DisposeExcessCollidersAfter (int requiredCount) {
var colliders = GetComponents<PolygonCollider2D>();
if (colliders.Length == 0) return;
for (int i = requiredCount; i < colliders.Length; ++i) {
var collider = colliders[i];
if (collider != null) {
#if UNITY_EDITOR
if (Application.isEditor && !Application.isPlaying)
DestroyImmediate(collider);
else
#endif
Destroy(collider);
}
}
}
void LateUpdate () {
if (slot != null && slot.Attachment != currentAttachment)
MatchAttachment(slot.Attachment);
}
/// <summary>Sets the current collider to match attachment.</summary>
/// <param name="attachment">If the attachment is not a bounding box, it will be treated as null.</param>
void MatchAttachment (Attachment attachment) {
var bbAttachment = attachment as BoundingBoxAttachment;
if (BoundingBoxFollower.DebugMessages && attachment != null && bbAttachment == null)
Debug.LogWarning("BoundingBoxFollower tried to match a non-boundingbox attachment. It will treat it as null.");
if (currentCollider != null)
currentCollider.enabled = false;
if (bbAttachment == null) {
currentCollider = null;
currentAttachment = null;
currentAttachmentName = null;
} else {
PolygonCollider2D foundCollider;
colliderTable.TryGetValue(bbAttachment, out foundCollider);
if (foundCollider != null) {
currentCollider = foundCollider;
currentCollider.enabled = true;
currentAttachment = bbAttachment;
currentAttachmentName = nameTable[bbAttachment];
} else {
currentCollider = null;
currentAttachment = bbAttachment;
currentAttachmentName = null;
if (BoundingBoxFollower.DebugMessages) Debug.LogFormat("Collider for BoundingBoxAttachment named '{0}' was not initialized. It is possibly from a new skin. currentAttachmentName will be null. You may need to call BoundingBoxFollower.Initialize(overwrite: true);", bbAttachment.Name);
}
}
}
}
}

View File

@@ -1,254 +1,254 @@
/******************************************************************************
* 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.
*****************************************************************************/
#if UNITY_2018_3 || UNITY_2019 || UNITY_2018_3_OR_NEWER
#define NEW_PREFAB_SYSTEM
#endif
using System.Collections.Generic;
using UnityEngine;
namespace Spine.Unity {
#if NEW_PREFAB_SYSTEM
[ExecuteAlways]
#else
[ExecuteInEditMode]
#endif
[HelpURL("http://esotericsoftware.com/spine-unity#BoundingBoxFollowerGraphic")]
public class BoundingBoxFollowerGraphic : MonoBehaviour {
internal static bool DebugMessages = true;
#region Inspector
public SkeletonGraphic skeletonGraphic;
[SpineSlot(dataField: "skeletonGraphic", containsBoundingBoxes: true)]
public string slotName;
public bool isTrigger, usedByEffector, usedByComposite;
public bool clearStateOnDisable = true;
#endregion
Slot slot;
BoundingBoxAttachment currentAttachment;
string currentAttachmentName;
PolygonCollider2D currentCollider;
public readonly Dictionary<BoundingBoxAttachment, PolygonCollider2D> colliderTable = new Dictionary<BoundingBoxAttachment, PolygonCollider2D>();
public readonly Dictionary<BoundingBoxAttachment, string> nameTable = new Dictionary<BoundingBoxAttachment, string>();
public Slot Slot { get { return slot; } }
public BoundingBoxAttachment CurrentAttachment { get { return currentAttachment; } }
public string CurrentAttachmentName { get { return currentAttachmentName; } }
public PolygonCollider2D CurrentCollider { get { return currentCollider; } }
public bool IsTrigger { get { return isTrigger; } }
void Start () {
Initialize();
}
void OnEnable () {
if (skeletonGraphic != null) {
skeletonGraphic.OnRebuild -= HandleRebuild;
skeletonGraphic.OnRebuild += HandleRebuild;
}
Initialize();
}
void HandleRebuild (SkeletonGraphic sr) {
//if (BoundingBoxFollowerGraphic.DebugMessages) Debug.Log("Skeleton was rebuilt. Repopulating BoundingBoxFollowerGraphic.");
Initialize();
}
/// <summary>
/// Initialize and instantiate the BoundingBoxFollowerGraphic colliders. This is method checks if the BoundingBoxFollowerGraphic has already been initialized for the skeleton instance and slotName and prevents overwriting unless it detects a new setup.</summary>
public void Initialize (bool overwrite = false) {
if (skeletonGraphic == null)
return;
skeletonGraphic.Initialize(false);
if (string.IsNullOrEmpty(slotName))
return;
// Don't reinitialize if the setup did not change.
if (!overwrite &&
colliderTable.Count > 0 && slot != null && // Slot is set and colliders already populated.
skeletonGraphic.Skeleton == slot.Skeleton && // Skeleton object did not change.
slotName == slot.Data.Name // Slot object did not change.
)
return;
slot = null;
currentAttachment = null;
currentAttachmentName = null;
currentCollider = null;
colliderTable.Clear();
nameTable.Clear();
var skeleton = skeletonGraphic.Skeleton;
if (skeleton == null)
return;
slot = skeleton.FindSlot(slotName);
if (slot == null) {
if (BoundingBoxFollowerGraphic.DebugMessages)
Debug.LogWarning(string.Format("Slot '{0}' not found for BoundingBoxFollowerGraphic on '{1}'. (Previous colliders were disposed.)", slotName, this.gameObject.name));
return;
}
int slotIndex = slot.Data.Index;
int requiredCollidersCount = 0;
var colliders = GetComponents<PolygonCollider2D>();
if (this.gameObject.activeInHierarchy) {
var canvas = skeletonGraphic.canvas;
if (canvas == null) canvas = skeletonGraphic.GetComponentInParent<Canvas>();
float scale = canvas != null ? canvas.referencePixelsPerUnit : 100.0f;
foreach (var skin in skeleton.Data.Skins)
AddCollidersForSkin(skin, slotIndex, colliders, scale, ref requiredCollidersCount);
if (skeleton.Skin != null)
AddCollidersForSkin(skeleton.Skin, slotIndex, colliders, scale, ref requiredCollidersCount);
}
DisposeExcessCollidersAfter(requiredCollidersCount);
if (BoundingBoxFollowerGraphic.DebugMessages) {
bool valid = colliderTable.Count != 0;
if (!valid) {
if (this.gameObject.activeInHierarchy)
Debug.LogWarning("Bounding Box Follower not valid! Slot [" + slotName + "] does not contain any Bounding Box Attachments!");
else
Debug.LogWarning("Bounding Box Follower tried to rebuild as a prefab.");
}
}
}
void AddCollidersForSkin (Skin skin, int slotIndex, PolygonCollider2D[] previousColliders, float scale, ref int collidersCount) {
if (skin == null) return;
var skinEntries = new List<Skin.SkinEntry>();
skin.GetAttachments(slotIndex, skinEntries);
foreach (var entry in skinEntries) {
var attachment = skin.GetAttachment(slotIndex, entry.Name);
var boundingBoxAttachment = attachment as BoundingBoxAttachment;
if (BoundingBoxFollowerGraphic.DebugMessages && attachment != null && boundingBoxAttachment == null)
Debug.Log("BoundingBoxFollowerGraphic tried to follow a slot that contains non-boundingbox attachments: " + slotName);
if (boundingBoxAttachment != null) {
if (!colliderTable.ContainsKey(boundingBoxAttachment)) {
var bbCollider = collidersCount < previousColliders.Length ?
previousColliders[collidersCount] : gameObject.AddComponent<PolygonCollider2D>();
++collidersCount;
SkeletonUtility.SetColliderPointsLocal(bbCollider, slot, boundingBoxAttachment, scale);
bbCollider.isTrigger = isTrigger;
bbCollider.usedByEffector = usedByEffector;
bbCollider.usedByComposite = usedByComposite;
bbCollider.enabled = false;
bbCollider.hideFlags = HideFlags.NotEditable;
colliderTable.Add(boundingBoxAttachment, bbCollider);
nameTable.Add(boundingBoxAttachment, entry.Name);
}
}
}
}
void OnDisable () {
if (clearStateOnDisable)
ClearState();
if (skeletonGraphic != null)
skeletonGraphic.OnRebuild -= HandleRebuild;
}
public void ClearState () {
if (colliderTable != null)
foreach (var col in colliderTable.Values)
col.enabled = false;
currentAttachment = null;
currentAttachmentName = null;
currentCollider = null;
}
void DisposeExcessCollidersAfter (int requiredCount) {
var colliders = GetComponents<PolygonCollider2D>();
if (colliders.Length == 0) return;
for (int i = requiredCount; i < colliders.Length; ++i) {
var collider = colliders[i];
if (collider != null) {
#if UNITY_EDITOR
if (Application.isEditor && !Application.isPlaying)
DestroyImmediate(collider);
else
#endif
Destroy(collider);
}
}
}
void LateUpdate () {
if (slot != null && slot.Attachment != currentAttachment)
MatchAttachment(slot.Attachment);
}
/// <summary>Sets the current collider to match attachment.</summary>
/// <param name="attachment">If the attachment is not a bounding box, it will be treated as null.</param>
void MatchAttachment (Attachment attachment) {
var bbAttachment = attachment as BoundingBoxAttachment;
if (BoundingBoxFollowerGraphic.DebugMessages && attachment != null && bbAttachment == null)
Debug.LogWarning("BoundingBoxFollowerGraphic tried to match a non-boundingbox attachment. It will treat it as null.");
if (currentCollider != null)
currentCollider.enabled = false;
if (bbAttachment == null) {
currentCollider = null;
currentAttachment = null;
currentAttachmentName = null;
} else {
PolygonCollider2D foundCollider;
colliderTable.TryGetValue(bbAttachment, out foundCollider);
if (foundCollider != null) {
currentCollider = foundCollider;
currentCollider.enabled = true;
currentAttachment = bbAttachment;
currentAttachmentName = nameTable[bbAttachment];
} else {
currentCollider = null;
currentAttachment = bbAttachment;
currentAttachmentName = null;
if (BoundingBoxFollowerGraphic.DebugMessages) Debug.LogFormat("Collider for BoundingBoxAttachment named '{0}' was not initialized. It is possibly from a new skin. currentAttachmentName will be null. You may need to call BoundingBoxFollowerGraphic.Initialize(overwrite: true);", bbAttachment.Name);
}
}
}
}
}
/******************************************************************************
* 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.
*****************************************************************************/
#if UNITY_2018_3 || UNITY_2019 || UNITY_2018_3_OR_NEWER
#define NEW_PREFAB_SYSTEM
#endif
using System.Collections.Generic;
using UnityEngine;
namespace Spine.Unity {
#if NEW_PREFAB_SYSTEM
[ExecuteAlways]
#else
[ExecuteInEditMode]
#endif
[HelpURL("http://esotericsoftware.com/spine-unity#BoundingBoxFollowerGraphic")]
public class BoundingBoxFollowerGraphic : MonoBehaviour {
internal static bool DebugMessages = true;
#region Inspector
public SkeletonGraphic skeletonGraphic;
[SpineSlot(dataField: "skeletonGraphic", containsBoundingBoxes: true)]
public string slotName;
public bool isTrigger, usedByEffector, usedByComposite;
public bool clearStateOnDisable = true;
#endregion
Slot slot;
BoundingBoxAttachment currentAttachment;
string currentAttachmentName;
PolygonCollider2D currentCollider;
public readonly Dictionary<BoundingBoxAttachment, PolygonCollider2D> colliderTable = new Dictionary<BoundingBoxAttachment, PolygonCollider2D>();
public readonly Dictionary<BoundingBoxAttachment, string> nameTable = new Dictionary<BoundingBoxAttachment, string>();
public Slot Slot { get { return slot; } }
public BoundingBoxAttachment CurrentAttachment { get { return currentAttachment; } }
public string CurrentAttachmentName { get { return currentAttachmentName; } }
public PolygonCollider2D CurrentCollider { get { return currentCollider; } }
public bool IsTrigger { get { return isTrigger; } }
void Start () {
Initialize();
}
void OnEnable () {
if (skeletonGraphic != null) {
skeletonGraphic.OnRebuild -= HandleRebuild;
skeletonGraphic.OnRebuild += HandleRebuild;
}
Initialize();
}
void HandleRebuild (SkeletonGraphic sr) {
//if (BoundingBoxFollowerGraphic.DebugMessages) Debug.Log("Skeleton was rebuilt. Repopulating BoundingBoxFollowerGraphic.");
Initialize();
}
/// <summary>
/// Initialize and instantiate the BoundingBoxFollowerGraphic colliders. This is method checks if the BoundingBoxFollowerGraphic has already been initialized for the skeleton instance and slotName and prevents overwriting unless it detects a new setup.</summary>
public void Initialize (bool overwrite = false) {
if (skeletonGraphic == null)
return;
skeletonGraphic.Initialize(false);
if (string.IsNullOrEmpty(slotName))
return;
// Don't reinitialize if the setup did not change.
if (!overwrite &&
colliderTable.Count > 0 && slot != null && // Slot is set and colliders already populated.
skeletonGraphic.Skeleton == slot.Skeleton && // Skeleton object did not change.
slotName == slot.Data.Name // Slot object did not change.
)
return;
slot = null;
currentAttachment = null;
currentAttachmentName = null;
currentCollider = null;
colliderTable.Clear();
nameTable.Clear();
var skeleton = skeletonGraphic.Skeleton;
if (skeleton == null)
return;
slot = skeleton.FindSlot(slotName);
if (slot == null) {
if (BoundingBoxFollowerGraphic.DebugMessages)
Debug.LogWarning(string.Format("Slot '{0}' not found for BoundingBoxFollowerGraphic on '{1}'. (Previous colliders were disposed.)", slotName, this.gameObject.name));
return;
}
int slotIndex = slot.Data.Index;
int requiredCollidersCount = 0;
var colliders = GetComponents<PolygonCollider2D>();
if (this.gameObject.activeInHierarchy) {
var canvas = skeletonGraphic.canvas;
if (canvas == null) canvas = skeletonGraphic.GetComponentInParent<Canvas>();
float scale = canvas != null ? canvas.referencePixelsPerUnit : 100.0f;
foreach (var skin in skeleton.Data.Skins)
AddCollidersForSkin(skin, slotIndex, colliders, scale, ref requiredCollidersCount);
if (skeleton.Skin != null)
AddCollidersForSkin(skeleton.Skin, slotIndex, colliders, scale, ref requiredCollidersCount);
}
DisposeExcessCollidersAfter(requiredCollidersCount);
if (BoundingBoxFollowerGraphic.DebugMessages) {
bool valid = colliderTable.Count != 0;
if (!valid) {
if (this.gameObject.activeInHierarchy)
Debug.LogWarning("Bounding Box Follower not valid! Slot [" + slotName + "] does not contain any Bounding Box Attachments!");
else
Debug.LogWarning("Bounding Box Follower tried to rebuild as a prefab.");
}
}
}
void AddCollidersForSkin (Skin skin, int slotIndex, PolygonCollider2D[] previousColliders, float scale, ref int collidersCount) {
if (skin == null) return;
var skinEntries = new List<Skin.SkinEntry>();
skin.GetAttachments(slotIndex, skinEntries);
foreach (var entry in skinEntries) {
var attachment = skin.GetAttachment(slotIndex, entry.Name);
var boundingBoxAttachment = attachment as BoundingBoxAttachment;
if (BoundingBoxFollowerGraphic.DebugMessages && attachment != null && boundingBoxAttachment == null)
Debug.Log("BoundingBoxFollowerGraphic tried to follow a slot that contains non-boundingbox attachments: " + slotName);
if (boundingBoxAttachment != null) {
if (!colliderTable.ContainsKey(boundingBoxAttachment)) {
var bbCollider = collidersCount < previousColliders.Length ?
previousColliders[collidersCount] : gameObject.AddComponent<PolygonCollider2D>();
++collidersCount;
SkeletonUtility.SetColliderPointsLocal(bbCollider, slot, boundingBoxAttachment, scale);
bbCollider.isTrigger = isTrigger;
bbCollider.usedByEffector = usedByEffector;
bbCollider.usedByComposite = usedByComposite;
bbCollider.enabled = false;
bbCollider.hideFlags = HideFlags.NotEditable;
colliderTable.Add(boundingBoxAttachment, bbCollider);
nameTable.Add(boundingBoxAttachment, entry.Name);
}
}
}
}
void OnDisable () {
if (clearStateOnDisable)
ClearState();
if (skeletonGraphic != null)
skeletonGraphic.OnRebuild -= HandleRebuild;
}
public void ClearState () {
if (colliderTable != null)
foreach (var col in colliderTable.Values)
col.enabled = false;
currentAttachment = null;
currentAttachmentName = null;
currentCollider = null;
}
void DisposeExcessCollidersAfter (int requiredCount) {
var colliders = GetComponents<PolygonCollider2D>();
if (colliders.Length == 0) return;
for (int i = requiredCount; i < colliders.Length; ++i) {
var collider = colliders[i];
if (collider != null) {
#if UNITY_EDITOR
if (Application.isEditor && !Application.isPlaying)
DestroyImmediate(collider);
else
#endif
Destroy(collider);
}
}
}
void LateUpdate () {
if (slot != null && slot.Attachment != currentAttachment)
MatchAttachment(slot.Attachment);
}
/// <summary>Sets the current collider to match attachment.</summary>
/// <param name="attachment">If the attachment is not a bounding box, it will be treated as null.</param>
void MatchAttachment (Attachment attachment) {
var bbAttachment = attachment as BoundingBoxAttachment;
if (BoundingBoxFollowerGraphic.DebugMessages && attachment != null && bbAttachment == null)
Debug.LogWarning("BoundingBoxFollowerGraphic tried to match a non-boundingbox attachment. It will treat it as null.");
if (currentCollider != null)
currentCollider.enabled = false;
if (bbAttachment == null) {
currentCollider = null;
currentAttachment = null;
currentAttachmentName = null;
} else {
PolygonCollider2D foundCollider;
colliderTable.TryGetValue(bbAttachment, out foundCollider);
if (foundCollider != null) {
currentCollider = foundCollider;
currentCollider.enabled = true;
currentAttachment = bbAttachment;
currentAttachmentName = nameTable[bbAttachment];
} else {
currentCollider = null;
currentAttachment = bbAttachment;
currentAttachmentName = null;
if (BoundingBoxFollowerGraphic.DebugMessages) Debug.LogFormat("Collider for BoundingBoxAttachment named '{0}' was not initialized. It is possibly from a new skin. currentAttachmentName will be null. You may need to call BoundingBoxFollowerGraphic.Initialize(overwrite: true);", bbAttachment.Name);
}
}
}
}
}

View File

@@ -1,164 +1,164 @@
/******************************************************************************
* 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.
*****************************************************************************/
#if UNITY_2018_3 || UNITY_2019 || UNITY_2018_3_OR_NEWER
#define NEW_PREFAB_SYSTEM
#endif
using UnityEngine;
namespace Spine.Unity {
#if NEW_PREFAB_SYSTEM
[ExecuteAlways]
#else
[ExecuteInEditMode]
#endif
[AddComponentMenu("Spine/Point Follower")]
[HelpURL("http://esotericsoftware.com/spine-unity#PointFollower")]
public class PointFollower : MonoBehaviour, IHasSkeletonRenderer, IHasSkeletonComponent {
public SkeletonRenderer skeletonRenderer;
public SkeletonRenderer SkeletonRenderer { get { return this.skeletonRenderer; } }
public ISkeletonComponent SkeletonComponent { get { return skeletonRenderer as ISkeletonComponent; } }
[SpineSlot(dataField: "skeletonRenderer", includeNone: true)]
public string slotName;
[SpineAttachment(slotField: "slotName", dataField: "skeletonRenderer", fallbackToTextField: true, includeNone: true)]
public string pointAttachmentName;
public bool followRotation = true;
public bool followSkeletonFlip = true;
public bool followSkeletonZPosition = false;
Transform skeletonTransform;
bool skeletonTransformIsParent;
PointAttachment point;
Bone bone;
bool valid;
public bool IsValid { get { return valid; } }
public void Initialize () {
valid = skeletonRenderer != null && skeletonRenderer.valid;
if (!valid)
return;
UpdateReferences();
#if UNITY_EDITOR
if (Application.isEditor) LateUpdate();
#endif
}
private void HandleRebuildRenderer (SkeletonRenderer skeletonRenderer) {
Initialize();
}
void UpdateReferences () {
skeletonTransform = skeletonRenderer.transform;
skeletonRenderer.OnRebuild -= HandleRebuildRenderer;
skeletonRenderer.OnRebuild += HandleRebuildRenderer;
skeletonTransformIsParent = Transform.ReferenceEquals(skeletonTransform, transform.parent);
bone = null;
point = null;
if (!string.IsNullOrEmpty(pointAttachmentName)) {
var skeleton = skeletonRenderer.Skeleton;
Slot slot = skeleton.FindSlot(slotName);
if (slot != null) {
int slotIndex = slot.Data.Index;
bone = slot.Bone;
point = skeleton.GetAttachment(slotIndex, pointAttachmentName) as PointAttachment;
}
}
}
void OnDestroy () {
if (skeletonRenderer != null)
skeletonRenderer.OnRebuild -= HandleRebuildRenderer;
}
public void LateUpdate () {
#if UNITY_EDITOR
if (!Application.isPlaying) skeletonTransformIsParent = Transform.ReferenceEquals(skeletonTransform, transform.parent);
#endif
if (point == null) {
if (string.IsNullOrEmpty(pointAttachmentName)) return;
UpdateReferences();
if (point == null) return;
}
Vector2 worldPos;
point.ComputeWorldPosition(bone, out worldPos.x, out worldPos.y);
float rotation = point.ComputeWorldRotation(bone);
Transform thisTransform = this.transform;
if (skeletonTransformIsParent) {
// Recommended setup: Use local transform properties if Spine GameObject is the immediate parent
thisTransform.localPosition = new Vector3(worldPos.x, worldPos.y, followSkeletonZPosition ? 0f : thisTransform.localPosition.z);
if (followRotation) {
float halfRotation = rotation * 0.5f * Mathf.Deg2Rad;
var q = default(Quaternion);
q.z = Mathf.Sin(halfRotation);
q.w = Mathf.Cos(halfRotation);
thisTransform.localRotation = q;
}
} else {
// For special cases: Use transform world properties if transform relationship is complicated
Vector3 targetWorldPosition = skeletonTransform.TransformPoint(new Vector3(worldPos.x, worldPos.y, 0f));
if (!followSkeletonZPosition)
targetWorldPosition.z = thisTransform.position.z;
Transform transformParent = thisTransform.parent;
if (transformParent != null) {
Matrix4x4 m = transformParent.localToWorldMatrix;
if (m.m00 * m.m11 - m.m01 * m.m10 < 0) // Determinant2D is negative
rotation = -rotation;
}
if (followRotation) {
Vector3 transformWorldRotation = skeletonTransform.rotation.eulerAngles;
thisTransform.SetPositionAndRotation(targetWorldPosition, Quaternion.Euler(transformWorldRotation.x, transformWorldRotation.y, transformWorldRotation.z + rotation));
} else {
thisTransform.position = targetWorldPosition;
}
}
if (followSkeletonFlip) {
Vector3 localScale = thisTransform.localScale;
localScale.y = Mathf.Abs(localScale.y) * Mathf.Sign(bone.Skeleton.ScaleX * bone.Skeleton.ScaleY);
thisTransform.localScale = localScale;
}
}
}
}
/******************************************************************************
* 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.
*****************************************************************************/
#if UNITY_2018_3 || UNITY_2019 || UNITY_2018_3_OR_NEWER
#define NEW_PREFAB_SYSTEM
#endif
using UnityEngine;
namespace Spine.Unity {
#if NEW_PREFAB_SYSTEM
[ExecuteAlways]
#else
[ExecuteInEditMode]
#endif
[AddComponentMenu("Spine/Point Follower")]
[HelpURL("http://esotericsoftware.com/spine-unity#PointFollower")]
public class PointFollower : MonoBehaviour, IHasSkeletonRenderer, IHasSkeletonComponent {
public SkeletonRenderer skeletonRenderer;
public SkeletonRenderer SkeletonRenderer { get { return this.skeletonRenderer; } }
public ISkeletonComponent SkeletonComponent { get { return skeletonRenderer as ISkeletonComponent; } }
[SpineSlot(dataField: "skeletonRenderer", includeNone: true)]
public string slotName;
[SpineAttachment(slotField: "slotName", dataField: "skeletonRenderer", fallbackToTextField: true, includeNone: true)]
public string pointAttachmentName;
public bool followRotation = true;
public bool followSkeletonFlip = true;
public bool followSkeletonZPosition = false;
Transform skeletonTransform;
bool skeletonTransformIsParent;
PointAttachment point;
Bone bone;
bool valid;
public bool IsValid { get { return valid; } }
public void Initialize () {
valid = skeletonRenderer != null && skeletonRenderer.valid;
if (!valid)
return;
UpdateReferences();
#if UNITY_EDITOR
if (Application.isEditor) LateUpdate();
#endif
}
private void HandleRebuildRenderer (SkeletonRenderer skeletonRenderer) {
Initialize();
}
void UpdateReferences () {
skeletonTransform = skeletonRenderer.transform;
skeletonRenderer.OnRebuild -= HandleRebuildRenderer;
skeletonRenderer.OnRebuild += HandleRebuildRenderer;
skeletonTransformIsParent = Transform.ReferenceEquals(skeletonTransform, transform.parent);
bone = null;
point = null;
if (!string.IsNullOrEmpty(pointAttachmentName)) {
var skeleton = skeletonRenderer.Skeleton;
Slot slot = skeleton.FindSlot(slotName);
if (slot != null) {
int slotIndex = slot.Data.Index;
bone = slot.Bone;
point = skeleton.GetAttachment(slotIndex, pointAttachmentName) as PointAttachment;
}
}
}
void OnDestroy () {
if (skeletonRenderer != null)
skeletonRenderer.OnRebuild -= HandleRebuildRenderer;
}
public void LateUpdate () {
#if UNITY_EDITOR
if (!Application.isPlaying) skeletonTransformIsParent = Transform.ReferenceEquals(skeletonTransform, transform.parent);
#endif
if (point == null) {
if (string.IsNullOrEmpty(pointAttachmentName)) return;
UpdateReferences();
if (point == null) return;
}
Vector2 worldPos;
point.ComputeWorldPosition(bone, out worldPos.x, out worldPos.y);
float rotation = point.ComputeWorldRotation(bone);
Transform thisTransform = this.transform;
if (skeletonTransformIsParent) {
// Recommended setup: Use local transform properties if Spine GameObject is the immediate parent
thisTransform.localPosition = new Vector3(worldPos.x, worldPos.y, followSkeletonZPosition ? 0f : thisTransform.localPosition.z);
if (followRotation) {
float halfRotation = rotation * 0.5f * Mathf.Deg2Rad;
var q = default(Quaternion);
q.z = Mathf.Sin(halfRotation);
q.w = Mathf.Cos(halfRotation);
thisTransform.localRotation = q;
}
} else {
// For special cases: Use transform world properties if transform relationship is complicated
Vector3 targetWorldPosition = skeletonTransform.TransformPoint(new Vector3(worldPos.x, worldPos.y, 0f));
if (!followSkeletonZPosition)
targetWorldPosition.z = thisTransform.position.z;
Transform transformParent = thisTransform.parent;
if (transformParent != null) {
Matrix4x4 m = transformParent.localToWorldMatrix;
if (m.m00 * m.m11 - m.m01 * m.m10 < 0) // Determinant2D is negative
rotation = -rotation;
}
if (followRotation) {
Vector3 transformWorldRotation = skeletonTransform.rotation.eulerAngles;
thisTransform.SetPositionAndRotation(targetWorldPosition, Quaternion.Euler(transformWorldRotation.x, transformWorldRotation.y, transformWorldRotation.z + rotation));
} else {
thisTransform.position = targetWorldPosition;
}
}
if (followSkeletonFlip) {
Vector3 localScale = thisTransform.localScale;
localScale.y = Mathf.Abs(localScale.y) * Mathf.Sign(bone.Skeleton.ScaleX * bone.Skeleton.ScaleY);
thisTransform.localScale = localScale;
}
}
}
}

View File

@@ -1,47 +1,47 @@
/******************************************************************************
* 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.
*****************************************************************************/
using UnityEngine;
using UnityEngine.UI;
namespace Spine.Unity {
/// <summary>
/// A minimal MaskableGraphic subclass for rendering multiple submeshes
/// at a <see cref="SkeletonGraphic"/>.
/// </summary>
[RequireComponent(typeof(CanvasRenderer))]
public class SkeletonSubmeshGraphic : MaskableGraphic {
public override void SetMaterialDirty () { }
public override void SetVerticesDirty () { }
protected override void OnPopulateMesh (VertexHelper vh) {
vh.Clear();
}
}
}
/******************************************************************************
* 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.
*****************************************************************************/
using UnityEngine;
using UnityEngine.UI;
namespace Spine.Unity {
/// <summary>
/// A minimal MaskableGraphic subclass for rendering multiple submeshes
/// at a <see cref="SkeletonGraphic"/>.
/// </summary>
[RequireComponent(typeof(CanvasRenderer))]
public class SkeletonSubmeshGraphic : MaskableGraphic {
public override void SetMaterialDirty () { }
public override void SetVerticesDirty () { }
protected override void OnPopulateMesh (VertexHelper vh) {
vh.Clear();
}
}
}

View File

@@ -1,120 +1,120 @@
/******************************************************************************
* 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.
*****************************************************************************/
using Spine.Unity.AnimationTools;
using System.Collections.Generic;
using UnityEngine;
namespace Spine.Unity {
/// <summary>
/// Add this component to a SkeletonMecanim GameObject
/// to turn motion of a selected root bone into Transform or RigidBody motion.
/// Local bone translation movement is used as motion.
/// All top-level bones of the skeleton are moved to compensate the root
/// motion bone location, keeping the distance relationship between bones intact.
/// </summary>
/// <remarks>
/// Only compatible with <c>SkeletonMecanim</c>.
/// For <c>SkeletonAnimation</c> or <c>SkeletonGraphic</c> please use
/// <see cref="SkeletonRootMotion">SkeletonRootMotion</see> instead.
/// </remarks>
[HelpURL("http://esotericsoftware.com/spine-unity#SkeletonMecanimRootMotion")]
public class SkeletonMecanimRootMotion : SkeletonRootMotionBase {
#region Inspector
const int DefaultMecanimLayerFlags = -1;
public int mecanimLayerFlags = DefaultMecanimLayerFlags;
#endregion
protected Vector2 movementDelta;
SkeletonMecanim skeletonMecanim;
public SkeletonMecanim SkeletonMecanim {
get {
return skeletonMecanim ? skeletonMecanim : skeletonMecanim = GetComponent<SkeletonMecanim>();
}
}
public override Vector2 GetRemainingRootMotion (int layerIndex) {
var pair = skeletonMecanim.Translator.GetActiveAnimationAndTime(layerIndex);
var animation = pair.Key;
var time = pair.Value;
if (animation == null)
return Vector2.zero;
float start = time;
float end = animation.Duration;
return GetAnimationRootMotion(start, end, animation);
}
public override RootMotionInfo GetRootMotionInfo (int layerIndex) {
var pair = skeletonMecanim.Translator.GetActiveAnimationAndTime(layerIndex);
var animation = pair.Key;
var time = pair.Value;
if (animation == null)
return new RootMotionInfo();
return GetAnimationRootMotionInfo(animation, time);
}
protected override void Reset () {
base.Reset();
mecanimLayerFlags = DefaultMecanimLayerFlags;
}
protected override void Start () {
base.Start();
skeletonMecanim = GetComponent<SkeletonMecanim>();
if (skeletonMecanim) {
skeletonMecanim.Translator.OnClipApplied -= OnClipApplied;
skeletonMecanim.Translator.OnClipApplied += OnClipApplied;
}
}
void OnClipApplied (Spine.Animation animation, int layerIndex, float weight,
float time, float lastTime, bool playsBackward) {
if (((mecanimLayerFlags & 1 << layerIndex) == 0) || weight == 0)
return;
if (!playsBackward) {
movementDelta += weight * GetAnimationRootMotion(lastTime, time, animation);
} else {
movementDelta -= weight * GetAnimationRootMotion(time, lastTime, animation);
}
}
protected override Vector2 CalculateAnimationsMovementDelta () {
// Note: movement delta is not gather after animation but
// in OnClipApplied after every applied animation.
Vector2 result = movementDelta;
movementDelta = Vector2.zero;
return result;
}
}
}
/******************************************************************************
* 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.
*****************************************************************************/
using Spine.Unity.AnimationTools;
using System.Collections.Generic;
using UnityEngine;
namespace Spine.Unity {
/// <summary>
/// Add this component to a SkeletonMecanim GameObject
/// to turn motion of a selected root bone into Transform or RigidBody motion.
/// Local bone translation movement is used as motion.
/// All top-level bones of the skeleton are moved to compensate the root
/// motion bone location, keeping the distance relationship between bones intact.
/// </summary>
/// <remarks>
/// Only compatible with <c>SkeletonMecanim</c>.
/// For <c>SkeletonAnimation</c> or <c>SkeletonGraphic</c> please use
/// <see cref="SkeletonRootMotion">SkeletonRootMotion</see> instead.
/// </remarks>
[HelpURL("http://esotericsoftware.com/spine-unity#SkeletonMecanimRootMotion")]
public class SkeletonMecanimRootMotion : SkeletonRootMotionBase {
#region Inspector
const int DefaultMecanimLayerFlags = -1;
public int mecanimLayerFlags = DefaultMecanimLayerFlags;
#endregion
protected Vector2 movementDelta;
SkeletonMecanim skeletonMecanim;
public SkeletonMecanim SkeletonMecanim {
get {
return skeletonMecanim ? skeletonMecanim : skeletonMecanim = GetComponent<SkeletonMecanim>();
}
}
public override Vector2 GetRemainingRootMotion (int layerIndex) {
var pair = skeletonMecanim.Translator.GetActiveAnimationAndTime(layerIndex);
var animation = pair.Key;
var time = pair.Value;
if (animation == null)
return Vector2.zero;
float start = time;
float end = animation.Duration;
return GetAnimationRootMotion(start, end, animation);
}
public override RootMotionInfo GetRootMotionInfo (int layerIndex) {
var pair = skeletonMecanim.Translator.GetActiveAnimationAndTime(layerIndex);
var animation = pair.Key;
var time = pair.Value;
if (animation == null)
return new RootMotionInfo();
return GetAnimationRootMotionInfo(animation, time);
}
protected override void Reset () {
base.Reset();
mecanimLayerFlags = DefaultMecanimLayerFlags;
}
protected override void Start () {
base.Start();
skeletonMecanim = GetComponent<SkeletonMecanim>();
if (skeletonMecanim) {
skeletonMecanim.Translator.OnClipApplied -= OnClipApplied;
skeletonMecanim.Translator.OnClipApplied += OnClipApplied;
}
}
void OnClipApplied (Spine.Animation animation, int layerIndex, float weight,
float time, float lastTime, bool playsBackward) {
if (((mecanimLayerFlags & 1 << layerIndex) == 0) || weight == 0)
return;
if (!playsBackward) {
movementDelta += weight * GetAnimationRootMotion(lastTime, time, animation);
} else {
movementDelta -= weight * GetAnimationRootMotion(time, lastTime, animation);
}
}
protected override Vector2 CalculateAnimationsMovementDelta () {
// Note: movement delta is not gather after animation but
// in OnClipApplied after every applied animation.
Vector2 result = movementDelta;
movementDelta = Vector2.zero;
return result;
}
}
}

View File

@@ -1,154 +1,154 @@
/******************************************************************************
* 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.
*****************************************************************************/
using Spine.Unity.AnimationTools;
using System.Collections.Generic;
using UnityEngine;
namespace Spine.Unity {
/// <summary>
/// Add this component to a SkeletonAnimation or SkeletonGraphic GameObject
/// to turn motion of a selected root bone into Transform or RigidBody motion.
/// Local bone translation movement is used as motion.
/// All top-level bones of the skeleton are moved to compensate the root
/// motion bone location, keeping the distance relationship between bones intact.
/// </summary>
/// <remarks>
/// Only compatible with SkeletonAnimation (or other components that implement
/// ISkeletonComponent, ISkeletonAnimation and IAnimationStateComponent).
/// For <c>SkeletonMecanim</c> please use
/// <see cref="SkeletonMecanimRootMotion">SkeletonMecanimRootMotion</see> instead.
/// </remarks>
[HelpURL("http://esotericsoftware.com/spine-unity#SkeletonRootMotion")]
public class SkeletonRootMotion : SkeletonRootMotionBase {
#region Inspector
const int DefaultAnimationTrackFlags = -1;
public int animationTrackFlags = DefaultAnimationTrackFlags;
#endregion
AnimationState animationState;
Canvas canvas;
public override Vector2 GetRemainingRootMotion (int trackIndex) {
TrackEntry track = animationState.GetCurrent(trackIndex);
if (track == null)
return Vector2.zero;
var animation = track.Animation;
float start = track.AnimationTime;
float end = animation.Duration;
return GetAnimationRootMotion(start, end, animation);
}
public override RootMotionInfo GetRootMotionInfo (int trackIndex) {
TrackEntry track = animationState.GetCurrent(trackIndex);
if (track == null)
return new RootMotionInfo();
var animation = track.Animation;
float time = track.AnimationTime;
return GetAnimationRootMotionInfo(track.Animation, time);
}
protected override float AdditionalScale {
get {
return canvas ? canvas.referencePixelsPerUnit : 1.0f;
}
}
protected override void Reset () {
base.Reset();
animationTrackFlags = DefaultAnimationTrackFlags;
}
protected override void Start () {
base.Start();
var animstateComponent = skeletonComponent as IAnimationStateComponent;
this.animationState = (animstateComponent != null) ? animstateComponent.AnimationState : null;
if (this.GetComponent<CanvasRenderer>() != null) {
canvas = this.GetComponentInParent<Canvas>();
}
}
protected override Vector2 CalculateAnimationsMovementDelta () {
Vector2 localDelta = Vector2.zero;
int trackCount = animationState.Tracks.Count;
for (int trackIndex = 0; trackIndex < trackCount; ++trackIndex) {
// note: animationTrackFlags != -1 below covers trackIndex >= 32,
// with -1 corresponding to entry "everything" of the dropdown list.
if (animationTrackFlags != -1 && (animationTrackFlags & 1 << trackIndex) == 0)
continue;
TrackEntry track = animationState.GetCurrent(trackIndex);
TrackEntry next = null;
while (track != null) {
var animation = track.Animation;
float start = track.AnimationLast;
float end = track.AnimationTime;
var currentDelta = GetAnimationRootMotion(start, end, animation);
if (currentDelta != Vector2.zero) {
ApplyMixAlphaToDelta(ref currentDelta, next, track);
localDelta += currentDelta;
}
// Traverse mixingFrom chain.
next = track;
track = track.MixingFrom;
}
}
return localDelta;
}
void ApplyMixAlphaToDelta (ref Vector2 currentDelta, TrackEntry next, TrackEntry track) {
// Apply mix alpha to the delta position (based on AnimationState.cs).
float mix;
if (next != null) {
if (next.MixDuration == 0) { // Single frame mix to undo mixingFrom changes.
mix = 1;
} else {
mix = next.MixTime / next.MixDuration;
if (mix > 1) mix = 1;
}
float mixAndAlpha = track.Alpha * next.InterruptAlpha * (1 - mix);
currentDelta *= mixAndAlpha;
} else {
if (track.MixDuration == 0) {
mix = 1;
} else {
mix = track.Alpha * (track.MixTime / track.MixDuration);
if (mix > 1) mix = 1;
}
currentDelta *= mix;
}
}
}
}
/******************************************************************************
* 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.
*****************************************************************************/
using Spine.Unity.AnimationTools;
using System.Collections.Generic;
using UnityEngine;
namespace Spine.Unity {
/// <summary>
/// Add this component to a SkeletonAnimation or SkeletonGraphic GameObject
/// to turn motion of a selected root bone into Transform or RigidBody motion.
/// Local bone translation movement is used as motion.
/// All top-level bones of the skeleton are moved to compensate the root
/// motion bone location, keeping the distance relationship between bones intact.
/// </summary>
/// <remarks>
/// Only compatible with SkeletonAnimation (or other components that implement
/// ISkeletonComponent, ISkeletonAnimation and IAnimationStateComponent).
/// For <c>SkeletonMecanim</c> please use
/// <see cref="SkeletonMecanimRootMotion">SkeletonMecanimRootMotion</see> instead.
/// </remarks>
[HelpURL("http://esotericsoftware.com/spine-unity#SkeletonRootMotion")]
public class SkeletonRootMotion : SkeletonRootMotionBase {
#region Inspector
const int DefaultAnimationTrackFlags = -1;
public int animationTrackFlags = DefaultAnimationTrackFlags;
#endregion
AnimationState animationState;
Canvas canvas;
public override Vector2 GetRemainingRootMotion (int trackIndex) {
TrackEntry track = animationState.GetCurrent(trackIndex);
if (track == null)
return Vector2.zero;
var animation = track.Animation;
float start = track.AnimationTime;
float end = animation.Duration;
return GetAnimationRootMotion(start, end, animation);
}
public override RootMotionInfo GetRootMotionInfo (int trackIndex) {
TrackEntry track = animationState.GetCurrent(trackIndex);
if (track == null)
return new RootMotionInfo();
var animation = track.Animation;
float time = track.AnimationTime;
return GetAnimationRootMotionInfo(track.Animation, time);
}
protected override float AdditionalScale {
get {
return canvas ? canvas.referencePixelsPerUnit : 1.0f;
}
}
protected override void Reset () {
base.Reset();
animationTrackFlags = DefaultAnimationTrackFlags;
}
protected override void Start () {
base.Start();
var animstateComponent = skeletonComponent as IAnimationStateComponent;
this.animationState = (animstateComponent != null) ? animstateComponent.AnimationState : null;
if (this.GetComponent<CanvasRenderer>() != null) {
canvas = this.GetComponentInParent<Canvas>();
}
}
protected override Vector2 CalculateAnimationsMovementDelta () {
Vector2 localDelta = Vector2.zero;
int trackCount = animationState.Tracks.Count;
for (int trackIndex = 0; trackIndex < trackCount; ++trackIndex) {
// note: animationTrackFlags != -1 below covers trackIndex >= 32,
// with -1 corresponding to entry "everything" of the dropdown list.
if (animationTrackFlags != -1 && (animationTrackFlags & 1 << trackIndex) == 0)
continue;
TrackEntry track = animationState.GetCurrent(trackIndex);
TrackEntry next = null;
while (track != null) {
var animation = track.Animation;
float start = track.AnimationLast;
float end = track.AnimationTime;
var currentDelta = GetAnimationRootMotion(start, end, animation);
if (currentDelta != Vector2.zero) {
ApplyMixAlphaToDelta(ref currentDelta, next, track);
localDelta += currentDelta;
}
// Traverse mixingFrom chain.
next = track;
track = track.MixingFrom;
}
}
return localDelta;
}
void ApplyMixAlphaToDelta (ref Vector2 currentDelta, TrackEntry next, TrackEntry track) {
// Apply mix alpha to the delta position (based on AnimationState.cs).
float mix;
if (next != null) {
if (next.MixDuration == 0) { // Single frame mix to undo mixingFrom changes.
mix = 1;
} else {
mix = next.MixTime / next.MixDuration;
if (mix > 1) mix = 1;
}
float mixAndAlpha = track.Alpha * next.InterruptAlpha * (1 - mix);
currentDelta *= mixAndAlpha;
} else {
if (track.MixDuration == 0) {
mix = 1;
} else {
mix = track.Alpha * (track.MixTime / track.MixDuration);
if (mix > 1) mix = 1;
}
currentDelta *= mix;
}
}
}
}

View File

@@ -1,364 +1,364 @@
/******************************************************************************
* 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.
*****************************************************************************/
using Spine.Unity.AnimationTools;
using System;
using System.Collections.Generic;
using UnityEngine;
namespace Spine.Unity {
/// <summary>
/// Base class for skeleton root motion components.
/// </summary>
abstract public class SkeletonRootMotionBase : MonoBehaviour {
#region Inspector
[SpineBone]
[SerializeField]
protected string rootMotionBoneName = "root";
public bool transformPositionX = true;
public bool transformPositionY = true;
public float rootMotionScaleX = 1;
public float rootMotionScaleY = 1;
/// <summary>Skeleton space X translation per skeleton space Y translation root motion.</summary>
public float rootMotionTranslateXPerY = 0;
/// <summary>Skeleton space Y translation per skeleton space X translation root motion.</summary>
public float rootMotionTranslateYPerX = 0;
[Header("Optional")]
public Rigidbody2D rigidBody2D;
public bool applyRigidbody2DGravity = false;
public Rigidbody rigidBody;
public bool UsesRigidbody {
get { return rigidBody != null || rigidBody2D != null; }
}
#endregion
protected ISkeletonComponent skeletonComponent;
protected Bone rootMotionBone;
protected int rootMotionBoneIndex;
protected List<Bone> topLevelBones = new List<Bone>();
protected Vector2 initialOffset = Vector2.zero;
protected Vector2 tempSkeletonDisplacement;
protected Vector2 rigidbodyDisplacement;
protected virtual void Reset () {
FindRigidbodyComponent();
}
protected virtual void Start () {
skeletonComponent = GetComponent<ISkeletonComponent>();
GatherTopLevelBones();
SetRootMotionBone(rootMotionBoneName);
if (rootMotionBone != null)
initialOffset = new Vector2(rootMotionBone.X, rootMotionBone.Y);
var skeletonAnimation = skeletonComponent as ISkeletonAnimation;
if (skeletonAnimation != null) {
skeletonAnimation.UpdateLocal -= HandleUpdateLocal;
skeletonAnimation.UpdateLocal += HandleUpdateLocal;
}
}
protected virtual void FixedUpdate () {
if (!this.isActiveAndEnabled)
return; // Root motion is only applied when component is enabled.
if (rigidBody2D != null) {
Vector2 gravityAndVelocityMovement = Vector2.zero;
if (applyRigidbody2DGravity) {
float deltaTime = Time.fixedDeltaTime;
float deltaTimeSquared = (deltaTime * deltaTime);
rigidBody2D.velocity += rigidBody2D.gravityScale * Physics2D.gravity * deltaTime;
gravityAndVelocityMovement = 0.5f * rigidBody2D.gravityScale * Physics2D.gravity * deltaTimeSquared +
rigidBody2D.velocity * deltaTime;
}
rigidBody2D.MovePosition(gravityAndVelocityMovement + new Vector2(transform.position.x, transform.position.y)
+ rigidbodyDisplacement);
}
if (rigidBody != null) {
rigidBody.MovePosition(transform.position
+ new Vector3(rigidbodyDisplacement.x, rigidbodyDisplacement.y, 0));
}
Vector2 parentBoneScale;
GetScaleAffectingRootMotion(out parentBoneScale);
ClearEffectiveBoneOffsets(parentBoneScale);
rigidbodyDisplacement = Vector2.zero;
tempSkeletonDisplacement = Vector2.zero;
}
protected virtual void OnDisable () {
rigidbodyDisplacement = Vector2.zero;
tempSkeletonDisplacement = Vector2.zero;
}
protected void FindRigidbodyComponent () {
rigidBody2D = this.GetComponent<Rigidbody2D>();
if (!rigidBody2D)
rigidBody = this.GetComponent<Rigidbody>();
if (!rigidBody2D && !rigidBody) {
rigidBody2D = this.GetComponentInParent<Rigidbody2D>();
if (!rigidBody2D)
rigidBody = this.GetComponentInParent<Rigidbody>();
}
}
protected virtual float AdditionalScale { get { return 1.0f; } }
abstract protected Vector2 CalculateAnimationsMovementDelta ();
abstract public Vector2 GetRemainingRootMotion (int trackIndex = 0);
public struct RootMotionInfo {
public Vector2 start;
public Vector2 current;
public Vector2 mid;
public Vector2 end;
public bool timeIsPastMid;
};
abstract public RootMotionInfo GetRootMotionInfo (int trackIndex = 0);
public void SetRootMotionBone (string name) {
var skeleton = skeletonComponent.Skeleton;
Bone bone = skeleton.FindBone(name);
if (bone != null) {
this.rootMotionBoneIndex = bone.Data.Index;
this.rootMotionBone = bone;
} else {
Debug.Log("Bone named \"" + name + "\" could not be found.");
this.rootMotionBoneIndex = 0;
this.rootMotionBone = skeleton.RootBone;
}
}
public void AdjustRootMotionToDistance (Vector2 distanceToTarget, int trackIndex = 0, bool adjustX = true, bool adjustY = true,
float minX = 0, float maxX = float.MaxValue, float minY = 0, float maxY = float.MaxValue,
bool allowXTranslation = false, bool allowYTranslation = false) {
Vector2 distanceToTargetSkeletonSpace = (Vector2)transform.InverseTransformVector(distanceToTarget);
Vector2 scaleAffectingRootMotion = GetScaleAffectingRootMotion();
if (UsesRigidbody)
distanceToTargetSkeletonSpace -= tempSkeletonDisplacement;
Vector2 remainingRootMotionSkeletonSpace = GetRemainingRootMotion(trackIndex);
remainingRootMotionSkeletonSpace.Scale(scaleAffectingRootMotion);
if (remainingRootMotionSkeletonSpace.x == 0)
remainingRootMotionSkeletonSpace.x = 0.0001f;
if (remainingRootMotionSkeletonSpace.y == 0)
remainingRootMotionSkeletonSpace.y = 0.0001f;
if (adjustX)
rootMotionScaleX = Math.Min(maxX, Math.Max(minX, distanceToTargetSkeletonSpace.x / remainingRootMotionSkeletonSpace.x));
if (adjustY)
rootMotionScaleY = Math.Min(maxY, Math.Max(minY, distanceToTargetSkeletonSpace.y / remainingRootMotionSkeletonSpace.y));
if (allowXTranslation)
rootMotionTranslateXPerY = (distanceToTargetSkeletonSpace.x - remainingRootMotionSkeletonSpace.x * rootMotionScaleX) / remainingRootMotionSkeletonSpace.y;
if (allowYTranslation)
rootMotionTranslateYPerX = (distanceToTargetSkeletonSpace.y - remainingRootMotionSkeletonSpace.y * rootMotionScaleY) / remainingRootMotionSkeletonSpace.x;
}
public Vector2 GetAnimationRootMotion (Animation animation) {
return GetAnimationRootMotion(0, animation.Duration, animation);
}
public Vector2 GetAnimationRootMotion (float startTime, float endTime,
Animation animation) {
TranslateTimeline timeline = animation.FindTranslateTimelineForBone(rootMotionBoneIndex);
if (timeline != null) {
return GetTimelineMovementDelta(startTime, endTime, timeline, animation);
}
TranslateXTimeline xTimeline = animation.FindTimelineForBone<TranslateXTimeline>(rootMotionBoneIndex);
TranslateYTimeline yTimeline = animation.FindTimelineForBone<TranslateYTimeline>(rootMotionBoneIndex);
if (xTimeline != null || yTimeline != null) {
return GetTimelineMovementDelta(startTime, endTime, xTimeline, yTimeline, animation);
}
return Vector2.zero;
}
public RootMotionInfo GetAnimationRootMotionInfo (Animation animation, float currentTime) {
RootMotionInfo rootMotion = new RootMotionInfo();
float duration = animation.Duration;
float mid = duration * 0.5f;
rootMotion.timeIsPastMid = currentTime > mid;
TranslateTimeline timeline = animation.FindTranslateTimelineForBone(rootMotionBoneIndex);
if (timeline != null) {
rootMotion.start = timeline.Evaluate(0);
rootMotion.current = timeline.Evaluate(currentTime);
rootMotion.mid = timeline.Evaluate(mid);
rootMotion.end = timeline.Evaluate(duration);
return rootMotion;
}
TranslateXTimeline xTimeline = animation.FindTimelineForBone<TranslateXTimeline>(rootMotionBoneIndex);
TranslateYTimeline yTimeline = animation.FindTimelineForBone<TranslateYTimeline>(rootMotionBoneIndex);
if (xTimeline != null || yTimeline != null) {
rootMotion.start = TimelineExtensions.Evaluate(xTimeline, yTimeline, 0);
rootMotion.current = TimelineExtensions.Evaluate(xTimeline, yTimeline, currentTime);
rootMotion.mid = TimelineExtensions.Evaluate(xTimeline, yTimeline, mid);
rootMotion.end = TimelineExtensions.Evaluate(xTimeline, yTimeline, duration);
return rootMotion;
}
return rootMotion;
}
Vector2 GetTimelineMovementDelta (float startTime, float endTime,
TranslateTimeline timeline, Animation animation) {
Vector2 currentDelta;
if (startTime > endTime) // Looped
currentDelta = (timeline.Evaluate(animation.Duration) - timeline.Evaluate(startTime))
+ (timeline.Evaluate(endTime) - timeline.Evaluate(0));
else if (startTime != endTime) // Non-looped
currentDelta = timeline.Evaluate(endTime) - timeline.Evaluate(startTime);
else
currentDelta = Vector2.zero;
return currentDelta;
}
Vector2 GetTimelineMovementDelta (float startTime, float endTime,
TranslateXTimeline xTimeline, TranslateYTimeline yTimeline, Animation animation) {
Vector2 currentDelta;
if (startTime > endTime) // Looped
currentDelta =
(TimelineExtensions.Evaluate(xTimeline, yTimeline, animation.Duration)
- TimelineExtensions.Evaluate(xTimeline, yTimeline, startTime))
+ (TimelineExtensions.Evaluate(xTimeline, yTimeline, endTime)
- TimelineExtensions.Evaluate(xTimeline, yTimeline, 0));
else if (startTime != endTime) // Non-looped
currentDelta = TimelineExtensions.Evaluate(xTimeline, yTimeline, endTime)
- TimelineExtensions.Evaluate(xTimeline, yTimeline, startTime);
else
currentDelta = Vector2.zero;
return currentDelta;
}
void GatherTopLevelBones () {
topLevelBones.Clear();
var skeleton = skeletonComponent.Skeleton;
foreach (var bone in skeleton.Bones) {
if (bone.Parent == null)
topLevelBones.Add(bone);
}
}
void HandleUpdateLocal (ISkeletonAnimation animatedSkeletonComponent) {
if (!this.isActiveAndEnabled)
return; // Root motion is only applied when component is enabled.
var boneLocalDelta = CalculateAnimationsMovementDelta();
Vector2 parentBoneScale;
Vector2 skeletonDelta = GetSkeletonSpaceMovementDelta(boneLocalDelta, out parentBoneScale);
ApplyRootMotion(skeletonDelta, parentBoneScale);
}
void ApplyRootMotion (Vector2 skeletonDelta, Vector2 parentBoneScale) {
// Apply root motion to Transform or RigidBody;
if (UsesRigidbody) {
rigidbodyDisplacement += (Vector2)transform.TransformVector(skeletonDelta);
// Accumulated displacement is applied on the next Physics update in FixedUpdate.
// Until the next Physics update, tempBoneDisplacement is offsetting bone locations
// to prevent stutter which would otherwise occur if we don't move every Update.
tempSkeletonDisplacement += skeletonDelta;
SetEffectiveBoneOffsetsTo(tempSkeletonDisplacement, parentBoneScale);
} else {
transform.position += transform.TransformVector(skeletonDelta);
ClearEffectiveBoneOffsets(parentBoneScale);
}
}
Vector2 GetScaleAffectingRootMotion () {
Vector2 parentBoneScale;
return GetScaleAffectingRootMotion(out parentBoneScale);
}
Vector2 GetScaleAffectingRootMotion (out Vector2 parentBoneScale) {
var skeleton = skeletonComponent.Skeleton;
Vector2 totalScale = Vector2.one;
totalScale.x *= skeleton.ScaleX;
totalScale.y *= skeleton.ScaleY;
parentBoneScale = Vector2.one;
Bone scaleBone = rootMotionBone;
while ((scaleBone = scaleBone.Parent) != null) {
parentBoneScale.x *= scaleBone.ScaleX;
parentBoneScale.y *= scaleBone.ScaleY;
}
totalScale = Vector2.Scale(totalScale, parentBoneScale);
totalScale *= AdditionalScale;
return totalScale;
}
Vector2 GetSkeletonSpaceMovementDelta (Vector2 boneLocalDelta, out Vector2 parentBoneScale) {
Vector2 skeletonDelta = boneLocalDelta;
Vector2 totalScale = GetScaleAffectingRootMotion(out parentBoneScale);
skeletonDelta.Scale(totalScale);
Vector2 rootMotionTranslation = new Vector2(
rootMotionTranslateXPerY * skeletonDelta.y,
rootMotionTranslateYPerX * skeletonDelta.x);
skeletonDelta.x *= rootMotionScaleX;
skeletonDelta.y *= rootMotionScaleY;
skeletonDelta.x += rootMotionTranslation.x;
skeletonDelta.y += rootMotionTranslation.y;
if (!transformPositionX) skeletonDelta.x = 0f;
if (!transformPositionY) skeletonDelta.y = 0f;
return skeletonDelta;
}
void SetEffectiveBoneOffsetsTo (Vector2 displacementSkeletonSpace, Vector2 parentBoneScale) {
// Move top level bones in opposite direction of the root motion bone
var skeleton = skeletonComponent.Skeleton;
foreach (var topLevelBone in topLevelBones) {
if (topLevelBone == rootMotionBone) {
if (transformPositionX) topLevelBone.X = displacementSkeletonSpace.x / skeleton.ScaleX;
if (transformPositionY) topLevelBone.Y = displacementSkeletonSpace.y / skeleton.ScaleY;
} else {
float offsetX = (initialOffset.x - rootMotionBone.X) * parentBoneScale.x;
float offsetY = (initialOffset.y - rootMotionBone.Y) * parentBoneScale.y;
if (transformPositionX) topLevelBone.X = (displacementSkeletonSpace.x / skeleton.ScaleX) + offsetX;
if (transformPositionY) topLevelBone.Y = (displacementSkeletonSpace.y / skeleton.ScaleY) + offsetY;
}
}
}
void ClearEffectiveBoneOffsets (Vector2 parentBoneScale) {
SetEffectiveBoneOffsetsTo(Vector2.zero, parentBoneScale);
}
}
}
/******************************************************************************
* 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.
*****************************************************************************/
using Spine.Unity.AnimationTools;
using System;
using System.Collections.Generic;
using UnityEngine;
namespace Spine.Unity {
/// <summary>
/// Base class for skeleton root motion components.
/// </summary>
abstract public class SkeletonRootMotionBase : MonoBehaviour {
#region Inspector
[SpineBone]
[SerializeField]
protected string rootMotionBoneName = "root";
public bool transformPositionX = true;
public bool transformPositionY = true;
public float rootMotionScaleX = 1;
public float rootMotionScaleY = 1;
/// <summary>Skeleton space X translation per skeleton space Y translation root motion.</summary>
public float rootMotionTranslateXPerY = 0;
/// <summary>Skeleton space Y translation per skeleton space X translation root motion.</summary>
public float rootMotionTranslateYPerX = 0;
[Header("Optional")]
public Rigidbody2D rigidBody2D;
public bool applyRigidbody2DGravity = false;
public Rigidbody rigidBody;
public bool UsesRigidbody {
get { return rigidBody != null || rigidBody2D != null; }
}
#endregion
protected ISkeletonComponent skeletonComponent;
protected Bone rootMotionBone;
protected int rootMotionBoneIndex;
protected List<Bone> topLevelBones = new List<Bone>();
protected Vector2 initialOffset = Vector2.zero;
protected Vector2 tempSkeletonDisplacement;
protected Vector2 rigidbodyDisplacement;
protected virtual void Reset () {
FindRigidbodyComponent();
}
protected virtual void Start () {
skeletonComponent = GetComponent<ISkeletonComponent>();
GatherTopLevelBones();
SetRootMotionBone(rootMotionBoneName);
if (rootMotionBone != null)
initialOffset = new Vector2(rootMotionBone.X, rootMotionBone.Y);
var skeletonAnimation = skeletonComponent as ISkeletonAnimation;
if (skeletonAnimation != null) {
skeletonAnimation.UpdateLocal -= HandleUpdateLocal;
skeletonAnimation.UpdateLocal += HandleUpdateLocal;
}
}
protected virtual void FixedUpdate () {
if (!this.isActiveAndEnabled)
return; // Root motion is only applied when component is enabled.
if (rigidBody2D != null) {
Vector2 gravityAndVelocityMovement = Vector2.zero;
if (applyRigidbody2DGravity) {
float deltaTime = Time.fixedDeltaTime;
float deltaTimeSquared = (deltaTime * deltaTime);
rigidBody2D.velocity += rigidBody2D.gravityScale * Physics2D.gravity * deltaTime;
gravityAndVelocityMovement = 0.5f * rigidBody2D.gravityScale * Physics2D.gravity * deltaTimeSquared +
rigidBody2D.velocity * deltaTime;
}
rigidBody2D.MovePosition(gravityAndVelocityMovement + new Vector2(transform.position.x, transform.position.y)
+ rigidbodyDisplacement);
}
if (rigidBody != null) {
rigidBody.MovePosition(transform.position
+ new Vector3(rigidbodyDisplacement.x, rigidbodyDisplacement.y, 0));
}
Vector2 parentBoneScale;
GetScaleAffectingRootMotion(out parentBoneScale);
ClearEffectiveBoneOffsets(parentBoneScale);
rigidbodyDisplacement = Vector2.zero;
tempSkeletonDisplacement = Vector2.zero;
}
protected virtual void OnDisable () {
rigidbodyDisplacement = Vector2.zero;
tempSkeletonDisplacement = Vector2.zero;
}
protected void FindRigidbodyComponent () {
rigidBody2D = this.GetComponent<Rigidbody2D>();
if (!rigidBody2D)
rigidBody = this.GetComponent<Rigidbody>();
if (!rigidBody2D && !rigidBody) {
rigidBody2D = this.GetComponentInParent<Rigidbody2D>();
if (!rigidBody2D)
rigidBody = this.GetComponentInParent<Rigidbody>();
}
}
protected virtual float AdditionalScale { get { return 1.0f; } }
abstract protected Vector2 CalculateAnimationsMovementDelta ();
abstract public Vector2 GetRemainingRootMotion (int trackIndex = 0);
public struct RootMotionInfo {
public Vector2 start;
public Vector2 current;
public Vector2 mid;
public Vector2 end;
public bool timeIsPastMid;
};
abstract public RootMotionInfo GetRootMotionInfo (int trackIndex = 0);
public void SetRootMotionBone (string name) {
var skeleton = skeletonComponent.Skeleton;
Bone bone = skeleton.FindBone(name);
if (bone != null) {
this.rootMotionBoneIndex = bone.Data.Index;
this.rootMotionBone = bone;
} else {
Debug.Log("Bone named \"" + name + "\" could not be found.");
this.rootMotionBoneIndex = 0;
this.rootMotionBone = skeleton.RootBone;
}
}
public void AdjustRootMotionToDistance (Vector2 distanceToTarget, int trackIndex = 0, bool adjustX = true, bool adjustY = true,
float minX = 0, float maxX = float.MaxValue, float minY = 0, float maxY = float.MaxValue,
bool allowXTranslation = false, bool allowYTranslation = false) {
Vector2 distanceToTargetSkeletonSpace = (Vector2)transform.InverseTransformVector(distanceToTarget);
Vector2 scaleAffectingRootMotion = GetScaleAffectingRootMotion();
if (UsesRigidbody)
distanceToTargetSkeletonSpace -= tempSkeletonDisplacement;
Vector2 remainingRootMotionSkeletonSpace = GetRemainingRootMotion(trackIndex);
remainingRootMotionSkeletonSpace.Scale(scaleAffectingRootMotion);
if (remainingRootMotionSkeletonSpace.x == 0)
remainingRootMotionSkeletonSpace.x = 0.0001f;
if (remainingRootMotionSkeletonSpace.y == 0)
remainingRootMotionSkeletonSpace.y = 0.0001f;
if (adjustX)
rootMotionScaleX = Math.Min(maxX, Math.Max(minX, distanceToTargetSkeletonSpace.x / remainingRootMotionSkeletonSpace.x));
if (adjustY)
rootMotionScaleY = Math.Min(maxY, Math.Max(minY, distanceToTargetSkeletonSpace.y / remainingRootMotionSkeletonSpace.y));
if (allowXTranslation)
rootMotionTranslateXPerY = (distanceToTargetSkeletonSpace.x - remainingRootMotionSkeletonSpace.x * rootMotionScaleX) / remainingRootMotionSkeletonSpace.y;
if (allowYTranslation)
rootMotionTranslateYPerX = (distanceToTargetSkeletonSpace.y - remainingRootMotionSkeletonSpace.y * rootMotionScaleY) / remainingRootMotionSkeletonSpace.x;
}
public Vector2 GetAnimationRootMotion (Animation animation) {
return GetAnimationRootMotion(0, animation.Duration, animation);
}
public Vector2 GetAnimationRootMotion (float startTime, float endTime,
Animation animation) {
TranslateTimeline timeline = animation.FindTranslateTimelineForBone(rootMotionBoneIndex);
if (timeline != null) {
return GetTimelineMovementDelta(startTime, endTime, timeline, animation);
}
TranslateXTimeline xTimeline = animation.FindTimelineForBone<TranslateXTimeline>(rootMotionBoneIndex);
TranslateYTimeline yTimeline = animation.FindTimelineForBone<TranslateYTimeline>(rootMotionBoneIndex);
if (xTimeline != null || yTimeline != null) {
return GetTimelineMovementDelta(startTime, endTime, xTimeline, yTimeline, animation);
}
return Vector2.zero;
}
public RootMotionInfo GetAnimationRootMotionInfo (Animation animation, float currentTime) {
RootMotionInfo rootMotion = new RootMotionInfo();
float duration = animation.Duration;
float mid = duration * 0.5f;
rootMotion.timeIsPastMid = currentTime > mid;
TranslateTimeline timeline = animation.FindTranslateTimelineForBone(rootMotionBoneIndex);
if (timeline != null) {
rootMotion.start = timeline.Evaluate(0);
rootMotion.current = timeline.Evaluate(currentTime);
rootMotion.mid = timeline.Evaluate(mid);
rootMotion.end = timeline.Evaluate(duration);
return rootMotion;
}
TranslateXTimeline xTimeline = animation.FindTimelineForBone<TranslateXTimeline>(rootMotionBoneIndex);
TranslateYTimeline yTimeline = animation.FindTimelineForBone<TranslateYTimeline>(rootMotionBoneIndex);
if (xTimeline != null || yTimeline != null) {
rootMotion.start = TimelineExtensions.Evaluate(xTimeline, yTimeline, 0);
rootMotion.current = TimelineExtensions.Evaluate(xTimeline, yTimeline, currentTime);
rootMotion.mid = TimelineExtensions.Evaluate(xTimeline, yTimeline, mid);
rootMotion.end = TimelineExtensions.Evaluate(xTimeline, yTimeline, duration);
return rootMotion;
}
return rootMotion;
}
Vector2 GetTimelineMovementDelta (float startTime, float endTime,
TranslateTimeline timeline, Animation animation) {
Vector2 currentDelta;
if (startTime > endTime) // Looped
currentDelta = (timeline.Evaluate(animation.Duration) - timeline.Evaluate(startTime))
+ (timeline.Evaluate(endTime) - timeline.Evaluate(0));
else if (startTime != endTime) // Non-looped
currentDelta = timeline.Evaluate(endTime) - timeline.Evaluate(startTime);
else
currentDelta = Vector2.zero;
return currentDelta;
}
Vector2 GetTimelineMovementDelta (float startTime, float endTime,
TranslateXTimeline xTimeline, TranslateYTimeline yTimeline, Animation animation) {
Vector2 currentDelta;
if (startTime > endTime) // Looped
currentDelta =
(TimelineExtensions.Evaluate(xTimeline, yTimeline, animation.Duration)
- TimelineExtensions.Evaluate(xTimeline, yTimeline, startTime))
+ (TimelineExtensions.Evaluate(xTimeline, yTimeline, endTime)
- TimelineExtensions.Evaluate(xTimeline, yTimeline, 0));
else if (startTime != endTime) // Non-looped
currentDelta = TimelineExtensions.Evaluate(xTimeline, yTimeline, endTime)
- TimelineExtensions.Evaluate(xTimeline, yTimeline, startTime);
else
currentDelta = Vector2.zero;
return currentDelta;
}
void GatherTopLevelBones () {
topLevelBones.Clear();
var skeleton = skeletonComponent.Skeleton;
foreach (var bone in skeleton.Bones) {
if (bone.Parent == null)
topLevelBones.Add(bone);
}
}
void HandleUpdateLocal (ISkeletonAnimation animatedSkeletonComponent) {
if (!this.isActiveAndEnabled)
return; // Root motion is only applied when component is enabled.
var boneLocalDelta = CalculateAnimationsMovementDelta();
Vector2 parentBoneScale;
Vector2 skeletonDelta = GetSkeletonSpaceMovementDelta(boneLocalDelta, out parentBoneScale);
ApplyRootMotion(skeletonDelta, parentBoneScale);
}
void ApplyRootMotion (Vector2 skeletonDelta, Vector2 parentBoneScale) {
// Apply root motion to Transform or RigidBody;
if (UsesRigidbody) {
rigidbodyDisplacement += (Vector2)transform.TransformVector(skeletonDelta);
// Accumulated displacement is applied on the next Physics update in FixedUpdate.
// Until the next Physics update, tempBoneDisplacement is offsetting bone locations
// to prevent stutter which would otherwise occur if we don't move every Update.
tempSkeletonDisplacement += skeletonDelta;
SetEffectiveBoneOffsetsTo(tempSkeletonDisplacement, parentBoneScale);
} else {
transform.position += transform.TransformVector(skeletonDelta);
ClearEffectiveBoneOffsets(parentBoneScale);
}
}
Vector2 GetScaleAffectingRootMotion () {
Vector2 parentBoneScale;
return GetScaleAffectingRootMotion(out parentBoneScale);
}
Vector2 GetScaleAffectingRootMotion (out Vector2 parentBoneScale) {
var skeleton = skeletonComponent.Skeleton;
Vector2 totalScale = Vector2.one;
totalScale.x *= skeleton.ScaleX;
totalScale.y *= skeleton.ScaleY;
parentBoneScale = Vector2.one;
Bone scaleBone = rootMotionBone;
while ((scaleBone = scaleBone.Parent) != null) {
parentBoneScale.x *= scaleBone.ScaleX;
parentBoneScale.y *= scaleBone.ScaleY;
}
totalScale = Vector2.Scale(totalScale, parentBoneScale);
totalScale *= AdditionalScale;
return totalScale;
}
Vector2 GetSkeletonSpaceMovementDelta (Vector2 boneLocalDelta, out Vector2 parentBoneScale) {
Vector2 skeletonDelta = boneLocalDelta;
Vector2 totalScale = GetScaleAffectingRootMotion(out parentBoneScale);
skeletonDelta.Scale(totalScale);
Vector2 rootMotionTranslation = new Vector2(
rootMotionTranslateXPerY * skeletonDelta.y,
rootMotionTranslateYPerX * skeletonDelta.x);
skeletonDelta.x *= rootMotionScaleX;
skeletonDelta.y *= rootMotionScaleY;
skeletonDelta.x += rootMotionTranslation.x;
skeletonDelta.y += rootMotionTranslation.y;
if (!transformPositionX) skeletonDelta.x = 0f;
if (!transformPositionY) skeletonDelta.y = 0f;
return skeletonDelta;
}
void SetEffectiveBoneOffsetsTo (Vector2 displacementSkeletonSpace, Vector2 parentBoneScale) {
// Move top level bones in opposite direction of the root motion bone
var skeleton = skeletonComponent.Skeleton;
foreach (var topLevelBone in topLevelBones) {
if (topLevelBone == rootMotionBone) {
if (transformPositionX) topLevelBone.X = displacementSkeletonSpace.x / skeleton.ScaleX;
if (transformPositionY) topLevelBone.Y = displacementSkeletonSpace.y / skeleton.ScaleY;
} else {
float offsetX = (initialOffset.x - rootMotionBone.X) * parentBoneScale.x;
float offsetY = (initialOffset.y - rootMotionBone.Y) * parentBoneScale.y;
if (transformPositionX) topLevelBone.X = (displacementSkeletonSpace.x / skeleton.ScaleX) + offsetX;
if (transformPositionY) topLevelBone.Y = (displacementSkeletonSpace.y / skeleton.ScaleY) + offsetY;
}
}
}
void ClearEffectiveBoneOffsets (Vector2 parentBoneScale) {
SetEffectiveBoneOffsetsTo(Vector2.zero, parentBoneScale);
}
}
}

View File

@@ -1,269 +1,269 @@
/******************************************************************************
* 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.
*****************************************************************************/
#if UNITY_2018_3 || UNITY_2019 || UNITY_2018_3_OR_NEWER
#define NEW_PREFAB_SYSTEM
#endif
using UnityEngine;
namespace Spine.Unity {
#if NEW_PREFAB_SYSTEM
[ExecuteAlways]
#else
[ExecuteInEditMode]
#endif
[AddComponentMenu("Spine/SkeletonAnimation")]
[HelpURL("http://esotericsoftware.com/spine-unity#SkeletonAnimation-Component")]
public class SkeletonAnimation : SkeletonRenderer, ISkeletonAnimation, IAnimationStateComponent {
#region IAnimationStateComponent
/// <summary>
/// This is the Spine.AnimationState object of this SkeletonAnimation. You can control animations through it.
/// Note that this object, like .skeleton, is not guaranteed to exist in Awake. Do all accesses and caching to it in Start</summary>
public Spine.AnimationState state;
/// <summary>
/// This is the Spine.AnimationState object of this SkeletonAnimation. You can control animations through it.
/// Note that this object, like .skeleton, is not guaranteed to exist in Awake. Do all accesses and caching to it in Start</summary>
public Spine.AnimationState AnimationState {
get {
Initialize(false);
return this.state;
}
}
private bool wasUpdatedAfterInit = true;
#endregion
#region Bone Callbacks ISkeletonAnimation
protected event UpdateBonesDelegate _BeforeApply;
protected event UpdateBonesDelegate _UpdateLocal;
protected event UpdateBonesDelegate _UpdateWorld;
protected event UpdateBonesDelegate _UpdateComplete;
/// <summary>
/// Occurs before the animations are applied.
/// Use this callback when you want to change the skeleton state before animations are applied on top.
/// </summary>
public event UpdateBonesDelegate BeforeApply { add { _BeforeApply += value; } remove { _BeforeApply -= value; } }
/// <summary>
/// Occurs after the animations are applied and before world space values are resolved.
/// Use this callback when you want to set bone local values.
/// </summary>
public event UpdateBonesDelegate UpdateLocal { add { _UpdateLocal += value; } remove { _UpdateLocal -= value; } }
/// <summary>
/// Occurs after the Skeleton's bone world space values are resolved (including all constraints).
/// Using this callback will cause the world space values to be solved an extra time.
/// Use this callback if want to use bone world space values, and also set bone local values.</summary>
public event UpdateBonesDelegate UpdateWorld { add { _UpdateWorld += value; } remove { _UpdateWorld -= value; } }
/// <summary>
/// Occurs after the Skeleton's bone world space values are resolved (including all constraints).
/// Use this callback if you want to use bone world space values, but don't intend to modify bone local values.
/// This callback can also be used when setting world position and the bone matrix.</summary>
public event UpdateBonesDelegate UpdateComplete { add { _UpdateComplete += value; } remove { _UpdateComplete -= value; } }
#endregion
#region Serialized state and Beginner API
[SerializeField]
[SpineAnimation]
private string _animationName;
/// <summary>
/// Setting this property sets the animation of the skeleton. If invalid, it will store the animation name for the next time the skeleton is properly initialized.
/// Getting this property gets the name of the currently playing animation. If invalid, it will return the last stored animation name set through this property.</summary>
public string AnimationName {
get {
if (!valid) {
return _animationName;
} else {
TrackEntry entry = state.GetCurrent(0);
return entry == null ? null : entry.Animation.Name;
}
}
set {
Initialize(false);
if (_animationName == value) {
TrackEntry entry = state.GetCurrent(0);
if (entry != null && entry.Loop == loop)
return;
}
_animationName = value;
if (string.IsNullOrEmpty(value)) {
state.ClearTrack(0);
} else {
var animationObject = skeletonDataAsset.GetSkeletonData(false).FindAnimation(value);
if (animationObject != null)
state.SetAnimation(0, animationObject, loop);
}
}
}
/// <summary>Whether or not <see cref="AnimationName"/> should loop. This only applies to the initial animation specified in the inspector, or any subsequent Animations played through .AnimationName. Animations set through state.SetAnimation are unaffected.</summary>
public bool loop;
/// <summary>
/// The rate at which animations progress over time. 1 means 100%. 0.5 means 50%.</summary>
/// <remarks>AnimationState and TrackEntry also have their own timeScale. These are combined multiplicatively.</remarks>
public float timeScale = 1;
#endregion
#region Runtime Instantiation
/// <summary>Adds and prepares a SkeletonAnimation component to a GameObject at runtime.</summary>
/// <returns>The newly instantiated SkeletonAnimation</returns>
public static SkeletonAnimation AddToGameObject (GameObject gameObject, SkeletonDataAsset skeletonDataAsset,
bool quiet = false) {
return SkeletonRenderer.AddSpineComponent<SkeletonAnimation>(gameObject, skeletonDataAsset, quiet);
}
/// <summary>Instantiates a new UnityEngine.GameObject and adds a prepared SkeletonAnimation component to it.</summary>
/// <returns>The newly instantiated SkeletonAnimation component.</returns>
public static SkeletonAnimation NewSkeletonAnimationGameObject (SkeletonDataAsset skeletonDataAsset,
bool quiet = false) {
return SkeletonRenderer.NewSpineGameObject<SkeletonAnimation>(skeletonDataAsset, quiet);
}
#endregion
/// <summary>
/// Clears the previously generated mesh, resets the skeleton's pose, and clears all previously active animations.</summary>
public override void ClearState () {
base.ClearState();
if (state != null) state.ClearTracks();
}
/// <summary>
/// Initialize this component. Attempts to load the SkeletonData and creates the internal Spine objects and buffers.</summary>
/// <param name="overwrite">If set to <c>true</c>, force overwrite an already initialized object.</param>
public override void Initialize (bool overwrite, bool quiet = false) {
if (valid && !overwrite)
return;
#if UNITY_EDITOR
if (BuildUtilities.IsInSkeletonAssetBuildPreProcessing)
return;
#endif
base.Initialize(overwrite, quiet);
if (!valid)
return;
state = new Spine.AnimationState(skeletonDataAsset.GetAnimationStateData());
wasUpdatedAfterInit = false;
if (!string.IsNullOrEmpty(_animationName)) {
var animationObject = skeletonDataAsset.GetSkeletonData(false).FindAnimation(_animationName);
if (animationObject != null) {
state.SetAnimation(0, animationObject, loop);
#if UNITY_EDITOR
if (!Application.isPlaying)
Update(0f);
#endif
}
}
}
void Update () {
#if UNITY_EDITOR
if (!Application.isPlaying) {
Update(0f);
return;
}
#endif
Update(Time.deltaTime);
}
/// <summary>Progresses the AnimationState according to the given deltaTime, and applies it to the Skeleton. Use Time.deltaTime to update manually. Use deltaTime 0 to update without progressing the time.</summary>
public void Update (float deltaTime) {
if (!valid || state == null)
return;
wasUpdatedAfterInit = true;
if (updateMode < UpdateMode.OnlyAnimationStatus)
return;
UpdateAnimationStatus(deltaTime);
if (updateMode == UpdateMode.OnlyAnimationStatus) {
state.ApplyEventTimelinesOnly(skeleton, issueEvents: false);
return;
}
ApplyAnimation();
}
protected void UpdateAnimationStatus (float deltaTime) {
deltaTime *= timeScale;
skeleton.Update(deltaTime);
state.Update(deltaTime);
}
protected void ApplyAnimation () {
if (_BeforeApply != null)
_BeforeApply(this);
if (updateMode != UpdateMode.OnlyEventTimelines)
state.Apply(skeleton);
else
state.ApplyEventTimelinesOnly(skeleton, issueEvents: true);
if (_UpdateLocal != null)
_UpdateLocal(this);
skeleton.UpdateWorldTransform();
if (_UpdateWorld != null) {
_UpdateWorld(this);
skeleton.UpdateWorldTransform();
}
if (_UpdateComplete != null) {
_UpdateComplete(this);
}
}
public override void LateUpdate () {
// instantiation can happen from Update() after this component, leading to a missing Update() call.
if (!wasUpdatedAfterInit) Update(0);
base.LateUpdate();
}
public override void OnBecameVisible () {
UpdateMode previousUpdateMode = updateMode;
updateMode = UpdateMode.FullUpdate;
// OnBecameVisible is called after LateUpdate()
if (previousUpdateMode != UpdateMode.FullUpdate &&
previousUpdateMode != UpdateMode.EverythingExceptMesh)
Update(0);
if (previousUpdateMode != UpdateMode.FullUpdate)
LateUpdate();
}
}
}
/******************************************************************************
* 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.
*****************************************************************************/
#if UNITY_2018_3 || UNITY_2019 || UNITY_2018_3_OR_NEWER
#define NEW_PREFAB_SYSTEM
#endif
using UnityEngine;
namespace Spine.Unity {
#if NEW_PREFAB_SYSTEM
[ExecuteAlways]
#else
[ExecuteInEditMode]
#endif
[AddComponentMenu("Spine/SkeletonAnimation")]
[HelpURL("http://esotericsoftware.com/spine-unity#SkeletonAnimation-Component")]
public class SkeletonAnimation : SkeletonRenderer, ISkeletonAnimation, IAnimationStateComponent {
#region IAnimationStateComponent
/// <summary>
/// This is the Spine.AnimationState object of this SkeletonAnimation. You can control animations through it.
/// Note that this object, like .skeleton, is not guaranteed to exist in Awake. Do all accesses and caching to it in Start</summary>
public Spine.AnimationState state;
/// <summary>
/// This is the Spine.AnimationState object of this SkeletonAnimation. You can control animations through it.
/// Note that this object, like .skeleton, is not guaranteed to exist in Awake. Do all accesses and caching to it in Start</summary>
public Spine.AnimationState AnimationState {
get {
Initialize(false);
return this.state;
}
}
private bool wasUpdatedAfterInit = true;
#endregion
#region Bone Callbacks ISkeletonAnimation
protected event UpdateBonesDelegate _BeforeApply;
protected event UpdateBonesDelegate _UpdateLocal;
protected event UpdateBonesDelegate _UpdateWorld;
protected event UpdateBonesDelegate _UpdateComplete;
/// <summary>
/// Occurs before the animations are applied.
/// Use this callback when you want to change the skeleton state before animations are applied on top.
/// </summary>
public event UpdateBonesDelegate BeforeApply { add { _BeforeApply += value; } remove { _BeforeApply -= value; } }
/// <summary>
/// Occurs after the animations are applied and before world space values are resolved.
/// Use this callback when you want to set bone local values.
/// </summary>
public event UpdateBonesDelegate UpdateLocal { add { _UpdateLocal += value; } remove { _UpdateLocal -= value; } }
/// <summary>
/// Occurs after the Skeleton's bone world space values are resolved (including all constraints).
/// Using this callback will cause the world space values to be solved an extra time.
/// Use this callback if want to use bone world space values, and also set bone local values.</summary>
public event UpdateBonesDelegate UpdateWorld { add { _UpdateWorld += value; } remove { _UpdateWorld -= value; } }
/// <summary>
/// Occurs after the Skeleton's bone world space values are resolved (including all constraints).
/// Use this callback if you want to use bone world space values, but don't intend to modify bone local values.
/// This callback can also be used when setting world position and the bone matrix.</summary>
public event UpdateBonesDelegate UpdateComplete { add { _UpdateComplete += value; } remove { _UpdateComplete -= value; } }
#endregion
#region Serialized state and Beginner API
[SerializeField]
[SpineAnimation]
private string _animationName;
/// <summary>
/// Setting this property sets the animation of the skeleton. If invalid, it will store the animation name for the next time the skeleton is properly initialized.
/// Getting this property gets the name of the currently playing animation. If invalid, it will return the last stored animation name set through this property.</summary>
public string AnimationName {
get {
if (!valid) {
return _animationName;
} else {
TrackEntry entry = state.GetCurrent(0);
return entry == null ? null : entry.Animation.Name;
}
}
set {
Initialize(false);
if (_animationName == value) {
TrackEntry entry = state.GetCurrent(0);
if (entry != null && entry.Loop == loop)
return;
}
_animationName = value;
if (string.IsNullOrEmpty(value)) {
state.ClearTrack(0);
} else {
var animationObject = skeletonDataAsset.GetSkeletonData(false).FindAnimation(value);
if (animationObject != null)
state.SetAnimation(0, animationObject, loop);
}
}
}
/// <summary>Whether or not <see cref="AnimationName"/> should loop. This only applies to the initial animation specified in the inspector, or any subsequent Animations played through .AnimationName. Animations set through state.SetAnimation are unaffected.</summary>
public bool loop;
/// <summary>
/// The rate at which animations progress over time. 1 means 100%. 0.5 means 50%.</summary>
/// <remarks>AnimationState and TrackEntry also have their own timeScale. These are combined multiplicatively.</remarks>
public float timeScale = 1;
#endregion
#region Runtime Instantiation
/// <summary>Adds and prepares a SkeletonAnimation component to a GameObject at runtime.</summary>
/// <returns>The newly instantiated SkeletonAnimation</returns>
public static SkeletonAnimation AddToGameObject (GameObject gameObject, SkeletonDataAsset skeletonDataAsset,
bool quiet = false) {
return SkeletonRenderer.AddSpineComponent<SkeletonAnimation>(gameObject, skeletonDataAsset, quiet);
}
/// <summary>Instantiates a new UnityEngine.GameObject and adds a prepared SkeletonAnimation component to it.</summary>
/// <returns>The newly instantiated SkeletonAnimation component.</returns>
public static SkeletonAnimation NewSkeletonAnimationGameObject (SkeletonDataAsset skeletonDataAsset,
bool quiet = false) {
return SkeletonRenderer.NewSpineGameObject<SkeletonAnimation>(skeletonDataAsset, quiet);
}
#endregion
/// <summary>
/// Clears the previously generated mesh, resets the skeleton's pose, and clears all previously active animations.</summary>
public override void ClearState () {
base.ClearState();
if (state != null) state.ClearTracks();
}
/// <summary>
/// Initialize this component. Attempts to load the SkeletonData and creates the internal Spine objects and buffers.</summary>
/// <param name="overwrite">If set to <c>true</c>, force overwrite an already initialized object.</param>
public override void Initialize (bool overwrite, bool quiet = false) {
if (valid && !overwrite)
return;
#if UNITY_EDITOR
if (BuildUtilities.IsInSkeletonAssetBuildPreProcessing)
return;
#endif
base.Initialize(overwrite, quiet);
if (!valid)
return;
state = new Spine.AnimationState(skeletonDataAsset.GetAnimationStateData());
wasUpdatedAfterInit = false;
if (!string.IsNullOrEmpty(_animationName)) {
var animationObject = skeletonDataAsset.GetSkeletonData(false).FindAnimation(_animationName);
if (animationObject != null) {
state.SetAnimation(0, animationObject, loop);
#if UNITY_EDITOR
if (!Application.isPlaying)
Update(0f);
#endif
}
}
}
void Update () {
#if UNITY_EDITOR
if (!Application.isPlaying) {
Update(0f);
return;
}
#endif
Update(Time.deltaTime);
}
/// <summary>Progresses the AnimationState according to the given deltaTime, and applies it to the Skeleton. Use Time.deltaTime to update manually. Use deltaTime 0 to update without progressing the time.</summary>
public void Update (float deltaTime) {
if (!valid || state == null)
return;
wasUpdatedAfterInit = true;
if (updateMode < UpdateMode.OnlyAnimationStatus)
return;
UpdateAnimationStatus(deltaTime);
if (updateMode == UpdateMode.OnlyAnimationStatus) {
state.ApplyEventTimelinesOnly(skeleton, issueEvents: false);
return;
}
ApplyAnimation();
}
protected void UpdateAnimationStatus (float deltaTime) {
deltaTime *= timeScale;
skeleton.Update(deltaTime);
state.Update(deltaTime);
}
protected void ApplyAnimation () {
if (_BeforeApply != null)
_BeforeApply(this);
if (updateMode != UpdateMode.OnlyEventTimelines)
state.Apply(skeleton);
else
state.ApplyEventTimelinesOnly(skeleton, issueEvents: true);
if (_UpdateLocal != null)
_UpdateLocal(this);
skeleton.UpdateWorldTransform();
if (_UpdateWorld != null) {
_UpdateWorld(this);
skeleton.UpdateWorldTransform();
}
if (_UpdateComplete != null) {
_UpdateComplete(this);
}
}
public override void LateUpdate () {
// instantiation can happen from Update() after this component, leading to a missing Update() call.
if (!wasUpdatedAfterInit) Update(0);
base.LateUpdate();
}
public override void OnBecameVisible () {
UpdateMode previousUpdateMode = updateMode;
updateMode = UpdateMode.FullUpdate;
// OnBecameVisible is called after LateUpdate()
if (previousUpdateMode != UpdateMode.FullUpdate &&
previousUpdateMode != UpdateMode.EverythingExceptMesh)
Update(0);
if (previousUpdateMode != UpdateMode.FullUpdate)
LateUpdate();
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,151 +1,151 @@
/******************************************************************************
* 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.
*****************************************************************************/
using UnityEngine;
namespace Spine.Unity {
[RequireComponent(typeof(MeshRenderer), typeof(MeshFilter))]
[HelpURL("http://esotericsoftware.com/spine-unity#SkeletonRenderSeparator")]
public class SkeletonPartsRenderer : MonoBehaviour {
#region Properties
MeshGenerator meshGenerator;
public MeshGenerator MeshGenerator {
get {
LazyIntialize();
return meshGenerator;
}
}
MeshRenderer meshRenderer;
public MeshRenderer MeshRenderer {
get {
LazyIntialize();
return meshRenderer;
}
}
MeshFilter meshFilter;
public MeshFilter MeshFilter {
get {
LazyIntialize();
return meshFilter;
}
}
#endregion
#region Callback Delegates
public delegate void SkeletonPartsRendererDelegate (SkeletonPartsRenderer skeletonPartsRenderer);
/// <summary>OnMeshAndMaterialsUpdated is called at the end of LateUpdate after the Mesh and
/// all materials have been updated.</summary>
public event SkeletonPartsRendererDelegate OnMeshAndMaterialsUpdated;
#endregion
MeshRendererBuffers buffers;
SkeletonRendererInstruction currentInstructions = new SkeletonRendererInstruction();
void LazyIntialize () {
if (buffers == null) {
buffers = new MeshRendererBuffers();
buffers.Initialize();
if (meshGenerator != null) return;
meshGenerator = new MeshGenerator();
meshFilter = GetComponent<MeshFilter>();
meshRenderer = GetComponent<MeshRenderer>();
currentInstructions.Clear();
}
}
public void ClearMesh () {
LazyIntialize();
meshFilter.sharedMesh = null;
}
public void RenderParts (ExposedList<SubmeshInstruction> instructions, int startSubmesh, int endSubmesh) {
LazyIntialize();
// STEP 1: Create instruction
var smartMesh = buffers.GetNextMesh();
currentInstructions.SetWithSubset(instructions, startSubmesh, endSubmesh);
bool updateTriangles = SkeletonRendererInstruction.GeometryNotEqual(currentInstructions, smartMesh.instructionUsed);
// STEP 2: Generate mesh buffers.
var currentInstructionsSubmeshesItems = currentInstructions.submeshInstructions.Items;
meshGenerator.Begin();
if (currentInstructions.hasActiveClipping) {
for (int i = 0; i < currentInstructions.submeshInstructions.Count; i++)
meshGenerator.AddSubmesh(currentInstructionsSubmeshesItems[i], updateTriangles);
} else {
meshGenerator.BuildMeshWithArrays(currentInstructions, updateTriangles);
}
buffers.UpdateSharedMaterials(currentInstructions.submeshInstructions);
// STEP 3: modify mesh.
var mesh = smartMesh.mesh;
if (meshGenerator.VertexCount <= 0) { // Clear an empty mesh
updateTriangles = false;
mesh.Clear();
} else {
meshGenerator.FillVertexData(mesh);
if (updateTriangles) {
meshGenerator.FillTriangles(mesh);
meshRenderer.sharedMaterials = buffers.GetUpdatedSharedMaterialsArray();
} else if (buffers.MaterialsChangedInLastUpdate()) {
meshRenderer.sharedMaterials = buffers.GetUpdatedSharedMaterialsArray();
}
meshGenerator.FillLateVertexData(mesh);
}
meshFilter.sharedMesh = mesh;
smartMesh.instructionUsed.Set(currentInstructions);
if (OnMeshAndMaterialsUpdated != null)
OnMeshAndMaterialsUpdated(this);
}
public void SetPropertyBlock (MaterialPropertyBlock block) {
LazyIntialize();
meshRenderer.SetPropertyBlock(block);
}
public static SkeletonPartsRenderer NewPartsRendererGameObject (Transform parent, string name, int sortingOrder = 0) {
var go = new GameObject(name, typeof(MeshFilter), typeof(MeshRenderer));
go.transform.SetParent(parent, false);
var returnComponent = go.AddComponent<SkeletonPartsRenderer>();
returnComponent.MeshRenderer.sortingOrder = sortingOrder;
return returnComponent;
}
}
}
/******************************************************************************
* 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.
*****************************************************************************/
using UnityEngine;
namespace Spine.Unity {
[RequireComponent(typeof(MeshRenderer), typeof(MeshFilter))]
[HelpURL("http://esotericsoftware.com/spine-unity#SkeletonRenderSeparator")]
public class SkeletonPartsRenderer : MonoBehaviour {
#region Properties
MeshGenerator meshGenerator;
public MeshGenerator MeshGenerator {
get {
LazyIntialize();
return meshGenerator;
}
}
MeshRenderer meshRenderer;
public MeshRenderer MeshRenderer {
get {
LazyIntialize();
return meshRenderer;
}
}
MeshFilter meshFilter;
public MeshFilter MeshFilter {
get {
LazyIntialize();
return meshFilter;
}
}
#endregion
#region Callback Delegates
public delegate void SkeletonPartsRendererDelegate (SkeletonPartsRenderer skeletonPartsRenderer);
/// <summary>OnMeshAndMaterialsUpdated is called at the end of LateUpdate after the Mesh and
/// all materials have been updated.</summary>
public event SkeletonPartsRendererDelegate OnMeshAndMaterialsUpdated;
#endregion
MeshRendererBuffers buffers;
SkeletonRendererInstruction currentInstructions = new SkeletonRendererInstruction();
void LazyIntialize () {
if (buffers == null) {
buffers = new MeshRendererBuffers();
buffers.Initialize();
if (meshGenerator != null) return;
meshGenerator = new MeshGenerator();
meshFilter = GetComponent<MeshFilter>();
meshRenderer = GetComponent<MeshRenderer>();
currentInstructions.Clear();
}
}
public void ClearMesh () {
LazyIntialize();
meshFilter.sharedMesh = null;
}
public void RenderParts (ExposedList<SubmeshInstruction> instructions, int startSubmesh, int endSubmesh) {
LazyIntialize();
// STEP 1: Create instruction
var smartMesh = buffers.GetNextMesh();
currentInstructions.SetWithSubset(instructions, startSubmesh, endSubmesh);
bool updateTriangles = SkeletonRendererInstruction.GeometryNotEqual(currentInstructions, smartMesh.instructionUsed);
// STEP 2: Generate mesh buffers.
var currentInstructionsSubmeshesItems = currentInstructions.submeshInstructions.Items;
meshGenerator.Begin();
if (currentInstructions.hasActiveClipping) {
for (int i = 0; i < currentInstructions.submeshInstructions.Count; i++)
meshGenerator.AddSubmesh(currentInstructionsSubmeshesItems[i], updateTriangles);
} else {
meshGenerator.BuildMeshWithArrays(currentInstructions, updateTriangles);
}
buffers.UpdateSharedMaterials(currentInstructions.submeshInstructions);
// STEP 3: modify mesh.
var mesh = smartMesh.mesh;
if (meshGenerator.VertexCount <= 0) { // Clear an empty mesh
updateTriangles = false;
mesh.Clear();
} else {
meshGenerator.FillVertexData(mesh);
if (updateTriangles) {
meshGenerator.FillTriangles(mesh);
meshRenderer.sharedMaterials = buffers.GetUpdatedSharedMaterialsArray();
} else if (buffers.MaterialsChangedInLastUpdate()) {
meshRenderer.sharedMaterials = buffers.GetUpdatedSharedMaterialsArray();
}
meshGenerator.FillLateVertexData(mesh);
}
meshFilter.sharedMesh = mesh;
smartMesh.instructionUsed.Set(currentInstructions);
if (OnMeshAndMaterialsUpdated != null)
OnMeshAndMaterialsUpdated(this);
}
public void SetPropertyBlock (MaterialPropertyBlock block) {
LazyIntialize();
meshRenderer.SetPropertyBlock(block);
}
public static SkeletonPartsRenderer NewPartsRendererGameObject (Transform parent, string name, int sortingOrder = 0) {
var go = new GameObject(name, typeof(MeshFilter), typeof(MeshRenderer));
go.transform.SetParent(parent, false);
var returnComponent = go.AddComponent<SkeletonPartsRenderer>();
returnComponent.MeshRenderer.sortingOrder = sortingOrder;
return returnComponent;
}
}
}

View File

@@ -1,269 +1,269 @@
/******************************************************************************
* 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.
*****************************************************************************/
#if UNITY_2018_3 || UNITY_2019 || UNITY_2018_3_OR_NEWER
#define NEW_PREFAB_SYSTEM
#endif
#define SPINE_OPTIONAL_RENDEROVERRIDE
using System.Collections.Generic;
using UnityEngine;
namespace Spine.Unity {
#if NEW_PREFAB_SYSTEM
[ExecuteAlways]
#else
[ExecuteInEditMode]
#endif
[HelpURL("http://esotericsoftware.com/spine-unity#SkeletonRenderSeparator")]
public class SkeletonRenderSeparator : MonoBehaviour {
public const int DefaultSortingOrderIncrement = 5;
#region Inspector
[SerializeField]
protected SkeletonRenderer skeletonRenderer;
public SkeletonRenderer SkeletonRenderer {
get { return skeletonRenderer; }
set {
#if SPINE_OPTIONAL_RENDEROVERRIDE
if (skeletonRenderer != null)
skeletonRenderer.GenerateMeshOverride -= HandleRender;
#endif
skeletonRenderer = value;
if (value == null)
this.enabled = false;
}
}
MeshRenderer mainMeshRenderer;
public bool copyPropertyBlock = true;
[Tooltip("Copies MeshRenderer flags into each parts renderer")]
public bool copyMeshRendererFlags = true;
public List<Spine.Unity.SkeletonPartsRenderer> partsRenderers = new List<SkeletonPartsRenderer>();
#if UNITY_EDITOR
void Reset () {
if (skeletonRenderer == null)
skeletonRenderer = GetComponent<SkeletonRenderer>();
}
#endif
#endregion
#region Callback Delegates
/// <summary>OnMeshAndMaterialsUpdated is called at the end of LateUpdate after the Mesh and
/// all materials have been updated.</summary>
public event SkeletonRenderer.SkeletonRendererDelegate OnMeshAndMaterialsUpdated;
#endregion
#region Runtime Instantiation
/// <summary>Adds a SkeletonRenderSeparator and child SkeletonPartsRenderer GameObjects to a given SkeletonRenderer.</summary>
/// <returns>The to skeleton renderer.</returns>
/// <param name="skeletonRenderer">The target SkeletonRenderer or SkeletonAnimation.</param>
/// <param name="sortingLayerID">Sorting layer to be used for the parts renderers.</param>
/// <param name="extraPartsRenderers">Number of additional SkeletonPartsRenderers on top of the ones determined by counting the number of separator slots.</param>
/// <param name="sortingOrderIncrement">The integer to increment the sorting order per SkeletonPartsRenderer to separate them.</param>
/// <param name="baseSortingOrder">The sorting order value of the first SkeletonPartsRenderer.</param>
/// <param name="addMinimumPartsRenderers">If set to <c>true</c>, a minimum number of SkeletonPartsRenderer GameObjects (determined by separatorSlots.Count + 1) will be added.</param>
public static SkeletonRenderSeparator AddToSkeletonRenderer (SkeletonRenderer skeletonRenderer, int sortingLayerID = 0, int extraPartsRenderers = 0, int sortingOrderIncrement = DefaultSortingOrderIncrement, int baseSortingOrder = 0, bool addMinimumPartsRenderers = true) {
if (skeletonRenderer == null) {
Debug.Log("Tried to add SkeletonRenderSeparator to a null SkeletonRenderer reference.");
return null;
}
var srs = skeletonRenderer.gameObject.AddComponent<SkeletonRenderSeparator>();
srs.skeletonRenderer = skeletonRenderer;
skeletonRenderer.Initialize(false);
int count = extraPartsRenderers;
if (addMinimumPartsRenderers)
count = extraPartsRenderers + skeletonRenderer.separatorSlots.Count + 1;
var skeletonRendererTransform = skeletonRenderer.transform;
var componentRenderers = srs.partsRenderers;
for (int i = 0; i < count; i++) {
var spr = SkeletonPartsRenderer.NewPartsRendererGameObject(skeletonRendererTransform, i.ToString());
var mr = spr.MeshRenderer;
mr.sortingLayerID = sortingLayerID;
mr.sortingOrder = baseSortingOrder + (i * sortingOrderIncrement);
componentRenderers.Add(spr);
}
srs.OnEnable();
#if UNITY_EDITOR
// Make sure editor updates properly in edit mode.
if (!Application.isPlaying) {
skeletonRenderer.enabled = false;
skeletonRenderer.enabled = true;
skeletonRenderer.LateUpdate();
}
#endif
return srs;
}
/// <summary>Add a child SkeletonPartsRenderer GameObject to this SkeletonRenderSeparator.</summary>
public SkeletonPartsRenderer AddPartsRenderer (int sortingOrderIncrement = DefaultSortingOrderIncrement, string name = null) {
int sortingLayerID = 0;
int sortingOrder = 0;
if (partsRenderers.Count > 0) {
var previous = partsRenderers[partsRenderers.Count - 1];
var previousMeshRenderer = previous.MeshRenderer;
sortingLayerID = previousMeshRenderer.sortingLayerID;
sortingOrder = previousMeshRenderer.sortingOrder + sortingOrderIncrement;
}
if (string.IsNullOrEmpty(name))
name = partsRenderers.Count.ToString();
var spr = SkeletonPartsRenderer.NewPartsRendererGameObject(skeletonRenderer.transform, name);
partsRenderers.Add(spr);
var mr = spr.MeshRenderer;
mr.sortingLayerID = sortingLayerID;
mr.sortingOrder = sortingOrder;
return spr;
}
#endregion
public void OnEnable () {
if (skeletonRenderer == null) return;
if (copiedBlock == null) copiedBlock = new MaterialPropertyBlock();
mainMeshRenderer = skeletonRenderer.GetComponent<MeshRenderer>();
#if SPINE_OPTIONAL_RENDEROVERRIDE
skeletonRenderer.GenerateMeshOverride -= HandleRender;
skeletonRenderer.GenerateMeshOverride += HandleRender;
#endif
if (copyMeshRendererFlags) {
var lightProbeUsage = mainMeshRenderer.lightProbeUsage;
bool receiveShadows = mainMeshRenderer.receiveShadows;
var reflectionProbeUsage = mainMeshRenderer.reflectionProbeUsage;
var shadowCastingMode = mainMeshRenderer.shadowCastingMode;
var motionVectorGenerationMode = mainMeshRenderer.motionVectorGenerationMode;
var probeAnchor = mainMeshRenderer.probeAnchor;
for (int i = 0; i < partsRenderers.Count; i++) {
var currentRenderer = partsRenderers[i];
if (currentRenderer == null) continue; // skip null items.
var mr = currentRenderer.MeshRenderer;
mr.lightProbeUsage = lightProbeUsage;
mr.receiveShadows = receiveShadows;
mr.reflectionProbeUsage = reflectionProbeUsage;
mr.shadowCastingMode = shadowCastingMode;
mr.motionVectorGenerationMode = motionVectorGenerationMode;
mr.probeAnchor = probeAnchor;
}
}
}
public void OnDisable () {
if (skeletonRenderer == null) return;
#if SPINE_OPTIONAL_RENDEROVERRIDE
skeletonRenderer.GenerateMeshOverride -= HandleRender;
#endif
skeletonRenderer.LateUpdate();
foreach (var partsRenderer in partsRenderers) {
if (partsRenderer != null)
partsRenderer.ClearMesh();
}
}
MaterialPropertyBlock copiedBlock;
void HandleRender (SkeletonRendererInstruction instruction) {
int rendererCount = partsRenderers.Count;
if (rendererCount <= 0) return;
if (copyPropertyBlock)
mainMeshRenderer.GetPropertyBlock(copiedBlock);
var settings = new MeshGenerator.Settings {
addNormals = skeletonRenderer.addNormals,
calculateTangents = skeletonRenderer.calculateTangents,
immutableTriangles = false, // parts cannot do immutable triangles.
pmaVertexColors = skeletonRenderer.pmaVertexColors,
tintBlack = skeletonRenderer.tintBlack,
useClipping = true,
zSpacing = skeletonRenderer.zSpacing
};
var submeshInstructions = instruction.submeshInstructions;
var submeshInstructionsItems = submeshInstructions.Items;
int lastSubmeshInstruction = submeshInstructions.Count - 1;
int rendererIndex = 0;
var currentRenderer = partsRenderers[rendererIndex];
for (int si = 0, start = 0; si <= lastSubmeshInstruction; si++) {
if (currentRenderer == null)
continue;
if (submeshInstructionsItems[si].forceSeparate || si == lastSubmeshInstruction) {
// Apply properties
var meshGenerator = currentRenderer.MeshGenerator;
meshGenerator.settings = settings;
if (copyPropertyBlock)
currentRenderer.SetPropertyBlock(copiedBlock);
// Render
currentRenderer.RenderParts(instruction.submeshInstructions, start, si + 1);
start = si + 1;
rendererIndex++;
if (rendererIndex < rendererCount) {
currentRenderer = partsRenderers[rendererIndex];
} else {
// Not enough renderers. Skip the rest of the instructions.
break;
}
}
}
if (OnMeshAndMaterialsUpdated != null)
OnMeshAndMaterialsUpdated(this.skeletonRenderer);
// Clear extra renderers if they exist.
for (; rendererIndex < rendererCount; rendererIndex++) {
currentRenderer = partsRenderers[rendererIndex];
if (currentRenderer != null)
partsRenderers[rendererIndex].ClearMesh();
}
}
}
}
/******************************************************************************
* 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.
*****************************************************************************/
#if UNITY_2018_3 || UNITY_2019 || UNITY_2018_3_OR_NEWER
#define NEW_PREFAB_SYSTEM
#endif
#define SPINE_OPTIONAL_RENDEROVERRIDE
using System.Collections.Generic;
using UnityEngine;
namespace Spine.Unity {
#if NEW_PREFAB_SYSTEM
[ExecuteAlways]
#else
[ExecuteInEditMode]
#endif
[HelpURL("http://esotericsoftware.com/spine-unity#SkeletonRenderSeparator")]
public class SkeletonRenderSeparator : MonoBehaviour {
public const int DefaultSortingOrderIncrement = 5;
#region Inspector
[SerializeField]
protected SkeletonRenderer skeletonRenderer;
public SkeletonRenderer SkeletonRenderer {
get { return skeletonRenderer; }
set {
#if SPINE_OPTIONAL_RENDEROVERRIDE
if (skeletonRenderer != null)
skeletonRenderer.GenerateMeshOverride -= HandleRender;
#endif
skeletonRenderer = value;
if (value == null)
this.enabled = false;
}
}
MeshRenderer mainMeshRenderer;
public bool copyPropertyBlock = true;
[Tooltip("Copies MeshRenderer flags into each parts renderer")]
public bool copyMeshRendererFlags = true;
public List<Spine.Unity.SkeletonPartsRenderer> partsRenderers = new List<SkeletonPartsRenderer>();
#if UNITY_EDITOR
void Reset () {
if (skeletonRenderer == null)
skeletonRenderer = GetComponent<SkeletonRenderer>();
}
#endif
#endregion
#region Callback Delegates
/// <summary>OnMeshAndMaterialsUpdated is called at the end of LateUpdate after the Mesh and
/// all materials have been updated.</summary>
public event SkeletonRenderer.SkeletonRendererDelegate OnMeshAndMaterialsUpdated;
#endregion
#region Runtime Instantiation
/// <summary>Adds a SkeletonRenderSeparator and child SkeletonPartsRenderer GameObjects to a given SkeletonRenderer.</summary>
/// <returns>The to skeleton renderer.</returns>
/// <param name="skeletonRenderer">The target SkeletonRenderer or SkeletonAnimation.</param>
/// <param name="sortingLayerID">Sorting layer to be used for the parts renderers.</param>
/// <param name="extraPartsRenderers">Number of additional SkeletonPartsRenderers on top of the ones determined by counting the number of separator slots.</param>
/// <param name="sortingOrderIncrement">The integer to increment the sorting order per SkeletonPartsRenderer to separate them.</param>
/// <param name="baseSortingOrder">The sorting order value of the first SkeletonPartsRenderer.</param>
/// <param name="addMinimumPartsRenderers">If set to <c>true</c>, a minimum number of SkeletonPartsRenderer GameObjects (determined by separatorSlots.Count + 1) will be added.</param>
public static SkeletonRenderSeparator AddToSkeletonRenderer (SkeletonRenderer skeletonRenderer, int sortingLayerID = 0, int extraPartsRenderers = 0, int sortingOrderIncrement = DefaultSortingOrderIncrement, int baseSortingOrder = 0, bool addMinimumPartsRenderers = true) {
if (skeletonRenderer == null) {
Debug.Log("Tried to add SkeletonRenderSeparator to a null SkeletonRenderer reference.");
return null;
}
var srs = skeletonRenderer.gameObject.AddComponent<SkeletonRenderSeparator>();
srs.skeletonRenderer = skeletonRenderer;
skeletonRenderer.Initialize(false);
int count = extraPartsRenderers;
if (addMinimumPartsRenderers)
count = extraPartsRenderers + skeletonRenderer.separatorSlots.Count + 1;
var skeletonRendererTransform = skeletonRenderer.transform;
var componentRenderers = srs.partsRenderers;
for (int i = 0; i < count; i++) {
var spr = SkeletonPartsRenderer.NewPartsRendererGameObject(skeletonRendererTransform, i.ToString());
var mr = spr.MeshRenderer;
mr.sortingLayerID = sortingLayerID;
mr.sortingOrder = baseSortingOrder + (i * sortingOrderIncrement);
componentRenderers.Add(spr);
}
srs.OnEnable();
#if UNITY_EDITOR
// Make sure editor updates properly in edit mode.
if (!Application.isPlaying) {
skeletonRenderer.enabled = false;
skeletonRenderer.enabled = true;
skeletonRenderer.LateUpdate();
}
#endif
return srs;
}
/// <summary>Add a child SkeletonPartsRenderer GameObject to this SkeletonRenderSeparator.</summary>
public SkeletonPartsRenderer AddPartsRenderer (int sortingOrderIncrement = DefaultSortingOrderIncrement, string name = null) {
int sortingLayerID = 0;
int sortingOrder = 0;
if (partsRenderers.Count > 0) {
var previous = partsRenderers[partsRenderers.Count - 1];
var previousMeshRenderer = previous.MeshRenderer;
sortingLayerID = previousMeshRenderer.sortingLayerID;
sortingOrder = previousMeshRenderer.sortingOrder + sortingOrderIncrement;
}
if (string.IsNullOrEmpty(name))
name = partsRenderers.Count.ToString();
var spr = SkeletonPartsRenderer.NewPartsRendererGameObject(skeletonRenderer.transform, name);
partsRenderers.Add(spr);
var mr = spr.MeshRenderer;
mr.sortingLayerID = sortingLayerID;
mr.sortingOrder = sortingOrder;
return spr;
}
#endregion
public void OnEnable () {
if (skeletonRenderer == null) return;
if (copiedBlock == null) copiedBlock = new MaterialPropertyBlock();
mainMeshRenderer = skeletonRenderer.GetComponent<MeshRenderer>();
#if SPINE_OPTIONAL_RENDEROVERRIDE
skeletonRenderer.GenerateMeshOverride -= HandleRender;
skeletonRenderer.GenerateMeshOverride += HandleRender;
#endif
if (copyMeshRendererFlags) {
var lightProbeUsage = mainMeshRenderer.lightProbeUsage;
bool receiveShadows = mainMeshRenderer.receiveShadows;
var reflectionProbeUsage = mainMeshRenderer.reflectionProbeUsage;
var shadowCastingMode = mainMeshRenderer.shadowCastingMode;
var motionVectorGenerationMode = mainMeshRenderer.motionVectorGenerationMode;
var probeAnchor = mainMeshRenderer.probeAnchor;
for (int i = 0; i < partsRenderers.Count; i++) {
var currentRenderer = partsRenderers[i];
if (currentRenderer == null) continue; // skip null items.
var mr = currentRenderer.MeshRenderer;
mr.lightProbeUsage = lightProbeUsage;
mr.receiveShadows = receiveShadows;
mr.reflectionProbeUsage = reflectionProbeUsage;
mr.shadowCastingMode = shadowCastingMode;
mr.motionVectorGenerationMode = motionVectorGenerationMode;
mr.probeAnchor = probeAnchor;
}
}
}
public void OnDisable () {
if (skeletonRenderer == null) return;
#if SPINE_OPTIONAL_RENDEROVERRIDE
skeletonRenderer.GenerateMeshOverride -= HandleRender;
#endif
skeletonRenderer.LateUpdate();
foreach (var partsRenderer in partsRenderers) {
if (partsRenderer != null)
partsRenderer.ClearMesh();
}
}
MaterialPropertyBlock copiedBlock;
void HandleRender (SkeletonRendererInstruction instruction) {
int rendererCount = partsRenderers.Count;
if (rendererCount <= 0) return;
if (copyPropertyBlock)
mainMeshRenderer.GetPropertyBlock(copiedBlock);
var settings = new MeshGenerator.Settings {
addNormals = skeletonRenderer.addNormals,
calculateTangents = skeletonRenderer.calculateTangents,
immutableTriangles = false, // parts cannot do immutable triangles.
pmaVertexColors = skeletonRenderer.pmaVertexColors,
tintBlack = skeletonRenderer.tintBlack,
useClipping = true,
zSpacing = skeletonRenderer.zSpacing
};
var submeshInstructions = instruction.submeshInstructions;
var submeshInstructionsItems = submeshInstructions.Items;
int lastSubmeshInstruction = submeshInstructions.Count - 1;
int rendererIndex = 0;
var currentRenderer = partsRenderers[rendererIndex];
for (int si = 0, start = 0; si <= lastSubmeshInstruction; si++) {
if (currentRenderer == null)
continue;
if (submeshInstructionsItems[si].forceSeparate || si == lastSubmeshInstruction) {
// Apply properties
var meshGenerator = currentRenderer.MeshGenerator;
meshGenerator.settings = settings;
if (copyPropertyBlock)
currentRenderer.SetPropertyBlock(copiedBlock);
// Render
currentRenderer.RenderParts(instruction.submeshInstructions, start, si + 1);
start = si + 1;
rendererIndex++;
if (rendererIndex < rendererCount) {
currentRenderer = partsRenderers[rendererIndex];
} else {
// Not enough renderers. Skip the rest of the instructions.
break;
}
}
}
if (OnMeshAndMaterialsUpdated != null)
OnMeshAndMaterialsUpdated(this.skeletonRenderer);
// Clear extra renderers if they exist.
for (; rendererIndex < rendererCount; rendererIndex++) {
currentRenderer = partsRenderers[rendererIndex];
if (currentRenderer != null)
partsRenderers[rendererIndex].ClearMesh();
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,211 +1,211 @@
/******************************************************************************
* 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.
*****************************************************************************/
#if UNITY_2018_3 || UNITY_2019 || UNITY_2018_3_OR_NEWER
#define NEW_PREFAB_SYSTEM
#endif
using System;
using System.Collections.Generic;
using UnityEngine;
namespace Spine.Unity {
#if NEW_PREFAB_SYSTEM
[ExecuteAlways]
#else
[ExecuteInEditMode]
#endif
[HelpURL("http://esotericsoftware.com/spine-unity#SkeletonGraphicCustomMaterials")]
public class SkeletonGraphicCustomMaterials : MonoBehaviour {
#region Inspector
public SkeletonGraphic skeletonGraphic;
[SerializeField] protected List<AtlasMaterialOverride> customMaterialOverrides = new List<AtlasMaterialOverride>();
[SerializeField] protected List<AtlasTextureOverride> customTextureOverrides = new List<AtlasTextureOverride>();
#if UNITY_EDITOR
void Reset () {
skeletonGraphic = GetComponent<SkeletonGraphic>();
// Populate material list
if (skeletonGraphic != null && skeletonGraphic.skeletonDataAsset != null) {
var atlasAssets = skeletonGraphic.skeletonDataAsset.atlasAssets;
var initialAtlasMaterialOverrides = new List<AtlasMaterialOverride>();
foreach (AtlasAssetBase atlasAsset in atlasAssets) {
foreach (Material atlasMaterial in atlasAsset.Materials) {
var atlasMaterialOverride = new AtlasMaterialOverride {
overrideEnabled = false,
originalTexture = atlasMaterial.mainTexture
};
initialAtlasMaterialOverrides.Add(atlasMaterialOverride);
}
}
customMaterialOverrides = initialAtlasMaterialOverrides;
}
// Populate texture list
if (skeletonGraphic != null && skeletonGraphic.skeletonDataAsset != null) {
var atlasAssets = skeletonGraphic.skeletonDataAsset.atlasAssets;
var initialAtlasTextureOverrides = new List<AtlasTextureOverride>();
foreach (AtlasAssetBase atlasAsset in atlasAssets) {
foreach (Material atlasMaterial in atlasAsset.Materials) {
var atlasTextureOverride = new AtlasTextureOverride {
overrideEnabled = false,
originalTexture = atlasMaterial.mainTexture
};
initialAtlasTextureOverrides.Add(atlasTextureOverride);
}
}
customTextureOverrides = initialAtlasTextureOverrides;
}
}
#endif
#endregion
void SetCustomMaterialOverrides () {
if (skeletonGraphic == null) {
Debug.LogError("skeletonGraphic == null");
return;
}
for (int i = 0; i < customMaterialOverrides.Count; i++) {
AtlasMaterialOverride atlasMaterialOverride = customMaterialOverrides[i];
if (atlasMaterialOverride.overrideEnabled)
skeletonGraphic.CustomMaterialOverride[atlasMaterialOverride.originalTexture] = atlasMaterialOverride.replacementMaterial;
}
}
void RemoveCustomMaterialOverrides () {
if (skeletonGraphic == null) {
Debug.LogError("skeletonGraphic == null");
return;
}
for (int i = 0; i < customMaterialOverrides.Count; i++) {
AtlasMaterialOverride atlasMaterialOverride = customMaterialOverrides[i];
Material currentMaterial;
if (!skeletonGraphic.CustomMaterialOverride.TryGetValue(atlasMaterialOverride.originalTexture, out currentMaterial))
continue;
// Do not revert the material if it was changed by something else
if (currentMaterial != atlasMaterialOverride.replacementMaterial)
continue;
skeletonGraphic.CustomMaterialOverride.Remove(atlasMaterialOverride.originalTexture);
}
}
void SetCustomTextureOverrides () {
if (skeletonGraphic == null) {
Debug.LogError("skeletonGraphic == null");
return;
}
for (int i = 0; i < customTextureOverrides.Count; i++) {
AtlasTextureOverride atlasTextureOverride = customTextureOverrides[i];
if (atlasTextureOverride.overrideEnabled)
skeletonGraphic.CustomTextureOverride[atlasTextureOverride.originalTexture] = atlasTextureOverride.replacementTexture;
}
}
void RemoveCustomTextureOverrides () {
if (skeletonGraphic == null) {
Debug.LogError("skeletonGraphic == null");
return;
}
for (int i = 0; i < customTextureOverrides.Count; i++) {
AtlasTextureOverride atlasTextureOverride = customTextureOverrides[i];
Texture currentTexture;
if (!skeletonGraphic.CustomTextureOverride.TryGetValue(atlasTextureOverride.originalTexture, out currentTexture))
continue;
// Do not revert the material if it was changed by something else
if (currentTexture != atlasTextureOverride.replacementTexture)
continue;
skeletonGraphic.CustomTextureOverride.Remove(atlasTextureOverride.originalTexture);
}
}
// OnEnable applies the overrides at runtime, and when the editor loads.
void OnEnable () {
if (skeletonGraphic == null)
skeletonGraphic = GetComponent<SkeletonGraphic>();
if (skeletonGraphic == null) {
Debug.LogError("skeletonGraphic == null");
return;
}
skeletonGraphic.Initialize(false);
SetCustomMaterialOverrides();
SetCustomTextureOverrides();
}
// OnDisable removes the overrides at runtime, and in the editor when the component is disabled or destroyed.
void OnDisable () {
if (skeletonGraphic == null) {
Debug.LogError("skeletonGraphic == null");
return;
}
RemoveCustomMaterialOverrides();
RemoveCustomTextureOverrides();
}
[Serializable]
public struct AtlasMaterialOverride : IEquatable<AtlasMaterialOverride> {
public bool overrideEnabled;
public Texture originalTexture;
public Material replacementMaterial;
public bool Equals (AtlasMaterialOverride other) {
return overrideEnabled == other.overrideEnabled && originalTexture == other.originalTexture && replacementMaterial == other.replacementMaterial;
}
}
[Serializable]
public struct AtlasTextureOverride : IEquatable<AtlasTextureOverride> {
public bool overrideEnabled;
public Texture originalTexture;
public Texture replacementTexture;
public bool Equals (AtlasTextureOverride other) {
return overrideEnabled == other.overrideEnabled && originalTexture == other.originalTexture && replacementTexture == other.replacementTexture;
}
}
}
}
/******************************************************************************
* 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.
*****************************************************************************/
#if UNITY_2018_3 || UNITY_2019 || UNITY_2018_3_OR_NEWER
#define NEW_PREFAB_SYSTEM
#endif
using System;
using System.Collections.Generic;
using UnityEngine;
namespace Spine.Unity {
#if NEW_PREFAB_SYSTEM
[ExecuteAlways]
#else
[ExecuteInEditMode]
#endif
[HelpURL("http://esotericsoftware.com/spine-unity#SkeletonGraphicCustomMaterials")]
public class SkeletonGraphicCustomMaterials : MonoBehaviour {
#region Inspector
public SkeletonGraphic skeletonGraphic;
[SerializeField] protected List<AtlasMaterialOverride> customMaterialOverrides = new List<AtlasMaterialOverride>();
[SerializeField] protected List<AtlasTextureOverride> customTextureOverrides = new List<AtlasTextureOverride>();
#if UNITY_EDITOR
void Reset () {
skeletonGraphic = GetComponent<SkeletonGraphic>();
// Populate material list
if (skeletonGraphic != null && skeletonGraphic.skeletonDataAsset != null) {
var atlasAssets = skeletonGraphic.skeletonDataAsset.atlasAssets;
var initialAtlasMaterialOverrides = new List<AtlasMaterialOverride>();
foreach (AtlasAssetBase atlasAsset in atlasAssets) {
foreach (Material atlasMaterial in atlasAsset.Materials) {
var atlasMaterialOverride = new AtlasMaterialOverride {
overrideEnabled = false,
originalTexture = atlasMaterial.mainTexture
};
initialAtlasMaterialOverrides.Add(atlasMaterialOverride);
}
}
customMaterialOverrides = initialAtlasMaterialOverrides;
}
// Populate texture list
if (skeletonGraphic != null && skeletonGraphic.skeletonDataAsset != null) {
var atlasAssets = skeletonGraphic.skeletonDataAsset.atlasAssets;
var initialAtlasTextureOverrides = new List<AtlasTextureOverride>();
foreach (AtlasAssetBase atlasAsset in atlasAssets) {
foreach (Material atlasMaterial in atlasAsset.Materials) {
var atlasTextureOverride = new AtlasTextureOverride {
overrideEnabled = false,
originalTexture = atlasMaterial.mainTexture
};
initialAtlasTextureOverrides.Add(atlasTextureOverride);
}
}
customTextureOverrides = initialAtlasTextureOverrides;
}
}
#endif
#endregion
void SetCustomMaterialOverrides () {
if (skeletonGraphic == null) {
Debug.LogError("skeletonGraphic == null");
return;
}
for (int i = 0; i < customMaterialOverrides.Count; i++) {
AtlasMaterialOverride atlasMaterialOverride = customMaterialOverrides[i];
if (atlasMaterialOverride.overrideEnabled)
skeletonGraphic.CustomMaterialOverride[atlasMaterialOverride.originalTexture] = atlasMaterialOverride.replacementMaterial;
}
}
void RemoveCustomMaterialOverrides () {
if (skeletonGraphic == null) {
Debug.LogError("skeletonGraphic == null");
return;
}
for (int i = 0; i < customMaterialOverrides.Count; i++) {
AtlasMaterialOverride atlasMaterialOverride = customMaterialOverrides[i];
Material currentMaterial;
if (!skeletonGraphic.CustomMaterialOverride.TryGetValue(atlasMaterialOverride.originalTexture, out currentMaterial))
continue;
// Do not revert the material if it was changed by something else
if (currentMaterial != atlasMaterialOverride.replacementMaterial)
continue;
skeletonGraphic.CustomMaterialOverride.Remove(atlasMaterialOverride.originalTexture);
}
}
void SetCustomTextureOverrides () {
if (skeletonGraphic == null) {
Debug.LogError("skeletonGraphic == null");
return;
}
for (int i = 0; i < customTextureOverrides.Count; i++) {
AtlasTextureOverride atlasTextureOverride = customTextureOverrides[i];
if (atlasTextureOverride.overrideEnabled)
skeletonGraphic.CustomTextureOverride[atlasTextureOverride.originalTexture] = atlasTextureOverride.replacementTexture;
}
}
void RemoveCustomTextureOverrides () {
if (skeletonGraphic == null) {
Debug.LogError("skeletonGraphic == null");
return;
}
for (int i = 0; i < customTextureOverrides.Count; i++) {
AtlasTextureOverride atlasTextureOverride = customTextureOverrides[i];
Texture currentTexture;
if (!skeletonGraphic.CustomTextureOverride.TryGetValue(atlasTextureOverride.originalTexture, out currentTexture))
continue;
// Do not revert the material if it was changed by something else
if (currentTexture != atlasTextureOverride.replacementTexture)
continue;
skeletonGraphic.CustomTextureOverride.Remove(atlasTextureOverride.originalTexture);
}
}
// OnEnable applies the overrides at runtime, and when the editor loads.
void OnEnable () {
if (skeletonGraphic == null)
skeletonGraphic = GetComponent<SkeletonGraphic>();
if (skeletonGraphic == null) {
Debug.LogError("skeletonGraphic == null");
return;
}
skeletonGraphic.Initialize(false);
SetCustomMaterialOverrides();
SetCustomTextureOverrides();
}
// OnDisable removes the overrides at runtime, and in the editor when the component is disabled or destroyed.
void OnDisable () {
if (skeletonGraphic == null) {
Debug.LogError("skeletonGraphic == null");
return;
}
RemoveCustomMaterialOverrides();
RemoveCustomTextureOverrides();
}
[Serializable]
public struct AtlasMaterialOverride : IEquatable<AtlasMaterialOverride> {
public bool overrideEnabled;
public Texture originalTexture;
public Material replacementMaterial;
public bool Equals (AtlasMaterialOverride other) {
return overrideEnabled == other.overrideEnabled && originalTexture == other.originalTexture && replacementMaterial == other.replacementMaterial;
}
}
[Serializable]
public struct AtlasTextureOverride : IEquatable<AtlasTextureOverride> {
public bool overrideEnabled;
public Texture originalTexture;
public Texture replacementTexture;
public bool Equals (AtlasTextureOverride other) {
return overrideEnabled == other.overrideEnabled && originalTexture == other.originalTexture && replacementTexture == other.replacementTexture;
}
}
}
}

View File

@@ -1,214 +1,214 @@
/******************************************************************************
* 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.
*****************************************************************************/
#if UNITY_2018_3 || UNITY_2019 || UNITY_2018_3_OR_NEWER
#define NEW_PREFAB_SYSTEM
#endif
#define SPINE_OPTIONAL_MATERIALOVERRIDE
// Contributed by: Lost Polygon
using System;
using System.Collections.Generic;
using UnityEngine;
namespace Spine.Unity {
#if NEW_PREFAB_SYSTEM
[ExecuteAlways]
#else
[ExecuteInEditMode]
#endif
[HelpURL("http://esotericsoftware.com/spine-unity#SkeletonRendererCustomMaterials")]
public class SkeletonRendererCustomMaterials : MonoBehaviour {
#region Inspector
public SkeletonRenderer skeletonRenderer;
[SerializeField] protected List<SlotMaterialOverride> customSlotMaterials = new List<SlotMaterialOverride>();
[SerializeField] protected List<AtlasMaterialOverride> customMaterialOverrides = new List<AtlasMaterialOverride>();
#if UNITY_EDITOR
void Reset () {
skeletonRenderer = GetComponent<SkeletonRenderer>();
// Populate atlas list
if (skeletonRenderer != null && skeletonRenderer.skeletonDataAsset != null) {
var atlasAssets = skeletonRenderer.skeletonDataAsset.atlasAssets;
var initialAtlasMaterialOverrides = new List<AtlasMaterialOverride>();
foreach (AtlasAssetBase atlasAsset in atlasAssets) {
foreach (Material atlasMaterial in atlasAsset.Materials) {
var atlasMaterialOverride = new AtlasMaterialOverride {
overrideDisabled = true,
originalMaterial = atlasMaterial
};
initialAtlasMaterialOverrides.Add(atlasMaterialOverride);
}
}
customMaterialOverrides = initialAtlasMaterialOverrides;
}
}
#endif
#endregion
void SetCustomSlotMaterials () {
if (skeletonRenderer == null) {
Debug.LogError("skeletonRenderer == null");
return;
}
for (int i = 0; i < customSlotMaterials.Count; i++) {
SlotMaterialOverride slotMaterialOverride = customSlotMaterials[i];
if (slotMaterialOverride.overrideDisabled || string.IsNullOrEmpty(slotMaterialOverride.slotName))
continue;
Slot slotObject = skeletonRenderer.skeleton.FindSlot(slotMaterialOverride.slotName);
if (slotObject != null)
skeletonRenderer.CustomSlotMaterials[slotObject] = slotMaterialOverride.material;
}
}
void RemoveCustomSlotMaterials () {
if (skeletonRenderer == null) {
Debug.LogError("skeletonRenderer == null");
return;
}
for (int i = 0; i < customSlotMaterials.Count; i++) {
SlotMaterialOverride slotMaterialOverride = customSlotMaterials[i];
if (string.IsNullOrEmpty(slotMaterialOverride.slotName))
continue;
Slot slotObject = skeletonRenderer.skeleton.FindSlot(slotMaterialOverride.slotName);
if (slotObject == null)
continue;
Material currentMaterial;
if (!skeletonRenderer.CustomSlotMaterials.TryGetValue(slotObject, out currentMaterial))
continue;
// Do not revert the material if it was changed by something else
if (currentMaterial != slotMaterialOverride.material)
continue;
skeletonRenderer.CustomSlotMaterials.Remove(slotObject);
}
}
void SetCustomMaterialOverrides () {
if (skeletonRenderer == null) {
Debug.LogError("skeletonRenderer == null");
return;
}
#if SPINE_OPTIONAL_MATERIALOVERRIDE
for (int i = 0; i < customMaterialOverrides.Count; i++) {
AtlasMaterialOverride atlasMaterialOverride = customMaterialOverrides[i];
if (atlasMaterialOverride.overrideDisabled)
continue;
skeletonRenderer.CustomMaterialOverride[atlasMaterialOverride.originalMaterial] = atlasMaterialOverride.replacementMaterial;
}
#endif
}
void RemoveCustomMaterialOverrides () {
if (skeletonRenderer == null) {
Debug.LogError("skeletonRenderer == null");
return;
}
#if SPINE_OPTIONAL_MATERIALOVERRIDE
for (int i = 0; i < customMaterialOverrides.Count; i++) {
AtlasMaterialOverride atlasMaterialOverride = customMaterialOverrides[i];
Material currentMaterial;
if (!skeletonRenderer.CustomMaterialOverride.TryGetValue(atlasMaterialOverride.originalMaterial, out currentMaterial))
continue;
// Do not revert the material if it was changed by something else
if (currentMaterial != atlasMaterialOverride.replacementMaterial)
continue;
skeletonRenderer.CustomMaterialOverride.Remove(atlasMaterialOverride.originalMaterial);
}
#endif
}
// OnEnable applies the overrides at runtime, and when the editor loads.
void OnEnable () {
if (skeletonRenderer == null)
skeletonRenderer = GetComponent<SkeletonRenderer>();
if (skeletonRenderer == null) {
Debug.LogError("skeletonRenderer == null");
return;
}
skeletonRenderer.Initialize(false);
SetCustomMaterialOverrides();
SetCustomSlotMaterials();
}
// OnDisable removes the overrides at runtime, and in the editor when the component is disabled or destroyed.
void OnDisable () {
if (skeletonRenderer == null) {
Debug.LogError("skeletonRenderer == null");
return;
}
RemoveCustomMaterialOverrides();
RemoveCustomSlotMaterials();
}
[Serializable]
public struct SlotMaterialOverride : IEquatable<SlotMaterialOverride> {
public bool overrideDisabled;
[SpineSlot]
public string slotName;
public Material material;
public bool Equals (SlotMaterialOverride other) {
return overrideDisabled == other.overrideDisabled && slotName == other.slotName && material == other.material;
}
}
[Serializable]
public struct AtlasMaterialOverride : IEquatable<AtlasMaterialOverride> {
public bool overrideDisabled;
public Material originalMaterial;
public Material replacementMaterial;
public bool Equals (AtlasMaterialOverride other) {
return overrideDisabled == other.overrideDisabled && originalMaterial == other.originalMaterial && replacementMaterial == other.replacementMaterial;
}
}
}
}
/******************************************************************************
* 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.
*****************************************************************************/
#if UNITY_2018_3 || UNITY_2019 || UNITY_2018_3_OR_NEWER
#define NEW_PREFAB_SYSTEM
#endif
#define SPINE_OPTIONAL_MATERIALOVERRIDE
// Contributed by: Lost Polygon
using System;
using System.Collections.Generic;
using UnityEngine;
namespace Spine.Unity {
#if NEW_PREFAB_SYSTEM
[ExecuteAlways]
#else
[ExecuteInEditMode]
#endif
[HelpURL("http://esotericsoftware.com/spine-unity#SkeletonRendererCustomMaterials")]
public class SkeletonRendererCustomMaterials : MonoBehaviour {
#region Inspector
public SkeletonRenderer skeletonRenderer;
[SerializeField] protected List<SlotMaterialOverride> customSlotMaterials = new List<SlotMaterialOverride>();
[SerializeField] protected List<AtlasMaterialOverride> customMaterialOverrides = new List<AtlasMaterialOverride>();
#if UNITY_EDITOR
void Reset () {
skeletonRenderer = GetComponent<SkeletonRenderer>();
// Populate atlas list
if (skeletonRenderer != null && skeletonRenderer.skeletonDataAsset != null) {
var atlasAssets = skeletonRenderer.skeletonDataAsset.atlasAssets;
var initialAtlasMaterialOverrides = new List<AtlasMaterialOverride>();
foreach (AtlasAssetBase atlasAsset in atlasAssets) {
foreach (Material atlasMaterial in atlasAsset.Materials) {
var atlasMaterialOverride = new AtlasMaterialOverride {
overrideDisabled = true,
originalMaterial = atlasMaterial
};
initialAtlasMaterialOverrides.Add(atlasMaterialOverride);
}
}
customMaterialOverrides = initialAtlasMaterialOverrides;
}
}
#endif
#endregion
void SetCustomSlotMaterials () {
if (skeletonRenderer == null) {
Debug.LogError("skeletonRenderer == null");
return;
}
for (int i = 0; i < customSlotMaterials.Count; i++) {
SlotMaterialOverride slotMaterialOverride = customSlotMaterials[i];
if (slotMaterialOverride.overrideDisabled || string.IsNullOrEmpty(slotMaterialOverride.slotName))
continue;
Slot slotObject = skeletonRenderer.skeleton.FindSlot(slotMaterialOverride.slotName);
if (slotObject != null)
skeletonRenderer.CustomSlotMaterials[slotObject] = slotMaterialOverride.material;
}
}
void RemoveCustomSlotMaterials () {
if (skeletonRenderer == null) {
Debug.LogError("skeletonRenderer == null");
return;
}
for (int i = 0; i < customSlotMaterials.Count; i++) {
SlotMaterialOverride slotMaterialOverride = customSlotMaterials[i];
if (string.IsNullOrEmpty(slotMaterialOverride.slotName))
continue;
Slot slotObject = skeletonRenderer.skeleton.FindSlot(slotMaterialOverride.slotName);
if (slotObject == null)
continue;
Material currentMaterial;
if (!skeletonRenderer.CustomSlotMaterials.TryGetValue(slotObject, out currentMaterial))
continue;
// Do not revert the material if it was changed by something else
if (currentMaterial != slotMaterialOverride.material)
continue;
skeletonRenderer.CustomSlotMaterials.Remove(slotObject);
}
}
void SetCustomMaterialOverrides () {
if (skeletonRenderer == null) {
Debug.LogError("skeletonRenderer == null");
return;
}
#if SPINE_OPTIONAL_MATERIALOVERRIDE
for (int i = 0; i < customMaterialOverrides.Count; i++) {
AtlasMaterialOverride atlasMaterialOverride = customMaterialOverrides[i];
if (atlasMaterialOverride.overrideDisabled)
continue;
skeletonRenderer.CustomMaterialOverride[atlasMaterialOverride.originalMaterial] = atlasMaterialOverride.replacementMaterial;
}
#endif
}
void RemoveCustomMaterialOverrides () {
if (skeletonRenderer == null) {
Debug.LogError("skeletonRenderer == null");
return;
}
#if SPINE_OPTIONAL_MATERIALOVERRIDE
for (int i = 0; i < customMaterialOverrides.Count; i++) {
AtlasMaterialOverride atlasMaterialOverride = customMaterialOverrides[i];
Material currentMaterial;
if (!skeletonRenderer.CustomMaterialOverride.TryGetValue(atlasMaterialOverride.originalMaterial, out currentMaterial))
continue;
// Do not revert the material if it was changed by something else
if (currentMaterial != atlasMaterialOverride.replacementMaterial)
continue;
skeletonRenderer.CustomMaterialOverride.Remove(atlasMaterialOverride.originalMaterial);
}
#endif
}
// OnEnable applies the overrides at runtime, and when the editor loads.
void OnEnable () {
if (skeletonRenderer == null)
skeletonRenderer = GetComponent<SkeletonRenderer>();
if (skeletonRenderer == null) {
Debug.LogError("skeletonRenderer == null");
return;
}
skeletonRenderer.Initialize(false);
SetCustomMaterialOverrides();
SetCustomSlotMaterials();
}
// OnDisable removes the overrides at runtime, and in the editor when the component is disabled or destroyed.
void OnDisable () {
if (skeletonRenderer == null) {
Debug.LogError("skeletonRenderer == null");
return;
}
RemoveCustomMaterialOverrides();
RemoveCustomSlotMaterials();
}
[Serializable]
public struct SlotMaterialOverride : IEquatable<SlotMaterialOverride> {
public bool overrideDisabled;
[SpineSlot]
public string slotName;
public Material material;
public bool Equals (SlotMaterialOverride other) {
return overrideDisabled == other.overrideDisabled && slotName == other.slotName && material == other.material;
}
}
[Serializable]
public struct AtlasMaterialOverride : IEquatable<AtlasMaterialOverride> {
public bool overrideDisabled;
public Material originalMaterial;
public Material replacementMaterial;
public bool Equals (AtlasMaterialOverride other) {
return overrideDisabled == other.overrideDisabled && originalMaterial == other.originalMaterial && replacementMaterial == other.replacementMaterial;
}
}
}
}

View File

@@ -1,92 +1,92 @@
/******************************************************************************
* 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.
*****************************************************************************/
using UnityEngine;
namespace Spine.Unity {
/// <summary>
/// Utility component to support flipping of 2D hinge chains (chains of HingeJoint2D objects) along
/// with the parent skeleton by activating the respective mirrored versions of the hinge chain.
/// Note: This component is automatically attached when calling "Create Hinge Chain 2D" at <see cref="SkeletonUtilityBone"/>,
/// do not attempt to use this component for other purposes.
/// </summary>
public class ActivateBasedOnFlipDirection : MonoBehaviour {
public SkeletonRenderer skeletonRenderer;
public SkeletonGraphic skeletonGraphic;
public GameObject activeOnNormalX;
public GameObject activeOnFlippedX;
HingeJoint2D[] jointsNormalX;
HingeJoint2D[] jointsFlippedX;
ISkeletonComponent skeletonComponent;
bool wasFlippedXBefore = false;
private void Start () {
jointsNormalX = activeOnNormalX.GetComponentsInChildren<HingeJoint2D>();
jointsFlippedX = activeOnFlippedX.GetComponentsInChildren<HingeJoint2D>();
skeletonComponent = skeletonRenderer != null ? (ISkeletonComponent)skeletonRenderer : (ISkeletonComponent)skeletonGraphic;
}
private void FixedUpdate () {
bool isFlippedX = (skeletonComponent.Skeleton.ScaleX < 0);
if (isFlippedX != wasFlippedXBefore) {
HandleFlip(isFlippedX);
}
wasFlippedXBefore = isFlippedX;
}
void HandleFlip (bool isFlippedX) {
GameObject gameObjectToActivate = isFlippedX ? activeOnFlippedX : activeOnNormalX;
GameObject gameObjectToDeactivate = isFlippedX ? activeOnNormalX : activeOnFlippedX;
gameObjectToActivate.SetActive(true);
gameObjectToDeactivate.SetActive(false);
ResetJointPositions(isFlippedX ? jointsFlippedX : jointsNormalX);
ResetJointPositions(isFlippedX ? jointsNormalX : jointsFlippedX);
CompensateMovementAfterFlipX(gameObjectToActivate.transform, gameObjectToDeactivate.transform);
}
void ResetJointPositions (HingeJoint2D[] joints) {
for (int i = 0; i < joints.Length; ++i) {
var joint = joints[i];
var parent = joint.connectedBody.transform;
joint.transform.position = parent.TransformPoint(joint.connectedAnchor);
}
}
void CompensateMovementAfterFlipX (Transform toActivate, Transform toDeactivate) {
Transform targetLocation = toDeactivate.GetChild(0);
Transform currentLocation = toActivate.GetChild(0);
toActivate.position += targetLocation.position - currentLocation.position;
}
}
}
/******************************************************************************
* 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.
*****************************************************************************/
using UnityEngine;
namespace Spine.Unity {
/// <summary>
/// Utility component to support flipping of 2D hinge chains (chains of HingeJoint2D objects) along
/// with the parent skeleton by activating the respective mirrored versions of the hinge chain.
/// Note: This component is automatically attached when calling "Create Hinge Chain 2D" at <see cref="SkeletonUtilityBone"/>,
/// do not attempt to use this component for other purposes.
/// </summary>
public class ActivateBasedOnFlipDirection : MonoBehaviour {
public SkeletonRenderer skeletonRenderer;
public SkeletonGraphic skeletonGraphic;
public GameObject activeOnNormalX;
public GameObject activeOnFlippedX;
HingeJoint2D[] jointsNormalX;
HingeJoint2D[] jointsFlippedX;
ISkeletonComponent skeletonComponent;
bool wasFlippedXBefore = false;
private void Start () {
jointsNormalX = activeOnNormalX.GetComponentsInChildren<HingeJoint2D>();
jointsFlippedX = activeOnFlippedX.GetComponentsInChildren<HingeJoint2D>();
skeletonComponent = skeletonRenderer != null ? (ISkeletonComponent)skeletonRenderer : (ISkeletonComponent)skeletonGraphic;
}
private void FixedUpdate () {
bool isFlippedX = (skeletonComponent.Skeleton.ScaleX < 0);
if (isFlippedX != wasFlippedXBefore) {
HandleFlip(isFlippedX);
}
wasFlippedXBefore = isFlippedX;
}
void HandleFlip (bool isFlippedX) {
GameObject gameObjectToActivate = isFlippedX ? activeOnFlippedX : activeOnNormalX;
GameObject gameObjectToDeactivate = isFlippedX ? activeOnNormalX : activeOnFlippedX;
gameObjectToActivate.SetActive(true);
gameObjectToDeactivate.SetActive(false);
ResetJointPositions(isFlippedX ? jointsFlippedX : jointsNormalX);
ResetJointPositions(isFlippedX ? jointsNormalX : jointsFlippedX);
CompensateMovementAfterFlipX(gameObjectToActivate.transform, gameObjectToDeactivate.transform);
}
void ResetJointPositions (HingeJoint2D[] joints) {
for (int i = 0; i < joints.Length; ++i) {
var joint = joints[i];
var parent = joint.connectedBody.transform;
joint.transform.position = parent.TransformPoint(joint.connectedAnchor);
}
}
void CompensateMovementAfterFlipX (Transform toActivate, Transform toDeactivate) {
Transform targetLocation = toDeactivate.GetChild(0);
Transform currentLocation = toActivate.GetChild(0);
toActivate.position += targetLocation.position - currentLocation.position;
}
}
}

View File

@@ -1,54 +1,54 @@
/******************************************************************************
* 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.
*****************************************************************************/
using UnityEngine;
namespace Spine.Unity {
/// <summary>
/// Utility component to support flipping of hinge chains (chains of HingeJoint objects) along with the parent skeleton.
///
/// Note: This component is automatically attached when calling "Create Hinge Chain" at <see cref="SkeletonUtilityBone"/>.
/// </summary>
[RequireComponent(typeof(Rigidbody))]
public class FollowLocationRigidbody : MonoBehaviour {
public Transform reference;
Rigidbody ownRigidbody;
private void Awake () {
ownRigidbody = this.GetComponent<Rigidbody>();
}
void FixedUpdate () {
ownRigidbody.rotation = reference.rotation;
ownRigidbody.position = reference.position;
}
}
}
/******************************************************************************
* 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.
*****************************************************************************/
using UnityEngine;
namespace Spine.Unity {
/// <summary>
/// Utility component to support flipping of hinge chains (chains of HingeJoint objects) along with the parent skeleton.
///
/// Note: This component is automatically attached when calling "Create Hinge Chain" at <see cref="SkeletonUtilityBone"/>.
/// </summary>
[RequireComponent(typeof(Rigidbody))]
public class FollowLocationRigidbody : MonoBehaviour {
public Transform reference;
Rigidbody ownRigidbody;
private void Awake () {
ownRigidbody = this.GetComponent<Rigidbody>();
}
void FixedUpdate () {
ownRigidbody.rotation = reference.rotation;
ownRigidbody.position = reference.position;
}
}
}

View File

@@ -1,58 +1,58 @@
/******************************************************************************
* 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.
*****************************************************************************/
using UnityEngine;
namespace Spine.Unity {
/// <summary>
/// Utility component to support flipping of hinge chains (chains of HingeJoint objects) along with the parent skeleton.
///
/// Note: This component is automatically attached when calling "Create Hinge Chain" at <see cref="SkeletonUtilityBone"/>.
/// </summary>
[RequireComponent(typeof(Rigidbody2D))]
public class FollowLocationRigidbody2D : MonoBehaviour {
public Transform reference;
public bool followFlippedX;
Rigidbody2D ownRigidbody;
private void Awake () {
ownRigidbody = this.GetComponent<Rigidbody2D>();
}
void FixedUpdate () {
if (followFlippedX) {
ownRigidbody.rotation = ((-reference.rotation.eulerAngles.z + 270f) % 360f) - 90f;
} else
ownRigidbody.rotation = reference.rotation.eulerAngles.z;
ownRigidbody.position = reference.position;
}
}
}
/******************************************************************************
* 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.
*****************************************************************************/
using UnityEngine;
namespace Spine.Unity {
/// <summary>
/// Utility component to support flipping of hinge chains (chains of HingeJoint objects) along with the parent skeleton.
///
/// Note: This component is automatically attached when calling "Create Hinge Chain" at <see cref="SkeletonUtilityBone"/>.
/// </summary>
[RequireComponent(typeof(Rigidbody2D))]
public class FollowLocationRigidbody2D : MonoBehaviour {
public Transform reference;
public bool followFlippedX;
Rigidbody2D ownRigidbody;
private void Awake () {
ownRigidbody = this.GetComponent<Rigidbody2D>();
}
void FixedUpdate () {
if (followFlippedX) {
ownRigidbody.rotation = ((-reference.rotation.eulerAngles.z + 270f) % 360f) - 90f;
} else
ownRigidbody.rotation = reference.rotation.eulerAngles.z;
ownRigidbody.position = reference.position;
}
}
}

View File

@@ -1,86 +1,86 @@
/******************************************************************************
* 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.
*****************************************************************************/
using UnityEngine;
namespace Spine.Unity {
/// <summary>
/// Utility component to support flipping of hinge chains (chains of HingeJoint objects) along with the parent skeleton.
/// Note that flipping needs to be performed by 180 degree rotation at <see cref="SkeletonUtility"/>,
/// by setting <see cref="SkeletonUtility.flipBy180DegreeRotation"/> to true, not via negative scale.
///
/// Note: This component is automatically attached when calling "Create Hinge Chain" at <see cref="SkeletonUtilityBone"/>,
/// do not attempt to use this component for other purposes.
/// </summary>
public class FollowSkeletonUtilityRootRotation : MonoBehaviour {
const float FLIP_ANGLE_THRESHOLD = 100.0f;
public Transform reference;
Vector3 prevLocalEulerAngles;
private void Start () {
prevLocalEulerAngles = this.transform.localEulerAngles;
}
void FixedUpdate () {
this.transform.rotation = reference.rotation;
bool wasFlippedAroundY = Mathf.Abs(this.transform.localEulerAngles.y - prevLocalEulerAngles.y) > FLIP_ANGLE_THRESHOLD;
bool wasFlippedAroundX = Mathf.Abs(this.transform.localEulerAngles.x - prevLocalEulerAngles.x) > FLIP_ANGLE_THRESHOLD;
if (wasFlippedAroundY)
CompensatePositionToYRotation();
if (wasFlippedAroundX)
CompensatePositionToXRotation();
prevLocalEulerAngles = this.transform.localEulerAngles;
}
/// <summary>
/// Compensates the position so that a child at the reference position remains in the same place,
/// to counter any movement that occurred by rotation.
/// </summary>
void CompensatePositionToYRotation () {
Vector3 newPosition = reference.position + (reference.position - this.transform.position);
newPosition.y = this.transform.position.y;
this.transform.position = newPosition;
}
/// <summary>
/// Compensates the position so that a child at the reference position remains in the same place,
/// to counter any movement that occurred by rotation.
/// </summary>
void CompensatePositionToXRotation () {
Vector3 newPosition = reference.position + (reference.position - this.transform.position);
newPosition.x = this.transform.position.x;
this.transform.position = newPosition;
}
}
}
/******************************************************************************
* 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.
*****************************************************************************/
using UnityEngine;
namespace Spine.Unity {
/// <summary>
/// Utility component to support flipping of hinge chains (chains of HingeJoint objects) along with the parent skeleton.
/// Note that flipping needs to be performed by 180 degree rotation at <see cref="SkeletonUtility"/>,
/// by setting <see cref="SkeletonUtility.flipBy180DegreeRotation"/> to true, not via negative scale.
///
/// Note: This component is automatically attached when calling "Create Hinge Chain" at <see cref="SkeletonUtilityBone"/>,
/// do not attempt to use this component for other purposes.
/// </summary>
public class FollowSkeletonUtilityRootRotation : MonoBehaviour {
const float FLIP_ANGLE_THRESHOLD = 100.0f;
public Transform reference;
Vector3 prevLocalEulerAngles;
private void Start () {
prevLocalEulerAngles = this.transform.localEulerAngles;
}
void FixedUpdate () {
this.transform.rotation = reference.rotation;
bool wasFlippedAroundY = Mathf.Abs(this.transform.localEulerAngles.y - prevLocalEulerAngles.y) > FLIP_ANGLE_THRESHOLD;
bool wasFlippedAroundX = Mathf.Abs(this.transform.localEulerAngles.x - prevLocalEulerAngles.x) > FLIP_ANGLE_THRESHOLD;
if (wasFlippedAroundY)
CompensatePositionToYRotation();
if (wasFlippedAroundX)
CompensatePositionToXRotation();
prevLocalEulerAngles = this.transform.localEulerAngles;
}
/// <summary>
/// Compensates the position so that a child at the reference position remains in the same place,
/// to counter any movement that occurred by rotation.
/// </summary>
void CompensatePositionToYRotation () {
Vector3 newPosition = reference.position + (reference.position - this.transform.position);
newPosition.y = this.transform.position.y;
this.transform.position = newPosition;
}
/// <summary>
/// Compensates the position so that a child at the reference position remains in the same place,
/// to counter any movement that occurred by rotation.
/// </summary>
void CompensatePositionToXRotation () {
Vector3 newPosition = reference.position + (reference.position - this.transform.position);
newPosition.x = this.transform.position.x;
this.transform.position = newPosition;
}
}
}

View File

@@ -1,467 +1,467 @@
/******************************************************************************
* 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.
*****************************************************************************/
#if UNITY_2018_3 || UNITY_2019 || UNITY_2018_3_OR_NEWER
#define NEW_PREFAB_SYSTEM
#endif
using System.Collections.Generic;
using UnityEngine;
namespace Spine.Unity {
#if NEW_PREFAB_SYSTEM
[ExecuteAlways]
#else
[ExecuteInEditMode]
#endif
[RequireComponent(typeof(ISkeletonAnimation))]
[HelpURL("http://esotericsoftware.com/spine-unity#SkeletonUtility")]
public sealed class SkeletonUtility : MonoBehaviour {
#region BoundingBoxAttachment
public static PolygonCollider2D AddBoundingBoxGameObject (Skeleton skeleton, string skinName, string slotName, string attachmentName, Transform parent, bool isTrigger = true) {
Skin skin = string.IsNullOrEmpty(skinName) ? skeleton.Data.DefaultSkin : skeleton.Data.FindSkin(skinName);
if (skin == null) {
Debug.LogError("Skin " + skinName + " not found!");
return null;
}
Slot slot = skeleton.FindSlot(slotName);
var attachment = slot != null ? skin.GetAttachment(slot.Data.Index, attachmentName) : null;
if (attachment == null) {
Debug.LogFormat("Attachment in slot '{0}' named '{1}' not found in skin '{2}'.", slotName, attachmentName, skin.Name);
return null;
}
var box = attachment as BoundingBoxAttachment;
if (box != null) {
return AddBoundingBoxGameObject(box.Name, box, slot, parent, isTrigger);
} else {
Debug.LogFormat("Attachment '{0}' was not a Bounding Box.", attachmentName);
return null;
}
}
public static PolygonCollider2D AddBoundingBoxGameObject (string name, BoundingBoxAttachment box, Slot slot, Transform parent, bool isTrigger = true) {
var go = new GameObject("[BoundingBox]" + (string.IsNullOrEmpty(name) ? box.Name : name));
#if UNITY_EDITOR
if (!Application.isPlaying)
UnityEditor.Undo.RegisterCreatedObjectUndo(go, "Spawn BoundingBox");
#endif
var got = go.transform;
got.parent = parent;
got.localPosition = Vector3.zero;
got.localRotation = Quaternion.identity;
got.localScale = Vector3.one;
return AddBoundingBoxAsComponent(box, slot, go, isTrigger);
}
public static PolygonCollider2D AddBoundingBoxAsComponent (BoundingBoxAttachment box, Slot slot, GameObject gameObject, bool isTrigger = true) {
if (box == null) return null;
var collider = gameObject.AddComponent<PolygonCollider2D>();
collider.isTrigger = isTrigger;
SetColliderPointsLocal(collider, slot, box);
return collider;
}
public static void SetColliderPointsLocal (PolygonCollider2D collider, Slot slot, BoundingBoxAttachment box, float scale = 1.0f) {
if (box == null) return;
if (box.IsWeighted()) Debug.LogWarning("UnityEngine.PolygonCollider2D does not support weighted or animated points. Collider points will not be animated and may have incorrect orientation. If you want to use it as a collider, please remove weights and animations from the bounding box in Spine editor.");
var verts = box.GetLocalVertices(slot, null);
if (scale != 1.0f) {
for (int i = 0, n = verts.Length; i < n; ++i)
verts[i] *= scale;
}
collider.SetPath(0, verts);
}
public static Bounds GetBoundingBoxBounds (BoundingBoxAttachment boundingBox, float depth = 0) {
float[] floats = boundingBox.Vertices;
int floatCount = floats.Length;
Bounds bounds = new Bounds();
bounds.center = new Vector3(floats[0], floats[1], 0);
for (int i = 2; i < floatCount; i += 2)
bounds.Encapsulate(new Vector3(floats[i], floats[i + 1], 0));
Vector3 size = bounds.size;
size.z = depth;
bounds.size = size;
return bounds;
}
public static Rigidbody2D AddBoneRigidbody2D (GameObject gameObject, bool isKinematic = true, float gravityScale = 0f) {
var rb = gameObject.GetComponent<Rigidbody2D>();
if (rb == null) {
rb = gameObject.AddComponent<Rigidbody2D>();
rb.isKinematic = isKinematic;
rb.gravityScale = gravityScale;
}
return rb;
}
#endregion
public delegate void SkeletonUtilityDelegate ();
public event SkeletonUtilityDelegate OnReset;
public Transform boneRoot;
/// <summary>
/// If true, <see cref="Skeleton.ScaleX"/> and <see cref="Skeleton.ScaleY"/> are followed
/// by 180 degree rotation. If false, negative Transform scale is used.
/// Note that using negative scale is consistent with previous behaviour (hence the default),
/// however causes serious problems with rigidbodies and physics. Therefore, it is recommended to
/// enable this parameter where possible. When creating hinge chains for a chain of skeleton bones
/// via <see cref="SkeletonUtilityBone"/>, it is mandatory to have <c>flipBy180DegreeRotation</c> enabled.
/// </summary>
public bool flipBy180DegreeRotation = false;
void Update () {
var skeleton = skeletonComponent.Skeleton;
if (skeleton != null && boneRoot != null) {
if (flipBy180DegreeRotation) {
boneRoot.localScale = new Vector3(Mathf.Abs(skeleton.ScaleX), Mathf.Abs(skeleton.ScaleY), 1f);
boneRoot.eulerAngles = new Vector3(skeleton.ScaleY > 0 ? 0 : 180,
skeleton.ScaleX > 0 ? 0 : 180,
0);
} else {
boneRoot.localScale = new Vector3(skeleton.ScaleX, skeleton.ScaleY, 1f);
}
}
if (canvas != null) {
positionScale = canvas.referencePixelsPerUnit;
}
}
[HideInInspector] public SkeletonRenderer skeletonRenderer;
[HideInInspector] public SkeletonGraphic skeletonGraphic;
private Canvas canvas;
[System.NonSerialized] public ISkeletonAnimation skeletonAnimation;
private ISkeletonComponent skeletonComponent;
[System.NonSerialized] public List<SkeletonUtilityBone> boneComponents = new List<SkeletonUtilityBone>();
[System.NonSerialized] public List<SkeletonUtilityConstraint> constraintComponents = new List<SkeletonUtilityConstraint>();
public ISkeletonComponent SkeletonComponent {
get {
if (skeletonComponent == null) {
skeletonComponent = skeletonRenderer != null ? skeletonRenderer.GetComponent<ISkeletonComponent>() :
skeletonGraphic != null ? skeletonGraphic.GetComponent<ISkeletonComponent>() :
GetComponent<ISkeletonComponent>();
}
return skeletonComponent;
}
}
public Skeleton Skeleton {
get {
if (SkeletonComponent == null)
return null;
return skeletonComponent.Skeleton;
}
}
public bool IsValid {
get {
return (skeletonRenderer != null && skeletonRenderer.valid) ||
(skeletonGraphic != null && skeletonGraphic.IsValid);
}
}
public float PositionScale { get { return positionScale; } }
float positionScale = 1.0f;
bool hasOverrideBones;
bool hasConstraints;
bool needToReprocessBones;
public void ResubscribeEvents () {
OnDisable();
OnEnable();
}
void OnEnable () {
if (skeletonRenderer == null) {
skeletonRenderer = GetComponent<SkeletonRenderer>();
}
if (skeletonGraphic == null) {
skeletonGraphic = GetComponent<SkeletonGraphic>();
}
if (skeletonAnimation == null) {
skeletonAnimation = skeletonRenderer != null ? skeletonRenderer.GetComponent<ISkeletonAnimation>() :
skeletonGraphic != null ? skeletonGraphic.GetComponent<ISkeletonAnimation>() :
GetComponent<ISkeletonAnimation>();
}
if (skeletonComponent == null) {
skeletonComponent = skeletonRenderer != null ? skeletonRenderer.GetComponent<ISkeletonComponent>() :
skeletonGraphic != null ? skeletonGraphic.GetComponent<ISkeletonComponent>() :
GetComponent<ISkeletonComponent>();
}
if (skeletonRenderer != null) {
skeletonRenderer.OnRebuild -= HandleRendererReset;
skeletonRenderer.OnRebuild += HandleRendererReset;
} else if (skeletonGraphic != null) {
skeletonGraphic.OnRebuild -= HandleRendererReset;
skeletonGraphic.OnRebuild += HandleRendererReset;
canvas = skeletonGraphic.canvas;
if (canvas == null)
canvas = skeletonGraphic.GetComponentInParent<Canvas>();
if (canvas == null)
positionScale = 100.0f;
}
if (skeletonAnimation != null) {
skeletonAnimation.UpdateLocal -= UpdateLocal;
skeletonAnimation.UpdateLocal += UpdateLocal;
}
CollectBones();
}
void Start () {
//recollect because order of operations failure when switching between game mode and edit mode...
CollectBones();
}
void OnDisable () {
if (skeletonRenderer != null)
skeletonRenderer.OnRebuild -= HandleRendererReset;
if (skeletonGraphic != null)
skeletonGraphic.OnRebuild -= HandleRendererReset;
if (skeletonAnimation != null) {
skeletonAnimation.UpdateLocal -= UpdateLocal;
skeletonAnimation.UpdateWorld -= UpdateWorld;
skeletonAnimation.UpdateComplete -= UpdateComplete;
}
}
void HandleRendererReset (SkeletonRenderer r) {
if (OnReset != null) OnReset();
CollectBones();
}
void HandleRendererReset (SkeletonGraphic g) {
if (OnReset != null) OnReset();
CollectBones();
}
public void RegisterBone (SkeletonUtilityBone bone) {
if (boneComponents.Contains(bone)) {
return;
} else {
boneComponents.Add(bone);
needToReprocessBones = true;
}
}
public void UnregisterBone (SkeletonUtilityBone bone) {
boneComponents.Remove(bone);
}
public void RegisterConstraint (SkeletonUtilityConstraint constraint) {
if (constraintComponents.Contains(constraint))
return;
else {
constraintComponents.Add(constraint);
needToReprocessBones = true;
}
}
public void UnregisterConstraint (SkeletonUtilityConstraint constraint) {
constraintComponents.Remove(constraint);
}
public void CollectBones () {
var skeleton = skeletonComponent.Skeleton;
if (skeleton == null) return;
if (boneRoot != null) {
var constraintTargets = new List<System.Object>();
var ikConstraints = skeleton.IkConstraints;
for (int i = 0, n = ikConstraints.Count; i < n; i++)
constraintTargets.Add(ikConstraints.Items[i].Target);
var transformConstraints = skeleton.TransformConstraints;
for (int i = 0, n = transformConstraints.Count; i < n; i++)
constraintTargets.Add(transformConstraints.Items[i].Target);
var boneComponents = this.boneComponents;
for (int i = 0, n = boneComponents.Count; i < n; i++) {
var b = boneComponents[i];
if (b.bone == null) {
b.DoUpdate(SkeletonUtilityBone.UpdatePhase.Local);
if (b.bone == null) continue;
}
hasOverrideBones |= (b.mode == SkeletonUtilityBone.Mode.Override);
hasConstraints |= constraintTargets.Contains(b.bone);
}
hasConstraints |= constraintComponents.Count > 0;
if (skeletonAnimation != null) {
skeletonAnimation.UpdateWorld -= UpdateWorld;
skeletonAnimation.UpdateComplete -= UpdateComplete;
if (hasOverrideBones || hasConstraints)
skeletonAnimation.UpdateWorld += UpdateWorld;
if (hasConstraints)
skeletonAnimation.UpdateComplete += UpdateComplete;
}
needToReprocessBones = false;
} else {
boneComponents.Clear();
constraintComponents.Clear();
}
}
void UpdateLocal (ISkeletonAnimation anim) {
if (needToReprocessBones)
CollectBones();
var boneComponents = this.boneComponents;
if (boneComponents == null) return;
for (int i = 0, n = boneComponents.Count; i < n; i++)
boneComponents[i].transformLerpComplete = false;
UpdateAllBones(SkeletonUtilityBone.UpdatePhase.Local);
}
void UpdateWorld (ISkeletonAnimation anim) {
UpdateAllBones(SkeletonUtilityBone.UpdatePhase.World);
for (int i = 0, n = constraintComponents.Count; i < n; i++)
constraintComponents[i].DoUpdate();
}
void UpdateComplete (ISkeletonAnimation anim) {
UpdateAllBones(SkeletonUtilityBone.UpdatePhase.Complete);
}
void UpdateAllBones (SkeletonUtilityBone.UpdatePhase phase) {
if (boneRoot == null)
CollectBones();
var boneComponents = this.boneComponents;
if (boneComponents == null) return;
for (int i = 0, n = boneComponents.Count; i < n; i++)
boneComponents[i].DoUpdate(phase);
}
public Transform GetBoneRoot () {
if (boneRoot != null)
return boneRoot;
var boneRootObject = new GameObject("SkeletonUtility-SkeletonRoot");
#if UNITY_EDITOR
if (!Application.isPlaying)
UnityEditor.Undo.RegisterCreatedObjectUndo(boneRootObject, "Spawn Bone");
#endif
if (skeletonGraphic != null)
boneRootObject.AddComponent<RectTransform>();
boneRoot = boneRootObject.transform;
boneRoot.SetParent(transform);
boneRoot.localPosition = Vector3.zero;
boneRoot.localRotation = Quaternion.identity;
boneRoot.localScale = Vector3.one;
return boneRoot;
}
public GameObject SpawnRoot (SkeletonUtilityBone.Mode mode, bool pos, bool rot, bool sca) {
GetBoneRoot();
Skeleton skeleton = this.skeletonComponent.Skeleton;
GameObject go = SpawnBone(skeleton.RootBone, boneRoot, mode, pos, rot, sca);
CollectBones();
return go;
}
public GameObject SpawnHierarchy (SkeletonUtilityBone.Mode mode, bool pos, bool rot, bool sca) {
GetBoneRoot();
Skeleton skeleton = this.skeletonComponent.Skeleton;
GameObject go = SpawnBoneRecursively(skeleton.RootBone, boneRoot, mode, pos, rot, sca);
CollectBones();
return go;
}
public GameObject SpawnBoneRecursively (Bone bone, Transform parent, SkeletonUtilityBone.Mode mode, bool pos, bool rot, bool sca) {
GameObject go = SpawnBone(bone, parent, mode, pos, rot, sca);
ExposedList<Bone> childrenBones = bone.Children;
for (int i = 0, n = childrenBones.Count; i < n; i++) {
Bone child = childrenBones.Items[i];
SpawnBoneRecursively(child, go.transform, mode, pos, rot, sca);
}
return go;
}
public GameObject SpawnBone (Bone bone, Transform parent, SkeletonUtilityBone.Mode mode, bool pos, bool rot, bool sca) {
GameObject go = new GameObject(bone.Data.Name);
#if UNITY_EDITOR
if (!Application.isPlaying)
UnityEditor.Undo.RegisterCreatedObjectUndo(go, "Spawn Bone");
#endif
if (skeletonGraphic != null)
go.AddComponent<RectTransform>();
var goTransform = go.transform;
goTransform.SetParent(parent);
SkeletonUtilityBone b = go.AddComponent<SkeletonUtilityBone>();
b.hierarchy = this;
b.position = pos;
b.rotation = rot;
b.scale = sca;
b.mode = mode;
b.zPosition = true;
b.Reset();
b.bone = bone;
b.boneName = bone.Data.Name;
b.valid = true;
if (mode == SkeletonUtilityBone.Mode.Override) {
if (rot) goTransform.localRotation = Quaternion.Euler(0, 0, b.bone.AppliedRotation);
if (pos) goTransform.localPosition = new Vector3(b.bone.X * positionScale, b.bone.Y * positionScale, 0);
goTransform.localScale = new Vector3(b.bone.ScaleX, b.bone.ScaleY, 0);
}
return go;
}
}
}
/******************************************************************************
* 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.
*****************************************************************************/
#if UNITY_2018_3 || UNITY_2019 || UNITY_2018_3_OR_NEWER
#define NEW_PREFAB_SYSTEM
#endif
using System.Collections.Generic;
using UnityEngine;
namespace Spine.Unity {
#if NEW_PREFAB_SYSTEM
[ExecuteAlways]
#else
[ExecuteInEditMode]
#endif
[RequireComponent(typeof(ISkeletonAnimation))]
[HelpURL("http://esotericsoftware.com/spine-unity#SkeletonUtility")]
public sealed class SkeletonUtility : MonoBehaviour {
#region BoundingBoxAttachment
public static PolygonCollider2D AddBoundingBoxGameObject (Skeleton skeleton, string skinName, string slotName, string attachmentName, Transform parent, bool isTrigger = true) {
Skin skin = string.IsNullOrEmpty(skinName) ? skeleton.Data.DefaultSkin : skeleton.Data.FindSkin(skinName);
if (skin == null) {
Debug.LogError("Skin " + skinName + " not found!");
return null;
}
Slot slot = skeleton.FindSlot(slotName);
var attachment = slot != null ? skin.GetAttachment(slot.Data.Index, attachmentName) : null;
if (attachment == null) {
Debug.LogFormat("Attachment in slot '{0}' named '{1}' not found in skin '{2}'.", slotName, attachmentName, skin.Name);
return null;
}
var box = attachment as BoundingBoxAttachment;
if (box != null) {
return AddBoundingBoxGameObject(box.Name, box, slot, parent, isTrigger);
} else {
Debug.LogFormat("Attachment '{0}' was not a Bounding Box.", attachmentName);
return null;
}
}
public static PolygonCollider2D AddBoundingBoxGameObject (string name, BoundingBoxAttachment box, Slot slot, Transform parent, bool isTrigger = true) {
var go = new GameObject("[BoundingBox]" + (string.IsNullOrEmpty(name) ? box.Name : name));
#if UNITY_EDITOR
if (!Application.isPlaying)
UnityEditor.Undo.RegisterCreatedObjectUndo(go, "Spawn BoundingBox");
#endif
var got = go.transform;
got.parent = parent;
got.localPosition = Vector3.zero;
got.localRotation = Quaternion.identity;
got.localScale = Vector3.one;
return AddBoundingBoxAsComponent(box, slot, go, isTrigger);
}
public static PolygonCollider2D AddBoundingBoxAsComponent (BoundingBoxAttachment box, Slot slot, GameObject gameObject, bool isTrigger = true) {
if (box == null) return null;
var collider = gameObject.AddComponent<PolygonCollider2D>();
collider.isTrigger = isTrigger;
SetColliderPointsLocal(collider, slot, box);
return collider;
}
public static void SetColliderPointsLocal (PolygonCollider2D collider, Slot slot, BoundingBoxAttachment box, float scale = 1.0f) {
if (box == null) return;
if (box.IsWeighted()) Debug.LogWarning("UnityEngine.PolygonCollider2D does not support weighted or animated points. Collider points will not be animated and may have incorrect orientation. If you want to use it as a collider, please remove weights and animations from the bounding box in Spine editor.");
var verts = box.GetLocalVertices(slot, null);
if (scale != 1.0f) {
for (int i = 0, n = verts.Length; i < n; ++i)
verts[i] *= scale;
}
collider.SetPath(0, verts);
}
public static Bounds GetBoundingBoxBounds (BoundingBoxAttachment boundingBox, float depth = 0) {
float[] floats = boundingBox.Vertices;
int floatCount = floats.Length;
Bounds bounds = new Bounds();
bounds.center = new Vector3(floats[0], floats[1], 0);
for (int i = 2; i < floatCount; i += 2)
bounds.Encapsulate(new Vector3(floats[i], floats[i + 1], 0));
Vector3 size = bounds.size;
size.z = depth;
bounds.size = size;
return bounds;
}
public static Rigidbody2D AddBoneRigidbody2D (GameObject gameObject, bool isKinematic = true, float gravityScale = 0f) {
var rb = gameObject.GetComponent<Rigidbody2D>();
if (rb == null) {
rb = gameObject.AddComponent<Rigidbody2D>();
rb.isKinematic = isKinematic;
rb.gravityScale = gravityScale;
}
return rb;
}
#endregion
public delegate void SkeletonUtilityDelegate ();
public event SkeletonUtilityDelegate OnReset;
public Transform boneRoot;
/// <summary>
/// If true, <see cref="Skeleton.ScaleX"/> and <see cref="Skeleton.ScaleY"/> are followed
/// by 180 degree rotation. If false, negative Transform scale is used.
/// Note that using negative scale is consistent with previous behaviour (hence the default),
/// however causes serious problems with rigidbodies and physics. Therefore, it is recommended to
/// enable this parameter where possible. When creating hinge chains for a chain of skeleton bones
/// via <see cref="SkeletonUtilityBone"/>, it is mandatory to have <c>flipBy180DegreeRotation</c> enabled.
/// </summary>
public bool flipBy180DegreeRotation = false;
void Update () {
var skeleton = skeletonComponent.Skeleton;
if (skeleton != null && boneRoot != null) {
if (flipBy180DegreeRotation) {
boneRoot.localScale = new Vector3(Mathf.Abs(skeleton.ScaleX), Mathf.Abs(skeleton.ScaleY), 1f);
boneRoot.eulerAngles = new Vector3(skeleton.ScaleY > 0 ? 0 : 180,
skeleton.ScaleX > 0 ? 0 : 180,
0);
} else {
boneRoot.localScale = new Vector3(skeleton.ScaleX, skeleton.ScaleY, 1f);
}
}
if (canvas != null) {
positionScale = canvas.referencePixelsPerUnit;
}
}
[HideInInspector] public SkeletonRenderer skeletonRenderer;
[HideInInspector] public SkeletonGraphic skeletonGraphic;
private Canvas canvas;
[System.NonSerialized] public ISkeletonAnimation skeletonAnimation;
private ISkeletonComponent skeletonComponent;
[System.NonSerialized] public List<SkeletonUtilityBone> boneComponents = new List<SkeletonUtilityBone>();
[System.NonSerialized] public List<SkeletonUtilityConstraint> constraintComponents = new List<SkeletonUtilityConstraint>();
public ISkeletonComponent SkeletonComponent {
get {
if (skeletonComponent == null) {
skeletonComponent = skeletonRenderer != null ? skeletonRenderer.GetComponent<ISkeletonComponent>() :
skeletonGraphic != null ? skeletonGraphic.GetComponent<ISkeletonComponent>() :
GetComponent<ISkeletonComponent>();
}
return skeletonComponent;
}
}
public Skeleton Skeleton {
get {
if (SkeletonComponent == null)
return null;
return skeletonComponent.Skeleton;
}
}
public bool IsValid {
get {
return (skeletonRenderer != null && skeletonRenderer.valid) ||
(skeletonGraphic != null && skeletonGraphic.IsValid);
}
}
public float PositionScale { get { return positionScale; } }
float positionScale = 1.0f;
bool hasOverrideBones;
bool hasConstraints;
bool needToReprocessBones;
public void ResubscribeEvents () {
OnDisable();
OnEnable();
}
void OnEnable () {
if (skeletonRenderer == null) {
skeletonRenderer = GetComponent<SkeletonRenderer>();
}
if (skeletonGraphic == null) {
skeletonGraphic = GetComponent<SkeletonGraphic>();
}
if (skeletonAnimation == null) {
skeletonAnimation = skeletonRenderer != null ? skeletonRenderer.GetComponent<ISkeletonAnimation>() :
skeletonGraphic != null ? skeletonGraphic.GetComponent<ISkeletonAnimation>() :
GetComponent<ISkeletonAnimation>();
}
if (skeletonComponent == null) {
skeletonComponent = skeletonRenderer != null ? skeletonRenderer.GetComponent<ISkeletonComponent>() :
skeletonGraphic != null ? skeletonGraphic.GetComponent<ISkeletonComponent>() :
GetComponent<ISkeletonComponent>();
}
if (skeletonRenderer != null) {
skeletonRenderer.OnRebuild -= HandleRendererReset;
skeletonRenderer.OnRebuild += HandleRendererReset;
} else if (skeletonGraphic != null) {
skeletonGraphic.OnRebuild -= HandleRendererReset;
skeletonGraphic.OnRebuild += HandleRendererReset;
canvas = skeletonGraphic.canvas;
if (canvas == null)
canvas = skeletonGraphic.GetComponentInParent<Canvas>();
if (canvas == null)
positionScale = 100.0f;
}
if (skeletonAnimation != null) {
skeletonAnimation.UpdateLocal -= UpdateLocal;
skeletonAnimation.UpdateLocal += UpdateLocal;
}
CollectBones();
}
void Start () {
//recollect because order of operations failure when switching between game mode and edit mode...
CollectBones();
}
void OnDisable () {
if (skeletonRenderer != null)
skeletonRenderer.OnRebuild -= HandleRendererReset;
if (skeletonGraphic != null)
skeletonGraphic.OnRebuild -= HandleRendererReset;
if (skeletonAnimation != null) {
skeletonAnimation.UpdateLocal -= UpdateLocal;
skeletonAnimation.UpdateWorld -= UpdateWorld;
skeletonAnimation.UpdateComplete -= UpdateComplete;
}
}
void HandleRendererReset (SkeletonRenderer r) {
if (OnReset != null) OnReset();
CollectBones();
}
void HandleRendererReset (SkeletonGraphic g) {
if (OnReset != null) OnReset();
CollectBones();
}
public void RegisterBone (SkeletonUtilityBone bone) {
if (boneComponents.Contains(bone)) {
return;
} else {
boneComponents.Add(bone);
needToReprocessBones = true;
}
}
public void UnregisterBone (SkeletonUtilityBone bone) {
boneComponents.Remove(bone);
}
public void RegisterConstraint (SkeletonUtilityConstraint constraint) {
if (constraintComponents.Contains(constraint))
return;
else {
constraintComponents.Add(constraint);
needToReprocessBones = true;
}
}
public void UnregisterConstraint (SkeletonUtilityConstraint constraint) {
constraintComponents.Remove(constraint);
}
public void CollectBones () {
var skeleton = skeletonComponent.Skeleton;
if (skeleton == null) return;
if (boneRoot != null) {
var constraintTargets = new List<System.Object>();
var ikConstraints = skeleton.IkConstraints;
for (int i = 0, n = ikConstraints.Count; i < n; i++)
constraintTargets.Add(ikConstraints.Items[i].Target);
var transformConstraints = skeleton.TransformConstraints;
for (int i = 0, n = transformConstraints.Count; i < n; i++)
constraintTargets.Add(transformConstraints.Items[i].Target);
var boneComponents = this.boneComponents;
for (int i = 0, n = boneComponents.Count; i < n; i++) {
var b = boneComponents[i];
if (b.bone == null) {
b.DoUpdate(SkeletonUtilityBone.UpdatePhase.Local);
if (b.bone == null) continue;
}
hasOverrideBones |= (b.mode == SkeletonUtilityBone.Mode.Override);
hasConstraints |= constraintTargets.Contains(b.bone);
}
hasConstraints |= constraintComponents.Count > 0;
if (skeletonAnimation != null) {
skeletonAnimation.UpdateWorld -= UpdateWorld;
skeletonAnimation.UpdateComplete -= UpdateComplete;
if (hasOverrideBones || hasConstraints)
skeletonAnimation.UpdateWorld += UpdateWorld;
if (hasConstraints)
skeletonAnimation.UpdateComplete += UpdateComplete;
}
needToReprocessBones = false;
} else {
boneComponents.Clear();
constraintComponents.Clear();
}
}
void UpdateLocal (ISkeletonAnimation anim) {
if (needToReprocessBones)
CollectBones();
var boneComponents = this.boneComponents;
if (boneComponents == null) return;
for (int i = 0, n = boneComponents.Count; i < n; i++)
boneComponents[i].transformLerpComplete = false;
UpdateAllBones(SkeletonUtilityBone.UpdatePhase.Local);
}
void UpdateWorld (ISkeletonAnimation anim) {
UpdateAllBones(SkeletonUtilityBone.UpdatePhase.World);
for (int i = 0, n = constraintComponents.Count; i < n; i++)
constraintComponents[i].DoUpdate();
}
void UpdateComplete (ISkeletonAnimation anim) {
UpdateAllBones(SkeletonUtilityBone.UpdatePhase.Complete);
}
void UpdateAllBones (SkeletonUtilityBone.UpdatePhase phase) {
if (boneRoot == null)
CollectBones();
var boneComponents = this.boneComponents;
if (boneComponents == null) return;
for (int i = 0, n = boneComponents.Count; i < n; i++)
boneComponents[i].DoUpdate(phase);
}
public Transform GetBoneRoot () {
if (boneRoot != null)
return boneRoot;
var boneRootObject = new GameObject("SkeletonUtility-SkeletonRoot");
#if UNITY_EDITOR
if (!Application.isPlaying)
UnityEditor.Undo.RegisterCreatedObjectUndo(boneRootObject, "Spawn Bone");
#endif
if (skeletonGraphic != null)
boneRootObject.AddComponent<RectTransform>();
boneRoot = boneRootObject.transform;
boneRoot.SetParent(transform);
boneRoot.localPosition = Vector3.zero;
boneRoot.localRotation = Quaternion.identity;
boneRoot.localScale = Vector3.one;
return boneRoot;
}
public GameObject SpawnRoot (SkeletonUtilityBone.Mode mode, bool pos, bool rot, bool sca) {
GetBoneRoot();
Skeleton skeleton = this.skeletonComponent.Skeleton;
GameObject go = SpawnBone(skeleton.RootBone, boneRoot, mode, pos, rot, sca);
CollectBones();
return go;
}
public GameObject SpawnHierarchy (SkeletonUtilityBone.Mode mode, bool pos, bool rot, bool sca) {
GetBoneRoot();
Skeleton skeleton = this.skeletonComponent.Skeleton;
GameObject go = SpawnBoneRecursively(skeleton.RootBone, boneRoot, mode, pos, rot, sca);
CollectBones();
return go;
}
public GameObject SpawnBoneRecursively (Bone bone, Transform parent, SkeletonUtilityBone.Mode mode, bool pos, bool rot, bool sca) {
GameObject go = SpawnBone(bone, parent, mode, pos, rot, sca);
ExposedList<Bone> childrenBones = bone.Children;
for (int i = 0, n = childrenBones.Count; i < n; i++) {
Bone child = childrenBones.Items[i];
SpawnBoneRecursively(child, go.transform, mode, pos, rot, sca);
}
return go;
}
public GameObject SpawnBone (Bone bone, Transform parent, SkeletonUtilityBone.Mode mode, bool pos, bool rot, bool sca) {
GameObject go = new GameObject(bone.Data.Name);
#if UNITY_EDITOR
if (!Application.isPlaying)
UnityEditor.Undo.RegisterCreatedObjectUndo(go, "Spawn Bone");
#endif
if (skeletonGraphic != null)
go.AddComponent<RectTransform>();
var goTransform = go.transform;
goTransform.SetParent(parent);
SkeletonUtilityBone b = go.AddComponent<SkeletonUtilityBone>();
b.hierarchy = this;
b.position = pos;
b.rotation = rot;
b.scale = sca;
b.mode = mode;
b.zPosition = true;
b.Reset();
b.bone = bone;
b.boneName = bone.Data.Name;
b.valid = true;
if (mode == SkeletonUtilityBone.Mode.Override) {
if (rot) goTransform.localRotation = Quaternion.Euler(0, 0, b.bone.AppliedRotation);
if (pos) goTransform.localPosition = new Vector3(b.bone.X * positionScale, b.bone.Y * positionScale, 0);
goTransform.localScale = new Vector3(b.bone.ScaleX, b.bone.ScaleY, 0);
}
return go;
}
}
}

View File

@@ -1,238 +1,238 @@
/******************************************************************************
* 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.
*****************************************************************************/
#if UNITY_2018_3 || UNITY_2019 || UNITY_2018_3_OR_NEWER
#define NEW_PREFAB_SYSTEM
#endif
using UnityEngine;
namespace Spine.Unity {
/// <summary>Sets a GameObject's transform to match a bone on a Spine skeleton.</summary>
#if NEW_PREFAB_SYSTEM
[ExecuteAlways]
#else
[ExecuteInEditMode]
#endif
[AddComponentMenu("Spine/SkeletonUtilityBone")]
[HelpURL("http://esotericsoftware.com/spine-unity#SkeletonUtilityBone")]
public class SkeletonUtilityBone : MonoBehaviour {
public enum Mode {
Follow,
Override
}
public enum UpdatePhase {
Local,
World,
Complete
}
#region Inspector
/// <summary>If a bone isn't set, boneName is used to find the bone.</summary>
public string boneName;
public Transform parentReference;
public Mode mode;
public bool position, rotation, scale, zPosition = true;
[Range(0f, 1f)]
public float overrideAlpha = 1;
#endregion
public SkeletonUtility hierarchy;
[System.NonSerialized] public Bone bone;
[System.NonSerialized] public bool transformLerpComplete;
[System.NonSerialized] public bool valid;
Transform cachedTransform;
Transform skeletonTransform;
bool incompatibleTransformMode;
public bool IncompatibleTransformMode { get { return incompatibleTransformMode; } }
public void Reset () {
bone = null;
cachedTransform = transform;
valid = hierarchy != null && hierarchy.IsValid;
if (!valid)
return;
skeletonTransform = hierarchy.transform;
hierarchy.OnReset -= HandleOnReset;
hierarchy.OnReset += HandleOnReset;
DoUpdate(UpdatePhase.Local);
}
void OnEnable () {
if (hierarchy == null) hierarchy = transform.GetComponentInParent<SkeletonUtility>();
if (hierarchy == null) return;
hierarchy.RegisterBone(this);
hierarchy.OnReset += HandleOnReset;
}
void HandleOnReset () {
Reset();
}
void OnDisable () {
if (hierarchy != null) {
hierarchy.OnReset -= HandleOnReset;
hierarchy.UnregisterBone(this);
}
}
public void DoUpdate (UpdatePhase phase) {
if (!valid) {
Reset();
return;
}
var skeleton = hierarchy.Skeleton;
if (bone == null) {
if (string.IsNullOrEmpty(boneName)) return;
bone = skeleton.FindBone(boneName);
if (bone == null) {
Debug.LogError("Bone not found: " + boneName, this);
return;
}
}
if (!bone.Active) return;
float positionScale = hierarchy.PositionScale;
var thisTransform = cachedTransform;
float skeletonFlipRotation = Mathf.Sign(skeleton.ScaleX * skeleton.ScaleY);
if (mode == Mode.Follow) {
switch (phase) {
case UpdatePhase.Local:
if (position)
thisTransform.localPosition = new Vector3(bone.X * positionScale, bone.Y * positionScale, 0);
if (rotation) {
if (bone.Data.TransformMode.InheritsRotation()) {
thisTransform.localRotation = Quaternion.Euler(0, 0, bone.Rotation);
} else {
Vector3 euler = skeletonTransform.rotation.eulerAngles;
thisTransform.rotation = Quaternion.Euler(euler.x, euler.y, euler.z + (bone.WorldRotationX * skeletonFlipRotation));
}
}
if (scale) {
thisTransform.localScale = new Vector3(bone.ScaleX, bone.ScaleY, 1f);
incompatibleTransformMode = BoneTransformModeIncompatible(bone);
}
break;
case UpdatePhase.World:
case UpdatePhase.Complete:
if (position)
thisTransform.localPosition = new Vector3(bone.AX * positionScale, bone.AY * positionScale, 0);
if (rotation) {
if (bone.Data.TransformMode.InheritsRotation()) {
thisTransform.localRotation = Quaternion.Euler(0, 0, bone.AppliedRotation);
} else {
Vector3 euler = skeletonTransform.rotation.eulerAngles;
thisTransform.rotation = Quaternion.Euler(euler.x, euler.y, euler.z + (bone.WorldRotationX * skeletonFlipRotation));
}
}
if (scale) {
thisTransform.localScale = new Vector3(bone.AScaleX, bone.AScaleY, 1f);
incompatibleTransformMode = BoneTransformModeIncompatible(bone);
}
break;
}
} else if (mode == Mode.Override) {
if (transformLerpComplete)
return;
if (parentReference == null) {
if (position) {
Vector3 clp = thisTransform.localPosition / positionScale;
bone.X = Mathf.Lerp(bone.X, clp.x, overrideAlpha);
bone.Y = Mathf.Lerp(bone.Y, clp.y, overrideAlpha);
}
if (rotation) {
float angle = Mathf.LerpAngle(bone.Rotation, thisTransform.localRotation.eulerAngles.z, overrideAlpha);
bone.Rotation = angle;
bone.AppliedRotation = angle;
}
if (scale) {
Vector3 cls = thisTransform.localScale;
bone.ScaleX = Mathf.Lerp(bone.ScaleX, cls.x, overrideAlpha);
bone.ScaleY = Mathf.Lerp(bone.ScaleY, cls.y, overrideAlpha);
}
} else {
if (transformLerpComplete)
return;
if (position) {
Vector3 pos = parentReference.InverseTransformPoint(thisTransform.position) / positionScale;
bone.X = Mathf.Lerp(bone.X, pos.x, overrideAlpha);
bone.Y = Mathf.Lerp(bone.Y, pos.y, overrideAlpha);
}
if (rotation) {
float angle = Mathf.LerpAngle(bone.Rotation, Quaternion.LookRotation(Vector3.forward, parentReference.InverseTransformDirection(thisTransform.up)).eulerAngles.z, overrideAlpha);
bone.Rotation = angle;
bone.AppliedRotation = angle;
}
if (scale) {
Vector3 cls = thisTransform.localScale;
bone.ScaleX = Mathf.Lerp(bone.ScaleX, cls.x, overrideAlpha);
bone.ScaleY = Mathf.Lerp(bone.ScaleY, cls.y, overrideAlpha);
}
incompatibleTransformMode = BoneTransformModeIncompatible(bone);
}
transformLerpComplete = true;
}
}
public static bool BoneTransformModeIncompatible (Bone bone) {
return !bone.Data.TransformMode.InheritsScale();
}
public void AddBoundingBox (string skinName, string slotName, string attachmentName) {
SkeletonUtility.AddBoneRigidbody2D(transform.gameObject);
SkeletonUtility.AddBoundingBoxGameObject(bone.Skeleton, skinName, slotName, attachmentName, transform);
}
#if UNITY_EDITOR
void OnDrawGizmos () {
if (IncompatibleTransformMode)
Gizmos.DrawIcon(transform.position + new Vector3(0, 0.128f, 0), "icon-warning");
}
#endif
}
}
/******************************************************************************
* 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.
*****************************************************************************/
#if UNITY_2018_3 || UNITY_2019 || UNITY_2018_3_OR_NEWER
#define NEW_PREFAB_SYSTEM
#endif
using UnityEngine;
namespace Spine.Unity {
/// <summary>Sets a GameObject's transform to match a bone on a Spine skeleton.</summary>
#if NEW_PREFAB_SYSTEM
[ExecuteAlways]
#else
[ExecuteInEditMode]
#endif
[AddComponentMenu("Spine/SkeletonUtilityBone")]
[HelpURL("http://esotericsoftware.com/spine-unity#SkeletonUtilityBone")]
public class SkeletonUtilityBone : MonoBehaviour {
public enum Mode {
Follow,
Override
}
public enum UpdatePhase {
Local,
World,
Complete
}
#region Inspector
/// <summary>If a bone isn't set, boneName is used to find the bone.</summary>
public string boneName;
public Transform parentReference;
public Mode mode;
public bool position, rotation, scale, zPosition = true;
[Range(0f, 1f)]
public float overrideAlpha = 1;
#endregion
public SkeletonUtility hierarchy;
[System.NonSerialized] public Bone bone;
[System.NonSerialized] public bool transformLerpComplete;
[System.NonSerialized] public bool valid;
Transform cachedTransform;
Transform skeletonTransform;
bool incompatibleTransformMode;
public bool IncompatibleTransformMode { get { return incompatibleTransformMode; } }
public void Reset () {
bone = null;
cachedTransform = transform;
valid = hierarchy != null && hierarchy.IsValid;
if (!valid)
return;
skeletonTransform = hierarchy.transform;
hierarchy.OnReset -= HandleOnReset;
hierarchy.OnReset += HandleOnReset;
DoUpdate(UpdatePhase.Local);
}
void OnEnable () {
if (hierarchy == null) hierarchy = transform.GetComponentInParent<SkeletonUtility>();
if (hierarchy == null) return;
hierarchy.RegisterBone(this);
hierarchy.OnReset += HandleOnReset;
}
void HandleOnReset () {
Reset();
}
void OnDisable () {
if (hierarchy != null) {
hierarchy.OnReset -= HandleOnReset;
hierarchy.UnregisterBone(this);
}
}
public void DoUpdate (UpdatePhase phase) {
if (!valid) {
Reset();
return;
}
var skeleton = hierarchy.Skeleton;
if (bone == null) {
if (string.IsNullOrEmpty(boneName)) return;
bone = skeleton.FindBone(boneName);
if (bone == null) {
Debug.LogError("Bone not found: " + boneName, this);
return;
}
}
if (!bone.Active) return;
float positionScale = hierarchy.PositionScale;
var thisTransform = cachedTransform;
float skeletonFlipRotation = Mathf.Sign(skeleton.ScaleX * skeleton.ScaleY);
if (mode == Mode.Follow) {
switch (phase) {
case UpdatePhase.Local:
if (position)
thisTransform.localPosition = new Vector3(bone.X * positionScale, bone.Y * positionScale, 0);
if (rotation) {
if (bone.Data.TransformMode.InheritsRotation()) {
thisTransform.localRotation = Quaternion.Euler(0, 0, bone.Rotation);
} else {
Vector3 euler = skeletonTransform.rotation.eulerAngles;
thisTransform.rotation = Quaternion.Euler(euler.x, euler.y, euler.z + (bone.WorldRotationX * skeletonFlipRotation));
}
}
if (scale) {
thisTransform.localScale = new Vector3(bone.ScaleX, bone.ScaleY, 1f);
incompatibleTransformMode = BoneTransformModeIncompatible(bone);
}
break;
case UpdatePhase.World:
case UpdatePhase.Complete:
if (position)
thisTransform.localPosition = new Vector3(bone.AX * positionScale, bone.AY * positionScale, 0);
if (rotation) {
if (bone.Data.TransformMode.InheritsRotation()) {
thisTransform.localRotation = Quaternion.Euler(0, 0, bone.AppliedRotation);
} else {
Vector3 euler = skeletonTransform.rotation.eulerAngles;
thisTransform.rotation = Quaternion.Euler(euler.x, euler.y, euler.z + (bone.WorldRotationX * skeletonFlipRotation));
}
}
if (scale) {
thisTransform.localScale = new Vector3(bone.AScaleX, bone.AScaleY, 1f);
incompatibleTransformMode = BoneTransformModeIncompatible(bone);
}
break;
}
} else if (mode == Mode.Override) {
if (transformLerpComplete)
return;
if (parentReference == null) {
if (position) {
Vector3 clp = thisTransform.localPosition / positionScale;
bone.X = Mathf.Lerp(bone.X, clp.x, overrideAlpha);
bone.Y = Mathf.Lerp(bone.Y, clp.y, overrideAlpha);
}
if (rotation) {
float angle = Mathf.LerpAngle(bone.Rotation, thisTransform.localRotation.eulerAngles.z, overrideAlpha);
bone.Rotation = angle;
bone.AppliedRotation = angle;
}
if (scale) {
Vector3 cls = thisTransform.localScale;
bone.ScaleX = Mathf.Lerp(bone.ScaleX, cls.x, overrideAlpha);
bone.ScaleY = Mathf.Lerp(bone.ScaleY, cls.y, overrideAlpha);
}
} else {
if (transformLerpComplete)
return;
if (position) {
Vector3 pos = parentReference.InverseTransformPoint(thisTransform.position) / positionScale;
bone.X = Mathf.Lerp(bone.X, pos.x, overrideAlpha);
bone.Y = Mathf.Lerp(bone.Y, pos.y, overrideAlpha);
}
if (rotation) {
float angle = Mathf.LerpAngle(bone.Rotation, Quaternion.LookRotation(Vector3.forward, parentReference.InverseTransformDirection(thisTransform.up)).eulerAngles.z, overrideAlpha);
bone.Rotation = angle;
bone.AppliedRotation = angle;
}
if (scale) {
Vector3 cls = thisTransform.localScale;
bone.ScaleX = Mathf.Lerp(bone.ScaleX, cls.x, overrideAlpha);
bone.ScaleY = Mathf.Lerp(bone.ScaleY, cls.y, overrideAlpha);
}
incompatibleTransformMode = BoneTransformModeIncompatible(bone);
}
transformLerpComplete = true;
}
}
public static bool BoneTransformModeIncompatible (Bone bone) {
return !bone.Data.TransformMode.InheritsScale();
}
public void AddBoundingBox (string skinName, string slotName, string attachmentName) {
SkeletonUtility.AddBoneRigidbody2D(transform.gameObject);
SkeletonUtility.AddBoundingBoxGameObject(bone.Skeleton, skinName, slotName, attachmentName, transform);
}
#if UNITY_EDITOR
void OnDrawGizmos () {
if (IncompatibleTransformMode)
Gizmos.DrawIcon(transform.position + new Vector3(0, 0.128f, 0), "icon-warning");
}
#endif
}
}

View File

@@ -1,62 +1,62 @@
/******************************************************************************
* 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.
*****************************************************************************/
#if UNITY_2018_3 || UNITY_2019 || UNITY_2018_3_OR_NEWER
#define NEW_PREFAB_SYSTEM
#endif
using UnityEngine;
namespace Spine.Unity {
#if NEW_PREFAB_SYSTEM
[ExecuteAlways]
#else
[ExecuteInEditMode]
#endif
[RequireComponent(typeof(SkeletonUtilityBone))]
[HelpURL("http://esotericsoftware.com/spine-unity#SkeletonUtilityConstraint")]
public abstract class SkeletonUtilityConstraint : MonoBehaviour {
protected SkeletonUtilityBone bone;
protected SkeletonUtility hierarchy;
protected virtual void OnEnable () {
bone = GetComponent<SkeletonUtilityBone>();
hierarchy = transform.GetComponentInParent<SkeletonUtility>();
hierarchy.RegisterConstraint(this);
}
protected virtual void OnDisable () {
hierarchy.UnregisterConstraint(this);
}
public abstract void DoUpdate ();
}
}
/******************************************************************************
* 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.
*****************************************************************************/
#if UNITY_2018_3 || UNITY_2019 || UNITY_2018_3_OR_NEWER
#define NEW_PREFAB_SYSTEM
#endif
using UnityEngine;
namespace Spine.Unity {
#if NEW_PREFAB_SYSTEM
[ExecuteAlways]
#else
[ExecuteInEditMode]
#endif
[RequireComponent(typeof(SkeletonUtilityBone))]
[HelpURL("http://esotericsoftware.com/spine-unity#SkeletonUtilityConstraint")]
public abstract class SkeletonUtilityConstraint : MonoBehaviour {
protected SkeletonUtilityBone bone;
protected SkeletonUtility hierarchy;
protected virtual void OnEnable () {
bone = GetComponent<SkeletonUtilityBone>();
hierarchy = transform.GetComponentInParent<SkeletonUtility>();
hierarchy.RegisterConstraint(this);
}
protected virtual void OnDisable () {
hierarchy.UnregisterConstraint(this);
}
public abstract void DoUpdate ();
}
}

View File

@@ -1,89 +1,89 @@
/******************************************************************************
* 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.
*****************************************************************************/
namespace Spine.Unity {
public enum UpdateMode {
Nothing = 0,
OnlyAnimationStatus,
OnlyEventTimelines = 4, // added as index 4 to keep scene behavior unchanged.
EverythingExceptMesh = 2,
FullUpdate,
//Reserved 4 for OnlyEventTimelines
};
public delegate void UpdateBonesDelegate (ISkeletonAnimation animated);
public interface ISpineComponent { }
public static class ISpineComponentExtensions {
public static bool IsNullOrDestroyed (this ISpineComponent component) {
if (component == null) return true;
return (UnityEngine.Object)component == null;
}
}
/// <summary>A Spine-Unity Component that animates a Skeleton but not necessarily with a Spine.AnimationState.</summary>
public interface ISkeletonAnimation : ISpineComponent {
event UpdateBonesDelegate UpdateLocal;
event UpdateBonesDelegate UpdateWorld;
event UpdateBonesDelegate UpdateComplete;
Skeleton Skeleton { get; }
}
/// <summary>Holds a reference to a SkeletonDataAsset.</summary>
public interface IHasSkeletonDataAsset : ISpineComponent {
/// <summary>Gets the SkeletonDataAsset of the Spine Component.</summary>
SkeletonDataAsset SkeletonDataAsset { get; }
}
/// <summary>A Spine-Unity Component that manages a Spine.Skeleton instance, instantiated from a SkeletonDataAsset.</summary>
public interface ISkeletonComponent : ISpineComponent {
/// <summary>Gets the SkeletonDataAsset of the Spine Component.</summary>
//[System.Obsolete]
SkeletonDataAsset SkeletonDataAsset { get; }
/// <summary>Gets the Spine.Skeleton instance of the Spine Component. This is equivalent to SkeletonRenderer's .skeleton.</summary>
Skeleton Skeleton { get; }
}
/// <summary>A Spine-Unity Component that uses a Spine.AnimationState to animate its skeleton.</summary>
public interface IAnimationStateComponent : ISpineComponent {
/// <summary>Gets the Spine.AnimationState of the animated Spine Component. This is equivalent to SkeletonAnimation.state.</summary>
AnimationState AnimationState { get; }
}
/// <summary>A Spine-Unity Component that holds a reference to a SkeletonRenderer.</summary>
public interface IHasSkeletonRenderer : ISpineComponent {
SkeletonRenderer SkeletonRenderer { get; }
}
/// <summary>A Spine-Unity Component that holds a reference to an ISkeletonComponent.</summary>
public interface IHasSkeletonComponent : ISpineComponent {
ISkeletonComponent SkeletonComponent { get; }
}
}
/******************************************************************************
* 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.
*****************************************************************************/
namespace Spine.Unity {
public enum UpdateMode {
Nothing = 0,
OnlyAnimationStatus,
OnlyEventTimelines = 4, // added as index 4 to keep scene behavior unchanged.
EverythingExceptMesh = 2,
FullUpdate,
//Reserved 4 for OnlyEventTimelines
};
public delegate void UpdateBonesDelegate (ISkeletonAnimation animated);
public interface ISpineComponent { }
public static class ISpineComponentExtensions {
public static bool IsNullOrDestroyed (this ISpineComponent component) {
if (component == null) return true;
return (UnityEngine.Object)component == null;
}
}
/// <summary>A Spine-Unity Component that animates a Skeleton but not necessarily with a Spine.AnimationState.</summary>
public interface ISkeletonAnimation : ISpineComponent {
event UpdateBonesDelegate UpdateLocal;
event UpdateBonesDelegate UpdateWorld;
event UpdateBonesDelegate UpdateComplete;
Skeleton Skeleton { get; }
}
/// <summary>Holds a reference to a SkeletonDataAsset.</summary>
public interface IHasSkeletonDataAsset : ISpineComponent {
/// <summary>Gets the SkeletonDataAsset of the Spine Component.</summary>
SkeletonDataAsset SkeletonDataAsset { get; }
}
/// <summary>A Spine-Unity Component that manages a Spine.Skeleton instance, instantiated from a SkeletonDataAsset.</summary>
public interface ISkeletonComponent : ISpineComponent {
/// <summary>Gets the SkeletonDataAsset of the Spine Component.</summary>
//[System.Obsolete]
SkeletonDataAsset SkeletonDataAsset { get; }
/// <summary>Gets the Spine.Skeleton instance of the Spine Component. This is equivalent to SkeletonRenderer's .skeleton.</summary>
Skeleton Skeleton { get; }
}
/// <summary>A Spine-Unity Component that uses a Spine.AnimationState to animate its skeleton.</summary>
public interface IAnimationStateComponent : ISpineComponent {
/// <summary>Gets the Spine.AnimationState of the animated Spine Component. This is equivalent to SkeletonAnimation.state.</summary>
AnimationState AnimationState { get; }
}
/// <summary>A Spine-Unity Component that holds a reference to a SkeletonRenderer.</summary>
public interface IHasSkeletonRenderer : ISpineComponent {
SkeletonRenderer SkeletonRenderer { get; }
}
/// <summary>A Spine-Unity Component that holds a reference to an ISkeletonComponent.</summary>
public interface IHasSkeletonComponent : ISpineComponent {
ISkeletonComponent SkeletonComponent { get; }
}
}

View File

@@ -1,45 +1,45 @@
/******************************************************************************
* 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.
*****************************************************************************/
namespace Spine.Unity {
public class DoubleBuffered<T> where T : new() {
readonly T a = new T();
readonly T b = new T();
bool usingA;
public T GetCurrent () {
return usingA ? a : b;
}
public T GetNext () {
usingA = !usingA;
return usingA ? a : b;
}
}
}
/******************************************************************************
* 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.
*****************************************************************************/
namespace Spine.Unity {
public class DoubleBuffered<T> where T : new() {
readonly T a = new T();
readonly T b = new T();
bool usingA;
public T GetCurrent () {
return usingA ? a : b;
}
public T GetNext () {
usingA = !usingA;
return usingA ? a : b;
}
}
}

View File

@@ -1,135 +1,135 @@
/******************************************************************************
* 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.
*****************************************************************************/
// Not for optimization. Do not disable.
#define SPINE_TRIANGLECHECK // Avoid calling SetTriangles at the cost of checking for mesh differences (vertex counts, memberwise attachment list compare) every frame.
//#define SPINE_DEBUG
using System;
using System.Collections.Generic;
using UnityEngine;
namespace Spine.Unity {
/// <summary>A double-buffered Mesh, and a shared material array, bundled for use by Spine components that need to push a Mesh and materials to a Unity MeshRenderer and MeshFilter.</summary>
public class MeshRendererBuffers : IDisposable {
DoubleBuffered<SmartMesh> doubleBufferedMesh;
internal readonly ExposedList<Material> submeshMaterials = new ExposedList<Material>();
internal Material[] sharedMaterials = new Material[0];
public void Initialize () {
if (doubleBufferedMesh != null) {
doubleBufferedMesh.GetNext().Clear();
doubleBufferedMesh.GetNext().Clear();
submeshMaterials.Clear();
} else {
doubleBufferedMesh = new DoubleBuffered<SmartMesh>();
}
}
/// <summary>Returns a sharedMaterials array for use on a MeshRenderer.</summary>
/// <returns></returns>
public Material[] GetUpdatedSharedMaterialsArray () {
if (submeshMaterials.Count == sharedMaterials.Length)
submeshMaterials.CopyTo(sharedMaterials);
else
sharedMaterials = submeshMaterials.ToArray();
return sharedMaterials;
}
/// <summary>Returns true if the materials were modified since the buffers were last updated.</summary>
public bool MaterialsChangedInLastUpdate () {
int newSubmeshMaterials = submeshMaterials.Count;
var sharedMaterials = this.sharedMaterials;
if (newSubmeshMaterials != sharedMaterials.Length) return true;
var submeshMaterialsItems = submeshMaterials.Items;
for (int i = 0; i < newSubmeshMaterials; i++)
if (!Material.ReferenceEquals(submeshMaterialsItems[i], sharedMaterials[i])) return true; //if (submeshMaterialsItems[i].GetInstanceID() != sharedMaterials[i].GetInstanceID()) return true;
return false;
}
/// <summary>Updates the internal shared materials array with the given instruction list.</summary>
public void UpdateSharedMaterials (ExposedList<SubmeshInstruction> instructions) {
int newSize = instructions.Count;
{ //submeshMaterials.Resize(instructions.Count);
if (newSize > submeshMaterials.Items.Length)
Array.Resize(ref submeshMaterials.Items, newSize);
submeshMaterials.Count = newSize;
}
var submeshMaterialsItems = submeshMaterials.Items;
var instructionsItems = instructions.Items;
for (int i = 0; i < newSize; i++)
submeshMaterialsItems[i] = instructionsItems[i].material;
}
public SmartMesh GetNextMesh () {
return doubleBufferedMesh.GetNext();
}
public void Clear () {
sharedMaterials = new Material[0];
submeshMaterials.Clear();
}
public void Dispose () {
if (doubleBufferedMesh == null) return;
doubleBufferedMesh.GetNext().Dispose();
doubleBufferedMesh.GetNext().Dispose();
doubleBufferedMesh = null;
}
///<summary>This is a Mesh that also stores the instructions SkeletonRenderer generated for it.</summary>
public class SmartMesh : IDisposable {
public Mesh mesh = SpineMesh.NewSkeletonMesh();
public SkeletonRendererInstruction instructionUsed = new SkeletonRendererInstruction();
public void Clear () {
mesh.Clear();
instructionUsed.Clear();
}
public void Dispose () {
if (mesh != null) {
#if UNITY_EDITOR
if (Application.isEditor && !Application.isPlaying)
UnityEngine.Object.DestroyImmediate(mesh);
else
UnityEngine.Object.Destroy(mesh);
#else
UnityEngine.Object.Destroy(mesh);
#endif
}
mesh = null;
}
}
}
}
/******************************************************************************
* 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.
*****************************************************************************/
// Not for optimization. Do not disable.
#define SPINE_TRIANGLECHECK // Avoid calling SetTriangles at the cost of checking for mesh differences (vertex counts, memberwise attachment list compare) every frame.
//#define SPINE_DEBUG
using System;
using System.Collections.Generic;
using UnityEngine;
namespace Spine.Unity {
/// <summary>A double-buffered Mesh, and a shared material array, bundled for use by Spine components that need to push a Mesh and materials to a Unity MeshRenderer and MeshFilter.</summary>
public class MeshRendererBuffers : IDisposable {
DoubleBuffered<SmartMesh> doubleBufferedMesh;
internal readonly ExposedList<Material> submeshMaterials = new ExposedList<Material>();
internal Material[] sharedMaterials = new Material[0];
public void Initialize () {
if (doubleBufferedMesh != null) {
doubleBufferedMesh.GetNext().Clear();
doubleBufferedMesh.GetNext().Clear();
submeshMaterials.Clear();
} else {
doubleBufferedMesh = new DoubleBuffered<SmartMesh>();
}
}
/// <summary>Returns a sharedMaterials array for use on a MeshRenderer.</summary>
/// <returns></returns>
public Material[] GetUpdatedSharedMaterialsArray () {
if (submeshMaterials.Count == sharedMaterials.Length)
submeshMaterials.CopyTo(sharedMaterials);
else
sharedMaterials = submeshMaterials.ToArray();
return sharedMaterials;
}
/// <summary>Returns true if the materials were modified since the buffers were last updated.</summary>
public bool MaterialsChangedInLastUpdate () {
int newSubmeshMaterials = submeshMaterials.Count;
var sharedMaterials = this.sharedMaterials;
if (newSubmeshMaterials != sharedMaterials.Length) return true;
var submeshMaterialsItems = submeshMaterials.Items;
for (int i = 0; i < newSubmeshMaterials; i++)
if (!Material.ReferenceEquals(submeshMaterialsItems[i], sharedMaterials[i])) return true; //if (submeshMaterialsItems[i].GetInstanceID() != sharedMaterials[i].GetInstanceID()) return true;
return false;
}
/// <summary>Updates the internal shared materials array with the given instruction list.</summary>
public void UpdateSharedMaterials (ExposedList<SubmeshInstruction> instructions) {
int newSize = instructions.Count;
{ //submeshMaterials.Resize(instructions.Count);
if (newSize > submeshMaterials.Items.Length)
Array.Resize(ref submeshMaterials.Items, newSize);
submeshMaterials.Count = newSize;
}
var submeshMaterialsItems = submeshMaterials.Items;
var instructionsItems = instructions.Items;
for (int i = 0; i < newSize; i++)
submeshMaterialsItems[i] = instructionsItems[i].material;
}
public SmartMesh GetNextMesh () {
return doubleBufferedMesh.GetNext();
}
public void Clear () {
sharedMaterials = new Material[0];
submeshMaterials.Clear();
}
public void Dispose () {
if (doubleBufferedMesh == null) return;
doubleBufferedMesh.GetNext().Dispose();
doubleBufferedMesh.GetNext().Dispose();
doubleBufferedMesh = null;
}
///<summary>This is a Mesh that also stores the instructions SkeletonRenderer generated for it.</summary>
public class SmartMesh : IDisposable {
public Mesh mesh = SpineMesh.NewSkeletonMesh();
public SkeletonRendererInstruction instructionUsed = new SkeletonRendererInstruction();
public void Clear () {
mesh.Clear();
instructionUsed.Clear();
}
public void Dispose () {
if (mesh != null) {
#if UNITY_EDITOR
if (Application.isEditor && !Application.isPlaying)
UnityEngine.Object.DestroyImmediate(mesh);
else
UnityEngine.Object.Destroy(mesh);
#else
UnityEngine.Object.Destroy(mesh);
#endif
}
mesh = null;
}
}
}
}

View File

@@ -1,178 +1,178 @@
/******************************************************************************
* 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.
*****************************************************************************/
// Not for optimization. Do not disable.
#define SPINE_TRIANGLECHECK // Avoid calling SetTriangles at the cost of checking for mesh differences (vertex counts, memberwise attachment list compare) every frame.
//#define SPINE_DEBUG
using System;
using System.Collections.Generic;
using UnityEngine;
namespace Spine.Unity {
/// <summary>Instructions used by a SkeletonRenderer to render a mesh.</summary>
public class SkeletonRendererInstruction {
public readonly ExposedList<SubmeshInstruction> submeshInstructions = new ExposedList<SubmeshInstruction>();
public bool immutableTriangles;
#if SPINE_TRIANGLECHECK
public bool hasActiveClipping;
public int rawVertexCount = -1;
public readonly ExposedList<Attachment> attachments = new ExposedList<Attachment>();
#endif
public void Clear () {
#if SPINE_TRIANGLECHECK
this.attachments.Clear(false);
rawVertexCount = -1;
hasActiveClipping = false;
#endif
this.submeshInstructions.Clear(false);
}
public void Dispose () {
attachments.Clear(true);
}
public void SetWithSubset (ExposedList<SubmeshInstruction> instructions, int startSubmesh, int endSubmesh) {
#if SPINE_TRIANGLECHECK
int runningVertexCount = 0;
#endif
var submeshes = this.submeshInstructions;
submeshes.Clear(false);
int submeshCount = endSubmesh - startSubmesh;
submeshes.Resize(submeshCount);
var submeshesItems = submeshes.Items;
var instructionsItems = instructions.Items;
for (int i = 0; i < submeshCount; i++) {
var instruction = instructionsItems[startSubmesh + i];
submeshesItems[i] = instruction;
#if SPINE_TRIANGLECHECK
this.hasActiveClipping |= instruction.hasClipping;
submeshesItems[i].rawFirstVertexIndex = runningVertexCount; // Ensure current instructions have correct cached values.
runningVertexCount += instruction.rawVertexCount; // vertexCount will also be used for the rest of this method.
#endif
}
#if SPINE_TRIANGLECHECK
this.rawVertexCount = runningVertexCount;
// assumption: instructions are contiguous. start and end are valid within instructions.
int startSlot = instructionsItems[startSubmesh].startSlot;
int endSlot = instructionsItems[endSubmesh - 1].endSlot;
attachments.Clear(false);
int attachmentCount = endSlot - startSlot;
attachments.Resize(attachmentCount);
var attachmentsItems = attachments.Items;
var drawOrderItems = instructionsItems[0].skeleton.DrawOrder.Items;
for (int i = 0; i < attachmentCount; i++) {
Slot slot = drawOrderItems[startSlot + i];
if (!slot.Bone.Active) continue;
attachmentsItems[i] = slot.Attachment;
}
#endif
}
public void Set (SkeletonRendererInstruction other) {
this.immutableTriangles = other.immutableTriangles;
#if SPINE_TRIANGLECHECK
this.hasActiveClipping = other.hasActiveClipping;
this.rawVertexCount = other.rawVertexCount;
this.attachments.Clear(false);
this.attachments.EnsureCapacity(other.attachments.Capacity);
this.attachments.Count = other.attachments.Count;
other.attachments.CopyTo(this.attachments.Items);
#endif
this.submeshInstructions.Clear(false);
this.submeshInstructions.EnsureCapacity(other.submeshInstructions.Capacity);
this.submeshInstructions.Count = other.submeshInstructions.Count;
other.submeshInstructions.CopyTo(this.submeshInstructions.Items);
}
public static bool GeometryNotEqual (SkeletonRendererInstruction a, SkeletonRendererInstruction b) {
#if SPINE_TRIANGLECHECK
#if UNITY_EDITOR
if (!Application.isPlaying)
return true;
#endif
if (a.hasActiveClipping || b.hasActiveClipping) return true; // Triangles are unpredictable when clipping is active.
// Everything below assumes the raw vertex and triangle counts were used. (ie, no clipping was done)
if (a.rawVertexCount != b.rawVertexCount) return true;
if (a.immutableTriangles != b.immutableTriangles) return true;
int attachmentCountB = b.attachments.Count;
if (a.attachments.Count != attachmentCountB) return true; // Bounds check for the looped storedAttachments count below.
// Submesh count changed
int submeshCountA = a.submeshInstructions.Count;
int submeshCountB = b.submeshInstructions.Count;
if (submeshCountA != submeshCountB) return true;
// Submesh Instruction mismatch
var submeshInstructionsItemsA = a.submeshInstructions.Items;
var submeshInstructionsItemsB = b.submeshInstructions.Items;
var attachmentsA = a.attachments.Items;
var attachmentsB = b.attachments.Items;
for (int i = 0; i < attachmentCountB; i++)
if (!System.Object.ReferenceEquals(attachmentsA[i], attachmentsB[i])) return true;
for (int i = 0; i < submeshCountB; i++) {
var submeshA = submeshInstructionsItemsA[i];
var submeshB = submeshInstructionsItemsB[i];
if (!(
submeshA.rawVertexCount == submeshB.rawVertexCount &&
submeshA.startSlot == submeshB.startSlot &&
submeshA.endSlot == submeshB.endSlot
&& submeshA.rawTriangleCount == submeshB.rawTriangleCount &&
submeshA.rawFirstVertexIndex == submeshB.rawFirstVertexIndex
))
return true;
}
return false;
#else
// In normal immutable triangle use, immutableTriangles will be initially false, forcing the smartmesh to update the first time but never again after that, unless there was an immutableTriangles flag mismatch..
if (a.immutableTriangles || b.immutableTriangles)
return (a.immutableTriangles != b.immutableTriangles);
return true;
#endif
}
}
}
/******************************************************************************
* 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.
*****************************************************************************/
// Not for optimization. Do not disable.
#define SPINE_TRIANGLECHECK // Avoid calling SetTriangles at the cost of checking for mesh differences (vertex counts, memberwise attachment list compare) every frame.
//#define SPINE_DEBUG
using System;
using System.Collections.Generic;
using UnityEngine;
namespace Spine.Unity {
/// <summary>Instructions used by a SkeletonRenderer to render a mesh.</summary>
public class SkeletonRendererInstruction {
public readonly ExposedList<SubmeshInstruction> submeshInstructions = new ExposedList<SubmeshInstruction>();
public bool immutableTriangles;
#if SPINE_TRIANGLECHECK
public bool hasActiveClipping;
public int rawVertexCount = -1;
public readonly ExposedList<Attachment> attachments = new ExposedList<Attachment>();
#endif
public void Clear () {
#if SPINE_TRIANGLECHECK
this.attachments.Clear(false);
rawVertexCount = -1;
hasActiveClipping = false;
#endif
this.submeshInstructions.Clear(false);
}
public void Dispose () {
attachments.Clear(true);
}
public void SetWithSubset (ExposedList<SubmeshInstruction> instructions, int startSubmesh, int endSubmesh) {
#if SPINE_TRIANGLECHECK
int runningVertexCount = 0;
#endif
var submeshes = this.submeshInstructions;
submeshes.Clear(false);
int submeshCount = endSubmesh - startSubmesh;
submeshes.Resize(submeshCount);
var submeshesItems = submeshes.Items;
var instructionsItems = instructions.Items;
for (int i = 0; i < submeshCount; i++) {
var instruction = instructionsItems[startSubmesh + i];
submeshesItems[i] = instruction;
#if SPINE_TRIANGLECHECK
this.hasActiveClipping |= instruction.hasClipping;
submeshesItems[i].rawFirstVertexIndex = runningVertexCount; // Ensure current instructions have correct cached values.
runningVertexCount += instruction.rawVertexCount; // vertexCount will also be used for the rest of this method.
#endif
}
#if SPINE_TRIANGLECHECK
this.rawVertexCount = runningVertexCount;
// assumption: instructions are contiguous. start and end are valid within instructions.
int startSlot = instructionsItems[startSubmesh].startSlot;
int endSlot = instructionsItems[endSubmesh - 1].endSlot;
attachments.Clear(false);
int attachmentCount = endSlot - startSlot;
attachments.Resize(attachmentCount);
var attachmentsItems = attachments.Items;
var drawOrderItems = instructionsItems[0].skeleton.DrawOrder.Items;
for (int i = 0; i < attachmentCount; i++) {
Slot slot = drawOrderItems[startSlot + i];
if (!slot.Bone.Active) continue;
attachmentsItems[i] = slot.Attachment;
}
#endif
}
public void Set (SkeletonRendererInstruction other) {
this.immutableTriangles = other.immutableTriangles;
#if SPINE_TRIANGLECHECK
this.hasActiveClipping = other.hasActiveClipping;
this.rawVertexCount = other.rawVertexCount;
this.attachments.Clear(false);
this.attachments.EnsureCapacity(other.attachments.Capacity);
this.attachments.Count = other.attachments.Count;
other.attachments.CopyTo(this.attachments.Items);
#endif
this.submeshInstructions.Clear(false);
this.submeshInstructions.EnsureCapacity(other.submeshInstructions.Capacity);
this.submeshInstructions.Count = other.submeshInstructions.Count;
other.submeshInstructions.CopyTo(this.submeshInstructions.Items);
}
public static bool GeometryNotEqual (SkeletonRendererInstruction a, SkeletonRendererInstruction b) {
#if SPINE_TRIANGLECHECK
#if UNITY_EDITOR
if (!Application.isPlaying)
return true;
#endif
if (a.hasActiveClipping || b.hasActiveClipping) return true; // Triangles are unpredictable when clipping is active.
// Everything below assumes the raw vertex and triangle counts were used. (ie, no clipping was done)
if (a.rawVertexCount != b.rawVertexCount) return true;
if (a.immutableTriangles != b.immutableTriangles) return true;
int attachmentCountB = b.attachments.Count;
if (a.attachments.Count != attachmentCountB) return true; // Bounds check for the looped storedAttachments count below.
// Submesh count changed
int submeshCountA = a.submeshInstructions.Count;
int submeshCountB = b.submeshInstructions.Count;
if (submeshCountA != submeshCountB) return true;
// Submesh Instruction mismatch
var submeshInstructionsItemsA = a.submeshInstructions.Items;
var submeshInstructionsItemsB = b.submeshInstructions.Items;
var attachmentsA = a.attachments.Items;
var attachmentsB = b.attachments.Items;
for (int i = 0; i < attachmentCountB; i++)
if (!System.Object.ReferenceEquals(attachmentsA[i], attachmentsB[i])) return true;
for (int i = 0; i < submeshCountB; i++) {
var submeshA = submeshInstructionsItemsA[i];
var submeshB = submeshInstructionsItemsB[i];
if (!(
submeshA.rawVertexCount == submeshB.rawVertexCount &&
submeshA.startSlot == submeshB.startSlot &&
submeshA.endSlot == submeshB.endSlot
&& submeshA.rawTriangleCount == submeshB.rawTriangleCount &&
submeshA.rawFirstVertexIndex == submeshB.rawFirstVertexIndex
))
return true;
}
return false;
#else
// In normal immutable triangle use, immutableTriangles will be initially false, forcing the smartmesh to update the first time but never again after that, unless there was an immutableTriangles flag mismatch..
if (a.immutableTriangles || b.immutableTriangles)
return (a.immutableTriangles != b.immutableTriangles);
return true;
#endif
}
}
}

View File

@@ -1,85 +1,85 @@
/******************************************************************************
* 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.
*****************************************************************************/
// Not for optimization. Do not disable.
#define SPINE_TRIANGLECHECK // Avoid calling SetTriangles at the cost of checking for mesh differences (vertex counts, memberwise attachment list compare) every frame.
//#define SPINE_DEBUG
using System;
using System.Collections.Generic;
using UnityEngine;
namespace Spine.Unity {
public static class SpineMesh {
internal const HideFlags MeshHideflags = HideFlags.DontSaveInBuild | HideFlags.DontSaveInEditor;
/// <summary>Factory method for creating a new mesh for use in Spine components. This can be called in field initializers.</summary>
public static Mesh NewSkeletonMesh () {
var m = new Mesh();
m.MarkDynamic();
m.name = "Skeleton Mesh";
m.hideFlags = SpineMesh.MeshHideflags;
return m;
}
}
/// <summary>Instructions for how to generate a mesh or submesh: "Render this skeleton's slots: start slot, up to but not including endSlot, using this material."</summary>
public struct SubmeshInstruction {
public Skeleton skeleton;
public int startSlot;
public int endSlot;
public Material material;
public bool forceSeparate;
public int preActiveClippingSlotSource;
#if SPINE_TRIANGLECHECK
// Cached values because they are determined in the process of generating instructions,
// but could otherwise be pulled from accessing attachments, checking materials and counting tris and verts.
public int rawTriangleCount;
public int rawVertexCount;
public int rawFirstVertexIndex;
public bool hasClipping;
#endif
public bool hasPMAAdditiveSlot;
/// <summary>The number of slots in this SubmeshInstruction's range. Not necessarily the number of attachments.</summary>
public int SlotCount { get { return endSlot - startSlot; } }
public override string ToString () {
return
string.Format("[SubmeshInstruction: slots {0} to {1}. (Material){2}. preActiveClippingSlotSource:{3}]",
startSlot,
endSlot - 1,
material == null ? "<none>" : material.name,
preActiveClippingSlotSource
);
}
}
}
/******************************************************************************
* 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.
*****************************************************************************/
// Not for optimization. Do not disable.
#define SPINE_TRIANGLECHECK // Avoid calling SetTriangles at the cost of checking for mesh differences (vertex counts, memberwise attachment list compare) every frame.
//#define SPINE_DEBUG
using System;
using System.Collections.Generic;
using UnityEngine;
namespace Spine.Unity {
public static class SpineMesh {
internal const HideFlags MeshHideflags = HideFlags.DontSaveInBuild | HideFlags.DontSaveInEditor;
/// <summary>Factory method for creating a new mesh for use in Spine components. This can be called in field initializers.</summary>
public static Mesh NewSkeletonMesh () {
var m = new Mesh();
m.MarkDynamic();
m.name = "Skeleton Mesh";
m.hideFlags = SpineMesh.MeshHideflags;
return m;
}
}
/// <summary>Instructions for how to generate a mesh or submesh: "Render this skeleton's slots: start slot, up to but not including endSlot, using this material."</summary>
public struct SubmeshInstruction {
public Skeleton skeleton;
public int startSlot;
public int endSlot;
public Material material;
public bool forceSeparate;
public int preActiveClippingSlotSource;
#if SPINE_TRIANGLECHECK
// Cached values because they are determined in the process of generating instructions,
// but could otherwise be pulled from accessing attachments, checking materials and counting tris and verts.
public int rawTriangleCount;
public int rawVertexCount;
public int rawFirstVertexIndex;
public bool hasClipping;
#endif
public bool hasPMAAdditiveSlot;
/// <summary>The number of slots in this SubmeshInstruction's range. Not necessarily the number of attachments.</summary>
public int SlotCount { get { return endSlot - startSlot; } }
public override string ToString () {
return
string.Format("[SubmeshInstruction: slots {0} to {1}. (Material){2}. preActiveClippingSlotSource:{3}]",
startSlot,
endSlot - 1,
material == null ? "<none>" : material.name,
preActiveClippingSlotSource
);
}
}
}

View File

@@ -1,156 +1,156 @@
/******************************************************************************
* 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.
*****************************************************************************/
#if SPINE_TK2D
using System;
using UnityEngine;
using Spine;
// MITCH: handle TPackerCW flip mode (probably not swap uv horizontaly)
namespace Spine.Unity.TK2D {
public class SpriteCollectionAttachmentLoader : AttachmentLoader {
private tk2dSpriteCollectionData sprites;
private float u, v, u2, v2;
private bool regionRotated;
private float regionOriginalWidth, regionOriginalHeight;
private float regionWidth, regionHeight;
private float regionOffsetX, regionOffsetY;
private Material material;
public SpriteCollectionAttachmentLoader (tk2dSpriteCollectionData sprites) {
if (sprites == null)
throw new ArgumentNullException("sprites cannot be null.");
this.sprites = sprites;
}
private void ProcessSpriteDefinition (String name) {
// Strip folder names.
int index = name.LastIndexOfAny(new char[] {'/', '\\'});
if (index != -1)
name = name.Substring(index + 1);
tk2dSpriteDefinition def = sprites.inst.GetSpriteDefinition(name);
if (def == null) {
Debug.Log("Sprite not found in atlas: " + name, sprites);
throw new Exception("Sprite not found in atlas: " + name);
}
if (def.complexGeometry)
throw new NotImplementedException("Complex geometry is not supported: " + name);
if (def.flipped == tk2dSpriteDefinition.FlipMode.TPackerCW)
throw new NotImplementedException("Only 2D Toolkit atlases are supported: " + name);
Vector2 minTexCoords = Vector2.one, maxTexCoords = Vector2.zero;
for (int i = 0; i < def.uvs.Length; ++i) {
Vector2 uv = def.uvs[i];
minTexCoords = Vector2.Min(minTexCoords, uv);
maxTexCoords = Vector2.Max(maxTexCoords, uv);
}
regionRotated = def.flipped == tk2dSpriteDefinition.FlipMode.Tk2d;
if (regionRotated) {
float temp = minTexCoords.x;
minTexCoords.x = maxTexCoords.x;
maxTexCoords.x = temp;
}
u = minTexCoords.x;
v = maxTexCoords.y;
u2 = maxTexCoords.x;
v2 = minTexCoords.y;
regionOriginalWidth = (int)(def.untrimmedBoundsData[1].x / def.texelSize.x);
regionOriginalHeight = (int)(def.untrimmedBoundsData[1].y / def.texelSize.y);
regionWidth = (int)(def.boundsData[1].x / def.texelSize.x);
regionHeight = (int)(def.boundsData[1].y / def.texelSize.y);
float x0 = def.untrimmedBoundsData[0].x - def.untrimmedBoundsData[1].x / 2;
float x1 = def.boundsData[0].x - def.boundsData[1].x / 2;
regionOffsetX = (int)((x1 - x0) / def.texelSize.x);
float y0 = def.untrimmedBoundsData[0].y - def.untrimmedBoundsData[1].y / 2;
float y1 = def.boundsData[0].y - def.boundsData[1].y / 2;
regionOffsetY = (int)((y1 - y0) / def.texelSize.y);
material = def.materialInst;
}
public RegionAttachment NewRegionAttachment (Skin skin, String name, String path) {
ProcessSpriteDefinition(path);
RegionAttachment region = new RegionAttachment(name);
region.Path = path;
region.RendererObject = material;
region.SetUVs(u, v, u2, v2, regionRotated);
region.RegionOriginalWidth = regionOriginalWidth;
region.RegionOriginalHeight = regionOriginalHeight;
region.RegionWidth = regionWidth;
region.RegionHeight = regionHeight;
region.RegionOffsetX = regionOffsetX;
region.RegionOffsetY = regionOffsetY;
return region;
}
public MeshAttachment NewMeshAttachment (Skin skin, String name, String path) {
ProcessSpriteDefinition(path);
MeshAttachment mesh = new MeshAttachment(name);
mesh.Path = path;
mesh.RendererObject = material;
mesh.RegionU = u;
mesh.RegionV = v;
mesh.RegionU2 = u2;
mesh.RegionV2 = v2;
mesh.RegionRotate = regionRotated;
mesh.RegionOriginalWidth = regionOriginalWidth;
mesh.RegionOriginalHeight = regionOriginalHeight;
mesh.RegionWidth = regionWidth;
mesh.RegionHeight = regionHeight;
mesh.RegionOffsetX = regionOffsetX;
mesh.RegionOffsetY = regionOffsetY;
return mesh;
}
public BoundingBoxAttachment NewBoundingBoxAttachment (Skin skin, String name) {
return new BoundingBoxAttachment(name);
}
public PathAttachment NewPathAttachment (Skin skin, string name) {
return new PathAttachment(name);
}
public PointAttachment NewPointAttachment (Skin skin, string name) {
return new PointAttachment(name);
}
public ClippingAttachment NewClippingAttachment (Skin skin, string name) {
return new ClippingAttachment(name);
}
}
}
#endif
/******************************************************************************
* 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.
*****************************************************************************/
#if SPINE_TK2D
using System;
using UnityEngine;
using Spine;
// MITCH: handle TPackerCW flip mode (probably not swap uv horizontaly)
namespace Spine.Unity.TK2D {
public class SpriteCollectionAttachmentLoader : AttachmentLoader {
private tk2dSpriteCollectionData sprites;
private float u, v, u2, v2;
private bool regionRotated;
private float regionOriginalWidth, regionOriginalHeight;
private float regionWidth, regionHeight;
private float regionOffsetX, regionOffsetY;
private Material material;
public SpriteCollectionAttachmentLoader (tk2dSpriteCollectionData sprites) {
if (sprites == null)
throw new ArgumentNullException("sprites cannot be null.");
this.sprites = sprites;
}
private void ProcessSpriteDefinition (String name) {
// Strip folder names.
int index = name.LastIndexOfAny(new char[] {'/', '\\'});
if (index != -1)
name = name.Substring(index + 1);
tk2dSpriteDefinition def = sprites.inst.GetSpriteDefinition(name);
if (def == null) {
Debug.Log("Sprite not found in atlas: " + name, sprites);
throw new Exception("Sprite not found in atlas: " + name);
}
if (def.complexGeometry)
throw new NotImplementedException("Complex geometry is not supported: " + name);
if (def.flipped == tk2dSpriteDefinition.FlipMode.TPackerCW)
throw new NotImplementedException("Only 2D Toolkit atlases are supported: " + name);
Vector2 minTexCoords = Vector2.one, maxTexCoords = Vector2.zero;
for (int i = 0; i < def.uvs.Length; ++i) {
Vector2 uv = def.uvs[i];
minTexCoords = Vector2.Min(minTexCoords, uv);
maxTexCoords = Vector2.Max(maxTexCoords, uv);
}
regionRotated = def.flipped == tk2dSpriteDefinition.FlipMode.Tk2d;
if (regionRotated) {
float temp = minTexCoords.x;
minTexCoords.x = maxTexCoords.x;
maxTexCoords.x = temp;
}
u = minTexCoords.x;
v = maxTexCoords.y;
u2 = maxTexCoords.x;
v2 = minTexCoords.y;
regionOriginalWidth = (int)(def.untrimmedBoundsData[1].x / def.texelSize.x);
regionOriginalHeight = (int)(def.untrimmedBoundsData[1].y / def.texelSize.y);
regionWidth = (int)(def.boundsData[1].x / def.texelSize.x);
regionHeight = (int)(def.boundsData[1].y / def.texelSize.y);
float x0 = def.untrimmedBoundsData[0].x - def.untrimmedBoundsData[1].x / 2;
float x1 = def.boundsData[0].x - def.boundsData[1].x / 2;
regionOffsetX = (int)((x1 - x0) / def.texelSize.x);
float y0 = def.untrimmedBoundsData[0].y - def.untrimmedBoundsData[1].y / 2;
float y1 = def.boundsData[0].y - def.boundsData[1].y / 2;
regionOffsetY = (int)((y1 - y0) / def.texelSize.y);
material = def.materialInst;
}
public RegionAttachment NewRegionAttachment (Skin skin, String name, String path) {
ProcessSpriteDefinition(path);
RegionAttachment region = new RegionAttachment(name);
region.Path = path;
region.RendererObject = material;
region.SetUVs(u, v, u2, v2, regionRotated);
region.RegionOriginalWidth = regionOriginalWidth;
region.RegionOriginalHeight = regionOriginalHeight;
region.RegionWidth = regionWidth;
region.RegionHeight = regionHeight;
region.RegionOffsetX = regionOffsetX;
region.RegionOffsetY = regionOffsetY;
return region;
}
public MeshAttachment NewMeshAttachment (Skin skin, String name, String path) {
ProcessSpriteDefinition(path);
MeshAttachment mesh = new MeshAttachment(name);
mesh.Path = path;
mesh.RendererObject = material;
mesh.RegionU = u;
mesh.RegionV = v;
mesh.RegionU2 = u2;
mesh.RegionV2 = v2;
mesh.RegionRotate = regionRotated;
mesh.RegionOriginalWidth = regionOriginalWidth;
mesh.RegionOriginalHeight = regionOriginalHeight;
mesh.RegionWidth = regionWidth;
mesh.RegionHeight = regionHeight;
mesh.RegionOffsetX = regionOffsetX;
mesh.RegionOffsetY = regionOffsetY;
return mesh;
}
public BoundingBoxAttachment NewBoundingBoxAttachment (Skin skin, String name) {
return new BoundingBoxAttachment(name);
}
public PathAttachment NewPathAttachment (Skin skin, string name) {
return new PathAttachment(name);
}
public PointAttachment NewPointAttachment (Skin skin, string name) {
return new PointAttachment(name);
}
public ClippingAttachment NewClippingAttachment (Skin skin, string name) {
return new ClippingAttachment(name);
}
}
}
#endif

View File

@@ -1,46 +1,46 @@
Contributed by ToddRivers
# Unity Sprite Shaders
An Uber Shader specialised for rendering Sprites in Unity.
Even though it's designed for Sprites it can be used for a whole range of uses. It supports a wide range of optional shader features that won't effect performance unless they are used.
It also supports per-pixel effects such as normal maps and diffuse ramping whilst using Vertex Lit rendering.
### Lighting
The shaders support lighting using both Forward Rendering and Vertex Lit Rendering.
Forward rendering is more accurate but is slower and crucially means the sprite has to write to depth using alpha clipping to avoid overdraw.
Vertex lit means all lighting can be done in one pass meaning full alpha can be used.
### Normal Mapping
Normals maps are supported in both lighting modes (in Vertex Lit rendering data for normal mapping is packed into texture channels and then processed per pixel).
### Blend Modes
Easily switch between blend modes including pre-multiplied alpha, additive, multiply etc.
### Rim Lighting
Camera-space rim lighting is supported in both lighting modes.
### Diffuse Ramp
A ramp texture is optionally supported for toon shading effects.
### Shadows
Shadows are supported using alpha clipping.
### Gradient based Ambient lighting
Both lighting modes support using a gradient for ambient light. In Vertex Lit mode the Spherical Harmonics is approximated from the ground, equator and sky colors.
### Emission Map
An optional emission map is supported.
### Camera Space Normals
As sprites are 2d their normals will always be constant. The shaders allow you to define a fixed normal in camera space rather than pass through mesh normals.
This not only saves vertex throughput but means lighting looks less 'flat' for rendering sprites with a perspective camera.
### Color Adjustment
The shaders allow optional adjustment of hue / saturation and brightness as well as applying a solid color overlay effect for flashing a sprite to a solid color (eg. for damage effects).
### Fog
Fog is optionally supported
## To Use
On your object's material click the drop down for shader and select Spine\Sprite\Pixel Lit, Vertex Lit or Unlit.
Contributed by ToddRivers
# Unity Sprite Shaders
An Uber Shader specialised for rendering Sprites in Unity.
Even though it's designed for Sprites it can be used for a whole range of uses. It supports a wide range of optional shader features that won't effect performance unless they are used.
It also supports per-pixel effects such as normal maps and diffuse ramping whilst using Vertex Lit rendering.
### Lighting
The shaders support lighting using both Forward Rendering and Vertex Lit Rendering.
Forward rendering is more accurate but is slower and crucially means the sprite has to write to depth using alpha clipping to avoid overdraw.
Vertex lit means all lighting can be done in one pass meaning full alpha can be used.
### Normal Mapping
Normals maps are supported in both lighting modes (in Vertex Lit rendering data for normal mapping is packed into texture channels and then processed per pixel).
### Blend Modes
Easily switch between blend modes including pre-multiplied alpha, additive, multiply etc.
### Rim Lighting
Camera-space rim lighting is supported in both lighting modes.
### Diffuse Ramp
A ramp texture is optionally supported for toon shading effects.
### Shadows
Shadows are supported using alpha clipping.
### Gradient based Ambient lighting
Both lighting modes support using a gradient for ambient light. In Vertex Lit mode the Spherical Harmonics is approximated from the ground, equator and sky colors.
### Emission Map
An optional emission map is supported.
### Camera Space Normals
As sprites are 2d their normals will always be constant. The shaders allow you to define a fixed normal in camera space rather than pass through mesh normals.
This not only saves vertex throughput but means lighting looks less 'flat' for rendering sprites with a perspective camera.
### Color Adjustment
The shaders allow optional adjustment of hue / saturation and brightness as well as applying a solid color overlay effect for flashing a sprite to a solid color (eg. for damage effects).
### Fog
Fog is optionally supported
## To Use
On your object's material click the drop down for shader and select Spine\Sprite\Pixel Lit, Vertex Lit or Unlit.

View File

@@ -1,128 +1,128 @@
/******************************************************************************
* 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.
*****************************************************************************/
using Spine;
using Spine.Unity;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Spine.Unity {
[CreateAssetMenu(menuName = "Spine/SkeletonData Modifiers/Blend Mode Materials", order = 200)]
public class BlendModeMaterialsAsset : SkeletonDataModifierAsset {
public Material multiplyMaterialTemplate;
public Material screenMaterialTemplate;
public Material additiveMaterialTemplate;
public bool applyAdditiveMaterial = true;
public override void Apply (SkeletonData skeletonData) {
ApplyMaterials(skeletonData, multiplyMaterialTemplate, screenMaterialTemplate, additiveMaterialTemplate, applyAdditiveMaterial);
}
public static void ApplyMaterials (SkeletonData skeletonData, Material multiplyTemplate, Material screenTemplate, Material additiveTemplate, bool includeAdditiveSlots) {
if (skeletonData == null) throw new ArgumentNullException("skeletonData");
using (var materialCache = new AtlasMaterialCache()) {
var entryBuffer = new List<Skin.SkinEntry>();
var slotsItems = skeletonData.Slots.Items;
for (int slotIndex = 0, slotCount = skeletonData.Slots.Count; slotIndex < slotCount; slotIndex++) {
var slot = slotsItems[slotIndex];
if (slot.BlendMode == BlendMode.Normal) continue;
if (!includeAdditiveSlots && slot.BlendMode == BlendMode.Additive) continue;
entryBuffer.Clear();
foreach (var skin in skeletonData.Skins)
skin.GetAttachments(slotIndex, entryBuffer);
Material templateMaterial = null;
switch (slot.BlendMode) {
case BlendMode.Multiply:
templateMaterial = multiplyTemplate;
break;
case BlendMode.Screen:
templateMaterial = screenTemplate;
break;
case BlendMode.Additive:
templateMaterial = additiveTemplate;
break;
}
if (templateMaterial == null) continue;
foreach (var entry in entryBuffer) {
var renderableAttachment = entry.Attachment as IHasRendererObject;
if (renderableAttachment != null) {
renderableAttachment.RendererObject = materialCache.CloneAtlasRegionWithMaterial((AtlasRegion)renderableAttachment.RendererObject, templateMaterial);
}
}
}
}
//attachmentBuffer.Clear();
}
class AtlasMaterialCache : IDisposable {
readonly Dictionary<KeyValuePair<AtlasPage, Material>, AtlasPage> cache = new Dictionary<KeyValuePair<AtlasPage, Material>, AtlasPage>();
/// <summary>Creates a clone of an AtlasRegion that uses different Material settings, while retaining the original texture.</summary>
public AtlasRegion CloneAtlasRegionWithMaterial (AtlasRegion originalRegion, Material materialTemplate) {
var newRegion = originalRegion.Clone();
newRegion.page = GetAtlasPageWithMaterial(originalRegion.page, materialTemplate);
return newRegion;
}
AtlasPage GetAtlasPageWithMaterial (AtlasPage originalPage, Material materialTemplate) {
if (originalPage == null) throw new ArgumentNullException("originalPage");
AtlasPage newPage = null;
var key = new KeyValuePair<AtlasPage, Material>(originalPage, materialTemplate);
cache.TryGetValue(key, out newPage);
if (newPage == null) {
newPage = originalPage.Clone();
var originalMaterial = originalPage.rendererObject as Material;
newPage.rendererObject = new Material(materialTemplate) {
name = originalMaterial.name + " " + materialTemplate.name,
mainTexture = originalMaterial.mainTexture
};
cache.Add(key, newPage);
}
return newPage;
}
public void Dispose () {
cache.Clear();
}
}
}
}
/******************************************************************************
* 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.
*****************************************************************************/
using Spine;
using Spine.Unity;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Spine.Unity {
[CreateAssetMenu(menuName = "Spine/SkeletonData Modifiers/Blend Mode Materials", order = 200)]
public class BlendModeMaterialsAsset : SkeletonDataModifierAsset {
public Material multiplyMaterialTemplate;
public Material screenMaterialTemplate;
public Material additiveMaterialTemplate;
public bool applyAdditiveMaterial = true;
public override void Apply (SkeletonData skeletonData) {
ApplyMaterials(skeletonData, multiplyMaterialTemplate, screenMaterialTemplate, additiveMaterialTemplate, applyAdditiveMaterial);
}
public static void ApplyMaterials (SkeletonData skeletonData, Material multiplyTemplate, Material screenTemplate, Material additiveTemplate, bool includeAdditiveSlots) {
if (skeletonData == null) throw new ArgumentNullException("skeletonData");
using (var materialCache = new AtlasMaterialCache()) {
var entryBuffer = new List<Skin.SkinEntry>();
var slotsItems = skeletonData.Slots.Items;
for (int slotIndex = 0, slotCount = skeletonData.Slots.Count; slotIndex < slotCount; slotIndex++) {
var slot = slotsItems[slotIndex];
if (slot.BlendMode == BlendMode.Normal) continue;
if (!includeAdditiveSlots && slot.BlendMode == BlendMode.Additive) continue;
entryBuffer.Clear();
foreach (var skin in skeletonData.Skins)
skin.GetAttachments(slotIndex, entryBuffer);
Material templateMaterial = null;
switch (slot.BlendMode) {
case BlendMode.Multiply:
templateMaterial = multiplyTemplate;
break;
case BlendMode.Screen:
templateMaterial = screenTemplate;
break;
case BlendMode.Additive:
templateMaterial = additiveTemplate;
break;
}
if (templateMaterial == null) continue;
foreach (var entry in entryBuffer) {
var renderableAttachment = entry.Attachment as IHasRendererObject;
if (renderableAttachment != null) {
renderableAttachment.RendererObject = materialCache.CloneAtlasRegionWithMaterial((AtlasRegion)renderableAttachment.RendererObject, templateMaterial);
}
}
}
}
//attachmentBuffer.Clear();
}
class AtlasMaterialCache : IDisposable {
readonly Dictionary<KeyValuePair<AtlasPage, Material>, AtlasPage> cache = new Dictionary<KeyValuePair<AtlasPage, Material>, AtlasPage>();
/// <summary>Creates a clone of an AtlasRegion that uses different Material settings, while retaining the original texture.</summary>
public AtlasRegion CloneAtlasRegionWithMaterial (AtlasRegion originalRegion, Material materialTemplate) {
var newRegion = originalRegion.Clone();
newRegion.page = GetAtlasPageWithMaterial(originalRegion.page, materialTemplate);
return newRegion;
}
AtlasPage GetAtlasPageWithMaterial (AtlasPage originalPage, Material materialTemplate) {
if (originalPage == null) throw new ArgumentNullException("originalPage");
AtlasPage newPage = null;
var key = new KeyValuePair<AtlasPage, Material>(originalPage, materialTemplate);
cache.TryGetValue(key, out newPage);
if (newPage == null) {
newPage = originalPage.Clone();
var originalMaterial = originalPage.rendererObject as Material;
newPage.rendererObject = new Material(materialTemplate) {
name = originalMaterial.name + " " + materialTemplate.name,
mainTexture = originalMaterial.mainTexture
};
cache.Add(key, newPage);
}
return newPage;
}
public void Dispose () {
cache.Clear();
}
}
}
}

View File

@@ -1,301 +1,301 @@
/******************************************************************************
* 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.
*****************************************************************************/
using System;
using System.Collections;
using UnityEngine;
namespace Spine.Unity {
[AttributeUsage(AttributeTargets.Field, Inherited = true, AllowMultiple = false)]
public abstract class SpineAttributeBase : PropertyAttribute {
public string dataField = "";
public string startsWith = "";
public bool includeNone = true;
public bool fallbackToTextField = false;
}
public class SpineBone : SpineAttributeBase {
/// <summary>
/// Smart popup menu for Spine Bones
/// </summary>
/// <param name="startsWith">Filters popup results to elements that begin with supplied string.</param>
/// <param name="includeNone">If true, the dropdown list will include a "none" option which stored as an empty string.</param>
/// <param name = "fallbackToTextField">If true, and an animation list source can't be found, the field will fall back to a normal text field. If false, it will show an error.</param>
/// <param name="dataField">If specified, a locally scoped field with the name supplied by in dataField will be used to fill the popup results.
/// Valid types are SkeletonDataAsset and SkeletonRenderer (and derivatives)
/// If left empty and the script the attribute is applied to is derived from Component, GetComponent<SkeletonRenderer>() will be called as a fallback.
/// </param>
public SpineBone (string startsWith = "", string dataField = "", bool includeNone = true, bool fallbackToTextField = false) {
this.startsWith = startsWith;
this.dataField = dataField;
this.includeNone = includeNone;
this.fallbackToTextField = fallbackToTextField;
}
public static Spine.Bone GetBone (string boneName, SkeletonRenderer renderer) {
return renderer.skeleton == null ? null : renderer.skeleton.FindBone(boneName);
}
public static Spine.BoneData GetBoneData (string boneName, SkeletonDataAsset skeletonDataAsset) {
var data = skeletonDataAsset.GetSkeletonData(true);
return data.FindBone(boneName);
}
}
public class SpineSlot : SpineAttributeBase {
public bool containsBoundingBoxes = false;
/// <summary>
/// Smart popup menu for Spine Slots
/// </summary>
/// <param name="startsWith">Filters popup results to elements that begin with supplied string.</param>
/// <param name="containsBoundingBoxes">Disables popup results that don't contain bounding box attachments when true.</param>
/// <param name = "includeNone">If true, the dropdown list will include a "none" option which stored as an empty string.</param>
/// <param name = "fallbackToTextField">If true, and an animation list source can't be found, the field will fall back to a normal text field. If false, it will show an error.</param>
/// <param name="dataField">If specified, a locally scoped field with the name supplied by in dataField will be used to fill the popup results.
/// Valid types are SkeletonDataAsset and SkeletonRenderer (and derivatives).
/// If left empty and the script the attribute is applied to is derived from Component, GetComponent<SkeletonRenderer>() will be called as a fallback.
/// </param>
public SpineSlot (string startsWith = "", string dataField = "", bool containsBoundingBoxes = false, bool includeNone = true, bool fallbackToTextField = false) {
this.startsWith = startsWith;
this.dataField = dataField;
this.containsBoundingBoxes = containsBoundingBoxes;
this.includeNone = includeNone;
this.fallbackToTextField = fallbackToTextField;
}
}
public class SpineAnimation : SpineAttributeBase {
/// <summary>
/// Smart popup menu for Spine Animations
/// </summary>
/// <param name="startsWith">Filters popup results to elements that begin with supplied string.</param>
/// <param name = "fallbackToTextField">If true, and an animation list source can't be found, the field will fall back to a normal text field. If false, it will show an error.</param>
/// <param name="includeNone">If true, the dropdown list will include a "none" option which stored as an empty string.</param>
/// <param name="dataField">If specified, a locally scoped field with the name supplied by in dataField will be used to fill the popup results.
/// Valid types are SkeletonDataAsset and SkeletonRenderer (and derivatives)
/// If left empty and the script the attribute is applied to is derived from Component, GetComponent<SkeletonRenderer>() will be called as a fallback.
/// </param>
public SpineAnimation (string startsWith = "", string dataField = "", bool includeNone = true, bool fallbackToTextField = false) {
this.startsWith = startsWith;
this.dataField = dataField;
this.includeNone = includeNone;
this.fallbackToTextField = fallbackToTextField;
}
}
public class SpineEvent : SpineAttributeBase {
/// <summary>
/// Smart popup menu for Spine Events (Spine.EventData)
/// </summary>
/// <param name="startsWith">Filters popup results to elements that begin with supplied string.</param>
/// <param name = "includeNone">If true, the dropdown list will include a "none" option which stored as an empty string.</param>
/// <param name="dataField">If specified, a locally scoped field with the name supplied by in dataField will be used to fill the popup results.
/// Valid types are SkeletonDataAsset and SkeletonRenderer (and derivatives).
/// If left empty and the script the attribute is applied to is derived from Component, GetComponent(SkeletonRenderer)() will be called as a fallback.
/// </param>
/// <param name="fallbackToTextField">If true, and an animation list source can't be found, the field will fall back to a normal text field. If false, it will show an error.</param>
public bool audioOnly = false;
public SpineEvent (string startsWith = "", string dataField = "", bool includeNone = true, bool fallbackToTextField = false, bool audioOnly = false) {
this.startsWith = startsWith;
this.dataField = dataField;
this.includeNone = includeNone;
this.fallbackToTextField = fallbackToTextField;
this.audioOnly = audioOnly;
}
}
public class SpineIkConstraint : SpineAttributeBase {
/// <summary>
/// Smart popup menu for Spine IK Constraints (Spine.IkConstraint)
/// </summary>
/// <param name="startsWith">Filters popup results to elements that begin with supplied string.</param>
/// <param name = "includeNone">If true, the dropdown list will include a "none" option which stored as an empty string.</param>
/// <param name="dataField">If specified, a locally scoped field with the name supplied by in dataField will be used to fill the popup results.
/// Valid types are SkeletonDataAsset and SkeletonRenderer (and derivatives).
/// If left empty and the script the attribute is applied to is derived from Component, GetComponent(SkeletonRenderer)() will be called as a fallback.
/// </param>
/// <param name = "fallbackToTextField">If true, and an animation list source can't be found, the field will fall back to a normal text field. If false, it will show an error.</param>
public SpineIkConstraint (string startsWith = "", string dataField = "", bool includeNone = true, bool fallbackToTextField = false) {
this.startsWith = startsWith;
this.dataField = dataField;
this.includeNone = includeNone;
this.fallbackToTextField = fallbackToTextField;
}
}
public class SpineTransformConstraint : SpineAttributeBase {
/// <summary>
/// Smart popup menu for Spine Transform Constraints (Spine.TransformConstraint)
/// </summary>
/// <param name="startsWith">Filters popup results to elements that begin with supplied string.</param>
/// <param name = "includeNone">If true, the dropdown list will include a "none" option which stored as an empty string.</param>
/// <param name = "fallbackToTextField">If true, and an animation list source can't be found, the field will fall back to a normal text field. If false, it will show an error.</param>
/// <param name="dataField">If specified, a locally scoped field with the name supplied by in dataField will be used to fill the popup results.
/// Valid types are SkeletonDataAsset and SkeletonRenderer (and derivatives).
/// If left empty and the script the attribute is applied to is derived from Component, GetComponent<SkeletonRenderer>() will be called as a fallback.
/// </param>
public SpineTransformConstraint (string startsWith = "", string dataField = "", bool includeNone = true, bool fallbackToTextField = false) {
this.startsWith = startsWith;
this.dataField = dataField;
this.includeNone = includeNone;
this.fallbackToTextField = fallbackToTextField;
}
}
public class SpinePathConstraint : SpineAttributeBase {
/// <summary>
/// Smart popup menu for Spine Events (Spine.PathConstraint)
/// </summary>
/// <param name="startsWith">Filters popup results to elements that begin with supplied string.</param>
/// <param name = "includeNone">If true, the dropdown list will include a "none" option which stored as an empty string.</param>
/// <param name="dataField">If specified, a locally scoped field with the name supplied by in dataField will be used to fill the popup results.
/// Valid types are SkeletonDataAsset and SkeletonRenderer (and derivatives).
/// If left empty and the script the attribute is applied to is derived from Component, GetComponent(SkeletonRenderer)() will be called as a fallback.
/// </param>
public SpinePathConstraint (string startsWith = "", string dataField = "", bool includeNone = true, bool fallbackToTextField = false) {
this.startsWith = startsWith;
this.dataField = dataField;
this.includeNone = includeNone;
this.fallbackToTextField = fallbackToTextField;
}
}
public class SpineSkin : SpineAttributeBase {
/// <summary>
/// Smart popup menu for Spine Skins
/// </summary>
/// <param name="startsWith">Filters popup results to elements that begin with supplied string.</param>
/// <param name = "includeNone">If true, the dropdown list will include a "none" option which stored as an empty string.</param>
/// <param name = "fallbackToTextField">If true, and an animation list source can't be found, the field will fall back to a normal text field. If false, it will show an error.</param>
/// <param name = "defaultAsEmptyString">If true, the default choice will be serialized as an empty string.</param>
/// <param name="dataField">If specified, a locally scoped field with the name supplied by in dataField will be used to fill the popup results.
/// Valid types are SkeletonDataAsset and SkeletonRenderer (and derivatives)
/// If left empty and the script the attribute is applied to is derived from Component, GetComponent<SkeletonRenderer>() will be called as a fallback.
/// </param>
public bool defaultAsEmptyString = false;
public SpineSkin (string startsWith = "", string dataField = "", bool includeNone = true, bool fallbackToTextField = false, bool defaultAsEmptyString = false) {
this.startsWith = startsWith;
this.dataField = dataField;
this.includeNone = includeNone;
this.fallbackToTextField = fallbackToTextField;
this.defaultAsEmptyString = defaultAsEmptyString;
}
}
public class SpineAttachment : SpineAttributeBase {
public bool returnAttachmentPath = false;
public bool currentSkinOnly = false;
public bool placeholdersOnly = false;
public string skinField = "";
public string slotField = "";
/// <summary>
/// Smart popup menu for Spine Attachments
/// </summary>
/// <param name="currentSkinOnly">Filters popup results to only include the current Skin. Only valid when a SkeletonRenderer is the data source.</param>
/// <param name="returnAttachmentPath">Returns a fully qualified path for an Attachment in the format "Skin/Slot/AttachmentName". This path format is only used by the SpineAttachment helper methods like SpineAttachment.GetAttachment and .GetHierarchy. Do not use full path anywhere else in Spine's system.</param>
/// <param name="placeholdersOnly">Filters popup results to exclude attachments that are not children of Skin Placeholders</param>
/// <param name="slotField">If specified, a locally scoped field with the name supplied by in slotField will be used to limit the popup results to children of a named slot</param>
/// <param name="skinField">If specified, a locally scoped field with the name supplied by in skinField will be used to limit the popup results to entries of the named skin</param>
/// <param name="includeNone">If true, the dropdown list will include a "none" option which stored as an empty string.</param>
/// <param name = "fallbackToTextField">If true, and an animation list source can't be found, the field will fall back to a normal text field. If false, it will show an error.</param>
/// <param name="dataField">If specified, a locally scoped field with the name supplied by in dataField will be used to fill the popup results.
/// Valid types are SkeletonDataAsset and SkeletonRenderer (and derivatives)
/// If left empty and the script the attribute is applied to is derived from Component, GetComponent<SkeletonRenderer>() will be called as a fallback.
/// </param>
public SpineAttachment (bool currentSkinOnly = true, bool returnAttachmentPath = false, bool placeholdersOnly = false, string slotField = "", string dataField = "", string skinField = "", bool includeNone = true, bool fallbackToTextField = false) {
this.currentSkinOnly = currentSkinOnly;
this.returnAttachmentPath = returnAttachmentPath;
this.placeholdersOnly = placeholdersOnly;
this.slotField = slotField;
this.dataField = dataField;
this.skinField = skinField;
this.includeNone = includeNone;
this.fallbackToTextField = fallbackToTextField;
}
public static SpineAttachment.Hierarchy GetHierarchy (string fullPath) {
return new SpineAttachment.Hierarchy(fullPath);
}
public static Spine.Attachment GetAttachment (string attachmentPath, Spine.SkeletonData skeletonData) {
var hierarchy = SpineAttachment.GetHierarchy(attachmentPath);
if (string.IsNullOrEmpty(hierarchy.name)) return null;
SlotData slot = skeletonData.FindSlot(hierarchy.slot);
if (slot == null) return null;
return skeletonData.FindSkin(hierarchy.skin).GetAttachment(slot.Index, hierarchy.name);
}
public static Spine.Attachment GetAttachment (string attachmentPath, SkeletonDataAsset skeletonDataAsset) {
return GetAttachment(attachmentPath, skeletonDataAsset.GetSkeletonData(true));
}
/// <summary>
/// A struct that represents 3 strings that help identify and locate an attachment in a skeleton.</summary>
public struct Hierarchy {
public string skin;
public string slot;
public string name;
public Hierarchy (string fullPath) {
string[] chunks = fullPath.Split(new char[] { '/' }, System.StringSplitOptions.RemoveEmptyEntries);
if (chunks.Length == 0) {
skin = "";
slot = "";
name = "";
return;
} else if (chunks.Length < 2) {
throw new System.Exception("Cannot generate Attachment Hierarchy from string! Not enough components! [" + fullPath + "]");
}
skin = chunks[0];
slot = chunks[1];
name = "";
for (int i = 2; i < chunks.Length; i++) {
name += chunks[i];
}
}
}
}
public class SpineAtlasRegion : PropertyAttribute {
public string atlasAssetField;
public SpineAtlasRegion (string atlasAssetField = "") {
this.atlasAssetField = atlasAssetField;
}
}
}
/******************************************************************************
* 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.
*****************************************************************************/
using System;
using System.Collections;
using UnityEngine;
namespace Spine.Unity {
[AttributeUsage(AttributeTargets.Field, Inherited = true, AllowMultiple = false)]
public abstract class SpineAttributeBase : PropertyAttribute {
public string dataField = "";
public string startsWith = "";
public bool includeNone = true;
public bool fallbackToTextField = false;
}
public class SpineBone : SpineAttributeBase {
/// <summary>
/// Smart popup menu for Spine Bones
/// </summary>
/// <param name="startsWith">Filters popup results to elements that begin with supplied string.</param>
/// <param name="includeNone">If true, the dropdown list will include a "none" option which stored as an empty string.</param>
/// <param name = "fallbackToTextField">If true, and an animation list source can't be found, the field will fall back to a normal text field. If false, it will show an error.</param>
/// <param name="dataField">If specified, a locally scoped field with the name supplied by in dataField will be used to fill the popup results.
/// Valid types are SkeletonDataAsset and SkeletonRenderer (and derivatives)
/// If left empty and the script the attribute is applied to is derived from Component, GetComponent<SkeletonRenderer>() will be called as a fallback.
/// </param>
public SpineBone (string startsWith = "", string dataField = "", bool includeNone = true, bool fallbackToTextField = false) {
this.startsWith = startsWith;
this.dataField = dataField;
this.includeNone = includeNone;
this.fallbackToTextField = fallbackToTextField;
}
public static Spine.Bone GetBone (string boneName, SkeletonRenderer renderer) {
return renderer.skeleton == null ? null : renderer.skeleton.FindBone(boneName);
}
public static Spine.BoneData GetBoneData (string boneName, SkeletonDataAsset skeletonDataAsset) {
var data = skeletonDataAsset.GetSkeletonData(true);
return data.FindBone(boneName);
}
}
public class SpineSlot : SpineAttributeBase {
public bool containsBoundingBoxes = false;
/// <summary>
/// Smart popup menu for Spine Slots
/// </summary>
/// <param name="startsWith">Filters popup results to elements that begin with supplied string.</param>
/// <param name="containsBoundingBoxes">Disables popup results that don't contain bounding box attachments when true.</param>
/// <param name = "includeNone">If true, the dropdown list will include a "none" option which stored as an empty string.</param>
/// <param name = "fallbackToTextField">If true, and an animation list source can't be found, the field will fall back to a normal text field. If false, it will show an error.</param>
/// <param name="dataField">If specified, a locally scoped field with the name supplied by in dataField will be used to fill the popup results.
/// Valid types are SkeletonDataAsset and SkeletonRenderer (and derivatives).
/// If left empty and the script the attribute is applied to is derived from Component, GetComponent<SkeletonRenderer>() will be called as a fallback.
/// </param>
public SpineSlot (string startsWith = "", string dataField = "", bool containsBoundingBoxes = false, bool includeNone = true, bool fallbackToTextField = false) {
this.startsWith = startsWith;
this.dataField = dataField;
this.containsBoundingBoxes = containsBoundingBoxes;
this.includeNone = includeNone;
this.fallbackToTextField = fallbackToTextField;
}
}
public class SpineAnimation : SpineAttributeBase {
/// <summary>
/// Smart popup menu for Spine Animations
/// </summary>
/// <param name="startsWith">Filters popup results to elements that begin with supplied string.</param>
/// <param name = "fallbackToTextField">If true, and an animation list source can't be found, the field will fall back to a normal text field. If false, it will show an error.</param>
/// <param name="includeNone">If true, the dropdown list will include a "none" option which stored as an empty string.</param>
/// <param name="dataField">If specified, a locally scoped field with the name supplied by in dataField will be used to fill the popup results.
/// Valid types are SkeletonDataAsset and SkeletonRenderer (and derivatives)
/// If left empty and the script the attribute is applied to is derived from Component, GetComponent<SkeletonRenderer>() will be called as a fallback.
/// </param>
public SpineAnimation (string startsWith = "", string dataField = "", bool includeNone = true, bool fallbackToTextField = false) {
this.startsWith = startsWith;
this.dataField = dataField;
this.includeNone = includeNone;
this.fallbackToTextField = fallbackToTextField;
}
}
public class SpineEvent : SpineAttributeBase {
/// <summary>
/// Smart popup menu for Spine Events (Spine.EventData)
/// </summary>
/// <param name="startsWith">Filters popup results to elements that begin with supplied string.</param>
/// <param name = "includeNone">If true, the dropdown list will include a "none" option which stored as an empty string.</param>
/// <param name="dataField">If specified, a locally scoped field with the name supplied by in dataField will be used to fill the popup results.
/// Valid types are SkeletonDataAsset and SkeletonRenderer (and derivatives).
/// If left empty and the script the attribute is applied to is derived from Component, GetComponent(SkeletonRenderer)() will be called as a fallback.
/// </param>
/// <param name="fallbackToTextField">If true, and an animation list source can't be found, the field will fall back to a normal text field. If false, it will show an error.</param>
public bool audioOnly = false;
public SpineEvent (string startsWith = "", string dataField = "", bool includeNone = true, bool fallbackToTextField = false, bool audioOnly = false) {
this.startsWith = startsWith;
this.dataField = dataField;
this.includeNone = includeNone;
this.fallbackToTextField = fallbackToTextField;
this.audioOnly = audioOnly;
}
}
public class SpineIkConstraint : SpineAttributeBase {
/// <summary>
/// Smart popup menu for Spine IK Constraints (Spine.IkConstraint)
/// </summary>
/// <param name="startsWith">Filters popup results to elements that begin with supplied string.</param>
/// <param name = "includeNone">If true, the dropdown list will include a "none" option which stored as an empty string.</param>
/// <param name="dataField">If specified, a locally scoped field with the name supplied by in dataField will be used to fill the popup results.
/// Valid types are SkeletonDataAsset and SkeletonRenderer (and derivatives).
/// If left empty and the script the attribute is applied to is derived from Component, GetComponent(SkeletonRenderer)() will be called as a fallback.
/// </param>
/// <param name = "fallbackToTextField">If true, and an animation list source can't be found, the field will fall back to a normal text field. If false, it will show an error.</param>
public SpineIkConstraint (string startsWith = "", string dataField = "", bool includeNone = true, bool fallbackToTextField = false) {
this.startsWith = startsWith;
this.dataField = dataField;
this.includeNone = includeNone;
this.fallbackToTextField = fallbackToTextField;
}
}
public class SpineTransformConstraint : SpineAttributeBase {
/// <summary>
/// Smart popup menu for Spine Transform Constraints (Spine.TransformConstraint)
/// </summary>
/// <param name="startsWith">Filters popup results to elements that begin with supplied string.</param>
/// <param name = "includeNone">If true, the dropdown list will include a "none" option which stored as an empty string.</param>
/// <param name = "fallbackToTextField">If true, and an animation list source can't be found, the field will fall back to a normal text field. If false, it will show an error.</param>
/// <param name="dataField">If specified, a locally scoped field with the name supplied by in dataField will be used to fill the popup results.
/// Valid types are SkeletonDataAsset and SkeletonRenderer (and derivatives).
/// If left empty and the script the attribute is applied to is derived from Component, GetComponent<SkeletonRenderer>() will be called as a fallback.
/// </param>
public SpineTransformConstraint (string startsWith = "", string dataField = "", bool includeNone = true, bool fallbackToTextField = false) {
this.startsWith = startsWith;
this.dataField = dataField;
this.includeNone = includeNone;
this.fallbackToTextField = fallbackToTextField;
}
}
public class SpinePathConstraint : SpineAttributeBase {
/// <summary>
/// Smart popup menu for Spine Events (Spine.PathConstraint)
/// </summary>
/// <param name="startsWith">Filters popup results to elements that begin with supplied string.</param>
/// <param name = "includeNone">If true, the dropdown list will include a "none" option which stored as an empty string.</param>
/// <param name="dataField">If specified, a locally scoped field with the name supplied by in dataField will be used to fill the popup results.
/// Valid types are SkeletonDataAsset and SkeletonRenderer (and derivatives).
/// If left empty and the script the attribute is applied to is derived from Component, GetComponent(SkeletonRenderer)() will be called as a fallback.
/// </param>
public SpinePathConstraint (string startsWith = "", string dataField = "", bool includeNone = true, bool fallbackToTextField = false) {
this.startsWith = startsWith;
this.dataField = dataField;
this.includeNone = includeNone;
this.fallbackToTextField = fallbackToTextField;
}
}
public class SpineSkin : SpineAttributeBase {
/// <summary>
/// Smart popup menu for Spine Skins
/// </summary>
/// <param name="startsWith">Filters popup results to elements that begin with supplied string.</param>
/// <param name = "includeNone">If true, the dropdown list will include a "none" option which stored as an empty string.</param>
/// <param name = "fallbackToTextField">If true, and an animation list source can't be found, the field will fall back to a normal text field. If false, it will show an error.</param>
/// <param name = "defaultAsEmptyString">If true, the default choice will be serialized as an empty string.</param>
/// <param name="dataField">If specified, a locally scoped field with the name supplied by in dataField will be used to fill the popup results.
/// Valid types are SkeletonDataAsset and SkeletonRenderer (and derivatives)
/// If left empty and the script the attribute is applied to is derived from Component, GetComponent<SkeletonRenderer>() will be called as a fallback.
/// </param>
public bool defaultAsEmptyString = false;
public SpineSkin (string startsWith = "", string dataField = "", bool includeNone = true, bool fallbackToTextField = false, bool defaultAsEmptyString = false) {
this.startsWith = startsWith;
this.dataField = dataField;
this.includeNone = includeNone;
this.fallbackToTextField = fallbackToTextField;
this.defaultAsEmptyString = defaultAsEmptyString;
}
}
public class SpineAttachment : SpineAttributeBase {
public bool returnAttachmentPath = false;
public bool currentSkinOnly = false;
public bool placeholdersOnly = false;
public string skinField = "";
public string slotField = "";
/// <summary>
/// Smart popup menu for Spine Attachments
/// </summary>
/// <param name="currentSkinOnly">Filters popup results to only include the current Skin. Only valid when a SkeletonRenderer is the data source.</param>
/// <param name="returnAttachmentPath">Returns a fully qualified path for an Attachment in the format "Skin/Slot/AttachmentName". This path format is only used by the SpineAttachment helper methods like SpineAttachment.GetAttachment and .GetHierarchy. Do not use full path anywhere else in Spine's system.</param>
/// <param name="placeholdersOnly">Filters popup results to exclude attachments that are not children of Skin Placeholders</param>
/// <param name="slotField">If specified, a locally scoped field with the name supplied by in slotField will be used to limit the popup results to children of a named slot</param>
/// <param name="skinField">If specified, a locally scoped field with the name supplied by in skinField will be used to limit the popup results to entries of the named skin</param>
/// <param name="includeNone">If true, the dropdown list will include a "none" option which stored as an empty string.</param>
/// <param name = "fallbackToTextField">If true, and an animation list source can't be found, the field will fall back to a normal text field. If false, it will show an error.</param>
/// <param name="dataField">If specified, a locally scoped field with the name supplied by in dataField will be used to fill the popup results.
/// Valid types are SkeletonDataAsset and SkeletonRenderer (and derivatives)
/// If left empty and the script the attribute is applied to is derived from Component, GetComponent<SkeletonRenderer>() will be called as a fallback.
/// </param>
public SpineAttachment (bool currentSkinOnly = true, bool returnAttachmentPath = false, bool placeholdersOnly = false, string slotField = "", string dataField = "", string skinField = "", bool includeNone = true, bool fallbackToTextField = false) {
this.currentSkinOnly = currentSkinOnly;
this.returnAttachmentPath = returnAttachmentPath;
this.placeholdersOnly = placeholdersOnly;
this.slotField = slotField;
this.dataField = dataField;
this.skinField = skinField;
this.includeNone = includeNone;
this.fallbackToTextField = fallbackToTextField;
}
public static SpineAttachment.Hierarchy GetHierarchy (string fullPath) {
return new SpineAttachment.Hierarchy(fullPath);
}
public static Spine.Attachment GetAttachment (string attachmentPath, Spine.SkeletonData skeletonData) {
var hierarchy = SpineAttachment.GetHierarchy(attachmentPath);
if (string.IsNullOrEmpty(hierarchy.name)) return null;
SlotData slot = skeletonData.FindSlot(hierarchy.slot);
if (slot == null) return null;
return skeletonData.FindSkin(hierarchy.skin).GetAttachment(slot.Index, hierarchy.name);
}
public static Spine.Attachment GetAttachment (string attachmentPath, SkeletonDataAsset skeletonDataAsset) {
return GetAttachment(attachmentPath, skeletonDataAsset.GetSkeletonData(true));
}
/// <summary>
/// A struct that represents 3 strings that help identify and locate an attachment in a skeleton.</summary>
public struct Hierarchy {
public string skin;
public string slot;
public string name;
public Hierarchy (string fullPath) {
string[] chunks = fullPath.Split(new char[] { '/' }, System.StringSplitOptions.RemoveEmptyEntries);
if (chunks.Length == 0) {
skin = "";
slot = "";
name = "";
return;
} else if (chunks.Length < 2) {
throw new System.Exception("Cannot generate Attachment Hierarchy from string! Not enough components! [" + fullPath + "]");
}
skin = chunks[0];
slot = chunks[1];
name = "";
for (int i = 2; i < chunks.Length; i++) {
name += chunks[i];
}
}
}
}
public class SpineAtlasRegion : PropertyAttribute {
public string atlasAssetField;
public SpineAtlasRegion (string atlasAssetField = "") {
this.atlasAssetField = atlasAssetField;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,108 +1,108 @@
/******************************************************************************
* 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.
*****************************************************************************/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Spine.Unity.AttachmentTools {
public static class AttachmentCloneExtensions {
#region RemappedClone Convenience Methods
/// <summary>
/// Gets a clone of the attachment remapped with a sprite image.</summary>
/// <returns>The remapped clone.</returns>
/// <param name="o">The original attachment.</param>
/// <param name="sprite">The sprite whose texture to use.</param>
/// <param name="sourceMaterial">The source material used to copy the shader and material properties from.</param>
/// <param name="premultiplyAlpha">If <c>true</c>, a premultiply alpha clone of the original texture will be created.
/// See remarks below for additional info.</param>
/// <param name="cloneMeshAsLinked">If <c>true</c> MeshAttachments will be cloned as linked meshes and will inherit animation from the original attachment.</param>
/// <param name="useOriginalRegionSize">If <c>true</c> the size of the original attachment will be followed, instead of using the Sprite size.</param>
/// <param name="pivotShiftsMeshUVCoords">If <c>true</c> and the original Attachment is a MeshAttachment, then
/// a non-central sprite pivot will shift uv coords in the opposite direction. Vertices will not be offset in
/// any case when the original Attachment is a MeshAttachment.</param>
/// <param name="useOriginalRegionScale">If <c>true</c> and the original Attachment is a RegionAttachment, then
/// the original region's scale value is used instead of the Sprite's pixels per unit property. Since uniform scale is used,
/// x scale of the original attachment (width scale) is used, scale in y direction (height scale) is ignored.</param>
/// <remarks>When parameter <c>premultiplyAlpha</c> is set to <c>true</c>, a premultiply alpha clone of the
/// original texture will be created. Additionally, this PMA Texture clone is cached for later re-use,
/// which might steadily increase the Texture memory footprint when used excessively.
/// See <see cref="AtlasUtilities.ClearCache()"/> on how to clear these cached textures.</remarks>
public static Attachment GetRemappedClone (this Attachment o, Sprite sprite, Material sourceMaterial,
bool premultiplyAlpha = true, bool cloneMeshAsLinked = true, bool useOriginalRegionSize = false,
bool pivotShiftsMeshUVCoords = true, bool useOriginalRegionScale = false) {
var atlasRegion = premultiplyAlpha ? sprite.ToAtlasRegionPMAClone(sourceMaterial) : sprite.ToAtlasRegion(new Material(sourceMaterial) { mainTexture = sprite.texture });
if (!pivotShiftsMeshUVCoords && o is MeshAttachment) {
// prevent non-central sprite pivot setting offsetX/Y and shifting uv coords out of mesh bounds
atlasRegion.offsetX = 0;
atlasRegion.offsetY = 0;
}
float scale = 1f / sprite.pixelsPerUnit;
if (useOriginalRegionScale) {
var regionAttachment = o as RegionAttachment;
if (regionAttachment != null)
scale = regionAttachment.Width / regionAttachment.RegionOriginalWidth;
}
return o.GetRemappedClone(atlasRegion, cloneMeshAsLinked, useOriginalRegionSize, scale);
}
/// <summary>
/// Gets a clone of the attachment remapped with an atlasRegion image.</summary>
/// <returns>The remapped clone.</returns>
/// <param name="o">The original attachment.</param>
/// <param name="atlasRegion">Atlas region.</param>
/// <param name="cloneMeshAsLinked">If <c>true</c> MeshAttachments will be cloned as linked meshes and will inherit animation from the original attachment.</param>
/// <param name="useOriginalRegionSize">If <c>true</c> the size of the original attachment will be followed, instead of using the Sprite size.</param>
/// <param name="scale">Unity units per pixel scale used to scale the atlas region size when not using the original region size.</param>
public static Attachment GetRemappedClone (this Attachment o, AtlasRegion atlasRegion, bool cloneMeshAsLinked = true, bool useOriginalRegionSize = false, float scale = 0.01f) {
var regionAttachment = o as RegionAttachment;
if (regionAttachment != null) {
RegionAttachment newAttachment = (RegionAttachment)regionAttachment.Copy();
newAttachment.SetRegion(atlasRegion, false);
if (!useOriginalRegionSize) {
newAttachment.Width = atlasRegion.width * scale;
newAttachment.Height = atlasRegion.height * scale;
}
newAttachment.UpdateOffset();
return newAttachment;
} else {
var meshAttachment = o as MeshAttachment;
if (meshAttachment != null) {
MeshAttachment newAttachment = cloneMeshAsLinked ? meshAttachment.NewLinkedMesh() : (MeshAttachment)meshAttachment.Copy();
newAttachment.SetRegion(atlasRegion);
return newAttachment;
}
}
return o.Copy();
}
#endregion
}
}
/******************************************************************************
* 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.
*****************************************************************************/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Spine.Unity.AttachmentTools {
public static class AttachmentCloneExtensions {
#region RemappedClone Convenience Methods
/// <summary>
/// Gets a clone of the attachment remapped with a sprite image.</summary>
/// <returns>The remapped clone.</returns>
/// <param name="o">The original attachment.</param>
/// <param name="sprite">The sprite whose texture to use.</param>
/// <param name="sourceMaterial">The source material used to copy the shader and material properties from.</param>
/// <param name="premultiplyAlpha">If <c>true</c>, a premultiply alpha clone of the original texture will be created.
/// See remarks below for additional info.</param>
/// <param name="cloneMeshAsLinked">If <c>true</c> MeshAttachments will be cloned as linked meshes and will inherit animation from the original attachment.</param>
/// <param name="useOriginalRegionSize">If <c>true</c> the size of the original attachment will be followed, instead of using the Sprite size.</param>
/// <param name="pivotShiftsMeshUVCoords">If <c>true</c> and the original Attachment is a MeshAttachment, then
/// a non-central sprite pivot will shift uv coords in the opposite direction. Vertices will not be offset in
/// any case when the original Attachment is a MeshAttachment.</param>
/// <param name="useOriginalRegionScale">If <c>true</c> and the original Attachment is a RegionAttachment, then
/// the original region's scale value is used instead of the Sprite's pixels per unit property. Since uniform scale is used,
/// x scale of the original attachment (width scale) is used, scale in y direction (height scale) is ignored.</param>
/// <remarks>When parameter <c>premultiplyAlpha</c> is set to <c>true</c>, a premultiply alpha clone of the
/// original texture will be created. Additionally, this PMA Texture clone is cached for later re-use,
/// which might steadily increase the Texture memory footprint when used excessively.
/// See <see cref="AtlasUtilities.ClearCache()"/> on how to clear these cached textures.</remarks>
public static Attachment GetRemappedClone (this Attachment o, Sprite sprite, Material sourceMaterial,
bool premultiplyAlpha = true, bool cloneMeshAsLinked = true, bool useOriginalRegionSize = false,
bool pivotShiftsMeshUVCoords = true, bool useOriginalRegionScale = false) {
var atlasRegion = premultiplyAlpha ? sprite.ToAtlasRegionPMAClone(sourceMaterial) : sprite.ToAtlasRegion(new Material(sourceMaterial) { mainTexture = sprite.texture });
if (!pivotShiftsMeshUVCoords && o is MeshAttachment) {
// prevent non-central sprite pivot setting offsetX/Y and shifting uv coords out of mesh bounds
atlasRegion.offsetX = 0;
atlasRegion.offsetY = 0;
}
float scale = 1f / sprite.pixelsPerUnit;
if (useOriginalRegionScale) {
var regionAttachment = o as RegionAttachment;
if (regionAttachment != null)
scale = regionAttachment.Width / regionAttachment.RegionOriginalWidth;
}
return o.GetRemappedClone(atlasRegion, cloneMeshAsLinked, useOriginalRegionSize, scale);
}
/// <summary>
/// Gets a clone of the attachment remapped with an atlasRegion image.</summary>
/// <returns>The remapped clone.</returns>
/// <param name="o">The original attachment.</param>
/// <param name="atlasRegion">Atlas region.</param>
/// <param name="cloneMeshAsLinked">If <c>true</c> MeshAttachments will be cloned as linked meshes and will inherit animation from the original attachment.</param>
/// <param name="useOriginalRegionSize">If <c>true</c> the size of the original attachment will be followed, instead of using the Sprite size.</param>
/// <param name="scale">Unity units per pixel scale used to scale the atlas region size when not using the original region size.</param>
public static Attachment GetRemappedClone (this Attachment o, AtlasRegion atlasRegion, bool cloneMeshAsLinked = true, bool useOriginalRegionSize = false, float scale = 0.01f) {
var regionAttachment = o as RegionAttachment;
if (regionAttachment != null) {
RegionAttachment newAttachment = (RegionAttachment)regionAttachment.Copy();
newAttachment.SetRegion(atlasRegion, false);
if (!useOriginalRegionSize) {
newAttachment.Width = atlasRegion.width * scale;
newAttachment.Height = atlasRegion.height * scale;
}
newAttachment.UpdateOffset();
return newAttachment;
} else {
var meshAttachment = o as MeshAttachment;
if (meshAttachment != null) {
MeshAttachment newAttachment = cloneMeshAsLinked ? meshAttachment.NewLinkedMesh() : (MeshAttachment)meshAttachment.Copy();
newAttachment.SetRegion(atlasRegion);
return newAttachment;
}
}
return o.Copy();
}
#endregion
}
}

View File

@@ -1,189 +1,189 @@
/******************************************************************************
* 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.
*****************************************************************************/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Spine.Unity.AttachmentTools {
public static class AttachmentRegionExtensions {
#region SetRegion
/// <summary>
/// Tries to set the region (image) of a renderable attachment. If the attachment is not renderable, nothing is applied.</summary>
public static void SetRegion (this Attachment attachment, AtlasRegion region, bool updateOffset = true) {
var regionAttachment = attachment as RegionAttachment;
if (regionAttachment != null)
regionAttachment.SetRegion(region, updateOffset);
var meshAttachment = attachment as MeshAttachment;
if (meshAttachment != null)
meshAttachment.SetRegion(region, updateOffset);
}
/// <summary>Sets the region (image) of a RegionAttachment</summary>
public static void SetRegion (this RegionAttachment attachment, AtlasRegion region, bool updateOffset = true) {
if (region == null) throw new System.ArgumentNullException("region");
// (AtlasAttachmentLoader.cs)
attachment.RendererObject = region;
attachment.SetUVs(region.u, region.v, region.u2, region.v2, region.degrees);
attachment.RegionOffsetX = region.offsetX;
attachment.RegionOffsetY = region.offsetY;
attachment.RegionWidth = region.width;
attachment.RegionHeight = region.height;
attachment.RegionOriginalWidth = region.originalWidth;
attachment.RegionOriginalHeight = region.originalHeight;
if (updateOffset) attachment.UpdateOffset();
}
/// <summary>Sets the region (image) of a MeshAttachment</summary>
public static void SetRegion (this MeshAttachment attachment, AtlasRegion region, bool updateUVs = true) {
if (region == null) throw new System.ArgumentNullException("region");
// (AtlasAttachmentLoader.cs)
attachment.RendererObject = region;
attachment.RegionU = region.u;
attachment.RegionV = region.v;
attachment.RegionU2 = region.u2;
attachment.RegionV2 = region.v2;
attachment.RegionDegrees = region.degrees;
attachment.RegionOffsetX = region.offsetX;
attachment.RegionOffsetY = region.offsetY;
attachment.RegionWidth = region.width;
attachment.RegionHeight = region.height;
attachment.RegionOriginalWidth = region.originalWidth;
attachment.RegionOriginalHeight = region.originalHeight;
if (updateUVs) attachment.UpdateUVs();
}
#endregion
#region Runtime RegionAttachments
/// <summary>
/// Creates a RegionAttachment based on a sprite. This method creates a real, usable AtlasRegion. That AtlasRegion uses a new AtlasPage with the Material provided./// </summary>
public static RegionAttachment ToRegionAttachment (this Sprite sprite, Material material, float rotation = 0f) {
return sprite.ToRegionAttachment(material.ToSpineAtlasPage(), rotation);
}
/// <summary>
/// Creates a RegionAttachment based on a sprite. This method creates a real, usable AtlasRegion. That AtlasRegion uses the AtlasPage provided./// </summary>
public static RegionAttachment ToRegionAttachment (this Sprite sprite, AtlasPage page, float rotation = 0f) {
if (sprite == null) throw new System.ArgumentNullException("sprite");
if (page == null) throw new System.ArgumentNullException("page");
var region = sprite.ToAtlasRegion(page);
var unitsPerPixel = 1f / sprite.pixelsPerUnit;
return region.ToRegionAttachment(sprite.name, unitsPerPixel, rotation);
}
/// <summary>
/// Creates a Spine.AtlasRegion that uses a premultiplied alpha duplicate texture of the Sprite's texture data.
/// Returns a RegionAttachment that uses it. Use this if you plan to use a premultiply alpha shader such as "Spine/Skeleton".</summary>
/// <remarks>The duplicate texture is cached for later re-use. See documentation of
/// <see cref="AttachmentCloneExtensions.GetRemappedClone"/> for additional details.</remarks>
public static RegionAttachment ToRegionAttachmentPMAClone (this Sprite sprite, Shader shader, TextureFormat textureFormat = AtlasUtilities.SpineTextureFormat, bool mipmaps = AtlasUtilities.UseMipMaps, Material materialPropertySource = null, float rotation = 0f) {
if (sprite == null) throw new System.ArgumentNullException("sprite");
if (shader == null) throw new System.ArgumentNullException("shader");
var region = sprite.ToAtlasRegionPMAClone(shader, textureFormat, mipmaps, materialPropertySource);
var unitsPerPixel = 1f / sprite.pixelsPerUnit;
return region.ToRegionAttachment(sprite.name, unitsPerPixel, rotation);
}
public static RegionAttachment ToRegionAttachmentPMAClone (this Sprite sprite, Material materialPropertySource, TextureFormat textureFormat = AtlasUtilities.SpineTextureFormat, bool mipmaps = AtlasUtilities.UseMipMaps, float rotation = 0f) {
return sprite.ToRegionAttachmentPMAClone(materialPropertySource.shader, textureFormat, mipmaps, materialPropertySource, rotation);
}
/// <summary>
/// Creates a new RegionAttachment from a given AtlasRegion.</summary>
public static RegionAttachment ToRegionAttachment (this AtlasRegion region, string attachmentName, float scale = 0.01f, float rotation = 0f) {
if (string.IsNullOrEmpty(attachmentName)) throw new System.ArgumentException("attachmentName can't be null or empty.", "attachmentName");
if (region == null) throw new System.ArgumentNullException("region");
// (AtlasAttachmentLoader.cs)
var attachment = new RegionAttachment(attachmentName);
attachment.RendererObject = region;
attachment.SetUVs(region.u, region.v, region.u2, region.v2, region.degrees);
attachment.RegionOffsetX = region.offsetX;
attachment.RegionOffsetY = region.offsetY;
attachment.RegionWidth = region.width;
attachment.RegionHeight = region.height;
attachment.RegionOriginalWidth = region.originalWidth;
attachment.RegionOriginalHeight = region.originalHeight;
attachment.Path = region.name;
attachment.ScaleX = 1;
attachment.ScaleY = 1;
attachment.Rotation = rotation;
attachment.R = 1;
attachment.G = 1;
attachment.B = 1;
attachment.A = 1;
// pass OriginalWidth and OriginalHeight because UpdateOffset uses it in its calculation.
attachment.Width = attachment.RegionOriginalWidth * scale;
attachment.Height = attachment.RegionOriginalHeight * scale;
attachment.SetColor(Color.white);
attachment.UpdateOffset();
return attachment;
}
/// <summary> Sets the scale. Call regionAttachment.UpdateOffset to apply the change.</summary>
public static void SetScale (this RegionAttachment regionAttachment, Vector2 scale) {
regionAttachment.ScaleX = scale.x;
regionAttachment.ScaleY = scale.y;
}
/// <summary> Sets the scale. Call regionAttachment.UpdateOffset to apply the change.</summary>
public static void SetScale (this RegionAttachment regionAttachment, float x, float y) {
regionAttachment.ScaleX = x;
regionAttachment.ScaleY = y;
}
/// <summary> Sets the position offset. Call regionAttachment.UpdateOffset to apply the change.</summary>
public static void SetPositionOffset (this RegionAttachment regionAttachment, Vector2 offset) {
regionAttachment.X = offset.x;
regionAttachment.Y = offset.y;
}
/// <summary> Sets the position offset. Call regionAttachment.UpdateOffset to apply the change.</summary>
public static void SetPositionOffset (this RegionAttachment regionAttachment, float x, float y) {
regionAttachment.X = x;
regionAttachment.Y = y;
}
/// <summary> Sets the rotation. Call regionAttachment.UpdateOffset to apply the change.</summary>
public static void SetRotation (this RegionAttachment regionAttachment, float rotation) {
regionAttachment.Rotation = rotation;
}
#endregion
}
}
/******************************************************************************
* 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.
*****************************************************************************/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Spine.Unity.AttachmentTools {
public static class AttachmentRegionExtensions {
#region SetRegion
/// <summary>
/// Tries to set the region (image) of a renderable attachment. If the attachment is not renderable, nothing is applied.</summary>
public static void SetRegion (this Attachment attachment, AtlasRegion region, bool updateOffset = true) {
var regionAttachment = attachment as RegionAttachment;
if (regionAttachment != null)
regionAttachment.SetRegion(region, updateOffset);
var meshAttachment = attachment as MeshAttachment;
if (meshAttachment != null)
meshAttachment.SetRegion(region, updateOffset);
}
/// <summary>Sets the region (image) of a RegionAttachment</summary>
public static void SetRegion (this RegionAttachment attachment, AtlasRegion region, bool updateOffset = true) {
if (region == null) throw new System.ArgumentNullException("region");
// (AtlasAttachmentLoader.cs)
attachment.RendererObject = region;
attachment.SetUVs(region.u, region.v, region.u2, region.v2, region.degrees);
attachment.RegionOffsetX = region.offsetX;
attachment.RegionOffsetY = region.offsetY;
attachment.RegionWidth = region.width;
attachment.RegionHeight = region.height;
attachment.RegionOriginalWidth = region.originalWidth;
attachment.RegionOriginalHeight = region.originalHeight;
if (updateOffset) attachment.UpdateOffset();
}
/// <summary>Sets the region (image) of a MeshAttachment</summary>
public static void SetRegion (this MeshAttachment attachment, AtlasRegion region, bool updateUVs = true) {
if (region == null) throw new System.ArgumentNullException("region");
// (AtlasAttachmentLoader.cs)
attachment.RendererObject = region;
attachment.RegionU = region.u;
attachment.RegionV = region.v;
attachment.RegionU2 = region.u2;
attachment.RegionV2 = region.v2;
attachment.RegionDegrees = region.degrees;
attachment.RegionOffsetX = region.offsetX;
attachment.RegionOffsetY = region.offsetY;
attachment.RegionWidth = region.width;
attachment.RegionHeight = region.height;
attachment.RegionOriginalWidth = region.originalWidth;
attachment.RegionOriginalHeight = region.originalHeight;
if (updateUVs) attachment.UpdateUVs();
}
#endregion
#region Runtime RegionAttachments
/// <summary>
/// Creates a RegionAttachment based on a sprite. This method creates a real, usable AtlasRegion. That AtlasRegion uses a new AtlasPage with the Material provided./// </summary>
public static RegionAttachment ToRegionAttachment (this Sprite sprite, Material material, float rotation = 0f) {
return sprite.ToRegionAttachment(material.ToSpineAtlasPage(), rotation);
}
/// <summary>
/// Creates a RegionAttachment based on a sprite. This method creates a real, usable AtlasRegion. That AtlasRegion uses the AtlasPage provided./// </summary>
public static RegionAttachment ToRegionAttachment (this Sprite sprite, AtlasPage page, float rotation = 0f) {
if (sprite == null) throw new System.ArgumentNullException("sprite");
if (page == null) throw new System.ArgumentNullException("page");
var region = sprite.ToAtlasRegion(page);
var unitsPerPixel = 1f / sprite.pixelsPerUnit;
return region.ToRegionAttachment(sprite.name, unitsPerPixel, rotation);
}
/// <summary>
/// Creates a Spine.AtlasRegion that uses a premultiplied alpha duplicate texture of the Sprite's texture data.
/// Returns a RegionAttachment that uses it. Use this if you plan to use a premultiply alpha shader such as "Spine/Skeleton".</summary>
/// <remarks>The duplicate texture is cached for later re-use. See documentation of
/// <see cref="AttachmentCloneExtensions.GetRemappedClone"/> for additional details.</remarks>
public static RegionAttachment ToRegionAttachmentPMAClone (this Sprite sprite, Shader shader, TextureFormat textureFormat = AtlasUtilities.SpineTextureFormat, bool mipmaps = AtlasUtilities.UseMipMaps, Material materialPropertySource = null, float rotation = 0f) {
if (sprite == null) throw new System.ArgumentNullException("sprite");
if (shader == null) throw new System.ArgumentNullException("shader");
var region = sprite.ToAtlasRegionPMAClone(shader, textureFormat, mipmaps, materialPropertySource);
var unitsPerPixel = 1f / sprite.pixelsPerUnit;
return region.ToRegionAttachment(sprite.name, unitsPerPixel, rotation);
}
public static RegionAttachment ToRegionAttachmentPMAClone (this Sprite sprite, Material materialPropertySource, TextureFormat textureFormat = AtlasUtilities.SpineTextureFormat, bool mipmaps = AtlasUtilities.UseMipMaps, float rotation = 0f) {
return sprite.ToRegionAttachmentPMAClone(materialPropertySource.shader, textureFormat, mipmaps, materialPropertySource, rotation);
}
/// <summary>
/// Creates a new RegionAttachment from a given AtlasRegion.</summary>
public static RegionAttachment ToRegionAttachment (this AtlasRegion region, string attachmentName, float scale = 0.01f, float rotation = 0f) {
if (string.IsNullOrEmpty(attachmentName)) throw new System.ArgumentException("attachmentName can't be null or empty.", "attachmentName");
if (region == null) throw new System.ArgumentNullException("region");
// (AtlasAttachmentLoader.cs)
var attachment = new RegionAttachment(attachmentName);
attachment.RendererObject = region;
attachment.SetUVs(region.u, region.v, region.u2, region.v2, region.degrees);
attachment.RegionOffsetX = region.offsetX;
attachment.RegionOffsetY = region.offsetY;
attachment.RegionWidth = region.width;
attachment.RegionHeight = region.height;
attachment.RegionOriginalWidth = region.originalWidth;
attachment.RegionOriginalHeight = region.originalHeight;
attachment.Path = region.name;
attachment.ScaleX = 1;
attachment.ScaleY = 1;
attachment.Rotation = rotation;
attachment.R = 1;
attachment.G = 1;
attachment.B = 1;
attachment.A = 1;
// pass OriginalWidth and OriginalHeight because UpdateOffset uses it in its calculation.
attachment.Width = attachment.RegionOriginalWidth * scale;
attachment.Height = attachment.RegionOriginalHeight * scale;
attachment.SetColor(Color.white);
attachment.UpdateOffset();
return attachment;
}
/// <summary> Sets the scale. Call regionAttachment.UpdateOffset to apply the change.</summary>
public static void SetScale (this RegionAttachment regionAttachment, Vector2 scale) {
regionAttachment.ScaleX = scale.x;
regionAttachment.ScaleY = scale.y;
}
/// <summary> Sets the scale. Call regionAttachment.UpdateOffset to apply the change.</summary>
public static void SetScale (this RegionAttachment regionAttachment, float x, float y) {
regionAttachment.ScaleX = x;
regionAttachment.ScaleY = y;
}
/// <summary> Sets the position offset. Call regionAttachment.UpdateOffset to apply the change.</summary>
public static void SetPositionOffset (this RegionAttachment regionAttachment, Vector2 offset) {
regionAttachment.X = offset.x;
regionAttachment.Y = offset.y;
}
/// <summary> Sets the position offset. Call regionAttachment.UpdateOffset to apply the change.</summary>
public static void SetPositionOffset (this RegionAttachment regionAttachment, float x, float y) {
regionAttachment.X = x;
regionAttachment.Y = y;
}
/// <summary> Sets the rotation. Call regionAttachment.UpdateOffset to apply the change.</summary>
public static void SetRotation (this RegionAttachment regionAttachment, float rotation) {
regionAttachment.Rotation = rotation;
}
#endregion
}
}

View File

@@ -1,42 +1,42 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2022, 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.
*****************************************************************************/
#if UNITY_EDITOR
namespace Spine.Unity {
public static class BuildUtilities {
public static bool IsInSkeletonAssetBuildPreProcessing = false;
public static bool IsInSkeletonAssetBuildPostProcessing = false;
public static bool IsInSpriteAtlasBuildPreProcessing = false;
public static bool IsInSpriteAtlasBuildPostProcessing = false;
}
}
#endif
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2022, 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.
*****************************************************************************/
#if UNITY_EDITOR
namespace Spine.Unity {
public static class BuildUtilities {
public static bool IsInSkeletonAssetBuildPreProcessing = false;
public static bool IsInSkeletonAssetBuildPostProcessing = false;
public static bool IsInSpriteAtlasBuildPreProcessing = false;
public static bool IsInSpriteAtlasBuildPostProcessing = false;
}
}
#endif

View File

@@ -1,337 +1,337 @@
/******************************************************************************
* 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.
*****************************************************************************/
using System.Collections.Generic;
using UnityEngine;
#if UNITY_EDITOR
namespace Spine.Unity {
/// <summary>Utility class providing methods to check material settings for incorrect combinations.</summary>
public class MaterialChecks {
static readonly int STRAIGHT_ALPHA_PARAM_ID = Shader.PropertyToID("_StraightAlphaInput");
static readonly string ALPHAPREMULTIPLY_ON_KEYWORD = "_ALPHAPREMULTIPLY_ON";
static readonly string ALPHAPREMULTIPLY_VERTEX_ONLY_ON_KEYWORD = "_ALPHAPREMULTIPLY_VERTEX_ONLY";
static readonly string ALPHABLEND_ON_KEYWORD = "_ALPHABLEND_ON";
static readonly string STRAIGHT_ALPHA_KEYWORD = "_STRAIGHT_ALPHA_INPUT";
static readonly string[] FIXED_NORMALS_KEYWORDS = {
"_FIXED_NORMALS_VIEWSPACE",
"_FIXED_NORMALS_VIEWSPACE_BACKFACE",
"_FIXED_NORMALS_MODELSPACE",
"_FIXED_NORMALS_MODELSPACE_BACKFACE",
"_FIXED_NORMALS_WORLDSPACE"
};
static readonly string NORMALMAP_KEYWORD = "_NORMALMAP";
static readonly string CANVAS_GROUP_COMPATIBLE_KEYWORD = "_CANVAS_GROUP_COMPATIBLE";
public static readonly string kPMANotSupportedLinearMessage =
"\nWarning: Premultiply-alpha atlas textures not supported in Linear color space!"
+ "You can use a straight alpha texture with 'PMA Vertex Color' by choosing blend mode 'PMA Vertex, Straight Texture'.\n\n"
+ "If you have a PMA Texture, please\n"
+ "a) re-export atlas as straight alpha texture with 'premultiply alpha' unchecked\n"
+ " (if you have already done this, please set the 'Straight Alpha Texture' Material parameter to 'true') or\n"
+ "b) switch to Gamma color space via\nProject Settings - Player - Other Settings - Color Space.\n";
public static readonly string kZSpacingRequiredMessage =
"\nWarning: Z Spacing required on selected shader! Otherwise you will receive incorrect results.\n\nPlease\n"
+ "1) make sure at least minimal 'Z Spacing' is set at the SkeletonRenderer/SkeletonAnimation component under 'Advanced' and\n"
+ "2) ensure that the skeleton has overlapping parts on different Z depth. You can adjust this in Spine via draw order.\n";
public static readonly string kZSpacingRecommendedMessage =
"\nWarning: Z Spacing recommended on selected shader configuration!\n\nPlease\n"
+ "1) make sure at least minimal 'Z Spacing' is set at the SkeletonRenderer/SkeletonAnimation component under 'Advanced' and\n"
+ "2) ensure that the skeleton has overlapping parts on different Z depth. You can adjust this in Spine via draw order.\n";
public static readonly string kAddNormalsMessage =
"\nWarning: 'Add Normals' required when not using 'Fixed Normals'!\n\nPlease\n"
+ "a) enable 'Add Normals' at the SkeletonRenderer/SkeletonAnimation component under 'Advanced' or\n"
+ "b) enable 'Fixed Normals' at the Material.\n";
public static readonly string kSolveTangentsMessage =
"\nWarning: 'Solve Tangents' required when using a Normal Map!\n\nPlease\n"
+ "a) enable 'Solve Tangents' at the SkeletonRenderer/SkeletonAnimation component under 'Advanced' or\n"
+ "b) clear the 'Normal Map' parameter at the Material.\n";
public static readonly string kNoSkeletonGraphicMaterialMessage =
"\nWarning: Normal non-UI shaders other than 'Spine/SkeletonGraphic *' are not compatible with 'SkeletonGraphic' components! "
+ "This will lead to incorrect rendering on some devices.\n\n"
+ "Please change the assigned Material to e.g. 'SkeletonGraphicDefault' or change the used shader to one of the 'Spine/SkeletonGraphic *' shaders.\n\n"
+ "Note that 'Spine/SkeletonGraphic *' shall still be used when using URP.\n";
public static readonly string kNoSkeletonGraphicTintBlackMaterialMessage =
"\nWarning: Only enable 'Canvas Group Tint Black' when using a 'SkeletonGraphic Tint Black' shader!\n"
+ "This will lead to incorrect rendering.\n\nPlease\n"
+ "a) disable 'Canvas Group Tint Black' under 'Advanced' or\n"
+ "b) use a 'SkeletonGraphic Tint Black' Material if you need Tint Black on a CanvasGroup.\n";
public static readonly string kTintBlackMessage =
"\nWarning: 'Advanced - Tint Black' required when using any 'Tint Black' shader!\n\nPlease\n"
+ "a) enable 'Tint Black' at the SkeletonRenderer/SkeletonGraphic component under 'Advanced' or\n"
+ "b) use a different shader at the Material.\n";
public static readonly string kCanvasTintBlackMessage =
"\nWarning: Canvas 'Additional Shader Channels' 'uv1' and 'uv2' are required when 'Advanced - Tint Black' is enabled!\n\n"
+ "Please enable both 'uv1' and 'uv2' channels at the parent Canvas component parameter 'Additional Shader Channels'.\n";
public static readonly string kCanvasGroupCompatibleMessage =
"\nWarning: 'Canvas Group Tint Black' is enabled at SkeletonGraphic but not 'CanvasGroup Compatible' at the Material!\n\nPlease\n"
+ "a) enable 'CanvasGroup Compatible' at the Material or\n"
+ "b) disable 'Canvas Group Tint Black' at the SkeletonGraphic component under 'Advanced'.\n"
+ "You may want to duplicate the 'SkeletonGraphicDefault' material and change settings at the duplicate to not affect all instances.";
public static bool IsMaterialSetupProblematic (SkeletonRenderer renderer, ref string errorMessage) {
var materials = renderer.GetComponent<Renderer>().sharedMaterials;
bool isProblematic = false;
foreach (var material in materials) {
if (material == null) continue;
isProblematic |= IsMaterialSetupProblematic(material, ref errorMessage);
if (renderer.zSpacing == 0) {
isProblematic |= IsZSpacingRequired(material, ref errorMessage);
}
if (renderer.addNormals == false && RequiresMeshNormals(material)) {
isProblematic = true;
errorMessage += kAddNormalsMessage;
}
if (renderer.calculateTangents == false && RequiresTangents(material)) {
isProblematic = true;
errorMessage += kSolveTangentsMessage;
}
if (renderer.tintBlack == false && RequiresTintBlack(material)) {
isProblematic = true;
errorMessage += kTintBlackMessage;
}
}
return isProblematic;
}
public static bool IsMaterialSetupProblematic (SkeletonGraphic skeletonGraphic, ref string errorMessage) {
var material = skeletonGraphic.material;
bool isProblematic = false;
if (material) {
isProblematic |= IsMaterialSetupProblematic(material, ref errorMessage);
var settings = skeletonGraphic.MeshGenerator.settings;
if (settings.zSpacing == 0) {
isProblematic |= IsZSpacingRequired(material, ref errorMessage);
}
if (IsSpineNonSkeletonGraphicMaterial(material)) {
isProblematic = true;
errorMessage += kNoSkeletonGraphicMaterialMessage;
}
if (settings.tintBlack == false && RequiresTintBlack(material)) {
isProblematic = true;
errorMessage += kTintBlackMessage;
}
if (settings.tintBlack == true && CanvasNotSetupForTintBlack(skeletonGraphic)) {
isProblematic = true;
errorMessage += kCanvasTintBlackMessage;
}
if (settings.canvasGroupTintBlack == true && !IsSkeletonGraphicTintBlackMaterial(material)) {
isProblematic = true;
errorMessage += kNoSkeletonGraphicTintBlackMaterialMessage;
}
if (settings.canvasGroupTintBlack == true && !IsCanvasGroupCompatible(material)) {
isProblematic = true;
errorMessage += kCanvasGroupCompatibleMessage;
}
}
return isProblematic;
}
public static bool IsMaterialSetupProblematic (Material material, ref string errorMessage) {
return !IsColorSpaceSupported(material, ref errorMessage);
}
public static bool IsZSpacingRequired (Material material, ref string errorMessage) {
bool hasForwardAddPass = material.FindPass("FORWARD_DELTA") >= 0;
if (hasForwardAddPass) {
errorMessage += kZSpacingRequiredMessage;
return true;
}
bool zWrite = material.HasProperty("_ZWrite") && material.GetFloat("_ZWrite") > 0.0f;
if (zWrite) {
errorMessage += kZSpacingRecommendedMessage;
return true;
}
return false;
}
public static bool IsColorSpaceSupported (Material material, ref string errorMessage) {
if (QualitySettings.activeColorSpace == ColorSpace.Linear) {
if (IsPMATextureMaterial(material)) {
errorMessage += kPMANotSupportedLinearMessage;
return false;
}
}
return true;
}
public static bool UsesSpineShader (Material material) {
return material.shader.name.Contains("Spine/");
}
public static bool IsTextureSetupProblematic (Material material, ColorSpace colorSpace,
bool sRGBTexture, bool mipmapEnabled, bool alphaIsTransparency,
string texturePath, string materialPath,
ref string errorMessage) {
if (material == null || !UsesSpineShader(material)) {
return false;
}
bool isProblematic = false;
if (IsPMATextureMaterial(material)) {
// 'sRGBTexture = true' generates incorrectly weighted mipmaps at PMA textures,
// causing white borders due to undesired custom weighting.
if (sRGBTexture && mipmapEnabled && colorSpace == ColorSpace.Gamma) {
errorMessage += string.Format("`{0}` : Problematic Texture Settings found: " +
"When enabling `Generate Mip Maps` in Gamma color space, it is recommended " +
"to disable `sRGB (Color Texture)` on `Premultiply alpha` textures. Otherwise " +
"you will receive white border artifacts on an atlas exported with default " +
"`Premultiply alpha` settings.\n" +
"(You can disable this warning in `Edit - Preferences - Spine`)\n", texturePath);
isProblematic = true;
}
if (alphaIsTransparency) {
string materialName = System.IO.Path.GetFileName(materialPath);
errorMessage += string.Format("`{0}` and material `{1}` : Problematic " +
"Texture / Material Settings found: It is recommended to disable " +
"`Alpha Is Transparency` on `Premultiply alpha` textures.\n" +
"Assuming `Premultiply alpha` texture because `Straight Alpha Texture` " +
"is disabled at material). " +
"(You can disable this warning in `Edit - Preferences - Spine`)\n", texturePath, materialName);
isProblematic = true;
}
} else { // straight alpha texture
if (!alphaIsTransparency) {
string materialName = System.IO.Path.GetFileName(materialPath);
errorMessage += string.Format("`{0}` and material `{1}` : Incorrect" +
"Texture / Material Settings found: It is strongly recommended " +
"to enable `Alpha Is Transparency` on `Straight alpha` textures.\n" +
"Assuming `Straight alpha` texture because `Straight Alpha Texture` " +
"is enabled at material). " +
"(You can disable this warning in `Edit - Preferences - Spine`)\n", texturePath, materialName);
isProblematic = true;
}
}
return isProblematic;
}
public static void EnablePMATextureAtMaterial (Material material, bool enablePMATexture) {
if (material.HasProperty(STRAIGHT_ALPHA_PARAM_ID)) {
material.SetInt(STRAIGHT_ALPHA_PARAM_ID, enablePMATexture ? 0 : 1);
if (enablePMATexture)
material.DisableKeyword(STRAIGHT_ALPHA_KEYWORD);
else
material.EnableKeyword(STRAIGHT_ALPHA_KEYWORD);
} else {
if (enablePMATexture) {
material.DisableKeyword(ALPHAPREMULTIPLY_ON_KEYWORD);
material.DisableKeyword(ALPHABLEND_ON_KEYWORD);
material.EnableKeyword(ALPHAPREMULTIPLY_VERTEX_ONLY_ON_KEYWORD);
} else {
material.DisableKeyword(ALPHAPREMULTIPLY_ON_KEYWORD);
material.DisableKeyword(ALPHAPREMULTIPLY_VERTEX_ONLY_ON_KEYWORD);
material.EnableKeyword(ALPHABLEND_ON_KEYWORD);
}
}
}
public static bool IsPMATextureMaterial (Material material) {
bool usesAlphaPremultiplyKeyword = IsSpriteShader(material);
if (usesAlphaPremultiplyKeyword)
return material.IsKeywordEnabled(ALPHAPREMULTIPLY_ON_KEYWORD);
else
return material.HasProperty(STRAIGHT_ALPHA_PARAM_ID) && material.GetInt(STRAIGHT_ALPHA_PARAM_ID) == 0;
}
static bool IsURP3DMaterial (Material material) {
return material.shader.name.Contains("Universal Render Pipeline/Spine");
}
static bool IsSpineNonSkeletonGraphicMaterial (Material material) {
return material.shader.name.Contains("Spine") && !material.shader.name.Contains("SkeletonGraphic");
}
static bool IsSkeletonGraphicTintBlackMaterial (Material material) {
return material.shader.name.Contains("Spine") && material.shader.name.Contains("SkeletonGraphic")
&& material.shader.name.Contains("Black");
}
static bool AreShadowsDisabled (Material material) {
return material.IsKeywordEnabled("_RECEIVE_SHADOWS_OFF");
}
static bool RequiresMeshNormals (Material material) {
bool anyFixedNormalSet = false;
foreach (string fixedNormalKeyword in FIXED_NORMALS_KEYWORDS) {
if (material.IsKeywordEnabled(fixedNormalKeyword)) {
anyFixedNormalSet = true;
break;
}
}
bool isShaderWithMeshNormals = IsLitSpriteShader(material);
return isShaderWithMeshNormals && !anyFixedNormalSet;
}
static bool IsLitSpriteShader (Material material) {
string shaderName = material.shader.name;
return shaderName.Contains("Spine/Sprite/Pixel Lit") ||
shaderName.Contains("Spine/Sprite/Vertex Lit") ||
shaderName.Contains("2D/Spine/Sprite") || // covers both URP and LWRP
shaderName.Contains("Pipeline/Spine/Sprite"); // covers both URP and LWRP
}
static bool IsSpriteShader (Material material) {
if (IsLitSpriteShader(material))
return true;
string shaderName = material.shader.name;
return shaderName.Contains("Spine/Sprite/Unlit");
}
static bool RequiresTintBlack (Material material) {
bool isTintBlackShader =
material.shader.name.Contains("Spine") &&
material.shader.name.Contains("Tint Black");
return isTintBlackShader;
}
static bool RequiresTangents (Material material) {
return material.IsKeywordEnabled(NORMALMAP_KEYWORD);
}
static bool IsCanvasGroupCompatible (Material material) {
return material.IsKeywordEnabled(CANVAS_GROUP_COMPATIBLE_KEYWORD);
}
static bool CanvasNotSetupForTintBlack (SkeletonGraphic skeletonGraphic) {
Canvas canvas = skeletonGraphic.canvas;
if (!canvas)
return false;
var requiredChannels =
AdditionalCanvasShaderChannels.TexCoord1 |
AdditionalCanvasShaderChannels.TexCoord2;
return (canvas.additionalShaderChannels & requiredChannels) != requiredChannels;
}
}
}
#endif // UNITY_EDITOR
/******************************************************************************
* 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.
*****************************************************************************/
using System.Collections.Generic;
using UnityEngine;
#if UNITY_EDITOR
namespace Spine.Unity {
/// <summary>Utility class providing methods to check material settings for incorrect combinations.</summary>
public class MaterialChecks {
static readonly int STRAIGHT_ALPHA_PARAM_ID = Shader.PropertyToID("_StraightAlphaInput");
static readonly string ALPHAPREMULTIPLY_ON_KEYWORD = "_ALPHAPREMULTIPLY_ON";
static readonly string ALPHAPREMULTIPLY_VERTEX_ONLY_ON_KEYWORD = "_ALPHAPREMULTIPLY_VERTEX_ONLY";
static readonly string ALPHABLEND_ON_KEYWORD = "_ALPHABLEND_ON";
static readonly string STRAIGHT_ALPHA_KEYWORD = "_STRAIGHT_ALPHA_INPUT";
static readonly string[] FIXED_NORMALS_KEYWORDS = {
"_FIXED_NORMALS_VIEWSPACE",
"_FIXED_NORMALS_VIEWSPACE_BACKFACE",
"_FIXED_NORMALS_MODELSPACE",
"_FIXED_NORMALS_MODELSPACE_BACKFACE",
"_FIXED_NORMALS_WORLDSPACE"
};
static readonly string NORMALMAP_KEYWORD = "_NORMALMAP";
static readonly string CANVAS_GROUP_COMPATIBLE_KEYWORD = "_CANVAS_GROUP_COMPATIBLE";
public static readonly string kPMANotSupportedLinearMessage =
"\nWarning: Premultiply-alpha atlas textures not supported in Linear color space!"
+ "You can use a straight alpha texture with 'PMA Vertex Color' by choosing blend mode 'PMA Vertex, Straight Texture'.\n\n"
+ "If you have a PMA Texture, please\n"
+ "a) re-export atlas as straight alpha texture with 'premultiply alpha' unchecked\n"
+ " (if you have already done this, please set the 'Straight Alpha Texture' Material parameter to 'true') or\n"
+ "b) switch to Gamma color space via\nProject Settings - Player - Other Settings - Color Space.\n";
public static readonly string kZSpacingRequiredMessage =
"\nWarning: Z Spacing required on selected shader! Otherwise you will receive incorrect results.\n\nPlease\n"
+ "1) make sure at least minimal 'Z Spacing' is set at the SkeletonRenderer/SkeletonAnimation component under 'Advanced' and\n"
+ "2) ensure that the skeleton has overlapping parts on different Z depth. You can adjust this in Spine via draw order.\n";
public static readonly string kZSpacingRecommendedMessage =
"\nWarning: Z Spacing recommended on selected shader configuration!\n\nPlease\n"
+ "1) make sure at least minimal 'Z Spacing' is set at the SkeletonRenderer/SkeletonAnimation component under 'Advanced' and\n"
+ "2) ensure that the skeleton has overlapping parts on different Z depth. You can adjust this in Spine via draw order.\n";
public static readonly string kAddNormalsMessage =
"\nWarning: 'Add Normals' required when not using 'Fixed Normals'!\n\nPlease\n"
+ "a) enable 'Add Normals' at the SkeletonRenderer/SkeletonAnimation component under 'Advanced' or\n"
+ "b) enable 'Fixed Normals' at the Material.\n";
public static readonly string kSolveTangentsMessage =
"\nWarning: 'Solve Tangents' required when using a Normal Map!\n\nPlease\n"
+ "a) enable 'Solve Tangents' at the SkeletonRenderer/SkeletonAnimation component under 'Advanced' or\n"
+ "b) clear the 'Normal Map' parameter at the Material.\n";
public static readonly string kNoSkeletonGraphicMaterialMessage =
"\nWarning: Normal non-UI shaders other than 'Spine/SkeletonGraphic *' are not compatible with 'SkeletonGraphic' components! "
+ "This will lead to incorrect rendering on some devices.\n\n"
+ "Please change the assigned Material to e.g. 'SkeletonGraphicDefault' or change the used shader to one of the 'Spine/SkeletonGraphic *' shaders.\n\n"
+ "Note that 'Spine/SkeletonGraphic *' shall still be used when using URP.\n";
public static readonly string kNoSkeletonGraphicTintBlackMaterialMessage =
"\nWarning: Only enable 'Canvas Group Tint Black' when using a 'SkeletonGraphic Tint Black' shader!\n"
+ "This will lead to incorrect rendering.\n\nPlease\n"
+ "a) disable 'Canvas Group Tint Black' under 'Advanced' or\n"
+ "b) use a 'SkeletonGraphic Tint Black' Material if you need Tint Black on a CanvasGroup.\n";
public static readonly string kTintBlackMessage =
"\nWarning: 'Advanced - Tint Black' required when using any 'Tint Black' shader!\n\nPlease\n"
+ "a) enable 'Tint Black' at the SkeletonRenderer/SkeletonGraphic component under 'Advanced' or\n"
+ "b) use a different shader at the Material.\n";
public static readonly string kCanvasTintBlackMessage =
"\nWarning: Canvas 'Additional Shader Channels' 'uv1' and 'uv2' are required when 'Advanced - Tint Black' is enabled!\n\n"
+ "Please enable both 'uv1' and 'uv2' channels at the parent Canvas component parameter 'Additional Shader Channels'.\n";
public static readonly string kCanvasGroupCompatibleMessage =
"\nWarning: 'Canvas Group Tint Black' is enabled at SkeletonGraphic but not 'CanvasGroup Compatible' at the Material!\n\nPlease\n"
+ "a) enable 'CanvasGroup Compatible' at the Material or\n"
+ "b) disable 'Canvas Group Tint Black' at the SkeletonGraphic component under 'Advanced'.\n"
+ "You may want to duplicate the 'SkeletonGraphicDefault' material and change settings at the duplicate to not affect all instances.";
public static bool IsMaterialSetupProblematic (SkeletonRenderer renderer, ref string errorMessage) {
var materials = renderer.GetComponent<Renderer>().sharedMaterials;
bool isProblematic = false;
foreach (var material in materials) {
if (material == null) continue;
isProblematic |= IsMaterialSetupProblematic(material, ref errorMessage);
if (renderer.zSpacing == 0) {
isProblematic |= IsZSpacingRequired(material, ref errorMessage);
}
if (renderer.addNormals == false && RequiresMeshNormals(material)) {
isProblematic = true;
errorMessage += kAddNormalsMessage;
}
if (renderer.calculateTangents == false && RequiresTangents(material)) {
isProblematic = true;
errorMessage += kSolveTangentsMessage;
}
if (renderer.tintBlack == false && RequiresTintBlack(material)) {
isProblematic = true;
errorMessage += kTintBlackMessage;
}
}
return isProblematic;
}
public static bool IsMaterialSetupProblematic (SkeletonGraphic skeletonGraphic, ref string errorMessage) {
var material = skeletonGraphic.material;
bool isProblematic = false;
if (material) {
isProblematic |= IsMaterialSetupProblematic(material, ref errorMessage);
var settings = skeletonGraphic.MeshGenerator.settings;
if (settings.zSpacing == 0) {
isProblematic |= IsZSpacingRequired(material, ref errorMessage);
}
if (IsSpineNonSkeletonGraphicMaterial(material)) {
isProblematic = true;
errorMessage += kNoSkeletonGraphicMaterialMessage;
}
if (settings.tintBlack == false && RequiresTintBlack(material)) {
isProblematic = true;
errorMessage += kTintBlackMessage;
}
if (settings.tintBlack == true && CanvasNotSetupForTintBlack(skeletonGraphic)) {
isProblematic = true;
errorMessage += kCanvasTintBlackMessage;
}
if (settings.canvasGroupTintBlack == true && !IsSkeletonGraphicTintBlackMaterial(material)) {
isProblematic = true;
errorMessage += kNoSkeletonGraphicTintBlackMaterialMessage;
}
if (settings.canvasGroupTintBlack == true && !IsCanvasGroupCompatible(material)) {
isProblematic = true;
errorMessage += kCanvasGroupCompatibleMessage;
}
}
return isProblematic;
}
public static bool IsMaterialSetupProblematic (Material material, ref string errorMessage) {
return !IsColorSpaceSupported(material, ref errorMessage);
}
public static bool IsZSpacingRequired (Material material, ref string errorMessage) {
bool hasForwardAddPass = material.FindPass("FORWARD_DELTA") >= 0;
if (hasForwardAddPass) {
errorMessage += kZSpacingRequiredMessage;
return true;
}
bool zWrite = material.HasProperty("_ZWrite") && material.GetFloat("_ZWrite") > 0.0f;
if (zWrite) {
errorMessage += kZSpacingRecommendedMessage;
return true;
}
return false;
}
public static bool IsColorSpaceSupported (Material material, ref string errorMessage) {
if (QualitySettings.activeColorSpace == ColorSpace.Linear) {
if (IsPMATextureMaterial(material)) {
errorMessage += kPMANotSupportedLinearMessage;
return false;
}
}
return true;
}
public static bool UsesSpineShader (Material material) {
return material.shader.name.Contains("Spine/");
}
public static bool IsTextureSetupProblematic (Material material, ColorSpace colorSpace,
bool sRGBTexture, bool mipmapEnabled, bool alphaIsTransparency,
string texturePath, string materialPath,
ref string errorMessage) {
if (material == null || !UsesSpineShader(material)) {
return false;
}
bool isProblematic = false;
if (IsPMATextureMaterial(material)) {
// 'sRGBTexture = true' generates incorrectly weighted mipmaps at PMA textures,
// causing white borders due to undesired custom weighting.
if (sRGBTexture && mipmapEnabled && colorSpace == ColorSpace.Gamma) {
errorMessage += string.Format("`{0}` : Problematic Texture Settings found: " +
"When enabling `Generate Mip Maps` in Gamma color space, it is recommended " +
"to disable `sRGB (Color Texture)` on `Premultiply alpha` textures. Otherwise " +
"you will receive white border artifacts on an atlas exported with default " +
"`Premultiply alpha` settings.\n" +
"(You can disable this warning in `Edit - Preferences - Spine`)\n", texturePath);
isProblematic = true;
}
if (alphaIsTransparency) {
string materialName = System.IO.Path.GetFileName(materialPath);
errorMessage += string.Format("`{0}` and material `{1}` : Problematic " +
"Texture / Material Settings found: It is recommended to disable " +
"`Alpha Is Transparency` on `Premultiply alpha` textures.\n" +
"Assuming `Premultiply alpha` texture because `Straight Alpha Texture` " +
"is disabled at material). " +
"(You can disable this warning in `Edit - Preferences - Spine`)\n", texturePath, materialName);
isProblematic = true;
}
} else { // straight alpha texture
if (!alphaIsTransparency) {
string materialName = System.IO.Path.GetFileName(materialPath);
errorMessage += string.Format("`{0}` and material `{1}` : Incorrect" +
"Texture / Material Settings found: It is strongly recommended " +
"to enable `Alpha Is Transparency` on `Straight alpha` textures.\n" +
"Assuming `Straight alpha` texture because `Straight Alpha Texture` " +
"is enabled at material). " +
"(You can disable this warning in `Edit - Preferences - Spine`)\n", texturePath, materialName);
isProblematic = true;
}
}
return isProblematic;
}
public static void EnablePMATextureAtMaterial (Material material, bool enablePMATexture) {
if (material.HasProperty(STRAIGHT_ALPHA_PARAM_ID)) {
material.SetInt(STRAIGHT_ALPHA_PARAM_ID, enablePMATexture ? 0 : 1);
if (enablePMATexture)
material.DisableKeyword(STRAIGHT_ALPHA_KEYWORD);
else
material.EnableKeyword(STRAIGHT_ALPHA_KEYWORD);
} else {
if (enablePMATexture) {
material.DisableKeyword(ALPHAPREMULTIPLY_ON_KEYWORD);
material.DisableKeyword(ALPHABLEND_ON_KEYWORD);
material.EnableKeyword(ALPHAPREMULTIPLY_VERTEX_ONLY_ON_KEYWORD);
} else {
material.DisableKeyword(ALPHAPREMULTIPLY_ON_KEYWORD);
material.DisableKeyword(ALPHAPREMULTIPLY_VERTEX_ONLY_ON_KEYWORD);
material.EnableKeyword(ALPHABLEND_ON_KEYWORD);
}
}
}
public static bool IsPMATextureMaterial (Material material) {
bool usesAlphaPremultiplyKeyword = IsSpriteShader(material);
if (usesAlphaPremultiplyKeyword)
return material.IsKeywordEnabled(ALPHAPREMULTIPLY_ON_KEYWORD);
else
return material.HasProperty(STRAIGHT_ALPHA_PARAM_ID) && material.GetInt(STRAIGHT_ALPHA_PARAM_ID) == 0;
}
static bool IsURP3DMaterial (Material material) {
return material.shader.name.Contains("Universal Render Pipeline/Spine");
}
static bool IsSpineNonSkeletonGraphicMaterial (Material material) {
return material.shader.name.Contains("Spine") && !material.shader.name.Contains("SkeletonGraphic");
}
static bool IsSkeletonGraphicTintBlackMaterial (Material material) {
return material.shader.name.Contains("Spine") && material.shader.name.Contains("SkeletonGraphic")
&& material.shader.name.Contains("Black");
}
static bool AreShadowsDisabled (Material material) {
return material.IsKeywordEnabled("_RECEIVE_SHADOWS_OFF");
}
static bool RequiresMeshNormals (Material material) {
bool anyFixedNormalSet = false;
foreach (string fixedNormalKeyword in FIXED_NORMALS_KEYWORDS) {
if (material.IsKeywordEnabled(fixedNormalKeyword)) {
anyFixedNormalSet = true;
break;
}
}
bool isShaderWithMeshNormals = IsLitSpriteShader(material);
return isShaderWithMeshNormals && !anyFixedNormalSet;
}
static bool IsLitSpriteShader (Material material) {
string shaderName = material.shader.name;
return shaderName.Contains("Spine/Sprite/Pixel Lit") ||
shaderName.Contains("Spine/Sprite/Vertex Lit") ||
shaderName.Contains("2D/Spine/Sprite") || // covers both URP and LWRP
shaderName.Contains("Pipeline/Spine/Sprite"); // covers both URP and LWRP
}
static bool IsSpriteShader (Material material) {
if (IsLitSpriteShader(material))
return true;
string shaderName = material.shader.name;
return shaderName.Contains("Spine/Sprite/Unlit");
}
static bool RequiresTintBlack (Material material) {
bool isTintBlackShader =
material.shader.name.Contains("Spine") &&
material.shader.name.Contains("Tint Black");
return isTintBlackShader;
}
static bool RequiresTangents (Material material) {
return material.IsKeywordEnabled(NORMALMAP_KEYWORD);
}
static bool IsCanvasGroupCompatible (Material material) {
return material.IsKeywordEnabled(CANVAS_GROUP_COMPATIBLE_KEYWORD);
}
static bool CanvasNotSetupForTintBlack (SkeletonGraphic skeletonGraphic) {
Canvas canvas = skeletonGraphic.canvas;
if (!canvas)
return false;
var requiredChannels =
AdditionalCanvasShaderChannels.TexCoord1 |
AdditionalCanvasShaderChannels.TexCoord2;
return (canvas.additionalShaderChannels & requiredChannels) != requiredChannels;
}
}
}
#endif // UNITY_EDITOR

View File

@@ -1,42 +1,42 @@
/******************************************************************************
* 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.
*****************************************************************************/
namespace Spine.Unity {
/// <summary>
/// TriState enum which can be used to replace and extend a bool variable by
/// a third <c>UseGlobalSettings</c> state. Automatically maps serialized
/// bool values to corresponding <c>Disable</c> and <c>Enable</c> states.
/// </summary>
public enum SettingsTriState {
Disable,
Enable,
UseGlobalSetting
}
}
/******************************************************************************
* 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.
*****************************************************************************/
namespace Spine.Unity {
/// <summary>
/// TriState enum which can be used to replace and extend a bool variable by
/// a third <c>UseGlobalSettings</c> state. Automatically maps serialized
/// bool values to corresponding <c>Disable</c> and <c>Enable</c> states.
/// </summary>
public enum SettingsTriState {
Disable,
Enable,
UseGlobalSetting
}
}

View File

@@ -1,461 +1,461 @@
/******************************************************************************
* 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.
*****************************************************************************/
using UnityEngine;
namespace Spine.Unity {
public static class SkeletonExtensions {
#region Colors
const float ByteToFloat = 1f / 255f;
public static Color GetColor (this Skeleton s) { return new Color(s.R, s.G, s.B, s.A); }
public static Color GetColor (this RegionAttachment a) { return new Color(a.R, a.G, a.B, a.A); }
public static Color GetColor (this MeshAttachment a) { return new Color(a.R, a.G, a.B, a.A); }
public static Color GetColor (this Slot s) { return new Color(s.R, s.G, s.B, s.A); }
public static Color GetColorTintBlack (this Slot s) { return new Color(s.R2, s.G2, s.B2, 1f); }
public static void SetColor (this Skeleton skeleton, Color color) {
skeleton.A = color.a;
skeleton.R = color.r;
skeleton.G = color.g;
skeleton.B = color.b;
}
public static void SetColor (this Skeleton skeleton, Color32 color) {
skeleton.A = color.a * ByteToFloat;
skeleton.R = color.r * ByteToFloat;
skeleton.G = color.g * ByteToFloat;
skeleton.B = color.b * ByteToFloat;
}
public static void SetColor (this Slot slot, Color color) {
slot.A = color.a;
slot.R = color.r;
slot.G = color.g;
slot.B = color.b;
}
public static void SetColor (this Slot slot, Color32 color) {
slot.A = color.a * ByteToFloat;
slot.R = color.r * ByteToFloat;
slot.G = color.g * ByteToFloat;
slot.B = color.b * ByteToFloat;
}
public static void SetColor (this RegionAttachment attachment, Color color) {
attachment.A = color.a;
attachment.R = color.r;
attachment.G = color.g;
attachment.B = color.b;
}
public static void SetColor (this RegionAttachment attachment, Color32 color) {
attachment.A = color.a * ByteToFloat;
attachment.R = color.r * ByteToFloat;
attachment.G = color.g * ByteToFloat;
attachment.B = color.b * ByteToFloat;
}
public static void SetColor (this MeshAttachment attachment, Color color) {
attachment.A = color.a;
attachment.R = color.r;
attachment.G = color.g;
attachment.B = color.b;
}
public static void SetColor (this MeshAttachment attachment, Color32 color) {
attachment.A = color.a * ByteToFloat;
attachment.R = color.r * ByteToFloat;
attachment.G = color.g * ByteToFloat;
attachment.B = color.b * ByteToFloat;
}
#endregion
#region Skeleton
/// <summary>Sets the Skeleton's local scale using a UnityEngine.Vector2. If only individual components need to be set, set Skeleton.ScaleX or Skeleton.ScaleY.</summary>
public static void SetLocalScale (this Skeleton skeleton, Vector2 scale) {
skeleton.ScaleX = scale.x;
skeleton.ScaleY = scale.y;
}
/// <summary>Gets the internal bone matrix as a Unity bonespace-to-skeletonspace transformation matrix.</summary>
public static Matrix4x4 GetMatrix4x4 (this Bone bone) {
return new Matrix4x4 {
m00 = bone.A,
m01 = bone.B,
m03 = bone.WorldX,
m10 = bone.C,
m11 = bone.D,
m13 = bone.WorldY,
m33 = 1
};
}
#endregion
#region Bone
/// <summary>Sets the bone's (local) X and Y according to a Vector2</summary>
public static void SetLocalPosition (this Bone bone, Vector2 position) {
bone.X = position.x;
bone.Y = position.y;
}
/// <summary>Sets the bone's (local) X and Y according to a Vector3. The z component is ignored.</summary>
public static void SetLocalPosition (this Bone bone, Vector3 position) {
bone.X = position.x;
bone.Y = position.y;
}
/// <summary>Gets the bone's local X and Y as a Vector2.</summary>
public static Vector2 GetLocalPosition (this Bone bone) {
return new Vector2(bone.X, bone.Y);
}
/// <summary>Gets the position of the bone in Skeleton-space.</summary>
public static Vector2 GetSkeletonSpacePosition (this Bone bone) {
return new Vector2(bone.WorldX, bone.WorldY);
}
/// <summary>Gets a local offset from the bone and converts it into Skeleton-space.</summary>
public static Vector2 GetSkeletonSpacePosition (this Bone bone, Vector2 boneLocal) {
Vector2 o;
bone.LocalToWorld(boneLocal.x, boneLocal.y, out o.x, out o.y);
return o;
}
/// <summary>Gets the bone's Unity World position using its Spine GameObject Transform. UpdateWorldTransform needs to have been called for this to return the correct, updated value.</summary>
public static Vector3 GetWorldPosition (this Bone bone, UnityEngine.Transform spineGameObjectTransform) {
return spineGameObjectTransform.TransformPoint(new Vector3(bone.WorldX, bone.WorldY));
}
public static Vector3 GetWorldPosition (this Bone bone, UnityEngine.Transform spineGameObjectTransform, float positionScale) {
return spineGameObjectTransform.TransformPoint(new Vector3(bone.WorldX * positionScale, bone.WorldY * positionScale));
}
/// <summary>Gets a skeleton space UnityEngine.Quaternion representation of bone.WorldRotationX.</summary>
public static Quaternion GetQuaternion (this Bone bone) {
var halfRotation = Mathf.Atan2(bone.C, bone.A) * 0.5f;
return new Quaternion(0, 0, Mathf.Sin(halfRotation), Mathf.Cos(halfRotation));
}
/// <summary>Gets a bone-local space UnityEngine.Quaternion representation of bone.rotation.</summary>
public static Quaternion GetLocalQuaternion (this Bone bone) {
var halfRotation = bone.Rotation * Mathf.Deg2Rad * 0.5f;
return new Quaternion(0, 0, Mathf.Sin(halfRotation), Mathf.Cos(halfRotation));
}
/// <summary>Returns the Skeleton's local scale as a UnityEngine.Vector2. If only individual components are needed, use Skeleton.ScaleX or Skeleton.ScaleY.</summary>
public static Vector2 GetLocalScale (this Skeleton skeleton) {
return new Vector2(skeleton.ScaleX, skeleton.ScaleY);
}
/// <summary>Calculates a 2x2 Transformation Matrix that can convert a skeleton-space position to a bone-local position.</summary>
public static void GetWorldToLocalMatrix (this Bone bone, out float ia, out float ib, out float ic, out float id) {
float a = bone.A, b = bone.B, c = bone.C, d = bone.D;
float invDet = 1 / (a * d - b * c);
ia = invDet * d;
ib = invDet * -b;
ic = invDet * -c;
id = invDet * a;
}
/// <summary>UnityEngine.Vector2 override of Bone.WorldToLocal. This converts a skeleton-space position into a bone local position.</summary>
public static Vector2 WorldToLocal (this Bone bone, Vector2 worldPosition) {
Vector2 o;
bone.WorldToLocal(worldPosition.x, worldPosition.y, out o.x, out o.y);
return o;
}
/// <summary>Sets the skeleton-space position of a bone.</summary>
/// <returns>The local position in its parent bone space, or in skeleton space if it is the root bone.</returns>
public static Vector2 SetPositionSkeletonSpace (this Bone bone, Vector2 skeletonSpacePosition) {
if (bone.Parent == null) { // root bone
bone.SetLocalPosition(skeletonSpacePosition);
return skeletonSpacePosition;
} else {
var parent = bone.Parent;
Vector2 parentLocal = parent.WorldToLocal(skeletonSpacePosition);
bone.SetLocalPosition(parentLocal);
return parentLocal;
}
}
#endregion
#region Attachments
public static Material GetMaterial (this Attachment a) {
object rendererObject = null;
var renderableAttachment = a as IHasRendererObject;
if (renderableAttachment != null)
rendererObject = renderableAttachment.RendererObject;
if (rendererObject == null)
return null;
#if SPINE_TK2D
return (rendererObject.GetType() == typeof(Material)) ? (Material)rendererObject : (Material)((AtlasRegion)rendererObject).page.rendererObject;
#else
return (Material)((AtlasRegion)rendererObject).page.rendererObject;
#endif
}
/// <summary>Fills a Vector2 buffer with local vertices.</summary>
/// <param name="va">The VertexAttachment</param>
/// <param name="slot">Slot where the attachment belongs.</param>
/// <param name="buffer">Correctly-sized buffer. Use attachment's .WorldVerticesLength to get the correct size. If null, a new Vector2[] of the correct size will be allocated.</param>
public static Vector2[] GetLocalVertices (this VertexAttachment va, Slot slot, Vector2[] buffer) {
int floatsCount = va.WorldVerticesLength;
int bufferTargetSize = floatsCount >> 1;
buffer = buffer ?? new Vector2[bufferTargetSize];
if (buffer.Length < bufferTargetSize) throw new System.ArgumentException(string.Format("Vector2 buffer too small. {0} requires an array of size {1}. Use the attachment's .WorldVerticesLength to get the correct size.", va.Name, floatsCount), "buffer");
if (va.Bones == null && slot.Deform.Count == 0) {
var localVerts = va.Vertices;
for (int i = 0; i < bufferTargetSize; i++) {
int j = i * 2;
buffer[i] = new Vector2(localVerts[j], localVerts[j + 1]);
}
} else {
var floats = new float[floatsCount];
va.ComputeWorldVertices(slot, floats);
Bone sb = slot.Bone;
float ia, ib, ic, id, bwx = sb.WorldX, bwy = sb.WorldY;
sb.GetWorldToLocalMatrix(out ia, out ib, out ic, out id);
for (int i = 0; i < bufferTargetSize; i++) {
int j = i * 2;
float x = floats[j] - bwx, y = floats[j + 1] - bwy;
buffer[i] = new Vector2(x * ia + y * ib, x * ic + y * id);
}
}
return buffer;
}
/// <summary>Calculates world vertices and fills a Vector2 buffer.</summary>
/// <param name="a">The VertexAttachment</param>
/// <param name="slot">Slot where the attachment belongs.</param>
/// <param name="buffer">Correctly-sized buffer. Use attachment's .WorldVerticesLength to get the correct size. If null, a new Vector2[] of the correct size will be allocated.</param>
public static Vector2[] GetWorldVertices (this VertexAttachment a, Slot slot, Vector2[] buffer) {
int worldVertsLength = a.WorldVerticesLength;
int bufferTargetSize = worldVertsLength >> 1;
buffer = buffer ?? new Vector2[bufferTargetSize];
if (buffer.Length < bufferTargetSize) throw new System.ArgumentException(string.Format("Vector2 buffer too small. {0} requires an array of size {1}. Use the attachment's .WorldVerticesLength to get the correct size.", a.Name, worldVertsLength), "buffer");
var floats = new float[worldVertsLength];
a.ComputeWorldVertices(slot, floats);
for (int i = 0, n = worldVertsLength >> 1; i < n; i++) {
int j = i * 2;
buffer[i] = new Vector2(floats[j], floats[j + 1]);
}
return buffer;
}
/// <summary>Gets the PointAttachment's Unity World position using its Spine GameObject Transform.</summary>
public static Vector3 GetWorldPosition (this PointAttachment attachment, Slot slot, Transform spineGameObjectTransform) {
Vector3 skeletonSpacePosition;
skeletonSpacePosition.z = 0;
attachment.ComputeWorldPosition(slot.Bone, out skeletonSpacePosition.x, out skeletonSpacePosition.y);
return spineGameObjectTransform.TransformPoint(skeletonSpacePosition);
}
/// <summary>Gets the PointAttachment's Unity World position using its Spine GameObject Transform.</summary>
public static Vector3 GetWorldPosition (this PointAttachment attachment, Bone bone, Transform spineGameObjectTransform) {
Vector3 skeletonSpacePosition;
skeletonSpacePosition.z = 0;
attachment.ComputeWorldPosition(bone, out skeletonSpacePosition.x, out skeletonSpacePosition.y);
return spineGameObjectTransform.TransformPoint(skeletonSpacePosition);
}
#endregion
}
}
namespace Spine {
using System;
public struct BoneMatrix {
public float a, b, c, d, x, y;
/// <summary>Recursively calculates a worldspace bone matrix based on BoneData.</summary>
public static BoneMatrix CalculateSetupWorld (BoneData boneData) {
if (boneData == null)
return default(BoneMatrix);
// End condition: isRootBone
if (boneData.Parent == null)
return GetInheritedInternal(boneData, default(BoneMatrix));
BoneMatrix result = CalculateSetupWorld(boneData.Parent);
return GetInheritedInternal(boneData, result);
}
static BoneMatrix GetInheritedInternal (BoneData boneData, BoneMatrix parentMatrix) {
var parent = boneData.Parent;
if (parent == null) return new BoneMatrix(boneData); // isRootBone
float pa = parentMatrix.a, pb = parentMatrix.b, pc = parentMatrix.c, pd = parentMatrix.d;
BoneMatrix result = default(BoneMatrix);
result.x = pa * boneData.X + pb * boneData.Y + parentMatrix.x;
result.y = pc * boneData.X + pd * boneData.Y + parentMatrix.y;
switch (boneData.TransformMode) {
case TransformMode.Normal: {
float rotationY = boneData.Rotation + 90 + boneData.ShearY;
float la = MathUtils.CosDeg(boneData.Rotation + boneData.ShearX) * boneData.ScaleX;
float lb = MathUtils.CosDeg(rotationY) * boneData.ScaleY;
float lc = MathUtils.SinDeg(boneData.Rotation + boneData.ShearX) * boneData.ScaleX;
float ld = MathUtils.SinDeg(rotationY) * boneData.ScaleY;
result.a = pa * la + pb * lc;
result.b = pa * lb + pb * ld;
result.c = pc * la + pd * lc;
result.d = pc * lb + pd * ld;
break;
}
case TransformMode.OnlyTranslation: {
float rotationY = boneData.Rotation + 90 + boneData.ShearY;
result.a = MathUtils.CosDeg(boneData.Rotation + boneData.ShearX) * boneData.ScaleX;
result.b = MathUtils.CosDeg(rotationY) * boneData.ScaleY;
result.c = MathUtils.SinDeg(boneData.Rotation + boneData.ShearX) * boneData.ScaleX;
result.d = MathUtils.SinDeg(rotationY) * boneData.ScaleY;
break;
}
case TransformMode.NoRotationOrReflection: {
float s = pa * pa + pc * pc, prx;
if (s > 0.0001f) {
s = Math.Abs(pa * pd - pb * pc) / s;
pb = pc * s;
pd = pa * s;
prx = MathUtils.Atan2(pc, pa) * MathUtils.RadDeg;
} else {
pa = 0;
pc = 0;
prx = 90 - MathUtils.Atan2(pd, pb) * MathUtils.RadDeg;
}
float rx = boneData.Rotation + boneData.ShearX - prx;
float ry = boneData.Rotation + boneData.ShearY - prx + 90;
float la = MathUtils.CosDeg(rx) * boneData.ScaleX;
float lb = MathUtils.CosDeg(ry) * boneData.ScaleY;
float lc = MathUtils.SinDeg(rx) * boneData.ScaleX;
float ld = MathUtils.SinDeg(ry) * boneData.ScaleY;
result.a = pa * la - pb * lc;
result.b = pa * lb - pb * ld;
result.c = pc * la + pd * lc;
result.d = pc * lb + pd * ld;
break;
}
case TransformMode.NoScale:
case TransformMode.NoScaleOrReflection: {
float cos = MathUtils.CosDeg(boneData.Rotation), sin = MathUtils.SinDeg(boneData.Rotation);
float za = pa * cos + pb * sin;
float zc = pc * cos + pd * sin;
float s = (float)Math.Sqrt(za * za + zc * zc);
if (s > 0.00001f)
s = 1 / s;
za *= s;
zc *= s;
s = (float)Math.Sqrt(za * za + zc * zc);
float r = MathUtils.PI / 2 + MathUtils.Atan2(zc, za);
float zb = MathUtils.Cos(r) * s;
float zd = MathUtils.Sin(r) * s;
float la = MathUtils.CosDeg(boneData.ShearX) * boneData.ScaleX;
float lb = MathUtils.CosDeg(90 + boneData.ShearY) * boneData.ScaleY;
float lc = MathUtils.SinDeg(boneData.ShearX) * boneData.ScaleX;
float ld = MathUtils.SinDeg(90 + boneData.ShearY) * boneData.ScaleY;
if (boneData.TransformMode != TransformMode.NoScaleOrReflection ? pa * pd - pb * pc < 0 : false) {
zb = -zb;
zd = -zd;
}
result.a = za * la + zb * lc;
result.b = za * lb + zb * ld;
result.c = zc * la + zd * lc;
result.d = zc * lb + zd * ld;
break;
}
}
return result;
}
/// <summary>Constructor for a local bone matrix based on Setup Pose BoneData.</summary>
public BoneMatrix (BoneData boneData) {
float rotationY = boneData.Rotation + 90 + boneData.ShearY;
float rotationX = boneData.Rotation + boneData.ShearX;
a = MathUtils.CosDeg(rotationX) * boneData.ScaleX;
c = MathUtils.SinDeg(rotationX) * boneData.ScaleX;
b = MathUtils.CosDeg(rotationY) * boneData.ScaleY;
d = MathUtils.SinDeg(rotationY) * boneData.ScaleY;
x = boneData.X;
y = boneData.Y;
}
/// <summary>Constructor for a local bone matrix based on a bone instance's current pose.</summary>
public BoneMatrix (Bone bone) {
float rotationY = bone.Rotation + 90 + bone.ShearY;
float rotationX = bone.Rotation + bone.ShearX;
a = MathUtils.CosDeg(rotationX) * bone.ScaleX;
c = MathUtils.SinDeg(rotationX) * bone.ScaleX;
b = MathUtils.CosDeg(rotationY) * bone.ScaleY;
d = MathUtils.SinDeg(rotationY) * bone.ScaleY;
x = bone.X;
y = bone.Y;
}
public BoneMatrix TransformMatrix (BoneMatrix local) {
return new BoneMatrix {
a = this.a * local.a + this.b * local.c,
b = this.a * local.b + this.b * local.d,
c = this.c * local.a + this.d * local.c,
d = this.c * local.b + this.d * local.d,
x = this.a * local.x + this.b * local.y + this.x,
y = this.c * local.x + this.d * local.y + this.y
};
}
}
public static class SpineSkeletonExtensions {
public static bool IsWeighted (this VertexAttachment va) {
return va.Bones != null && va.Bones.Length > 0;
}
#region Transform Modes
public static bool InheritsRotation (this TransformMode mode) {
const int RotationBit = 0;
return ((int)mode & (1U << RotationBit)) == 0;
}
public static bool InheritsScale (this TransformMode mode) {
const int ScaleBit = 1;
return ((int)mode & (1U << ScaleBit)) == 0;
}
#endregion
}
}
/******************************************************************************
* 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.
*****************************************************************************/
using UnityEngine;
namespace Spine.Unity {
public static class SkeletonExtensions {
#region Colors
const float ByteToFloat = 1f / 255f;
public static Color GetColor (this Skeleton s) { return new Color(s.R, s.G, s.B, s.A); }
public static Color GetColor (this RegionAttachment a) { return new Color(a.R, a.G, a.B, a.A); }
public static Color GetColor (this MeshAttachment a) { return new Color(a.R, a.G, a.B, a.A); }
public static Color GetColor (this Slot s) { return new Color(s.R, s.G, s.B, s.A); }
public static Color GetColorTintBlack (this Slot s) { return new Color(s.R2, s.G2, s.B2, 1f); }
public static void SetColor (this Skeleton skeleton, Color color) {
skeleton.A = color.a;
skeleton.R = color.r;
skeleton.G = color.g;
skeleton.B = color.b;
}
public static void SetColor (this Skeleton skeleton, Color32 color) {
skeleton.A = color.a * ByteToFloat;
skeleton.R = color.r * ByteToFloat;
skeleton.G = color.g * ByteToFloat;
skeleton.B = color.b * ByteToFloat;
}
public static void SetColor (this Slot slot, Color color) {
slot.A = color.a;
slot.R = color.r;
slot.G = color.g;
slot.B = color.b;
}
public static void SetColor (this Slot slot, Color32 color) {
slot.A = color.a * ByteToFloat;
slot.R = color.r * ByteToFloat;
slot.G = color.g * ByteToFloat;
slot.B = color.b * ByteToFloat;
}
public static void SetColor (this RegionAttachment attachment, Color color) {
attachment.A = color.a;
attachment.R = color.r;
attachment.G = color.g;
attachment.B = color.b;
}
public static void SetColor (this RegionAttachment attachment, Color32 color) {
attachment.A = color.a * ByteToFloat;
attachment.R = color.r * ByteToFloat;
attachment.G = color.g * ByteToFloat;
attachment.B = color.b * ByteToFloat;
}
public static void SetColor (this MeshAttachment attachment, Color color) {
attachment.A = color.a;
attachment.R = color.r;
attachment.G = color.g;
attachment.B = color.b;
}
public static void SetColor (this MeshAttachment attachment, Color32 color) {
attachment.A = color.a * ByteToFloat;
attachment.R = color.r * ByteToFloat;
attachment.G = color.g * ByteToFloat;
attachment.B = color.b * ByteToFloat;
}
#endregion
#region Skeleton
/// <summary>Sets the Skeleton's local scale using a UnityEngine.Vector2. If only individual components need to be set, set Skeleton.ScaleX or Skeleton.ScaleY.</summary>
public static void SetLocalScale (this Skeleton skeleton, Vector2 scale) {
skeleton.ScaleX = scale.x;
skeleton.ScaleY = scale.y;
}
/// <summary>Gets the internal bone matrix as a Unity bonespace-to-skeletonspace transformation matrix.</summary>
public static Matrix4x4 GetMatrix4x4 (this Bone bone) {
return new Matrix4x4 {
m00 = bone.A,
m01 = bone.B,
m03 = bone.WorldX,
m10 = bone.C,
m11 = bone.D,
m13 = bone.WorldY,
m33 = 1
};
}
#endregion
#region Bone
/// <summary>Sets the bone's (local) X and Y according to a Vector2</summary>
public static void SetLocalPosition (this Bone bone, Vector2 position) {
bone.X = position.x;
bone.Y = position.y;
}
/// <summary>Sets the bone's (local) X and Y according to a Vector3. The z component is ignored.</summary>
public static void SetLocalPosition (this Bone bone, Vector3 position) {
bone.X = position.x;
bone.Y = position.y;
}
/// <summary>Gets the bone's local X and Y as a Vector2.</summary>
public static Vector2 GetLocalPosition (this Bone bone) {
return new Vector2(bone.X, bone.Y);
}
/// <summary>Gets the position of the bone in Skeleton-space.</summary>
public static Vector2 GetSkeletonSpacePosition (this Bone bone) {
return new Vector2(bone.WorldX, bone.WorldY);
}
/// <summary>Gets a local offset from the bone and converts it into Skeleton-space.</summary>
public static Vector2 GetSkeletonSpacePosition (this Bone bone, Vector2 boneLocal) {
Vector2 o;
bone.LocalToWorld(boneLocal.x, boneLocal.y, out o.x, out o.y);
return o;
}
/// <summary>Gets the bone's Unity World position using its Spine GameObject Transform. UpdateWorldTransform needs to have been called for this to return the correct, updated value.</summary>
public static Vector3 GetWorldPosition (this Bone bone, UnityEngine.Transform spineGameObjectTransform) {
return spineGameObjectTransform.TransformPoint(new Vector3(bone.WorldX, bone.WorldY));
}
public static Vector3 GetWorldPosition (this Bone bone, UnityEngine.Transform spineGameObjectTransform, float positionScale) {
return spineGameObjectTransform.TransformPoint(new Vector3(bone.WorldX * positionScale, bone.WorldY * positionScale));
}
/// <summary>Gets a skeleton space UnityEngine.Quaternion representation of bone.WorldRotationX.</summary>
public static Quaternion GetQuaternion (this Bone bone) {
var halfRotation = Mathf.Atan2(bone.C, bone.A) * 0.5f;
return new Quaternion(0, 0, Mathf.Sin(halfRotation), Mathf.Cos(halfRotation));
}
/// <summary>Gets a bone-local space UnityEngine.Quaternion representation of bone.rotation.</summary>
public static Quaternion GetLocalQuaternion (this Bone bone) {
var halfRotation = bone.Rotation * Mathf.Deg2Rad * 0.5f;
return new Quaternion(0, 0, Mathf.Sin(halfRotation), Mathf.Cos(halfRotation));
}
/// <summary>Returns the Skeleton's local scale as a UnityEngine.Vector2. If only individual components are needed, use Skeleton.ScaleX or Skeleton.ScaleY.</summary>
public static Vector2 GetLocalScale (this Skeleton skeleton) {
return new Vector2(skeleton.ScaleX, skeleton.ScaleY);
}
/// <summary>Calculates a 2x2 Transformation Matrix that can convert a skeleton-space position to a bone-local position.</summary>
public static void GetWorldToLocalMatrix (this Bone bone, out float ia, out float ib, out float ic, out float id) {
float a = bone.A, b = bone.B, c = bone.C, d = bone.D;
float invDet = 1 / (a * d - b * c);
ia = invDet * d;
ib = invDet * -b;
ic = invDet * -c;
id = invDet * a;
}
/// <summary>UnityEngine.Vector2 override of Bone.WorldToLocal. This converts a skeleton-space position into a bone local position.</summary>
public static Vector2 WorldToLocal (this Bone bone, Vector2 worldPosition) {
Vector2 o;
bone.WorldToLocal(worldPosition.x, worldPosition.y, out o.x, out o.y);
return o;
}
/// <summary>Sets the skeleton-space position of a bone.</summary>
/// <returns>The local position in its parent bone space, or in skeleton space if it is the root bone.</returns>
public static Vector2 SetPositionSkeletonSpace (this Bone bone, Vector2 skeletonSpacePosition) {
if (bone.Parent == null) { // root bone
bone.SetLocalPosition(skeletonSpacePosition);
return skeletonSpacePosition;
} else {
var parent = bone.Parent;
Vector2 parentLocal = parent.WorldToLocal(skeletonSpacePosition);
bone.SetLocalPosition(parentLocal);
return parentLocal;
}
}
#endregion
#region Attachments
public static Material GetMaterial (this Attachment a) {
object rendererObject = null;
var renderableAttachment = a as IHasRendererObject;
if (renderableAttachment != null)
rendererObject = renderableAttachment.RendererObject;
if (rendererObject == null)
return null;
#if SPINE_TK2D
return (rendererObject.GetType() == typeof(Material)) ? (Material)rendererObject : (Material)((AtlasRegion)rendererObject).page.rendererObject;
#else
return (Material)((AtlasRegion)rendererObject).page.rendererObject;
#endif
}
/// <summary>Fills a Vector2 buffer with local vertices.</summary>
/// <param name="va">The VertexAttachment</param>
/// <param name="slot">Slot where the attachment belongs.</param>
/// <param name="buffer">Correctly-sized buffer. Use attachment's .WorldVerticesLength to get the correct size. If null, a new Vector2[] of the correct size will be allocated.</param>
public static Vector2[] GetLocalVertices (this VertexAttachment va, Slot slot, Vector2[] buffer) {
int floatsCount = va.WorldVerticesLength;
int bufferTargetSize = floatsCount >> 1;
buffer = buffer ?? new Vector2[bufferTargetSize];
if (buffer.Length < bufferTargetSize) throw new System.ArgumentException(string.Format("Vector2 buffer too small. {0} requires an array of size {1}. Use the attachment's .WorldVerticesLength to get the correct size.", va.Name, floatsCount), "buffer");
if (va.Bones == null && slot.Deform.Count == 0) {
var localVerts = va.Vertices;
for (int i = 0; i < bufferTargetSize; i++) {
int j = i * 2;
buffer[i] = new Vector2(localVerts[j], localVerts[j + 1]);
}
} else {
var floats = new float[floatsCount];
va.ComputeWorldVertices(slot, floats);
Bone sb = slot.Bone;
float ia, ib, ic, id, bwx = sb.WorldX, bwy = sb.WorldY;
sb.GetWorldToLocalMatrix(out ia, out ib, out ic, out id);
for (int i = 0; i < bufferTargetSize; i++) {
int j = i * 2;
float x = floats[j] - bwx, y = floats[j + 1] - bwy;
buffer[i] = new Vector2(x * ia + y * ib, x * ic + y * id);
}
}
return buffer;
}
/// <summary>Calculates world vertices and fills a Vector2 buffer.</summary>
/// <param name="a">The VertexAttachment</param>
/// <param name="slot">Slot where the attachment belongs.</param>
/// <param name="buffer">Correctly-sized buffer. Use attachment's .WorldVerticesLength to get the correct size. If null, a new Vector2[] of the correct size will be allocated.</param>
public static Vector2[] GetWorldVertices (this VertexAttachment a, Slot slot, Vector2[] buffer) {
int worldVertsLength = a.WorldVerticesLength;
int bufferTargetSize = worldVertsLength >> 1;
buffer = buffer ?? new Vector2[bufferTargetSize];
if (buffer.Length < bufferTargetSize) throw new System.ArgumentException(string.Format("Vector2 buffer too small. {0} requires an array of size {1}. Use the attachment's .WorldVerticesLength to get the correct size.", a.Name, worldVertsLength), "buffer");
var floats = new float[worldVertsLength];
a.ComputeWorldVertices(slot, floats);
for (int i = 0, n = worldVertsLength >> 1; i < n; i++) {
int j = i * 2;
buffer[i] = new Vector2(floats[j], floats[j + 1]);
}
return buffer;
}
/// <summary>Gets the PointAttachment's Unity World position using its Spine GameObject Transform.</summary>
public static Vector3 GetWorldPosition (this PointAttachment attachment, Slot slot, Transform spineGameObjectTransform) {
Vector3 skeletonSpacePosition;
skeletonSpacePosition.z = 0;
attachment.ComputeWorldPosition(slot.Bone, out skeletonSpacePosition.x, out skeletonSpacePosition.y);
return spineGameObjectTransform.TransformPoint(skeletonSpacePosition);
}
/// <summary>Gets the PointAttachment's Unity World position using its Spine GameObject Transform.</summary>
public static Vector3 GetWorldPosition (this PointAttachment attachment, Bone bone, Transform spineGameObjectTransform) {
Vector3 skeletonSpacePosition;
skeletonSpacePosition.z = 0;
attachment.ComputeWorldPosition(bone, out skeletonSpacePosition.x, out skeletonSpacePosition.y);
return spineGameObjectTransform.TransformPoint(skeletonSpacePosition);
}
#endregion
}
}
namespace Spine {
using System;
public struct BoneMatrix {
public float a, b, c, d, x, y;
/// <summary>Recursively calculates a worldspace bone matrix based on BoneData.</summary>
public static BoneMatrix CalculateSetupWorld (BoneData boneData) {
if (boneData == null)
return default(BoneMatrix);
// End condition: isRootBone
if (boneData.Parent == null)
return GetInheritedInternal(boneData, default(BoneMatrix));
BoneMatrix result = CalculateSetupWorld(boneData.Parent);
return GetInheritedInternal(boneData, result);
}
static BoneMatrix GetInheritedInternal (BoneData boneData, BoneMatrix parentMatrix) {
var parent = boneData.Parent;
if (parent == null) return new BoneMatrix(boneData); // isRootBone
float pa = parentMatrix.a, pb = parentMatrix.b, pc = parentMatrix.c, pd = parentMatrix.d;
BoneMatrix result = default(BoneMatrix);
result.x = pa * boneData.X + pb * boneData.Y + parentMatrix.x;
result.y = pc * boneData.X + pd * boneData.Y + parentMatrix.y;
switch (boneData.TransformMode) {
case TransformMode.Normal: {
float rotationY = boneData.Rotation + 90 + boneData.ShearY;
float la = MathUtils.CosDeg(boneData.Rotation + boneData.ShearX) * boneData.ScaleX;
float lb = MathUtils.CosDeg(rotationY) * boneData.ScaleY;
float lc = MathUtils.SinDeg(boneData.Rotation + boneData.ShearX) * boneData.ScaleX;
float ld = MathUtils.SinDeg(rotationY) * boneData.ScaleY;
result.a = pa * la + pb * lc;
result.b = pa * lb + pb * ld;
result.c = pc * la + pd * lc;
result.d = pc * lb + pd * ld;
break;
}
case TransformMode.OnlyTranslation: {
float rotationY = boneData.Rotation + 90 + boneData.ShearY;
result.a = MathUtils.CosDeg(boneData.Rotation + boneData.ShearX) * boneData.ScaleX;
result.b = MathUtils.CosDeg(rotationY) * boneData.ScaleY;
result.c = MathUtils.SinDeg(boneData.Rotation + boneData.ShearX) * boneData.ScaleX;
result.d = MathUtils.SinDeg(rotationY) * boneData.ScaleY;
break;
}
case TransformMode.NoRotationOrReflection: {
float s = pa * pa + pc * pc, prx;
if (s > 0.0001f) {
s = Math.Abs(pa * pd - pb * pc) / s;
pb = pc * s;
pd = pa * s;
prx = MathUtils.Atan2(pc, pa) * MathUtils.RadDeg;
} else {
pa = 0;
pc = 0;
prx = 90 - MathUtils.Atan2(pd, pb) * MathUtils.RadDeg;
}
float rx = boneData.Rotation + boneData.ShearX - prx;
float ry = boneData.Rotation + boneData.ShearY - prx + 90;
float la = MathUtils.CosDeg(rx) * boneData.ScaleX;
float lb = MathUtils.CosDeg(ry) * boneData.ScaleY;
float lc = MathUtils.SinDeg(rx) * boneData.ScaleX;
float ld = MathUtils.SinDeg(ry) * boneData.ScaleY;
result.a = pa * la - pb * lc;
result.b = pa * lb - pb * ld;
result.c = pc * la + pd * lc;
result.d = pc * lb + pd * ld;
break;
}
case TransformMode.NoScale:
case TransformMode.NoScaleOrReflection: {
float cos = MathUtils.CosDeg(boneData.Rotation), sin = MathUtils.SinDeg(boneData.Rotation);
float za = pa * cos + pb * sin;
float zc = pc * cos + pd * sin;
float s = (float)Math.Sqrt(za * za + zc * zc);
if (s > 0.00001f)
s = 1 / s;
za *= s;
zc *= s;
s = (float)Math.Sqrt(za * za + zc * zc);
float r = MathUtils.PI / 2 + MathUtils.Atan2(zc, za);
float zb = MathUtils.Cos(r) * s;
float zd = MathUtils.Sin(r) * s;
float la = MathUtils.CosDeg(boneData.ShearX) * boneData.ScaleX;
float lb = MathUtils.CosDeg(90 + boneData.ShearY) * boneData.ScaleY;
float lc = MathUtils.SinDeg(boneData.ShearX) * boneData.ScaleX;
float ld = MathUtils.SinDeg(90 + boneData.ShearY) * boneData.ScaleY;
if (boneData.TransformMode != TransformMode.NoScaleOrReflection ? pa * pd - pb * pc < 0 : false) {
zb = -zb;
zd = -zd;
}
result.a = za * la + zb * lc;
result.b = za * lb + zb * ld;
result.c = zc * la + zd * lc;
result.d = zc * lb + zd * ld;
break;
}
}
return result;
}
/// <summary>Constructor for a local bone matrix based on Setup Pose BoneData.</summary>
public BoneMatrix (BoneData boneData) {
float rotationY = boneData.Rotation + 90 + boneData.ShearY;
float rotationX = boneData.Rotation + boneData.ShearX;
a = MathUtils.CosDeg(rotationX) * boneData.ScaleX;
c = MathUtils.SinDeg(rotationX) * boneData.ScaleX;
b = MathUtils.CosDeg(rotationY) * boneData.ScaleY;
d = MathUtils.SinDeg(rotationY) * boneData.ScaleY;
x = boneData.X;
y = boneData.Y;
}
/// <summary>Constructor for a local bone matrix based on a bone instance's current pose.</summary>
public BoneMatrix (Bone bone) {
float rotationY = bone.Rotation + 90 + bone.ShearY;
float rotationX = bone.Rotation + bone.ShearX;
a = MathUtils.CosDeg(rotationX) * bone.ScaleX;
c = MathUtils.SinDeg(rotationX) * bone.ScaleX;
b = MathUtils.CosDeg(rotationY) * bone.ScaleY;
d = MathUtils.SinDeg(rotationY) * bone.ScaleY;
x = bone.X;
y = bone.Y;
}
public BoneMatrix TransformMatrix (BoneMatrix local) {
return new BoneMatrix {
a = this.a * local.a + this.b * local.c,
b = this.a * local.b + this.b * local.d,
c = this.c * local.a + this.d * local.c,
d = this.c * local.b + this.d * local.d,
x = this.a * local.x + this.b * local.y + this.x,
y = this.c * local.x + this.d * local.y + this.y
};
}
}
public static class SpineSkeletonExtensions {
public static bool IsWeighted (this VertexAttachment va) {
return va.Bones != null && va.Bones.Length > 0;
}
#region Transform Modes
public static bool InheritsRotation (this TransformMode mode) {
const int RotationBit = 0;
return ((int)mode & (1U << RotationBit)) == 0;
}
public static bool InheritsScale (this TransformMode mode) {
const int ScaleBit = 1;
return ((int)mode & (1U << ScaleBit)) == 0;
}
#endregion
}
}

View File

@@ -1,105 +1,105 @@
/******************************************************************************
* 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.
*****************************************************************************/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Spine.Unity.AnimationTools {
public static class TimelineExtensions {
/// <summary>Evaluates the resulting value of a TranslateTimeline at a given time.
/// SkeletonData can be accessed from Skeleton.Data or from SkeletonDataAsset.GetSkeletonData.
/// If no SkeletonData is given, values are returned as difference to setup pose
/// instead of absolute values.</summary>
public static Vector2 Evaluate (this TranslateTimeline timeline, float time, SkeletonData skeletonData = null) {
if (time < timeline.Frames[0]) return Vector2.zero;
float x, y;
timeline.GetCurveValue(out x, out y, time);
if (skeletonData == null) {
return new Vector2(x, y);
} else {
BoneData boneData = skeletonData.Bones.Items[timeline.BoneIndex];
return new Vector2(boneData.X + x, boneData.Y + y);
}
}
/// <summary>Evaluates the resulting value of a pair of split translate timelines at a given time.
/// SkeletonData can be accessed from Skeleton.Data or from SkeletonDataAsset.GetSkeletonData.
/// If no SkeletonData is given, values are returned as difference to setup pose
/// instead of absolute values.</summary>
public static Vector2 Evaluate (TranslateXTimeline xTimeline, TranslateYTimeline yTimeline,
float time, SkeletonData skeletonData = null) {
float x = 0, y = 0;
if (xTimeline != null && time > xTimeline.Frames[0]) x = xTimeline.GetCurveValue(time);
if (yTimeline != null && time > yTimeline.Frames[0]) y = yTimeline.GetCurveValue(time);
if (skeletonData == null) {
return new Vector2(x, y);
} else {
var bonesItems = skeletonData.Bones.Items;
BoneData boneDataX = bonesItems[xTimeline.BoneIndex];
BoneData boneDataY = bonesItems[yTimeline.BoneIndex];
return new Vector2(boneDataX.X + x, boneDataY.Y + y);
}
}
/// <summary>Gets the translate timeline for a given boneIndex.
/// You can get the boneIndex using SkeletonData.FindBoneIndex.
/// The root bone is always boneIndex 0.
/// This will return null if a TranslateTimeline is not found.</summary>
public static TranslateTimeline FindTranslateTimelineForBone (this Animation a, int boneIndex) {
foreach (var timeline in a.Timelines) {
if (timeline.GetType().IsSubclassOf(typeof(TranslateTimeline)))
continue;
var translateTimeline = timeline as TranslateTimeline;
if (translateTimeline != null && translateTimeline.BoneIndex == boneIndex)
return translateTimeline;
}
return null;
}
/// <summary>Gets the IBoneTimeline timeline of a given type for a given boneIndex.
/// You can get the boneIndex using SkeletonData.FindBoneIndex.
/// The root bone is always boneIndex 0.
/// This will return null if a timeline of the given type is not found.</summary>
public static T FindTimelineForBone<T> (this Animation a, int boneIndex) where T : class, IBoneTimeline {
foreach (var timeline in a.Timelines) {
T translateTimeline = timeline as T;
if (translateTimeline != null && translateTimeline.BoneIndex == boneIndex)
return translateTimeline;
}
return null;
}
}
}
/******************************************************************************
* 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.
*****************************************************************************/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Spine.Unity.AnimationTools {
public static class TimelineExtensions {
/// <summary>Evaluates the resulting value of a TranslateTimeline at a given time.
/// SkeletonData can be accessed from Skeleton.Data or from SkeletonDataAsset.GetSkeletonData.
/// If no SkeletonData is given, values are returned as difference to setup pose
/// instead of absolute values.</summary>
public static Vector2 Evaluate (this TranslateTimeline timeline, float time, SkeletonData skeletonData = null) {
if (time < timeline.Frames[0]) return Vector2.zero;
float x, y;
timeline.GetCurveValue(out x, out y, time);
if (skeletonData == null) {
return new Vector2(x, y);
} else {
BoneData boneData = skeletonData.Bones.Items[timeline.BoneIndex];
return new Vector2(boneData.X + x, boneData.Y + y);
}
}
/// <summary>Evaluates the resulting value of a pair of split translate timelines at a given time.
/// SkeletonData can be accessed from Skeleton.Data or from SkeletonDataAsset.GetSkeletonData.
/// If no SkeletonData is given, values are returned as difference to setup pose
/// instead of absolute values.</summary>
public static Vector2 Evaluate (TranslateXTimeline xTimeline, TranslateYTimeline yTimeline,
float time, SkeletonData skeletonData = null) {
float x = 0, y = 0;
if (xTimeline != null && time > xTimeline.Frames[0]) x = xTimeline.GetCurveValue(time);
if (yTimeline != null && time > yTimeline.Frames[0]) y = yTimeline.GetCurveValue(time);
if (skeletonData == null) {
return new Vector2(x, y);
} else {
var bonesItems = skeletonData.Bones.Items;
BoneData boneDataX = bonesItems[xTimeline.BoneIndex];
BoneData boneDataY = bonesItems[yTimeline.BoneIndex];
return new Vector2(boneDataX.X + x, boneDataY.Y + y);
}
}
/// <summary>Gets the translate timeline for a given boneIndex.
/// You can get the boneIndex using SkeletonData.FindBoneIndex.
/// The root bone is always boneIndex 0.
/// This will return null if a TranslateTimeline is not found.</summary>
public static TranslateTimeline FindTranslateTimelineForBone (this Animation a, int boneIndex) {
foreach (var timeline in a.Timelines) {
if (timeline.GetType().IsSubclassOf(typeof(TranslateTimeline)))
continue;
var translateTimeline = timeline as TranslateTimeline;
if (translateTimeline != null && translateTimeline.BoneIndex == boneIndex)
return translateTimeline;
}
return null;
}
/// <summary>Gets the IBoneTimeline timeline of a given type for a given boneIndex.
/// You can get the boneIndex using SkeletonData.FindBoneIndex.
/// The root bone is always boneIndex 0.
/// This will return null if a timeline of the given type is not found.</summary>
public static T FindTimelineForBone<T> (this Animation a, int boneIndex) where T : class, IBoneTimeline {
foreach (var timeline in a.Timelines) {
T translateTimeline = timeline as T;
if (translateTimeline != null && translateTimeline.BoneIndex == boneIndex)
return translateTimeline;
}
return null;
}
}
}

View File

@@ -1,106 +1,106 @@
/******************************************************************************
* 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.
*****************************************************************************/
using System;
using System.Collections;
using UnityEngine;
namespace Spine.Unity {
/// <summary>
/// Use this as a condition-blocking yield instruction for Unity Coroutines.
/// The routine will pause until the AnimationState.TrackEntry fires any of the
/// configured events.
/// <p/>
/// See the <see cref="http://esotericsoftware.com/spine-unity-events">Spine Unity Events documentation page</see>
/// and <see cref="http://esotericsoftware.com/spine-api-reference#AnimationStateListener"/>
/// for more information on when track events will be triggered.</summary>
public class WaitForSpineAnimation : IEnumerator {
[Flags]
public enum AnimationEventTypes {
Start = 1,
Interrupt = 2,
End = 4,
Dispose = 8,
Complete = 16
}
bool m_WasFired = false;
public WaitForSpineAnimation (Spine.TrackEntry trackEntry, AnimationEventTypes eventsToWaitFor) {
SafeSubscribe(trackEntry, eventsToWaitFor);
}
#region Reuse
/// <summary>
/// One optimization high-frequency YieldInstruction returns is to cache instances to minimize GC pressure.
/// Use NowWaitFor to reuse the same instance of WaitForSpineAnimationComplete.</summary>
public WaitForSpineAnimation NowWaitFor (Spine.TrackEntry trackEntry, AnimationEventTypes eventsToWaitFor) {
SafeSubscribe(trackEntry, eventsToWaitFor);
return this;
}
#endregion
#region IEnumerator
bool IEnumerator.MoveNext () {
if (m_WasFired) {
((IEnumerator)this).Reset(); // auto-reset for YieldInstruction reuse
return false;
}
return true;
}
void IEnumerator.Reset () { m_WasFired = false; }
object IEnumerator.Current { get { return null; } }
#endregion
protected void SafeSubscribe (Spine.TrackEntry trackEntry, AnimationEventTypes eventsToWaitFor) {
if (trackEntry == null) {
// Break immediately if trackEntry is null.
Debug.LogWarning("TrackEntry was null. Coroutine will continue immediately.");
m_WasFired = true;
} else {
if ((eventsToWaitFor & AnimationEventTypes.Start) != 0)
trackEntry.Start += HandleComplete;
if ((eventsToWaitFor & AnimationEventTypes.Interrupt) != 0)
trackEntry.Interrupt += HandleComplete;
if ((eventsToWaitFor & AnimationEventTypes.End) != 0)
trackEntry.End += HandleComplete;
if ((eventsToWaitFor & AnimationEventTypes.Dispose) != 0)
trackEntry.Dispose += HandleComplete;
if ((eventsToWaitFor & AnimationEventTypes.Complete) != 0)
trackEntry.Complete += HandleComplete;
}
}
void HandleComplete (TrackEntry trackEntry) {
m_WasFired = true;
}
}
}
/******************************************************************************
* 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.
*****************************************************************************/
using System;
using System.Collections;
using UnityEngine;
namespace Spine.Unity {
/// <summary>
/// Use this as a condition-blocking yield instruction for Unity Coroutines.
/// The routine will pause until the AnimationState.TrackEntry fires any of the
/// configured events.
/// <p/>
/// See the <see cref="http://esotericsoftware.com/spine-unity-events">Spine Unity Events documentation page</see>
/// and <see cref="http://esotericsoftware.com/spine-api-reference#AnimationStateListener"/>
/// for more information on when track events will be triggered.</summary>
public class WaitForSpineAnimation : IEnumerator {
[Flags]
public enum AnimationEventTypes {
Start = 1,
Interrupt = 2,
End = 4,
Dispose = 8,
Complete = 16
}
bool m_WasFired = false;
public WaitForSpineAnimation (Spine.TrackEntry trackEntry, AnimationEventTypes eventsToWaitFor) {
SafeSubscribe(trackEntry, eventsToWaitFor);
}
#region Reuse
/// <summary>
/// One optimization high-frequency YieldInstruction returns is to cache instances to minimize GC pressure.
/// Use NowWaitFor to reuse the same instance of WaitForSpineAnimationComplete.</summary>
public WaitForSpineAnimation NowWaitFor (Spine.TrackEntry trackEntry, AnimationEventTypes eventsToWaitFor) {
SafeSubscribe(trackEntry, eventsToWaitFor);
return this;
}
#endregion
#region IEnumerator
bool IEnumerator.MoveNext () {
if (m_WasFired) {
((IEnumerator)this).Reset(); // auto-reset for YieldInstruction reuse
return false;
}
return true;
}
void IEnumerator.Reset () { m_WasFired = false; }
object IEnumerator.Current { get { return null; } }
#endregion
protected void SafeSubscribe (Spine.TrackEntry trackEntry, AnimationEventTypes eventsToWaitFor) {
if (trackEntry == null) {
// Break immediately if trackEntry is null.
Debug.LogWarning("TrackEntry was null. Coroutine will continue immediately.");
m_WasFired = true;
} else {
if ((eventsToWaitFor & AnimationEventTypes.Start) != 0)
trackEntry.Start += HandleComplete;
if ((eventsToWaitFor & AnimationEventTypes.Interrupt) != 0)
trackEntry.Interrupt += HandleComplete;
if ((eventsToWaitFor & AnimationEventTypes.End) != 0)
trackEntry.End += HandleComplete;
if ((eventsToWaitFor & AnimationEventTypes.Dispose) != 0)
trackEntry.Dispose += HandleComplete;
if ((eventsToWaitFor & AnimationEventTypes.Complete) != 0)
trackEntry.Complete += HandleComplete;
}
}
void HandleComplete (TrackEntry trackEntry) {
m_WasFired = true;
}
}
}

View File

@@ -1,61 +1,61 @@
/******************************************************************************
* 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.
*****************************************************************************/
using Spine;
using System.Collections;
using UnityEngine;
namespace Spine.Unity {
/// <summary>
/// Use this as a condition-blocking yield instruction for Unity Coroutines.
/// The routine will pause until the AnimationState.TrackEntry fires its Complete event.
/// It can be configured to trigger on the End event as well to cover interruption.
/// <p/>
/// See the <see cref="http://esotericsoftware.com/spine-unity-events">Spine Unity Events documentation page</see>
/// and <see cref="http://esotericsoftware.com/spine-api-reference#AnimationStateListener"/>
/// for more information on when track events will be triggered.</summary>
public class WaitForSpineAnimationComplete : WaitForSpineAnimation, IEnumerator {
public WaitForSpineAnimationComplete (Spine.TrackEntry trackEntry, bool includeEndEvent = false) :
base(trackEntry,
includeEndEvent ? (AnimationEventTypes.Complete | AnimationEventTypes.End) : AnimationEventTypes.Complete) {
}
#region Reuse
/// <summary>
/// One optimization high-frequency YieldInstruction returns is to cache instances to minimize GC pressure.
/// Use NowWaitFor to reuse the same instance of WaitForSpineAnimationComplete.</summary>
public WaitForSpineAnimationComplete NowWaitFor (Spine.TrackEntry trackEntry, bool includeEndEvent = false) {
SafeSubscribe(trackEntry,
includeEndEvent ? (AnimationEventTypes.Complete | AnimationEventTypes.End) : AnimationEventTypes.Complete);
return this;
}
#endregion
}
}
/******************************************************************************
* 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.
*****************************************************************************/
using Spine;
using System.Collections;
using UnityEngine;
namespace Spine.Unity {
/// <summary>
/// Use this as a condition-blocking yield instruction for Unity Coroutines.
/// The routine will pause until the AnimationState.TrackEntry fires its Complete event.
/// It can be configured to trigger on the End event as well to cover interruption.
/// <p/>
/// See the <see cref="http://esotericsoftware.com/spine-unity-events">Spine Unity Events documentation page</see>
/// and <see cref="http://esotericsoftware.com/spine-api-reference#AnimationStateListener"/>
/// for more information on when track events will be triggered.</summary>
public class WaitForSpineAnimationComplete : WaitForSpineAnimation, IEnumerator {
public WaitForSpineAnimationComplete (Spine.TrackEntry trackEntry, bool includeEndEvent = false) :
base(trackEntry,
includeEndEvent ? (AnimationEventTypes.Complete | AnimationEventTypes.End) : AnimationEventTypes.Complete) {
}
#region Reuse
/// <summary>
/// One optimization high-frequency YieldInstruction returns is to cache instances to minimize GC pressure.
/// Use NowWaitFor to reuse the same instance of WaitForSpineAnimationComplete.</summary>
public WaitForSpineAnimationComplete NowWaitFor (Spine.TrackEntry trackEntry, bool includeEndEvent = false) {
SafeSubscribe(trackEntry,
includeEndEvent ? (AnimationEventTypes.Complete | AnimationEventTypes.End) : AnimationEventTypes.Complete);
return this;
}
#endregion
}
}

View File

@@ -1,58 +1,58 @@
/******************************************************************************
* 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.
*****************************************************************************/
using Spine;
using System.Collections;
using UnityEngine;
namespace Spine.Unity {
/// <summary>
/// Use this as a condition-blocking yield instruction for Unity Coroutines.
/// The routine will pause until the AnimationState.TrackEntry fires its End event.
/// <p/>
/// See the <see cref="http://esotericsoftware.com/spine-unity-events">Spine Unity Events documentation page</see>
/// and <see cref="http://esotericsoftware.com/spine-api-reference#AnimationStateListener"/>
/// for more information on when track events will be triggered.</summary>
public class WaitForSpineAnimationEnd : WaitForSpineAnimation, IEnumerator {
public WaitForSpineAnimationEnd (Spine.TrackEntry trackEntry) :
base(trackEntry, AnimationEventTypes.End) {
}
#region Reuse
/// <summary>
/// One optimization high-frequency YieldInstruction returns is to cache instances to minimize GC pressure.
/// Use NowWaitFor to reuse the same instance of WaitForSpineAnimationComplete.</summary>
public WaitForSpineAnimationEnd NowWaitFor (Spine.TrackEntry trackEntry) {
SafeSubscribe(trackEntry, AnimationEventTypes.End);
return this;
}
#endregion
}
}
/******************************************************************************
* 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.
*****************************************************************************/
using Spine;
using System.Collections;
using UnityEngine;
namespace Spine.Unity {
/// <summary>
/// Use this as a condition-blocking yield instruction for Unity Coroutines.
/// The routine will pause until the AnimationState.TrackEntry fires its End event.
/// <p/>
/// See the <see cref="http://esotericsoftware.com/spine-unity-events">Spine Unity Events documentation page</see>
/// and <see cref="http://esotericsoftware.com/spine-api-reference#AnimationStateListener"/>
/// for more information on when track events will be triggered.</summary>
public class WaitForSpineAnimationEnd : WaitForSpineAnimation, IEnumerator {
public WaitForSpineAnimationEnd (Spine.TrackEntry trackEntry) :
base(trackEntry, AnimationEventTypes.End) {
}
#region Reuse
/// <summary>
/// One optimization high-frequency YieldInstruction returns is to cache instances to minimize GC pressure.
/// Use NowWaitFor to reuse the same instance of WaitForSpineAnimationComplete.</summary>
public WaitForSpineAnimationEnd NowWaitFor (Spine.TrackEntry trackEntry) {
SafeSubscribe(trackEntry, AnimationEventTypes.End);
return this;
}
#endregion
}
}

View File

@@ -1,159 +1,159 @@
/******************************************************************************
* 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.
*****************************************************************************/
using Spine;
using System.Collections;
using UnityEngine;
namespace Spine.Unity {
/// <summary>
/// Use this as a condition-blocking yield instruction for Unity Coroutines.
/// The routine will pause until the AnimationState fires an event matching the given event name or EventData reference.</summary>
public class WaitForSpineEvent : IEnumerator {
Spine.EventData m_TargetEvent;
string m_EventName;
Spine.AnimationState m_AnimationState;
bool m_WasFired = false;
bool m_unsubscribeAfterFiring = false;
#region Constructors
void Subscribe (Spine.AnimationState state, Spine.EventData eventDataReference, bool unsubscribe) {
if (state == null) {
Debug.LogWarning("AnimationState argument was null. Coroutine will continue immediately.");
m_WasFired = true;
return;
} else if (eventDataReference == null) {
Debug.LogWarning("eventDataReference argument was null. Coroutine will continue immediately.");
m_WasFired = true;
return;
}
m_AnimationState = state;
m_TargetEvent = eventDataReference;
state.Event += HandleAnimationStateEvent;
m_unsubscribeAfterFiring = unsubscribe;
}
void SubscribeByName (Spine.AnimationState state, string eventName, bool unsubscribe) {
if (state == null) {
Debug.LogWarning("AnimationState argument was null. Coroutine will continue immediately.");
m_WasFired = true;
return;
} else if (string.IsNullOrEmpty(eventName)) {
Debug.LogWarning("eventName argument was null. Coroutine will continue immediately.");
m_WasFired = true;
return;
}
m_AnimationState = state;
m_EventName = eventName;
state.Event += HandleAnimationStateEventByName;
m_unsubscribeAfterFiring = unsubscribe;
}
public WaitForSpineEvent (Spine.AnimationState state, Spine.EventData eventDataReference, bool unsubscribeAfterFiring = true) {
Subscribe(state, eventDataReference, unsubscribeAfterFiring);
}
public WaitForSpineEvent (SkeletonAnimation skeletonAnimation, Spine.EventData eventDataReference, bool unsubscribeAfterFiring = true) {
// If skeletonAnimation is invalid, its state will be null. Subscribe handles null states just fine.
Subscribe(skeletonAnimation.state, eventDataReference, unsubscribeAfterFiring);
}
public WaitForSpineEvent (Spine.AnimationState state, string eventName, bool unsubscribeAfterFiring = true) {
SubscribeByName(state, eventName, unsubscribeAfterFiring);
}
public WaitForSpineEvent (SkeletonAnimation skeletonAnimation, string eventName, bool unsubscribeAfterFiring = true) {
// If skeletonAnimation is invalid, its state will be null. Subscribe handles null states just fine.
SubscribeByName(skeletonAnimation.state, eventName, unsubscribeAfterFiring);
}
#endregion
#region Event Handlers
void HandleAnimationStateEventByName (Spine.TrackEntry trackEntry, Spine.Event e) {
m_WasFired |= (e.Data.Name == m_EventName); // Check event name string match.
if (m_WasFired && m_unsubscribeAfterFiring)
m_AnimationState.Event -= HandleAnimationStateEventByName; // Unsubscribe after correct event fires.
}
void HandleAnimationStateEvent (Spine.TrackEntry trackEntry, Spine.Event e) {
m_WasFired |= (e.Data == m_TargetEvent); // Check event data reference match.
if (m_WasFired && m_unsubscribeAfterFiring)
m_AnimationState.Event -= HandleAnimationStateEvent; // Usubscribe after correct event fires.
}
#endregion
#region Reuse
/// <summary>
/// By default, WaitForSpineEvent will unsubscribe from the event immediately after it fires a correct matching event.
/// If you want to reuse this WaitForSpineEvent instance on the same event, you can set this to false.</summary>
public bool WillUnsubscribeAfterFiring { get { return m_unsubscribeAfterFiring; } set { m_unsubscribeAfterFiring = value; } }
public WaitForSpineEvent NowWaitFor (Spine.AnimationState state, Spine.EventData eventDataReference, bool unsubscribeAfterFiring = true) {
((IEnumerator)this).Reset();
Clear(state);
Subscribe(state, eventDataReference, unsubscribeAfterFiring);
return this;
}
public WaitForSpineEvent NowWaitFor (Spine.AnimationState state, string eventName, bool unsubscribeAfterFiring = true) {
((IEnumerator)this).Reset();
Clear(state);
SubscribeByName(state, eventName, unsubscribeAfterFiring);
return this;
}
void Clear (Spine.AnimationState state) {
state.Event -= HandleAnimationStateEvent;
state.Event -= HandleAnimationStateEventByName;
}
#endregion
#region IEnumerator
bool IEnumerator.MoveNext () {
if (m_WasFired) {
((IEnumerator)this).Reset(); // auto-reset for YieldInstruction reuse
return false;
}
return true;
}
void IEnumerator.Reset () { m_WasFired = false; }
object IEnumerator.Current { get { return null; } }
#endregion
}
}
/******************************************************************************
* 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.
*****************************************************************************/
using Spine;
using System.Collections;
using UnityEngine;
namespace Spine.Unity {
/// <summary>
/// Use this as a condition-blocking yield instruction for Unity Coroutines.
/// The routine will pause until the AnimationState fires an event matching the given event name or EventData reference.</summary>
public class WaitForSpineEvent : IEnumerator {
Spine.EventData m_TargetEvent;
string m_EventName;
Spine.AnimationState m_AnimationState;
bool m_WasFired = false;
bool m_unsubscribeAfterFiring = false;
#region Constructors
void Subscribe (Spine.AnimationState state, Spine.EventData eventDataReference, bool unsubscribe) {
if (state == null) {
Debug.LogWarning("AnimationState argument was null. Coroutine will continue immediately.");
m_WasFired = true;
return;
} else if (eventDataReference == null) {
Debug.LogWarning("eventDataReference argument was null. Coroutine will continue immediately.");
m_WasFired = true;
return;
}
m_AnimationState = state;
m_TargetEvent = eventDataReference;
state.Event += HandleAnimationStateEvent;
m_unsubscribeAfterFiring = unsubscribe;
}
void SubscribeByName (Spine.AnimationState state, string eventName, bool unsubscribe) {
if (state == null) {
Debug.LogWarning("AnimationState argument was null. Coroutine will continue immediately.");
m_WasFired = true;
return;
} else if (string.IsNullOrEmpty(eventName)) {
Debug.LogWarning("eventName argument was null. Coroutine will continue immediately.");
m_WasFired = true;
return;
}
m_AnimationState = state;
m_EventName = eventName;
state.Event += HandleAnimationStateEventByName;
m_unsubscribeAfterFiring = unsubscribe;
}
public WaitForSpineEvent (Spine.AnimationState state, Spine.EventData eventDataReference, bool unsubscribeAfterFiring = true) {
Subscribe(state, eventDataReference, unsubscribeAfterFiring);
}
public WaitForSpineEvent (SkeletonAnimation skeletonAnimation, Spine.EventData eventDataReference, bool unsubscribeAfterFiring = true) {
// If skeletonAnimation is invalid, its state will be null. Subscribe handles null states just fine.
Subscribe(skeletonAnimation.state, eventDataReference, unsubscribeAfterFiring);
}
public WaitForSpineEvent (Spine.AnimationState state, string eventName, bool unsubscribeAfterFiring = true) {
SubscribeByName(state, eventName, unsubscribeAfterFiring);
}
public WaitForSpineEvent (SkeletonAnimation skeletonAnimation, string eventName, bool unsubscribeAfterFiring = true) {
// If skeletonAnimation is invalid, its state will be null. Subscribe handles null states just fine.
SubscribeByName(skeletonAnimation.state, eventName, unsubscribeAfterFiring);
}
#endregion
#region Event Handlers
void HandleAnimationStateEventByName (Spine.TrackEntry trackEntry, Spine.Event e) {
m_WasFired |= (e.Data.Name == m_EventName); // Check event name string match.
if (m_WasFired && m_unsubscribeAfterFiring)
m_AnimationState.Event -= HandleAnimationStateEventByName; // Unsubscribe after correct event fires.
}
void HandleAnimationStateEvent (Spine.TrackEntry trackEntry, Spine.Event e) {
m_WasFired |= (e.Data == m_TargetEvent); // Check event data reference match.
if (m_WasFired && m_unsubscribeAfterFiring)
m_AnimationState.Event -= HandleAnimationStateEvent; // Usubscribe after correct event fires.
}
#endregion
#region Reuse
/// <summary>
/// By default, WaitForSpineEvent will unsubscribe from the event immediately after it fires a correct matching event.
/// If you want to reuse this WaitForSpineEvent instance on the same event, you can set this to false.</summary>
public bool WillUnsubscribeAfterFiring { get { return m_unsubscribeAfterFiring; } set { m_unsubscribeAfterFiring = value; } }
public WaitForSpineEvent NowWaitFor (Spine.AnimationState state, Spine.EventData eventDataReference, bool unsubscribeAfterFiring = true) {
((IEnumerator)this).Reset();
Clear(state);
Subscribe(state, eventDataReference, unsubscribeAfterFiring);
return this;
}
public WaitForSpineEvent NowWaitFor (Spine.AnimationState state, string eventName, bool unsubscribeAfterFiring = true) {
((IEnumerator)this).Reset();
Clear(state);
SubscribeByName(state, eventName, unsubscribeAfterFiring);
return this;
}
void Clear (Spine.AnimationState state) {
state.Event -= HandleAnimationStateEvent;
state.Event -= HandleAnimationStateEventByName;
}
#endregion
#region IEnumerator
bool IEnumerator.MoveNext () {
if (m_WasFired) {
((IEnumerator)this).Reset(); // auto-reset for YieldInstruction reuse
return false;
}
return true;
}
void IEnumerator.Reset () { m_WasFired = false; }
object IEnumerator.Current { get { return null; } }
#endregion
}
}

View File

@@ -1,85 +1,85 @@
/******************************************************************************
* 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.
*****************************************************************************/
using Spine;
using System.Collections;
using UnityEngine;
namespace Spine.Unity {
/// <summary>
/// Use this as a condition-blocking yield instruction for Unity Coroutines.
/// The routine will pause until the AnimationState.TrackEntry fires its End event.</summary>
public class WaitForSpineTrackEntryEnd : IEnumerator {
bool m_WasFired = false;
public WaitForSpineTrackEntryEnd (Spine.TrackEntry trackEntry) {
SafeSubscribe(trackEntry);
}
void HandleEnd (TrackEntry trackEntry) {
m_WasFired = true;
}
void SafeSubscribe (Spine.TrackEntry trackEntry) {
if (trackEntry == null) {
// Break immediately if trackEntry is null.
Debug.LogWarning("TrackEntry was null. Coroutine will continue immediately.");
m_WasFired = true;
} else {
trackEntry.End += HandleEnd;
}
}
#region Reuse
/// <summary>
/// One optimization high-frequency YieldInstruction returns is to cache instances to minimize GC pressure.
/// Use NowWaitFor to reuse the same instance of WaitForSpineAnimationEnd.</summary>
public WaitForSpineTrackEntryEnd NowWaitFor (Spine.TrackEntry trackEntry) {
SafeSubscribe(trackEntry);
return this;
}
#endregion
#region IEnumerator
bool IEnumerator.MoveNext () {
if (m_WasFired) {
((IEnumerator)this).Reset(); // auto-reset for YieldInstruction reuse
return false;
}
return true;
}
void IEnumerator.Reset () { m_WasFired = false; }
object IEnumerator.Current { get { return null; } }
#endregion
}
}
/******************************************************************************
* 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.
*****************************************************************************/
using Spine;
using System.Collections;
using UnityEngine;
namespace Spine.Unity {
/// <summary>
/// Use this as a condition-blocking yield instruction for Unity Coroutines.
/// The routine will pause until the AnimationState.TrackEntry fires its End event.</summary>
public class WaitForSpineTrackEntryEnd : IEnumerator {
bool m_WasFired = false;
public WaitForSpineTrackEntryEnd (Spine.TrackEntry trackEntry) {
SafeSubscribe(trackEntry);
}
void HandleEnd (TrackEntry trackEntry) {
m_WasFired = true;
}
void SafeSubscribe (Spine.TrackEntry trackEntry) {
if (trackEntry == null) {
// Break immediately if trackEntry is null.
Debug.LogWarning("TrackEntry was null. Coroutine will continue immediately.");
m_WasFired = true;
} else {
trackEntry.End += HandleEnd;
}
}
#region Reuse
/// <summary>
/// One optimization high-frequency YieldInstruction returns is to cache instances to minimize GC pressure.
/// Use NowWaitFor to reuse the same instance of WaitForSpineAnimationEnd.</summary>
public WaitForSpineTrackEntryEnd NowWaitFor (Spine.TrackEntry trackEntry) {
SafeSubscribe(trackEntry);
return this;
}
#endregion
#region IEnumerator
bool IEnumerator.MoveNext () {
if (m_WasFired) {
((IEnumerator)this).Reset(); // auto-reset for YieldInstruction reuse
return false;
}
return true;
}
void IEnumerator.Reset () { m_WasFired = false; }
object IEnumerator.Current { get { return null; } }
#endregion
}
}