Using Coroutine in Unity - c#

Am working on Unity and here is what I want to do: play the animationType in a time difference of 10sec. I want the code to loop through the animations and play them each for 10seconds. The code runs without errors, except the result is not what I expected it to be. It plays the first animation,Boxing, for 10 seconds and just when it's about to play the Backflip animation, it starts to do some weird thing to the character. That's where is goes wrong.
Here is my code:
public class BeBot_Controller : MonoBehaviour
{
Animator anim;
string animationType;
string[] split;
int arrayLength;
void Start()
{
//AndroidJavaClass pluginClass = new AndroidJavaClass("yenettaapp.my_bebot_plugin.My_BeBot_Plugin");
//animationType = pluginClass.CallStatic<string>("getMessage");
animationType="Null,Boxing,Backflip";
split = animationType.Split(',');
anim = gameObject.GetComponentInChildren<Animator> ();
arrayLength = split.Length;
}
// Update is called once per frame
void Update () {
if (arrayLength > 1){
StartCoroutine ("LoopThroughAnimation");
}
}
IEnumerator LoopThroughAnimation()
{
for (int i = 1 ; i < arrayLength; i++) {
animationType = split [i];
//anim.SetInteger ("AnimPar", 0);
anim.Play (animationType);
yield return new WaitForSeconds (10);
}
}
}
So what did I do wrong here ? Is there any other way I can solve this problem ?

Since your animation loop only needs to be called once, simply move StartCoroutine() to Start() and remove the Update() stuff:
public class BeBot_Controller : MonoBehaviour
{
private Animator anim;
private string animationType;
private string[] split;
private int arrayLength;
void Start ()
{
//AndroidJavaClass pluginClass = new AndroidJavaClass("yenettaapp.my_bebot_plugin.My_BeBot_Plugin");
//animationType = pluginClass.CallStatic<string>("getMessage");
animationType = "Null,Boxing,Backflip";
split = animationType.Split(',');
anim = gameObject.GetComponentInChildren<Animator>();
arrayLength = split.Length;
// Call here
StartCoroutine(LoopThroughAnimation());
}
IEnumerator LoopThroughAnimation ()
{
for (int i = 1; i < arrayLength; i++)
{
animationType = split[i];
Debug.Log(animationType);
//anim.SetInteger ("AnimPar", 0);
anim.Play(animationType);
yield return new WaitForSeconds(10);
}
}
}

Related

Unity: Fast-Forward Type Writer Effect upon Keypress

For the cutscenes of a 2D platformer game, I wrote a script that shows the text like it's written by a typewriter. Since the text can be very long, I want to implement an option for the user to fast-forward/skip the animation and show the full text upon a keypress. This is what I have right now:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class TypeWriter : MonoBehaviour
{
public float delay = 0.05f;
public string fullText;
private string currentText = "";
// Start is called before the first frame update
void Start()
{
StartCoroutine(ShowText());
}
IEnumerator ShowText()
{
for (int i = 0; i < fullText.Length + 1; i++)
{
currentText = fullText.Substring(0, i);
this.GetComponent<Text>().text = currentText;
yield return new WaitForSeconds(delay);
}
}
}
Can someone help me please? I'm new to unity as well as C#.
Setting a flag should do the trick:
bool skip = false;
IEnumerator ShowText()
{
for (int i = 0; i < fullText.Length + 1; i++)
{
currentText = fullText.Substring(0, i);
this.GetComponent<Text>().text = currentText;
if (!skip)
yield return new WaitForSeconds(delay);
}
}
Just set skip to true at some point and it will cause the loop to not yield which will result in the loop full completing (a minor optimisation would be to just not loop when the skip flag is set and just fill the text with the remaining letters instead, but I doubt you will have performance issues even with the loop as it's so simple).
You can set the flag to true based on receiving a keypress/some input.
void Start()
{
textRef = GetComponent<TextMeshProUGUI>();
fullText = textRef.text;
GetComponent<TextMeshProUGUI>().text = "";
if (typeOnStart)
StartCoroutine(ShowText(typingDelay));
}
public void StartTyping(float delay)
{
StartCoroutine(ShowText(typingDelay));
}
IEnumerator ShowText(float delay)
{
// float time = Time.deltaTime;
for (int i = 0; i <= fullText.Length; i++)
{
// Debug.Log("Typing");
currentText = fullText.Substring(0, i);
textRef.text = currentText;
yield return new WaitForSeconds(typingDelay);
}
// float time2 = Time.deltaTime;
// Debug.Log("Total Time Taken : " + (time2 - time));
}
}

