how can I set an amount of game objects to spawn? - c#

here is my code and i have no idea how to work it up. im a beginner in c# and this is for our project.
the project is about a balloon popper and i am having trouble to set an amount of balloons to spawn.
I am planning to set the amount of balloons to spawn at 5, 10 even 20 and after that, the spawning will stop.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SpawnScript: MonoBehaviour
{
public Transform[] spawnPoints;
public GameObject[] balloons;
public float spawnTime = 0f;
float spawnTimeLeft = 0f;
// Start is called before the first frame update
void Update()
{
if (spawnTimeLeft >= spawnTime)
{
int randBalloon = Random.Range(0, balloons.Length);
int randSpawnPoint = Random.Range(0, spawnPoints.Length);
Instantiate(balloons[randBalloon], spawnPoints[randSpawnPoint].position, transform.rotation);
spawnTimeLeft = 0f;
}
else
{
spawnTimeLeft = spawnTimeLeft + Time.deltaTime;
}
}
}

Add a variable to the class for the maximum amount. Have done this as public so you can set it in the inspector to the amount you wish.
Also add a counter variable and set it to 0. This is private as nothing but this class needs access to it. Do serialize this field.
public int spawnAmountMax = 5;
[SerializeField]
private int spawned = 0;
Start the Update() with the next check, return from the method when it evaluates to true.
if (spawned >= spawnAmountMax)
return;
Than after instantiating the GameObject, increase the variable:
spawned++;
The adjusted code
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using static System.Windows.Forms.VisualStyles.VisualStyleElement.TaskbarClock;
public class SpawnScript : MonoBehaviour
{
public int spawnAmountMax = 5;
private int spawned = 0;
public Transform[] spawnPoints;
public GameObject[] balloons;
public float spawnTime = 0f;
float spawnTimeLeft = 0f;
// Start is called before the first frame update
void Update()
{
if (spawned >= spawnAmountMax)
return;
if (spawnTimeLeft >= spawnTime)
{
int randBalloon = Random.Range(0, balloons.Length);
int randSpawnPoint = Random.Range(0, spawnPoints.Length);
Instantiate(balloons[randBalloon], spawnPoints[randSpawnPoint].position, transform.rotation);
spawnTimeLeft = 0f;
spawned++;
} else
{
spawnTimeLeft = spawnTimeLeft + Time.deltaTime;
}
}
}

Try this code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SpawnScript: MonoBehaviour
{
public Transform[] spawnPoints;
public GameObject[] balloons;
public float spawnTime = 5.0f;
public float maxSpawnTime = 20.0f;
private void Start()
{
Invoke(nameof(Spawn), spawnTime); //This will start the spawning process after the initially set spawnTime, so first this will wait for 5 seconds.
}
private void Spawn()
{
int randBalloon = Random.Range(0, balloons.Length);
int randSpawnPoint = Random.Range(0, spawnPoints.Length);
Instantiate(balloons[randBalloon], spawnPoints[randSpawnPoint].position, transform.rotation);
spawnTime *= 2; //doubles the spawnTime, according to your request, so the second time this will be 10, and the third time, this will be 20 seconds
if (spawnTime <= maxSpawnTime) //check if we reached or not the maximum spawn time
{
Invoke(nameof(Spawn), spawnTime); //if we not yet reached the maximum spawn time, it will start to wait and spawn one more balloon again.
}
}
}

since you're doing this in you update, you can add a counter after your Instantiate call.
Instantiate(balloons[randBalloon], spawnPoints[randSpawnPoint].position, transform.rotation);
counter++;
And then, nest your if statement inside another if checking for the counter.
if(counter < amountToSpawn)
{
//your code here
}
For me personally, I would make a Coroutine method with delay that will have a for loop inside, rather than doing this in update.
Sample:
private IEnumerator SpawnObjects(int spawnCount)
{
for(int i = 0; i < spawnCount; i++)
{
//Instantiate here
yield return new WaitForSeconds(5);
}
}
To call the method:
StartCoroutine(SpawnObjects(10));

Related

Why the transform is rotating 360 degrees even if I limit the rotation randomly between specific degrees ranges?

The guns is rotating 360 degrees and not between -40 and 0
This screenshot is the default before running the game the rotation on x is 0 :
and this screenshot is after I changed the guns x rotation value to -40
I want the guns to rotate up down between 0 and -40 but when I'm running the game the guns rotating 360 degrees.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MissleLauncherRotation : MonoBehaviour
{
public Transform body;
public Transform guns;
public Vector3 spinAxisBody;
public Vector3 spinAxisGuns;
public float timeToSpin = 5f;
public float spinSpeed = 20f;
public bool randomSpin = false;
private void Start()
{
StartCoroutine("Spin");
}
private void Update()
{
}
IEnumerator Spin()
{
float spinTimer;
while (true)
{
if (randomSpin == true)
{
spinAxisBody = new Vector3(0,Random.Range(-180, 180),0);
spinAxisGuns = new Vector3(Random.Range(-40, 0), 0, 0);
}
spinTimer = timeToSpin;
while (spinTimer > 0f)
{
body.transform.Rotate(spinAxisBody, Time.deltaTime * spinSpeed);
guns.Rotate(spinAxisGuns, Time.deltaTime * spinSpeed);
spinTimer -= Time.deltaTime;
yield return null;
}
}
}
}
I tried this :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MissleLauncherRotation : MonoBehaviour
{
public Transform body;
public Transform guns;
public Vector3 spinAxisBody;
public Vector3 spinAxisGuns;
public float timeToSpin = 5f;
public float spinSpeed = 20f;
public bool randomSpin = false;
Quaternion rot;
private void Start()
{
rot = guns.rotation;
}
private void Update()
{
guns.Rotate(guns.up, (spinSpeed * Time.deltaTime));
var rot = guns.rotation.eulerAngles;
rot = new Vector3
(
guns.eulerAngles.x,
Mathf.Clamp(rot.x, -40, 0),
guns.eulerAngles.z
);
guns.rotation = Quaternion.Euler(rot);
}
}
The only thing is working now is the clamping but the clamping is not only on the x and I want to use coroutine or somehow to make that it will rotate ever x seconds.
I did it this way to test the clamping if it's working at all now how do I use it with my first code ?
This script is attached to empty gameobject :
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class DronesManager : MonoBehaviour
{
public Transform dronesUnchild;
private List<GameObject> drones = new List<GameObject>();
private static System.Random rnd = new System.Random();
// Start is called before the first frame update
void Start()
{
StartCoroutine(MoveDrone());
}
// Update is called once per frame
void Update()
{
}
private IEnumerator MoveDrone()
{
// same as you did:
var drones = GameObject.FindGameObjectsWithTag("Drone").ToList();
while (drones.Count > 0)
{
// pick one at random, get it
int index = Random.Range(0, drones.Count);
var drone = drones[index];
// remove it from list
drones.RemoveAt(index);
// TODO: might want to check/guard if drone == null ... this guards against it
// being Destroy()ed and yet still lying around in our list marked as "dead"
// simplified your get-component-and-go-if-not-already-going code here
var droneControl = drone.GetComponent<DroneControl>();
if (droneControl.go == false)
{
droneControl.movingSpeed = 5f;
droneControl.go = true;
drone.transform.parent = dronesUnchild;
}
// wait
yield return new WaitForSeconds(0.3f);
}
}
}
And this script is attached to each drone object :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DroneControl : MonoBehaviour
{
public float movingSpeed;
public bool go = false;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if(go)
{
transform.position -= transform.forward * movingSpeed * Time.deltaTime;
}
}
}
Maybe one of this scripts making the problem that it's freezing when trying to change values of variables in the script MissleLauncherRotation ?
As already mentioned Rotate adds a rotation to the current one.
I would rather precalculate the target rotation and use e.g.
public Transform body;
public Transform guns;
public Vector3 spinAxisBody;
public Vector3 spinAxisGuns;
public float timeToSpin = 5f;
public bool randomSpin = false;
// Yes, if you make Start return IEnumerator it is automatically run as a
// Coroutine by Unity without the need for extra code
IEnumerator Start()
{
while (true)
{
// This is the more convenient way of checking for a bool == true
if (randomSpin)
{
spinAxisBody = new Vector3(0,Random.Range(-180, 180),0);
spinAxisGuns = new Vector3(Random.Range(-40, 0), 0, 0);
}
else
{
// whatever you want to happen otherwise
}
// Pre cache/calculate the start and end rotations
// Rotate per default uses the LOCAL space so we will do the same and use localRotation
// But instead of adding to the existing rotation we will use it as a complete new rotation
var startBodyRotation = body.localRotation;
var targetBodyRotation = Quaternion.Euler(spinAxisBody);
var startGunsRotation = guns.localRotation;
var targetGunsRotation = Quaternion.Euler(spinAxisGuns);
// Basically same as the while loop but I prefer to use a for loop so I can't forget to increase the timer
// In my opinion this is simply better readable
for(var spinTimer = 0f; spinTimer < timeToSpin; spinTimer += Time.deltaTime)
{
// Factor linear moving from 0 to 1 within timeToSpin seconds
var factor = spinTimer / timeToSpin;
// optional add ease-in and -out
//factor = Mathf.SmoothStep(0, 1, factor);
// Interpolate linear (or with ease-in and -out) between the pre cached start and end rotations
body.transform.localRotation = Quaternion.Lerp(startBodyRotation, targetBodyRotation, factor);
guns.localRotation = Quaternion.Lerp(startGunsRotation, targetGunsRotation, factor);
yield return null;
}
// Just to be sure to end up with clean values
body.transform.localRotation = targetBodyRotation;
guns.localRotation = targetGunsRotation;
yield return null;
}
}

How to stop Timer when count equals 12

I am working through the unity tutorials and I was wondering how to stop a timer when you hit 12 of count.
I currently have two scripts I am using.
PlayerController
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
using TMPro;
public class PlayerController : MonoBehaviour
{
public float speed;
public TextMeshProUGUI countText;
public GameObject winTextObject;
private float movementX;
private float movementY;
private Rigidbody rb;
private int count;
// At the start of the game..
void Start()
{
rb = GetComponent<Rigidbody>();
count = 0;
SetCountText();
winTextObject.SetActive(false);
}
void FixedUpdate()
{
// Create a Vector3 variable, and assign X and Z to feature the horizontal and vertical float variables above
Vector3 movement = new Vector3(movementX, 0.0f, movementY);
rb.AddForce(movement * speed);
}
void OnTriggerEnter(Collider other)
{
// ..and if the GameObject you intersect has the tag 'Pick Up' assigned to it..
if (other.gameObject.CompareTag("Pickup"))
{
other.gameObject.SetActive(false);
// Add one to the score variable 'count'
count = count + 1;
// Run the 'SetCountText()' function (see below)
SetCountText();
}
if (other.gameObject.CompareTag("SpeedPickup"))
{
other.gameObject.SetActive(false);
speed = speed + 25;
count = count + 1;
}
}
void OnMove(InputValue value)
{
Vector2 v = value.Get<Vector2>();
movementX = v.x;
movementY = v.y;
}
void SetCountText()
{
countText.text = "Count: " + count.ToString();
if (count >= 12)
{
winTextObject.SetActive(true);
}
}
}
And a second script Timer.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
public class Timer : MonoBehaviour
{
[SerializeField]
private TextMeshProUGUI timerUILabel; //drag UI Text object here via Inspector
private float t_offset = 0f; // set to nothing, can be set to an offset if needed.
private int t_minutes;
private int t_seconds;
private int t_milliseconds;
private bool finished = false;
private void Update()
{
float t = Time.time - t_offset;
//Debug.Log("currentTime in seconds = " + t);
t_minutes = ((int)t / 60); // t(seconds) / 60 = total minutes
t_seconds = ((int)t % 60); // t(seconds) % 60 = remaining seconds
t_milliseconds = ((int)(t * 100)) % 100; // (total seconds * 1000) % 1000 = remaining milliseconds
//display the text in a 00:00:00 format
timerUILabel.text = string.Format("{0:00}:{1:00}:{2:00}", t_minutes, t_seconds, t_milliseconds);
}
}
My questions are
Do I need to import the Timer.cs script into the PlayerController to get this functionality to work? If so how? (is it as simple as using a using statement?)
I am thinking I need to put in an additional conditional to change the win condition from false to true. Is that the right path?
Thanks for all your help!
Why not use a coroutine?
private int _time;
private Coroutine _cr;
void Start ()
{
//Call this to start it and save coroutine to a variable
_cr = StartCoroutine(Timer());
//Then to stop it call:
//StopCoroutine(_cr);
//And read the value of _time to get seconds since start.
}
private IEnumerator Timer ()
{
yield return new WaitForSeconds(1);
_time++;
_cr = StartCoroutine(Timer());
}

Start/Update variable scoping

