Unity3D - Cant attach more then one body limb/mesh - c#

I am trying to assemble together human from 3D model that have all limbs split. Like Head,Torso,Arms,Legs etc. And in runtime I would like to build a whole human with all those limbs assembled together. Here is what I do:
private void Start()
{
Debug.Log("Trying to build a Race!");
RaceModelSO firstRace = _races[0];
GameObject stiches = Instantiate(firstRace.GetRaceBodySlots()[0].mesh, new Vector3(773,0.83F,778), Quaternion.identity) as GameObject;
stiches.name = "HumanMale";
for (int i = 0; i < firstRace.GetRaceBodySlots().Length; i++)
{
if (i != 0)
{
GameObject bodyPart = Instantiate(firstRace.GetRaceBodySlots()[i].mesh) as GameObject;
_stitcher.Stitch(bodyPart, stiches);
}
}
}
Here is the actual Stitcher class that is assembling the limbs together.
public class Stitcher
{
public GameObject Stitch(GameObject sourceClothing, GameObject targetAvatar)
{
var boneCatalog = new TransformCatalog(targetAvatar.transform);
var skinnedMeshRenderers = sourceClothing.GetComponentsInChildren<SkinnedMeshRenderer>();
var targetClothing = AddChild(sourceClothing, targetAvatar.transform);
foreach (var sourceRenderer in skinnedMeshRenderers)
{
var targetRenderer = AddSkinnedMeshRenderer(sourceRenderer, targetClothing);
targetRenderer.bones = TranslateTransforms(sourceRenderer.bones, boneCatalog);
}
return targetClothing;
}
private GameObject AddChild(GameObject source, Transform parent)
{
source.transform.parent = parent;
foreach (Transform child in source.transform)
{
Object.Destroy(child.gameObject);
}
return source;
}
private SkinnedMeshRenderer AddSkinnedMeshRenderer(SkinnedMeshRenderer source, GameObject parent)
{
GameObject meshObject = new GameObject(source.name);
meshObject.transform.parent = parent.transform;
var target = meshObject.AddComponent<SkinnedMeshRenderer>();
target.sharedMesh = source.sharedMesh;
target.materials = source.materials;
return target;
}
private Transform[] TranslateTransforms(Transform[] sources, TransformCatalog transformCatalog)
{
var targets = new Transform[sources.Length];
for (var index = 0; index < sources.Length; index++)
targets[index] = DictionaryExtensions.Find(transformCatalog, sources[index].name);
return targets;
}
#region TransformCatalog
private class TransformCatalog : Dictionary<string, Transform>
{
#region Constructors
public TransformCatalog(Transform transform)
{
Catalog(transform);
}
#endregion
#region Catalog
private void Catalog(Transform transform)
{
if (ContainsKey(transform.name))
{
Remove(transform.name);
Add(transform.name, transform);
}
else
Add(transform.name, transform);
foreach (Transform child in transform)
Catalog(child);
}
#endregion
}
#endregion
#region DictionaryExtensions
private class DictionaryExtensions
{
public static TValue Find<TKey, TValue>(Dictionary<TKey, TValue> source, TKey key)
{
TValue value;
source.TryGetValue(key, out value);
return value;
}
}
#endregion
}
So let me explain the issue. In firstRace.GetRaceBodySlots() I have all the prefabs with all limbs of the body. I get all of them and I call _stitcher.Stitch() for each of them.
The problem is that the first limb is the one created before the :
for (int i = 0; i < firstRace.GetRaceBodySlots().Length; i++)
{
if (i != 0)
{
GameObject bodyPart = Instantiate(firstRace.GetRaceBodySlots()[i].mesh) as GameObject;
_stitcher.Stitch(bodyPart, stiches);
}
}
And that only the second limb gets attached properly all the others are there but not visible. Take a look:
So SOH_HM_1_Head is the first limb that was created before the foreach than is the second one SOH_HM_1_Body which is the first one inside the loop and only that one was created visible from all inside the foreach loop. All others are created but invisible. Take a look:
Do you have any idea why only the first limb from inside the foreach loop gets created visible? Any idea how can I solve this issue.
P.S.
Looks like the exact same issue was described here: Here
Can you suggest any other method rather than coroutine ?

For all people searching for a way to stitch many meshes into one all using same rig, I've come up with some small script: Unity-3D-MeshStitcher

Related

How to use "enum" with object Pooling

