接上文;本文介绍一下关于汽车位置的计算

 关于汽车的位置计算

我们可以使用之前学过的本地坐标的计算方法,首先计算前一帧小车的位置,然后根据这个坐标得到模型矩阵,然后再通过小车的朝向与速度,计算下一帧的位置

image.png

在监听事件中添加前进和后退的逻辑

//   开始自主漫游
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);
	}
}

代码功能解读

该代码段实现了一个名为startRoam()的方法,用于启动一个基于键盘交互的自主漫游功能。核心功能包括键盘事件监听和根据按键状态更新物体运动。


代码结构分析

键盘事件监听
通过document.addEventListener绑定keydownkeyup事件,分别调用keyDonwCallbackkeyUpCallback方法(注意拼写错误应为keyDownCallback)。这两个方法用于设置移动方向标志位(如this.flag.moveLeft)。

漫游逻辑执行
this.roamEvent是一个箭头函数,会在每次时钟 tick 时触发:

  1. 调用traceHandler()处理轨迹逻辑(具体实现未展示)。
  2. 根据方向标志位更新物体的旋转角度或位移:
    • 左/右键调整this.hpRoll.heading角度(this.radian为旋转弧度值)。
    • 上/下键调用moveCar()方法控制前后移动(参数1-1表示方向)。

时钟事件绑定
通过this.viewer.clock.onTick.addEventListener将漫游逻辑绑定到时钟的 tick 事件,实现逐帧更新。


关键变量说明

  • this.carEntity:被控制的物体对象(如车辆模型)。
  • this.flag:存储按键状态的标志位对象(需外部初始化)。
  • this.hpRoll.heading:物体的水平旋转角度。
  • this.radian:单次旋转的弧度增量。
  • this.moveCar():控制物体移动的方法(参数为方向系数)。

典型应用场景

适用于 3D 场景中的交互式物体控制(如游戏角色、模拟驾驶),需配合以下外部实现:

  • 方向标志位(this.flag)的初始化与管理。
  • moveCar()方法的具体移动逻辑。
  • 视图对象(this.viewer)的时钟系统支持。

其中moveCar函数,处理小车的位置逻辑

具体逻辑如下:

  1. 我们将当前小车的位置clone一份,用来计算下一帧小车会出现的位置

  2. 通过当前小车的速度,如果isUp为true,说明按下的是前进键,这时候我们会得到一个X轴方向的向量,这个向量的模为速度*时间,同理,如果是后退,我们将朝着-X轴构造一个向量

  3. 根据当前的hpRoll小车朝向,以及当前的世界坐标clonePosition,通过headingPitchRollToFixedFrame构造一个方向和hpRoll一致的模型矩阵

  4. 通过模型矩阵左乘我们刚刚构造出来的向量,得到下一帧小车的位置position

  5. 然后把position丢给sampleHeight处理一下真实的地形高度,避免小车跑到地下去

  6. 还可以对比上一帧和下一帧的位置地形高,做一个碰撞检测

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);
}

代码功能概述

这段代码属于三维场景(基于Cesium.js)中控制车辆移动的逻辑,主要实现以下功能:

  • 根据输入参数 isUp 决定车辆的移动方向(前进/后退/停止)
  • 计算位移距离并生成速度向量
  • 通过局部坐标系转换处理车辆姿态(考虑航向、俯仰、横滚角)
  • 执行碰撞检测(高度差限制)
  • 更新车辆位置

核心逻辑分解

变量初始化
const clonePosition = _.clone(this.position);  
const {height:prevHeight} = this.setHeight(clonePosition);  
  • 深拷贝当前车辆位置对象,避免直接修改原始数据
  • setHeight 方法提取当前位置的海拔高度并保存为 prevHeight
位移计算
const distance = this._speed / 20;  
let speedVectorX = new Cesium.Cartesian3();  
  • 根据速度 this._speed 计算帧位移量(假设 20 为帧率调节系数)
  • 初始化空的三维向量 speedVectorX 用于存储速度方向
方向控制
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);  
}  
  • isUp > 0:车辆前进,沿X轴正方向移动
  • isUp < 0:车辆后退,沿X轴负方向移动
  • 未处理 isUp = 0 的情况(默认速度向量为0)
坐标系转换
let fixedFrameTransforms = Cesium.Transforms.localFrameToFixedFrameGenerator("east", "north");  
let modelMatrix = Cesium.Transforms.headingPitchRollToFixedFrame(  
    clonePosition,  
    this.hpRoll,  
    Cesium.Ellipsoid.WGS84,  
    fixedFrameTransforms  
);  
  • 生成东北天(ENU)局部坐标系到固定坐标系的转换矩阵
  • 根据车辆的航向角(heading)、俯仰角(pitch)、横滚角(roll)构建模型矩阵
位移应用
let position = Cesium.Matrix4.multiplyByPoint(modelMatrix, speedVectorX, new Cesium.Cartesian3());  
  • 将速度向量从局部坐标系转换到全局坐标系
  • 计算结果存储在 position
碰撞检测
const { lng: lng1, lat: lat1, height: real } = this.setHeight(position);  
const heightDiff = real - prevHeight;  
if(heightDiff > 1) return;  
  • 计算移动后的新位置海拔高度
  • 若高度差超过阈值 1(单位:米),判定为碰撞并终止移动
位置更新
this.position = Cesium.Cartesian3.fromDegrees(lng1, lat1, real);  
  • 将经纬度和高度转换为 Cartesian3 坐标并更新车辆位置

关键依赖说明

  1. Cesium.js:提供三维地理空间计算能力(坐标系转换、向量运算等)
  2. Lodash (_.clone):用于深拷贝对象
  3. 坐标系约定
    • 局部坐标系:X轴为东,Y轴为北,Z轴为天(ENU)
    • 全局坐标系:WGS84椭球体下的笛卡尔坐标

改进建议

  1. 参数校验:检查 isUp 是否为数字类型
  2. 停止状态显式处理else 分支可添加日志或状态标记
  3. 高度差阈值可配置化:将 1 改为变量提升灵活性
  4. 错误处理:捕获 setHeight 或矩阵运算可能出现的异常

完整代码:

最后我们可以将自主漫游封装为一个class,方便调用

由于文章篇幅有限

需要完整代码的同学+小编无偿分享

图片

PS:本文为新中地原创,转载请标注来源。

本文学习前提需要具备一定的GIS开发能力,若你还不熟悉 Cesium 基础,建议学习《Cesium 零基础入门教程》,掌握坐标转换、图元操作等知识,助力理解标绘原理!

图片

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