How to delete all prefabs when an event happens in unity - c#

Extremely new to unity and c# after switching across from Python. Once all the balls are scored in my game I want all the 'blockers' (prefabs that I have instantiated) to be removed from the screen and a new set to be spawned randomly on screen in random positions. The blocker prefabs spawn randomly when all balls are scored, however, the old prefabs, besides the one which deletes each time, stay on screen rather than deleting. I have tried looping through the blockers in the code below to delete and I think this is where the issue is as only one game object deletes at this stage:
if (SpawnManager.tempclonecount == 0 )
{
for (int i = 0; i < SpawnManager.blockeramounts; i++)
{
Destroy(gameObject);
}
SpawnManager.tempclonecount = 1;
}
SpawnManager is an empty object which I have used to spawn objects onto the screen, tempclonecount is a variable stating when the old game objects should be removed from the game. This part of the code works well. blockeramounts is the number of prefabs initially on screen and I hoped to loop through the number of prefabs would delete all of the prefabs. It only deletes one. How do I change this?
Here is the code for creating the blockers in spawn manager also, if helpful:
void Update()
{
int blockeramount = Random.Range(2, 7);
blockeramounts = blockeramount;
for (int i = 0; i < blockeramount; i++)
{
int blockerindex = Random.Range(0, blockerPrefabs.Length);
Instantiate(blockerPrefabs[blockerindex], new Vector3(Random.Range(-30, 30), 0, Random.Range(-30, 30)), blockerPrefabs[blockerindex].transform.rotation);
}
}

While reading your description of the problem I got a bit confused by some of the terms you used. That's all fine since you are new to Unity and still learning. What i managed to figure out is that you have a SpawnManager script attached to an empty game object which instantiates your blocker prefabs. Then in another script you are getting the SpawnManager refernce and checking if you should destroy the current ones and instantiate a new set.
First of all what i would do is, after instantiating an object, to store it in an array or a list
...
public List<GameObject> blockers = new List<GameObject>();
...
void Start()
{
...
}
void Update()
{
int blockeramount = Random.Range(2, 7);
blockeramounts = blockeramount;
for (int i = 0; i < blockeramount; i++)
{
int blockerindex = Random.Range(0, blockerPrefabs.Length);
var blocker = Instantiate(blockerPrefabs[blockerindex], new Vector3(Random.Range(-30, 30), 0, Random.Range(-30, 30)), blockerPrefabs[blockerindex].transform.rotation);
blockers.Add(blocker);
}
}
After which i would add a new method which does the check to see how many remaining blockers there are. This method should go inside SpawnManager.
public void CheckAndDeleteBlockers()
{
if (tempclonecount == 0 )
{
foreach(var blocker in blockers)
{
blockers.Remove(blocker);
Destroy(blocker);
}
}
}
And you should call it from the other script with:
...
public SpawnManager spawnManager;
...
void Start()
{
spawnManager = FindOjectOfType<SpawnManager>();
}
//for example
void Update()
{
spawnManager.CheckAndDeleteBlockers();
}
I understand the way you are trying to do this, but let's say that this isn't the correct way. I would suggest that you look up what object pooling is.
NOTE: The creator of the pooling tutorial that I mentioned above is a great source for Unity and C# beginners, so I would recommend that you watch some of his other videos. Good luck in learning Unity.

What I believe is causing your issue, is that your are only specifying one gameObject to be destroyed. You need to call Destroy() on each blocker you instantiated.
Hope my advice helps.

Related

Instantiate a Object below another object instead of above

First of all, i created a GIF to show what is currently happen.
GIF with my current problem
and
Awhat I want
I have a List of GameObject which add the bodyParts temp and Instantiate it in the correct time and position.
Now this is working like expected, but i want this new bodyParts below another object instead of above.
As you can see the Head is "under" the new body parts, but it should always on Top and every new part should spawn under the next. (only should looks like! I dont want to change the Z position.)
i tried :
bodyParts.transform.SetAsFirstSibling();
to change the Hierarchy, but this do nothing. I also can drag and drop the Clones to a other position in Hierarchy but they just stay at the same position (above another).
Is this possible and what should i have to do?
Here some of my Code which makes the process:
private void CreateBodyParts()
{
if (snakeBody.Count == 0)
{
GameObject temp1 = Instantiate(bodyParts[0], transform.position, transform.rotation, transform);
if (!temp1.GetComponent<MarkerManager>())
temp1.AddComponent<MarkerManager>();
if (!temp1.GetComponent<Rigidbody2D>())
{
temp1.AddComponent<Rigidbody2D>();
temp1.GetComponent<Rigidbody2D>().gravityScale = 0;
}
snakeBody.Add(temp1);
bodyParts.RemoveAt(0);
}
MarkerManager markM = snakeBody[snakeBody.Count - 1].GetComponent<MarkerManager>();
if (countUp == 0)
{
markM.ClearMarkerList();
}
countUp += Time.deltaTime;
if (countUp >= distanceBetween)
{
GameObject temp = Instantiate(bodyParts[0], markM.markerList[0].position, markM.markerList[0].rotation, transform);
if (!temp.GetComponent<MarkerManager>())
temp.AddComponent<MarkerManager>();
if (!temp.GetComponent<Rigidbody2D>())
{
temp.AddComponent<Rigidbody2D>();
temp.GetComponent<Rigidbody2D>().gravityScale = 0;
}
snakeBody.Add(temp);
bodyParts.RemoveAt(0);
temp.GetComponent<MarkerManager>().ClearMarkerList();
countUp = 0;
}
}
Finally i found the working Solution.
It has nothing to do with which hierarchy order GameObjects spawn in.
Just the Layer and the LayerOrder are responsible for it.
So I give my parent object a specific layer name (manually in the inspector under "Additional Settings" or programmatically)
I chose the programmatic way...
Any newly spawned GameObject that is Child would get a lower number
yourGameObject.GetComponent<Renderer>().sortingLayerID = SortingLayer.NameToID("Player");
yourGameObject.GetComponent<Renderer>().sortingOrder = -snakeBody.Count;

How can I make a destroyer that destroys all the overtaken platforms in unity?

I'm doing a unity project for an exam I'm attending, so I'm not really good at unity, hope you can help.
I'm doing this 2d endless runner game and I created an empty object to set inactive all the platforms that the player has overtaken. The object moves forward and when its position is > than the platform position, it sets it inactive (since destroying them also removes them from the list so it wouldnt work for others platforms of the same kind). My problem is that I've created a list in which I put all the types of my prefabs but the way I did it, it only sets inactive the first prefab of a kind and then stops working.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DistruttoreLivelli : MonoBehaviour
{
public List<GameObject> objs;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
for (int i = 0; i < objs.Count; i++)
{
if (objs[i].transform.position.x < transform.position.x)
{
objs[i].SetActive(false);
}
}
}
}
How can I manage to make it go forever? I tried to use a while but it makes unity crash. :/
Ty for the help!
The easiest way to make it endless is to move the objects to the furthest position instead of deactivating them. Instead of objs[i].SetActive(false); it should be something like:
Vector3 endOfTheLinePos = objs[i].transform.position;
endOfTheLinePos .x = maxX;
objs[i].transform.position = endOfTheLinePos;
With maxX being a float parameter of the highest value of x that you are using on the objects.
Also, as already pointed out, on the for you should use i < objs.Count instead.

Unity - Instantiated objects spawn on the same position in Coroutine, while instantiate them directly do spawn correctly

