Instantiate multiple prefabs - c#

I am making a ball game, So the ball pass the columns and it instantiate itself base on the columnPoolSize,
I want to intantiate multiple prefab in here. It only intantiate one prefabs base on the column size...I need to create another array for columnPrefab GameObject. However I tried to do that but it did not success...
public class ColumbPool : MonoBehaviour
{
public int columnPoolSize = 5;
public GameObject[] columns;
public GameObject columnPrefab;
private Vector2 objectPoolPosition = new Vector2(-15f,-25f);
private float timeSinceLastSpawn;
public float spawnRate = 4f;
public float columnMin = -1f;
public float columnMax = 3.5f;
private float spawnXPosition = 10f;
private int currentColumn = 0;
void Start()
{
columns = new GameObject[columnPoolSize];
for (int i = 0; i < columnPoolSize; i++)
{
columns[i] = (GameObject)Instantiate(columnPrefab, objectPoolPosition, Quaternion.identity);
}
}
void Update()
{
timeSinceLastSpawn += Time.deltaTime;
if (GameController.instance.gameOver==false && timeSinceLastSpawn>=spawnRate)
{
timeSinceLastSpawn = 0;
float spawnYPosition = Random.Range(columnMin, columnMax);
columns[currentColumn].transform.position = new Vector2(spawnXPosition,spawnYPosition);
currentColumn++;
if (currentColumn>=columnPoolSize)
{
currentColumn = 0;
}
}
}
}

void Start()
{
columns = new GameObject[columnPoolSize];
for (int i = 0; i < columnPoolSize; i++)
{
columns[i] = (GameObject)Instantiate(columnPrefab, objectPoolPosition, Quaternion.identity);
}
}
becomes
private GameObject[] instantiatedColumns;
public GameObject[] columnPrefabs;
void Start()
{
instantiatedColumns= new GameObject[columnPrefabs.Length];
for (int i = 0; i < columnPrefabs.Length; i++)
{
instantiatedColumns[i] = Instantiate(columnPrefabs[i], objectPoolPosition, Quaternion.identity);
}
}
This way you will instantiate each prefab in prefab columns array (populated in the inspector) and save references into a new array "instantiatedColumns", then you may use this array in Update()

Related

C# powerups how to continue spawning when to 10 when 1 powerups is collected

Now it will only spawn to 10 powerups. when 1 is collected, it doesn't spawn to 10 powerups
[SerializeField] public GameObject[] spawnPowerups;
public int amountOfPowerUpsToSpawn = 10;
[SerializeField] public float PowerupsWaitTime = 1f;
IEnumerator SpawnPowerUpsCoroutine()
{
for (int i = amountOfPowerUpsToSpawn; i > 0; i--)
{
int spawnPointX = Random.Range(-18, -3);
int spawnPointY = Random.Range(5, 0);
Vector3 spawnPosition = new Vector3(spawnPointX, spawnPointY, 0);
Instantiate(spawnPowerups[Random.Range(0, spawnPowerups.Length)], spawnPosition, Quaternion.identity);
yield return new WaitForSeconds(PowerupsWaitTime);
}
}
void Start()
{
StartCoroutine(SpawnPowerUpsCoroutine());
}
Add this to the script you sent.
IEnumerator SpawnPowerup()
{
yield return new WaitForSeconds(PowerupsWaitTime);
int spawnPointX = Random.Range(-18, -3);
int spawnPointY = Random.Range(5, 0);
Vector3 spawnPosition = new Vector3(spawnPointX, spawnPointY, 0);
Instantiate(spawnPowerups[Random.Range(0, spawnPowerups.Length)], spawnPosition, Quaternion.identity);
}
and call this from your Collecting Powerup script
YourPowerupGameobject.GetComponent<YourComponent>().SpawnPowerup();
I think maybe this is what you want to achieve. I made some changes and replaced the for with a while loop. Also added something to test the powerup collection to trigger the spawner on pickup. Tis code below.
PowerUpSpawner.cs
[SerializeField] public GameObject[] spawnPowerups;
public int amountOfPowerUpsToSpawn = 10;
public int PowerupsWaitTime = 1;
private int currentPowerUps = 0;
private Coroutine _coroutine;
public void PickedUpPowerUp()
{
currentPowerUps--;
//Check if Coroutine is null
if(_coroutine is null)
{
_coroutine = StartCoroutine(SpawnPowerUpsCoroutine());
}
}
IEnumerator SpawnPowerUpsCoroutine()
{
while (currentPowerUps < amountOfPowerUpsToSpawn)
{
yield return new WaitForSeconds(PowerupsWaitTime);
int spawnPointX = Random.Range(-5, 5);
int spawnPointY = Random.Range(5, 0);
Vector3 spawnPosition = new Vector3(spawnPointX, spawnPointY, 0);
GameObject clone = Instantiate(spawnPowerups[Random.Range(0, spawnPowerups.Length)], spawnPosition, Quaternion.identity);
//Set parent to get the spawner
clone.transform.SetParent(transform, false);
//Increment powerups counter
currentPowerUps++;
}
//Set coroutine to null
_coroutine = null;
}
void Start()
{
_coroutine = StartCoroutine(SpawnPowerUpsCoroutine());
}
PowerUp.cs
private PowerUpSpawner powerUpSpawner;
private void Start()
{
powerUpSpawner = GetComponentInParent<PowerUpSpawner>();
}
private void OnMouseDown()
{
powerUpSpawner.PickedUpPowerUp();
Destroy(gameObject);
}