I have a SceneController that's supposed to initialize a set of empty GameObject spawners, each working together at the same rhythm. The RockSpawners receive an array of time delays and wait the X seconds between spawning another rock.
I set the _nextSpawn = float.maxValue when the spawners start and plan to overwrite this after "Initializing" them (my own method), however even though my debug logs say I've overwritten my _nextSpawn value while initializing, the update loop is still reporting float.maxValue and nothing ends up spawning because _timeSinceLastSpawn hasn't exceeded float.maxValue seconds.
Is there something I'm missing with the scope of my _nextSpawn variables? It doesn't seem to be a "this" vs "local" issue, at least at first glance.
Debug output: 0 0 3 3. 0's stay the same, 3's will vary based on rng.
SceneController.cs:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SceneController : MonoBehaviour {
[SerializeField] private GameObject _rockSpawnerPrefab;
public int numRocks = 6;
public int minSpawnDelaySec = 1;
public int maxSpawnDelaySec = 3;
private bool spawnersInitialized = false;
void Start () {
InitializeSpawners();
}
void Update () {
}
void InitializeSpawners() {
float[] pattern = new float[numRocks];
for (int i = 0; i < numRocks; i++) {
// Generate delays at half second increments within bounds
float delay = Mathf.Floor(Random.value * ((float)(maxSpawnDelaySec + 0.5f - minSpawnDelaySec) / 0.5f));
delay = delay * 0.5f + minSpawnDelaySec;
pattern[i] = delay;
}
GameObject spawner = Instantiate(_rockSpawnerPrefab) as GameObject;
spawner.transform.position = new Vector3(0, 4, 0);
RockSpawner rockSpawnerScript = spawner.GetComponent<RockSpawner>();
rockSpawnerScript.Initialize(pattern);
}
}
RockSpawner.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RockSpawner : MonoBehaviour {
[SerializeField] private GameObject _rockPrefab;
public float minSpawnDelay = 3f;
public float maxSpawnDelay = 6f;
private float[] _pattern;
private int _currentPattern;
private float _timeSinceLastSpawn;
private float _nextSpawn;
void Start () {
_currentPattern = -1;
_nextSpawn = float.MaxValue;
}
void Update () {
if (_pattern == null) return;
_timeSinceLastSpawn += Time.deltaTime;
if (_timeSinceLastSpawn > _nextSpawn) {
GameObject rock = Instantiate(_rockPrefab) as GameObject;
rock.transform.position = transform.position;
NextTimer();
}
}
public void Initialize(float[] pattern) {
_pattern = pattern;
NextTimer();
}
private void NextTimer() {
_timeSinceLastSpawn = 0;
_currentPattern += 1;
Debug.Log(_nextSpawn);
Debug.Log(this._nextSpawn);
this._nextSpawn = _pattern[_currentPattern];
Debug.Log(_nextSpawn);
Debug.Log(this._nextSpawn);
}
}
It's not about scoping, it's about call order. When you create a GameObject its Start method is called on the frame it's enabled, not when the object is created. So your code will call Initialize first, then Start which overwrites the values.
Remove the code in Start and handle everything in Initialize and it should work as you want.

Set delay time to spawn object

I have the SpawnScript (Below)
I want to create a function within the SPAWN, so I can put the minimum and maximum delay time that an object can be created by the inspector.
using System.Collections;
using System.Collections.Generic;
public class SpawnController : MonoBehaviour
{
public float maxWidth;
public float minWidth;
public float minTime;
public float maxTime;
public float rateSpawn;
private float currentRateSpawn;
public GameObject tubePrefab;
public int maxSpawnTubes;
public List<GameObject> tubes;
// Use this for initialization
void Start ()
{
for (int i = 0; i < maxSpawnTubes; i++) {
GameObject tempTube = Instantiate (tubePrefab) as GameObject;
tubes.Add (tempTube);
tempTube.SetActive (false);
}
currentRateSpawn = rateSpawn;
}
// Update is called once per frame
void Update ()
{
currentRateSpawn += Time.deltaTime;
if (currentRateSpawn > rateSpawn) {
currentRateSpawn = 0;
Spawn ();
}
}
private void Spawn ()
{
float randWitdh = Random.Range (minWidth, maxWidth);
GameObject tempTube = null;
for (int i = 0; i < maxSpawnTubes; i++) {
if (tubes [i].activeSelf == false) {
tempTube = tubes [i];
break;
}
}
if (tempTube != null)
tempTube.transform.position = new Vector3 (randWitdh, transform.position.y, transform.position.z);
tempTube.SetActive (true);
}
}
you could use Time.realtimeSinceStartup for timestamps - this kinda would fit the way you do it atm.
or you use coroutines which is very unity and one better learns to use them early than late.
or you use invoke which probably is the shortest way of doing it.
http://docs.unity3d.com/ScriptReference/Time-realtimeSinceStartup.html http://docs.unity3d.com/Manual/Coroutines.html
http://docs.unity3d.com/ScriptReference/MonoBehaviour.Invoke.html
edit:
well actually you also could just rateSpawn = Random.Range(minTime, maxTime); inside the if statement in update, this would fit your current approach most.
InvokeRepeating Method is the way to deal with code repetation. You can call your Spawn method with invoke repeating inside of Start event and specify time according to your choice as invoke repeating define:
public void InvokeRepeating(string methodName, float time, float repeatRate);
Invokes the method methodName in time seconds, then repeatedly every
repeatRate seconds.
something like this edit require in your script:
void Start(){
InvokeRepeating("Spawn",2, 0.3F);
}

