Issue With Using Velocity to Push Character in Direction

I’m trying to make a boss, and whenever you jump on the boss, you launch back off the boss. I have a method for jumping which works perfectly fine, so I thought I would do the same thing, but left instead of up. added a slight delay, but it seems to only push very very slightly, even though I cranked it up to 500 for testing purposes lol
Here’s my code,

        else if (collision.CompareTag("Stomp") && velocityY < -1f && collision.transform.parent.CompareTag("Boss"))
        {
            collision.GetComponentInParent<SnailBoss>().splatty = true;
            collision.GetComponentInParent<SnailBoss>().ReceiveDamage(1);
            if (collision.GetComponentInParent<SnailBoss>().eHealth >= 1)
            {
                Jump(jumpHeight / 2);
                StartCoroutine(PushBack());
            }
        if (!pushBack)
        {
            transform.Translate(Vector2.right * velocityX * Time.deltaTime);
            transform.Translate(Vector2.up * velocityY * Time.deltaTime);
        }

    public IEnumerator PushBack()
    {
        pushBack = true;
        yield return new WaitForSeconds(0.3f);
        rb.velocity = Vector2.left * 500;
        yield return new WaitForSeconds(0.2f);
        pushBack = false;
    }

My jump seems to work just fine, even though it is only set to 80, why does this code, which is exactly the same as much jump but with a slight delay not seem to work? I even tried removing the “if(!pushback)” to see if that was affecting it, but it wasn’t.

Thanks in advance

You are mixing physics with direct transform manipulation. Either use physics or transform manipulation, not both.

I also recommend to use rb.AddForce instead for things like knockback and jumping.

Well, it’s the same code for my jump, which works well,

void Jump(float jumpSpeed)
    {
        randomSound = Random.Range(0, jumpy.Length);
        rb.velocity = Vector2.up * jumpSpeed;
        jumpy[randomSound].Play();
    }

I tried this just now,

public IEnumerator PushBack()
    {
        pushBack = true;
        yield return new WaitForSeconds(0.3f);
        rb.AddForce(Vector2.left * 1000 * Time.deltaTime);
        yield return new WaitForSeconds(0.2f);
        pushBack = false;
    }

but nothing happens. I tried a debug right before the addforce, it is definitely calling the method, but no force is added.

Don’t multiply it by Time.deltaTime. AddForce is a force that happens all at once, using Time.deltaTime will cause it to be way less than what you want, keep it at 1000f not 1000f * 0.016f(average delta time for 60fps) which is 16. So you get a force of 16 for an instance, which is not much force at all.

Edit: Again though you want to use physics entirely for movement. So I would switch out to using that as soon as possible to avoid further conflicts between the two systems.

The

if (!pushBack)
        {
            transform.Translate(Vector2.right * velocityX * Time.deltaTime);
            transform.Translate(Vector2.up * velocityY * Time.deltaTime);
        }

Is my entire walking/jumping system, I don’t know how else to make him walk… I realized I can instantly teleport by increasing velocityX, but this is of course, not ideal… Everything else I’ve attempted hasn’t worked, but if you need it here’s my entire script, though it’s kinda sloppy lol

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

public class Player : MonoBehaviour
{
    public Magic[] Magics;
    public CameraBoy cameraBoy;
    public bool isGrounded, isLeft;
    public Rigidbody2D rb;
    [SerializeField]
    private LayerMask Layer;
    public LayerMask enemies;
    public GameObject attackTrig;
    public HeartMng heartMng;
    public AttackBoy attackBoy;

    public CameraShake.Properties properties;
    public CameraShake shakey;
    public PlayerBlock block;
    public Camera mainCam;

    public Transform attackPos;
    public float attackRng, force;

    public AudioSource[] jumpy;
    public AudioSource[] ouch;
    public AudioSource[] deado;
    public AudioSource[] blockHit;
    private int randomSound;

    public static int coinBag, playerHealth, startHealth;
    public static int curMaxHealth = 3;
    public static int maxHealthEver = 6;
  
    public float maxVelocity = 30;
    public bool isAttacking, isDead, stopMove,pushBack;
    public float walkSpeed = 7, speedSet;
    public float gravity = -12;
    public float jumpHeight;
    private float screenHalfWidthInWorldUnits;

    public Vector2 hitBox;

    public float velocityY, velocityX;

    public Animator animator;
   
    void Awake()
    {
        cameraBoy = FindObjectOfType<CameraBoy>();
        startHealth = curMaxHealth;
        playerHealth = startHealth;
    }
    private void Start()
    {
        block = GetComponentInChildren<PlayerBlock>();
        shakey = FindObjectOfType<CameraShake>();
        attackBoy = GetComponent<AttackBoy>();
        heartMng = FindObjectOfType<HeartMng>();
        speedSet = walkSpeed;
        hitBox = this.GetComponent<BoxCollider2D>().offset;
        animator = GetComponent<Animator>();
        rb = GetComponent<Rigidbody2D>();
    }

    private void OnCollisionStay2D(Collision2D collision)
    {
        if (collision.gameObject.CompareTag("ground"))
        {
            isGrounded = true;
        }
        if (collision.gameObject.CompareTag("Boss"))
        {
            transform.Translate(Vector3.left / 2);
        }
    }

    private void OnCollisionExit2D(Collision2D collision)
    {
        if (collision.gameObject.CompareTag("ground"))
        {
            isGrounded = false;
        }
    }

    public void Update()
    {
        if (stopMove)
        {
            velocityX = 0;
        }
        if (Input.GetKeyDown(KeyCode.F11)){
            SceneManager.LoadScene(1);
        }
       
        if(playerHealth < 1)
        {
            playerHealth = 0;
            Death();
        }
        velocityY = rb.velocity.y;
        if (!isDead && !stopMove)
        {
            float inputX = Input.GetAxisRaw("Horizontal");
            velocityX = inputX * walkSpeed;
        }
       
        if (Input.GetMouseButtonDown(1))
        {
            foreach (var spell in Magics)
            {
            }
        }
        if (Input.GetKeyDown(KeyCode.G))
        {
            shakey.StartShake(properties);
        }
        if(velocityX > 0)
        {
            isLeft = false;
            this.GetComponent<BoxCollider2D>().offset = new Vector2(hitBox.x,hitBox.y);
            attackPos.position = new Vector3(5.72f, 0, 0) + transform.position;
            animator.SetBool("isRunning", true);
            animator.SetBool("isLeft", false);
        }
        if (velocityX < 0)
        {
            isLeft = true;
            this.GetComponent<BoxCollider2D>().offset = new Vector2(hitBox.x - 0.3f, hitBox.y);
            attackPos.position = new Vector3(-5.72f, 0, 0) + transform.position;
            animator.SetBool("isRunning",true);
            animator.SetBool("isLeft", true);
        }
        else if(velocityX == 0)
        {
            animator.SetBool("isRunning", false);
        }
        if(velocityY < -0.5f)
        {
            isGrounded = false;
            animator.speed = 1;
        }
        else if(velocityY > 0.5f)
        {
            isGrounded = false;
            animator.speed = 0;
        }
        else if(velocityY == 0 && !attackBoy.charging)
        {
            animator.speed = 1;
        }
        Vector2 input = new Vector2(Input.GetAxisRaw("Horizontal"), velocityY);
        Vector2 direction = input.normalized;

        if (Input.GetKeyDown(KeyCode.W)&& !isDead && isGrounded && !stopMove)
        {
            Jump(jumpHeight);
        }
        if (Input.GetKeyUp(KeyCode.W) && !isDead)
        {
            if(velocityY > 0.1f)
            {
                rb.AddForce(new Vector2(0, -5000));
            }
        }

        if (isGrounded == false)
        {
            animator.SetBool("isJumping", true);
        }
        else
        {
            animator.SetBool("isJumping", false);
        }
        if(transform.position.y < cameraBoy.minCamPos.y - 40)
        {
            Death();
        }
    }
    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.CompareTag("Stomp")&& velocityY < -1f && collision.transform.parent.CompareTag("Enemy"))
        {
            collision.GetComponentInParent<EnemyCreator>().splatty = true;
            collision.GetComponentInParent<EnemyCreator>().ReceiveDamage(1);
            if (collision.GetComponentInParent<EnemyCreator>().eHealth >= 1)
            {
                Jump(jumpHeight / 3);
            }
            else
            {
               collision.GetComponent<BoxCollider2D>().isTrigger = true;
            }
        }
        else if (collision.CompareTag("Stomp") && velocityY < -1f && collision.transform.parent.CompareTag("Boss"))
        {
            collision.GetComponentInParent<SnailBoss>().splatty = true;
            collision.GetComponentInParent<SnailBoss>().ReceiveDamage(1);
            if (collision.GetComponentInParent<SnailBoss>().eHealth >= 1)
            {
                Jump(jumpHeight / 2);
                StartCoroutine(PushBack());
            }
            else
            {
                collision.GetComponent<BoxCollider2D>().isTrigger = true;
            }
        }


    }
    void FixedUpdate()
    {    
        transform.Translate(Vector2.right * velocityX * Time.deltaTime);
        transform.Translate(Vector2.up * velocityY * Time.deltaTime);
       
        rb.velocity = Vector2.ClampMagnitude(rb.velocity, maxVelocity);
    }
    void Jump(float jumpSpeed)
    {
        randomSound = Random.Range(0, jumpy.Length);
        rb.velocity = Vector2.up * jumpSpeed;
        jumpy[randomSound].Play();
    }
    public void TakeDamage(int dmg, float force, Vector3 dir)
    {
        if (!block.blockHitter)
        {
            StartCoroutine(Damaged(dmg, force, dir));
        }
    }
    public IEnumerator PushBack()
    {
        pushBack = true;
        yield return new WaitForSeconds(0.2f);
        print("Win");
        velocityX = -100f;
        yield return new WaitForSeconds(0.2f);
        pushBack = false;
    }
    public IEnumerator Damaged(int dmg, float force,Vector3 dir)
    {
        if(dmg > playerHealth)
        {
            dmg = playerHealth;
        }
        heartMng.TakeDamage(dmg);
        if (playerHealth > 0)
        {
            randomSound = Random.Range(0, ouch.Length);
            ouch[randomSound].Play();
        }
        rb.AddForce(dir * force);
        animator.SetBool("isDamaged", true);
        yield return new WaitForSeconds(0.5f);
        animator.SetBool("isDamaged", false);
    }
    public void Death()
    {
        if (!isDead)
        {
            shakey.StartShake(properties);
            isDead = true;
            randomSound = Random.Range(0, deado.Length);
            deado[randomSound].Play();
            playerHealth = 0;
            animator.SetBool("isDead", true);
            StartCoroutine(RestartLevel(3));
        }
    }
    void OnDrawGizmosSelected()
    {
        Gizmos.color = Color.blue;
        Gizmos.DrawWireSphere(attackPos.position,attackRng);
    }

    public void CoinCollect(int coinNum)
    {
        coinBag += coinNum;
    }
    IEnumerator RestartLevel(int delay)
    {
        yield return new WaitForSeconds(delay);
        string sceneName = SceneManager.GetActiveScene().name;
        SceneManager.LoadScene(sceneName);
    }
}

