Unity2D: Creating a wavy score text animation - unity3d

I want to animate my score text into a wavy animation each time the player picks up a coin as well as make another wavy text animation but going the opposite way each time the player loses some coins. I already have a code (shown below) to make a certain text do a wavy animation, however to do this I assigned each character of the text to either go up or down to initially make a wavy animation, as shown here:
.
My problem is: If I were to do this method for the score text, there is no telling how much coins the player will get and so it will make it difficult for me to estimate how much characters I would need for the score text as the possibility of the player adding a new digit to the score is kind of random.
So is there a way for me to create a wavy text animation to my score text? Thank you!
public GameObject WaveTextObj, text1, text2, text3, text4;
// Use this for initialization
void Start () {
text1.SetActive (false);
text2.SetActive (false);
text3.SetActive (false);
text4.SetActive (false);
StartCoroutine (WaveEffect ());
}
IEnumerator WaveEffect() {
while (true) {
WaveTextObj.SetActive (true);
yield return new WaitForSeconds (0.3f);
text1.SetActive (true);
yield return new WaitForSeconds (0.08f);
text2.SetActive (true);
yield return new WaitForSeconds (0.17f);
text3.SetActive (true);
yield return new WaitForSeconds (0.19f);
text4.SetActive (true);
yield return new WaitForSeconds(0.4f);
WaveTextObj.SetActive (false);
}
}
NEW UPDATE
text = "Testing";
string[] characters = new string[text.Length];
for (int i = 0; i < text.Length; i++)
{
characters[i] = text[i].ToString();
}

Use Mathf.Sin(Time.time)
public GameObject WaveTextObj;
public GameObject textPrefab;
IEnumerator WaveEffect(string text) {
//clear out the existing text
while(WaveTextObj.transform.childCount > 0) {
Destroy(WaveTextObj.transform.GetChild(0));
}
//create new text
float scalar = 0.1f;
float timeScalar = 3;
for (int i = 0; i < text.Length; i++)
{
GameObject o = Instantiate(textPrefab, new Vector3(i * scalar,0,0), Quaternion.Identity, WaveTextObj.transform)
o.GetComponent<Text>().text = text[i].ToString();
}
//animate text
while (true) {
int i = 0;
foreach (Transform child in transform) {
Vector3 p = child.localPosition;
// By setting each text object's y position to a value controlled by a sine wave,
// they will jiggle up and down. Their index in the string handles each letter being
// at a different point on the wave.
p.y = Mathf.Sin((Time.time + i * scalar) * timeScalar) * scalar;
child.localPosition = p;
i++;
}
yield return new WaitForEndOfFrame();
}
}
Up to you to figure out how you want exit the loop. Several values are arbitrary.

Related

Reduce Skid Mark Alpha Gradually

For my car war game, I have generated skid marks as the car takes a turn.
It's working properly, now after a few seconds passed, I want to reduce its alpha and remove it from the game. I have used trail renderer to generate skid marks.
what shader do I require to assign to the material?
which way I can reduce its alpha?
At present, I have used this kind of trail renderer material:
Now for reducing alpha gradually, I have this kind of code:
private IEnumerator Start()
{
Material myMaterial = GetComponent<Renderer>().material;
while (true)
{
yield return new WaitForSeconds(1f);
// check whether this skid trail has finished
// (the Wheel script sets the parent to null when the skid finishes)
if (transform.parent.name == "SkidTrailsDetachedParent")
{
// set the start colour
//Color startCol = GetComponent<Renderer>().material.color;
//Color startCol = myMaterial.GetColor("_EmisColor");
Color startCol = myMaterial.GetColor("_TintColor");
// wait for the persist time
yield return new WaitForSeconds(persistTime);
float t = Time.time;
// fade out the skid mark
while (Time.time < t + fadeDuration)
{
float i = Mathf.InverseLerp(t, t + fadeDuration, Time.time);
//myMaterial.color = startCol * new Color(1, 1, 1, 1 - i);
//myMaterial.SetColor("_EmisColor", startCol * new Color(1f, 1f, 1f, 1f - i));
myMaterial.SetColor("_TintColor", startCol * new Color(1f, 1f, 1f, 1f - i));
yield return null;
}
// the object has faded and is now done so destroy it
Destroy(gameObject);
}
}
}
Still, it's not working for me.
TrailRenderer has a property named endColor that you could set the alpha of the Color so that the line fades to the end.
void Start()
{
Color noAlphaEndColor = line.endColor;
line.endColor = new Color(noAlphaEndColor.r, noAlphaEndColor.g, noAlphaEndColor.b, 0.0f);
}
void Update()
{
if (line.endColor.a < 0.001)
{
Destroy(gameObject);
}
}

How to resize all particles from a particle system?

I'm trying to dynamically resize particles using a slider, as well as change their colour.
Particles are used to display datapoints in a 3D scatterplot. I'm using this code: https://github.com/PrinzEugn/Scatterplot_Standalone
private ParticleSystem.Particle[] particlePoints;
void Update () {
pointScale = sizeSlider.value;
for (int i = 0; i < pointList.Count; i++) {
Quaternion quaternion = Camera.current.transform.rotation;
Vector3 angles = quaternion.eulerAngles;
// Set point color
particlePoints[i].startColor = new Color(angles.x, angles.y, angles.z, 1.0f);
particlePoints[i].transform.localScale = new Vector3(pointScale, pointScale, pointScale);
}
}
The issue is that there's no transform method for Particles, and changing the "startColour" doesn't change anything.
The API states that "The current size of the particle is calculated procedurally based on this value and the active size modules."
What does that mean, and how can I change the size of the particles ?
Thanks to previous answers I managed to get this working:
In the PlacePrefabPoints method I add every instantiated prefab to a List, and I add a listener to the slider, which looks like this:
void changedPointSize(){
pointScale = sizeSlider.value;
for (int i = 0; i < objects.Count; i++) {
objects[i].transform.localScale = new Vector3(pointScale, pointScale, pointScale);
}
}
Thanks all !
I just had a look at PointRenderer.cs -> CreateParticles and PlacePrefabPoints give a good hint what has to be changed.
So I guess you would simply change the scale values
foreach (var point in particlePoints)
{
Quaternion quaternion = Camera.current.transform.rotation;
Vector3 angles = quaternion.eulerAngles;
// Set point color
point.startColor = new Color(angles.x, angles.y, angles.z, 1.0f);
point.startSize = sizeSlider.value;
}
and than re-call
GetComponent<ParticleSystem>().SetParticles(particlePoints, particlePoints.Length);
it is questionable though if you really would do this in Update. I would rather do it in sizeSlider.onValueChanged to only do it when neccesarry (you could even make a certain treshold that has to be changed before updating the view) but well for the color there might be no other option than doing it in Update but atleast there I would use a Threshold:
privtae ParticleSystem ps;
// I assume you have that referenced in the inspector
public Slider sizeSlider;
// flag to control whether system should be updated
private bool updateSystem;
privtae void Awake()
{
ps = GetComponent<ParticleSystem>();
}
private void OnEnable()
{
// add a listener to onValueChanged
// it is secure to remove it first (even if not there yet)
// this makes sure it is not added twice
sizeSlider.onValueChanged.RemoveListener(OnsliderChanged());
sizeSlider.onValueChanged.AddListener(OnsliderChanged());
}
private void OnDisable()
{
// cleanup listener
sizeSlider.onValueChanged.RemoveListener(OnsliderChanged());
}
private void OnSliderChanged()
{
foreach (var point in particlePoints)
{
point.startSize = sizeSlider.value;
}
// do the same also for the instantiated prefabs
foreach(Transform child in PointHolder.transform)
{
child.localScale = Vecto3.one * sizeSlider.value;
}
updateSystem = true;
}
private Quaternion lastCameraRot;
public float CameraUpdateThreshold;
private void Update()
{
if(Quaternion.Angle(Camera.current.transform.rotation, lastCameraRot) > CameraUpdateThreshold)
{
foreach (var point in particlePoints)
{
Quaternion quaternion = Camera.current.transform.rotation;
Vector3 angles = quaternion.eulerAngles;
// Set point color
point.startColor = new Color(angles.x, angles.y, angles.z, 1.0f);
}
lastCameraRot = Camera.current.transform.rotation;
updateSystem = true;
}
if(!updateSystem) return;
updateSystem = false;
ps.SetParticles(particlePoints, particlePoints.Length);
}

