How to remove object from array in loop after some time - c#

I want to remove(Destroy) my gameobjects(hurdles) from the loop that are random generated after some time by random.range but it is not removing. I tried to change the " Destroy(newSelected[ran], 5)" position but not working.
private void OnTriggerExit(Collider theCollision)
{
if (theCollision.gameObject.tag == "Ob2")
{
a = theCollision.transform.position;
x = a.z + 3f;
x_ = a.z - 1f;
startingpoint = a.x;
for (int i = 8; i <= 64; i += 8)
{
var ran = Random.Range(0, 4);
//selecting gameobjects(hurdles) by default.
print(ran);
b = new Vector3(startingpoint + i, 0.050f, Random.Range((int)x_, (int)x));
if (ran == 2)
{
newSelected[2]=Instantiate(gameObjects[2], new Vector3(startingpoint + i, 0.050f, a.z), Quaternion.identity);
print(x_);
}
else if (ran != 2)
{
newSelected[ran]=Instantiate(gameObjects[ran], b, Quaternion.identity);
}
Destroy(newSelected[ran], 5);
//I want to destroy my gameobjects(hurdles) here that are instantiate after some time.
}
}
}
}

Your code above will attempt 7 times to destroy an object in the newSelected array at a random index of 0, 1, 2 or 3. Meaning that, for example, it might attempt to destroy the object at index 0 multiple times and therefore returning a null error after the object has been destroyed the first time.
To avoid that error you should check that newSelected[ran] is not null before attempting to destroy it.
I would recommend you create a separate component to handle the random delay destroy functionality and add it to the GameObject you are instantiating.
Here's a quick C# Script that will handle destroying the GameObject it is attached to:
using UnityEngine;
public class DestroyAfterRandomDelay : MonoBehaviour
{
[Tooltip("Minimum delay value")]
public float MinimumDelay = 0f;
[Tooltip("Maximum delay value")]
public float MaximumDelay = 4f;
// keeps track of elapsed time
private float _elapsedTime= 0;
// Keeps track of random delay
private float _randomDelay;
// Start is called before the first frame update
void Start()
{
// Sets random delay on start
_randomDelay = Random.Range(MinimumDelay, MaximumDelay);
Debug.LogFormat("{0} will be destroyed in {1} seconds!", this.name, _randomDelay);
}
// Update is called once per frame
void Update()
{
// Increment time passed
_elapsedTime += Time.deltaTime;
// Proceed only if the elapsed time is superior to the delay
if(_elapsedTime < _randomDelay) return;
Debug.LogFormat("Destroying {0} after {1} seconds! See Ya :)", this.name, _elapsedTime);
// Destroy this GameObject!
Destroy(this.gameObject);
}
}

Related

How can i improve my time delayed MMO like mob spawner code?

I tried to make a mob spawner which does followings:
1) spawns pre-defined amount of mobs with time interval of choice
2) checks if spawned gameobject destroyed if so spawns new ones till it reaches maximum amount again
Code works but i still think there can be improvements i want it to be mmo like slot spawner with pre defined maximum mob amount and intervals between every spawn
Issues im having:
1) at start works properly by 5 sec intervals between spawns but sometimes after you delete gameobject next spawn in line spawns instantly or very quickly
private void Start()
{
spawnedCount = gameObject.transform.childCount;
if (spawnedCount != 6)
isSpawning = false;
}
private void Update()
{
spawnedCount = gameObject.transform.childCount;
if (!isSpawning && spawnedCount < maxSpawnCount + 1) // check if should start spawn status and if coroutine currently working already isSpawning = false > yes you can can if you want to spawn or not isSpawning = True > no it is already spawning you cant check anymore and send requests
{
isSpawning = true; // set spawning status to true
StartCoroutine(DelayedSpawn(delayInterval));
}
}
IEnumerator DelayedSpawn(float delay)
{
yield return new WaitForSecondsRealtime(delay);
if (spawnedCount <= maxSpawnCount)
{
GetRandomZombie(); // gets random zombie prefab to instantiate
spawnedObj = Instantiate(PrefabToSpawn, new Vector3(gameObject.transform.position.x + Random.Range(-5f, 5), 0, gameObject.transform.position.z + Random.Range(-5f, 5)), transform.rotation);
spawnedObj.transform.parent = gameObject.transform;
}
if (spawnedCount <= maxSpawnCount )
StartCoroutine(DelayedSpawn(delayInterval));
else if (spawnedCount == 6)
{
isSpawning = false;
yield break;
}
}
I would do this like this. It seems cleaner.
Also I would suggest GetRandomZombie() method to return a prefab. That would be cleaner as well.
A reminder: The game object with script that has the coroutine method is disabled/deleted somehow, unity gives an error and coroutine stops. You can solve this like this:
private void OnDisable() //To prevent the error
{
StopAllCoroutines();
}
private void OnEnable() //To start the coroutine again whenever the spawner object is enabled again.
{
StartCoroutine(DelayedSpawn());
}
private IEnumerator DelayedSpawn()
{
while (true)
{
yield return new WaitForSecondsRealtime(delayInterval);
if (gameObject.transform.childCount <= maxSpawnCount)
{
GetRandomZombie(); // gets random zombie prefab to instantiate
spawnedObj = Instantiate(PrefabToSpawn, new Vector3(gameObject.transform.position.x + Random.Range(-5f, 5), 0, gameObject.transform.position.z + Random.Range(-5f, 5)), transform.rotation);
spawnedObj.transform.parent = gameObject.transform;
}
}
}

