资讯动态

Cesium飞行漫游实战:从flyTo到路径动画的相机控制与避坑指南

发布时间:2026/10/17 9:27:52 来源:尧图企业网站定制
简介Cesium飞行漫游是一份基于Cesium开源库的3D飞行漫游功能资源包面向Web GIS开发与三维可视化学习者重点解决如何在三维地球场景中实现相机自由飞行、路线规划和漫游交互。资源共包含6854个文件压缩包约58.26MB文件类型以JavaScript脚本、HTML页面、CSS样式为主并含大量b3dm、gltf、glb等三维模型瓦片以及png、jpg、gif图像素材和json、czml数据配置文件构成可直接运行且目录结构清晰的Cesium项目资源体系。内容围绕Cesium的Viewer对象、Scene场景、Camera控制机制展开涵盖飞行启动、暂停、前进后退、预设路线飞行、路线序列化保存与加载、清空路线、退出飞行等完整交互逻辑涉及相机朝向与移动速度调整并配有路径点数据、示例模型与完整示例页面便于对照学习与二次开发。目前已有5849人学习下载适合需要快速上手Cesium漫游开发、参考相机控制与路径管理代码的开发者。1. Cesium飞行漫游先搞清楚你要的是“相机飞”还是“模型飞”Cesium飞行漫游这四个字听起来像是让模型飞起来但真做几个项目你就会发现它本质上是一套相机动画方案。某次项目里同事A同学让我看刚做好的“无人机巡检漫游”结果无人机模型悬在半空不动画面却一直在往前推——他把相机飞当成了模型飞。这类需求在三维GIS里非常高频汇报演示、路线预演、航道巡检、园区鸟瞰本质上都是在问“怎么让镜头按照我想要的方式动起来”。这篇笔记面向正在做Cesium原生开发的人不讲封装好的第三方库只讲底层方案的选型、最小可跑通代码、参数调整方法和最容易翻车的几个坑。新手照着能落地熟手可以直接跳到第5章看避坑清单。2. 先弄懂飞行漫游的四种实现路径flyTo、lookAt、路径动画与实体轨迹2.1 为什么飞行漫游不是简单调一个API很多人第一次接触Cesium飞行漫游第一反应是“我调一下camera.flyTo不就行了”。确实flyTo能让你从当前视角飞到另一个点但这只是最外层的表现。实际做需求的时候“漫游”这个词背后藏了三种完全不同的诉求点对点飞跃、沿路径回放、围绕目标观察。这三种诉求对应的实现机制并不一样用错方案大概率要返工。Cesium里所有“动”都由三样东西配合完成场景中的Camera、驱动时间的Clock、承载轨迹数据的DataSource/实体。Camera负责你看到什么Clock负责什么时刻看到轨迹数据负责在哪个时刻出现在哪个位置。理解了这个结构飞行漫游就不再是“一个API的事”而是一套“相机时间位置”的组合编排。新手最容易犯的错是只盯着camera说明文档忽略了clock和时间轴结果路径怎么都跑不起来。我的习惯是先问自己三个问题观众看到的是第一人称还是第三人称是否需要暂停、倍速、循环回放轨迹点是从后端拿还是前端手写这三个答案基本决定了方案选型比先查API更省时间。2.2 四种实现思路的适用边界与选型对照Cesium里常用的实现路径有四类我列一个我平时做技术方案时直接套用的对照表实现方案核心机制适合场景主要坑Camera.flyTo相机从一个视角飞到另一个视角单点定位、场景切换、汇报开场飞行过程不可控中间姿态是插值出来的Camera.lookAt相机锁定某个目标点保持相对偏移围绕单体建筑观察、跟随移动目标目标移动时必须每帧更新否则镜头会“飘走”SampledPositionProperty Clock按时间戳采样位置驱动相机或实体运动沿轨迹飞行、巡检预演、历史轨迹回放时间轴设置稍复杂容易搞出瞬间移位Entity路径动画实体按轨迹移动同时画出一条轨迹线无人机路径展示、车辆轨迹可视化实体运动不等于相机运动观看体验偏“上帝视角”从我做过几个模拟项目X和各类型Demo的角度说90%的“飞行漫游”需求只需要两种组合第一种是画面要跟着轨迹走用SampledPositionProperty驱动相机第二种是画面要稳定观察某一个对象用lookAt。flyTo更多是作为“入场动作”或“场景调度”存在而不是漫游主体。选型时你先问观众是坐在驾驶员位置看窗外还是站在旁边看飞机飞。前者重点调相机后者重点调实体。2.3 坐标系与相机姿态绕不开的heading/pitch/roll不管选哪条路你都得先过坐标系这一关。Cesium里常规的做法是用经纬度高程描述一个位置也就是Cartographic经度、纬度、高度但Camera的destination接口吃的是Cartesian3世界坐标所以第一步永远是转换const position Cesium.Cartesian3.fromDegrees(115.0, 34.0, 800);这一行代码做过无数遍但每次都要提醒自己经纬度单位是度高度单位是米方向别写反。接下来是相机姿态也就是orientation里的三个量heading、pitch、roll。它们都是弧度制不是度数。pitch为负代表向下看roll为0代表不倾斜这是最简单也最容易漏的点。我在项目里一般会准备一个“视角预设”表比如视角类型heading弧度pitch弧度roll弧度效果平视前方000正前方平视俯视向下0-45度转弧度0斜向下看垂直俯视0-90度转弧度0正上方看侧向环绕90度转弧度-30度转弧度0从侧面斜视一个很容易踩的坑是很多人以为heading永远是“以正北为0”但实际Cesium对heading的基准定义在不同版本和不同局部坐标系下会有细节差异而且你的模型建模朝向也会影响最终表现。所以我的习惯是不要死记基准先用默认姿态打一个点飞过去看一眼然后微调heading通过前后对比去校准这比对着文档猜要高效得多。另外所有数学计算里用Cesium.Math.toRadians包一层能省掉大量“度数当弧度传”的翻车现场。3. 从最小可运行场景到路径漫游用Viewer与Camera写第一版3.1 初始化Viewer并关闭默认UI的三个最小参数任何一个飞行漫游页面第一步都是new一个Viewer。但默认Viewer带着一堆控件对漫游场景来说又遮挡视线又拖性能。我常用的初始化方式是这样的const viewer new Cesium.Viewer(cesiumContainer, { animation: false, // 关闭左下角动画控件 timeline: false, // 关闭底部时间轴 baseLayerPicker: false, // 关闭底图切换器 geocoder: false, // 关闭地址搜索框 homeButton: false, // 关闭首页按钮 sceneModePicker: false, // 关闭2D/3D切换 navigationHelpButton: false // 关闭帮助按钮 });这一行一眼看过去很啰嗦但每一个参数都在解决实际问题。animation和timeline是和时间轴强相关的控件飞行漫游你自己有clock控制逻辑留着默认控件反而会让用户乱拖时间轴导致状态错乱。baseLayerPicker和sceneModePicker在业务系统里基本用不上关掉可以减少误操作。另外提醒一句这里没有写imageryProvider和terrainProvider意味着用的是默认配置。默认底图在不同版本里加载情况不一样有的需要配置Token有的会打印警告。项目里不要依赖默认值后续都应该显式配置。我的建议是把这个初始化封装成一个函数统一设置地形Provider和影像Provider这样后面所有页面复用。飞行漫游强依赖地形数据否则第5章里说的穿地问题一定会遇到。3.2 flyTo飞跃单点定位与视角落版初始化完Viewer第一个能跑通的效果就是flyTo。它的作用是让相机从当前位置平顺飞到目标点demo里最常用的代码是viewer.camera.flyTo({ destination: Cesium.Cartesian3.fromDegrees(115.0, 34.0, 2000), orientation: { heading: Cesium.Math.toRadians(0), pitch: Cesium.Math.toRadians(-45), roll: 0 }, duration: 4 });这里的参数逐个解释destination是目标位置必须是一个Cartesian3世界坐标不能直接传数组。高度2000是相机距离椭球面的高度单位米注意这是视点高度不是飞行高度。orientation决定相机到达目标点后的朝向pitch取-45度表示从斜上方往下看这是航拍里最常见视角。duration是飞行持续秒数默认3秒项目里我一般设4到5秒太短会像瞬移太长观众会不耐烦。flyTo其实会返回一个Promise等飞行结束之后会resolve。这个特性很适合做“先飞到路口再开始沿路漫游”这类嵌套逻辑await viewer.camera.flyTo({ destination: Cesium.Cartesian3.fromDegrees(115.0, 34.0, 2000), duration: 3 }); // 飞行结束后的业务逻辑 startRoutePlayback();有一个细节需要注意如果用户在飞行过程中手动拖动了相机这个Promise会被reject所以后面做长串飞行编排时一定要用try/catch包住否则控制台会刷一堆未处理异常。这个问题我在第6章还会用到。3.3 沿路径飞行SampledPositionProperty与时间轴绑定单点飞跃只是入场真正的飞行漫游核心是“让相机沿一条路径移动”。这里的关键组件是SampledPositionProperty它负责存储“某个时刻在某个位置”的采样点然后按时间插值得到任意时刻的位置。用它驱动相机的典型写法如下// 1. 创建采样位置属性 const positionProperty new Cesium.SampledPositionProperty(); // 2. 定义起止时间用JulianDate表示 const start Cesium.JulianDate.fromDate(new Date()); const stop Cesium.JulianDate.addSeconds(start, 30, new Cesium.JulianDate()); // 3. 往属性里添加“时间位置”采样点 positionProperty.addSample(start, Cesium.Cartesian3.fromDegrees(115.0, 34.0, 500)); positionProperty.addSample(Cesium.JulianDate.addSeconds(start, 10, new Cesium.JulianDate()), Cesium.Cartesian3.fromDegrees(115.2, 34.1, 450)); positionProperty.addSample(stop, Cesium.Cartesian3.fromDegrees(115.4, 34.2, 400)); // 4. 配置时钟 viewer.clock.startTime start; viewer.clock.stopTime stop; viewer.clock.currentTime start; viewer.clock.clockRange Cesium.ClockRange.CLAMPED; viewer.clock.shouldAnimate true; // 5. 监听tick每帧取出当前位置并让相机看向它 viewer.clock.onTick.addEventListener(() { const position positionProperty.getValue(viewer.clock.currentTime); if (Cesium.defined(position)) { viewer.camera.lookAt(position, new Cesium.HeadingPitchRange(0, Cesium.Math.toRadians(-30), 500)); } });这段代码拆开看前两步是准备时间容器SampledPositionProperty本身不关心你从哪里来它只认时间和位置。addSample的每一个参数都必须是JulianDate和Cartesian3混入JS Date或者数组会得到很诡异的结果。第四步是让Viewer的时钟跑起来重点是clockRange设成CLAMPED表示播完就停在终点不循环不跳跃。第五步是关键中的关键每帧从当前位置属性里getValue然后调camera.lookAt让相机看向那个点。这里我把相机的目标锁定在这个位置点所以画面看起来就是“第一视角贴地飞行”视线方向由HeadingPitchRange控制。第三个参数500是相机到目标点的距离单位米值越大视角越远。实际项目中我一般会做一个小弹窗实时展示当前经纬度和高度方便确认轨迹是否正常。这个最小实现跑通之后你就有了一个可以替换数据源的完整骨架把addSample换成后端返回的GPS轨迹就是一条真实巡检路径。4. 把漫游做成航拍级效果轨迹插值、缓动曲线与模型跟随4.1 轨迹采样密度与插值算法如何影响飞行平滑度用第3章的骨架跑真实轨迹时你会发现一个明显问题路径转弯处非常生硬镜头像折线一样一顿一顿。原因是SampledPositionProperty默认用线性插值两点之间直接连直线速度方向在采样点处突变。解决方案是切换插值算法positionProperty.setInterpolationOptions({ interpolationAlgorithm: Cesium.HermitePolynomialApproximation, interpolationDegree: 2 // 或3 });Hermite多项式插值会考虑前后点的切线方向让轨迹在采样点处连续过渡。这里有一个经验参数degree取2通常已经足够平滑取3会更圆润但计算量稍大。点数量几千个时差别不明显但如果是几万点的长轨迹1和3的帧率差距肉眼可见。插值算法不是万能的它解决的是“点之间怎么补”的问题但补不出来你没采样的地形和转弯。我做过一条沿河道的模拟路径最开始只在每个拐弯处点了几个关键点结果飞行时轨迹直接切过山体后来改成每隔三五十米一个采样点转弯处加密到十米一个视觉上就正常了。这里有一个判断标准直行段采样点间距可以放到500到1000米但一旦heading方向变化超过15度就必须在转弯前后各补一个点让插值有足够的“空间”转弯。另外一个关键点是插值算法在“大跨度跳跃”的情况下会出现弧线飞出的现象比如从起点到终点只给了两个点Hermite插值会画出一条越过地表的弧线。所以如果路径中有长距离直飞中间至少保留一个中间点避免插值自动“拉弓”。这算是最容易忽略的玄学细节出问题不要怀疑算法先怀疑采样点间距。4.2 让相机锁定目标lookAt跟随与第一人称克制前面用了camera.lookAt来跟随轨迹点这个API的全名是“把相机姿态锁定到目标的一个相对方向上”。它有三个参数目标位置、HeadingPitchRange。经常有人把它和flyTo搞混觉得lookAt是“飞的更稳的flyTo”其实两者完全不同。flyTo是一次性过渡动画而lookAt是持续锁定状态。只要设置一次相机会在后续每帧都保持这个相对方位直到你调用另一个方法把它解锁。典型应用是实体跟随比如你有一个车辆模型沿轨迹移动相机要始终跟在它侧后方。写法是viewer.clock.onTick.addEventListener(() { const target trackedEntity.position.getValue(viewer.clock.currentTime); if (Cesium.defined(target)) { viewer.camera.lookAt(target, new Cesium.HeadingPitchRange( Cesium.Math.toRadians(45), // 从目标右后侧看 Cesium.Math.toRadians(-20), // 俯角20度 200 // 距离目标200米 )); } });这里HeadingPitchRange的第一个参数是水平方位角0表示正前方正值是顺时针偏。实际效果就是镜头一直悬停在被跟踪对象侧面。但我的血泪经验是不要全程lookAt。原因有两个一是相机被锁定时用户手动拖拽视角会非常别扭镜头刚被拖动又被拉回二是长时间固定相对角度容易让观众晕因为这些角度是人为设定的不是自然视角。正确做法是只有需要稳定跟随目标时打开lookAt需要在场景里自由观察时先调用viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY)把锁定解除。这个IDENTITY参数看起来像魔法数字实际就是“清空锁定矩阵”的标准写法。4.3 clock控制倍速、暂停与循环回放飞行漫游是给观众看的必须提供播放控制。Cesium的clock对象天然支持这些能力不需要自己造轮子。常用操作如下// 暂停 viewer.clock.shouldAnimate false; // 继续 viewer.clock.shouldAnimate true; // 2倍速 viewer.clock.multiplier 2.0; // 回到起点重新播 viewer.clock.currentTime viewer.clock.startTime; viewer.clock.shouldAnimate true; // 播放结束后循环 viewer.clock.clockRange Cesium.ClockRange.LOOP_STOP;clockRange有三个值CLAMPED播完停在最后LOOP_STOP播完回到开头UNBOUNDED允许超过起止时间继续走。漫游回放我一般用LOOP_STOP因为客户总要在会上反复看。multiplier的单位是“虚拟时间流逝速度 / 真实时间”默认1也就是动画里的时间和现实同步。做预览时我习惯设2到3看得清癥结又不太拖沓。但倍速不要设太高因为采样点的时间间隔是固定的multiplier超过10以后每帧跨越的虚拟时间变大getValue可能跳过一个完整的转弯点造成抖动。我试过一次设20倍速想快速预览结果画面像幻灯片一样跳最后还得老老实实降回来。还有一个容易忽视的问题如果你在页面代码里每次都用new Date()来生成startTime那么每次刷新或重播起止时间都会变化导致循环回放的“起点”对不齐实体位置会闪跳。正确做法是把startTime做成固定值比如用JulianDate.fromISO8601(2024-06-01T08:00:00Z)写死这样反复重播永远在同一时间基准上。5. 飞行漫游避坑实战五个高频问题的现象、原因与修复5.1 相机穿入地形高度模式与地形采样现象轨迹在山区飞行时相机直接插入山体然后从另一侧穿出来镜头里看到一大片土黄色断层。原因轨迹点的经纬度高程通常是“椭球高”也就是相对WGS84椭球面的高度而不是相对真实地形表面的高度。Cesium默认场景只有椭球没有地形起伏你没加载地形Provider时高度500米的点可能已经远低于真实地表。即使加载了地形采样点高度是写死的静态值不会跟随地形起伏变化穿山是必然结果。解决必须在播放前对轨迹点做地形贴合。Cesium提供的一个方案是sampleTerrainMostDetailed它根据地形Provider实时查询每个经纬度点的高程const terrainProvider await Cesium.createWorldTerrainAsync(); const positions rawPoints.map(p Cesium.Cartesian3.fromDegrees(p.lon, p.lat, 0)); const updatedPositions await Cesium.sampleTerrainMostDetailed(terrainProvider, positions); // 覆盖原高度并加上安全净空 rawPoints.forEach((p, i) { p.height updatedPositions[i].height 100; // 净空100米 });这段代码要注意几点sampleTerrainMostDetailed要求传入的是Cartesian3数组且需要地形Provider已经加载完成查询结果是异步的要在await之后使用加净空是必须的否则贴合地形后相机贴地飞行遇到起伏镜头会剧烈抖动。如果用的是离线地形方案这个方法不一定可用需要走后端高程服务逻辑一样只是数据源不同。我一般会在轨迹编辑阶段就把这个净空算好而不是播放时实时算原因是播放时算会造成前几帧的高度跳变。5.2 时间轴错乱clock、JulianDate与“突然瞬移”现象点击播放按钮后相机或模型没有按预期从起点出发而是瞬间出现在轨迹中途或者来回闪跳看起来完全失控。原因这个我排查过好几次最常见的是当前时间没设置到起点。Viewer的clock在页面初始化之后就开始走它内部维护一个currentTime。你把采样点加进positionProperty后如果没有显式把clock.currentTime设成startTimegetValue拿到的可能是轨迹末尾甚至轨迹范围之外的位置于是出现“一跳到位”。还有一个原因是创建了全局复用的positionProperty上一次播放的采样点没清掉新旧轨迹混合在一起位置当然乱。解决每次开始前重建属性并固定时间基准。正确习惯是const positionProperty new Cesium.SampledPositionProperty(); // 每次重建不要复用 viewer.clock.currentTime start; // 必须显式指定 viewer.clock.startTime start; viewer.clock.stopTime stop; viewer.clock.shouldAnimate true;顺序别反。先重建属性再设currentTime最后再设shouldAnimate否则前几帧会取到旧数据。另外不要对clock挂着多个onTick监听每加一次监听同一个tick就会执行多次getValue和lookAt性能下降且可能出现两个监听互相覆盖相机的现象。我因为这个查了半天最后发现是初始化函数被调用了两次监听挂了两份。5.3 长距离飞行白屏/卡顿影像级别与瓦片预加载现象轨迹长度超过几十公里时飞行过程中影像模糊飞到哪里哪里才加载落地后整个画面白屏好几秒才出图。原因相机在移动时不断请求新位置的影像瓦片瓦片加载速度跟不上相机移动速度。尤其影像层级越高瓦片数据量越大移动相机相当于让浏览器同时拉几十张图再宽的带宽也受限。Cesium的maximumScreenSpaceError参数控制着“当前画质下多大误差才加载更细层级”值越小画质越高但加载越频繁。解决做“动态画质切换”。我给自己的项目定了一套规则飞行过程中把maximumScreenSpaceError调高让Cesium优先保证加载速度落地后再降回高画质。代码如下const scene viewer.scene; // 飞行开始时 scene.maximumScreenSpaceError 16; // 飞行结束回到默认 scene.maximumScreenSpaceError 2;经验值默认是2飞行中8到16是比较舒服的区间太高画面会明显模糊。另一个技巧是“预加载”正式播放前用低速先跑一遍路径让瓦片进入缓存然后重置时间再以正常速度播放。这种做法看起来有点像玄学但实际效果非常明显因为瓦片缓存是真实的第二次播同一路径会顺滑很多。做细心一点可以把预跑的轨迹分段缓存避免一次性加载太大导致内存占用飙升。5.4 模型姿态反转heading与基准方向不一致现象给轨迹上的车辆或飞机实体设置了orientation模型确实沿路径动了但机头方向指向侧面甚至反向转弯时姿态特别怪。原因模型朝向问题和Cesium的坐标基准有关本质是“模型建模时的初始朝向”和“Cesium世界坐标系中的前进方向”不一致。glTF模型大多数建模工具默认正面朝向是Z轴正方向但Cesium加载后会把模型摆到经纬度坐标里不同编辑器导出的模型局部坐标五花八门这就导致通过heading算出来的理论朝向和实际朝向差了一个固定角度。解决第一步拿到模型后先在0度heading下看一眼它朝哪个方向第二步记住这个基准偏差角第三步把所有轨迹点的heading统一加上这个偏差。代码里用Transforms来生成实体朝向const hpr new Cesium.HeadingPitchRoll( Cesium.Math.toRadians(heading MODEL_HEADING_OFFSET), 0, 0 ); const orientation Cesium.Transforms.headingPitchRollQuaternion(position, hpr); entity.orientation orientation;MODEL_HEADING_OFFSET就是基准偏差角这个值需要针对你的模型实测。不要试图在代码里通过数学推导解决因为每个模型不一样。这个坑踩过一次之后我已经把“先标定模型朝向”写进项目流程里了拿到任何glTF第一件事就是放平地上转几个角度截图存档。另外如果你的模型是倾斜摄影的BIM构件还要检查模型本身是否带旋转矩阵那会在朝向之外再叠一层偏移。5.5 flyTo时相机“甩飞”到地球背面现象调用flyTo后镜头没有飞向目标点反而飞向太空或者地球背面画面一片漆黑。原因这是最让我哭笑不得的一个坑。destination参数接收的是一个Cartesian3世界坐标但很多人在代码里图省事直接传数组viewer.camera.flyTo({ destination: [115.0, 34.0, 2000] // 错误示例 });Cesium拿到数组后按xyz分量去解释但纬度和经度的数值量级在百而世界坐标的分量量级是百万于是相机朝着一个“经纬度数值”对应的世界坐标方向飞了出去看起来就是甩飞到不知名角落。解决统一用Cesium.Cartesian3.fromDegrees包装。我的习惯是在项目里封装一个辅助函数function createCamDest(lon, lat, height) { return Cesium.Cartesian3.fromDegrees(lon, lat, height); }所有flyTo和setView的destination都走这个函数从根上杜绝数组传入。另外执行重要飞行前先用viewer.camera.setView({destination: dest})原地看一眼落点确认经纬和高程没问题再放flyTo。setView没有飞行过程立等可见是验证目标点最实用的调试手段。6. 进阶技巧把多个飞行段串成“导演级”镜头序列漫游需求做到后期一定会遇到“镜头叙事”诉求先全景飞到项目上空再俯冲到目标建筑侧面跟随车辆走一段最后拉高俯瞰结束。这就是多段飞行的编排问题。很多人用嵌套回调写两层还能忍三四层直接变回调地狱。我用async/await封装了一个串行播放函数几百行代码就能撑起一个演示片头async function playRoute(stops) { viewer.clock.shouldAnimate false; for (let i 0; i stops.length; i) { const stop stops[i]; try { await viewer.camera.flyTo({ destination: Cesium.Cartesian3.fromDegrees(stop.lon, stop.lat, stop.height), orientation: { heading: Cesium.Math.toRadians(stop.heading), pitch: Cesium.Math.toRadians(stop.pitch), roll: 0 }, duration: stop.duration }); await sleep(stop.hold || 1000); // 每段结束后停留1秒 } catch (e) { // 用户手动中断飞行时Promise会被reject // 这里吞掉异常让序列继续下一段 } } } function sleep(ms) { return new Promise(resolve setTimeout(resolve, ms)); }stops是一个数组每个元素定义“飞到哪、什么姿态、飞多久、停多久”。所有段串行执行上一段结束才飞下一段。try/catch是必须的因为用户一旦拖动视角当前flyTo会被取消Promise状态变成rejected不接住的话后续所有段都会断掉。飞行落地后的hold停留也很重要我给某智慧园区项目做演示时最开始每段之间零停留镜头连续性很强但客户看完说“有点晕车”。后来给每段结束到下一段开始之间加了1秒左右的观众缓冲观感一下子稳定了。这个缓冲时间本质上是给观众一个“上一段画面消失、下一段画面出现”的认知切换点。这套串行思路还可以扩展出去每段不只用flyTo也可以交替使用flyTo和lookAt跟随比如第一段飞过去第二段锁定目标环绕一圈第三段再拉远。数组结构天然支持这种混排只要每段在函数里按类型分支处理就行。我现在做所有漫游类需求都会先把镜头序列数据抽出来放进配置里脚本和代码分离改镜头参数时完全不用动业务代码。这个习惯帮我应付过好几次“领导临时想看另一种运镜”的场景改完参数刷新就行。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