threejs(20)-AI寻路避障自动导航

一、使用yuka实现路径跟随

yuka官方文档:https://mugen87.github.io/yuka/

在这里插入图片描述

<script setup>
import * as THREE from "three";
import * as YUKA from "yuka";
// 导入控制器
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader.js";

// 创建场景
const scene = new THREE.Scene();
// 创建相机
const camera = new THREE.PerspectiveCamera(
  75,
  window.innerWidth / window.innerHeight,
  0.1,
  1000
);
camera.position.set(0, 10, 20);
camera.lookAt(0, 0, 0);

// 创建渲染器
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.shadowMap.enabled = true;
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

// 创建地面
const planeGeometry = new THREE.PlaneGeometry(50, 50);
const planeMaterial = new THREE.MeshStandardMaterial({ color: 0x999999 });
const plane = new THREE.Mesh(planeGeometry, planeMaterial);
plane.receiveShadow = true;
plane.rotation.x = -Math.PI / 2;
plane.position.y = -0.5;
scene.add(plane);

// 创建灯光
const light = new THREE.SpotLight(0xffffff, 3, 100, Math.PI / 6, 0.5);
light.position.set(10, 40, 10);
light.castShadow = true;
scene.add(light);

// 创建控制器
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;

const time = new YUKA.Time();
requestAnimationFrame(function animate() {
  const delta = time.update().getDelta();
  controls.update();
  entityManager.update(delta);
  renderer.render(scene, camera);
  requestAnimationFrame(animate);
});

// 创建椎体
// const coneGeometry = new THREE.ConeGeometry(0.2, 1, 32);
// coneGeometry.rotateX(Math.PI / 2);
// const coneMaterial = new THREE.MeshStandardMaterial({ color: 0xff0000 });
// const cone = new THREE.Mesh(coneGeometry, coneMaterial);
// // cone.matrixAutoUpdate = false;
// cone.receiveShadow = true;
// cone.castShadow = true;
// scene.add(cone);

// 加载模型
const loader = new GLTFLoader();
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath("./draco/");
loader.setDRACOLoader(dracoLoader);
loader.load("./model/car.gltf", function (gltf) {
  console.log(gltf);
  const car = gltf.scene;
  car.children[0].rotation.y = Math.PI / 2;
  car.children[0].scale.set(0.2, 0.2, 0.2);
  scene.add(car);
  vehicle.setRenderComponent(car, callback);
});

// 创建yuka的车辆
const vehicle = new YUKA.Vehicle();
vehicle.maxSpeed = 5;
// 设置车辆的渲染对象
// vehicle.setRenderComponent(cone, callback);
function callback(entity, renderComponent) {
  // console.log(entity, renderComponent);
  renderComponent.position.copy(entity.position);
  renderComponent.quaternion.copy(entity.rotation);
  // renderComponent.matrix.copy(entity.worldMatrix);
}

// 创建yuka的路径
const path = new YUKA.Path();
path.add(new YUKA.Vector3(0, 0, 0));
path.add(new YUKA.Vector3(0, 0, 10));
path.add(new YUKA.Vector3(10, 0, 10));
path.add(new YUKA.Vector3(10, 0, 0));
path.add(new YUKA.Vector3(0, 0, 0));
// 设置路径的循环模式
path.loop = true;

// 将路径当前的位置设置为车辆的位置
vehicle.position.copy(path.current());

// 跟随路径的行为
const followPathBehavior = new YUKA.FollowPathBehavior(path);
vehicle.steering.add(followPathBehavior);

// 保持在路径中行为
const onPathBehavior = new YUKA.OnPathBehavior(path, 0.1);
onPathBehavior.weight = 10;
vehicle.steering.add(onPathBehavior);

// 创建对实体管理对象
const entityManager = new YUKA.EntityManager();
entityManager.add(vehicle);

showPathLine(path);
function showPathLine(path) {
  console.log(path);
  const positions = [];
  for (let i = 0; i < path._waypoints.length; i++) {
    positions.push(
      path._waypoints[i].x,
      path._waypoints[i].y,
      path._waypoints[i].z
    );
  }
  const geometry = new THREE.BufferGeometry();
  geometry.setAttribute(
    "position",
    new THREE.Float32BufferAttribute(positions, 3)
  );
  const material = new THREE.LineBasicMaterial({ color: 0x0000ff });
  const line = new THREE.Line(geometry, material);
  scene.add(line);
}
</script>

