三个Js / Physijs游戏MMO角色控件

时间:2013-12-01 16:29:31

标签: three.js game-engine game-physics physijs

我正在为我正在开发的游戏开发第三人称角色控制。到目前为止,我对结果感到满意。角色控件有很多简洁的功能,例如:如果一个物体位于相机前面,它会向前移动,这样你仍然可以看到角色,但是当我将它旋转到一侧然后将我的玩家转走时,相机会严重干扰从中。我上传了一个关于JSFiddle的测试:http://jsfiddle.net/nA8SV/我只在chrome中测试了这个,并且由于某种原因,结果部分没有得到按键,直到你点击该帧上画布边界的空白区域。

[我也开始使用“--disable-web-security”来忽略交叉原点]

但是一旦你点击页面,按键就会起作用。控件是轨道控件的修改版本。因此,您可以左键单击并旋转视图。此外,您可以使用wasd键移动,当您移动/旋转时,摄像机视图应该返回到播放器后面。

我为这个很难在JSFiddle上工作的小车道歉。 (但旋转错误正在发生,所以它至少会重现它。)

基本上我试图让我的相机旋转回到我的角色后面,所以我有一些代码可以修复第250行的旋转,但是当角色移动时相机会断断续续。

以下是我的理论我认为相机的整体性能与物理模拟有点关系,但是我不知道如何解决这个问题,任何帮助都会受到赞赏。

这里是完整性的代码,但我会推荐JSFiddle链接,我觉得它更容易使用。

THREE.PlayerControls = function (anchor, scene, player, camera, domElement) {

this.walking = false;
this.occ = false;
this.scene = scene;
this.occLastZoom = 0;
this.jumpRelease = true;
this.jumping = false;
this.falling = false;
this.moving = false;
this.turning = false;
this.anchor = anchor;
this.player = player;
this.camera = camera;
this.camera.position.set(0, 8.25, -20);
this.domElement = (domElement !== undefined) ? domElement : document;

this.anchor.add(this.camera);

// API
this.enabled = true;

this.center = new THREE.Vector3(0, 4, 0);

this.userZoom = true;
this.userZoomSpeed = 2.0;

this.userRotate = true;
this.userRotateSpeed = 1.0;

this.minPolarAngle = 0; // radians
this.maxPolarAngle = Math.PI; // radians

this.minDistance = 2;
this.maxDistance = 30;

this.keys = {
    LEFT: 65,
    STRAFFLEFT: 81,
    UP: 87,
    RIGHT: 68,
    STRAFFRIGHT: 69,
    DOWN: 83,
    JUMP: 32,
    SLASH: 191
};

// internals
var scope = this;

var EPS = 0.000001;
var PIXELS_PER_ROUND = 1800;

var rotateStart = new THREE.Vector2();
var rotateEnd = new THREE.Vector2();
var rotateDelta = new THREE.Vector2();

var zoomStart = new THREE.Vector2();
var zoomEnd = new THREE.Vector2();
var zoomDelta = new THREE.Vector2();

var phiDelta = 0;
var thetaDelta = 0;
var scale = 1;

var lastPosition = new THREE.Vector3();

var STATE = {
    NONE: -1,
    ROTATE: 0,
    ZOOM: 1
};
var state = STATE.NONE;
var key_state = [];

// events
var changeEvent = {
    type: 'change'
};


this.rotateLeft = function (angle) {
    thetaDelta -= angle;
};

this.rotateRight = function (angle) {
    thetaDelta += angle;
};

this.rotateUp = function (angle) {
    phiDelta -= angle;
};

this.rotateDown = function (angle) {
    phiDelta += angle;
};

this.zoomIn = function (zoomScale) {
    if (zoomScale === undefined) {
        zoomScale = getZoomScale();
    }
    scale /= zoomScale;
};

this.zoomOut = function (zoomScale) {
    if (zoomScale === undefined) {
        zoomScale = getZoomScale();
    }
    scale *= zoomScale;
};

this.update = function (delta) {
    // detect falling
    if (this.scene.children.length > 0) {
        var originPoint = this.anchor.position.clone();
        var ray = new THREE.Raycaster(originPoint, new THREE.Vector3(0, -1, 0));
        var collisionResults = ray.intersectObjects(this.scene.children.filter(function (child) {
            return child.occ;
        }));
        if (collisionResults.length > 0) {
            if (collisionResults[0].distance < 1.25 && this.falling) {
                this.falling = false;
                this.jumping = false;
            } else if (collisionResults[0].distance > 2 + (this.jumping ? 1 : 0) && !this.falling) {
                this.falling = true;
            }
        }
    }

    // handle movement
    if (!this.falling) {
        if (key_state.indexOf(this.keys.JUMP) > -1 && this.jumpRelease && !this.jumping) {
            // jump
            var lv = this.anchor.getLinearVelocity();
            this.anchor.setLinearVelocity(new THREE.Vector3(lv.x, 15, lv.z));
            this.jumpRelease = false;
            this.jumping = true;
            //jump
        } else if (!this.jumping) {
            // move
            if (key_state.indexOf(this.keys.UP) > -1) {

                var rotation_matrix = new THREE.Matrix4().extractRotation(this.anchor.matrix);

                var speed = this.walking ? 2.5 : 10;
                var force_vector;

                // straffing?
                if (key_state.indexOf(this.keys.STRAFFLEFT) > -1 && key_state.indexOf(this.keys.STRAFFRIGHT) < 0) {
                    force_vector = new THREE.Vector3((2 * speed / 3), 0, (2 * speed / 3)).applyMatrix4(rotation_matrix);
                    this.player.rotation.set(0, Math.PI / 4, 0);
                } else if (key_state.indexOf(this.keys.STRAFFRIGHT) > -1) {
                    force_vector = new THREE.Vector3((-2 * speed / 3), 0, (2 * speed / 3)).applyMatrix4(rotation_matrix);
                    this.player.rotation.set(0, -Math.PI / 4, 0);
                } else {
                    force_vector = new THREE.Vector3(0, 0, speed).applyMatrix4(rotation_matrix);
                    this.player.rotation.set(0, 0, 0);
                }

                this.anchor.setLinearVelocity(force_vector);
                this.moving = true;

                // forward
            } else if (key_state.indexOf(this.keys.DOWN) > -1) {
                var rotation_matrix = new THREE.Matrix4().extractRotation(this.anchor.matrix);

                var speed = this.walking ? -2.5 : -5;
                var force_vector;

                // straffing?
                if (key_state.indexOf(this.keys.STRAFFLEFT) > -1 && key_state.indexOf(this.keys.STRAFFRIGHT) < 0) {
                    force_vector = new THREE.Vector3((-2 * speed / 3), 0, (2 * speed / 3)).applyMatrix4(rotation_matrix);
                    this.player.rotation.set(0, -Math.PI / 4, 0);
                } else if (key_state.indexOf(this.keys.STRAFFRIGHT) > -1) {
                    force_vector = new THREE.Vector3((2 * speed / 3), 0, (2 * speed / 3)).applyMatrix4(rotation_matrix);
                    this.player.rotation.set(0, Math.PI / 4, 0);
                } else {
                    force_vector = new THREE.Vector3(0, 0, speed).applyMatrix4(rotation_matrix);
                    this.player.rotation.set(0, 0, 0);
                }

                this.anchor.setLinearVelocity(force_vector);
                this.moving = true;

                //back
            } else if (key_state.indexOf(this.keys.STRAFFLEFT) > -1) {
                var rotation_matrix = new THREE.Matrix4().extractRotation(this.anchor.matrix);

                var speed = this.walking ? 2.5 : 10;
                var force_vector = new THREE.Vector3(speed, 0, 0).applyMatrix4(rotation_matrix);
                this.player.rotation.set(0, Math.PI / 2, 0);

                this.anchor.setLinearVelocity(force_vector);
                this.moving = true;

                //straff
            } else if (key_state.indexOf(this.keys.STRAFFRIGHT) > -1) {
                var rotation_matrix = new THREE.Matrix4().extractRotation(this.anchor.matrix);

                var speed = this.walking ? 2.5 : 10;
                var force_vector = new THREE.Vector3(-speed, 0, 0).applyMatrix4(rotation_matrix);
                this.player.rotation.set(0, -Math.PI / 2, 0);

                this.anchor.setLinearVelocity(force_vector);
                this.moving = true;

                //straff
            } else if (this.moving) {
                this.player.rotation.set(0, 0, 0);
                this.anchor.setLinearVelocity(new THREE.Vector3(0, 0, 0));
                this.moving = false;
            }

            //turn
            if (key_state.indexOf(this.keys.LEFT) > -1 && key_state.indexOf(this.keys.RIGHT) < 0) {
                this.anchor.setAngularVelocity(new THREE.Vector3(0, 1.5, 0));
                this.turning = true;
                //turning
            } else if (key_state.indexOf(this.keys.RIGHT) > -1) {
                this.anchor.setAngularVelocity(new THREE.Vector3(0, -1.5, 0));
                this.turning = true;
                //turning
            } else if (this.turning) {
                this.anchor.setAngularVelocity(new THREE.Vector3(0, 0, 0));
                this.turning = false;
            }

            //idle
        }

        if (key_state.indexOf(this.keys.JUMP) == -1) {
            this.jumpRelease = true;
        }
    } else {
        //falling
    }

    var position = this.camera.position;
    var offset = position.clone().sub(this.center);

    // angle from z-axis around y-axis
    var theta = Math.atan2(offset.x, offset.z);

    // angle from y-axis
    var phi = Math.atan2(Math.sqrt(offset.x * offset.x + offset.z * offset.z), offset.y);

    theta += thetaDelta;
    phi += phiDelta;

    if ((this.moving || this.turning) && state != STATE.ROTATE) {
        // fix camera rotation
        if (theta < 0) theta -= Math.max(delta, (-1 * Math.PI) + theta);
        else theta += Math.min(delta, Math.PI - theta);

        // fix pitch (should be an option or it could get anoying)
        //phi = 9*Math.PI/24;
    }

    // restrict phi to be between desired limits
    phi = Math.max(this.minPolarAngle, Math.min(this.maxPolarAngle, phi));

    // restrict phi to be betwee EPS and PI-EPS
    phi = Math.max(EPS, Math.min(Math.PI - EPS, phi));

    var radius;
    if (this.occ) {
        this.occLastZoom = Math.max(this.minDistance, Math.min(this.maxDistance, this.occLastZoom * scale));
        radius = this.occLastZoom;
    } else {
        radius = offset.length() * scale;
    }

    // restrict radius to be between desired limits
    radius = Math.max(this.minDistance, Math.min(this.maxDistance, radius));

    // check for objects infront of camera
    var projector = new THREE.Projector();
    var vector = new THREE.Vector3(0, 0, 1);
    projector.unprojectVector(vector, camera);
    var point = new THREE.Vector3(this.anchor.position.x + this.center.x, this.anchor.position.y + this.center.y, this.anchor.position.z + this.center.z);
    var vec = camera.position.clone().sub(vector).normalize()

    var checkray = new THREE.Raycaster(point, vec, this.minDistance, this.maxDistance);
    var checkcollisionResults = checkray.intersectObjects(this.scene.children.filter(function (child) {
        return child.occ;
    }));
    if (checkcollisionResults.length > 0) {
        var min = radius;
        for (var i = 0; i < checkcollisionResults.length; i++) {
            if (min > checkcollisionResults[i].distance) min = checkcollisionResults[i].distance;
        }
        if (min < radius) {
            if (!this.occ) {
                this.occ = true;
                this.occLastZoom = radius;
            }
            radius = min;
        } else {
            this.occ = false;
        }
    }

    offset.x = radius * Math.sin(phi) * Math.sin(theta);
    offset.y = radius * Math.cos(phi);
    offset.z = radius * Math.sin(phi) * Math.cos(theta);

    if (radius < 5) {
        this.player.material.opacity = Math.max(0, radius / 5.0);
        this.center.y = 4 + ((5 - radius) / 2.5);
    } else {
        if (this.player.material.opacity != 1.0) {
            this.player.material.opacity = 1.0;
            this.center.y = 4;
        }
    }

    position.copy(this.center).add(offset);
    this.camera.lookAt(this.center);

    thetaDelta = 0;
    phiDelta = 0;
    scale = 1;

    if (lastPosition.distanceTo(this.camera.position) > 0) {
        this.dispatchEvent(changeEvent);
        lastPosition.copy(this.camera.position);
    }
};

function roundTothree(num) {
    return +(Math.round(num + "e+3") + "e-3");
}

function getZoomScale() {
    return Math.pow(0.95, scope.userZoomSpeed);
}

function onMouseDown(event) {
    if (scope.enabled === false) return;
    if (scope.userRotate === false) return;

    event.preventDefault();

    if (state === STATE.NONE) {
        if (event.button === 0) state = STATE.ROTATE;
    }

    if (state === STATE.ROTATE) {
        rotateStart.set(event.clientX, event.clientY);
    }

    document.addEventListener('mousemove', onMouseMove, false);
    document.addEventListener('mouseup', onMouseUp, false);
}

function onMouseMove(event) {
    if (scope.enabled === false) return;
    event.preventDefault();

    if (state === STATE.ROTATE) {
        rotateEnd.set(event.clientX, event.clientY);
        rotateDelta.subVectors(rotateEnd, rotateStart);
        scope.rotateLeft(2 * Math.PI * rotateDelta.x / PIXELS_PER_ROUND * scope.userRotateSpeed);
        scope.rotateUp(2 * Math.PI * rotateDelta.y / PIXELS_PER_ROUND * scope.userRotateSpeed);
        rotateStart.copy(rotateEnd);
    } else if (state === STATE.ZOOM) {
        zoomEnd.set(event.clientX, event.clientY);
        zoomDelta.subVectors(zoomEnd, zoomStart);
        if (zoomDelta.y > 0) {
            scope.zoomIn();
        } else {
            scope.zoomOut();
        }
        zoomStart.copy(zoomEnd);
    }
}

function onMouseUp(event) {
    if (scope.enabled === false) return;
    if (scope.userRotate === false) return;

    document.removeEventListener('mousemove', onMouseMove, false);
    document.removeEventListener('mouseup', onMouseUp, false);

    state = STATE.NONE;
}

function onMouseWheel(event) {
    if (scope.enabled === false) return;
    if (scope.userZoom === false) return;

    var delta = 0;

    if (event.wheelDelta) { // WebKit / Opera / Explorer 9
        delta = event.wheelDelta;
    } else if (event.detail) { // Firefox
        delta = -event.detail;
    }

    if (delta > 0) {
        scope.zoomOut();
    } else {
        scope.zoomIn();
    }
}

function onKeyDown(event) {
    console.log('onKeyDown')
    if (scope.enabled === false) return;
    switch (event.keyCode) {
        case scope.keys.UP:
            var index = key_state.indexOf(scope.keys.UP);
            if (index == -1) key_state.push(scope.keys.UP);
            break;
        case scope.keys.DOWN:
            var index = key_state.indexOf(scope.keys.DOWN);
            if (index == -1) key_state.push(scope.keys.DOWN);
            break;
        case scope.keys.LEFT:
            var index = key_state.indexOf(scope.keys.LEFT);
            if (index == -1) key_state.push(scope.keys.LEFT);
            break;
        case scope.keys.STRAFFLEFT:
            var index = key_state.indexOf(scope.keys.STRAFFLEFT);
            if (index == -1) key_state.push(scope.keys.STRAFFLEFT);
            break;
        case scope.keys.RIGHT:
            var index = key_state.indexOf(scope.keys.RIGHT);
            if (index == -1) key_state.push(scope.keys.RIGHT);
            break;
        case scope.keys.STRAFFRIGHT:
            var index = key_state.indexOf(scope.keys.STRAFFRIGHT);
            if (index == -1) key_state.push(scope.keys.STRAFFRIGHT);
            break;
        case scope.keys.JUMP:
            var index = key_state.indexOf(scope.keys.JUMP);
            if (index == -1) key_state.push(scope.keys.JUMP);
            break;
    }
}

function onKeyUp(event) {
    switch (event.keyCode) {
        case scope.keys.UP:
            var index = key_state.indexOf(scope.keys.UP);
            if (index > -1) key_state.splice(index, 1);
            break;
        case scope.keys.DOWN:
            var index = key_state.indexOf(scope.keys.DOWN);
            if (index > -1) key_state.splice(index, 1);
            break;
        case scope.keys.LEFT:
            var index = key_state.indexOf(scope.keys.LEFT);
            if (index > -1) key_state.splice(index, 1);
            break;
        case scope.keys.STRAFFLEFT:
            var index = key_state.indexOf(scope.keys.STRAFFLEFT);
            if (index > -1) key_state.splice(index, 1);
            break;
        case scope.keys.RIGHT:
            var index = key_state.indexOf(scope.keys.RIGHT);
            if (index > -1) key_state.splice(index, 1);
            break;
        case scope.keys.STRAFFRIGHT:
            var index = key_state.indexOf(scope.keys.STRAFFRIGHT);
            if (index > -1) key_state.splice(index, 1);
            break;
        case scope.keys.JUMP:
            var index = key_state.indexOf(scope.keys.JUMP);
            if (index > -1) key_state.splice(index, 1);
            break;
        case scope.keys.SLASH:
            scope.walking = !scope.walking;
            break;

    }
}

this.domElement.addEventListener('contextmenu', function (event) {
    event.preventDefault();
}, false);
this.domElement.addEventListener('mousedown', onMouseDown, false);
this.domElement.addEventListener('mousewheel', onMouseWheel, false);
this.domElement.addEventListener('DOMMouseScroll', onMouseWheel, false); // firefox
window.addEventListener('keydown', onKeyDown, false);
window.addEventListener('keyup', onKeyUp, false);
};

