Bullet reload problem

Hi I have this script that rotates a turret and tries to set amount of clips, bullets in clips, and total bullet amount. The bullets in clip prints to console and decrements just fine as single bullet should. The problem lies in… Well in a few things actually

  1. The total bullet amount decrements to the clip value that gets set. e.g. Bullets in clip = 500 clips = 2 total bullets = 1000 as the counter counts down bullets in clip goes to 499,498,497,496… but the total bullets goes to 998,996,994,992… I’m honestly confused and overwhelmed, because I cobbled this code together and got lost along the way… What is going wrong here?

  2. The reload function is not working because I don’t know where to put it, let alone know if it actually works because of problem number 1. I’m laughing at myself right now because I know these problems are stemming from how I calculate the total bullet count as a multiplication of clips and bullets in clips, but I don’t know how to fix it.

  3. Problem number three isn’t that urgent, but could be addressed down the road. When bullets run out the turret canons stop rotating. Maybe it has to do with the fact that I am asking if bulletsLeft != 0 in this while loop, but I am not sure.

while( nextFireTime < Time.time && bulletsLeft != 0)

The reason I am including the entire script is the fact that my questions are relevant to the whole thing not just sections of it. Please don’t chastise me for including it. Also I am sure that other people that are starting out like me will find it useful in creating turrets with “clips”

    using UnityEngine; 
    using System.Collections;
    
    public class EnemyControl : MonoBehaviour {
    
        //  Where the enemy bullets will originate from. This is a transform that is assigned in the game. 
        public Transform enemyMuzzle;

    // A Rigidbody placeholder to assign a bullet prefab
    public Rigidbody enemyBullet;

    //  EnemyBulletSpeed default asignment. Change value for each object in explorer
    public float enemyBulletSpeed = 0.5f;

    //  This is the Target Transform to follow while rotating.
    public Transform Target;

    //  The speed at which the body of the turret swivels/rotates.
    public float SwivelSpeed= 1.0f;

    //  Location where the muzzleFlash particle system needs to be physically assigned in the explorer.
    public Renderer muzzleFlash;

    //  The range which the turrets are able to track targets. Default assignment. 
    public float distanceTillShoot = 1;

    //  The rate at which the turret fires it's bullets. This is set as default to 0.05f
    public float fireRate= 0.05f;

    public float force= 10.0f;

    //  The amount of damage that the bullet inflicts on other bodies.
    public float damage= 5.0f;

    //  Amount of clips for the guns that the turret has.
    public int clips= 2;

    // The amount of bullets per clip available to the turret.
    public int bulletsPerClip= 300;

    //  The time it takes to reload a clip for the turret.
    //  Default value is set at 0.5f which means half a second.
    public float reloadTime= 0.5f;

    //  Amount of bullets left in clips
    private int bulletsLeft = 0;

    public ParticleEmitter hitParticles;

    //  These variables keep track of how many bullets are being shot at a given time. Allowing for only 1
    //  bullet per frame
    private float nextFireTime= 0.0f;
    private int m_LastFrameShot= -1;

    // This is for the pointer target. Aiming the ship at the custom mouse pointer.
    private Transform target;
    private Transform myTransform;

    // Sound clip locations. The AudioClips need to be physically dragged here in the explorer.
    // DISABLED: for testing. Don't want to listen to sounds or get errors in console
    // because they aren't assigned.
    // public AudioClip laserSound;
    // public AudioClip thrusterSound;

    // Initialization of game
    void Start() 
    {
    hitParticles = GetComponentInChildren<ParticleEmitter>();

    // We don't want to emit particles all the time,
    // only when we hit something.
    if (hitParticles) hitParticles.emit = false;
    bulletsLeft = bulletsPerClip * clips;

        myTransform = transform;

    } // End Start() function.

    // Update is called once per frame.
    private void Update () 
    {
        float distance = Vector3.Distance(Target.transform.position, transform.position);
        if (distance <= distanceTillShoot && bulletsPerClip > 0)
        {

        Fire();
        GetBulletsLeft();

        }
        // Reload gun in reload Time        
        else 
		{
		
			Reload();
			
		}

        Movement();


    } // End Update() function.

    void  LateUpdate ()
    {

        if (muzzleFlash) {
        // We shot this frame, enable the muzzle flash
        if (m_LastFrameShot == Time.frameCount) 
        {

            muzzleFlash.transform.localRotation = Quaternion.AngleAxis(Random.value * 360, Vector3.forward);
            muzzleFlash.enabled = true;

            if (audio) {

                if (!audio.isPlaying)
                    audio.Play();
                audio.loop = true;

            }
        } 
        else 
        {

            // We didn't, disable the muzzle flash
            muzzleFlash.enabled = false;
            enabled = false;

            // Play sound
            if (audio)
            {

                audio.loop = false;

            }
        }
    }
} // End LateUpdate() function.

    // This is where the movement code goes.
    private void Movement()
    {
        if(Target)
        {

            Vector3 localTarget = transform.InverseTransformDirection(Target.transform.position - transform.position);

            float targetAngle= Mathf.Atan2(localTarget.x, localTarget.z) * Mathf.Rad2Deg;

            float adjustedAngle= Mathf.Lerp(0, targetAngle, Time.deltaTime * SwivelSpeed);

            transform.Rotate(Vector3.up, adjustedAngle);

        }


    } // End Movement() Function.


void Fire ()
{
if (bulletsLeft == 0) return;

    // If there is more than one bullet between the last and this frame
    // Reset the nextFireTime
    if (Time.time - fireRate > nextFireTime)
    {

        nextFireTime = Time.time - Time.deltaTime;

    }

    // Keep firing until we used up the fire time
    while( nextFireTime < Time.time && bulletsLeft != 0) 
    {

        FireOneShot();
        nextFireTime += fireRate;

    }

        Rigidbody clone;    
        clone = Instantiate(enemyBullet, enemyMuzzle.position, enemyMuzzle.rotation) as Rigidbody;

        //clone.transform.Rotate(randomNumberX, randomNumberY, randomNumberZ);
        // clone.rigidbody.AddForce(enemyBullet.transform.forward * enemyBulletSpeed);
        clone.velocity = transform.forward * enemyBulletSpeed;

} // End Fire() function.

void FireOneShot(){
    Vector3 direction = transform.TransformDirection(Vector3.forward);
    RaycastHit hit;

    // Did we hit anything?
    if (Physics.Raycast (myTransform.position, direction, out hit, distanceTillShoot)) {
        // Apply a force to the rigidbody we hit
        if (hit.rigidbody)
        {

            hit.rigidbody.AddForceAtPosition(force * direction, hit.point);

        }

        // Place the particle system for spawing out of place
        // where we hit the surface!
        // And spawn a couple of particles
        if (hitParticles) {

            hitParticles.transform.position = hit.point;
            hitParticles.transform.rotation = 
                Quaternion.FromToRotation(Vector3.up, hit.normal);
            hitParticles.Emit();

        }

        // Send a damage message to the hit object          
        hit.collider.SendMessageUpwards("ApplyDamage", damage, 
            SendMessageOptions.DontRequireReceiver);
    }

    // Register that we shot this frame,
    // so that the LateUpdate function enabled 
    // the muzzleflash renderer for one frame
    m_LastFrameShot = Time.frameCount;
    enabled = true;

} // End FireOneShot() function.	
	
IEnumerator Reload (){

yield return new WaitForSeconds(reloadTime);

	if (clips > 0) 
	{
	    clips--;
	    bulletsLeft = clips * bulletsPerClip;
	}
}
    		
private int GetBulletsLeft()
{
        if(bulletsLeft > 0)
        {
            print("Bullets Left :"+bulletsLeft.ToString());
            print("Bullets Per Clip :"+bulletsPerClip.ToString());
bulletsLeft--;

        }
		
    return bulletsLeft;
} // End GetBulletsLeft() function.
    
    }

