How to get variables/functions from a child GameObject? - c#

Hello! I am trying to make a room that will generate a random Layout from a list and get a variable from that layout. I have had some trouble with this since the layout is not there from the start of the game, and I have been working on this problem for a few days now. Currently, the "Room" GameObject searches it's children for the ones tagged "Layouts", then, after finding that GameObject, the code gets the "waves" variable from the Layouts component of the Layout. The code is here:
foreach (Transform child in transform)
{
if (child.tag == "Layouts")
{
layout = child.gameObject.GetComponent<Layout>();
}
}
layout.testFunction();
totalKillsNeeded = layout.waves;
When I run the code, nothing happens. No errors or anything. testFunction doesn't run, despite my Visual Studio showing the reference between the two scripts, and totalKillsNeeded returns 0 despite me checking and seeing that layout.waves equals 1 or 2. I have tried multiple methods of getting the Layout script, all of which have returned the same result. It is probably something small I havn't considered, but I still have not been able to figure it out. Thank you!

Your code should work, but in case, try this. Here's ScriptA.cs, put this on the parent
using UnityEngine;
public class ScriptA: MonoBehaviour
{
int totalKillsNeeded = 0;
ScriptB layout;
// Start is called before the first frame update
void Start()
{
foreach(Transform child in transform)
{
if(child.CompareTag("Layouts"))
{
layout = child.gameObject.GetComponent<ScriptB>();
}
}
layout.testFunction();
totalKillsNeeded = layout.waves;
Debug.Log($"Total Kills needed: {totalKillsNeeded}, layout waves {layout.waves}");
}
}
And ScriptB.cs, put this on the Child game objects, and set the wave in inspector if needed
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ScriptB: MonoBehaviour
{
public int waves = 12;
public void testFunction()
{
Debug.Log("Test Function Called, waves: {waves}");
}
}
Console output:
The hierarchy should look something like this:
If your last child has 0 waves, your for loop will only show the last one:

Related

One script changing multiple UI images, but it's not working

