【问题标题】:Mouse controlled first person movement JS鼠标控制的第一人称动作JS
【发布时间】:2013-08-15 11:14:06
【问题描述】:

我正在尝试使用鼠标实现第一人称动作。 我确实让它与键盘一起使用,但我在使用鼠标实现它时遇到了困难,因为移动到特定的一侧不是那么清楚(即向左移动可以包括向上或向下移动)。 我想使用matrix3d 来接收位置的更改值。

编辑#2这是jsfiddle

编辑我已经粘贴了我设法解决的新代码:

$(document).on('mousemove', function (e) {
        var MOVE = 10; // how much to move
        var XTURN = 1; // how much to rotate
        var YTURN = 1; // how much to rotate
        var transformer, origMat, translationMatrix, result;
        transformer = document.getElementById("transformer");

        if ($.browser.webkit)
            origMat = new WebKitCSSMatrix(window.getComputedStyle(transformer).webkitTransform);

        //turn left
        if (e.pageX < xPrev) {
            if (XTURN < 0) {
                XTURN *= -1;
            }
            xPrev = e.pageX;
        //turn right
        } else {
            if (XTURN > 0) {
                XTURN *= -1;
            }
            xPrev = e.pageX;

        }
        //look up
        if (e.pageY < yPrev) {
            if (YTURN < 0) {
                YTURN *= -1;
            } 
            yPrev = e.pageY;
        //look down
        } else {
            if (YTURN > 0) {
                YTURN *= -1;
            }
            yPrev = e.pageY;
        }

        translationMatrix = new WebKitCSSMatrix("matrix3d(" + cos(XTURN).toFixed(10) + ",0," + sin(XTURN).toFixed(10) + ",0,0,"+ cos(-YTURN).toFixed(10) +","+ sin(YTURN).toFixed(10) +",0, " + sin(-XTURN).toFixed(10) + ","+ sin(-YTURN).toFixed(10) +"," + cos(XTURN).toFixed(10) + ",0,0,0,0,1)");

        transformer.style.webkitTransform = translationMatrix.multiply(origMat).toString();
    });

如您所见(对单行矩阵表示抱歉)我正在说明 X 和 Y 旋转在同一个矩阵变化上的变化然后提交它,现在的问题是 cos(XTURN).toFixed(10) 可以相关到 X 和 Y 旋转,所以你可以看到它有效但并不完美。 将不胜感激任何提示/想法。

P.S 我不想使用Pointer Lock API,尽管它很棒,因为我希望它支持最大数量的浏览器。

【问题讨论】:

  • 如果您可以使用,请在答案中发布您更改的内容,或删除问题(答案会更好)。
  • 它没有按应有的方式工作,因此没有解决,因此发布答案将是错误的做法。我已经发布了自第一个问题以来发生的变化的编辑。
  • 哦,对不起,我误会了。我以为你说你解决了。
  • 你看过quaternions 吗?如果您有兴趣,我可以尝试使用此技术找到我的一些旧代码。这些确实更容易操作,计算量更少。

标签: javascript jquery css


【解决方案1】:

纯 JavaScript 大多比库好(除非它是“代码少做多”的东西),
因为你可以理解你的代码的真正作用。

这是我的整个 JavaScript 代码:

var velocity = 0.5;

document.onmousemove = function(e) {
    var angleX = e.pageY * velocity * Math.PI / 180;
    var angleY = e.pageX * velocity * Math.PI / 180;
    document.getElementById('transformer').style.webkitTransform = 'matrix3d(' + Math.cos(-angleY) + ',0,' + Math.sin(-angleY) + ',0,' + (Math.sin(angleX)*Math.sin(-angleY)) + ',' + Math.cos(angleX) + ',' + (-Math.sin(angleX)*Math.cos(-angleY)) + ',0,' + (-Math.cos(angleX)*Math.sin(-angleY)) + ',' + Math.sin(angleX) + ',' + (Math.cos(angleX)*Math.cos(-angleY)) + ',0,0,0,0,1)';
};

还有this is the fiddle

有效!

(我什至使用 Pointer Lock API 做了一个示例:fiddle(单击方块开始)


解释:

首先,一个速度变量可以轻松设置旋转速度。
然后,设置了两个旋转变量的mousemove 事件。
最后一行是根据要求从rotateXrotateY 转换转换为matrix3dThis Stackoverflow question 帮助我找到了以下解决方案。


rotateX(angleX) 等于以下矩阵:

1               0               0               0


0               cos(angleX)     -sin(angleX)    0


0               sin(angleX)     cos(angleX)     0


0               0               0               1

rotateY(angleY) 等于以下矩阵:

cos(angleY)     0               sin(angleY)     0


0               1               0               0


-sin(angleY)    0               cos(angleY)     0


0               0               0               1

要同时使用它们,您需要将两个矩阵相乘。 所以我写了一个小的 JavaScript 工具来计算我需要做的计算来得到这个乘法的结果。

结果:

cos(angleY)               sin(angleX)*sin(angleY)   cos(angleX)*sin(angleY)   0



0                         cos(angleX)               -sin(angleX)              0



-sin(angleY)              sin(angleX)*cos(angleY)   cos(angleX)*cos(angleY)   0



0                         0                         0                         1

这就是将rotateXrotateY 转换为matrix3d 的方法。

希望对你有帮助:)

