Transform.Find sends null value - c#

I am making a simple deck/card project. My project has a card prefab, with a Canvas child which would contain the UI version of the suit/card information. The information can be displayed with one card, but has problems when it comes to multiple cards. My theory on the matter is due to GameObject.FindGameObjectWithTag(string) finds the first instance of the gameObject that holds the tag. So, when I draw multiple cards, it will just rewrite the first card, rather than draw on the other cards.
I have tried Transform.Find(string), but it has turned up null, despite having a game object set in the Editor. One solution would be to use multiple tags with numbers after the name, ala topNum1, topNum2, etc., but doing that for 52 different numbers 3 times each sounds very repetitive, and frustrating. Is there a better way to do this?
Card code below:
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
/// <summary>
/// This class sets up the card object, which can be used in a variety of games.
/// </summary>
public class Card: MonoBehaviour
{
int cardNum;//The Value of the card.
int cardType;//The ‘Suit’ of the card. Hearts, Spades, Diamonds, Clubs
//Text topText;
Text botText;
Text faceText;
Text topText;
//Creates a default card.
public Card()
{
cardNum = -1;
cardType = -1;
topText = GameObject.FindGameObjectWithTag("topText").GetComponent<Text>();
//topText = gameObject.transform.FindChild("Canvas").gameObject.transform.FindChild("TopCardValue").GetComponent<Text>();
botText = GameObject.FindGameObjectWithTag("botText").GetComponent<Text>();
faceText = GameObject.FindGameObjectWithTag("faceText").GetComponent<Text>();
}
//Creates a custom card, with the provided values.
public Card(int cN, int cT)
{
cardNum = cN;
cardType = cT;
topText = GameObject.FindGameObjectWithTag("topText").GetComponent<Text>();
//topText = gameObject.transform.FindChild("Canvas").gameObject.transform.FindChild("TopCardValue").GetComponent<Text>();
botText = GameObject.FindGameObjectWithTag("botText").GetComponent<Text>();
faceText = GameObject.FindGameObjectWithTag("faceText").GetComponent<Text>();
}
//returns the card’s value.
public int getCardNum()
{
return cardNum;
}
//returns the card’s suit.
public int getCardType()
{
return cardType;
}
//Sets the card’s value.
public void setCardNum(int newNum)
{
cardNum = newNum;
}
//Sets the card’s suit.
public void setCardType(int newType)
{
cardType = newType;
}
//Checks if the card’s value is a face card (Jack, Queen, King, or Ace)
public bool checkIfFace()
{
if (getCardNum() > 10 && getCardNum() < 15 || getCardNum() == 0)
return true;
else
return false;
}
//Checks if the card is a valid card.
public bool checkifValid()
{
if (getCardType() < -1 || getCardType() > 4)
{
Debug.LogError("Error: Card Type not valid. Card type is: " + getCardType());
return false;
}
if (getCardNum() < 0 || getCardNum() > 15)
{
Debug.LogError("Error: Card Value not valid. Card value is : " + getCardNum());
return false;
}
return true;
}
//Prints out the card information.
public void printOutCardInfo()
{
string value = "";
string suit = "";
if (getCardNum() == 1)
value = (" Ace");
else if (getCardNum() > 1 && getCardNum() < 11)
value = (getCardNum().ToString());
else if (getCardNum() == 11)
value = ("Jack");
else if (getCardNum() == 12)
value = ("Queen");
else if (getCardNum() == 13)
value = ("King");
else
Debug.LogError("Error: No Num Found! The number in question is: " + getCardNum());
switch(getCardType())
{
case 0:
suit = ("Hearts");
break;
case 1:
suit = ("Spades");
break;
case 2:
suit = ("Diamonds");
break;
case 3:
suit = ("Clubs");
break;
default:
Debug.LogError("Error: Suit not found.");
break;
}
topText.text = value;
botText.text = value;
faceText.text = suit;
}
}
Any and all help would be greatly appreciated. Thank you for your time.
EDIT: Since a few people asked for it, I have edited this question to include the code that this class is called in:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class ListDeck : MonoBehaviour
{
//Card[] deckOfCards;
List<Card> deckOfCards;
// public GameObject spawner;
//public GameObject card;
//The default constructor.
public ListDeck()
{
deckOfCards = new List<Card>();
setUpDeck(4, 14);
randomizeDeck();
}
//Sets up the deck
public void setUpDeck(int numSuits, int numValues)
{
int counter = 0;
for (int i = 1; i < numValues; i++)//the thirteen values.
{
for (int j = 0; j < numSuits; j++)//The four suits.
{
Debug.Log("I value: " + i + " j value: " + j);
deckOfCards.Add(new Card(i, j));
counter++;//Increments the counter.
}
}
}
//Randomizes the deck so that the card dealout is random.
//http://answers.unity3d.com/questions/486626/how-can-i-shuffle-alist.html
public void randomizeDeck()
{
for (int i = 0; i < deckOfCards.Count; i++)
{
Debug.Log(i);
Card temp = deckOfCards[i];
int randomIndex = Random.Range(i, deckOfCards.Count);
deckOfCards[i] = deckOfCards[randomIndex];
deckOfCards[randomIndex] = temp;
}
}
//Prints out the deck for the game.
public void printOutDeck()
{
for (int i = 0; i < deckOfCards.Count; i++)
{
Debug.Log("Card " + i + ": ");
deckOfCards[i].printOutCardInfo();
}
}
public List<Card> getDeck()
{
return deckOfCards;
}
public void transferCards(List<Card> deckTo, int numCards)
{
for (int i = 0; i < numCards; i++)
{
deckTo.Add(deckOfCards[0]);
deckOfCards.RemoveAt(0);
}
}
}

This is because you are calling the Unity API functions from a constructor function. Basically, you are doing this during deserialization and in another Thread.
In Unity 5.3.4f1 and below, the Find function will silently fail when called from a constructor and you won't know. This is one of the mistakes that is complicated to track in Unity.
In Unity 5.4 and above, Unity decided to add error message to alert you about this problem. You won't see it now because you are still using 5.3. The error is as fellow:
FindGameObjectWithTag is not allowed to be called from a MonoBehaviour
constructor (or instance field initializer), call it in Awake or Start
instead. Called from MonoBehaviour 'Card' on game object 'Cube'.
Similar error message will appear when the Find function is called in a constructor function.
Continue reading for more descriptive information and solution:
Inheriting from MonoBehaviour vs not inheriting from MonoBehaviour
Inheriting from MonoBehaviour:
1.You can attach the script to a GameObject.
2.You can't use the new keyword to create a new instance of a script that inherits from MonoBehaviour. Your deckOfCards.Add(new Card(i, j)); is wrong in this case since Card inherits from MonoBehaviour.
3.You use gameobject.AddComponent<Card>() or the Instantiate(clone prefab) function to create new instance of script. There is an example at the end of this.
Rules for using a constructor function in Unity:
1.Do not use a constructor in a script that inherits from MonoBehaviour unless you understand what's going on under the hood in Unity.
2.If you are going to use a constructor, do not inherit the script from MonoBehaviour.
3.If you break Rule #2, do not use any Unity API in a constructor function of a class that inherits from MonoBehaviour.
Why?
You cannot call Unity API from another Thread. It will fail. You will either get an exception or it will silently fail.
What does this have to do with Threads?
A constructor function is called from another Thread in Unity.
When a script is attached to a GameObject and that script inherits from MonoBehaviour and has a constructor, that constructor is first called from Unity's main Thread(which is fine) then it is called again from another Thread (non Unity's main Thread). This breaks rule #3. You cannot use Unity API from another function.
You can prove this by running the code below:
using UnityEngine;
using System.Threading;
public class Card : MonoBehaviour
{
public Card()
{
Debug.Log("Constructor Thread ID: " + Thread.CurrentThread.ManagedThreadId);
}
void Start()
{
Debug.Log("Start() function Thread ID: " + Thread.CurrentThread.ManagedThreadId);
}
// Update is called once per frame
void Update()
{
Debug.Log("Update() function Thread ID: " + Thread.CurrentThread.ManagedThreadId);
}
}
Output when attached to a GameObject:
Constructor Thread ID: 1
Constructor Thread ID: 20
Start() function Thread ID 1
Update() function Thread ID: 1
As you can see, the Start() and Update() functions are called from the-same Thread (ID 1)which is the main Thread. The Constructor function is also called from the main Thread but then called again from another Thread (ID 20).
Example of BAD code: Because there is a constructor in a script that inherits from MonoBehaviour. Also bad because new instance is created with the new keyword.
public class Card : MonoBehaviour
{
Text topText;
//Bad, because `MonoBehaviour` is inherited
public Card()
{
topText = GameObject.FindGameObjectWithTag("topText").GetComponent<Text>();
}
}
then creating new instance with the new keyword:
Card card = new Card(); //Bad, because MonoBehaviour is inherited
Example of Good code:
public class Card : MonoBehaviour
{
Text topText;
public Awake()
{
topText = GameObject.FindGameObjectWithTag("topText").GetComponent<Text>();
}
}
then creating new instance with the AddComponent function:
Card card = gameObject.AddComponent<Card>()
OR clone from prefab with the Instantiate function:
public Card cardPrfab;
Card card = (Card)Instantiate(cardPrfab);
Not inheriting from MonoBehaviour:
1.You can't attach the script to a GameObject but you can use it from another script.
2.You can simply use the new keyword to create new instance of the script when it doesn't inherit from MonoBehaviour.
public class Card
{
Text topText;
//Constructor
//Correct, because no `MonoBehaviour` inherited
public Card()
{
topText = GameObject.FindGameObjectWithTag("topText").GetComponent<Text>();
}
}
Then you can create new instance with the new keyword like:
Card card = new Card(); //Correct, because no MonoBehaviour inherited
Solution:
1.If you decide to inherit from MonoBehaviour and have to attach the script to a GameObject , you must remove all your constructor functions and put code inside them into Awake() or Start() function. The Awake() and Start() functions are automatically called by Unity once and you use them initialize your variables. You don't have to call them manually. Do not use the new keyword to create instance of scripts that inherit from MonoBehaviour.
2.If you decide not to inherit from MonoBehaviour and you are not required to attach the script to a GameObject, you can have a constructor function like you did in your current code and you can use Unity's API in those constructor functions.You can now use new keyword to create instance of the script since it doesn't inherit from MonoBehaviour.

