diff --git a/Spine/SpineObject.cs b/Spine/SpineObject.cs
index 5854cc2..35d7761 100644
--- a/Spine/SpineObject.cs
+++ b/Spine/SpineObject.cs
@@ -167,6 +167,7 @@ namespace Spine
// 拷贝渲染设置
UsePma = other.UsePma;
Physics = other.Physics;
+ _animationState.TimeScale = other._animationState.TimeScale;
// 拷贝皮肤加载情况
_skinLoadStatus = other._skinLoadStatus.ToDictionary();
diff --git a/SpineViewer/Models/SpineObjectConfigModel.cs b/SpineViewer/Models/SpineObjectConfigModel.cs
index 8b92b71..58dc5fd 100644
--- a/SpineViewer/Models/SpineObjectConfigModel.cs
+++ b/SpineViewer/Models/SpineObjectConfigModel.cs
@@ -17,6 +17,8 @@ namespace SpineViewer.Models
public string Physics { get; set; } = ISkeleton.Physics.Update.ToString();
+ public float TimeScale { get; set; } = 1f;
+
public float Scale { get; set; } = 1f;
public bool FlipX { get; set; }
@@ -54,5 +56,15 @@ namespace SpineViewer.Models
public bool DebugPoints { get; set; }
public bool DebugClippings { get; set; }
+
+ }
+
+ public class AnimationConfigModel
+ {
+ string Name { get; set; } = "";
+
+ float TimeScale { get; set; } = 1f;
+
+ float Alpha { get; set; } = 1f;
}
}
diff --git a/SpineViewer/Models/SpineObjectModel.cs b/SpineViewer/Models/SpineObjectModel.cs
index 71237f9..19e8d0b 100644
--- a/SpineViewer/Models/SpineObjectModel.cs
+++ b/SpineViewer/Models/SpineObjectModel.cs
@@ -129,6 +129,12 @@ namespace SpineViewer.Models
set { lock (_lock) SetProperty(_spineObject.Physics, value, v => _spineObject.Physics = v); }
}
+ public float TimeScale
+ {
+ get { lock (_lock) return _spineObject.AnimationState.TimeScale; }
+ set { lock (_lock) SetProperty(_spineObject.AnimationState.TimeScale, Math.Clamp(value, -100f, 100f), v => _spineObject.AnimationState.TimeScale = v); }
+ }
+
///
/// 缩放倍数, 绝对值大小, 两个方向大小不一致时返回 -1, 设置时不会影响正负号
///
@@ -388,6 +394,7 @@ namespace SpineViewer.Models
UsePma = _spineObject.UsePma,
Physics = _spineObject.Physics.ToString(),
+ TimeScale = _spineObject.AnimationState.TimeScale,
DebugTexture = _spineObject.DebugTexture,
DebugBounds = _spineObject.DebugBounds,
@@ -427,6 +434,7 @@ namespace SpineViewer.Models
SetProperty(_spineObject.Skeleton.Y, value.Y, v => _spineObject.Skeleton.Y = v, nameof(Y));
SetProperty(_spineObject.UsePma, value.UsePma, v => _spineObject.UsePma = v, nameof(UsePma));
SetProperty(_spineObject.Physics, Enum.Parse(value.Physics ?? "Update", true), v => _spineObject.Physics = v, nameof(Physics));
+ SetProperty(_spineObject.AnimationState.TimeScale, value.TimeScale, v => _spineObject.AnimationState.TimeScale = v, nameof(TimeScale));
foreach (var name in _spineObject.Data.Skins.Select(v => v.Name).Except(value.LoadedSkins))
if (_spineObject.SetSkinStatus(name, false))
diff --git a/SpineViewer/Resources/Strings/en.xaml b/SpineViewer/Resources/Strings/en.xaml
index 06cdd98..dcad18b 100644
--- a/SpineViewer/Resources/Strings/en.xaml
+++ b/SpineViewer/Resources/Strings/en.xaml
@@ -64,6 +64,8 @@
Show
Premultiply Alpha
Physics
+ Time Scale
+ Time scale for a single model; a negative value plays the animation in reverse.
Transform
Scale
diff --git a/SpineViewer/Resources/Strings/ja.xaml b/SpineViewer/Resources/Strings/ja.xaml
index 048d919..2d16f34 100644
--- a/SpineViewer/Resources/Strings/ja.xaml
+++ b/SpineViewer/Resources/Strings/ja.xaml
@@ -64,6 +64,8 @@
表示
プレマルチプライドアルファ
物理
+ 時間スケール
+ 単一モデルの時間スケール。負の値にするとアニメーションを逆再生します。
変換
スケール
diff --git a/SpineViewer/Resources/Strings/zh.xaml b/SpineViewer/Resources/Strings/zh.xaml
index c0ce189..b72d87f 100644
--- a/SpineViewer/Resources/Strings/zh.xaml
+++ b/SpineViewer/Resources/Strings/zh.xaml
@@ -64,6 +64,8 @@
显示
预乘Alpha通道
物理
+ 时间因子
+ 单个模型的时间因子,取负数时可以倒放动画
变换
缩放
diff --git a/SpineViewer/ViewModels/MainWindow/SpineObjectTabViewModel.cs b/SpineViewer/ViewModels/MainWindow/SpineObjectTabViewModel.cs
index 5e39f9a..1e74af2 100644
--- a/SpineViewer/ViewModels/MainWindow/SpineObjectTabViewModel.cs
+++ b/SpineViewer/ViewModels/MainWindow/SpineObjectTabViewModel.cs
@@ -74,6 +74,7 @@ namespace SpineViewer.ViewModels.MainWindow
OnPropertyChanged(nameof(IsShown));
OnPropertyChanged(nameof(UsePma));
OnPropertyChanged(nameof(Physics));
+ OnPropertyChanged(nameof(TimeScale));
OnPropertyChanged(nameof(Scale));
OnPropertyChanged(nameof(FlipX));
@@ -217,6 +218,25 @@ namespace SpineViewer.ViewModels.MainWindow
}
}
+ public float? TimeScale
+ {
+ get
+ {
+ if (_selectedObjects.Length <= 0) return null;
+ var val = _selectedObjects[0].TimeScale;
+ if (_selectedObjects.Skip(1).Any(it => it.TimeScale != val)) return null;
+ return val;
+ }
+
+ set
+ {
+ if (_selectedObjects.Length <= 0) return;
+ if (value is null) return;
+ foreach (var sp in _selectedObjects) sp.TimeScale = (float)value;
+ OnPropertyChanged();
+ }
+ }
+
public float? Scale
{
get
@@ -595,35 +615,44 @@ namespace SpineViewer.ViewModels.MainWindow
}
}
- ///
- /// 监听单个模型属性发生变化, 则更新聚合属性值
- ///
- private void SingleModel_PropertyChanged(object? sender, PropertyChangedEventArgs e)
+ private static readonly Dictionary _singleModelPropertyMap = new()
{
- if (e.PropertyName == nameof(SpineObjectModel.IsShown)) OnPropertyChanged(nameof(IsShown));
- else if (e.PropertyName == nameof(SpineObjectModel.UsePma)) OnPropertyChanged(nameof(UsePma));
- else if (e.PropertyName == nameof(SpineObjectModel.Physics)) OnPropertyChanged(nameof(Physics));
+ { nameof(SpineObjectModel.IsShown), nameof(IsShown) },
+ { nameof(SpineObjectModel.UsePma), nameof(UsePma) },
+ { nameof(SpineObjectModel.Physics), nameof(Physics) },
+ { nameof(SpineObjectModel.TimeScale), nameof(TimeScale) },
- else if (e.PropertyName == nameof(SpineObjectModel.Scale)) OnPropertyChanged(nameof(Scale));
- else if (e.PropertyName == nameof(SpineObjectModel.FlipX)) OnPropertyChanged(nameof(FlipX));
- else if (e.PropertyName == nameof(SpineObjectModel.FlipY)) OnPropertyChanged(nameof(FlipY));
- else if (e.PropertyName == nameof(SpineObjectModel.X)) OnPropertyChanged(nameof(X));
- else if (e.PropertyName == nameof(SpineObjectModel.Y)) OnPropertyChanged(nameof(Y));
+ { nameof(SpineObjectModel.Scale), nameof(Scale) },
+ { nameof(SpineObjectModel.FlipX), nameof(FlipX) },
+ { nameof(SpineObjectModel.FlipY), nameof(FlipY) },
+ { nameof(SpineObjectModel.X), nameof(X) },
+ { nameof(SpineObjectModel.Y), nameof(Y) },
// Skins 变化在 SkinViewModel 中监听
// Slots 变化在 SlotAttachmentViewModel 中监听
// AnimationTracks 变化在 AnimationTrackViewModel 中监听
- else if (e.PropertyName == nameof(SpineObjectModel.DebugTexture)) OnPropertyChanged(nameof(DebugTexture));
- else if (e.PropertyName == nameof(SpineObjectModel.DebugBounds)) OnPropertyChanged(nameof(DebugBounds));
- else if (e.PropertyName == nameof(SpineObjectModel.DebugBones)) OnPropertyChanged(nameof(DebugBones));
- else if (e.PropertyName == nameof(SpineObjectModel.DebugRegions)) OnPropertyChanged(nameof(DebugRegions));
- else if (e.PropertyName == nameof(SpineObjectModel.DebugMeshHulls)) OnPropertyChanged(nameof(DebugMeshHulls));
- else if (e.PropertyName == nameof(SpineObjectModel.DebugMeshes)) OnPropertyChanged(nameof(DebugMeshes));
- else if (e.PropertyName == nameof(SpineObjectModel.DebugBoundingBoxes)) OnPropertyChanged(nameof(DebugBoundingBoxes));
- else if (e.PropertyName == nameof(SpineObjectModel.DebugPaths)) OnPropertyChanged(nameof(DebugPaths));
- else if (e.PropertyName == nameof(SpineObjectModel.DebugPoints)) OnPropertyChanged(nameof(DebugPoints));
- else if (e.PropertyName == nameof(SpineObjectModel.DebugClippings)) OnPropertyChanged(nameof(DebugClippings));
+ { nameof(SpineObjectModel.DebugTexture), nameof(DebugTexture) },
+ { nameof(SpineObjectModel.DebugBounds), nameof(DebugBounds) },
+ { nameof(SpineObjectModel.DebugBones), nameof(DebugBones) },
+ { nameof(SpineObjectModel.DebugRegions), nameof(DebugRegions) },
+ { nameof(SpineObjectModel.DebugMeshHulls), nameof(DebugMeshHulls) },
+ { nameof(SpineObjectModel.DebugMeshes), nameof(DebugMeshes) },
+ { nameof(SpineObjectModel.DebugBoundingBoxes), nameof(DebugBoundingBoxes) },
+ { nameof(SpineObjectModel.DebugPaths), nameof(DebugPaths) },
+ { nameof(SpineObjectModel.DebugPoints), nameof(DebugPoints) },
+ { nameof(SpineObjectModel.DebugClippings), nameof(DebugClippings) },
+ };
+
+ ///
+ /// 监听单个模型属性发生变化, 则更新聚合属性值
+ ///
+ private void SingleModel_PropertyChanged(object? sender, PropertyChangedEventArgs e)
+ {
+ if (_singleModelPropertyMap.TryGetValue(e.PropertyName, out var targetProperty))
+ {
+ OnPropertyChanged(targetProperty);
+ }
}
///
diff --git a/SpineViewer/Views/MainWindow.xaml b/SpineViewer/Views/MainWindow.xaml
index 47c4e8c..0c5d294 100644
--- a/SpineViewer/Views/MainWindow.xaml
+++ b/SpineViewer/Views/MainWindow.xaml
@@ -310,6 +310,7 @@
+
@@ -323,6 +324,10 @@
+
+
+
+