What i want to achieve:
Im creating a procedural generated city. I got the roads working, together with the spawning of the houses, they spawn along the road and dont get spawned if they are colliding with either the road and/or another building. Now since i sometimes i want to load a big city, this costs a lot of computing time, so i wanted to put in a Coroutine to slowly load in all the houses. (Please tell me if there is a better way, or a "correct" way, this is the only thing that i knew that could work).
What i did / Problem:
So what i did, is all the segments i use to create a house i put in a list, and once all the houses are pregenerated (not spawned/instantiated yet) i want to use the coroutine to spawn them individually. But whenever i use the coroutine to spawn them, they spawn inside eachother.
While if i spawn them directly from their script (still using the list) they do spawn correctly, i have no clue what i am doing wrong and how to fix this.
public void Spawner(Vector3 buildingPosition, Quaternion buildingRotation, int buildingHeight, GameObject buildingParent, LayerMask m_LayerMask, BuildingGenerator buildGenerator)
{
GameObject baseBuilding = buildingBase[Random.Range(0, buildingBase.Count)];
baseBuilding.transform.position = buildingPosition;
baseBuilding.transform.rotation = buildingRotation;
Debug.Log(baseBuilding.transform.position);
Collider[] hitColliders = Physics.OverlapBox(baseBuilding.transform.position, baseBuilding.GetComponentInChildren<Renderer>().bounds.extents /2, Quaternion.identity, m_LayerMask);
if (hitColliders.Count() > 1)
{
return;
}
buildGenerator.Segments.Add(baseBuilding);
buildingPosition.y += baseBuilding.GetComponentInChildren<Renderer>().bounds.max.y - buildingPosition.y;
for (int i = 0; i <= buildingHeight; i++)
{
buildingRotation *= Quaternion.Euler(0, 0, 0);
GameObject middleBuilding = buildingMiddle[Random.Range(0, buildingMiddle.Count)];
middleBuilding.transform.position = buildingPosition;
middleBuilding.transform.rotation = buildingRotation;
//buildGenerator.Segments.Add(middleBuilding);
buildingPosition.y += middleBuilding.GetComponentInChildren<Renderer>().bounds.max.y - buildingPosition.y;
}
if (buildingRoof.Count != 0)
{
GameObject roofBuilding = buildingRoof[Random.Range(0, buildingRoof.Count)];
roofBuilding.transform.position = buildingPosition;
roofBuilding.transform.rotation = buildingRotation;
buildGenerator.Segments.Add(roofBuilding);
}
Instantiate(buildGenerator.Segments[1]); //If i use this, they spawn on the correct place.
Debug.Log(buildGenerator.Segments.Count);
}
{
StartCoroutine(LoadSegments(buildingParent));
}
public IEnumerator LoadSegments(GameObject buildingParent)
{
for (int i = 0; i < Segments.Count; i++)
{
GameObject SpawnedSegment = Instantiate(Segments[i]);
//SpawnedSegment.transform.parent = buildingParent.transform;
yield return new WaitForEndOfFrame();
}
}
Extra info:
I make use of 3 scripts to spawn the whole city, the roadGenerator, which will spawn the roads, those road points get stored in a list. After the roads are generater the roadgenerator will call the building Generator. The building generator will go through the whole list of all the roads, and create buildings aside them, the buildings are created via a 3rd script (not instantiated). The building script, this script contains the building segments, and will piece them together and also check for collisions (First code block). Once these buildings are all created and put into a list, the buildingGenerator will start the coroutine and instantiate all these buildings (thus on the wrong position)(Second code block).
Hope i provided enough information, else id be happy to provide more.
Okay so i finally got it working. The problem was not at the coroutine, but at the list. My code would constantly override my list objects. So i created a new script, with basic attributes like the object, the position and rotation. So then i add those new scripts to the list, and spawn them in the coroutine. (In my comment i said i had some other problems after i fixed this, this is because i used .bounds.max.y, while i should have used .bounds.size.y. This is thus also the fix for that).
I hope someone in the future might find this helpful!

Moving a object randomly without it being inside a wall - Unity 5

i am trying to make some types of pickups spawn inside a given area, although some usually get stuck within the walls, how would i fix this?
Code in question for moving objects
for (int x = 0; x < garbage.Length; x++)
{
if (x < 5)
{
garbage[x].transform.position = new Vector3(Random.Range(-33.0f, 30.0f), 2.35f, Random.Range(30.0f, -35.0f));
}
}
Fixed it using Physics.OverlapSphere. Thanks.
You could have a while loop inside your if statement, so it would be like
int attempts = 0;
while(garbage[x].transform.position == /*[the range of coordinates for the wall]*/ || attempts = 0)
{
garbage[x].transform.position = new Vector3(Random.Range(-33.0f, 30.0f), 2.35f, Random.Range(30.0f, -35.0f));
attempts += 1;
}
You can try OnCollisionStay to tackle this with collisions. OnCollisionStay can be very cpu heavy if not used carefully, so you may want to think of a better way if you can.
You will have to create a new script using the following code, which you will attach to your power-up prefab.
bool keepChecking = true;
void OnCollisionStay(Collision collision)
{
if(keepChecking)
{
if(collision.gameobject.tag == "Wall")
{
collision.gameobject.transform.position = new Vector3(Random.Range(-33.0f, 30.0f), 2.35f, Random.Range(30.0f, -35.0f));
}
else
{
keepChecking = false;
}
}
}
https://docs.unity3d.com/ScriptReference/Collider.OnCollisionStay.html
Read that link and make sure your objects have all the requirements. Your wall and Power-Up should have colliders, and at least one of these two should have a rigid body. None of these objects should be kinematic.
Let me know if this works for you.

