0
\$\begingroup\$

I am pretty new to Unity... watched quite a few of Brackey's and other YouTube videos along with a lot of research. I have been trying to add an impulse to my player so that after it comes in contact with an enemy my player will be launched backwards. I have tried setting the horizontal speed to zero for a short time as the character impulse takes effect... Still not working. The 2D rigid body has a mass of 100, a gravity scale of 10, and continuous collision detection. Plz hlp...

Further Details, in case it helps:

  • It is a 2D mobile game that I'm building on Unity version 2021.3.18f1

  • I have added a virtual joystick that converts the input into a normalized vector3 InputDirection.

  • Code has been written in CSharp.

      private void OnCollisionEnter2D(Collision2D col)
      {
    
      if (col.collider.tag == "Ground")
      {
          canJump = true;
      }
    
      if (col.collider.tag == "Slime")
      {
          EnemyCollision(1000000, 15, 0.5f);
      }
      if (col.collider.tag == "Crocodilian")
      {
          EnemyCollision(10000000, 25, 1.0f);
    
      }
    
      }
    
      private void EnemyCollision(int hitForce, int damage, float time) {
          TakeDamage(damage);
          speed=0;
          if(facingDirection != Vector3.right){
              rb.AddForce(Vector2.right * hitForce, ForceMode2D.Impulse);
          } else {
              rb.AddForce(Vector2.left * hitForce, ForceMode2D.Impulse);
          }
          StartCoroutine(EnableMovementAfterDelay(time)); 
     }
    
      private IEnumerator EnableMovementAfterDelay(float delay)
    {
        yield return new WaitForSeconds(delay);
        speed = 80f; 
    //speed is multiplied by the InputDirection.x vector inside Update(). So, by setting it to 0, I am hoping it stops movement.
    // Looks like this: rb.velocity = new Vector2(horizontalInput * speed, rb.velocity.y);
    } 
    
\$\endgroup\$

2 Answers 2

0
\$\begingroup\$

Your mistake appears to be here:

//speed is multiplied by the InputDirection.x vector inside Update(). So, by setting it to 0, I am hoping it stops movement.

// Looks like this: rb.velocity = new Vector2(horizontalInput * speed, rb.velocity.y);

This would do the exact opposite of what you hope it would do. horizontalInput * 0 is 0. So you set the horizontal speed to a value of 0. Which means that you explicitly tell the rigidbody to stop where it is.

If you want the player to be in a "pushed away" state where its velocity is controlled by the physics engine, then you need to leave the velocity as it is, so the physics engine can control it.

A quick and easy way to do that is to just add a bool flag with a name like "playerControlEnabled" to the script that controls the player movement and have your "EnemyCollision" code set and unset that flag. When that variable is false, you simply skip the code that manipulates the player velocity by wrapping it in a if(playerControlEnabled) { block.

There are many other solutions to combine physics-driven and player-driven movement. Some of them might result in much better game-feel than this simple on-off switch. Like interpolating between the physics velocity and the player input velocity, adding the two together or changing the player controls to also use AddForce instead of directly manipulating the velocity. But explaining all of these would break the scope of this answer.

\$\endgroup\$
9
  • \$\begingroup\$ Thank you, that was very helpful! \$\endgroup\$
    – shanksVR
    May 12 at 10:02
  • \$\begingroup\$ I did mean that lol. But by writing that it worked, just not how I wanted, now the character is being launched off indefinitely. I have changed the code in my update() to incorporate the boolean flag as you had suggested and so when there is a collision it makes it such that the object relies on its momentum. When the flag turns off it goes back to using a virtual joystick. \$\endgroup\$
    – shanksVR
    May 12 at 10:38
  • \$\begingroup\$ @Vrishank Isn't that what you wanted? Launch the player away when they get hit, and then after they flew a little give control back to the player? If not, what exactly is the behavior you want and what behavior do you get instead? Also note that you gave the player an extremely high impulse (1 million to 10 million), so when the player gets launched through the whole level, then you might want to reduce those to more reasonable numbers. \$\endgroup\$
    – Philipp
    May 12 at 10:43
  • \$\begingroup\$ So, I reduced the hitForce down to 2f and the time delay till 0.01s now the player gets flung far, sometimes. Other times it doesn't really move. Right now, 1/3 of the time the object almost teleports as opposed to being flung back and the other times it barely moves... (I'm pretty sure it is colliding. My falling animations run). If an impulse acts in a horizontal direction would I need to add drag or something like friction to slow down the object instead of using time to moderate this behavior? And thank you again! \$\endgroup\$
    – shanksVR
    May 12 at 11:22
  • \$\begingroup\$ I have taken a video of the issue--> canva.com/design/DAFis1-INtI/bAozwPa1LiArD62EjdBXvQ/… \$\endgroup\$
    – shanksVR
    May 12 at 11:35
0
\$\begingroup\$

So, I have now managed to solve all of the issues (With major help from Mr. Philipp)->

  1. Instead of adding an impulse to the object. I have emulated that behavior using transformations. After a time delay the object's position is offset by a certain amount. This way the motion can be controlled by the time stamps and the distance it is being offset by.

  2. You can mess around with the time stamps and distances more easily. Here is my code and the adjustments that work for me. At the bottom I have included the if/else statements in my Update() that toggles the movementInput on and off.

     private void EnemyCollision(float push, int damage, float time) 
     {
       TakeDamage(damage);
       beenHit = true;
       PushBack(push);         
       StartCoroutine(EnableMovementAfterDelay(time)); // enable movement after 0.5 seconds
       }
     public void PushBack(float offset)
     {
       if(facingDirection == Vector3.right)
     {
       transform.position += new Vector3(-offset * 0.5f, 0, 0);
       DelayedOffset(new Vector3 (-offset,0, 0), 0.6f);
       DelayedOffset(new Vector3 (-offset,0, 0), 0.5f);
       DelayedOffset(new Vector3 (-offset,0, 0), 0.2f);
     } else { 
       transform.position += new Vector3(offset, 0, 0);       
       DelayedOffset(new Vector3 (offset,0, 0), 0.6f);
       DelayedOffset(new Vector3 (offset,0, 0), 0.5f);
       DelayedOffset(new Vector3 (offset,0, 0), 0.2f);
     }    
     }
    
     //After a certain amount of time should offset the position
     private IEnumerator DelayedOffset(Vector3 offset, float delay)
     {
       yield return new WaitForSeconds(delay);
       transform.position += offset;
     }
    
     private IEnumerator EnableMovementAfterDelay(float delay)
     {
       yield return new WaitForSeconds(delay);
       beenHit = false; // restore horizontal movement
     }
    
     //The code inside of Update() for toggling joystick movement on and off.
     if(beenHit){
         speed = 1.0f;
         horizontalInput = rb.velocity.x;
     } else {
         speed = 160.0f;
         horizontalInput = InputDirection.x;
     }
     rb.velocity = new Vector2(horizontalInput * speed, rb.velocity.y);
    
\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .