Hello All,
I just wanted to share a code snippet I found while searching the internet for some assistance.
In my game, I was thinking of having some trailing shadows like in Castlevania when a special magic spell is activated. Well, I was having a doozy of a time trying to get it to work so I did a search and found that my issue was already solved and the answer was shared with the public. I thought it would be nice to see the solution and give credit where credit is due.
The effect I was trying to achieve was the trailing shadows shown here
The solution was given by a user named jeffreyschoch in the Unity forums.
using UnityEngine; using System.Collections; using System.Collections.Generic; public class SpriteAfterImage : MonoBehaviour { [Tooltip("The color each after-image will fade to over its lifetime. Alpha of 0 is recommended")] public Color finalColor = Color.clear; [Tooltip("The amount of time an after-image will take to fade away.")] public float trailLifetime = .25f; [Tooltip("The distance this object must move to spawn one after-image.")] public float distancePerSpawn = .1f; [Tooltip("Optimization - number of after-images to create before the effect starts, to reduce the start-up load.")] public int spawnOnStart = 0; private SpriteRenderer mainSpriteRenderer; // the sprite renderer to trail after private List<SpriteRenderer> readyObjects; // the list of objects ready to be shown private float distanceTraveledSinceLastSpawn; // the distance this object has moved since the last object was shown private Vector3 lastSpawnPosition; // the position the last object was spawned private Color initialColor; private void Awake() { // get the sprite renderer on this object mainSpriteRenderer = GetComponent<SpriteRenderer>(); initialColor = mainSpriteRenderer.color; // initialize the empty list readyObjects = new List<SpriteRenderer>(); // optionally populate list beforehand with objects to use for(int i = 0; i < spawnOnStart; i++) { readyObjects.Add(makeSpriteObject()); } } private void OnEnable() { StartCoroutine(trailCoroutine()); } // function to create a sprite gameobject ready for use private SpriteRenderer makeSpriteObject() { // create a gameobject named "TrailSprite" with a SpriteRenderer component GameObject spriteObject = new GameObject("TrailSprite", typeof(SpriteRenderer)); // parent the object to this object so that it follows it spriteObject.transform.SetParent(transform); // center it on this object spriteObject.transform.localPosition = Vector3.zero; // hide it spriteObject.SetActive(false); return spriteObject.GetComponent<SpriteRenderer>(); } private IEnumerator trailCoroutine() { // keep running while this component is enabled while(enabled) { // get the distance between the current position and the last position // a trail object was spawned distanceTraveledSinceLastSpawn = Vector2.Distance(lastSpawnPosition, transform.position); // if that distance is greater than the specified distance per spawn if(distanceTraveledSinceLastSpawn > distancePerSpawn) { // if there aren't any objects ready to show, spawn a new one if(readyObjects.Count == 0) { // add that object's sprite renderer to the trail list readyObjects.Add(makeSpriteObject()); } // get the next object in the ready list SpriteRenderer nextObject = readyObjects[0]; // set this trailSprite to reflect the current player sprite nextObject.sprite = mainSpriteRenderer.sprite; // this makes it so that the trail will render behind the main sprite nextObject.sortingLayerID = mainSpriteRenderer.sortingLayerID; nextObject.sortingOrder = mainSpriteRenderer.sortingOrder - 1; // set it loose in the world nextObject.transform.SetParent(null, true); // match the copy's scale to the sprite's world-space scale nextObject.transform.localScale = mainSpriteRenderer.transform.lossyScale; // show it nextObject.gameObject.SetActive(true); // start it fading out over time StartCoroutine(fadeOut(nextObject)); // remove it from the list of ready objects readyObjects.Remove(nextObject); // save this position as the last spawned position lastSpawnPosition = transform.position; // reset the distance traveled distanceTraveledSinceLastSpawn = 0; } // wait until next frame to continue the loop yield return null; } // reduce number of sprites back to original pool size foreach(SpriteRenderer sprite in this.readyObjects) { if(this.readyObjects.Count > spawnOnStart) { Destroy(sprite.gameObject); } else { resetObject(sprite); } } } private IEnumerator fadeOut(SpriteRenderer sprite) { float timeElapsed = 0; // while the elapsed time is less than the specified trailLifetime while(timeElapsed < trailLifetime) { // get a number between 0 and 1 that represents how much time has passed // 0 = no time has passed, 1 = trailLifetime seconds has passed float progress = Mathf.Clamp01(timeElapsed / trailLifetime); // linearly interpolates between the initial color and the final color // based on the value of progress (0 to 1) sprite.color = Color.Lerp(initialColor, finalColor, progress); // track the time passed timeElapsed += Time.deltaTime; // wait until next frame to continue the loop yield return null; } // reset the object so that it can be reused resetObject(sprite); } // resets the object so that it is ready to use again private void resetObject(SpriteRenderer sprite) { // hide the sprite sprite.gameObject.SetActive(false); // reset the tint to default sprite.color = initialColor; // parent it to this object sprite.transform.SetParent(transform); // center it on this object sprite.transform.localPosition = Vector3.zero; // add it to the ready list readyObjects.Add(sprite); } }
You can find the original article located here: https://forum.unity.com/threads/shadow-clones-castlevania-style-2d.482427/