Okay, you have a lot of code there. I would try to simplify the movement and jumping using rigidbody movement, then build on that.

Here is an example.

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

public class Player : MonoBehaviour
{
    protected Rigidbody2D rBody;

    float move = 0f;

    [SerializeField] protected float speed = 3f;
    [SerializeField] float jumpForce = 900f;

    bool grounded = false;

    // Start is called before the first frame update
    protected void Awake()
    {
        rBody = GetComponent<Rigidbody2D>();
    }

    // Update is called once per frame
    protected void Update()
    {
        move = Input.GetAxisRaw("Horizontal");
      
        if (grounded && Input.GetButtonDown("Jump"))
        {
            rBody.AddForce(Vector2.up * jumpForce);
        }
    }

    protected void FixedUpdate()
    {
        if (grounded)
        {
            rBody.velocity = new Vector2(move * speed, rBody.velocity.y);
        }
        else
        {
            rBody.AddForce(new Vector2(move * speed, 0f));

            if (Mathf.Abs(rBody.velocity.x) > speed)
            {
                if (rBody.velocity.x < 0)
                {
                    rBody.velocity = new Vector2(-speed, rBody.velocity.y);
                }
                else
                {
                    rBody.velocity = new Vector2(speed, rBody.velocity.y);
                }
            }
        }

        //Flip the player if you need to
        if (move > 0 && transform.localScale.x < 0)
        {
            transform.localScale = Vector3.one;
        }
        else if (move < 0 && transform.localScale.x > 0)
        {
            transform.localScale = new Vector3(-1f, 1f, 1f);
        }
    }

    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.CompareTag("Ground"))
        {
            grounded = true;
        }
    }

    private void OnTriggerExit2D(Collider2D collision)
    {
        if (collision.CompareTag("Ground"))
        {
            grounded = false;
        }
    }
}

