Unity c# mario game - c#

I am following this youtube series that creates a Unity 3D Mario game.
( https://www.youtube.com/watch?v=vjL3S5dKLN4&index=6&list=PLZ1b66Z1KFKgm4QrzZ11jfaeHVaWHSHW7 )
So I am trying to recreate the "going down the pipe" script like on the Mario game. I create an Animator and I am trying to trigger the animation using a C# script on key down press.
1. The problem:
This guys writes his scripts in Javascript and apparently my Unity version does no longer support js so I need to write it in C# ( and I prefer writing it in C#).
Basically I need to convert this js to c#:
var PipeEntry : GameObject;
var StoodOn : int;
function OnTriggerEnter (col : Collider) {
StoodOn = 1;
}
function OnTriggerExit (col : Collider) {
StoodOn = 0;
}
function Update () {
if (Input.GetButtonDown("GoDown")) {
if (StoodOn == 1) {
//GameObject.Find("FPSController").GetComponent("FirstPersonController").enabled=false;
transform.position = Vector3(0, -1000, 0);
WaitingForPipe();
}
}
}
function WaitingForPipe () {
PipeEntry.GetComponent("Animator").enabled=true;
yield WaitForSeconds(2);
PipeEntry.GetComponent("Animator").enabled=false;
//GameObject.Find("FPSController").GetComponent("FirstPersonController").enabled=true;
}
2. MY CODE
My script currently looks like this:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//
using UnityStandardAssets.Characters.FirstPerson;
public class PipeEntry : MonoBehaviour
{
public GameObject pipe_entry;
public int StoodOn;
public int has_run = 0;
public int waiting_pipe = 0;
public int waiting_pipe_animation = 0;
IEnumerator OnTriggerEnter(Collider col)
{
StoodOn = 1;
yield return StoodOn;
}
IEnumerator OnTriggerExit(Collider col)
{
StoodOn = 0;
yield return StoodOn;
}
IEnumerator WaitPipeAnimation()
{
waiting_pipe_animation = 1;
yield return new WaitForSeconds(2);
}
// Update is called once per frame
void Update()
{
if (Input.GetButtonDown("PipeDown"))
{
if (StoodOn == 1)
{
// Freeze player
//GameObject.Find("Player").GetComponent<FirstPersonController>().enabled = false;
transform.position = new Vector3(0, -1000, 0);
WaitingForPipe();
has_run = 1;
}
}
}
public void WaitingForPipe()
{
pipe_entry.GetComponent<Animator>().enabled = true;
WaitPipeAnimation();
pipe_entry.GetComponent<Animator>().enabled = false;
waiting_pipe = 1;
//GameObject.Find("Player").GetComponent<FirstPersonController>().enabled = true;
}
// Start is called before the first frame update
void Start()
{
}
}
3. Facts
The animation works good if triggered directly from Unity, so the script does not work.
I set some debug variables that are set to value 1 too where script breaks and waiting_pipe_animation is the only one that doesn't reach.
4. UPDATE
- So basically the main issue is with this function:
yield return new WaitForSeconds(2);
If I place it in a void function I get this:
The body cannot be a iterator block because void is not a iterator
interface type.
If I set animation to true and never set it back to false, it works - but not as expected because obviously animation goes forever.

1. The functions OnTriggerEnter and OnTriggerExit don't return IEnumerator and aren't coroutines.
private void OnTriggerEnter(Collider col)
{
StoodOn = 1;
}
private void OnTriggerExit(Collider col)
{
StoodOn = 0;
}
2. If the character is going to have more than one animation, I suggest you to create transitions between the animations and create variables to control it. There are some useful functions, like SetFloat, SetInteger, SetBool, SetTrigger and so on. If not, enabling and disabling the animator like you did will be fine.
3. As stated by Okeme Christian, it's required to use StartCoroutine("WaitPipeAnimation") to call your coroutine.
4. pipe_entry.GetComponent<Animator>().enabled = false; should be inside the WaitPipeAnimation function, because all the code ouside it will still be executed in the same frame. So basically you are activating and deactivating the animator in the same frame and won't be able to see the animation.
IEnumerator WaitPipeAnimation()
{
waiting_pipe_animation = 1;
yield return new WaitForSeconds(2);
pipe_entry.GetComponent<Animator>().enabled = false;
}
If you want to understand better how it works, please test this code below:
void Start()
{
StartCoroutine("CoroutineTest");
Debug.Log("3");
}
IEnumerator CoroutineTest()
{
Debug.Log("1");
yield return new WaitForSeconds(5f);
Debug.Log("2");
}
Note that the print order in the console will be "1", "3", "2".

You cant call a coroutine as a function. Instead you start a coroutine like this
StartCoroutine("Fade");
Where Fade is the coroutine name, so in your case you will start it like this
StartCoroutine("WaitPipeAnimation")

Related

How do I get my collision function into my private void Update() on Unity?

I have object 1 with rigid body and box collider, I want it to hit object 2 (also has collider and rigid body) so I can use the collision to stop object 1 for 5 seconds then have it move through object 2 whish has kinematics on
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TRAINMOVE : MonoBehaviour
{
private int speed = 200;
private Rigidbody rb;
// Start is called before the first frame update
void Start()
{
StartCoroutine(WaitBeforeMove());
}
// Update is called once per frame
private void Awake()
{
rb = GetComponent<Rigidbody>();
}
private void Update()
{
rb.AddForce(transform.up * speed * Time.deltaTime);
}
//void OnCollision(Collision stop)
// {
// if (stop.gameObject.name == "train trigger station1")
// {
// speed = 0;
// WaitBeforeMove();
// speed = 200;
// rb.AddForce(transform.up * speed * Time.deltaTime);
// }
// else
// {
// speed = 200;
// }
// }
IEnumerator WaitBeforeMove()
{
yield return new WaitForSeconds(5);
}
}
Thats not exactly how Coroutines work.
The method/message you are looking for is called OnCollisionEnter. If it is not exactly written like that and with the expected signature then Unity doesn't find it and never invokes it.
A Coroutine does not delay the outer method which runs/starts it.
A Coroutine has to be started using StartCoroutine otherwise it does nothing (or at least it only runs until the first yield statement).
private void OnCollisionEnter(Collision stop)
{
if (stop.gameObject.name == "train trigger station1")
{
StartCoroutine(WaitBeforeMove());
}
}
private IEnumerator WaitBeforeMove()
{
speed = 0;
// Note that without this you would have no force applied but still the remaining velocity it already has
rb.velocity = Vector3.zero;
yield return new WaitForSeconds(5f);
speed = 200;
}
The method/message OnCollisionEnter can be a Courine itself! So in your specific case you actually can just do
// By making this an IEnumerator Unity automatically starts it as a Coroutine!
private IEnumerator OnCollisionEnter(Collision stop)
{
if (stop.gameObject.name == "train trigger station1")
{
speed = 0;
rb.velocity = Vector3.zero;
yield return new WaitForSeconds(5f);
speed = 200;
}
}
Finally instead of permanently continue to add 0 force I would in general rather use a simple bool flag and pause the adding of force entirely.
You should AddForce not every frame but rather in FixedUpdate.
And then you should not multiply force by Time.deltaTime. Since it is applied in fixed time intervals in FixedUpdate it is already a frame-rate independent event.
private bool stopped;
private void FixedUpdate()
{
if(stopped) return;
rb.AddForce(transform.up * speed);
}
// By making this an IEnumerator Unity automatically starts it as a Coroutine!
private IEnumerator OnCollisionEnter(Collision stop)
{
if (stop.gameObject.name == "train trigger station1")
{
stopped = true;
rb.velocity = Vector3.zero;
yield return new WaitForSeconds(5f);
stopped = false;
}
}
I suggest instead of comparing a name you should rather use a Tag and use stop.gameObject.CompareTag("Station")

Unity - deleting a specific gameobject when mouse/pointer is over it

I am creating a virtual reality game where when you double click on an object it deletes it. However multiple objects are duplicates of eachother so when i attach my double click script to them it will delete all the objects upon double click. I want it to just delete the one the mouse is on. I will attach my script below:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class doubleClick : MonoBehaviour
{
private float firstClickTime, timebetweenClicks;
private bool coroutineAllowed;
private int clickCounter;
public GameObject toDelete;
// Start is called before the first frame update
void Start()
{
firstClickTime = 0f;
timebetweenClicks = 0.2f;
clickCounter = 0;
coroutineAllowed = true;
}
// Update is called once per frame
void Update()
{
if (Input.GetMouseButtonUp(0))
clickCounter += 1;
if (clickCounter == 1 && coroutineAllowed)
{
firstClickTime = Time.time;
StartCoroutine(DoubleClickDetection());
}
}
private IEnumerator DoubleClickDetection()
{
coroutineAllowed = false;
while (Time.time < firstClickTime + timebetweenClicks)
{
if (clickCounter == 2)
{
//Destroy(toDelete);
break;
}
yield return new WaitForEndOfFrame();
}
clickCounter = 0;
firstClickTime = 0f;
coroutineAllowed = true;
}
}
I can't see anything that jumps out in your code. Padia's answer is on the right track, though. Mouse input handling when you want to interact with objects is best done from within that object/prefab. That way, you're guaranteed that only that object is interacted with. If not, then there's likely something else going on with your code.
Unfortunately, it's a little late for me to work out a complete solution. But here's a possible solution.
Within the prefab's code, insert an OnMouseDown function. In that function, check if the double-click timer's been started. If not, set a flag to tell the prefab that the double-click timer needs to start (call it WaitingForTimerStart). You could alternatively use an enum to hold the timer state. In any event, don't start the timer yet.
Within the prefab's code, insert an OnMouseUp function. That should check if the timer start flag's (WaitingForTimerStart) been set. If it has, set another timer flag to tell it to run the timer (RunTimer).
In the prefab's Update function, run the timer. I'm sure you know what to do here.
In the prefab's OnMouseDown function (the one you added earlier), check if the timer's running. If it is, then you know a double-click's been performed. Destroy the object.
This way, there's no need to check names, use raycasting or coroutines. Each object will have its own timer and be fully independent of each other.
One main issue with your code is: You are starting the Coroutine everyframe!
You have to wrap your code block in { } after the if, otherwise the condition only applies for the one line after it!
Instead of
if (Input.GetMouseButtonUp(0))
clickCounter += 1;
if (clickCounter == 1 && coroutineAllowed)
{
firstClickTime = Time.time;
StartCoroutine(DoubleClickDetection());
}
it should probably rather be
if (Input.GetMouseButtonUp(0))
{
clickCounter += 1;
if (clickCounter == 1 && coroutineAllowed)
{
firstClickTime = Time.time;
StartCoroutine(DoubleClickDetection());
}
}
Then
I want it to just delete the one the mouse is on.
Well currently you are using Input.GetMouseButtonUp which is true global in the entire app, not only the object the mouse is currently over.
You can rather use e.g. OnMouseDown which is called when the mouse goes down over a collider or UI element.
private void OnMouseDown ()
{
clickCounter += 1;
if (clickCounter == 1 && coroutineAllowed)
{
firstClickTime = Time.time;
StartCoroutine(DoubleClickDetection());
}
}
Try this code:
private float firstClickTime, timebetweenClicks;
private bool coroutineAllowed;
private int clickCounter;
RaycastHit hit;
// Start is called before the first frame update
void Start()
{
firstClickTime = 0f;
timebetweenClicks = 0.2f;
clickCounter = 0;
coroutineAllowed = true;
}
private void OnMouseUp()
{
clickCounter += 1;
}
// Update is called once per frame
void Update()
{
if (clickCounter == 1 && coroutineAllowed)
{
firstClickTime = Time.time;
StartCoroutine(DoubleClickDetection());
}
}
private IEnumerator DoubleClickDetection()
{
coroutineAllowed = false;
while (Time.time < firstClickTime + timebetweenClicks)
{
if (clickCounter == 2)
{
if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out hit))
{
if (hit.collider.transform == this.transform)
{
Destroy(this.gameObject);
}
}
break;
}
yield return new WaitForEndOfFrame();
}
clickCounter = 0;
firstClickTime = 0f;
coroutineAllowed = true;
}
This code use OnMouseDown() Unity function to detect when the game object is clicked by the mouse, and add 1 to the clickCounter variable.
Also you can use OnMouseUp() function if you prefer to detect the click when the mouse is up.

