來源 :https://blog.csdn.net/qq_30100043/article/details/80087471
簡介
上一節本想直接了結動畫這一章。最后一想,沒有做過模型動畫切換的案例。就此,再加一章,關於模型多個動畫之間如何切換的問題。
案例實現
案例查看地址:http://www.wjceo.com/blog/threejs/2018-04-25/153.html
首先,我們需要先將模型導入,之前案例已經講過如何導入,這里就不贅述。
//加載模型
var loader = new THREE.FBXLoader(); loader.load("/lib/models/fbx/Naruto.fbx", function (mesh) { mesh.position.y += 100; scene.add(mesh); });
導入模型以后,我們需要查看當前模型的動畫的長度,也就是mesh.animations的長度,如果只有一個長度,代表只有一個動畫,如果有多個,就是多個動畫的模型。然后我們需要遍歷mesh.animations數組,為每個動畫創建一個action,作為后面調用:
mixer = mesh.mixer = new THREE.AnimationMixer(mesh); var actions = []; //所有的動畫數組 for(var i=0; i<mesh.animations.length; i++){ actions[i] = mixer.clipAction(mesh.animations[i]); }
在切換動畫的時候,我們需要做的就是,將當前的所有的action除去需要播放的那個action全部暫停動畫播放,讓需要播放的哪一個動畫調用play()事件:
gui["action"+i] = function () { for(var j=0; j<actions.length; j++){ if(j === i){ actions[j].play(); } else{ actions[j].stop(); } } }; animations.add(gui, "action"+i);
這樣,我們就實現了對模型的動畫切換。
案例代碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style type="text/css">
html, body {
margin: 0;
height: 100%;
}
canvas {
display: block;
}
</style>
</head>
<body onload="draw();">
</body>
<script src="https://cdn.bootcss.com/three.js/91/three.min.js"></script>
<script src="/lib/js/libs/inflate.min.js"></script>
<script src="/lib/js/loaders/FBXLoader.js"></script>
<script src="/lib/js/controls/OrbitControls.js"></script>
<script src="https://cdn.bootcss.com/stats.js/r17/Stats.min.js"></script>
<script src="https://cdn.bootcss.com/dat-gui/0.7.1/dat.gui.min.js"></script>
<script src="/lib/js/Detector.js"></script>
<script>
var renderer, camera, scene, gui, light, stats, controls, meshHelper, mixer, action,datGui;
var clock = new THREE.Clock();
function initRender() {
renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0xeeeeee);
renderer.shadowMap.enabled = true;
//告訴渲染器需要陰影效果
document.body.appendChild(renderer.domElement);
}
function initCamera() {
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 2000);
camera.position.set(100, 200, 300 );
}
function initScene() {
scene = new THREE.Scene();
scene.background = new THREE.Color( 0xa0a0a0 );
scene.fog = new THREE.Fog( 0xa0a0a0, 200, 1000 );
}
//初始化dat.GUI簡化試驗流程
function initGui() {
//聲明一個保存需求修改的相關數據的對象
gui = {
helper:true //模型輔助線
};
datGui = new dat.GUI();
//將設置屬性添加到gui當中,gui.add(對象,屬性,最小值,最大值)
datGui.add(gui, "helper").onChange(function (e) {
meshHelper.visible = e;
})
}
function initLight() {
scene.add(new THREE.AmbientLight(0x444444));
light = new THREE.DirectionalLight(0xffffff);
light.position.set(0, 200, 100 );
light.castShadow = true;
light.shadow.camera.top = 180;
light.shadow.camera.bottom = -100;
light.shadow.camera.left = -120;
light.shadow.camera.right = 120;
//告訴平行光需要開啟陰影投射
light.castShadow = true;
scene.add(light);
}
function initModel() {
//輔助工具
var helper = new THREE.AxesHelper(50);
scene.add(helper);
// 地板
var mesh = new THREE.Mesh( new THREE.PlaneBufferGeometry( 2000, 2000 ), new THREE.MeshPhongMaterial( { color: 0xffffff, depthWrite: false } ) );
mesh.rotation.x = - Math.PI / 2;
mesh.receiveShadow = true;
scene.add( mesh );
//添加地板割線
var grid = new THREE.GridHelper( 2000, 20, 0x000000, 0x000000 );
grid.material.opacity = 0.2;
grid.material.transparent = true;
scene.add( grid );
//加載模型
var loader = new THREE.FBXLoader();
loader.load("/lib/models/fbx/Naruto.fbx", function (mesh) {
console.log(mesh);
//添加骨骼輔助
meshHelper = new THREE.SkeletonHelper(mesh);
scene.add(meshHelper);
//設置模型的每個部位都可以投影
mesh.traverse( function ( child ) {
if ( child.isMesh ) {
child.castShadow = true;
child.receiveShadow = true;
}
} );
//AnimationMixer是場景中特定對象的動畫播放器。當場景中的多個對象獨立動畫時,可以為每個對象使用一個AnimationMixer
mixer = mesh.mixer = new THREE.AnimationMixer(mesh);
//mixer.clipAction 返回一個可以控制動畫的AnimationAction對象 參數需要一個AnimationClip 對象
//AnimationAction.setDuration 設置一個循環所需要的時間,當前設置了一秒
//告訴AnimationAction啟動該動作
//action = mixer.clipAction(mesh.animations[0]);
//action.play();
var actions = []; //所有的動畫數組
var animations = datGui.addFolder("animations");
for(var i=0; i<mesh.animations.length; i++){
createAction(i);
}
function createAction(i){
actions[i] = mixer.clipAction(mesh.animations[i]);
gui["action"+i] = function () {
for(var j=0; j<actions.length; j++){
if(j === i){
actions[j].play();
}
else{
actions[j].stop();
}
}
};
animations.add(gui, "action"+i);
}
//添加暫停所有動畫的按鍵
gui.stop = function(){
for(var i=0; i<actions.length; i++){
actions[i].stop();
}
};
datGui.add(gui, "stop");
mesh.position.y += 100;
scene.add(mesh);
});
}
//初始化性能插件
function initStats() {
stats = new Stats();
document.body.appendChild(stats.dom);
}
function initControls() {
controls = new THREE.OrbitControls(camera, renderer.domElement);
//設置控制器的中心點
//controls.target.set( 0, 100, 0 );
// 如果使用animate方法時,將此函數刪除
//controls.addEventListener( 'change', render );
// 使動畫循環使用時阻尼或自轉 意思是否有慣性
controls.enableDamping = true;
//動態阻尼系數 就是鼠標拖拽旋轉靈敏度
//controls.dampingFactor = 0.25;
//是否可以縮放
controls.enableZoom = true;
//是否自動旋轉
controls.autoRotate = false;
controls.autoRotateSpeed = 0.5;
//設置相機距離原點的最遠距離
controls.minDistance = 1;
//設置相機距離原點的最遠距離
controls.maxDistance = 2000;
//是否開啟右鍵拖拽
controls.enablePan = true;
}
function render() {
var time = clock.getDelta();
if (mixer) {
mixer.update(time);
}
controls.update();
}
//窗口變動觸發的函數
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function animate() {
//更新控制器
render();
//更新性能插件
stats.update();
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
function draw() {
//兼容性判斷
if (!Detector.webgl) Detector.addGetWebGLMessage();
initGui();
initRender();
initScene();
initCamera();
initLight();
initModel();
initControls();
initStats();
animate();
window.onresize = onWindowResize;
}
</script>
</html>
