I’m making a 2d SHMUP, with one of the weapons available to the player being a missile that automatically homes in on enemies. When there are no enemies on the screen, the code returns a null reference error, the missile launcher continues firing, and the missiles float in place forever. I’m pretty new to C# and Unity scripting - I only started with this in February - so any advice would be helpful.
The code:
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
[RequireComponent(typeof(Rigidbody2D))]
public class MissileScript : MonoBehaviour
{
public Transform target;
public float speed = 10f;
public float rotateSpeed = 300f;
public Vector2 direction = new Vector2(1, 0);
MissileTurretScript[] missileTurret;
private Rigidbody2D rb;
// Use this for initialization
void Start()
{
rb = GetComponent<Rigidbody2D>();
target = GameObject.FindGameObjectWithTag("Enemy").transform;
missileTurret = transform.GetComponentsInChildren<MissileTurretScript>();
Destroy(this.gameObject, 3);
}
void FixedUpdate()
{
TryToFindTarget();
if (target == null)
{
transform.position = Vector2.up * speed * Time.deltaTime;
Destroy(this.gameObject, 1);
return;
}
if (target != null)
{
Vector2 direction = (Vector2)target.position - rb.position;
direction.Normalize();
float rotateAmount = Vector3.Cross(direction, transform.up).z;
rb.angularVelocity = -rotateAmount * rotateSpeed;
rb.velocity = transform.up * speed;
}
}
public void OnTriggerEnter2D(Collider2D collision)
{
// Put a particle effect here
Destroy(this.gameObject);
}
public void TryToFindTarget()
{
target = GameObject.FindWithTag("Enemy").transform;
}
}
This is code from this video tutorial that I’ve modified to point towards enemies and not players:
Null ref just means you’re trying to access something that isn’t there. Consider this bit:
public void TryToFindTarget()
{
target = GameObject.FindWithTag("Enemy").transform;
}
If there is no enemy and FindWithTag finds nothing, thus returning null, you cannot access the .transform property of nothing.
So you’d want to not immediately access the property and instead assign a value or null depending on whether you get something:
public void TryToFindTarget()
{
GameObject targetGO = GameObject.FindWithTag("Enemy");
taget = targetGO ? targetGO.transform : null; // this is short syntax for an if-else statement
}
…yeah, this seems like the obvious solution. Got some more cleaning up to do - the damn things still don’t seem to want to despawn - but that fixes the NRE error. Thanks!
They don’t despawn because you’re repeatedly resetting the destroy time to a second later and so the time is never reached. Instead try this:
float targetSeenTime;
void FixedUpdate()
{
if (target == null)
{
TryToFindTarget();
if (target == null && Time.time>targetSeenTime+1) // still no target and a second has passed since we last saw a target?
Destroy(gameObject);
}
else // I've not checked your code below..
{
targetSeenTime=Time.time;
Vector2 direction = (Vector2)target.position - rb.position;
direction.Normalize();
float rotateAmount = Vector3.Cross(direction, transform.up).z;
rb.angularVelocity = -rotateAmount * rotateSpeed;
rb.velocity = transform.up * speed;
}
}
(I don’t think the “this” part of “this.gameObject” is necessary but it works…)
This also seems to have allowed the initial cleanup destroy(this.gameObject, 3) in void start to work - it’s duplication of effort, but if I keep that one it allows me to have a time-out explosion linked to it.