<template>
  <div></div>
</template>

<style>
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

canvas {
  width: 100vw;
  height: 100vh;
  position: fixed;
  left: 0;
  top: 0;
}
</style>

二、搜索目标前进与抵达 并避开障碍

在这里插入图片描述

<script setup>
import * as THREE from "three";
import * as YUKA from "yuka";
// 导入控制器
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader.js";

// 创建场景
const scene = new THREE.Scene();
// 创建相机
const camera = new THREE.PerspectiveCamera(
  75,
  window.innerWidth / window.innerHeight,
  0.1,
  1000
);
camera.position.set(0, 10, 20);
camera.lookAt(0, 0, 0);

// 创建渲染器
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.shadowMap.enabled = true;
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

// 创建地面
const planeGeometry = new THREE.PlaneGeometry(50, 50);
const planeMaterial = new THREE.MeshStandardMaterial({ color: 0x999999 });
const plane = new THREE.Mesh(planeGeometry, planeMaterial);
plane.receiveShadow = true;
plane.rotation.x = -Math.PI / 2;
plane.position.y = -0.5;
scene.add(plane);

// 创建灯光
const light = new THREE.SpotLight(0xffffff, 3, 100, Math.PI / 6, 0.5);
light.position.set(10, 40, 10);
light.castShadow = true;
scene.add(light);

// 创建控制器
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;

const time = new YUKA.Time();
requestAnimationFrame(function animate() {
  const delta = time.update().getDelta();
  controls.update();
  entityManager.update(delta);
  renderer.render(scene, camera);
  requestAnimationFrame(animate);
});

// 创建椎体
// const coneGeometry = new THREE.ConeGeometry(0.2, 1, 32);
// coneGeometry.rotateX(Math.PI / 2);
// const coneMaterial = new THREE.MeshStandardMaterial({ color: 0xff0000 });
// const cone = new THREE.Mesh(coneGeometry, coneMaterial);
// // cone.matrixAutoUpdate = false;
// cone.receiveShadow = true;
// cone.castShadow = true;
// scene.add(cone);

// 加载模型
const loader = new GLTFLoader();
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath("./draco/");
loader.setDRACOLoader(dracoLoader);
loader.load("./model/car.gltf", function (gltf) {
  console.log(gltf);
  const car = gltf.scene;
  car.children[0].rotation.y = Math.PI / 2;
  car.children[0].scale.set(0.2, 0.2, 0.2);
  scene.add(car);
  vehicle.setRenderComponent(car, callback);
});

// 创建yuka的车辆
const vehicle = new YUKA.Vehicle();
vehicle.maxSpeed = 5;
// 设置车辆的渲染对象
// vehicle.setRenderComponent(cone, callback);
function callback(entity, renderComponent) {
  // console.log(entity, renderComponent);
  renderComponent.position.copy(entity.position);
  renderComponent.quaternion.copy(entity.rotation);
  // renderComponent.matrix.copy(entity.worldMatrix);
}

// 创建目标小球
const sphereGeometry = new THREE.SphereGeometry(0.1, 32, 32);
const sphereMaterial = new THREE.MeshStandardMaterial({ color: 0xff00ff });
const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
sphere.receiveShadow = true;
sphere.castShadow = true;
scene.add(sphere);
// 创建目标
const target = new YUKA.GameEntity();
target.setRenderComponent(sphere, callback);

target.position.set(Math.random() * 20 - 10, 0, Math.random() * 20 - 10);

const entityManager = new YUKA.EntityManager();
entityManager.add(vehicle);
entityManager.add(target);

// 到达行为
const arriveBehavior = new YUKA.ArriveBehavior(target.position);
vehicle.steering.add(arriveBehavior);

// 搜索目标行为
// const seekBehavior = new YUKA.SeekBehavior(target.position);
// vehicle.steering.add(seekBehavior);

// setInterval(() => {
//   target.position.set(Math.random() * 20 - 10, 0, Math.random() * 20 - 10);
// }, 2000);

