NavisworksTransport/doc/migration/Animation_Migration_Guide.md

499 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 动画系统迁移实施指南
## 🎯 迁移目标
将现有的手动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%
- **扩展性**:为未来高级功能奠定基础
这个迁移将彻底改变项目的动画实现质量,从业余级别的手工实现升级为专业级的工业标准实现。