增加多轨方法

This commit is contained in:
ww-rm
2025-04-02 11:43:25 +08:00
parent bb529729b6
commit 8b7866d37f
8 changed files with 257 additions and 90 deletions

View File

@@ -109,7 +109,7 @@ namespace SpineViewer.Spine.Implementations.Spine
var pos = position; var pos = position;
var fX = flipX; var fX = flipX;
var fY = flipY; var fY = flipY;
var animation = track0Animation; // TODO: 适配多轨道 var animations = animationState.Tracks.Where(te => te is not null).Select(te => te.Animation.Name).ToArray();
var sk = skin; var sk = skin;
var val = Math.Max(value, SCALE_MIN); var val = Math.Max(value, SCALE_MIN);
@@ -133,7 +133,7 @@ namespace SpineViewer.Spine.Implementations.Spine
position = pos; position = pos;
flipX = fX; flipX = fX;
flipY = fY; flipY = fY;
track0Animation = animation; // TODO: 适配多轨道 for (int i = 0; i < animations.Length; i++) setAnimation(i, animations[i]);
skin = sk; skin = sk;
} }
} }
@@ -171,18 +171,22 @@ namespace SpineViewer.Spine.Implementations.Spine
} }
} }
protected override string track0Animation protected override int[] trackIndices => animationState.Tracks.Select((_, i) => i).Where(i => animationState.Tracks[i] is not null).ToArray();
protected override string getAnimation(int track) => animationState.GetCurrent(track)?.Animation.Name ?? EMPTY_ANIMATION;
protected override void setAnimation(int track, string name)
{ {
get => animationState.GetCurrent(0)?.Animation.Name ?? EMPTY_ANIMATION; if (name == EMPTY_ANIMATION)
set animationState.SetAnimation(track, EmptyAnimation, false);
{ else if (animationNames.Contains(name))
if (value == EMPTY_ANIMATION) animationState.SetAnimation(track, name, true);
animationState.SetAnimation(0, EmptyAnimation, false);
else if (animationNames.Contains(value))
animationState.SetAnimation(0, value, true);
}
} }
protected override void clearTrack(int i) => animationState.ClearTrack(i);
public override float GetAnimationDuration(string name) { return skeletonData.FindAnimation(name)?.Duration ?? 0f; }
protected override RectangleF bounds protected override RectangleF bounds
{ {
get get
@@ -233,8 +237,6 @@ namespace SpineViewer.Spine.Implementations.Spine
} }
} }
public override float GetAnimationDuration(string name) { return skeletonData.FindAnimation(name)?.Duration ?? 0f; }
protected override void update(float delta) protected override void update(float delta)
{ {
animationState.Update(delta); animationState.Update(delta);

View File

@@ -108,7 +108,7 @@ namespace SpineViewer.Spine.Implementations.Spine
var pos = position; var pos = position;
var fX = flipX; var fX = flipX;
var fY = flipY; var fY = flipY;
var animation = track0Animation; // TODO: 适配多轨道 var animations = animationState.Tracks.Where(te => te is not null).Select(te => te.Animation.Name).ToArray();
var sk = skin; var sk = skin;
var val = Math.Max(value, SCALE_MIN); var val = Math.Max(value, SCALE_MIN);
@@ -132,7 +132,7 @@ namespace SpineViewer.Spine.Implementations.Spine
position = pos; position = pos;
flipX = fX; flipX = fX;
flipY = fY; flipY = fY;
track0Animation = animation; // TODO: 适配多轨道 for (int i = 0; i < animations.Length; i++) setAnimation(i, animations[i]);
skin = sk; skin = sk;
} }
} }
@@ -170,18 +170,22 @@ namespace SpineViewer.Spine.Implementations.Spine
} }
} }
protected override string track0Animation protected override int[] trackIndices => animationState.Tracks.Select((_, i) => i).Where(i => animationState.Tracks.Items[i] is not null).ToArray();
protected override string getAnimation(int track) => animationState.GetCurrent(track)?.Animation.Name ?? EMPTY_ANIMATION;
protected override void setAnimation(int track, string name)
{ {
get => animationState.GetCurrent(0)?.Animation.Name ?? EMPTY_ANIMATION; if (name == EMPTY_ANIMATION)
set animationState.SetAnimation(track, EmptyAnimation, false);
{ else if (animationNames.Contains(name))
if (value == EMPTY_ANIMATION) animationState.SetAnimation(track, name, true);
animationState.SetAnimation(0, EmptyAnimation, false);
else if (animationNames.Contains(value))
animationState.SetAnimation(0, value, true);
}
} }
protected override void clearTrack(int i) => animationState.ClearTrack(i);
public override float GetAnimationDuration(string name) { return skeletonData.FindAnimation(name)?.Duration ?? 0f; }
protected override RectangleF bounds protected override RectangleF bounds
{ {
get get
@@ -192,8 +196,6 @@ namespace SpineViewer.Spine.Implementations.Spine
} }
} }
public override float GetAnimationDuration(string name) { return skeletonData.FindAnimation(name)?.Duration ?? 0f; }
protected override void update(float delta) protected override void update(float delta)
{ {
animationState.Update(delta); animationState.Update(delta);

View File

@@ -140,18 +140,22 @@ namespace SpineViewer.Spine.Implementations.Spine
} }
} }
protected override string track0Animation protected override int[] trackIndices => animationState.Tracks.Select((_, i) => i).Where(i => animationState.Tracks.Items[i] is not null).ToArray();
protected override string getAnimation(int track) => animationState.GetCurrent(track)?.Animation.Name ?? EMPTY_ANIMATION;
protected override void setAnimation(int track, string name)
{ {
get => animationState.GetCurrent(0)?.Animation.Name ?? EMPTY_ANIMATION; if (name == EMPTY_ANIMATION)
set animationState.SetAnimation(track, EmptyAnimation, false);
{ else if (animationNames.Contains(name))
if (value == EMPTY_ANIMATION) animationState.SetAnimation(track, name, true);
animationState.SetAnimation(0, EmptyAnimation, false);
else if (animationNames.Contains(value))
animationState.SetAnimation(0, value, true);
}
} }
protected override void clearTrack(int i) => animationState.ClearTrack(i);
public override float GetAnimationDuration(string name) { return skeletonData.FindAnimation(name)?.Duration ?? 0f; }
protected override RectangleF bounds protected override RectangleF bounds
{ {
get get
@@ -162,8 +166,6 @@ namespace SpineViewer.Spine.Implementations.Spine
} }
} }
public override float GetAnimationDuration(string name) { return skeletonData.FindAnimation(name)?.Duration ?? 0f; }
protected override void update(float delta) protected override void update(float delta)
{ {
animationState.Update(delta); animationState.Update(delta);

View File

@@ -146,18 +146,22 @@ namespace SpineViewer.Spine.Implementations.Spine
} }
} }
protected override string track0Animation protected override int[] trackIndices => animationState.Tracks.Select((_, i) => i).Where(i => animationState.Tracks.Items[i] is not null).ToArray();
protected override string getAnimation(int track) => animationState.GetCurrent(track)?.Animation.Name ?? EMPTY_ANIMATION;
protected override void setAnimation(int track, string name)
{ {
get => animationState.GetCurrent(0)?.Animation.Name ?? EMPTY_ANIMATION; if (name == EMPTY_ANIMATION)
set animationState.SetAnimation(track, EmptyAnimation, false);
{ else if (animationNames.Contains(name))
if (value == EMPTY_ANIMATION) animationState.SetAnimation(track, name, true);
animationState.SetAnimation(0, EmptyAnimation, false);
else if (animationNames.Contains(value))
animationState.SetAnimation(0, value, true);
}
} }
protected override void clearTrack(int i) => animationState.ClearTrack(i);
public override float GetAnimationDuration(string name) { return skeletonData.FindAnimation(name)?.Duration ?? 0f; }
protected override RectangleF bounds protected override RectangleF bounds
{ {
get get
@@ -168,8 +172,6 @@ namespace SpineViewer.Spine.Implementations.Spine
} }
} }
public override float GetAnimationDuration(string name) { return skeletonData.FindAnimation(name)?.Duration ?? 0f; }
protected override void update(float delta) protected override void update(float delta)
{ {
animationState.Update(delta); animationState.Update(delta);

View File

@@ -142,18 +142,22 @@ namespace SpineViewer.Spine.Implementations.Spine
} }
} }
protected override string track0Animation protected override int[] trackIndices => animationState.Tracks.Select((_, i) => i).Where(i => animationState.Tracks.Items[i] is not null).ToArray();
protected override string getAnimation(int track) => animationState.GetCurrent(track)?.Animation.Name ?? EMPTY_ANIMATION;
protected override void setAnimation(int track, string name)
{ {
get => animationState.GetCurrent(0)?.Animation.Name ?? EMPTY_ANIMATION; if (name == EMPTY_ANIMATION)
set animationState.SetAnimation(track, EmptyAnimation, false);
{ else if (animationNames.Contains(name))
if (value == EMPTY_ANIMATION) animationState.SetAnimation(track, name, true);
animationState.SetAnimation(0, EmptyAnimation, false);
else if (animationNames.Contains(value))
animationState.SetAnimation(0, value, true);
}
} }
protected override void clearTrack(int i) => animationState.ClearTrack(i);
public override float GetAnimationDuration(string name) { return skeletonData.FindAnimation(name)?.Duration ?? 0f; }
protected override RectangleF bounds protected override RectangleF bounds
{ {
get get
@@ -164,8 +168,6 @@ namespace SpineViewer.Spine.Implementations.Spine
} }
} }
public override float GetAnimationDuration(string name) { return skeletonData.FindAnimation(name)?.Duration ?? 0f; }
protected override void update(float delta) protected override void update(float delta)
{ {
animationState.Update(delta); animationState.Update(delta);

View File

@@ -142,18 +142,22 @@ namespace SpineViewer.Spine.Implementations.Spine
} }
} }
protected override string track0Animation protected override int[] trackIndices => animationState.Tracks.Select((_, i) => i).Where(i => animationState.Tracks.Items[i] is not null).ToArray();
protected override string getAnimation(int track) => animationState.GetCurrent(track)?.Animation.Name ?? EMPTY_ANIMATION;
protected override void setAnimation(int track, string name)
{ {
get => animationState.GetCurrent(0)?.Animation.Name ?? EMPTY_ANIMATION; if (name == EMPTY_ANIMATION)
set animationState.SetAnimation(track, EmptyAnimation, false);
{ else if (animationNames.Contains(name))
if (value == EMPTY_ANIMATION) animationState.SetAnimation(track, name, true);
animationState.SetAnimation(0, EmptyAnimation, false);
else if (animationNames.Contains(value))
animationState.SetAnimation(0, value, true);
}
} }
protected override void clearTrack(int i) => animationState.ClearTrack(i);
public override float GetAnimationDuration(string name) { return skeletonData.FindAnimation(name)?.Duration ?? 0f; }
protected override RectangleF bounds protected override RectangleF bounds
{ {
get get
@@ -164,8 +168,6 @@ namespace SpineViewer.Spine.Implementations.Spine
} }
} }
public override float GetAnimationDuration(string name) { return skeletonData.FindAnimation(name)?.Duration ?? 0f; }
protected override void update(float delta) protected override void update(float delta)
{ {
animationState.Update(delta); animationState.Update(delta);

View File

@@ -142,18 +142,22 @@ namespace SpineViewer.Spine.Implementations.Spine
} }
} }
protected override string track0Animation protected override int[] trackIndices => animationState.Tracks.Select((_, i) => i).Where(i => animationState.Tracks.Items[i] is not null).ToArray();
protected override string getAnimation(int track) => animationState.GetCurrent(track)?.Animation.Name ?? EMPTY_ANIMATION;
protected override void setAnimation(int track, string name)
{ {
get => animationState.GetCurrent(0)?.Animation.Name ?? EMPTY_ANIMATION; if (name == EMPTY_ANIMATION)
set animationState.SetAnimation(track, EmptyAnimation, false);
{ else if (animationNames.Contains(name))
if (value == EMPTY_ANIMATION) animationState.SetAnimation(track, name, true);
animationState.SetAnimation(0, EmptyAnimation, false);
else if (animationNames.Contains(value))
animationState.SetAnimation(0, value, true);
}
} }
protected override void clearTrack(int i) => animationState.ClearTrack(i);
public override float GetAnimationDuration(string name) { return skeletonData.FindAnimation(name)?.Duration ?? 0f; }
protected override RectangleF bounds protected override RectangleF bounds
{ {
get get
@@ -164,8 +168,6 @@ namespace SpineViewer.Spine.Implementations.Spine
} }
} }
public override float GetAnimationDuration(string name) { return skeletonData.FindAnimation(name)?.Duration ?? 0f; }
protected override void update(float delta) protected override void update(float delta)
{ {
animationState.Update(delta); animationState.Update(delta);

View File

@@ -15,6 +15,8 @@ using System.Globalization;
using System.Text.Json.Nodes; using System.Text.Json.Nodes;
using System.Collections.Immutable; using System.Collections.Immutable;
using SpineViewer.Exporter; using SpineViewer.Exporter;
using System.ComponentModel.Design;
using System.Drawing.Design;
namespace SpineViewer.Spine namespace SpineViewer.Spine
{ {
@@ -77,6 +79,7 @@ namespace SpineViewer.Spine
{ {
SkinNames = skinNames.AsReadOnly(); SkinNames = skinNames.AsReadOnly();
AnimationNames = animationNames.AsReadOnly(); AnimationNames = animationNames.AsReadOnly();
AnimationTracks = new(this);
// 必须 Update 一次否则包围盒还没有值 // 必须 Update 一次否则包围盒还没有值
update(0); update(0);
@@ -105,7 +108,7 @@ namespace SpineViewer.Spine
// 取最后一个作为初始, 尽可能去显示非默认的内容 // 取最后一个作为初始, 尽可能去显示非默认的内容
skin = SkinNames.Last(); skin = SkinNames.Last();
track0Animation = AnimationNames.Last(); setAnimation(0, AnimationNames.Last());
return this; return this;
} }
@@ -199,7 +202,7 @@ namespace SpineViewer.Spine
/// </summary> /// </summary>
[TypeConverter(typeof(PointFConverter))] [TypeConverter(typeof(PointFConverter))]
[Category("[2] "), DisplayName("")] [Category("[2] "), DisplayName("")]
public PointF Position public PointF Position
{ {
get { lock (_lock) return position; } get { lock (_lock) return position; }
set { lock (_lock) { position = value; update(0); } } set { lock (_lock) { position = value; update(0); } }
@@ -262,20 +265,46 @@ namespace SpineViewer.Spine
/// 默认轨道动画名称, 如果设置的动画不存在则忽略 /// 默认轨道动画名称, 如果设置的动画不存在则忽略
/// </summary> /// </summary>
[TypeConverter(typeof(AnimationConverter))] [TypeConverter(typeof(AnimationConverter))]
[Category("[3] "), DisplayName("")] [Category("[3] "), DisplayName(" 0 ")]
public string Track0Animation public string Track0Animation
{ {
get { lock (_lock) return track0Animation; } get { lock (_lock) return getAnimation(0); }
set { lock (_lock) { track0Animation = value; update(0); } } set { lock (_lock) { setAnimation(0, value); update(0); } }
} }
protected abstract string track0Animation { get; set; }
/// <summary> /// <summary>
/// 默认轨道动画时长 /// 默认轨道动画时长
/// </summary> /// </summary>
[Category("[3] "), DisplayName("")] [Category("[3] "), DisplayName(" 0 ")]
public float Track0AnimationDuration { get => GetAnimationDuration(Track0Animation); } // TODO: 动画时长变成伪属性在面板显示 public float Track0AnimationDuration { get => GetAnimationDuration(Track0Animation); } // TODO: 动画时长变成伪属性在面板显示
/// <summary>
/// 默认轨道动画时长
/// </summary>
[Editor(typeof(CollectionEditor), typeof(UITypeEditor))]
[Category("[3] "), DisplayName("")]
public AnimationTrackDict AnimationTracks { get; private set; }
/// <summary>
/// 获取所有非 null 的轨道索引
/// </summary>
protected abstract int[] trackIndices { get; }
/// <summary>
/// 获取指定轨道的当前动画, 如果没有, 应当返回空动画名称
/// </summary>
protected abstract string getAnimation(int track);
/// <summary>
/// 设置某个轨道动画
/// </summary>
protected abstract void setAnimation(int track, string name);
/// <summary>
/// 清除某个轨道, 与设置空动画不同, 是彻底删除轨道内的东西
/// </summary>
protected abstract void clearTrack(int i);
#endregion #endregion
#region | [4] #region | [4]
@@ -284,8 +313,8 @@ namespace SpineViewer.Spine
/// 显示调试 /// 显示调试
/// </summary> /// </summary>
[Browsable(false)] [Browsable(false)]
public bool IsDebug public bool IsDebug
{ {
get { lock (_lock) return isDebug; } get { lock (_lock) return isDebug; }
set { lock (_lock) isDebug = value; } set { lock (_lock) isDebug = value; }
} }
@@ -395,5 +424,129 @@ namespace SpineViewer.Spine
protected abstract void draw(SFML.Graphics.RenderTarget target, SFML.Graphics.RenderStates states); protected abstract void draw(SFML.Graphics.RenderTarget target, SFML.Graphics.RenderStates states);
#endregion #endregion
/// <summary>
/// 多轨动画管理集合
/// </summary>
/// <param name="spine"></param>
public class AnimationTrackDict(Spine spine) : IDictionary<int, string>
{
private readonly Spine sp = spine;
public string this[int key]
{
get
{
lock (sp._lock)
{
if (!sp.trackIndices.Contains(key))
throw new KeyNotFoundException($"Track {key} not found.");
return sp.getAnimation(key);
}
}
set
{
lock (sp._lock) sp.setAnimation(key, value);
}
}
public ICollection<int> Keys
{
get { lock (sp._lock) return sp.trackIndices; }
}
public ICollection<string> Values
{
get { lock (sp._lock) return sp.trackIndices.Select(sp.getAnimation).ToArray(); }
}
public int Count
{
get { lock (sp._lock) return sp.trackIndices.Length; }
}
public bool IsReadOnly => false;
public void Add(int key, string value)
{
lock (sp._lock) sp.setAnimation(key, value);
}
public void Add(KeyValuePair<int, string> item)
{
lock (sp._lock) sp.setAnimation(item.Key, item.Value);
}
public void Clear()
{
lock (sp._lock) foreach (var i in sp.trackIndices) sp.setAnimation(i, EMPTY_ANIMATION);
}
public bool Contains(KeyValuePair<int, string> item)
{
lock (sp._lock) return sp.trackIndices.Contains(item.Key) && sp.getAnimation(item.Key) == item.Value;
}
public bool ContainsKey(int key)
{
lock (sp._lock) return sp.trackIndices.Contains(key);
}
public void CopyTo(KeyValuePair<int, string>[] array, int arrayIndex)
{
lock (sp._lock) foreach (var i in sp.trackIndices) array[arrayIndex++] = new KeyValuePair<int, string>(i, sp.getAnimation(i));
}
public IEnumerator<KeyValuePair<int, string>> GetEnumerator()
{
List<KeyValuePair<int, string>> cache;
lock (sp._lock)
{
cache = sp.trackIndices.Select(i => new KeyValuePair<int, string>(i, sp.getAnimation(i))).ToList();
}
foreach (var item in cache)
{
yield return item;
}
}
public bool Remove(int key)
{
lock (sp._lock)
{
sp.setAnimation(key, EMPTY_ANIMATION);
sp.clearTrack(key);
}
return true;
}
public bool Remove(KeyValuePair<int, string> item)
{
lock (sp._lock)
{
sp.setAnimation(item.Key, EMPTY_ANIMATION);
sp.clearTrack(item.Key);
}
return true;
}
public bool TryGetValue(int key, [MaybeNullWhen(false)] out string value)
{
value = null;
lock (sp._lock)
{
if (!sp.trackIndices.Contains(key)) return false;
value = sp.getAnimation(key);
return true;
}
}
public override string ToString()
{
lock (sp._lock) return string.Join(", ", sp.trackIndices.Select(sp.getAnimation));
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
} }
} }