function showPathLine(path) {
  console.log(path);
  const positions = [];
  for (let i = 0; i < path._waypoints.length; i++) {
    positions.push(
      path._waypoints[i].x,
      path._waypoints[i].y,
      path._waypoints[i].z
    );
  }
  const geometry = new THREE.BufferGeometry();
  geometry.setAttribute(
    "position",
    new THREE.Float32BufferAttribute(positions, 3)
  );
  const material = new THREE.LineBasicMaterial({ color: 0x0000ff });
  const line = new THREE.Line(geometry, material);
  scene.add(line);
}

// 点击将目标移动到点击的位置
const ndc = new THREE.Vector2();
const raycaster = new THREE.Raycaster();
window.addEventListener("pointerdown", (event) => {
  ndc.x = (event.clientX / window.innerWidth) * 2 - 1;
  ndc.y = -(event.clientY / window.innerHeight) * 2 + 1;
  raycaster.setFromCamera(ndc, camera);
  const intersects = raycaster.intersectObject(plane);
  if (intersects.length > 0) {
    const point = intersects[0].point;
    target.position.set(point.x, 0, point.z);
  }
});

// 创建障碍物
const obstacles = [];

for (let i = 0; i < 5; i++) {
  const boxGeometry = new THREE.BoxGeometry(3, 3, 3);
  const boxMaterial = new THREE.MeshStandardMaterial({ color: 0x00ff00 });
  const box = new THREE.Mesh(boxGeometry, boxMaterial);
  box.position.set(Math.random() * 30 - 15, 0, Math.random() * 30 - 5);
  box.receiveShadow = true;
  box.castShadow = true;
  scene.add(box);
  // 创建障碍物
  const obstacle = new YUKA.GameEntity();
  obstacle.position.copy(box.position);

  // 设置障碍物半径
  boxGeometry.computeBoundingSphere();
  obstacle.boundingRadius = boxGeometry.boundingSphere.radius;
  obstacles.push(obstacle);

  entityManager.add(obstacle);
}

// 避障行为
const obstacleAvoidanceBehavior = new YUKA.ObstacleAvoidanceBehavior(obstacles);
vehicle.steering.add(obstacleAvoidanceBehavior);
vehicle.smoother = new YUKA.Smoother(30);
</script>

<template>
  <div></div>
</template>

<style>
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

canvas {
  width: 100vw;
  height: 100vh;
  position: fixed;
  left: 0;
  top: 0;
}
</style>

三、逃避行为

在这里插入图片描述

<script setup>
import * as THREE from "three";
import * as YUKA from "yuka";
// 导入控制器
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader.js";

// 创建场景
const scene = new THREE.Scene();
// 创建相机
const camera = new THREE.PerspectiveCamera(
  75,
  window.innerWidth / window.innerHeight,
  0.1,
  1000
);
camera.position.set(0, 10, 20);
camera.lookAt(0, 0, 0);

// 创建渲染器
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.shadowMap.enabled = true;
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

// 创建地面
const planeGeometry = new THREE.PlaneGeometry(50, 50);
const planeMaterial = new THREE.MeshStandardMaterial({ color: 0x999999 });
const plane = new THREE.Mesh(planeGeometry, planeMaterial);
plane.receiveShadow = true;
plane.rotation.x = -Math.PI / 2;
plane.position.y = -0.5;
scene.add(plane);

// 创建灯光
const light = new THREE.SpotLight(0xffffff, 3, 100, Math.PI / 6, 0.5);
light.position.set(10, 40, 10);
light.castShadow = true;
scene.add(light);

// 创建控制器
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;

const time = new YUKA.Time();
requestAnimationFrame(function animate() {
  const delta = time.update().getDelta();
  controls.update();
  entityManager.update(delta);
  renderer.render(scene, camera);
  requestAnimationFrame(animate);
});

// 创建椎体
// const coneGeometry = new THREE.ConeGeometry(0.2, 1, 32);
// coneGeometry.rotateX(Math.PI / 2);
// const coneMaterial = new THREE.MeshStandardMaterial({ color: 0xff0000 });
// const cone = new THREE.Mesh(coneGeometry, coneMaterial);
// // cone.matrixAutoUpdate = false;
// cone.receiveShadow = true;
// cone.castShadow = true;
// scene.add(cone);

// 加载模型
const loader = new GLTFLoader();
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath("./draco/");
loader.setDRACOLoader(dracoLoader);
loader.load("./model/car.gltf", function (gltf) {
  console.log(gltf);
  const car = gltf.scene;
  car.children[0].rotation.y = Math.PI / 2;
  car.children[0].scale.set(0.2, 0.2, 0.2);
  scene.add(car);
  vehicle.setRenderComponent(car, callback);
});

