开源|cesium自主漫游实战教程(附完整源码)
在 Cesium 三维项目开发中,有时候客户并不希望将漫游的路径规划的太死,而是想要自己使用键盘的wasd或者上下左右控制物体在三维世界中漫游,那么这个功能应该如何实现呢?今天就带大家实现一下。
在 Cesium 三维项目开发中,有时候客户并不希望将漫游的路径规划的太死,而是想要自己使用键盘的wasd或者上下左右控制物体在三维世界中漫游,那么这个功能应该如何实现呢?今天就带大家实现一下。

实现思路
Step 1:创建可动态控制的小车实体
首先我们可以想到这是一个会动的实体,所以肯定需要使用之前所学的callbackProperty属性来实现
CallbackProperty 是一种用于动态计算属性值的对象。它允许用户提供一个回调函数,该回调函数会在需要计算属性值时被调用,并返回相应的属性值。这使得属性值可以根据特定的条件或动态变化的数据进行实时计算。
所以模型的位置属性和方向属性都应该被设置为callbackProperty,并且通过我们的交互事件,不断的更新
我们可以初始化模型,并构造初始的位置和朝向变量
这里初始的位置绑定在this.position上,初始的方向写在this.hpRoll上
// 传入初始位置,笛卡尔坐标,添加小车addModel(position) {// 每次点击左右键模型旋转的角度this.radian = Cesium.Math.toRadians(1.0);this.speedVector = new Cesium.Cartesian3();this.position = position;// 小车朝向this.hpRoll = new Cesium.HeadingPitchRoll();this.hpRoll.heading =this.viewer.scene.camera.heading + Cesium.Math.toRadians(-90);this.carEntity = this.viewer.entities.add({ id: "car", position: new Cesium.CallbackProperty(this.getPositin.bind(this), false),// 根据所提供的速度计算点 orientation: new Cesium.CallbackProperty(this.getOrientation.bind(this),false ), model: { uri: "/src/assets/gltf/redCar.glb", scale: 0.04, }, });return this.carEntity;}
getPositin() { return this.position; }
getOrientation() {
Step2:两个关键变量的计算
如何更新这两个关键变量
关于汽车的方向:
直接控制this.hpRoll的朝向角即可,这个过程中可以通过参数调节朝向角的变化速度,这样我们可以得到准确的hpRoll的值
this.roamEvent = () => {this.traceHandler();if (this.flag.moveLeft) {this.hpRoll.heading -= this.radian; }if (this.flag.moveRight) {this.hpRoll.heading += this.radian; }};this.viewer.clock.onTick.addEventListener(this.roamEvent);
其中flag是一个标识,用于确定当前小车的状态,当触发对应按键的时候,会改变flag的属性,然后调整小车的方向
flag的结构如下
this.flag = { moveUp: false, moveDown: false, moveLeft: false, moveRight: false,};
然后通过监听键盘事件修改flag的数据
// 上下左右 wasd控制车辆移动setFlagStatus(key, value) { switch (key.keyCode) { case 37:// 左this.flag.moveLeft = value;
break; case 38:// 上this.flag.moveUp = value;
break; case 39:// 右this.flag.moveRight = value;
break; case 40:this.flag.moveDown = value;
// 下break; case 65:this.flag.moveLeft = value;
// 左break; case 68:this.flag.moveRight = value;
// 右break; case 83:this.flag.moveDown = value;
// 下break; case 87:this.flag.moveUp = value;
// 下break; }}
keyDonwCallback(e) {this.setFlagStatus(e, true);}
关于汽车的位置计算:
我们可以使用之前学过的本地坐标的计算方法,首先计算前一帧小车的位置,然后根据这个坐标得到模型矩阵,然后再通过小车的朝向与速度,计算下一帧的位置

在监听事件中添加前进和后退的逻辑
// 开始自主漫游startRoam() {if (this.carEntity) { document.addEventListener("keydown", this.keyDonwCallback.bind(this)); document.addEventListener("keyup", this.keyUpCallback.bind(this));this.roamEvent = () => {this.traceHandler();
if (this.flag.moveLeft) {this.hpRoll.heading -= this.radian; }if (this.flag.moveRight) {this.hpRoll.heading += this.radian; }
if (this.flag.moveUp) {this.moveCar(1); }if (this.flag.moveDown) {this.moveCar(-1); } };this.viewer.clock.onTick.addEventListener(this.roamEvent); }}
其中moveCar函数,处理小车的位置逻辑
具体逻辑如下:
-
我们将当前小车的位置clone一份,用来计算下一帧小车会出现的位置
-
通过当前小车的速度,如果isUp为true,说明按下的是前进键,这时候我们会得到一个X轴方向的向量,这个向量的模为速度*时间,同理,如果是后退,我们将朝着-X轴构造一个向量
-
根据当前的hpRoll小车朝向,以及当前的世界坐标clonePosition,通过headingPitchRollToFixedFrame构造一个方向和hpRoll一致的模型矩阵
-
通过模型矩阵左乘我们刚刚构造出来的向量,得到下一帧小车的位置position
-
然后把position丢给sampleHeight处理一下真实的地形高度,避免小车跑到地下去
-
还可以对比上一帧和下一帧的位置地形高,做一个碰撞检测
moveCar(isUp) {const clonePosition = _.clone(this.position);const {height:prevHeight}=this.setHeight(clonePosition)
// 位移的距离const distance = this._speed / 20;let speedVectorX = new Cesium.Cartesian3();
// 计算速度矩阵x轴方向if (isUp > 0) { speedVectorX = Cesium.Cartesian3.multiplyByScalar(Cesium.Cartesian3.UNIT_X, distance, speedVectorX ); } else if (isUp < 0) { speedVectorX = Cesium.Cartesian3.multiplyByScalar(Cesium.Cartesian3.UNIT_X, -distance, speedVectorX ); } else { speedVectorX = Cesium.Cartesian3.multiplyByScalar(Cesium.Cartesian3.UNIT_X,0, speedVectorX ); }
let fixedFrameTransforms =Cesium.Transforms.localFrameToFixedFrameGenerator("east", "north");let modelMatrix = Cesium.Transforms.headingPitchRollToFixedFrame( clonePosition,this.hpRoll,Cesium.Ellipsoid.WGS84, fixedFrameTransforms );let position = Cesium.Matrix4.multiplyByPoint( modelMatrix, speedVectorX,new Cesium.Cartesian3() );
const { lng: lng1, lat: lat1, height: real } = this.setHeight(position);const heightDiff=real-prevHeight// 碰撞检测if(heightDiff>1){return }this.position = Cesium.Cartesian3.fromDegrees(lng1, lat1, real);}
完整代码:
最后我们可以将自主漫游封装为一个class,方便调用
由于文章篇幅有限
需要完整代码的同学

PS:本文为新中地原创,转载请标注来源。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐



所有评论(0)