I was wondering how I would avoid an infinite loop by running this code within the start function in unity.
void Start ()
{
Room[] rooms = generateRooms();
int falseCount = 0;
while (true)
{
for (int i = 0; i < rooms.Length; i++)
{
if (!rooms[i].separationFlocking(rooms)) {
falseCount++;
}
}
if (falseCount >= rooms.Length) break;
}
}
separationFlocking returns false if the object didn't need to be moved, so if rooms.Length number of objects weren't moved then I want it to break the while loop. Although, everytime I compile it, it never seems to compile to play mode, presumably because of the infinite loop.
void Update ()
{
int falseCount = 0;
if (falseCount != rooms.Length) {
for (int i = 0; i < rooms.Length; i++)
{
if (!rooms[i].separationFlocking(rooms)) {
falseCount += 1;
}
}
}
}
If I run similar code in the update function it seems to work fine, so I know that detecting if the object didn't move isn't broken.
When I run the start function with a iterator that breaks after 100 loops I get this result. The cubes seem to separate from each other in odd ways, spreading out from their original circular arrangement.
This is how they're supposed to turn out and they successful do so in the update function. Although, I want to avoid running this code every frame for performance reasons.
This whole project is based on this blog post. Also here's the pastebin link so you can see the whole code. If you want to run it in unity all you need to do is add it to an empty game object.
You need to call the method continously? I would call it only when needed. If you one to lower the call frequency you can space that call in the update, or use a corroutine. Not debugged examples below.
frequency execution control in the update:
float elapsedTime = 0f;
int runFreqTime = 2; //to run every 2 seconds
void Update ()
{
elapsedTime += Time.deltaTime;
if (elapsedTime > runFreqTime) {
int falseCount = 0;
if (falseCount != rooms.Length) {
for (int i = 0; i < rooms.Length; i++)
{
if (!rooms[i].separationFlocking(rooms)) {
falseCount += 1;
}
}
}
elapsedTime = 0f; //time control float reset
}
}
coroutine:
public void Start() {
StartCoroutine(RepetitiveExec());
}
IEnumerator RepetitiveExec()
{
while (true)
{
for (int i = 0; i < rooms.Length; i++)
{
if (!rooms[i].separationFlocking(rooms)) {
falseCount++;
}
}
if (falseCount >= rooms.Length) break;
}
yield return new WaitForSeconds(2f);
}
Need to be careful with coroutines. It would be executing until you Stop it
Related
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();
}
}
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...
I am working on a game in unity 5 similar to the old school mario party games. I am running into an issue with my dice code where it causes unity to crash when i run the game. I want my code to constantly change the number that appears on the dice block. Example (https://www.youtube.com/watch?v=7suvd4UA6cU) just pay attention to the numbers on the dice.
I have tried two different ways to code this, but neither are working for me.
Example 1:
void Update () {
while (Clicked == false)
{
for (int i = 1; i < 10; i++)
{
numberText.text = i.ToString();
}
}
}
Example 2:
void Update () {
while (Clicked == false)
{
AutoIncrement();
}
}
void AutoIncrement()
{
for (int i = 1; i < 10; i++)
{
numberText.text = i.ToString();
}
}
I know these are basically the same but i figured i'd try it anyway.
Any help would be greatly appreciated as I am fairly new to unity and c#.
You can use a Coroutine to do that
IEnumerator DiceNumberManager(){
int i = 1;
while (true){
numberText.text = i.ToString();
yield return new WaitForSeconds(0.1f);
i++;
if (i>10) i=1;
}
}
Start it when you want it to change numbers and stop it when you want it to stop with:
StartCoroutine &
StopCoroutine
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.
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.