Search Unity

Share code between float and Vector3?

Discussion in 'Scripting' started by rakkarage, Nov 28, 2014.

  1. rakkarage

    rakkarage

    Joined:
    Feb 3, 2014
    Posts:
    683
    i got some ease code for float and vector3/color
    the math code for some functions (bounce etc) is the same in both spots except the type
    is there any way to share one set of code?

    i cannot share with template because cannot T + T
    cannot restrict types to both float and Vector3
    maybe lambda or something idk

    thanks

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.Events;
    5. public enum EaseType
    6. {
    7.     Linear,
    8.     Hermite,
    9.     Sinerp,
    10.     Coserp,
    11.     Spring,
    12.     BounceIn,
    13.     BounceOut,
    14.     BounceInOut
    15. }
    16. public static class Ease
    17. {
    18.     private delegate float EaseHandler(float start, float end, float time);
    19.     private static Dictionary<EaseType, EaseHandler> _types = new Dictionary<EaseType, EaseHandler>
    20.     {
    21.         {EaseType.Linear, Mathf.Lerp},
    22.         {EaseType.Hermite, Hermite},
    23.         {EaseType.Sinerp, Sinerp},
    24.         {EaseType.Coserp, Coserp},
    25.         {EaseType.Spring, Spring},
    26.         {EaseType.BounceIn, BounceIn},
    27.         {EaseType.BounceOut, BounceOut},
    28.         {EaseType.BounceInOut, BounceInOut}
    29.     };
    30.     public static void Go(MonoBehaviour m, float start, float end, float time, float delay, EaseType type, UnityAction<float> update, UnityAction complete)
    31.     {
    32.         m.StartCoroutine(GoCoroutine(start, end, time, delay, type, update, complete));
    33.     }
    34.     private static IEnumerator GoCoroutine(float start, float end, float time, float delay, EaseType type, UnityAction<float> update, UnityAction complete)
    35.     {
    36.         if (delay > 0f)
    37.         yield return new WaitForSeconds(delay);
    38.         var i = 0f;
    39.         while (i <= 1f)
    40.         {
    41.             i += Time.deltaTime / time;
    42.             update(_types[type](start, end, i));
    43.             yield return null;
    44.         }
    45.         if (complete != null)
    46.         complete();
    47.     }
    48.     private static float Hermite(float start, float end, float time)
    49.     {
    50.         return Mathf.Lerp(start, end, time * time * (3f - 2f * time));
    51.     }
    52.     private static float Sinerp(float start, float end, float time)
    53.     {
    54.         return Mathf.Lerp(start, end, Mathf.Sin(time * Mathf.PI * .5f));
    55.     }
    56.     private static float Coserp(float start, float end, float time)
    57.     {
    58.         return Mathf.Lerp(start, end, 1f - Mathf.Cos(time * Mathf.PI * .5f));
    59.     }
    60.     private static float Spring(float start, float end, float time)
    61.     {
    62.         time = Mathf.Clamp01(time);
    63.         time = (Mathf.Sin(time * Mathf.PI * (.2f + 2.5f * time * time * time)) * Mathf.Pow(1f - time, 2.2f) + time) * (1f + (1.2f * (1f - time)));
    64.         return start + (end - start) * time;
    65.     }
    66.     public static float BounceIn(float start, float end, float time)
    67.     {
    68.         end -= start;
    69.         return end - BounceOut(0f, end, 1f - time) + start;
    70.     }
    71.     public static float BounceOut(float start, float end, float time)
    72.     {
    73.         time /= 1f;
    74.         end -= start;
    75.         if (time < (1f / 2.75f))
    76.         return end * (7.5625f * time * time) + start;
    77.         if (time < (2f / 2.75f))
    78.         return end * (7.5625f * (time -= (1.5f / 2.75f)) * time + .75f) + start;
    79.         if (time < (2.5f / 2.75f))
    80.         return end * (7.5625f * (time -= (2.25f / 2.75f)) * time + .9375f) + start;
    81.         return end * (7.5625f * (time -= (2.625f / 2.75f)) * time + .984375f) + start;
    82.     }
    83.     public static float BounceInOut(float start, float end, float time)
    84.     {
    85.         end -= start;
    86.         if (time < .5f)
    87.         return BounceIn(0f, end, time * 2f) * .5f + start;
    88.         return BounceOut(0f, end, time * 2f - 1f) * .5f + end * .5f + start;
    89.     }
    90. }
    91. public static class Ease3
    92. {
    93.     private delegate Vector3 EaseHandler(Vector3 start, Vector3 end, float time);
    94.     private static Dictionary<EaseType, EaseHandler> _types = new Dictionary<EaseType, EaseHandler>
    95.     {
    96.         {EaseType.Linear, Vector3.Lerp},
    97.         {EaseType.Hermite, Hermite},
    98.         {EaseType.Sinerp, Sinerp},
    99.         {EaseType.Coserp, Coserp},
    100.         {EaseType.Spring, Spring},
    101.         {EaseType.BounceIn, BounceIn},
    102.         {EaseType.BounceOut, BounceOut},
    103.         {EaseType.BounceInOut, BounceInOut}
    104.     };
    105.     public static void Go(MonoBehaviour m, Vector3 start, Vector3 end, float time, float delay, EaseType type, UnityAction<Vector3> update, UnityAction complete)
    106.     {
    107.         m.StartCoroutine(GoCoroutine(start, end, time, delay, type, update, complete));
    108.     }
    109.     private static IEnumerator GoCoroutine(Vector3 start, Vector3 end, float time, float delay, EaseType type, UnityAction<Vector3> update, UnityAction complete)
    110.     {
    111.         if (delay > 0f)
    112.         yield return new WaitForSeconds(delay);
    113.         var i = 0f;
    114.         while (i <= 1f)
    115.         {
    116.             i += Time.deltaTime / time;
    117.             update(_types[type](start, end, i));
    118.             yield return null;
    119.         }
    120.         if (complete != null)
    121.         complete();
    122.     }
    123.     public static void GoPosition(MonoBehaviour m, GameObject o, Vector3 start, Vector3 end, float time, float delay, EaseType type)
    124.     {
    125.         m.StartCoroutine(GoPositionCoroutine(o, start, end, time, delay, type));
    126.     }
    127.     private static IEnumerator GoPositionCoroutine(GameObject o, Vector3 start, Vector3 end, float time, float delay, EaseType type)
    128.     {
    129.         if (delay > 0f)
    130.         yield return new WaitForSeconds(delay);
    131.         var i = 0f;
    132.         while (i <= 1f)
    133.         {
    134.             i += Time.deltaTime / time;
    135.             o.transform.localPosition = _types[type](start, end, i);
    136.             yield return null;
    137.         }
    138.         o.transform.localPosition = end;
    139.     }
    140.     public static void GoRotation(MonoBehaviour m, GameObject o, Vector3 start, Vector3 end, float time, float delay, EaseType type)
    141.     {
    142.         m.StartCoroutine(GoRotationCoroutine(o, start, end, time, delay, type));
    143.     }
    144.     private static IEnumerator GoRotationCoroutine(GameObject o, Vector3 start, Vector3 end, float time, float delay, EaseType type)
    145.     {
    146.         if (delay > 0f)
    147.         yield return new WaitForSeconds(delay);
    148.         var i = 0f;
    149.         while (i <= 1f)
    150.         {
    151.             i += Time.deltaTime / time;
    152.             o.transform.localRotation = Quaternion.Euler(_types[type](start, end, i));
    153.             yield return null;
    154.         }
    155.         o.transform.localRotation = Quaternion.Euler(end);
    156.     }
    157.     public static void GoScale(MonoBehaviour m, GameObject o, Vector3 start, Vector3 end, float time, float delay, EaseType type)
    158.     {
    159.         m.StartCoroutine(GoScaleCoroutine(o, start, end, time, delay, type));
    160.     }
    161.     private static IEnumerator GoScaleCoroutine(GameObject o, Vector3 start, Vector3 end, float time, float delay, EaseType type)
    162.     {
    163.         if (delay > 0f)
    164.         yield return new WaitForSeconds(delay);
    165.         var i = 0f;
    166.         while (i <= 1f)
    167.         {
    168.             i += Time.deltaTime / time;
    169.             o.transform.localScale = _types[type](start, end, i);
    170.             yield return null;
    171.         }
    172.         o.transform.localScale = end;
    173.     }
    174.     private static Vector3 Hermite(Vector3 start, Vector3 end, float time)
    175.     {
    176.         return Vector3.Lerp(start, end, time * time * (3f - 2f * time));
    177.     }
    178.     private static Vector3 Sinerp(Vector3 start, Vector3 end, float time)
    179.     {
    180.         return Vector3.Lerp(start, end, Mathf.Sin(time * Mathf.PI * .5f));
    181.     }
    182.     private static Vector3 Coserp(Vector3 start, Vector3 end, float time)
    183.     {
    184.         return Vector3.Lerp(start, end, 1f - Mathf.Cos(time * Mathf.PI * .5f));
    185.     }
    186.     private static Vector3 Spring(Vector3 start, Vector3 end, float time)
    187.     {
    188.         time = Mathf.Clamp01(time);
    189.         time = (Mathf.Sin(time * Mathf.PI * (.2f + 2.5f * time * time * time)) * Mathf.Pow(1f - time, 2.2f) + time) * (1f + (1.2f * (1f - time)));
    190.         return start + (end - start) * time;
    191.     }
    192.     public static Vector3 BounceIn(Vector3 start, Vector3 end, float time)
    193.     {
    194.         end -= start;
    195.         return end - BounceOut(Vector3.zero, end, 1f - time) + start;
    196.     }
    197.     public static Vector3 BounceOut(Vector3 start, Vector3 end, float time)
    198.     {
    199.         time /= 1f;
    200.         end -= start;
    201.         if (time < (1f / 2.75f))
    202.         return end * (7.5625f * time * time) + start;
    203.         if (time < (2f / 2.75f))
    204.         return end * (7.5625f * (time -= (1.5f / 2.75f)) * time + .75f) + start;
    205.         if (time < (2.5f / 2.75f))
    206.         return end * (7.5625f * (time -= (2.25f / 2.75f)) * time + .9375f) + start;
    207.         return end * (7.5625f * (time -= (2.625f / 2.75f)) * time + .984375f) + start;
    208.     }
    209.     public static Vector3 BounceInOut(Vector3 start, Vector3 end, float time)
    210.     {
    211.         end -= start;
    212.         if (time < .5f)
    213.         return BounceIn(Vector3.zero, end, time * 2f) * .5f + start;
    214.         return BounceOut(Vector3.zero, end, time * 2f - 1f) * .5f + end * .5f + start;
    215.     }
    216. }
    217.  
     
    Stoven likes this.
  2. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,325
    I think it's impossible to make this much cleaner due to a combination of
    1. C#'s operators are static, meaning they can't be in interfaces.
    2. C# doesn't allow you to add interface conformance in extensions.
    3. Vector3 is sealed.
     
    rakkarage likes this.
  3. Gibbonator

    Gibbonator

    Joined:
    Jul 27, 2012
    Posts:
    204
    You could try something like the policy-based design pattern from c++ (http://en.wikipedia.org/wiki/Policy-based_design). This is similar to the strategy pattern but gives the compiler more information it can use to optimise function calls.

    Wrap up all the core maths like the Lerp function into a policy and implement the policy for each type you need to handle.

    In c sharp a generic utility class would look something like this:

    Code (csharp):
    1.  
    2.   public interface ILerpPolicy<T>
    3.   {
    4.     T Lerp(T start, T end, float time);
    5.   }
    6.  
    7.   public struct FloatLerpPolicy : ILerpPolicy<float>
    8.   {
    9.     public float Lerp(float start, float end, float time)
    10.     {
    11.       time = Mathf.Clamp01(time);
    12.       return ((1.0f - time) * start) + (time * end);
    13.     }
    14.   }
    15.  
    16.   public struct Vector3LerpPolicy : ILerpPolicy<Vector3>
    17.   {
    18.     public Vector3 Lerp(Vector3 start, Vector3 end, float time)
    19.     {
    20.       time = Mathf.Clamp01(time);
    21.       return ((1.0f - time) * start) + (time * end);
    22.     }
    23.   }
    24.  
    25.   public static class GenericUtility<T, LerpPolicy> where LerpPolicy : struct, ILerpPolicy<T>
    26.   {
    27.     public static T Hermite(T start, T end, float time)
    28.     {
    29.       return s_lerpPolicy.Lerp(start, end, time * time * (3.0f - 2.0f * time));
    30.     }
    31.  
    32.     public static T Sinerp(T start, T end, float time)
    33.     {
    34.       return s_lerpPolicy.Lerp(start, end, Mathf.Sin(time * Mathf.PI * 0.5f));
    35.     }
    36.     public static T Coserp(T start, T end, float time)
    37.     {
    38.       return s_lerpPolicy.Lerp(start, end, 1.0f - Mathf.Cos(time * Mathf.PI * 0.5f));
    39.     }
    40.  
    41.     static readonly LerpPolicy s_lerpPolicy = default(LerpPolicy);
    42.   }
    43.  
    44.   public static class PolicyTest
    45.   {
    46.     public static void DoPolicyTest(float time)
    47.     {
    48.       float floatResult = GenericUtility<float, FloatLerpPolicy>.Hermite(0.0f, 1.0f, time);
    49.  
    50.       Debug.Log(floatResult.ToString());
    51.  
    52.       Vector3 vectorResult = GenericUtility<Vector3, Vector3LerpPolicy>.Hermite(Vector3.zero, Vector3.one, time);
    53.  
    54.       Debug.Log(vectorResult.ToString());
    55.     }
    56.   }
    57.  
    If you're lucky the compiler may be smart enough to avoid virtual calls on the policy. The function calls into the utility are a bit ugly though...
     
    rakkarage and Stoven like this.
  4. rakkarage

    rakkarage

    Joined:
    Feb 3, 2014
    Posts:
    683