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