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();
}
}
Related
I need this pre-existing script to loop every forever, I do not know much code and am unable to edit this myself, I'm hoping one of you smart boys can assist in the editing of this script.
This script is C# built for a game called Space Engineers where it takes information from a solar panel to determine time of day/light level and respond by turning on the lights or turning off the lights.
//How to use:
//Setup a timer that triggers it self and this programmable block every 5-10 minutes. Then set the light strength(
//when the solar panel gets less kW's than that, the lights are turned on). Then set measureSolarPanelName to
//the customname of the solar panel that you want to use to measure the light. If you want to use a prefix, set
//usePrefix to true und set a prefix. When you set overwrite to true, this script will stop
//working until you deactivate overwrite(= false), for eg. battles.
//Thank you for using my script :)
//Settings:
int lightStrength = 10;
string measureSolarPanelName = "Solar Panel";
bool overwrite = false;
bool usePrefix = false;
string prefix = "[NL]";
//Code
public void Main(string argument) {
if (getSolarPower() < lightStrength) {
triggerLights(1);
} else {
triggerLights(0);
}
}
int getSolarPower () {
bool watts;
string info = GridTerminalSystem.GetBlockWithName(measureSolarPanelName).DetailedInfo;
var lines = info.Split('\n');
var output = lines[1].Split(':')[1].Trim();
var final = output.Replace(" kW", "");
if (final.Contains("W")) { final = final.Replace(" W", ""); watts = true; } else watts = false;
var finalb = final.Split ('.')[0];
if (watts) { finalb = "1"; }
return Int32.Parse(finalb);
}
void triggerLights(int status) {
if (status == 1 && !overwrite) {
List<IMyInteriorLight> lights = new List<IMyInteriorLight> ();
GridTerminalSystem.GetBlocksOfType<IMyInteriorLight>(lights);
List<IMyReflectorLight> spotlights = new List<IMyReflectorLight> ();
GridTerminalSystem.GetBlocksOfType<IMyReflectorLight>(spotlights);
for (int i = 0; i < lights.Count; i++) {
if (getPrefixBool(lights[i].CustomName)) {
lights[i].GetActionWithName ("OnOff_On").Apply(lights[i]);
}
}
for (int i = 0; i < spotlights.Count; i++) {
if (getPrefixBool(spotlights[i].CustomName)) {
spotlights[i].GetActionWithName("OnOff_On").Apply(spotlights[i]);
}
}
}
else
if (status == 0 && !overwrite) {
List<IMyInteriorLight> lights = new List<IMyInteriorLight> ();
GridTerminalSystem.GetBlocksOfType<IMyInteriorLight>(lights);
List<IMyReflectorLight> spotlights = new List<IMyReflectorLight> ();
GridTerminalSystem.GetBlocksOfType<IMyReflectorLight>(spotlights);
for (int i = 0; i < lights.Count; i++) {
if (getPrefixBool(lights[i].CustomName)) {
lights[i].GetActionWithName("OnOff_Off").Apply(lights[i]);
}
}
for (int i = 0; i < spotlights.Count; i++) {
if (getPrefixBool(spotlights[i].CustomName)) {
spotlights[i].GetActionWithName("OnOff_Off").Apply(spotlights[i]);
}
}
}
}
bool getPrefixBool (string name) {
if (usePrefix) {
if (name.StartsWith(prefix)) {
return true;
} else return false;
} else return true;
}
I haven't tried much at this point as I can't exactly code... I have found a couple lines but they don't seem to work.
I want to create a minigame which select random buttons from an array of game objects and store the value in an array. After the first step is completed the user need to tap on the same buttons or he will lose. The problem is when I want to compare the values from this two arrays, every value from index 0-2 is set to 0 in both arrays. I tried to debug the adding part and that works fine, even in my editor I can see the stored values. Here is a photo:
storedobjectsEditor. I even put two for loops to check the values from array in the game, instead of getting 3 prints I get 6 of them, first 3 represent the values right and the other 3 have value = 0 (this apply to both arrays). In my CheckWin() the result will be always true because the values which are compared there are 0 for every position from both arrays. I can't figure it out what force the arrays to have all components set to zero in that method. Here is the script:
public class Script : MonoBehaviour
{
[SerializeField] private GameObject[] buttons;
[SerializeField] private int[] culoriINT;
[SerializeField] private int[] culoriComparareINT;
int index = 0;
int index2 = 0;
private bool win = false;
private void Start()
{
StartCoroutine(ChangeColors2());
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.P))
{
for (int i = 0; i < culoriINT.Length; i++)
{
Debug.Log("INT vector[" + i + "]: " + culoriINT[i]);
}
}
if (Input.GetKeyDown(KeyCode.W))
{
for (int i = 0; i < culoriComparareINT.Length; i++)
{
Debug.Log("Al doilea vector[" + i + "]: " + culoriComparareINT[i]);
}
}
}
IEnumerator ChangeColors2()
{
yield return new WaitForSeconds(1f);
for (int j = 0; j < buttons.Length; j++)
{
var randomBtn = UnityEngine.Random.Range(0, buttons.Length);
buttons[randomBtn].GetComponent<Image>().color = Color.green;
var introducereIndex = buttons[randomBtn].GetComponent<IndexButtons>().index;
culoriINT[index] = introducereIndex;
Debug.Log($"Index adaugat {introducereIndex} total {culoriINT.Length}");
index++;
yield return new WaitForSeconds(0.5f); //seteaza coloare alb pe acelas buton in cazu in care nimereste acelas sa se vada
buttons[randomBtn].GetComponent<Image>().color = Color.white;
Debug.Log("verde");
yield return new WaitForSeconds(1f);
}
}
public void OnButtonClick()
{
index2++;
}
public void numePeClick()
{
if (index2 < buttons.Length)
{
string a = EventSystem.current.currentSelectedGameObject.name;
culoriComparareINT[index2] = Convert.ToInt32(a);
Debug.Log($"Index adaugat {Convert.ToInt32(a)} total {culoriComparareINT.Length}");
}
else
{
Debug.Log("Array plin");
}
}
public void CheckWin()
{
win = true;
for (var i = 0; i < culoriINT.Length; i++)
{
if (culoriINT[i] != culoriComparareINT[i])
{
win = false;
break;
}
else
{
win = true;
}
}
if (win)
{
Debug.Log("Ai castigat!");
}
else
{
Debug.Log("Ai pierdut!");
}
}
}
It sounds like you have two instances of the same Script component in your scene. One of them has the correct values 3,2,3, the other one has the wrong values 0,0,0. This is why six values get printed to the console instead of three with a single input.
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
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...
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.