If you happen to see any other problems or any improvements you may have to the script, feel free to throw your kind help in it’s direction. Thanks in advance for seeing this question and not running in another direction. It’s long, but everything is pertinent.

The method here is awkward but this looks wrong:

 bulletsPerClip = bulletsPerClip - 1;

Surely you dont want to change how many bullets there are per clip. Shouldnt you be modifying: bulletsLeft

Also you seem to be returning bulletsPerClip.

You were mixing up bulletsPerClip and bulletsLeft so fixed that below. Also I split the routine that display bullets left as it was confusingly also decrementing the bullet count. Put that in a seperate routine. Lastly I give you lots of stats for what bullet you actually got left.

        // Initialization of game
	    void Start() 
	    {
	    hitParticles = GetComponentInChildren<ParticleEmitter>();

	    // We don't want to emit particles all the time,
	    // only when we hit something.
	    if (hitParticles) hitParticles.emit = false;
	    bulletsLeft = bulletsPerClip * clips;

		myTransform = transform;

	    } // End Start() function.

	    // Update is called once per frame.
	    private void Update () 
	    {
		float distance = Vector3.Distance(Target.transform.position, transform.position);
		if (distance <= distanceTillShoot && bulletsLeft > 0) // FABKINS - changed bulletsPerClip to bulletsLeft
		{

		Fire();
		DisplayBulletsLeft();

		}
		// Reload gun in reload Time        
		else 
	       {

		 Reload();

	       }

		Movement();

	    } // End Update() function.

	    void  LateUpdate ()
	    {

		if (muzzleFlash) {
		// We shot this frame, enable the muzzle flash
		if (m_LastFrameShot == Time.frameCount) 
		{

		    muzzleFlash.transform.localRotation = Quaternion.AngleAxis(Random.value * 360, Vector3.forward);
		    muzzleFlash.enabled = true;

		    if (audio) {

			if (!audio.isPlaying)
			    audio.Play();
			audio.loop = true;

		    }
		} 
		else 
		{

		    // We didn't, disable the muzzle flash
		    muzzleFlash.enabled = false;
		    enabled = false;

		    // Play sound
		    if (audio)
		    {

			audio.loop = false;

		    }
		}
	    }
	} // End LateUpdate() function.

	    // This is where the movement code goes.
	    private void Movement()
	    {
		if(Target)
		{

		    Vector3 localTarget = transform.InverseTransformDirection(Target.transform.position - transform.position);

		    float targetAngle= Mathf.Atan2(localTarget.x, localTarget.z) * Mathf.Rad2Deg;

		    float adjustedAngle= Mathf.Lerp(0, targetAngle, Time.deltaTime * SwivelSpeed);

		    transform.Rotate(Vector3.up, adjustedAngle);

		}

	    } // End Movement() Function.

	void Fire ()
	{
	if (bulletsLeft == 0) return;

	    // If there is more than one bullet between the last and this frame
	    // Reset the nextFireTime
	    if (Time.time - fireRate > nextFireTime)
	    {

		nextFireTime = Time.time - Time.deltaTime;

	    }

	    // Keep firing until we used up the fire time
	    while( nextFireTime < Time.time && bulletsLeft != 0) 
	    {

		FireOneShot();
		nextFireTime += fireRate;

	    }

		Rigidbody clone;    
		clone = Instantiate(enemyBullet, enemyMuzzle.position, enemyMuzzle.rotation) as Rigidbody;

		//clone.transform.Rotate(randomNumberX, randomNumberY, randomNumberZ);
		// clone.rigidbody.AddForce(enemyBullet.transform.forward * enemyBulletSpeed);
		clone.velocity = transform.forward * enemyBulletSpeed;

	} // End Fire() function.

	void FireOneShot(){
	    Vector3 direction = transform.TransformDirection(Vector3.forward);
	    RaycastHit hit;
 
	UseBullet();
 
	    // Did we hit anything?
	    if (Physics.Raycast (myTransform.position, direction, out hit, distanceTillShoot)) {
		// Apply a force to the rigidbody we hit
		if (hit.rigidbody)
		{

		    hit.rigidbody.AddForceAtPosition(force * direction, hit.point);

		}

		// Place the particle system for spawing out of place
		// where we hit the surface!
		// And spawn a couple of particles
		if (hitParticles) {

		    hitParticles.transform.position = hit.point;
		    hitParticles.transform.rotation = 
			Quaternion.FromToRotation(Vector3.up, hit.normal);
		    hitParticles.Emit();

		}

		// Send a damage message to the hit object          
		hit.collider.SendMessageUpwards("ApplyDamage", damage, 
		    SendMessageOptions.DontRequireReceiver);
	    }

	    // Register that we shot this frame,
	    // so that the LateUpdate function enabled 
	    // the muzzleflash renderer for one frame
	    m_LastFrameShot = Time.frameCount;
	    enabled = true;

	} // End FireOneShot() function.    

	IEnumerator Reload (){

	yield return new WaitForSeconds(reloadTime);

	    if (clips > 0) 
	    {
		clips--;
		bulletsLeft = bulletsPerClip;
	    }
	}

	private int UseBullet()
	{
		//bulletsLeft = clips * bulletsPerClip;
		if(bulletsLeft > 0)
		{
		    bulletsLeft--;
		}

	    return bulletsLeft;
	} 

	private void DisplayBulletsLeft()
	{

		print("Bullets Left in clip:"+bulletsLeft.ToString());
		print("Clips Left :"+clips.ToString());
		print("Bullets Per Clip :"+bulletsPerClip.ToString());
		var totalBullets=clips*bulletsPerClip+bulletsLeft;
		print("Total Bullets: "+totalBullets.ToString());

	} 
	// End DisplayBulletsLeft() function.
       
            // Returns the amount of bullets left in clip
            private int GetBulletsLeft()
            {
                return(bulletsLeft); 
            }

