A Flash Developer Resource Site

Results 1 to 10 of 10

Thread: [AS3] Finding shortest distance to angle (ccw or cw)

  1. #1
    bibuti. nolen's Avatar
    Join Date
    Sep 2002
    Location
    az.
    Posts
    191

    [AS3] Finding shortest distance to angle (ccw or cw)

    I'm working on some rotation/movement code where you press an arrow key, the clip rotates to the desired angle, and then moves in the desired direction once it has reached said angle.

    I'm having an issue forcing the clip to rotate in the proper direction towards the desired angle.

    http://www.nolentabner.com/test/keytest.swf

    If you follow the link above, press and hold the arrow keys to move and rotate the clip around the stage. Try rotating the clip all the way until it's facing down then press and hold the LEFT arrow key. Instead of rotating to the left, the clip rotates all the way around (counter-clockwise) until it is facing left.

    Code:
    var speed:int = 3;
    var dir:String = "right";
    var targetDir:String = " ";
    
    var keys:Object = new Object();
    
    keys[Keyboard.UP] = {down:false, dirx:0, diry:-1, rot:-90, dir:"up"};
    keys[Keyboard.DOWN] = {down:false, dirx:0, diry:1, rot:90, dir:"down"};
    keys[Keyboard.LEFT] = {down:false, dirx:-1, diry:0, rot:-180, dir:"left"};
    keys[Keyboard.RIGHT] = {down:false, dirx:1, diry:0, rot:0, dir:"right"};
    
    
    stage.addEventListener(KeyboardEvent.KEY_DOWN, downKeys);
    stage.addEventListener(KeyboardEvent.KEY_UP, upKeys);
    stage.addEventListener(Event.ENTER_FRAME, onFrame);
    
    function downKeys(e:KeyboardEvent):void
    {
        if(keys[e.keyCode] != undefined)
        {
            keys[e.keyCode].down = true;
            targetDir = keys[e.keyCode].dir;
    
        }
    }
    
    function upKeys(e:KeyboardEvent):void
    {
        if(keys[e.keyCode] != undefined)
        {
            keys[e.keyCode].down = false;
        }
    }
    
    function onFrame(e:Event):void
    {
       
        //trace(dir+" "+box.rotation+" "+targetDir);
    
        for each(var keyOb:Object in keys)
        {
            if(keyOb.down == true)
            {
                // if we are already at our target direction, move ahead
    
                if(box.rotation == keyOb.rot)
                {
                    dir = keyOb.dir;
    
                    box.x += speed*keyOb.dirx;
                    box.y += speed*keyOb.diry;
                }
                else
                {
                    // if our desired angle is less than our current angle, decrease
                    if(keyOb.rot < box.rotation)
                    {
    					box.rotation -= speed;
    
                    }
                    // if our desired angle is greater than our current angle, increase
                    else
                    {
    					box.rotation += speed;
                    }
                }
            }
        }
    }
    I understand that this is happening because technically the angle for the LEFT direction is less than the current angle when facing down (-180 and 90, respectively). What I'm curious about is how to accomplish proper rotation without having specific handlers for each direction. Wouldn't I need to use something like the distance formula and check it against 180, then use that to determine which direction to rotate?

    Sorry if this is lengthy, I just want to make sure it's understandable. Check the .swf and you will see what I'm trying to accomplish.
    i'm obsessed with video games.

  2. #2
    M.D. mr_malee's Avatar
    Join Date
    Dec 2002
    Location
    Shelter
    Posts
    4,139
    i made this a while ago, its in AS2, but not hard to port to AS3
    Last edited by mr_malee; 07-16-2008 at 10:17 AM.
    lather yourself up with soap - soap arcade

  3. #3
    Senior Member
    Join Date
    Mar 2008
    Posts
    301
    The desired change in rotation (in degrees) will be equal to this, if you want to go from angle0 to angle1 and both angles are in degrees:
    PHP Code:
    (180/Math.PI)*Math.atan2((Math.cos(angle0*Math.PI/180)*Math.sin(angle1*Math.PI/180)-Math.sin(angle0*Math.PI/180)*Math.cos(angle1*Math.PI/180)),(Math.sin(angle0*Math.PI/180)*Math.sin(angle1*Math.PI/180)+Math.cos(angle0*Math.PI/180)*Math.cos(angle1*Math.PI/180))) 
    the desired change in rotation (in radians) if the angles are given in radians:
    PHP Code:
    Math.atan2((Math.cos(angle0)*Math.sin(angle1)-Math.sin(angle0)*Math.cos(angle1)),(Math.sin(angle0)*Math.sin(angle1)+Math.cos(angle0)*Math.cos(angle1))) 
    I believe this is what you're looking for? It should work. Haven't used it before, but I probably will now that I've written it down. Someone correct me if it's wrong, but I don't think it is. It agrees with mr_malee's function.
    Last edited by WesIsGood; 05-05-2008 at 09:26 PM.

  4. #4
    Senior Member
    Join Date
    Mar 2008
    Posts
    301
    So a nice easing function in one line would be something like....
    PHP Code:
    this.rotation+=(180/Math.PI)*Math.atan2((Math.cos(this.rotation*Math.PI/180)*Math.sin(targetrotation*Math.PI/180)-Math.sin(this.rotation*Math.PI/180)*Math.cos(targetrotation*Math.PI/180)),(Math.sin(this.rotation*Math.PI/180)*Math.sin(targetrotation*Math.PI/180)+Math.cos(this.rotation*Math.PI/180)*Math.cos(targetrotation*Math.PI/180)))/20
    where the variable targetrotation is your target rotation.

    If you want to set a maximum turn speed, you can use this:
    PHP Code:
    this.rotation+=Math.max(Math.min((180/Math.PI)*Math.atan2((Math.cos(this.rotation*Math.PI/180)*Math.sin(targetrotation*Math.PI/180)-Math.sin(this.rotation*Math.PI/180)*Math.cos(targetrotation*Math.PI/180)),(Math.sin(this.rotation*Math.PI/180)*Math.sin(targetrotation*Math.PI/180)+Math.cos(this.rotation*Math.PI/180)*Math.cos(targetrotation*Math.PI/180))),turnspeed),-turnspeed); 
    where the variable turnspeed is the maximum turn speed.
    Last edited by WesIsGood; 05-05-2008 at 09:44 PM.

  5. #5
    Senior Member
    Join Date
    Mar 2008
    Posts
    301
    So specifically for you this is what you want:

    PHP Code:
        for each(var keyOb:Object in keys)
        {
            if(
    keyOb.down == true)
            {
                
    // if we are already at our target direction, move ahead

                
    if(box.rotation == keyOb.rot)
                {
                    
    dir keyOb.dir;

                    
    box.+= speed*keyOb.dirx;
                    
    box.+= speed*keyOb.diry;
                }
                else
                {
    box.rotation+=Math.max(Math.min((180/Math.PI)*Math.atan2((Math.cos(box.rotation*Math.PI/180)*Math.sin(keyOb.rot*Math.PI/180)-Math.sin(box.rotation*Math.PI/180)*Math.cos(keyOb.rot*Math.PI/180)),(Math.sin(box.rotation*Math.PI/180)*Math.sin(keyOb.rot*Math.PI/180)+Math.cos(box.rotation*Math.PI/180)*Math.cos(keyOb.rot*Math.PI/180))),speed),-speed);
                }
            }
        }


  6. #6
    bibuti. nolen's Avatar
    Join Date
    Sep 2002
    Location
    az.
    Posts
    191
    Holy cow. Thanks WesIsGood and mr_malee!

    Wes, that code works perfectly but if I may ask if you could explain what it's doing so I can learn from this experience?
    i'm obsessed with video games.

  7. #7
    Senior Member
    Join Date
    Mar 2008
    Posts
    301
    PHP Code:
    box.rotation+=Math.max(Math.min((180/Math.PI)*Math.atan2(Math.sin((keyOb.rot-box.rotation)*(Math.PI/180)),Math.cos((keyOb.rot-box.rotation)*(Math.PI/180))),speed),-speed); 
    Actually the one I gave you originally was unnecessarily complicated. This one is slightly simpler to type and much simpler to understand. Computationally less expensive I would imagine also. Without all the radian/degree conversion it looks a lot nicer:

    PHP Code:
    rotationdifference=Math.atan2(Math.sin(targetrotation-originalrotation),Math.cos(targetrotation-originalrotation)); 

    It's probably more clear now what's happening. I just subtract the rotation of one from the other, like you probably did originally, but then plug that rotation in to cos and sin to get a vector, and then I atan2 to get the rotation of that vector. The original way did basically the same thing (after a trig identity), but when I derived it I went through a more complex (and interesting) process. I'm a little bit disappointed that it turned out to be as straight forward as it did.

  8. #8
    Senior Member rachil0's Avatar
    Join Date
    Jul 2007
    Location
    Columbus, OH.
    Posts
    465
    You could also solve the problem using a cross product/determinant, it might even be equivalent (I see cross-product looking terms lurking in Wes's code). Construct two unit vectors out from the origin, one (v1) points along the angle you'd like to be facing, the other points along the angle you're currently facing (v2). Compute C=z_hat DOT (v2 CROSS v1). If C is positive, you should turn left. If it's negative, you should turn right.

    If C is zero (or sufficiently close to zero), either (i) you're pointed exactly the right way so no turn is needed, or (ii) you're pointed completely the wrong way, and turning either left or right is fine. Dotting v1 and v2 and checking the sign will distinguish these two cases.
    Last edited by rachil0; 05-06-2008 at 07:02 PM.

  9. #9
    bibuti. nolen's Avatar
    Join Date
    Sep 2002
    Location
    az.
    Posts
    191
    So let me just reiterate then:

    PHP Code:
            // convert the difference between the two angles to radians
        
    var diff:Number = (keyOb.rot-box.rotation)*(Math.PI/180);
            
    // find the rotation of the vector created by the sin and cos of the difference
        
    var rotationDifference:Number Math.atan2(Math.sin(diff),Math.cos(diff));
        
    // rotate the clip accordingly
            
    box.rotation+=Math.max(Math.min((180/Math.PI)*rotationDifference,speed),-speed);




    ///// 
    This makes a LOT more sense and works beautifully. Cheers!
    Last edited by nolen; 05-06-2008 at 07:09 PM.
    i'm obsessed with video games.

  10. #10
    Senior Member
    Join Date
    Mar 2008
    Posts
    301
    nolen:
    Yep, that should do it. It's also a lot clearer when you break it in to parts like you did. I tend to clump things up in to one line whenever possible. It makes my code unreadable and hard to edit, but it makes me feel like a man.

    rachil0:
    Good eye, I did get my original solution using cross products.
    Last edited by WesIsGood; 05-06-2008 at 07:16 PM.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  




Click Here to Expand Forum to Full Width

HTML5 Development Center