How can I display collected amount of coins without overflow (accessing another script) in Unity?

I have two scripts. PlayerMove.cs and Spawner.cs .
In PlayerMove.cs I want a function that changes the text to the number of coins.
void CountCoin()
{
cointext.text = ": "+ coincount;
CountCoin();
}
In Spawner.cs I spawn coins and I want to increase coincount(which is public variable of PlayerMove.cs) each time I spawn.
IEnumerator StartSpawningCoins ()
{
coin.GetComponent<Renderer>().enabled=true;
yield return new WaitForSeconds(Random.Range(6f, 16f));
GameObject k = Instantiate(coin);
// my problem starts here
GameObject Player = GameObject.Find("PlayerMove");
PlayerMove playermove = Player.GetComponent<PlayerMove>();
playermove.coincount++;
//till here
float x = Random.Range(min_X,max_X);
k.transform.position = new Vector2(x, transform.position.y);
StartCoroutine(StartSpawningCoins());
}
When I use these I get stack overflow error. How can I fix it?
The problem is that in
void CountCoin()
{
cointext.text = ": "+ coincount;
CountCoin();
}
you call CountCoin() itself!
If you wanted a repeated Update you could e.g. use Invoke
void CountCoin()
{
cointext.text = ": "+ coincount;
// calls this method again after 1 second
Invoke(nameof(CountCoin), 1f);
}
or directly InvokeRepeating
void Start()
{
// calls CountCoin every second, the first time after 1 second delay
InvokeRepeating(nameof(CountCoin), 1f, 1f);
}
in order to call it every second.
Or simply remove the nested call entirely and add one to Update to make it get called every frame
void Update()
{
CountCoin();
}
Or this is what I would rather do: Make coincount a property and add the according behavior to the setter:
private int _coincount;
public int coincount
{
get { return _coincount; }
set
{
_coincount = value;
cointext.text = ": "+ value;
}
}
so everytime you change the property the code is executed automatically
Btw Find is quite expensive and should be avoided. I would change your code to
private PlayerMove playermove;
IEnumerator StartSpawningCoins ()
{
// as long as you yield inside this is ok
while(true)
{
// only do find if needed
if(!player) player = GameObject.Find("PlayerMove");
// this should probably actually be done in the prefab not via code
coin.GetComponent<Renderer>().enabled = true;
yield return new WaitForSeconds(Random.Range(6f, 16f));
GameObject k = Instantiate(coin);
playermove.coincount++;
float x = Random.Range(min_X,max_X);
k.transform.position = new Vector2(x, transform.position.y);
}
}

C# how to Make function/Method Wait without blocking program (Unity)

