Math/Transform problem - Raycast direction is different depending on axis

Hi,

I’m trying to make a predictable faked-random gun recoil pattern for a multiplayer game.
After playing with unity a bit I have a nice result, when unloading my weapon the pattern is always the same and impact decals are placed on points that look like random.

I have still a strange problem : The pattern is different depending if i’m shooting in horizontal or vertical axis.
Here is a screenshot to show you, I have 2 perpendicular walls (perfectly aligned on world rotation) and shot twice on each of them.

I can’t explain this difference.

My prefabs are set like this :
Player > PlayerWeapon > BulletSpawner.
When I shoot, a raycast go forward from bullet spawner.

Here is my shooting code :

                rayCastDirection = bulletSpawner.forward;
                //if(shootNb > 1) {

                    // Here is the pattern function.
                    spread = Mathf.Lerp(0, (Mathf.Cos(shootNb) * 0.05f), (shootNb / BulletsNb)) * 2;
                    spread = Mathf.Clamp(spread, -gunAccuracy, gunAccuracy);

                    rayCastDirection = rayCastDirection + new Vector3(
                        ((shootNb % 2 != 0) ? spread : -spread) + (Mathf.Cos(shootNb) * 0.1f),
                        ((shootNb % 2 != 0) ? -spread : spread) + (Mathf.Cos(-shootNb) * 0.1f),
                        spread
                    );
                //}

            ray = new Ray(bulletSpawner.position, rayCastDirection);

            if(Physics.Raycast(ray, out hit, FireRange)) {
                // hit something
                Debug.DrawRay(bulletSpawner.position, rayCastDirection * Vector3.Distance(hit.point, bulletSpawner.position), Color.green, 10, false);


            } else {
                // nothing hit
                Debug.DrawRay(bulletSpawner.position, rayCastDirection * FireRange, Color.green, 10, false);
            }

        GameObject impactDecal = (GameObject) Instantiate(
            wallImpactDecals[Random.Range(0, wallImpactDecals.Length)],
            hit.point + hit.normal * 0.01f,
            Quaternion.FromToRotation(Vector3.up, hit.normal)
        );

Maybe you’ll find something stupid.
Thanks if you can help or give advice.

I’m not sure, but it seems you’re are computing your spread directly in world space. Maybe you could try to, first, compute the spread in local space, then, rotate the directions with the player rotation. This way the pattern is rotation independent.

Aren’t they already in local space ? Because the direction is not wrong, when i shot it follows the player rotation.

raycastDirection is initialized with bulletSpawner local forward, then I just add a small spread noise.

I tried to replace position with localPosition but it’s worst, raycast goes in strange directions and player is shooting at itself

                    rayCastDirection = rayCastDirection + new Vector3(
                        ((shootNb % 2 != 0) ? spread : -spread) + (Mathf.Cos(shootNb) * 0.1f),
                        ((shootNb % 2 != 0) ? -spread : spread) + (Mathf.Cos(-shootNb) * 0.1f),
                        spread
                    );

Here the rayCastDirection is probably ok and I guess it rotate with the player. However the term you are adding to it is in world space… though I don’t see where you’re using rayCastDirection.

BTW, why are you not using a random function to compute the spread? Why cos?

Post edited, rayCastDirection was called direction (original code is good, is just made it shorter to post).

I’m not using a random function because i need a predictable impact position to keep server and clients synced.
Cos and %2 are little tweaks to make it look random using shootNb.

Did you fixed it?

Do you know that you can seed the random number generator with the same number both on the client and the server so that you obtain the exact same series of random numbers?

I didn’t know, this can useful.
I’ll still use cos because it allows me to have some kind of point distribution continuity, like a natural recoil. In game it feels realistic enough.

I still don’t understand why the pattern is different on horizontal/vertical axis…

I think i’m on the right way !
You were right with the spread being world-spaced !

I replaced

rayCastDirection = rayCastDirection + new Vector3...

by :

rayCastDirection = rayCastDirection + bulletSpawner.TransformDirection(new Vector3...)

Now the pattern is the same regardless of what side i’m shooting :smile:
Thanks for your help

Nice.

As I said, the spread is computed in world space not in local space. You have to rotate it with the player, try this:

        var spreadVectorLocal = new Vector3(
        ((shootNb % 2 != 0) ? spread : -spread) + (Mathf.Cos(shootNb) * 0.1f),
        ((shootNb % 2 != 0) ? -spread : spread) + (Mathf.Cos(-shootNb) * 0.1f),
        spread);

        var spreadVectorWorld = bulletSpawner.localToWorldMatrix.MultiplyPoint(spreadVectorLocal);

        rayCastDirection = rayCastDirection + spreadVectorWorld;

If you want, more realism the spread should increase in size with the distance.

Edit:
Oh! You’ve already figured it out.:slight_smile: