Unity keeps crashing due to unknown infinite loop? [duplicate] - c#

This question already has an answer here:
Unity Crashing on Play, most likely infinite While loop, but cannot locate issue
(1 answer)
Closed 5 years ago.
So I am very nooby with C#. I am currently following a tutorial to make a memory game (https://www.youtube.com/watch?v=prfzIpNhQMM).
I have followed it all and managed to fix all problems I ran into until now; every time I click play, Unity freezes. There a few comments on the videos of people having the same issues with the creator saying that its probably due to an infinite loop in the code. I do not have enough knowledge to enable me to recognise what one of those would be.
I know that the issue is with my GameManager script. If someone could take a look over it and see if they can find my issue, I would be most grateful:
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
using System.Collections;
using System.Collections.Generic;
public class GameManager : MonoBehaviour {
public Sprite[] cardFace;
public Sprite cardBack;
public GameObject[] cards;
public Text matchText;
private bool _init = false;
private int _matches = 6;
// Update is called once per frame
void Update () {
if (!_init)
initializeCards ();
if (Input.GetMouseButtonUp(0))
checkCards();
}
void initializeCards()
{
for(int id = 0; id < 2; id++)
{
for(int i = 1; i < 6; i++)
{
bool test = false;
int choice = 0;
while (!test) {
choice = Random.Range(0, cards.Length);
test = !(cards[choice].GetComponent<Card>().initialized);
}
cards[choice].GetComponent<Card>().cardValue = i;
cards[choice].GetComponent<Card>().initialized = true;
}
}
foreach (GameObject c in cards)
c.GetComponent<Card>().setupGraphics();
if (!_init)
_init = true;
}
public Sprite getCardBack()
{
return cardBack;
}
public Sprite getCardFace(int i)
{
return cardFace[i - 1];
}
void checkCards()
{
List<int> c = new List<int>();
for(int i = 0; i < cards.Length; i++)
{
if (cards[i].GetComponent<Card>().state == 1)
c.Add(i);
}
if (c.Count == 2)
cardComparison(c);
}
void cardComparison(List<int> c)
{
Card.DO_NOT = true;
int x = 0;
if(cards[c[0]].GetComponent<Card>().cardValue == cards[c[1]].GetComponent<Card> ().cardValue)
{
x = 2;
_matches--;
matchText.text = "Number of Matches: " + _matches;
if (_matches == 0)
SceneManager.LoadScene("VirusInfo3");
}
for(int i = 0; i < c.Count; i++)
{
cards[c[i]].GetComponent<Card>().state = x;
cards[c[i]].GetComponent<Card>().falseCheck();
}
}
}
Thanks!

The only code part where I think something could lead to an infinite loop in your code is the following:
while (!test) {
choice = Random.Range(0, cards.Length);
test = !(cards[choice].GetComponent<Card>().initialized);
}
The problem of this section is that, if all your cards are initialized (so initialized being equal to true), your test variable will always be equal to false. So you will end up with while(!test) being while(true) everytime, leading to the infinite loop.
Add a way to not enter this while section or to exit it if such a case happens, and you should be done.

Related

Incorrectly grabbing elements with Physics.RaycastAll

I'm creating a tetris-like game, now I'm trying to check if there are 10 blocks left on the line where the block fell (in this case) so I can remove them and add points.
My script runs with a weird delay except for the blocks that just fell (all the other ones that fell earlier counts).
It happens, however, that the sent RaycastAll will correctly catch all blocks in a given line, but it is quite rare and random.
using System.Collections.Generic;
using UnityEngine;
public class CheckBlocksRow : MonoBehaviour
{
public bool check;
public List<float> blocksHeight = new List<float>();
public int blocksInRow;
public GameObject[] caughtBlocks;
public LayerMask layerMask;
RaycastHit[] hittedBlocks;
void Update()
{
if (check)
{
blocksHeight.Sort();
for (int i = 0; i < blocksHeight.Count; i++)
{
if (SendLaser(blocksHeight[i])) CheckBlocks();
}
}
}
bool SendLaser(float heightToCheck)
{
RaycastHit[] hits = Physics.RaycastAll(new Vector2(-5.78f, heightToCheck), transform.TransformDirection(Vector3.right), Mathf.Infinity, layerMask);
blocksInRow = hits.Length;
if (blocksInRow >= 1)
{
hittedBlocks = hits;
return true;
}
else
{
check = false;
return false;
}
}
void CheckBlocks()
{
caughtBlocks = new GameObject[0];
caughtBlocks = new GameObject[hittedBlocks.Length];
for (int i = 0; i < caughtBlocks.Length; i++)
{
caughtBlocks[i] = hittedBlocks[i].transform.gameObject;
}
if (caughtBlocks.Length == 10) print("Full row: " + caughtBlocks[0].transform.position.y);
check = false;
}
}
The script is run as soon as the current block that is about to fall finally falls. Then the script that divides it into smaller parts is run, but it does not run the script of the earlier script.
void SetToCheck()
{
CheckBlocksRow _checkBlocksRow = GameObject.FindGameObjectWithTag("Board").transform.GetChild(1).GetComponent<CheckBlocksRow>();
_checkBlocksRow.blocksHeight.Clear();
for (int i = 0; i < transform.childCount; i++)
{
if(transform.GetChild(i).name != "Center") _checkBlocksRow.blocksHeight.Add(transform.GetChild(i).transform.position.y);
}
_checkBlocksRow.check = true;
}
In this picture, the script on the first line only caught 8 blocks out of the 10 that are there.
I managed to maybe not remove the error but make everything work with a slight delay.
private IEnumerator Delay()
{
print("before");
yield return new WaitForSeconds(0.1f);
print("after");
for (int i = 0; i < blocksHeight.Count; i++)
{
if (SendLaser(blocksHeight[i])) CheckBlocks();
}
}

Checking if all objects of an array is active at the same time Unity C#

I've tried to make a script that if all the lights in my scene tagged "Light" are active at the same time, the game proceeds. None of the answers I've found have helped so far. I always end up with it only scanning the active objects, just randomly jumping out of the loop, or stopping when it has found one object that's active. Here is a simple piece of code that should have worked according to other posts
void Update()
{
bool allActive = false;
GameObject[] allLights = GameObject.FindGameObjectsWithTag("Light");
if(currenObjective > 0 && currenObjective < 3)
{
for (int i = 0; i < allLights.Length; i++)
{
if (allLights[i].activeInHierarchy)
{
allActive = true;
break;
}
}
if (allActive)
{
currentObjective = 2;
}
}
}
This code just sets the allActive variable to true at the moment one light is turned on.
You would need to invert the check:
private void Update()
{
// Since Find is always a bit expensive I would do the cheapest check first
if(currenObjective > 0 && currenObjective < 3)
{
var allLights = GameObject.FindGameObjectsWithTag("Light");
var allActive = true;
for (int i = 0; i < allLights.Length; i++)
{
if (!allLights[i].activeInHierarchy)
{
allActive = false;
break;
}
}
if (allActive)
{
currentObjective = 2;
}
}
}
Or You can do this in one line using Linq All
using System.Linq;
...
private void Update()
{
// Since Find is always a bit expensive I would do the cheapest check first
if(currenObjective > 0 && currenObjective < 3)
{
var allLights = GameObject.FindGameObjectsWithTag("Light");
if (allLights.All(light => light.activeInHierarchy))
{
currentObjective = 2;
}
}
}
As a general note: You should avoid using FindGameObjectsWithTag every frame! Either store these references ONCE at start, or if you spawn more lights on runtime implement it event driven and add the newly spawned lights to a list and then use that list to check.
If i understant you want to know if all objects are active:
using Linq does the job
you have to add using system.Linq to your script.
GameObject[] allLights = GameObject.FindGameObjectsWithTag("Light");
bool result = allLights.All(p => p.activeInHierarchy);
you could simplify your code like this:
private void Update()
{
if(currenObjective > 0 && currenObjective < 3 && GameObject.FindGameObjectsWithTag("Light").All(p => p.activeInHierarchy))
{
currentObjective = 2;
}
}
As says derHugo, FindGameObjectsWithTag is very expensive in each frame...

Cannot output all elements of a gameObject array into the unity console

I am currently unable to print all elements of my array. I am trying to use a nested for loop to print and number all elements but I can only print one element over and over again which is the last element that I click in the game.
The first 7 elements printed are all the same and the last element is left completely blank.
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class PickUpItem : MonoBehaviour, IInteractable
{
public string DisplaySprite;
public string DisplayImage;
public int counter;
public int j;
public string[] ChoosenItems = new string[7];
private GameObject InventorySlots;
public void Interact(DisplayImage currentDisplay)
{
ItemPickUp();
}
void Start()
{
}
void Update()
{
}
void ItemPickUp()
{
InventorySlots = GameObject.Find("Slots");
counter = 0;
foreach (Transform slot in InventorySlots.transform)
{
if (slot.transform.GetChild(0).GetComponent<Image>().sprite.name == "empty_item")
{
slot.transform.GetChild(0).GetComponent<Image>().sprite =
Resources.Load<Sprite>("Inventory Items/" + DisplaySprite);
Destroy(gameObject);
break;
}
if (counter <= 7)
{
ChoosenItems[counter] = (gameObject.name);
counter++;
if (counter >= 7)
{
Debug.Log("You have choosen 8 itmes, would you like to continue or retry?");
for (j = 0; j <= 7; j++)
{
Debug.LogFormat("Element[{0}] = {1}", j, ChoosenItems[j]);
}
}
}
}
}
}
//http://csharp.net-informations.com/collection/list.html
You are declaring your ChoosenItems = new string[7] with 7 element, ChoosenItems[0], through ChoosenItems[6]. As you can see, this is only 7 items. Thus, you will never have 8 items. Try declaring it as ChoosenItems = new string[8] to have it contain 8 items. See Mozilla Developer Docs for more information.
Hope this helps!

Why is my code skipping this line

