|
@@ -426,13 +426,13 @@ export default {
|
|
|
*/
|
|
*/
|
|
|
const calculateMoveDirection = (Cesium, viewer) => {
|
|
const calculateMoveDirection = (Cesium, viewer) => {
|
|
|
const keys = keyState.value
|
|
const keys = keyState.value
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
if (!keys.forward && !keys.backward && !keys.left && !keys.right) {
|
|
if (!keys.forward && !keys.backward && !keys.left && !keys.right) {
|
|
|
return null
|
|
return null
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
const heading = viewer.camera.heading
|
|
const heading = viewer.camera.heading
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
// 前方向(相机朝向)
|
|
// 前方向(相机朝向)
|
|
|
// heading=0(北): forward=(0,1,0)
|
|
// heading=0(北): forward=(0,1,0)
|
|
|
// heading=π/2(东): forward=(1,0,0)
|
|
// heading=π/2(东): forward=(1,0,0)
|
|
@@ -442,7 +442,7 @@ export default {
|
|
|
0
|
|
0
|
|
|
)
|
|
)
|
|
|
Cesium.Cartesian3.normalize(forward, forward)
|
|
Cesium.Cartesian3.normalize(forward, forward)
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
// 右方向(垂直于前方向,指向右侧)
|
|
// 右方向(垂直于前方向,指向右侧)
|
|
|
// heading=0(北): right=(1,0,0) 东
|
|
// heading=0(北): right=(1,0,0) 东
|
|
|
// heading=π/2(东): right=(0,-1,0) 南
|
|
// heading=π/2(东): right=(0,-1,0) 南
|
|
@@ -452,10 +452,10 @@ export default {
|
|
|
0
|
|
0
|
|
|
)
|
|
)
|
|
|
Cesium.Cartesian3.normalize(right, right)
|
|
Cesium.Cartesian3.normalize(right, right)
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
// 计算移动方向
|
|
// 计算移动方向
|
|
|
const moveDirection = new Cesium.Cartesian3(0, 0, 0)
|
|
const moveDirection = new Cesium.Cartesian3(0, 0, 0)
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
if (keys.forward) {
|
|
if (keys.forward) {
|
|
|
Cesium.Cartesian3.add(moveDirection, forward, moveDirection)
|
|
Cesium.Cartesian3.add(moveDirection, forward, moveDirection)
|
|
|
}
|
|
}
|
|
@@ -468,14 +468,75 @@ export default {
|
|
|
if (keys.left) {
|
|
if (keys.left) {
|
|
|
Cesium.Cartesian3.subtract(moveDirection, right, moveDirection)
|
|
Cesium.Cartesian3.subtract(moveDirection, right, moveDirection)
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
if (Cesium.Cartesian3.magnitude(moveDirection) > 0) {
|
|
if (Cesium.Cartesian3.magnitude(moveDirection) > 0) {
|
|
|
Cesium.Cartesian3.normalize(moveDirection, moveDirection)
|
|
Cesium.Cartesian3.normalize(moveDirection, moveDirection)
|
|
|
return moveDirection
|
|
return moveDirection
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
return null
|
|
return null
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 碰撞检测 - 使用射线检测
|
|
|
|
|
+ * 检测从当前位置到目标位置是否有障碍物
|
|
|
|
|
+ */
|
|
|
|
|
+ const checkCollision = (Cesium, viewer, currentPos, targetPos) => {
|
|
|
|
|
+ if (!props.enableCollision) return false
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ // 计算方向和距离
|
|
|
|
|
+ const direction = Cesium.Cartesian3.subtract(targetPos, currentPos, new Cesium.Cartesian3())
|
|
|
|
|
+ const distance = Cesium.Cartesian3.magnitude(direction)
|
|
|
|
|
+ Cesium.Cartesian3.normalize(direction, direction)
|
|
|
|
|
+
|
|
|
|
|
+ // 创建射线
|
|
|
|
|
+ const ray = new Cesium.Ray(currentPos, direction)
|
|
|
|
|
+
|
|
|
|
|
+ // 使用 scene.pickFromRay 检测与场景中物体的碰撞
|
|
|
|
|
+ const pickResult = viewer.scene.pickFromRay(ray, [])
|
|
|
|
|
+
|
|
|
|
|
+ if (pickResult && pickResult.position) {
|
|
|
|
|
+ // 计算到碰撞点的距离
|
|
|
|
|
+ const collisionDistance = Cesium.Cartesian3.distance(currentPos, pickResult.position)
|
|
|
|
|
+
|
|
|
|
|
+ // 如果碰撞距离小于移动距离,则发生碰撞
|
|
|
|
|
+ if (collisionDistance < distance + props.collisionDistance) {
|
|
|
|
|
+ return true
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return false
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ // 某些情况下 pickFromRay 可能失败,忽略错误继续移动
|
|
|
|
|
+ console.warn('碰撞检测失败:', e)
|
|
|
|
|
+ return false
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 检测目标点是否在地形/模型内部
|
|
|
|
|
+ */
|
|
|
|
|
+ const checkPointCollision = (Cesium, viewer, position) => {
|
|
|
|
|
+ if (!props.enableCollision) return false
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ // 使用 scene.pick 检测该点是否有模型
|
|
|
|
|
+ const windowPosition = new Cesium.Cartesian2()
|
|
|
|
|
+ viewer.scene.cartesianToCanvasCoordinates(position, windowPosition)
|
|
|
|
|
+
|
|
|
|
|
+ const pickResult = viewer.scene.pick(windowPosition)
|
|
|
|
|
+
|
|
|
|
|
+ // 如果检测到了物体,检查距离
|
|
|
|
|
+ if (pickResult && pickResult.primitive) {
|
|
|
|
|
+ return true
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return false
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ return false
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* 更新相机位置
|
|
* 更新相机位置
|
|
@@ -484,43 +545,51 @@ export default {
|
|
|
const updateCameraPosition = (Cesium, viewer, deltaTime) => {
|
|
const updateCameraPosition = (Cesium, viewer, deltaTime) => {
|
|
|
const moveDirection = calculateMoveDirection(Cesium, viewer)
|
|
const moveDirection = calculateMoveDirection(Cesium, viewer)
|
|
|
if (!moveDirection) return
|
|
if (!moveDirection) return
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
const moveDistance = actualMoveSpeed.value * deltaTime
|
|
const moveDistance = actualMoveSpeed.value * deltaTime
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
// 获取当前位置
|
|
// 获取当前位置
|
|
|
- const currentPos = viewer.camera.position
|
|
|
|
|
|
|
+ const currentPos = viewer.camera.position.clone()
|
|
|
const currentCartographic = Cesium.Cartographic.fromCartesian(currentPos)
|
|
const currentCartographic = Cesium.Cartographic.fromCartesian(currentPos)
|
|
|
const lon = Cesium.Math.toDegrees(currentCartographic.longitude)
|
|
const lon = Cesium.Math.toDegrees(currentCartographic.longitude)
|
|
|
const lat = Cesium.Math.toDegrees(currentCartographic.latitude)
|
|
const lat = Cesium.Math.toDegrees(currentCartographic.latitude)
|
|
|
const height = currentCartographic.height
|
|
const height = currentCartographic.height
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
// 将移动方向转换为经纬度增量
|
|
// 将移动方向转换为经纬度增量
|
|
|
// moveDirection 是单位向量,需要转换为米/度
|
|
// moveDirection 是单位向量,需要转换为米/度
|
|
|
// 1度经度 ≈ 111km * cos(lat),1度纬度 ≈ 111km
|
|
// 1度经度 ≈ 111km * cos(lat),1度纬度 ≈ 111km
|
|
|
const metersPerDegreeLat = 111000 // 纬度每度约111km
|
|
const metersPerDegreeLat = 111000 // 纬度每度约111km
|
|
|
const metersPerDegreeLon = 111000 * Math.cos(Cesium.Math.toRadians(lat)) // 经度每度随纬度变化
|
|
const metersPerDegreeLon = 111000 * Math.cos(Cesium.Math.toRadians(lat)) // 经度每度随纬度变化
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
// moveDirection.x 对应经度变化(东/西)
|
|
// moveDirection.x 对应经度变化(东/西)
|
|
|
// moveDirection.y 对应纬度变化(北/南)
|
|
// moveDirection.y 对应纬度变化(北/南)
|
|
|
const deltaLon = (moveDirection.x * moveDistance) / metersPerDegreeLon
|
|
const deltaLon = (moveDirection.x * moveDistance) / metersPerDegreeLon
|
|
|
const deltaLat = (moveDirection.y * moveDistance) / metersPerDegreeLat
|
|
const deltaLat = (moveDirection.y * moveDistance) / metersPerDegreeLat
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
// 计算新位置
|
|
// 计算新位置
|
|
|
const newLon = lon + deltaLon
|
|
const newLon = lon + deltaLon
|
|
|
const newLat = lat + deltaLat
|
|
const newLat = lat + deltaLat
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
|
|
+ // 计算目标位置(世界坐标)
|
|
|
|
|
+ const targetPosition = Cesium.Cartesian3.fromDegrees(newLon, newLat, height)
|
|
|
|
|
+
|
|
|
|
|
+ // 碰撞检测
|
|
|
|
|
+ if (checkCollision(Cesium, viewer, currentPos, targetPosition)) {
|
|
|
|
|
+ // 发生碰撞,不移动
|
|
|
|
|
+ emit('collision', { position: currentPos })
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// 更新相机位置
|
|
// 更新相机位置
|
|
|
- const finalPosition = Cesium.Cartesian3.fromDegrees(newLon, newLat, height)
|
|
|
|
|
-
|
|
|
|
|
viewer.camera.setView({
|
|
viewer.camera.setView({
|
|
|
- destination: finalPosition,
|
|
|
|
|
|
|
+ destination: targetPosition,
|
|
|
orientation: {
|
|
orientation: {
|
|
|
heading: viewer.camera.heading,
|
|
heading: viewer.camera.heading,
|
|
|
pitch: viewer.camera.pitch,
|
|
pitch: viewer.camera.pitch,
|
|
|
roll: 0
|
|
roll: 0
|
|
|
}
|
|
}
|
|
|
})
|
|
})
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
// 发射位置变化事件
|
|
// 发射位置变化事件
|
|
|
emit('position-change', {
|
|
emit('position-change', {
|
|
|
lon: newLon,
|
|
lon: newLon,
|