From my understanding of the code and question, every card gets it's text from one and the same GameObject as all of them are searching for the same Object by text.
I believe your issue might be resolved if you actually initialize the values you want to get, in the Start() method of each object.
Thus in reverse for drawing cards, every gameObject that is beeing drawn receives the same text.
And as stated in the answer quicker than me, try not to use Find() as it get's really slow when it comes to more cards or even just objects.
Try setting the text to display from a Start() method inside of a script on the prefab, then you have easy access to all properties of that object and if needed a database of all cards from where you can reference the text to draw.
Might be that I am completely wrong on what you're asking however.
This is just how I would do it, since you said all help is welcome :)

Transform.Find()/Transform.FindChild() is searching direct CHILD.
I believe, you trying to find some subChild, that's why its returns null.
So if you have to find "heart" gameObject that have location: room/human/body/heart
inside of "room" element, you need to perform:
Transform.Find("human/body/heart")
but not
Transform.Find("heart")
as "heart" is not subChild of "room" game object

Related

Unity Question: How Can I Switch From One prefab To Another With GetAxis?

I want to bind the up and down arrow keys to cycle through different sprites upon being pressed. If one end is reached, it would loop back to the first sprite. I've tried using the following code:
public class PhaseChanger : MonoBehaviour
{
// saved for efficiency
[SerializeField]
public GameObject prefabMoon0;
[SerializeField]
public GameObject prefabMoon1;
[SerializeField]
public GameObject prefabMoon2;
[SerializeField]
public GameObject prefabMoon3;
[SerializeField]
public GameObject prefabMoon4;
// needed for new phase
GameObject currentPhase;
bool previousFramePhaseChangeInput = false;
/// <summary>
/// Start is called before the first frame update
/// </summary>
void Start()
{
currentPhase = Instantiate<GameObject>(prefabMoon0);
}
/// <summary>
/// Update is called once per frame
/// </summary>
void Update()
{
// change phase on up arrow or down arrow
if (Input.GetAxis("ChangePhase") > 0)
{
// only change phase on first input frame
if (!previousFramePhaseChangeInput)
{
previousFramePhaseChangeInput = true;
// Save current position and destroy current phase
Destroy(currentPhase);
// instantiate next phase
if (currentPhase = prefabMoon0)
{
currentPhase = Instantiate(prefabMoon1);
}
else if (currentPhase = prefabMoon1)
{
currentPhase = Instantiate(prefabMoon2);
}
else if (currentPhase = prefabMoon2)
{
currentPhase = Instantiate(prefabMoon3);
}
else if (currentPhase = prefabMoon3)
{
currentPhase = Instantiate(prefabMoon4);
else
{
// no phase change input
previousFramePhaseChangeInput = false;
}
}
}
}
When I attach the script to my main camera and run it, I'm able to make a single change with the up arrow, and then nothing else happens on subsequent presses.
I feel like I'm really close to making this work, but I also may being doing the whole thing inefficiently. Help would be much appreciated, thanks!
Also: I know I said sprites in my post and am sharing a script that calls on prefabs. I didn't know how to approach this using just the sprites without making a prefab for each. Is it possible to do this without separate prefabs for each sprite?
Problems
First of all you are using assignments
currentPhase = XY
where you should be using
currentPhase == XY
The reason why it still compiles is the implicit conversion operator for UnityEngine.Object -> bool. Basically your assigning equals writing
currentPhase = XY;
if(currentPhase)
It won't work like this either way because you are using Instantiate to create a new clone of a prefab which will of course have a different reference than the original prefab it was cloned from.
So even if your checks where looking like
if(currentPhase == XY)
they will ever be true.
Solution
Instead of checking for reference equality I would rather store all prefabs/instances in an array
public GameObject[] phases;
and then simply have an int index for this array so you can simply move to the next element from the array by increasing the index.
private int currentPhase;
And you can increase it and make it wrap around using e.g.
currentPhase = (currentPhase + 1) % phases.Length;
so it will always grow from 0 up to phases.Length - 1 and then start over from 0 again.
And then I don't know the exact requirements of your use case but I would suggest to rather not all the time use Instantiate and Destroy but rather have already all the objects as instances under your object and just (de)actívate them!
you could do this like e.g.
public GameObject[] phases;
private int currentPhase;
private void Awake ()
{
Init();
}
private void Update ()
{
if (Input.GetAxis("ChangePhase") > 0)
{
if (!previousFramePhaseChangeInput)
{
previousFramePhaseChangeInput = true;
NextPhase();
}
}
else
{
previousFramePhaseChangeInput = false;
}
}
// Disables all phases except the first one and sets the current index to 0
private void Init()
{
for(var i = 1; i < phases.Length; i++)
{
phases[i].SetActive(false);
}
phases[0].SetActive(true);
currentPhase = 0;
}
// Disables the current phase and enables the next one
// wraps around at the end of the array
public void NextPhase()
{
phases[currentPhase].SetActive(false);
// increase the counter and wrap around at the end of the array
currentPhase = (currentPhase + 1) % phases.Length;
phases[currentPhase].SetActive(true);
}
If you still want to Instantiate the objects because having them already in the scene is no option (for whatever reason) you could do it right before calling Init like e.g.
public GameObject[] phasePrefabs;
private GameObject[] phases;
private void Awake ()
{
var amount = phasePrefabs.Length;
phases = new GameObject [amount];
for(var i = 0; i < amount; i++)
{
phases[i] = Instantiate(phasePrefabs[i]);
}
Init();
}
Though as said I would prefer to already have them right away as this is way less error prone ;)

How do I properly detect game objects?

My goal is to write one script that I can use on different game objects and it should have specific variables tied to it on that game object only without affecting other scripts in the process.
For example, if I take this script and put it on two game objects each game object should have their own unique variable value in that same script.
If my question is not clear enough, I'm more than happy to elaborate further.
I have a good understanding of the Unity Editor, however, I'm pretty new to C# so I don't think it's unreasonable that I made a rookie mistake somewhere in my code.
The way I've got things setup is that I have two separate scripts:
Fighting controls the values like the Team, Health, Attack Damage, Cool Down, Cooling down and Snap
TrigDetect controls the detection of a trigger being activated as a result of an enemy entering the trigger radius.
The problem I'm currently having lies in the TrigDetect script I guess.
It should also be noted that an empty attached to each game object in question contains both of these scripts and is tagged as "Troop".
TrigDetect
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TrigDetect : MonoBehaviour
{
//public GameObject[] Enemy;
bool once = false;
void OnTriggerEnter(Collider other)
{
if (other.CompareTag("Troop"))
{
//Debug.Log("Entered");
}
}
void OnTriggerExit(Collider other)
{
if (other.CompareTag("Troop"))
{
//Debug.Log("Exitted");
}
}
void OnTriggerStay(Collider other)
{
if (other.CompareTag("Troop"))
{
Fighting self = GetComponent<Fighting>();
GameObject g = GameObject.Find("Detection");
Fighting fScript = g.GetComponent<Fighting>();
//Enemy = GameObject.FindGameObjectsWithTag("Troop");
//Debug.Log("Staying");
//Debug.Log(Enemy);
//Debug.Log(self.Health);
//Debug.Log(fScript.Health);
if (once == false)
{
Debug.Log("I am the team:" + self.Team);
Debug.Log("I have detected the team:" + fScript.Team);
once = true;
}
if (self.Team != fScript.Team)
{
if (self.CoolingDown == false)
{
self.CoolingDown = true;
fScript.Health -= self.AttackDamage;
}
else
{
self.CoolDown -= Time.deltaTime;
if (self.CoolDown <= 0)
{
self.CoolingDown = false;
self.CoolDown = self.original;
}
}
}
}
}
}
Fighting
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Fighting : MonoBehaviour
{
public int Team = 1;
public int Health = 100;
public int AttackDamage = 10;
public float CoolDown = 2;
public float original = 2;
public bool CoolingDown = false;
public bool Snap = false;
// Update is called once per frame
void Update () {
if (Snap == true || Health <= 0)
{
//Destroy(gameObject, .5f);
Destroy(transform.parent.gameObject);
}
if (Input.GetKey(KeyCode.N)) Instantiate(transform.parent.gameObject);
}
}
The expected result when I move one game object into the trigger radius of the other is that they should both start subtracting Health from each other based on the AttackDamage value. They should do this every time the CoolingDown value is false. When an attack is executed, it's flipped to true and a timer starts, when the timer is done it's flipped back to false.
However, upon moving the two objects into each other's radius', the first object has its health taken away as expected and then proceeds to do nothing until it's health reaches 0 then it dies because of the object attacking it. The object attacking is successfully attacking the other object but, is still not being affected by the object it's attacking.
Basically, Find(name) only returns the first instance of anything by that name, thus your g = Find(name) is almost guaranteed to never be the object related to your trigger/collision condition. The OnTriggerStay(Collider other) already gives you the 'other' collider that's in your trigger zone, so use it. :)
Replace this:
GameObject g = GameObject.Find("Detection");
Fighting fScript = g.GetComponent<Fighting>();
with this:
Fighting fScript = other.GetComponent<Fighting>();
To your question header:
Every instaced (non-static) value is allways unique to the according component and thereby to the according GameObject it is attached to. You might want to refrase the question because this is actually not your issue.
The problem is that when you do
GameObject.Find("Detection");
it actually finds the same object both times: Namely the first one in the hierarchy. So in one of of the two components you find your own empty object and skip the rest in
if(self.Team != FScript.Team)
.. you could try to use
other.Find("Detection");
instead to only search in the according context .. However, you should not use Find at all!
It is very performance intense
You should allways reuse references and not search them over and over again
You don't need it in your case
Since you say both scripts are attached to the same object you can simply use
GetComponent<Fighting>();
and you can do so already in Awake and reuse the reference instead:
private Fighting myFighting;
private void Awake()
{
myFighting = GetComponent<Fighting>();
}
Than for the collision you don't have to use Find either because you already have the reference of the object you collide with: other.gameObject. I don't know your entire setup but you can search for the component either downwards in the hierachy
// the flag true is sued to also find inactive gameObjects and components
// leave it without parameters if you don't want this
var otherFighting = other.GetComponentInChildren<Fighting>(true);
or searcg upwards in the hierachy
var otherFighting = other.GetComponentInParent<Fighting>(true);
or if you already know you collide exactly with the correct GameObject anyway simply use
var otherFighting = other.GetComponent<Fighting>();
I will use the latter in my example.
Than cheking the health all the time in Update is a huge perfomance issue. You should rather have a method e.g. TakeDamage and do your check only if your health is actually changed:
Fighting
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Fighting : MonoBehaviour
{
public int Team = 1;
public int Health = 100;
public int AttackDamage = 10;
public float CoolDown = 2;
public float original = 2;
// you don't need that flag see below
//public bool CoolingDown = false;
public bool Snap = false;
private void Update()
{
// you might also put this in a callback instead of update at some point later
if(Snap == true)
{
Destroy(transform.parent.gameObject);
}
// Note: this also makes not muh sense because if you destroyed
// the parent than you cannot instantiate it again!
// use a prefab instead
if (Input.GetKey(KeyCode.N)) Instantiate(transform.parent.gameObject);
}
public void TakeDamge(int DamageAmount)
{
Health -= DamageAmount;
if (Health > 0) return;
Destroy(transform.parent.gameObject);
}
}
Another performance issue in general: Even if Start, Update etc are empty, if they are present in your script Unity will call them. So if you don't use them then completely remove them to avoid that useless overhead.
So I would have
TrigDetect
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TrigDetect : MonoBehaviour
{
bool once = false;
private Fighting myFighting;
private void Awake()
{
myFighting = GetComponent<Fighting>();
}
void OnTriggerStay(Collider other)
{
// if wrong tag do nothing
if (!other.CompareTag("Troop")) return;
Fighting fScript = other.GetComponent<Fighting>();
// here you should add a check
if(!fScript)
{
// By using the other.gameObject as context you can find with which object
// you collided exactly by clicking on the Log
Debug.LogError("Something went wrong: Could not get the Fighting component of other", other.gameObject);
}
if (!once)
{
Debug.Log("I am the team:" + self.Team);
Debug.Log("I have detected the team:" + fScript.Team);
once = true;
}
// if same team do nothing
if (self.Team == fScript.Team) return;
// you don't need the CoolingDown bool at all:
self.CoolDown -= Time.deltaTime;
// if still cooling down do nothing
if(self.CoolDown > 0) return;
fScript.TakeDamage(self.AttackDamage);
self.CoolDown = self.original;
}
}

How can I enable one by one an array element(C#) Unity

This code is C# and I'm using the Unity game engine.
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class CollisionDetectionForQuest : MonoBehaviour {
public GameObject[] Garbages;
public GameObject[] GarbagesDummy;
public bool activateInactive;
//public GameObject[] Counter;
void Start(){
Garbages = GameObject.FindGameObjectsWithTag("Trash");
foreach (GameObject garb in Garbages) {
}
GarbagesDummy = GameObject.FindGameObjectsWithTag ("TrashDummy");
foreach(GameObject dummy in GarbagesDummy){
dummy.gameObject.SetActive (false);
}
}
void OnCollisionEnter(Collision obj){
if (obj.gameObject.CompareTag ("Trash")) {
obj.gameObject.SetActive (false);
activateInactive = true;
//in this line i want to activate my GarbageDummy 1 by 1 base on
//every Trash i put in my collider.
foreach(GameObject a in GarbagesDummy){
a.gameObject.SetActive (true);
}
}
So basically I want to activate the garbagedummy gameobject 1 by 1 on every trash I put on my collider. But I tried this and it doesn't work:
foreach(GameObject dummy in GarbageDummy){
for(int i = 0; i < GarbageDummy.Length; i++){
dummy.gameobject.setActive(true);
}
}
anyone help please.
Here is my screenshot:
If you would like to activate a single garbage dummy object (any of them in the array), then you don't want to use a loop there. Instead, keep track of which one you've activated up to as a field like this:
public int ActivatedDummyIndex; // Add this!
public GameObject[] Garbages;
public GameObject[] GarbagesDummy;
...
Then whenever a trash object is added, increase that number and use it as an index in your GarbagesDummy array:
void OnCollisionEnter(Collision obj){
if (obj.gameObject.CompareTag ("Trash")) {
obj.gameObject.SetActive (false);
activateInactive = true;
// Get the next dummy:
GameObject dummy = GarbagesDummy[ActivatedDummyIndex];
// Increase the index for next time:
ActivatedDummyIndex++;
// Activate that dummy:
dummy.SetActive(true);
}
}
This assumes that the number of dummy objects is the same as the number of trash objects. If you've got more trash objects and add them all, you'll eventually get an index out of range exception (as it would "run out" of dummy objects to use).
Whilst that should answer your question, it's worth mentioning that this isn't the typical approach. Rather than having a fixed number of hidden dummy objects and showing one when needed, you'd normally instance a new dummy object instead (using GameObject.Instantiate).

Trying to access instantiated object in 2d array Unity c#. Cant access or change elements?

First time question here.
I'm trying to build a game board like one you would see playing chess. I've made two scripts, one that spawns the board and stores each instantiated tile in a 2d array. And another script that sets the row and column location and onMouseOver prints that location to the console. However this is always print Row = 0 and Col = 0.
I've come to the conclusion that the 2d array is just setting a clone in memory and therefor the stored values I set via calling the function setRC(); are not being being found as the instantiated objects the the Unity scene don't share the memory location.
Anyone have an idea as to how I can fix this?
public class Map : MonoBehaviour {
public static int Row = 7;
public static int Col = 7;
private GameObject[,]boardPiece = new GameObject[Row,Col];
public GameObject prefab;
public GameObject prefab2;
void Start () {
for (int i = 0; i < Row; i++) {
for (int j = 0; j < Col; j++)
{
if(i%2 ==1 && j%2 ==1 || i%2 ==0 && j%2 ==0)
{
boardPiece[i,j] = (GameObject)Instantiate(prefab, new Vector3(i*4.0f,0,j*4.0f),Quaternion.identity);
}else{
boardPiece[i,j] = (GameObject)Instantiate(prefab2, new Vector3(i*4.0f,0,j*4.0f),Quaternion.identity);
}
boardPiece[i,j].GetComponent<BoardPiece>().setRC(i,j);
}
}
}
}
public class BoardPiece : MonoBehaviour {
private int rowPlace;
private int colPlace;
// Use this for initialization
void Start () {
rowPlace = 0;
colPlace = 0;
}
// Update is called once per frame
void Update () {
}
void OnMouseOver(){
string message = "Row: " + rowPlace + " Col: " + colPlace;
print (message);
}
public void setRC(int R, int C)
{
rowPlace = R;
colPlace = C;
print ("set " + rowPlace + "," + colPlace);
}
}
Without a complete code example (difficult or even impractical to provide for Unity3d projects I know, but still...) it is difficult to know for sure what the problem is. However…
While the Start() method is often used in a fashion similar to a constructor, it's important to understand that it's not in fact one. I don't think it's called yet by the time your code is calling the setRC() method on your new object.
This means that the flow of execution is that you first set the row and column values as desired, and then later the BoardPiece.Start() method is called by the Unity3d framework, setting the row and column values back to 0.
Since the rowPlace and colPlace fields are already set to 0 by default when the object is created, I think the best fix is to just remove those two lines from the Start() method altogether. They aren't needed, and I believe they are responsible for the problem you are having.

How do I access an updated variable between two scripts in Unity with C#?

Hopefully this isn't too much detail, I'm not used to asking programming questions.
I'm attempting to do the 3D Video Game Development with Unity 3D course that's on Udemy, though using C# instead of Javascript. I just finished up the tutorial that involves creating a space shooter game.
In it, a shield is created by the user when pressing a button. The shield has a "number of uses" variable that does not actually get used by the time the tutorial has finished. I'm trying to add it in, and have successfully managed to implement it so that with each use, we decrease the number of uses remaining, and no longer are able to instantiate the shield once that number is <=0.
This variable is stored on the player, and if I print it from the player, it returns the value I would expect.
However, I'm using a separate SceneManager.cs (this is where the tutorial placed the lives, and score, and timer variables ) where I print numbers into the GUI. My problem is that I cannot get my number of uses variable to stay current when I try to print it from the scene manager... it registers the initial value, but doesn't update after that.
Here is the Player Script
using UnityEngine;
using System.Collections;
public class player_script : MonoBehaviour {
// Inspector Variables
public int numberOfShields = 2; // The number of times the user can create a shield
public Transform shieldMesh; // path to the shield
public KeyCode shieldKeyInput; // the key to activate the shield
public static bool shieldOff = true; // initialize the shield to an "off" state
public int NumberOfShields
{
get{return numberOfShields;}
set{numberOfShields = value;}
}
// Update is called once per frame
void Update()
{
// create a shield when shieldKey has been pressed by player
if (Input.GetKeyDown (shieldKeyInput)) {
if(shieldOff && numberOfShields>0)
{
// creates an instance of the shield
Transform clone;
clone = Instantiate (shieldMesh, transform.position, transform.rotation) as Transform;
// transforms the instance of the shield
clone.transform.parent = gameObject.transform;
// set the shield to an on position
shieldOff = false;
// reduce the numberOfShields left
numberOfShields -=1;
}
}
print ("NumberOfShields = " + NumberOfShields);
}
public void turnShieldOff()
{
shieldOff = true;
}
}
when I run "print ("NumberOfShields = " + NumberOfShields);" I get the value I expect. (astroids trigger the turnShieldOff() when they collide with a shield.
Over in my Scene Manager however... this is the code I'm running:
using UnityEngine;
using System.Collections;
public class SceneManager_script : MonoBehaviour {
// Inspector Variables
public GameObject playerCharacter;
private player_script player_Script;
private int shields = 0;
// Use this for initialization
void Start ()
{
player_Script = playerCharacter.GetComponent<player_script>();
}
// Update is called once per frame
void Update ()
{
shields = player_Script.NumberOfShields;
print(shields);
}
// GUI
void OnGUI()
{
GUI.Label (new Rect (10, 40, 100, 20), "Shields: " + shields);
}
}
Any idea what I'm doing wrong that prevents shields in my SceneManager script from updating when NumberOfShields in my player_script updates?
I think you might have assigned a prefab into playerCharacter GameObject variable instead of an actual in game unit. In this case it will always print the default shield value of prefab. Instead of assigning that variable via inspector try to find player GameObject in Start function. You can for example give your player object a tag and then:
void Start() {
playerCharacter = GameObject.FindGameObjectWithTag("Player");
}

Categories