So I renamed “bulletsLeft” To be “loadedBulletsLeft” and “clips” to be “clipsLeft” Tried out the UseBullet() method in the FireOneShot() method where you suggested but that resulted in an interesting massive single shot occurring so I reverted back to executing UseBullet() after the Fire() method under Update(). After a huge amount of debug prints to try and figure out what was going wrong I found out that the Reload() method wasn’t being called whatsoever because you have to use

StartCoroutine(Reload(reloadTime));

to invoke an IEnumerator such as

IEnumerator Reload (float reloadTimelocal)

you apparently can’t call it directly in C# because of the line

yield return new WaitForSeconds(reloadTimelocal);

strict! strict! strict!

So now the bullets get decremented, Reload() gets called, and clips get decremented as well. I left the line print("Reload"+Time.time.ToString());

just to check the execution time for Reload() Hope this helps people in the future. I noticed that others have asked how to implement reload in their own scripts and this might be helpful to them. I also removed a return value somewhere which was causing the canons to stop rotating once the bullets were out but it’s late here in Seattle and I have classes in the morning so I can’t remember where that was now and comparing code is out of my range of capacity at the moment. The point is that I appreciate the assistance from you Fabkins you have been great even though you almost got frustrated and folded out with your Excuse Quest earlier. Good show! It did turn out to be a unity question after all with the IEnumerator problem. Seeing as though you were really the only one who helped on this problem I’m going to give you the points and close this question out. Thank you kindly sir! I posted the working code below as I would like to keep the non-working code for future reference to show what I had to change. I Think something that I will be learning next is the Mono Debugger as I am sure that would have been much easier to use then all the manual print statements stack tracing… :slight_smile:

using UnityEngine;
using System.Collections;

public class EnemyControl : MonoBehaviour {

    //  Where the enemy bullets will originate from. This is a transform that is assigned in the game. 
    public Transform enemyMuzzle;

    // A Rigidbody placeholder to assign a bullet prefab
    public Rigidbody enemyBullet;

    //  EnemyBulletSpeed default asignment. Change value for each object in explorer
    public float enemyBulletSpeed = 0.5f;

    //  This is the Target Transform to follow while rotating.
    public Transform Target;

    //  The speed at which the body of the turret swivels/rotates.
    public float SwivelSpeed= 1.0f;

    //  Location where the muzzleFlash particle system needs to be physically assigned in the explorer.
    public Renderer muzzleFlash;

    //  The range which the turrets are able to track targets. Default assignment. 
    public float distanceTillShoot = 1;

    //  The rate at which the turret fires it's bullets. This is set as default to 0.05f
    public float fireRate= 0.05f;

    public float force= 10.0f;

    //  The amount of damage that the bullet inflicts on other bodies.
    public float damage= 5.0f;

    //  Amount of clips for the guns that the turret has.
    public int weaponClipsLeft= 2;

    // The amount of bullets per clip available to the turret.
    public int bulletsPerClip= 10;

    //  The time it takes to reload a clip for the turret.
    //  Default value is set at 0.5f which means half a second.
    public float reloadTime= 2.0f;

