代码之家  ›  专栏  ›  技术社区  ›  Philipp Lenssen

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

  •  2
  • Philipp Lenssen  · 技术社区  · 14 年前

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

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

    四处寻找,我发现…

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

    …但不知怎么的,我还是错过了什么。

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

    非常感谢您的帮助!

    3 回复  |  直到 11 年前
        1
  •  3
  •   tom10    14 年前

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

    (还要注意,您不需要像rcix建议的那样正常化,但是如果您愿意的话也可以。你拥有什么, angle = Math.atan2(speed.y, speed.x); ,应该工作得很好。)

        2
  •  10
  •   Oren Trutner    11 年前

    好问题!我喜欢Tom10的答案(在标记上是+1),但不知道它是否可以在不使用太多三角法的情况下完成。下面是一个简单的解决方案,然后是一个解释。

    //slope is a constant,0.414…;calculate it just one
    var slope=math.tan(math.pi/8);
    
    //对每个x,y点执行此操作
    变量s1=x*斜率+y>0?0:1;
    变量s2=y*坡度+x>0?0:1;
    变量s3=y*斜率-x<0?0:1;
    变量s4=x*斜率-y>0?0:1;
    
    var段=4*s4+2*(s2^s4)+(s1^s2^s3^s4);
    < /代码> 
    
    

    这将在0和7之间设置segment下面是一个有2000个随机点的例子(答案末尾是完整的源代码)。使用sprite速度的x,y值,您可以使用分段值来选取适当的sprite图像。

    < TADAA!

    那么这是如何工作的?oursegmentexpression does look a bit cryptic.

    观察一:我们要将围绕该点的圆分割成8段等角尺寸。360/8=每段45度。8个分段中的4个位于X和Y轴两侧的其中一个的中心,每个分段以45/2=22.5度的角度进行切割。

    观察二:平面上直线的方程,a*x+b*y+c=0,当变成不等式时,a*x+b*y+c>0可用于测试A点位于哪一侧。我们的四条线都穿过原点(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*棕褐色(22.5)-Y>0; x*tan(22.5)+y>0; Y*棕褐色(22.5)-X>0; Y*棕褐色(22.5)+X>0

    测试一个给定点的不等式可以让我们知道它所在的每一行的每一面:

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

    按顺序:4*s4,2*(s2^s4)and the sum4*s4+2*(s2^s4)

    (符号^是javascript xor运算符。)

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

    额外学分:Can we tweak the calculation to use only integer algorithm?是--如果已知xy是整数,我们可以将不等式的两边乘以某个常数(和舍入),从而得到完全整数的数学。(然而,在数字总是双精度浮点的javascript上,这将丢失。):

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

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

    (请参阅jsbin上的实时演示)

    <html>
    &头;
    <style type=“text/css”>
    .dot位置:绝对;字体:10px Arial
    .d0颜色:ff0000;
    .d1颜色:ffbf00;
    .d2颜色:7fcc00;
    .d3颜色:00ff7f;
    .d4颜色:00ffff;
    .d5颜色:5555ff;
    .d6颜色:af00ff;
    .d7颜色:ff00bf;
    &风格/风格;
    <script type=“text/javascript”src=“http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js”></script>
    <script type=“text/javascript”>
    $(函数){
    var$canvas=美元(“canvas”);
    var canvassize=300;
    VaR计数=2000;
    var slope=math.tan(math.pi/8);
    
    $canvas.css(宽度:拉票,高度:拉票);
    对于(var i=0;i<count;++i){
    
    //生成随机点
    var x=math.random()-0.5;
    变量y=math.random()-0.5;
    
    //画出我们的点
    var$point=$(“<DIV class='dot'></DIV>”)
    CSS({)
    左:数学楼层((X+0.5)*拉票)-3,
    顶部:数学楼层((Y+0.5)*拉票)-6)
    .appendto($canvas);
    
    //找出我们的观点所在的部分
    变量s1=x*斜率+y>0?0:1;
    变量s2=y*坡度+x>0?0:1;
    变量s3=y*斜率-x<0?0:1;
    变量s4=x*斜率-y>0?0:1;
    var段=4*s4+2*(s2^s4)+(s1^s2^s3^s4);
    
    //修改点的HTML内容和颜色
    //(通过其CSS类)来指示其段
    美元点
    文本(段)
    .addclass(“d”+段);
    }
    (});
    & /脚本& GT;
    和/头& GT;
    和身体;
    <div id=“canvas”style=“position:absolute;border:1px solid blue”>
    &L/DIV & GT;
    和/身体;
    & lt//html & gt;
    < /代码> 
    但不知道是否可以不用太多的三角学来完成。下面是一个简单的解决方案,然后是一个解释。

    // 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个随机点的例子(答案末尾是完整的源代码)。使用sprite速度的x,y值,可以使用分段值来选取合适的sprite图像。

    alt text

    塔达亚!

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

    观察一:我们要将围绕点的圆分割成8段等角尺寸。360/8=每段45度。8个分段中的4个位于X轴和Y轴的其中一侧的中心,每个分段的切割角度为45/2=22.5度。

    alt text

    观察二平面上直线的方程,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*棕褐色(22.5)-Y>0; x*tan(22.5)+y>0; Y*棕褐色(22.5)-X>0; Y*棕褐色(22.5)+X>0

    测试一个给定点的不等式可以让我们知道它所在的每一行的每一面: alt text alt text

    alt text alt text

    观察三:我们可以结合测试结果来获得所需的段号模式。这是一个视觉分解:

    顺序:4 * s4,2 * (s2 ^ s4)和和4 * s4 + 2 * (s2 ^ s4) alt text alt text alt text

    (符号^是javascript xor运算符。)

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

    额外贷款:我们能把计算调整为只使用整数算术吗?是的--如果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>
    
        3
  •  0
  •   RCIX    14 年前

    你走对了。规范化你的速度向量(首先检查两个组件都是0),调用它上面的atan2,然后将得到的弧度值转换为某种友好的方向枚举或者你可以用来选择正确的sprite的东西。