Unity: Using a random.range result from inside an iterator (rolling dice)

There's plenty of tutorials on how to make a dice script but for some reason I can't find one that shows how to have something happen based on the roll. I know how to make random.range. But that's too simple for a dice because you want to make the dice appear that it's rolling or spinning through the sides before stopping. That seems to mean we use an iterator or IEnumerator or Coroutine and there lies the problem. Those don't spit out the int result we need (at least not in the script I'm using).
Here's the dice script I've been using from a tutorial. I haven't found a way to get the dice results to be visiable(usable) anywhere outside of the RollTheDice method.
using System.Collections;
using UnityEngine;
public class Dice : MonoBehaviour {
private Sprite[] diceSides;
private SpriteRenderer rend;
private void Start () {
rend = GetComponent<SpriteRenderer>();
diceSides = Resources.LoadAll<Sprite>("DiceSides/");
}
private void OnMouseDown()
{
StartCoroutine("RollTheDice");
}
// Coroutine that rolls the dice
private IEnumerator RollTheDice()
{
int randomDiceSide = 0;
int finalSide = 0;
for (int i = 0; i <= 20; i++)
{
randomDiceSide = Random.Range(0, 5);
rend.sprite = diceSides[randomDiceSide];
yield return new WaitForSeconds(0.05f);
}
finalSide = randomDiceSide + 1;
Debug.Log(finalSide);
}
}
You should define a method that has an int as a parameter which does whatever you need to do with the int, then call that method inside of the coroutine with the result of each roll, before you yield.
Then you could call another method after the rolling completes if you would like.
E.g.:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Dice : MonoBehaviour {
private Sprite[] diceSides;
private SpriteRenderer rend;
private List<int> results;
private void Start () {
rend = GetComponent<SpriteRenderer>();
diceSides = Resources.LoadAll<Sprite>("DiceSides/");
}
private void OnMouseDown()
{
StartCoroutine("RollTheDice");
}
private void UseResult(int result)
{
results.Add(result);
}
private void FinishResults()
{
foreach (int i : results)
{
Debug.Log(i);
}
}
// Coroutine that rolls the dice
private IEnumerator RollTheDice()
{
int randomDiceSide = 0;
int finalSide = 0;
for (int i = 0; i <= 20; i++)
{
randomDiceSide = Random.Range(0, 5);
rend.sprite = diceSides[randomDiceSide];
UseResult(randomDiceSide);
yield return new WaitForSeconds(0.05f);
}
FinishResults();
finalSide = randomDiceSide + 1;
Debug.Log(finalSide);
}
}
Note: Typed on phone, please excuse any syntax errors or other minor mistakes.
Yes, IEnumerator RollTheDice() displays the throughout rolling process till the dice stops. Its final result can be accessed by rend.sprite when RollTheDice is Done.