How can I create a boundary using C# code only?

I am trying to create a unity script that detects when the player has left a certain area using C# code only (no boundary boxes). My code should play a sound once the player leaves but the code is not working as expected and I cannot see a fault in the logic.
Expected behaviour
When the player steps outside the boundary, a sound will play once.
Actual Behaviour
The sound plays all the time no matter where the player is.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EX2BoundaryDetect : MonoBehaviour {
// create a GameObject to represent the player
public GameObject playerObject;
public bool leaveArea = false;
// create an Audio-clip object. This assumes we drag a sound file onto the myclip box in the Inspector.
public AudioClip myclip;
// Use this for initialization
void Start () {
// associate playerObject with the player. This assumes the First Person Controller is name "player
playerObject = GameObject.Find ("player");
// get the actual Sound file from the Inspector
GetComponent<AudioSource>().clip = myclip;
}
// Update is called once per frame
void Update () {
PlayAudio1();
if (leaveArea) {
GetComponent<AudioSource> ().Play ();
}
}
public void PlayAudio1 () {
// play the sound if the player is outside some boundaries
if (transform.position.x > 10) {
leaveArea = true;
}
if (transform.position.x < -29) {
leaveArea = true;
}
if (transform.position.z > 10) {
leaveArea = true;
}
if (transform.position.z < -29) {
leaveArea = true;
}
}
}
leaveArea is only set to false in the class definition. Once set to true, it may never be set to false again, and it may be inadvertently overwritten in the scene definition in the first place.
To fix this issue, set it to false at the beginning of Update:
void Update () {
leaveArea = false;
PlayAudio1();
if (leaveArea) {
GetComponent<AudioSource> ().Play ();
}
}
Also, GetComponent is an expensive action and it is good to avoid calling it in Update wherever possible. Therefore, you may want to move it into a class property and set it once in Start:
private AudioSource audioSource;
void Start () {
// associate playerObject with the player. This assumes the First Person Controller is name "player
playerObject = GameObject.Find ("player");
audioSource = GetComponent<AudioSource>();
// get the actual Sound file from the Inspector
audioSource.clip = myclip;
}
// Update is called once per frame
void Update () {
leaveArea = false;
PlayAudio1();
if (leaveArea) {
audioSource.Play ();
}
}
Got it working now, by adding
leaveArea = false;
to the Update method.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EX2BoundaryDetect : MonoBehaviour {
// create a GameObject to represent the player
public GameObject playerObject;
public bool leaveArea = false;
// create an Audio-clip object. This assumes we drag a sound file onto the myclip box in the Inspector.
public AudioClip myclip;
// Use this for initialization
void Start () {
// associate playerObject with the player. This assumes the First Person Controller is name "player
playerObject = GameObject.Find ("player");
// get the actual Sound file from the Inspector
GetComponent<AudioSource>().clip = myclip;
}
// Update is called once per frame
void Update () {
PlayAudio1();
if (leaveArea) {
GetComponent<AudioSource> ().Play ();
}
leaveArea = false;
}
public void PlayAudio1 () {
// play the sound if the player is outside some boundaries
if (transform.position.x > 10) {
leaveArea = true;
}
if (transform.position.x < -29) {
leaveArea = true;
}
if (transform.position.z > 10) {
leaveArea = true;
}
if (transform.position.z < -29) {
leaveArea = true;
}
}
}
I think the above answers still do not solve your problem of "When the player steps outside the boundary, a sound will play once."
I will share a cleaner way of doing it (possibly optimized)
Using Bounds
Create our own bounds by using Bounds https://docs.unity3d.com/ScriptReference/Bounds.html
The constructor of Bounds is Bounds(Vector3 center,vector3 size)
So in your case center will be center = (-9.5f, 0, -9.5f) (midpoint formula) and finally the size of the bounding box will be size = (39.0f, 0f, 39.0f)
Let's move to the Code Part
public class BoundsCheck: MonoBehaviour
{
public bool isInside = false;
public bool isPlayedOnce = false;
private Bounds bounds;
void Start()
{
bounds = new Bounds(new Vector3(-9.5f, 0, -9.5f), new Vector3(39.0f, 0f, 39.0f));
}
void Update()
{
CheckBounds();// play the sound if the player is outside some boundaries
if (!isInside &&!isPlayedOnce)
{
Debug.Log("PLAY");
isPlayedOnce = true;
}
}
private void CheckBounds()
{
bool isInsideBound = bounds.Contains(transform.position);
if (isInsideBound)
{
isInside = true;
if (isPlayedOnce)
{
isPlayedOnce = false;
}
}
else
{
isInside = false;
}
}
}
So instead of multiple ifs, we can just check if the position of the transform is inside the bound by using bool isInsideBound = bounds.Contains(transform.position);
Note:- To play sound only once, I have used one more boolean isPlayedOnce
I am not sure if this the most optimized way of doing it. But surely a cleaner way(via code).

