Make gun damage on projectile impact, not on click.

Hey guys, I’m still working on my gun script here.

So i am just now attempting to learn about sending out projectiles when I shoot my gun.

Essentially my error here is when I shoot my objects take damage and force before the projectile even hits them.

If anyone would be so kind as to inspect my script and inform me on why it is my projectiles do not interact correctly with the world I’d be greatly appreciative.

Also as a side note, how would I delete the bullet prefab upon impact with objects?

Just wanna say thanks again for any help that comes my way. <3

Below is my gun script currently.

using UnityEngine;

public class Carbine : MonoBehaviour
{
    public int minimumDamage = 5;

    public int maximumDamage = 15;

    public int maximumRange = 50;

    public float fireRate = .05f;

    public float impactForce = 30f;

    public float projectileSpeed = 1000f;

    private float nextFire = 0f;

    public ParticleSystem MuzzleFlash;

    public Camera MainCamera;

    public GameObject ImpactEffect;

    public GameObject MainProjectile;

    public GameObject ProjectileEmitter;

    void FixedUpdate()
    {
        if (Input.GetButton("Fire1") && Time.time > nextFire)
        {
            Shoot();
        }
    }

    void Shoot()
    {
        GameObject Temporary_Bullet_Handler;

        Temporary_Bullet_Handler = Instantiate(MainProjectile, ProjectileEmitter.transform.position, ProjectileEmitter.transform.rotation) as GameObject;

        Rigidbody Temporary_RigidBody;

        Temporary_RigidBody = Temporary_Bullet_Handler.GetComponent<Rigidbody>();

        Temporary_RigidBody.AddForce(transform.forward * projectileSpeed);

        Destroy(Temporary_Bullet_Handler, 1f);

        MuzzleFlash.Play();

        nextFire = Time.time + fireRate;

        MuzzleFlash.Play();

        nextFire = Time.time + fireRate;

        RaycastHit hit;

        if (Physics.Raycast(MainCamera.transform.position, MainCamera.transform.forward, out hit, Mathf.Infinity))
        {

            if (hit.rigidbody != null)
            {
                hit.rigidbody.AddForce(-hit.normal * impactForce);
            }

            Instantiate(ImpactEffect, hit.point, Quaternion.LookRotation(hit.normal));

            GameObject go = Instantiate<GameObject>(ImpactEffect, hit.point, Quaternion.LookRotation(hit.normal));

            IDamageable damageable = hit.collider.GetComponent<IDamageable>();

            nextFire = Time.time + fireRate;

            if (damageable != null)
            {
                float normalisedDistance = hit.distance / maximumRange;

                if (normalisedDistance <= 1)
                {
                    damageable.DealDamage(Mathf.RoundToInt(Mathf.Lerp(maximumDamage, minimumDamage, normalisedDistance)));
                }
            }
        }
    }
}

And below is my gun script without my code for adding bullets.

using UnityEngine;

public class Carbine : MonoBehaviour
{
    public int minimumDamage = 5;

    public int maximumDamage = 15;

    public int maximumRange = 50;

    public float fireRate = .05f;

    public float impactForce = 30f;

    private float nextFire = 0f;

    public ParticleSystem MuzzleFlash;

    public Camera MainCamera;

    public GameObject ImpactEffect;

    void FixedUpdate()
    {
        if (Input.GetButton("Fire1") && Time.time > nextFire)
        {
            Shoot();
        }
    }

    void Shoot()
    {
        MuzzleFlash.Play();

        nextFire = Time.time + fireRate;

        RaycastHit hit;

        if (Physics.Raycast(MainCamera.transform.position, MainCamera.transform.forward, out hit, Mathf.Infinity))
        {

            if (hit.rigidbody != null)
            {
                hit.rigidbody.AddForce(-hit.normal * impactForce);
            }

            Instantiate(ImpactEffect, hit.point, Quaternion.LookRotation(hit.normal));

            GameObject go = Instantiate<GameObject>(ImpactEffect, hit.point, Quaternion.LookRotation(hit.normal));

            IDamageable damageable = hit.collider.GetComponent<IDamageable>();

            nextFire = Time.time + fireRate;

            if (damageable != null)
            {
                float normalisedDistance = hit.distance / maximumRange;

                if (normalisedDistance <= 1)
                {
                    damageable.DealDamage(Mathf.RoundToInt(Mathf.Lerp(maximumDamage, minimumDamage, normalisedDistance)));
                }
            }
        }
    }
}
1 Like

This is because your Shoot() command does two things: 1.) it emits a projectile object (line 41 of the first script), and 2.) runs a raycasting operation (line 61 of the first script) that finds the first unobstructed object in front of the gun. You’re confusing the logic for a projectile weapon (e.g., missile) with the logic for a hitscan weapon (e.g., firearm).

If the raycast detects anything destructible in its path, it will instantaneously deal damage to it. For a projectile-based weapon, you should remove the raycasting code (lines 61 to 86). You then need to ensure the projectile MainProjectile is equipped with a Collider and a custom projectile script to handle collision events. You’ll override OnCollisionEnter() with the DealDamage() instructions there.

In short, remove the raycasting code, add a collider to your projectile, create a custom script called Projectile (or whatever you want to call it), add that script to the projectile, and then drop the following in there:

// Make sure you copy all of your constants/parameters (e.g., maximumRange)
void OnCollisionEnter(Collision collision)
{
    IDamageable damageable = collision.gameObject.GetComponent<IDamageable>();
  
    if (damageable != null)
    {
        float normalisedDistance = hit.distance / maximumRange;

        if (normalisedDistance <= 1)
        {
            damageable.DealDamage(Mathf.RoundToInt(Mathf.Lerp(maximumDamage, minimumDamage, normalisedDistance)));
        }
    }
}
2 Likes

Hey, I’m getting the error “The name"hit"does not exist in the current context”

Is this due to hit being called by my RayCast? If so what should I call it to instead?

Also, this is what I came up with below:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ProjectileDamage : MonoBehaviour
{
    public int maximumRange = 50;

    public int minimumDamage = 5;

    public int maximumDamage = 15;

    RaycastHit hit;

    void OnCollisionEnter (Collision collision)
    {
        IDamageable damageable = collision.gameObject.GetComponent<IDamageable>();

        if (damageable != null)
        {
            float normalisedDistance = hit.distance / maximumRange;

            if (normalisedDistance <= 1)
            {
                damageable.DealDamage(Mathf.RoundToInt(Mathf.Lerp(maximumDamage, minimumDamage, normalisedDistance)));
            }
        }
    }
}

Apologies! I made a mistake when I copied your code over to my snippet. The object “hit” doesn’t exist in that context, as it’s something created by the raycast operation. In fact, normalisedDistance, hit, and maximumRange don’t make any sense either, since they only apply to the raycast context. In your new code I would remove everything from line 21 to line 26, and simply replace it with:
damageable.DealDamage(damage);

Add damage as a float field in the prologue of the class, and remove maximumRange, minimumDamage, and maximumDamage. Oh and also get rid of hit (line 13).

1 Like

Thanks brother, you really helped me out with this one!

1 Like