Add GameObject to array / list so i can access a function within that specific item

I am setting up a simple scene with a wall, 3 green bottles, and a target.

When the mouse is clicked on a bottle the bottle is destroyed.

I only know how to do this with 1 bottle, by loading that gameObject manually into a public variable in the Target Controller script. As per screenshot below. This means whenever any bottle gets hit, only that one actually falls.

What I want is, all the bottles in scene (as new ones will be added at run-time) to be added into an array / list. And then when one is shot at, that particular one falls.

How do I do this? I have given my bottles the tag ā€œBottlesā€.

I have tried using lists and arrays (though possibly incorrectly) but I can’t get it to access that function for that particular item hit.

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

public class TargetController : MonoBehaviour {

    private Camera cam;
    private Image player1;
    public BottleManager bottles;

    // Use this for initialization
    void Start () {
 
        cam = GameObject.Find("Main Camera").GetComponent<Camera>();
        player1 = GetComponentInChildren<Image> ();

    }


    // Update is called once per frame
    void Update () {
     
        player1.transform.position = Input.mousePosition;
        Ray ray = cam.ScreenPointToRay (new Vector3 (Input.mousePosition.x, Input.mousePosition.y, 0));
        Debug.DrawLine (ray.origin, ray.direction * 10, Color.red);

        RaycastHit hitInfo;
        int layerMask = 8;

        if (Input.GetMouseButton(0)) {
            if (Physics.Raycast (ray, out hitInfo, 300)) {
                if (hitInfo.transform.gameObject.layer == layerMask) {

                    bottles.hit (); // this function just contains the line "rb.AddForce (transform.forward * 100);"
                    //Destroy (hitInfo.collider.gameObject);
                }
            }
        }
    }
}

The raycasthit gives you a reference to get the gameobject that was hit by the raycast, use that, not a inspector set variable.

would you please expand on that, I’m also a newbie :slight_smile:

You could add a tag to the bottle prefab (e.g. ā€œBottleā€).

When raycasting, check to see if the collider has the Bottle-tag. If it doesn, destroy it.

Something like this:

float dist = 100; //set to fit project
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;

if(Physics.Raycast(ray, out hit, dist))
{
    if(hit.collider.tag == "Bottle")
    {
        Destroy(hit.tranform.gameObject);
    }
}

With this code attached to e.g. a game manager or any object really, you should be able to shoot away your bottles without them having to be in any kind of list or array, as long as the bottles have the ā€œBottlesā€ tag…

To add a tag, select the prefab. Access the tag dropdown below the name of the game object. If you don’t see a tag that you want to use, add a custom one by clicking ā€œAdd Tagā€ or something like that. Then simply add a new one to the list in the new menu. Remember to add this new tag to the bottle afterwards, as Unity doesn’t do this automatically after creating a new tag.

If you want to optimize this further, you could also put the bottles on a separate raycast layer, and then only accept collisions from that layer. In this case, it’s not required though.

That’s great thank you! I had something very similar at one stage but did not use .collider after hit so it didn’t work.

This now destroys each bottle I click on.

But how can I pass that bottle identity to my bottles.hit() function? As currently if this is enabled force is still only being applied to the bottle dropped into my public variable (as per first image)

This is why I thought I needed to have all bottles in an array.

Can I detect the rigid body of a raycast collision and apply force to the bottle that way?

A couple of options…

  1. setup the bottles.hit() to accept a game object/transform/rigidbody as a parameter. This way, you can pass in what you hit with the raycast and act on it.
  2. move the code from a centralized spot, to a script instance on each bottle. Then, ā€˜bottle.hit()’ (or some other name for the method) is a component on the bottle, already knows itself, and you can look it up and call it when hit with the raycast.

I think this is what I’ve been trying to do.

So I have a BottleManager script which is attached to each bottle. This script contains:

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

public class BottleManager : MonoBehaviour {

    private Rigidbody rb;

    void Start () {

        rb = GetComponent<Rigidbody> ();

    }
  
    void Update () {
      
    }

    public void hit()
    {
        rb.AddForce (transform.forward * 100);
    }
}

So maybe all I’m not doing is passing a variable into the function bottleHit() from the main script as per first post?

If you mean:

// inside the raycast hit (successful)
BottleManager bm = hit.collider.GetComponent<BottleManager>();
if(bm != null) bm.hit();

Like that?

1 Like

Exactly like that! Thank you!

No problem. :slight_smile: