代码拉取完成,页面将自动刷新
<!DOCTYPE html>
<html lang="en">
<head>
<title>A*寻路算法(threeJS)使用多view绘图实现小地图功能</title>
<meta charset="utf-8">
<script type="text/javascript" src="libs/statistics.js"></script>
<script type="text/javascript" src="libs/steak.js"></script>
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
</head>
<body>
<script type="text/javascript" src="libs/three.js"></script>
<script type="text/javascript" src="libs/Detector.js"></script>
<script type="text/javascript" src="libs/OrbitControls.js"></script>
<script type="text/javascript" src="libs/astar.js"></script>
<div id="ThreeJS" style="position: absolute; left:0px; top:0px"></div>
<script>
//容器,场景,相机,渲染器,控制器
var container, scene, camera, renderer, controls;
//屏幕宽度,屏幕高度
var SCREEN_WIDTH = window.innerWidth,
SCREEN_HEIGHT = window.innerHeight;
// 视角,窗口宽高比,近裁截面,远裁截面. aspect表示渲染窗口的长宽比,如果一个网页上只有一个全屏的canvas画布且画布上只有一个窗口,那么aspect的值就是网页窗口客户区的宽高比
//near属性表示的是从距离相机多远的位置开始渲染,一般情况会设置一个很小的值。0.1
//far属性表示的是距离相机多远的位置截止渲染,如果设置的值偏小,会有部分场景看不到 1000
var VIEW_ANGLE = 45,
ASPECT = SCREEN_WIDTH / SCREEN_HEIGHT,
NEAR = 0.1,
FAR = 20000;
var windowWidth, windowHeight;
var length = 200,
flex = 2;
var graph = [];
var views = [{
left: 0,
top: 0,
width: 1,
height: 1,
//background: new THREE.Color( 0.5, 0.5, 0.7 ),
eye: [0, 200, 250],
up: [0, 1, 0],
fov: 45,
updateCamera: function(camera, scene) {
// camera.position.x += mouseX * 0.05;
// camera.position.x = Math.max( Math.min( camera.position.x, 2000 ), -2000 );
camera.lookAt(scene.position); //设置相机方向(指向的场景对象)
}
},
{
left: 0.75,
top: 0.7,
width: 0.3,
height: 0.3,
background: new THREE.Color(0.7, 0.5, 0.5),
eye: [0, 200, 0],
up: [0, 1, 0],
fov: 60,
updateCamera: function(camera, scene) {
// camera.position.x -= mouseX * 0.05;
// camera.position.x = Math.max( Math.min( camera.position.x, 2000 ), -2000 );
camera.lookAt(camera.position.clone().setY(0));
}
},
];
threeStart();
//启动
function threeStart() {
initThree();
initScene();
initCamera();
initLight();
initGround();
initGrid();
initControl();
renderer.clear();
animate();
}
//初始化Three
function initThree() {
if (Detector.webgl)
renderer = new THREE.WebGLRenderer({
antialias: true
});
else
renderer = new THREE.CanvasRenderer();
renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);
container = document.getElementById('ThreeJS');
container.appendChild(renderer.domElement);
}
//初始化相机
function initCamera() {
//fov表示视场,所谓视场就是能够看到的角度范围,人的眼睛大约能够看到180度的视场,视角大小设置要根据具体应用,一般游戏会设置60~90度
var view = views[0];
camera = new THREE.PerspectiveCamera(view.fov, ASPECT, NEAR, FAR); //透视投影相机
camera.position.set(0, 200, 250); //设置相机位置
camera.lookAt(scene.position);
view.camera = camera;
var view2 = views[1];
camera = new THREE.PerspectiveCamera(view2.fov, ASPECT, NEAR, FAR);
camera.position.fromArray(view2.eye);
camera.up.fromArray(view2.up);
view2.camera = camera;
}
//初始化场景
function initScene() {
scene = new THREE.Scene();
}
//初始化光
function initLight() {
var ambientLight = new THREE.AmbientLight(0x111111);
scene.add(ambientLight);
var ambientLight = new THREE.AmbientLight(0x111111);
scene.add(ambientLight);
}
//播放动画请去掉注释
var isMoved;
var isMoved2;
var i;
var geometry;
var line, line2, rota;
function groundAnimate() {
if (i > length / 10) {
return;
}
if (!isMoved) {
scene.add(line);
isMoved = true;
}
if (line.position.z > (i * 10) - length / 2) {
line.position.z -= 2;
} else if (line.position.z < (i * 10) - length / 2) {
line.position.z += 2;
} else {
i++;
isMoved = false;
isMoved2 = false;
line = new THREE.Line(geometry, new THREE.LineBasicMaterial({
color: 0x808080,
opacity: 1
}));
//var line2=new THREE.Line( geometry, new THREE.LineBasicMaterial( { color: 0x808080, opacity: 1 } ) );
if (!isMoved2) {
scene.add(line2);
isMoved2 = true;
}
if (line2.position.x < (i * 10) - length / 2) {
line2.position.x += 1;
} else if (line2.position.x > (i * 10) - length / 2) {
line2.position.x -= 1;
} else {
if (line2.rotation.y < 90 * Math.PI / 180) {
line2.rotation.y += 10 * Math.PI / 180;
} else if (line2.rotation.y > 90 * Math.PI / 180) {
line2.rotation.y -= 10 * Math.PI / 180;
} else {
i++;
isMoved = false;
isMoved2 = false;
line = new THREE.Line(geometry, new THREE.LineBasicMaterial({
color: 0x808080,
opacity: 1
}));
line2 = new THREE.Line(geometry, new THREE.LineBasicMaterial({
color: 0x808080,
opacity: 1
}));
}
}
}
requestAnimationFrame(groundAnimate);
}
function initGround() {
//播放动画请去掉注释
// lines
//isMoved=false;
//isMoved2=false;
//i=0;
//line = new THREE.Line( geometry, new THREE.LineBasicMaterial( { color: 0x808080, opacity: 1 } ) );
//line2 = new THREE.Line( geometry, new THREE.LineBasicMaterial( { color: 0x808080, opacity: 1 } ) )
//groundAnimate()
geometry = new THREE.Geometry();
geometry.vertices.push(new THREE.Vector3(-length / 2, 0, 0));
geometry.vertices.push(new THREE.Vector3(length / 2, 0, 0));
//播放动画请注释一下内容
for (var i = 0; i <= length / 10; i++) {
var line = new THREE.Line(geometry, new THREE.LineBasicMaterial({
color: 0x808080,
opacity: 1
}));
line.position.z = (i * 10) - length / 2;
scene.add(line);
var line = new THREE.Line(geometry, new THREE.LineBasicMaterial({
color: 0x808080,
opacity: 1
}));
line.position.x = (i * 10) - length / 2;
line.rotation.y = 90 * Math.PI / 180;
scene.add(line);
}
//播放动画请注释以上内容
var geometry = new THREE.PlaneGeometry(length, 10);
var material = new THREE.MeshBasicMaterial({
color: 0x808080,
side: THREE.DoubleSide
});
var plane = new THREE.Mesh(geometry, material);
plane.position.set(0, 5, length / 2);
scene.add(plane);
var plane = new THREE.Mesh(geometry, material);
plane.rotation.y = 90 * Math.PI / 180;
plane.position.set(length / 2, 5, 0);
scene.add(plane);
var plane = new THREE.Mesh(geometry, material);
plane.position.set(0, 5, -length / 2);
scene.add(plane);
var plane = new THREE.Mesh(geometry, material);
plane.rotation.y = 90 * Math.PI / 180;
plane.position.set(-length / 2, 5, 0);
scene.add(plane);
var skyBoxGeometry = new THREE.CubeGeometry(10000, 10000, 10000);
var skyBoxMaterial = new THREE.MeshBasicMaterial({
color: 0xFFFFFF,
side: THREE.BackSide
});
var skyBox = new THREE.Mesh(skyBoxGeometry, skyBoxMaterial);
scene.add(skyBox);
}
//初始化控制
function initControl() {
controls = new THREE.OrbitControls(views[0].camera, renderer.domElement); //轨道控制
}
//初始化网格
function initGrid() {
var geometry = new THREE.CubeGeometry(8, 8, 8); //立方体几何
var material = new THREE.MeshBasicMaterial({
color: 0xC0C0C0
});
for (var i = 0; i < length / 10; i++) {
var nodeRow = [];
graph.push(nodeRow);
for (var j = 0; j < length / 10; j++) {
var salt = randomNum(1, 7);
if (salt > flex) {
nodeRow.push(1);
} else {
nodeRow.push(0);
}
if (salt <= flex) {
var cube = new THREE.Mesh(geometry, material);
cube.position.set(10 * j - length / 2 + 5, 5, 10 * i - length / 2 + 5);
scene.add(cube);
}
}
}
// console.log(graph)
}
var resultArray = new Array();
//拾取对象
function pickupObjects(e) {
var raycaster = new THREE.Raycaster();
let mouse = new THREE.Vector2();
var fxl = new THREE.Vector3(0, 1, 0);
var groundplane = new THREE.Plane(fxl, 0);
mouse.x = (e.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(e.clientY / window.innerHeight) * 2 + 1;
//从相机发射一条射线,经过鼠标点击位置
raycaster.setFromCamera(mouse, views[0].camera);
//计算射线相机到的对象,可能有多个对象,因此返回的是一个数组,按离相机远近排列
var ray = raycaster.ray;
let intersects = ray.intersectPlane(groundplane);
let x = intersects.x;
let z = intersects.z;
if (Math.abs(x) > length / 2 || Math.abs(z) > (length / 2)) {
return;
}
var k, m;
for (var i = -length / 2; i < length / 2; i += 10) {
if (x >= i && x < i + 10) {
k = i + 5;
}
}
for (var j = -length / 2; j < length / 2; j += 10) {
if (z >= j && z < j + 10) {
m = j + 5;
}
}
initSphere(k, m); //初始化球体
}
document.addEventListener('click', pickupObjects, false); //监听单击拾取对象初始化球体
var isCaculate = false;
//清除小球
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(5, 32, 32); //球体几何
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 = 5;
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 = 5;
sphere.position.z = z;
resultArray.push(sphere);
scene.add(sphere);
caculatePath(resultArray);
isCaculate = true;
// console.log("起始点坐标为 x:"+resultArray[0].position.x+",z:"+resultArray[0].position.z);
// console.log("终止点坐标为 x:"+resultArray[1].position.x+",z:"+resultArray[1].position.z);
resultArray = new Array();
}
}
var result;
//计算路径
function caculatePath(resultArray) {
// console.log(resultArray);
var maps = new Graph(graph); //地图
var startX = (resultArray[0].position.z - 5 + length / 2) / 10;
var startY = (resultArray[0].position.x - 5 + length / 2) / 10;
var endX = (resultArray[1].position.z - 5 + length / 2) / 10;
var endY = (resultArray[1].position.x - 5 + length / 2) / 10;
//console.log(startX+' '+startY+' '+endX+' '+endY);
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 timesRun = 0;
timesRun = 0;
tempSphere = resultArray[0];
pathAnimate();
// var interval = setInterval(function(){
// var geometry = new THREE.SphereGeometry( 5, 32, 32 );
// var material = new THREE.MeshBasicMaterial( {color: 0xffff00} );
// var sphere = new THREE.Mesh( geometry, material );
// sphere.position.x=result[timesRun].y*10-length/2+5;
// sphere.position.y=5;
// sphere.position.z=result[timesRun].x*10-length/2+5;
// scene.add( sphere );
// timesRun += 1;
// if(timesRun == result.length){
// clearInterval(interval);
// }
// }, 200);
}
var timesRun = 0;
var tempSphere;
var isAdded = false;
//路径动画
function pathAnimate() {
if (!isAdded) {
scene.add(tempSphere);
isAdded = true;
}
if (timesRun == result.length) {
return;
}
var next = {
x: result[timesRun].y * 10 - length / 2 + 5,
z: result[timesRun].x * 10 - length / 2 + 5
}
if (tempSphere.position.x == next.x && tempSphere.position.z == next.z) {
timesRun++;
requestAnimationFrame(pathAnimate);
return;
}
if (tempSphere.position.x > next.x) tempSphere.position.x -= 1;
if (tempSphere.position.x < next.x) tempSphere.position.x += 1;
if (tempSphere.position.z > next.z) tempSphere.position.z -= 1;
if (tempSphere.position.z < next.z) tempSphere.position.z += 1;
requestAnimationFrame(pathAnimate);
}
function randomNum(minNum, maxNum) {
switch (arguments.length) {
case 1:
return parseInt(Math.random() * minNum + 1, 10);
break;
case 2:
return parseInt(Math.random() * (maxNum - minNum + 1) + minNum, 10);
break;
default:
return 0;
break;
}
}
function updateSize() {
if (windowWidth != window.innerWidth || windowHeight != window.innerHeight) {
windowWidth = window.innerWidth;
windowHeight = window.innerHeight;
renderer.setSize(windowWidth, windowHeight);
}
}
function animate() {
render();
controls.update();
requestAnimationFrame(animate);
}
//渲染
function render() {
updateSize();
for (var ii = 0; ii < views.length; ++ii) {
var view = views[ii];
var camera = view.camera;
view.updateCamera(camera, scene);
var left = Math.floor(windowWidth * view.left);
var top = Math.floor(windowHeight * view.top);
var width = Math.floor(windowWidth * view.width);
var height = Math.floor(windowHeight * view.height);
renderer.setViewport(left, top, width, height);
renderer.setScissor(left, top, width, height);
renderer.setScissorTest(true);
renderer.setClearColor(view.background);
camera.aspect = width / height;
camera.updateProjectionMatrix(); //更新投影矩阵
renderer.render(scene, camera); //更新渲染器
}
}
</script>
</body>
</html>
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。