- Home /
 
Moving 2D Object To New Position With Angle and Distance
I have a player object with a charge ability. What I'm trying to do, is when I use my charge ability, I want to move to that new position in a straight line over time. Maybe this has already been answered, but every answer I found only provided and answer to basically "teleport" your player object to the new position. I don't want to move to my new position immediately, I want to travel over time like. Similiar to:
Barbarian in Diablo 3
Magnus in Dota 2
Here is how my charge ability works. I am tracking the position of the analog (from a controller) or mouse position to capture the current angles. When the ability button is pressed, it calls UseAbility. Then I start a Coroutine, and use an IEnumerator to iterate my position to the newPosition.
Here is my Charge ability
 public override void UseAbility(GameObject player)
     {
         abilityCooldownTimer = new Stopwatch();
         abilityCooldownTimer.Start();
         
         StartCoroutine(ChargeUpdate(player));
     }
 
     public IEnumerator ChargeUpdate(GameObject player)
     {
         // Capture DirectionIndicator for proper angle
         GameObject directionIndicator = GameObject.Find("DirectionIndicator");
         float angle = directionIndicator.transform.eulerAngles.z;
 
         // Calculate the distance we need to travel on x and y
         Vector3 distanceToTravel = new Vector3(baseEffectDistance * Mathf.Cos(angle * Mathf.PI / 180), baseEffectDistance * Mathf.Sin(angle * Mathf.PI / 180), 0);
         Vector3 newPosition = new Vector3(player.transform.localPosition.x + distanceToTravel.x, player.transform.localPosition.y + distanceToTravel.y, 0);
         
         Vector2 input;
         player.GetComponent<Player>().canMove = false;
         isCharging = true;
         collidedWithObject = false;
 
         // Wait .4 second to "prepare" our charge ability
         yield return new WaitForSeconds(baseEffectPreparation);
 
         while (abilityCooldownTimer.IsRunning && newPosition != player.transform.localPosition && isCharging)
         {
             // TODO: fix this, for some reason our x and y position can't make it to the final points. We are always .1 or .2 off.
             // Making a check to see if we are .5 off, if so then we will say our charge is complete
             float safeCheckDistance = .5f;
             if (collidedWithObject ||
                 ((newPosition.x - player.transform.localPosition.x <= safeCheckDistance && newPosition.x - player.transform.localPosition.x >= -safeCheckDistance) &&
                 (newPosition.y - player.transform.localPosition.y <= safeCheckDistance && newPosition.y - player.transform.localPosition.y >= -safeCheckDistance)))
             {
                 isCharging = false;
             }
 
             // Determine X Direction to Move
             if (newPosition.x - player.transform.localPosition.x == 0 ||
                 (newPosition.x - player.transform.localPosition.x <= safeCheckDistance && newPosition.x - player.transform.localPosition.x >= -safeCheckDistance))
                 input.x = 0;
             else if (newPosition.x - player.transform.localPosition.x > 0)
                 input.x = 1;
             else
                 input.x = -1;
 
             // Determine Y Direction to Move
             if (newPosition.y - player.transform.localPosition.y == 0 ||
                 (newPosition.y - player.transform.localPosition.y <= safeCheckDistance && newPosition.y - player.transform.localPosition.y >= -safeCheckDistance))
                 input.y = 0;
             else if (newPosition.y - player.transform.localPosition.y > 0)
                 input.y = 1;
             else
                 input.y = -1;
 
             player.GetComponent<Player>().UpdateMotor(input, baseEffectSpeed, baseEffectSpeed);
 
             yield return null;
         }
 
         player.GetComponent<Player>().canMove = true;
 
         abilityCooldownTimer.Stop();
         abilityCooldownTimer.Reset();
 
         yield return null;
     }
 
               And here is my UpdateMotor function
 public virtual void UpdateMotor(Vector2 input, float motorXSpeed, float motorYSpeed)
     {
 
         // Reset MoveDelta
         moveDelta = new Vector2(input.x * motorXSpeed, input.y * motorYSpeed);
 
         // Move on y axis based on moveDelta
         transform.Translate(0, moveDelta.y * Time.deltaTime, 0);
         myAnimator.SetFloat("yspeed", moveDelta.y);
 
         // Move on x axis based on moveDelta
         transform.Translate(moveDelta.x * Time.deltaTime, 0, 0);
         myAnimator.SetFloat("xspeed", moveDelta.x);
     }
 
               Two points to mention: 1. You'll notice the first thing in my while loop, I have a TODO. My UpdateMotor function for some reason doesn't move my character smoothly and accurately to the new position. I'm always .5 to 1.5-ish away from my final position. Meaning, when I call my UpdateMotor, and my final position is (3.5, 4.3), I'll get really close to 3.5 on the x axis, but will never get exactly (to the tenth decimal, obviously don't need to be float accurate). This is the same for the y-axis. 2. My UpdateMotor function works, and gets me to my location, but as you'll see in the image below, I travel to my x and y position at the same speeds. This is a problem, because when I have reached the x or y position I stop traveling on that axis. This gives an awkward movement to the player and looks funny.

With all of this, I'm trying to figure out: 1. Given an angle, how can I move my player object to the new position over time 2. Additionally, having the player move to the exact position (since I know my new x,y position)
Thank you all in advance for any help you can provide.
If i get it right you want to point a direction with a controller or sth and move your player to new position according to new direction and your baseEffectDistance in given time. So this is what you can do:
 void Update () {
         if (Input.Get$$anonymous$$eyDown($$anonymous$$eyCode.Space)) { //this is just for testing. You could modify it as your needs 
             StartCoroutine($$anonymous$$oveToPosition(1f));
         }
     }
 
     public Vector3 GetDirection(Vector3 relative, Vector3 reference) {
         var heading = relative - reference;
         var direction = heading.normalized;
     
         return direction;
     }
     
     public IEnumerator $$anonymous$$oveToPosition(float timeTo$$anonymous$$ove) {
         //this is where you want to do your preparations
         yield return new WaitForSeconds(baseEffectPreparation);
     
         var t = 0f;
         var target = GetDirection(relative.transform.position, transform.position);
         var initialPos = transform.position;
         while (t < 1) {
             t += Time.deltaTime / timeTo$$anonymous$$ove;
             transform.position = Vector3.Lerp(transform.position,
                                                       initialPos + target * baseEffectDistance, t);
             yield return null;
         }
        //this line ensures that to get exactly the target position 
         transform.position = initialPos + target * baseEffectDistance;
     }
 
                  Here is some walkthrough: In GetDirection method reference is the position you pointed to move your player and relative is your players positon. I tested script before posting and it works as expected (at least what i understood from your question). Hope this helps.
Thanks for answering.
I don't want to charge based on time (like what you have in your while). I want to charge until I reach my new position (which is based on the angle and the variable baseEffectDistance). This way I can reuse the charge ability, and the total distance to travel will be deter$$anonymous$$ed by baseEffectDistance.
Additionally, the speed it takes to travel to that distance will be based on the variable baseEffectSpeed. So this will either make us charge really quickly, or it might be a slower charge. But time itself is not a part of the movement.
Is there anyway I can charge, and move to my new position without time? $$anonymous$$y Update$$anonymous$$otor function is currently what I'm using to actually move my player using WASD on keyboard, and analog with controller. This works fine to move, but as I mentioned using my image, its not ideal because it will look weird.
Edit 1: I do have checks to see if I need to stop charging, which is why I have the variable isCharging. I have a few checks that say "if this happens, isCharging = false". All that works, I just want a better way to charge in a straight line to my new position.
Time can't be the only way to make this work, right?
Ofc not. But i cant say i got what you want to achieve completely. Is there a sample video, gif or sth. to visualize it?
Your answer
 
             Follow this Question
Related Questions
Making a bubble level (not a game but work tool) 1 Answer
Multiple Cars not working 1 Answer
2D game with two point perspective and distortion? 1 Answer
Sprite will not roate when arrow keys are pressed 2 Answers
Distribute terrain in zones 3 Answers