imp v34 & v35
This commit is contained in:
23
Spine/Implementations/SpineWrappers/V34/Animation34.cs
Normal file
23
Spine/Implementations/SpineWrappers/V34/Animation34.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using Spine.SpineWrappers;
|
||||
using SpineRuntime34;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V34
|
||||
{
|
||||
internal sealed class Animation34(Animation innerObject) : IAnimation
|
||||
{
|
||||
private readonly Animation _o = innerObject;
|
||||
|
||||
public Animation InnerObject => _o;
|
||||
|
||||
public string Name => _o.Name;
|
||||
|
||||
public float Duration => _o.Duration;
|
||||
|
||||
public override string ToString() => _o.ToString();
|
||||
}
|
||||
}
|
||||
180
Spine/Implementations/SpineWrappers/V34/AnimationState34.cs
Normal file
180
Spine/Implementations/SpineWrappers/V34/AnimationState34.cs
Normal file
@@ -0,0 +1,180 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.SpineWrappers;
|
||||
using SpineRuntime34;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V34
|
||||
{
|
||||
internal sealed class AnimationState34(AnimationState innerObject, SpineObjectData34 data) : IAnimationState
|
||||
{
|
||||
private readonly AnimationState _o = innerObject;
|
||||
private readonly SpineObjectData34 _data = data;
|
||||
|
||||
private readonly Dictionary<TrackEntry, TrackEntry34> _trackEntryPool = [];
|
||||
|
||||
private readonly Dictionary<IAnimationState.TrackEntryDelegate, AnimationState.TrackEntryDelegate> _eventMapping = [];
|
||||
private readonly Dictionary<IAnimationState.TrackEntryDelegate, int> _eventCount = [];
|
||||
|
||||
public AnimationState InnerObject => _o;
|
||||
|
||||
#pragma warning disable CS0067
|
||||
|
||||
// NOTE: 3.4 以下没有这两个事件
|
||||
public event IAnimationState.TrackEntryDelegate? Interrupt;
|
||||
public event IAnimationState.TrackEntryDelegate? Dispose;
|
||||
|
||||
#pragma warning restore CS0067
|
||||
|
||||
|
||||
public event IAnimationState.TrackEntryDelegate? Start
|
||||
{
|
||||
add
|
||||
{
|
||||
if (value is null) return;
|
||||
if (!_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_eventMapping[value] = f = (TrackEntry t) => value(GetTrackEntry(t));
|
||||
_eventCount[value] = 0;
|
||||
}
|
||||
_o.Start += f;
|
||||
_eventCount[value]++;
|
||||
}
|
||||
remove
|
||||
{
|
||||
if (value is null) return;
|
||||
if (_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_o.Start -= f;
|
||||
_eventCount[value]--;
|
||||
if (_eventCount[value] <= 0)
|
||||
{
|
||||
_eventMapping.Remove(value);
|
||||
_eventCount.Remove(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public event IAnimationState.TrackEntryDelegate? End
|
||||
{
|
||||
add
|
||||
{
|
||||
if (value is null) return;
|
||||
if (!_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_eventMapping[value] = f = (TrackEntry t) => value(GetTrackEntry(t));
|
||||
_eventCount[value] = 0;
|
||||
}
|
||||
_o.End += f;
|
||||
_eventCount[value]++;
|
||||
}
|
||||
remove
|
||||
{
|
||||
if (value is null) return;
|
||||
if (_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_o.End -= f;
|
||||
_eventCount[value]--;
|
||||
if (_eventCount[value] <= 0)
|
||||
{
|
||||
_eventMapping.Remove(value);
|
||||
_eventCount.Remove(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public event IAnimationState.TrackEntryDelegate? Complete
|
||||
{
|
||||
add
|
||||
{
|
||||
if (value is null) return;
|
||||
if (!_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_eventMapping[value] = f = (TrackEntry t) => value(GetTrackEntry(t));
|
||||
_eventCount[value] = 0;
|
||||
}
|
||||
_o.Complete += f;
|
||||
_eventCount[value]++;
|
||||
}
|
||||
remove
|
||||
{
|
||||
if (value is null) return;
|
||||
if (_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_o.Complete -= f;
|
||||
_eventCount[value]--;
|
||||
if (_eventCount[value] <= 0)
|
||||
{
|
||||
_eventMapping.Remove(value);
|
||||
_eventCount.Remove(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public float TimeScale { get => _o.TimeScale; set => _o.TimeScale = value; }
|
||||
|
||||
public void Update(float delta) => _o.Update(delta);
|
||||
|
||||
public void Apply(ISkeleton skeleton)
|
||||
{
|
||||
if (skeleton is Skeleton34 skel)
|
||||
{
|
||||
_o.Apply(skel.InnerObject);
|
||||
return;
|
||||
}
|
||||
throw new ArgumentException($"Received {skeleton.GetType().Name}", nameof(skeleton));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取 <see cref="ITrackEntry"/> 对象, 不存在则创建
|
||||
/// </summary>
|
||||
public ITrackEntry GetTrackEntry(TrackEntry trackEntry)
|
||||
{
|
||||
if (!_trackEntryPool.TryGetValue(trackEntry, out var tr))
|
||||
_trackEntryPool[trackEntry] = tr = new(trackEntry, this, _data);
|
||||
return tr;
|
||||
}
|
||||
|
||||
public IEnumerable<ITrackEntry?> IterTracks() => _o.Tracks.Select(t => t is null ? null : GetTrackEntry(t));
|
||||
|
||||
public ITrackEntry? GetCurrent(int index) { var t = _o.GetCurrent(index); return t is null ? null : GetTrackEntry(t); }
|
||||
|
||||
public void ClearTrack(int index) => _o.ClearTrack(index);
|
||||
|
||||
public void ClearTracks() => _o.ClearTracks();
|
||||
|
||||
public ITrackEntry SetAnimation(int trackIndex, string animationName, bool loop)
|
||||
=> GetTrackEntry(_o.SetAnimation(trackIndex, animationName, loop));
|
||||
|
||||
public ITrackEntry SetAnimation(int trackIndex, IAnimation animation, bool loop)
|
||||
{
|
||||
if (animation is Animation34 anime)
|
||||
return GetTrackEntry(_o.SetAnimation(trackIndex, anime.InnerObject, loop));
|
||||
throw new ArgumentException($"Received {animation.GetType().Name}", nameof(animation));
|
||||
}
|
||||
|
||||
public ITrackEntry SetEmptyAnimation(int trackIndex, float mixDuration) => GetTrackEntry(_o.SetEmptyAnimation(trackIndex, mixDuration));
|
||||
|
||||
public void SetEmptyAnimations(float mixDuration) => _o.SetEmptyAnimations(mixDuration);
|
||||
|
||||
public ITrackEntry AddAnimation(int trackIndex, string animationName, bool loop, float delay)
|
||||
=> GetTrackEntry(_o.AddAnimation(trackIndex, animationName, loop, delay));
|
||||
|
||||
public ITrackEntry AddAnimation(int trackIndex, IAnimation animation, bool loop, float delay)
|
||||
{
|
||||
if (animation is Animation34 anime)
|
||||
return GetTrackEntry(_o.AddAnimation(trackIndex, anime.InnerObject, loop, delay));
|
||||
throw new ArgumentException($"Received {animation.GetType().Name}", nameof(animation));
|
||||
}
|
||||
|
||||
public ITrackEntry AddEmptyAnimation(int trackIndex, float mixDuration, float delay)
|
||||
=> GetTrackEntry(_o.AddEmptyAnimation(trackIndex, mixDuration, delay));
|
||||
|
||||
public override string ToString() => _o.ToString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.SpineWrappers;
|
||||
using Spine.SpineWrappers.Attachments;
|
||||
using SpineRuntime34;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V34.Attachments
|
||||
{
|
||||
internal abstract class Attachment34(Attachment innerObject) : IAttachment
|
||||
{
|
||||
private readonly Attachment _o = innerObject;
|
||||
|
||||
public virtual Attachment InnerObject => _o;
|
||||
|
||||
public string Name => _o.Name;
|
||||
|
||||
public abstract int ComputeWorldVertices(ISlot slot, ref float[] worldVertices);
|
||||
|
||||
public override string ToString() => _o.ToString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.SpineWrappers.Attachments;
|
||||
using SpineRuntime34;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V34.Attachments
|
||||
{
|
||||
internal sealed class BoundingBoxAttachment34(BoundingBoxAttachment innerObject) :
|
||||
Attachment34(innerObject),
|
||||
IBoundingBoxAttachment
|
||||
{
|
||||
private readonly BoundingBoxAttachment _o = innerObject;
|
||||
|
||||
public override BoundingBoxAttachment InnerObject => _o;
|
||||
|
||||
public override int ComputeWorldVertices(Spine.SpineWrappers.ISlot slot, ref float[] worldVertices)
|
||||
{
|
||||
if (slot is Slot34 st)
|
||||
{
|
||||
var length = _o.WorldVerticesLength;
|
||||
if (worldVertices.Length < length) worldVertices = new float[length];
|
||||
_o.ComputeWorldVertices(st.InnerObject, worldVertices);
|
||||
return length;
|
||||
}
|
||||
throw new ArgumentException($"Invalid slot type. Expected {nameof(Slot34)}, but received {slot.GetType().Name}", nameof(slot));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.SpineWrappers.Attachments;
|
||||
using SpineRuntime34;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V34.Attachments
|
||||
{
|
||||
internal sealed class MeshAttachment34(MeshAttachment innerObject) :
|
||||
Attachment34(innerObject),
|
||||
IMeshAttachment
|
||||
{
|
||||
private readonly MeshAttachment _o = innerObject;
|
||||
|
||||
public override MeshAttachment InnerObject => _o;
|
||||
|
||||
public override int ComputeWorldVertices(Spine.SpineWrappers.ISlot slot, ref float[] worldVertices)
|
||||
{
|
||||
if (slot is Slot34 st)
|
||||
{
|
||||
var length = _o.WorldVerticesLength;
|
||||
if (worldVertices.Length < length) worldVertices = new float[length];
|
||||
_o.ComputeWorldVertices(st.InnerObject, worldVertices);
|
||||
return length;
|
||||
}
|
||||
throw new ArgumentException($"Invalid slot type. Expected {nameof(Slot34)}, but received {slot.GetType().Name}", nameof(slot));
|
||||
}
|
||||
|
||||
public float R { get => _o.R; set => _o.R = value; }
|
||||
public float G { get => _o.G; set => _o.G = value; }
|
||||
public float B { get => _o.B; set => _o.B = value; }
|
||||
public float A { get => _o.A; set => _o.A = value; }
|
||||
|
||||
public SFML.Graphics.Texture RendererObject => (SFML.Graphics.Texture)((AtlasRegion)_o.RendererObject).page.rendererObject;
|
||||
|
||||
public float[] UVs => _o.UVs;
|
||||
|
||||
public int[] Triangles => _o.Triangles;
|
||||
|
||||
public int HullLength => _o.HullLength;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.SpineWrappers.Attachments;
|
||||
using SpineRuntime34;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V34.Attachments
|
||||
{
|
||||
internal sealed class PathAttachment34(PathAttachment innerObject) :
|
||||
Attachment34(innerObject),
|
||||
IPathAttachment
|
||||
{
|
||||
private readonly PathAttachment _o = innerObject;
|
||||
|
||||
public override PathAttachment InnerObject => _o;
|
||||
|
||||
public override int ComputeWorldVertices(Spine.SpineWrappers.ISlot slot, ref float[] worldVertices)
|
||||
{
|
||||
if (slot is Slot34 st)
|
||||
{
|
||||
var length = _o.WorldVerticesLength;
|
||||
if (worldVertices.Length < length) worldVertices = new float[length];
|
||||
_o.ComputeWorldVertices(st.InnerObject, worldVertices);
|
||||
return length;
|
||||
}
|
||||
throw new ArgumentException($"Invalid slot type. Expected {nameof(Slot34)}, but received {slot.GetType().Name}", nameof(slot));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.SpineWrappers.Attachments;
|
||||
using SpineRuntime34;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V34.Attachments
|
||||
{
|
||||
internal sealed class RegionAttachment34(RegionAttachment innerObject) :
|
||||
Attachment34(innerObject),
|
||||
IRegionAttachment
|
||||
{
|
||||
private readonly RegionAttachment _o = innerObject;
|
||||
|
||||
public override RegionAttachment InnerObject => _o;
|
||||
|
||||
public override int ComputeWorldVertices(Spine.SpineWrappers.ISlot slot, ref float[] worldVertices)
|
||||
{
|
||||
if (slot is Slot34 st)
|
||||
{
|
||||
if (worldVertices.Length < 8) worldVertices = new float[8];
|
||||
_o.ComputeWorldVertices(st.InnerObject.Bone, worldVertices);
|
||||
return 8;
|
||||
}
|
||||
throw new ArgumentException($"Invalid slot type. Expected {nameof(Slot34)}, but received {slot.GetType().Name}", nameof(slot));
|
||||
}
|
||||
|
||||
public float R { get => _o.R; set => _o.R = value; }
|
||||
public float G { get => _o.G; set => _o.G = value; }
|
||||
public float B { get => _o.B; set => _o.B = value; }
|
||||
public float A { get => _o.A; set => _o.A = value; }
|
||||
|
||||
public SFML.Graphics.Texture RendererObject => (SFML.Graphics.Texture)((AtlasRegion)_o.RendererObject).page.rendererObject;
|
||||
|
||||
public float[] UVs => _o.UVs;
|
||||
}
|
||||
}
|
||||
33
Spine/Implementations/SpineWrappers/V34/Bone34.cs
Normal file
33
Spine/Implementations/SpineWrappers/V34/Bone34.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.SpineWrappers;
|
||||
using SpineRuntime34;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V34
|
||||
{
|
||||
internal sealed class Bone34(Bone innerObject, Bone34? parent = null) : IBone
|
||||
{
|
||||
private readonly Bone _o = innerObject;
|
||||
private readonly Bone34? _parent = parent;
|
||||
|
||||
public Bone InnerObject => _o;
|
||||
|
||||
public string Name => _o.Data.Name;
|
||||
public int Index => _o.Data.Index;
|
||||
|
||||
public IBone? Parent => _parent;
|
||||
public bool Active => true; // NOTE: 3.7 及以下没有 Active 属性, 此处总是返回 true
|
||||
public float Length => _o.Data.Length;
|
||||
public float WorldX => _o.WorldX;
|
||||
public float WorldY => _o.WorldY;
|
||||
public float A => _o.A;
|
||||
public float B => _o.B;
|
||||
public float C => _o.C;
|
||||
public float D => _o.D;
|
||||
|
||||
public override string ToString() => _o.ToString();
|
||||
}
|
||||
}
|
||||
106
Spine/Implementations/SpineWrappers/V34/Skeleton34.cs
Normal file
106
Spine/Implementations/SpineWrappers/V34/Skeleton34.cs
Normal file
@@ -0,0 +1,106 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections.Frozen;
|
||||
using System.Collections.Immutable;
|
||||
using Spine.SpineWrappers;
|
||||
using SpineRuntime34;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V34
|
||||
{
|
||||
internal sealed class Skeleton34 : ISkeleton
|
||||
{
|
||||
private readonly Skeleton _o;
|
||||
private readonly SpineObjectData34 _data;
|
||||
|
||||
private readonly ImmutableArray<IBone> _bones;
|
||||
private readonly FrozenDictionary<string, IBone> _bonesByName;
|
||||
private readonly ImmutableArray<ISlot> _slots;
|
||||
private readonly FrozenDictionary<string, ISlot> _slotsByName;
|
||||
|
||||
private Skin34? _skin;
|
||||
|
||||
public Skeleton34(Skeleton innerObject, SpineObjectData34 data)
|
||||
{
|
||||
_o = innerObject;
|
||||
_data = data;
|
||||
|
||||
List<Bone34> bones = [];
|
||||
Dictionary<string, IBone> bonesByName = [];
|
||||
foreach (var b in _o.Bones)
|
||||
{
|
||||
var bone = new Bone34(b, b.Parent is null ? null : bones[b.Parent.Data.Index]);
|
||||
bones.Add(bone);
|
||||
bonesByName[bone.Name] = bone;
|
||||
}
|
||||
_bones = bones.Cast<IBone>().ToImmutableArray();
|
||||
_bonesByName = bonesByName.ToFrozenDictionary();
|
||||
|
||||
List<Slot34> slots = [];
|
||||
Dictionary<string, ISlot> slotsByName = [];
|
||||
foreach (var s in _o.Slots)
|
||||
{
|
||||
var slot = new Slot34(s, _data, bones[s.Bone.Data.Index]);
|
||||
slots.Add(slot);
|
||||
slotsByName[slot.Name] = slot;
|
||||
}
|
||||
_slots = slots.Cast<ISlot>().ToImmutableArray();
|
||||
_slotsByName = slotsByName.ToFrozenDictionary();
|
||||
}
|
||||
|
||||
public Skeleton InnerObject => _o;
|
||||
|
||||
public float R { get => _o.R; set => _o.R = value; }
|
||||
public float G { get => _o.G; set => _o.G = value; }
|
||||
public float B { get => _o.B; set => _o.B = value; }
|
||||
public float A { get => _o.A; set => _o.A = value; }
|
||||
public float X { get => _o.X; set => _o.X = value; }
|
||||
public float Y { get => _o.Y; set => _o.Y = value; }
|
||||
public float ScaleX { get => _o.ScaleX; set => _o.ScaleX = value; }
|
||||
public float ScaleY { get => _o.ScaleY; set => _o.ScaleY = value; }
|
||||
|
||||
public ImmutableArray<IBone> Bones => _bones;
|
||||
public FrozenDictionary<string, IBone> BonesByName => _bonesByName;
|
||||
public ImmutableArray<ISlot> Slots => _slots;
|
||||
public FrozenDictionary<string, ISlot> SlotsByName => _slotsByName;
|
||||
|
||||
public ISkin? Skin
|
||||
{
|
||||
get => _skin;
|
||||
set
|
||||
{
|
||||
if (value is null)
|
||||
{
|
||||
_o.Skin = null;
|
||||
_skin = null;
|
||||
return;
|
||||
}
|
||||
if (value is Skin34 sk)
|
||||
{
|
||||
_o.Skin = sk.InnerObject;
|
||||
_skin = sk;
|
||||
return;
|
||||
}
|
||||
throw new ArgumentException($"Received {value.GetType().Name}", nameof(value));
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<ISlot> IterDrawOrder() => _o.DrawOrder.Select(s => _slots[s.Data.Index]);
|
||||
public void UpdateCache() => _o.UpdateCache();
|
||||
public void UpdateWorldTransform(ISkeleton.Physics physics) => _o.UpdateWorldTransform();
|
||||
public void SetToSetupPose() => _o.SetToSetupPose();
|
||||
public void SetBonesToSetupPose() => _o.SetBonesToSetupPose();
|
||||
public void SetSlotsToSetupPose() => _o.SetSlotsToSetupPose();
|
||||
public void Update(float delta) => _o.Update(delta);
|
||||
|
||||
public void GetBounds(out float x, out float y, out float w, out float h)
|
||||
{
|
||||
float[] _ = [];
|
||||
_o.GetBounds(out x, out y, out w, out h);
|
||||
}
|
||||
|
||||
public override string ToString() => _o.ToString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
using Spine.SpineWrappers;
|
||||
using Spine.SpineWrappers.Attachments;
|
||||
using Spine.Utils;
|
||||
using SpineRuntime34;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V34
|
||||
{
|
||||
internal sealed class SkeletonClipping34 : ISkeletonClipping
|
||||
{
|
||||
public bool IsClipping => false;
|
||||
|
||||
public float[] ClippedVertices { get; private set; } = [];
|
||||
|
||||
public int ClippedVerticesLength { get; private set; } = 0;
|
||||
|
||||
public int[] ClippedTriangles { get; private set; } = [];
|
||||
|
||||
public int ClippedTrianglesLength { get; private set; } = 0;
|
||||
|
||||
public float[] ClippedUVs { get; private set; } = [];
|
||||
|
||||
public void ClipEnd(ISlot slot) { }
|
||||
|
||||
public void ClipEnd() { }
|
||||
|
||||
public void ClipStart(ISlot slot, IClippingAttachment clippingAttachment) { }
|
||||
|
||||
public void ClipTriangles(float[] vertices, int verticesLength, int[] triangles, int trianglesLength, float[] uvs)
|
||||
{
|
||||
ClippedVertices = vertices.ToArray();
|
||||
ClippedVerticesLength = verticesLength;
|
||||
ClippedTriangles = triangles.ToArray();
|
||||
ClippedTrianglesLength = trianglesLength;
|
||||
ClippedUVs = uvs.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
45
Spine/Implementations/SpineWrappers/V34/Skin34.cs
Normal file
45
Spine/Implementations/SpineWrappers/V34/Skin34.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.SpineWrappers;
|
||||
using SpineRuntime34;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V34
|
||||
{
|
||||
internal sealed class Skin34 : ISkin
|
||||
{
|
||||
private readonly Skin _o;
|
||||
|
||||
/// <summary>
|
||||
/// 使用指定名字创建空皮肤
|
||||
/// </summary>
|
||||
public Skin34(string name) => _o = new(name);
|
||||
|
||||
/// <summary>
|
||||
/// 包装已有皮肤对象
|
||||
/// </summary>
|
||||
public Skin34(Skin innerObject) => _o = innerObject;
|
||||
|
||||
public Skin InnerObject => _o;
|
||||
|
||||
public string Name => _o.Name;
|
||||
|
||||
public void AddSkin(ISkin skin)
|
||||
{
|
||||
if (skin is Skin34 sk)
|
||||
{
|
||||
// NOTE: 3.7 及以下不支持 AddSkin
|
||||
foreach (var (k, v) in sk._o.Attachments)
|
||||
_o.AddAttachment(k.slotIndex, k.name, v);
|
||||
return;
|
||||
}
|
||||
throw new ArgumentException($"Received {skin.GetType().Name}", nameof(skin));
|
||||
}
|
||||
|
||||
public void Clear() => _o.Attachments.Clear();
|
||||
|
||||
public override string ToString() => _o.ToString();
|
||||
}
|
||||
}
|
||||
78
Spine/Implementations/SpineWrappers/V34/Slot34.cs
Normal file
78
Spine/Implementations/SpineWrappers/V34/Slot34.cs
Normal file
@@ -0,0 +1,78 @@
|
||||
using System;
|
||||
using System.Collections.Frozen;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.Utils;
|
||||
using Spine.SpineWrappers;
|
||||
using SpineRuntime34;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V34
|
||||
{
|
||||
internal sealed class Slot34 : ISlot
|
||||
{
|
||||
private readonly Slot _o;
|
||||
private readonly SpineObjectData34 _data;
|
||||
|
||||
private readonly Bone34 _bone;
|
||||
private readonly SFML.Graphics.BlendMode _blendMode;
|
||||
|
||||
public Slot34(Slot innerObject, SpineObjectData34 data, Bone34 bone)
|
||||
{
|
||||
_o = innerObject;
|
||||
_data = data;
|
||||
|
||||
_bone = bone;
|
||||
_blendMode = _o.Data.BlendMode switch
|
||||
{
|
||||
BlendMode.normal => SFMLBlendMode.NormalPma,
|
||||
BlendMode.additive => SFMLBlendMode.AdditivePma,
|
||||
BlendMode.multiply => SFMLBlendMode.MultiplyPma,
|
||||
BlendMode.screen => SFMLBlendMode.ScreenPma,
|
||||
_ => throw new NotImplementedException($"{_o.Data.BlendMode}"),
|
||||
};
|
||||
}
|
||||
|
||||
public Slot InnerObject => _o;
|
||||
|
||||
public string Name => _o.Data.Name;
|
||||
public int Index => _o.Data.Index;
|
||||
public SFML.Graphics.BlendMode Blend => _blendMode;
|
||||
|
||||
public float R { get => _o.R; set => _o.R = value; }
|
||||
public float G { get => _o.G; set => _o.G = value; }
|
||||
public float B { get => _o.B; set => _o.B = value; }
|
||||
public float A { get => _o.A; set => _o.A = value; }
|
||||
public IBone Bone => _bone;
|
||||
|
||||
public Spine.SpineWrappers.Attachments.IAttachment? Attachment
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_o.Attachment is Attachment att)
|
||||
{
|
||||
return _data.SlotAttachments[Name][att.Name];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (value is null)
|
||||
{
|
||||
_o.Attachment = null;
|
||||
return;
|
||||
}
|
||||
if (value is Attachments.Attachment34 att)
|
||||
{
|
||||
_o.Attachment = att.InnerObject;
|
||||
return;
|
||||
}
|
||||
throw new ArgumentException($"Received {value.GetType().Name}", nameof(value));
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString() => _o.ToString();
|
||||
}
|
||||
}
|
||||
136
Spine/Implementations/SpineWrappers/V34/SpineObjectData34.cs
Normal file
136
Spine/Implementations/SpineWrappers/V34/SpineObjectData34.cs
Normal file
@@ -0,0 +1,136 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Frozen;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.Utils;
|
||||
using Spine.SpineWrappers;
|
||||
using Spine.SpineWrappers.Attachments;
|
||||
using SpineRuntime34;
|
||||
using Spine.Implementations.SpineWrappers.V34.Attachments;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V34
|
||||
{
|
||||
[SpineImplementation(3, 4)]
|
||||
internal sealed class SpineObjectData34 : SpineObjectData
|
||||
{
|
||||
private readonly Atlas _atlas;
|
||||
private readonly SkeletonData _skeletonData;
|
||||
private readonly AnimationStateData _animationStateData;
|
||||
|
||||
private readonly ImmutableArray<ISkin> _skins;
|
||||
private readonly FrozenDictionary<string, ISkin> _skinsByName;
|
||||
private readonly FrozenDictionary<string, FrozenDictionary<string, IAttachment>> _slotAttachments;
|
||||
private readonly ImmutableArray<IAnimation> _animations;
|
||||
private readonly FrozenDictionary<string, IAnimation> _animationsByName;
|
||||
|
||||
public SpineObjectData34(string skelPath, string atlasPath, Spine.SpineWrappers.TextureLoader textureLoader)
|
||||
: base(skelPath, atlasPath, textureLoader)
|
||||
{
|
||||
// 加载 atlas
|
||||
try { _atlas = new Atlas(atlasPath, textureLoader); }
|
||||
catch (Exception ex) { throw new InvalidDataException($"Failed to load atlas '{atlasPath}'", ex); }
|
||||
|
||||
try
|
||||
{
|
||||
if (Utf8Validator.IsUtf8(skelPath))
|
||||
{
|
||||
try
|
||||
{
|
||||
_skeletonData = new SkeletonJson(_atlas).ReadSkeletonData(skelPath);
|
||||
}
|
||||
catch
|
||||
{
|
||||
_skeletonData = new SkeletonBinary(_atlas).ReadSkeletonData(skelPath);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
_skeletonData = new SkeletonBinary(_atlas).ReadSkeletonData(skelPath);
|
||||
}
|
||||
catch
|
||||
{
|
||||
_skeletonData = new SkeletonJson(_atlas).ReadSkeletonData(skelPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_atlas.Dispose();
|
||||
throw new InvalidDataException($"Failed to load skeleton file {skelPath}", ex);
|
||||
}
|
||||
|
||||
// 加载动画数据
|
||||
_animationStateData = new AnimationStateData(_skeletonData);
|
||||
|
||||
// 整理皮肤和附件
|
||||
Dictionary<string, Dictionary<string, IAttachment>> slotAttachments = [];
|
||||
List<ISkin> skins = [];
|
||||
Dictionary<string, ISkin> skinsByName = [];
|
||||
foreach (var s in _skeletonData.Skins)
|
||||
{
|
||||
var skin = new Skin34(s);
|
||||
skins.Add(skin);
|
||||
skinsByName[s.Name] = skin;
|
||||
foreach (var (k, att) in s.Attachments)
|
||||
{
|
||||
var slotName = _skeletonData.Slots.Items[k.slotIndex].Name;
|
||||
if (!slotAttachments.TryGetValue(slotName, out var attachments))
|
||||
slotAttachments[slotName] = attachments = [];
|
||||
|
||||
attachments[att.Name] = att switch
|
||||
{
|
||||
RegionAttachment regionAtt => new RegionAttachment34(regionAtt),
|
||||
MeshAttachment meshAtt => new MeshAttachment34(meshAtt),
|
||||
BoundingBoxAttachment bbAtt => new BoundingBoxAttachment34(bbAtt),
|
||||
PathAttachment pathAtt => new PathAttachment34(pathAtt),
|
||||
_ => throw new InvalidOperationException($"Unrecognized attachment type {att.GetType().FullName}")
|
||||
};
|
||||
}
|
||||
}
|
||||
_slotAttachments = slotAttachments.ToFrozenDictionary(it => it.Key, it => it.Value.ToFrozenDictionary());
|
||||
_skins = skins.ToImmutableArray();
|
||||
_skinsByName = skinsByName.ToFrozenDictionary();
|
||||
|
||||
// 整理所有动画数据
|
||||
List<IAnimation> animations = [];
|
||||
Dictionary<string, IAnimation> animationsByName = [];
|
||||
foreach (var a in _skeletonData.Animations)
|
||||
{
|
||||
var anime = new Animation34(a);
|
||||
animations.Add(anime);
|
||||
animationsByName[anime.Name] = anime;
|
||||
}
|
||||
_animations = animations.ToImmutableArray();
|
||||
_animationsByName = animationsByName.ToFrozenDictionary();
|
||||
}
|
||||
|
||||
public override string SkeletonVersion => _skeletonData.Version;
|
||||
|
||||
public override ImmutableArray<ISkin> Skins => _skins;
|
||||
|
||||
public override FrozenDictionary<string, ISkin> SkinsByName => _skinsByName;
|
||||
|
||||
public override FrozenDictionary<string, FrozenDictionary<string, IAttachment>> SlotAttachments => _slotAttachments;
|
||||
|
||||
public override float DefaultMix { get => _animationStateData.DefaultMix; set => _animationStateData.DefaultMix = value; }
|
||||
|
||||
public override ImmutableArray<IAnimation> Animations => _animations;
|
||||
|
||||
public override FrozenDictionary<string, IAnimation> AnimationsByName => _animationsByName;
|
||||
|
||||
protected override void DisposeAtlas() => _atlas.Dispose();
|
||||
|
||||
public override ISkeleton CreateSkeleton() => new Skeleton34(new(_skeletonData), this);
|
||||
|
||||
public override IAnimationState CreateAnimationState() => new AnimationState34(new(_animationStateData), this);
|
||||
|
||||
public override ISkeletonClipping CreateSkeletonClipping() => new SkeletonClipping34();
|
||||
|
||||
public override ISkin CreateSkin(string name) => new Skin34(name);
|
||||
}
|
||||
}
|
||||
135
Spine/Implementations/SpineWrappers/V34/TrackEntry34.cs
Normal file
135
Spine/Implementations/SpineWrappers/V34/TrackEntry34.cs
Normal file
@@ -0,0 +1,135 @@
|
||||
using Spine.SpineWrappers;
|
||||
using SpineRuntime34;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V34
|
||||
{
|
||||
internal sealed class TrackEntry34(TrackEntry innerObject, AnimationState34 animationState, SpineObjectData34 data): ITrackEntry
|
||||
{
|
||||
private readonly TrackEntry _o = innerObject;
|
||||
private readonly AnimationState34 _animationState = animationState;
|
||||
private readonly SpineObjectData34 _data = data;
|
||||
|
||||
private readonly Dictionary<IAnimationState.TrackEntryDelegate, AnimationState.TrackEntryDelegate> _eventMapping = [];
|
||||
private readonly Dictionary<IAnimationState.TrackEntryDelegate, int> _eventCount = [];
|
||||
|
||||
public TrackEntry InnerObject => _o;
|
||||
|
||||
#pragma warning disable CS0067
|
||||
|
||||
// 3.4 及以下没有这两个事件
|
||||
public event IAnimationState.TrackEntryDelegate? Interrupt;
|
||||
public event IAnimationState.TrackEntryDelegate? Dispose;
|
||||
|
||||
#pragma warning restore CS0067
|
||||
|
||||
public event IAnimationState.TrackEntryDelegate? Start
|
||||
{
|
||||
add
|
||||
{
|
||||
if (value is null) return;
|
||||
if (!_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_eventMapping[value] = f = (TrackEntry t) => value(_animationState.GetTrackEntry(t));
|
||||
_eventCount[value] = 0;
|
||||
}
|
||||
_o.Start += f;
|
||||
_eventCount[value]++;
|
||||
}
|
||||
remove
|
||||
{
|
||||
if (value is null) return;
|
||||
if (_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_o.Start -= f;
|
||||
_eventCount[value]--;
|
||||
if (_eventCount[value] <= 0)
|
||||
{
|
||||
_eventMapping.Remove(value);
|
||||
_eventCount.Remove(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public event IAnimationState.TrackEntryDelegate? End
|
||||
{
|
||||
add
|
||||
{
|
||||
if (value is null) return;
|
||||
if (!_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_eventMapping[value] = f = (TrackEntry t) => value(_animationState.GetTrackEntry(t));
|
||||
_eventCount[value] = 0;
|
||||
}
|
||||
_o.End += f;
|
||||
_eventCount[value]++;
|
||||
}
|
||||
remove
|
||||
{
|
||||
if (value is null) return;
|
||||
if (_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_o.End -= f;
|
||||
_eventCount[value]--;
|
||||
if (_eventCount[value] <= 0)
|
||||
{
|
||||
_eventMapping.Remove(value);
|
||||
_eventCount.Remove(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public event IAnimationState.TrackEntryDelegate? Complete
|
||||
{
|
||||
add
|
||||
{
|
||||
if (value is null) return;
|
||||
if (!_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_eventMapping[value] = f = (TrackEntry t) => value(_animationState.GetTrackEntry(t));
|
||||
_eventCount[value] = 0;
|
||||
}
|
||||
_o.Complete += f;
|
||||
_eventCount[value]++;
|
||||
}
|
||||
remove
|
||||
{
|
||||
if (value is null) return;
|
||||
if (_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_o.Complete -= f;
|
||||
_eventCount[value]--;
|
||||
if (_eventCount[value] <= 0)
|
||||
{
|
||||
_eventMapping.Remove(value);
|
||||
_eventCount.Remove(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int TrackIndex { get => _o.TrackIndex; }
|
||||
|
||||
public IAnimation Animation { get => _data.AnimationsByName[_o.Animation.Name]; }
|
||||
|
||||
public ITrackEntry? Next { get { var t = _o.Next; return t is null ? null : _animationState.GetTrackEntry(t); } }
|
||||
|
||||
public bool Loop { get => _o.Loop; set => _o.Loop = value; }
|
||||
|
||||
public float TrackTime { get => _o.Time; set => _o.Time = value; }
|
||||
|
||||
public float TimeScale { get => _o.TimeScale; set => _o.TimeScale = value; }
|
||||
|
||||
public float Alpha { get => _o.Mix; set => _o.Mix = value; }
|
||||
|
||||
public float MixDuration { get => _o.MixDuration; set => _o.MixDuration = value; }
|
||||
|
||||
public override string ToString() => _o.ToString();
|
||||
}
|
||||
}
|
||||
23
Spine/Implementations/SpineWrappers/V35/Animation35.cs
Normal file
23
Spine/Implementations/SpineWrappers/V35/Animation35.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using Spine.SpineWrappers;
|
||||
using SpineRuntime35;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V35
|
||||
{
|
||||
internal sealed class Animation35(Animation innerObject) : IAnimation
|
||||
{
|
||||
private readonly Animation _o = innerObject;
|
||||
|
||||
public Animation InnerObject => _o;
|
||||
|
||||
public string Name => _o.Name;
|
||||
|
||||
public float Duration => _o.Duration;
|
||||
|
||||
public override string ToString() => _o.ToString();
|
||||
}
|
||||
}
|
||||
229
Spine/Implementations/SpineWrappers/V35/AnimationState35.cs
Normal file
229
Spine/Implementations/SpineWrappers/V35/AnimationState35.cs
Normal file
@@ -0,0 +1,229 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.SpineWrappers;
|
||||
using SpineRuntime35;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V35
|
||||
{
|
||||
internal sealed class AnimationState35(AnimationState innerObject, SpineObjectData35 data) : IAnimationState
|
||||
{
|
||||
private readonly AnimationState _o = innerObject;
|
||||
private readonly SpineObjectData35 _data = data;
|
||||
|
||||
private readonly Dictionary<TrackEntry, TrackEntry35> _trackEntryPool = [];
|
||||
|
||||
private readonly Dictionary<IAnimationState.TrackEntryDelegate, AnimationState.TrackEntryDelegate> _eventMapping = [];
|
||||
private readonly Dictionary<IAnimationState.TrackEntryDelegate, int> _eventCount = [];
|
||||
|
||||
public AnimationState InnerObject => _o;
|
||||
|
||||
public event IAnimationState.TrackEntryDelegate? Start
|
||||
{
|
||||
add
|
||||
{
|
||||
if (value is null) return;
|
||||
if (!_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_eventMapping[value] = f = (TrackEntry t) => value(GetTrackEntry(t));
|
||||
_eventCount[value] = 0;
|
||||
}
|
||||
_o.Start += f;
|
||||
_eventCount[value]++;
|
||||
}
|
||||
remove
|
||||
{
|
||||
if (value is null) return;
|
||||
if (_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_o.Start -= f;
|
||||
_eventCount[value]--;
|
||||
if (_eventCount[value] <= 0)
|
||||
{
|
||||
_eventMapping.Remove(value);
|
||||
_eventCount.Remove(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public event IAnimationState.TrackEntryDelegate? Interrupt
|
||||
{
|
||||
add
|
||||
{
|
||||
if (value is null) return;
|
||||
if (!_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_eventMapping[value] = f = (TrackEntry t) => value(GetTrackEntry(t));
|
||||
_eventCount[value] = 0;
|
||||
}
|
||||
_o.Interrupt += f;
|
||||
_eventCount[value]++;
|
||||
}
|
||||
remove
|
||||
{
|
||||
if (value is null) return;
|
||||
if (_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_o.Interrupt -= f;
|
||||
_eventCount[value]--;
|
||||
if (_eventCount[value] <= 0)
|
||||
{
|
||||
_eventMapping.Remove(value);
|
||||
_eventCount.Remove(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public event IAnimationState.TrackEntryDelegate? End
|
||||
{
|
||||
add
|
||||
{
|
||||
if (value is null) return;
|
||||
if (!_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_eventMapping[value] = f = (TrackEntry t) => value(GetTrackEntry(t));
|
||||
_eventCount[value] = 0;
|
||||
}
|
||||
_o.End += f;
|
||||
_eventCount[value]++;
|
||||
}
|
||||
remove
|
||||
{
|
||||
if (value is null) return;
|
||||
if (_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_o.End -= f;
|
||||
_eventCount[value]--;
|
||||
if (_eventCount[value] <= 0)
|
||||
{
|
||||
_eventMapping.Remove(value);
|
||||
_eventCount.Remove(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public event IAnimationState.TrackEntryDelegate? Complete
|
||||
{
|
||||
add
|
||||
{
|
||||
if (value is null) return;
|
||||
if (!_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_eventMapping[value] = f = (TrackEntry t) => value(GetTrackEntry(t));
|
||||
_eventCount[value] = 0;
|
||||
}
|
||||
_o.Complete += f;
|
||||
_eventCount[value]++;
|
||||
}
|
||||
remove
|
||||
{
|
||||
if (value is null) return;
|
||||
if (_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_o.Complete -= f;
|
||||
_eventCount[value]--;
|
||||
if (_eventCount[value] <= 0)
|
||||
{
|
||||
_eventMapping.Remove(value);
|
||||
_eventCount.Remove(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public event IAnimationState.TrackEntryDelegate? Dispose
|
||||
{
|
||||
add
|
||||
{
|
||||
if (value is null) return;
|
||||
if (!_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_eventMapping[value] = f = (TrackEntry t) => value(GetTrackEntry(t));
|
||||
_eventCount[value] = 0;
|
||||
}
|
||||
_o.Dispose += f;
|
||||
_eventCount[value]++;
|
||||
}
|
||||
remove
|
||||
{
|
||||
if (value is null) return;
|
||||
if (_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_o.Dispose -= f;
|
||||
_eventCount[value]--;
|
||||
if (_eventCount[value] <= 0)
|
||||
{
|
||||
_eventMapping.Remove(value);
|
||||
_eventCount.Remove(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public float TimeScale { get => _o.TimeScale; set => _o.TimeScale = value; }
|
||||
|
||||
public void Update(float delta) => _o.Update(delta);
|
||||
|
||||
public void Apply(ISkeleton skeleton)
|
||||
{
|
||||
if (skeleton is Skeleton35 skel)
|
||||
{
|
||||
_o.Apply(skel.InnerObject);
|
||||
return;
|
||||
}
|
||||
throw new ArgumentException($"Received {skeleton.GetType().Name}", nameof(skeleton));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取 <see cref="ITrackEntry"/> 对象, 不存在则创建
|
||||
/// </summary>
|
||||
public ITrackEntry GetTrackEntry(TrackEntry trackEntry)
|
||||
{
|
||||
if (!_trackEntryPool.TryGetValue(trackEntry, out var tr))
|
||||
_trackEntryPool[trackEntry] = tr = new(trackEntry, this, _data);
|
||||
return tr;
|
||||
}
|
||||
|
||||
public IEnumerable<ITrackEntry?> IterTracks() => _o.Tracks.Select(t => t is null ? null : GetTrackEntry(t));
|
||||
|
||||
public ITrackEntry? GetCurrent(int index) { var t = _o.GetCurrent(index); return t is null ? null : GetTrackEntry(t); }
|
||||
|
||||
public void ClearTrack(int index) => _o.ClearTrack(index);
|
||||
|
||||
public void ClearTracks() => _o.ClearTracks();
|
||||
|
||||
public ITrackEntry SetAnimation(int trackIndex, string animationName, bool loop)
|
||||
=> GetTrackEntry(_o.SetAnimation(trackIndex, animationName, loop));
|
||||
|
||||
public ITrackEntry SetAnimation(int trackIndex, IAnimation animation, bool loop)
|
||||
{
|
||||
if (animation is Animation35 anime)
|
||||
return GetTrackEntry(_o.SetAnimation(trackIndex, anime.InnerObject, loop));
|
||||
throw new ArgumentException($"Received {animation.GetType().Name}", nameof(animation));
|
||||
}
|
||||
|
||||
public ITrackEntry SetEmptyAnimation(int trackIndex, float mixDuration) => GetTrackEntry(_o.SetEmptyAnimation(trackIndex, mixDuration));
|
||||
|
||||
public void SetEmptyAnimations(float mixDuration) => _o.SetEmptyAnimations(mixDuration);
|
||||
|
||||
public ITrackEntry AddAnimation(int trackIndex, string animationName, bool loop, float delay)
|
||||
=> GetTrackEntry(_o.AddAnimation(trackIndex, animationName, loop, delay));
|
||||
|
||||
public ITrackEntry AddAnimation(int trackIndex, IAnimation animation, bool loop, float delay)
|
||||
{
|
||||
if (animation is Animation35 anime)
|
||||
return GetTrackEntry(_o.AddAnimation(trackIndex, anime.InnerObject, loop, delay));
|
||||
throw new ArgumentException($"Received {animation.GetType().Name}", nameof(animation));
|
||||
}
|
||||
|
||||
public ITrackEntry AddEmptyAnimation(int trackIndex, float mixDuration, float delay)
|
||||
=> GetTrackEntry(_o.AddEmptyAnimation(trackIndex, mixDuration, delay));
|
||||
|
||||
public override string ToString() => _o.ToString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.SpineWrappers;
|
||||
using Spine.SpineWrappers.Attachments;
|
||||
using SpineRuntime35;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V35.Attachments
|
||||
{
|
||||
internal abstract class Attachment35(Attachment innerObject) : IAttachment
|
||||
{
|
||||
private readonly Attachment _o = innerObject;
|
||||
|
||||
public virtual Attachment InnerObject => _o;
|
||||
|
||||
public string Name => _o.Name;
|
||||
|
||||
public abstract int ComputeWorldVertices(ISlot slot, ref float[] worldVertices);
|
||||
|
||||
public override string ToString() => _o.ToString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.SpineWrappers.Attachments;
|
||||
using SpineRuntime35;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V35.Attachments
|
||||
{
|
||||
internal sealed class BoundingBoxAttachment35(BoundingBoxAttachment innerObject) :
|
||||
Attachment35(innerObject),
|
||||
IBoundingBoxAttachment
|
||||
{
|
||||
private readonly BoundingBoxAttachment _o = innerObject;
|
||||
|
||||
public override BoundingBoxAttachment InnerObject => _o;
|
||||
|
||||
public override int ComputeWorldVertices(Spine.SpineWrappers.ISlot slot, ref float[] worldVertices)
|
||||
{
|
||||
if (slot is Slot35 st)
|
||||
{
|
||||
var length = _o.WorldVerticesLength;
|
||||
if (worldVertices.Length < length) worldVertices = new float[length];
|
||||
_o.ComputeWorldVertices(st.InnerObject, worldVertices);
|
||||
return length;
|
||||
}
|
||||
throw new ArgumentException($"Invalid slot type. Expected {nameof(Slot35)}, but received {slot.GetType().Name}", nameof(slot));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.SpineWrappers.Attachments;
|
||||
using SpineRuntime35;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V35.Attachments
|
||||
{
|
||||
internal sealed class ClippingAttachment35(ClippingAttachment innerObject) :
|
||||
Attachment35(innerObject),
|
||||
IClippingAttachment
|
||||
{
|
||||
private readonly ClippingAttachment _o = innerObject;
|
||||
|
||||
public override ClippingAttachment InnerObject => _o;
|
||||
|
||||
public override int ComputeWorldVertices(Spine.SpineWrappers.ISlot slot, ref float[] worldVertices)
|
||||
{
|
||||
if (slot is Slot35 st)
|
||||
{
|
||||
var length = _o.WorldVerticesLength;
|
||||
if (worldVertices.Length < length) worldVertices = new float[length];
|
||||
_o.ComputeWorldVertices(st.InnerObject, worldVertices);
|
||||
return length;
|
||||
}
|
||||
throw new ArgumentException($"Invalid slot type. Expected {nameof(Slot35)}, but received {slot.GetType().Name}", nameof(slot));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.SpineWrappers.Attachments;
|
||||
using SpineRuntime35;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V35.Attachments
|
||||
{
|
||||
internal sealed class MeshAttachment35(MeshAttachment innerObject) :
|
||||
Attachment35(innerObject),
|
||||
IMeshAttachment
|
||||
{
|
||||
private readonly MeshAttachment _o = innerObject;
|
||||
|
||||
public override MeshAttachment InnerObject => _o;
|
||||
|
||||
public override int ComputeWorldVertices(Spine.SpineWrappers.ISlot slot, ref float[] worldVertices)
|
||||
{
|
||||
if (slot is Slot35 st)
|
||||
{
|
||||
var length = _o.WorldVerticesLength;
|
||||
if (worldVertices.Length < length) worldVertices = new float[length];
|
||||
_o.ComputeWorldVertices(st.InnerObject, worldVertices);
|
||||
return length;
|
||||
}
|
||||
throw new ArgumentException($"Invalid slot type. Expected {nameof(Slot35)}, but received {slot.GetType().Name}", nameof(slot));
|
||||
}
|
||||
|
||||
public float R { get => _o.R; set => _o.R = value; }
|
||||
public float G { get => _o.G; set => _o.G = value; }
|
||||
public float B { get => _o.B; set => _o.B = value; }
|
||||
public float A { get => _o.A; set => _o.A = value; }
|
||||
|
||||
public SFML.Graphics.Texture RendererObject => (SFML.Graphics.Texture)((AtlasRegion)_o.RendererObject).page.rendererObject;
|
||||
|
||||
public float[] UVs => _o.UVs;
|
||||
|
||||
public int[] Triangles => _o.Triangles;
|
||||
|
||||
public int HullLength => _o.HullLength;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.SpineWrappers.Attachments;
|
||||
using SpineRuntime35;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V35.Attachments
|
||||
{
|
||||
internal sealed class PathAttachment35(PathAttachment innerObject) :
|
||||
Attachment35(innerObject),
|
||||
IPathAttachment
|
||||
{
|
||||
private readonly PathAttachment _o = innerObject;
|
||||
|
||||
public override PathAttachment InnerObject => _o;
|
||||
|
||||
public override int ComputeWorldVertices(Spine.SpineWrappers.ISlot slot, ref float[] worldVertices)
|
||||
{
|
||||
if (slot is Slot35 st)
|
||||
{
|
||||
var length = _o.WorldVerticesLength;
|
||||
if (worldVertices.Length < length) worldVertices = new float[length];
|
||||
_o.ComputeWorldVertices(st.InnerObject, worldVertices);
|
||||
return length;
|
||||
}
|
||||
throw new ArgumentException($"Invalid slot type. Expected {nameof(Slot35)}, but received {slot.GetType().Name}", nameof(slot));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.SpineWrappers.Attachments;
|
||||
using SpineRuntime35;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V35.Attachments
|
||||
{
|
||||
internal sealed class PointAttachment35(PointAttachment innerObject) :
|
||||
Attachment35(innerObject),
|
||||
IPointAttachment
|
||||
{
|
||||
private readonly PointAttachment _o = innerObject;
|
||||
|
||||
public override PointAttachment InnerObject => _o;
|
||||
|
||||
public override int ComputeWorldVertices(Spine.SpineWrappers.ISlot slot, ref float[] worldVertices)
|
||||
{
|
||||
if (slot is Slot35 st)
|
||||
{
|
||||
if (worldVertices.Length < 2) worldVertices = new float[2];
|
||||
_o.ComputeWorldPosition(st.InnerObject.Bone, out worldVertices[0], out worldVertices[1]);
|
||||
return 2;
|
||||
}
|
||||
throw new ArgumentException($"Invalid slot type. Expected {nameof(Slot35)}, but received {slot.GetType().Name}", nameof(slot));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.SpineWrappers.Attachments;
|
||||
using SpineRuntime35;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V35.Attachments
|
||||
{
|
||||
internal sealed class RegionAttachment35(RegionAttachment innerObject) :
|
||||
Attachment35(innerObject),
|
||||
IRegionAttachment
|
||||
{
|
||||
private readonly RegionAttachment _o = innerObject;
|
||||
|
||||
public override RegionAttachment InnerObject => _o;
|
||||
|
||||
public override int ComputeWorldVertices(Spine.SpineWrappers.ISlot slot, ref float[] worldVertices)
|
||||
{
|
||||
if (slot is Slot35 st)
|
||||
{
|
||||
if (worldVertices.Length < 8) worldVertices = new float[8];
|
||||
_o.ComputeWorldVertices(st.InnerObject.Bone, worldVertices, 0);
|
||||
return 8;
|
||||
}
|
||||
throw new ArgumentException($"Invalid slot type. Expected {nameof(Slot35)}, but received {slot.GetType().Name}", nameof(slot));
|
||||
}
|
||||
|
||||
public float R { get => _o.R; set => _o.R = value; }
|
||||
public float G { get => _o.G; set => _o.G = value; }
|
||||
public float B { get => _o.B; set => _o.B = value; }
|
||||
public float A { get => _o.A; set => _o.A = value; }
|
||||
|
||||
public SFML.Graphics.Texture RendererObject => (SFML.Graphics.Texture)((AtlasRegion)_o.RendererObject).page.rendererObject;
|
||||
|
||||
public float[] UVs => _o.UVs;
|
||||
}
|
||||
}
|
||||
33
Spine/Implementations/SpineWrappers/V35/Bone35.cs
Normal file
33
Spine/Implementations/SpineWrappers/V35/Bone35.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.SpineWrappers;
|
||||
using SpineRuntime35;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V35
|
||||
{
|
||||
internal sealed class Bone35(Bone innerObject, Bone35? parent = null) : IBone
|
||||
{
|
||||
private readonly Bone _o = innerObject;
|
||||
private readonly Bone35? _parent = parent;
|
||||
|
||||
public Bone InnerObject => _o;
|
||||
|
||||
public string Name => _o.Data.Name;
|
||||
public int Index => _o.Data.Index;
|
||||
|
||||
public IBone? Parent => _parent;
|
||||
public bool Active => true; // NOTE: 3.7 及以下没有 Active 属性, 此处总是返回 true
|
||||
public float Length => _o.Data.Length;
|
||||
public float WorldX => _o.WorldX;
|
||||
public float WorldY => _o.WorldY;
|
||||
public float A => _o.A;
|
||||
public float B => _o.B;
|
||||
public float C => _o.C;
|
||||
public float D => _o.D;
|
||||
|
||||
public override string ToString() => _o.ToString();
|
||||
}
|
||||
}
|
||||
106
Spine/Implementations/SpineWrappers/V35/Skeleton35.cs
Normal file
106
Spine/Implementations/SpineWrappers/V35/Skeleton35.cs
Normal file
@@ -0,0 +1,106 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections.Frozen;
|
||||
using System.Collections.Immutable;
|
||||
using Spine.SpineWrappers;
|
||||
using SpineRuntime35;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V35
|
||||
{
|
||||
internal sealed class Skeleton35 : ISkeleton
|
||||
{
|
||||
private readonly Skeleton _o;
|
||||
private readonly SpineObjectData35 _data;
|
||||
|
||||
private readonly ImmutableArray<IBone> _bones;
|
||||
private readonly FrozenDictionary<string, IBone> _bonesByName;
|
||||
private readonly ImmutableArray<ISlot> _slots;
|
||||
private readonly FrozenDictionary<string, ISlot> _slotsByName;
|
||||
|
||||
private Skin35? _skin;
|
||||
|
||||
public Skeleton35(Skeleton innerObject, SpineObjectData35 data)
|
||||
{
|
||||
_o = innerObject;
|
||||
_data = data;
|
||||
|
||||
List<Bone35> bones = [];
|
||||
Dictionary<string, IBone> bonesByName = [];
|
||||
foreach (var b in _o.Bones)
|
||||
{
|
||||
var bone = new Bone35(b, b.Parent is null ? null : bones[b.Parent.Data.Index]);
|
||||
bones.Add(bone);
|
||||
bonesByName[bone.Name] = bone;
|
||||
}
|
||||
_bones = bones.Cast<IBone>().ToImmutableArray();
|
||||
_bonesByName = bonesByName.ToFrozenDictionary();
|
||||
|
||||
List<Slot35> slots = [];
|
||||
Dictionary<string, ISlot> slotsByName = [];
|
||||
foreach (var s in _o.Slots)
|
||||
{
|
||||
var slot = new Slot35(s, _data, bones[s.Bone.Data.Index]);
|
||||
slots.Add(slot);
|
||||
slotsByName[slot.Name] = slot;
|
||||
}
|
||||
_slots = slots.Cast<ISlot>().ToImmutableArray();
|
||||
_slotsByName = slotsByName.ToFrozenDictionary();
|
||||
}
|
||||
|
||||
public Skeleton InnerObject => _o;
|
||||
|
||||
public float R { get => _o.R; set => _o.R = value; }
|
||||
public float G { get => _o.G; set => _o.G = value; }
|
||||
public float B { get => _o.B; set => _o.B = value; }
|
||||
public float A { get => _o.A; set => _o.A = value; }
|
||||
public float X { get => _o.X; set => _o.X = value; }
|
||||
public float Y { get => _o.Y; set => _o.Y = value; }
|
||||
public float ScaleX { get => _o.ScaleX; set => _o.ScaleX = value; }
|
||||
public float ScaleY { get => _o.ScaleY; set => _o.ScaleY = value; }
|
||||
|
||||
public ImmutableArray<IBone> Bones => _bones;
|
||||
public FrozenDictionary<string, IBone> BonesByName => _bonesByName;
|
||||
public ImmutableArray<ISlot> Slots => _slots;
|
||||
public FrozenDictionary<string, ISlot> SlotsByName => _slotsByName;
|
||||
|
||||
public ISkin? Skin
|
||||
{
|
||||
get => _skin;
|
||||
set
|
||||
{
|
||||
if (value is null)
|
||||
{
|
||||
_o.Skin = null;
|
||||
_skin = null;
|
||||
return;
|
||||
}
|
||||
if (value is Skin35 sk)
|
||||
{
|
||||
_o.Skin = sk.InnerObject;
|
||||
_skin = sk;
|
||||
return;
|
||||
}
|
||||
throw new ArgumentException($"Received {value.GetType().Name}", nameof(value));
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<ISlot> IterDrawOrder() => _o.DrawOrder.Select(s => _slots[s.Data.Index]);
|
||||
public void UpdateCache() => _o.UpdateCache();
|
||||
public void UpdateWorldTransform(ISkeleton.Physics physics) => _o.UpdateWorldTransform();
|
||||
public void SetToSetupPose() => _o.SetToSetupPose();
|
||||
public void SetBonesToSetupPose() => _o.SetBonesToSetupPose();
|
||||
public void SetSlotsToSetupPose() => _o.SetSlotsToSetupPose();
|
||||
public void Update(float delta) => _o.Update(delta);
|
||||
|
||||
public void GetBounds(out float x, out float y, out float w, out float h)
|
||||
{
|
||||
float[] _ = [];
|
||||
_o.GetBounds(out x, out y, out w, out h, ref _);
|
||||
}
|
||||
|
||||
public override string ToString() => _o.ToString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
using Spine.SpineWrappers;
|
||||
using Spine.SpineWrappers.Attachments;
|
||||
using Spine.Utils;
|
||||
using SpineRuntime35;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V35
|
||||
{
|
||||
internal sealed class SkeletonClipping35 : ISkeletonClipping
|
||||
{
|
||||
private readonly SkeletonClipping _o = new();
|
||||
|
||||
public bool IsClipping => _o.IsClipping();
|
||||
|
||||
public float[] ClippedVertices => _o.ClippedVertices.Items;
|
||||
|
||||
public int ClippedVerticesLength => _o.ClippedVertices.Count;
|
||||
|
||||
public int[] ClippedTriangles => _o.ClippedTriangles.Items;
|
||||
|
||||
public int ClippedTrianglesLength => _o.ClippedTriangles.Count;
|
||||
|
||||
public float[] ClippedUVs => _o.ClippedUVs.Items;
|
||||
|
||||
public void ClipTriangles(float[] vertices, int verticesLength, int[] triangles, int trianglesLength, float[] uvs)
|
||||
=> _o.ClipTriangles(vertices, verticesLength, triangles, trianglesLength, uvs);
|
||||
|
||||
public void ClipStart(ISlot slot, IClippingAttachment clippingAttachment)
|
||||
{
|
||||
if (slot is Slot35 st && clippingAttachment is Attachments.ClippingAttachment35 att)
|
||||
{
|
||||
_o.ClipStart(st.InnerObject, att.InnerObject);
|
||||
return;
|
||||
}
|
||||
throw new ArgumentException($"Received {slot.GetType().Name} {clippingAttachment.GetType().Name}");
|
||||
}
|
||||
|
||||
public void ClipEnd(ISlot slot)
|
||||
{
|
||||
if (slot is Slot35 st)
|
||||
{
|
||||
_o.ClipEnd(st.InnerObject);
|
||||
return;
|
||||
}
|
||||
throw new ArgumentException($"Received {slot.GetType().Name}", nameof(slot));
|
||||
}
|
||||
|
||||
public void ClipEnd() => _o.ClipEnd();
|
||||
|
||||
public override string ToString() => _o.ToString();
|
||||
}
|
||||
}
|
||||
45
Spine/Implementations/SpineWrappers/V35/Skin35.cs
Normal file
45
Spine/Implementations/SpineWrappers/V35/Skin35.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.SpineWrappers;
|
||||
using SpineRuntime35;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V35
|
||||
{
|
||||
internal sealed class Skin35 : ISkin
|
||||
{
|
||||
private readonly Skin _o;
|
||||
|
||||
/// <summary>
|
||||
/// 使用指定名字创建空皮肤
|
||||
/// </summary>
|
||||
public Skin35(string name) => _o = new(name);
|
||||
|
||||
/// <summary>
|
||||
/// 包装已有皮肤对象
|
||||
/// </summary>
|
||||
public Skin35(Skin innerObject) => _o = innerObject;
|
||||
|
||||
public Skin InnerObject => _o;
|
||||
|
||||
public string Name => _o.Name;
|
||||
|
||||
public void AddSkin(ISkin skin)
|
||||
{
|
||||
if (skin is Skin35 sk)
|
||||
{
|
||||
// NOTE: 3.7 及以下不支持 AddSkin
|
||||
foreach (var (k, v) in sk._o.Attachments)
|
||||
_o.AddAttachment(k.slotIndex, k.name, v);
|
||||
return;
|
||||
}
|
||||
throw new ArgumentException($"Received {skin.GetType().Name}", nameof(skin));
|
||||
}
|
||||
|
||||
public void Clear() => _o.Attachments.Clear();
|
||||
|
||||
public override string ToString() => _o.ToString();
|
||||
}
|
||||
}
|
||||
78
Spine/Implementations/SpineWrappers/V35/Slot35.cs
Normal file
78
Spine/Implementations/SpineWrappers/V35/Slot35.cs
Normal file
@@ -0,0 +1,78 @@
|
||||
using System;
|
||||
using System.Collections.Frozen;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.Utils;
|
||||
using Spine.SpineWrappers;
|
||||
using SpineRuntime35;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V35
|
||||
{
|
||||
internal sealed class Slot35 : ISlot
|
||||
{
|
||||
private readonly Slot _o;
|
||||
private readonly SpineObjectData35 _data;
|
||||
|
||||
private readonly Bone35 _bone;
|
||||
private readonly SFML.Graphics.BlendMode _blendMode;
|
||||
|
||||
public Slot35(Slot innerObject, SpineObjectData35 data, Bone35 bone)
|
||||
{
|
||||
_o = innerObject;
|
||||
_data = data;
|
||||
|
||||
_bone = bone;
|
||||
_blendMode = _o.Data.BlendMode switch
|
||||
{
|
||||
BlendMode.Normal => SFMLBlendMode.NormalPma,
|
||||
BlendMode.Additive => SFMLBlendMode.AdditivePma,
|
||||
BlendMode.Multiply => SFMLBlendMode.MultiplyPma,
|
||||
BlendMode.Screen => SFMLBlendMode.ScreenPma,
|
||||
_ => throw new NotImplementedException($"{_o.Data.BlendMode}"),
|
||||
};
|
||||
}
|
||||
|
||||
public Slot InnerObject => _o;
|
||||
|
||||
public string Name => _o.Data.Name;
|
||||
public int Index => _o.Data.Index;
|
||||
public SFML.Graphics.BlendMode Blend => _blendMode;
|
||||
|
||||
public float R { get => _o.R; set => _o.R = value; }
|
||||
public float G { get => _o.G; set => _o.G = value; }
|
||||
public float B { get => _o.B; set => _o.B = value; }
|
||||
public float A { get => _o.A; set => _o.A = value; }
|
||||
public IBone Bone => _bone;
|
||||
|
||||
public Spine.SpineWrappers.Attachments.IAttachment? Attachment
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_o.Attachment is Attachment att)
|
||||
{
|
||||
return _data.SlotAttachments[Name][att.Name];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (value is null)
|
||||
{
|
||||
_o.Attachment = null;
|
||||
return;
|
||||
}
|
||||
if (value is Attachments.Attachment35 att)
|
||||
{
|
||||
_o.Attachment = att.InnerObject;
|
||||
return;
|
||||
}
|
||||
throw new ArgumentException($"Received {value.GetType().Name}", nameof(value));
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString() => _o.ToString();
|
||||
}
|
||||
}
|
||||
138
Spine/Implementations/SpineWrappers/V35/SpineObjectData35.cs
Normal file
138
Spine/Implementations/SpineWrappers/V35/SpineObjectData35.cs
Normal file
@@ -0,0 +1,138 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Frozen;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.Utils;
|
||||
using Spine.SpineWrappers;
|
||||
using Spine.SpineWrappers.Attachments;
|
||||
using SpineRuntime35;
|
||||
using Spine.Implementations.SpineWrappers.V35.Attachments;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V35
|
||||
{
|
||||
[SpineImplementation(3, 5)]
|
||||
internal sealed class SpineObjectData35 : SpineObjectData
|
||||
{
|
||||
private readonly Atlas _atlas;
|
||||
private readonly SkeletonData _skeletonData;
|
||||
private readonly AnimationStateData _animationStateData;
|
||||
|
||||
private readonly ImmutableArray<ISkin> _skins;
|
||||
private readonly FrozenDictionary<string, ISkin> _skinsByName;
|
||||
private readonly FrozenDictionary<string, FrozenDictionary<string, IAttachment>> _slotAttachments;
|
||||
private readonly ImmutableArray<IAnimation> _animations;
|
||||
private readonly FrozenDictionary<string, IAnimation> _animationsByName;
|
||||
|
||||
public SpineObjectData35(string skelPath, string atlasPath, Spine.SpineWrappers.TextureLoader textureLoader)
|
||||
: base(skelPath, atlasPath, textureLoader)
|
||||
{
|
||||
// 加载 atlas
|
||||
try { _atlas = new Atlas(atlasPath, textureLoader); }
|
||||
catch (Exception ex) { throw new InvalidDataException($"Failed to load atlas '{atlasPath}'", ex); }
|
||||
|
||||
try
|
||||
{
|
||||
if (Utf8Validator.IsUtf8(skelPath))
|
||||
{
|
||||
try
|
||||
{
|
||||
_skeletonData = new SkeletonJson(_atlas).ReadSkeletonData(skelPath);
|
||||
}
|
||||
catch
|
||||
{
|
||||
_skeletonData = new SkeletonBinary(_atlas).ReadSkeletonData(skelPath);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
_skeletonData = new SkeletonBinary(_atlas).ReadSkeletonData(skelPath);
|
||||
}
|
||||
catch
|
||||
{
|
||||
_skeletonData = new SkeletonJson(_atlas).ReadSkeletonData(skelPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_atlas.Dispose();
|
||||
throw new InvalidDataException($"Failed to load skeleton file {skelPath}", ex);
|
||||
}
|
||||
|
||||
// 加载动画数据
|
||||
_animationStateData = new AnimationStateData(_skeletonData);
|
||||
|
||||
// 整理皮肤和附件
|
||||
Dictionary<string, Dictionary<string, IAttachment>> slotAttachments = [];
|
||||
List<ISkin> skins = [];
|
||||
Dictionary<string, ISkin> skinsByName = [];
|
||||
foreach (var s in _skeletonData.Skins)
|
||||
{
|
||||
var skin = new Skin35(s);
|
||||
skins.Add(skin);
|
||||
skinsByName[s.Name] = skin;
|
||||
foreach (var (k, att) in s.Attachments)
|
||||
{
|
||||
var slotName = _skeletonData.Slots.Items[k.slotIndex].Name;
|
||||
if (!slotAttachments.TryGetValue(slotName, out var attachments))
|
||||
slotAttachments[slotName] = attachments = [];
|
||||
|
||||
attachments[att.Name] = att switch
|
||||
{
|
||||
RegionAttachment regionAtt => new RegionAttachment35(regionAtt),
|
||||
MeshAttachment meshAtt => new MeshAttachment35(meshAtt),
|
||||
ClippingAttachment clipAtt => new ClippingAttachment35(clipAtt),
|
||||
BoundingBoxAttachment bbAtt => new BoundingBoxAttachment35(bbAtt),
|
||||
PathAttachment pathAtt => new PathAttachment35(pathAtt),
|
||||
PointAttachment ptAtt => new PointAttachment35(ptAtt),
|
||||
_ => throw new InvalidOperationException($"Unrecognized attachment type {att.GetType().FullName}")
|
||||
};
|
||||
}
|
||||
}
|
||||
_slotAttachments = slotAttachments.ToFrozenDictionary(it => it.Key, it => it.Value.ToFrozenDictionary());
|
||||
_skins = skins.ToImmutableArray();
|
||||
_skinsByName = skinsByName.ToFrozenDictionary();
|
||||
|
||||
// 整理所有动画数据
|
||||
List<IAnimation> animations = [];
|
||||
Dictionary<string, IAnimation> animationsByName = [];
|
||||
foreach (var a in _skeletonData.Animations)
|
||||
{
|
||||
var anime = new Animation35(a);
|
||||
animations.Add(anime);
|
||||
animationsByName[anime.Name] = anime;
|
||||
}
|
||||
_animations = animations.ToImmutableArray();
|
||||
_animationsByName = animationsByName.ToFrozenDictionary();
|
||||
}
|
||||
|
||||
public override string SkeletonVersion => _skeletonData.Version;
|
||||
|
||||
public override ImmutableArray<ISkin> Skins => _skins;
|
||||
|
||||
public override FrozenDictionary<string, ISkin> SkinsByName => _skinsByName;
|
||||
|
||||
public override FrozenDictionary<string, FrozenDictionary<string, IAttachment>> SlotAttachments => _slotAttachments;
|
||||
|
||||
public override float DefaultMix { get => _animationStateData.DefaultMix; set => _animationStateData.DefaultMix = value; }
|
||||
|
||||
public override ImmutableArray<IAnimation> Animations => _animations;
|
||||
|
||||
public override FrozenDictionary<string, IAnimation> AnimationsByName => _animationsByName;
|
||||
|
||||
protected override void DisposeAtlas() => _atlas.Dispose();
|
||||
|
||||
public override ISkeleton CreateSkeleton() => new Skeleton35(new(_skeletonData), this);
|
||||
|
||||
public override IAnimationState CreateAnimationState() => new AnimationState35(new(_animationStateData), this);
|
||||
|
||||
public override ISkeletonClipping CreateSkeletonClipping() => new SkeletonClipping35();
|
||||
|
||||
public override ISkin CreateSkin(string name) => new Skin35(name);
|
||||
}
|
||||
}
|
||||
185
Spine/Implementations/SpineWrappers/V35/TrackEntry35.cs
Normal file
185
Spine/Implementations/SpineWrappers/V35/TrackEntry35.cs
Normal file
@@ -0,0 +1,185 @@
|
||||
using Spine.SpineWrappers;
|
||||
using SpineRuntime35;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V35
|
||||
{
|
||||
internal sealed class TrackEntry35(TrackEntry innerObject, AnimationState35 animationState, SpineObjectData35 data): ITrackEntry
|
||||
{
|
||||
private readonly TrackEntry _o = innerObject;
|
||||
private readonly AnimationState35 _animationState = animationState;
|
||||
private readonly SpineObjectData35 _data = data;
|
||||
|
||||
private readonly Dictionary<IAnimationState.TrackEntryDelegate, AnimationState.TrackEntryDelegate> _eventMapping = [];
|
||||
private readonly Dictionary<IAnimationState.TrackEntryDelegate, int> _eventCount = [];
|
||||
|
||||
public TrackEntry InnerObject => _o;
|
||||
|
||||
public event IAnimationState.TrackEntryDelegate? Start
|
||||
{
|
||||
add
|
||||
{
|
||||
if (value is null) return;
|
||||
if (!_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_eventMapping[value] = f = (TrackEntry t) => value(_animationState.GetTrackEntry(t));
|
||||
_eventCount[value] = 0;
|
||||
}
|
||||
_o.Start += f;
|
||||
_eventCount[value]++;
|
||||
}
|
||||
remove
|
||||
{
|
||||
if (value is null) return;
|
||||
if (_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_o.Start -= f;
|
||||
_eventCount[value]--;
|
||||
if (_eventCount[value] <= 0)
|
||||
{
|
||||
_eventMapping.Remove(value);
|
||||
_eventCount.Remove(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public event IAnimationState.TrackEntryDelegate? Interrupt
|
||||
{
|
||||
add
|
||||
{
|
||||
if (value is null) return;
|
||||
if (!_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_eventMapping[value] = f = (TrackEntry t) => value(_animationState.GetTrackEntry(t));
|
||||
_eventCount[value] = 0;
|
||||
}
|
||||
_o.Interrupt += f;
|
||||
_eventCount[value]++;
|
||||
}
|
||||
remove
|
||||
{
|
||||
if (value is null) return;
|
||||
if (_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_o.Interrupt -= f;
|
||||
_eventCount[value]--;
|
||||
if (_eventCount[value] <= 0)
|
||||
{
|
||||
_eventMapping.Remove(value);
|
||||
_eventCount.Remove(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public event IAnimationState.TrackEntryDelegate? End
|
||||
{
|
||||
add
|
||||
{
|
||||
if (value is null) return;
|
||||
if (!_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_eventMapping[value] = f = (TrackEntry t) => value(_animationState.GetTrackEntry(t));
|
||||
_eventCount[value] = 0;
|
||||
}
|
||||
_o.End += f;
|
||||
_eventCount[value]++;
|
||||
}
|
||||
remove
|
||||
{
|
||||
if (value is null) return;
|
||||
if (_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_o.End -= f;
|
||||
_eventCount[value]--;
|
||||
if (_eventCount[value] <= 0)
|
||||
{
|
||||
_eventMapping.Remove(value);
|
||||
_eventCount.Remove(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public event IAnimationState.TrackEntryDelegate? Complete
|
||||
{
|
||||
add
|
||||
{
|
||||
if (value is null) return;
|
||||
if (!_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_eventMapping[value] = f = (TrackEntry t) => value(_animationState.GetTrackEntry(t));
|
||||
_eventCount[value] = 0;
|
||||
}
|
||||
_o.Complete += f;
|
||||
_eventCount[value]++;
|
||||
}
|
||||
remove
|
||||
{
|
||||
if (value is null) return;
|
||||
if (_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_o.Complete -= f;
|
||||
_eventCount[value]--;
|
||||
if (_eventCount[value] <= 0)
|
||||
{
|
||||
_eventMapping.Remove(value);
|
||||
_eventCount.Remove(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public event IAnimationState.TrackEntryDelegate? Dispose
|
||||
{
|
||||
add
|
||||
{
|
||||
if (value is null) return;
|
||||
if (!_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_eventMapping[value] = f = (TrackEntry t) => value(_animationState.GetTrackEntry(t));
|
||||
_eventCount[value] = 0;
|
||||
}
|
||||
_o.Dispose += f;
|
||||
_eventCount[value]++;
|
||||
}
|
||||
remove
|
||||
{
|
||||
if (value is null) return;
|
||||
if (_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_o.Dispose -= f;
|
||||
_eventCount[value]--;
|
||||
if (_eventCount[value] <= 0)
|
||||
{
|
||||
_eventMapping.Remove(value);
|
||||
_eventCount.Remove(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int TrackIndex { get => _o.TrackIndex; }
|
||||
|
||||
public IAnimation Animation { get => _data.AnimationsByName[_o.Animation.Name]; }
|
||||
|
||||
public ITrackEntry? Next { get { var t = _o.Next; return t is null ? null : _animationState.GetTrackEntry(t); } }
|
||||
|
||||
public bool Loop { get => _o.Loop; set => _o.Loop = value; }
|
||||
|
||||
public float TrackTime { get => _o.TrackTime; set => _o.TrackTime = value; }
|
||||
|
||||
public float TimeScale { get => _o.TimeScale; set => _o.TimeScale = value; }
|
||||
|
||||
public float Alpha { get => _o.Alpha; set => _o.Alpha = value; }
|
||||
|
||||
public float MixDuration { get => _o.MixDuration; set => _o.MixDuration = value; }
|
||||
|
||||
public override string ToString() => _o.ToString();
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,8 @@
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\SpineRuntimes\SpineRuntime21\SpineRuntime21.csproj" />
|
||||
<ProjectReference Include="..\SpineRuntimes\SpineRuntime34\SpineRuntime34.csproj" />
|
||||
<ProjectReference Include="..\SpineRuntimes\SpineRuntime35\SpineRuntime35.csproj" />
|
||||
<ProjectReference Include="..\SpineRuntimes\SpineRuntime36\SpineRuntime36.csproj" />
|
||||
<ProjectReference Include="..\SpineRuntimes\SpineRuntime37\SpineRuntime37.csproj" />
|
||||
<ProjectReference Include="..\SpineRuntimes\SpineRuntime38\SpineRuntime38.csproj" />
|
||||
|
||||
@@ -19,6 +19,8 @@ namespace Spine
|
||||
public sealed class SpineVersion : IEquatable<SpineVersion>, IComparable<SpineVersion>
|
||||
{
|
||||
public static readonly SpineVersion V21 = new(typeof(SpineRuntime21.Skeleton));
|
||||
public static readonly SpineVersion V34 = new(typeof(SpineRuntime34.Skeleton));
|
||||
public static readonly SpineVersion V35 = new(typeof(SpineRuntime35.Skeleton));
|
||||
public static readonly SpineVersion V36 = new(typeof(SpineRuntime36.Skeleton));
|
||||
public static readonly SpineVersion V37 = new(typeof(SpineRuntime37.Skeleton));
|
||||
public static readonly SpineVersion V38 = new(typeof(SpineRuntime38.Skeleton));
|
||||
|
||||
@@ -11,6 +11,8 @@ namespace Spine.SpineWrappers
|
||||
/// </summary>
|
||||
public class TextureLoader :
|
||||
SpineRuntime21.TextureLoader,
|
||||
SpineRuntime34.TextureLoader,
|
||||
SpineRuntime35.TextureLoader,
|
||||
SpineRuntime36.TextureLoader,
|
||||
SpineRuntime37.TextureLoader,
|
||||
SpineRuntime38.TextureLoader,
|
||||
@@ -106,6 +108,76 @@ namespace Spine.SpineWrappers
|
||||
page.rendererObject = texture;
|
||||
}
|
||||
|
||||
public virtual void Load(SpineRuntime34.AtlasPage page, string path)
|
||||
{
|
||||
var texture = ReadTexture(path);
|
||||
|
||||
if (page.magFilter == SpineRuntime34.TextureFilter.Linear)
|
||||
{
|
||||
texture.Smooth = true;
|
||||
}
|
||||
if (page.uWrap == SpineRuntime34.TextureWrap.Repeat && page.vWrap == SpineRuntime34.TextureWrap.Repeat)
|
||||
{
|
||||
texture.Repeated = true;
|
||||
}
|
||||
switch (page.minFilter)
|
||||
{
|
||||
case SpineRuntime34.TextureFilter.Linear:
|
||||
texture.Smooth = true;
|
||||
break;
|
||||
case SpineRuntime34.TextureFilter.MipMap:
|
||||
case SpineRuntime34.TextureFilter.MipMapNearestNearest:
|
||||
texture.GenerateMipmap();
|
||||
break;
|
||||
case SpineRuntime34.TextureFilter.MipMapLinearNearest:
|
||||
case SpineRuntime34.TextureFilter.MipMapNearestLinear:
|
||||
case SpineRuntime34.TextureFilter.MipMapLinearLinear:
|
||||
texture.Smooth = true;
|
||||
texture.GenerateMipmap();
|
||||
break;
|
||||
}
|
||||
|
||||
if (ForceNearest) texture.Smooth = false;
|
||||
if (ForceMipmap) texture.GenerateMipmap();
|
||||
|
||||
page.rendererObject = texture;
|
||||
}
|
||||
|
||||
public virtual void Load(SpineRuntime35.AtlasPage page, string path)
|
||||
{
|
||||
var texture = ReadTexture(path);
|
||||
|
||||
if (page.magFilter == SpineRuntime35.TextureFilter.Linear)
|
||||
{
|
||||
texture.Smooth = true;
|
||||
}
|
||||
if (page.uWrap == SpineRuntime35.TextureWrap.Repeat && page.vWrap == SpineRuntime35.TextureWrap.Repeat)
|
||||
{
|
||||
texture.Repeated = true;
|
||||
}
|
||||
switch (page.minFilter)
|
||||
{
|
||||
case SpineRuntime35.TextureFilter.Linear:
|
||||
texture.Smooth = true;
|
||||
break;
|
||||
case SpineRuntime35.TextureFilter.MipMap:
|
||||
case SpineRuntime35.TextureFilter.MipMapNearestNearest:
|
||||
texture.GenerateMipmap();
|
||||
break;
|
||||
case SpineRuntime35.TextureFilter.MipMapLinearNearest:
|
||||
case SpineRuntime35.TextureFilter.MipMapNearestLinear:
|
||||
case SpineRuntime35.TextureFilter.MipMapLinearLinear:
|
||||
texture.Smooth = true;
|
||||
texture.GenerateMipmap();
|
||||
break;
|
||||
}
|
||||
|
||||
if (ForceNearest) texture.Smooth = false;
|
||||
if (ForceMipmap) texture.GenerateMipmap();
|
||||
|
||||
page.rendererObject = texture;
|
||||
}
|
||||
|
||||
public virtual void Load(SpineRuntime36.AtlasPage page, string path)
|
||||
{
|
||||
var texture = ReadTexture(path);
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace SpineRuntime21 {
|
||||
static readonly Animation EmptyAnimation = new Animation("<empty>", new List<Timeline>(), 0);
|
||||
private AnimationStateData data;
|
||||
|
||||
Pool<TrackEntry> trackEntryPool = new Pool<TrackEntry>();
|
||||
private readonly Pool<TrackEntry> trackEntryPool = new Pool<TrackEntry>();
|
||||
private List<TrackEntry> tracks = new List<TrackEntry>();
|
||||
private List<Event> events = new List<Event>();
|
||||
private float timeScale = 1;
|
||||
|
||||
@@ -34,26 +34,25 @@ using System.Text;
|
||||
|
||||
namespace SpineRuntime34 {
|
||||
public class AnimationState {
|
||||
private AnimationStateData data;
|
||||
static readonly Animation EmptyAnimation = new Animation("<empty>", new ExposedList<Timeline>(), 0);
|
||||
private AnimationStateData data;
|
||||
private ExposedList<TrackEntry> tracks = new ExposedList<TrackEntry>();
|
||||
private ExposedList<Event> events = new ExposedList<Event>();
|
||||
private float timeScale = 1;
|
||||
|
||||
public AnimationStateData Data { get { return data; } }
|
||||
/// <summary>A list of tracks that have animations, which may contain nulls.</summary>
|
||||
public ExposedList<TrackEntry> Tracks { get { return tracks; } }
|
||||
private readonly Pool<TrackEntry> trackEntryPool = new Pool<TrackEntry>();
|
||||
|
||||
/// <summary>A list of tracks that have animations, which may contain nulls.</summary>
|
||||
public ExposedList<TrackEntry> Tracks { get { return tracks; } }
|
||||
public float TimeScale { get { return timeScale; } set { timeScale = value; } }
|
||||
|
||||
public delegate void StartEndDelegate (AnimationState state, int trackIndex);
|
||||
public event StartEndDelegate Start;
|
||||
public event StartEndDelegate End;
|
||||
public delegate void TrackEntryDelegate(TrackEntry trackEntry);
|
||||
public event TrackEntryDelegate Start, End, Complete;
|
||||
|
||||
public delegate void EventDelegate (AnimationState state, int trackIndex, Event e);
|
||||
public delegate void EventDelegate (AnimationState state, int trackIndex, Event e);
|
||||
public event EventDelegate Event;
|
||||
|
||||
public delegate void CompleteDelegate (AnimationState state, int trackIndex, int loopCount);
|
||||
public event CompleteDelegate Complete;
|
||||
|
||||
public AnimationState (AnimationStateData data) {
|
||||
if (data == null) throw new ArgumentNullException("data", "data cannot be null.");
|
||||
this.data = data;
|
||||
@@ -78,8 +77,8 @@ namespace SpineRuntime34 {
|
||||
// Check if completed the animation or a loop iteration.
|
||||
if (current.loop ? (current.lastTime % endTime > time % endTime) : (current.lastTime < endTime && time >= endTime)) {
|
||||
int count = (int)(time / endTime);
|
||||
current.OnComplete(this, i, count);
|
||||
if (Complete != null) Complete(this, i, count);
|
||||
current.OnComplete();
|
||||
if (Complete != null) Complete(current);
|
||||
}
|
||||
|
||||
TrackEntry next = current.next;
|
||||
@@ -149,11 +148,18 @@ namespace SpineRuntime34 {
|
||||
TrackEntry current = tracks.Items[trackIndex];
|
||||
if (current == null) return;
|
||||
|
||||
current.OnEnd(this, trackIndex);
|
||||
if (End != null) End(this, trackIndex);
|
||||
current.OnEnd();
|
||||
if (End != null) End(current);
|
||||
|
||||
tracks.Items[trackIndex] = null;
|
||||
}
|
||||
|
||||
while (current is not null)
|
||||
{
|
||||
var tmp = current.next;
|
||||
trackEntryPool.Free(current);
|
||||
current = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
private TrackEntry ExpandToIndex (int index) {
|
||||
if (index < tracks.Count) return tracks.Items[index];
|
||||
@@ -168,8 +174,8 @@ namespace SpineRuntime34 {
|
||||
TrackEntry previous = current.previous;
|
||||
current.previous = null;
|
||||
|
||||
current.OnEnd(this, index);
|
||||
if (End != null) End(this, index);
|
||||
current.OnEnd();
|
||||
if (End != null) End(current);
|
||||
|
||||
entry.mixDuration = data.GetMix(current.animation, entry.animation);
|
||||
if (entry.mixDuration > 0) {
|
||||
@@ -184,8 +190,15 @@ namespace SpineRuntime34 {
|
||||
|
||||
tracks.Items[index] = entry;
|
||||
|
||||
entry.OnStart(this, index);
|
||||
if (Start != null) Start(this, index);
|
||||
while (current is not null)
|
||||
{
|
||||
var tmp = current.next;
|
||||
trackEntryPool.Free(current);
|
||||
current = tmp;
|
||||
}
|
||||
|
||||
entry.OnStart();
|
||||
if (Start != null) Start(entry);
|
||||
}
|
||||
|
||||
/// <seealso cref="SetAnimation(int, Animation, bool)" />
|
||||
@@ -198,8 +211,9 @@ namespace SpineRuntime34 {
|
||||
/// <summary>Set the current animation. Any queued animations are cleared.</summary>
|
||||
public TrackEntry SetAnimation (int trackIndex, Animation animation, bool loop) {
|
||||
if (animation == null) throw new ArgumentNullException("animation", "animation cannot be null.");
|
||||
TrackEntry entry = new TrackEntry();
|
||||
entry.animation = animation;
|
||||
TrackEntry entry = trackEntryPool.Obtain();
|
||||
entry.trackIndex = trackIndex;
|
||||
entry.animation = animation;
|
||||
entry.loop = loop;
|
||||
entry.time = 0;
|
||||
entry.endTime = animation.Duration;
|
||||
@@ -218,8 +232,10 @@ namespace SpineRuntime34 {
|
||||
/// <param name="delay">May be <= 0 to use duration of previous animation minus any mix duration plus the negative delay.</param>
|
||||
public TrackEntry AddAnimation (int trackIndex, Animation animation, bool loop, float delay) {
|
||||
if (animation == null) throw new ArgumentNullException("animation", "animation cannot be null.");
|
||||
TrackEntry entry = new TrackEntry();
|
||||
entry.animation = animation;
|
||||
TrackEntry entry = trackEntryPool.Obtain();
|
||||
entry.trackIndex = trackIndex;
|
||||
entry.animation = animation;
|
||||
entry.animation = animation;
|
||||
entry.loop = loop;
|
||||
entry.time = 0;
|
||||
entry.endTime = animation.Duration;
|
||||
@@ -243,8 +259,48 @@ namespace SpineRuntime34 {
|
||||
return entry;
|
||||
}
|
||||
|
||||
/// <returns>May be null.</returns>
|
||||
public TrackEntry GetCurrent (int trackIndex) {
|
||||
/// <summary>
|
||||
/// Sets an empty animation for a track, discarding any queued animations, and mixes to it over the specified mix duration.</summary>
|
||||
public TrackEntry SetEmptyAnimation(int trackIndex, float mixDuration)
|
||||
{
|
||||
TrackEntry entry = SetAnimation(trackIndex, AnimationState.EmptyAnimation, false);
|
||||
entry.mixDuration = mixDuration;
|
||||
entry.endTime = mixDuration;
|
||||
return entry;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds an empty animation to be played after the current or last queued animation for a track, and mixes to it over the
|
||||
/// specified mix duration.</summary>
|
||||
/// <returns>
|
||||
/// A track entry to allow further customization of animation playback. References to the track entry must not be kept after <see cref="AnimationState.Dispose"/>.
|
||||
/// </returns>
|
||||
/// <param name="trackIndex">Track number.</param>
|
||||
/// <param name="mixDuration">Mix duration.</param>
|
||||
/// <param name="delay">Seconds to begin this animation after the start of the previous animation. May be <= 0 to use the animation
|
||||
/// duration of the previous track minus any mix duration plus the negative delay.</param>
|
||||
public TrackEntry AddEmptyAnimation(int trackIndex, float mixDuration, float delay)
|
||||
{
|
||||
if (delay <= 0) delay -= mixDuration;
|
||||
TrackEntry entry = AddAnimation(trackIndex, AnimationState.EmptyAnimation, false, delay);
|
||||
entry.mixDuration = mixDuration;
|
||||
entry.endTime = mixDuration;
|
||||
return entry;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets an empty animation for every track, discarding any queued animations, and mixes to it over the specified mix duration.</summary>
|
||||
public void SetEmptyAnimations(float mixDuration)
|
||||
{
|
||||
for (int i = 0, n = tracks.Count; i < n; i++)
|
||||
{
|
||||
TrackEntry current = tracks.Items[i];
|
||||
if (current != null) SetEmptyAnimation(i, mixDuration);
|
||||
}
|
||||
}
|
||||
|
||||
/// <returns>May be null.</returns>
|
||||
public TrackEntry GetCurrent (int trackIndex) {
|
||||
if (trackIndex >= tracks.Count) return null;
|
||||
return tracks.Items[trackIndex];
|
||||
}
|
||||
@@ -262,15 +318,17 @@ namespace SpineRuntime34 {
|
||||
}
|
||||
}
|
||||
|
||||
public class TrackEntry {
|
||||
public class TrackEntry : Pool<TrackEntry>.IPoolable {
|
||||
internal TrackEntry next, previous;
|
||||
internal Animation animation;
|
||||
internal int trackIndex;
|
||||
internal Animation animation;
|
||||
internal bool loop;
|
||||
internal float delay, time, lastTime = -1, endTime, timeScale = 1;
|
||||
internal float mixTime, mixDuration, mix = 1;
|
||||
|
||||
public Animation Animation { get { return animation; } }
|
||||
public float Delay { get { return delay; } set { delay = value; } }
|
||||
public int TrackIndex { get { return trackIndex; } }
|
||||
public Animation Animation { get { return animation; } }
|
||||
public float Delay { get { return delay; } set { delay = value; } }
|
||||
public float Time { get { return time; } set { time = value; } }
|
||||
public float LastTime { get { return lastTime; } set { lastTime = value; } }
|
||||
public float EndTime { get { return endTime; } set { endTime = value; } }
|
||||
@@ -278,29 +336,111 @@ namespace SpineRuntime34 {
|
||||
public float Mix { get { return mix; } set { mix = value; } }
|
||||
public bool Loop { get { return loop; } set { loop = value; } }
|
||||
|
||||
public event AnimationState.StartEndDelegate Start;
|
||||
public event AnimationState.StartEndDelegate End;
|
||||
public event AnimationState.EventDelegate Event;
|
||||
public event AnimationState.CompleteDelegate Complete;
|
||||
/// <summary>
|
||||
/// Seconds for mixing from the previous animation to this animation. Defaults to the value provided by
|
||||
/// <see cref="AnimationStateData"/> based on the animation before this animation (if any).
|
||||
///
|
||||
/// The mix duration can be set manually rather than use the value from AnimationStateData.GetMix.
|
||||
/// In that case, the mixDuration must be set before <see cref="AnimationState.Update(float)"/> is next called.
|
||||
/// <para>
|
||||
/// When using <seealso cref="AnimationState.AddAnimation(int, Animation, bool, float)"/> with a
|
||||
/// <code>delay</code> less than or equal to 0, note the <seealso cref="Delay"/> is set using the mix duration from the <see cref=" AnimationStateData"/>
|
||||
/// </para>
|
||||
///
|
||||
/// </summary>
|
||||
public float MixDuration { get { return mixDuration; } set { mixDuration = value; } }
|
||||
|
||||
internal void OnStart (AnimationState state, int index) {
|
||||
if (Start != null) Start(state, index);
|
||||
}
|
||||
/// <summary>
|
||||
/// The animation queued to start after this animation, or null.</summary>
|
||||
public TrackEntry Next { get { return next; } }
|
||||
|
||||
internal void OnEnd (AnimationState state, int index) {
|
||||
if (End != null) End(state, index);
|
||||
}
|
||||
public event AnimationState.TrackEntryDelegate Start, End, Complete;
|
||||
public event AnimationState.EventDelegate Event;
|
||||
|
||||
internal void OnEvent (AnimationState state, int index, Event e) {
|
||||
if (Event != null) Event(state, index, e);
|
||||
}
|
||||
// IPoolable.Reset()
|
||||
public void Reset()
|
||||
{
|
||||
next = null;
|
||||
previous = null;
|
||||
animation = null;
|
||||
|
||||
internal void OnComplete (AnimationState state, int index, int loopCount) {
|
||||
if (Complete != null) Complete(state, index, loopCount);
|
||||
}
|
||||
Start = null;
|
||||
End = null;
|
||||
Complete = null;
|
||||
Event = null;
|
||||
}
|
||||
|
||||
override public String ToString () {
|
||||
internal void OnStart() { if (Start != null) Start(this); }
|
||||
internal void OnEnd() { if (End != null) End(this); }
|
||||
internal void OnComplete() { if (Complete != null) Complete(this); }
|
||||
|
||||
internal void OnEvent(AnimationState state, int index, Event e)
|
||||
{
|
||||
if (Event != null) Event(state, index, e);
|
||||
}
|
||||
|
||||
override public String ToString () {
|
||||
return animation == null ? "<none>" : animation.name;
|
||||
}
|
||||
}
|
||||
|
||||
public class Pool<T> where T : class, new()
|
||||
{
|
||||
public readonly int max;
|
||||
readonly Stack<T> freeObjects;
|
||||
|
||||
public int Count { get { return freeObjects.Count; } }
|
||||
public int Peak { get; private set; }
|
||||
|
||||
public Pool(int initialCapacity = 16, int max = int.MaxValue)
|
||||
{
|
||||
freeObjects = new Stack<T>(initialCapacity);
|
||||
this.max = max;
|
||||
}
|
||||
|
||||
public T Obtain()
|
||||
{
|
||||
return freeObjects.Count == 0 ? new T() : freeObjects.Pop();
|
||||
}
|
||||
|
||||
public void Free(T obj)
|
||||
{
|
||||
if (obj == null) throw new ArgumentNullException("obj", "obj cannot be null");
|
||||
if (freeObjects.Count < max)
|
||||
{
|
||||
freeObjects.Push(obj);
|
||||
Peak = Math.Max(Peak, freeObjects.Count);
|
||||
}
|
||||
Reset(obj);
|
||||
}
|
||||
|
||||
// protected void FreeAll (List<T> objects) {
|
||||
// if (objects == null) throw new ArgumentNullException("objects", "objects cannot be null.");
|
||||
// var freeObjects = this.freeObjects;
|
||||
// int max = this.max;
|
||||
// for (int i = 0; i < objects.Count; i++) {
|
||||
// T obj = objects[i];
|
||||
// if (obj == null) continue;
|
||||
// if (freeObjects.Count < max) freeObjects.Push(obj);
|
||||
// Reset(obj);
|
||||
// }
|
||||
// Peak = Math.Max(Peak, freeObjects.Count);
|
||||
// }
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
freeObjects.Clear();
|
||||
}
|
||||
|
||||
protected void Reset(T obj)
|
||||
{
|
||||
var poolable = obj as IPoolable;
|
||||
if (poolable != null) poolable.Reset();
|
||||
}
|
||||
|
||||
public interface IPoolable
|
||||
{
|
||||
void Reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,22 +105,13 @@ namespace SpineRuntime34 {
|
||||
Bone parent = this.parent;
|
||||
if (parent == null) { // Root bone.
|
||||
Skeleton skeleton = this.skeleton;
|
||||
if (skeleton.flipX) {
|
||||
x = -x;
|
||||
la = -la;
|
||||
lb = -lb;
|
||||
}
|
||||
if (skeleton.flipY != yDown) {
|
||||
y = -y;
|
||||
lc = -lc;
|
||||
ld = -ld;
|
||||
}
|
||||
a = la;
|
||||
b = lb;
|
||||
c = lc;
|
||||
d = ld;
|
||||
worldX = x;
|
||||
worldY = y;
|
||||
float sx = skeleton.scaleX, sy = skeleton.scaleY;
|
||||
a = la * sx;
|
||||
b = lb * sx;
|
||||
c = lc * sy;
|
||||
d = ld * sy;
|
||||
worldX = x * sx;
|
||||
worldY = y * sy;
|
||||
worldSignX = Math.Sign(scaleX);
|
||||
worldSignY = Math.Sign(scaleY);
|
||||
return;
|
||||
@@ -196,15 +187,11 @@ namespace SpineRuntime34 {
|
||||
c = lc;
|
||||
d = ld;
|
||||
}
|
||||
if (skeleton.flipX) {
|
||||
a = -a;
|
||||
b = -b;
|
||||
}
|
||||
if (skeleton.flipY != yDown) {
|
||||
c = -c;
|
||||
d = -d;
|
||||
}
|
||||
}
|
||||
a *= skeleton.scaleX;
|
||||
b *= skeleton.scaleX;
|
||||
c *= skeleton.scaleY;
|
||||
d *= skeleton.scaleY;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetToSetupPose () {
|
||||
|
||||
@@ -44,8 +44,8 @@ namespace SpineRuntime34 {
|
||||
internal Skin skin;
|
||||
internal float r = 1, g = 1, b = 1, a = 1;
|
||||
internal float time;
|
||||
internal bool flipX, flipY;
|
||||
internal float x, y;
|
||||
internal float scaleX = 1, scaleY = 1;
|
||||
internal float x, y;
|
||||
|
||||
public SkeletonData Data { get { return data; } }
|
||||
public ExposedList<Bone> Bones { get { return bones; } }
|
||||
@@ -63,10 +63,16 @@ namespace SpineRuntime34 {
|
||||
public float Time { get { return time; } set { time = value; } }
|
||||
public float X { get { return x; } set { x = value; } }
|
||||
public float Y { get { return y; } set { y = value; } }
|
||||
public bool FlipX { get { return flipX; } set { flipX = value; } }
|
||||
public bool FlipY { get { return flipY; } set { flipY = value; } }
|
||||
public float ScaleX { get { return scaleX; } set { scaleX = value; } }
|
||||
public float ScaleY { get { return scaleY; } set { scaleY = value; } }
|
||||
|
||||
public Bone RootBone {
|
||||
[Obsolete("Use ScaleX instead. FlipX is when ScaleX is negative.")]
|
||||
public bool FlipX { get { return scaleX < 0; } set { scaleX = value ? -1f : 1f; } }
|
||||
|
||||
[Obsolete("Use ScaleY instead. FlipY is when ScaleY is negative.")]
|
||||
public bool FlipY { get { return scaleY < 0; } set { scaleY = value ? -1f : 1f; } }
|
||||
|
||||
public Bone RootBone {
|
||||
get { return bones.Count == 0 ? null : bones.Items[0]; }
|
||||
}
|
||||
|
||||
@@ -453,5 +459,50 @@ namespace SpineRuntime34 {
|
||||
public void Update (float delta) {
|
||||
time += delta;
|
||||
}
|
||||
}
|
||||
|
||||
public void GetBounds(out float x, out float y, out float width, out float height)
|
||||
{
|
||||
float[] temp = new float[8];
|
||||
float minX = int.MaxValue, minY = int.MaxValue, maxX = int.MinValue, maxY = int.MinValue;
|
||||
for (int i = 0, n = drawOrder.Count; i < n; i++)
|
||||
{
|
||||
Slot slot = drawOrder.Items[i];
|
||||
int verticesLength = 0;
|
||||
float[] vertices = null;
|
||||
Attachment attachment = slot.Attachment;
|
||||
if (attachment is RegionAttachment regionAttachment)
|
||||
{
|
||||
verticesLength = 8;
|
||||
vertices = temp;
|
||||
if (vertices.Length < 8) vertices = temp = new float[8];
|
||||
regionAttachment.ComputeWorldVertices(slot.Bone, temp);
|
||||
}
|
||||
else if (attachment is MeshAttachment meshAttachment)
|
||||
{
|
||||
|
||||
MeshAttachment mesh = meshAttachment;
|
||||
verticesLength = mesh.Vertices.Length;
|
||||
vertices = temp;
|
||||
if (vertices.Length < verticesLength) vertices = temp = new float[verticesLength];
|
||||
mesh.ComputeWorldVertices(slot, temp);
|
||||
}
|
||||
|
||||
if (vertices != null)
|
||||
{
|
||||
for (int ii = 0; ii < verticesLength; ii += 2)
|
||||
{
|
||||
float vx = vertices[ii], vy = vertices[ii + 1];
|
||||
minX = Math.Min(minX, vx);
|
||||
minY = Math.Min(minY, vy);
|
||||
maxX = Math.Max(maxX, vx);
|
||||
maxY = Math.Max(maxY, vy);
|
||||
}
|
||||
}
|
||||
}
|
||||
x = minX;
|
||||
y = minY;
|
||||
width = maxX - minX;
|
||||
height = maxY - minY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,9 +47,9 @@ namespace SpineRuntime35 {
|
||||
|
||||
private float timeScale = 1;
|
||||
|
||||
Pool<TrackEntry> trackEntryPool = new Pool<TrackEntry>();
|
||||
private readonly Pool<TrackEntry> trackEntryPool = new Pool<TrackEntry>();
|
||||
|
||||
public AnimationStateData Data { get { return data; } }
|
||||
public AnimationStateData Data { get { return data; } }
|
||||
/// <summary>A list of tracks that have animations, which may contain nulls.</summary>
|
||||
public ExposedList<TrackEntry> Tracks { get { return tracks; } }
|
||||
public float TimeScale { get { return timeScale; } set { timeScale = value; } }
|
||||
|
||||
@@ -152,37 +152,19 @@ namespace SpineRuntime35 {
|
||||
|
||||
Bone parent = this.parent;
|
||||
if (parent == null) { // Root bone.
|
||||
float rotationY = rotation + 90 + shearY;
|
||||
float la = MathUtils.CosDeg(rotation + shearX) * scaleX;
|
||||
float lb = MathUtils.CosDeg(rotationY) * scaleY;
|
||||
float lc = MathUtils.SinDeg(rotation + shearX) * scaleX;
|
||||
float ld = MathUtils.SinDeg(rotationY) * scaleY;
|
||||
if (skeleton.flipX) {
|
||||
x = -x;
|
||||
la = -la;
|
||||
lb = -lb;
|
||||
}
|
||||
if (skeleton.flipY != yDown) {
|
||||
y = -y;
|
||||
lc = -lc;
|
||||
ld = -ld;
|
||||
}
|
||||
a = la;
|
||||
b = lb;
|
||||
c = lc;
|
||||
d = ld;
|
||||
worldX = x + skeleton.x;
|
||||
worldY = y + skeleton.y;
|
||||
// worldSignX = Math.Sign(scaleX);
|
||||
// worldSignY = Math.Sign(scaleY);
|
||||
float rotationY = rotation + 90 + shearY, sx = skeleton.scaleX, sy = skeleton.scaleY;
|
||||
a = MathUtils.CosDeg(rotation + shearX) * scaleX * sx;
|
||||
b = MathUtils.CosDeg(rotationY) * scaleY * sx;
|
||||
c = MathUtils.SinDeg(rotation + shearX) * scaleX * sy;
|
||||
d = MathUtils.SinDeg(rotationY) * scaleY * sy;
|
||||
worldX = x * sx + skeleton.x;
|
||||
worldY = y * sy + skeleton.y;
|
||||
return;
|
||||
}
|
||||
|
||||
float pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d;
|
||||
worldX = pa * x + pb * y + parent.worldX;
|
||||
worldY = pc * x + pd * y + parent.worldY;
|
||||
// worldSignX = parent.worldSignX * Math.Sign(scaleX);
|
||||
// worldSignY = parent.worldSignY * Math.Sign(scaleY);
|
||||
|
||||
switch (data.transformMode) {
|
||||
case TransformMode.Normal: {
|
||||
@@ -232,13 +214,16 @@ namespace SpineRuntime35 {
|
||||
case TransformMode.NoScale:
|
||||
case TransformMode.NoScaleOrReflection: {
|
||||
float cos = MathUtils.CosDeg(rotation), sin = MathUtils.SinDeg(rotation);
|
||||
float za = pa * cos + pb * sin;
|
||||
float zc = pc * cos + pd * sin;
|
||||
float za = (pa * cos + pb * sin) / skeleton.scaleX;
|
||||
float zc = (pc * cos + pd * sin) / skeleton.scaleY;
|
||||
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);
|
||||
if (data.transformMode == TransformMode.NoScale
|
||||
&& (pa * pd - pb * pc < 0) != (skeleton.scaleX < 0 != skeleton.scaleY < 0)) s = -s;
|
||||
|
||||
float r = MathUtils.PI / 2 + MathUtils.Atan2(zc, za);
|
||||
float zb = MathUtils.Cos(r) * s;
|
||||
float zd = MathUtils.Sin(r) * s;
|
||||
@@ -250,23 +235,15 @@ namespace SpineRuntime35 {
|
||||
b = za * lb + zb * ld;
|
||||
c = zc * la + zd * lc;
|
||||
d = zc * lb + zd * ld;
|
||||
if (data.transformMode != TransformMode.NoScaleOrReflection ? pa * pd - pb * pc < 0 : skeleton.flipX != skeleton.flipY) {
|
||||
b = -b;
|
||||
d = -d;
|
||||
}
|
||||
return;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (skeleton.flipX) {
|
||||
a = -a;
|
||||
b = -b;
|
||||
}
|
||||
if (skeleton.flipY != Bone.yDown) {
|
||||
c = -c;
|
||||
d = -d;
|
||||
}
|
||||
}
|
||||
a *= skeleton.scaleX;
|
||||
b *= skeleton.scaleX;
|
||||
c *= skeleton.scaleY;
|
||||
d *= skeleton.scaleY;
|
||||
}
|
||||
|
||||
public void SetToSetupPose () {
|
||||
BoneData data = this.data;
|
||||
|
||||
@@ -45,8 +45,8 @@ namespace SpineRuntime35 {
|
||||
internal Skin skin;
|
||||
internal float r = 1, g = 1, b = 1, a = 1;
|
||||
internal float time;
|
||||
internal bool flipX, flipY;
|
||||
internal float x, y;
|
||||
internal float scaleX = 1, scaleY = 1;
|
||||
internal float x, y;
|
||||
|
||||
public SkeletonData Data { get { return data; } }
|
||||
public ExposedList<Bone> Bones { get { return bones; } }
|
||||
@@ -64,8 +64,14 @@ namespace SpineRuntime35 {
|
||||
public float Time { get { return time; } set { time = value; } }
|
||||
public float X { get { return x; } set { x = value; } }
|
||||
public float Y { get { return y; } set { y = value; } }
|
||||
public bool FlipX { get { return flipX; } set { flipX = value; } }
|
||||
public bool FlipY { get { return flipY; } set { flipY = value; } }
|
||||
public float ScaleX { get { return scaleX; } set { scaleX = value; } }
|
||||
public float ScaleY { get { return scaleY; } set { scaleY = value; } }
|
||||
|
||||
[Obsolete("Use ScaleX instead. FlipX is when ScaleX is negative.")]
|
||||
public bool FlipX { get { return scaleX < 0; } set { scaleX = value ? -1f : 1f; } }
|
||||
|
||||
[Obsolete("Use ScaleY instead. FlipY is when ScaleY is negative.")]
|
||||
public bool FlipY { get { return scaleY < 0; } set { scaleY = value ? -1f : 1f; } }
|
||||
|
||||
public Bone RootBone {
|
||||
get { return bones.Count == 0 ? null : bones.Items[0]; }
|
||||
|
||||
@@ -38,8 +38,8 @@ namespace SpineRuntime36 {
|
||||
|
||||
private AnimationStateData data;
|
||||
|
||||
Pool<TrackEntry> trackEntryPool = new Pool<TrackEntry>();
|
||||
private readonly ExposedList<TrackEntry> tracks = new ExposedList<TrackEntry>();
|
||||
private readonly Pool<TrackEntry> trackEntryPool = new Pool<TrackEntry>();
|
||||
private readonly ExposedList<TrackEntry> tracks = new ExposedList<TrackEntry>();
|
||||
private readonly ExposedList<Event> events = new ExposedList<Event>();
|
||||
private readonly EventQueue queue; // Initialized by constructor.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user