Fade in and out complex GameObject in Unity [duplicate]

I'm fairly new to coding, I'm still trying to develop that logic of thinking to help me create the solutions I'm wanting for games. Currently, I'm in Unity trying to create a 2D GameObject that's a wall hiding a secret door. I want that GameObject to fade out (about 90%) when the player GameObject triggers it, revealing the space behind and the hidden door.
So far, I've managed to figure out how to render the "secret wall" GO inactive on the trigger, so it disappears, but this doesn't produce the visual that I'm going for. As I said, I'm still working on developing that coder's way of thinking, so while I've done a lot of research to solve this problem, many of the results I don't readily understand.
Here's my code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SecretDoor1 : MonoBehaviour {
void OnTriggerEnter2D (Collider2D SecretDoorTrig) {
if (SecretDoorTrig.gameObject.tag == "Player") {
GetComponent<SpriteRenderer> ().enabled = false;
}
else {
GetComponent<SpriteRenderer> ().enabled = true;
}
}
void OnTriggerExit2D (Collider2D SecretDoorTrig) {
if (SecretDoorTrig.gameObject.tag == "Player") {
GetComponent<SpriteRenderer> ().enabled = true;
}
else {
GetComponent<SpriteRenderer> ().enabled = false;
}
}
}
Fading a Sprite is almost the-same as moving GameObject over time except that you modify its alpha instead of it's position.
The three most important stuff about fading an Object are Time.deltaTime, Mathf.Lerp/Color.Lerp and coroutine. You need to understand how these work together.
Start coroutine, use Time.deltaTime to increment a variable. That variable is used to use to determine how much that function has ran. In a for/while loop, use that variable that is incremented every-frame and the duration you want the fade to happen to generate the alpha with the help of the Mathf.Lerp function. Create new color with that alpha and and assign it to the Sprite.
This is done every frame until that variable that is incremented with Time.deltaTime reaches the duration you want to the fade to happen within.
Here is a simple SpriteRenderer fade function:
public SpriteRenderer spriteToFade;
IEnumerator fadeOut(SpriteRenderer MyRenderer, float duration)
{
float counter = 0;
//Get current color
Color spriteColor = MyRenderer.material.color;
while (counter < duration)
{
counter += Time.deltaTime;
//Fade from 1 to 0
float alpha = Mathf.Lerp(1, 0, counter / duration);
Debug.Log(alpha);
//Change alpha only
MyRenderer.color = new Color(spriteColor.r, spriteColor.g, spriteColor.b, alpha);
//Wait for a frame
yield return null;
}
}
If you want it to fade in, change Mathf.Lerp(1, 0, counter / duration); to Mathf.Lerp(0, 1, counter / duration); which will make the alpha go from 0 to 1 over-time instead of 1 to 0.
From the example above, writing a fade-out and fade-in functions only requires a way to tell the function to change the alpha from 1 to 0 or from 0 to 1. You can make the function use a boolean or enum variable to determine which type of fade to perform. Of-course, you can separate the fade-in/fade-out functions but it's good to have it in one function.
Here is the extended version of that function that supports fade-in and fade-out. It also supports almost all GameObjects like MeshRenderer(3D), SpriteRenderer(2D), Image, RawImage....You can extend it to support more components that's missing.
IEnumerator fadeInAndOut(GameObject objectToFade, bool fadeIn, float duration)
{
float counter = 0f;
//Set Values depending on if fadeIn or fadeOut
float a, b;
if (fadeIn)
{
a = 0;
b = 1;
}
else
{
a = 1;
b = 0;
}
int mode = 0;
Color currentColor = Color.clear;
SpriteRenderer tempSPRenderer = objectToFade.GetComponent<SpriteRenderer>();
Image tempImage = objectToFade.GetComponent<Image>();
RawImage tempRawImage = objectToFade.GetComponent<RawImage>();
MeshRenderer tempRenderer = objectToFade.GetComponent<MeshRenderer>();
Text tempText = objectToFade.GetComponent<Text>();
//Check if this is a Sprite
if (tempSPRenderer != null)
{
currentColor = tempSPRenderer.color;
mode = 0;
}
//Check if Image
else if (tempImage != null)
{
currentColor = tempImage.color;
mode = 1;
}
//Check if RawImage
else if (tempRawImage != null)
{
currentColor = tempRawImage.color;
mode = 2;
}
//Check if Text
else if (tempText != null)
{
currentColor = tempText.color;
mode = 3;
}
//Check if 3D Object
else if (tempRenderer != null)
{
currentColor = tempRenderer.material.color;
mode = 4;
//ENABLE FADE Mode on the material if not done already
tempRenderer.material.SetFloat("_Mode", 2);
tempRenderer.material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
tempRenderer.material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
tempRenderer.material.SetInt("_ZWrite", 0);
tempRenderer.material.DisableKeyword("_ALPHATEST_ON");
tempRenderer.material.EnableKeyword("_ALPHABLEND_ON");
tempRenderer.material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
tempRenderer.material.renderQueue = 3000;
}
else
{
yield break;
}
while (counter < duration)
{
counter += Time.deltaTime;
float alpha = Mathf.Lerp(a, b, counter / duration);
switch (mode)
{
case 0:
tempSPRenderer.color = new Color(currentColor.r, currentColor.g, currentColor.b, alpha);
break;
case 1:
tempImage.color = new Color(currentColor.r, currentColor.g, currentColor.b, alpha);
break;
case 2:
tempRawImage.color = new Color(currentColor.r, currentColor.g, currentColor.b, alpha);
break;
case 3:
tempText.color = new Color(currentColor.r, currentColor.g, currentColor.b, alpha);
break;
case 4:
tempRenderer.material.color = new Color(currentColor.r, currentColor.g, currentColor.b, alpha);
break;
}
yield return null;
}
}
Usage:
GameObject to fade:
public GameObject SpriteRend;
Fade-out in 3 seconds
StartCoroutine(fadeInAndOut(SpriteRend, false, 3f));
Fade-in in 3 seconds
StartCoroutine(fadeInAndOut(SpriteRend, true, 3f));
The way I have accomplished this has been change the alpha on the sprite color.
Color(R,G,B,A). A is the alpha.
SpriteRenderer.color = new Color(1f,1f,1f,1f) is opaque.
SpriteRenderer.color = new Color(1f,1f,1f,.5f) is about 50%
transparent.
SpriteRenderer.color = new Color(1f,1f,1f,0f) is about
100% transparent, sprite cannot be seen.

