I have an IENumerator in Unity wich activates a floor object every 0.70f-0.82f, so the gaps between the floor objects are randomly generated. The problem is, that when someone gets an FPS drop, the gaps are going to be huge and they player has no chance to reach the next floor. Can I make the "yield WaitForSeconds" fps-independent?
This is the code:
IEnumerator spawnFloors () {
while (playerControll.isAlive) {
for (int i = 0; i < floors.Length; i++) {
spawnWait = Random.Range (0.70f, 0.82f); // 0.65f, 0.85f; aktuell = 0.70f, 0.82f;
Vector2 spawnPosition = new Vector2 (transform.position.x, transform.position.y);
Quaternion spawnRotation = Quaternion.identity;
for (int a = 0; a < floors.Length; a++) {
int randomFloor = Random.Range (0, 9);
if (!floors [randomFloor].activeInHierarchy) {
floors [randomFloor].SetActive (true);
break;
}
}
//Instantiate(floors[Random.Range (0, 3)], spawnPosition, spawnRotation);
yield return new WaitForSeconds (spawnWait);
}
}
}`
Have you considered using FixedUpdate() instead?
In case you are wondering what's the difference, unity has a tutorial for it.
It's simply used for frame-independent updates in your game.
Related
I have almost finished my first game in Unity, which is a simple maze with characters collecting different coins. As it was a hassle to manually put all the different coins in the terrain, I decided to try and make a random generator for the coins.
The script is largely working, but the problem is that it's spawning objects most of the time inside walls. So I tried to add rigid bodies to coins, but this is messing with the coins position as the coins have a script for rotation. I also have the coins collision triggered because they perform a certain action when colliding with the character.
So after some research I saw that I need to check in my generator script for a near object collision and somehow change the spawn position of the coin if it's true. I'm trying to figure out how I can do that. My script is attached to my terrain and it looks like this:
using System.Collections;
using CustomArrayExtensions;
using UnityEngine;
public class ObjectSpawner : MonoBehaviour
{
[SerializeField] public GameObject[] letters;
GameObject selectedObject;
private int amount = 0;
public int limit;
void Start()
{
StartCoroutine(SpawnObjects());
}
IEnumerator SpawnObjects()
{
selectedObject = letters.GetRandom();
while (amount < limit)
{
Instantiate(selectedObject, new Vector3(Random.Range(35.0f, 950.0f), 13.0f, Random.Range(35.0f, 950.0f)), Quaternion.identity);
yield return new WaitForSeconds(5.0f);
amount++;
}
}
void OnCollisionEnter(Collision col)
{
if (col.gameObject.name == "Fox_Main")
{
Debug.Log("Collision Detected");
}
}
}
So i tried both answers and they didn't help me.Coins and other objects is still spawning close/inside the walls.My boxes have box collider and rigidbody with all positions/rotations locked(i need it that way so that i can destroy it later without moving it with the character),walls have box collider.So i tried to modify your solutions following Unity Documentation.I failed too.Here is what i have done so far.
See here
using System.Collections;
using UnityEngine;
public class RandomCrates : MonoBehaviour
{
public GameObject[] crates;
private GameObject selectedCrate;
public int limit = 0;
public float x, y, z;
public float forX_From,forX_To,forZ_From,forZ_To;
private int mask = 1 << 7;
void Start()
{
StartCoroutine(SpawnObjects());
}
IEnumerator SpawnObjects()
{
for (int i = 0; i < crates.Length; i++)
{
for (int j = 0; j < limit; j++)
{
Vector3 freePos = GetFreePosition();
Instantiate(crates[i], freePos, Quaternion.identity);
yield return new WaitForSeconds(0.0f);
}
}
}
Vector3 GetFreePosition()
{
Vector3 position;
Collider[] collisions = new Collider[1];
do
{
position = new Vector3(Random.Range(forX_From, forX_To), 10.0f, Random.Range(forZ_From, forZ_To));
} while (Physics.OverlapBoxNonAlloc(position, new Vector3(x, y, z), collisions, Quaternion.identity, mask) > 0);
return position;
}
}
Unity provides a simple way to detect collisions in specific position with Physics.OverlapSphere:
public class ObjectSpawner : MonoBehaviour
{
[SerializeField] public GameObject[] letters;
GameObject selectedObject;
private int amount = 0;
public int limit;
private float radius = 2f; //Radius of object to spawn
void Start()
{
StartCoroutine(SpawnObjects());
}
IEnumerator SpawnObjects()
{
selectedObject = letters[Random.Range(0, letters.Length)];
while (amount < limit)
{
Vector3 spawnPos = new Vector3(Random.Range(35.0f, 950.0f), 13.0f, Random.Range(35.0f, 950.0f));
//Check collisions
if (DetectCollisions(spawnPos) > 0)
continue;
Instantiate(selectedObject, spawnPos, Quaternion.identity);
yield return new WaitForSeconds(5.0f);
amount++;
}
}
private int DetectCollisions(Vector3 pos)
{
Collider[] hitColliders = Physics.OverlapSphere(pos, radius);
return hitColliders.Length;
}
}
In this way, if there is a collision in spawn position, the coin will not be spawned.
There are different ways of approaching this problem and given more information one could come up with a smarter approach. However, to provide you with a simple solution to your problem: You could just pick random positions until you find a "free spot".
Assuming your coins to have a size of 1, you could do something like:
IEnumerator SpawnObjects()
{
selectedObject = letters.GetRandom();
while(amount < limit)
{
Vector3 freePos = GetFreePosition();
Instantiate(selectedObject, freePos), Quaternion.identity);
yield return new WaitForSeconds(5.0f);
amount++;
}
}
Vector3 GetFreePosition()
{
Vector3 position;
Collider[] collisions = new Collider[1];
do
{
position = new Vector3(Random.Range(35.0f, 950.0f), 13.0f, Random.Range(35.0f, 950.0f));
}
while(Physics.OverlapSphereNonAlloc(position, 1f, collisions) > 0);
return position;
}
Please note that Physics.OverlapSphereNonAlloc() can also accept a LayerMask for filtering collisions based on layers. You might want to look into this, to prevent detecting collision with your terrain, which may well keep the do-while loop from exiting and freeze Unity.
I have a spawn obstacles for a player, and when a game begins, obstacles appear in each other. I tried to solve this problem through the while and physics 2d cycle, but when I started the game, the Unity crashed.
And I also tried through Raycast2D to check when the beam collides with some kind of collider, and change its position. But ray a small didn't always work.
First way
while (!Physics.CheckBox(pos, BlotPref[1].transform.localScale))
{
pos = new Vector2(Random.Range(-1f, 1f), YPosSetter());
}
Second way
while (hit.collider == null)
{
transform.position = new Vector2(Random.Range(-1f, 1f), YPosSetter());
}
Spawner
private void SpawnerPaper()
{
for (int i = 0; i < PlaningSpawn; i++)
{
if (rndType <= _dbPaperSpawn)
{
var blot = Instantiate(BlotPref[Random.Range(0, BlotPref.Length)], new Vector2(Random.Range(-1f, 1f), Random.Range(30f, 70f)), Quaternion.identity);
blot.transform.SetParent(paper.transform);
}
}
}
one solution is to test collider if obstacles is near the random position with Physics2D.OverlapCircleAll and using Layers:
of course obstacles have collider...
private void SpwanerPaper()
{
for (int i = 0; i < PlaningSpawn; i++)
{
if (rndType <= _dbPaperSpawn)
{
while (true)
{
var position = new Vector2(Random.Range(-1f, 1f), Random.Range(30f, 70f));
// i have choosen radius 1f, you could increase it if needed
Collider2D[] colliders = Physics2D.OverlapCircleAll(position, 1f, obstacleLayer);
if (colliders.Length == 0)
{
//so no obstacles present, spawn here
var blot = Instantiate(BlotPref[Random.Range(0, BlotPref.Length)],
position, Quaternion.identity);
blot.transform.SetParent(paper.transform);
break;// get out while loop
}
// so obstacles within 1m of the player loop again and give new random position
}
}
}
}
Following the situation you could use too Physics2D.CircleCastAll
If you dont know Layers, you have a tutorial youtube HERE
not exactly sure why. basically what I wanted to do is create a bullet hell game. so I would send a clone of a bullet to the gun, and then the gun would shoot the bullet depending on what direction it was facing. here is the code I used it is in c#. Edit: it crashes when I run the game.
using System.Collections.Generic;
using UnityEngine;
public class Bulletcontrol1 : MonoBehaviour
{
void Start()
{
StartCoroutine(Countinggggg());
}
// Update is called once per frame
void Shoot()
{
Debug.Log("clone");
GameObject prefab = Resources.Load("Picture1") as GameObject;
GameObject direction = GameObject.Find("b");
GameObject go = Instantiate(prefab) as GameObject;
go.transform.position = new Vector3(-23.25f, 8.75f, 1.494792f);
while (go.transform.position.x > -10)
{
Debug.Log("moving");
go.transform.position = new Vector3(go.transform.position.x + Mathf.Sin(direction.transform.rotation.z) * 5, go.transform.position.y + Mathf.Cos(direction.transform.rotation.z) * 5, go.transform.position.z);
}
}
IEnumerator Countinggggg()
{
yield return new WaitForSeconds(1);
Shoot();
Debug.Log("Bang");
}
}```
I have no idea what was wrong, but I changed the while loop to a for loop, and then noticed how the bullet did all it's moves at once: so I assume something with the while loop not ending and the bullet moving infinite spaces all at once caused it.
so I put in a a WaitForSeconds to slow it down, here is the code now
IEnumerator Shoot()
{
Debug.Log("clone");
GameObject prefab = Resources.Load("Picture1") as GameObject;
GameObject direction = GameObject.Find("b");
GameObject go = Instantiate(prefab) as GameObject;
go.transform.position = new Vector3(-10.13f, 3.52f, 1.494792f);
for (int i = 0; i < 100; i++)
{
Debug.Log("moving");
go.transform.position = new Vector3(go.transform.position.x + Mathf.Sin(direction.transform.rotation.z) * -0.1f, go.transform.position.y + Mathf.Cos(direction.transform.rotation.z) * -0.1f, go.transform.position.z);
yield return new WaitForSeconds(0.1f);
}
I just don't know how to write the if part. The rest is just instantiate position and quaternion stuff.... Probably.
I want to know if the GameObject rocketspawnblue and GameObject rocketspawnred are gone, so I can spawn the next pair until all the pairs in the loop are spawned.
This is my spawning coroutine I put in Start(). however that's probably won't work because it needs to be check every frame until i take all my rockets....
for (int i = 1; i < 6; i++)
{
yield return new WaitForSeconds(1);
GameObject rocketspawnblue = Instantiate(
rocketblue,
new Vector3(Random.Range(-15, 15), (Random.Range(-15, 15))),
Quaternion.identity);
SpriteRenderer rocketscolor1 = rocketspawnblue.GetComponent<SpriteRenderer>();
//rocketscolor.color = colors[Random.Range(0,colors.Length)];
rocketscolor1.color = Color.blue;
GameObject rocketspawnred = Instantiate(
rocketred,
new Vector3(Random.Range(-15, 15), (Random.Range(-15, 15))),
Quaternion.identity);
SpriteRenderer rocketscolor2 = rocketspawnred.GetComponent<SpriteRenderer>();
//rocketscolor.color = colors[Random.Range(0, colors.Length)];
rocketscolor2.color = Color.red;
}
Use a List as a field to track the rockets:
private List<GameObject> rockets;
At the beginning of Start(), instantiate the list:
rockets = new List<GameObject>();
In your coroutine, clear the list with Clear then use Add to add rockets to your list. After you're done adding, use WaitWhile to loop until there are no rockets left. We'll write a method called AnyRocketsLeft that returns true if there are any that are not yet destroyed.
After the WaitWhile, use WaitForSeconds to wait for a second to give time between batches.
for (int i = 1; i < 6; i++)
{
rockets.Clear();
GameObject rocketspawnblue = Instantiate(
rocketblue,
new Vector3(Random.Range(-15, 15), (Random.Range(-15, 15))),
Quaternion.identity);
SpriteRenderer rocketscolor1 = rocketspawnblue.GetComponent<SpriteRenderer>();
//rocketscolor.color = colors[Random.Range(0,colors.Length)];
rocketscolor1.color = Color.blue;
rockets.Add(rocketspawnblue);
GameObject rocketspawnred = Instantiate(
rocketred,
new Vector3(Random.Range(-15, 15), (Random.Range(-15, 15))),
Quaternion.identity);
SpriteRenderer rocketscolor2 = rocketspawnred.GetComponent<SpriteRenderer>();
//rocketscolor.color = colors[Random.Range(0, colors.Length)];
rocketscolor2.color = Color.red;
rockets.Add(rocketspawnred);
yield return new WaitWhile(() => AnyRocketsLeft());
yield return new WaitForSeconds(1);
}
// ran out of rockets
// set up the "next level" prompt, etc. here
To check if any rockets are not yet destroyed, we can check their activeInHierarchy property. Just loop through the list of rockets and return true if you find any, and false otherwise.
private bool AnyRocketsLeft() {
foreach (int i = 0 ; i < rockets.count ; i++) {
if (rockets[i].activeInHierarchy) return true;
}
return false;
}
If you're using Linq, you can use List.Any():
private bool AnyRocketsLeft() {
return rockets.Any(o => o.activeInHierarchy);
}
Currently I have two balls that spawn randomly around a circle, with a minimum distance of 0.3 between them so they do not collide. However, I want the player to set the amount of balls to spawn, and I need these balls to spawn randomly but with a minimum amount of distance from each other.
With my code, any ball amount that is greater than 2 may have a chance of two balls spawning on top of each other, since I only reference the position of the last spawned ball with spawnConstant.
When I tried to figure it out with arrays, I ran into the same problem, having the position only refer to one position in the array. Attached is the current code:
IEnumerator SpawnBalls () {
Vector3 spawnConstant = new Vector3 ();
Vector3 spawnPosition = new Vector3 ();
for (int i = 0; i < ballNumber; i++) {
if (i == 0) {
spawnPosition = Random.insideUnitCircle.normalized * 0.7f;
Instantiate (ball, spawnPosition, Quaternion.identity);
Debug.Log ("wolo");
spawnConstant = spawnPosition;
} else if (i > 0) {
spawnPosition = Random.insideUnitCircle.normalized * 0.7f;
while (Vector3.Distance (spawnPosition, spawnConstant) <= 0.3f) {
spawnPosition = Random.insideUnitCircle.normalized * 0.7f;
}
spawnConstant = spawnPosition;
Instantiate (ball, spawnPosition, Quaternion.identity);
Debug.Log ("yolo");
}
}
yield return null;
}
How can I make each ball refer to the positions of all the previous ones and spawn a minimum distance away from them?
IEnumerator SpawnBalls () {
Vector3[] ballArray = new Vector3[ballNumber];
Vector3 spawnPosition = new Vector3 ();
int index = 0;
for (int i = 0; i < ballNumber; i++) {
// Create first ball and store coordinates into our ballArray at index 0
if (i == 0) {
spawnPosition = Random.insideUnitCircle.normalized * 0.7f;
Instantiate (ball, spawnPosition, Quaternion.identity);
Debug.Log ("wolo");
ballArray[index] = spawnPosition;
// Else we already have one ball
} else if (i > 0) {
// Create new spawn coordinates
spawnPosition = Random.insideUnitCircle.normalized * 0.7f;
// Loop through indicies of array that have been filled
for( int k = 0; k <= index; k++ ) {
// Check that each value in array meets distance requirement
if(Vector3.Distance (spawnPosition, ballArray[k]) <= 0.3f) {
// If not, create new coordinates and reset k
spawnPosition = Random.insideUnitCircle.normalized * 0.7f;
k = 0;
}
// Update array
ballArray[index++] = spawnPosition;
Instantiate (ball, spawnPosition, Quaternion.identity);
Debug.Log ("yolo");
}
}
yield return null;
}