How can I generate the units at the same time?

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GenerateStairs : MonoBehaviour
{
[Header("Stairs Prefb")]
public GameObject stairsPrefab;
[Space(5)]
[Header("Platforms")]
public bool addPlatforms = false;
public GameObject platformsPrefab;
[Space(5)]
[Header("Settings")]
[Range(1, 20)]
public int numberOfUnits = 1;
public float delay = 3;
public int stairsNumber = 5;
public Vector3 stairsStartPosition;
public Vector3 stairSize;
public Vector3 stairsSize;
public float stepWidthFactor = 1f;
private Vector3 stairsPosition;
private GameObject stairsParent;
private int oldNumberOfUnits = 0;
// Use this for initialization
void Start()
{
oldNumberOfUnits = numberOfUnits;
for (int i = 0; i < numberOfUnits; i++)
{
stairsParent = new GameObject();
stairsParent.name = "Stairs";
StartCoroutine(BuildStairs());
}
}
// Update is called once per frame
void Update()
{
if(oldNumberOfUnits != numberOfUnits)
{
StartCoroutine(BuildStairs());
oldNumberOfUnits = numberOfUnits;
}
}
private IEnumerator BuildStairs()
{
for (int i = 1; i <= stairsNumber; i++)
{
stairsPosition = new Vector3(
stairsStartPosition.x,
stairsStartPosition.y + (i * stairsSize.y),
stairsStartPosition.z + (i * stairsSize.y) * stepWidthFactor);
GameObject stair = Instantiate(
stairsPrefab,
stairsPosition,
Quaternion.identity);
stair.tag = "Stair";
stair.transform.parent = stairsParent.transform;
stair.transform.localScale = stairSize;
yield return new WaitForSeconds(delay);
}
stairsParent.AddComponent<MoveObjects>().Init();
}
}
In the Start I'm doing a loop and start the Coroutine according to the numberOfunits.
It's working fine if numberOfUnits is 1. But is it's more then 1 for example 2 it's first creating the first set of stairs but then on the second "Stairs" parent it's creating only 1 stair. I don't want it to wait to finish the first Coroutine I want in the same time to create number of Coroutine's of stairs.
And I want also to add a gap between each stairs unit.
And also to make that in the Update if I change the numberOfUnits it will add/destroy more stairs units. All the stairs units should be Instantiate inside StartCoroutine.
You are mistaking how the coroutine works its not at thread. What is happening is your continually invoking the coroutine so its starting over and over again not creating a separate instance.
what you should do is create create a prefab and Instantiate that to do the work. My last remark was about threads but you wont be able to instantiate anything unless its on the main thread so the easiest way to get this done would be like so.
public GameObject yourGoWithAboveClassOnIt;
void Start()
{
oldNumberOfUnits = numberOfUnits;
for (int i = 0; i < numberOfUnits; i++)
{
Instantiate(yourGoWithAboveClassOnIt);
}
}
your prior class will remove this
void Start()
{
//oldNumberOfUnits = numberOfUnits;
//for (int i = 0; i < numberOfUnits; i++)
//{
stairsParent = new GameObject();
stairsParent.name = "Stairs";
StartCoroutine(BuildStairs());
//}
}

Unity what's wrong with my instantiating algorithm?

I dont know if I can call this algorithm. But I am working on a game in which player will move in a circular path.
As you can see in the picture player is suppose to orbit the circle. And obstacle shall be instantiated in the circle.I am trying to first create the obstacle in first half(left to the long cube) and then in the second half. But things are getting created in the next half too when code is not supposed to do that. Also, it is showing argument exception error. Please have a look at my code and tell me whether my method is wrong or my formulas are wrong or anything else.
public class ObjectInstantiater : MonoBehaviour {
DataHolder dataholder;
GameObject Obstacle;
LevelData leveldata;
private int currentlevel=0; // default level starts from 0
private List<GameObject> Inactivegameobject = new List<GameObject>(); // this object can be used
private List<GameObject> Activegameobject = new List<GameObject>();
private int totalgameobjects;
private int firsthalfgameobjects, secondhalfgameobjects;
public float outerradius;
public float innerradius;
private bool shallspawnouterradiues = true;
// Use this for initialization
void Awake () {
dataholder = (Object)GameObject.FindObjectOfType<DataHolder>() as DataHolder;
Obstacle = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
leveldata = dataholder.Leveldata[0];
}
void Start()
{
Updateleveldata();
FirstHalf();
}
public int Currentlevel
{
get { return currentlevel; }
set { currentlevel = value;
leveldata = dataholder.Leveldata[currentlevel];//sets the level data
}
}
private void Updateleveldata() // this function gets called after a round
{
totalgameobjects = Random.Range(leveldata.MinimumObstacle, leveldata.MaximumObstacle);
firsthalfgameobjects = Mathf.RoundToInt(totalgameobjects / 2);
secondhalfgameobjects = totalgameobjects - firsthalfgameobjects;
}
private void FirstHalf()
{
Debug.Log(firsthalfgameobjects);
Vector3 pos;
if (Inactivegameobject.Count < firsthalfgameobjects)
{
for (int x = 0; x <= (firsthalfgameobjects - Inactivegameobject.Count); x++)
{
GameObject obs = Instantiate(Obstacle) as GameObject;
obs.SetActive(false);
Inactivegameobject.Add(obs);
}
}
float spawnangledivision = 180 / firsthalfgameobjects;
float spawnangle = 180f;
for(int x = 0; x < firsthalfgameobjects; x++)
{
float proceduralRandomangle = spawnangle;
proceduralRandomangle = Random.Range(proceduralRandomangle , proceduralRandomangle + 2f);
if (shallspawnouterradiues)
{
pos = new Vector3(outerradius * Mathf.Cos(spawnangle), outerradius * Mathf.Sin(spawnangle), 0f);
shallspawnouterradiues = false;
}else
{
pos = new Vector3(innerradius * Mathf.Cos(spawnangle), innerradius * Mathf.Sin(spawnangle), 0f);
shallspawnouterradiues = true;
}
spawnangle += spawnangledivision;
Inactivegameobject[0].SetActive(true); // set it to 0
Inactivegameobject[0].transform.position = pos;
Activegameobject.Add(Inactivegameobject[0]);
Inactivegameobject.RemoveAt(0);
}
}
private void SecondHalf()// No need to check this
{
if (Inactivegameobject.Count < firsthalfgameobjects)
{
GameObject obs = Instantiate(Obstacle) as GameObject;
obs.SetActive(false);
Inactivegameobject.Add(obs);
}
}
}