Unity - GetKeyDown and GetKey using the same KeyCode to get a delay on input if held down

I an trying to get the user input do two different behaviors with the same input key.
like this :
if (Input.GetKeyDown(KeyCode.D) || Input.GetKey(KeyCode.D))
Making a tetris game: The goal is, tapping "D" once, I want tetromino to move one world unit per tap. AND when holding down the same key "D" I want the block to move right continuously until it reaches the edge of the game board, without having to tap.
This sort of works with the code above but, the problem I have is that tapping once moves 2 or 3 world unit instead of once because there is no delay before unity realizes that I am holding the key down.
I would like unity to wait .5 seconds before activating "Input.GetKey(KeyCode.D)" so that I can keep the behavior "Input.GetKeyDown(KeyCode.D)"
Bottom line,
I want to be able to tap "D" to move one world unit per tap
I want the block to move continuously right until it reaches the edge of the game board if I hold down "D" but, only after holding it down for .5 seconds
How can I do this ?
Full code for the Tetromino.cs:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Tetromino : MonoBehaviour {
//#####################################################################################################
//#####################################################################################################
float fallTimer = 0f; // timer counting the seconds to check if mino needs to fall
public float fallSpeed = 1f; // variable to determine how fast the mino needs to fall
public bool allowRotation = true;
public bool limitRotation = false;
//#####################################################################################################
//#####################################################################################################
// Use this for initialization
void Start () {
}
//#####################################################################################################
//#####################################################################################################
// Update is called once per frame
void Update ()
{
CheckUserInput(); // --------------------------- // Checks the user input every frames
FallBehavior(); // checks if the block needs to fall and increments the timer
}
//#####################################################################################################
//#####################################################################################################
void CheckUserInput()
{
if (Input.GetKeyDown(KeyCode.D)) // moves the mino to the right
{
transform.position += new Vector3(1,0,0);
if (CheckIsValidPosition()) // if minos is not in a valid position, the transform pushes the minos
{ // back to the left, to keep it inside the grid
}
else
{
transform.position += new Vector3(-1, 0, 0); // this counters the first attempt to move
}
}
else if (Input.GetKeyDown(KeyCode.A)) // moves the mino to the left
{
transform.position += new Vector3(-1, 0, 0);
if (CheckIsValidPosition())
{
}
else
{
transform.position += new Vector3(1, 0, 0);
}
}
else if (Input.GetKeyDown(KeyCode.W)) // rotates the mino
{
if (allowRotation)
{
if (limitRotation) //limited rotation ON, to prevent rotating outside the grid
{ // after the tetromino landed at the bottom
if (transform.rotation.eulerAngles.z >= 90)
{
transform.Rotate(0, 0, -90);
}
else
{
transform.Rotate(0, 0, 90);
}
}
else
{
transform.Rotate(0, 0, 90); // 90 degrees rotation on the mino
}
if (CheckIsValidPosition())
{
}
else
{
if (limitRotation)
{
if (transform.rotation.eulerAngles.z >= 90)
{
transform.Rotate(0, 0, -90);
}
else
{
transform.Rotate(0, 0, 90);
}
}
else
{
transform.Rotate(0, 0, -90);
}
}
}
}
else if (Input.GetKeyDown(KeyCode.S))
{
transform.position += new Vector3(0, -1, 0); // makes the mino go down when pressing
if (CheckIsValidPosition())
{
}
else
{
transform.position += new Vector3(0, 1, 0);
}
}
}
//#####################################################################################################
//#####################################################################################################
/// <summary>
/// Makes the block fall by 1 unit and checks how fast it needs to fall
/// </summary>
void FallBehavior()
{
if (Time.time - fallTimer >= fallSpeed) // on the first frame, Time.time = 0 & fallTimer = 0
// so 0 - 0 = 0, is it >= then fallSpeed = 1? no
// so the if statement does not exectute, block dont fall
// after 1 sec, Time.time = 1 & fallTimer = 0
// so 1 - 0 = 1, is it >= then fallSpeed = 1? yes
// so block falls after 1 sec, because we increment it
// in the if statment also
{
transform.position += new Vector3(0, -1, 0); // moves the mino down
fallTimer = Time.time; // Time.time check the time since game started and is assigned
} // to fallTimer so that the timer updates every frame
// when called in the Update method. fallTimer = 0, 1, 2, 3 ...
if (CheckIsValidPosition()) // also helps checking if the Y is invalid, which tells the game to spawn
{ // the next tetromino when Y is less <= to the bottom of the grid
}
else
{
transform.position += new Vector3(0, 1, 0);
enabled = false; // disables the current piece, because it is at the bottom. So that the controls are not still
// attached to the current piece, after the next one spawned
FindObjectOfType<Game>().SpawnNextTetromino(); // spawns the next tetromino after the last one reached the bottom
}
}
//#####################################################################################################
//#####################################################################################################
/// <summary>
/// check the position of the individual tiles of the minos (children of the prefab)
/
/// </summary>
/// <returns></returns>
bool CheckIsValidPosition()
{
foreach (Transform mino in transform)
{
Vector2 pos = FindObjectOfType<Game>().RoundingTheMinoPosition (mino.position);
if (FindObjectOfType<Game>().CheckIsInsideGrid(pos) == false)
{
return false;
}
}
return true;
}
}
Full code of Game.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Game : MonoBehaviour {
public static int gridWidth = 10; // fixed grid size varibles
public static int gridHeight = 20; // for the blocks to fall in
// the grid need to be in a 2d array and we want to store all the x and y values for each world unit of the grid
// so that we can know which point on the grid are beind occupied by tetrominos that fell in.
//
// the array is gonna store the transforms so we use "gridWidth" and "gridHeight" to define the size of the array.
public static Transform[,] grid = new Transform[gridWidth, gridHeight];
// Use this for initialization
void Start () {
SpawnNextTetromino(); // spawns the first tetromino in the game
}
// Update is called once per frame
void Update () {
}
public void SpawnNextTetromino() // the Resources folder is included when the game compiles, we placed our prefabs
{ // in "Assets\Resources\Prefabs" to allow instantiation in the code.
// we cast a gameobject -> "(GameObject)" to let "Instantiate" know what we want to instantiate.
GameObject nextTetromino = (GameObject)Instantiate(Resources.Load(GetRandomTetromino(), typeof(GameObject)), new Vector2(5.0f, 20.0f), Quaternion.identity);
}
//gonna pass in the mino position in this method to see
// if it is still in the grid
public bool CheckIsInsideGrid(Vector2 pos)
{
return ((int)pos.x >= 0 && (int)pos.x < gridWidth && (int)pos.y >= 0);
}
public Vector2 RoundingTheMinoPosition(Vector2 pos)
{
return new Vector2(Mathf.Round(pos.x), Mathf.Round(pos.y));
}
/// <summary>
/// Genreates a random int and assings a teromino prefab to the outcome
/// </summary>
/// <returns></returns>
string GetRandomTetromino()
{
int randomTetromino = Random.Range(1, 8); //
string randomTetrominoName = null;
switch (randomTetromino)
{
case 1:
randomTetrominoName = "Prefabs/Tetromino_T";
break;
case 2:
randomTetrominoName = "Prefabs/Tetromino_Long";
break;
case 3:
randomTetrominoName = "Prefabs/Tetromino_Square";
break;
case 4:
randomTetrominoName = "Prefabs/Tetromino_J";
break;
case 5:
randomTetrominoName = "Prefabs/Tetromino_L";
break;
case 6:
randomTetrominoName = "Prefabs/Tetromino_S";
break;
case 7:
randomTetrominoName = "Prefabs/Tetromino_Z";
break;
}
return randomTetrominoName;
}
}
Looks like I misunderstood the original question. You want to move on "D" key press only but move until your you have reached the Edge when the the "D" key is held down.You need a timer when the the key is held down and this can be done with Time.deltaTime. Check while the key is held down with Input.GetKey, and if the timer reaches the amount of value you think makes it a held down, then you know the key is held down.
Also, check when the key is released with Input.GetKeyUp(KeyCode.D). If the key is released but timer has not reached the value you think makes it a held down,then it's simply a key press. It's worth doing this in a coroutine function instead of the Update function to simplify it and also reduce the amount of variables required to do it.
const float timeToCountAsHeldDown = 0.3f;
float pressTimer = 0;
IEnumerator moveChecker()
{
while (true)
{
//Check when the D key is pressed
if (Input.GetKeyDown(KeyCode.D))
{
//Continue to check if it is still heldown and keep counting the how long
while (Input.GetKey(KeyCode.D))
{
//Start incrementing timer
pressTimer += Time.deltaTime;
//Check if this counts as being "Held Down"
if (pressTimer > timeToCountAsHeldDown)
{
//It a "key held down", call the OnKeyHeldDown function and wait for it to return
yield return OnKeyHeldDown();
//No need to continue checking for Input.GetKey(KeyCode.D). Break out of this whule loop
break;
}
//Wait for a frame
yield return null;
}
}
//Check if "D" key is released
if (Input.GetKeyUp(KeyCode.D))
{
//Check if we have not not reached the timer then it is only a key press
if (pressTimer < timeToCountAsHeldDown)
{
//It just a key press, call the OnKeyPressedOnly function and wait for it to return
yield return OnKeyPressedOnly();
}
//Reset timer to 0 for the next key press
pressTimer = 0f;
}
//Wait for a frame
yield return null;
}
}
IEnumerator OnKeyPressedOnly()
{
Debug.Log("D key was only Pressed");
//Move 1 unit only
transform.position += new Vector3(1, 0, 0);
yield return null;
}
IEnumerator OnKeyHeldDown()
{
Debug.LogWarning("D key is Held Down");
//Don't move for 0.5 seconds
yield return new WaitForSeconds(0.5f);
//Move 1 unit every frame until edge detection is reached!
while (!CheckIsValidPosition())
{
transform.position += new Vector3(1, 0, 0);
//Wait for a frame
yield return null;
}
}