I have a collider as a child to the player that has a layer called GroundSensor this is set to be a trigger. All the ground objects have their layer set to Ground and a tag Ground. So when GroundSensor enters Ground it will set off the OnTriggerEnter2D() method and set grounded to true if it passes the CompareTag method. I haven’t fully tested this setup, but it seems to work fine so far. You can do any check if grounded method to set grounded, it’s up to you.

Maybe test if the player hit your boss and if so get the direction and multiply the negative of that with something to “push” your player away

Okay, after like an hour and a half of fiddling around, I was unable to get the rigidbody working lol, the jump is either way too slow or teleports, the pushback just teleports! I feel like there is an easier way to do this with the system I have in place, isn’t there? Really, the only difference between my current system and the one you gave me is I used transform.Translate instead of AddForce. I wonder if there is a way to just smoothly transform.Translate push him away instead of redesigning my whole system. The walk looks okay, but I think my current rb/translate hybrid is just fine.

@TheOtherUserName_1 I tried this, but the issue is, I want him to be pushed back even if the X velocity is at 0. So It can’t just be polarized.

Well, for clarity’s sake, I’ll add a video here of what it currently looks like.

The part where I kinda jump back off her at like 0:06 is EXACTLY what I want to happen when jumping off her head normally. Also, I do what control midair. Maybe I will add a coroutine to stop midair movement after landing on her head for a brief period. Basically what I’m trying to do is prevent exactly what I do at the end of the video from happening, jumping off her head continuously until she dies lol, the platform is for testing, it will not be there in the actual boss fight.

Okay, well it’s a sloppy solution, but it creates the exact result I wanted.

I realized in my script I had the stopMove bool setting velocityX to 0 as well as limiting the inputX, which are both the exact same effect. So I removed the part where it sets velocityX to 0 and changed the coroutine to this,

    public IEnumerator PushBack()
    {
        stopMove = true;
        pushBack = true;
        yield return new WaitForSeconds(0.1f);
        velocityX = -15;
        velocityY = -5;
        yield return new WaitForSeconds(0.5f);
        pushBack = false;
        stopMove = false;
    }

I probably don’t need the “pushBack” bool but lol, it works perfectly!

1 Like