Coroutine is not working fine - Unity [C#]

i'm using Unity to track some image target (ARCamera - Vuforia) and everything works fine. I've inserted some 2D sprites on the image target etc... but now i don't want to show all the targets as soon as the image target is found, so i went to DefaultTrackableEventHandler.cs and instantiated my custom class Example like this:
#region PRIVATE_MEMBER_VARIABLES
private TrackableBehaviour mTrackableBehaviour;
#endregion // PRIVATE_MEMBER_VARIABLES
public Example mainClass;
...
private void OnTrackingFound()
{
...
Debug.Log("Trackable " + mTrackableBehaviour.TrackableName + " found");
mainClass = new Example();
mainClass.OnTrackableFound(); // Public method on 'Example' Class
}
Now on my Example class:
public class Example : MonoBehaviour {
public SpriteRenderer forYou;
public SpriteRenderer map;
public Example () {}
private bool isInTransition = false;
private float transition = 1;
private bool isShowing = false;
private float duration = 0;
void Start()
{
forYou = new SpriteRenderer();
map = new SpriteRenderer();
}
public void OnTrackableFound() {
StartCoroutine(FadeCoroutine());
}
private IEnumerator FadeCoroutine() {
yield return new WaitForSeconds(1);
Fade(true, .1f);
}
public void OnTrackableLost() {}
public void Fade (bool showing, float duration)
{
isShowing = showing;
isInTransition = true;
this.duration = duration;
transition = (isShowing) ? 0 : 1;
}
void Update ()
{
if (Input.GetMouseButtonDown (0)) {
Fade (true, .1f);
}
if (Input.GetMouseButtonUp (0)) {
Fade (false, .1f);
}
if (!isInTransition) {
return;
}
transition += (isShowing) ? Time.deltaTime * (1 / duration) : -Time.deltaTime * (1 / duration);
forYou.color = Color.Lerp (Color.white, new Color (1, 1, 1, 0), transition);
map.color = Color.Lerp (Color.white, new Color (1, 1, 1, 0), transition);
if (transition > 1 || transition < 0) {
isInTransition = false;
}
}
}
Sorry for the amount of code, but basically this is just for fading in/out some sprites. I want to yield as soon as my OnTrackingFound is called at Example class and after idk .5 seconds fade in some of my sprites.
Thank you!
EDIT - SOLVED
So, to attach a script to a game object it must inherit from Monobehaviour but than it cannot be instantiated so what i did was:
I needed the OnTrackingFound() from the DefaultTrackerHandler... so i coded everything on that class [i know it's far from the best solution but...] instead of instantiate another script because as i said it wouldn't be possible to attach it to a game object since it couldn't inherit from Monobehaviour. With public game objects, i attached everything directly from this class that also inherits from Monobehvr. If you instantiate a script that inherits monobehv it will be null. Thanks everyone!
The issue with your code is that you try to instantiate component Example that derives from MonoBehaviour calling constructor with new keyword.
Never do this.
All components in Unity3D Engine needs to be added to existing GameObject with GameObject.AddComponent<T>()
So what you need to do is:
private void OnTrackingFound()
{
...
Debug.Log("Trackable " + mTrackableBehaviour.TrackableName + " found");
// create new GameObject and add component
mainClass = new GameObject("MyNewGameobject").AddComponent<Example>();
mainClass.OnTrackableFound(); // Public method on 'Example' Class
}
Seems like you are calling mainClass = new Example(); every time a new target is tracked. This will create a new object even-though there might be an existing objects. I don't believe you want this.
Instead try call the GameObject gobj = GameObject.Find() (because the GameObject already exists right?) function to get the existing GameObject that the script is attached to. Then do a gobj.GetComponent<Example>(). OnTrackableFound() to call the event on the GameObject. Hope that helps.
I've answered a question which needs a delay like this in gamedev.
Basically you will increment a float value overtime, and when desired amount of time passed you will fade the sprite.
Pseudo:
float counter;
bool isFadeReady = false;
void Update ()
{
if (OnTrackingFound)
{
isFadeReady = true;
}
if (isFadeReady == true)
{
counter +=0.1f; //change the value for increase speed
}
if(counter>=1.0f) //change the value for desired time
{
//Fade your sprite here
isFadeReady = false;
}
}
Hope this helps! Cheers!

Prefab doesnot activate when collide with object

I'm making a project and there is a problem that I am facing.
I have two gameObject with 2D colliders (coming from a prefab) which moves right to left. When they touch each other they deactivate.
I also have an empty game object in which i add a script Respawner which randomly generates obstacles.
The problem is when they touch each other once, they never get re-activated again.
Respawner Empty GameObject :
Border :
Prefabs :
Respawn Script:
public class Respawn : MonoBehaviour {
[SerializeField]
private GameObject[] obstacles;
private List<GameObject> listname = new List<GameObject>();
void Awake(){
InitilizeObstacle();
}
void Start() {
StartCoroutine(RandomObstacleSpawn());
}
void InitilizeObstacle(){
int index = 0;
for (int i=0; i<obstacles.Length * 3 ; i++) {
GameObject obj = Instantiate(obstacles[index],new Vector3(transform.position.x,transform.position.y,-2f),Quaternion.identity) as GameObject;
listname.Add(obj);
listname[i].SetActive(false);
index++;
if(index==obstacles.Length){
index =0;
}
}
}
void shuffle(){
for (int i=0; i<listname.Count; i++) {
GameObject temp = listname [i];
int random = Random.Range (i, listname.Count);
listname [i] = listname [random];
listname [random] = temp;
}
}
IEnumerator RandomObstacleSpawn(){
yield return new WaitForSeconds(Random.Range(1.5f,2.5f));
int index = Random.Range (0, listname.Count);
while (true) {
if(!listname[index].activeInHierarchy){
listname[index].SetActive(true);
listname[index].transform.position = new Vector3(transform.position.x,transform.position.y,-2f);
break;
} else {
index = Random.Range(0,listname.Count);
}
StartCoroutine(RandomObstacleSpawn());
}
}
}
Script attach to prefab for move:
public class ObstacleMove : MonoBehaviour {
private float speed = -1.25f;
void Start() { }
void Update() {
Vector3 pos = transform.position;
pos.x += speed * Time.deltaTime;
transform.position = pos;
}
}
Scripts attach to prefab for touch border:
public class BorderTouch : MonoBehaviour {
void OnTriggerEnter2D(Collider2D target){
if(target.tag=="Border"){
gameObject.SetActive(false);
}
}
}
New answer based on new question:
If you want to make things happen AFTER the collision. Put a script on Border:
using UnityEngine;
using System.Collections;
public class borderListener : MonoBehaviour {
public Respawn rS;
void OnTriggerEnter2D(Collider2D target){
rS.spawnIt ();
}
}
On Unity Editor, drag the Respawn object to the Border Script on hierarchy.
Do not skip this step or things won't work!
On Respawn script, remove the last StartCoroutine(RandomObstacleSpawn()); line on IEnumerator RandomObstacleSpawn() method. And create a public method (to access from other script) anywhere inside Respawn script:
public void spawnIt(){
StartCoroutine(RandomObstacleSpawn());
}
Old answer based on old code:
From what I see on your package:
while (true) { //A
if(!listname[index].activeInHierarchy){
//B
listname[index].SetActive(true);
listname[index].transform.position = new Vector3(transform.position.x,transform.position.y,-2f);
break; //C
} else {
index = Random.Range(0,listname.Count);
}
StartCoroutine(RandomObstacleSpawn()); //D
}
I am a lil noob, I will try my best to help. But this piece of code makes me wonder:
while(true) what? what is true? (EDIT: found some observation bellow)
The code seem to do this path:
Go inside the while loop (A)
Go at the first option in if statement (B)
Go to the line break; (C)
Never reaches the StartCoroutine (D) > that is why it does not activate again.
If you try and put a StartCoroutine(RandomObstacleSpawn()); before the break; you probably will get an Unity crash. What if you take off the while statement at all? You need to adjust time of yield tho.
This is the code I would use:
IEnumerator RandomObstacleSpawn(){
yield return new WaitForSeconds(Random.Range(3.5f,4.5f));
int index = Random.Range (0, listname.Count);
if(!listname[index].activeInHierarchy){
listname[index].SetActive(true);
listname[index].transform.position = new Vector3(transform.position.x,transform.position.y,-2f);
}else{
index = Random.Range(0,listname.Count);
}
StartCoroutine(RandomObstacleSpawn());
}
EDIT: about the while(true) I've manage to find more information about this concept here: R, How does while (TRUE) work?
But still... the break; on the code is really making the Access to StartCoroutine(RandomObstacleSpawn()); unreachable .
The problem you are seeing is fairly common, when the following code is executed gameObject.SetActive(false); it deactivate everything on that gameObject.
Thus, the next time they would have collided will never be triggered.
I'm not sure what you want to achieve for the behaviour, but if you just want to hide the gameObjects you could disable the Renderer component instead.
gameObject.GetComponent<SpriteRenderer>().enable = false;
And switch it to true when needed.
That's why we must be carefull when we use SetActive(false) on gameObject in Unity.

Categories