three.js 中的 GLB 动画太快

GLB animation in three.js is too fast

我上传了一个带有动画的glb文件,而且动画移动的特别快,不知道为什么。

这是我角色的动画代码:

class MainChar extends THREE.Object3D {
    constructor() {
        super();
       this.object = new THREE.Object3D();
        this.object.position.set(0, 1, 50);
        this.object.scale.x=20;
        this.object.scale.y=20;
        this.object.scale.z=20;

        //load house model form blender file
    
    /*  
        loader.setPath('../../models/characters/');
        
        const gltf = loader.load('Douglas.glb', (gltf) => {
          gltf.scene.traverse(c => {
            c.castShadow = true;
          });
          this.object.add( gltf.scene);
        });
    
    */  

        const loader = new THREE.GLTFLoader();
    loader.setPath('../../models/characters/');
    
    const gltf = loader.load('walk.glb', (gltf) => {
        gltf.scene.traverse(c => {
            c.castShadow = true;
          });
        this.mixer = new THREE.AnimationMixer( gltf.scene );
        this.mixer.timeScale=1/5;
        var action = this.mixer.clipAction( gltf.animations[ 0 ] );
        action.play();

        this.object.add( gltf.scene );
    });

        //save keyboard bindings
        this.keyboard = new THREEx.KeyboardState();
        /*
        //creating a box (need to change it to a character with animations)
        const geometry = new THREE.BoxGeometry( 1, 1, 1 );
        const material = new THREE.MeshBasicMaterial( {color: 0x00ff00} );
        this.object = new THREE.Mesh( geometry, material );
        
        this.object.scale.x=5;
        this.object.scale.y=10;
        this.object.scale.z=5;
            //starting position for character
        this.object.position.set(0, 10, 50);

        */
        
    

        
        this.update = function (time) {
            if ( this.mixer ){

             this.mixer.update( time );
             console.log(time);
             
            }
            //MOVEMENT OF BOX

            //speed
            var moveDistance = 0.5 ; 
        //  var rotateAngle = Math.PI / 2 * 0.05;   
            
            // move forwards/backwards/left/right
            if ( this.keyboard.pressed("W") ){
                this.object.translateZ( -moveDistance );
            }
            if (  this.keyboard.pressed("S") ){
                this.object.translateZ(  moveDistance );
            }
            if (  this.keyboard.pressed("A") ){
                this.object.translateX( -moveDistance );
            }
            if (  this.keyboard.pressed("D") ){
                this.object.translateX( moveDistance );
            }

            // move forwards/backwards/left/right
            if ( this.keyboard.pressed("up") ){
                this.object.translateZ( -moveDistance );
            }
            if (  this.keyboard.pressed("down") ){
                this.object.translateZ(  moveDistance );
            }
            if (  this.keyboard.pressed("left") ){
                this.object.translateX( -moveDistance );
            }
            if (  this.keyboard.pressed("right") ){
                this.object.translateX( moveDistance );
            }
            
        
            // FOR CAMERA ROTATIONS
            //this.object.rotateOnAxis( new THREE.Vector3(0,1,0), -rotateAngle);
            //this.object.rotateOnAxis( new THREE.Vector3(0,1,0), rotateAngle);
            //var rotation_matrix = new THREE.Matrix4().identity();
            
            
            if (  this.keyboard.pressed("Z") )
            {
                this.object.position.set(0, 1, 50);
                this.object.rotation.set(0,0,0);
            }
                
            /*
            // global coordinates
            if (  this.keyboard.pressed("left") )
            this.object.position.x -= moveDistance;
            if (  this.keyboard.pressed("right") )
            this.object.position.x += moveDistance;
            if (  this.keyboard.pressed("up") )
            this.object.position.z -= moveDistance;
            if (  this.keyboard.pressed("down") )
            this.object.position.z += moveDistance;
            */

        };
    }
}

这是允许游戏暂停的时间 class,以及 returns 增量时间:

class Time {
constructor(){
    this.is_pause = false;
    this.accumalated_run_time = 0;
    this.clock = new THREE.Clock();
    this.pause_clock = new THREE.Clock();
}


getRunTime()
{
    this.accumalated_run_time += this.clock.getDelta();

    return this.accumalated_run_time
}

pause()
{
    this.is_pause = true;

}

unpause()
{
    this.is_pause = false;
    this.clock.getDelta();
}



    


}

这是调用我的角色更新动画的场景管理器:

class SceneManager {
    constructor(canvas) {
        //this entire function renders a scene where you can add as many items as you want to it (e.g. we can create the house and add as
        //many items as we want to the house). It renders objects from other javascript files
        //------------------------------------------------------------------------------------------------------------------------------------------
        //These are supposed to act like constants. DO NOT CHANGE
        this.GAME_PAUSE = "pause";
        this.GAME_RUN = "run";
        //------------------------------------------------------------------------------------------------------------------------------------------
    //we use (this) to make variables accessible in other classes
    this.time = new Time();

    

    this.game_state = this.GAME_RUN;


    this.screenDimensions = {
        width: canvas.width,
        height: canvas.height
    };

    //the essentials for rendering a scene
    this.scene = this.buildScene();
    this.renderer = this.buildRender(this.screenDimensions);
    this.camera = this.buildCamera(this.screenDimensions);
    this.managers = this.createManagers();
    this.loadToScene(this.managers[0].entities);
    //allow camera to orbit target (player)
    this.controls = new THREE.OrbitControls(this.camera, this.renderer.domElement);
    this.controls.target.set(0, 20, 0);
    this.controls.update();

}

loadToScene(entities)
{
    for (let i = 0 ; i < entities.length ; i++)
    {
        console.log("before" +i.toString());
        this.scene.add(entities[i].object);
        console.log("after");
    }
}
    //this function creates our scene
    buildScene() {
        //create a new scene
        const scene = new THREE.Scene();

        //set the scene's background-> in this case it is our skybox
        const loader = new THREE.CubeTextureLoader();
        //it uses different textures per face of cube
        const texture = loader.load([
            '../skybox/House/posx.jpg',
            '../skybox/House/negx.jpg',
            '../skybox/House/posy.jpg',
            '../skybox/House/negy.jpg',
            '../skybox/House/posz.jpg',
            '../skybox/House/negz.jpg'
        ]);
        scene.background = texture;

        //if we wanted it to be a colour, it would have been this commented code:
        //scene.background = new THREE.Color("#000");
        return scene;
    }

    //this creates a renderer for us
    buildRender({ width, height }) {

        const renderer = new THREE.WebGLRenderer({
            canvas: canvas,
            antialias: true, alpha: true
        });
        renderer.shadowMap.enabled = true;
        renderer.shadowMap.type = THREE.PCFSoftShadowMap;
        renderer.setPixelRatio(window.devicePixelRatio);
        renderer.setSize(window.innerWidth, window.innerHeight);

        return renderer;
    }

    //create a camera for the screen
    buildCamera({ width, height }) {

        //SETTING FIELD OF VIEW, ASPECT RATIO (which should generally be width/ height), NEAR AND FAR (anything outside near/ far is clipped)
        const aspectRatio = width / height;
        const fieldOfView = 60;
        const nearPlane = 1;
        const farPlane = 1000;

        //there are 2 types of cameras: orthographic and perspective- we will use perspective (more realistic)
        const camera = new THREE.PerspectiveCamera(fieldOfView, aspectRatio, nearPlane, farPlane);

        //set where the camera is
        camera.position.set(-50, 50, 70);

        return camera;
    }

    //add subjects to the scene
    createManagers() {

        const managers=[new EntityManager()];
        //can be altered so we can add multiple entities, and depending on which position
        //it is, certain ones won't be paused, and some will be
        managers[0].register(new GeneralLights());
        managers[0].register(new House());
        managers[0].register(new MainChar());
        managers[0].register(new SceneSubject())


        return managers;
    }

    //this updates the subject/model every frame
    update() {
        
        //won't call this loop if it's paused-> only for objects that need to be paused (managers that need to be paused)
        if (this.game_state == this.GAME_RUN)
        {
            const runTime = this.time.getRunTime();
            this.managers[0].update(runTime);
        }
  
        //update orbit controls
        this.controls.update();

        this.renderer.render(this.scene, this.camera);
    }

    //this resizes our game when screen size changed
    onWindowResize() {

        this.camera.aspect = window.innerWidth / window.innerHeight;
        this.camera.updateProjectionMatrix();

        this.renderer.setSize(window.innerWidth, window.innerHeight);

    }

    pause(){ //when pause mode is entered. The pause menu needs to be rendered.
        this.game_state = this.GAME_PAUSE;
        this.time.pause();

    }

    unpause(){
        this.game_state = this.GAME_RUN;
        this.time.unpause();

        }
    
}

我认为问题出在您的 AnimationMixer.update() 电话上。如果您 look at the docs,更新期望以秒为单位的时间增量,但看起来您正在传递总 运行 时间。这意味着它应该接收自上一帧以来经过的时间。您可以使用 clock.getDelta(); 作为参数来解决此问题:

this.update = function (time) {
    if ( this.mixer ){
        const delta = this.clock.getDelta();
        this.mixer.update(delta);
    }
    
    // ...
}