1
\$\begingroup\$

I'm trying to create a grid movement in Unity, as where the player will ease-out on a tile when stopping, but I can't seem to get it right. I need a little nudge in the right direction for how to implement something like this:

Example of how I'd like my movement

I used a coroutine to execute the movement when input is requested and use an ease-out curve to make the player stop smoothly. But, when walking multiple tiles it is jittery because the ease-out is applied. Also, the smoother I want the ease-out to be, the slower the player moves.

Own movement with high speed

Own movement with low speed and better ease-out

My full code.

public float moveDuration = .5F;
public AnimationCurve moveCurve = AnimationCurve.Linear(0F, 0F, 1F, 1F);

private Rigidbody2D rigidBody;
private BoxCollider2D boxCollider;
private bool isMoving;

void Start() {
    rigidBody = GetComponent<Rigidbody2D>();
    boxCollider = GetComponent<BoxCollider2D>();
}

void Reset() {
    moveSpeed = 8F;
    moveDuration = .5F;
    moveCurve = AnimationCurve.Linear(0F, 0F, 1F, 1F);
    isMoving = false;
}

void Update() {
    if (!isMoving) {
        int x = 0;
    int y = 0;

    x = (int)(Input.GetAxisRaw("Horizontal"));
    y = (int)(Input.GetAxisRaw("Vertical"));
    if (x != 0) y = 0;
    if (x != 0 || y != 0) {
            AttemptMove(x, y);
        }
    }
}

private void AttemptMove(int x, int y) {
    Debug.Log("Attempt move: "+x+","+y);

    Vector2 startPosition = transform.position;
    Vector2 endPosition = startPosition + new Vector2(x, y);

    StartCoroutine(SmoothMovement(endPosition, moveDuration));
}

protected IEnumerator SmoothMovement(Vector3 endPosition, float duration) {
    float elapsed = 0F;
    Vector3 startPosition = transform.position;

    isMoving = true;

    while (elapsed < duration) {
        Vector3 newPosition = Vector3.Lerp(startPosition, endPosition, moveCurve.Evaluate((elapsed/duration)));

        rigidBody.MovePosition(newPosition);
        elapsed += Time.deltaTime;
        yield return null;
    }

    isMoving = false;

}
\$\endgroup\$
  • \$\begingroup\$ Can you show us how you're calling this coroutine based on player input? \$\endgroup\$ – DMGregory Aug 12 '19 at 13:27
  • \$\begingroup\$ I've added the full code in my question above for you. \$\endgroup\$ – Jeffrey Aug 12 '19 at 14:31
  • \$\begingroup\$ Are you able to move rapidly, or do you have to wait for the transition to complete before moving to another tile? \$\endgroup\$ – Iggy Aug 12 '19 at 16:17
  • \$\begingroup\$ I have to wait for the movement to finish and hit the center of a new tile (: \$\endgroup\$ – Jeffrey Aug 12 '19 at 17:44
0
\$\begingroup\$

you have mentioned that you want to ease the movement in or out. in this case you do not want to use lerp with a linear percentage of progress.

the problem

Also, the smoother I want the ease-out to be, the slower the player moves.

you are looking for Easing Curves. a different type of curve achieves a different gradient of smoothing and thus allows for appropriate smoothness without changing duration. i may be missing something, but your animation curve class appears to be using a linear curve. i believe you want a Quadratice curve if i am understanding you correctly.

moveCurve = AnimationCurve.Linear(0F, 0F, 1F, 1F);

needs to be something like

moveCurve = AnimationCurve(new Keyframe(0, 0), new Keyframe(.5f, .8f), new Keyframe(1, 1))

https://easings.net/en

when faced with similar problems, i actually dislike unity's animation classes, however I wont tell you to use a 3rd party for something natively supported. If you want to use the above line of code to try solving the problem you can,

if you want something a little more straight forward with more fine grain control. Without going to the trouble of writing a bunch of classes, you could simply add a function

Tween(float percentage){
    var val = //YOUR EASING CURVE HERE
    return val
}

for tile movement i recommend a Quartic Ease Out. and if you refer to one of many sites which elaborate on such curves you can find a straight forward function to convert a linear percentage to this percentage

http://www.gizma.com/easing/

//quartic easing out - decelerating to zero velocity


Math.easeOutQuart = function (t, b, c, d) {
    t /= d;
    t--;
    return -c * (t*t*t*t - 1) + b;
};

Further, in a similar situation to yours, i found that the smoothest appearance of movement is actually for new movement to immediately stop the current movement where it is. and start a movement to the new destination. depending on if you want to restrict movement time you can set the new movement's time to equal normal movement time + the cancelled movement's remaining time

I also found(in high performance situations) that coroutines do not invoke regularly as reliably as Invoke repeating. this may add to smoothness if you are observing missing frames of movement.

| improve this answer | |
\$\endgroup\$
  • 1
    \$\begingroup\$ Thank you! This helped so much. I'm still trying out the easing function but this looks very promising! Stopping the movement for the new movement/ease out looks better. \$\endgroup\$ – Jeffrey Aug 12 '19 at 20:12
  • \$\begingroup\$ Thank you:) its always nice to know I've helped out the community. Plus procedural animation is a passion. If you have any other questions feel totally free to reply or dm me. \$\endgroup\$ – Jody Sowald Aug 13 '19 at 21:07
0
\$\begingroup\$

Should only call the smooth when the player intends to stop moving so let's say. I am not sure if u have player class or if u have multiple class calling function so here is two different ways to do. 2nd way u would inherit from with you player and just call the function.

PLAYERCLASS

Psuedocode If player press walk input Apply movement without smoothing.

If player release walk input Apply smoothing.

SMOOTHING CLASS.

If movement is applied and player still pressing walk input Apply walk without smooth. Else player has released the walk input Apply smooth

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Thank you for helping me out. I'm trying to implement your idea, but I'm running into another problem: When should I check if a key is still pressed? If I do it too early Unity thinks I still press a button while actually I'm releasing it, but it checks too fast. If I check it too late, the input feels laggy. I tried to check when we're half way, as I can still do half an ease. Still this does not work right. \$\endgroup\$ – Jeffrey Aug 12 '19 at 14:26
  • \$\begingroup\$ Can you post your code. Or even a SS of your code since it might be a simple error but if not, with seeing your code it will allow me to make a more confident choice on how to address your issue. \$\endgroup\$ – PsyberGames Studio Aug 13 '19 at 15:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.