Reputation:
My name is Laurenz and my question is how to delay the color changing of my sprites in Unity with c#.
Right now I have a random generator which choses a color based on a number, but this happens every frame. So now the real challenge is how to delay it so that it changes less often.
public class colorchange : MonoBehaviour
{
public int color;
public bool stop = true;
void Start()
{
}
void Update()
{
Debug.Log("Hello");
color = Random.Range(1, 5);
if (color == 2)
{
gameObject.GetComponent<SpriteRenderer>().color = Color.blue;
}
if (color == 3)
{
gameObject.GetComponent<SpriteRenderer>().color = Color.red;
}
if (color == 4)
{
gameObject.GetComponent<SpriteRenderer>().color = Color.yellow;
}
}
}
Upvotes: 2
Views: 9863
Reputation: 6439
This can be done in a number of ways. Before showing you the specific stuff, here is a basic structure, common to all example's codes, that I'll be using:
public class ColorChanger : MonoBehaviour {
//Avoid Find and GetComponent methods in performance-critical contexts like Update and FixedUpdate
//Store the value once in the beginning. This is called 'caching'
public SpriteRenderer _renderer;
//Don't hard-code stuff like this
public Color[] _colors;
public float _colorChangeInterval = 0.5f;
//Convenience property to access _renderer.color
public Color Color {
get => _renderer.color;
set => _renderer.color = value;
}
private void Start() {
//Attempts to find the SpriteRenderer in the object if it wasn't set in the inspector
if (!_renderer)
_renderer = GetComponent<SpriteRenderer>();
}
//This piece of code does a specific thing, so it's best to put it in a method
public void ChangeColor() {
if (_colors.Length < 1)
Debug.LogError($"You forgot to set {nameof(_colors)} in the Inspector. Shame! Shame!");
Color = _colors[Random.Range(0, _colors.Length - 1)];
}
}
Here are some of the main, in order of how intuitive they are, in my opinion:
Two flavours for this.
1) Can be an accumulator for elapsed time (as in the code below), or the reverse, and decrement from interval to zero:
private float _elapsed;
private void Update() {
_elapsed += Time.deltaTime;
if (_elapsed < _colorChangeInterval)
return;
ChangeColor();
_elapsed %= _colorChangeInterval;
}
Or 2) Can be a timestamp-check trigger from last, or until next (as below), timestamp:
//Replaces _elapsed
private float _timestamp;
private void Start() {
//...
_timestamp = Time.time; //Initial timestamp
}
private void Update() {
if (Time.time < _timestamp + _colorChangeInterval)
return;
ChangeColor();
_timestamp = Time.time;
}
This is the recommended procedure for when you need to delay or sequence code in unity.
Note that there are other types of wait methods provided by unity, like WaitWhile
, WaitUntil
, etc...
//Since unlike code in Update, coroutines need to be started and stopped, we start it when the script is enabled
private void OnEnable() {
StartCoroutine(ChangeColorContinuously());
}
//This is automatically stopped by unity when the script is disabled
private IEnumerator ChangeColorContinuously() {
while (true) {
yield return new WaitForSeconds(_colorChangeInterval);
ChangeColor();
}
}
Well, it can be done, but it has a lot of pitfalls and is very much NOT recommended for beginners.
And it's not intended to replace Coroutines anyways.
It's a method that relies on magic strings and reflection. Useful for quick & easy setups for example code, but that if at all possible (and it is possible, thanks to methods above) should be avoided like the plague in production code.
Upvotes: 2
Reputation: 20269
You can put your code in a loop in a Coroutine that iterates once every number of seconds:
public class colorchange : MonoBehaviour
{
public int color;
public float delaySeconds = 1f;
IEnumerator changeColorCoroutine;
SpriteRenderer mySprite;
public bool doChangeColor;
void Start()
{
// cache result of expensive GetComponent call
mySprite = GetComponent<SpriteRenderer>();
// initialize flag
doChangeColor = true;
// create coroutine
changeColorCoroutine = ChangeColor();
// start coroutine
StartCoroutine(changeColorCoroutine);
}
void OnMouseDown()
{
// toggle doChangeColor
doChangeColor = !doChangeColor;
}
IEnumerator ChangeColor()
{
WaitUntil waitForFlag = new WaitUntil( () => doChangeColor);
while (true)
{
yield return waitForFlag;
Debug.Log("Hello");
color = Random.Range(1, 5);
// switch for neater code
switch (color)
{
case 2:
mySprite.color = Color.blue;
break;
case 3:
mySprite.color = Color.red;
break;
case 4:
mySprite.color = Color.yellow;
break;
}
yield return new WaitForSeconds(delaySeconds);
}
}
}
Upvotes: 3
Reputation: 90862
Another alternatively to Coroutines or a simple timer in Update
would be to use InvokeRepeating
and stop it using CancelInvoke
. In such a use case as yours I find this way easier to implement and control:
public float Interval = 1;
[SerializeField] private SpriteRenderer spriteRenderer;
private void Awake()
{
// DO THIS ONLY ONCE
if(!spriteRenderer) spriteRenderer = GetComponent<SpriteRenderer>();
}
// Automatically called when this component or GameObject gets enabled
private void OnEnable ()
{
// Start invoking every Interval seconds
InvokeRepeating(nameof(ChangeColor), Interval, Interval);
}
// Automatically called when this component or GameObject gets disabled
private void OnDisable()
{
// Stop the repeated invoking
CancelInvoke();
}
private void ChangeColor()
{
Debug.Log("Hello");
color = Random.Range(1, 5);
// You should also use a switch case here
switch(color)
{
case 2:
spriteRenderer.color = Color.blue;
break;
case 3:
spriteRenderer.color = Color.red;
break;
case 4:
spriteRenderer.color = Color.yellow;
break;
}
}
Then you could simply enable and disable the color changing by enabling and disabling this component.
Or alternatively simply move the according code lines from OnEnable
and OnDisable
to according public methods like public void StartColorChange
and public void StopColorChange
.
Upvotes: 0
Reputation: 270
There is multiple ways to do what you want.
You can use a timer to change the color every x amount of time.
There is at least 2 ways to do that :
1
public class colorchange : MonoBehaviour
{
public int color;
public bool stop = true;
private SpriteRenderer _mySpriteRenderer;
private float _timeBetweenChanges = 0.5F;
private float _lastChange = 0f;
void Start()
{
_mySpriteRenderer = GetComponent<SpriteRenderer>();
}
void Update()
{
if (Time.time - _lastChange >= _timeBetweenChanges)
{
_lastChange = Time.time;
color = Random.Range(1, 5);
if (color == 2)
{
_mySpriteRenderer.color = Color.blue;
}
if (color == 3)
{
_mySpriteRenderer.color = Color.red;
}
if (color == 4)
{
_mySpriteRenderer.color = Color.yellow;
}
}
}
}
Note : you can cache your component SpriteRenderer to avoid the GetComponent
call each time. See here
2
public class colorchange : MonoBehaviour
{
public int color;
public bool stop = true;
private SpriteRenderer _mySpriteRenderer;
private float _timeBetweenChanges = 0.5F;
void Start()
{
_mySpriteRenderer = GetComponent<SpriteRenderer>();
InvokeRepeating("ChangeColor", 0F, _timeBetweenChanges);
}
void Update()
{
// You don't need Update here, you can safly remove it unless you need it for anything else
}
void ChangeColor()
{
color = Random.Range(1, 5);
if (color == 2)
{
_mySpriteRenderer.color = Color.blue;
}
if (color == 3)
{
_mySpriteRenderer.color = Color.red;
}
if (color == 4)
{
_mySpriteRenderer.color = Color.yellow;
}
}
}
You can use an RNG to dertermine if you want to change color this frame or not. It implies you can't define how many times per sec / min / in general
you will change the color.
public class colorchange : MonoBehaviour
{
public int color;
public bool stop = true;
private SpriteRenderer _mySpriteRenderer;
private float _randomChance = 0.2F;
void Start()
{
_mySpriteRenderer = GetComponent<SpriteRenderer>();
}
void Update()
{
if (Random.Range(0F, 1F) < _randomChance)
{
color = Random.Range(1, 5);
if (color == 2)
{
_mySpriteRenderer.color = Color.blue;
}
if (color == 3)
{
_mySpriteRenderer.color = Color.red;
}
if (color == 4)
{
_mySpriteRenderer.color = Color.yellow;
}
}
}
}
Here you have 20
Upvotes: -1
Reputation: 9
You can handle this with a simple coroutine. If you dont know how, take a look at this link ( https://docs.unity3d.com/ScriptReference/WaitForSeconds.html ) on Unity's official API documentation.
If you dont want to work with coroutines, you can use Unity's InvokeRepating method. Create a new function for changing the color and Invoke it every x amount of seconds. Here's the link for InvokeRepating method too https://docs.unity3d.com/ScriptReference/MonoBehaviour.InvokeRepeating.html
ps: In this case using Switch for determining the color integer is more efficient rather using if and else if for each color code. Its more efficient and easier to scale up.
Upvotes: 0
Reputation: 414
You can use time counter and Time.deltaTime:
public class colorchange : MonoBehaviour
{
public int color;
public bool stop = true;
public float delay;
private float timer;
void Start()
{
timer = delay;
}
void Update()
{
timer -= Time.deltaTime;
if (timer <= 0) {
timer = delay;
Debug.Log("Hello");
color = Random.Range(1, 5);
if (color == 2)
{
gameObject.GetComponent<SpriteRenderer>().color = Color.blue;
}
if (color == 3)
{
gameObject.GetComponent<SpriteRenderer>().color = Color.red;
}
if (color == 4)
{
gameObject.GetComponent<SpriteRenderer>().color = Color.yellow;
}
}
}
}
Upvotes: 2