Scripting a Shield to Spawn when the Shield Game Object is not present?

I am new to game development and at this point have just worked on a few tutorials. I try to add at least one or two new features as a learning challenge. I am adding a shield to the tutorial Space Shooter. The concept is I want a shield to spawn, take the hit in place of the player ship, be destroyed by the collision, and spawn a new shield after a public variable amount of time. I have gotten to the point where I can instantiate the shield, as a child object on the player ship on a button press Fire2 for testing purposes. What I am looking for help on is replacing the code for the button press with code that will check for if there is a shield, and if not wait an amount of time before spawning one.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[System.Serializable]
public class Boundary
{
    public float xMin, xMax, zMin, zMax;
}

public class PlayerController : MonoBehaviour
{
    public float speed;
    public float tilt;
    public Boundary boundary;
    public GameObject shot;
    public GameObject shield;
    public Transform shieldspawn;
    public Transform shotSpawn;
    public float fireRate;
    public float shieldSpawnRate;

    private Rigidbody rb;
    private float nextFire;
    private AudioSource audioSource;
    
        private void Start()
    {
        rb = GetComponent<Rigidbody>();
        audioSource = GetComponent<AudioSource>();
    }
    private void Update()
    {
        if (Input.GetButton("Fire1") && Time.time > nextFire)
        {
            nextFire = Time.time + fireRate;
            Instantiate(shot, shotSpawn.position, shotSpawn.rotation);
            audioSource.Play();
        }
       if (Input.GetButton("Fire2"))
        {
            GameObject go = Instantiate(shield, shieldspawn.position, shieldspawn.rotation);
            go.transform.parent = GameObject.Find("Player").transform;
        }
    private void FixedUpdate()
    {
        float moveHorizontal = Input.GetAxis("Horizontal");
        float moveVertical = Input.GetAxis("Vertical");

        Vector3 movement = new Vector3 (moveHorizontal, 0.0f, moveVertical);
        rb.velocity = movement * speed;

        rb.position = new Vector3
            (
                Mathf.Clamp (rb.position.x, boundary.xMin, boundary.xMax),
                0.0f,
                Mathf.Clamp (rb.position.z, boundary.zMin, boundary.zMax)
            );
        rb.rotation = Quaternion.Euler(0.0f, 0.0f, rb.velocity.x * -tilt);
    }


}

The closest I got on my own was using if (shield != null) in place of the Input, however it spawns a shield every single frame. I pretty much knew it wouldn’t work but I was focusing on at least getting the shield to spawn once on its own without being placed or triggered by input.

Well, I’ll make a suggestion. Instead of instantiating the shield. Add the shield to the ship gameobject. Turn it off. Then when it’s suppose to “activate” turn the gameobject on. When it takes a hit, turn it off again.

That way your shield is always assigned to the same variable. You don’t have to create a new one or destroy it or reassign it, etc. It’s either on and working or off and not.

You would then check shield.activeSelf to see if it’s on or not.

Otherwise, with your current code, each time you spawn a shield, you’ll have a variable that is your shield variable. If it’s not null, you have a shield, if it’s null, you don’t.

If it was a stand alone project I would definitely prefer to active and deactivate the shield. I went with instantiating just because of how the tutorial had programmed the other scripts. I was looking to avoid having to change multiple scripts to make the shield work. I am still new so there may be better ways to do that, than what I came up with.

Then the second method will have to do. You’ll need to assign the shield to a variable when it’s created. ANd then make that variable null when you destroy it.

I am not 100% sure what you are saying, as I said I am new. I do not think what I did is what you intended, however it does seem to work at least for the first stage of the issue as to actually get the shield to spawn on its own. I created a private bool shieldstatus, I moved the shield code to a function as it is growing and going to grow more. It seems doing it this way I have to have a public function to set it back to false, and call that function in the Destroy script.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[System.Serializable]
public class Boundary
{
    public float xMin, xMax, zMin, zMax;
}

public class PlayerController : MonoBehaviour
{
    public float speed;
    public float tilt;
    public Boundary boundary;
    public GameObject shot;
    public GameObject shield;
    public Transform shieldspawn;
    public Transform shotSpawn;
    public float fireRate;
    public float shieldSpawnRate;

    private Rigidbody rb;
    private float nextFire;
    private AudioSource audioSource;
    private bool shieldstatus;

    private void Start()
    {
        rb = GetComponent<Rigidbody>();
        audioSource = GetComponent<AudioSource>();
        shieldstatus = false;
    }
    private void Update()
    {
        if (Input.GetButton("Fire1") && Time.time > nextFire)
        {
            nextFire = Time.time + fireRate;
            Instantiate(shot, shotSpawn.position, shotSpawn.rotation);
            audioSource.Play();
        }
        ShieldState();

    }
    private void FixedUpdate()
    {
        float moveHorizontal = Input.GetAxis("Horizontal");
        float moveVertical = Input.GetAxis("Vertical");

        Vector3 movement = new Vector3(moveHorizontal, 0.0f, moveVertical);
        rb.velocity = movement * speed;

        rb.position = new Vector3
            (
                Mathf.Clamp(rb.position.x, boundary.xMin, boundary.xMax),
                0.0f,
                Mathf.Clamp(rb.position.z, boundary.zMin, boundary.zMax)
            );
        rb.rotation = Quaternion.Euler(0.0f, 0.0f, rb.velocity.x * -tilt);
    }

    void ShieldState()
        {
            if (shieldstatus == false)
            {
                GameObject go = Instantiate(shield, shieldspawn.position, shieldspawn.rotation);
                go.transform.parent = GameObject.Find("Player").transform;
                shieldstatus = true;
            }

        }

    public void ShieldDown()
    {
        shieldstatus = false;
    }
   
}

There are a bunch of different ways you can handle this and certainly nothing wrong with how you did it. My choice would have been the first method I described. But if you are getting the intended effect and learning, I don’t see that what you did is wrong.

Well I did get it to work. I ran into a bit of trouble calling the public function, but I got that sorted out. To be honest, now that I worked through my solution, I can see how to implement yours. Far as I can tell, its better performance to active and deactivate rather than spawn and destroy.