【讨论】:

  • 这真是一个很好的解释,让我觉得这个答案更好。 (即使@IcanDivideBy0 有相同的答案但不太详细)。我想知道其他答案中提到的四元数的使用是否以不同的方式为解决方案做出了贡献。 (不仅仅是更好的代码)
【解决方案2】:

我不太清楚你的高级目标是什么。听起来您正在尝试在 JS 和 CSS 中实现类似 Counterstrike 的游戏。太棒了!对于这个答案的其余部分,我将假设您正在尝试做类似的事情。

实际上,您必须使用 Pointer Lock API。否则,您将无法仅通过向左移动鼠标来转身。你会碰到浏览器窗口的边缘并停止转动。浏览器支持不是很好,但它对游戏玩家来说是更好的体验!

为了使用 CSS 变换来渲染您的世界,您需要执行一系列复杂的变换来为游戏世界中每个可见对象的每一侧生成矩阵。这是因为浏览器的视角总是直接沿着 Z 轴看。因此,为了“围绕”观看者的眼睛制作动画,您必须平移和旋转它们。经过一番摸索,我得出结论,在 CSS 中进行所有转换都非常缓慢(而且复杂!)。但不要害怕,还有另一种方法! WebGL 或 Canvas 助您一臂之力!

看看 Isaac Sukin 的游戏Nemesis。这是一个很好的例子,他写了一个tutorial 来想出类似的东西!它所基于的库 Three.js 使用非常广泛,并且具有非常易于理解的 API。它几乎消除了所有困难的部分,让您只需制作一个 3D 世界!

祝游戏好运!

【讨论】:

  • 你链接的游戏Nemsis虽然没有指针锁定,但也不是太难控制。你转动你的角色不是用鼠标移动,而是用鼠标偏离窗口中心。因此,当鼠标位于屏幕左侧时,您继续向左转。您还可以在指针所在的位置拍摄,而不是直接向前拍摄。指针锁会更好,如果 Shahar 的游戏检测到是否支持指针锁并使用它会很好,但我不会称之为必需
  • 感谢您的详细回答,但它并没有真正解决我的问题,我将解释:我可以使用鼠标移动分别实现 X 旋转和 Y 旋转,与 Nemesis 相同(那里只有 Y 旋转),但我希望能够并结合这 2 个动作,这样用户就会体验到 FPS 风格的环顾四周,在 Z 轴上的移动将通过键盘完成,所以那里没有问题。关于 WebGL,我同意这真是太棒了,但与我的具体项目或目标无关,而且由于我没有使用 Canvas 创建的现有环境,这对我也没有帮助。
  • 哦,好的。如果您只想同时在两个方向上旋转视图,则应该对 matrix3d() 进行两次转换。或围绕 (或类似)旋转一次。看看Mozilla documentation for rotate3d(),然后使用那个矩阵变换而不是你拥有的那个。
  • @tangphillip 我确实知道 rotate3d() 但正如您从我的问题中看到的那样,我想使用 matrix3d,因为我想在更改后绘制值,因为我将它们用于其他放置在“世界”中的物体。如果有办法将使用 rotate3d 所做的更改转换为 matrix3d 值,我会将其视为一种可能的解决方案,但我找不到执行此类值转换的方法。
  • @ShaharGalukman 如 Mozilla 文档中所列,rotate3d() 对应的矩阵运算为: 1+(1-cos(a))(x^2-1) // z sin(a)+xy(1-cos(a)) // -ysin(a)+xz(1-cos(a)) // 0 ||| -zsin(a)+xy(1-cos(a)) // 1+(1-cos(a))(y^2-1) // xsin(a)+ yz(1-cos(a)) // 0 ||| ysin(a) + xz(1-cos(a)) // -xsin(a)+yz(1-cos(a)) // 1+(1-cos(a) ))(z^2-1) // 0 ||| 0 // 0 // 0 // 1
【解决方案3】:

使用四元数确实更容易。我在Google closure library 找到了一个实现,所以我做了一个例子(另外,检查the jsFiddle):

goog.require('goog.vec.Quaternion');

var velocity = 0.5;

var lastX = null;
var lastY = null;
var angleX = 0;
var angleY = 0;

$(document).on('mousemove', function (e) {
    if (lastX == null) lastX = e.pageX;
    if (lastY == null) lastY = e.pageY;

    angleX += (e.pageX - lastX) * velocity * Math.PI / 180;
    angleY += (e.pageY - lastY) * velocity * Math.PI / 180;

    lastX = e.pageX;
    lastY = e.pageY;

    var quat = goog.vec.Quaternion.concat(
        goog.vec.Quaternion.fromAngleAxis(angleX, [0, 1, 0], []),
        goog.vec.Quaternion.fromAngleAxis(-angleY, [1, 0, 0], []), []);

    var matrix = goog.vec.Quaternion.toRotationMatrix4(quat, []);

    $("#transformer").css({
        webkitTransform: "matrix3d(" + matrix.join(",") + ")"
    });
});

【讨论】:

  • 不知道这个很棒的库和使用它的解决方案非常优雅。尚未对其进行测试,但它似乎解决了我遇到的问题,并且作为奖励,我了解了这个库。您能否进一步解释一下 Quaternion 的用法以及它如何解析为 matrix3d,以便完全理解其背后的代码和数学,谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-09-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-16
相关资源
最近更新 更多