Search Unity

  1. Good news ✨ We have more Unite Now videos available for you to watch on-demand! Come check them out and ask our experts any questions!
    Dismiss Notice

Instantiate shotgun pellets with a little delay between each other

Discussion in 'Scripting' started by fernito69, Jul 31, 2020.

  1. fernito69

    fernito69

    Joined:
    Oct 27, 2018
    Posts:
    9
    Hello everyone, my first post ever here.

    I thought you're never too old to start so I downloaded Unity and now I'm trying to put together some videogame ideas I had since my childhood. I've had some experience with PHP + JS in the past, and with Python and JS + React for front-end development in the present. I'm no expert, but I'm normally good at understanding the logic behind.

    Anyway, I'm quite motivated so it's not unusual that I plan doing things I still don't have the skills for (like this one right here).

    It's some sort of "magical shotgun". It's working and all, but I'd really really like that the pellets didn't spawn all at the same time when the player fires; I'd like to give them some small delay between each other.

    I've googled a bit and found solutions involving "Invoke", "IEnumerator" and Coroutines, but somehow I haven't been able to them pull off.

    Basically what I need is that the function "fire()" gets executed as many times as there are pellets, but with a little delay between each time. This is my code and a short video showing how it looks right now (never mind the silly hand, it's just a placeholder):



    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class SG : MonoBehaviour
    6. {
    7.     public int pelletCount = 15;
    8.     public float spreadAngle = 5;
    9.     public int pelletFireVelocity=3000;
    10.     public GameObject weaponHolder;
    11.     public GameObject pellet;
    12.     public Transform BarrelExit;
    13.     List<Quaternion> pellets;
    14.  
    15.  
    16.     void Awake()
    17.     {
    18.        //?
    19.     }
    20.  
    21.  
    22.     void Update()
    23.     {
    24.  
    25.         if(Input.GetButtonDown("Fire1"))
    26.         {
    27.             fire();        
    28.         }
    29.  
    30.     }
    31.  
    32.  
    33.     void fire()
    34.     {
    35.         int selectedPowerLevel = weaponHolder.transform.GetComponent<WeaponSwitching>().selectedPowerLevel;
    36.         int newPelletCount = pelletCount + (selectedPowerLevel * 5);
    37.         float newSpreadAngle = spreadAngle + selectedPowerLevel * .75f;
    38.         int newPelletFireVelocity = pelletFireVelocity + selectedPowerLevel * 1000;
    39.  
    40.         pellets = new List<Quaternion>(newPelletCount);
    41.  
    42.         for (int i = 0; i < newPelletCount; i++)
    43.         {
    44.             pellets.Add(Quaternion.Euler(Vector3.zero));
    45.         }          
    46.  
    47.         for (int i = 0; i < newPelletCount; i++)
    48.         {
    49.             pellets[i] = Random.rotation;
    50.             GameObject p = Instantiate(pellet, BarrelExit.position, BarrelExit.rotation);        
    51.             p.transform.rotation = Quaternion.RotateTowards(p.transform.rotation, pellets[i], newSpreadAngle);
    52.             p.GetComponent<Rigidbody>().AddForce(p.transform.forward * newPelletFireVelocity);
    53.             Destroy(p, 3f);        
    54.         }
    55.  
    56.     }
    57. }
    Any ideas on how to approach this would be very welcome!

    Thanks in advance!
     
  2. Yanne065

    Yanne065

    Joined:
    Feb 24, 2018
    Posts:
    175
    Set a fire rate
     
  3. fernito69

    fernito69

    Joined:
    Oct 27, 2018
    Posts:
    9
    Yeah, already did that (attached new code)... but it's not exactly that... I mean that every time that the player fires, I want all of the 15 (or so) pellets be fired in the same "burst", just with a little delay from each other (I mean, hundredths of a second), just for artistic purposes.

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class SG : MonoBehaviour
    6. {
    7.     public float damage = 5f;
    8.     public float fireRate = 1f;
    9.     public int pelletCount = 15;
    10.     public float spreadAngle = 5;
    11.     public int pelletFireVelocity=3000;
    12.     public GameObject weaponHolder;
    13.     public GameObject pellet;
    14.     public Transform BarrelExit;  
    15.     List<Quaternion> pellets;
    16.  
    17.     private float nextTimeToFire = 0f;
    18.  
    19.  
    20.     void Awake()
    21.     {
    22.        //?
    23.     }
    24.  
    25.    
    26.     void Update()
    27.     {
    28.  
    29.         if(Input.GetButton("Fire1") && Time.time >= nextTimeToFire)
    30.         {
    31.             float selectedPowerLevel = weaponHolder.transform.GetComponent<WeaponSwitching>().selectedPowerLevel;
    32.             nextTimeToFire = Time.time + 1f + selectedPowerLevel/5f;
    33.             Debug.Log(nextTimeToFire);
    34.             fire();          
    35.         }
    36.  
    37.     }
    38.  
    39.    
    40.     void fire()
    41.     {
    42.         int selectedPowerLevel = weaponHolder.transform.GetComponent<WeaponSwitching>().selectedPowerLevel;
    43.         int newPelletCount = pelletCount + (selectedPowerLevel * 5);
    44.         float newSpreadAngle = spreadAngle + selectedPowerLevel * .75f;
    45.         int newPelletFireVelocity = pelletFireVelocity + selectedPowerLevel * 1000;
    46.  
    47.         pellets = new List<Quaternion>(newPelletCount);
    48.  
    49.         for (int i = 0; i < newPelletCount; i++)
    50.         {
    51.             pellets.Add(Quaternion.Euler(Vector3.zero));
    52.         }              
    53.  
    54.         for (int i = 0; i < newPelletCount; i++)
    55.         {
    56.             pellets[i] = Random.rotation;
    57.             GameObject p = Instantiate(pellet, BarrelExit.position, BarrelExit.rotation);          
    58.             p.transform.rotation = Quaternion.RotateTowards(p.transform.rotation, pellets[i], newSpreadAngle);
    59.             p.GetComponent<Rigidbody>().AddForce(p.transform.forward * newPelletFireVelocity);
    60.             Destroy(p, 3f);          
    61.         }
    62.  
    63.     }
    64. }
    65.  
     
  4. Yanne065

    Yanne065

    Joined:
    Feb 24, 2018
    Posts:
    175
    Then coroutine will be the best option I think
    Code (CSharp):
    1. for (int i = 0; i < newPelletCount; i++)
    2.         {
    3.             pellets[i] = Random.rotation;
    4.             GameObject p = Instantiate(pellet, BarrelExit.position, BarrelExit.rotation);        
    5.             p.transform.rotation = Quaternion.RotateTowards(p.transform.rotation, pellets[i], newSpreadAngle);
    6.             p.GetComponent<Rigidbody>().AddForce(p.transform.forward * newPelletFireVelocity);
    7.             Destroy(p, 3f);        
    8.         }
    And set a random float to be the time you want and yield return new waitforsecond of the random float for each pellet
     
    Last edited: Jul 31, 2020
  5. Owen-Reynolds

    Owen-Reynolds

    Joined:
    Feb 15, 2012
    Posts:
    1,155
    If you just want to program it straight, make a public variable like
    int burstRemaining=0;
    . Your program would be something like:
    Code (CSharp):
    1.   if(fireDelay<=0 && playerPressedFire) burstsReamining=4;
    2.  
    3.   if(burstsRemaining>0) {
    4.     burstsRemaining--;
    5.     for( ... ) // the same loop you had, to fire 2 or 3 pellets
    6.   }
    When you fire, this frame and the next 3 will spit out a few pellets. If you want more of a delay, make it 8 and have only odd-numbers shoot pellets.

    But Unity coroutines are made for exactly this sort of thing. They're the only way to add "wait until next frame" right in the middle of your code. They take a bit to set-up, but the guides explain it well-enough.
     
    Joe-Censored likes this.
  6. fernito69

    fernito69

    Joined:
    Oct 27, 2018
    Posts:
    9
    OK guys, thanks for your answers. I'll take a look into it and let you know how it goes.
     
  7. Joe-Censored

    Joe-Censored

    Joined:
    Mar 26, 2013
    Posts:
    9,255
    I'd be concerned with how this feels when running on a computer with low frame rates. Might end up feeling more like a machine gun than a shotgun.
     
  8. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    3,799
    Just remember that anything happening in the range of "hundredths of a second" is likely to happen faster than your framerate. Since our code in Unity can only operate more or less once every frame, you'll have to do some trickery to get things to look right. This may mean, for example, spawning multiple projectiles in a single frame and positioning them as if they had been fired at the intended rate based on their velocities and the "real" time they would have spawned if Unity was continuous.

    I'm seeing the "multiple pellets per frame" thing in the code examples above, but missing the "properly positioning them in space as if they had been fired at different times" part.
     
  9. fernito69

    fernito69

    Joined:
    Oct 27, 2018
    Posts:
    9
    It worked apparently quite well with the Coroutine. Here's the code of the new "fire" function and a video of the result:

    Code (CSharp):
    1. void fire()
    2.     {
    3.         int selectedPowerLevel = weaponHolder.transform.GetComponent<WeaponSwitching>().selectedPowerLevel;
    4.         int newPelletCount = pelletCount + (selectedPowerLevel * 5);
    5.         float newSpreadAngle = spreadAngle + selectedPowerLevel * .75f;
    6.         int newPelletFireVelocity = pelletFireVelocity + selectedPowerLevel * 1000;
    7.  
    8.        
    9.         for (int i = 0; i < newPelletCount; i++)
    10.         {
    11.             IEnumerator WaitAndShoot(float time)
    12.             {            
    13.                 yield return new WaitForSeconds(time);            
    14.                 GameObject p = Instantiate(pellet, BarrelExit.position, BarrelExit.rotation);
    15.                 p.transform.rotation = Quaternion.RotateTowards(p.transform.rotation, Random.rotation, newSpreadAngle);
    16.                 p.GetComponent<Rigidbody>().AddForce(p.transform.forward * newPelletFireVelocity);
    17.                 Destroy(p, 3f);
    18.             }
    19.          
    20.             coroutine = WaitAndShoot(0.005f*i);
    21.             StartCoroutine(coroutine);
    22.         }
    23.     }


    At least it's visually what I wanted. I don't know if it's any good in terms of performance, but I guess I'll find that out eventually.

    Thanks guys! You've been quite helpful!

    I'll keep that in mind, thanks!
     
    Joe-Censored likes this.
unityunity