stop instantiating prefab if hit limit but continue if prefabs get destroyed - unity3d

i have a wave system set up and it instantiates a specific amount of enemy prefabs, however say there are supposed to be 100 enemies in total that have to be instantiated on said wave, thats a lot for the player to handle so i want to set a limit (doing it like in COD: Zombies) for how many prefabs are allowed to be active at a time.
IEnumerator SpawnWave (Wave _wave)
{
for (int i = 0; i < _wave.count; i++)
{
if (curZombiesSpawned < maxZombiesSpawned)
{
SpawnEnemy();
Debug.Log("spawnenemy");
}
yield return new WaitForSeconds( 1f/_wave.rate );
}
state = SpawnState.WAITING;
yield break;
}
void SpawnEnemy()
{
Transform _sp = spawnPoints[Random.Range(0, spawnPoints.Length)];
PhotonNetwork.InstantiateRoomObject (enemyObject.name, _sp.position, _sp.rotation);
curZombiesSpawned++;
}
before i instantiate the enemy i want to check if the currently spawned enemies is less than the max amount of enemies spawned at a time.
but lets say on x wave there are supposed to be 50 enemies, but the limit is 24, i only want 24 to spawn, and when i kill x amount of enemies, i want to spawn more enemies till it reaches the limit again. doing that until all 50 are dead, then the next wave starts again
current problem: if the enemy amount on x wave is greater than the limit, it spawns x amount of enemies (spawn the amount of what the limit is set to) but when i kill some (thus destroy the object) they dont keep spawning again.

You can try something like this:
IEnumerator SpawnWave (Wave _wave)
{
int spawnedEnemiesCount = 0;
while(spawnedEnemiesCount < _wave.count)
{
while (curZombiesSpawned < maxZombiesSpawned && spawnedEnemiesCount < _wave.count)
{
SpawnEnemy();
spawnedEnemiesCount++;
Debug.Log("spawnenemy");
}
yield return new WaitForSeconds( 1f/_wave.rate );
}
}

Related

Recursive spawner instantiating way too many gameobjects

I'm trying to make a recursive function that will instantiate a bird at a random interval of time between a min and max value, and then call itself so that the process starts over and a new bird will be instantiated after another random interval. The problem is that currently after the first random interval passes an infinite (close) number of birds are being instantiated all at once. But I don't see where I went wrong. SpawnBird() has been tested and only instantiates one bird when called on its own.
void Start()
{
minBirdWait = 10;
maxBirdWait = 25;
BirdGenerator();
}
void BirdGenerator()
{
float timer = Random.Range(minBirdWait, maxBirdWait);
Invoke("SpawnBird", timer);
BirdGenerator();
}
I ended up turning it into a coroutine and it works now.
IEnumerator BirdGenerator()
{
while(true)
{
float timer = Random.Range(minBirdWait, maxBirdWait);
yield return new WaitForSeconds(timer);
SpawnBird();
}
}

How to control particles directly while preventing the particle system from doing the same?

I have lots of same simple objects that are affecting gameplay, thousands of them! Well, not thousands, but really many. So if I make them GameObjects, the FPS decreases, especially when spawning them. Even with pooling. I should try a different approach.
You know that particle system in Unity3D can render many particles very fast. It also automatically controls particles, emits and removes them. But in my case, positions and lifetimes of objects are managed by game logic, and particle system is not allowed to do anything without my command, even reorder particles.
I am trying to use SetParticles method to control particles. It works in test project, where I use GetParticles first. I can even remove particles setting lifetime to -1, but can't spawn new ones. Also it does not prevent particle system from controlling particles.
I can disable emission so no particles will be created automatically.
I can set particles speed to 0 so they will not move.
I can set lifetime to huge number so they will not be removed.
I have an pool of Particle instances, to avoid unnessessary GC allocations. Objects receive a reference to particle when they are spawned, change it when updated, and set lifetime -1 and return it to pool when deleted. The pool stores this:
private ParticleSystem.Particle [] _unusedParticles;
private int _unusedCount;
private ParticleSystem.Particle [] _array;
_unused array and counter are needed for pooling, and _array stores all particles, used and unused alike, and is used in SetParticles call.
The main disadvantage of this method is that it doesn't work, probably because SetParticles does not create new particles. Also I guess it doesn't do anything with particles draw order, that's why it's poorly suited for bullet hell games where bullet patterns should look nice.
What should I do to properly disable automatic control of particles and properly setup direct control, with spawning and removing?
What you are looking for might be
List<Matrix4x4> matrixes=new List<Matrix4x4>();
for (...)
{
matrixes.Add(Matrix4x4.TRS( position,rotation,scale));
}
Graphics.DrawMeshInstanced(mesh,0,material, matrixes);
On each frame you can just update positions, rotations and scales, and get all the instances rendered on the GPU in one drawcall (pretty darn fast compared to seperate gameobjects). You can render up to 1000 instances in one call using this way
Create an empty GameObject, then add the ParticleSystem as child. Set playOnAwake to true.
Now when you need it, set GameObject.SetActive to true else false.
To get each of them use ParticleSystem.GetParticles, modify them and ParticleSystem.GetParticles.
I hope this is what you are looking for.
ParticleSystem m_System;
ParticleSystem.Particle[] m_Particles;
public float m_Drift = 0.01f;
private void LateUpdate()
{
InitializeIfNeeded();
// GetParticles is allocation free because we reuse the m_Particles buffer between updates
int numParticlesAlive = m_System.GetParticles(m_Particles);
// Change only the particles that are alive
for (int i = 0; i < numParticlesAlive; i++)
{
m_Particles[i].velocity += Vector3.up * m_Drift;
}
// Apply the particle changes to the Particle System
m_System.SetParticles(m_Particles, numParticlesAlive);
}
void InitializeIfNeeded()
{
if (m_System == null)
m_System = GetComponent<ParticleSystem>();
if (m_Particles == null || m_Particles.Length < m_System.main.maxParticles)
m_Particles = new ParticleSystem.Particle[m_System.main.maxParticles];
}
After we created a particle system in editor, we should disable emission and shape, so only core part and renderer stay active.
The most important part is that Simulation Speed must be zero. A particle system will no longer emit, remove or process particles automatically. Only your code now manages them.
I use this class to control particles. Instead of binding a particle to an object, it has API for registering objects, smoke in this case. Also, it stores a temporary array for particles to avoid GC allocations, and particle count, to avoid using particleCount property of the particle system.
In Update, which is called by my game logic, the following happens:
All objects that were despawned by game logic, are removed from list.
If object count is greater than array length, the array is resized.
If object count is greater than live particle count, _particleSystem.Emit call is made. If we call SetParticles without that, no new particles will appear. We must emit them first.
GetParticles is called. Simple, but probably not the most efficient solution, but it preserves particles data. It may be optimized by setting all particle data on array creation and resizing. If you do so, remove GetParticles call and uncomment the line above. Also, your game logic should manage particles even more carefully.
For each object, we let that object change a particle.
Each particle without object should be removed, so their lifetime is set to negative number.
SetParticles updates particles in system.
public class SmokeSystem {
private ParticleSystem _particleSystem;
private List <Smoke> _smoke = new List <Smoke> ();
private ParticleSystem.Particle [] _particles = new ParticleSystem.Particle[256];
private int _particleCount;
public SmokeSystem (ParticleSystem particleSystem) {
_particleSystem = particleSystem;
}
public void AddSmoke (Smoke smoke) => _smoke.Add (smoke);
public void Update () {
_smoke.RemoveAll (e => e.Despawned);
if (_smoke.Count > _particles.Length) {
int newSize = Max (_smoke.Count, 2 * _particles.Length);
Array.Resize (ref _particles, newSize);
}
int count = _smoke.Count;
if (count > _particleCount) {
_particleSystem.Emit (count - _particleCount);
// _particleCount = count;
}
_particleCount = _particleSystem.GetParticles (_particles);
for (int i = 0; i < count; i++) {
_smoke [i].UpdateParticle (ref _particles [i]);
}
for (int i = count; i < _particleCount; i++) {
_particles [i].remainingLifetime = -1;
}
_particleSystem.SetParticles (_particles, _particleCount);
_particleCount = count;
}
}
It does not depend on GPU instancing support so it will work on WebGL.

Unity 2D: How to decrement static int in while loop using deltaTime