I'm having trouble with this code I'm trying to make.
My goal: Make each level button have that level's rank on it as well,
but I'm trying to do it from one script.
Firstly, here's how I have set up everything:
Those are the ranks inside each button and with a Rankdictator script that I made inside each rank gameobject.
Here's the script for the Rankdictator:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Rankdictator : MonoBehaviour
{
int levelnumberrank;
private Image thisimage;
public Sprite rankgold;
public Sprite ranksilver;
public Sprite rankbronze;
public Sprite blank;
private Levellockcheck levellockcheck;
public void Start()
{
levellockcheck = FindObjectOfType<Levellockcheck>();
thisimage = this.GetComponent<Image>();
levelnumberrank = PlayerPrefs.GetInt(("rankoflevel") + (levellockcheck.thisbuttonlevel));
if(levelnumberrank == 3)
{
thisimage.sprite = rankgold;
}
else if(levelnumberrank == 2)
{
thisimage.sprite = ranksilver;
}
else if (levelnumberrank == 1)
{
thisimage.sprite = rankbronze;
}
else if (levelnumberrank == 0)
{
thisimage.sprite = blank;
}
}
}
Now here's the problem. As I said, the ranks don't show on each button correctly when there are multiple buttons active, however, when I only have one button active, the rank shows. Here's an example:
That is when I have both buttons active. (Level 1 should have full rank and level 2 should have low rank. But it's showing nothing)
Now here's when only one button is active. It shows correctly.
Edit
I already used Debug.Log to confirm the levelnumberrank on each button and it references as expected.
But, it still doesn't reference the rank on each button when all of them are active, only does so when one button is active while the others are inactive.
I figured the problem out: it was the levellockcheck!
On each button, there was a levellockcheck script with a public int thisbuttonlevel.
I was, on each Rankdictator, referencing the levellockcheck like so:
Levellockcheck = FindObjectofType<levellockcheck>();
levelnumberrank = PlayerPrefs.GetInt(("rankoflevel") + (levellockcheck.thisbuttonlevel));
By doing that, I'm referencing different numbers all at the same time from each single button. Because the (thisbuttonlevel) on each button was different.
What I was supposed to do on Rankdictator:
Levellockcheck = this.GetComponentInParent<levellockcheck>();
By doing that, I referenced the (thisbuttonlevel) of the button itself.
And now:
It works!

Variables in other scripts in Unity

I am trying to create a game in Unity 2d. I have finished most of what I want to do and have moved on to the enemies. The enemies (dragons) come in from different points of screen. To do this I have placed sprite game objects where I want the dragon to spawn. I have made all of these objects a child of another object called DragonAncores. I attached a script to DragonAncores which says this...
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DragonTracker : MonoBehaviour {
// is gold dragon in play?
public bool GoldDragonInit = false;
// curently active dragons
public int DragonCount = 0;
// defalts to 5
public int Difficulty = 5;
}
I am then attaching a script to each sprite which will eventually summon in a dragon Prefab (containing 2 colliders and an animator) biased of If statment logic derived from the other variables.
Below is the code I am using.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Dragons : MonoBehaviour {
// same as GoldDragonInit
bool GoldDragonSpawn = false;
// same number as DragonCount in DragonTrackeer
int LiveDragons;
// same as Difficulty
int DifLev;
//get cariables from other script
DragonAncors.cs.GetComponent.<DragonTracker>() GoldDragonInit = GoldDragonSpawn;
System.Random RNG= new System.Random();
void update()
{
RSpawn=RNG.Next(0,2)
DragonType=RNG.Next(0,101)
if (RSpawn = 1) ;
{
if (LiveDragons > DifLev) ;
{
if (DragonType > 99) ;
{
// summon regular dragon
}
if (DragonType = 100) ;
{
if (GoldDragonSpawn = true) ;
{
// summon gold dragon
}
}
}
}
}
}
This is throwing up this error list.
This shows my hierarchy in unity and the anchor points (the Squair crosshair looking things)
I have looked for other threads that adress this topic and they all try different methods, none work.
I am using Unity 2018.2.18f1
There are a few errors in your code here. The following is incorrect.
//get cariables from other script
DragonAncors.cs.GetComponent.<DragonTracker>() GoldDragonInit = GoldDragonSpawn;
The correct way to access this, seeing as you said DragonAncors is the parent would be:
GetComponentInParent<DragonTracker>().GoldDragonInit = GoldDragonSpawn;
This sets the GoldDragonInit Boolean to the value of GoldDragonSpawn. This has to be inside a function, as you have it outside of a function I presume you needed this set on start. Therefore I have placed it in the void Start() function. This is called at the start of the game(loaded scene).
You also do not need semi-colons ; after an if statement, however it does need to appear after every line of difinitive code. The code you have provided should instead look like this.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Dragons : MonoBehaviour {
// same as GoldDragonInit
bool GoldDragonSpawn = false;
// same number as DragonCount in DragonTrackeer
int LiveDragons;
// same as Difficulty
int DifLev;
void Start()
{
// variables from other script
GetComponentInParent<DragonTracker>().GoldDragonInit = GoldDragonSpawn;
}
System.Random RNG= new System.Random();
void update()
{
RSpawn=RNG.Next(0,2);
DragonType=RNG.Next(0,101);
if (RSpawn = 1)
{
if (LiveDragons > DifLev)
{
if (DragonType > 99)
{
// summon regular dragon
}
if (DragonType = 100)
{
if (GoldDragonSpawn = true)
{
// summon gold dragon
}
}
}
}
}
}
This works because DragonTracker is a script in the objects parent. If this was not the case then GetComponentInParent().GoldDragonInit = GoldDragonSpawn; would be replaced like so:
[SerializeField]
private GameObject DragonAncors;
void Start()
{
DragonAncors.GetComponent<DragonTracker>().GoldDragonInit = GoldDragonSpawn;
}
This is not valid c# code:
//get cariables from other script
DragonAncors.cs.GetComponent.<DragonTracker>() GoldDragonInit = GoldDragonSpawn;
Why? Because it isn't inside a method.
Also, the comment is wrong. It isn't getting a variable (typo, too), its setting a variable in another script!
The reason for the first...16 problems Visual Studio is complaining about are because of this line.
At this location you are only allowed to declare methods, fields, and properties and you're currently trying to access another class and change one of its members (something you can only do inside a method).
Additionally, you have .cs which I assume is because "DragonAnchors.cs is the file name!" which you don't need to do. I'm not sure how to go about rewriting this line (inside Start()) as I'm not sure what you're trying to actually do. That is, I don't know where an instance of DragonAnchors actually resides. You're calling GetComponent(), which is typically reserved for accessing components attached to game objects, but as you've attempted to do so on a static class reference, I'm not sure if you meant to call it on this or on something else.
This is how you can get to DragonTracker:
DragonTracker dt = GameObject.Find("DragonAncores").GetComponent<DragonTracker>()
Debug.Log(dt.DragonCount);
There are a lot of errors there and they may take some steps to go through, but first things first you should clear up the issue with the code you're trying to use being unsupported. Go into the project settings and change the compiler language version as it notes on the 5th error down. This should allow you to use the newer functionality.

Using foreach loop to find GameObjects that are children of something else (Unity3d)

I am making a video game, and while I have an existing code in generic C#, I now have to move it to Unity. I have some basic knowledge with generic C#, but I just started to learn the Unity way of coding.
For starters, I want to write a code that positions all game areas to correct positions, then turn them invisible. Yes, don't be surprised, they need to be all in same places.
Areas can have three size options, I called them Small, Medium and Large. Small and large areas have to be written manually.
List <GameObject> SmallAreas = new List<GameObject>();
void DefineSmallAreas()
{
SmallAreas.Add(areaConfirmLoad);
SmallAreas.Add(areaConfirmQuit);
SmallAreas.Add(areaConfirmSave);
SmallAreas.Add(areaGameSaved);
SmallAreas.Add(areaSave);
SmallAreas.Add(areaLoad);
}
Same with large areas.
Now, all other areas, are medium, and there is a large number of them.
So, I want to go through all game objects that are children of "areaContainer", check if their names start with "area", and if they do, I want to add them to MediumAreas list.
That's how I tried it:
void DefineMediumAreas()
{
GameObject areaContainer = GameObject.Find("areaContainer");
foreach (GameObject thisObject in areaContainer)
{
char[] a = thisObject.Name.ToCharArray();
if (a.Length >= 4)
{
char[] b = { a[0], a[1], a[2], a[3] };
string thisObjectType = new string(b);
(if (thisObjectType == "area")&&(!(SmallAreas.Contains(thisObject))
&&(!(LargeAreas.Contains(thisObject)))
{
MediumAreas.Add(thisObject);
}
}
}
This however shows an error, that "areaContainer" can't be used that way, I don't have access to Unity now, so can't copy exact message. I think that it's something like "Gameobject doesn't have IEnumerator".
I did try to google for the better approach, and found something called "transform".
foreach(Transform child in transform)
{
Something(child.gameObject);
}
What I don't understand, is how to use this "transform" in my specific situation.
Please don't get angry at me if this question is silly, I am very new to Unity, and have to learn it from scratch.
And a small second question. Will this work of turning object invisible work:
foreach(GameObject thisObject in MediumAreas)
{
thisObject.position = MediumVector;
thisObject.GetComponent<Renderer>().enabled = false;
}
MediumVector is location where the object must be moved to, and it seems to be working.
You can do this: foreach(Transform child in transform)
because the Transform class implements IEnumerable and have some mechanism that enables you to access the child GameObjects with the foreach loop.
Unfortunately, you can't do this: foreach (GameObject thisObject in areaContainer)
because areaContainer is a GameObject and this implementation is not done for the GameObject class. That's why you are getting this error:
foreach statement cannot operate on variables of type
'UnityEngine.GameObject' because 'UnityEngine.GameObject' does not
contain a public definition for 'GetEnumerator'
To fix it, change your loop to use Transform after finding the GameObject:
GameObject areaContainer = GameObject.Find("areaContainer");
foreach (Transform thisObject in areaContainer.transform){}
The complete code:
List<GameObject> MediumAreas = new List<GameObject>();
void DefineMediumAreas()
{
GameObject areaContainer = GameObject.Find("areaContainer");
foreach (Transform thisObject in areaContainer.transform)
{
//Check if it contains area
if (thisObject.name.StartsWith("area"))
{
//Add to MediumAreas List
MediumAreas.Add(thisObject.gameObject);
}
}
}
There is multiple approaches to your problem. One of them is using Tags. Simply mark your MediumArea prefab with some Tag and then you can find all tagged GameObjects with FindGameObjectsWithTag(string) (Unity Docs). Then you can populate your collection like that:
MediumAreas.AddRange(FindGameObjectsWithTag("MediumArea"));
Second approach could be finding all objects with same attached script FindObjectsOfType<T>() (Unity Docs). This is usefull when you are searching for instances of same type, like Medium Area.
Lets say that you have an Area script
public class Area : MonoBehaviour {
public AreaSize Size; // AreaSize is Enum
}
Then you can simply find your areas like:
var allAreas = FindGameObjectsOfType<Area>();
var mediumAreas = allAreas.Where(e => e.Size == AreaSize.Medium); // using System.Linq;
I created a project to answer your question, the final result will be like this :
As you can see I have created a game object called "areaContainer" and added 3 children with respective names : "area01", "area02" and "anotherObject".
The script that manage to get all "areaContainer" children that start with "area" looks like :
public GameObject areaContainer;
public List<GameObject> MediumAreas = new List<GameObject>();
private void Start()
{
DefineMediumAreas();
}
void DefineMediumAreas()
{
for (int i = 0; i < areaContainer.transform.childCount; i++)
{
var childGameObject = areaContainer.transform.GetChild(i).gameObject;
if (childGameObject.name.StartsWith("area"))
MediumAreas.Add(childGameObject);
}
}
1- I ended up referencing the areaContainer object in a script rather than using GameObject.Find because it's more performant.
2- To get a child of a game object you need to access to its transform and call GetChild(index). So by iterating through the parent container which is "areaContainer" we are getting its childCount.
3- To check if the name start with "area", simply use .StartsWith("area") which return true or false.
For your second question, you can hide object disabling the Renderer or by deactivating it (thisObject.SetActive(false);
I hope this help you; Happy coding!
You want to access all game objects that are children of "areaContainer"
In void DefineMediumAreas() function, you need to Transform[] to get an array of childeren. Use this:
Transform[] areaContainer = GameObject.Find("areaContainer").GetComponentsInChildren<Transform>();
foreach(Transform thisTransform in areaContainer)
{
...
}
I hope it helps you

simple score system in unity5

using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class score : MonoBehaviour {
public int ballValue;
public Text scoretext;
// Use this for initialization
void Start () {
ballValue = 0;
}
// Update is called once per frame
void Update () {
scoretext.text = ballValue.ToString();
}
void OnTriggerEnter2D(Collider2D other)
{
if(other.gameObject.tag == "bucket")
{
ballValue = ballValue + 1;
}
}
}
ok guys what am i doing wrong over here ,i am a beginner.what i am trying to achieve here is i want my ball to fall down to the bucket and get 1 point or score ,my ball has a actual circle collider and a rigidbody and my bucket has box collider which is a trigger and both of these are prefabs which is being used multiple times in the games just in case if anyone want to know.so can anyone tell me what i am doing wrong hereor can someone guide me to the right tutorial .thank you
(after playing with it for a whilei am able to get 1 point it does not increase and i am getting this error)
Object reference not set to an instance of an object.
and it refers to this line.
void Update () {
scoretext.text = ballValue.ToString();
}
ok guys i just found the real problem,as i said the bucket is a prefab which randomly generating like the pipes in flappy bird, so after creating my core system i drag and drop the text ui into the given place ,and i apply the changes and delete that prefab and when i go back to asset and check that prefab the given place for text says none.so how can i link the text ui directly to the script so it wont dlete it self.
A way to get around this, is having a static class that holds the score and then have your buckets invoke a method in the static class to increase the score.
All you have to do then, is have an empty game object with the static class attached.

Simple way to Delete the Last Child of a GameObject

I'm trying to write a simple script that gets the child count of a GameObject and then destroys the last child (I want it to basically function like a delete key) but I'm getting the error: Can't remove RectTransform because Image (Script) depends on it. Can someone tell me how to resolve this?
using UnityEngine;
using System.Collections;
using UnityEngine.EventSystems;
public class DeleteSymbol : MonoBehaviour, IPointerClickHandler
{
public GameObject deleteButton;
public GameObject encodePanel;
public GameObject decodePanel;
#region IPointerClickHandler implementation
public void OnPointerClick (PointerEventData eventData)
{
int numChildren = encodePanel.transform.childCount; // get child count
Debug.Log("There are " + numChildren + " children");
if (numChildren > 0)
{
Destroy(encodePanel.transform.GetChild(numChildren - 1)); // destroy last child
}
}
#endregion
}
Solved it with this:
Destroy(encodePanel.transform.GetChild(numChildren - 1).gameObject);
The answer is that you need to destroy the game object itself, but your code tries tried to destroy the transform instead. The transform (and other components) may have dependencies that do not allow them to be destroyed in isolation. Unfortunately Unity provides the same method for destroying components and the game object itself, and an unhelpful error message if you pick wrong.
So the answer:
Destroy(encodePanel.transform.GetChild(numChildren - 1).gameObject);
is correct, and that's why.

Categories

Resources