Quest: Cloning Colors Quest
Challenge: Flashy Coin
Feel free to share your solutions, ideas and creations below. If you get stuck, you can find some ideas here for completing this challenge.
Quest: Cloning Colors Quest
Challenge: Flashy Coin
Feel free to share your solutions, ideas and creations below. If you get stuck, you can find some ideas here for completing this challenge.
There may be a better way, but I made it like this.
using System.Collections;
using UnityEngine;
public class CoinController : MonoBehaviour
{
private bool isBlink;
private float elapsedTime;
private ColorChanger myColorChanger;
private SpriteRenderer mySpriteRenderer;
private Coroutine blinkCoroutine;
private void Start()
{
myColorChanger = GetComponent<ColorChanger>();
mySpriteRenderer = GetComponent<SpriteRenderer>();
}
private void Update()
{
if (isBlink)
{
mySpriteRenderer.color = new Color(mySpriteRenderer.color.r, mySpriteRenderer.color.g, mySpriteRenderer.color.b,
Mathf.Lerp(1.0f, 0.0f, Mathf.PingPong(elapsedTime * 2, 1)));
elapsedTime += Time.deltaTime;
}
}
private void OnTriggerEnter2D(Collider2D other)
{
ColorChanger otherColorChanger = other.GetComponent<ColorChanger>();
if (otherColorChanger == null)
return;
if (otherColorChanger.blockColor == myColorChanger.blockColor)
{
Destroy(gameObject);
}
else
{
if (blinkCoroutine == null)
blinkCoroutine = StartCoroutine(StartBlink());
}
}
private IEnumerator StartBlink()
{
elapsedTime = 0.0f;
isBlink = true;
yield return new WaitForSeconds(2.0f);
isBlink = false;
blinkCoroutine = null;
}
}
I used 2 animations for this challenge. The first one is Idle, with alpha set to 1, and the second animation is changing the alpha value from 1 to 0 to 1. I used a coroutine for this.
using System.Collections;
using UnityEngine;
public class CoinCollection : MonoBehaviour
{
private SpriteRenderer mySpriteRenderer;
private Animator animator;
private void Start()
{
mySpriteRenderer = GetComponent<SpriteRenderer>();
animator = GetComponent<Animator>();
}
private void OnTriggerEnter2D(Collider2D collision)
{
if(collision.gameObject.CompareTag("Coin"))
{
Color blockColor = mySpriteRenderer.color;
Color coinColor = collision.gameObject.GetComponent<SpriteRenderer>().color;
if (!blockColor.Equals(coinColor))
{
StartCoroutine(ChangeAlpha());
}
else
{
Destroy(collision.gameObject);
}
}
}
private IEnumerator ChangeAlpha()
{
animator.Play("alphaChanging");
yield return new WaitForSeconds(1);
animator.Play("Idle");
}
}
My new solution
public class FlashCoin : MonoBehaviour
{
private SpriteRenderer mySpriteRenderer;
private bool isColliding = false;
private void Start()
{
mySpriteRenderer = GetComponent<SpriteRenderer>();
}
private void Update()
{
if (isColliding)
{
StartCoroutine(CollidWithCoin());
}
}
private void OnTriggerStay2D(Collider2D collision)
{
if(collision.gameObject.CompareTag("Block") && collision.gameObject.GetComponent<SpriteRenderer>().color != mySpriteRenderer.color)
{
isColliding = true;
}
}
private IEnumerator CollidWithCoin()
{
Color color = mySpriteRenderer.color;
color.a = Mathf.PingPong(Time.time, 1);
mySpriteRenderer.color = color;
yield return new WaitForSecondsRealtime(.5f);
color.a = 1;
mySpriteRenderer.color = color;
isColliding = false;
}
}
You’ll multicall the Coroutine in this solution calling it from Update…
I’d usually do something like this in the animator, but since the whole point is to challenge myself, I gave doing it in script a shot.
I started by using lerp to smoothly change the alpha, with it slowing as it approached the min and max. I didn’t like the way it looked. So I changed it to smoothly adjust the alpha at a constant rate and I’m much happier with this:
void Update()
{
if (!isFlashing) return;
tempColour = spriteRenderer.color;
if (tempColour.a < 0.31f)
{
tempColour.a = 0.31f;
flashSpeed = -flashSpeed;
}
else if (tempColour.a > 0.99f)
{
tempColour.a = 0.99f;
flashSpeed = -flashSpeed;
}
tempColour.a += flashSpeed * Time.deltaTime;
spriteRenderer.color = tempColour;
}
OnTriggerEnter and Exit turn that isFlashing bool flag on and off.
Here is my approach with everything placed in an OnTriggerEnter method that caches/compares the coin/block colors to either destroy the coin or start a Coroutine. The blink quality is controlled by the WaitForSeconds value (0.25) and the blink duration by the For Loop number (3 blinks). Admittedly not the prettiest of effects…
public class CoinController : MonoBehaviour
{
void OnTriggerEnter2D(Collider2D other)
{
Color coinColor = GetComponent<SpriteRenderer>().color;
Color blockColor = other.gameObject.GetComponent<SpriteRenderer>().color;
if (blockColor == coinColor)
{
Destroy(gameObject);
}
else
{
StartCoroutine(Blink());
}
IEnumerator Blink()
{
for (int i = 0; i < 3; i++)
{
blockColor = new Color(blockColor.r, blockColor.g, blockColor.b, Mathf.PingPong(Time.time, 0));
other.gameObject.GetComponent<SpriteRenderer>().color = blockColor;
yield return new WaitForSeconds(0.25f);
blockColor = new Color(blockColor.r, blockColor.g, blockColor.b, Mathf.PingPong(Time.time, 255));
other.gameObject.GetComponent<SpriteRenderer>().color = blockColor;
yield return new WaitForSeconds(0.25f);
}
}
}
}
I went ahaed and used the scripting way aswell since I wanted to dive a bit deeper into coroutines. Not quite happy yet with the double while loop in my Coroutine. But for now it’s okay
using System.Collections;
using UnityEngine;
public class CoinHandler : MonoBehaviour
{
private Color _myColor;
private SpriteRenderer _mySpriteRenderer;
private GameHandler _myGameHandler;
[SerializeField] private int points;
// I want the actual fading to take 0.1 seconds. I split it up into fading in and fading out so I need to divide it;
private const float TimeToComplete = 0.1f / 2;
// I want the transition to me smooth so the alpha value should only be changed by 0.01f at a time
private const float AlphaChangeStep = 0.01f;
// Since I want it to be done in timeToComplete I need wait between each step timeToComplete * step
private const float SleepTime = TimeToComplete * AlphaChangeStep;
// I want the animation to blink a total of x times
private const int TotalBlinkTime = 4;
public void Start()
{
if (_mySpriteRenderer == null) _mySpriteRenderer = GetComponent<SpriteRenderer>();
_myGameHandler = FindObjectOfType<GameHandler>();
}
// Init is being Called from within the ColorChanger Start Method since I need to wait for that script to set
// the corresponding color on my SpriteRenderer and I wanted to cache the result and not get it each
// time the Trigger is called
public void Init()
{
// My own Start might have not run yet so I cannot rely on values being set in the Start Method
if (_mySpriteRenderer == null)
{
_mySpriteRenderer = GetComponent<SpriteRenderer>();
}
_myColor = _mySpriteRenderer.color;
}
private void OnTriggerEnter2D(Collider2D collision)
{
if (!collision.gameObject.CompareTag("Block")) return;
if (collision.GetComponent<SpriteRenderer>()?.color == _myColor)
{
HandlePickup();
return;
}
StartCoroutine(FlashCoin());
}
private void HandlePickup()
{
// I added a new property in the GameHandler "Score" and added the AddScore function that just simply increases the value
_myGameHandler.AddScore(points);
Destroy(gameObject);
}
private IEnumerator FlashCoin()
{
float alpha = _myColor.a;
for (int i = 0; i < TotalBlinkTime; i++)
{
while (alpha > 0f)
{
alpha -= AlphaChangeStep;
_mySpriteRenderer.color = new Color(_myColor.r, _myColor.g, _myColor.b, alpha);
yield return new WaitForSeconds(SleepTime);
}
while (alpha < 1f)
{
alpha += AlphaChangeStep;
_mySpriteRenderer.color = new Color(_myColor.r, _myColor.g, _myColor.b, alpha);
yield return new WaitForSeconds(SleepTime);
}
}
// Setting it back to the original value just to clean it up properly incase of small float rounding issues
_mySpriteRenderer.color = _myColor;
}
}
It works.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CoinControl : MonoBehaviour
{
SpriteRenderer thisSpriteRenderer;
SpriteRenderer blockSpritRenderer;
void Awake()
{
thisSpriteRenderer = GetComponent<SpriteRenderer>();
}
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.gameObject.CompareTag("Block"))
{
blockSpritRenderer = collision.gameObject.GetComponent<SpriteRenderer>();
Color blockColor = blockSpritRenderer.color;
if (collision.gameObject.GetComponent<BlockMovement>().isActiveBool
&& blockColor == thisSpriteRenderer.color)
{
Destroy(gameObject);
}
else if (blockColor != thisSpriteRenderer.color)
{
StartCoroutine(Flash());
}
}
}
IEnumerator Flash()
{
Color fadeColor = thisSpriteRenderer.color;
fadeColor.a = 0.1f;
Color solidColor = thisSpriteRenderer.color;
solidColor.a = 1f;
thisSpriteRenderer.color = fadeColor;
yield return new WaitForSeconds(.08f);
thisSpriteRenderer.color = solidColor;
yield return new WaitForSeconds(.08f);
thisSpriteRenderer.color = fadeColor;
yield return new WaitForSeconds(.08f);
thisSpriteRenderer.color = solidColor;
yield return new WaitForSeconds(.08f);
thisSpriteRenderer.color = fadeColor;
yield return new WaitForSeconds(.08f);
thisSpriteRenderer.color = solidColor;
yield return new WaitForSeconds(.08f);
thisSpriteRenderer.color = fadeColor;
yield return new WaitForSeconds(.08f);
thisSpriteRenderer.color = solidColor;
}
}
Here is my solution
public class Coin : MonoBehaviour
{
private SpriteRenderer spriteRenderer;
private bool flashingRoutineIsRunning;
private void Awake()
{
spriteRenderer = GetComponent<SpriteRenderer>();
}
private void OnTriggerStay2D(Collider2D collision)
{
if (collision.GetComponent<BlockMovement>())
{
SpriteRenderer blockColor = collision.GetComponent<SpriteRenderer>();
if (blockColor.color == spriteRenderer.color)
{
Destroy(this.gameObject);
}
else
{
if (!flashingRoutineIsRunning)
{
StartCoroutine(FlashingRoutine());
}
}
}
}
private IEnumerator FlashingRoutine()
{
flashingRoutineIsRunning = true;
Color initialColor = spriteRenderer.color;
for (int i = 0; i < 6; i++)
{
yield return new WaitForSeconds(.1f);
spriteRenderer.color = new Color(spriteRenderer.color.r, spriteRenderer.color.g, spriteRenderer.color.b, .5f);
yield return new WaitForSeconds(.1f);
spriteRenderer.color = initialColor;
}
flashingRoutineIsRunning = false;
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.PlayerLoop;
public class CoinController : MonoBehaviour
{
bool isTouching;
SpriteRenderer blockRenderer;
SpriteRenderer coinRenderer;
private void Start()
{
coinRenderer = GetComponent<SpriteRenderer>();
}
private void Update()
{
if (blockRenderer != null)
{
Color jeffColor = blockRenderer.color;
jeffColor.a = Mathf.PingPong(Time.time, 1);
if (isTouching)
{
blockRenderer.color = jeffColor;
}
else
{
jeffColor.a = 1;
blockRenderer.color = jeffColor;
}
}
}
private void OnTriggerEnter2D(Collider2D collision)
{
blockRenderer = collision.GetComponent<SpriteRenderer>();
if(blockRenderer.color != coinRenderer.color)
{
isTouching = true;
}
else if(blockRenderer.color == coinRenderer.color)
{
Destroy(gameObject);
}
}
private void OnTriggerExit2D(Collider2D collision)
{
blockRenderer = collision.GetComponent<SpriteRenderer>();
isTouching = false;
}