Shooting multiple weapons

Hello,

I am trying to implement a system where a controller (MechController) is accessing a child object’s (Mech) weapons prefabs and fires them according to their rate of fire. Each weapon prefab has its own damage and rate of fire variables.

I’m trying to implement it in such a way that the weapon prefabs can be reused on different Mech prefabs so that for example, Mech1 can have 1x Gun and 1x Missile and Mech2 can have 2xGun and 2xMissile. The weapons themselves do not change, but depending on the Mech selected by the player, multiple weapons may be firing at the same time.

What should I use to store the weapons as in the script? List, array? An extra consideration is that the slots may not always be full i.e. Mech 2 might only have 2xGun equipped or 1xGun and 1xMissile even though it has more slots free.

Right now I am able to fire all weapons and while their damage is working properly, they all have the same rate of fire.

Any starting points will be greatly appreciated!

Thank you.

List/Array is a minor detail here… either would work. whichever you feel is more comfortable.
Maybe you need to setup the rate of fire to be different? :slight_smile: :slight_smile:

And as for empty slots… you can check an array (or list) for empty entries. With a list, if you don’t explicitly add an empty entry, you can iterate the list up to ‘.Count - 1’ and you won’t hit anything “empty”. :slight_smile:

Hope that helps/points you in some decent direction.

So this is what I have right now… I put the weapons into an array.

private float nextFire;
public float fireRate;
public GameObject [] weapons;
public Transform [] hardpoints;

if (Input.GetButton("Fire" + m_PlayerNumber) && Time.timeScale > 0 && Time.time > nextFire)
{
nextFire = Time.time + fireRate;

for (int i = 0; i < weapons.Length; i++)
      {
         if(weapons[i] != null)
               {
               Instantiate(weapons[i], hardpoints[i].position, hardpoints[i].rotation);
               }
      }
}

This goes through the array of weapons and instatiates them at the next hardpoint location. However as you can see, fireRate is the same across the board.
Now inside the GameObject weapon I have a float called fireRate that I want to use instead of the above general fire rate. So the flowchart would be something like this:
- Access the array
- If there is a weapon, check its fire rate
- If it can fire (fireRate), instantiate it at the hardpoint location/rotation.
- If it can’t fire, move to and evaluate the next weapon in the array (break; )
Thanks!

that code doesn’t appear to be even correct… You don’t use indexers in the loop; maybe you re-wrote this & didn’t copy/paste?
Please try to use code tags , also when you’re posting code :slight_smile:

You’ve gotta store the next fire time for each weapon/ammo type :slight_smile:

Sorry about that - first time posting code :frowning: I updated it.

Much nicer. :slight_smile:

If you don’t have any weapons around other than the instantiated ones, you can still store the next fire time (you can take the fire rate from the script on the prefab and add that to the current time, like you do now with the fire rate.)
You could , as just 1 example, make an array that is the same size as the weapons and store the firing times there. Also, you’d have to store the rates, to know how to increment them. Shouldn’t be too hard. Then instead of looping over a general fire, you can loop over the objects (as you’re doing) but check their times, instead.
Just make sure that you update the fire rates if you add/alter/remove weapons, etc…

So this is what it looks like right now, and it’s working fine (no errors even if some weapon slots are empty), however the fire rate is uniform accross weapons.

The weapon’s fireRate variable is accessed through:
var rate = weapons*.GetComponent();*
var fireRate = rate.fireRate;
csharp* *if (Input.GetButton("Fire" + m_PlayerNumber) && Time.timeScale > 0 && Time.time > nextFire) { for (int i = 0; i < weapons.Length; i++) { if(weapons[i] != null) { var rate = weapons[i].GetComponent<PlayerWeaponDamage>(); var fireRate = rate.fireRate; nextFire = Time.time + fireRate; Instantiate(weapons[i], hardpoints[i].position, hardpoints[i].rotation); GetComponent<AudioSource>().Play (); } } }* *

The updated version doesn’t quite make sense to me for achieving your goal. When ever you add/load/equip a weapon (not sure how you go about that in your game), that’s when you want to assign the fire rate, I would think.
When you fire, you want to not check the fire rate on the whole group at once, but check the current time against the nextFire for each weapon individually and if it’s the right time, then fire… :slight_smile:
And you didn’t include an array/list for nextFire, either. Once you setup the fireRate outside of where it is now, that’d be an array/list, too. :slight_smile:

Thank you for your reply. How you translate that to flowchart or code?

