Unity Vector3 does not add correct amount

I have this script for adding a random spread to the gun’s accuracy each time it fires in my FPS game:

                Vector3 BulletPath = transform.forward;
                print(BulletPath);

                if (CurrentSpread > 0f)
                {
                    BulletPath.x += Random.Range(-CurrentSpread, CurrentSpread);
                    BulletPath.y += Random.Range(-CurrentSpread, CurrentSpread);
                    BulletPath.z += Random.Range(-CurrentSpread, CurrentSpread);
                    print(BulletPath);
                }

The script works just fine for hitscan weapons, but I struggle to make it work for projectile based weapons (such as crossbows and rocket launchers), and because of that I placed the print commands and made a special hitscan weapon just to test the spread. Upon doing so, I noticed something very odd: The change in the BulletPath’s vector3 values is extremely small, most of the time only a few decimals lower or higher. This is really odd because I used a float value for CurrentSpread that is 100 on the test weapon, meaning that by pure chance it’s pretty much impossible for me to get an increase in only a few decimals in 5 out of 5 shots. I used 2 different weapons for this and they both returned this same behavior, and even more odd is that in-game they worked as intended, shooting with extremely low accuracy even though the BulletPath vector barely changed.

What is going on here? I know that the transform.forward is normalized so it only has a magnitude of 1, but shouldn’t it still get float values that range from -101 to 101 since that is added after the normalization?

I’d print CurrentSpread as well, to make sure those values are in fact what you think they are.

1 Like

Sorry the late reply, I guess I can try that.

Here is what I tried:

                Vector3 BulletPath = transform.forward;
                print("BulletPathInitial:" + BulletPath);
                print("CurrentSpreadInitial:" + CurrentSpread);

                if (CurrentSpread > 0f)
                {
                    float CurrentSpreadRandomizedX = Random.Range(-CurrentSpread, CurrentSpread);
                    BulletPath.x += CurrentSpreadRandomizedX;
                    print("CurrentSpreadRandomizedX:" + CurrentSpreadRandomizedX);

                    float CurrentSpreadRandomizedY = Random.Range(-CurrentSpread, CurrentSpread);
                    BulletPath.y += CurrentSpreadRandomizedY;
                    print("CurrentSpreadRandomizedY:" + CurrentSpreadRandomizedY);

                    float CurrentSpreadRandomizedZ = Random.Range(-CurrentSpread, CurrentSpread);
                    BulletPath.z += CurrentSpreadRandomizedZ;
                    print("CurrentSpreadRandomizedZ:" + CurrentSpreadRandomizedZ);

                    print("BulletPathFinal:" + BulletPath);
                }

Here is the result on console:

BulletPathInitial:(0.2, 0.1, -1.0)
CurrentSpreadInitial:0,2
CurrentSpreadRandomizedX:-0,026527
CurrentSpreadRandomizedY:0,02924216
CurrentSpreadRandomizedZ:0,1357439
BulletPathFinal:(0.1, 0.1, -0.8)

I realised the issue with the spread being small: I forgot that CurrentSpread is actually the spread of the weapon at the state of the player (standing, walking, airborne, etc) divided by 1000f, hence why it’s so small. My bad.

                if (!NoSpread)
                {
                    switch (Player.PlayerMovementStatus)
                    {
                        case PlayerStats.PlayerState.Standing:
                            CurrentSpread = StandingSpread / 1000f;
                            break;

                        case PlayerStats.PlayerState.Walking:
                            CurrentSpread = WalkingSpread / 1000f;
                            break;

                        case PlayerStats.PlayerState.Crouching:
                            CurrentSpread = CrouchingSpread / 1000f;
                            break;

                        case PlayerStats.PlayerState.Sprinting:
                            CurrentSpread = AerialSpread / 1000f;
                            break;

                        case PlayerStats.PlayerState.Falling:
                            CurrentSpread = AerialSpread / 1000f;
                            break;

                        case PlayerStats.PlayerState.Jumping:
                            CurrentSpread = AerialSpread / 1000f;
                            break;
                    }
                }
                else
                {
                    CurrentSpread = 0f;
                }

I still don’t know why it isn’t working for projectiles, I know that it has something to do with converting a local vector3 into a global one, but I don’t understand why it works fine for the hitscan ones but not projectile ones. I will make another thread for that however since it’s a different issue.

just a observation but why are you calculating 3 random ranges for a, Vector3 instead of just using Random.insideUnitSphere

you can multiply it by your current speed

BulletPath += Random.insideUnitSphere * CurrentSpread;

this will get a bit more of a correct value too, since its within a max radius not within a max cube of a area.

2 Likes

Oh thanks! I didn’t know that function existed.

I also noticed there is a function called Random.insideUnitCircle. I wonder if the circle is better for reproducing gun spread than a sphere, though if I am getting this correctly the sphere makes so the bullets are more likely to hit the center; There is more volume on the center so there are more points that can be randomly be chosen, and since there is no way to draw a sphere on the wall the bullet will simply pass through that point in space and hit the wall behind. A circle doesn’t have that and bullets can lead anywhere on the circle if their Z value is always the same.

I am not sure what option works best, but I want to make sure I am getting the understanding correctly: Should a spherical distribution make bullets more likely to hit the center than a circular distribution?

This might help. It calculates a point on a spherical cone with a certain spread angle around the main direction.