Skip to content

Action Renderer(动作渲染器系统)

动作渲染器系统提供一种声明式的动作执行框架:将动作定义为数据(Action),由对应的渲染器(ActionRenderer)在 Cesium 场景中执行可视化。系统基于观察者模式,通过注册中心管理各类渲染器。

Action 接口

typescript
interface Action {
  id: string                              // 唯一标识
  type: string                            // 动作类型(如 'MoveTo', 'TLEOrbit')
  entityId: string                        // 目标实体 ID
  name?: string                           // 动作名称(可选)
  startTime?: number                      // 开始时间(epoch 毫秒)
  endTime?: number                        // 结束时间(epoch 毫秒)
  parameters: Record<string, unknown>     // 动作参数(类型相关)
}

ActionRenderContext 接口

typescript
interface ActionRenderContext {
  viewer: Viewer   // Cesium Viewer 实例
}

渲染器通过 context 访问 Viewer 和场景数据。

ActionRenderer 接口

所有渲染器需实现此接口:

typescript
interface ActionRenderer {
  readonly type: string                                    // 渲染器类型标识
  create(action: Action, context: ActionRenderContext): void   // 创建动作可视化
  update(action: Action, context: ActionRenderContext): void   // 更新动作
  destroy(actionId: string): void                              // 销毁动作
}

ActionRendererRegistry(注册中心)

单例模式,全局共享:

typescript
import { actionRendererRegistry } from './ActionRendererRegistry'

// 注册渲染器
actionRendererRegistry.register('MoveTo', new MoveToRenderer())

// 获取渲染器
const renderer = actionRendererRegistry.get('TLEOrbit')

// 检查是否已注册
if (actionRendererRegistry.has('MissileStrike')) { /* ... */ }

// 注销渲染器
actionRendererRegistry.unregister('MoveTo')

内置渲染器

1. MoveToRenderer(type: 'MoveTo'

实体平滑移动渲染器。支持从起点移动到终点的过程动画,在 startTimeendTime 内完成插值。

参数类型: { from?: { lon, lat, alt }, to: { lon, lat, alt } }

参数类型必填说明
from{ lon, lat, alt }起始位置,省略则使用实体当前位置
to{ lon, lat, alt }目标位置

行为说明:

  • startTimeendTime 为空时执行瞬移
  • 支持多个动作叠加到同一实体(自动合并轨迹)
  • 实体位置使用 SampledPositionProperty 驱动

示例:

typescript
const action: Action = {
  id: 'move-001',
  type: 'MoveTo',
  entityId: 'aircraft-1',
  startTime: Date.now(),
  endTime: Date.now() + 5000,
  parameters: {
    from: { lon: 116.4, lat: 39.9, alt: 1000 },
    to: { lon: 116.5, lat: 40.0, alt: 2000 }
  }
}

2. TLEOrbitRenderer(type: 'TLEOrbit'

基于 TLE 轨道根数的卫星轨道渲染器。使用 SatelliteOrbitTool 计算轨道传播,驱动实体沿轨道运动。

参数类型: { tleLine1: string, tleLine2: string, step?: number }

参数类型必填说明
tleLine1stringTLE 第一行
tleLine2stringTLE 第二行
stepnumber采样步长(秒),默认 60

行为说明:

  • 自动计算 startTimeendTime 范围内的轨道轨迹
  • 自动为实体添加 VelocityOrientationProperty 速度朝向
  • 支持多个 TLEOrbit 动作叠加到同一实体

示例:

typescript
const action: Action = {
  id: 'tle-001',
  type: 'TLEOrbit',
  entityId: 'satellite-1',
  name: 'ISS',
  startTime: Date.now(),
  endTime: Date.now() + 3600000,  // 1 小时
  parameters: {
    tleLine1: '1 25544U 98067A   24123.51782528  .00016717  00000-0  10270-4 0  9001',
    tleLine2: '2 25544  51.6414  80.5114 0006702  89.1582 271.0270 15.50367711452180',
    step: 30
  }
}

3. MissileStrikeRenderer(type: 'MissileStrike'

导弹打击动画渲染器。支持直线轨迹和弹道轨迹两种模式。

参数类型:

typescript
{
  missileUri?: string                                 // 导弹模型 URL(可选)
  targetType?: 'entity' | 'point'                     // 目标类型
  targetEntityId?: string                             // 目标实体 ID
  targetPoint?: { lon: number; lat: number; alt?: number }  // 目标坐标点
  trajectory?: 'line' | 'ballistic'                  // 轨迹类型
}
参数类型必填说明
missileUristring3D 模型地址,省略则只显示路径
targetType'entity' | 'point'目标类型
targetEntityIdstring动态目标实体 ID
targetPointobject静态目标坐标点
trajectory'line' | 'ballistic'轨迹类型,默认 'line'

轨迹计算:

  • 'line' — 线性插值轨迹
  • 'ballistic' — 贝塞尔曲线弹道轨迹(峰值高度 150km)

示例:

typescript
const action: Action = {
  id: 'strike-001',
  type: 'MissileStrike',
  entityId: 'launcher-1',
  startTime: Date.now(),
  endTime: Date.now() + 10000,
  parameters: {
    missileUri: '/models/missile.gltf',
    targetType: 'point',
    targetPoint: { lon: 120.5, lat: 30.5, alt: 0 },
    trajectory: 'ballistic'
  }
}

自定义渲染器

实现自定义渲染器只需实现 ActionRenderer 接口并注册:

typescript
import { actionRendererRegistry } from '../ActionRendererRegistry'
import type { Action, ActionRenderer, ActionRenderContext } from '../action-renderers'

class RadarScanRenderer implements ActionRenderer {
  readonly type = 'RadarScan'

  create(action: Action, context: ActionRenderContext): void {
    const entity = context.viewer.entities.getById(action.entityId)
    if (!entity) return
    // 创建雷达扫描可视化...
    console.log(`RadarScan: ${action.entityId}`, action.parameters)
  }

  update(action: Action, context: ActionRenderContext): void {
    this.destroy(action.id)
    this.create(action, context)
  }

  destroy(actionId: string): void {
    // 清理资源...
  }
}

// 注册自定义渲染器
actionRendererRegistry.register('RadarScan', new RadarScanRenderer())

使用内置渲染器

typescript
import { actionRendererRegistry } from './ActionRendererRegistry'
import { MoveToRenderer } from './renderers/MoveToRenderer'
import { TLEOrbitRenderer } from './renderers/TLEOrbitRenderer'
import { MissileStrikeRenderer } from './renderers/MissileStrikeRenderer'

// 注册内置渲染器
actionRendererRegistry.register('MoveTo', new MoveToRenderer())
actionRendererRegistry.register('TLEOrbit', new TLEOrbitRenderer())
actionRendererRegistry.register('MissileStrike', new MissileStrikeRenderer())

// 执行动作
const renderer = actionRendererRegistry.get('MoveTo')
if (renderer) {
  renderer.create(moveAction, { viewer })
}