坦克打无人机模拟(three-tile 地形)

本文分享一个用 three-tile 渲染真实地形的 3D 演示:坦克在山地中巡逻并实时锁定最近的无人机, 显示坦克行驶轨迹炮弹弹道预测(抛物线虚线 + 发射命中特效)。坦克、无人机用简单几何体示意。

表面是做一个坦克打无人机的可视化,实际上......你往下看就懂了。

先看效果

先说你打开页面会看到啥: 一座真实的山地,坦克趴在坡上沿着椭圆路线慢悠悠巡逻,身后拖着一条金色轨迹,记着它走过的每一个坑洼;天上几架无人机在绕圈飞;坦克每隔几秒就锁定最近的那个,炮口甩出一道青色虚线------这是弹道预测------紧接着一枚黄色炮弹顺着弧线飞出去,命中的瞬间炸开一团橙色闪光(后期可以加上,之前那个爆炸特效)。

当然了,实际上得先等地图加载出来才能看到。

需要说明的是,坦克和无人机都是几何体凑的,纯示意,别当军事模拟。真正想跟你聊的,是地形那块。

既然都要做可视化了,地形要是还用个平面糊弄,那也太没意思了。所以地形我直接拉真实地形。

地形:本来最头大,结果几行就搞定

说真的,做"真实地形"这四个字,从前最劝退我。你要自己搞高程数据、瓦片切分、LOD 分层、墨卡托反算坐标、法线重算......一套下来热情就没了。

后来我摸到了 three-tile ------一个基于 Three.js 的 3D 瓦片地形引擎。它的思路特别克制:你给它一个影像源 和一个高程源,剩下的瓦片加载、LOD、坐标转换它全包了。

先看最关键的几行:

js 复制代码
import * as plugin from "three-tile/plugin";

// 影像源 + 高程(DEM)源,这里用 ArcGIS
const imgSource = new plugin.ArcGisSource({
  url: "https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/",
  // 瓦片地址模板
});
const demSource = new plugin.ArcGisDemSource({
  url: "https://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer/tile/",
});

// 给个中心经度,开个最小层级,地图就出来了
const map = plugin.TileMap.create({
  imgSource,
  demSource,
  lon0: 116.39,   // 场景中心经度(北京附近随便挑的)
  minLevel: 8,
});
map.rotateX(-Math.PI / 2); // 转到 xz 平面,y 朝上,跟 Three.js 习惯一致
scene.add(map);

// 主循环里每帧一句,LOD / 瓦片调度全自动
map.update(camera);

就这些。你没看错,地形起伏、瓦片按需加载、远处降级,库全管了。你只管往 sceneadd,然后安心去摆你的坦克。

坐标互转是它最让我舒服的地方。做贴地、做轨迹、做锁定目标,全靠这俩:

js 复制代码
const world = map.geo2world(geo); // 经纬度 -> 世界坐标
const geo   = map.world2geo(world); // 世界坐标 -> 经纬度

贴地取高度也只要一句话:

js 复制代码
const geo = map.world2geo(worldPos);
const info = map.getLocalInfoFromGeo(geo);
const groundY = info.location.y; // 脚下地形高度,直接拿来贴地

我本来以为贴地得自己采样高程图、插值,结果库一句话就吐出来了。这种"本来以为要写一堆,结果没有"的感觉,后面还会反复出现。

坦克与行驶轨迹

地形有了,那就顺手做个坦克示意。我们的坦克 = 身子(盒子)+ 炮管(圆柱)+ 履带(两排小方块糊弄一下)。示意嘛,不丢人。

重点是让它趴在山坡上巡逻 ,而不是浮在半空。做法其实很朴素:定义一条中心附近的椭圆路径,每帧把路径点从经纬度变成世界坐标,再用 getLocalInfoFromGeo 取地面高度贴上------坦克就老老实实跟着地势起伏了。

js 复制代码
// 巡逻路径:以场景中心为基准的椭圆
const lon = lon0 + rx * Math.cos(t);
const lat = lat0 + ry * Math.sin(t);
const world = map.geo2world(new THREE.Vector3(lon, lat, 0));
const info  = map.getLocalInfoFromGeo(map.world2geo(world));

tank.group.position.set(world.x, info.location.y + tank.alt, world.z);
// 朝向也顺手用前后两点算一下,让炮管朝着前进方向

行驶轨迹更简单,每帧把当前位置喂给一条累加的 Line 就行:

js 复制代码
trailPoints.push(world.x, info.location.y + 0.5, world.z);
trail.geometry.setFromPoints(trailPoints); // 金色线条就出来了

就这么个思路。至于坦克怎么"真的"开起来、履带怎么转(之前有写过,但是在这个大世界尺度上,根本看不清履带),示意版我直接省略了------今天主要是想说地形和贴地,那部分不重要。

无人机

无人机就是个盒子加四个旋翼(细圆柱),飞在「贴地高度 + 巡航高度」上面。多来几架,各自绕不同的圈,供坦克挑最近的锁。代码没啥好讲的,几何体和 position 的事。

炮弹弹道预测

这块才是今天的小高潮(其实也不难)。坦克锁定最近那架无人机,算出炮口到目标的水平距离,按抛物线反推一个初速度 v0,保证顶点高度随距离抬高------这样远的目标弹道高、近的目标弹道平,看着就合理。

js 复制代码
// 已知水平距离 d、重力 g,取一个顶点高度 h,反解 v0
const v0 = Math.sqrt(g * (h + Math.sqrt(h * h + (d * d) / 4)));
// 沿抛物线采样一堆点 -> 青色虚线,就是"弹道预测"

到点就发射一枚黄色炮弹,沿这条弧线飞,命中播放橙色闪光。虚线预测 + 实弹飞行,视觉上就有那味儿了。当然了,就按这么计算的话,那肯定是射不中的,需要提前预判无人机的轨迹才有可能射中。除非是更高级的导弹,发射出去之后还能控制一下方向的。

思路就是这么个思路。

为什么是 three-tile,而不是 Mapbox / Cesium

做这个之前我也纠结过。地形方案翻来翻去就那几个,挨个想过一遍:

  • Mapbox:确实轻,还自带 custom three.js layer,能把 three 场景叠在它上面。但资料真不多,社区也小,真要改点什么、踩个坑,搜半天搜不到答案。而且它的「层」是阉割版的三维能力,想自由放飞其实没那么灵活。
  • Cesium:强大是真强大,地球级地形、时间轴、一堆影像格式都现成。但它太重了,而且它本身是一套独立的渲染管线,跟 three 揉一块儿总有点别扭。要么用双canvas简单同步相机,要么共享 WebGL 上下文 + 自定义 Primitive 那套桥接,心智负担不小。

我就一个朴素诉求:我想用 three,纯纯的 three,地形只是我场景里的一层。 three-tile 正好长在这条需求上------它本身就是 Three.js 的一个扩展,地形就是一堆普通 Mesh,你爱往场景里加啥加啥;坐标互转、贴地取高度都是现成 API,没有第二套渲染体系要你去适配。

所以如果你也用 three、又想搞地形,真心可以考虑 three-tile。

收尾,以及......顺便安利一下

坦克打无人机本身没啥技术含量:几何体拼一拼、轨迹记一记、抛物线解一解,都不难。真正让我省了大劲的,是地形------本来最怕的真实地形那堆瓦片、高程、LOD,three-tile 几行就拉起来了,坐标互转还特别顺手,贴地取高度一句话的事。上面也说了,比起 Mapbox 的资料少、Cesium 的笨重,它刚好卡在「用 three 又想要真实地形」这个舒服的位子上。

所以如果你也在 Three.js 里折腾真实地形、做数字孪生、做地理相关的可视化,真心建议去瞅瞅 three-tile 。它不是那种只给个 demo 就跑路的玩具:瓦片调度、DEM 高程、多数据源(ArcGIS / 天地图 / Mapbox / Bing ......换源基本就是换个 Source 类)都给你管好了,API 也克制,不会一上来就给你一百个配置项劝退。

链接我放评论区了,感兴趣的自取。用熟了你会发现,以前那些"地形好难"的心理负担,其实只是没找对轮子。

希望这篇能给在 Three.js 里折腾地形、折腾可视化的你,一点参考和一点偷懒的灵感。

相关推荐
钛态3 小时前
Vite 中的 CSS 工程化:从 CSS Modules 到 UnoCSS 的渐进式迁移
前端·vue·react·web
Csvn5 小时前
原型链、this 与闭包内存:三座深水区一次打通(含内存泄漏排查实战)
前端·javascript
葡萄城技术团队5 小时前
如何通过 JSON 数据源 / Web API 快速构建轻量级报表(Report)?
前端·json·原型模式
浪兎兎5 小时前
【Java Web】Servlet + JSP 笔记
java·前端·servlet
Patrick_Wilson6 小时前
iOS Safari Clipboard API 权限弹窗问题
前端·ios·safari
OpenTiny社区7 小时前
Naive UI × GenUI SDK:自定义物料库搭建实战
前端·ai编程
kyriewen7 小时前
我拿 4 个真实前端任务试了 GLM-5.3 Flash:代码一遍跑通,账单 4 分钱
前端·程序员·ai编程
IT_陈寒7 小时前
Vue的响应式更新有时候真的不听话
前端·人工智能·后端
风骏时光牛马7 小时前
AI编程常见问题梳理与要点解析
前端
前端snow7 小时前
ai agent -- LCEL 汇总
前端