2

I was working on my top down game, but I got stuck. I am trying to make player be knockbacked when an enemy hits him(their rigidbodies collide).

Now the problem is I am using MovePosition for player movement and when I then use anything that will change the velocity of the players RB(setting velocity/AddForce()), the force/velocity is applied just for a split second. I think it is because I use the MovePosition which somehow ignores or resets the velocity.

Is there some way I can go around this without having to make the player controls based on adding forces with max velocity or with calculating how long will the knockback last for?

Shortly, I want the knock back smooth, because now I have to add a really high force to have really fast (basically instant) movement.

Claude Brisson
  • 4,085
  • 1
  • 22
  • 30
  • why not use velocity to move your player too – BugFinder Feb 26 '20 at 13:24
  • @BugFinder It had the same result. The velocity of the knockback gets overwritten by the player movement velocity instantly. – Daniel Martinek Feb 26 '20 at 13:29
  • only if you manually set velocity, not add force – BugFinder Feb 26 '20 at 13:32
  • Right now, I have this for player (in player script): `rb.MovePosition((Vector2)transform.position + velocity * Time.fixedDeltaTime);` and this when enemy hits player (in enemy script): `player.GetComponent().AddForce(velocity.normalized * knockback);` – Daniel Martinek Feb 26 '20 at 13:52
  • You could disable the player movement for half a second or for how much the knockback effect takes. So enemy hits player -> player movement is disabled -> add knockback force -> enable player movement. – Razvan S. Feb 26 '20 at 14:10
  • ok Im pretty sure moveposition doesnt generate velocity, so, if you had had rb.velocity += .. and then applied knock back of the direction of the enemy it would be more smooth – BugFinder Feb 26 '20 at 14:21
  • @RazvanS. I tried you solution. Its not bad, but I would like to be able to move while in knockback, because now it feels bit sketchy. – Daniel Martinek Feb 26 '20 at 14:38
  • @BugFinder I already tried that. The problem is that the velocity does nothing. I can see the velocity is there with Debug.Log(), but the player doesnt seem to move at all – Daniel Martinek Feb 26 '20 at 14:40
  • So while in this 0.5sec knockback state, use AddForce equivalents. Besides why not to use them anyway in the first place? – Eric Feb 26 '20 at 14:40
  • 1
    Player will not move, if you're setting it's position with MovePlayer as it overrides any sort of impact from physics engine thus your velocity too – Eric Feb 26 '20 at 14:41
  • well velocity is a real thing if the player isnt moving is it set kinematic or something? moveposition also is teleporting and kills velocity – BugFinder Feb 26 '20 at 14:41
  • @Rixment So should I use AddForce() for everything? Like movement, knockback. Have max speed and acceleration or...? – Daniel Martinek Feb 26 '20 at 14:44
  • I have rigidbody set to dynamic – Daniel Martinek Feb 26 '20 at 14:45
  • It seems like too many complications with AddForce() for just a movement without acceleration and knockback – Daniel Martinek Feb 26 '20 at 14:46
  • You should decide for yourself, to use `MovePosition` and forget the physics engine or start using `AddForce` and utilise it's power. Or eventually you can try to mix it together setting flags, setting your velocity parameter yourself and make your own physics computations. My advise is to stick with `AddForce`, but of course it all depends on the effect you're after, the kind of movement and reactions you're seeking for. – Eric Feb 26 '20 at 14:51

3 Answers3

1

Try to rewrite it so that the physics engine take care of everything for you. You can try moving your object with the use of AddForce and while at "knockback" state you can utilise AddForce with Force.Impulse as a parameter. It should work as intended and will take the problem of the movement from your shoulders.

Eric
  • 1,685
  • 1
  • 17
  • 32
  • 1
    That sounds good. Just, I am not really sure how to make movement with AddForce when I dont want any acceleration. – Daniel Martinek Feb 26 '20 at 14:52
  • I don't fully understand. Are you mentioning the situation when you take your finger from the keyboard and you want the player to instantly stop itself? – Eric Feb 26 '20 at 14:54
  • 1
    I want the player to instantly reach its max speed when I press a key and stop as you said instantly as well. – Daniel Martinek Feb 26 '20 at 14:58
  • You can try setting up different ForceMode's as parameters for AddForce and see if that's what you want to achieve – Eric Feb 26 '20 at 15:02
  • Also you can try setting up `Rigidbody.velocity` directly if that's what you want. Then you'll omit all the acceleration and deceleration altogether – Eric Feb 26 '20 at 15:05
  • Great. I used AddForce and tried using different ForceModes. Now it works, thanks. – Daniel Martinek Feb 26 '20 at 15:10
  • @DanielMartinek How did you make it? :) I am in the exactly same situation as in your question but I can not find the proper combination of making a nice "knockbacked" effect and at the same time make the player velocity changes sharp. – fguillen Feb 15 '22 at 18:48
0

Just in case someone else stumbles across this: Technically you can do what the TE initially wanted by replacing MovePosition(pos) with transform.position = pos.

This will maintain inertia\velocity etc. and might be what you need in very specific situations (in my case creating a custom type of non-stretchy hinge joint where the enforced position change is very small).

The main downside of this solution is that the physics engine can do unexpected things when the position is shifted into a solid collision. The engine will still try to resolve the situation and push the object out of a collision, but it doesn't know what direction it came from (unlike with MovePosition), shifting possibly in the wrong direction and thus let Objects clip through terrain. That's why in general it's not recommended to touch 'transform' when a rigidbody is applied.

DragonGamer
  • 834
  • 3
  • 9
  • 27
  • I don't think is good advice to play with `transform.position` when you have a RigidBody. You are already mentioning downside, and it is a big one. – fguillen Feb 15 '22 at 18:38
0

The only thing that worked for me was to set up a knockedOut temporal flag to decide when to stop invoking RigidBody.MovePosition() temporarily to give RigidBody.AddForce() some time to act.

Then playing around with forceValue, RigidBody.mass and RigidBody.linearDrag to get the desired effect.

This is the implementation of my PlayerController:

using System.Collections;
using UnityEngine;
using UnityEngine.InputSystem;

public class PlayerController : MonoBehaviour
{
    [SerializeField] float speed = 1f;
    [SerializeField] float impactEffect = 5f;
    [SerializeField] float knockOutTime = 0.05f;

    Vector2 direction = Vector2.zero;
    Rigidbody2D rbody;
    bool knockedOut;
    IEnumerator knockOutCoroutine;

    void Awake()
    {
        rbody = GetComponent<Rigidbody2D>();
    }

    void FixedUpdate()
    {
        if(!knockedOut) // Don't call Move() if I am knockedOut
            Move();
    }

    // Triggered by the InputSystem
    void OnMove(InputValue value)
    {
        // Set the direction value
        direction = value.Get<Vector2>();
    }

    // Controls the normal movement of the Player based on the actual `direction` value
    void Move()
    {
        Vector2 adjustedMovement = direction * speed * Time.fixedDeltaTime;
        Vector2 newPos = rbody.position + adjustedMovement;
        rbody.MovePosition(newPos);
    }

    // Invoke this method when Collision
    public void Impact(Vector2 impactPosition)
    {
        Vector2 impactDirection = ((Vector2)transform.position - impactPosition).normalized;
        rbody.velocity = Vector2.zero;
        rbody.AddForce(impactDirection * impactEffect, ForceMode2D.Impulse);
        KnockOut(knockOutTime);
    }

    void KnockOut(float seconds)
    {
        if(knockOutCoroutine != null)
            StopCoroutine(knockOutCoroutine);

        knockOutCoroutine = KnockOutCoroutine(seconds);
        StartCoroutine(knockOutCoroutine);
    }

    // KnockedOut stop movement effect
    IEnumerator KnockOutCoroutine(float seconds)
    {
        direction = Vector2.zero;
        knockedOut = true;
        yield return new WaitForSeconds(seconds);
        knockedOut = false;
    }
}

fguillen
  • 36,125
  • 23
  • 149
  • 210