In my Player script I set a static int called boost to be used from any script.
I have a few boost pick up coins through out the game, when the player gets one it fills their boost tank by +1
You need a min of 3 boost coins to start boosting in the vehicle. When the player is using boost, the boost will last for about as many seconds as many boost coins collected. Using the boost should decrement the boost tank.
Player Script
public static int boost;
private void OnTriggerEnter2D(Collider2D otherObject)
{
if (otherObject.tag == "Boost")
{
boost++;
GetComponent<AudioSource>().PlaySound(BoostSound, 5.7F);
Destroy(otherObject.gameObject);
}
}
Button call code
public void BoostButton()
{
StartCoroutine("Use_VehicleBoost");
}
Booster code that a button calls.
IEnumerator Use_VehicleBoost()
{
// Check which boost package player picked
int boostLevel = SecurePlayerPrefs.GetInt("BoostLevel");
if (boostLevel == 0)
{
Debug.Log("Boost Level: None");
yield return null;
}
else if (boostLevel == 1)
{
Debug.Log("Boost Level: 1");
float aceleRate = 400, maxFWD = -2500;
while (Player.boost >= 3)
{
vehicleController.GetComponent<CarMovement>().accelerationRate += aceleRate;
vehicleController.GetComponent<CarMovement>().maxFwdSpeed += maxFWD;
// Meant to slowly take one point/ Time second away from boost tank // Problem is here ----->>>
Player.boost = Player.boost - Mathf.RoundToInt(Time.deltaTime);
yield return null;
}
if (Player.boost <= 0)
{
yield return null;
}
}
yield return null;
}
Problem is here in this line
Player.boost = Player.boost - Mathf.RoundToInt(Time.deltaTime);
It is supposed to be decrementing with seconds from Player.boost. For example if player collects 3 boost coins then boost is active when used it will last for 3 seconds before turning off.
Not exactly sure on what to do here. They told me that in the while loop the deltaTime stays at value 0 because its stuck in one frame? Am I supposed to start a timer variable? Thank you.
Not exactly sure on what to do here. They told me that in the while
loop the deltaTime stays at value 0 because its stuck in one frame? Am
I supposed to start a timer variable?
Yes, I did say that but you have fixed the one frame issue with yield return null; which let's the loop to wait one frame therefore giving Time.deltaTime chance to change. Even with that being fixed, there is still 0 issue I told you but because while (Player.boost >= 3) is still true. That is true because Mathf.RoundToInt(Time.deltaTime); is returning zero. You can verify this with Debug.Log(Mathf.RoundToInt(Time.deltaTime)).
If Player.boost type is an int, change it to a float type, to gradually decrease it with Time.deltaTime directly:
while (Player.boost >= 3)
{
vehicleController.GetComponent<CarMovement>().accelerationRate += aceleRate;
vehicleController.GetComponent<CarMovement>().maxFwdSpeed += maxFWD;
// Meant to slowly take one point/ Time second away from boost tank
Player.boost = Player.boost - Time.deltaTime;
//OR Player.boost -= Time.deltaTime;
yield return null;
}
If Player.boost type is an int but you don't want to change it to float, remove the Time.deltaTime as that's used for float values then do the waiting with WaitForSeconds. With this you can subtract one from boost after each WaitForSeconds call.
while (Player.boost >= 3)
{
vehicleController.GetComponent<CarMovement>().accelerationRate += aceleRate;
vehicleController.GetComponent<CarMovement>().maxFwdSpeed += maxFWD;
// Meant to slowly take one point/ Time second away from boost tank
Player.boost = Player.boost - 1;
//OR Player.boost -= 1;
yield return new WaitForSeconds(1f);
}

How to change a sprite to another and then back after 1 second

