人物按照上一篇博客所設(shè)定的關(guān)鍵點位置,勻速移動
文章來源:http://www.zghlxwxcb.cn/news/detail-830832.html
1. 初始化加載模型
// 加載巡航人物模型 callback 動作完成的回調(diào)函數(shù)
initWalkPerson(callback) {
fbxloader("walk").then((obj) => {
obj.scale.set(2.5, 2.5, 2.5);
obj.name = "person";
person = obj;
scene.add(obj);
//有回調(diào)函數(shù) 就執(zhí)行回調(diào)函數(shù)
callback && callback();
});
},
2. 開始移動模型
// 開始移動模型
startAnimation() {
if (isAnimating) return this.elMessage("當前巡航已開始,請勿多次操作", "error");
isAnimating = true;
//說明模型已加載完成,無需重復(fù)加載,直接執(zhí)行動畫效果
if (person) {
this.personPositionStart();
} else {
//人物模型加載完畢后在執(zhí)行
this.initWalkPerson(() => {
this.personPositionStart();
});
}
},
3. 人物模型啟動
//人物動畫啟動
personPositionStart() {
personMixer = new THREE.AnimationMixer(person);
let AnimationAction = personMixer.clipAction(person.animations[0]);
AnimationAction.play();
person.position.set(pointArr[0]);
scene.getObjectByName("path").material.visible = false; //隱藏行動軌跡動畫
scene.getObjectByName("person").visible = true;
tweenHandlers = [];
// 定義速度(單位:單位長度/秒)
const speed = 300; // 你可以根據(jù)需要調(diào)整這個速度值
let prevTween = null;
let startPos = new THREE.Vector3(...pointArr[0]);
for (let i = 1; i < pointArr.length; i++) {
// 每次循環(huán)設(shè)置下一個目標點
const endPos = new THREE.Vector3(...pointArr[i]);
const newTween = this.createTween(startPos.clone(), endPos, speed);
tweenHandlers.push(newTween);
if (prevTween) {
prevTween.chain(newTween);
} else {
// 如果是序列中的第一個tween,立即開始動畫
newTween.start();
}
// 將此tween存儲為下一個tween的'prevTween'
prevTween = newTween;
// 更新起始點為當前結(jié)束點,為下一個循環(huán)準備
startPos.copy(endPos);
}
// 開始第一個tween動畫
if (tweenHandlers.length > 0) {
currentTween = tweenHandlers[0];
currentTween.start();
isAnimating = true;
}
// 在最后一個Tween結(jié)束后執(zhí)行的動作
prevTween.onComplete(() => {
// 在動畫被標記為完成時才重置位置
this.resetPosition();
});
},
4. 暫停模型移動
// 暫停模型移動
pauseAnimation() {
if (!isAnimating) {
this.elMessage("當前巡航未開始", "warning");
return;
}
if (this.isPaused) {
// 恢復(fù)攝像機狀態(tài)
camera.position.copy(savedCameraPosition);
controls.target.copy(savedCameraTarget);
controls.update();
// 恢復(fù)動畫
tweenHandlers.forEach((tween) => tween.resume());
personMixer.timeScale = 1;
this.isPaused = false; //設(shè)置this.isPaused為false
isAnimating = true;
this.elMessage("動畫已恢復(fù)", "success");
this.updateCameraPosition(person, camera, new THREE.Vector3(0, 250, 200));
} else {
// 保存當前攝像機狀態(tài)
savedCameraPosition = camera.position.clone();
savedCameraTarget = controls.target.clone();
// 暫停動畫
tweenHandlers.forEach((tween) => tween.pause());
personMixer.timeScale = 0;
this.isPaused = true; //設(shè)置this.isPaused為true
this.elMessage("動畫已暫停", "success");
}
},
5. 重置模型位置
// 重置模型位置
resetPosition() {
isAnimating = false;
this.pauseAnimation();
// 將模型從場景中移除
scene.getObjectByName("person").visible = false;
// 清理動畫混合器
if (personMixer) {
personMixer.uncacheClip(personMixer._actions[0]._clip);
personMixer = null;
}
tweenHandlers.forEach((item) => item.stop());
tweenHandlers = [];
// 重置動畫狀態(tài)
this.isPaused = false;
this.tweenArea({ x: -5000, y: 7000, z: 16000 }, { x: 0, y: 0, z: 1 });
//顯示行動軌跡
scene.getObjectByName("path").material.visible = true;
},
6. 切換區(qū)域動畫
// 切換區(qū)域動畫
tweenArea(Position, controlsTarget) {
// 傳遞任意目標位置,從當前位置運動到目標位置
const p1 = {
// 定義相機位置是目標位置到中心點距離的2.2倍
x: camera.position.x,
y: camera.position.y,
z: camera.position.z,
};
const p2 = {
x: Position.x,
y: Position.y,
z: Position.z,
};
changeAreaTween = new TWEEN.Tween(p1).to(p2, 1200); // 第一段動畫
const update = function (object) {
camera.rotation.y = (90 * Math.PI) / 180;
camera.position.set(object.x, object.y, object.z);
controls.target = new THREE.Vector3(controlsTarget.x, controlsTarget.y, controlsTarget.z);
// camera.lookAt(lookAt); // 保證動畫執(zhí)行時,相機焦距在中心點
controls.enabled = false;
controls.update();
};
changeAreaTween.onUpdate(update);
// 動畫完成后的執(zhí)行函數(shù)
changeAreaTween.onComplete(() => {
controls.enabled = true; // 執(zhí)行完成后開啟控制
});
changeAreaTween.easing(TWEEN.Easing.Quadratic.InOut);
changeAreaTween.start();
},
7. 攝像機追蹤模型
// 攝像機追蹤模型
updateCameraPosition(model, camera, offset) {
if (!this.isPaused && isAnimating) {
// 添加條件判斷
const desiredPosition = new THREE.Vector3().copy(model.position).add(offset);
camera.position.lerp(desiredPosition, 0.05);
camera.lookAt(model.position);
}
},
8. 移動模型位置
// 移動模型位置
createTween(startPosition, endPosition, speed) {
// 計算起點到終點的距離
const distance = startPosition.distanceTo(endPosition);
// 使用距離除以速度來計算持續(xù)時間
const duration = (distance / speed) * 1000; // 持續(xù)時間(以毫秒為單位)
// 創(chuàng)建并返回一個新的Tween動畫
return new TWEEN.Tween(startPosition)
.to({ x: endPosition.x, y: endPosition.y, z: endPosition.z }, duration)
.easing(TWEEN.Easing.Quadratic.InOut)
.onUpdate(() => {
//相機的相對偏移量,z=-400 在人物模型的后面
const relativeCameraOffset = new THREE.Vector3(0, 100, -400);
const targetCameraPosition = relativeCameraOffset.applyMatrix4(person.matrixWorld);
camera.position.set(targetCameraPosition.x, targetCameraPosition.y, targetCameraPosition.z);
//更新控制器的目標為Person的位置
const walkerPosition = person.position.clone();
controls.target = new THREE.Vector3(walkerPosition.x, 100, walkerPosition.z);
// 確??刂破鞯淖兏?/span>
controls.update();
// 更新模型位置
person.position.copy(startPosition);
person.lookAt(endPosition);
})
.onComplete(() => {
// 動畫完成時,確保模型位置與結(jié)束位置相匹配
person.position.copy(endPosition);
});
},
9.動畫執(zhí)行
全局定義的參數(shù):文章來源地址http://www.zghlxwxcb.cn/news/detail-830832.html
let personMixer = null; // 巡航混合器變量
let personClock = new THREE.Clock(); // 巡航計時工具
// 獲取巡航時間差
const personDelta = personClock.getDelta();
if (personMixer && isAnimating) {
personMixer.update(personDelta);
}
TWEEN.update();
到了這里,關(guān)于VUE2+THREE.JS 按照行動軌跡移動人物模型并相機視角跟隨人物的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!