Updating 3D text each frame crashes my game - c#

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

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();
}
}

Code causes infinite loop in start function of Unity

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

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...

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

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.

For-Loop index gets reset to 0 after nested While-Loop

As a coding execise, I'm working on a visualisation of different sorting algorithms. I have a button that pauses the algorithm and shows every item of the array as a bar-diagramm. To achive this, I have a nested while-loop inside of the two for-loops for the sorting. It loops throug, until I press the button again, a boolean variable gets set to true and the sorting continues.
However, after the programm exits the while loop, the two indecies of the for-loops (i and j) get reset to 0 and the sorting starts from the beginning again.
swap() and draw() are custom functions that do pretty much exactly what the name suggests.
Here's my code for the sorting:
for (i = 0; i < items.Count(); i++)
{
for (j = 0; j < items.Count() - 1 - i; j++)
{
//lbl_i.Text = Convert.ToString(i);
//lbl_j.Text = Convert.ToString(j);
if (items[j] > items[j + 1])
{
swap(j, j + 1); //swaps the items at two given indecies
draw(); // draws the array to the picturebox
}
while (sorting == false) //the sorting is paused
{
Application.DoEvents();
}
}
}
Any idea why this could happen?
I have a suspition that it could be a problem with the Application.DoEvents()-call, but I need that so I can press the button.
Also, if you notice anything else in my code that I could do better, please let me know, I'm not very experienced at coding, so any help and constructive criticism is welcome. :-)
Thank You!
Benjamin
You don't create i and j in the for-loops, i and j might get changed somewhere else in your app. Try this:
for (int i = 0; i < items.Count(); i++)
{
for (int j = 0; j < items.Count() - 1 - i; j++)
{
//lbl_i.Text = Convert.ToString(i);
//lbl_j.Text = Convert.ToString(j);
if (items[j] > items[j + 1])
{
swap(j, j + 1); //swaps the items at two given indecies
draw(); // draws the array to the picturebox
}
while (sorting == false) //the sorting is paused
{
Application.DoEvents();
}
}
}
The change is in: int i, int j
Found the answer: The problem was, that I was calling this function from the same button in the first place. In its click-event, I have an if-statement to check if the button said "Pause" or "Start", but when i paused the programm, it said "Start" again. So when I click it, the function gets called again and int i and int j are reinitialised to 0.
Code in the click-event:
private void btn_start_Click(object sender, EventArgs e)
{
//new added code
if (btn_start.Text == "Start")
{
btn_start.Text = "Pause";
sorting = true;
sort();
}
else if (btn_start.Text == "Pause")
{
btn_start.Text = "Continue"; // '= "Start"' in the old code
tmr_sorting.Stop();
sorting = false;
}
// new added code
else if (btn_start.Text == "Continue")
{
btn_start.Text = "Pause";
sorting = true;
}
}

Categories

Resources