    //  Amount of bullets left in clips
    private int loadedBulletsLeft;

    public ParticleEmitter hitParticles;

    //  These variables keep track of how many bullets are being shot at a given time. Allowing for only 1
    //  bullet per frame
    private float nextFireTime= 0.0f;
    private int m_LastFrameShot= -1;

    // This is for the pointer target. Aiming the ship at the custom mouse pointer.
    private Transform target;
    private Transform myTransform;

    // Sound clip locations. The AudioClips need to be physically dragged here in the explorer.
    // DISABLED: for testing. Don't want to listen to sounds or get errors in console
    // because they aren't assigned.
    // public AudioClip laserSound;
    // public AudioClip thrusterSound;

 // Initialization of game
        void Start() 
        {
        hitParticles = GetComponentInChildren<ParticleEmitter>();

        // We don't want to emit particles all the time,
        // only when we hit something.
        if (hitParticles) hitParticles.emit = false;
        loadedBulletsLeft = bulletsPerClip * weaponClipsLeft;

        myTransform = transform;

        } // End Start() function.

        // Update is called once per frame.
        private void Update () 
        {
        float distance = Vector3.Distance(Target.transform.position, transform.position);
        if (distance <= distanceTillShoot && loadedBulletsLeft > 0) // FABKINS - changed bulletsPerClip to loadedBulletsLeft
        {

        Fire();
        UseBullet();

        }
       // Reload gun in reload Time        
       

       Movement();


        } // End Update() function.

        void  LateUpdate ()
        {

       if (muzzleFlash) {
       // We shot this frame, enable the muzzle flash
       if (m_LastFrameShot == Time.frameCount) 
       {

           muzzleFlash.transform.localRotation = Quaternion.AngleAxis(Random.value * 360, Vector3.forward);
           muzzleFlash.enabled = true;

           if (audio) {

         if (!audio.isPlaying)
             audio.Play();
         audio.loop = true;

           }
       } 
       else 
       {

           // We didn't, disable the muzzle flash
           muzzleFlash.enabled = false;
           enabled = false;

           // Play sound
           if (audio)
           {

         audio.loop = false;

           }
       }
        }
    } // End LateUpdate() function.

        // This is where the movement code goes.
        private void Movement()
        {
       if(Target)
       {

           Vector3 localTarget = transform.InverseTransformDirection(Target.transform.position - transform.position);

           float targetAngle= Mathf.Atan2(localTarget.x, localTarget.z) * Mathf.Rad2Deg;

           float adjustedAngle= Mathf.Lerp(0, targetAngle, Time.deltaTime * SwivelSpeed);

           transform.Rotate(Vector3.up, adjustedAngle);

       }


        } // End Movement() Function.