I want to simulate health regeneration in my game in unity, in the function RestoreHealth().
Am I overthinking it by wanting to create a child process, so when I call wait, it won't affect any current running process or thread and the child process will die when the function is done.
public void RestoreHealth() {
if (health >= MaxHealth) return; // health and MaxHealth are Class variables
if (health % 10 != 0) { // if health is not dividable by 10
int temp = health % 10; // then we round it to the closest
//tenth
temp = 10 - temp;
health += temp;
}
int i = health;
for (; i < MaxHealth; i += 10) { // where the health grows till 100
health += 10;
// sleep(1000); // make function wait for '1 second' to iterate again
Debug.Log("Health: " + health);
}
}
How do I create a child process in C# or unity in this case and cause it to wait?
Is there an equivalent to Fork(); like in C?
Also, this function is called when the player initially receives damage.
Solution:
note: I changed Health to Armour
public IEnumerator RestoreArmour() {
while (_Armour < _MaxArmour) {
_Armour++;
Debug.Log("Health: " + _Armour);
yield return new WaitForSeconds(ArmourRegenRate); // ArmourRegenRate is a
// float for the seconds
}
}
and use this to initiate the coroutine
void Start(){
StartCoroutine(player.RestoreArmour());
}
Basic Coroutine concept
In Unity you work with Coroutines to achieve this asychronous "threaded" behaviour
IEnumerator RestoreHealth() {
while (health != MaxHealth) {
health++;
yield return new WaitForEndOfFrame();
}
}
and then invoke it with
StartCoroutine(RestoreHealth());
Restarting a Coroutine
In order to stop an existing Coroutine from running and start a new one, this is how you would achieve that:
private Coroutine _myCoroutine = null;
void SomeMethod()
{
if (_myCoroutine != null)
StopCoroutine(_myCoroutine);
_myCoroutine = StartCoroutine(SomeOtherMethod());
}
Pausing armor restoration for X seconds when player has taken damage
A common functionality is to have something restore armor when player hasn't taken damage for X seconds:
private bool _shouldRestoreArmour = true;
private Coroutine _pauseArmorCoroutine = null;
void Update()
{
if (_shouldRestoreArmour)
Armor += ArmorRegenerationPerSecond * Time.deltaTime;
}
void PlayerTakeDamage()
{
if (_pauseArmorCoroutine != null)
StopCoroutine(_pauseArmorCoroutine);
_pauseArmorCoroutine = StartCoroutine(PauseRestoreArmor());
// Take damage code
}
IEnumerator PauseRestoreArmor()
{
_shouldRestoreArmor = false;
yield return new WaitForSeconds(RESTORE_ARMOR_DELAY_TIME);
_shouldRestoreArmor = true;
}
Here the player will regenerate armor at all times, except for X seconds after the player has taken damage. If the player takes damage multiple times we will simply abort the previous coroutine and start a new one, so that it will be a fresh X seconds from the last hit.
You can create a timer assuming that you run this in the update method
private float timer = 1;
private float timerReset = 1;
private void Update(){
RestoreHealth();
}
public void RestoreHealth() {
if (health >= MaxHealth) return; // health and MaxHealth are Class variables
if (health % 10 != 0) { // if health is not dividable by 10
int temp = health % 10; // then we round it to the closest tenth
temp = 10 - temp;
health += temp;
}
int i = health;
for (; i < MaxHealth; i += 10) { // where the health grows till 100
if(timer > 0){
timer -= 1 * time.deltaTime;
}else{
health += 10;
Debug.Log("Health: " + health);
timer = timerReset;
}
}
}
or you can simply just give your player 1 health every second or whatever amount for instance
public void RestoreHealth() {
if (health >= MaxHealth) return; // health and MaxHealth are Class variables
if (health % 10 != 0) { // if health is not dividable by 10
int temp = health % 10; // then we round it to the closest tenth
temp = 10 - temp;
health += temp;
}
int i = health;
for (; i < MaxHealth; i += 10) { // where the health grows till 100
health += 10 * 1 * time.deltaTime;
Debug.Log("Health: " + health);
timer = timerReset;
}
}
So in this scenario your player recieves 10 health every second but the value will always go up so in the matter of a second the player health would go up 1 health every 100ms

Memory overload on for loop

