Delete spawning list objects consistently - c#

Already 2 days I'm trying to solve this problem but I can't.I'm trying to delete the unlimited spawning barriers consistently when they are collide with the boundary.The barriers are made from 5 cubes.Any ideas?
public List<GameObject> spawning=new List<GameObject>();
public Vector3[] positions = new Vector3[5];
public GameObject barrier;
public GameObject boundary;
void Start()
{
StartCoroutine (SpawnBarrier());
}
void Update()
{
if(true)
{
foreach (GameObject move in spawning)
move.transform.Translate (0f, 0f, -0.1f);
}
}
IEnumerator SpawnBarrier(){
yield return new WaitForSeconds (3f);
while (true) {
for(int i=0;i<=4;i++)
{
spawning.Add (Instantiate (barrier, positions [i], Quaternion.identity)as GameObject);
}
yield return new WaitForSeconds (3f);
}
}

Your barriers are marked as isTrigger on their colliders therefore you can use OnTriggerEnter to detect when any of the barriers collides with the boundary.
You need to create new script, lets call that BoundaryDetector and attach it to the barrier prefab so that every instance of the barrier will have this script attached to it.
When OnTriggerEnter is called, check if the trigger is made by the boundary. This can be done by checking for the "Player" tag since boundary is tagged as Player in your screenshot. If the detected trigger tag is "Player", first remove the spawning from the spawning List then Destroy it.
The BoundaryDetector script is as below (Must be attached to the barrier prefab):
ScriptFromYourQuestion yourInstancingSript;
void Start()
{
GameObject obj = GameObject.Find("NameOfObjectScriptInYourQuestionIsAttachedTo");
yourInstancingSript = obj.GetComponent<ScriptFromYourQuestion>();
}
void OnTriggerEnter(Collider other)
{
//Detect if we collided with the boundary
if (other.CompareTag("Player"))
{
//Remove Self/barrier from the List
yourInstancingSript.spawning.Remove(this.gameObject);
//Delete Self/barrier
Destroy(this.gameObject);
}
}
Note: The ScriptFromYourQuestion should be replaced with the name of the script in your question.
MUST DO:
To get the code above working, the following changes must be made in the Update function of the script from your question:
1.You must attach Rigidbody to at-least one of the Objects (boundary or barrier). In this case, I suggest you do so to the barrier prefab.
2.You must remove move.transform.Translate (0f, 0f, -0.1f); and replace it with Rigidbody.MovePosition and use that to move your barries because you have now attached Rigidbody to your barries and this is the proper way to move a Rigidbody Object.
Maybe something like this:
Your Update function in the code form your quesion should looks something like this:
public float speed = 100.0f;
void Update()
{
if (true)
{
foreach (GameObject move in spawning)
{
//Get Rigidbody component
Rigidbody rb = move.GetComponent<Rigidbody>();
//Calculate Z-axis pos to move to
Vector3 pos = new Vector3(0, 0, 1);
pos = pos.normalized * speed * Time.deltaTime;
//Move with Rigidbody
rb.MovePosition(rb.transform.position + pos);
}
}
}

Your problem is that you have an infinite loop while true that you're not breaking out of. Instead you should add a counter. You haven't explained what you want to do but my example is that it will breakout once it completes 10 instantiations of 5 blocks.
IEnumerator SpawnBarrier(){
spawnCount=0
yield return new WaitForSeconds (3f);
while (true) {
for(int i=0;i<=4;i++)
{
spawning.Add (Instantiate (barrier, positions [i], Quaternion.identity)as GameObject);
}
if (++spawnCount==10)
{
break;
}
yield return new WaitForSeconds (3f);
}
}

