I am new to using unity and I am still learning C#. So pls bear with me if the problem mentioned below may seem a little odd or easy to fix.
I am creating a project in order to try to shoot a bullet from a turret, and I have included a function in my bullet script that will destroy the bullet after it has crossed certain boundaries and a function in my bulletSpawner script to Instantiate the bullet if it is destroyed. For some reason whenever I play and shoot the bullet and it crosses the boundary, it doesn't get cloned
Here is the Bullet script;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Bullet : MonoBehaviour
{
[SerializeField]
private float Power = 50f;
private Rigidbody2D myBody;
private SpriteRenderer sr;
private float posX;
private float posY;
private void Awake()
{
myBody = GetComponent<Rigidbody2D>();
sr = GetComponent<SpriteRenderer>();
}
void Update()
{
Shoot();
destroyBullet();
}
void Shoot()
{
if (Input.GetKeyDown(KeyCode.Space))
{
myBody.AddForce(new Vector2(Power, Power - myBody.gravityScale),
ForceMode2D.Impulse);
}
}
void destroyBullet()
{
posX = transform.position.x;
posY = transform.position.y;
if (posX > 100 || posX < -100 || posY > 100 || posY < -100)
Destroy(gameObject);
}
}//class
Here is the BulletSpawner script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BulletSpawner : MonoBehaviour
{
public Transform bullet;
void Update()
{
StartCoroutine(SpawnBullet());
}
IEnumerator SpawnBullet()
{
while (!GameObject.FindWithTag("Bullet"))
{
yield return new WaitForSeconds(3);
Instantiate(bullet);
}//while
}//Ienum
}//class
Note: I have attached my Bullet Prefab to bullet in the inspector panel
Whenever the bullet is out of bounds, or gets destroyed, I get this error:
MissingReferenceException: The object of type 'Transform' has been destroyed but you are still trying to access it.
I know it has been destroyed but I want to find a way to access it so that it can clone the bullet(which can be fired again, destroyed and so on...)
Please advise on what I can do to fix this code, or any alternatives in order to prevent such errors from happening in the future. Any feedback would be appreciated :) ...
Thanks in advance!
CodePudding user response:
So as already mentioned the main issue was you used the original scene instance as original bullet for the Instantiate but then destroyed that original.
If you are going to use destroy and instantiate you rather want a prefab asset as bullet original.
However, after also what MickyD mentioned: You will always have one single bullet at a time anyway so you probably don't have to destroy it at all. Simply reuse the very same bullet instance.
And then regarding the Shoot and your Coroutine in Update this is how I would rather do it
The bullet itself doesn't do anything at all and is rather only passive.
And then do
public class BulletSpawner : MonoBehaviour
{
// Now again the one from the Scene!
public Rigidbody2D bullet;
// Yes if Start is IEnumerator it is automatically run as Coroutine by Unity
IEnumerator Start ()
{
// Disable the Rigidbody of the bullet so it doesn't fall down
bullet.isKinematic = true;
// Huh?! This is okey in an IEnumerator as long as you yield inside
while(true)
{
// wait until Space is pressed
yield return new WaitUntil (() => Input.GetKeyDown(KeyCode.Space));
// enable the Rigidbody
bullet.isKinematic = false;
// shoot the bullet
bullet.AddForce(new Vector2(Power, Power - bullet.gravityScale), ForceMode2D.Impulse);
// Now wait until it is out of bounds
//TODO: .. or it hits something ;)
yield return new WaitUntil (() => bullet.position.magnitude > 100f /*TODO || hasHitSomething */);
// Then reset it's movement
bullet.velocity = Vector2.zero;
bullet.angularVelocity = 0;
// Reset position
bullet.position = Vector2.zero;
bullet.rotation = 0;
bullet.transform.position = Vector2.zero;
bullet.transform.rotation = Quaternion.identity;
// disable the Rigidbody for the next iteration
bullet.isKinematic = true;
// Disable the entire object
bullet.gameObject.SetActive(false);
// Wait for the cooldown
yield return new WaitForSeconds (3);
// enable the object (but not yet the Rigidbody)
bullet.gameObject.SetActive(true);
}
}
}
for checking the hasHitSomething I would put a dedicated component on the bullet like e.g.
public class Bullet : MonoBehaviour
{
// Basically simply forward your OnCollisionEnter2D to an event
// so others can listen to it and react
public UnityEvent<Collision2D> OnCollided;
private void OnCollisionEnter2D(Collision2D collision)
{
OnCollided.Invoke(collision);
}
}
and then in the BulletSpawner add
private void HandleCollision(Collision2D collision)
{
// maybe add some filters
hasCollided = true;
}
bool hasHitSomething;
IEnumerator Start ()
{
// Hook up ONCE to the event in the beginning
bullet.GetComponent<Bullet>().OnCollided.AddListener(HandleCollision);
...
// Wait until one of the conditions is true
WaitUntil (() => bullet.position.magnitude > 100f || hasHitSomething);
// reset the flag along with the bullet values
hasHitSomething = false;
...
CodePudding user response:
This answer is just a slight correction to the code that derHugo has written above,
Here is the edited version, the changes are marked in ** below...
public class BulletSpawner : MonoBehaviour
{
public Rigidbody2D bullet;
public Transform bulletPos;//change**
[SerializeField]
private float Power = 50f;
void Start()
{
StartCoroutine(SpawnBullets());
}
void Update() {
}
IEnumerator SpawnBullets()
{
bullet.simulated = false;
while (true)
{
yield return new WaitUntil(() => Input.GetKeyDown(KeyCode.Space));
bullet.simulated = true;
bullet.AddForce(new Vector2(Power, Power - bullet.gravityScale),
ForceMode2D.Impulse);
yield return new WaitUntil(() => bullet.position.magnitude > 100f
|| hasHitSomething */);
bullet.velocity = Vector2.zero;
bullet.angularVelocity = 0;
bulletPos.position = Vector2.zero;//change**
bullet.rotation = 0;
bullet.simulated = false;
bullet.gameObject.SetActive(false);
yield return new WaitForSeconds(3);
bullet.gameObject.SetActive(true);
}
}
}
I have done the two changes because when the bullet gets fired and goes out of bounds, the bullet's position does not go back to it's original position (that is (0,0)) that may be because in the code, bullet is declared as the rigidbody for my Bullet object (this meant that the rigidbody will revert it's position back to the origin, not the Bullet object itelf...which doesn't make sense!) .So I had to declare bulletPos as the Transform component of the Bullet object and change bullet to bulletPos in
bullet.position = Vector2.zero;
This is to ensure that the Bullet object instead of the bullet rigidbody reverts it's position back to the origin.
Don't forget to attach the transform component of the bullet to the script in bulletSpawner, then it will run smoothly...
Just wanted to include this so that the same mistake is not made again.