I created an object pooling function, and when I call a Get() function in a player script, I don't want to pass it a number like Get(0), but instead I want to call it like Get((int)ObjPrefabs.PlayerBullet).
So I tried to use "enum", but I don't know how to assign the prefab object to enum.
public class ObjectManagerFirst : MonoBehaviour
{
public GameObject[] prefabs;
public enum ObjPrefabs {PlayerBullet};
List<GameObject>[] pools;
void Awake()
{
pools = new List<GameObject>[prefabs.Length];
for (int index = 0; index < pools.Length; index++)
{
pools[index] = new List<GameObject>();
}
}
public GameObject Get(int index)
{
GameObject select = null;
foreach(GameObject item in pools[index])
{
if(!item.activeSelf)
{
select = item;
select.SetActive(true);
break;
}
}
if(!select)
{
select = Instantiate(prefabs[index], transform);
pools[index].Add(select);
}
return select;
}
}
This is current way I call it in the Player Script:
GameObject bullet = objectManager.Get(0);
But I want:
GameObject bullet = objectManager.Get((int)ObjPrefabs.PlayerBullet);
I tried already tried the following, but it didn't work:
List<int> ObjList = new List<int>();
int value = ObjList[(int)ObjPrefabs.PlayerBullet]
You can assign int values to enum and then pass it to function here is an example
enum ObjPrefabs
{
PlayerBullet = 0,
EnemyBullet = 1
}
and then call your Get function like this
GameObject bullet = objectManager.Get((int)ObjPrefabs.PlayerBullet);
and it'll work.
Instead of using int for the index why not rather stick to the enum and use a
private Dictionary<ObjPrefabs, XXX> pools;
and rather use
public GameObject Get(ObjPrefabs type)
{
if(!pools.TryGetValue(type, out var pool)
{
Debug.LogError($"Pool for {type} has not been initialized!");
return null;
}
// otherwise get object from "pool"
}
For the XXX see below
Now to the pool itself .. an array is fine but even better would be to e.g. simply use a Queue.
I would recommend a more complex but easier to handle pooling approach:
[Serializable]
public class Pool
{
public class PoolObject : MonoBehaviour
{
private Pool _pool;
public void Initialize (Pool pool)
{
_pool = pool;
}
// Use this component and method to release instances instead of calling Destroy!
public void Release()
{
_pool.Release(this);
}
private void OnDestroy ()
{
Debug.LogWarning("Destroyed a pooled object! Rather use Release!", this);
_pool.OnDestroyed(this);
}
}
// In theory you could even get rid of this enum and use the prefab itself as key ;)
[SerializeField] private ObjPrefab _type;
[SerializeField] private GameObject _prefab;
[SerializeField] [Min(1)] private int _initialAmount;
private readonly Queue<PoolObject> _availableInstances = new();
private readonly List<PoolObject> _hotInstaces = new();
public ObjPrefabs Type => _type;
public void Initialize (Transform inactiveParent)
{
for(var i = 0; i < _initialAmount; i++)
{
var instance = CreateInstance();
instance.gameObject.SetActive(false);
_availableInstances.Enqueue(instance);
}
}
private PoolObject CreateInstance ()
{
var instance = Instantiate (_prefab, inactiveParent);
if(! instance.TryGetComponent<PoolObject>(out var obj))
{
obj = instance.AddComponent<PoolObject>();
}
obj.Initialize(this);
}
private void OnDestroyed (PoolObject obj)
{
hotInstaces.Remove(obj);
}
public PoolObject GetInstance()
{
var instance = _avaialableInstances.Count != 0 ? _availableInstances.Dequeue() : CreateInstance();
instance.gameObject.SetActive(true);
hotInstances.Add(instance);
return instance;
}
public void Release(PoolObject instance)
{
if(_hotInstances.Remove(instance))
{
instance.gameObject.SetActive(false);
_availableInstances.Enqueue(instance);
}
else
{
Debug.LogError($"{instance} isn't an active object!");
}
}
}
And then finally you would use
[SerializeField] private Pool[] _pools;
private readonly Dictionary<ObjPrefabs, Pool> _activePools = new();
private void Awake ()
{
foreach(var pool in _pools)
{
if(_activePools.ContainsKey(pool.Type))
{
Debug.LogError($"Duplicate pools for type {Type}!");
continue;
}
pool.Initialize(transform);
_activePools[pool.Type] = pool;
}
}
public Pool.PoolObject Get(ObjPrefabs type)
{
if(!pools.TryGetValue(type, out var pool)
{
Debug.LogError($"Pool for {type} has not been initialized!");
return null;
}
return pool.GetInstance();
}

Problems with C # Lists - RemoveAt

I am practicing with C # Lists in Unity and I have encountered a problem.
My test script, instantiates 5 prefabs which are added in a gameobject list. I then wrote a code that generates a random int and from that number moves the prefab instantiated with that index (indexof). Everything works correctly, but the method that moves and deletes the prefab is repeated for all the gameobjects in the scene with an index higher than the one chosen. I enclose the two scripts to better explain the problem. (I would need the unlist method to be done only once.
how can i solve this problem and remove one item from the list at a time? (one each time the button is pressed, not all as it is now. Thanks)
script:
NpcController: Added in each instantiated prefab
ListCOntroller: added in the scene.
public class ListCOntroller : MonoBehaviour
{
public GameObject cubePrefab;
private GameObject cubeInstance;
public static List<GameObject> cubeList = new List<GameObject> ();
public TextMeshProUGUI checkText;
public static event Action chooseNpc;
public static int randNpcValue;
int rand;
private void Start()
{
for(int i =0; i < 5; i++)
{
cubeInstance = Instantiate(cubePrefab, new Vector3(i, -2, 0), Quaternion.identity);
cubeList.Add(cubeInstance);
}
}
public void CheckListText()
{
checkText.text = "Npc in list: " + cubeList.Count.ToString();
}
public static void SelectRandomCube()
{
randNpcValue = Random.Range(0, cubeList.Count);
chooseNpc?.Invoke();
}
}
public class NpcController : MonoBehaviour
{
void Start()
{
ListCOntroller.chooseNpc += NpcEvent;
}
private void NpcEvent()
{
if (ListCOntroller.cubeList.IndexOf(gameObject) == ListCOntroller.randNpcValue)
{
transform.localPosition = new Vector3(transform.position.x, 2, 0);
DeleteFromList();
}
}
private void DeleteFromList()
{
ListCOntroller.cubeList.RemoveAt(ListCOntroller.randNpcValue);
Debug.Log($"Delete from list: {ListCOntroller.randNpcValue}");
}
}
the int random number generated in the attached images is: 2
Because events are executed one after another.
Let's say you have 3 NPCs: NPC0, NPC1, NPC2
Now the random number you choosen is 1, when NPC1's NpcEvent runs, ListCOntroller.cubeList.IndexOf(gameObject) is 1 which equals to the randNpcValue, and then NPC1 will be removed from the list.
Note that now the list has 2 items left: NPC0, NPC2. Next NPC2's NpcEvent runs in turn, at this time, ListCOntroller.cubeList.IndexOf(gameObject) is still 1 because the list has only 2 items, so NPC2 is also removed from the list.
A solution is you can change the randNpcValue to an invalid value when a NPC is removed.
if (ListCOntroller.cubeList.IndexOf(gameObject) == ListCOntroller.randNpcValue)
{
transform.localPosition = new Vector3(transform.position.x, 2, 0);
DeleteFromList();
ListCOntroller.randNpcValue = -2;
}
In addition to this answer in general I don't really see the purpose mixing the the logic into two different scripts.
You have one for storing a list and raising an event and the other one listens to the event and manipulates the stored list -> This doesn't sounds right.
You could as well simply do `why don't you pass a long the the according object into the event in the first place and rather do something like
public class ListCOntroller : MonoBehaviour
{
// Singleton instance
private static ListCOntroller _instance;
// Read-only getter
public static ListCOntroller Instance => _instance;
// pass in the target object reference instead of doing things index based
public event Action<GameObject> chooseNpc;
public GameObject cubePrefab;
// THIS is the list controller -> nobody else should be able to manipulate this list
private readonly List<GameObject> cubeList = new ();
public TextMeshProUGUI checkText;
private void Awake()
{
if(_instance && _instance != this)
{
Destroy(this);
return;
}
_instance = this;
}
private void Start()
{
for(int i =0; i < 5; i++)
{
cubeInstance = Instantiate(cubePrefab, new Vector3(i, -2, 0), Quaternion.identity);
cubeList.Add(cubeInstance);
}
}
public static void SelectRandomCube()
{
if(cubeList.Count == 0)
{
Debug.LogWarning("Trying to pick item from empty list");
return;
}
var randomIndex = Random.Range(0, cubeList.Count);
var randomItem = cubeList[randomIndex];
// remove from the list yourself instead of relying on others to work correctly
cubeList.RemoveAt(randomIndex);
// pass along the target object
chooseNpc?.Invoke(randomItem);
}
}
and then
public class NpcController : MonoBehaviour
{
void Start()
{
ListCOntroller.Instance.chooseNpc += NpcEvent;
}
private void NpcEvent(GameObject target)
{
// check if you are the target object
if (target == gameObject))
{
// adjust your position - you don't care about other NPCs or the existence of a list
transform.localPosition = new Vector3(transform.position.x, 2, 0);
}
}
}

How do I store the same types of classes that have a different generic inside of a list?

I've been tinkering with this and I have a 'RespawnManager' that I want to use to manage my multiple 'SpawnPoint' classes with different generics but it ended up forcing me to use generics for my 'RespawnManager' which I don't want.
Let's say I had a SpawnPoint<T> class and I made a SpawnPoint<Enemy1>, SpawnPoint<Enemy2>, and SpawnPoint<Enemy3>. Is there any way I can make a list that can just manage multiple 'SpawnPoint's of any generic?
Base class:
public abstract class SpawnPoint<T> : MonoBehaviour
{
//how big the range of the spawn protection is
public int spawnProtectionRadius = 20;
public bool Occupied { get; set; }
public bool IsInSpawn(Transform target)
{
Debug.Log((target.position - transform.position).magnitude);
if ((target.position - transform.position).magnitude <= spawnProtectionRadius)
{
return true;
}
return false;
}
public abstract T Get();
}
Class that Inherits this
public class SeaMineSpawnPoint : SpawnPoint<Seamine>
{
public override Seamine Get()
{
return SeaMineObjectPool.PoolInstance.Get();
}
private void Start()
{
RespawnManager<Seamine>.respawnManager.AddSpawn(this);
}
}
Respawn manager:
public class RespawnManager<T> : MonoBehaviour where T : Component
{
public static RespawnManager<T> respawnManager;
[SerializeField]
private List<Transform> playerList;
[SerializeField]
private List<SpawnPoint<T>> spawnpoints;
private float respawnCounter;
private void Awake()
{
respawnManager = this;
}
private void Start()
{
foreach (SpawnPoint<T> sp in spawnpoints)
{
Debug.Log(sp.transform.position);
}
}
public void AddSpawn(SpawnPoint<T> spawnPoint)
{
spawnpoints.Add(spawnPoint);
}
public void RespawnSeaMines()
{
if (respawnCounter > 5)
{
respawnCounter = 0;
foreach (SpawnPoint<T> sp in spawnpoints)
{
foreach (Transform playerT in playerList)
{
if (sp.Occupied == false && !sp.IsInSpawn(playerT))
{
Component ourGameObj = sp.Get();
ourGameObj.transform.position = sp.transform.position;
ourGameObj.gameObject.SetActive(true);
sp.Occupied = true;
return;
}
}
}
}
}
private void Update()
{
respawnCounter += Time.deltaTime;
Debug.Log(respawnCounter);
RespawnSeaMines();
}
}
ObjectPool
//Class that's used for object pooling of different types.
//'T' must be a Unity component or it will error.
public abstract class ObjectPool<T> : MonoBehaviour where T : Component
{
//An object with this specific component that we use to copy.
[SerializeField]
private T prefab;
//Makes sure that only 1 coroutine runs at a time
private bool coroutineIsRunning;
//The singleton instance to our object pool.
public static ObjectPool<T> PoolInstance { get; private set; }
//A queue is used to organize plus activate and deactivate objects which
//have this component.
protected Queue<T> objects = new Queue<T>();
private void Awake()
{
//Set the instance of this pool to this class instance. Only one of these can be set.
if (PoolInstance != null)
{
throw new System.Exception("Singleton already exists. Cannot make another copy of this");
}
PoolInstance = this;
}
public T Get()
{
//If the queue happens to be empty, then add a brand new component.
if (objects.Count == 0) AddObjects(1);
//Returns the generic component and removes it from the queue.
return objects.Dequeue();
}
public void ReturnToPool(T objectToReturn)
{
//Disables the game object that the T component is attached to.
objectToReturn.gameObject.SetActive(false);
//Stores the T component in the queue.
objects.Enqueue(objectToReturn);
}
public void AddObjects(int count)
{
for (int i = 0; i < count; i++)
{
//Create a new copy of the prefab.
//The prefab is a game object with the T component attached to it.
T newObject = Instantiate(prefab);
//Disable the game object.
newObject.gameObject.SetActive(false);
//Add the T component to the queue.
//The T component is attached to the game object we created earlier.
objects.Enqueue(newObject);
}
}
public T GetWithDelay(int time)
{
T genericToReturn = null;
if (!coroutineIsRunning)
{
coroutineIsRunning = true;
StartCoroutine(GetCoroutine(time, genericToReturn));
}
return genericToReturn;
}
private IEnumerator GetCoroutine(int time, T generic)
{
float counter = 0;
while (counter < time)
{
counter += Time.deltaTime;
yield return null;
}
generic = Get();
generic.gameObject.SetActive(true);
coroutineIsRunning = false;
}
}
You should be able to declare your spawnpoints property in RespawnManager as a List<SpawnPoint<Component>> instead of List<SpawnPoint<T>>. That will allow you to get rid of the <T> type parameter entirely from RespawnManager and make it non-generic.

How can I destroy all the shaders in the world?

This script check if specific GameObjects exist then destroy them and add new same GameObjects.
If not exist it will add a new once anyway. The idea is to destroy anyway if exist and then add new o make it clean and not messed.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.SceneManagement;
public class ObjectsReplace : MonoBehaviour
{
public GameObject prefabToInit;
public bool deleteAllShaders = false;
private const string c_doorRight = "Door_Right";
private const string c_doorLeft = "Door_Left";
private const string c_doorShieldFxLocked = "DoorShieldFXLocked";
public List<GameObject> FindDoors(string[] SpecificParents, bool AllParents)
{
GameObject[] doorsLeft = GameObject.FindGameObjectsWithTag(c_doorLeft);
GameObject[] doorsRight = GameObject.FindGameObjectsWithTag(c_doorRight);
List<GameObject> allDoors = doorsLeft.Union(doorsRight).ToList();
if (AllParents == false)
{
List<GameObject> toRemove = new List<GameObject>();
for (int i = 0; i < allDoors.Count; i++)
{
bool match = true;
for (int x = 0; x < SpecificParents.Length; x++)
{
match &= allDoors[i].transform.parent.name != SpecificParents[x];
}
if (match)
{
toRemove.Add(allDoors[i]);
}
}
foreach (var it in toRemove)
{
allDoors.Remove(it);
}
}
return allDoors;
}
public void DeleteAllShaders()
{
if(deleteAllShaders == true)
{
FindDoors(null, true);
}
}
public void UpdateOrAddShaderPrefabToDoors()
{
var allDoors = FindDoors(new string[]{ "Wall_Door_Long_01", "Wall_Door_Long_02" }, false);
HashSet<GameObject> prefabParentsOfDoorsNeedRemove = new HashSet<GameObject>();
allDoors.ForEach(doorGameObject =>
{
List<GameObject> shadersChildren = new List<GameObject>();
for (int i=0; i<doorGameObject.transform.childCount ;i++)
{
if (doorGameObject.transform.GetChild(i).name.StartsWith(c_doorShieldFxLocked))
{
shadersChildren.Add(doorGameObject.transform.GetChild(i).gameObject);
}
}
foreach (GameObject shader in shadersChildren)
{
GameObject outermostPrefabInstanceRoot = PrefabUtility.GetOutermostPrefabInstanceRoot(shader);
prefabParentsOfDoorsNeedRemove.Add(outermostPrefabInstanceRoot);
}
});
foreach (GameObject parent in prefabParentsOfDoorsNeedRemove)
{
Modify(parent, RemoveFunc);
}
HashSet<GameObject> prefabParentsOfDoors = new HashSet<GameObject>();
allDoors.ForEach(doorGameObject =>
{
GameObject outermostPrefabInstanceRoot = PrefabUtility.GetOutermostPrefabInstanceRoot(doorGameObject);
prefabParentsOfDoors.Add(outermostPrefabInstanceRoot);
});
foreach (GameObject parent in prefabParentsOfDoors)
{
AddShaderToPrefab(parent);
}
}
private void AddShaderToPrefab(GameObject child)
{
Modify(child, AddShaderToAllDoorsFunc);
}
private GameObject AddShaderToAllDoorsFunc(GameObject prefab)
{
var children = prefab.GetComponentsInChildren<Transform>();
//Debug.Log($"Total child count before:{children.Count()}");
int doorsFound = 0;
foreach (Transform trans in children)
{
if (trans.name == c_doorLeft || (trans.name == c_doorRight))
{
//Debug.Log("Found door, adding");
GameObject shader = GetDoorShaderPrefab();
// clone prefab and attach to parent
Instantiate(shader, trans);
doorsFound++;
}
}
children = prefab.GetComponentsInChildren<Transform>();
//Debug.Log($"Total child count after:{children.Count()}, doors found:{doorsFound}");
return prefab;
}
private GameObject GetDoorShaderPrefab()
{
string[] shieldPrefab = AssetDatabase.FindAssets(c_doorShieldFxLocked);
//Debug.Assert(shieldPrefab.Length == 1, "Expected exactly 1 shield like this...");
string shieldGuid = shieldPrefab[0];
string prefabPath = AssetDatabase.GUIDToAssetPath(shieldGuid);
GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
//Debug.Assert(prefab != null, "Expected prefab to load");
return prefab;
}
private GameObject RemoveFunc(GameObject prefab)
{
var children = prefab.GetComponentsInChildren<Transform>();
//Debug.Log($"child count:{children.Count()}");
foreach (Transform trans in children)
{
if (trans.name.StartsWith(c_doorShieldFxLocked))
{
//Debug.Log("Found door shader");
DestroyImmediate(trans.gameObject);
}
}
children = prefab.GetComponentsInChildren<Transform>();
//Debug.Log($"child count:{children.Count()}");
return prefab;
}
private void Modify(GameObject parentPrefab, Func<GameObject,GameObject> modifyActionOnPrefab)
{
// Get the Prefab Asset root GameObject and its asset path.
string assetPath = PrefabUtility.GetPrefabAssetPathOfNearestInstanceRoot(parentPrefab);
// Load the contents of the Prefab Asset.
GameObject prefab = PrefabUtility.LoadPrefabContents(assetPath);
//PrefabUtility.UnpackPrefabInstance(mostPrefabInstanceRoot, PrefabUnpackMode.Completely, UnityEditor.InteractionMode.AutomatedAction);
prefab = modifyActionOnPrefab(prefab);
PrefabUtility.SaveAsPrefabAsset(prefab, assetPath);
PrefabUtility.UnloadPrefabContents(prefab);
}
}
I want to do it in the method DeleteAllShaders.
Now the function RemoveFunc will remove only the shaders childs that are under specific prefabs and parents.
I want to give a choice to destroy all the shaders in the world including shaders that are under prefabs.
Maybe to use the RemoveFunc but in this case I don't want to add a new shaders only to destroy.
I'm using AllParents flag to decide if to get all the doors left and right in the world or if to get the doors left and right under specific parents. When AllParents is true then I want to be able to delete all the shaders in the world those under any of the doors and those that not under any doors. All the shaders.

How to add first child to an Array-List

I'm trying to make an array or a list with the first childs of a GameObject so I can edit them from another scripts.
I have tried it doing an Array, but it will take the childs of this childs too and I need to take only the first ones.
I have tried also with a List with a foreach, but it will add the same element every frame to the list, and I only need it one time.
public GameObject GOGranjaTerrain;
public GameObject GOFabricaTerrain;
public GameObject GOOficinaTerrain;
public Transform[] granjaTerrainArray;
public Transform[] fabricaTerrainArray;
public Transform[] oficinaTerrainArray;
public List<Transform> granjaTerrainList = new List<Transform>();
void Update()
{
SearchTerrains1();
SearchTerrains2();
}
//Array way
void SearchTerrains1()
{
granjaTerrainArray = GOGranjaTerrain.GetComponentsInChildren<Transform>();
fabricaTerrainArray = GOFabricaTerrain.GetComponentsInChildren<Transform>();
oficinaTerrainArray = GOOficinaTerrain.GetComponentsInChildren<Transform>();
}
//List way
void SearchTerrains2()
{
foreach(Transform child in GOGranjaTerrain.transform)
{
granjaTerrainList.Add(child);
}
}
Here's how you can do it:
void Update()
{
for (int i = 0; i < GOGranjaTerrain.transform.childCount; i++)
{
Transform child = GOGranjaTerrain.transform.GetChild(i);
if (granjaTerrainList.Contains(child) == false)
{
granjaTerrainList.Add(child);
}
}
}
Or even better, if you only need to do it once - do in in Start instead of Update.

Categories