- Home /
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
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?
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.
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.
I also just updated GetBulletsLeft() to only print bulletsLeft as long as bulletsLeft > 0 Otherwise it ends up printing infinitely that Bullets Left :0
Answer by Fabkins · Apr 19, 2012 at 09:31 PM
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);
}
I'm getting a "'EnemyControl.GetBulletsLeft()': not all code paths return a value" error when trying to build on the method
private int GetBulletsLeft()
Ah well I didnt know you were using it elsewhere. Just add:
// Returns the amount of bullets left in clip
private int GetBulletsLeft()
{
return(bulletsLeft);
}
Oh the return value on that method needs to be void not int because it doesn't actually return a value.
If you add the function I just provided it should be good. The other function was to display the bullets.
This is getting me off to a good start!!! Thank you! I usually am a very private person and asking questions concerning problems has been the hardest thing for me to do. It's exciting to actually get feedback though!
Answer by Fabkins · Apr 19, 2012 at 09:12 PM
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.
This is so true thank you! I updated the script above to reflect the changes you suggested.
I'm working through the logic right now. I am putting effort forth! Just need some more nudges in the right direction...
Answer by ozfive · Apr 20, 2012 at 07:57 AM
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... :)
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.
}