1

我正在为我正在开发的游戏开发第三人称角色控制。到目前为止,我对结果感到满意。角色控件有很多简洁的功能,例如:如果一个物体在相机前面,它会向前移动,所以你仍然可以看到角色,但是当我将它旋转到一边然后将我的玩家转开时,相机会严重卡顿从中。我在 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);
}

谢谢!!!

4

1 回答 1

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/并且效果更好。但仍然存在轻微的口吃问题,但我将不得不继续调查。

于 2013-12-05T03:32:43.400 回答