I'm developing a simple game in Unity2D, in which several monsters eat things that are dragged to them. If the right object is dragged to the monster, the score goes up by one and the monster should make a happy face, otherwise, score goes down and makes a sad face. This is the code I'm using for that (minus the transitions to happy/sad):
if (transform.name.Equals ("yellow")){
if (collinfo.name.Equals ("plastic(Clone)")) {
Debug.Log ("+1");
audio.Play ();
GameSetup.playerScore += 1;
gs.GetComponent<GameSetup>().removeit(aux);
}
else {
Debug.Log ("-1");
audio.Play ();
if (GameSetup.playerScore == 0)
{}
else
{
GameSetup.playerScore -= 1;
}
gs.GetComponent<GameSetup>().removeit(aux);
}
The audio played is just a 'munching' sound.
I want the monster to change sprites to happyFace (via GameObject.GetComponent ().sprite = happyFace), wait one second and then switch back to it's normal sprite, but don't know how to implement that waiting period.
Any and all help would be much appreciated.
This can be implemented several ways, however, I would use a method that returns an IEnumerator like so…
This assumes you have a variable in your script that has a reference to the SpriteRenderer that is attached to the GameObject with this script.
SpriteRenderer sr = GetComponent <SpriteRenderer> ();
I also assume you have an original sprite and the possible sprites to change to as variables too.
public Sprite originalSprite;
public Sprite happyFaceSprite;
public Sprite sadFaceSprite;
public IEnumerator ChangeFace (Sprite changeToSprite)
{
sr.sprite = changeToSprite;
yield return new WaitForSeconds (1.0f);
sr.sprite = originalFaceSprite;
}
You would then call this function with the applicable sprite as the variable.
if (happy)
StartCoroutine (ChangeFace (happyFaceSprite);
else
StartCoroutine (ChangeFace (sadFaceSprite);
Because the ChangeFace method returns an IEnumerator, we must call that function with the StartCoroutine function. The method will run until it reaches the yield return new WaitForSeconds (1.0f) function, then wait for 1.0f seconds and then resume where it stopped last time.
Understand?
Note
I haven't tested this but I don't see why it wouldn't work.
Put a floating point variable in your monster controller, call it happyTimer or something. It should start at zero.
Then in your Update function you should check happyTimer is above zero. If it is, then subtract Time.deltaTime and check again. If happyTimer is zero or below for the second check, then call your function that resets the sprite.
When you set the sprite to "happy face", also set happyTimer = 1. This will begin the countdown starting with the next Update call.
The relevant part of Update would look like this:
if(happyTimer > 0) {
happyTimer -= Time.deltaTime;
if(happyTimer <= 0) {
resetSprite();
}
}

How to spawn a 'boss' after a certain amount of time has passed and then kill that 'boss'?

I only have basic ability with Unity and cannot figure out the answer to what looks like a simple question. I have three enemies in my level, and would like to spawn a 'boss' once they're defeated. Here is my original spawn code so far:
//here are the 3 enemys we want to spawn (the orb, asteroid, and ship)
var enemy1:GameObject;
var enemy2:GameObject;
var enemy3:GameObject;
//here is the minimum and maximum height we want them to spawn. it can be changed in the inspector
var maxHeight:float = 6.0;
var minHeight:float = -6.0;
//here are private variables we use for the spawner
private var counter:float = 0.0;
private var player:GameObject;
private var randomChoice:int;
private var spawnRate:float = 2.0;
function Update () {
//counter counts based on time here so the spawner can spawn based on time
counter += Time.deltaTime;
//here we make the spawner slowly spawn faster until it is spawning 3 a second.
if(spawnRate > 0.33){
spawnRate -= Time.deltaTime/80;
}
//if the counter is higher than the spawnrate number, it'll spawn an object
if(counter > spawnRate){
//before one is spawned we have to decide at random which one to spawn
randomChoice = Random.Range(1,6);
//asteroids take up 3 out of the 5 possible spawns, making it spawn the most often
if(randomChoice >= 1 && randomChoice <= 3){
Instantiate(enemy1, Vector3(transform.position.x,0,Random.Range(minHeight,maxHeight)), Quaternion.Euler(-90,0,0));
}
//if the random number ends up being 4, it'll spawn the orb, set as enemy2
if(randomChoice == 4){
Instantiate(enemy2, Vector3(transform.position.x,0,Random.Range(minHeight/3,maxHeight/3)), Quaternion.Euler(-90,0,0));
}
//if the random number ends up being 5, it'll spawn the ship, set as enemy3
if(randomChoice == 5){
Instantiate(enemy3, Vector3(transform.position.x,0,Random.Range(minHeight,maxHeight)), Quaternion.Euler(-90,90,0));
}
counter = 0.0;
}
}
The code I experimented with got really messed up and disorganized. This script spawns all my enemies and is attached to my level scene. Please point me in the right directions as to how to add the boss part to the script.
Thanks in advance
The boss should spawn the moment all three enemies all defeated, is that right?
In that case, you already have the condition for when the boss will spawn.
Make a variable like private var defeatedEnemies:int = 0; and increment it the moment an enemy dies.
Then you just need to add that condition in your update() function:
if (defeatedEnemies == 3){
// spawn the boss
}