System.Array.IndexOf returns -1 - c#

I put a screenshot that will tell you everything.
private Transform[] hiddenObjects;
void Start()
{
leftImageRandom = new Randomizer(0, LeftImageSequence.transform.childCount - 1, true);
DoStart();
}
private void DoStart()
{
leftImageIndex = leftImageRandom.getRandom();
LeftImageSequence.setCurrentChildIndex(leftImageIndex);
RightImageSequence.setCurrentChildIndex(leftImageIndex);
//take hidden objects and put them in an array
hiddenObjects = RightImageSequence.CurrentChild.transform.GetChild(0).transform.GetComponentsInChildren<Transform>();
for(int i=1;i<hiddenObjects.Length;i++)
hiddenObjects[i].gameObject.GetOrAddComponent<MouseEventSystem>().MouseEvent += ClickedHiddenObject;
Debug.Log(hiddenObjects.Length);
}
private void ClickedHiddenObject(GameObject target, MouseEventType type)
{
if (type == MouseEventType.CLICK && CanClick)
{
int targetIndex = System.Array.IndexOf(hiddenObjects, target.gameObject);
Debug.Log(targetIndex);
hiddenObjects[targetIndex].GetComponent<SpriteRenderer>().DOFade(1f, 0.3f).SetEase(Ease.Linear);
}
}
I have a targetIndex that needs to return the index of the object I clicked on. Each object contains the PoligonCollider2D component. The problem is that it always returns -1 to any object. What's the problem, what am I doing wrong?

From your code:
private Transform[] hiddenObjects;
...
System.Array.IndexOf(hiddenObjects, target.gameObject)
hiddenObjects is an array of Transform, but target.gameObject is a GameObject (actually target is already a GameObject, so this is redundant): the types don't match, so indeed a GameObject is never gonna be equal to a Transform.
Instead try:
System.Array.IndexOf(hiddenObjects, target.transform)

Related