THREE.PlayerControls.prototype = Object.create(THREE.EventDispatcher.prototype);


// end player controlls
Physijs.scripts.worker = 'https://rawgithub.com/chandlerprall/Physijs/master/physijs_worker.js';
Physijs.scripts.ammo = 'http://chandlerprall.github.io/Physijs/examples/js/ammo.js';

// standard global variables
var container, scene, camera, renderer, controls;
//var keyboard = new THREEx.KeyboardState();
var clock = new THREE.Clock();

// MAIN //
window.onload = function() {
console.log('loaded')

// SCENE //
scene = new Physijs.Scene();
scene.setGravity(new THREE.Vector3(0, -32, 0));
scene.addEventListener(
    'update',

function () {
    scene.simulate();
});

// CAMERA //
var SCREEN_WIDTH = window.innerWidth,
    SCREEN_HEIGHT = window.innerHeight;
var VIEW_ANGLE = 45,
    ASPECT = SCREEN_WIDTH / SCREEN_HEIGHT,
    NEAR = 1,
    FAR = 1000;
camera = new THREE.PerspectiveCamera(VIEW_ANGLE, ASPECT, NEAR, FAR);

// RENDERER //
renderer = new THREE.WebGLRenderer({
    antialias: true
});
renderer.shadowMapEnabled = true;
// to antialias the shadow
renderer.shadowMapSoft = true;

renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);