// 创建yuka的车辆
const vehicle = new YUKA.Vehicle();
vehicle.maxSpeed = 5;
// 设置车辆的渲染对象
// vehicle.setRenderComponent(cone, callback);
function callback(entity, renderComponent) {
  // console.log(entity, renderComponent);
  renderComponent.position.copy(entity.position);
  renderComponent.quaternion.copy(entity.rotation);
  // renderComponent.matrix.copy(entity.worldMatrix);
}

// 创建目标小球
const sphereGeometry = new THREE.SphereGeometry(0.1, 32, 32);
const sphereMaterial = new THREE.MeshStandardMaterial({ color: 0xff00ff });
const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
sphere.receiveShadow = true;
sphere.castShadow = true;
scene.add(sphere);
// 创建目标
const target = new YUKA.GameEntity();
target.setRenderComponent(sphere, callback);

target.position.set(Math.random() * 20 - 10, 0, Math.random() * 20 - 10);

const entityManager = new YUKA.EntityManager();
entityManager.add(vehicle);
entityManager.add(target);

// 到达行为
// const arriveBehavior = new YUKA.ArriveBehavior(target.position);
// vehicle.steering.add(arriveBehavior);

// 搜索目标行为
// const seekBehavior = new YUKA.SeekBehavior(target.position);
// vehicle.steering.add(seekBehavior);

// setInterval(() => {
//   target.position.set(Math.random() * 20 - 10, 0, Math.random() * 20 - 10);
// }, 2000);

function showPathLine(path) {
  console.log(path);
  const positions = [];
  for (let i = 0; i < path._waypoints.length; i++) {
    positions.push(
      path._waypoints[i].x,
      path._waypoints[i].y,
      path._waypoints[i].z
    );
  }
  const geometry = new THREE.BufferGeometry();
  geometry.setAttribute(
    "position",
    new THREE.Float32BufferAttribute(positions, 3)
  );
  const material = new THREE.LineBasicMaterial({ color: 0x0000ff });
  const line = new THREE.Line(geometry, material);
  scene.add(line);
}

// 点击将目标移动到点击的位置
const ndc = new THREE.Vector2();
const raycaster = new THREE.Raycaster();
window.addEventListener("pointerdown", (event) => {
  ndc.x = (event.clientX / window.innerWidth) * 2 - 1;
  ndc.y = -(event.clientY / window.innerHeight) * 2 + 1;
  raycaster.setFromCamera(ndc, camera);
  const intersects = raycaster.intersectObject(plane);
  if (intersects.length > 0) {
    const point = intersects[0].point;
    target.position.set(point.x, 0, point.z);
  }
});

// 创建障碍物
const obstacles = [];

for (let i = 0; i < 5; i++) {
  const boxGeometry = new THREE.BoxGeometry(3, 3, 3);
  const boxMaterial = new THREE.MeshStandardMaterial({ color: 0x00ff00 });
  const box = new THREE.Mesh(boxGeometry, boxMaterial);
  box.position.set(Math.random() * 30 - 15, 0, Math.random() * 30 - 5);
  box.receiveShadow = true;
  box.castShadow = true;
  scene.add(box);
  // 创建障碍物
  const obstacle = new YUKA.GameEntity();
  obstacle.position.copy(box.position);

  // 设置障碍物半径
  boxGeometry.computeBoundingSphere();
  obstacle.boundingRadius = boxGeometry.boundingSphere.radius;
  obstacles.push(obstacle);

  entityManager.add(obstacle);
}

// 避障行为
const obstacleAvoidanceBehavior = new YUKA.ObstacleAvoidanceBehavior(obstacles);
vehicle.steering.add(obstacleAvoidanceBehavior);
vehicle.smoother = new YUKA.Smoother(30);

// 逃离行为
const fleeBehavior = new YUKA.FleeBehavior(target.position, 3);
vehicle.steering.add(fleeBehavior);
</script>

<template>
  <div></div>
</template>

<style>
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

canvas {
  width: 100vw;
  height: 100vh;
  position: fixed;
  left: 0;
  top: 0;
}
</style>

四、追击行为

在这里插入图片描述

<script setup>
import * as THREE from "three";
import * as YUKA from "yuka";
// 导入控制器
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader.js";

// 创建场景
const scene = new THREE.Scene();
// 创建相机
const camera = new THREE.PerspectiveCamera(
  75,
  window.innerWidth / window.innerHeight,
  0.1,
  1000
);
camera.position.set(0, 10, 20);
camera.lookAt(0, 0, 0);

// 创建渲染器
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.shadowMap.enabled = true;
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

// 创建地面
const planeGeometry = new THREE.PlaneGeometry(50, 50);
const planeMaterial = new THREE.MeshStandardMaterial({ color: 0x999999 });
const plane = new THREE.Mesh(planeGeometry, planeMaterial);
plane.receiveShadow = true;
plane.rotation.x = -Math.PI / 2;
plane.position.y = -0.5;
scene.add(plane);

// 创建灯光
const light = new THREE.SpotLight(0xffffff, 3, 100, Math.PI / 6, 0.5);
light.position.set(10, 40, 10);
light.castShadow = true;
scene.add(light);

// 创建控制器
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;

const time = new YUKA.Time();
requestAnimationFrame(function animate() {
  const delta = time.update().getDelta();
  controls.update();
  entityManager.update(delta);
  renderer.render(scene, camera);
  requestAnimationFrame(animate);
});

// 创建椎体
// const coneGeometry = new THREE.ConeGeometry(0.2, 1, 32);
// coneGeometry.rotateX(Math.PI / 2);
// const coneMaterial = new THREE.MeshStandardMaterial({ color: 0xff0000 });
// const cone = new THREE.Mesh(coneGeometry, coneMaterial);
// // cone.matrixAutoUpdate = false;
// cone.receiveShadow = true;
// cone.castShadow = true;
// scene.add(cone);

// 加载模型
const loader = new GLTFLoader();
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath("./draco/");
loader.setDRACOLoader(dracoLoader);
let c1, c2;
c1 = loader.loadAsync("./model/car.gltf");
c2 = loader.loadAsync("./model/truck.gltf");
Promise.all([c1, c2]).then((res) => {
  const car = res[0].scene;
  const truck = res[1].scene;
  car.children[0].rotation.y = Math.PI / 2;
  car.children[0].scale.set(0.2, 0.2, 0.2);
  const vehicle = new YUKA.Vehicle();
  vehicle.maxSpeed = 3;
  vehicle.position.set(Math.random() * 20 - 10, 0, Math.random() * 20 - 10);
  vehicle.setRenderComponent(car, callback);
  entityManager.add(vehicle);

  truck.children[0].rotation.y = Math.PI / 2;
  truck.children[0].scale.set(0.2, 0.2, 0.2);
  const vehicle2 = new YUKA.Vehicle();
  vehicle2.maxSpeed = 6;
  vehicle2.position.set(Math.random() * 20 - 10, 0, Math.random() * 20 - 10);
  vehicle2.setRenderComponent(truck, callback);
  entityManager.add(vehicle2);

  scene.add(car);
  scene.add(truck);

  // 设置追击行为
  const pursuitBehavior = new YUKA.PursuitBehavior(vehicle2, 5);
  vehicle.steering.add(pursuitBehavior);

  // 设置卡车到达目标行为
  const arriveBehavior = new YUKA.ArriveBehavior(target.position);
  vehicle2.steering.add(arriveBehavior);

  setInterval(() => {
    target.position.set(Math.random() * 50 - 25, 0, Math.random() * 50 - 25);
  }, 3000);
});
function callback(entity, renderComponent) {
  // console.log(entity, renderComponent);
  renderComponent.position.copy(entity.position);
  renderComponent.quaternion.copy(entity.rotation);
  // renderComponent.matrix.copy(entity.worldMatrix);
}

// 创建目标小球
const sphereGeometry = new THREE.SphereGeometry(0.1, 32, 32);
const sphereMaterial = new THREE.MeshStandardMaterial({ color: 0xff00ff });
const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
sphere.receiveShadow = true;
sphere.castShadow = true;
scene.add(sphere);
// 创建目标
const target = new YUKA.GameEntity();
target.setRenderComponent(sphere, callback);

target.position.set(Math.random() * 20 - 10, 0, Math.random() * 20 - 10);

const entityManager = new YUKA.EntityManager();
entityManager.add(target);
</script>

<template>
  <div></div>
</template>

<style>
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

canvas {
  width: 100vw;
  height: 100vh;
  position: fixed;
  left: 0;
  top: 0;
}
</style>

五、候鸟群集聚散效果

在这里插入图片描述

<script setup>
import * as THREE from "three";
import * as YUKA from "yuka";
// 导入控制器
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader.js";

// 创建场景
const scene = new THREE.Scene();
// 创建相机
const camera = new THREE.PerspectiveCamera(
  75,
  window.innerWidth / window.innerHeight,
  0.1,
  1000
);
camera.position.set(0, 10, 20);
camera.lookAt(0, 0, 0);

// 创建渲染器
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.shadowMap.enabled = true;
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

// 创建地面
const planeGeometry = new THREE.PlaneGeometry(50, 50);
const planeMaterial = new THREE.MeshStandardMaterial({ color: 0x999999 });
const plane = new THREE.Mesh(planeGeometry, planeMaterial);
plane.receiveShadow = true;
plane.rotation.x = -Math.PI / 2;
plane.position.y = -0.5;
scene.add(plane);

// 创建灯光
const light = new THREE.SpotLight(0xffffff, 3, 100, Math.PI / 6, 0.5);
light.position.set(10, 40, 10);
light.castShadow = true;
scene.add(light);

// 创建控制器
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;

const time = new YUKA.Time();
requestAnimationFrame(function animate() {
  const delta = time.update().getDelta();
  controls.update();
  entityManager.update(delta);
  renderer.render(scene, camera);
  requestAnimationFrame(animate);
});

// 创建椎体
// const coneGeometry = new THREE.ConeGeometry(0.2, 1, 32);
// coneGeometry.rotateX(Math.PI / 2);
// const coneMaterial = new THREE.MeshStandardMaterial({ color: 0xff0000 });
// const cone = new THREE.Mesh(coneGeometry, coneMaterial);
// // cone.matrixAutoUpdate = false;
// cone.receiveShadow = true;
// cone.castShadow = true;
// scene.add(cone);

// 加载模型
const loader = new GLTFLoader();
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath("./draco/");
loader.setDRACOLoader(dracoLoader);
loader.load("./model/car.gltf", function (gltf) {
  // console.log(gltf);
  // 创建群体随机行走行为
  const wanderBehavior = new YUKA.WanderBehavior(3);

  // 设置整齐群体转向
  const alignmentBehavior = new YUKA.AlignmentBehavior();
  alignmentBehavior.weight = 5;

  // 设置聚集行为
  const cohesionBehavior = new YUKA.CohesionBehavior();
  cohesionBehavior.weight = 5;

  // 设置分离行为
  const separationBehavior = new YUKA.SeparationBehavior();
  separationBehavior.weight = 0.5;

  for (let i = 0; i < 40; i++) {
    // 创建yuka的车辆
    const vehicle = new YUKA.Vehicle();
    vehicle.position.set(Math.random() * 20 - 10, 0, Math.random() * 20 - 10);
    vehicle.rotation.fromEuler(0, Math.random() * Math.PI, 0);
    vehicle.maxSpeed = 3;
    const car = gltf.scene.clone();
    car.children[0].rotation.y = Math.PI / 2;
    car.children[0].scale.set(0.2, 0.2, 0.2);
    scene.add(car);
    vehicle.setRenderComponent(car, callback);
    entityManager.add(vehicle);

    vehicle.steering.add(wanderBehavior);
    vehicle.steering.add(alignmentBehavior);
    vehicle.steering.add(cohesionBehavior);
    vehicle.steering.add(separationBehavior);
  }
});

// 设置车辆的渲染对象
// vehicle.setRenderComponent(cone, callback);
function callback(entity, renderComponent) {
  // console.log(entity, renderComponent);
  renderComponent.position.copy(entity.position);
  renderComponent.quaternion.copy(entity.rotation);
  // renderComponent.matrix.copy(entity.worldMatrix);
}

// 创建对实体管理对象
const entityManager = new YUKA.EntityManager();
</script>

<template>
  <div></div>
</template>

<style>
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

canvas {
  width: 100vw;
  height: 100vh;
  position: fixed;
  left: 0;
  top: 0;
}
</style>

  • 10
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值