How to use List<GameObject> insteal GameObject[] when instantiating and destroying objects?

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.UI;
[ExecuteAlways]
public class SliderManager : MonoBehaviour
{
public Slider slider;
public List<GameObject> InstantiatedObjects = new List<GameObject>();
public GameObject Model;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
SetSlider((int)slider.value);
}
protected void SetSlider(int x)
{
for (int i = 0; i < slider.value; i++)
{
bool mustBeAnObject = i < x;
if ((mustBeAnObject) && (InstantiatedObjects[i] == null))
InstantiatedObjects.Add(Instantiate(Model));
else if ((!mustBeAnObject) && (InstantiatedObjects[i] != null))
{
if (!Application.isPlaying)
{
InstantiatedObjects.RemoveAt(i);
DestroyImmediate(InstantiatedObjects[i]);
}
else
{
InstantiatedObjects.RemoveAt(i);
Destroy(InstantiatedObjects[i]);
}
}
}
}
}
When i used array before :
public GameObject[] Objects = new GameObject[100];
it was working fine i looped over the Objects array but then i had a problem to remove items from the array when destroying the objects so i tried to switch the array to list and changed the name from Objects to InstantiatedObjects.
but now i'm getting in the editor all the time out of bound index exception error on the line 32:
if ((mustBeAnObject) && (InstantiatedObjects[i] == null))
ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection.
Parameter name: index
System.Collections.Generic.List`1[T].get_Item (System.Int32 index) (at <75633565436c42f0a6426b33f0132ade>:0)
SliderManager.SetSlider (System.Int32 x) (at Assets/SliderManager.cs:32)
SliderManager.Update () (at Assets/SliderManager.cs:23)
line 23 is the line inside the Update.
i know what it means the error but not sure how to fix it.
what i'm trying to do is to use the Slider to insatiate objects when sliding the slider to the right and to destroy/remove objects when moving to the left.
it was working fine with the array until i needed to remove items from the array that's why i switched to List
Updated what i tried :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.UI;
using static UnityEditor.Progress;
[ExecuteAlways]
public class SliderManager : MonoBehaviour
{
public Slider slider;
public List<GameObject> InstantiatedObjects = new List<GameObject>();
public GameObject Model;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
SetSlider((int)slider.value);
}
protected void SetSlider(int x)
{
for (int i = 0; i < slider.value; i++)
{
bool mustBeAnObject = i < x;
if (mustBeAnObject)
InstantiatedObjects.Add(Instantiate(Model));
// one loop for killing too many items
while (InstantiatedObjects.Count > slider.value)
{
// could as well simply remove the first item if you prefer (0)
var index = InstantiatedObjects.Count - 1;
var item = InstantiatedObjects[index];
InstantiatedObjects.RemoveAt(index);
if (!Application.isPlaying)
{
DestroyImmediate(item);
}
else
{
Destroy(item);
}
}
// one loop for spawning missing items
while (InstantiatedObjects.Count > slider.value)
{
InstantiatedObjects.Add(Instantiate(Model));
}
}
}
}
but this not working good when moving the slider to value 0 back one item left and if moving the slider very fast some items left both in hierarchy and the list.
if i'm trying this code without the lines :
bool mustBeAnObject = i < x;
if (mustBeAnObject)
InstantiatedObjects.Add(Instantiate(Model));
then it's not creating objects at all.
The moment you remove an object from the list (InstantiatedObjects.RemoveAt(0);) the list shrinks
=> all the later items are shifted down one index
=> all your later indices are wrong! Even directly in the next call already for e.g. DestroyImmediate(InstantiatedObjects[i]); this already refers to the item after the one you just removed!
You could rather do e.g.
protected void SetSlider(int x)
{
// one loop for killing too many items
while(InstantiatedObjects.Count > slider.value)
{
// could as well simply remove the first item if you prefer (0)
var index = InstantiatedObjects.Count - 1;
var item = InstantiatedObjects[index];
InstantiatedObjects.RemoveAt(index);
if (!Application.isPlaying)
{
DestroyImmediate(item);
}
else
{
Destroy(Item);
}
}
// one loop for spawning missing items
while(InstantiatedObjects.Count < slider.value)
{
InstantiatedObjects.Add(Instantiate(Model));
}
}

Using same Boolean with multiple objects in Unity 2D

I have four game objects with the same script and the same bool, when one of them get hit by ray the bool state is true, but the function will not start because the other three game objects is set to false.
What I tried:
the code works fine with the last object being instantiated
if I disabled the script on the first object and re-enabled it again the function works fine on this object only
public bool selected;
void Start(){
selected = false;
}
void Update(){
showRange ();
}
public void showRange(){
if (selected == true) {
for (int i = 0; i < tileRange.Count; i++) {
tileRange [i].GetComponent<SpriteRenderer> ().enabled = true;
}
} else {
for (int i = 0; i < tileRange.Count; i++) {
tileRange [i].GetComponent<SpriteRenderer> ().enabled = false;
}
}
}
Simply use a static variable: What is the use of static variable in C#? When to use it? Why can't I declare the static variable inside method?
You would have:
public static bool selected;
Try using gamemanager.
public static gamemanager Instance;
public bool selected;
private void Awake()
{
Instance = this;
}
Your gamemanager can have a public bool variable "selected".
New start method:
void Start() {
gamemanager.Instance.selected = false;
}
New showRange function
public void showRange(){
if (gamemanager.Instance.selected) {
for (int i = 0; i < tileRange.Count; i++) {
tileRange [i].GetComponent<SpriteRenderer> ().enabled = true;
}
} else {
for (int i = 0; i < tileRange.Count; i++) {
tileRange [i].GetComponent<SpriteRenderer> ().enabled = false;
}
}
}
I am sure this code can be improved. Let me know if it helps.
The problem isn't in the selected bool, but it's in showRange() method. If the ray hit one object it will be selected and the other three will remain unselected ((that's because I use a list that stores the last hitted object, then the code works only for the object inside this list))
showRange() will not work with the selected object, because the method want all the four object to be selected, then it works (stupid method, I was unable to sleep because of it).
I managed to fix showRange() problem, using a new script that turns off all game objects script component and turns on the selected one script, this will make showRange() method unable to check the bool state of the other three objects.
For (Guilherme, misher and Josep) thank you for your help, I really appreciate it, but as it is shown above the problem wasn't in the Boolean.
For (Muhammad Farhan Aqeel) I believe your code should work, but I didn't manage to get it work maybe because I'm still new to programming.

why when i change reference A's value, the original instance doesn't change?

I'm writing a script for locking scale of objects in Unity.
Since objectTransformScale = objectTransform.localScale.
Changes made on objectTransformScale should also affect objectTransform.localScale, but it doesn't.
Hence I have to set the value back as objectTransform.localScale = objectTransformScale;
Why doesn't it work?
public string demension;
private Transform objectTransform;
private Vector3 objectTransformScale;
private float originalX;
private float originalY;
private float originalZ;
// Use this for initialization
void Start () {
objectTransform = GetComponent<Transform>();
objectTransformScale = objectTransform.localScale;
originalX = objectTransformScale.x;
originalY = objectTransformScale.y;
originalZ = objectTransformScale.z;
}
// Update is called once per frame
void Update () {
objectTransformScale = objectTransform.localScale;
if (demension.Equals("x"))
{
objectTransformScale.x = originalX;
}
else if(demension.Equals("y"))
{
objectTransformScale.y = originalY;
}
else if(demension.Equals("z"))
{
objectTransformScale.z = originalZ;
}
else if (demension.Equals("a"))
{
objectTransformScale.z = originalZ;
objectTransformScale.y = originalY;
objectTransformScale.x = originalX;
}
//The scale of object won't be locked if I command the line below.
objectTransform.localScale = objectTransformScale;
}
objectTransformScale = Vector3(ref objectTransform.localScale);
Structs are value types so they just pass the data without including a reference to themselves. You were essentially making a copy of local scale and editing the copy. Using ref in a constructor makes sure the two are linked.

how GetComponentInChildren in unity

I have a problem:
How to assign [] status to rend[]
public GameObject[] Obj;
private bool[] Status;
private MeshRenderer[] rend;
private void Start ()
{
for (int i = 0; i < Obj.Length; i++)
{
rend[i] = GetComponentInChildren<MeshRenderer>();
Status[i] = rend[i];
}
}
Accorting to Object.bool you can try this way:
public GameObject[] Obj ;
private bool[] Status;
private MeshRenderer[] rend;
void Start () {
for (int i = 0; i < Obj.Length; i++)
{
rend[i] = Obj[i].GetComponentInChildren<MeshRenderer>();
Status[i] = rend[i] != null;
}
}
Your question is very unclear so I can just guess what you are trying to archieve:
I'ld say you want to find the first MeshRenderer under your GameObjects in Obj and get an array Status saying whether they are enabled or not.
You are currently allways searching for MeshRenderer in the GameObject this script is attached to instead of looking in the Obj GameObjects. You have to get it using
Obj[i].GetComponentInChildren<MeshRenderer>();
you probably don't initailize your arrays -> you cannot just assign values using Status[i] = and rend[i] = if the arrays were never initialized using new. You can either do that in start once you have the size of Obj
public GameObject[] Obj;
private bool[] Status;
private MeshRenderer[] rend;
private void start()
{
Status = new bool[Obj.Length];
rend = new MeshRenderer[Obj.Length];
// ...
}
but I would prefer using a List<bool> and List<MeshRenderer> instead so you can simply add and remove values later (while an array has a fixed length).
Finally I guess you want to know whether the component MeshRenderer is enabled so you can assign rend[i].enabled
all together
something like
public GameObject[] Obj ;
// Using List here you can already initialize it here
// which would be possible using an array
private List<bool> Status = new List<bool>();
private List<MeshRenderer> rend = new List<MeshRenderer>();
private void Start(){
for (int i = 0; i < Obj.Length; i++)
{
rend.Add(Obj[i].GetComponentInChildren<MeshRenderer>());
bool existsAndEnabled = rend[i] != null && rend[i].enabled;
Status.Add(existsAndEnabled);
}
}
Note that it is still possible that for one of the GameObjects in Obj there is no MeshRenderer found so I say Status shall also be false if there was none.

Strange Null Reference Error while adding element to IList

I've got a very confusing problem with a null reference error. It reads as:
NullReferenceException: Object reference not set to an instance of an object
GameHandler.Update () (at Assets/Scripts/GameHandler.cs:33)
Here's the lengthy context:
C#
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class GameHandler : MonoBehaviour {
public GameObject canvas;
IList<GameObject> selected;
GameObject[] troopObjects;
bool isSelecting = false;
Vector3 mousePosition1;
void Update() {
// If we press the left mouse button, save mouse location and begin selection
if (Input.GetMouseButtonDown(0)) {
isSelecting = true;
mousePosition1 = Input.mousePosition;
if(selected != null) {
foreach (GameObject selectedTroop in selected) {
selectedTroop.transform.GetChild(0).gameObject.SetActive(false);
};
};
selected = null;
};
// If we let go of the left mouse button, end selection
if (Input.GetMouseButtonUp(0)) {
isSelecting = false;
};
if (isSelecting) {
troopObjects = GameObject.FindGameObjectsWithTag("Troop");
foreach (GameObject troop in troopObjects) {
if (IsWithinSelectionBounds(troop)) {
selected.Add(troop);
troop.transform.GetChild(0).gameObject.SetActive(true);
};
};
};
if (selected != null) {
};
}
// Use this for initialization
void Start() {
canvas.SetActive(false);
}
void OnGUI() {
if (isSelecting) {
// Create a rect from both mouse positions
var rect = Utils.GetScreenRect(mousePosition1, Input.mousePosition);
Utils.DrawScreenRect(rect, new Color(0.8f, 0.8f, 0.95f, 0.25f));
Utils.DrawScreenRectBorder(rect, 2, new Color(0.8f, 0.8f, 0.95f));
}
}
public bool IsWithinSelectionBounds(GameObject gameObject) {
if (!isSelecting)
return false;
var camera = Camera.main;
var viewportBounds = Utils.GetViewportBounds(camera, mousePosition1, Input.mousePosition);
return viewportBounds.Contains(camera.WorldToViewportPoint(gameObject.transform.position));
}
}
Now the problem code would appear to be this right here:
if (isSelecting) {
troopObjects = GameObject.FindGameObjectsWithTag("Troop");
foreach (GameObject troop in troopObjects) {
if (IsWithinSelectionBounds(troop)) {
selected.Add(troop);
troop.transform.GetChild(0).gameObject.SetActive(true);
};
};
};
What the error is referencing is this:
selected.Add(troop);
It's saying that "troop" is a null reference. Now when I remove this line of code, the rest works just fine. Which makes no sense because right after that problem code, there is this:
troop.transform.GetChild(0).gameObject.SetActive(true);
Which uses that same "troop" reference. I would love for some assistance on this one because it's got me stumped. If any extra information is needed just let me know.
Are you sure that the troop is null, not selected? Your code above checks selected and if it is not null it nulls it.
selected = null;
You are nullifying the instance and then based on some condition trying to add the elements to the NULL object. That is not right.
Please change this line :
selected = null;
to this :
selected.Clear();
And instantiate the list in the beginning :
void Update() {
selected = new List<GameObject>();

Categories

Resources