container = document.getElementById('container');
container.appendChild(renderer.domElement);

// EVENTS //
//THREEx.WindowResize(renderer, camera);

// LIGHT //
var hemiLight = new THREE.HemisphereLight(0xffffff, 0xffffff, 0.6);
hemiLight.color.setHSL(0.6, 1, 0.6);
hemiLight.groundColor.setHSL(0.095, 1, 0.75);
hemiLight.position.set(0, 500, 0);
scene.add(hemiLight);

var light = new THREE.DirectionalLight(0xffffff, 1);
light.color.setHSL(0.1, 1, 0.95);
light.position.set(-1, 1.75, 1);
light.position.multiplyScalar(50);
light.castShadow = true;
light.shadowMapWidth = 2048;
light.shadowMapHeight = 2048;
light.shadowDarkness = 0.5;
var d = 50;
light.shadowCameraLeft = -d;
light.shadowCameraRight = d;
light.shadowCameraTop = d;
light.shadowCameraBottom = -d;
light.shadowCameraFar = 3500;
light.shadowBias = -0.0001;
light.shadowDarkness = 0.35;
scene.add(light);

// GEOMETRY //
var checkerboard = new THREE.ImageUtils.loadTexture('http://www.cns.nyu.edu/lcv/texture/artificial-periodic/checkerboard.o.jpg');
checkerboard.wrapS = checkerboard.wrapT = THREE.RepeatWrapping;
checkerboard.repeat.set(4, 4);