Trying to find all of the objects in my scene by a custom tag script I have created, however i continue getting a null reference exception

The Tag script has been working for me so far as an alternate to the unity tags up to this point, allowing me to assign multiple tags to an object at once. Now I want to create a method that will get all of the objects in the scene, filter them by the tag, and then return it as an array. The null reference exception refers to line 41 of the Tag.cs script. How do I fix this?
Tags.cs file
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Tags : MonoBehaviour
{
public string[] startTags;
private string[] tags;
private void Start()
{
tags = startTags;
}
public bool FindTag(string search)
{
bool results = false;
for (int i = 0; i < tags.Length; i++)
{
if(search == tags[i])
{
results = true;
break;
}
}
return results;
}
//Find objects by custom script tags
//HERE IS WHERE THE METHOD IS CREATED
public static GameObject[] ObjectsByTag(string search)
{
//Get all objects in scene
GameObject[] allObjects = FindObjectsOfType<GameObject>();
GameObject[] storedObjects = new GameObject[allObjects.Length];
GameObject[] finalObjects;
//Filter
int count = 0;
for (int i = 0; i < allObjects.Length; i++)
{
if (allObjects[i].GetComponent<Tags>().FindTag(search)) //line 41
{
storedObjects[count] = allObjects[i];
count++;
}
}
//Assign final length
finalObjects = new GameObject[count];
//Reassign to final array
for (int i = 0; i < count; i++)
{
finalObjects[i] = storedObjects[i];
}
return finalObjects;
}
}
GameController.cs file (How it is being used
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GameController : MonoBehaviour
{
//SCREEN START
//Get Screen Size
private float sHeight;
private float sWidth;
//Intended Screen Size
readonly float iH = 695f;
readonly float iW = 1540f;
//Convert
private float cH;
private float cW;
public float ConvertedHeight => cH;
public float ConvertedWidth => cW;
//SCREEN END
//MOUSE CAM START
//mousePostion
private float mX;
private float mZ;
public float MouseX => mX;
public float MouseZ => mZ;
//MOUSE CAM END
//EnemySpeedModifier
private float esm;
public float ESM
{
get { return esm; }
set { esm = value; }
}
//GameOver
private bool gameOver = false;
public bool GameOver
{
get { return gameOver; }
set { gameOver = value; }
}
//game speed
public float speed;
/*
//projectile list
private GameObject[] projectiles;
public GameObject[] Projectiles()
{
return projectiles;
}
public void Projectiles(GameObject value)
{
GameObject[] tempArray = projectiles;
tempArray[projectiles.Length] = value;
projectiles = tempArray;
Debug.Log("Projectile Count: " + projectiles.Length);
}
*/
//HERE IS WHERE IT IS USED
public GameObject[] ProjectilesInScene
{
get
{
return Tags.ObjectsByTag("projectile");
}
}
// Start is called before the first frame update
void Start()
{
//CONVERT SCREEN SIZES START
sHeight = Screen.height;
sWidth = Screen.width;
cH = iH / sHeight;
cW = iW / sWidth;
//CONVERT SCREEN SIZES END
}
// Update is called once per frame
void Update()
{
if (gameOver)
{
speed /= 1 + 0.5f * Time.deltaTime;
}
//Update mose position
mX = Input.mousePosition.x;
mZ = Input.mousePosition.y;
}
}
It seems that not all of your GameObject objects have the Tags component. Per the GameObject.GetComponent documentation
Returns the component of Type type if the game object has one attached, null if it doesn't.
If you know that some objects won't have the Tags component, your line 41 can use a simple null conditional operator:
if (allObjects[i].GetComponent<Tags>()?.FindTag(search) == true)
{
...
}
Note the ? after GetComponent<Tags>().

How can I create a manager script to control all drawn circles at once?

The first script is for drawing the circle:
When I attach this script to a gameobject it's drawing a circle around the object.
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
[RequireComponent(typeof(LineRenderer))]
public class DrawCircle : MonoBehaviour
{
[Range(0, 50)]
public int segments = 50;
[Range(1, 50)]
public float xradius = 5;
[Range(1, 50)]
public float yradius = 5;
[Range(-10, 10)]
public float height = 0;
public bool changeBothRadius = false;
[Range(0.1f, 2)]
public float lineThickness = 0.1f;
public bool minimumRadius = false;
private LineRenderer line;
void Start()
{
line = gameObject.GetComponent<LineRenderer>();
line.positionCount = segments + 1;
line.useWorldSpace = false;
}
void Update()
{
line.startWidth = lineThickness;
line.endWidth = lineThickness;
CreatePoints();
}
void CreatePoints()
{
float x;
float z;
float angle = 20;
for (int i = 0; i < (segments + 1); i++)
{
x = Mathf.Sin(Mathf.Deg2Rad * angle) * xradius;
z = Mathf.Cos(Mathf.Deg2Rad * angle) * yradius;
line.SetPosition(i, new Vector3(x, height, z));
angle += (360f / segments + 1);
}
}
}
Now I created a manager script that should control all the circles at once:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CirclesManager : MonoBehaviour
{
public GameObject[] objectsToAddCircles;
[Range(0, 50)]
public int segments = 50;
[Range(1, 50)]
public float xradius = 5;
[Range(1, 50)]
public float yradius = 5;
[Range(-10, 10)]
public float height = 0;
public bool changeBothRadius = false;
[Range(0.1f, 2)]
public float lineThickness = 0.1f;
public bool minimumRadius = false;
void Start()
{
for (int i = 0; i < objectsToAddCircles.Length; i++)
{
objectsToAddCircles[i].AddComponent<DrawCircle>();
objectsToAddCircles[i].AddComponent<LineRenderer>();
}
}
void Update()
{
for (int i = 0; i < objectsToAddCircles.Length; i++)
{
var lr = objectsToAddCircles[i].GetComponent<LineRenderer>();
lr.startWidth = lineThickness;
lr.endWidth = lineThickness;
var dc = objectsToAddCircles[i].GetComponent<DrawCircle>();
dc.segments = segments;
dc.xradius = xradius;
dc.yradius = yradius;
dc.height = height;
dc.changeBothRadius = changeBothRadius;
dc.minimumRadius = minimumRadius;
}
}
}
So now each object have the LineRenderer component and the DrawCircle script and now the CirclesManager script is working on all objects fine but if I try to change individual object settings it will not change. For example I can change the xrdaius or yradius sliders in the manager script but if I try to change them in a specific object the sliders won't move will not change.
Can't figure out why the manager script is working but not each individual object with the script and LineRenderer.
In CirclesManager class in Update you have these lines:
dc.xradius = xradius;
dc.yradius = yradius;
No matter where and how you change individual DrawCircle instance radius, these lines would always overwrite those values.
I don`t know what kind of behaviour you want to archive but you can create a bool array so you can manually set which circles would be driven by CirclesManager and which circles would use their own values:
// you can change it in the inspector which is handy
// if i'th value of this array is false
// then i'th CircleDrawer GameObject in objectsToAddCircles array
// won't be affected by this manager
public bool changableCircle[];
void Start() {
// your code
changableCircle = new bool[objectsToAddCircles.Length];
}
void Update() {
for(...) {
// values which are always overwritten by manager
if(changableCircle[i]) {
// values which you don't want to be changed by this manager
}
}
}

Unity: My enemy projectile is being destroyed before ever leaving it's spawn location. What am i doing wrong?

Like the title says, the enemy projectiles are not launching. They are spawned and destroyed in the same place. They do not fire toward the target. Code in link:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MonkeyController : MonoBehaviour {
private const int MAX_INJURES = 1;
public float DurationMovement = 2f;
public Transform SpawnLocation;
public Transform[] PatrolPoints;
public GameObject MonkeyProjectile;
public Rigidbody2D Abby;
public float AttackDelay = 0.75f;
public float ProjectileSpeed = 1;
public int ProjectilesCount = 4;
public float activePosition;
public float passivePosition;
private List<GameObject> ProjectileList = new List<GameObject>();
private int PatrolIndex = 0;
private int injures = 0;
private bool canPatrol;
private Coroutine fireCoroutine;
private Coroutine patrolCoroutine;
private Coroutine movementCoroutine;
WaitForSeconds delay;
Vector2 AttackVector = Vector2.zero;
// Use this for initialization
private void Start () {
delay = new WaitForSeconds(AttackDelay);
InitializeProjectileList();
fireCoroutine = StartCoroutine(MonkeyFireProjectileBehaviour());
patrolCoroutine = StartCoroutine(PatrolBehaviour(PatrolPoints[PatrolIndex].position));
}
private IEnumerator MonkeyFireProjectileBehaviour()
{
yield return delay;
if (GameManager.GetInstance().gameState == GameState.GAME_OVER)
yield return null;
AttackVector = Abby.transform.position - (transform.position /*+ (Vector3)Abby.velocity/10f*/);
FireProjectile(AttackVector);
fireCoroutine = StartCoroutine(MonkeyFireProjectileBehaviour());
}
private IEnumerator PatrolBehaviour(Vector3 animationLocation)
{
canPatrol = true;
float distance = (transform.position - animationLocation).magnitude;
float duration = DurationMovement;
Vector3 startingPos = transform.position;
float t = 0;
while (t < 1 && canPatrol)
{
t += Time.deltaTime / duration;
transform.position = Vector3.Lerp(startingPos, animationLocation, t);
yield return null;
}
if (!canPatrol)
yield break;
transform.position = animationLocation;
IncrementMovementIndex();
patrolCoroutine = StartCoroutine(PatrolBehaviour(PatrolPoints[PatrolIndex].position));
yield return null;
}
private void IncrementMovementIndex()
{
PatrolIndex++;
if(PatrolIndex == PatrolPoints.Length)
{
PatrolIndex = 0;
}
}
private void InitializeProjectileList()
{
GameObject Projectile;
for (int i = 0; i < ProjectilesCount; i++)
{
Projectile = Instantiate(MonkeyProjectile);
Projectile.SetActive(false);
ProjectileList.Add(Projectile);
}
}
private void FireProjectile(Vector2 forceProjectile)
{
foreach (GameObject projectile in ProjectileList)
{
if (!projectile.activeInHierarchy)
{
projectile.transform.position = SpawnLocation.position;
projectile.SetActive(true);
projectile.GetComponent<TrailRenderer>().enabled = true;
projectile.GetComponent<Rigidbody2D>().bodyType = RigidbodyType2D.Dynamic;
projectile.GetComponent<Rigidbody2D>().AddForce(forceProjectile * (ProjectileSpeed + Random.Range(0, ProjectileSpeed/2)), ForceMode2D.Impulse);
break;
}
}
}
private IEnumerator DoMovementCoroutine()
{
yield return new WaitForSeconds (0.01F);
transform.localPosition = new Vector2(passivePosition, 0);
yield return AnimatorExecutive.AnimatePositionCoroutine (gameObject, new Vector2 (activePosition, 0), 5.0F);
fireCoroutine = StartCoroutine(MonkeyFireProjectileBehaviour());
patrolCoroutine = StartCoroutine(PatrolBehaviour(PatrolPoints[PatrolIndex].position));
}
private void OnCollisionEnter2D(Collision2D otherCollision)
{
if (otherCollision.gameObject.tag == "Projectile")
{
injures++;
if (injures >= MAX_INJURES)
{
injures = 0;
canPatrol = false;
GetComponent<AudioSource>().Play();
if(fireCoroutine != null) StopCoroutine (fireCoroutine);
if(patrolCoroutine != null) StopCoroutine (patrolCoroutine);
movementCoroutine = StartCoroutine (DoMovementCoroutine());
}
}
}
}
With the information your provided, I would say the problem you may be facing is the GameObject you pass in the inspector to get the SpawnLocation has got a collider and a script with a OnCollisionEnter2D, which detect the projectil when you instantiate it and destroy it.
However, you are not destroying the projectil inside this OnCollisionEnter2D.
private void OnCollisionEnter2D(Collision2D otherCollision)
{
if (otherCollision.gameObject.tag == "Projectile")
{
injures++;
if (injures >= MAX_INJURES)
{
injures = 0;
canPatrol = false;
GetComponent<AudioSource>().Play();
if(fireCoroutine != null) StopCoroutine (fireCoroutine);
if(patrolCoroutine != null) StopCoroutine (patrolCoroutine);
movementCoroutine = StartCoroutine (DoMovementCoroutine());
}
}
}
In the case you dont have in your code any line to destroy the projectil gameobject after a collision. The problem could be you are not reaching this line projectile.SetActive(true);
I will try to replicate your code and check what may be happening

How can I add properties for each element in array of gameObject?

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//[ExecuteInEditMode]
public class InstantiateObjects : MonoBehaviour
{
public GameObject[] objectsToInstantiate;
public Terrain terrain;
public float yOffset = 0.5f;
public int numberOfObjectsToCreate;
public bool parent = true;
public bool randomScale = false;
public float setRandScaleXMin, setRandScaleXMax;
public float setTandScaleYMin, setTandScaleYMax;
public float setTandScaleZMin, setRandScaleZMax;
private float terrainWidth;
private float terrainLength;
private float xTerrainPos;
private float zTerrainPos;
private GameObject objInstance;
private GameObject[] createdObjects;
private string objname;
public void Start()
{
//Get terrain size
terrainWidth = terrain.terrainData.size.x;
terrainLength = terrain.terrainData.size.z;
//Get terrain position
xTerrainPos = terrain.transform.position.x;
zTerrainPos = terrain.transform.position.z;
for(int i = 0; i < objectsToInstantiate.Length; i++)
{
objname = objectsToInstantiate[i].name;
MyCustomEditor.TagsAndLayers.AddTag(objname);
}
generateObjectOnTerrain();
}
public void Update()
{
}
public void DestroyObjects()
{
UpdateList(true);
if (createdObjects != null && createdObjects.Length > 0)
{
for (int i = 0; i < createdObjects.Length; i++)
{
DestroyImmediate(createdObjects[i]);
}
createdObjects = new GameObject[0];
}
}
public void generateObjectOnTerrain()
{
for (int i = 0; i < objectsToInstantiate.Length; i++)
{
//Generate random x,z,y position on the terrain
float randX = UnityEngine.Random.Range(xTerrainPos, xTerrainPos + terrainWidth);
float randZ = UnityEngine.Random.Range(zTerrainPos, zTerrainPos + terrainLength);
float yVal = Terrain.activeTerrain.SampleHeight(new Vector3(randX, 0, randZ));
//Generate random x,y,z scale on the terrain
float randScaleX = Random.Range(setRandScaleXMin, setRandScaleXMax);
float randScaleY = Random.Range(setTandScaleYMin, setTandScaleYMax);
float randScaleZ = Random.Range(setTandScaleYMax, setRandScaleZMax);
//Apply Offset if needed
yVal = yVal + yOffset;
//Generate the Prefab on the generated position
objInstance = Instantiate(objectsToInstantiate[i], new Vector3(randX, yVal, randZ), Quaternion.identity);
if (randomScale == true)
objInstance.transform.localScale = new Vector3(randScaleX, randScaleY, randScaleZ);
if (parent)
objInstance.transform.parent = this.transform;
objInstance.tag = objname;
}
createdObjects = GameObject.FindGameObjectsWithTag(objname);
if (objname == "Teleportation Booth")
UpdateList(false);
}
private void UpdateList(bool destroy)
{
GameObject go = GameObject.Find("Main Camera");
var list = go.GetComponent<PatrolOverTerrain>().Targets;
if (destroy == false)
{
list.AddRange(createdObjects);
go.GetComponent<PatrolOverTerrain>().Targets = list;
}
if (destroy == true)
{
list.Clear();
go.GetComponent<PatrolOverTerrain>().Targets.Clear();
}
}
}
Instead attaching the script to every gameObject I will want to clone I want to use array of objects.
For example let's say I have in the array 2 objects, now I want in the inspector to have under each element its own properties.
And when I will set the properties as children for each element it will take effect for the specific element only.
Instead general properties for all the elements to make this properties from Y Offset until the last Set Rand Scale Z Max to be children for each element.
So i can set the properties per element.
So I can expand each element and see his properties.
Btw: How can I change the random for example that: Set Rand Scale X Min and Set Rand Scale X Min will be on one line and not two lines ? Like:
Set Rand Scale X Min Max
Set Rand Scale Y Min Max
Set Rand Scale Z Min Max
Good way to do this would be creating custom class and then making array of instances of that class but it require a bit more lines. If you like to read more about this search ISerializationCallbackReceiver.
Simplest would be probably wrapping your variables in struct like below. You can then "unwrap" them to your classes or not. That depends on what you do want.
using UnityEngine;
using System;
public class InstantiateObjects : MonoBehaviour {
public ObjectsToInst[] objectsToInstantiate;
[Serializable]
public struct ObjectsToInst
{
public GameObject objectToInstantiate;
public Terrain terrain;
public float yOffset;
public int numberOfObjectsToCreate;
public bool parent;
public bool randomScale;
public float setRandScaleXMin, setRandScaleXMax;
public float setTandScaleYMin, setTandScaleYMax;
public float setTandScaleZMin, setRandScaleZMax;
}
}

Categories