C# - Unity crashes with this code - creating Transform array based on GameObject tag

I'm working in a 3D environment, a house, with 16 rooms. In each room I placed an invisible cube with a tag called "RoomsToScanTag". I'm working with an asset called Behavior Designer, and in my inspector I have given the tagname to look for (in this case "RoomsToScanTag"). With this tagname, I want the robot to go to room1 first, then continue to room2, up to room16.
This is my code
using UnityEngine;
using BehaviorDesigner.Runtime;
using BehaviorDesigner.Runtime.Tasks;
public class MoveTowardsNew : Action
{
private Transform[] roomsToScanHAHA;
public string targetTagRooms;
public Transform targetyo;
int i = 0;
int controlNumber = 0;
public override void OnAwake()
{
var targets = GameObject.FindGameObjectsWithTag (targetTagRooms);
roomsToScanHAHA = new Transform[targets.Length];
for (int i = 0; i < targets.Length; i++) {
roomsToScanHAHA [i] = targets [i].transform;
}
}
public override TaskStatus OnUpdate()
{
while (controlNumber < roomsToScanHAHA.Length)
{
targetyo = roomsToScanHAHA [controlNumber];
if (Vector3.SqrMagnitude (transform.position - targetyo.position) < 0.5f)
{
if (controlNumber < roomsToScanHAHA.Length) {
controlNumber++;
}
return TaskStatus.Success;
}
}
NavMeshAgent agent = GetComponent<NavMeshAgent> ();
agent.destination = targetyo.position;
return TaskStatus.Running;
}
}
My problem
Unity crashes... It has something to do with this code, I can't figure it out. Anyone has an idea what is going wrong?
Here is an image of what I'm dealing with:
Thanks in advance!
As usual, Its the while loop which is not terminating:
while (controlNumber < roomsToScanHAHA.Length)
{
targetyo = roomsToScanHAHA [controlNumber];
if (Vector3.SqrMagnitude (transform.position - targetyo.position) < 0.5f)
{
if (controlNumber < roomsToScanHAHA.Length) {
controlNumber++;
}
return TaskStatus.Success;
}
}
First run: controlNumber = 0;
lets say your first element in array roomsToScanHAHA doesn't match the condition : (Vector3.SqrMagnitude (transform.position - targetyo.position) < 0.5f). controlNumber wont be incremented and would keep checking the condition for the first element. Which results in infinite loop.
So you can change your code to something like this:
while (controlNumber < roomsToScanHAHA.Length)
{
targetyo = roomsToScanHAHA [controlNumber];
if (Vector3.SqrMagnitude (transform.position - targetyo.position) < 0.5f)
{
return TaskStatus.Success;
}
controlNumber++;
}
Hope this helps

Categories