well, if there’s a place where you add weapons, whichever weapons index you’re adding (or altering), that’s the index of the firerate and nextfire you’d want to update.
if you have 2 weapons say, and you add a 3rd in there.
fireRate[2] = whatever_fire_rate;
nextFire[2] = Time.time // right now, because it just loaded.

as for the flow of how it would go:
if the fire button is hit …
check each weapon slot…
if it’s not empty
check weapon [0…2 - assuming 3 weapons possible] 's next fire time
if that’s less than or equal to the current time, fire weapon [index we’re at]
set this weapon’s nextfire to the time + rate (all 3 of these variables would have the same index)
… repeat until all weapons have been checked.

Thank you so very much, your help has been invaluable! I’ll test it out and report back with the results.

Cool, cool. Hope it goes well :slight_smile: You’re welcome.

So I finally got around to working on this, and this is what I’ve come up with currenty:

       void Update ()
        {
            if (Input.GetButton("Fire" + m_PlayerNumber))
                {
                    if (weapons[0] != null){fire0();}
                    if (weapons[1] != null){fire1();}
                    if (weapons[2] != null){fire2();}
                    if (weapons[3] != null){fire3();}
                }
        }

        void fire0()
        {
            if (Time.timeScale > 0 && Time.time > nextFire0)
            {
            var rate = weapons[0].GetComponent<PlayerWeaponDamage>();
            var fireRate = rate.fireRate;
            nextFire0 = Time.time + fireRate;
            Instantiate(weapons[0], hardpoints[0].position, hardpoints[0].rotation);
            GetComponent<AudioSource>().Play ();
            }
        }

        void fire1()
        {
            if (Time.timeScale > 0 && Time.time > nextFire1)
            {
            var rate = weapons[1].GetComponent<PlayerWeaponDamage>();
            var fireRate = rate.fireRate;
            nextFire1 = Time.time + fireRate;
            Instantiate(weapons[1], hardpoints[1].position, hardpoints[1].rotation);
            GetComponent<AudioSource>().Play ();
            }
        }

While the code works, obviously this is sub-optimal and I would like to use something like this:

for (int i = 0; i < weapons.Length; i++)
                    {
                        if(weapons[i] != null)
                        {
            if (Time.timeScale > 0 && Time.time > nextFire1)
            {
                        var rate = weapons[i].GetComponent<PlayerWeaponDamage>();
                        var fireRate = rate.fireRate;
                        nextFire = Time.time + fireRate;
                        Instantiate(weapons[i], hardpoints[i].position, hardpoints[i].rotation);
                        GetComponent<AudioSource>().Play ();
             }
                        }
                    }

But I can’t figure out how to convert nextFire to an array. I’ve tried to declare it using:

public float [ ] nextFire

And then changing

Time.timeScale > 0 && Time.time > nextFire1 to Time.timeScale > 0 && Time.time > nextFire
and
nextFire = Time.time + fireRate;
But no dice, even if I initialize the array using for example
nextFire[0] = 1.0f;

One way to do it is nextFire could be in the PlayerWeaponDamage on your game object just like you have fireRate. Initialize it to 0. So it’s look something like this:

for (int i = 0; i < weapons.Length; i++)
{
    if(weapons[i] != null)
    {
        var rate = weapons[i].GetComponent<PlayerWeaponDamage>();
        if (Time.timeScale > 0 && Time.time > rate.nextFire)
        {             
                    rate.nextFire = Time.time + rate.fireRate;
                    Instantiate(weapons[i], hardpoints[i].position, hardpoints[i].rotation);
                    GetComponent<AudioSource>().Play ();
        }
    }
}

I’m not sure it would make sense to store the nextFire values outside of that PlayerWeaponDamage class… if you were really anal about separating things, maybe it would (in which case you’d have to set up some sort of array I guess to map the nextFire value to a particular weapons reference). That’s how I’d do it though.

Why not move the whole Fire method into the PlayerWeaponDamage component. Let each instance of PlayerWeaponDamage manage its own internal fire rate.

How would instantiate work in that case?

My recommendation is to push this kind of logic as far down into the subcomponents as possible. In this case, I guess you would need to move the weapon type and knowledge of its position and rotation down as well.

If that’s too hard for now, try a smaller step. You could have a CanFire method on your PlayerWeaponDamage which simply returns a Boolean if its internal fire rate allows it.

Thank you both for your replies. I’ll try both and see what works.

Actually, in hindsight, the way eisenpony describes it is how I personally have done it, as well as setting up an interface for my shootable weapons. basically let the weapon’s script handle the logic of whether it can fire again or not.

Something like this?