把动画从Timer改成Idle事件机制

This commit is contained in:
tian 2025-09-08 08:38:25 +08:00
parent bd74b42df3
commit 9924c3b304
4 changed files with 754 additions and 142 deletions

View File

@ -0,0 +1,296 @@
# Idle事件动画实现策略
基于Autodesk官方文章和实践分析采用Application.Idle事件替代Timer实现动画。
## 背景问题
当前使用Timer实现存在的问题
1. Timer更新可能与UI刷新不同步导致动画卡顿
2. 使用Thread.Sleep()会导致"先延迟后一次性执行"的问题
3. Timer在系统繁忙时可能积压更新
## Idle事件方案优势
1. **与UI刷新同步**Idle事件在UI消息循环空闲时触发确保画面刷新
2. **避免积压**:不会出现更新积压问题
3. **系统资源友好**:不会在系统繁忙时强制更新
4. **实现简单**:不需要管理定时器生命周期
## 核心实现
### 1. 基础Idle动画框架
```csharp
public class PathAnimationManager
{
// 帧率控制
private int _targetFPS = 30; // 目标帧率
private double _frameInterval; // 每帧间隔(毫秒)
private DateTime _lastFrameTime = DateTime.MinValue;
// 动画状态
private double _animationDuration = 10.0; // 秒
private DateTime _animationStartTime;
private AnimationState _currentState = AnimationState.Idle;
public void StartAnimation()
{
_frameInterval = 1000.0 / _targetFPS;
_animationStartTime = DateTime.Now;
_lastFrameTime = DateTime.MinValue;
// 使用Idle事件替代Timer
Autodesk.Navisworks.Api.Application.Idle += OnApplicationIdle;
SetState(AnimationState.Playing);
LogManager.Info($"动画开始Idle事件模式目标 {_targetFPS} FPS");
}
private void OnApplicationIdle(object sender, EventArgs e)
{
// 帧率控制
var now = DateTime.Now;
var elapsed = (now - _lastFrameTime).TotalMilliseconds;
if (elapsed < _frameInterval)
return; // 跳过本次Idle控制帧率
// 计算动画进度
var totalElapsed = (now - _animationStartTime).TotalSeconds;
var progress = Math.Min(totalElapsed / _animationDuration, 1.0);
// 更新动画
UpdateAnimation(progress);
// 触发进度事件
ProgressChanged?.Invoke(this, progress * 100);
// 记录帧时间
_lastFrameTime = now;
// 检查完成
if (progress >= 1.0)
{
StopAnimation();
}
}
public void StopAnimation()
{
// 注销Idle事件
Autodesk.Navisworks.Api.Application.Idle -= OnApplicationIdle;
SetState(AnimationState.Stopped);
LogManager.Info("动画停止");
}
}
```
### 2. 暂停/恢复机制
```csharp
public class PausableIdleAnimation
{
private double _pausedProgress = 0.0;
private TimeSpan _pausedElapsed = TimeSpan.Zero;
private bool _isPaused = false;
public void PauseAnimation()
{
if (_currentState != AnimationState.Playing)
return;
// 记录暂停时的进度
var elapsed = DateTime.Now - _animationStartTime;
_pausedElapsed = elapsed;
_pausedProgress = Math.Min(elapsed.TotalSeconds / _animationDuration, 1.0);
// 暂时注销Idle事件
Autodesk.Navisworks.Api.Application.Idle -= OnApplicationIdle;
_isPaused = true;
SetState(AnimationState.Paused);
}
public void ResumeAnimation()
{
if (_currentState != AnimationState.Paused)
return;
// 调整开始时间,使进度连续
_animationStartTime = DateTime.Now - _pausedElapsed;
_lastFrameTime = DateTime.MinValue;
// 重新订阅Idle事件
Autodesk.Navisworks.Api.Application.Idle += OnApplicationIdle;
_isPaused = false;
SetState(AnimationState.Playing);
}
}
```
### 3. 性能监控
```csharp
public class PerformanceMonitor
{
private int _frameCount = 0;
private DateTime _fpsCounterStart = DateTime.Now;
private double _actualFPS = 0;
private Queue<double> _frameIntervals = new Queue<double>(30);
public void RecordFrame(double interval)
{
_frameCount++;
_frameIntervals.Enqueue(interval);
if (_frameIntervals.Count > 30)
_frameIntervals.Dequeue();
// 每秒更新FPS
var elapsed = (DateTime.Now - _fpsCounterStart).TotalSeconds;
if (elapsed >= 1.0)
{
_actualFPS = _frameCount / elapsed;
LogManager.Debug($"实际FPS: {_actualFPS:F1}, 平均帧间隔: {_frameIntervals.Average():F1}ms");
_frameCount = 0;
_fpsCounterStart = DateTime.Now;
}
}
}
```
### 4. 碰撞检测集成
```csharp
private void OnApplicationIdle(object sender, EventArgs e)
{
// 帧率控制
if (!ShouldUpdateFrame())
return;
// 更新动画
var progress = UpdateAnimation();
// 碰撞检测(降频执行)
if (ShouldCheckCollision())
{
CheckCollisions();
}
_lastFrameTime = DateTime.Now;
}
private bool ShouldCheckCollision()
{
// 碰撞检测可以降频比如每3帧检测一次
return _frameCount % 3 == 0;
}
```
## 实施步骤
### 第一步保留原Timer代码注释
```csharp
// === 原Timer实现保留备用 ===
/*
private Timer _animationTimer;
private Timer _collisionTimer;
private void StartAnimation_Timer()
{
_animationTimer = new Timer();
_animationTimer.Interval = (int)(1000.0 / _animationFrameRate);
_animationTimer.Tick += AnimationTimer_Tick;
_animationTimer.Start();
}
private void AnimationTimer_Tick(object sender, EventArgs e)
{
// 原动画更新逻辑
}
*/
```
### 第二步实现Idle事件版本
```csharp
// === 新Idle事件实现 ===
private void StartAnimation_Idle()
{
_frameInterval = 1000.0 / _targetFPS;
_animationStartTime = DateTime.Now;
_lastFrameTime = DateTime.MinValue;
// 订阅Idle事件
Autodesk.Navisworks.Api.Application.Idle += OnApplicationIdle;
LogManager.Info($"[Idle模式] 动画启动目标FPS: {_targetFPS}");
}
```
### 第三步:添加切换机制
```csharp
public enum AnimationMode
{
Timer, // 原Timer模式备用
Idle // 新Idle事件模式默认
}
private AnimationMode _animationMode = AnimationMode.Idle;
public void StartAnimation()
{
if (_animationMode == AnimationMode.Idle)
{
StartAnimation_Idle();
}
else
{
StartAnimation_Timer(); // 备用方案
}
}
```
## 注意事项
1. **事件注销**必须在适当时机注销Idle事件避免内存泄漏
2. **帧率控制**:通过时间检查控制实际更新频率
3. **进度计算**:基于实际时间而非帧数,确保动画时长准确
4. **异常处理**Idle事件处理器中的异常要妥善处理
## 性能对比
| 指标 | Timer方式 | Idle事件方式 |
|-----|----------|-------------|
| UI同步性 | 可能不同步 | 完美同步 |
| CPU占用 | 固定开销 | 按需使用 |
| 实现复杂度 | 需管理定时器 | 简单直接 |
| 帧率稳定性 | 较稳定 | 受系统负载影响 |
| 响应性 | 一般 | 更好 |
## 预期效果
1. **更流畅的动画**与UI刷新完美同步
2. **更低的资源占用**:只在需要时更新
3. **更好的稳定性**避免Timer积压问题
4. **保持功能完整**:支持暂停、恢复、进度跟踪等
## 回滚方案
如果Idle事件方式出现问题可以快速切换回Timer模式
```csharp
_animationMode = AnimationMode.Timer; // 切换回Timer模式
```
## 参考资料
- Autodesk官方文章《Transform Object with a Series of Moves》
- Navisworks API文档Application.Idle事件
- 最佳实践与UI消息循环同步的动画实现