var checkerboard2 = new THREE.ImageUtils.loadTexture('http://www.cns.nyu.edu/lcv/texture/artificial-periodic/checkerboard.o.jpg');

var cubeMaterial = Physijs.createMaterial(
new THREE.MeshLambertMaterial({
    map: checkerboard2
}),
1.0, // high friction
0.0 // low restitution
);
var cubeGeometry = new THREE.CubeGeometry(10, 5, 10, 1, 1, 1);
var cube = new Physijs.BoxMesh(
cubeGeometry,
cubeMaterial,
1);

cube.position.set(-10, 1, -10);
cube.castShadow = true;
cube.receiveShadow = true;
cube.occ = true;
scene.add(cube);


var cubeMaterial2 = Physijs.createMaterial(
new THREE.MeshLambertMaterial({
    map: checkerboard2
}),
1.0, // high friction
0.0 // low restitution
);
var cubeGeometry2 = new THREE.CubeGeometry(10, 5, 10, 1, 1, 1);
var cube2 = new Physijs.BoxMesh(
cubeGeometry2,
cubeMaterial2,
1);

cube2.position.set(-10, 7, -1);
cube2.castShadow = true;
cube2.receiveShadow = true;
cube2.occ = true;
scene.add(cube2);

var cubeMaterial3 = Physijs.createMaterial(
new THREE.MeshLambertMaterial({
    map: checkerboard2
}),
1.0, // high friction
0.0 // low restitution
);
var cubeGeometry3 = new THREE.CubeGeometry(10, 5, 10, 1, 1, 1);
var cube3 = new Physijs.BoxMesh(
cubeGeometry3,
cubeMaterial3,
1);

