i have a strange problem. My Code works fine. Everything works as it should until it goes the last time through a foreach loop. The code:
public List<string> waveInput = new List<string>(); //die Eingabe der Welle als Zeichenfolge
public List<GameObject> enemyTyps = new List<GameObject>();
public List<Vector3> path = new List<Vector3>();
public GameObject tempPathStart;
public int currentWave = 0;
public int currentAmountOfEnemies;
private int spawnAmountOfEnemies;
private double spawnDelay;
private List<GameObject> enemyToSpawn = new List<GameObject>();
void Start() {
path.Add(tempPathStart.transform.position);
StartCoroutine(function());
}
IEnumerator function() {
while(waveInput.Capacity >= currentWave) {
if(waveInput[currentWave] == "" && currentAmountOfEnemies <= 0) {
currentWave++;
enemyToSpawn.Clear();
spawnAmountOfEnemies = 0;
} else if(currentAmountOfEnemies <= 0) {
string _substring = waveInput[currentWave].Substring(0, waveInput[currentWave].IndexOf(";") + 1);
ManageSubstring(_substring);
for(int i = 0; i < spawnAmountOfEnemies; i++) {
foreach(GameObject element in enemyToSpawn) {
this.SpawnEnemy(element);
yield return new WaitForSeconds((float)spawnDelay);
}
}
}
}
}
void ManageSubstring(string _substring) {
string _tempStringAmount = "";
string _tempStringDelay = "";
string _tempStringType = "";
bool _switchAmountDelay = false;
for(int i = 0; i < _substring.Length; i++) {
char c = _substring[i];
if(c >= '0' && c <= '9') {
if(_switchAmountDelay) {
_tempStringDelay += c;
} else {
_tempStringAmount += c;
}
} else if(c == ';') {
} else if(c == '.') {
_tempStringDelay += c;
} else {
_switchAmountDelay = true;
_tempStringType += c;
}
}
spawnDelay = double.Parse(_tempStringDelay);
spawnAmountOfEnemies = int.Parse(_tempStringAmount);
foreach(char c in _tempStringType) { //die Buchstaben in GameObjekte / Gegner umwandeln
int _tempConvertedInt = TranslateStringToInt(c);
enemyToSpawn.Add(enemyTyps[_tempConvertedInt]);
}
}
int TranslateStringToInt(char pToConvertChar) {
List<char> _alphabet = new List<char>() {'a','b','c','d','e','f','g','h','i','j','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'};
return _alphabet.IndexOf(pToConvertChar);
}
void SpawnEnemy(GameObject prefab) {
currentAmountOfEnemies++;
GameObject e = Instantiate(prefab) as GameObject;
e.transform.position = path[0];
}
the very strange thing as i already said is: the code works, even the line works fine until it goes through the last time. Then Unity crashes and i have no idea what i should do. If some more code is needed for context just say.
Thanks for every answer!
I just pulled your script into the debugger and stepped through the last iteration:
When the iteration is over, it goes back into the while() loop which encloses the whole body of function().
The while condition remains true since waveInput.Capacity is greater than the currentWave (still zero).
The if(waveInput[currentWave] == "" && currentAmountOfEnemies <= 0) is false since the waveInput[] array has an element zero which is not "", and the enemycount was not reset yet.
the else if(currentAmountOfEnemies <= 0) condition is also false since the current amount was not reset.
nothing more in the while-loop, nothing chagned, you have an endless-loop, which never hits a yield return. This locks up the Update() cycle of Unity and only allow killing.
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'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 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 am trying to make my character pickup and drop with the same key "space" in unity but since void update happens so fast then when I drop a monster then it instantly picks it up.
void Update() {
if (Input.GetKeyDown("space") /*( || Input.GetMouseButtonDown(1))*/) {
if (map.selectedUnit.GetComponent<Unit>().carrying) {
print("DROP space");
map.selectedUnit.GetComponent<Unit>().carrying = false;
}
// pick up monster while on top of it
else {
for(int i = 0; i < map.monsterList.Count; i++) {
if (map.monsterList[i].GetComponent<Monsters>().tileX == map.selectedUnit.GetComponent<Unit>().tileX &&
map.monsterList[i].GetComponent<Monsters>().tileY == map.selectedUnit.GetComponent<Unit>().tileY &&
map.occupationArray [map.monsterList[i].GetComponent<Monsters>().tileX, map.monsterList[i].GetComponent<Monsters>().tileY] == true) {
print("PICKUP space");
map.selectedUnit.GetComponent<Unit>().carrying = true;
}
}
}
}
}
Is there a way to fix this or do I have to put pickup key as other key. I have been thinking if I add 1 second between each "space" key. So you can press space only once in one second and it could somehow fix this.
How about using GetKeyUp instead.
GetKeyUp is called only once when you released key.
See this doc
https://docs.unity3d.com/ScriptReference/Input.GetKeyUp.html
I end up using seconds methods in my code. This means that it waits 0.25 sec before you can pick up or drop again. Now this prevents it to loop all the time. If someone else has better solution then please provide it. For now I am using this.
void Update() {
// there is 0.25 second between that player can PickUp again or Drop again
if (!map.selectedUnit.GetComponent<Unit>().carrying) {
timeLeftToPressAgain -= Time.deltaTime;
timeLeftToPressAgain = Mathf.Max(timeLeftToPressAgain, -0.25f);
}
else {
timeLeftToPressAgain += Time.deltaTime;
timeLeftToPressAgain = Mathf.Min(timeLeftToPressAgain, 0.25f);
}
if (Input.GetKeyDown("space") /*( || Input.GetMouseButtonDown(1))*/) {
// while carrying the time builds up for player to Drop and vice versa in PickUp
if (map.selectedUnit.GetComponent<Unit>().carrying && timeLeftToPressAgain > 0) {
map.selectedUnit.GetComponent<Unit>().carrying = false;
timeLeftToPressAgain = 0.25f;
}
// pick up monster while on top of it
else if (!map.selectedUnit.GetComponent<Unit>().carrying && timeLeftToPressAgain < 0) {
for(int i = 0; i < map.monsterList.Count; i++) {
if (map.monsterList[i].GetComponent<Monsters>().tileX == map.selectedUnit.GetComponent<Unit>().tileX &&
map.monsterList[i].GetComponent<Monsters>().tileY == map.selectedUnit.GetComponent<Unit>().tileY &&
map.occupationArray [map.monsterList[i].GetComponent<Monsters>().tileX, map.monsterList[i].GetComponent<Monsters>().tileY] == true) {
map.selectedUnit.GetComponent<Unit>().carrying = true;
timeLeftToPressAgain = -0.25f;
}
}
}
}
}
Just to complete it: You could have done something like this:
bool pressedSpace = false;
void Update() {
pressedSpace = true; // Only called once
if (!pressedSpace && Input.GetKeyDown("space") /*( || Input.GetMouseButtonDown(1))*/) {
if (map.selectedUnit.GetComponent<Unit>().carrying) {
print("DROP space");
map.selectedUnit.GetComponent<Unit>().carrying = false;
}
// pick up monster while on top of it
else {
for(int i = 0; i < map.monsterList.Count; i++) {
if (map.monsterList[i].GetComponent<Monsters>().tileX == map.selectedUnit.GetComponent<Unit>().tileX &&
map.monsterList[i].GetComponent<Monsters>().tileY == map.selectedUnit.GetComponent<Unit>().tileY &&
map.occupationArray [map.monsterList[i].GetComponent<Monsters>().tileX, map.monsterList[i].GetComponent<Monsters>().tileY] == true) {
print("PICKUP space");
map.selectedUnit.GetComponent<Unit>().carrying = true;
}
}
}
}
}
And then after a little wait you can reset pressedSpace to false.
The game i am working on has a player that needs to jump and duck under obstacles. I already have it coded in C# to have an random generator. The only thing is, i want to have a sequence of obstacles to spawn that i have in mind. For example, instead of the random, obstacle1, 5, 2, 3, etc. I want obstacle1,2,3,4,5,6,7,8, and so on, to spawn in the order i would like it to. Not sure on how to start the code for this, can anyone point me in the right direction? Thanks in advance!
Here is my random example code.
private void Creation()
{
for(int i = 0; i < numberOfObjects; i++)
{
cObst = Random.Range(0,100);
if(cObst <= obstL)
{
obstacleQueue.Enqueue((Transform) Instantiate(obstHPrefab.transform));
if(i == 0 && start)
{
nextPos = highPos;
}
}
else if(cObst <= obstH && cObst > obstL)
{
obstacleQueue.Enqueue((Transform) Instantiate(obstLPrefab.transform));
if(i == 0 && start)
{
nextPos = lowPos;
}
}
else if(cObst <= obstC && cObst > obstH)
{
obstacleQueue.Enqueue((Transform) Instantiate(obstCPrefab.transform));
if(i == 0 && start)
{
nextPos =cPos;
}
}
else if(cObst <= obstC && cObst > obstC)
{
obstacleQueue.Enqueue((Transform) Instantiate(obstCPrefab.transform));
if(i == 0 && start)
{
nextPos =cPos;
}
}
else
{
Debug.Log ("BREAK");
}
}
}
private void Recycle(float extraZ = 0f)
{
Transform o = obstacleQueue.Dequeue();
if(o.name == obstH.name+"(Clone)")
{
nextPos = new Vector3(highPos.x,highPos.y,nextPos.z);
}
else if(o.name == obstLPrefab.name+"(Clone)")
{
nextPos = new Vector3(lowPos.x,lowPos.y,nextPos.z);
}
else
{
nextPos = new Vector3(carPos.x,carPos.y,nextPos.z);
}
nextPos += new Vector3(
0f,
0f,
Random.Range((0-minZ)+extraZ, (0-maxZ)+extraZ));
Vector3 position = nextPos;
o.localPosition = position;
obstacleQueue.Enqueue(o);
}
void Update()
{
if(curQue <= numberOfObjects && !nextCreate)
{
waitCreate = true;
if(obstacleQueue.Peek().localPosition.z - recycle > char.position.z)
{
Destroy(GameObject.Find (obstacleQueue.Peek().name));
obstacleQueue.Dequeue();
curQue++;
}
}
else
{
nextCreate = true;
StartCoroutine(Wait(0.5f));
curQue = 1;
}
}
private void GameStart()
{
nextPos = transform.localPosition;
for(int i = 0; i < numberOfObjects; i++)
{
Recycle();
}
}
IEnumerator Wait(float duration)
{
if(waitCreate)
{
waitCreate = false;
yield return new WaitForSeconds(duration); //Wait
Creation();
for(int i = 0; i < numberOfObjects; i++)
{
if(i == 0)
{
Recycle(-25f);
}
else { Recycle(); }
}
nextCreate = false;
}
}
}
You could just assign all of the numbers which represent your obstacles in a List. You can just order them sequentially in the List and iterate through one at a time.
List<int> Obstacles = new List<int>();
Assign them using the following,
Obstacles.Add(1);
Obstacles.Add(3);
Obstacles.Add(7);
Then you can reference them by replacing,
CObst = Random.Range(0,100);
Use,
CObst = List[i];
I see you're using Unity. I think your real problem is not about lists or randomness, but how to spawn objects in order without spawning them all at the same time. The easiest way to do this will be for your spawner script to be a prefab or a scene object, coupled with a Coroutine to delay/prolong execution. The script will have a list of objects. These objects will be set in the inspector.
public List<GameObject> objectsToSpawn;
Then your spawning logic will spawn one at a time, probably waiting for something in between:
void Start()
{
StartCoroutine(SpawnAll());
}
public IEnumerator SpawnAll()
{
foreach (GameObject obj in objectsToSpawn)
{
GameObject instance = Instantiate(obj) as GameObject;
// wait for object, see below
}
}
For the delay/wait, you can either use time, or wait for the object to become inactive (if it self-deactivates, like an enemy that will eventually be killed):
yield return new WaitForSeconds(20);
or:
while (instance.activeSelf)
yield return null;