计算角度差2D游戏的JS函数

Posted

技术标签:

【中文标题】计算角度差2D游戏的JS函数【英文标题】:JS function to calculate angle difference 2D gaming 【发布时间】:2017-07-11 17:28:56 【问题描述】:

我编写了一些代码只是为了弄清楚如何计算 2D 游戏 AI 的角度差。我添加了一张图片(示例问题),我正在编写的函数接受角度 A 和 B 的输入,并且应该返回一个方向!返回值应该在 -pi 到 +pi 之间,或者在 -180 到 180 度之间。

顺便说一句,代码主要是:|但在某些时候它失败了。代码可以完全复制到 .html 文件中并运行以进行测试。

example problem pic

代码:

<!DOCTYPE html>
<html style="width:100%;">
    <head>
        <meta charset="utf-8">
        <title>Angle</title>
        <style type="text/css">
            body 
                height: 600px;
                background-color: #EEEEEE;
            
            #main 
                text-align: center;
                /*border: 1px solid #000000;*/
            
        </style>
    </head>
    <body>

        <center>
            <h3>Canvas</h3>
            <canvas id="main"  >
                Your browser does not support the HTML5 canvas tag.
            </canvas>
        </center>

        <script type="text/javascript">

window.onload=function()
    "use strict";


    var canvas = document.getElementById('main');
    var ctx = canvas.getContext('2d');
    var font_size = 18;
    ctx.font = font_size + 'px Courier New'; // Courier New / Lucida Console
    var clearCanvas = function(c)
        if (c)
            c.updateFill();
            ctx.fillRect(0, 0, canvas.width, canvas.height);
        else
            ctx.clearRect(0, 0, canvas.width, canvas.height);
        
    ;


    // math extras
    Math.M_PI_DB_180 = 0.01745329251994;
    Math.M_180_DB_PI = 57.2957795130823;
    Math.M_2PI = 6.28318530717959;
    Math.M_PI = 3.14159265358979;
    Math.M_PI_DB_2 = 1.57079632679489;

    Math.DEG2RAD = function(d) return (d * Math.M_PI_DB_180); ;
    Math.RAD2DEG = function(r) return (r * Math.M_180_DB_PI); ;


    Math._fmod_precesion = 1000000;
    Math.fmod = function(a, b)
        return (Math.round(a * Math._fmod_precesion) % Math.round(b * Math._fmod_precesion)) / Math._fmod_precesion;
    ;

    Math.roundp = function(value, precision)
        var p = Math.pow(10, precision);
        return Math.round(value * p) / p;
    ;


    Math.wrapDegree = function(value)
        if (value > 360) value = Math.fmod(value, 360);
        if (value < -360) value = Math.fmod(value, 360);
        if (value < 0) value += 360;
        return value;
    ;
    Math.wrapRadian = function(value)
        if (value > Math.M_2PI) value = Math.fmod(value, Math.M_2PI);
        if (value < -Math.M_2PI) value = Math.fmod(value, Math.M_2PI);
        if (value < 0) value += Math.M_2PI;
        return value;
    ;

    // gives -179.9 to 179.9
    Math.angleDifference = function(a1, a2)
        var diff = a1 - a2;
        if (diff > Math.M_PI) diff = -(Math.M_2PI - diff);
        if (diff < -Math.M_PI) diff = (Math.M_2PI + diff);
        if (diff > -0.01 && diff < 0.01) diff = 0; // fix
        return diff;
    ;

    Math.angleWithinFOV = function(angle, fov)
        var half = fov / 2;
        return angle < half && angle > -half;
    ;



    // vector 2D class
    var vec2 = function(x, y)
        this.x = function() return this._x; ;
        this.y = function() return this._y; ;
        this.set = function(x, y)
            this._x = typeof x == 'number' ? x : 0;
            this._y = typeof y == 'number' ? y : 0;
        ;
        this.set(x, y);

        // extras

        this.add = function(v) this._x += v.x(); this._y += v.y(); ;
        this.sub = function(v) this._x -= v.x(); this._y -= v.y(); ;
        this.mul = function(v) this._x *= v.x(); this._y *= v.y(); ;

        this.lengthMath = function()
            return Math.sqrt( Math.pow(this._x, 2) + Math.pow(this._y, 2) );
        ;

        this.normalizedCopy = function()
            var len = this.lengthMath();
            return len != 0 ? new vec2( this._x / len, this._y / len ) : null;
        ;


        this.clone = function() return new vec2(this._x, this._y); ;

        this.next = function(angle, distance)
            return new vec2( this._x + (distance * Math.sin(angle)), this._y + (distance * Math.cos(angle)) );
        ;


        this.slopeAngle = function(v)
            return Math.wrapRadian( Math.atan2( this._x - v.x(), this._y - v.y() ) + Math.M_PI );
        ;

    ;

    // color class (updateFill, updateStroke) (colorFill, colorStroke)
    var ccolor = function(c)
        this._color = c||'#000000';
        this.updateFill = function(draw)
            ctx.fillStyle = this._color;
            if(draw)ctx.fill();
        ;
        this.updateStroke = function(draw)
            ctx.strokeStyle = this._color;
            if(draw)ctx.stroke();
        ;
    ;


    // draw fuunctions
    var _text = function(v, t, c)
        c.updateFill();
        ctx.fillText(t, v.x(), v.y() + font_size - (font_size * 0.4));
    ;
    var _line = function(v1, v2, c)
        ctx.moveTo(v1.x(), v1.y());
        ctx.lineTo(v2.x(), v2.y());
        c.updateStroke(true);
    ;
    var _circle = function(v, radius, c, shouldFill)
        ctx.beginPath();
        ctx.arc(v.x(), v.y(), radius, 0, Math.M_2PI);
        if (shouldFill)
            c.updateFill(true);
        else
            c.updateStroke(true);
    ;

    var _viewcone = function(v, radius, c, r1, r2)
        ctx.beginPath();
        ctx.arc(v.x(), v.y(), radius, r1, r2);
        c.updateStroke(true);
    ;


    // mouse move store
    var _mousePos = new vec2();
    canvas.addEventListener('mousemove', function(e)
        var rect = canvas.getBoundingClientRect();
        _mousePos.set(e.clientX - rect.left, e.clientY - rect.top);
    );



    // colors
    var _red    = new ccolor('#FF0000');
    var _green  = new ccolor('#00FF00');
    var _blue   = new ccolor('#0000FF');
    var _black  = new ccolor('#000000');
    var _white  = new ccolor('#FFFFFF');

    // game vars
    var LOOP_INT = 1000 / 60; // 16.6666
    var AI_BODY_RADIUS = 50;
    var TEST_SPEED = LOOP_INT / 800;
    var timer_id = 0;

    var text_position = new vec2(5, 5);

    var AI_VIEW_CONE = Math.DEG2RAD(120);

    var ai_position = new vec2(300, 260);

    var ai_angle = Math.DEG2RAD(225);

    //var test_angle = 0; // rad

    // main loop
    var MAIN_LOOP = function()

        clearCanvas(_white);


        // test
        //test_angle += LOOP_INT / 300;
        //test_angle = Math.wrapRadian(test_angle + (LOOP_INT / 300));
        //_text( text_position, 'ANGLE: ' + (Math.round(test_angle * 100) / 100), _blue );

        var target_angle = ai_position.slopeAngle(_mousePos);


        var angle_diff = Math.angleDifference(target_angle, ai_angle);

        // within FOV
        //if ( Math.angleWithinFOV(angle_diff, AI_VIEW_CONE) )
            if (angle_diff > 0) ai_angle += TEST_SPEED;
            if (angle_diff < 0) ai_angle -= TEST_SPEED;
        //



        var txt = 'ANGLE DIFF: ' + Math.roundp(Math.RAD2DEG(angle_diff), 2);
        _text( text_position, txt, _blue );



        // AI
        _line(ai_position, ai_position.next(ai_angle, AI_BODY_RADIUS), _red);


        // cone
        var aa = Math.M_PI_DB_2 - ai_angle;
        var vc = AI_VIEW_CONE / 2;
        _viewcone(ai_position, AI_BODY_RADIUS, _red, aa - vc, aa + vc);
        _line(ai_position, ai_position.next(ai_angle - vc, AI_BODY_RADIUS), _red);
        _line(ai_position, ai_position.next(ai_angle + vc, AI_BODY_RADIUS), _red);



        // target angle
        _line( ai_position, ai_position.next( target_angle, AI_BODY_RADIUS ), _blue );


        // cursor
        _circle( _mousePos, 5, _green, true );

        //requestAnimationFrame(MAIN_LOOP);
    ;

    // srart up
    timer_id = setInterval(MAIN_LOOP, LOOP_INT);


;

        </script>

    </body>
</html>

【问题讨论】:

【参考方案1】:

要使用度数计算 2 个角度之间的差异,您可以使用:

var diffA = targetA - sourceA;
diffA = (a + 180) % 360 - 180;

【讨论】:

这是用于游戏的,它需要浮点值高精度 如果 targetAsourceA 是浮点数,那么你应该很好。 我刚试了下,问题依旧,试着把线拉到10点左右,然后在7-5点左右移动鼠标 也许您可以更具体地说明您的问题是什么? 否则,我们甚至不知道从哪里开始。

以上是关于计算角度差2D游戏的JS函数的主要内容,如果未能解决你的问题,请参考以下文章

js怎么计算2个日期之间差多少天

js如何计算两个日期的月份差?

游戏开发物理学

游戏开发物理学

js乘法精度计算代码 求解释

解决JS浮点数(小数)计算加减乘除的BUG