- Home /
How do I get a component from multiple objects?
Hello friends. I am trying to make an RTS game as my first completed project and I am stuck on something. I have a storage script that accesses all the mines in my game and gets their scripts. I have a general idea on how to do that but my question lies in the fact that I want to do it in the Start() method. If I do it there, when I add another mine, it wont be read, but if I do it in the Update() or the FixedUpdate() method it will be resource heavy? Any ideas?
my code is:
using UnityEngine;
public class StorageScript : MonoBehaviour
{
GameObject[] mines;
GameObject[] farms;
[SerializeField] MineScript ms;
[SerializeField] FarmScript fs;
[SerializeField] int storageFood;
int maxStorageStone = 10000;
int newStone;
[SerializeField] int storageStone;
int maxStorageFood = 10000;
int newFood;
void Start()
{
mines = GameObject.FindGameObjectsWithTag("Mine"); // me trying to fix what I told you
farms = GameObject.FindGameObjectsWithTag("Farm");
}
void FixedUpdate()
{
// Stone Storage
newStone = storageStone - maxStorageStone;
if (storageFood > maxStorageStone)
{
storageStone = maxStorageStone;
ms.stone = newStone;
}
else if (ms.stone >= 80 && storageStone < maxStorageStone)
{
storageStone += ms.stone;
ms.stone = newFood;
}
// Food Storage
newFood = storageFood - maxStorageFood;
if (storageFood > maxStorageFood)
{
storageFood = maxStorageFood;
fs.food = newFood;
}
else if (fs.food >= 80 && storageFood < maxStorageFood)
{
storageFood += fs.food;
fs.food = newFood;
}
}
}
and
using UnityEngine;
public class MineScript : MonoBehaviour
{
public int stone;
public int maxStone = 500;
float timeToMine = 1;
void FixedUpdate()
{
timeToMine -= Time.fixedDeltaTime;
if (timeToMine <= 0 && stone < maxStone)
{
stone ++;
timeToMine = 1;
}
if (stone < 0)
{
stone = 0;
}
}
}
Answer by yocat332mlg · May 14 at 11:00 AM
I have one idea. Make a static List that will contain mines, and then you can access it from mines themselves, so it would be something like:
void -Awake or Start, what you prefer- ()
{
MineScript.Mines.Add(this.gameObject);
//Add the game object that this script is attached to to the list of mines
}
However, it will work only if mines list is static! So it is basically the same for all the storages, kind of a part of MineScript class.
So now, when new mine is added, it will add itself to the storage's list of mines.
There are other solutions though, and other ways to do that, ask if you have any questions, I'll try to help!
In addition: if you need a list of MineScripts, instead of
MineScript.Mines.Add(this.gameObject);
you can
MineScript.Mines.Add(this);
so it will add the component itself instead of a GameObject that component is attached to.
but will adding a new mine update the list or will it still show the starting mines (Awake/Start method). It is a good idea but putting it in Update() or FixedUpdate() will be extremely resource heavy. Tell me if I am wrong tho
Adding a mine to the list simply adds a mine to the list! I may have misunderstood your comment, but if you'll put that code to the Update(), it will add all the mines to the list with each update over and over again. So you put that in Awake(), each Mine will add itself to the list once. Maybe I haven't explained clearly, that piece of code goes to the MineScript class!
Oh, and there is a mistake in my example - i thought about adding Mines list to the StorageScript class, but in my code it is MineScript.Mines. You could do that too, and it will make sense, but in this case, it absolutely has to be static.
Sorry if my answers are a little confusing, it feels like they may be. English is not my native language and I'm not that good in Unity, but I know the basics!
Answer by Spokkazoni · May 14 at 01:32 PM
I found a solution myself through another forum. Here is the code I used.
GameObject[] mines; [SerializeField] MineScript ms; void Start() { mines = GameObject.FindGameObjectsWithTag("Mine"); } void Update() { foreach (GameObject mineObject in mines) { ms = mineObject.GetComponent<MineScript>(); } }
Answer by sharatachary · May 14 at 03:13 PM
Hello @Spokkazoni ,
Yes using fixed or update methods will populate the array every time they execute so please avoid it, will impact the runtime experience with the increase in number of mines.
Now addressing the part where you said how to detect a mine which is created runtime as the start method of StorageScript would have already been fired so will miss this new mine.
The answer to that is using events or Action, which means the mine should notify the StorageScript that I was just created please add me to your mines list. I would replace mines array with mines list as I can add a the newly created mine gameobject to the list.
Following is a sample for MineNotifier class which should be attached immediately to the newly instantiated mine gameobject so that it can raise the event which should be listened by StorageScript.
/* author : Sharat Achary
* date : 20220514
*/
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MineNotifier : MonoBehaviour
{
public static Action<GameObject> OnMineInstantiated;
private void Start()
{
OnMineInstantiated?.Invoke(gameObject);
}
}
Now a modified StorageScript should implement the methods shown below to listen to event and add the newly created mine gameobject to the mines list.
/* author : Sharat Achary
* date : 20220514
*/
using System.Collections.Generic;
using UnityEngine;
public class StorageScript : MonoBehaviour
{
// Using list instead of array to add objects during runtime.
List<GameObject> mines = new List<GameObject>();
// Subscribe to the event.
private void OnEnable()
{
MineNotifier.OnMineInstantiated += OnMineInstantiatedHandler;
}
// Unsubscribe from the event.
private void OnDisable()
{
MineNotifier.OnMineInstantiated -= OnMineInstantiatedHandler;
}
// Add the mine gameobject to the list.
private void OnMineInstantiatedHandler(GameObject newMineGameObject)
{
mines.Add(newMineGameObject);
}
}
This way there is no need to populate the mines every frame using Update or FixedUpdate methods. Update a mines whenever a new mine is added runtime. And those mines which are already in the scene, you should fetch in the Awake or Start method of the StorageScript.
Hope this helps