先看下最终效果吧
开始 创建多个仓库自定义长宽高
可参考上一节three.js教程创建仓库场景示例一
addHouse(){
const house1 = new House(150,80,20,this.scene)
house1.setPosition(-50, 1, 100)
house1.addToScene(this.scene)
this.meshList.push(...house1.instance.children)
const house2 = new House(80,40,20,this.scene)
house2.setPosition(90, 1, 120)
house2.addToScene(this.scene)
this.meshList.push(...house2.instance.children)
const house3 = new House(200,100,20,this.scene)
house3.setPosition(0, 1, -120)
house3.setRotation(0,-Math.PI/2,0)
house3.addToScene(this.scene)
this.meshList.push(...house3.instance.children)
}
addBox(){
let w=6
let x_length = 0
let y_length = 0
let z_length = 0
let boxgroup = new THREE.Group()
const geometry = new THREE.BoxBufferGeometry(w,w,w)
const material = new THREE.MeshLambertMaterial({
color:0xb99b75
})
for (let index = 1; index <= 5; index++) {
if(index%2 == 0){
x_length += w
}else if(index%3 == 0){
x_length -= w , z_length += w
}else if(index%4 == 0){
x_length += w
}else if(index%5 == 0){
x_length -= w , y_length += w , z_length -= w
}else{
x_length += w/2 , y_length += w/2 , z_length += w/2
}
let box = new THREE.Mesh(geometry, material)
box.name = '盒子'+index
let edges = new THREE.EdgesHelper( box, 0x000000 );//设置边框,可以旋转
box.name = '盒子边框'+index
box.position.set(x_length,y_length,z_length)
edges.position.set(x_length,y_length,z_length)
boxgroup.add(box)
boxgroup.add( edges );
this.meshList.push(box,edges)
}
boxgroup.position.set(30,0,-140)
this.scene.add(boxgroup)
}
addCar(){
this.loader.load('./src/assets/gltf/xiaofang/car.gltf', (gltf) => {
this.carmodel = gltf.scene;
this.carmodel.scale.set(5, 5, 5);
this.carmodel.position.set(this.carX,0,220)
this.carmodel.rotation.y = Math.PI/2
this.scene.add(this.carmodel)
});
}
消防车运动起来
animate () {
// 执行运动动画
if(this.renderer && this.camera){
this.renderer.render(this.scene, this.camera)
requestAnimationFrame(this.animate.bind(this))
if(this.carmodel && this.carX <= 20) {
this.carmodel.position.set(this.carX,0,220)
this.carX += 0.5
}
}
}
消防车运动结束后消防员下车,创建消防员模型。此时如果看不见人,可能是因为太小了试着放大模型,调用消防员跑步动画,,添加消防路线 ,运动员跟随线跑起来并可转向
addPerson(){
this.loader.load('./src/assets/gltf/xiaofang/person.gltf', (gltf) => {
console.log(gltf);
this.personmodel = gltf.scene;
this.personmodel.scale.set(15, 15, 15);
this.personmodel.position.set(20,0,200)
this.personmodel.rotation.y = Math.PI
this.scene.add(this.personmodel)
// 调用动画
var mixer = new THREE.AnimationMixer( gltf.scene.children[0] );
mixer.clipAction( gltf.animations[ 0 ] ).setDuration( 1 ).play();
this.mixers.push( mixer );
this.addLine()
});
}
personanimate(){
var delta = this.clock.getDelta();
for ( var i = 0; i < this.mixers.length; i ++ ) { // 重复播放动画
this.mixers[ i ].update( delta );
}
}
addLine(){
this.curve = new THREE.CatmullRomCurve3( [
new THREE.Vector3( 20,1,200 ),
new THREE.Vector3( 20, 1, 50 ),
new THREE.Vector3( 100, 1, 20 ),
new THREE.Vector3( 0, 1, -60 ),
],false ); // 是否闭合
var points = this.curve.getPoints( 50 ); // 值越大越平滑
var geometry = new THREE.BufferGeometry().setFromPoints( points );
var material = new THREE.LineBasicMaterial( { color : 0xffffff } );
this.line = new THREE.Line( geometry, material )
this.scene.add(this.line)
}
addRunperson(){
if (this.progress > 1.0) {
return; //停留在管道末端,否则会一直跑到起点 循环再跑
}
this.progress += 0.0009; // 控制速度
let point = this.curve.getPoint(this.progress);
if (point && point.x) {
//模型的偏移量
let offsetAngle = Math.PI;
//创建一个4维矩阵
let mtx = new THREE.Matrix4();
mtx.lookAt(this.personmodel.position.clone(), point, this.personmodel.up);
mtx.multiply(new THREE.Matrix4().makeRotationFromEuler(new THREE.Euler(0, offsetAngle, 0)));
//计算出需要进行旋转的四元数值
let toRot = new THREE.Quaternion().setFromRotationMatrix(mtx);
//根据以上值调整角度
this.personmodel.quaternion.slerp(toRot, 0.2);
this.personmodel.position.set(point.x, point.y, point.z);
}
}
animate () {
// 执行运动动画
if(this.renderer && this.camera){
this.renderer.render(this.scene, this.camera)
requestAnimationFrame(this.animate.bind(this))
this.mixers.length>0 && this.personanimate()
if(this.carmodel && this.carX <= 20) {
this.carmodel.position.set(this.carX,0,220)
this.carX += 0.5
}else if(this.carmodel && this.carX > 20){
!this.exitPerson && !this.personmodel && this.addPerson()
this.exitPerson = true
}
this.line && this.personmodel && this.addRunperson()
}
}
完整代码如下
director.js
import Template from "../common/Template"
import * as THREE from 'three'
import House from "../objects/House"
import TWEEN from '@tweenjs/tween.js'
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js"
export default class Director extends Template{
constructor (ele) {
super()
this.ele = ele
this.PCamera.fov = 45
this.PCamera.far = 10000
this.PCamera.near = 1
this.rendererColor = new THREE.Color(0x01031c)
this.cameraPostion = new THREE.Vector3(300, 200, 0)
this.clock = new THREE.Clock();
this.mixers = []
this.doorstatus = 'close'
this.loader = new GLTFLoader();
this.carmodel = undefined
this.carX = -100
this.exitPerson = false
this.personmodel = undefined
this.line = undefined
this.curve = undefined
this.progress = 0
this.init(this.ele)
this.addLight()
this.addAxesHelper(1000)
if(this.camera && this.renderer) this.addOrbitControls(this.camera, this.renderer.domElement);
this.animate()
this.addGround()
this.addHouse()
this.addBox()
// 监听鼠标点击
this.onMouseDown(this.addMouseDown.bind(this))
// 添加消防车人
this.addCar()
}
addRunperson(){
if (this.progress > 1.0) {
return; //停留在管道末端,否则会一直跑到起点 循环再跑
}
this.progress += 0.0009; // 控制速度
let point = this.curve.getPoint(this.progress);
if (point && point.x) {
//模型的偏移量
let offsetAngle = Math.PI;
//创建一个4维矩阵
let mtx = new THREE.Matrix4();
mtx.lookAt(this.personmodel.position.clone(), point, this.personmodel.up);
mtx.multiply(new THREE.Matrix4().makeRotationFromEuler(new THREE.Euler(0, offsetAngle, 0)));
//计算出需要进行旋转的四元数值
let toRot = new THREE.Quaternion().setFromRotationMatrix(mtx);
//根据以上值调整角度
this.personmodel.quaternion.slerp(toRot, 0.2);
this.personmodel.position.set(point.x, point.y, point.z);
}
}
addLine(){
this.curve = new THREE.CatmullRomCurve3( [
new THREE.Vector3( 20,1,200 ),
new THREE.Vector3( 20, 1, 50 ),
new THREE.Vector3( 100, 1, 20 ),
new THREE.Vector3( 0, 1, -60 ),
],false ); // 是否闭合
var points = this.curve.getPoints( 50 ); // 值越大越平滑
var geometry = new THREE.BufferGeometry().setFromPoints( points );
var material = new THREE.LineBasicMaterial( { color : 0xffffff } );
this.line = new THREE.Line( geometry, material )
this.scene.add(this.line)
}
addPerson(){
this.loader.load('./src/assets/gltf/xiaofang/person.gltf', (gltf) => {
console.log(gltf);
this.personmodel = gltf.scene;
this.personmodel.scale.set(15, 15, 15);
this.personmodel.position.set(20,0,200)
this.personmodel.rotation.y = Math.PI
this.scene.add(this.personmodel)
// 调用动画
var mixer = new THREE.AnimationMixer( gltf.scene.children[0] );
mixer.clipAction( gltf.animations[ 0 ] ).setDuration( 1 ).play();
this.mixers.push( mixer );
this.addLine()
});
}
personanimate(){
var delta = this.clock.getDelta();
for ( var i = 0; i < this.mixers.length; i ++ ) { // 重复播放动画
this.mixers[ i ].update( delta );
}
}
addCar(){
this.loader.load('./src/assets/gltf/xiaofang/car.gltf', (gltf) => {
this.carmodel = gltf.scene;
this.carmodel.scale.set(5, 5, 5);
this.carmodel.position.set(this.carX,0,220)
this.carmodel.rotation.y = Math.PI/2
this.scene.add(this.carmodel)
});
}
addMouseDown(){
console.log(this.intersects);
// 门特效
let list = this.intersects.filter((item)=>item.object.name == '门')
console.log(list[0].object,list[0].object.geometry.parameters.width/2);
if(list.length > 0){
if(this.doorstatus == 'close'){
list[0].object.rotateY(-Math.PI / 2);
list[0].object.translateOnAxis(new THREE.Vector3(0, 0, 1), list[0].object.geometry.parameters.width/2);
list[0].object.translateOnAxis(new THREE.Vector3(1, 0, 0), list[0].object.geometry.parameters.width/2);
this.doorstatus = 'open'
}else{
list[0].object.rotateY(Math.PI/2);
list[0].object.translateOnAxis(new THREE.Vector3(0, 0, 1), -list[0].object.geometry.parameters.width/2);
list[0].object.translateOnAxis(new THREE.Vector3(1, 0, 0), list[0].object.geometry.parameters.width/2);
this.doorstatus = 'close'
}
}
}
addBox(){
let w=6
let x_length = 0
let y_length = 0
let z_length = 0
let boxgroup = new THREE.Group()
const geometry = new THREE.BoxBufferGeometry(w,w,w)
const material = new THREE.MeshLambertMaterial({
color:0xb99b75
})
for (let index = 1; index <= 5; index++) {
if(index%2 == 0){
x_length += w
}else if(index%3 == 0){
x_length -= w , z_length += w
}else if(index%4 == 0){
x_length += w
}else if(index%5 == 0){
x_length -= w , y_length += w , z_length -= w
}else{
x_length += w/2 , y_length += w/2 , z_length += w/2
}
let box = new THREE.Mesh(geometry, material)
box.name = '盒子'+index
let edges = new THREE.EdgesHelper( box, 0x000000 );//设置边框,可以旋转
box.name = '盒子边框'+index
box.position.set(x_length,y_length,z_length)
edges.position.set(x_length,y_length,z_length)
boxgroup.add(box)
boxgroup.add( edges );
this.meshList.push(box,edges)
}
boxgroup.position.set(30,0,-140)
this.scene.add(boxgroup)
}
addHouse(){
const house1 = new House(150,80,20,this.scene)
house1.setPosition(-50, 1, 100)
house1.addToScene(this.scene)
this.meshList.push(...house1.instance.children)
const house2 = new House(80,40,20,this.scene)
house2.setPosition(90, 1, 120)
house2.addToScene(this.scene)
this.meshList.push(...house2.instance.children)
const house3 = new House(200,100,20,this.scene)
house3.setPosition(0, 1, -120)
house3.setRotation(0,-Math.PI/2,0)
house3.addToScene(this.scene)
this.meshList.push(...house3.instance.children)
}
addGround(){
const groundGeometry = new THREE.PlaneGeometry(300,500)
const groundMaterial = new THREE.MeshLambertMaterial({
color:0x454942
})
let ground = new THREE.Mesh(groundGeometry, groundMaterial)
ground.rotation.x = -Math.PI/2
ground.name = '地'
this.scene.add(ground)
this.meshList.push(ground)
}
addLight(){
this.addAmbientLight(0x666666)
this.addDirectionalLight(0xdfebff,{x:50, y:200, z:100},1)
}
animate () {
// 执行运动动画
if(this.renderer && this.camera){
this.renderer.render(this.scene, this.camera)
requestAnimationFrame(this.animate.bind(this))
TWEEN.update();
this.mixers.length>0 && this.personanimate()
if(this.carmodel && this.carX <= 20) {
this.carmodel.position.set(this.carX,0,220)
this.carX += 0.5
}else if(this.carmodel && this.carX > 20){
console.log(111111);
!this.exitPerson && !this.personmodel && this.addPerson()
this.exitPerson = true
}
this.line && this.personmodel && this.addRunperson()
}
}
}
House.js和其他template等文件见three.js教程创建仓库场景示例一