If I understood corrently, you want to destroy barriers that collided with some kind of boundary?
If the 'boundary' is the visible screen space
In this case, we can assume that, once the object is invisible to MainCamera it can be counted as out of the boundary. Keeping that in mind, we can use OnBecameInvisible() method, which is method (or message, as stated in Unity Scripting API) called by MonoBehaviour class:
// A piece of barrier that gets destroyed once out of screen
public sealed class BarrierPiece : MonoBehaviour
{
// Called once the object is no longer visible to any (scene editor included) camera
private void OnBecameInvisible()
{
Destroy(gameObject);
}
}
NOTE: if you have more than one Camera in your scene, the object must be invisible to all of the cameras in order for the OnBecameInvisible() to be called.
If the 'boundary' is other object
In this case, there are many approaches based on your game:
Create GameObject with Rigidbody component and add a script which would have OnTriggerEnter(...) method and destroy colliding barriers in it.
(Inefficient) have two static Vector2 variables, one for pivotPosition, other for maximumOffset (this one should be squared for faster calculations). Then, have script on each of the barriers and in Update() checks whether the object's squared distance to the pivotPosition is greater than maximumOffset and call Destroy(gameObject) if the condition is true.
(others) ...
Those are two that came up quickly in my mind.
Hope this helps!
EDIT
Didn't look at the images...
So for your solution, you would need to assign Rigidbody component to the Boundary and add BoxCollider component on all of the barriers. Now, create a tag, called Barrier and tag all the barriers with it. Create a script:
// Destroys barriers on collision
public sealed class BarrierBoundary : MonoBehaviour
{
// Called once the script is created
// Checks if the object has Rigidbody component attached
private void Awake()
{
Debug.Assert(GetComponent<Rigidbody>() != null);
}
private void OnCollisionEnter(Collision collision)
{
if(collision.collider.tag == "Barrier")
Destroy(collision.collider.gameObject);
}
}
(The code wasn't tested, so it can have some typos.)
Now, assign the script to the Boundary. And voila! Barriers are destroyed once hitting the boundary.
Hope (again) this helps!

Related

Not all gameobjects with the same script attached are respawning

I'm working on a Unity2D platformer which has blocks that break/fall when the player jumps onto them. Once the player makes contact, there is a short delay, then the block begins to slide down and shortly disappears (using SetActive(false)). If the player dies, then all "falling" blocks should respawn and return to their original positions.
I've made a level where there are two of these blocks (two completely independent gameobjects), but the problem is that when the player breaks both and then dies, when the player respawns only one of the two blocks respawns to its original position. (The other is never to be seen again.)
I think this may have something to do with how they are both attached to the same script. However I don't see how because a different gameobject is still specified in each instance of the script for each block.
Here's the code for the script attached to the block gameobjects (called breakingBlock).
public class BreakingBlock : MonoBehaviour
{
Collider2D boxCollider;
public GameObject slidingBlock;
public PlayerController player;
public float delay;
public float X;
public float startY;
public float endY;
public float speed;
void Start() // Initialises the position of the blocks
{
boxCollider = GetComponent<Collider2D>();
boxCollider.enabled = true;
slidingBlock.transform.position = new Vector2(X, startY);
player = FindObjectOfType<PlayerController>();
}
void OnCollisionEnter2D(Collision2D collision)
// Starts sliding coroutine when touched by player
{
StartCoroutine(Slide(slidingBlock, new Vector2(X, startY), new Vector2(X, endY), delay, speed));
}
public IEnumerator Slide(GameObject slidingBlock, Vector2 start, Vector2 end, float delay, float speed)
{
yield return new WaitForSeconds(delay);
while (slidingBlock.transform.position.y != end.y)
{
// Move towards end position over duration given
slidingBlock.transform.position = Vector2.MoveTowards(slidingBlock.transform.position, end, speed * Time.deltaTime);
yield return new WaitForEndOfFrame();
}
boxCollider.enabled = false;
slidingBlock.SetActive(false);
}
public void ResetSlidingBlock() // Called by another script when player dies
{
Debug.Log("Reset block");
slidingBlock.transform.position = new Vector2(X, startY);
slidingBlock.SetActive(true);
boxCollider.enabled = true;
}
}
This is the part of the other script that calls ResetSlidingBlock from the above script breakingBlock when the player dies.
void OnTriggerEnter2D(Collider2D collision)
{
if (collision.name == "Player")
{
levelManager.RespawnPlayer();
breakingBlock.ResetSlidingBlock();
}
}
Why is it that only one of the blocks is respawning when separate gameobjects are specified in the separate scripts on completely distinct gameobjects?
You have a public GameObject where only one object is assigned. Doesn't matter how many objects you assign this script to, only that GameObject that you refer to will be reset. If you have X number of GameObjects that you want to follow the same properties, you can:
Make a prefab and access the children using transform.getchild() or tags - assign them to GameObjects
ex:
GameObject objs[];
for(int i=0;i<How many u want;i++)
{
objs[i]=GameObject.Find("ParentObj").transform.GetChild(i);
//something similar to this
}
Or Simply Make public references and assign them manually.
Ex:
public GameObject slideblock1;
public GameObject slideblock2;
etc.

OnCollisionEnter2D only gets called once

I have two objects in my scene, one which only has a BoxCollider2D (the Column class) and the second object has a Rigibody2D as well as its own BoxCollider2D (Player class).
I added a script to the first object to have an OnCollisionEnter2D. I see it gets triggered when my second object collides with it, and it bounces my 2nd object back when it tries to enter.
I do see my OnCollisionEnter2D method getting called. But if I move my 2nd object again to my first object it gets bounced back again, however I don't see my OnCollisionEnter2D method getting called the 2nd time.
Is this intended behavior? If so, what method would get called every time a collision occurs between these two objects?
Note: I saw OnCollisionStay2D get called a few more times and then it stopped. I assume this is when it's bouncing my 2nd object out. I also see that OnCollisionExit2D never got called. I zoomed into the editor and saw clearly the green lines of the BoxCollider2D did not overlap between my objects so it should've exited the collision when it bounces it back.
public class Column : MonoBehaviour
{
private BoxCollider2D columnColl;
// Start is called before the first frame update
void Start()
{
columnColl = GetComponent<BoxCollider2D>();
}
// Update is called once per frame
void Update()
{
}
private void OnCollisionExit2D(Collision2D collision)
{
Debug.Log("Collision Exit");
}
void OnCollisionStay2D(Collision2D collision)
{
//Debug.Log("Collision Stayed");
}
void OnCollisionEnter2D(Collision2D collision)
{
Debug.Log("Collision Happened");
}
}
and
public class Player : MonoBehaviour
{
public float xMoveSpeed = 1f;
private Rigidbody2D rbody;
// Start is called before the first frame update
void Start()
{
rbody = GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown("d"))
{
rbody.position = rbody.position + new Vector2(xMoveSpeed, 0);
}
}
}
You shouldn't "manually" change rbody.position.
Rather use Rigidbody.MovePosition
Use Rigidbody.MovePosition to move a Rigidbody, complying with the Rigidbody's interpolation setting.
If Rigidbody interpolation is enabled on the Rigidbody, calling Rigidbody.MovePosition results in a smooth transition between the two positions in any intermediate frames rendered. This should be used if you want to continuously move a rigidbody in each FixedUpdate.
Set Rigidbody.position instead, if you want to teleport a rigidbody from one position to another, with no intermediate positions being rendered.
You also should rather do it in FixedUpdate which is used for the Physics instead of Update
void FixedUpdate()
{
if (Input.GetKeyDown("d"))
{
rbody.MovePosition(rbody.position + new Vector2(xMoveSpeed, 0);
}
}
Also make sure at least one of the objects is not kinematic as to OnCollisionEnter:
Collision events are only sent if one of the colliders also has a non-kinematic rigidbody attached.

What is the best way to pass a List from a class and use it in other scripts? [duplicate]

I've searched around and I just can't get this to work. I think I just don't know the proper syntax, or just doesn't quite grasp the context.
I have a BombDrop script that holds a public int. I got this to work with public static, but Someone said that that is a really bad programming habit and that I should learn encapsulation. Here is what I wrote:
BombDrop script:
<!-- language: c# -->
public class BombDrop : MonoBehaviour {
public GameObject BombPrefab;
//Bombs that the player can drop
public int maxBombs = 1;
// Update is called once per frame
void Update () {
if (Input.GetKeyDown(KeyCode.Space)){
if(maxBombs > 0){
DropBomb();
//telling in console current bombs
Debug.Log("maxBombs = " + maxBombs);
}
}
}
void DropBomb(){
// remove one bomb from the current maxBombs
maxBombs -= 1;
// spawn bomb prefab
Vector2 pos = transform.position;
pos.x = Mathf.Round(pos.x);
pos.y = Mathf.Round(pos.y);
Instantiate(BombPrefab, pos, Quaternion.identity);
}
}
So I want the Bomb script that's attached to the prefabgameobject Bombprefab to access the maxBombs integer in BombDrop, so that when the bomb is destroyed it adds + one to maxBombs in BombDrop.
And this is the Bomb script that needs the reference.
public class Bomb : MonoBehaviour {
// Time after which the bomb explodes
float time = 3.0f;
// Explosion Prefab
public GameObject explosion;
BoxCollider2D collider;
private BombDrop BombDropScript;
void Awake (){
BombDropScript = GetComponent<BombDrop> ();
}
void Start () {
collider = gameObject.GetComponent<BoxCollider2D> ();
// Call the Explode function after a few seconds
Invoke("Explode", time);
}
void OnTriggerExit2D(Collider2D other){
collider.isTrigger = false;
}
void Explode() {
// Remove Bomb from game
Destroy(gameObject);
// When bomb is destroyed add 1 to the max
// number of bombs you can drop simultaneously .
BombDropScript.maxBombs += 1;
// Spawn Explosion
Instantiate(explosion,
transform.position,
Quaternion.identity);
In the documentation it says that it should be something like
BombDropScript = otherGameObject.GetComponent<BombDrop>();
But that doesn't work. Maybe I just don't understand the syntax here. Is it suppose to say otherGameObject? Cause that doesn't do anything. I still get the error : "Object reference not set do an instance of an object" on my BombDropScript.maxBombs down in the explode()
You need to find the GameObject that contains the script Component that you plan to get a reference to. Make sure the GameObject is already in the scene, or Find will return null.
GameObject g = GameObject.Find("GameObject Name");
Then you can grab the script:
BombDrop bScript = g.GetComponent<BombDrop>();
Then you can access the variables and functions of the Script.
bScript.foo();
I just realized that I answered a very similar question the other day, check here:
Don't know how to get enemy's health
I'll expand a bit on your question since I already answered it.
What your code is doing is saying "Look within my GameObject for a BombDropScript, most of the time the script won't be attached to the same GameObject.
Also use a setter and getter for maxBombs.
public class BombDrop : MonoBehaviour
{
public void setMaxBombs(int amount)
{
maxBombs += amount;
}
public int getMaxBombs()
{
return maxBombs;
}
}
use it in start instead of awake and dont use Destroy(gameObject); you are destroying your game Object then you want something from it
void Start () {
BombDropScript =gameObject.GetComponent<BombDrop> ();
collider = gameObject.GetComponent<BoxCollider2D> ();
// Call the Explode function after a few seconds
Invoke("Explode", time);
}
void Explode() {
//..
//..
//at last
Destroy(gameObject);
}
if you want to access a script in another gameObject you should assign the game object via inspector and access it like that
public gameObject another;
void Start () {
BombDropScript =another.GetComponent<BombDrop> ();
}
Can Use this :
entBombDropScript.maxBombs += 1;
Before :
Destroy(gameObject);
I just want to say that you can increase the maxBombs value before Destroying the game object. it is necessary because, if you destroy game object first and then increases the value so at that time the reference of your script BombDropScript will be gone and you can not modify the value's in it.

How do I Transfer Instantiate put inside OnCollisionEnter() after created on another function OnCollisionExit() in Unity 3D?

I'm creating my first game "Endless Runner" and my problem is in the code, I don’t need to destroy the new object, I need to transfer them to the old place.
public class GenerateEnv1 : MonoBehaviour {
public GameObject []EnvTile;
float tileZ = 29.31f;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
void OnCollisionEnter (Collision col) {
if(col.gameObject.tag =="ground"){
GameObject o = Instantiate (EnvTile[Random.Range(0,4)], new Vector3(0f,0f,tileZ), Quaternion.EulerAngles(0,0,0));
tileZ += 2.96f;
}
}
void OnCollisionExit (Collision col) {
if(col.gameObject.tag =="ground"){
Destroy (col.gameObject,3); // this's i need to replace that in the new object
}
}
}
Since instantiating and destroying a tile is more costly, every time you leave a tile OnCollisionExit will fire and you will move that tile right after the next tile and so on and so forth, also instantiating two tiles onStart is enough since we're reusing them
public class GenerateEnv1 : MonoBehaviour
{
public GameObject[] EnvTile;
float tileZ = 29.31f;
// Use this for initialization
void Start()
{
Instantiate(EnvTile[Random.Range(0, 4)], new Vector3(0f, 0f, tileZ), Quaternion.EulerAngles(0, 0, 0));
Instantiate(EnvTile[Random.Range(0, 4)], new Vector3(0f, 0f, tileZ * 2), Quaternion.EulerAngles(0, 0, 0));
}
void OnCollisionExit(Collision col)
{
if (col.gameObject.tag == "ground")
{
col.gameObject.transform.position = new Vector3(col.gameObject.transform.position.x , col.gameObject.transform.position.y, col.gameObject.transform.position.z + (tileZ * 2));
}
}
}
The solution to your problem is called Object Pooling.
There's a tiny bit too little detail to provide a full code as an answer, but in general it works like that: Instantiate all the objects you'll need when the game loads, this is farily expensive and is best done at front.
Than, everything you do is do some juggling with gameObject.SetActive(true/false).
Generic implementation will contain a list of gameobjects (pool), you deactivate (instead of destroing) an object on OnCollisionExit, than you take new object from the pool (pick one that is not active), and activate it (and place it).
It is not clear however how the colliders are placed, obviously collision will not be triggered by an object that is not yet active on scene.
Is your script meant to go on the player?

Unity - How to have two of the same GameObjects collide, destroy each other, and spawn a new GameObject at the target position?

So I want to have two balls collide, destroy themselves, and then have another ball spawn at their place (preferably with a specific velocity). When I try to attach the script to the ball, however, both instances of the ball are destroyed on contact, then immediately spawns two prefabs of the ball, since they both have the code. This causes both balls to spawn and destroy each other over and over. I have this script attached to the balls:
private Vector3 ballPosition;
void OnTriggerEnter2D (Collider2D other) {
if (other.gameObject.tag == "Ball") {
ballPosition = new Vector3 ((transform.position.x + other.transform.position.x) / 2, (transform.position.y + other.transform.position.y) / 2, 0.0f);
StartCoroutine ("RespawnBall");
}
}
IEnumerator RespawnBall () {
Instantiate (gameObject, ballPosition, Quaternion.identity);
Destroy (gameObject);
yield return null;
}
How do I make this code destroy both balls, then spawn only one instance of the prefab?
You can also use a boolean on your script that is set so only the first ball having its OnTriggerEnter2D() method call will spawn a new ball :
private Vector3 ballPosition;
public bool spawnNewBall;
void Start() {
spawnNewBall = true;
}
void OnTriggerEnter2D (Collider2D other) {
if (other.gameObject.tag == "Ball") {
if (spawnNewBall) {
other.GetComponent</*YourScriptName*/>().spawnNewBall = false;
}
ballPosition = new Vector3 ((transform.position.x + other.transform.position.x) / 2, (transform.position.y + other.transform.position.y) / 2, 0.0f);
StartCoroutine ("RespawnBall");
}
}
IEnumerator RespawnBall () {
if (spawnNewBall) {
Instantiate (gameObject, ballPosition, Quaternion.identity);
}
Destroy (gameObject);
yield return null;
}
More simply, just do this:
public class TwoToOne : MonoBehaviour {
public bool doNothing;
void OnCollisionEnter (Collision col)
{
if (doNothing) return;
col.gameObject.GetComponent<TwoToOne>().doNothing = true;
Destroy(col.gameObject);
GameObject newCube = Instantiate(gameObject);
Destroy(gameObject);
}
}
That's a totally common script or pattern in Unity.
So, conceptually you just "destroy the other one"; since one of the scripts has to run first, it works out fine. In different systems from Unity you can do one of these things:
A, "destroy" the other game object in some way. That would be DestroyImmediate(col.gameObject) in Unity.
B, "destroy" or disable the other script in some way. that would be col.gameObject.GetComponent<TwoToOne>().enabled = false in Unity
C, "flag" (set a boolean) on the other game object in some way - as shown here.
As it happens, in Unity specifically you can't do A or B, so you just do C. B is the most elegant solution, but it doesn't work in Unity5, so for now do C!
You can solve this with the use of delegates OR as #JoeBlow suggested, the use of UnityEvents from an Event Manager.
In the following example I have opt'd to use delegates.
There are 2 scripts. One which sits on the spheres (which you already have) and the second, a static class which can be referenced from any other script in the application without instantiation.
EventManager.cs
using UnityEngine;
using System.Collections;
public static class EventManager{
// Create our delegate with expected params.
// NOTE params must match SphereScript.PostCollision declaration
public delegate void CollideEvent(string message);
// Create the delegate instance. This is the one we will invoke.
public static event CollideEvent PostCollision;
// Called whenever an object has collided with another
public static void Collision(GameObject obj1, GameObject obj2, Vector3 collisionPoint){
if (obj1.GetComponent<sphereScript>().isAlive && obj2.GetComponent<sphereScript>().isAlive) {
//Kill the 2 objects which haev collided.
obj1.GetComponent<sphereScript> ().Kill ();
obj2.GetComponent<sphereScript> ().Kill ();
//Create a cube.
GameObject cube = GameObject.CreatePrimitive (PrimitiveType.Cube);
cube.transform.position = collisionPoint;
// Invoke delegate invocation list
PostCollision("Something is dead");
}
}
}
SphereScript .cs
using UnityEngine;
using System.Collections;
public class sphereScript : MonoBehaviour {
// Am I alive?
public bool isAlive;
// Use this for initialization
void Start () {
// Add a function we want to be called when the EventManager invokes PostCollision
EventManager.PostCollision += PostCollision;
isAlive = true;
}
// Update is called once per frame
void Update () {
}
//Invoked from EventManager.PostCollision delegate
void PostCollision(string message){
if(isAlive)
Debug.Log (this.name + " message received: " + message);
}
// Called when it is time to destroy this gameobject
public void Kill(){
isAlive = false;
Destroy (this.gameObject);
}
//On collision with another object
void OnCollisionEnter2D(Collision2D collision){
if (collision.gameObject.GetComponent<sphereScript>()) {
EventManager.Collision (this.gameObject, collision.gameObject, collision.contacts [0].point);
}
}
// Called after this object has been destroyed
void OnDestroy(){
// cleanup events for performance.
EventManager.PostCollision -= PostCollision;
}
}
So why use this method?
This method is ideal for controlling the release of multiple 'happenings' from a single location. In this case, we trigger a function on each ball each of the colliding balls. The EventManager then creates a single cube in their wake and following this, notifies any remaining cubes that a collision has occurred elsewhere in the scene.
what you can do is create another script which only destroys the ball but not spawn and attach it to the 2nd ball. the first ball will have both destroy and respawn feature. so when both balls destroy each other, one will respawn and the other will not because you did not attach the spawn feature to the 2nd ball

Categories

Resources