【问题标题】:Calculate correct sprite direction image in bird's view game? (Math here might be speed vector to degrees angle?)在鸟瞰游戏中计算正确的精灵方向图像? (这里的数学可能是速度矢量到度角?)
【发布时间】:2010-09-08 01:50:30
【问题描述】:

背景:我的鸟瞰 JavaScript 游戏中的每个精灵都有 8 张图像,分别代表顶部、右上角、右侧、右下等,具体取决于玩家的太空船速度。

问题:给定 sprite.speed.x 和 sprite.speed.y 的值(例如可能是 4 和 -2.5,或 2 和 0),我如何获得正确的角度(以度为单位)?给定这个角度,我可以查找哪个度数值代表哪个精灵图像。或者也许有更简单的方法。 (目前我只是使用类似“如果 x 低于零使用左图像”等,这将导致几乎所有时间都使用对角线图像。)

四处寻找,我发现...

angle = Math.atan2(speed.y, speed.x);

...但不知何故,我仍然错过了一些东西。

PS:零速度可以忽略,这些精灵只会使用最后一个有效的方向图像。

非常感谢您的帮助!

【问题讨论】:

    标签: math 2d vector angle


    【解决方案1】:

    好问题!我喜欢 tom10 的回答(在标记上,+1),但想知道是否可以在没有太多三角函数的情况下完成。这是一个简短的解决方案,然后是解释。

    // slope is a constant, 0.414...; calculate it just once
    var slope = Math.tan(Math.PI/8);
    
    // do this for each x,y point
    var s1 = x * slope + y > 0 ? 0 : 1;
    var s2 = y * slope + x > 0 ? 0 : 1;
    var s3 = y * slope - x < 0 ? 0 : 1;
    var s4 = x * slope - y > 0 ? 0 : 1;
    
    var segment = 4 * s4 + 2 * (s2 ^ s4) + (s1 ^ s2 ^ s3 ^ s4);
    

    这会将segment 的值设置在 0 到 7 之间。这是一个包含 2000 个随机点的示例(答案末尾有完整的源代码)。使用精灵速度的 x,y 值,您可以使用分段值来拾取适当的精灵图像。

    多多!

    那么它是如何工作的呢?我们的segment表达式看起来确实有点神秘。

    观察一:我们要将围绕该点的圆分成8个角度尺寸相等的段。 360/8 = 每段 45 度。 8 段中的 4 段以 x 轴和 y 轴两侧之一为中心,分别以 45/2 = 22.5 度切割。

    观察二:平面上直线的方程,a*x + b*y + c = 0,转化为不等式后,a*x + b*y + c &gt; 0可以用来测试点在直线的哪一边.我们所有的四条线都穿过原点(x=0,y=0),因此强制 c=0。此外,它们都与 x 或 y 轴成 22.5 度角。这让我们得到了四线方程:

    y = x * tan(22.5); y = -x * tan(22.5); x = y * tan(22.5); x = -y * tan(22.5)

    变成我们得到的不等式:

    x * tan(22.5) - y > 0; x * tan(22.5) + y > 0; y * tan(22.5) - x > 0; y * tan(22.5) + x > 0

    测试给定点的不等式让我们知道它所在的每条线的每一侧:

    观察三:我们可以结合测试结果得到我们想要的段数模式。这是一个视觉细分:

    依次为:4 * s42 * (s2 ^ s4) 和总和4 * s4 + 2 * (s2 ^ s4)

    (^ 符号是 Javascript XOR 运算符。)

    这里是s1 ^ s2 ^ s3 ^ s4,首先是单独的,然后添加到4 * s4 + 2 * (s2 ^ s4)

    额外功劳:我们可以调整计算以仅使用整数算术吗?是的——如果已知 xy 是整数,我们可以将不等式的两边乘以某个常数(并四舍五入),从而得到完全整数数学。 (但是,这在 Javascript 上会丢失,其数字始终是双精度浮点数。):

    var s1 = x * 414 + y * 1000 > 0 ? 0 : 1;
    var s2 = y * 414 + x * 1000 > 0 ? 0 : 1;
    var s3 = y * 414 - x * 1000 < 0 ? 0 : 1;
    var s4 = x * 414 - y * 1000 > 0 ? 0 : 1;
    

    以上示例的完整源代码:(只需将其放入新的 html 文件中,然后在任何浏览器中打开)

    (see as a live demo on jsbin)

    <html>
        <head>
            <style type="text/css">
                .dot { position: absolute; font: 10px Arial }
                .d0 { color: #FF0000; }
                .d1 { color: #FFBF00; }
                .d2 { color: #7fcc00; }
                .d3 { color: #00FF7F; }
                .d4 { color: #00FFFF; }
                .d5 { color: #5555FF; }
                .d6 { color: #aF00FF; }
                .d7 { color: #FF00BF; }
            </style>
            <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
            <script type="text/javascript">
                $(function() {
                    var $canvas = $("#canvas");
                    var canvasSize = 300;
                    var count = 2000;
                    var slope = Math.tan(Math.PI/8);
    
                    $canvas.css({ width: canvasSize, height: canvasSize });
                    for (var i = 0; i < count; ++i) {
    
                        // generate a random point
                        var x = Math.random() - 0.5;
                        var y = Math.random() - 0.5;
    
                        // draw our point
                        var $point = $("<div class='dot'></div>")
                            .css({
                                left: Math.floor((x + 0.5) * canvasSize) - 3,
                                top:  Math.floor((y + 0.5) * canvasSize) - 6 })
                            .appendTo($canvas);
    
                        // figure out in what segment our point lies
                        var s1 = x * slope + y > 0 ? 0 : 1;
                        var s2 = y * slope + x > 0 ? 0 : 1;
                        var s3 = y * slope - x < 0 ? 0 : 1;
                        var s4 = x * slope - y > 0 ? 0 : 1;
                        var segment = 4 * s4 + 2 * (s2 ^ s4) + (s1 ^ s2 ^ s3 ^ s4);
    
                        // modify the point's html content and color
                        // (via its CSS class) to indicate its segment
                        $point
                            .text(segment)
                            .addClass("d" + segment);
                    }
                });
            </script>
        </head>
        <body>
            <div id="canvas" style="position: absolute; border: 1px solid blue">
            </div>
        </body>
    </html>
    

    【讨论】:

    • 只是说我对这个答案的质量和深度感到震惊。
    • 我想将两个答案标记为正确,但 StackOverflow 不允许!
    【解决方案2】:

    您的建议完全正确!请注意,Math.atan2 的结果是以弧度表示的,您可能更熟悉度数;您可以使用angle_degrees = angle*(180./pi) 进行转换。

    (另请注意,您不需要按照 RCIX 的建议进行标准化,但如果您愿意也可以。您所拥有的 angle = Math.atan2(speed.y, speed.x); 应该可以正常工作。)

    【讨论】:

      【解决方案3】:

      你在正确的轨道上。标准化您的速度向量(首先检查两个分量是否为 0),对其调用 atan2,然后将获得的弧度值转换为某种友好的方向枚举或可用于选择正确精灵的东西。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-01-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-06-16
        • 2011-05-24
        • 2013-10-02
        • 2021-04-05
        相关资源
        最近更新 更多