How i can get GameObject index in the list? - c#

At the start, the object adds a link to it to the list. Then when i click on this object, i need to get index reference of this object. How to do it?
Little example what i need:
public static List<GameObject> myObjects = new List<GameObject> ();
public GameObject ObjectA; //this is prefab
void Start(){
for (int i = 0; i < 10; i++) {
GameObject ObjectACopy = Instantiate (ObjectA);
myObjects.Add (ObjectACopy);
}
}
ObjectA script:
void OnMouseDown(){
Debug.Log(//Here i need to return index of this clicked object from the list);
}

Loop through the Objects List. Check if it matches with the GameObject that is clicked. The GameObject that is clicked can be obtained in the OnMouseDown function with the gameObject property. If they match, return the current index from that loop. If they don't match, return -1 as an error code.
void OnMouseDown()
{
int index = GameObjectToIndex(gameObject);
Debug.Log(index);
}
int GameObjectToIndex(GameObject targetObj)
{
//Loop through GameObjects
for (int i = 0; i < Objects.Count; i++)
{
//Check if GameObject is in the List
if (Objects[i] == targetObj)
{
//It is. Return the current index
return i;
}
}
//It is not in the List. Return -1
return -1;
}
This should work but it is better that you stop using the OnMouseDown function and instead use the OnPointerClick functions.
public class Test : MonoBehaviour, IPointerClickHandler
{
public static List<GameObject> Objects = new List<GameObject>();
public void OnPointerClick(PointerEventData eventData)
{
GameObject clickedObj = eventData.pointerCurrentRaycast.gameObject;
int index = GameObjectToIndex(clickedObj);
Debug.Log(index);
}
int GameObjectToIndex(GameObject targetObj)
{
//Loop through GameObjects
for (int i = 0; i < Objects.Count; i++)
{
//Check if GameObject is in the List
if (Objects[i] == targetObj)
{
//It is. Return the current index
return i;
}
}
//It is not in the List. Return -1
return -1;
}
}
See this post for more information on OnPointerClick.
EDIT:
Objects.IndexOf can also be used for this. This answer is here to make you understand how to do it yourself so that you can solve similar issues in the future.

Related

deactivate and activate an object in a list

I am a beginner. I want to create a game where there are six different positions and objects that spawn from them everytime player collides one of them and then they respawn. it s a loop until game finish.
I am having a problem with positions; if player stays at a position it just collides continually. I need to deactivate the position that player collides and reactivate when player collides other position. Position were made with empthy object and its collider. I dont know where to add what.
Thanks for your help
public class NewSpawnScript : MonoBehaviour{
public GameObject[] SpawnPoints;
public float spawnTime;
public GameObject[] threeD;
public List<GameObject> possibleSpawn = new List<GameObject>();
public List<GameObject> possibleThreeD = new List<GameObject>();
void Start()
{
//to avoid spawn more then one object at a position
for(int i=0;i<SpawnPoints.Length;i++)
{
possibleSpawn.Add(SpawnPoints[i]);
}
// to avoid to spawn same objects at different positions
for (int l=0;l<threeD.Length;l++)
{
possibleThreeD.Add(threeD[l]);
}
InvokeRepeating("SpawnItems", spawnTime, spawnTime);
}
private void OnTriggerEnter(Collider other)
{
if (other.gameObject.tag=="Obj")
{
//to avoid spawn more then one object at a position
for (int i = 0; i < SpawnPoints.Length; i++)
{
possibleSpawn.Add(SpawnPoints[i]);
}
// to avoid to spawn same objects at different positions
for (int l = 0; l<threeD.Length; l++)
{
possibleThreeD.Add(threeD[l]);
}
InvokeRepeating("SpawnItems", 2.5f, 2.5f);
}
}
void Update()
{
}
void SpawnItems()
{
if(possibleSpawn.Count>0 && possibleThreeD.Count>0)
{
for(int i=0;i<12;i++)
{
int spawnIndex = Random.Range(0, possibleSpawn.Count);
int spawnObject = Random.Range(0, possibleThreeD.Count);
GameObject newObj = Instantiate(possibleThreeD[spawnObject], possibleSpawn[spawnIndex].transform.position, possibleSpawn[spawnIndex].transform.rotation) as GameObject;
newObj.GetComponent<Destroyer>().mySpawnPoint = possibleSpawn[spawnIndex];
newObj.transform.parent = newObj.GetComponent<Destroyer>().mySpawnPoint.transform;
possibleSpawn.RemoveAt(spawnIndex);
possibleThreeD.RemoveAt(spawnObject);
i++;
}
}
}
}

Unity3D - Cant attach more then one body limb/mesh

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

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.

How to reference a class from child to parent?

It may sound stupid but how can i reference a class from one script in child in another script in parent? I cant find anything on google. Note: There are couple errors in my script, that's not the point of the post.
//Public
//Private
private Rigidbody myRigidbody;
private Renderer myRenderer;
private Material tileDefaultMaterial;
private Material tileSelectedMaterial;
private Material tileSameGroupMaterial;
void Start () {
myRigidbody = GetComponent<Rigidbody> ();
myRenderer = GetComponent<Renderer> ();
tileDefaultMaterial = Resources.Load ("TileDefault", typeof(Material)) as Material;
tileSelectedMaterial = Resources.Load ("TileSelected", typeof(Material)) as Material;
tileSameGroupMaterial = Resources.Load ("TileSameGroup", typeof(Material)) as Material;
}
void Update () {
}
public class TileClass {
public int tileGroup = 0; //Indicates the Tile Group Number.
}
//Public
public GameObject[] allTiles; //Aray of all Tile GameObject.
public bool tileIsSelected = false; //If a Tile is Selected.
public int selectedTileGroup = 0; //Indicates the Selected Tile Group Number.
public int tileGroup = 0; //Indicates the Tile Group Number.
//Private
void Start () {
allTiles = new GameObject[transform.childCount];
for(int i = 0; i < transform.childCount; i++){
allTiles [i] = transform.GetChild (i).gameObject;
}
}
void Update () {
}
void OnMouseDown (){
RaycastHit hitInfo = new RaycastHit ();
bool hit = Physics.Raycast (Camera.main.ScreenPointToRay (Input.mousePosition), out hitInfo);
if (hitInfo.transform.gameObject.tag == "Tile" && tileIsSelected == false) {
Debug.Log ("A Tile is Selected!");
tileIsSelected = true;
selectedTileGroup = ;
for(int i = 0; i < allTiles.Length; i++){
if (this.tileGroup == selectedTileGroup) {
allTiles [i].GetComponent<Renderer> ().material = tileSameGroupMaterial;
}
}
myRenderer.material = tileSelectedMaterial;
} else if (hitInfo.transform.gameObject.tag == "Tile" && tileIsSelected == true) {
Debug.Log ("Second Tile is Clicked! (Should Swap them!)");
myRenderer.material = tileDefaultMaterial;
}
}
There is a famous saying :
var allTiles = transform.GetComponentsInChildren<Tile>();
And as I told you yesterday, Add OnMouseDown() in Tile.cs and write myRenderer.material = tileDefaultMaterial; there. No need to write this in TileManager.cs. And NO need to use Raycast when using OnMouseDown().
I can't read your image code, so I'll make up my own class names for the example. I'll call the parent class TileManager and the child class Tile.
Say in Tile you want access to the array of tiles in the TileManager. If allTiles is declared as public, you'd do in Tile somewhere.
TileManager tileManager = transform.parent.GetComponent<TileManager>();
var allTiles = tileManager.allTiles;
Now the Tile child has a reference to the array. Was this what you were wanting?
how about base.Method?
That should do it

Unity Reusing Ui element in list

I have ran into a sort of a problem in my project, I am creating a Scroll listview to show all elements present in list in screen.
I am using button in panel to show list.
Now When I call ShowList() it shows elements in the list.
But if i add some objects to list and again call ShowList() then it also make clone of previous object as instantiated objects are present.
To solve this I am deleting clones using Destroy() but when list contain too many item (300~400) deleting them cause lag in the game . How can I create object pool for ui button or just deactivate them.
public class two : MonoBehaviour {
public GameObject Button_Template;
private List<GameName> gm = new List<GameName>();
public void Exit()
{
var og = GameObject.FindGameObjectsWithTag("clone");
for (int i = 0; i < og.Length; i++)
{
Destroy(og[i]);
}
}
void Start()
{ gm.Add(new GameName("1"));
gm.Add(new GameName("2"));
gm.Add(new GameName("3"));
gm.Add(new GameName("4"));
}
public void ShowList()
{
for (int i = 0 ; i < gm.Count; i++)
{
GameObject go = Instantiate(Button_Template) as GameObject;
go.SetActive(true);
one TB = go.GetComponent<one>();
TB.SetName(gm[i].GName);
go.transform.SetParent(Button_Template.transform.parent);
go.tag = "clone";
}
}
}
List can be used to implement Object Pooling. Simply disable the GameObject when you are done using it. Re-enable it again if you want to use it again. The simple GameObject Pool script below re-implements the Instantiate and Destroy functions.
public class BUTTONPOOL : MonoBehaviour
{
GameObject buttonPrefab;
List<GameObject> buttonPool;
bool ready = false;
public void createButtonPool(GameObject buttonPrefab, int amount = 400)
{
if (ready)
{
Debug.LogError("createButtonPool can only be called once");
return;
}
this.buttonPrefab = buttonPrefab;
//Create 400 Buttons
buttonPool = new List<GameObject>();
for (int i = 0; i < amount; i++)
{
buttonPool.Add(Instantiate(this.buttonPrefab) as GameObject);
}
ready = true;
}
//Creates/Enables single Button
public GameObject Instantiate()
{
if (!ready)
{
showError();
return null;
}
//Return any Button that is not active
for (int i = 0; i < buttonPool.Count; i++)
{
if (!buttonPool[i].activeSelf)
{
return buttonPool[i];
}
}
//Create new Button if there is none available in the pool. Add it to the list then return it
GameObject tempButton = Instantiate(this.buttonPrefab) as GameObject;
buttonPool.Add(tempButton);
return tempButton;
}
//Destroys/Disables single Button
public void Destroy(GameObject button)
{
if (!ready)
{
showError();
return;
}
button.SetActive(false);
}
//Destroys/Disables all Buttons
public void DestroyAll()
{
if (!ready)
{
showError();
return;
}
for (int i = 0; i < buttonPool.Count; i++)
{
if (buttonPool[i].activeSelf)
{
buttonPool[i].SetActive(false);
}
}
}
private void showError()
{
Debug.LogError("createButtonPool must be called once before any other function can be called");
}
}
Usage:
public GameObject ButtonPrefab;
BUTTONPOOL bPool;
void test()
{
if ((bPool = GetComponent<BUTTONPOOL>()) == null)
{
gameObject.AddComponent<BUTTONPOOL>();
bPool = GetComponent<BUTTONPOOL>();
}
//Initiate with 300 Buttons
bPool.createButtonPool(ButtonPrefab, 50);
GameObject tempButton = bPool.Instantiate();
//MUST SET BUTTON ACTIVE CALLING Instantiate()
tempButton.SetActive(true);
//You can do other things with the button
one TB = tempButton.GetComponent<one>();
//To destroy that single button
bPool.Destroy(tempButton);
//OR destroy that all button
bPool.DestroyAll();
}
Note that you must set the Button to active after calling the custom Instantiate() function from the BUTTONPOOL script.

Categories