javascript"><!DOCTYPE html>
<html><head><title>Threejs中使用A*算法寻路导航,Threejs室内室外地图导航</title><script type="text/javascript" src="libs/three.js"></script><script type="text/javascript" src="libs/OrbitControls.js"></script><script type="text/javascript" charset="UTF-8" src="libs/other/Tween.min.js"></script><script type="text/javascript" charset="UTF-8" src="libs/three/loaders/GLTFLoader.js"></script><script type="text/javascript" src="libs/OBJLoader.js"></script><script type="text/javascript" src="libs/MTLLoader.js"></script><script type="text/javascript" src="libs/util.js"></script><script type="text/javascript" src="libs/util/dat.gui.js"></script><script type="text/javascript" src="libs/astar.js"></script><style>body {margin: 0;overflow: hidden;}</style></head><body><div id="webgl-output"></div><script type="text/javascript">var camera;var renderer;var clock = new THREE.Clock();var mixer = new THREE.AnimationMixer();var clipAction;var animationClip;var length = 36;var ws = 2;var graph = []; // 记录地图var meshArr = new Array(); // 记录障碍物对象数据var meshxy = [{"x": 35,"y": 7},{"x": 35,"y": 7},{"x": 35,"y": 9},{"x": 35,"y": 11},{"x": 29,"y": 11},{"x": 29,"y": 13},{"x": 33,"y": 13},{"x": 33,"y": 11},{"x": 33,"y": 9},{"x": 33,"y": 5},{"x": 33,"y": 7},{"x": 29,"y": 5},{"x": 31,"y": 5},{"x": 31,"y": 11},{"x": 27,"y": 5},{"x": 25,"y": 5},{"x": 25,"y": 7},{"x": 25,"y": 9},{"x": 25,"y": 11},{"x": 25,"y": 13},{"x": 23,"y": 9},{"x": 21,"y": 9},{"x": 19,"y": 9},{"x": 19,"y": 11},{"x": 19,"y": 13},{"x": 15,"y": 9},{"x": 13,"y": 7},{"x": 13,"y": 9},{"x": 13,"y": 11},{"x": 11,"y": 7},{"x": 13,"y": 5},{"x": 11,"y": 5},{"x": 7,"y": 5},{"x": 5,"y": 5},{"x": 5,"y": 7},{"x": 7,"y": 7},{"x": 7,"y": 9},{"x": 7,"y": 11},{"x": 5,"y": 11},{"x": 5,"y": 9},{"x": 7,"y": 15},{"x": 7,"y": 17},{"x": 7,"y": 19},{"x": 11,"y": 23},{"x": 11,"y": 17},{"x": 11,"y": 21},{"x": 11,"y": 19},{"x": 17,"y": 25},{"x": 17,"y": 27},{"x": 17,"y": 29},{"x": 19,"y": 25},{"x": 21,"y": 25},{"x": 17,"y": 21},{"x": 17,"y": 23},{"x": 17,"y": 19},{"x": 15,"y": 19},{"x": 13,"y": 19},{"x": 7,"y": 29},{"x": 7,"y": 31},{"x": 7,"y": 33},{"x": 9,"y": 33},{"x": 13,"y": 33},{"x": 11,"y": 33},{"x": 13,"y": 31},{"x": 13,"y": 29},{"x": 15,"y": 33},{"x": 17,"y": 33},{"x": 19,"y": 33},{"x": 21,"y": 33},{"x": 25,"y": 29},{"x": 25,"y": 31},{"x": 25,"y": 33},{"x": 27,"y": 29},{"x": 29,"y": 29},{"x": 29,"y": 23},{"x": 27,"y": 23},{"x": 25,"y": 23},{"x": 29,"y": 21},{"x": 29,"y": 19},{"x": 33,"y": 19},{"x": 33,"y": 21},{"x": 33,"y": 23},{"x": 33,"y": 25},{"x": 33,"y": 27},{"x": 33,"y": 29},{"x": 33,"y": 31},{"x": 33,"y": 33},{"x": 31,"y": 35},{"x": 29,"y": 35},{"x": 27,"y": 35},{"x": 25,"y": 35},{"x": 7,"y": 23},{"x": 11,"y": 15},{"x": 9,"y": 15},{"x": 35,"y": 9},{"x": 35,"y": 11},{"x": 29,"y": 11},{"x": 29,"y": 13},{"x": 33,"y": 13},{"x": 33,"y": 11},{"x": 33,"y": 9},{"x": 33,"y": 5},{"x": 33,"y": 7},{"x": 29,"y": 5},{"x": 31,"y": 5},{"x": 31,"y": 11},{"x": 27,"y": 5},{"x": 25,"y": 5},{"x": 25,"y": 7},{"x": 25,"y": 9},{"x": 25,"y": 11},{"x": 25,"y": 13},{"x": 23,"y": 9},{"x": 21,"y": 9},{"x": 19,"y": 9},{"x": 19,"y": 11},{"x": 19,"y": 13},{"x": 15,"y": 9},{"x": 13,"y": 7},{"x": 13,"y": 9},{"x": 13,"y": 11},{"x": 11,"y": 7},{"x": 13,"y": 5},{"x": 11,"y": 5},{"x": 7,"y": 5},{"x": 5,"y": 5},{"x": 5,"y": 7},{"x": 7,"y": 7},{"x": 7,"y": 9},{"x": 7,"y": 11},{"x": 5,"y": 11},{"x": 5,"y": 9},{"x": 7,"y": 15},{"x": 7,"y": 17},{"x": 7,"y": 19},{"x": 11,"y": 23},{"x": 11,"y": 17},{"x": 11,"y": 21},{"x": 11,"y": 19},{"x": 17,"y": 25},{"x": 17,"y": 27},{"x": 17,"y": 29},{"x": 19,"y": 25},{"x": 21,"y": 25},{"x": 17,"y": 21},{"x": 17,"y": 23},{"x": 17,"y": 19},{"x": 15,"y": 19},{"x": 13,"y": 19},{"x": 7,"y": 29},{"x": 7,"y": 31},{"x": 7,"y": 33},{"x": 9,"y": 33},{"x": 13,"y": 33},{"x": 11,"y": 33},{"x": 13,"y": 31},{"x": 13,"y": 29},{"x": 15,"y": 33},{"x": 17,"y": 33},{"x": 19,"y": 33},{"x": 21,"y": 33},{"x": 25,"y": 29},{"x": 25,"y": 31},{"x": 25,"y": 33},{"x": 27,"y": 29},{"x": 29,"y": 29},{"x": 29,"y": 23},{"x": 27,"y": 23},{"x": 25,"y": 23},{"x": 29,"y": 21},{"x": 29,"y": 19},{"x": 33,"y": 19},{"x": 33,"y": 21},{"x": 33,"y": 23},{"x": 33,"y": 25},{"x": 33,"y": 27},{"x": 33,"y": 29},{"x": 33,"y": 31},{"x": 33,"y": 33},{"x": 31,"y": 35},{"x": 29,"y": 35},{"x": 27,"y": 35},{"x": 25,"y": 35},{"x": 7,"y": 23},{"x": 11,"y": 15},{"x": 9,"y": 15},{"x": 35,"y": 9},{"x": 35,"y": 11},{"x": 29,"y": 11},{"x": 29,"y": 13},{"x": 33,"y": 13},{"x": 33,"y": 11},{"x": 33,"y": 9},{"x": 33,"y": 5},{"x": 33,"y": 7},{"x": 29,"y": 5},{"x": 31,"y": 5},{"x": 31,"y": 11},{"x": 27,"y": 5},{"x": 25,"y": 5},{"x": 25,"y": 7},{"x": 25,"y": 9},{"x": 25,"y": 11},{"x": 25,"y": 13},{"x": 23,"y": 9},{"x": 21,"y": 9},{"x": 19,"y": 9},{"x": 19,"y": 11},{"x": 19,"y": 13},{"x": 15,"y": 9},{"x": 13,"y": 7},{"x": 13,"y": 9},{"x": 13,"y": 11},{"x": 11,"y": 7},{"x": 13,"y": 5},{"x": 11,"y": 5},{"x": 7,"y": 5},{"x": 5,"y": 5},{"x": 5,"y": 7},{"x": 7,"y": 7},{"x": 7,"y": 9},{"x": 7,"y": 11},{"x": 5,"y": 11},{"x": 5,"y": 9},{"x": 7,"y": 15},{"x": 7,"y": 17},{"x": 7,"y": 19},{"x": 11,"y": 23},{"x": 11,"y": 17},{"x": 11,"y": 21},{"x": 11,"y": 19},{"x": 17,"y": 25},{"x": 17,"y": 27},{"x": 17,"y": 29},{"x": 19,"y": 25},{"x": 21,"y": 25},{"x": 17,"y": 21},{"x": 17,"y": 23},{"x": 17,"y": 19},{"x": 15,"y": 19},{"x": 13,"y": 19},{"x": 7,"y": 29},{"x": 7,"y": 31},{"x": 7,"y": 33},{"x": 9,"y": 33},{"x": 13,"y": 33},{"x": 11,"y": 33},{"x": 13,"y": 31},{"x": 13,"y": 29},{"x": 15,"y": 33},{"x": 17,"y": 33},{"x": 19,"y": 33},{"x": 21,"y": 33},{"x": 25,"y": 29},{"x": 25,"y": 31},{"x": 25,"y": 33},{"x": 27,"y": 29},{"x": 29,"y": 29},{"x": 29,"y": 23},{"x": 27,"y": 23},{"x": 25,"y": 23},{"x": 29,"y": 21},{"x": 29,"y": 19},{"x": 33,"y": 19},{"x": 33,"y": 21},{"x": 33,"y": 23},{"x": 33,"y": 25},{"x": 33,"y": 27},{"x": 33,"y": 29},{"x": 33,"y": 31},{"x": 33,"y": 33},{"x": 31,"y": 35},{"x": 29,"y": 35},{"x": 27,"y": 35},{"x": 25,"y": 35},{"x": 7,"y": 23},{"x": 11,"y": 15},{"x": 9,"y": 15},{"x": 19,"y": 21},{"x": 21,"y": 21},{"x": 21,"y": 19},{"x": 29,"y": 27},{"x": 29,"y": 25}]; // 记录障碍坐标物数据var lineArr = new Array(); // 记录路网数据var meshbool = false; // 显示障碍物var linebool = false; // 显示路网var resultArray = new Array();var isCaculate = false;function init() {// 创建一个场景,它将包含我们所有的元素,如物体,相机和灯光。var scene = new THREE.Scene();var urls = ['assets/textures/cubemap/flowers/posx.jpg','assets/textures/cubemap/flowers/negx.jpg','assets/textures/cubemap/flowers/posy.jpg','assets/textures/cubemap/flowers/negy.jpg','assets/textures/cubemap/flowers/posz.jpg','assets/textures/cubemap/flowers/negz.jpg'];var cubeLoader = new THREE.CubeTextureLoader();scene.background = cubeLoader.load(urls);// 创建一个摄像机,它定义了我们正在看的地方camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 0.1, 1000);// 将摄像机对准场景的中心camera.position.x = 60;camera.position.y = 35;camera.position.z = 60;camera.lookAt({x: 0,y: 5,z: -20});var orbit = new THREE.OrbitControls(camera);orbit.target = camera.position;orbit.update();// 创建一个渲染器并设置大小,WebGLRenderer将会使用电脑显卡来渲染场景// initialize basic rendererrenderer = initRenderer({antialias: true,logarithmicDepthBuffer: true,});// 将平面添加到场景中var plane = createPlaneGeometryBasicMaterial();scene.add(plane);// 在屏幕上显示坐标轴var axes = new THREE.AxesHelper(100);scene.add(axes);scene.add(new THREE.AmbientLight(0x666666));scene.add(new THREE.AmbientLight("#ffffff", 1));initModel();// initPeople();initGround();initGrid();var i = 0;function tweenComplete() {if (i < points.length) {switch (i) {case 0:aaa.rotateY(Math.PI);break;case 1:case 5:case 8:case 9:aaa.rotateY(-0.5 * Math.PI);break;case 2:case 3:case 4:case 6:case 7:aaa.rotateY(0.5 * Math.PI);break;case 10:mixer.stopAllAction();break;}tween = new TWEEN.Tween(points[i]).to(points[i + 1], 3000).easing(TWEEN.Easing.Linear.None).onUpdate(function() {aaa.position.set(this.x, this.y, this.z);}).onComplete(tweenComplete).start();i++;}}// 使用GUI调试库var controls = new function() {this.clickBool = false;this.displayRoadGrid = function() {for (var i = 0; i < lineArr.length; i++) {if (linebool) {// 显示路网scene.add(lineArr[i].obj);} else {// 隐藏路网var obj = scene.getObjectByName(lineArr[i].name);scene.remove(obj);}}linebool = !linebool}this.displayObstacles = function() {for (var i = 0; i < meshArr.length; i++) {if (meshbool) {// 显示障碍物scene.add(meshArr[i].obj);} else {// 隐藏障碍物var obj = scene.getObjectByName(meshArr[i].name);scene.remove(obj);}}meshbool = !meshbool}this.outputObstacles = function() {for (var i = 0; i < meshArr.length; i++) {meshxy.push({x: meshArr[i].x,y: meshArr[i].y})}console.log(meshArr)console.log(lineArr)console.log(meshxy)localStorage.setItem('meshArr', JSON.stringify(meshArr));localStorage.setItem('lineArr', JSON.stringify(lineArr));localStorage.setItem('meshxy', JSON.stringify(meshxy));}}var gui = new dat.GUI();// 隐藏显示障路网gui.add(controls, 'displayRoadGrid');// 隐藏显示障碍物gui.add(controls, 'displayObstacles');// 输出障碍物对象gui.add(controls, 'outputObstacles');gui.add(controls, "clickBool");// 启动动画renderScene();// 添加模型function initModel() {var mtlLoader = new THREE.MTLLoader();mtlLoader.setPath("assets/models/obj_mtl/")mtlLoader.load('city.mtl', function(materials) {materials.preload();var objLoader = new THREE.OBJLoader();objLoader.setMaterials(materials);objLoader.load('assets/models/obj_mtl/city.obj', function(object) {var mesh = object;mesh.scale.set(3, 3, 3);mesh.position.set(18, 0, 18);scene.add(mesh);});});}// 添加人物模型function initPeople() {var loader = new THREE.GLTFLoader();loader.load('assets/models/CesiumMan/CesiumMan.gltf', function(result) {result.scene.scale.set(1, 1, 1);result.scene.translateY(0);aaa = result.scene;scene.add(result.scene);tweenComplete();mixer = new THREE.AnimationMixer(result.scene);animationClip = result.animations[0];clipAction = mixer.clipAction(animationClip).play();animationClip = clipAction.getClip();});}// 创建一个地面并设置草坪材质function createPlaneGeometryBasicMaterial() {var textureLoader = new THREE.TextureLoader();var cubeMaterial = new THREE.MeshStandardMaterial({map: textureLoader.load("assets/textures/stone/cd.jpg"),});cubeMaterial.map.wrapS = THREE.RepeatWrapping;cubeMaterial.map.wrapT = THREE.RepeatWrapping;cubeMaterial.map.repeat.set(18, 18)// 创建地平面并设置大小var planeGeometry = new THREE.PlaneGeometry(500, 500);var plane = new THREE.Mesh(planeGeometry, cubeMaterial);// 设置平面位置并旋转plane.rotation.x = -0.5 * Math.PI;plane.position.x = 0;plane.position.z = 0;return plane;}// 绘制线路function initLine(pArr) {var points = [];var geometry = new THREE.Geometry();for (var i = 0; i < pArr.length; i++) {var randomX = pArr[i].x;var randomY = pArr[i].y;var randomZ = pArr[i].z;var vector = new THREE.Vector3(randomX, randomY, randomZ);geometry.vertices.push(vector);points.push(vector);}var material = new THREE.LineBasicMaterial({color: 0x0000FF});var line = new THREE.Line(geometry, material);scene.add(line);return points;}// 绘制路网function initGround() {var geometry = new THREE.Geometry();geometry.vertices.push(new THREE.Vector3(0, 0, 0));geometry.vertices.push(new THREE.Vector3(length, 0, 0));for (var i = 0; i <= length / ws; i++) {var material = new THREE.LineBasicMaterial({color: 0x808080});var line = new THREE.Line(geometry, material);line.position.z = i * ws;line.name = "line_" + i;lineArr.push({name: "line_" + i,obj: line});scene.add(line);var line = new THREE.Line(geometry, material);line.position.x = i * ws;line.position.z = length;line.rotation.y = 90 * Math.PI / 180;line.name = "line_" + i + 180;lineArr.push({name: "line_" + i + 180,obj: line});scene.add(line);}}// 初始化障碍物随机function initGrid() {for (var i = 0; i < length / ws; i++) {var nodeRow = [];for (var j = 0; j < length / ws; j++) {nodeRow.push(1);}graph.push(nodeRow);}if (meshxy.length > 0) {for (var i = 0; i < meshxy.length; i++) {initObstacles(meshxy[i].x, meshxy[i].y);}} else {for (var i = 0; i < length / ws; i++) {var nodeRow = [];for (var j = 0; j < length / ws; j++) {var salt = Math.random() * 7;if (salt > 2) {nodeRow.push(1);} else {nodeRow.push(0);}if (salt <= 2) {var cube = new THREE.Mesh(new THREE.CubeGeometry(1, 1, 1), new THREE.MeshBasicMaterial({color: 0xC0C0C0}));let x = ws * j + ws / 2;let z = ws * i + ws / 2;cube.position.set(x, 1.2, z);scene.add(cube);}}graph.push(nodeRow);}}}//计算路径function caculatePath(resultArray) {var maps = new Graph(graph); // 地图var startX = parseInt(resultArray[0].position.z / ws);var startY = parseInt(resultArray[0].position.x / ws);var endX = parseInt(resultArray[1].position.z / ws);var endY = parseInt(resultArray[1].position.x / ws);var start = maps.grid[startX][startY];var end = maps.grid[endX][endY];result = astar.search(maps, start, end);if (result.length == 0) {alert("无可到达路径");cleanSphere();return;}var nArr = [{x: resultArray[0].position.x,z: resultArray[0].position.z,y: 1.2}];for (var i = 0; i < result.length; i++) {let d = {x: result[i].y * ws + ws / 2,y: 1.2,z: result[i].x * ws + ws / 2,}nArr.push(d);}initLine(nArr);}//清除小球function cleanSphere() {let child = scene.children; //获取场景中的所有子对象for (var i = 0; i < child.length; i++) {if (child[i].geometry instanceof THREE.SphereGeometry) { //几何对象是球体几何scene.remove(child[i]); //从场景中移除i--;}}isCaculate = false;}//初始球体function initSphere(x, z) {if (isCaculate) {cleanSphere();}var geometry = new THREE.SphereGeometry(ws / 2, 30, 30); //球体几何var material = new THREE.MeshBasicMaterial({color: 0xffff00}); //网格基础材料if (resultArray.length == 0) {var sphere = new THREE.Mesh(geometry, material);sphere.position.x = x;sphere.position.y = 1;sphere.position.z = z;resultArray.push(sphere);scene.add(sphere);} else if (resultArray[0].position.x != x || resultArray[0].position.z != z) {var sphere = new THREE.Mesh(geometry, material);sphere.position.x = x;sphere.position.y = 1;sphere.position.z = z;resultArray.push(sphere);scene.add(sphere);caculatePath(resultArray);isCaculate = true;resultArray = new Array();}}// 绘制障碍物function initObstacles(x, z) {var name = "mesh_" + x + z;var obj = scene.getObjectByName(name);if (obj) {scene.remove(obj);for (var i = 0; i < meshArr.length; i++) {if (meshArr[i].name == name) {meshArr.splice(i, 1);}}graph[parseInt(z / ws)][parseInt(x / ws)] = 1;} else {var geometry = new THREE.BoxGeometry(ws, ws, ws); //球体几何var material = new THREE.MeshBasicMaterial({color: 0xff0000});var sphere = new THREE.Mesh(geometry, material);sphere.position.x = x;sphere.position.y = 1;sphere.position.z = z;sphere.name = name;scene.add(sphere);meshArr.push({name: name,obj: sphere,x: x,y: z,});graph[parseInt(z / ws)][parseInt(x / ws)] = 0;}}// 拾取对象function pickupObjects(event) {// 点击屏幕创建一个向量var raycaster = new THREE.Raycaster();var vector = new THREE.Vector2((event.clientX / window.innerWidth) * 2 - 1, -(event.clientY / window.innerHeight) * 2 + 1);var fxl = new THREE.Vector3(0, 1, 0);var groundplane = new THREE.Plane(fxl, 0);raycaster.setFromCamera(vector, camera);var ray = raycaster.ray;let intersects = ray.intersectPlane(groundplane);let x = intersects.x;let z = intersects.z;if (x < 0 || z < 0 || length < z || length < x) {return;}var k, m;for (var i = 0; i < length; i += ws) {if (x >= i && x < i + ws) {k = i + ws / 2;}}for (var j = 0; j < length; j += ws) {if (z >= j && z < j + ws) {m = j + ws / 2;}}if (controls.clickBool) {initSphere(k, m); //初始化球体} else {initObstacles(k, m);}}document.addEventListener('click', pickupObjects, false); //监听单击拾取对象初始化球体// 动画渲染var step = 5;function renderScene() {TWEEN.update();var delta = clock.getDelta();orbit.update();mixer.update(delta);// 使用requestAnimationFrame函数进行渲染requestAnimationFrame(renderScene);renderer.render(scene, camera);}// 渲染的场景renderer.render(scene, camera);// 创建一个球形几何体function createSphereGeometryLambertMaterial(point) {// 创建一个球体var sphereGeometry = new THREE.SphereGeometry(0.2, 20, 20);var sphereMaterial = new THREE.MeshBasicMaterial({color: 0x7777ff,wireframe: true});var sphereMaterial = new THREE.MeshLambertMaterial({color: 0xff0000});var sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);// 设置该物体投射阴影sphere.castShadow = true;// 位置范围sphere.position.x = point.x;sphere.position.y = point.y;sphere.position.z = point.z;return sphere;}}window.onload = init;function onResize() {camera.aspect = window.innerWidth / window.innerHeight;camera.updateProjectionMatrix();renderer.setSize(window.innerWidth, window.innerHeight);}// 监听调整大小事件window.addEventListener('resize', onResize, false);</script></body>
</html>