Unity - How to run a timer in parallel to a series of coroutines and WaitForSeconds

In the code below, I have a cycle for enemies appearing and disappearing.
IEnumerator EnemyCycle() {
while (isRepeating)
{
for (int j = 0; j < enemies.Length; j++) {
canStartUpdatingReset = true;
Enemy currentEnemy = enemies [j];
var _myMaterial = currentEnemy.GetComponent<Renderer>().material;
var _currentFade = StartCoroutine(FadeToForEnemy(_myMaterial, 0f, fTime, currentEnemy.gameObject, false));
coroutinesToStop.Add(_currentFade);
if (currentEnemy.hasWeapon) {
weaponCycleCoroutines.Add(StartCoroutine(FadeToForWeapon(currentEnemy.weapon.GetComponent<Renderer>().material, 0f, fadeTime, currentEnemy.weapon, false)));
}
}
yield return new WaitForSeconds (hideTime);
for (int j = 0; j < enemies.Length; j++) {
Enemy currentEnemy = enemies [j];
var _myMaterial = currentEnemy.GetComponent<Renderer>().material;
var _currentFade = StartCoroutine(FadeToForEnemy(_myMaterial, 1f, fTime, currentEnemy.gameObject, true));
coroutinesToStop.Add(_currentFade);
if (currentEnemy.hasWeapon) {
weaponCycleCoroutines.Add(StartCoroutine(FadeToForWeapon(currentEnemy.weapon.GetComponent<Renderer>().material, 1f, fadeTime, currentEnemy.weapon, true)));
}
yield return new WaitForSeconds (showTime);
}
}
}
I have an enemyCycleDuration float which is
enemyCycleDuration = 60*(hideTime + fadeTime + showTime + fadeTime)
Note that fadeTime comes from FadeToForWeapon and FadeToForEnemy. The idea here is I want the enemyCycleDuration to be run in parallel to EnemyCycle() so it is reset (please see code below) at the same time that
yield return new WaitForSeconds (showTime);
is reached in the EnemyCycle() (end of method).
I am resetting enemyCycleDuration in the following way in the update method but EnemyCycle() seems to be ahead of enemyCycleDuration, always completing before enemyCycleDuration is reset. How can I get both to run parallel to each other in timing and complete at the same time?
if (canStartUpdatingReset) {
timeElapsed +=1;
if(timeElapsed >= enemyCycleDuration) {
timeElapsed = 0;
Debug.Log ("Reset CycleDuration");
}
}
When I need to something similar to what you ask for I don't use coroutines. I use instead something like this:
int TimeBetweenAction1 = 3f;
int TimeBetweenAction2 = 3f;
void Update () {
if(Time.time > NextAction1){
DoAction1();
}
if(Time.time > NextAction2){
DoAction2();
}
}
void DoAction1(){
NextAction1 = Time.time + TimeBetweenActions1;
}
void DoAction2(){
NextAction2 = Time.time + TimeBetweenActions2;
}
The action could be from respawn a type of enemies or shoot a gun. You can have as many as you need in a gameObject to manage all enemies respawn, or in different gameObjects linked to different triggers in a Scene...
I know you asked exactly about coroutines, but that may be an easier approach that will do the trick. Let me know if it is useful for your issue or if I need to clarify anything.