I am making an object spawn script in which on the start of the script the spawning function is called and in it is a for loop which creates an object each iteration. It first picks a random X position for it and then checks if it is in a range of another prefabs coordinates so they don't spawn too close or worse, one in each other. If it is in the same coordinates as another prefab it will return 0 and this same goes out for the Z axis too. It also picks a random Y axis rotation so it doesn't all face the same direction. After this it spawns the prefab and sets it's coordinates and rotation after which it check if the coordinates in the X or Z axis are 0, and if any of those two are 0 it goes back one iteration and the last object to be spawned is destroyed so it doesn't flood. This works perfectly but when you want to set it to spawn too much objects it floods the RAM because there is nowhere to spawn more objects. I tried finding the highest X position and highest Z position and multiplying them, and setting them both to positive, and then dividing them by the space between the prefabs but this doesn't work as it sets it to a really really high number. How would you fix this?
Script:
using UnityEngine;
using System.Collections;
public class PrefabSpawner : MonoBehaviour {
public int amountOfPrefabs;
public int maxAmountOfPrefabs;
private int currentSpawnedPrefab;
public float spaceBetweenPrefabs;
private float positionX;
private float positionZ;
private float maxPositionX;
private float maxPositionZ;
private float multipliedPosXZ;
private bool previousSpawnHadZero;
public GameObject prefab;
private GameObject point1;
private GameObject point2;
private GameObject currentSpawn;
private Vector2[] positions;
void Start () {
currentSpawnedPrefab = 0;
previousSpawnHadZero = false;
point1 = gameObject.transform.GetChild (0).gameObject;
point2 = gameObject.transform.GetChild (1).gameObject;
if (point1.transform.position.x > point2.transform.position.x)
maxPositionX = point1.transform.position.x;
else
maxPositionX = point2.transform.position.x;
if (point1.transform.position.z > point2.transform.position.z)
maxPositionZ = point1.transform.position.z;
else
maxPositionZ = point2.transform.position.z;
multipliedPosXZ = maxPositionX * maxPositionZ;
if (multipliedPosXZ < 0)
multipliedPosXZ += multipliedPosXZ + multipliedPosXZ;
maxAmountOfPrefabs = Mathf.FloorToInt (multipliedPosXZ / spaceBetweenPrefabs);
if (amountOfPrefabs > maxAmountOfPrefabs)
amountOfPrefabs = maxAmountOfPrefabs;
point1.GetComponent<MeshRenderer> ().enabled = false;
point2.GetComponent<MeshRenderer> ().enabled = false;
gameObject.GetComponent<MeshRenderer> ().enabled = false;
positions = new Vector2[amountOfPrefabs];
SpawnPrefabs (amountOfPrefabs);
}
void SpawnPrefabs (int amount) {
for (int i = 0; i < amount; i++) {
if(previousSpawnHadZero)
i -= 1;
currentSpawn = (GameObject)Instantiate (prefab);
positionX = GetRandomPositionX ();
positionZ = GetRandomPositionZ ();
currentSpawn.transform.position = new Vector3 (positionX, this.transform.position.y + currentSpawn.transform.localScale.y, positionZ);
currentSpawnedPrefab += 1;
if (positionX == 0 || positionZ == 0) {
previousSpawnHadZero = true;
currentSpawnedPrefab -= 1;
Destroy (currentSpawn);
}
if (positionX != 0 && positionZ != 0) {
previousSpawnHadZero = false;
positionX = 0;
positionZ = 0;
}
}
}
IEnumerator Pause () {
yield return null;
}
float GetRandomPositionX () {
//Finds a random position for the X axis and then checks it and returns either 0 if the position is taken or the position if not
}
float GetRandomPositionZ () {
//Finds a random position for the Z axis and then checks it and returns either 0 if the position is taken or the position if not
}
bool CheckPositionAvailable (float pos, int axis) {
//Checks if the position is available.
}
}
Code is really long to debug but the problem is clearly visible and is from the SpawnPrefabs function. Currently, when you instantiate a prefab, you check if the generated position is 0. If 0, you subtract 1 from the i in the for loop then destroy the instantiated object and then start the for loop again from the current loop-1.
So the combination of Instantiate, Destroy and repeating it over again in the for loop is causing the memory issue.
What to do:
You have to re-write the whole function and this will require modification in your whole code too. Do not instantiate and destroy object in that loop unless when needed.
1.In the Start() function, create one prefab.
2.Make it to be invisible in the scene by disabling its mesh/sprite renderer.
3.Use that prefab in the for loop to check if the generated position is valid. If it is valid, you can now create/instantiate an object in the loop.
This prevents instantiating and destroying objects in the loop when you only create objects when if (positionX != 0 && positionZ != 0) .

