CLONING COLORS QUEST: ‘Flashy Coin’ - Solutions

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;
    }
}
4 Likes

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");
    }
}

2 Likes

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.

1 Like

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);
            }
        }
    }
}
1 Like

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 :slight_smile:

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;
}

Privacy & Terms