Using coroutines to fade in/out TextMeshPro Text Element - c#

I am setting up a "scene intro" with some text saying 'Level 1' using TextMeshPro. I have created the text element in canvas and i am trying to find a way to make it fade in, then wait, and then fade out (Somewhat like what you see when you discover a new place in Skyrim).
So far i tried a versatile solution so i can use the same script for other uses (eg not in the start of the scene, not only fade in etc).
Using TMPro:
...
using TMPro;
...
Start and declaration:
public class IntroFade : MonoBehaviour
{
[SerializeField] private TextMeshProUGUI textToUse;
[SerializeField] private bool fadeIn = false;
[SerializeField] private bool fadeOnStart = false;
[SerializeField] private float timeMultiplier;
private bool FadeIncomplete = false;
private void Start()
{
if (fadeOnStart)
{
if (fadeIn)
{
StartCoroutine(FadeInText(timeMultiplier, textToUse));
FadeIncomplete = true;
}
else
{
StartCoroutine(FadeOutText(timeMultiplier, textToUse));
}
}
}
...
Update in which i want to fadeout once fadein is done
private void Update()
{
if (FadeIncomplete)
{
StartCoroutine(FadeOutText(timeMultiplier, textToUse));
}
}
Corouritnes for the actual fading:
private IEnumerator FadeInText(float timeSpeed, TextMeshProUGUI text)
{
text.color = new Color(text.color.r, text.color.g, text.color.b, 0);
while (text.color.a < 1.0f)
{
text.color = new Color(text.color.r, text.color.g, text.color.b, text.color.a + (Time.deltaTime * timeSpeed));
yield return null;
}
}
private IEnumerator FadeOutText(float timeSpeed, TextMeshProUGUI text)
{
text.color = new Color(text.color.r, text.color.g, text.color.b, 1);
while (text.color.a > 0.0f)
{
text.color = new Color(text.color.r, text.color.g, text.color.b, text.color.a - (Time.deltaTime * timeSpeed));
yield return null;
}
}
public void FadeInText(float timeSpeed = -1.0f)
{
if (timeSpeed <= 0.0f)
{
timeSpeed = timeMultiplier;
}
StartCoroutine(FadeInText(timeSpeed, textToUse));
}
public void FadeOutText(float timeSpeed = -1.0f)
{
if (timeSpeed <= 0.0f)
{
timeSpeed = timeMultiplier;
}
StartCoroutine(FadeOutText(timeSpeed, textToUse));
}
So what happens is it either fades in OR fades out depending on the Coroutine that starts first. I am not able to make it so it fades in, stays on screen for like 2 seconds and then fades out.
I also tried fading in then creating a coroutine to waitforseconds and then call the fadeout coroutine but that didn't work either.

A Coroutine can wait for the completion of another coroutine, thinking about it this way will simplify the problem immensely. You have already created your fade in and fade out, now you just have to run them in sequence with a 2 second wait between them.
private IEnumerator IntroFade (TextMeshProUGUI textToUse) {
yield return StartCoroutine(FadeInText(1f, textToUse));
yield return new WaitForSeconds(2f);
yield return StartCoroutine(FadeOutText(1f, textToUse));
//End of transition, do some extra stuff!!
}
If you are interested, these articles are pretty insightful when it comes to learning more about coroutines.

Related

Fade out and fade in texture over time

I am changing a texture upon an event trigger. At the moment the change is quite abrupt. I would like to make the transition so that the current texture fades out and the next texture fades in (using the _Glow parameter. Both transitions should happen over a period of 3 seconds.
So far I was able to make the fade in but it is for 12sec. Should I use two coroutines?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
public class VFXController : MonoBehaviour
{
private AssetBundle assetBundle;
private Texture texture;
private Texture nextTexture;
private Renderer renderer;
public GameObject visual;
private float currentGlowValue = 0.0f;
private float targetGlowValue = 12.6f;
private IEnumerator toggleTexture;
// Start is called before the first frame update
void Start()
{
assetBundle = AssetBundle.LoadFromFile(Path.Combine(Application.streamingAssetsPath, "session_vfx_textures"));
if (assetBundle == null)
{
Debug.Log("Failed to load AssetBundle!");
return;
}
renderer = visual.GetComponent<Renderer>();
toggleTexture = ToggleTexture(12.6f);
InitEvents();
}
private void InitEvents()
{
EventsManager.current.onChangeVFXTexture += ChangeTexture;
}
private void OnDestroy()
{
EventsManager.current.onChangeVFXTexture -= ChangeTexture;
}
private void ChangeTexture(SessionNames name)
{
nextTexture = assetBundle.LoadAsset<Texture>(name.ToString());
StartCoroutine(toggleTexture);
}
IEnumerator ToggleTexture(float duration)
{
for (float t = 0f; t < duration; t += Time.deltaTime)
{
renderer.material.SetFloat("_Glow", currentGlowValue + t);
yield return null;
if (renderer.material.GetFloat("_Glow") >= 12.0f)
{
renderer.material.SetTexture("_MainTex", nextTexture);
StopCoroutine(toggleTexture);
}
}
}
}
Please check Range in the shader for _GLOW property. It might be set Range(0,15) or something. If you want to bring it upto 3 seconds then change range to _Glow ("Intensity", Range(0, 3)) = 1
For anyone wondering how to create a Fade in/out effect using a coroutine. Here is the solution.
IEnumerator Fade()
{
float elapsedTime = 0;
while (elapsedTime < transitionDuration)
{
currentGlowValue = Mathf.Lerp(13.0f, 0.0f, (elapsedTime / transitionDuration));
visual.GetComponent<Renderer>().material.SetFloat("_Glow", currentGlowValue);
elapsedTime += Time.deltaTime;
yield return null;
}
visual.GetComponent<Renderer>().material.SetTexture("_MainTex", nextTexture);
elapsedTime = 0;
while (elapsedTime < transitionDuration)
{
currentGlowValue = Mathf.Lerp(0.0f, 13.0f, (elapsedTime / transitionDuration));
visual.GetComponent<Renderer>().material.SetFloat("_Glow", currentGlowValue);
elapsedTime += Time.deltaTime;
yield return null;
}
}

How to stop function after 1 second Unity 3D?

In my code, I am calling a Slow motion function when the player enter to a Trigger, but I want this function to stop after 1 second, I tried the code below but the slow motion didn't work. Do you have any idea?
Player Script:
public Rigidbody Ball;
public float Speed = 50f;
public TimeManager timeManager;
bool SlowOn;
bool ClickDone = false;
// Use this for initialization
void Start () {
StartCoroutine(SlowOff());
}
// Update is called once per frame
void FixedUpdate () {
if (!ClickDone){
if (Input.GetMouseButton (0)) {
ClickDone = true;
Ball.velocity = transform.forward * Speed;
}
}
}
private void OnTriggerEnter (Collider other) {
if(other.gameObject.CompareTag ("SlowMotionArea")) {
if (SlowOn) {
timeManager.DoSlowMotion();
}
}
}
IEnumerator SlowOff () {
yield return new WaitForSeconds(2.0f);
SlowOn = false;
}
TimeManager Script:
public float SlowDownFactor = 0.05f;
public float SlowdownLength = 2f;
public void DoSlowMotion () {
Time.timeScale = SlowDownFactor;
Time.fixedDeltaTime = 0.02f * Time.timeScale ;
}
There are a few problems with your code
You never set SlowOn to true so your line
timeManager.DoSlowMotion();
is never executed. Somewhere in your code you should call
SlowOn = true;
I'm guessing but it seems that you wanted to use that bool to prevent multiple calls of timeManager.DoSlowMotion()? In this case it should rather be something like
if(!SlowOn)
{
timeManager.DoSlowMotion();
SlowOn = true;
}
Why do you call StartCoroutine(SlowOff()); in your Start() method?
I guess you should rather remove that line and place it after
timeManager.DoSlowMotion();
StartCoroutine(SlowOff());
so the Coroutine is started everytime a SlowMotion starts.
It is not enough for the SlowMotion to stop to just set your SlowOn = false.
In your TimeManager you should rather store the original TimeScale and reset them in a second method:
public float SlowDownFactor = 0.05f;
public float SlowdownLength = 2f;
private float originalTimeScale;
private float originalFixedDeltaTime;
public void DoSlowMotion () {
// before changing store current values
originalTimeScale = Time.timeScale;
originalFixedDeltaTime = Time.fixedDeltaTime;
Time.timeScale = SlowDownFactor;
Time.fixedDeltaTime = 0.02f * Time.timeScale ;
}
public void ResetTimeScales()
{
Time.timeScale = originalTimeScale;
Time.fixedDeltaTime = originalFixedDeltaTime;
}
and than you also have to call that ResetTimeScales method from the player
IEnumerator SlowOff () {
yield return new WaitForSeconds(2.0f);
timeManager.ResetTimeScales();
SlowOn = false;
}
I guess you know that
yield return new WaitForSeconds(2.0f);
will also be affected by the changed Timescale so your SlowMotion currently would be longer than the expected 2 seconds namely 2 / SlowDownFactor = 40!
You could avoid this by using the Time.unscaledDeltaTime as a countdown like
private IEnumerator SlowOff()
{
// since your title actually claims you want to reset after 1 second
float timer = 1;
while (timer > 0)
{
timer -= Time.unscaledDeltaTime;
yield return null;
}
timeManager.ResetTimeScales();
SlowDown = false;
}

How to set the yield wait for seconds back to 60 seconds when the player gets hit by an enemy?

Basically I want to make a player that can transform into demon at will whenever the user press the power-up button however I want the transformation to end after 60 seconds (when the transformation ends I want the player revert back to his original state). I also want the transformation to end if the player gets hit by an enemy. So far I've made this code and it works but I'm having trouble resetting the yield wait for seconds back to 60 seconds when if the player gets hit by an enemy and if the user decided to press the button to transform the player back into a demon. Can anyone help me with this problem?
In my hierarchy I have my player as the parent and my demon player as the child. A playermovement script attached to the player as well as the transformation script below:
public GameObject demon;
public BoxCollider2D col;
public Renderer rend;
public ParticleSystem par1;
public static Vector3 target;
void Start () {
target = transform.position;
}
void Update () {
target.z = transform.position.z;
}
public void DemonCharacter() {
StartCoroutine (PowerUpCoroutine ());
}
private IEnumerator PowerUpCoroutine() {
yield return new WaitForSeconds (0.3f);
par1.Play (); // particle system animation to cover transformation happening
par1.transform.position = target;
yield return new WaitForSeconds (0.2f);
demon.SetActive (true); // activates demon gameobject
rend.enabled = false; // deactivate players spriterenderer
col.enabled = false;
yield return new WaitForSeconds (60f);
demon.SetActive (false); // deactivates demon gameobject
rend.enabled = true; // activate players spriterenderer
col.enabled = true;
par1.Stop ();
}
And on my demon player, I attached this script;
I works but when the user clicks on the button to transform into a demon the yield waitforseconds doesn't stop, so when the player transform into a demon seconds later the demon player transforms back into the player rather than resetting the yield wait for seconds.
public BoxCollider2D Playercol;
public Renderer PlayerRend;
void Start()
{
}
void Update ()
{
}
void OnTriggerEnter2D(Collider2D col) {
if (col.tag == "enemy") {
demon.SetActive (false);
PlayerRend.enabled = true;
Playercol.enabled = true;
}
}
Another way than what #m.rogalski suggested would be to use a simple float variable as timer:
public GameObject demon;
public BoxCollider2D col;
public Renderer rend;
public ParticleSystem par1;
public static Vector3 target;
private float demonTimer;
void Start()
{
target = transform.position;
demonTimer = 0.0f;
}
void Update()
{
target.z = transform.position.z;
if (demonTimer > 0.0f)
{
demonTimer -= Time.deltaTime;
if (demonTimer <= 0.0f)
{
demon.SetActive(false);
rend.enabled = true;
col.enabled = true;
}
}
}
public void DemonCharacter()
{
par1.Play();
par1.transform.position = target;
demon.SetActive(true);
rend.enabled = false;
col.enabled = false;
demonTimer = 60.0f;
}
public void CancelDemon()
{
demonTimer = 0.0f;
}
Hope this helps,
My suggestion would be for you to modify your Coroutine to not use WaitForSeconds but use it's own timing calculations.
// create the flag indicating interruption
bool _interrupt = false;
// create your coroutine
IEnumerator PowerUpCoroutine()
{
// set the time you want to hold transformation
const float TRANSFORMATION_INTERVAL = 60.0f;
// currently elapsed time
float currentlyElapsed = 0.0f;
// add your logic for pre-transformation
while ( currentlyElapsed < TRANSFORMATION_INTERVAL && !_interrupt )
{
yield return null;
currentlyElapsed += Time.deltaTime;
}
// add post-transformation logic
// revert transformation process
_interrupt = false;
}
Now if you run this coroutine calling StartCoroutine(PowerUpCoroutine()); you can interrupt it setting _interrupt flag to true. eg :
public void Interrupt()
{
_interrupt = true;
}
// in some update :
if ( gotHitThisFrame == true )
Interrupt();

Blink GameObject

I am a bit of a noob to programming and I am trying to make a GameObject , deactivate and reactivate over a set amount of seconds.For example I want my star to slowly blink before it goes away, to create a cool looking effect. If there is a better way of using this method done with out using SetActive(false) and what not , please feel free to give me your method - This is my code , Sorry if its messy i gotta get better at this but i will in due time
Thanks guys
//Timers
public float ScoreTimer;
public float[] TimeMarkStamp;
//Scoring
public int totalCollStars;
[Space]
public int maxStars = 5;
public GameObject[] StarImages;
// Use this for initialization
void Start()
{
}
// Update is called once per frame
void Update()
{
ScoreTimer += Time.deltaTime;
if (ScoreTimer <= TimeMarkStamp[0])
{
Debug.Log("It works");
StarImages[0].SetActive(false);
}
else if (ScoreTimer <= TimeMarkStamp[1])
{
Debug.Log("It workds" + TimeMarkStamp[1]);
StarImages[1].SetActive(false);
}
else if (ScoreTimer <= TimeMarkStamp[2])
{
Debug.Log("It works" + TimeMarkStamp[2]);
StarImages[2].SetActive(false);
}
else if (ScoreTimer <= TimeMarkStamp[3])
{
//This is not working
InvokeRepeating("flickerEffect", 3f, 1f);
}
}
void flickerEffect()
{
bool flickCheck = false;
if (flickCheck == false)
{
StarImages[3].SetActive(true);
flickCheck = true;
}
else if (flickCheck == true)
{
StarImages[3].SetActive(false);
flickCheck = false;
}
}
}
If there is a better way of using this method done with out using
SetActive(false) and what not
Yes, there is a better way, other than using the SetActive function. You should change the alpha color of the GameObject from 0 to 1 back and forth. After that you can then disable the GameObject with SetActive. This saves how much garbage would have been generated when repeatedly calling the SetActive function.
If this is a 3D GameObject, change the Rendering Mode from Opaque(default) to Fade or Transparent.
A simple function that can do this:
void blink(GameObject obj, float blinkSpeed, float duration)
{
StartCoroutine(_blinkCOR(obj, blinkSpeed, duration));
}
IEnumerator _blinkCOR(GameObject obj, float blinkSpeed, float duration)
{
obj.SetActive(true);
Color defualtColor = obj.GetComponent<MeshRenderer>().material.color;
float counter = 0;
float innerCounter = 0;
bool visible = false;
while (counter < duration)
{
counter += Time.deltaTime;
innerCounter += Time.deltaTime;
//Toggle and reset if innerCounter > blinkSpeed
if (innerCounter > blinkSpeed)
{
visible = !visible;
innerCounter = 0f;
}
if (visible)
{
//Show
show(obj);
}
else
{
//Hide
hide(obj);
}
//Wait for a frame
yield return null;
}
//Done Blinking, Restore default color then Disable the GameObject
obj.GetComponent<MeshRenderer>().material.color = defualtColor;
obj.SetActive(false);
}
void show(GameObject obj)
{
Color currentColor = obj.GetComponent<MeshRenderer>().material.color;
currentColor.a = 1;
obj.GetComponent<MeshRenderer>().material.color = currentColor;
}
void hide(GameObject obj)
{
Color currentColor = obj.GetComponent<MeshRenderer>().material.color;
currentColor.a = 0;
obj.GetComponent<MeshRenderer>().material.color = currentColor;
}
Usage:
void Start()
{
blink(gameObject, 0.2f, 5f);
}
If this is a SpriteRender, you have to replace all the obj.GetComponent<MeshRenderer>().material.color code with obj.GetComponent<SpriteRenderer>().color.

Unity2D C# Reloading progress bar not working propertly

So I'm making a top-down tank shooter game and I want to make a better reloading system than it was before. So I came to the idea that I need some king of progress bar. I knew how to make it so I started doing it. The problem is that it doesn't work properly. As I show in the .gif above, the progress bar don't go down when you shoot second time. Because I'm new to unity, I still don't know everything very good. So I came here, maybe someone could help.
EDIT:
I just found another problem and maybe an answer why I have this problem. The second time my script tries to reload, my "needTimer" bool is false, thus the progress bar is not going down when it's false. The new question would be why it becomes false instead of true?
My reloading script:
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
public class Reload : MonoBehaviour {
public float ammo;
public Image progress;
public bool alreadyReloading;
public bool needTimer;
void Start () {
alreadyReloading = false;
}
IEnumerator needtimertime(){
yield return new WaitForSeconds (6.5f);
needTimer = false;
}
IEnumerator uztaisyt(){
Debug.Log ("REEELOUUUDING!");
yield return new WaitForSeconds(6.5f);
ammo += 1;
alreadyReloading = false;
}
void Update () {
if (needTimer == true) {
timer ("");
}
if (ammo < 5) {
if(alreadyReloading == false){
needTimer = true;
StartCoroutine(uztaisyt());
alreadyReloading = true;
}
}
if (progress.fillAmount <= 0) {
progress.fillAmount = 1.0f;
}
}
void timer(string tipas){
progress.fillAmount -= Time.deltaTime / 6.5f;
StartCoroutine (needtimertime ());
}
}
When you start the uztaisyt() as a coroutine after shooting the first time (in the animation), needTimer is set to true and in the next Update() call, the needtimertime() coroutine will start. Since both the uztaisyt() and needtimertime() have identical 6.5 second waits, they will not both return on the same frame update because needtimertime() will always be started in the next frame after uztaisyt(). And, since there is no guarantee of the time interval between Update() calls, (see Time and Frame Managment), this interval may be more than expected and needtimertime() could return false in the frame right after uztaisyt() is called after firing the second time.
To ensure that the needtimertime() is always started (if not already running) immediately following a call for uztaisyt() (and called within the same frame update), you could try the following update to Reload script, (basically changes to the Update() method and when/how _isTimerRunning is set).
public class Reload : MonoBehaviour {
public float ammo;
public Image progress;
private bool _alreadyReloading;
private bool _isTimerRunning;
void Start () {
_alreadyReloading = false;
_isTimerRunning = false;
}
IEnumerator needtimertime(){
yield return new WaitForSeconds (6.5f);
_needTimer = false;
}
IEnumerator uztaisyt(){
Debug.Log ("REEELOUUUDING!");
yield return new WaitForSeconds(6.5f);
ammo += 1;
_alreadyReloading = false;
}
void Update () {
if (ammo < 5) {
if(_alreadyReloading == false){
StartCoroutine(uztaisyt());
_alreadyReloading = true;
//this will check for and start the progress bar timer in the same udate call
//so both coroutines finish on the same frame update
if(!_isTimerRunning){
_isTimerRunning = true;
timer ("");
}
}
}
if (progress.fillAmount <= 0) {
progress.fillAmount = 1.0f;
}
}
void timer(string tipas){
progress.fillAmount -= Time.deltaTime / 6.5f;
StartCoroutine (needtimertime ());
}
}
I found my problem and fixed it.
The problem was that needTimer was becoming false. So I found where and removed it.
my new code:
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
public class Reload : MonoBehaviour {
public float ammo;
public Image progress;
public bool alreadyReloading;
public bool needTimer;
void Start () {
alreadyReloading = false;
needTimer = false;
}
IEnumerator uztaisyt(){
Debug.Log ("REEELOUUUDING!");
yield return new WaitForSeconds(6.5f);
ammo += 1;
alreadyReloading = false;
}
void Update () {
if (ammo < 5.0f) {
if(alreadyReloading == false){
progress.fillAmount = 1.0f;
needTimer = true;
StartCoroutine(uztaisyt());
alreadyReloading = true;
}
if (needTimer == true) {
timer ("");
}
}
}
void timer(string tipas){
progress.fillAmount -= Time.deltaTime / 6.5f;
}
}

Categories

Resources