cube3.position.set(-10, 13, 8);
cube3.castShadow = true;
cube3.receiveShadow = true;
cube3.occ = true;
scene.add(cube3);

var cone = new Physijs.ConeMesh(
new THREE.CylinderGeometry(0, 5, 4, 30, 30, true),
Physijs.createMaterial(
new THREE.MeshLambertMaterial({
    map: checkerboard2
}),
1.0, // high friction
0.0 // low restitution
),
0);
cone.position.set(0, 2, 0);
scene.castShadow = true;
scene.receiveShadow = true;
cone.occ = true;
scene.add(cone);


// FLOOR //
var floorMaterial = new THREE.MeshLambertMaterial({
    map: checkerboard
});
var floorGeometry = new THREE.PlaneGeometry(100, 100, 1, 1);
var floor = new Physijs.PlaneMesh(floorGeometry, floorMaterial);
floor.rotation.x = -Math.PI / 2;
floor.castShadow = false;
floor.receiveShadow = true;
floor.occ = true;
scene.add(floor);

// SKY //
var skyBoxGeometry = new THREE.CubeGeometry( 1000, 1000, 1000 );
var skyBox = new THREE.Mesh(skyBoxGeometry, new THREE.MeshLambertMaterial({
    color: '#3333bb'
}));
scene.add(skyBox);