spawn obstacles c# and accessing from another script

I'm Getting a error when i try to instantiate in the manager class. saying
Error CS1061: Type UnityEngine.Object' does not contain a definition forGetComponent' and no extension method GetComponent' of typeUnityEngine.Object' could be found. Are you missing an assembly reference? (CS1061) (Assembly-CSharp)
Add this to your Obstacle class:
void Start()
{
manager = GameObject.FindWithTag("ObstacleManager").GetComponent<ObstacleManager>();
}
The tag obviously has to be the tag of the gameobject the manager is attached to.
Also: Always start class names with a capital letter (I did that in the snippet, keep that in mind, you will get an error right know with that).
Maybe you want to actually change your spawning a bit though. Have two lists, one for free spawnpoints and one for occupied. When you destroy an obstacle, pass the position to the spawning function to move the position to the free list.
Edit:
Another option to create the reference is to set it in your ObstacleManager on spawning. You need to grab a reference to the instantiated obstacle for this. I believe this should work without actually grabbing the obstacle gameobject, but you could do that too.
Obstacle obs = ((GameObject)Instantiate(TypeOfObstacles[j], pointsAvailiable[pointsIndex].position, Quaternion.identity)).GetComponent<Obstacle>();
obs.SetManagerReference(this);
And in Obstacle add
public void SetManagerReference(ObstacleManager obsManager)
{
manager = obsManager;
}
For the free position you can do something like this:
// in Obstacle.cs
public void OnMouseDown()
{
manager.SpawnNewObstacle(transform.position); // you might be able to actually pass the transform, but I'm not sure if it will get destroyed before used in the other function
Destroy(gameObject);
}
In the Manager:
public int noOfObsacles;
public float[] xPercent;
public GameObject[] TypeOfObstacles;
float y;
// to keep track of which spawn points are free and which aren't use these lists
private List<Transform> freePositions;
private List<Transform> occupiedPositions;
private void Start()
{
freePositions = new List<Transform>(spawnPoints);
occupiedPositions = new List<Transform>();
SpawnObstacles();
}
private void SpawnObstacles()
{
// just use this for initial obstacles
// call Spawn as often as needed
for(int i = 0; i < noOfObstacles; i++)
{
Spawn();
}
}
// you call this function from the obstacle that gets destroyed
public void SpawnNewObstacle(Vector3 freePos)
{
// find the spawnpoint in the occupied points
// and move it to the free ones since the obstacle got destroyed
for(int i = 0; i < occupiedPositions.Count; i++)
{
if(occupiedPositions[i].position == freePos)
{
freePositions.Add(occupiedPositions[i]);
occupiedPositions.RemoveAt(i);
break;
}
}
// and call Spawn
Spawn();
}
private void Spawn()
{
y = Random.value;
int pointsIndex = Random.Range (0, freePositions.Count);
for (int j =0; j<xPercent.Length; j++)
{
if ( y < xPercent[j])
{
// these 4 lines are essential for the spawning
Obstacle obs = ((GameObject)Instantiate(TypeOfObstacles[j], freePositions[pointsIndex], Quaternion.identity).GetComponent<Obstacle>();
obs.SetManagerReference(this);
occupiedPositions.Add(freePositions[pointsIndex]);
freePositions.RemoveAt(pointsIndex);
break;
}
}
}
had a bracket issue! my bad
obstacle obs = ((GameObject)Instantiate(TypeOfObstacles[j], freePositions[pointsIndex].position, Quaternion.identity)).GetComponent();

Categories