The code is here:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SpawnManager : MonoBehaviour
{
public GameObject[] animalprefabs;
public float spawninterval = 2.5f;
public float spawnDelay = 2.0f;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
InvokeRepeating("spawnRandomAnimal",spawnDelay, spawninterval);
}
void spawnRandomAnimal()
{
int animalIndex = Random.Range(0, animalprefabs.Length);
int SpawnIndex = 20;
Vector3 spawnPosition = new Vector3(Random.Range(-SpawnIndex, SpawnIndex), 0, 25);
Instantiate(animalprefabs[animalIndex], spawnPosition ,animalprefabs[animalIndex].transform.rotation);
}
}
I want to instantiate the prefab after certain time interval randomly but somehow tonnes of prefabs are getting generated .I want one prefab to be instantiated at a random place after the time interval ... Someone plzzzz help
You are re-calling invokeRepeating every single time Update() is called. Everytime it gets called it adds another task to repeat.
Move it to start to fix your issue.
void Start()
{
InvokeRepeating("spawnRandomAnimal",spawnDelay, spawninterval);
}
you are using invokerepeating() function in update(). that's why too many prefabs are generated. remove invokerepeating() from update() and try it in start() or onenable().
Related
This question already has answers here:
How to make the script wait/sleep in a simple way in unity
(7 answers)
Closed 4 months ago.
I'm a new C# programmer here in the early stages of creating a project in Unity where you play as a microbe that needs to eat blue food pellets to grow and survive. I've got the blue food pellets to spawn randomly across the map but I want to add a delay because too much is spawning at once. This is what I've attempted to do so far. Any help would be appreciated!
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Threading.Tasks;
public class Spawner : MonoBehaviour
{
public GameObject food;
public async void Wait(float duration)
{
Vector3 randomSpawnPosition = new Vector3(Random.Range(-50, 50), 1, Random.Range(-50, 50));
Instantiate(food, randomSpawnPosition, Quaternion.identity);
await Task.Delay((int)duration * 1000);
}
// Update is called once per frame
void Update()
{
async void Wait(float duration);
}
}
What I've tried:
Putting the delay function in the update function. The program just gets confused and thinks I'm trying to call the function.
Calling the function after combining all my code into the one function, the program rejects this.
Like the default code snippet says, Update runs every frame. Using Task.Delay to delay events would still spawn objects with the same frequency, they would only start spawning delayed.
The typical way to do this in Unity is to use a coroutine.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Spawner : MonoBehaviour
{
[SerializeField] GameObject food;
protected void OnEnable()
{
StartCoroutine(SpawnFoodRoutine());
}
IEnumerator SpawnFoodRoutine()
{
while(enabled)
{
SpawnFood();
var waitTime = Random.Range(1f, 5f);
yield return new WaitForSeconds(waitTime);
}
}
void SpawnFood()
{
Vector3 randomSpawnPosition = new Vector3(
Random.Range(-50f, 50f),
1f,
Random.Range(-50f, 50f));
Instantiate(food, randomSpawnPosition, Quaternion.identity);
}
}
I have made this prefab called “pellet”, then I have created a new gameObject called “SpawnManager” and, finally, added this script to SpawnManager:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SpawnPellet : MonoBehaviour
{
public GameObject prefab;
public bool canSpawnNewPellet = true;
public float delay;
void Update(){
if(canSpawnNewPellet){
Invoke("SpawnNewPellet", delay);
canSpawnNewPellet = false;
}
}
void SpawnNewPellet()
{
GameObject instance = Instantiate(prefab);
instance.transform.position = new Vector3(Random.Range(0,10), Random.Range(0,10), 0);
canSpawnNewPellet = true;
}
}
The I have dragged and dropped/edited values on the fields in the inspector (Prefab, canSpawnNewPellet and Delay). Those are public fields inside the script, so you can populate them drag and dropping directly from your assets folder.
Hit play and you have a new spawn every X seconds where X is the value of delay in seconds.
Screenshot of the game executing after 21 seconds.
What does it do?
Every frame it evaluates if can spawn a new pellet (bool canSpawnNewPellet). If it can, then it starts an invocation of another method with X seconds (and mark the bool as false since we don’t want to call more invocations during de instantiation of our first sample).
For more references: https://docs.unity3d.com/ScriptReference/MonoBehaviour.Invoke.html
Edited: typo.
I have a hit points bar which is supposed to fill up by 0.1 each time the ball object collides with the goodOrb object. However the bar fills up by 0.1 only on the first collision. It does not move when the ball collides again.
I tried Debug.Log the value of the variable hitPoints which stores the current amount the bar is filled by. This variable is initialized to 0, I have another variable called increase which is set to 0.1, each time the two objects collide variable increase is supposed to be added to hitpoints. But this is happening only once. Debug.Log(hitpoints) shows 0.1 only once.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class goodOrb : MonoBehaviour
{
public int maxHealth = 100;
public int currentHealth;
public Image barImage;
private Rigidbody2D rb;
public float increase = 0.1f; //amount to increase bar each time a collision happens
public float hitPoints = 0.0f; // current amount the bar is filled by
// public AudioSource collisionSound;
int scoreValue= 5 ;
// Start is called before the first frame update
public void Start()
{
GameObject Filler = GameObject.Find("Filler");
Image barImage = Filler.GetComponent<Image>();
}
void OnTriggerEnter2D(Collider2D other)
{
ParticleSystem ps = GetComponent<ParticleSystem>();
if(other.tag=="Ball")
{
ps.Play();
HitPoints();
scoreManager.score += scoreValue;
// barImage.fillAmount = (float)currentHealth / (float)maxHealth;
// collisionSound.Play();
}
}
void Update()
{
}
// Update is called once per frame
void HitPoints()
{
GameObject Filler = GameObject.Find("Filler");
Image barImage = Filler.GetComponent<Image>();
hitPoints = hitPoints + increase;
barImage.fillAmount = hitPoints;
//print(hitPoints);
Debug.Log(hitPoints);
}
}
I expect increase to be added to hitpoints each time the collision happens and the hitpoints bar to be filled.
1- make sure the ball collides with same orb object every time.
2- make sure one collider isTrigger is true.
3- make sure trigger function is called every time ball collides.
by doing this
void OnTriggerEnter2D(Collider2D other){
Debug.Log (other.tag);
}
I'm making a spawn system for my game, that spawns enemies at random positions, but Unity is telling me that I'm not checking if the object is already destroyed. I've tried to solve it with some other topics here but I couldn't do it.
Here is my code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemySpawn : MonoBehaviour {
public GameObject Enemy2Spawn;
public float maxWidth;
public float minWidth;
public float rateSpwan;
private float currentRSpawn = 2.0f;
public int maxInimigo;
public int Inimigos = 0;
void Start()
{
transform.position = new Vector3(0, 6.03f, 0);
}
// Update is called once per frame
void Update()
{
if (currentRSpawn > Time.time & Inimigos<=maxInimigo)
{
transform.position = new Vector3(Random.Range(minWidth, maxWidth), transform.position.y, transform.position.z);
Instantiate(Enemy2Spawn);
currentRSpawn = currentRSpawn + rateSpwan;
Inimigos++;
}
if (Enemy2Spawn. == null)
{
Destroy(this.gameObject);
}
}
}
The error I'm getting is:
"The object of type 'GameObject' has been already destroyed but you are still trying to access it. Your script should either check if it is null or you should not destroy the object"
In the Update function, you are checking if the GameObject is null, which means that it does not exist, then you are using Destroy() to destroy that object that does not exist. Instead, you will want to check if the object exists in the if statement that spawns the enemies. Add that to the if statement, like this, and you should be good.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemySpawn : MonoBehaviour {
public GameObject Enemy2Spawn;
public float maxWidth;
public float minWidth;
public float rateSpwan;
private float currentRSpawn = 2.0f;
public int maxInimigo;
public int Inimigos = 0;
void Start()
{
transform.position = new Vector3(0, 6.03f, 0);
}
// Update is called once per frame
void Update()
{
if (currentRSpawn > Time.time && Inimigos <= maxInimigo && Enemy2Spawn == null)
{
transform.position = new Vector3(Random.Range(minWidth, maxWidth), transform.position.y, transform.position.z);
Instantiate(Enemy2Spawn);
currentRSpawn = currentRSpawn + rateSpwan;
Inimigos++;
}
}
}
Well there are a couple things that seem off on this script:
before that: For the error if you set Enemy2Spawn to an object in the scene and it gets destroyed then the value is null then when it tries to Instantiate again, which causes the error (could put the condition "Enemy2Spawn != null" in the first if statement as a fix)
"transform.position = new Vector3(Random.Range(minWidth, maxWidth), transform.position.y, transform.position.z);"
This line changes the object attached to the script, it's position to be exact, which won't affect the position of the enemy you are going to spawn.
"Instantiate(Enemy2Spawn);" makes a clone of the variable's value. The clone inherits all values of the original, so position will be what the original's position is.
Unless you didn't set the value of "Enemy2Spawn" it would not become null, unless a different script changes the value and that it is a prefab. So, "if (Enemy2Spawn == null)" assuming the value is set, does not change else where and is set as a prefab in this case then the statement is never true. If the statement ever became true the line inside "Destroy(this.gameObject);" destroys the object that the script is attached to so it seems counter productive to me (to destroy the spawner) but if it's a measure to prevent error it should be in the Start or Awake function or if setting it with a different script just destroy the script instead of setting the variable to null (which i really doubt though).
Here is a changed script that should fix the issues i stated and fit your needs
using System.Collections;
using System.Collection.Generic;
using UnityEngine;
public class EnemySpawn : MonoBehaviour {
public GameObject Enemy2Spawn;
public float maxWidth;
public float minWidth;
public float rateSpwan;
private float currentRSpawn = 2.0f;
public int maxInimigo;
public int Inimigos = 0;
void Start()
{
transform.position = new Vector3(0, 6.03f, 0);
}
// Update is called once per frame
void Update()
{
if (currentRSpawn > Time.time & Inimigos<=maxInimigo)
{
Instantiate(Enemy2Spawn, new Vector3(Random.Range(minWidth, maxWidth), transform.position.y, transform.position.z), Quaternion.identity);
currentRSpawn = currentRSpawn + rateSpwan;
Inimigos++;
}
}
}
Different reason for the error, the given script may spit out the error but i don't see how it'd cause it so it would have to be caused by a different script that causes the object with the script attached to be destroyed just before executing the update function.
Also, the scripting API suggests calling Destroy like the following if using JS/unityscript
UnityEngine.Object.Destroy(this.gameObject);
I hope this helps, if not then more information is needed
I am trying to create a ball hitting game in the baseball format. I create a ball as a prefab. I want to push the ball to the main scene within a certain period of time.
For example; when the first ball is in the scene, the second ball will spawn after 5-6 seconds, then the third, fourth etc. I am the beginner level of Unity and I am not good at C#. I am not sure whether I am using the true functions such as Instantiate. Here is my script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Ball : MonoBehaviour {
public float RotateSpeed = 45; //The ball rotates around its own axis
public float BallSpeed = 0.2f;
public GameObject[] prefab;
public Rigidbody2D rb2D;
void Start() {
rb2D = GetComponent<Rigidbody2D>(); //Get component attached to gameobject
Spawn ();
}
void FixedUpdate() {
rb2D.MoveRotation(rb2D.rotation + RotateSpeed * Time.fixedDeltaTime); //The ball rotates around its own axis
rb2D.AddForce(Vector2.left * BallSpeed);
InvokeRepeating("Spawn", 2.0f, 2.0f);
}
public void Spawn ()
{
int prefab_num = Random.Range(0,3);
Instantiate(prefab[prefab_num]);
}
}
After I apply this script, the result is not what I want.
Add InvokeRepeating("Spawn", 2.0f, 2.0f); to the Start not the FixedUpdate.
InvokeRepeating invokes the method methodName in time seconds, then repeatedly every repeatRate seconds.
You can check the documentation here.
Use Coroutines
private IEnumerator SpawnBall() {
while(true) {
Instantiate(baseball);
yield return new WaitForSeconds(5);
}
}
Which can then be started with StartCoroutine() and terminated in one of three ways:
internally by breaking out of the while loop with break (the function would then have no more lines to execute and exit)
internally by yield break
externally by calling StopCoroutine() on a reference to the coroutine
Alternative to the other answers: Just use a countdown. This sometimes gives you more control
// Set your offset here (in seconds)
float timeoutDuration = 2;
float timeout = 2;
void Update()
{
if(timeout > 0)
{
// Reduces the timeout by the time passed since the last frame
timeout -= Time.deltaTime;
// return to not execute any code after that
return;
}
// this is reached when timeout gets <= 0
// Spawn object once
Spawn();
// Reset timer
timeout = timeoutDuration;
}
I updated my script by considering your feedbacks and it works like a charm. Thanks to all!
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System.Threading;
public class Ball : MonoBehaviour {
public float RotateSpeed = 45; //The ball rotates around its own axis
public float BallSpeed = 0.2f;
public GameObject BaseBall;
public Transform BallLocation;
public Rigidbody2D Ball2D;
void Start() {
Ball2D = GetComponent<Rigidbody2D>(); //Get component attached to gameobject
InvokeRepeating("Spawn", 5.0f, 150f);
}
void FixedUpdate() {
Ball2D.MoveRotation(Ball2D.rotation + RotateSpeed * Time.fixedDeltaTime); //The ball rotates around its own axis
Ball2D.AddForce(Vector2.left * BallSpeed);
}
public void Spawn ()
{
Instantiate (BaseBall, BallLocation.position, BallLocation.rotation);
}
}
I want to start an animation when my player enters a specific distance to an object, but my animation isn't starting. Any suggestion as to why it isn't starting?
Here is the code that I have so far:
using UnityEngine;
using System.Collections;
public class MoveTo : MonoBehaviour {
public Transform Player;
public Transform goal;
public Animator ani;
public Animator ani2;
// Use this for initialization
void Start ()
{
ani.Stop (); // this stop function is working accurate
ani2.Stop ();
}
void Update()
{
float dist = Vector3.Distance (Player.position, transform.position);
if (dist < 5)
{
ani.Play("Horse_Walk");// this is not working (Horse_Walk) is a state name
ani2.Play("Horse_Run");
pstart();
}
}
void pstart(){
NavMeshAgent agent=GetComponent<NavMeshAgent>();
agent.destination = goal.position;
}
}
There can be several reason including (1) wrong string (2) string name not matched and finally which seems more appropriate reason is that you playing animation again. you should need be call it with bool
bool isPlayAnim =true;
void Update()
{
float dist = Vector3.Distance (Player.position, transform.position);
if (dist < 5 && isPlayAnim)
{
isPlayAnim = false;//again true it on you specfic event
ani.Play("Horse_Walk");// this is not working (Horse_Walk) is a state name
ani2.Play("Horse_Run");
pstart();
}
}
isPlayAnim bool you can make and use to play animation only one time and again true it in specific event.
Or
Alternatively you need to use collider and its event to run this update code. I guess it is best way to do this job .