我正在尝试使用游戏手柄在 Three.js 中使用第一人称射击游戏风格的控件来旋转相机。
浏览器检测到游戏手柄并识别其输入,但相机的旋转顺序错误。当我在相机的局部 Y 轴上旋转时,它也会考虑局部 X 旋转,这是不需要的。
我似乎遇到了与这个家伙相同的问题,但他的问题已使用 Three.js r54 解决,而我正在使用 r60。他设置了
camera.eulerOrder = "YXZ";
来让它工作,但当前的等效 camera.rotation.order = "YXZ";
似乎对我不起作用。
我知道 Three.js 的内置“FirstPersonControls”类,但它不适合我,因为它不接受控制器输入,而且稍后在其中塞入其他非移动控件会很混乱。我也知道 gamepad.js,但没有兴趣使用它。
有人可以帮忙吗?
我的轮换代码:
function pollGamepad()
{
gamepad = navigator.webkitGetGamepads()[0];
//Rotation
if(gamepad.axes[3] > 0.20)
{
camera.rotateX(-gamepad.axes[3] * 0.02);
}
if(gamepad.axes[3] < -0.20)
{
camera.rotateX(-gamepad.axes[3] * 0.02);
}
if(gamepad.axes[2] < -0.20)
{
camera.rotateY(-gamepad.axes[2] * 0.02);
}
if(gamepad.axes[2] > 0.20)
{
camera.rotateY(-gamepad.axes[2] * 0.02);
}
}
PointerLockControls
使用的方法是创建对象的层次结构:yawObject
包含pitchObject
包含camera
。然后,水平鼠标(或操纵杆)移动将改变偏航对象的 Y 旋转,垂直鼠标(或操纵杆)移动将改变俯仰对象的 X 旋转,而相机的旋转将保持默认值(0, 0, -1)
。这只是手动模拟 Euler YXZ
排序,但它可能更适合您。如果您需要整体旋转,它确实会造成一些尴尬。
在我最近编写的自定义控制器中,我通过将相机
add()
设置为单个父对象并将父对象的欧拉阶设置为YXZ
来实现相同的结果。我不记得为什么这比直接将其设置在相机上效果更好,但确实如此。
我自己也遇到过这个问题,我想出了一个简单的解决方案。在执行 y 旋转之前,始终将相机的 x 旋转重置为零。然后,恢复 x 旋转。所以:
// Rotate camera Fps style
function rotateCamera(dx,dy){
//store previous x rotation
var x = camera.rotation.x;
//reset camera's x rotation.
camera.rotateX(-x);
//rotate camera on y axis
camera.rotateY(dy);
//check if we are trying to look to high or too low
if ( Math.abs( dx + x ) > Math.Pi/2 - 0.05) {
camera.rotateX(x);
else
camera.rotateX(x+dx);
//reset z rotation. Floating point operations might change z rotation during the above operations.
camera.rotation.z = 0;
}
我按下了每个按钮,直到它起作用,这是弹出的代码
var controlers = [];
controlers[0] = 0;
controlers[1] = 0;
controlers[2] = 0;
controlers[3] = 0;
controlers[4] = 0;
controlers[5] = 0;
controlers[6] = 0;
controlers[7] = 0;
let controllerIndex = null;
window.addEventListener("gamepadconnected", (event) => {
const gamepad = event.gamepad;
controllerIndex = gamepad.index;
console.log("connected");
});
window.addEventListener("gamepaddisconnected", (event) => {
controllerIndex = null;
console.log("disconnected");
});
function handleButtons(buttons) {
for (let i = 0; i < buttons.length; i++) {
const button = buttons[i];
const buttonElement = document.getElementById(`controller-b${i}`);
const selectedButtonClass = "selected-button";
if (buttonElement) {
if (button.value > 0) {
buttonElement.classList.add(selectedButtonClass);
buttonElement.style.filter = `contrast(${button.value * 150}%)`;
} else {
buttonElement.classList.remove(selectedButtonClass);
buttonElement.style.filter = `contrast(100%)`;
}
}
}
}
function updateStick(elementId, leftRightAxis, upDownAxis) {
const multiplier = 25;
const stickLeftRight = leftRightAxis;
const stickUpDown = upDownAxis;
camera.object = camera;
camera.target = new THREE.Vector3( 0, 0, 0 );
camera.mouseX = 0;
camera.mouseY = 0;
camera.lat = 0;
camera.lon = 360;
camera.phi = 0;
camera.theta = 0;
controlers[4] = (stickLeftRight ) ;
controlers[5] = (stickUpDown ) ;
camera.lon += stickLeftRight * 10 ;
camera.lat -= stickUpDown * 10 ; // * this.invertVertical?-1:1;
camera.lat = Math.max( -15, Math.min( 15, camera.lat ) );
camera.phi = ( 90 - camera.lat ) * Math.PI / 180;
camera.theta = camera.lon * Math.PI / 360;
var targetPosition = camera.target,
position = camera.object.position;
targetPosition.x = position.x + 100 * Math.cos( camera.theta );
targetPosition.y = position.y + 200 * Math.cos( camera.phi );
targetPosition.z = position.z + 100 * Math.sin( camera.phi ) * Math.sin( camera.theta );
var stickLeftRight_a = ( (stickLeftRight) * 100);
var stickUpDown_a = ( (stickUpDown) * 100);
if( stickLeftRight_a < -87 || stickLeftRight_a > 87 || stickUpDown_a < -87 || stickUpDown_a > 87 ){
if( stickLeftRight_a > -87 && stickLeftRight_a < 87 ){
camera.lon += stickLeftRight ;
camera.lat -= stickUpDown ; // * tstickLeftRight_as.invertVertical?-1:1;
camera.lat = Math.max( -15, Math.min( 15, camera.lat ) );
camera.phi = ( 90 - camera.lat ) * Math.PI / 180;
camera.theta = camera.lon * Math.PI / 360;
var targetPosition = camera.target,
position = camera.object.position;
targetPosition.x = position.x + 100 * Math.cos( camera.theta );
targetPosition.y = position.y + 200 * Math.cos( camera.phi );
targetPosition.z = position.z + 100 * Math.sin( camera.phi ) * Math.sin( camera.theta );
camera.lookAt(targetPosition);
}
if( stickLeftRight_a > 87 ){
controlers[6] = controlers[6] + 0.01;
if( controlers[6] > 360 ){
controlers[6] = 0;
}
camera.rotation.y -= controlers[6];
camera.rotation.z = 0 ;
camera.rotation.x = 0 ;
}
if( stickLeftRight_a < -87 ){
controlers[6] = controlers[6] - 0.01;
if( controlers[6] < 0 ){
controlers[6] = 360;
}
camera.rotation.y += controlers[6];
camera.rotation.z = 0 ;
camera.rotation.x = 0 ;
}
}
}
}
}
function handleSticks(axes) {
updateStick("controller-b10", axes[0], axes[1]);
updateStick("controller-b11", axes[2], axes[3]);
}
function gameLoop() {
if (controllerIndex !== null) {
const gamepad = navigator.getGamepads()[controllerIndex];
handleButtons(gamepad.buttons);
handleSticks(gamepad.axes);
}
requestAnimationFrame(gameLoop);
}
gameLoop();