Search Unity

How to always move a top down 2D character in a rotated y direction

Discussion in 'Scripting' started by Jananton, Mar 27, 2015.

  1. Jananton

    Jananton

    Joined:
    Oct 8, 2014
    Posts:
    22
    Hi All,

    Well, I thought this would be easy since in 3D the rigid body z direction is always pointing forward rotated or not, but it seems this isn't the case in top down 2D. ;-)

    A picture is probably quicker then a thousand words to explain what's being tried by me so here:

    The green arrow is what I would like to accomplish when pressing the up arrow on the keyboard, while the rotation left or right (as shown here) is done with the corresponding arrow keys.
    However, rotating the sprite doesn't rotate the y direction in 2D as I found out so initially the player kept moving in the undesired direction as represented with the red arrow.

    I presume one needs trigonometry to get this working so I tried a lot of things, did I say a lot of things, but after I don't know how many iterations I'm at this and, dare I say so, completely lost.

    Code (CSharp):
    1.    
    2. void FixedUpdate () {
    3.         float h = Input.GetAxis ("Horizontal");
    4.         float v = Input.GetAxis ("Vertical");
    5.  
    6.         MovementManager (h, v);
    7.     }
    8.  
    9.     void MovementManager(float horizontal, float vertical) {
    10.         if (horizontal != 0 || vertical != 0)
    11.         {
    12.             rotAngle = horizontal * turnSmoothing * Time.fixedDeltaTime;
    13.             rb2d.rotation = rb2d.rotation - rotAngle;
    14.             Vector2 direction = new Vector2(Mathf.Clamp(Mathf.Tan(rotAngle) + transform.position.x, -1, 1), 1f);
    15.             Vector2 newPosition = direction * vertical * speedDampTime * Time.fixedDeltaTime;
    16.             //rb2d.position = rb2d.position + newPosition;
    17.             rb2d.AddForce(newPosition);
    18.  
    19.             anim.SetBool ("Walking", true);
    20.         } else {
    21.             anim.SetBool("Walking", false);
    22.         }
    23.     }
    24.  
    I hope I explained myself well enough for someone to put me in the right direction solving this puzzle.
    Thanks in advance,

    Jan
     
  2. renman3000

    renman3000

    Joined:
    Nov 7, 2011
    Posts:
    6,697
    Could you alter the games plane so that z is the axis of rotation and so look at, angle ect can be used with ease?
     
  3. Jananton

    Jananton

    Joined:
    Oct 8, 2014
    Posts:
    22
    Hmm, maybe I misunderstand what you mean, but as far as I know the z axis is the rotation axis in my setup. I just started this as a default 2D empty project, so x is left and right, and Y is up and down, with up being the intended direction of forward travel, and z is depth, or in other words from the camera straight down through the floor.
    If you mean something else, please explain. ;-)

    Greets,

    Jan
     
  4. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    transform.forward is an object's local forward vector in world coordinates. I would just use that.
     
  5. Jananton

    Jananton

    Joined:
    Oct 8, 2014
    Posts:
    22
    Actually I started with using transform.forward initially, but I probably did something wrong there, because even when I managed to turn the character(sprite) it kept traveling straight up when pressing the up arrow key.
    As said I've tried so many iterations of the movement code in hindsight I'm not absolutely sure if I really used transform.forward. Better have another go at it to make sure. ;-)

    Greets,

    Jan
     
  6. renman3000

    renman3000

    Joined:
    Nov 7, 2011
    Posts:
    6,697
    I am just thinking your set up is topdown, xy. If you made it topdown xz, it might allow you to use the native transform.forward feature.
     
  7. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    By default 2D mode looks along the Z axis.

    In either case - if forward isn't viable because it represents the local Z axis then I would imagine transform.up would work (local Y axis).
     
  8. Jananton

    Jananton

    Joined:
    Oct 8, 2014
    Posts:
    22
    Well, I'm sure I did use transform.up in one of my tries. ;-)
    However, I did give it a go again and now it seems to work as I expected it would. I'm still puzzled why this didn't do the trick when I tried before.

    The code now looks like this:
    Code (CSharp):
    1.  
    2. void FixedUpdate () {
    3.         float h = Input.GetAxis ("Horizontal");
    4.         float v = Input.GetAxis ("Vertical");
    5.  
    6.         MovementManager (h, v);
    7.     }
    8.  
    9.     void MovementManager(float horizontal, float vertical) {
    10.         if (horizontal != 0 || vertical != 0)
    11.         {
    12.             rotAngle = horizontal * turnSmoothing * Time.fixedDeltaTime;
    13.             rb2d.rotation = rb2d.rotation - rotAngle;
    14.  
    15.             rb2d.AddForce(transform.up * vertical * speedDampTime * Time.fixedDeltaTime);
    16.  
    17.             if( vertical != 0)
    18.                 anim.SetBool ("Walking", true);
    19.             else
    20.                 anim.SetBool("Walking", false);
    21.         } else {
    22.             anim.SetBool("Walking", false);
    23.         }
    24.     }
    25.  
    One thing that bothers me is that the speedDampTime needs to be set to 10000f to let the sprite move at a reasonable walking speed while both linear and anglular drag in its rigid body are the default values of 0 and 0.05 respectively.

    Anyhow thanks for all your suggestions that kept me hacking at this and if you have ideas of further code improvements I'd be happy to hear them. ;-)

    Greets,

    Jan
     
  9. Timelog

    Timelog

    Joined:
    Nov 22, 2014
    Posts:
    528
    To use a lower value to move, just simply use RigidBody2D.velocity instead of AddForce(). Also use Time.deltaTime instead of Time.fixedDeltatime, as fixedDeltaTime gives back seconds, and is meant for slow motion effects: "This can be used for slow motion effects." http://docs.unity3d.com/ScriptReference/30_search.html?q=fixedDeltaTime

    Personally for a game I am making (2D breakout game) I use this to set the velocity of the paddle:
    controller script:
    Code (CSharp):
    1.  private void Update()
    2. {
    3.     model.UpdateVelocity(
    4.         new Vector2(
    5.             Input.GetAxis("Horizontal"), 0));
    6. }
    model script:
    Code (CSharp):
    1. public void UpdateVelocity(Vector2 input)
    2. {
    3.     _velocity = input * _speed;
    4. }
    view script:
    Code (CSharp):
    1. public void Update()
    2. {
    3.     _body.velocity = _model.Velocity;
    4. }
    _speed is a float variable that I currently have set as a value of 10f, and movement is quick, normal character movement would be at about 5f.
     
    Last edited: Mar 27, 2015
  10. Jananton

    Jananton

    Joined:
    Oct 8, 2014
    Posts:
    22
    Good tips, I'll implement them first thing tomorrow. ;-)

    Were that fixedDeltaTime came from, I actually always use deltaTime, probably overheated gray matter after trying to solve this for too long. lol

    Greets,

    Jan
     
  11. arkon

    arkon

    Joined:
    Jun 27, 2011
    Posts:
    1,122
    Not sure if this will help but it's how I rotate my 2d sprites to face an enemy, you could probably adapt it to your situation.:

    Code (CSharp):
    1.    
    2. Vector3 dir = Target.transform.position - Pivot.transform.position;
    3. float angle = Mathf.Atan2(dir.y,dir.x) * Mathf.Rad2Deg;
    4. Pivot.transform.rotation = Quaternion.Slerp(Pivot.transform.rotation,Quaternion.AngleAxis(angle,Vector3.forward),SlerpSpeed * Time.deltaTime);
    5.  
     
  12. Jananton

    Jananton

    Joined:
    Oct 8, 2014
    Posts:
    22
    Interesting, I'll put that in the bag of useful code snippets, thanks. ;-)

    Greets,

    Jan