This code is creating an inventory for an RPG game. I'm calling the item information from Items database that is a json file. For some reasons it is skipping this line. I marked it in the code to make it stand out a little more.
data.transform.GetChild(0).GetComponent<Text>().text = data.amount.ToString();
here is the entire code
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Inventory : MonoBehaviour {
ItemDataBase database;
GameObject inventoryPanel;
GameObject slotPanel;
public GameObject inventorySlot;
public GameObject inventoryItem;
int slotAmount;
public List<Item> items = new List<Item>();
public List<GameObject> slots = new List<GameObject>();
private void Start()
{
database = GetComponent<ItemDataBase>();
slotAmount = 20;
inventoryPanel = GameObject.Find("Inventory Panel");
slotPanel = inventoryPanel.transform.FindChild("Slot Panel").gameObject;
for (int i = 0; i < slotAmount; i++)
{
items.Add(new Item());
// and empty slot
slots.Add(Instantiate(inventorySlot));
//set parent to slot panel
slots[i].transform.SetParent(slotPanel.transform);
}
for (int i = 0; i < items.Count; i++)
{
items[i].ID = -1;
}
AddItem(0);
AddItem(1);
AddItem(1);
Debug.Log(items[1].Title);
}
public void AddItem(int id)
{
Item itemToAdd = database.FetchItemByID(id);
if (itemToAdd.Stackable && checkIfItemIsInInventory(itemToAdd))
{
for (int i = 0; i < items.Count; i++)
{
if (items[i].ID == id)
{
ItemData data = slots[i].transform.GetChild(0).GetComponent<ItemData>();
data.amount++;
**data.transform.GetChild(0).GetComponent<Text>().text = data.amount.ToString();**
break;
}
}
}
else
{
for (int i = 0; i < items.Count; i++)
{
// in video had this set to -1
if (items[i].ID == -1)
{
items[i] = itemToAdd;
GameObject itemObj = Instantiate(inventoryItem);
itemObj.transform.SetParent(slots[i].transform);
itemObj.transform.position = Vector2.zero;
itemObj.GetComponent<Image>().sprite = itemToAdd.Sprite;
itemObj.name = itemToAdd.Title;
itemObj.name = itemToAdd.Title;
break;
}
}
}
}
bool checkIfItemIsInInventory(Item item)
{
for (int i = 0; i<items.Count; i++)
{
if (items[i].ID == item.ID)
{
return true;
}
}
return false;
}
}
Here is some debugging information. First here is the whole function it is messing up on
public void AddItem(int id)
{
Item itemToAdd = database.FetchItemByID(id);
if (itemToAdd.Stackable && checkIfItemIsInInventory(itemToAdd))
{
for (int i = 0; i < items.Count; i++)
{
if (items[i].ID == id)
{
ItemData data = slots[i].transform.GetChild(0).GetComponent<ItemData>();
data.amount++;
data.transform.GetChild(0).GetComponent<Text>().text = data.amount.ToString();
break;
}
}
}
so when I get to this line while debugging
data.amount++;
the watch says
data ---- null
data.amount ---- Could not find the member 'amount' for 'object'
amount---- the identifier 'amount'is not in the scope
so to break all of this down data is initialized here
ItemData data = slots[i].transform.GetChild(0).GetComponent();
amount is here (this is a different file called ItemData
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ItemData : MonoBehaviour {
public Item item;
public int amount;
}
thanks everyone for being helpful I really just want to learn how to code better.
Try placing a break point at the block above:
if (items[i].ID == id){
or even higher at:
if (itemToAdd.Stackable && checkIfItemIsInInventory(itemToAdd)){
Step through (using F10) and see if it's hitting the line. It's probably jumping out when one of the conditions aren't met. That, or if you need to execute this line through the entirety of the for loop, it's jumping out of the loop when it hits the line the first time because of the break; following.
Either way, step through the lines after the break point, and check the values of everything. Make sure you're not getting null references or values.

If loop using timeSinceLevelLoad variable

I have a toggle button that is initialized as true, when it is turned off it should trigger a function that makes a subtraction for each second that passes by.
The variable to be subtracted it named TEMPERATURE, it should be subtracted by 1 every second until it reaches 0. To count the seconds I use the Monobehaviour variable called TimeSinceLevelLoad.
I built I function but the subtraction is never happening, can you help me?
using UnityEngine;
using System.Collections;
using Assets.Code.PowerPlants;
public class thermoPowerControlPanel : MonoBehaviour {
private ThermoElectric thermo;
public bool t1= true;
int temperature;
private int tempUP = 10;
private int tempDOWN = 1;
private int mark;
private int i =0;
public thermoPowerControlPanel (){
temperature = 100;
}
public void turbine1State (bool t1) {
Debug.Log (temperature);
if (i ==0) {
mark = (int)Time.timeSinceLevelLoad;
i = 1;
}
if (t1 == false) {
ThermoElectric.t1Bool = t1;
if (temperature != 0) {
if (mark + 1 == (int)Time.timeSinceLevelLoad ) {
temperature = temperature - tempDOWN;
i = 0;
Debug.Log (temperature);
}
}
}
}
}
The bool t1 is working fine, I already checked, it changes to False.
This is a bit too complicated. Maybe you should look into Coroutines?
You can make a method (coroutine) like this
private IEnumerator decreseTemperature()
{
for (int i = 0; i < temperature; i++) // this will make couroutine cycle
{
yield return new WaitForSeconds(1.0f); // this makes coroutine wait for 1 second
temperature = temperature - tempDown;
}
}
You can star and stop coroutine easly:
StartCoroutine(decreseTemperature());
Stop Coroutine(decreseTemperature());
You probably would need to adjust this code to your needs, but coroutine are very useful in this kind of problems.

Categories

Resources