// fog must be added to scene before first render
scene.fog = new THREE.FogExp2(0x999999, 0.001);


var bounding = new Physijs.SphereMesh(
new THREE.SphereGeometry(0.75, 4, 4),
Physijs.createMaterial(
new THREE.MeshBasicMaterial({
    color: '#ff0000'
}),
1.0, // high friction
0.0 // low restitution
),
0.1);

var player = new THREE.Mesh(
new THREE.CubeGeometry(1, 6, 1, 1, 1, 1),
new THREE.MeshLambertMaterial({
    color: '#00ff00'
}),
1);
player.position.set(0, 3, 0);

bounding.position.set(10, 0.75, -10);
bounding.add(player);

scene.add(bounding);
bounding.setAngularFactor(new THREE.Vector3(0, 0, 0));
controls = new THREE.PlayerControls(bounding, scene, player, camera, renderer.domElement);

// animation loop / game loop
scene.simulate();
animate();
};

function animate() {
requestAnimationFrame(animate);
render();
update();
}

function update() {
// delta = change in time since last call (in seconds)
var delta = clock.getDelta();
THREE.AnimationHandler.update(delta);
if (controls) controls.update(delta);
}

function render() {
    renderer.render(scene, camera);
}

谢谢!!!

1 个答案:

答案 0 :(得分:4)

好的,我最终自己解决了这个问题,但这是一个非常困难的过程。

我花了很多时间在这上面。我尝试完全重新开始并最终以不同的方式重写我的所有控件对象但没有成功事实上这种方法稍微恶化了。我学到了一些东西:

渲染后更新控件会导致可怕的口吃(或使物理特性变得更糟)。我必须没有注意我放置更新功能的位置,但它需要在渲染之前。

我也开始研究Physijs的演示,看看他们用什么设置让事情顺利进行。这一个具体(http://chandlerprall.github.io/Physijs/examples/body.html

我调整了摩擦和质量设置,我开始使用BoxMesh作为地板而不是飞机,这似乎有助于消除抖动。

最后我改变了一下玩家控制类:

而不是将我的相机直接连接到我的播放器,我开始使用陀螺仪来缓冲旋转。

this.camera_anchor_gyro = new THREE.Gyroscope();
this.camera_anchor_gyro.add(this.camera);
this.anchor.add(this.camera_anchor_gyro);

接下来我想旋转camera_anchor_gyro而不是相机以匹配旋转,这变得非常令人头疼,直到我了解到:http://en.wikipedia.org/wiki/Gimbal_lock

所以我很快就在陀螺仪之后添加了这个:

this.anchor.rotation.order = "YXZ";
this.camera_anchor_gyro.rotation.order = "YXZ";
this.camera.rotation.order = "YXZ";

最后这是我更新的轮换修正逻辑:

if ((this.moving || this.turning) && state != STATE.ROTATE) {
    var curr_rot = new THREE.Euler(0, 0, 0, "YXZ").setFromRotationMatrix(this.camera.matrixWorld).y;
    var dest_rot = new THREE.Euler(0, 0, 0, "YXZ").setFromRotationMatrix(this.anchor.matrixWorld).y;
    var dest_rot = dest_rot + (dest_rot > 0 ? -Math.PI : Math.PI);
    var step = shortestArc(curr_rot,dest_rot)*delta*2;
    this.camera_anchor_gyro.rotation.y += step;//Math.max(-delta, diff);

    // fix pitch (should be an option or it could get anoying)
    //phi = 9*Math.PI/24;
}

我更新了我的小提琴http://jsfiddle.net/nA8SV/2/,这样做效果更好。但仍然存在轻微的口吃问题,但我将不得不继续调查。