Smooth 'turning' script

天涯浪子 提交于 2019-12-12 03:47:59

问题


I am making an HTML5 Canvas game using Javascript.

I want to make an object turn smoothly to a certain direction.

I am storing the direction as a variable, and using radians. The code works like this:

window.setInterval(loop,25);

var dir = 0;
var targetDir = Math.PI/2;

function loop() {
  dir += (targetDir - dir) / 5;
  document.getElementById('text').innerHTML = dir; //To display the direction
}
<p id='text'></p>

It will work most of the time, but if the direction and target direction are on opposite left/right sides, the computer will assume the fastest way to get to the target is to to all the way around, like this: (Image)

Does anyone know an algorithm I can use to make it work correctly?


回答1:


Was hoping someone would answer with an elegant solution, nothing yet so here is how I do it.

I have always found the solution to this problem very inelegant, that said, I do not know if there is a better way, nor have i endeavored to find one. Below are two functions that will return the shortest direction between two angles that derived from the function Math.atan2(yDif, xDif) which returns an angle in the range -Math.PI to Math.PI

Get shortest direction.

// returns true if shortest direction is clockwise else false
function isShortestDirClockwise(ang1, ang2){
    if (ang1 < 0) {
        if ( (ang2 < 0 && ang1 > ang2) || (ang2 >= 0 && ang1 + Math.PI < ang2) ) {
            return false;        
        }
    } else {
        if ( (ang2 > 0 && ang1 > ang2) || (ang2 <= 0 && ang1 - Math.PI < ang2) ) {
            return false;        
        }
    }
    return true;
}

Get shortest angle

// returns the shortest angle neg angles are counterClockwise positive are clockwise
function getShortestAngle(ang1, ang2){
    var cw = true; // clockwise
    var ang;
    if (ang1 < 0) {
        if( (ang2 < 0 && ang1 > ang2) || (ang2 >= 0 && ang1 + Math.PI < ang2) ) {
            cw = false;        
        }
    } else {
        if ( (ang2 > 0 && ang1 > ang2) || (ang2 <= 0 && ang1 - Math.PI < ang2) ) {
            cw = false;        
        }
    }
    if (cw) {
        var ang = ang2 - ang1;
        if (ang < 0) {
            ang = ang2 + Math.PI * 2 - ang1;
        }
        return ang;
    }
    var ang = ang1 - ang2;
    if (ang < 0) {
        ang = ang1 + Math.PI * 2 - ang2;
    }   
    return -ang;
}

As I loath using these functions I much prefer the slightly more complex yet more elegant solution below. This finds the shortest angle in terms of a set of 3 points. the center point, and the two points that I wish to find the angle between and it always returns the shortest angle between any two lines.

 // Warning may return NAN if there is no solution (ie one or both points (p1,p2) are at center)
 // Also uses Math.hypot check browser compatibility if you wish to use this function or use Math.sqrt(v1.x * v1.x + v1.y * v1.y) instead
 function getAngleBetween(center, p1, p2){
     var d;
     var ang;
     // get vectors from center to both points
     var v1 = { x : p1.x - center.x, y : p1.y - center.y};
     var v2 = { x : p2.x - center.x, y : p2.y - center.y};
     // normalise both vectors
     d = Math.hypot(v1.x, v1.y);
     v1.x /= d;
     v1.y /= d;
     d = Math.hypot(v2.x, v2.y);
     v2.x /= d;
     v2.y /= d;
     // cross product gets the angle in range -Math.PI / 2 to Math.PI / 2
     ang = Math.asin(v1.x * v2.y - v1.y * v2.x);
     // use the cross product of the line perpendicular to the first to find the quadrant
     if(-v1.y * v2.y - v1.x * v2.x > 0){
         if(ang < 0){
            ang = -Math.PI - ang;
         }else{
            ang = Math.PI - ang;
         }
     }
     return ang;
 }


来源:https://stackoverflow.com/questions/37780814/smooth-turning-script

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!