View File

@ -0,0 +1,314 @@
# Idle事件方法在项目其他地方的应用方案
## 背景
在成功将PathAnimationManager的动画系统从Timer改为Idle事件后动画运行变得非常流畅。这证明了Idle事件方法的优势
- 与UI刷新同步
- 避免更新积压
- 系统资源友好
现在考虑将这种方法应用到项目的其他地方改进UI卡顿问题。
## 当前问题分析
### 1. UIStateManager的Timer机制
- 使用`System.Threading.Timer`定期刷新500ms间隔
- 固定间隔可能造成延迟感
- 与UI刷新不同步
### 2. 大量Task.Delay使用
项目中发现多处使用Task.Delay
- AnimationControlViewModel: `Task.Delay(1000)` 等待碰撞检测
- SystemManagementViewModel: `Thread.Sleep(2000)` 模拟检查更新
- LogisticsControlViewModel: `Thread.Sleep(2000)`
- 其他多处延迟操作
### 3. DispatcherTimer的固定间隔更新
- SystemManagementViewModel: 5秒间隔更新性能信息
- AnimationControlViewModel: 参数更新防抖机制
- 固定间隔可能与实际需求不匹配
## 改进方案
### 1. UIStateManager改进 - 用Idle事件替代Timer刷新机制
**现有代码问题**
```csharp
private System.Threading.Timer _flushTimer;
private readonly int _flushInterval = 500; // 500ms保底刷新间隔
```
**改进方案**
```csharp
// 使用Idle事件处理更新队列
private void OnApplicationIdle(object sender, EventArgs e)
{
// 时间控制,避免过于频繁
if ((DateTime.Now - _lastProcessTime).TotalMilliseconds < 50) return;
// 处理更新队列
ProcessUpdateQueue();
_lastProcessTime = DateTime.Now;
}
```
**优势**
- 更新与UI刷新同步
- 避免固定延迟
- 减少不必要的更新
### 2. AnimationControlViewModel - 替换Task.Delay延迟检查
**现有代码问题**
```csharp
// 给一个短暂延迟让CreateAllAnimationCollisionTests有时间完成
Task.Delay(1000).ContinueWith(_ => _uiStateManager.ExecuteUIUpdateAsync(() =>
{
// 检查碰撞检测结果
}));
```
**改进方案**
```csharp
// 使用Idle事件监听完成状态
private bool _waitingForCollisionResults = false;
private void OnAnimationCompleted()
{
_waitingForCollisionResults = true;
Application.Idle += OnIdleCheckCollisionResults;
}
private void OnIdleCheckCollisionResults(object sender, EventArgs e)
{
if (ClashDetectiveIntegration.Instance.IsProcessingComplete)
{
Application.Idle -= OnIdleCheckCollisionResults;
UpdateCollisionResults();
}
}
```
**优势**
- 响应更及时
- 避免固定1秒延迟
- 实际完成时立即响应
### 3. SystemManagementViewModel - 性能监控改进
**现有代码问题**
```csharp
_performanceTimer = new System.Windows.Threading.DispatcherTimer();
_performanceTimer.Interval = TimeSpan.FromSeconds(5);
```
**改进方案**
```csharp
private DateTime _lastPerformanceUpdate = DateTime.MinValue;
private void OnApplicationIdle(object sender, EventArgs e)
{
// 控制更新频率5秒更新一次
if ((DateTime.Now - _lastPerformanceUpdate).TotalSeconds < 5) return;
UpdatePerformanceMetrics();
_lastPerformanceUpdate = DateTime.Now;
}
```
**优势**
- 只在UI空闲时更新
- 不会干扰用户操作
- 更灵活的更新时机
### 4. 耗时操作优化 - 使用Idle事件分片处理
对于长时间操作可以分解为小片段在Idle中执行
```csharp
public class IdleTaskProcessor
{
private Queue<Action> _taskQueue = new Queue<Action>();
private DateTime _lastExecutionTime = DateTime.MinValue;
public void ProcessLargeOperation(IEnumerable<Item> items)
{
// 将大操作分解为小任务
foreach (var batch in items.Batch(10))
{
_taskQueue.Enqueue(() => ProcessBatch(batch));
}
Application.Idle += OnIdleProcessTasks;
}
private void OnIdleProcessTasks(object sender, EventArgs e)
{
// 每次Idle只处理一个小任务
if (_taskQueue.Count > 0)
{
var task = _taskQueue.Dequeue();
task();
if (_taskQueue.Count == 0)
{
Application.Idle -= OnIdleProcessTasks;
}
}
}
}
```
### 5. 创建通用的IdleEventManager类
```csharp
/// <summary>
/// 统一的Idle事件管理器
/// </summary>
public class IdleEventManager
{
private static IdleEventManager _instance;
private readonly Dictionary<string, IdleTask> _tasks = new Dictionary<string, IdleTask>();
private bool _isSubscribed = false;
public static IdleEventManager Instance => _instance ??= new IdleEventManager();
/// <summary>
/// 注册Idle任务
/// </summary>
public void RegisterTask(string taskId, Action action, double minIntervalMs = 0, int priority = 0)
{
_tasks[taskId] = new IdleTask
{
Action = action,
MinInterval = minIntervalMs,
Priority = priority,
LastExecutionTime = DateTime.MinValue
};
EnsureSubscribed();
}
/// <summary>
/// 取消注册任务
/// </summary>
public void UnregisterTask(string taskId)
{
_tasks.Remove(taskId);
if (_tasks.Count == 0)
{
Unsubscribe();
}
}
private void OnApplicationIdle(object sender, EventArgs e)
{
var now = DateTime.Now;
// 按优先级排序执行
var tasksToRun = _tasks.Values
.Where(t => (now - t.LastExecutionTime).TotalMilliseconds >= t.MinInterval)
.OrderByDescending(t => t.Priority)
.ToList();
foreach (var task in tasksToRun)
{
try
{
task.Action();
task.LastExecutionTime = now;
}
catch (Exception ex)
{
LogManager.Error($"Idle任务执行失败: {ex.Message}");
}
}
}
private class IdleTask
{
public Action Action { get; set; }
public double MinInterval { get; set; }
public int Priority { get; set; }
public DateTime LastExecutionTime { get; set; }
}
}
```
## 具体改进文件列表
### 高优先级改进
1. **UIStateManager.cs** - 核心UI更新机制
2. **AnimationControlViewModel.cs** - 移除Task.Delay
3. **SystemManagementViewModel.cs** - 性能监控优化
### 中优先级改进
4. **LogisticsControlViewModel.cs** - 替换Thread.Sleep
5. **PathAnalysisViewModel.cs** - 优化分析延迟
6. **TimeTagViewModel.cs** - 时间标签更新
### 低优先级改进
7. **LogManager.cs** - 日志写入优化
8. **CommandExecutor.cs** - 命令执行等待
## 预期效果
### 性能提升
- **UI响应性提升30-50%** - 避免固定延迟和阻塞
- **CPU使用率降低** - 只在需要时执行更新
- **内存使用优化** - 减少Timer和Task对象创建
### 用户体验改进
- **操作更流畅** - 与UI刷新完美同步
- **延迟感减少** - 实时响应而非固定间隔
- **卡顿现象消除** - 避免阻塞主线程
### 代码质量提升
- **统一的更新模式** - 所有UI更新使用相同机制
- **更易维护** - 集中管理Idle任务
- **更好的错误处理** - 统一的异常捕获
## 实施步骤
### 第一阶段:基础设施
1. 创建IdleEventManager类
2. 添加单元测试
3. 集成到现有框架
### 第二阶段:核心组件改进
1. 改进UIStateManager
2. 优化AnimationControlViewModel
3. 测试UI响应性
### 第三阶段:全面推广
1. 逐步替换所有Timer使用
2. 消除Task.Delay和Thread.Sleep
3. 性能测试和优化
### 第四阶段:监控和调优
1. 添加性能监控
2. 收集使用数据
3. 持续优化
## 风险和注意事项
### 潜在风险
1. **过度使用Idle事件** - 可能导致Idle处理过重
2. **优先级冲突** - 需要合理安排任务优先级
3. **兼容性问题** - 某些场景可能仍需要Timer
### 缓解措施
1. **任务限流** - 控制每次Idle执行的任务数量
2. **优先级队列** - 确保重要任务优先执行
3. **混合模式** - 保留Timer作为备选方案
## 结论
通过将Idle事件方法推广到整个项目可以显著改善UI性能和用户体验。这种方法已经在PathAnimationManager中得到验证证明是可行和有效的。建议分阶段实施逐步改进确保稳定性。
## 参考资料
- Autodesk官方文档Application.Idle事件使用指南
- PathAnimationManager.cs成功的Idle事件实现案例
- doc/working/idle_event_animation_strategy.md动画系统改进文档

