Strange behavior adding objects to Array Unity3d C# - c#

I am seeing some odd behavior, when adding objects to an array of GameObjects. Here is my sudo code:
public class GameController : MonoBehaviour {
public GameObject[] MyArray;
void Awake() {
Application.runInBackground = true;
MyArray= new GameObject[144];
}
//some code that calls myFunction
public IEnumerator myFunction(float time)
{
int counter = 0;
GameObject card= AnotherArrayOfGameObjects[0];
for (int j = 0; j < 144; j++)
{
card.name = "BLANK_" + (j+1);
Instantiate(card, cardVector, Quaternion.Euler(0, rotateAdujust, 180));
MyArray[counter] = card;
yield return new WaitForSeconds(time);
counter++;
}
}
}
This function kind of works ok. It creates my object and instantiates it. But, when it adds the object "card" to the array MyArray, it does not add it correctly. I.e, as this is in a loop, 0-143, I would expect the array to look like this:
MyArray[0] = "BLANK_1"
MyArray[1] = "BLANK_2"
....
MyArray[143] = "BLANK_144"
Instead, it looks like this:
MyArray[0] = "BLANK_144"
MyArray[1] = "BLANK_144"
....
MyArray[143] = "BLANK_144"
I can see all this, by the way, because MyArray is a public variable in my code, so I can see it in Unity's Inspector. If I debug, I can see each iteration overwrite the previous. So, in the first iteration of the loop, i get:
MyArray[0] = "BLANK_1"
Then in the second iteration of the loop, I get:
MyArray[0] = "BLANK_2"
MyArray[1] = "BLANK_2"
In the Third:
MyArray[0] = "BLANK_3"
MyArray[1] = "BLANK_3"
MyArray[2] = "BLANK_3"
Until it completes the full loop and all items in the array say "BLANK_144"
Can anyone explain why this is happening?

You are adding same card object to array on each iteration. And you are changing name of this object on each iteration. Thus you end up with array, all items of which are pointing to same card instance. And that card instance whill have latest name which you have assigned (on last iteration):
MyArray[counter] = card;
Remember - Instantiate method returns cloned object, but it does not change the object which you are cloning. So you instantiate new clone on each iteration, but you don't save it anywhere.
You should add to array card clones which you are instantiating:
GameObject card = AnotherArrayOfGameObjects[0];
for (int j = 0; j < 144; j++)
{
var cardClone = Instantiate(card, cardVector, Quaternion.Euler(0, rotateAdujust, 180));
cardClone.name = "BLANK_" + (j+1);
MyArray[j] = cardClone;
yield return new WaitForSeconds(time);
}

Related

Expensive GetComponent<CanvasGroup>() call, unsure how to cache

I am getting a performance warning from my IDE that the GetComponent() call is expensive, especially since I am calling MoveCards() in Update(). I understand why it's expensive and usually know how to fix with caching in Awake() but since it's prefixed with cards[i], I am unsure how to go about it.
private void MoveCards()
{
// This loop moves the cards.
for (var i = 0; i < cards.Length; i++)
{
cards[i].localPosition = Vector3.Lerp(cards[i].localPosition, cardPositions[i + cardArrayOffset],
Time.deltaTime * cardMoveSpeed);
if (!(Mathf.Abs(cards[i].localPosition.x - cardPositions[i + cardArrayOffset].x) < 0.01f)) continue;
cards[i].localPosition = cardPositions[i + cardArrayOffset];
// This disables interaction with cards that are not on top of the stack.
cards[i].gameObject.GetComponent<CanvasGroup>().interactable = cards[i].localPosition.x == 0;
}
}
What I would do is make the card GameObject have it’s own MonoBehaviour to store a reference to those components. Then, change the cards collection to store a reference to the card MonoBehaviour.
You would store cards[] as a CanvasGroup instead of a GameObject.
You would declare it like this in Awake():
GameObject[] cardObjects = new GameObject[10];
CanvasGroup[] cards = new CanvasGroup[cardObjects.Length];
for (int i = 0; i < cardObjects.Length; i++)
{
cards[i] = cardObjects[i].GetComponent<CanvasGroup>();
}
We loop through all of the card objects, and assign that component to a certain object in an array.
In your function you showed in the question, you would change it to this:
private void MoveCards()
{
for (int i = 0; i < cardObjects.Length; i++)
{
cardObjects[i].transform.localPosition = Vector3.Lerp(cardObjects[i].transform.localPosition, cardPositions[i + cardArrayOffset], Time.deltaTime * cardMoveSpeed);
if (!(Mathf.Abs(cardObjects[i].transform.localPosition.x - cardPositions[i + cardArrayOffset].x) < 0.01f)) continue;
cardObjects[i].localPosition = cardPositions[i + cardArrayOffset]
cards[i].interactable = cardObjects[i].transform.localPosition.x == 0;
}
}
We change all of the cards variables to cardObjects. On the GetComponent<>() line, we keep cards and remove the .gameObject.GetComponent<CanvasGroup>().
Important: You didn’t show us where some of the variables were created. Like cardPositions. If this is created using cards, make sure to use the cardObjects variable.

I want to create an array of scene indexes and use it, when i need. The problem is that i don't know how to create the array itself

I did this
public int[] sceneIndex;
public Text[] texts;
IEnumerator ChoosingModes()
{
string[] modes = new string[] { "Cocks", "Tanks", "Cars" };
sceneIndex = new int[] { };
for (int i = 0; i < 5; i++)
{
int x = Random.Range(0, modes.Length);
texts[i].text = modes[x];
sceneIndex[i] = x + 3;
yield return new WaitForSeconds(0.75f);
}
}
It obviously doesn't work, what to do with my in array named 'sceneIndex'?
When you do sceneIndex = new int[] { };, you're locking the length of the length of sceneIndex to 0. Instead, try either sceneIndex = new int[number or scenes]; (locking the length of sceneIndex to the number of scenes you have) or just doing nothing. Since the array is public, you can set the values in the inspector and you won't have to define it in the code.

Unity want to get gameobjects from another array

I have an array that gets all the gameobjects with the enemyMelee tag and i want to put the ones that have the idEnemy equal to the idDestination in another array.
How can i do this?
The maybeBlobsEnemy is the array that has all of them and blobsEnemy is the array that i want with only the condition above
Here is my code
void Update()
{
maybeBlobsEnemy = GameObject.FindGameObjectsWithTag("blobMelee");
for (int i = 0; i < maybeBlobsEnemy.Length; i++)
{
if (maybeBlobsEnemy[i].GetComponent<BlobBehavior>().idBlob == idDestination)
{
blobsEnemy[i] = maybeBlobsEnemy[i];
}
}
Thank you for you time

The unity console claims that the variable temp is declared but never used

using UnityEngine;
using System.Collections;
public class GameBoardScript : MonoBehaviour {
public int m_size;
public GameObject m_PuzzlePiece;
void Start() {
GameObject temp;
for (int i = 0; i < m_size; i++) {
for (int j = 0; j<m_size; j++) {
temp = (GameObject)Instantiate(m_PuzzlePiece, new Vector2(i*400/m_size, j*400/m_size), Quaternion.identity);
}
}
}
}
The reason you get this warning is because you assign the variable. But you don't use the variable of temp. Do anything with the variable after it's deceleration and the warning will go away! For example:
for (int i = 0; i < m_size; i++)
{
for (int j = 0; j<m_size; j++)
{
temp = (GameObject)Instantiate(m_PuzzlePiece, new Vector2(i*400/m_size, j*400/m_size), Quaternion.identity);
temp.SetActive(false); // this is an arbitrary use of temp. don't actually do this
}
}
Joe Blow is right that you should just call instantiate without assigning it's return to a variable if you don't intend to do anything to the instantiated GameObject afterwards. However just declaring the variable local to the inner for loop will still not remove the warning if you still fail to use it.
Please Note The compiler doesn't work out that the m_size variable could be 0 and might not reach the inner for loop. The warning occurrs because the line
temp = (GameObject)Instantiate(m_PuzzlePiece, new Vector2(i*400/m_size, j*400/m_size), Quaternion.identity);
is an assignment not a use of the variable temp. You could assign it as many times as you want and you will still get the same warning. You need to either use it or not assign the returned GameObject from Instantiate to a variable at all.

Get object data outside for loop in C#

I'm really new to C#, I started with Javascript so it's a bit of a struggle to understand the behaviour of C#.
I am trying to create a simple object inside my for loop and then display the assigned value afterwards but there is a scope issue with this, and am unclear on how I can then access it outside of the for loop?
This is my code setup:
public class Player{
public int identity;
public int score;
public Player(int id){
identity = id;
score = 0;
}
}
for (int i = 0; i < max; i++){
Player player = new Player(i);
}
//here i want to access a player and print the information for said player
I don't know how i can access the newly created players outside of the loop, how is this done?
I can understand how this could be confusing coming from JavaScript, in JavaScript all your variables are hoisted to the function (or global) scope. That's not true in C#, in C# variables are generally scoped within the nearest set of {}s. But are available for use within child scopes.
So you could write your code like this:
List<Player> players = new List<Player>();
Player player = null;
for (int i = 0; i < max; i++){
player = new Player(i);
players.Add(player);
}
Console.WriteLine(players[x]);
In this example you would have access to each individual player outside of the foreach because it's declared at the higher scope. But this would be more appropriately written:
List<Player> players = new List<Player>();
for (int i = 0; i < max; i++){
Player player = new Player(i);
players.Add(player);
}
Console.WriteLine(players[x]);
It's generally considered good practice to declare your variables as close to their usage as you can.
A final optimisation, that you yourself suggested, would be to eliminate the player declaration completely (as it's unnecessary) and inline it. That leaves us with the final version:
List<Player> players = new List<Player>();
for (int i = 0; i < max; i++){
players.Add(new Player(i));
}
Console.WriteLine(players[x]);
you can try something like this
Player player = new Player();
List<Player> playerlist = new List<Player>();
for (int i = 0; i < max; i++){
player = new Player(i);
playerlist.Add(player);
}

Categories

Resources