Need to decrease spawn wait time interval(float) based on time.time value , Instantiation of two objects should decrease by some proportion

I am writing a small game where objects arrives randomly , I need to implement a logic where I can calculate wait interval for next instantiation as per timer.(As game progresses spawn wait time should decrease).
i am trying to spawn objects in the game those object are having lifetime until next appear , player need to tab fingers as they appear and i am trying to instantiate these fingers faster as game progress
only struggling to get mathematical equation to get value for spawnWait based Time.time . i hope i have written it properly now.
i need equation for graph something like in first Quater(+x,+y) for time.time & spawn wait. although not very much sure.
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class GameController : MonoBehaviour {
// Use this for initialization
public GameObject GoodThumb;
public GameObject BadThumb;
public int max = 22;
public float spanWait = 10.0f;
public static bool gameOver = false;
Vector3 theNewPos = Camera.main.ViewportToWorldPoint(new Vector3(0,0,11));
Quaternion theNewRotation= Quaternion.Euler(0,0,0);
void Start () {
StartCoroutine(spawn());
OnGameStart();
}
void Update () {
Time.deltaTime
}
// Update is called once per frame
void Update () {
// SpawnRandom ();
ChooseFinger ();
}
public int ChooseFinger()
{
return Random.Range (1, 5);
}
void OnGameStart()
{
//Debug.Log ("Game Started");
}
IEnumerator spawn() {
GameObject go;
while(true)
{
int randomLoop = Random.Range(2,5);
for (int i = 0; i < randomLoop; i++)
{
pickRandomFinger();
go = Instantiate(GoodThumb);
go.transform.position = theNewPos;
go.transform.rotation = theNewRotation;
// Debug.Log(theNewPos);
// Debug.Log(theNewRotation);
yield return new WaitForSeconds(spanWait);
}
pickRandomFinger();
go = Instantiate(BadThumb);
go.transform.position = theNewPos;
go.transform.rotation = theNewRotation;
yield return new WaitForSeconds(spanWait);
if (gameOver)
{
break;
}
}
}
public void pickRandomFinger()
{
int FingerNo = ChooseFinger();
Debug.Log (FingerNo);
switch (FingerNo)
{
case 1: // finger1 type bottom good
theNewPos = Camera.main.ViewportToWorldPoint(new Vector3(Random.Range(0.0F,1.0F),1.0F, 11));
theNewRotation = Quaternion.Euler(0,0,180);
break;
case 2: // finger4 type right good
theNewPos = Camera.main.ViewportToWorldPoint(new Vector3(1.0F,Random.Range(0.0F,1.0F), 11));
theNewRotation = Quaternion.Euler(0,0,90);
break;
case 3: // finger2 type top good
theNewPos = Camera.main.ViewportToWorldPoint(new Vector3(Random.Range(0.0F,1.0F),0.0F, 11));
theNewRotation = Quaternion.Euler(0,0,0);
break;
case 4: // finger3 type left good
theNewPos = Camera.main.ViewportToWorldPoint(new Vector3(0.0F,Random.Range(0.0F,1.0F),11));
theNewRotation = Quaternion.Euler(0,0,270);
break;
}
}
}
This questions is incredibly vague,
From what I can gather, you want there to be some sort of inverse correlation between the amount of time the user has been in the game and the time between spawns.
There are a million ways to do this, and the correct answer will be completely dependent on the kind of behavior you want the game to exhibit.
timeBetweenSpawns = minTime + max(defaultTime - (timeInGame*constant),0)
This ( very simple and generalized ) code will give you a very simple inverse relationship between time spent in game and the time between items spawning.
You're going to have to think of your own solution and find what works best for your project.

Categories

Resources