在鸟的视图游戏中计算正确的精灵方向图像?(这里的数学可能是速度向量到角度?)

Phi*_*sen 2 math 2d vector angle

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

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

四处搜寻,我发现......

angle = Math.atan2(speed.y, speed.x);
Run Code Online (Sandbox Code Playgroud)

......但不知怎的,我仍然缺少一些东西.

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

非常感谢您的帮助!

Ore*_*ner 10

好问题!我喜欢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);
Run Code Online (Sandbox Code Playgroud)

这将设置segment0到7之间的值.这是一个带有2000个随机点的示例(答案结尾处的完整源代码).使用精灵速度的x,y值,您可以使用段值来拾取适当的精灵图像.

替代文字

Tadaa!

那么这是如何工作的呢? 我们的细分表达确实看起来有点神秘.

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

替代文字

观察二:平面上的直线方程a*x + b*y + c = 0,当变成不等式时,a*x + b*y + c > 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 * s4,2 * (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;
Run Code Online (Sandbox Code Playgroud)

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

(参见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>
Run Code Online (Sandbox Code Playgroud)