Search Unity

  1. Unity 2018.3 is now released.
    Dismiss Notice
  2. The Unity Pro & Visual Studio Professional Bundle gives you the tools you need to develop faster & collaborate more efficiently. Learn more.
    Dismiss Notice
  3. Want more efficiency in your development work? Sign up to receive weekly tech and creative know-how from Unity experts.
    Dismiss Notice
  4. Build games and experiences that can load instantly and without install. Explore the Project Tiny Preview today!
    Dismiss Notice
  5. Nominations have been announced for this years Unity Awards. Celebrate the wonderful projects made by your peers this year and get voting! Vote here!
    Dismiss Notice
  6. Want to provide direct feedback to the Unity team? Join the Unity Advisory Panel.
    Dismiss Notice
  7. Improve your Unity skills with a certified instructor in a private, interactive classroom. Watch the overview now.
    Dismiss Notice

shadow clones(Castlevania style) 2D

Discussion in '2D' started by fosmark13, Jul 9, 2017.

  1. fosmark13

    fosmark13

    Joined:
    Feb 16, 2015
    Posts:
    91
    hi i was wondering if anyone can guide me through how i can create shadow clones for my character?? i just don't know how to achieve just like Castlevania Symphony of the Night, the way richter moves, it's awesome but i can't figure out how they made it since the shadow clone reacts to the sprite moves richter does. Here is a reference image of what i'm trying to explain.

    If someone could tell me a method or some info to achieve this would be great, i'll be in touch, thanks Captura de pantalla 2017-07-09 a la(s) 16.14.57.png to all.
     
  2. jeffreyschoch

    jeffreyschoch

    Joined:
    Jan 21, 2015
    Posts:
    2,309
    This has been asked for several times, so I'll just pass along a script I wrote awhile back.

    Put this on anything with a sprite renderer, enable/disable the component to turn it on and off.

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4.  
    5. public class SpriteAfterImage : MonoBehaviour {
    6.     [Tooltip("The color each after-image will fade to over its lifetime. Alpha of 0 is recommended")]
    7.     public Color finalColor = Color.clear;
    8.     [Tooltip("The amount of time an after-image will take to fade away.")]
    9.     public float trailLifetime = .25f;
    10.     [Tooltip("The distance this object must move to spawn one after-image.")]
    11.     public float distancePerSpawn = .1f;
    12.     [Tooltip("Optimization - number of after-images to create before the effect starts, to reduce the start-up load.")]
    13.     public int spawnOnStart = 0;
    14.     private SpriteRenderer mainSpriteRenderer;    // the sprite renderer to trail after
    15.     private List<SpriteRenderer> readyObjects;    // the list of objects ready to be shown
    16.     private float distanceTraveledSinceLastSpawn; // the distance this object has moved since the last object was shown
    17.     private Vector3 lastSpawnPosition;            // the position the last object was spawned
    18.     private Color initialColor;
    19.     private void Awake() {
    20.         // get the sprite renderer on this object
    21.         mainSpriteRenderer = GetComponent<SpriteRenderer>();
    22.         initialColor = mainSpriteRenderer.color;
    23.         // initialize the empty list
    24.         readyObjects = new List<SpriteRenderer>();
    25.         // optionally populate list beforehand with objects to use
    26.         for(int i = 0; i < spawnOnStart; i++) {
    27.             readyObjects.Add(makeSpriteObject());
    28.         }
    29.     }
    30.     private void OnEnable() {
    31.         StartCoroutine(trailCoroutine());
    32.     }
    33.     // function to create a sprite gameobject ready for use
    34.     private SpriteRenderer makeSpriteObject() {
    35.         // create a gameobject named "TrailSprite" with a SpriteRenderer component
    36.         GameObject spriteObject = new GameObject("TrailSprite", typeof(SpriteRenderer));
    37.         // parent the object to this object so that it follows it
    38.         spriteObject.transform.SetParent(transform);
    39.         // center it on this object
    40.         spriteObject.transform.localPosition = Vector3.zero;
    41.         // hide it
    42.         spriteObject.SetActive(false);
    43.         return spriteObject.GetComponent<SpriteRenderer>();
    44.     }
    45.     private IEnumerator trailCoroutine() {
    46.         // keep running while this component is enabled
    47.         while(enabled) {
    48.             // get the distance between the current position and the last position
    49.             // a trail object was spawned
    50.             distanceTraveledSinceLastSpawn = Vector2.Distance(lastSpawnPosition, transform.position);
    51.             // if that distance is greater than the specified distance per spawn
    52.             if(distanceTraveledSinceLastSpawn > distancePerSpawn) {
    53.                 // if there aren't any objects ready to show, spawn a new one
    54.                 if(readyObjects.Count == 0) {
    55.                     // add that object's sprite renderer to the trail list
    56.                     readyObjects.Add(makeSpriteObject());
    57.                 }
    58.                 // get the next object in the ready list
    59.                 SpriteRenderer nextObject = readyObjects[0];
    60.                 // set this trailSprite to reflect the current player sprite
    61.                 nextObject.sprite = mainSpriteRenderer.sprite;
    62.                 // this makes it so that the trail will render behind the main sprite
    63.                 nextObject.sortingLayerID = mainSpriteRenderer.sortingLayerID;
    64.                 nextObject.sortingOrder = mainSpriteRenderer.sortingOrder - 1;
    65.                 // set it loose in the world
    66.                 nextObject.transform.SetParent(null, true);
    67.                 // match the copy's scale to the sprite's world-space scale
    68.                 nextObject.transform.localScale = mainSpriteRenderer.transform.lossyScale;
    69.                 // show it
    70.                 nextObject.gameObject.SetActive(true);
    71.                 // start it fading out over time
    72.                 StartCoroutine(fadeOut(nextObject));
    73.                 // remove it from the list of ready objects
    74.                 readyObjects.Remove(nextObject);
    75.                 // save this position as the last spawned position
    76.                 lastSpawnPosition = transform.position;
    77.                 // reset the distance traveled
    78.                 distanceTraveledSinceLastSpawn = 0;
    79.             }
    80.             // wait until next frame to continue the loop
    81.             yield return null;
    82.         }
    83.         // reduce number of sprites back to original pool size
    84.         foreach(SpriteRenderer sprite in this.readyObjects) {
    85.             if(this.readyObjects.Count > spawnOnStart) {
    86.                 Destroy(sprite.gameObject);
    87.             } else {
    88.                 resetObject(sprite);
    89.             }
    90.         }
    91.     }
    92.     private IEnumerator fadeOut(SpriteRenderer sprite) {
    93.         float timeElapsed = 0;
    94.         // while the elapsed time is less than the specified trailLifetime
    95.         while(timeElapsed < trailLifetime) {
    96.             // get a number between 0 and 1 that represents how much time has passed
    97.             // 0 = no time has passed, 1 = trailLifetime seconds has passed
    98.             float progress = Mathf.Clamp01(timeElapsed / trailLifetime);
    99.             // linearly interpolates between the initial color and the final color
    100.             // based on the value of progress (0 to 1)
    101.             sprite.color = Color.Lerp(initialColor, finalColor, progress);
    102.             // track the time passed
    103.             timeElapsed += Time.deltaTime;
    104.             // wait until next frame to continue the loop
    105.             yield return null;
    106.         }
    107.         // reset the object so that it can be reused
    108.         resetObject(sprite);
    109.     }
    110.     // resets the object so that it is ready to use again
    111.     private void resetObject(SpriteRenderer sprite) {
    112.         // hide the sprite
    113.         sprite.gameObject.SetActive(false);
    114.         // reset the tint to default
    115.         sprite.color = initialColor;
    116.         // parent it to this object
    117.         sprite.transform.SetParent(transform);
    118.         // center it on this object
    119.         sprite.transform.localPosition = Vector3.zero;
    120.         // add it to the ready list
    121.         readyObjects.Add(sprite);
    122.     }
    123. }
     
  3. fosmark13

    fosmark13

    Joined:
    Feb 16, 2015
    Posts:
    91

    Dude this is just awesome!!! it works so amazing that i just can't describe how happy i am!!! you're really a master!!!
    I appreciate so much!!! thank you!!!!
     
    jeffreyschoch likes this.