来源 :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>