Adding impulse based to an object using other object's orientation

Hi guys,

I’ve got a weird case here. I’m trying to implement a knockback effect of a bullet if it hits an enemy. This is some of the snippets of the code:

Bullet.cs

    ...
    public override voidOnEnterCollision(Collision collision)
    {
        try
        {
            Enemy enemy = collision.collider.GetComponent<Enemy>();
            if (enemy != null)
            {
                Vector3 direction = this.transform.forward;
                //Vector3 direction = rigidBody.velocity.normalized;
                Knockback(enemy, direction, 10000);
             }
        }
        catch (InvalidCastException)
        {
            Debug.Log("The entity that collided is not an Enemy type. Cannot apply knockback.");
        }
    }

    void Knockback(Enemy target, Vector3 knockbackDirection, float knockbackForce)
    {
        target.ApplyForce(knockbackDirection, knockbackForce);
    }

    ...

Enemy.cs

    ...
    public void ApplyForce(Vector3 forceDirection, float forceValue)
    {
        rigidBody.AddForce(forceDirection * forceValue, ForceMode.Impulse); ///< This doesn't work
        //rigidBody.AddForce(transform.forward * forceValue, ForceMode.Impulse); ///<This works but it can only move the enemy forwards
    }

    ...

I only put the necessary methods and not the whole class to make it simpler. As you can see, if the bullet collided with the enemy, it will call a method on the enemy to apply / add force to itself. The one I wanted (which is failed) is the uncommented on. But it didn’t work as I thought it would. It either doesn’t do anything / the enemy is still stationary on its original position. Or, if it gets hit more than one bullet in a short time, it will move to an arbitrary direction, often the bullet’s back / -forward direction. I even tried using the velocity of the bullet, and still it doesn’t work. Now if I commented the top line in the “Enemy.ApplyForce” method and uncommented the one below it, the impulse worked fine. So I’m stuck here scratching my head and pretty much confused. What did I do wrong here? Can anybody help me?

Thanks in advance.

Not related probably, but are you missing a right brace there after if(enemy !=null)?

Why use direction = this.transform.forward instead of just transform.forward? Is it the same?

What I’d probably do is print(forceDirection) in your ApplyForce function in the cases you have where it doesn’t work just to make sure direction isn’t going to 0,0,0 for some reason. Might as well print forceValue too while you’re at it. I don’t see anything wrong there, but that’s what I’d check first. If that’s all fine then I don’t know what the problem could be. Maybe another set of eyes will spot something.

Also most likely not related, i assume [quote=“bawenang, post:1, topic: 586707, username:bawenang”]
public override voidOnEnterCollision(Collision collision)
[/quote]
is meant to be “void OnEnterCollision”? Even though I don’t quite understand why you would put “public override” in front of it. Are you implementing an interface here?

“this.transform.forward” is indeed the same as “transform.forward”, so there should be no problem at that part.
If the bullet is allways traveling along it’s transforms forward direction, “this.transform.forward” should actually give the same result as “rigidBody.velocity.normalized”.

First thing to test would probably be if the OnCollisionEnter method is really called each time the bullet collides with an enemy, or if some collisions are just not detected. Depending on the bullet speed it might be an better approach to detect the collisions via Raycasting.

Todd Wasson is totally right, you should also check the direction vector, even though code wise it should always have the length 1 and the forceValue should always be 10000 resulting in an impulse force of 10kN. So the next question would be: How much mass does your enemy have?

Last thing that comes to my mind as the source of your problem: Could it be possible that the bullet movement direction is pointing towards the ground just steeply enough to “slam” the enemy into it so that no movement is visible?

The try-catch is not neccessary - you’re not doing any casts, so you will never get an InvalidCastException.

Other than that, the other posters have the problem down - your bullet is probably pointing down. If the bullet is an fbx model (or a blender file), it will automatically be rotated to -90 on the x-axis*, which means that transform.forward is straight down.

The easiest fix is to store the bullet’s initial direction when you shoot it, and then apply that as your force direction.

Using the bullet’s current velocity is not a good idea - it will probably have changed due to hitting the enemy, if it’s a physics object.

  • this happens because Unity and most 3D programs have different axes, and Unity has solved that through a terrible hack.

Hi guys,

Sorry for the late reply. I was busy doing my other project which is in a post production phase.

@
Yeah, thanks for noticing the typo. Editing it now. transform forward and this.transform.forward is the same thing, I believe. One is implicit the other explicit.

@Damasch
Yes, I believe you were right. The bullet was slightly facing down. Thus the enemy was slammed into the ground. I’m using override because I am inheriting the class from a parent class which already has a base OnCollision method. (Bullet → KnockbackBullet)

@Baste
Using the velocity was indeed not a good idea because it was Vector3.zero after hitting the object. I thought it would give the velocity before impact. In the end, I fixed it by using your idea, but slightly modified. I stored the initial normalized velocity of the rigidbody instead. At first, I was storing the initial forward vector (with a 0 y value) using this:

void override Start()
{
direction = this.transform.forward;
direction = new Vector3(direction.x, 0.0f, direction.z);
direction.Normalize();
}

However, I don’t know why but sometimes the knockback direction was wrong. Sometimes it can even be knocked forward (so it’s a knockfront maybe?). So now I’m storing the the initial normalized velocity after a few frame like this:

void override Start()
{
StartCoroutine(SetDirection());
}

IEnumerator SetDirection()
{
yield return 0;
yield return 0;

direction = rigidBody.velocity;
direction = new Vector3(direction.x, 0.0f, direction.z);
direction.Normalize();
}

Although I’m rather concerned about the performance because of square roots that were used in the diretion.Normalize() method. Because it is possible to have like 30 bullets at once in a scene. But it’s pretty good for now. At least it worked now.

Thanks a lot guys.

The direction.Normalize() method overhead call itself is much more expensive than the square root operation inside it. If you’re concerned with performance here, get rid of all the math function calls and unroll everything like this:

http://www.performancesimulations.com/wp/how-to-get-big-speed-increases-in-unitys-physics-or-any-math-heavy-code/

Granted, if you’ve only got 30 bullets or so in your scene, it’s probably not worth the trouble.

You don’t have to normalize the direction in that coroutine - just leave it as it is, and normalize it when you use it.

Granted, an important principle is that you should not optimize before you actually need to (because optimization often leads to less readable code), but hey, if you’re worried.

I see. Yeah, that makes sense. It is a small issue, but I’d like to squeeze out any performance enhancement that I can have since I’m developing for mobile. Thanks a lot guys.