How to spawn enemy at random intervals?

I am using EnemyScript to move the enemy towards the player and killing the player, but I'm unable to spawn it randomly in code. I am currently spawning it directly through screen by placing the prefab on the scene.
Here is my EnemyScript
using UnityEngine;
using System.Collections;
public class EnemyScript : MonoBehaviour {
public Transform target;
public float speed = 2f;
void Update ()
{
transform.position = Vector2.MoveTowards(transform.position, target.position, speed * Time.deltaTime);
}
void OnTriggerEnter2D(Collider2D otherCollider)
{
PlayerControl shot = otherCollider.gameObject.GetComponent<PlayerControl>();
if (shot != null)
{
Destroy(shot.gameObject);
}
}
}
you could use something similar to this:
public GameObject myObj;
void Start ()
{
enemy = GameObject.Find("enemy");
InvokeRepeating("SpawnEnemy", 1.6F, 1F);
}
public void SpawnEnemy()
{
Vector3 position = new Vector3(Random.Range(35.0F, 40.0F), Random.Range(-4F, 2F), 0);
Instantiate(myObj, position, Quaternion.identity);
}
in the InvokeRepeating call you could possibly add the random range there also instead of a timed instantiate. This example is just a snippet of some prototype code i did a while ago, it may not suit your needs directly but hopefully will give you a general idea on what to do.
EDIT: to make sense, put this into a blank object somewhere in your scene, dont attach this to the actual enemy.
i programmed it like this.
public GameObject enemyPrefab;
public float numEnemies;
public float xMin = 20F;
public float xMax = 85F;
public float yMin = 3.5F;
public float yMax = -4.5F;
void Start () {
for (int i=0; i< numEnemies; i++) {
Vector3 newPos = new Vector3(Random.Range(xMin,xMax),Random.Range(yMin,yMax),0);
GameObject enemy = Instantiate(enemyPrefab,newPos,Quaternion.identity) as GameObject;
}
}
This will spawn a random number of enemies, at random locations, after random periods of time, all adjustable in the Inspector.
public float minTime = 1;
public float maxTime = 3;
public int minSpawn = 1;
public int maxSpawn = 4;
public Bounds spawnArea; //set these bounds in the inspector
public GameObject enemyPrefab;
private spawnTimer = 0;
Vector3 randomWithinBounds(Bounds r) {
return new Vector3(
Random.Range(r.min.x, r.max.x),
Random.Range(r.min.y, r.max.y),
Random.Range(r.min.z, r.max.z));
}
void Update() {
spawnTimer -= Time.deltaTime;
if(spawnTimer <= 0) {
spawnTimer += Random.Range(minTime, maxTime);
int randomSpawnCount = Random.Range(minSpawn, maxSpawn);
for(int i = 0; i < randomSpawnCount; i++) {
Instantiate(transform.transformPoint(enemyPrefab), randomWithinBounds(spawnArea), Quaternion.identity);
}
}
}
//bonus: this will show you the spawn area in the editor
void OnDrawGizmos() {
Gizmos.matrix = transform.localToWorldMatrix;
Gizmos.color = Color.yellow;
Gizmos.drawWireCube(spawnArea.center, spawnArea.size);
}
People on here seem to like using coroutines for time delays, but I personally prefer to track my own timers in Update() to maximize control and predictability.

Categories

Resources