Problems with making a dodge mechanic similar to Super Punch Out

I’m trying to make it so when I press down and hold either A or D the player will move to a fixed position either left or right, and will remain there for as long as you hold it, or until a set amount of time passes then the player will return to the neutral position. This makes sense in my head, I have scoured the Unity Manual, watched many tutorials on Youtube and on the Unity Learn site as well. I looked top and bottom through GitHub for code similar to what I’m trying to achieve, but I’ve found nothing. So, this is me at my wits end. What I’m looking for specifically is to either optimize this code, or an alternative that would work better. I’m also using a Rigidbody2D Kinematics component as well. Any and all help is greatly appreciated!!

Here is the code:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerController : MonoBehaviour
{
    private Rigidbody2D rb;
    public Vector2 posA;
    public Vector2 posB;
    public Vector2 posC;
    public float speed = 15f;

    // Start is called before the first frame update
    void Start()
    {
        rb = gameObject.GetComponent<Rigidbody2D>();
        posA = transform.position.Vector3.normalize(-5f, -3f, 0f);
        posB = transform.position.Vector2.normalize(0f, -3f, 0f);
        posC = transform.position.Vector2.normalize(5f, -3f, 0f);
    }

    // Update is called once per frame
    void FixedUpdate()
    {
        float x = Input.GetAxis("Horizontal") * Time.fixedDeltaTime * speed;
        rb.MovePosition(rb.position + Vector2.right * x);

        if (Input.GetKey(KeyCode.A))
        {
            StartCoroutine(IEnumerator, PlayerController.DodgeLeft);
        }

        if (Input.GetKey(KeyCode.D))
        {
            StartCoroutine(IEnumerator, PlayerController.DodgeRight);
        }
    }

    IEnumerator DodgeLeft(float delayTime)
    {
        yield return new WaitForSeconds(delayTime);

        float startTime = Time.time;

        while (Time.time - startTime <= 1)
        {
            rb.MovePosition(transform.position = Vector3.Lerp(posB, posA, Time.time - startTime));
            yield return 1;
        }

    }

    IEnumerator DodgeRight(float delayTime)
    {
        yield return new WaitForSeconds(delayTime);

        float startTime = Time.time;

        while (Time.time - startTime <= 1)
        {
            rb.MovePosition(transform.position = Vector2.Lerp(posB, posC, Time.time - startTime));
            yield return 1;
        }

    }

}

Whoa there nelly … what’s going on here?!

Nothing?! I actually told you the solution and gave you example code here:

https://discussions.unity.com/t/885116/2

For the return after timing, just use a cooldown timer.

Cooldown timers, gun bullet intervals, shot spacing, rate of fire:

https://discussions.unity.com/t/821482/2

1 Like

I know I asked before and you replied with help and I appreciate it, but when I used your code it didn’t physically move my character. I understand that the player will always want to move to desired quantity, which in this scenario would be the center position. However, I’ve tried the code you provided as is, I’ve tried setting Vector2/Vector2Int to tell the player the the desired/current quantity’s refer to positions in the scene, I’ve tried referencing an RB2D and using physics to propel the character into specific locations after a key is pressed, etc. There’s nothing telling my character what position/where to go when a key is pressed. Using transform.position to move the character doesn’t seem ideal as everything I’ve seen online says that if you need your character to deal/take damage, then using RB2D Kinematic rb.MovePosition is a similar but better choice. In the Unity Manual it describes the rb.MovePosition function as being able to move the rigid body to a specific position and that kinematic is the intended setting. However, using RB2D eventually just turns into moving horizontally freely instead of going to a specific position either to the left or right and then returning to the center.

I thought that establishing a start position and two other positions would circumvent that, however I receive errors regarding Vector2s when I do. I’ve also tried to set up three empty game objects in the scene and telling the player to move towards a specific empty game object when told to do so, but that didn’t work either.

So, with all that being said, I definitely tried your code and tried tweaking it to make the player character move in the scene, but it just doesn’t work from what I’ve tried. Is there a solution to making the character physically move positions in the scene?