View File

@ -1,7 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using Autodesk.Navisworks.Api;
using NavisApplication = Autodesk.Navisworks.Api.Application;
@ -20,6 +18,7 @@ namespace NavisworksTransport.Core.Animation
Finished // 已完成
}
/// <summary>
/// 路径动画管理器 - 基于TimeLiner和动态变换实现沿路径的动画效果
/// 注意由于Navisworks API限制无法直接使用Animator API因此使用OverridePermanentTransform实现动画
@ -30,19 +29,25 @@ namespace NavisworksTransport.Core.Animation
private ModelItem _animatedObject;
private List<Point3D> _pathPoints;
// 动画渲染系统
private Timer _animationTimer;
// === Idle事件动画系统 ===
private double _frameInterval; // 帧间隔(毫秒)
private DateTime _lastFrameTime = DateTime.MinValue; // 上一帧时间
private int _collisionCheckCounter = 0; // 碰撞检测计数器(用于降频)
// === 动画参数 ===
private double _animationDuration = 10.0; // 动画总时长(秒)
private DateTime _animationStartTime;
private int _animationFrameRate = 30; // 动画帧率默认30FPS
private int _animationFrameCount = 0; // 动画帧计数
// 碰撞检测系统
private Timer _collisionTimer;
private double _collisionDetectionAccuracy = 0.1; // 检测精度默认0.1米)
private double _movementSpeed = 1.0; // 运动速度默认1米/秒)
private double _detectionGap = 0.05; // 检测间隙默认0.05米)
// === 性能监控 ===
private int _fpsFrameCount = 0;
private DateTime _fpsCounterStart = DateTime.Now;
private double _actualFPS = 0;
private Transform3D _originalTransform;
private Point3D _originalCenter; // 存储部件的原始中心位置
private Point3D _currentPosition; // 存储部件的当前位置
@ -76,11 +81,16 @@ namespace NavisworksTransport.Core.Animation
{
_pathPoints = new List<Point3D>();
// 初始化动画模式
_frameInterval = 1000.0 / _animationFrameRate; // 计算帧间隔
_lastFrameTime = DateTime.MinValue;
_fpsCounterStart = DateTime.Now;
// 初始化 TimeLiner 集成
try
{
_timeLinerManager = new TimeLinerIntegrationManager();
LogManager.Info("PathAnimationManager 已集成 TimeLiner 功能");
LogManager.Info($"PathAnimationManager 初始化完成 - Idle事件模式, 目标FPS: {_animationFrameRate}");
}
catch (Exception ex)
{
@ -281,24 +291,18 @@ namespace NavisworksTransport.Core.Animation
_animationFrameCount = 0; // 重置帧计数
_pausedProgress = 0.0; // 重置暂停进度
// 创建并启动动画渲染定时器
_animationTimer = new Timer();
_animationTimer.Interval = (int)(1000.0 / _animationFrameRate); // 根据帧率计算间隔
_animationTimer.Tick += AnimationTimer_Tick;
_animationTimer.Start();
// 创建并启动碰撞检测定时器
var collisionInterval = CalculateCollisionDetectionInterval();
_collisionTimer = new Timer();
_collisionTimer.Interval = collisionInterval;
_collisionTimer.Tick += CollisionTimer_Tick;
_collisionTimer.Start();
LogManager.Info($"动画定时器:{_animationTimer.Interval}ms间隔{_animationFrameRate}FPS");
LogManager.Info($"碰撞检测定时器:{collisionInterval}ms间隔");
// 重置Idle模式状态
_lastFrameTime = DateTime.MinValue;
_collisionCheckCounter = 0;
_fpsFrameCount = 0;
_fpsCounterStart = DateTime.Now;
_frameInterval = 1000.0 / _animationFrameRate;
// 订阅Idle事件
NavisApplication.Idle += OnApplicationIdle;
SetState(AnimationState.Playing);
LogManager.Info("动画开始播放");
LogManager.Info("动画开始播放 - Idle事件模式");
}
catch (Exception ex)
{
@ -321,8 +325,9 @@ namespace NavisworksTransport.Core.Animation
return;
}
// 清理定时器资源
CleanupTimers();
// 注销Idle事件
NavisApplication.Idle -= OnApplicationIdle;
LogManager.Debug("[Idle模式] Idle事件已注销");
SetState(AnimationState.Stopped);
_pausedProgress = 0.0; // 重置暂停进度
@ -350,8 +355,8 @@ namespace NavisworksTransport.Core.Animation
{
try
{
// 清理定时器资源
CleanupTimers();
// 注销Idle事件
NavisApplication.Idle -= OnApplicationIdle;
// 重置暂停进度
_pausedProgress = 0.0;
@ -359,7 +364,7 @@ namespace NavisworksTransport.Core.Animation
// 直接设置为完成状态,避免中间状态切换
SetState(AnimationState.Finished);
LogManager.Info("动画播放完成");
LogManager.Info($"动画播放完成,总帧数: {_animationFrameCount}, 平均FPS: {_actualFPS:F1}");
// 触发旧版完成事件(保持兼容性)
AnimationCompleted?.Invoke(this, EventArgs.Empty);
@ -380,26 +385,6 @@ namespace NavisworksTransport.Core.Animation
}
}
/// <summary>
/// 清理定时器资源的私有方法
/// </summary>
private void CleanupTimers()
{
if (_animationTimer != null)
{
_animationTimer.Stop();
_animationTimer.Dispose();
_animationTimer = null;
}
if (_collisionTimer != null)
{
_collisionTimer.Stop();
_collisionTimer.Dispose();
_collisionTimer = null;
}
}
/// <summary>
/// 暂停动画
/// </summary>
@ -413,16 +398,9 @@ namespace NavisworksTransport.Core.Animation
double elapsedSeconds = (DateTime.Now - _animationStartTime).TotalSeconds;
_pausedProgress = Math.Min(elapsedSeconds / _animationDuration, 1.0);
// 停止定时器
if (_animationTimer != null)
{
_animationTimer.Stop();
}
if (_collisionTimer != null)
{
_collisionTimer.Stop();
}
// 暂时注销Idle事件
NavisApplication.Idle -= OnApplicationIdle;
LogManager.Debug("[Idle模式] 暂停时注销Idle事件");
SetState(AnimationState.Paused);
LogManager.Info($"动画已暂停,当前进度: {_pausedProgress:F3}");
@ -453,16 +431,10 @@ namespace NavisworksTransport.Core.Animation
// 设置开始时间,使得当前时间对应保存的暂停进度
_animationStartTime = DateTime.Now.AddSeconds(-(_pausedProgress * _animationDuration));
// 重新启动定时器
if (_animationTimer != null)
{
_animationTimer.Start();
}
if (_collisionTimer != null)
{
_collisionTimer.Start();
}
// 重新订阅Idle事件
_lastFrameTime = DateTime.MinValue; // 确保下一帧立即执行
NavisApplication.Idle += OnApplicationIdle;
LogManager.Debug("[Idle模式] 恢复时重新订阅Idle事件");
SetState(AnimationState.Playing);
LogManager.Info($"动画已恢复,从进度 {_pausedProgress:F3} 继续播放");
@ -571,41 +543,6 @@ namespace NavisworksTransport.Core.Animation
}
}
/// <summary>
/// 动画定时器事件处理
/// </summary>
private void AnimationTimer_Tick(object sender, EventArgs e)
{
try
{
double elapsedSeconds = (DateTime.Now - _animationStartTime).TotalSeconds;
double progress = Math.Min(elapsedSeconds / _animationDuration, 1.0);
// 更新UI进度条直接传递百分比double值
var progressPercent = progress * 100;
ProgressChanged?.Invoke(this, progressPercent);
// 同步进度到 TimeLiner如果可用
if (_timeLinerManager != null && !string.IsNullOrEmpty(_currentTaskId))
{
_timeLinerManager.UpdateTaskProgress(_currentTaskId, progress, _currentState);
}
Point3D newPosition = InterpolatePosition(progress);
UpdateObjectPosition(newPosition);
if (progress >= 1.0)
{
// 避免双重状态设置,直接完成动画并清理资源
FinishAnimation();
}
}
catch (Exception ex)
{
LogManager.Error($"动画帧更新失败: {ex.Message}");
StopAnimation(); // 发生错误时停止动画
}
}
/// <summary>
/// 根据进度插值计算当前位置
@ -982,7 +919,7 @@ namespace NavisworksTransport.Core.Animation
/// <summary>
/// 获取动画状态
/// </summary>
public bool IsAnimating => _animationTimer != null && _animationTimer.Enabled;
public bool IsAnimating => _currentState == AnimationState.Playing;
/// <summary>
/// 获取 TimeLiner 是否可用
@ -1166,43 +1103,7 @@ namespace NavisworksTransport.Core.Animation
}
}
/// <summary>
/// 碰撞检测定时器事件处理
/// </summary>
private void CollisionTimer_Tick(object sender, EventArgs e)
{
try
{
// 只在动画播放时执行碰撞检测
if (_currentState == AnimationState.Playing)
{
CheckAndHighlightCollisionsWithClashDetective();
}
}
catch (Exception ex)
{
LogManager.Error($"碰撞检测失败: {ex.Message}");
}
}
/// <summary>
/// 计算碰撞检测间隔(毫秒)
/// </summary>
private int CalculateCollisionDetectionInterval()
{
// 碰撞检测频率 = 运动速度 / 检测精度 (次/秒)
var detectionFrequency = _movementSpeed / _collisionDetectionAccuracy;
// 转换为毫秒间隔
var intervalMs = (int)(1000.0 / detectionFrequency);
// 确保间隔不小于50ms避免过于频繁
intervalMs = Math.Max(50, intervalMs);
LogManager.Info($"碰撞检测参数:速度={_movementSpeed}m/s, 精度={_collisionDetectionAccuracy}m, 频率={detectionFrequency:F2}次/s, 间隔={intervalMs}ms");
return intervalMs;
}
/// <summary>
/// 设置动画帧率
@ -1289,5 +1190,106 @@ namespace NavisworksTransport.Core.Animation
SetupAnimation(animatedObject, pathPoints, durationSeconds);
SetState(AnimationState.Ready);
}
#region
/// <summary>
/// Idle事件处理器 - 新的动画核心
/// </summary>
private void OnApplicationIdle(object sender, EventArgs e)
{
try
{
// 检查动画状态
if (_currentState != AnimationState.Playing)
{
return; // 非播放状态,跳过更新
}
// 帧率控制
var now = DateTime.Now;
var elapsed = (now - _lastFrameTime).TotalMilliseconds;
if (elapsed < _frameInterval)
{
return; // 还没到下一帧时间跳过本次Idle
}
// 计算动画进度
var totalElapsed = (now - _animationStartTime).TotalSeconds;
var progress = Math.Min(totalElapsed / _animationDuration, 1.0);
// 更新动画位置(使用原有逻辑)
Point3D newPosition = InterpolatePosition(progress);
UpdateObjectPosition(newPosition);
// 触发进度事件
var progressPercent = progress * 100;
ProgressChanged?.Invoke(this, progressPercent);
// 同步进度到 TimeLiner如果可用
if (_timeLinerManager != null && !string.IsNullOrEmpty(_currentTaskId))
{
_timeLinerManager.UpdateTaskProgress(_currentTaskId, progress, _currentState);
}
// 碰撞检测降频执行每3帧检测一次
_collisionCheckCounter++;
if (_collisionCheckCounter >= 3)
{
_collisionCheckCounter = 0;
if (_currentState == AnimationState.Playing)
{
CheckAndHighlightCollisionsWithClashDetective();
}
}
// 记录帧时间
_lastFrameTime = now;
_animationFrameCount++;
// 性能监控
UpdateFPSCounter();
// 检查动画完成
if (progress >= 1.0)
{
// 先注销事件,避免重复调用
NavisApplication.Idle -= OnApplicationIdle;
FinishAnimation();
}
}
catch (Exception ex)
{
LogManager.Error($"[Idle模式] 动画更新异常: {ex.Message}");
// 发生异常时停止动画,避免持续错误
StopAnimation();
}
}
/// <summary>
/// 更新FPS计数器
/// </summary>
private void UpdateFPSCounter()
{
_fpsFrameCount++;
var elapsed = (DateTime.Now - _fpsCounterStart).TotalSeconds;
// 每秒更新一次FPS统计
if (elapsed >= 1.0)
{
_actualFPS = _fpsFrameCount / elapsed;
LogManager.Debug($"[性能监控] 实际FPS: {_actualFPS:F1} (目标: {_animationFrameRate})");
// 重置计数器
_fpsFrameCount = 0;
_fpsCounterStart = DateTime.Now;
}
}
#endregion
}
}

View File

@ -1363,7 +1363,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
_pathAnimationManager.SetMovementSpeed(_movementSpeed);
_pathAnimationManager.SetDetectionGap(_detectionGap);
LogManager.Debug($"碰撞检测参数已更新: 帧率={_animationFrameRate}FPS, 精度={_collisionDetectionAccuracy:F2}m, 速度={_movementSpeed:F1}m/s, 间隙={_detectionGap:F2}m");
LogManager.Debug($"碰撞检测参数已更新: 动画帧率={_animationFrameRate}FPS, 精度={_collisionDetectionAccuracy:F2}m, 速度={_movementSpeed:F1}m/s, 间隙={_detectionGap:F2}m");
}
}
catch (Exception ex)