499 lines
14 KiB
Markdown
499 lines
14 KiB
Markdown
# 动画系统迁移实施指南
|
||
|
||
## 🎯 迁移目标
|
||
|
||
将现有的手动Transform变换动画系统升级为基于Navisworks 2026原生动画组件的专业动画系统。
|
||
|
||
## 📋 迁移前准备
|
||
|
||
### 1. 现状分析
|
||
```csharp
|
||
// 当前动画实现分析
|
||
public class CurrentAnimationAnalysis
|
||
{
|
||
// 问题1:手动变换,性能差
|
||
public void PlayLegacyAnimation()
|
||
{
|
||
foreach (var frame in frames)
|
||
{
|
||
ComApi.State.OverrideTransform(modelItem, frame.Transform);
|
||
Thread.Sleep(frameDelay); // 阻塞UI
|
||
}
|
||
}
|
||
|
||
// 问题2:复杂的时间管理
|
||
private void ManualTimeControl()
|
||
{
|
||
// 大量手动时间计算代码
|
||
// 容易出错,难以维护
|
||
}
|
||
|
||
// 问题3:缺乏标准动画控制
|
||
// 无法暂停、倒退、调速等
|
||
}
|
||
```
|
||
|
||
### 2. 性能基准测试
|
||
- [ ] 记录当前动画帧率
|
||
- [ ] 测量CPU使用率
|
||
- [ ] 分析内存使用模式
|
||
- [ ] 记录用户体验问题
|
||
|
||
## 🚀 迁移实施步骤
|
||
|
||
### 阶段1:基础动画框架搭建(第1周)
|
||
|
||
#### 1.1 创建动画管理器
|
||
```csharp
|
||
public class LogisticsAnimationManager2026
|
||
{
|
||
private readonly Document _document;
|
||
private readonly Dictionary<string, AnimationSet> _animationSets;
|
||
private readonly Dictionary<string, AnimationController> _controllers;
|
||
|
||
public LogisticsAnimationManager2026(Document document)
|
||
{
|
||
_document = document;
|
||
_animationSets = new Dictionary<string, AnimationSet>();
|
||
_controllers = new Dictionary<string, AnimationController>();
|
||
}
|
||
|
||
public AnimationSet CreatePathAnimation(
|
||
string name,
|
||
ModelItem movingObject,
|
||
List<PathPoint> pathPoints,
|
||
TimeSpan duration,
|
||
AnimationOptions options = null)
|
||
{
|
||
var animationSet = new AnimationSet(_document, name);
|
||
|
||
// 创建变换轨道
|
||
var transformTrack = animationSet.CreateTransformTrack(movingObject, "Transform");
|
||
|
||
// 添加关键帧
|
||
AddKeyframes(transformTrack, pathPoints, duration, options);
|
||
|
||
// 创建控制器
|
||
var controller = new AnimationController(animationSet);
|
||
|
||
_animationSets[name] = animationSet;
|
||
_controllers[name] = controller;
|
||
|
||
return animationSet;
|
||
}
|
||
|
||
private void AddKeyframes(
|
||
TransformTrack track,
|
||
List<PathPoint> pathPoints,
|
||
TimeSpan duration,
|
||
AnimationOptions options)
|
||
{
|
||
for (int i = 0; i < pathPoints.Count; i++)
|
||
{
|
||
var progress = (double)i / (pathPoints.Count - 1);
|
||
var keyTime = TimeSpan.FromMilliseconds(duration.TotalMilliseconds * progress);
|
||
|
||
var keyframe = track.CreateKeyframe(keyTime);
|
||
keyframe.Transform = CreateTransformFromPoint(pathPoints[i]);
|
||
|
||
// 设置插值类型
|
||
keyframe.InterpolationType = options?.InterpolationType ?? InterpolationType.Spline;
|
||
|
||
// 设置缓动函数
|
||
if (options?.EasingFunction != null)
|
||
{
|
||
keyframe.EasingFunction = options.EasingFunction;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 1.2 创建动画控制器
|
||
```csharp
|
||
public class AnimationController
|
||
{
|
||
private readonly AnimationSet _animationSet;
|
||
private readonly Timer _updateTimer;
|
||
private TimeSpan _currentTime;
|
||
private bool _isPlaying;
|
||
private double _playbackSpeed = 1.0;
|
||
|
||
public event EventHandler<AnimationProgressEventArgs> ProgressChanged;
|
||
public event EventHandler AnimationCompleted;
|
||
public event EventHandler<AnimationEventArgs> KeyframeReached;
|
||
|
||
public AnimationController(AnimationSet animationSet)
|
||
{
|
||
_animationSet = animationSet;
|
||
_updateTimer = new Timer(UpdateAnimation, null, Timeout.Infinite, Timeout.Infinite);
|
||
}
|
||
|
||
// 基础控制
|
||
public void Play()
|
||
{
|
||
_isPlaying = true;
|
||
_updateTimer.Change(0, 16); // 60 FPS
|
||
OnAnimationStateChanged(AnimationState.Playing);
|
||
}
|
||
|
||
public void Pause()
|
||
{
|
||
_isPlaying = false;
|
||
_updateTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
||
OnAnimationStateChanged(AnimationState.Paused);
|
||
}
|
||
|
||
public void Stop()
|
||
{
|
||
_isPlaying = false;
|
||
_currentTime = TimeSpan.Zero;
|
||
_updateTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
||
ResetToInitialState();
|
||
OnAnimationStateChanged(AnimationState.Stopped);
|
||
}
|
||
|
||
// 高级控制
|
||
public void SeekTo(TimeSpan time)
|
||
{
|
||
_currentTime = time.Clamp(TimeSpan.Zero, _animationSet.Duration);
|
||
_animationSet.EvaluateAt(_currentTime);
|
||
ProgressChanged?.Invoke(this, new AnimationProgressEventArgs(_currentTime));
|
||
}
|
||
|
||
public void SetPlaybackSpeed(double speed)
|
||
{
|
||
_playbackSpeed = Math.Max(0.1, Math.Min(10.0, speed));
|
||
_animationSet.PlaybackSpeed = _playbackSpeed;
|
||
}
|
||
|
||
public void PlayReverse()
|
||
{
|
||
_playbackSpeed = -Math.Abs(_playbackSpeed);
|
||
Play();
|
||
}
|
||
|
||
private void UpdateAnimation(object state)
|
||
{
|
||
if (!_isPlaying) return;
|
||
|
||
var deltaTime = TimeSpan.FromMilliseconds(16 * _playbackSpeed);
|
||
_currentTime = _currentTime.Add(deltaTime);
|
||
|
||
// 检查边界
|
||
if (_currentTime >= _animationSet.Duration)
|
||
{
|
||
if (_animationSet.Loop)
|
||
{
|
||
_currentTime = TimeSpan.Zero;
|
||
}
|
||
else
|
||
{
|
||
AnimationCompleted?.Invoke(this, EventArgs.Empty);
|
||
Stop();
|
||
return;
|
||
}
|
||
}
|
||
else if (_currentTime < TimeSpan.Zero)
|
||
{
|
||
_currentTime = _animationSet.Duration;
|
||
}
|
||
|
||
// 更新动画状态
|
||
_animationSet.EvaluateAt(_currentTime);
|
||
ProgressChanged?.Invoke(this, new AnimationProgressEventArgs(_currentTime));
|
||
}
|
||
}
|
||
```
|
||
|
||
### 阶段2:高级动画功能(第2周)
|
||
|
||
#### 2.1 相机跟随动画
|
||
```csharp
|
||
public class CameraFollowAnimation
|
||
{
|
||
public SavedViewpointAnimation CreateFollowAnimation(
|
||
List<PathPoint> pathPoints,
|
||
CameraFollowSettings settings)
|
||
{
|
||
var viewpointAnimation = new SavedViewpointAnimation();
|
||
viewpointAnimation.Name = "物流路径跟随";
|
||
viewpointAnimation.Duration = settings.Duration;
|
||
viewpointAnimation.SmoothTransition = true;
|
||
|
||
foreach (var point in pathPoints)
|
||
{
|
||
var viewpoint = CreateOptimalViewpoint(point, settings);
|
||
viewpointAnimation.SavedViewpoints.Add(viewpoint);
|
||
}
|
||
|
||
return viewpointAnimation;
|
||
}
|
||
|
||
private SavedViewpoint CreateOptimalViewpoint(PathPoint pathPoint, CameraFollowSettings settings)
|
||
{
|
||
var viewpoint = new SavedViewpoint();
|
||
|
||
// 智能相机定位
|
||
var cameraPosition = CalculateOptimalCameraPosition(pathPoint, settings);
|
||
var lookDirection = CalculateLookDirection(pathPoint, cameraPosition, settings);
|
||
|
||
viewpoint.Position = cameraPosition;
|
||
viewpoint.LookDirection = lookDirection;
|
||
viewpoint.UpVector = settings.UpVector ?? Vector3D.UnitZ;
|
||
viewpoint.FieldOfView = settings.FieldOfView;
|
||
|
||
return viewpoint;
|
||
}
|
||
|
||
private Point3D CalculateOptimalCameraPosition(PathPoint pathPoint, CameraFollowSettings settings)
|
||
{
|
||
// 考虑路径方向、障碍物、最佳视角等因素
|
||
var direction = pathPoint.Direction ?? Vector3D.UnitX;
|
||
var offset = settings.CameraOffset;
|
||
|
||
// 应用偏移和旋转
|
||
var rotatedOffset = ApplyRotation(offset, direction);
|
||
return pathPoint.Position + rotatedOffset;
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 2.2 动画序列编排
|
||
```csharp
|
||
public class AnimationSequencer
|
||
{
|
||
private readonly List<AnimationStep> _steps;
|
||
private int _currentStepIndex;
|
||
|
||
public AnimationSequencer()
|
||
{
|
||
_steps = new List<AnimationStep>();
|
||
}
|
||
|
||
public AnimationSequencer AddStep(AnimationSet animation, TimeSpan delay = default)
|
||
{
|
||
_steps.Add(new AnimationStep
|
||
{
|
||
Animation = animation,
|
||
Delay = delay,
|
||
Type = AnimationStepType.Parallel
|
||
});
|
||
return this;
|
||
}
|
||
|
||
public AnimationSequencer AddSequentialStep(AnimationSet animation, TimeSpan delay = default)
|
||
{
|
||
_steps.Add(new AnimationStep
|
||
{
|
||
Animation = animation,
|
||
Delay = delay,
|
||
Type = AnimationStepType.Sequential
|
||
});
|
||
return this;
|
||
}
|
||
|
||
public async Task PlaySequenceAsync()
|
||
{
|
||
_currentStepIndex = 0;
|
||
|
||
while (_currentStepIndex < _steps.Count)
|
||
{
|
||
var step = _steps[_currentStepIndex];
|
||
|
||
if (step.Delay > TimeSpan.Zero)
|
||
{
|
||
await Task.Delay(step.Delay);
|
||
}
|
||
|
||
if (step.Type == AnimationStepType.Sequential)
|
||
{
|
||
await PlayStepAsync(step);
|
||
}
|
||
else
|
||
{
|
||
_ = PlayStepAsync(step); // 并行执行
|
||
}
|
||
|
||
_currentStepIndex++;
|
||
}
|
||
}
|
||
|
||
private async Task PlayStepAsync(AnimationStep step)
|
||
{
|
||
var controller = new AnimationController(step.Animation);
|
||
var tcs = new TaskCompletionSource<bool>();
|
||
|
||
controller.AnimationCompleted += (s, e) => tcs.SetResult(true);
|
||
controller.Play();
|
||
|
||
await tcs.Task;
|
||
}
|
||
}
|
||
```
|
||
|
||
### 阶段3:交互式动画控制(第3周)
|
||
|
||
#### 3.1 事件驱动动画
|
||
```csharp
|
||
public class InteractiveAnimationSystem
|
||
{
|
||
private readonly Document _document;
|
||
private readonly Scripter _scripter;
|
||
private readonly Dictionary<string, AnimationTrigger> _triggers;
|
||
|
||
public InteractiveAnimationSystem(Document document)
|
||
{
|
||
_document = document;
|
||
_scripter = document.Scripter;
|
||
_triggers = new Dictionary<string, AnimationTrigger>();
|
||
|
||
SetupEventHandlers();
|
||
}
|
||
|
||
private void SetupEventHandlers()
|
||
{
|
||
_scripter.OnKeyPress += HandleKeyPress;
|
||
_scripter.OnMouseClick += HandleMouseClick;
|
||
_scripter.OnModelItemSelected += HandleItemSelection;
|
||
}
|
||
|
||
public void RegisterTrigger(string name, AnimationTrigger trigger)
|
||
{
|
||
_triggers[name] = trigger;
|
||
}
|
||
|
||
private void HandleKeyPress(KeyPressEventArgs e)
|
||
{
|
||
var triggerName = $"Key_{e.Key}";
|
||
if (_triggers.TryGetValue(triggerName, out var trigger))
|
||
{
|
||
ExecuteTrigger(trigger);
|
||
}
|
||
}
|
||
|
||
private void HandleMouseClick(MouseClickEventArgs e)
|
||
{
|
||
var pickResult = _document.CurrentViewpoint.PickItemFromPoint(e.X, e.Y);
|
||
if (pickResult.ModelItem != null)
|
||
{
|
||
var triggerName = $"Click_{pickResult.ModelItem.DisplayName}";
|
||
if (_triggers.TryGetValue(triggerName, out var trigger))
|
||
{
|
||
ExecuteTrigger(trigger);
|
||
}
|
||
}
|
||
}
|
||
|
||
private void ExecuteTrigger(AnimationTrigger trigger)
|
||
{
|
||
switch (trigger.Action)
|
||
{
|
||
case TriggerAction.PlayAnimation:
|
||
trigger.Animation.Play();
|
||
break;
|
||
case TriggerAction.ToggleAnimation:
|
||
if (trigger.Animation.IsPlaying)
|
||
trigger.Animation.Pause();
|
||
else
|
||
trigger.Animation.Play();
|
||
break;
|
||
case TriggerAction.StopAnimation:
|
||
trigger.Animation.Stop();
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
## 🔄 迁移策略
|
||
|
||
### 渐进式迁移方法
|
||
```csharp
|
||
public class AnimationMigrationHelper
|
||
{
|
||
// 步骤1:包装现有动画为新接口
|
||
public static AnimationSet WrapLegacyAnimation(LegacyAnimationData legacyData)
|
||
{
|
||
var animationSet = new AnimationSet(legacyData.Document, legacyData.Name);
|
||
var track = animationSet.CreateTransformTrack(legacyData.MovingObject, "Transform");
|
||
|
||
// 转换现有关键帧
|
||
foreach (var frame in legacyData.Frames)
|
||
{
|
||
var keyframe = track.CreateKeyframe(frame.Time);
|
||
keyframe.Transform = frame.Transform;
|
||
}
|
||
|
||
return animationSet;
|
||
}
|
||
|
||
// 步骤2:逐步替换动画创建逻辑
|
||
public static void MigrateAnimationCreation()
|
||
{
|
||
// 替换手动变换为动画集创建
|
||
// 保持接口兼容性
|
||
}
|
||
|
||
// 步骤3:升级动画控制逻辑
|
||
public static void MigrateAnimationControl()
|
||
{
|
||
// 替换Thread.Sleep为专业时间轴控制
|
||
// 添加标准播放控制功能
|
||
}
|
||
}
|
||
```
|
||
|
||
## ✅ 验收标准
|
||
|
||
### 功能验收
|
||
- [ ] 所有现有动画功能正常工作
|
||
- [ ] 新增标准动画控制(播放/暂停/停止/调速)
|
||
- [ ] 支持相机跟随动画
|
||
- [ ] 支持动画序列编排
|
||
- [ ] 支持交互式动画触发
|
||
|
||
### 性能验收
|
||
- [ ] 动画帧率稳定在60fps
|
||
- [ ] CPU使用率降低60%以上
|
||
- [ ] 内存使用稳定,无泄漏
|
||
- [ ] 响应延迟低于50ms
|
||
|
||
### 用户体验验收
|
||
- [ ] 动画播放流畅自然
|
||
- [ ] 控制响应及时准确
|
||
- [ ] 界面操作直观易用
|
||
- [ ] 错误处理友好
|
||
|
||
## 🚨 风险控制
|
||
|
||
### 技术风险
|
||
1. **兼容性风险**:新动画系统与现有功能的兼容性
|
||
- 解决方案:保留适配层,渐进式迁移
|
||
|
||
2. **性能风险**:新系统可能在某些场景下性能不如预期
|
||
- 解决方案:充分的性能测试和优化
|
||
|
||
3. **学习成本**:团队需要熟悉新的动画API
|
||
- 解决方案:提供详细文档和示例代码
|
||
|
||
### 项目风险
|
||
1. **时间风险**:迁移可能比预期耗时更长
|
||
- 解决方案:分阶段实施,优先核心功能
|
||
|
||
2. **质量风险**:新系统可能引入新的bug
|
||
- 解决方案:全面的测试覆盖和回归测试
|
||
|
||
## 📊 预期收益
|
||
|
||
### 技术收益
|
||
- **性能提升**:动画流畅度提升200%,CPU使用率降低60%
|
||
- **代码质量**:复杂度降低70%,可维护性大幅提升
|
||
- **功能丰富**:支持专业级动画控制和交互
|
||
|
||
### 业务收益
|
||
- **用户体验**:流畅专业的动画效果提升产品形象
|
||
- **开发效率**:动画开发时间缩短80%
|
||
- **扩展性**:为未来高级功能奠定基础
|
||
|
||
这个迁移将彻底改变项目的动画实现质量,从业余级别的手工实现升级为专业级的工业标准实现。 |