This is my treatment, just a quick start to it. Full package enclosed with everything set up.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

// @kurtdekker
//
// related to: https://forum.unity.com/threads/problems-with-making-a-dodge-mechanic-similar-to-super-punch-out.1301418
//

public class PunchOutPlayer : MonoBehaviour
{
    [Header( "Center of player")]
    public Transform Center;

    [Header( "Invisible left/right markers.")]
    public Transform Left;
    public Transform Middle;
    public Transform Right;

    // how long have you been holding a move?
    float holdTimer;
    // how long can you hold to one side
    const float MaximumHoldTime = 0.5f;
    // how much faster does not holding input cool the timer down?
    const float CoolDownRatio = 5.0f;

    float currentDodge;
    // lateral dodge left/right, in lerp alpha coord space
    const float DodgeSpeed = 10.0f;

    // lateral move left/right in world coords
    const float MoveSpeed = 10.0f;

    void UpdateCenter()
    {
        float movement = 0;

        if (Input.GetKey( KeyCode.LeftArrow))
        {
            movement = -1;
        }
        if (Input.GetKey( KeyCode.RightArrow))
        {
            movement = +1;
        }

        movement *= MoveSpeed;

        Vector3 position = Center.position;

        position.x += movement * Time.deltaTime;

        // lateral move limits
        position.x = Mathf.Clamp( position.x, -2.0f, +2.0f);

        Center.position = position;
    }

    void UpdateDodge()
    {
        // presume middle (no dodge)
        float desiredDodge = 0;

        // read inputs
        if (Input.GetKey( KeyCode.A))
        {
            desiredDodge = -1;
        }
        else
        {
            if (Input.GetKey( KeyCode.D))
            {
                desiredDodge = +1;
            }
            else
            {
                if (holdTimer > 0)
                {
                    holdTimer -= Time.deltaTime * CoolDownRatio;
                }
            }
        }

        // timer
        holdTimer += Time.deltaTime;

        // no input if you hold too long
        if (holdTimer >= MaximumHoldTime)
        {
            desiredDodge = 0;
        }

        // move
        currentDodge = Mathf.MoveTowards( currentDodge, desiredDodge, DodgeSpeed * Time.deltaTime);

        // presume we're lerping on the right
        Transform t1 = Middle;
        Transform t2 = Right;

        // presume it is 0 to 1 (it might not be)
        float alpha = currentDodge;

        // handle left to middle
        if (currentDodge < 0)
        {
            alpha = -currentDodge;
            t2 = Left;
        }

        // TODO: you could look up alpha through an AnimationCurve
        // to get "Easing" at each end of the movement

        // compute where to be
        Quaternion rotation = Quaternion.Lerp( t1.rotation, t2.rotation, alpha);
        Vector3 position = Vector3.Lerp( t1.position, t2.position, alpha);

        // now we just have to "drive" between the appropriate transforms
        // TODO: if you're using Rigidbody, then replace this with MovePosition / MoveRotation
        transform.position = position;
        transform.rotation = rotation;
    }

    void Update()
    {
        UpdateCenter();
        UpdateDodge();
    }
}

8238372–1077078–PunchOutDodge.unitypackage (5.98 KB)

1 Like

An easy solution to solve this is to calculate your final dodged position by always adding a vector “_dodgeVec” to your real position. Something like (pseudocode):

Vector3 targetDodgeVec = Vector3.Zero;
if(holdingDodgeButton()) targetDodgeVec = _playertransform.right * _dodgestrength; //.right is just an example, can be any direction you like to dodge

_dodgeVec = Vector3.Lerp(_dodgeVec, targetDodgeVec, Time.deltaTime * _dodgeTransitionSpeed);
finalPos = playerpos + dodgeVec;
_playerTransform.position = finalPos;

While variables with underscore “_” are member variables.

1 Like

You are the MVP and an awesome human. I have been banging my head against a wall for a month now, but seeing it written out it all seems so simple. I seriously can’t even begin to thank you enough, if there’s anything I can do to repay you please let me know. Again, thank you!

1 Like