    void Fire ()
    {
    if (loadedBulletsLeft == 0) return;

        // If there is more than one bullet between the last and this frame
        // Reset the nextFireTime
        if (Time.time - fireRate > nextFireTime)
        {

       nextFireTime = Time.time - Time.deltaTime;

        }

        // Keep firing until we used up the fire time
        while( nextFireTime < Time.time && loadedBulletsLeft != 0) 
        {

       FireOneShot();
       nextFireTime += fireRate;

        }

       Rigidbody clone;    
       clone = Instantiate(enemyBullet, enemyMuzzle.position, enemyMuzzle.rotation) as Rigidbody;

       //clone.transform.Rotate(randomNumberX, randomNumberY, randomNumberZ);
       // clone.rigidbody.AddForce(enemyBullet.transform.forward * enemyBulletSpeed);
       clone.velocity = transform.forward * enemyBulletSpeed;

    } // End Fire() function.

    void FireOneShot(){
        Vector3 direction = transform.TransformDirection(Vector3.forward);
        RaycastHit hit;

        // Did we hit anything?
        if (Physics.Raycast (myTransform.position, direction, out hit, distanceTillShoot)) {
       // Apply a force to the rigidbody we hit
       if (hit.rigidbody)
       {

           hit.rigidbody.AddForceAtPosition(force * direction, hit.point);

       }

       // Place the particle system for spawing out of place
       // where we hit the surface!
       // And spawn a couple of particles
       if (hitParticles) {

           hitParticles.transform.position = hit.point;
           hitParticles.transform.rotation = 
         Quaternion.FromToRotation(Vector3.up, hit.normal);
           hitParticles.Emit();

       }

       // Send a damage message to the hit object          
       hit.collider.SendMessageUpwards("ApplyDamage", damage, 
           SendMessageOptions.DontRequireReceiver);
        }

        // Register that we shot this frame,
        // so that the LateUpdate function enabled 
        // the muzzleflash renderer for one frame
        m_LastFrameShot = Time.frameCount;
        enabled = true;

    } // End FireOneShot() function.    

    void UseBullet()
    {
		// print("Bullets left in clip before reload check: "+loadedBulletsLeft.ToString());
		int dividedClips = bulletsPerClip * weaponClipsLeft / weaponClipsLeft;
		// print("bulletsPerClip: "+bulletsPerClip.ToString());
		// print("weaponClipsLeft: "+weaponClipsLeft.ToString());
	    // print("dividedClips: "+dividedClips.ToString());
    	if(loadedBulletsLeft == dividedClips)
        {
			print("Reload"+Time.time.ToString());
			StartCoroutine(Reload(reloadTime));
			// print("Reload Coroutine Done"+Time.time.ToString());
		}
    	loadedBulletsLeft--;
		// GetBulletsLeft();
    } 
	
	IEnumerator Reload (float reloadTimelocal){

    yield return new WaitForSeconds(reloadTimelocal);

	   // print("Weapon Clips Left : "+weaponClipsLeft.ToString());
		
       if (weaponClipsLeft > 0) 
       {
			
	   // print("Decrement weaponClipsLeft");
       weaponClipsLeft--;
			
	   // print("weaponClipsLeft after decrement: "+weaponClipsLeft.ToString());
	   loadedBulletsLeft = weaponClipsLeft * bulletsPerClip;
			
       }
		
	// print("BulletsLeft Variable after reload check: "+loadedBulletsLeft.ToString());
		
    } // End Reload() function.
	
       // void GetBulletsLeft()
       // {

       // print("Bullets left in clip after loadedBulletsLeft decrement : "+loadedBulletsLeft.ToString());
       // print("weaponClipsLeft after loadedBulletsLeft decrement: "+weaponClipsLeft.ToString());
       // print("Bullets Per Clip :"+bulletsPerClip.ToString());
       // int totalBullets=weaponClipsLeft*bulletsPerClip+loadedBulletsLeft;
       // print("Total Bullets: "+totalBullets.ToString()); 

       // } // End GetBulletsLeft() function.
}