I am trying to iterate through a list of GameObjects and find the first GameObject that is not active in the hierarchy and return that GameObject.
The problem I am having is my foreach statement keep returning null on "enemy"
"enemy" in this statement below is where the problem is
"GameObject enemy in pizzaEnemyList"
I do have one Gameobject inside of the list. Included in picture below
private List<GameObject> pizzaEnemyList = new List<GameObject> ();
private GameObject FetchPizzaEnemy()
{
GameObject pizzaEnemy = null;
int count = 0;
foreach (GameObject enemy in pizzaEnemyList)
{
if (enemy.activeInHierarchy == false)
{
pizzaEnemy = pizzaEnemyList [count];
pizzaEnemyList.Remove (enemy);
return pizzaEnemy;
}
count += 1;
}
return pizzaEnemy;
}
//Function I am using to add the enemies to the list.
public void DeactivateAndStoreEnemy(GameObject enemy)
{
//decativates the enemy
enemy.SetActive(false);
//Store the enemy into the correct list
if (enemy.name == "PizzaGuy")
{
pizzaEnemyList.Add (enemy);
}
else if (enemy.name == "FiresGuy")
{
friesEnemyList.Add (enemy);
}
else if (enemy.name == "SodaGuy")
{
sodaEnemyList.Add (enemy);
}
}
public int PizzaListUnactiveEnemyCount()
{
int unactiveEnemiesCount = 0;
foreach (GameObject enemies in pizzaEnemyList)
{
if (enemies.activeInHierarchy == false)
{
unactiveEnemiesCount += 1;
}
}
return unactiveEnemiesCount;
}
I have tested your code and can tell you it works if the gameObject are added properly and that they are actually not active.
Your problem is most probably not adding the game objects correctly to the list and maybe also not making sure that pizzaEnemyList has at least 1 item that is set inactive. When all item are active in your list its normal that it returns null because it will never go in if (enemy.activeInHierarchy == false)
It would be better if you had a public List<GameObject> pizzaEnemyList; that you setup in the inspector of unity. Unless if this solution doesn't fit with the game you're building.
As Serlite said you don't need to keep a counter when using foreach, your code could look like this :
private GameObject FetchPizzaEnemy()
{
GameObject pizzaEnemy = null;
foreach (GameObject enemy in pizzaEnemyList)
{
if (enemy.activeInHierarchy == false)
{
pizzaEnemy = enemy;
pizzaEnemyList.Remove(enemy);
return pizzaEnemy;
}
}
return pizzaEnemy;
}
Edit after your edit:
I've tested your code with this and it works :
private List<GameObject> pizzaEnemyList = new List<GameObject>();
void Start()
{
GameObject pizzaGuy = GameObject.Find("PizzaGuy");
DeactivateAndStoreEnemy(pizzaGuy);
Debug.Log(FetchPizzaEnemy());
}
private GameObject FetchPizzaEnemy()
{
GameObject pizzaEnemy = null;
foreach (GameObject enemy in pizzaEnemyList)
{
if (enemy.activeInHierarchy == false)
{
pizzaEnemy = enemy;
pizzaEnemyList.Remove(enemy);
return pizzaEnemy;
}
}
return pizzaEnemy;
}
public void DeactivateAndStoreEnemy(GameObject enemy)
{
enemy.name = "PizzaGuy";
//decativates the enemy
enemy.SetActive(false);
//Store the enemy into the correct list
if (enemy.name == "PizzaGuy")
{
pizzaEnemyList.Add(enemy);
}
}
I have a gameobject named PizzaGuy in my scene.
Related
Hello everyone I am trying to make a simple Emote functionality. I have a bubble(like a chat bubble) and inside of that there is an animation. Like in Legends of Runeterra and Clash Royale type games.
public class EmoteBubble : MonoBehaviour
{
private Animator animator;
private Animator childAnimator;
public List<GameObject> objectList;
// Start is called before the first frame update
void Start()
{
animator = gameObject.GetComponent<Animator>();
foreach (GameObject obj in objectList)
{
obj.SetActive(false);
}
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.Space)){
childAnimator = transform.Find(objectList[0].name).GetComponent<Animator>();
objectList[0].SetActive(true);
StartCoroutine(HeroAnimator(2f, objectList[0].name + "Bool"));
}
IEnumerator HeroAnimator(float animationLength, string parameter)
{
animator.SetBool("EmoteBool", true);
childAnimator.SetBool(parameter, true);
yield return new WaitForSeconds(animationLength);
animator.SetBool("EmoteBool", false);
childAnimator.SetBool(parameter, false);
yield return new WaitForSeconds(0.2f);
foreach (GameObject obj in objectList)
{
obj.SetActive(false);
}
}
}
This is the script of the bubble and it has childs in objectList. If I hit space I want a specific animation to kick in like the first one or the second one. I am trying to disable every object in Start function and enable the one that i choose. But when I use prefabs only the real one gets disabled. Should I use prefabs in this emote concept or should I make every object different ?
public class EmoteBubble : MonoBehaviour
{
private Animator animator;
private Animator childAnimator;
public List<GameObject> objectList;
// Start is called before the first frame update
void Start()
{
Debug.Log(objectList[2]);
animator = gameObject.GetComponent<Animator>();
foreach (GameObject obj in objectList)
{
obj.SetActive(false);
}
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.Space)){
objectList[0].SetActive(true);
childAnimator = transform.Find(objectList[0].name).GetComponent<Animator>();
StartCoroutine(HeroAnimator(2f, objectList[0].name + "Bool"));
}
if (Input.GetKeyDown(KeyCode.A)) {
objectList[1].SetActive(true);
childAnimator = transform.Find(objectList[1].name).GetComponent<Animator>();
StartCoroutine(HeroAnimator(2f, objectList[1].name + "Bool"));
}
if (Input.GetKeyDown(KeyCode.S)){
objectList[2].SetActive(true);
childAnimator = transform.Find(objectList[2].name).GetComponent<Animator>();
StartCoroutine(HeroAnimator(2f, objectList[2].name + "Bool"));
}
IEnumerator HeroAnimator(float animationLength, string parameter)
{
animator.SetBool("EmoteBool", true);
childAnimator.SetBool(parameter, true);
yield return new WaitForSeconds(animationLength);
animator.SetBool("EmoteBool", false);
childAnimator.SetBool(parameter, false);
yield return new WaitForSeconds(0.2f);
foreach (GameObject obj in objectList)
{
obj.SetActive(false);
}
}
}
}
I think this works, It looks simple and it works but I don't know if it's the best way.
I want to find the GameObject that is in the hierarchy correctly in the hierarchy. But this does not happen correctly, so I want 'if' to work when the gameobject I have tagged is active, but 'if' works even though there is no other gameobject tag, which is why this problem is caused.
Note: My level of English is not good, I try to explain the problem to you as much as I can, please tell me if there is anything you do not understand, I will try to explain it in detail as much as I can. Thank you for your understanding.
Image tagged gameobject: enter image description here
The point where the problem occurred: enter image description here
Gameobject tag name: enter image description here
Cubes Gameobject: enter image description here
void Update()
{
if (cubes.instance.objPool.Count == 1) //It works when the number of gameobject in the list is 1 when I do a random deletion.
{
RewardSystem();
}
}
public void RewardSystem()
{
GameObject FoundCube = GameObject.FindGameObjectWithTag(cube_Tag_Number);
if (FoundCube == null)
{
SoundManager.instance.GameOverMenuSound();
game_Over_Menu.SetActive(true);
next_Level_Menu.SetActive(false);
confetti.Stop();
clickButton.gameObject.SetActive(false);
missionButton.gameObject.SetActive(false);
enabled = false;
}
if (FoundCube != null)// problematic if
{
if (FoundCube.activeInHierarchy == true) // problematic if
{
SoundManager.instance.NextLevelMenuSound();
next_Level_Menu.SetActive(true);
confetti.Play();
game_Over_Menu.SetActive(false);
clickButton.gameObject.SetActive(false);
missionButton.gameObject.SetActive(false);
enabled = false;
}
}
}
public class cubes : MonoBehaviour
{
public static cubes instance;
public int randomRangeNumber1, randomRangeNumber2;
public List<GameObject> objPool = new List<GameObject>();
private void Awake()
{
if (instance == null)
{
instance = this;
}
else if (instance != null)
{
Destroy(gameObject);
}
}
public void ButtonOnDown()
{
SoundManager.instance.LuckButtonSound();
for (int i = 0; i < 1; i++)
{
int index = Random.Range(randomRangeNumber1, randomRangeNumber2);
GameObject.Destroy(objPool[index]);
objPool.RemoveAt(index);
randomRangeNumber2 = objPool.Count;
}
}
}
me and my colleagues are creating a 2D sidescroller game in Unity with C#.
The player, is supposed to be able to pick objects (by touching them) and throw them.
Our plan is this:
1) make variables for the objects, their booleans and their rigid bodies.
2) verify if the object is touching the player
3) if it's true, then the object will parent to the player (setting their position to the player's hand).
4) to throw, the code will check if the player has the object (by using a boolean) and then it will unparent and throw by addforce.
The code doesn't have any errors and it works except on the throwing part (the player can grab and ungrab but can't throw).
The player can pick and unpick but not throwing and i don't understand why because the code looks right and the console doesn't show me any errors :/
Have a look to my code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Inventory : MonoBehaviour
{
public bool ispicked1 = false;
public GameObject pickable1;
public Rigidbody2D pickable1rb;
public GameObject Parent;
public float force;
void Start() {
pickable1rb = GetComponent<Rigidbody2D>();
}
void Update() {
if (ispicked1 == true) {
pickable1.transform.position = Parent.transform.position;
}
else if (ispicked1 == false) {
pickable1.transform.parent = null;
}
if (Input.GetMouseButton(1) && ispicked1 == true) {
ispicked1 = false;
pickable1rb.AddForce(transform.up * force, ForceMode2D.Impulse);
}
}
private void OnCollisionEnter2D(Collision2D collision) {
if (collision.gameObject.name == "pickable1") {
Debug.Log("Tocou em objecto");
ispicked1 = true;
pickable1.transform.SetParent(Parent.transform);
}
}
}
Side question: i want the player to throw at the direction he's facing, what is the best way to do that? I can only choose between right, left or up :/
UPDATE:
I solved all the problems and i created a side script for the object to be thrown and they are 100% working! Here are the codes:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Inventory : MonoBehaviour
{
public bool ispicked1 = false;
public bool ispicked2 = false;
public GameObject pickable1;
public GameObject pickable2;
public GameObject Parent;
public bool isThrown = false;
public ThrowableObject throwableinstance1;
public ThrowableObject throwableinstance2;
public bool isfull = false;
void Start() {
throwableinstance1 = GameObject.Find("pickable1").GetComponent<ThrowableObject>();
throwableinstance2 = GameObject.Find("pickable2").GetComponent<ThrowableObject>();
}
void Update() {
if (ispicked1 == true) {
pickable1.transform.position = Parent.transform.position;
isfull = true;
}
else if (ispicked1 == false) {
pickable1.transform.parent = null;
}
if (ispicked2 == true) {
pickable2.transform.position = Parent.transform.position;
isfull = true;
} else if (ispicked2 == false) {
pickable2.transform.parent = null;
}
if (Input.GetMouseButton(1) && ispicked1 == true) {
ispicked1 = false;
isThrown = true;
throwableinstance1.Throw();
isfull = false;
}
if (Input.GetMouseButton(1) && ispicked2 == true) {
ispicked2 = false;
isThrown = true;
throwableinstance2.Throw();
isfull = false;
}
}
private void OnCollisionEnter2D(Collision2D collision) {
if (collision.gameObject.name == "pickable1" && isfull == false) {
ispicked1 = true;
pickable1.transform.SetParent(Parent.transform);
}
if (collision.gameObject.name == "pickable2" && isfull == false) {
ispicked2 = true;
pickable2.transform.SetParent(Parent.transform);
}
}
}
Here's the code to throw the pickable object:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ThrowableObject : MonoBehaviour
{
public Inventory inventoryinstance;
public Rigidbody2D throwablerb;
public Transform Player;
public GameObject PickableObject;
public EnemyHealth1 enemyhealth1instance;
public EnemyHealth2 enemyhealth2instance;
public EnemyHealth3 enemyhealth3instance;
void Start()
{
inventoryinstance = GameObject.Find("Player").GetComponent<Inventory>();
enemyhealth1instance = GameObject.Find("enemy1").GetComponent<EnemyHealth1>();
enemyhealth2instance = GameObject.Find("enemy2").GetComponent<EnemyHealth2>();
enemyhealth3instance = GameObject.Find("enemy3_leper").GetComponent<EnemyHealth3>();
}
public void Throw()
{
if(Player.localScale.x < 1)
{
throwablerb.AddForce(transform.right * -1);
} else if(Player.localScale.x > 0)
{
throwablerb.AddForce(transform.right);
}
}
private void OnCollisionEnter2D(Collision2D collision) {
if (collision.gameObject.name == "enemy1") {
enemyhealth1instance.GetComponent<EnemyHealth1>().EnemyHealthbar1-= 1;
Destroy(PickableObject);
}
if (collision.gameObject.name == "enemy2") {
enemyhealth2instance.GetComponent<EnemyHealth2>().EnemyHealthbar2-=1;
Destroy(PickableObject);
}
if (collision.gameObject.name == "enemy3_leper") {
enemyhealth3instance.GetComponent<EnemyHealth3>().EnemyHealthbar3-=1;
Destroy(PickableObject);
}
}
private void OnTriggerEnter2D(Collider2D col)
{
if (col.gameObject.name == "enemy1_hitbox") {
enemyhealth1instance.GetComponent<EnemyHealth1>().EnemyHealthbar1-=1;
Destroy(PickableObject);
}
}
}
While you're answering my question, I'll do some quick code reviewing:
Overall, it feels weird to be keeping track and maintaining "holding & throwing an item" in a class called "Inventory" that has references to a separate object, that is being held & a reference to the player. What is this script? The item, the player or a third party; a separate inventory? (Questions to ask yourself, you don't need to answer them - just think about it 😊)
pickable1
pickable1object
pickable1rb
These variable names makes little sense to me in current context; why not just isPickedUp, object, rb?
if (pickable1 == true) {
pickable1object.transform.position = Playerparent.transform.position;
}
if (pickable1 == false) {
pickable1object.transform.parent = null;
}
pickable1 will not be both true and false, doing 2 ifs are just unnecessary computations. What you're looking for here is an if/else or if/elseif.
Furthermore, if you child it to the player, logically, shouldn't you NOT have to set the position every frame? Else, what's the purpose of childing?
void FixedUpdate() {
if (Input.GetMouseButton(1)) {
// [...]
}
// [...]
}
Logically, you shouldn't be checking for input in the fixed update since inputs are tied to a frame, meaning Update(). In this case, it may accidentally work reliably most of the time because you're not checking if the player CLICKED a button, rather you're checking if player is HOLDING a button. (GetMouseButton vs GetMouseButtonDown). If you were to check for if the player CLICKED a button in the FixedUpdate, it would only work on the few lucky frames where FixedUpdate and Update were running at the exact same time.
Regarding your question: The code looks alright, it's probably not behave in the way you expect. From what I can tell in the code it will...
If player has picked up an item, it will have force added to it if holding down Mouse 1 for as long as player is holding down mouse.
Why nothing is happening may be due to inspector values being off. Debug the values of force and see if the code is triggering at all. Consider moving the parent = null code (or just remove the parenting alltogether).
It could also be that as soon as you push the item outside of the player, it falls down and triggers OnCollisionEnter and is grabbed by the player again.
The problem with your code is that you use transform.up which will just cause it to throw upwards rather than the direction the player is facing. So you can use transform.right for the direction the player is facing. You can use both to create more of an arc.
You'll have some problems with pivoting with the way you have it setup.
What I recommend is to have two transforms which are children of the player to pivot the sprite properly. (You can change the position of the transform in the keyframes of the player's animation)
The code would look something like this:
public class Inventory : MonoBehaviour
{
private bool carryingItem => pickedUpItem == null;
private PickableItem pickedUpItem;
[SerializeField] private Vector2 throwingForce;
[SerializeField] private Transform spawnPosition; //Should be a child of the player gameobject. This will be position of the object
[SerializeField] private Transform handTransform; //Should also be a child of the player gameobject. This needs to be animated for the keyframes that move the hand
void Update()
{
if (carryingItem && Input.GetMouseButtonDown(1))
{
pickedUpItem.transform.SetParent(null);
pickedUpItem.transform.position = spawnPosition.position;
pickedUpItem.GetComponent<Rigidbody2D>().AddForce(transform.right * throwingForce.x + transform.up * throwingForce.y);
pickedUpItem = null;
}
}
private void OnCollisionEnter2D(Collision2D other)
{
var pickable = other.transform.GetComponent<PickableItem>();
if (pickable && pickedUpItem == null) //This kind of depends on the game design because maybe you want to pick it up if you're carrying something already
{
pickedUpItem = pickable;
pickable.transform.SetParent(handTransform);
}
pickable.transform.localPosition.Set(0,0,0);
}
}
You set the picked up item to the parent where you want to be pivoted around.
I currently have a script that instantiates 2 prefabs as GameObjects. Which have attached to them a script that the game-object is set active to false, then is added to my inventory array. Which is another script, but my issue is the other game object that wasn't selected is still there. It is supposed to be you have a choice of 2. Once you take one the other disappears. I have no idea how to do this, could anyone please help.
public int moralityCounter=0;
public bool Inventory;
public void Store()
{
if (gameObject.CompareTag("Good"))
{
moralityCounter++;
Debug.Log(moralityCounter);
gameObject.SetActive(false);
}
if (gameObject.CompareTag("Bad"))
{
moralityCounter--;
Debug.Log(moralityCounter);
gameObject.SetActive(false);
}
}
If there's only the single good and single bad tagged objects, you can just set everything tagged good and bad to be inactive.
private void DeactivateAllGoodBads()
{
// Deactivate all goods
GameObject[] goodObjects = GameObject.FindGameObjectsWithTag("Good");
foreach (GameObject goodObject in goodObjects)
{
goodObject.SetActive(false);
}
// Deactivate all bads
GameObject[] badObjects = GameObject.FindGameObjectsWithTag("Bad");
foreach (GameObject badObject in badObjects)
{
badObject.SetActive(false);
}
}
public void Store()
{
bool isGood = gameObject.CompareTag("Good");
bool isBad = gameObject.CompareTag("Bad");
if (isGood)
{
moralityCounter++;
Debug.Log(moralityCounter);
}
if (isBad)
{
moralityCounter--;
Debug.Log(moralityCounter);
}
if (isGood || isBad)
{
DeactivateAllGoodBads();
}
}
If there are multiple, you can do something like only disable ones closer to the Stored object than some distance.
private void DeactivateCloseGoodBads(Vector3 position, float maxDistance)
{
// Deactivate close goods
GameObject[] goodObjects = GameObject.FindGameObjectsWithTag("Good");
foreach (GameObject goodObject in goodObjects)
{
// Check distance of found good
if (Vector3.Distance(position, goodObject.transform.position) <= maxDistance) {
goodObject.SetActive(false);
}
}
// Deactivate close bads
GameObject[] badObjects = GameObject.FindGameObjectsWithTag("Bad");
foreach (GameObject badObject in badObjects)
{
// Check distance of found bad
if (Vector3.Distance(position, badObject.transform.position) <= maxDistance) {
badObject.SetActive(false);
}
}
}
public void Store()
{
bool isGood = gameObject.CompareTag("Good");
bool isBad = gameObject.CompareTag("Bad");
if (isGood)
{
moralityCounter++;
Debug.Log(moralityCounter);
}
if (isBad)
{
moralityCounter--;
Debug.Log(moralityCounter);
}
if (isGood || isBad)
{
DeactivateCloseGoodBads(gameObject.transform.position, 10f);
}
}
I have an array to instantiate multiple gameobjects when the player enters on a trigger but I don't know how to destroy these gameobjects when the player exits the trigger. This is my code:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class SpawnDestroy : MonoBehaviour {
public List<GameObject> spawnPositions;
public List<GameObject> spawnObjects;
void OnTriggerEnter(Collider other)
{
if (other.gameObject.tag == "Player")
{
SpawnObjects ();
}
}
void OnTriggerExit(Collider other)
{
if (other.gameObject.tag == "Player")
{
............
}
}
void SpawnObjects()
{
foreach(GameObject spawnPosition in spawnPositions)
{
int selection = Random.Range(0, spawnObjects.Count);
Instantiate(spawnObjects[selection], spawnPosition.transform.position, spawnPosition.transform.rotation);
}
}
}
UPDATE CODE:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class SpawnDestroy : MonoBehaviour {
public List<GameObject> spawnPositions;
public List<GameObject> spawnObjects;
private List<GameObject> instantiated;
void OnTriggerEnter(Collider other)
{
if (other.gameObject.tag == "Player")
{
SpawnObjects ();
}
}
void OnTriggerExit(Collider other)
{
if (other.gameObject.tag == "Player")
{
for(int i = 0; i < instantiated.Count; i++)
{
Destroy(instantiated[i]);
}
}
}
void SpawnObjects()
{
// (re-)initialize as empty list
instantiated = new List<GameObject>();
foreach(GameObject spawnPosition in spawnPositions)
{
int selection = Random.Range(0, spawnObjects.Count);
instantiated.Add(Instantiate(spawnObjects[selection], spawnPosition.transform.position, spawnPosition.transform.rotation));
}
}
}
Currently you don't store a reference to the instantiated gameobjects.
For this you can create another list and add the instantiated objects, something like this (this is a short version, you could also store them in a temp):
private List<GameObject> instantiated;
...
void SpawnObjects()
{
// (re-)initialize as empty list
instantiated = new List<GameObject>();
foreach(GameObject spawnPosition in spawnPositions)
{
int selection = Random.Range(0, spawnObjects.Count);
instantiated.Add((GameObject)Instantiate(spawnObjects[selection], spawnPosition.transform.position, spawnPosition.transform.rotation));
}
}
And then for destroying:
for(int i = 0; i < instantiated.Count; i++)
{
Destroy(instantiated[i]);
}
There are other ways too. E.g. if you don't want to re-initialize the list every time, you can remove the items from the list before destroying them. In this case you need to iterate the list from back to front though. (Also, with the above, you must not try to reference something from the list if the objects were destroyed obviously, since this would raise an error.)
Another way would be to add the gameobjects as child to a certain parent upon instantiation and iterate them for destroying, something like
while(parentTransform.childCount > 0)
{
Transform child = parentTransform.GetChild(0);
Destroy(child.gameObject);
child.SetParent(null);
}
Iterate through the list to destroy each object, then create a new list where null objects are removed:
public List<GameObject> list;
foreach (GameObject item in list) { Destroy(item); }
list.RemoveAll(GameObject => GameObject == null); // or do list = new List<GameObject>();
Note, when removing null objects from a list you need to wait for the next frame for the object to actually be destroyed and become a null object in the list, so you could clean the list in an IEnumerator function a frame later, example script below:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ListCLeaner: MonoBehaviour
{
public List<GameObject> list;
public void ClearList(){
foreach (GameObject item in list) { Destroy(item); }
StartCoroutine(CleanListAfterFrame());
}
IEnumerator CleanListAfterFrame(){
yield return new WaitForEndOfFrame();
list.RemoveAll(GameObject => GameObject == null);
}
}
}