2016-03-20 10 views
4

Beachten Sie die beiden Erweiterungen, eine für Float, eine für Vector3.C# generisch mit kleinen Unterschied für Typen?

Beachten Sie, es gibt nur einen kleinen Unterschied in der var( Anruf.

In C# könnten diese als eine wie eine generische ??

Das Wesen meiner Frage ist:

innerhalb eines generischen, können Sie Zweig auf die Art des Typ?

public static IEnumerator Tweeng(this float duration, 
     System.Action<float> vary, float aa, float zz) 
{ 
    float sT = Time.time; 
    float eT = sT + duration; 

    while (Time.time < eT) 
    { 
     float t = (Time.time-sT)/duration; 
     vary(Mathf.SmoothStep(aa,zz, t)); // slight difference here 
     yield return null; 
    } 

    vary(zz); 
} 

public static IEnumerator Tweeng(this float duration, 
     System.Action<Vector3> vary, Vector3 aa, Vector3 zz) 
{ 
    float sT = Time.time; 
    float eT = sT + duration; 

    while (Time.time < eT) 
    { 
     float t = (Time.time-sT)/duration; 
     vary(Vector3.Lerp(aa,zz, t)); // slight difference here 
     yield return null; 
    } 

    vary(zz); 
} 

(BTW für alle # Gurus c lesen, ist das Codebeispiel in Unity, in dem Sie das Rahmensystem in einem Koroutine zuzugreifen.)

Für alle Unity Devs Lesen, Beispiele dafür, wie Sie rufen Tweeng

// tweeng z to 20 degrees in .12 seconds 
StartCoroutine(.12f.Tweeng((t)=>transform.Eulers(0f,0f,t), 0f,20f)); 
// fade in alpha in .75 seconds 
StartCoroutine(.75f.Tweeng((u)=>{c.a=u;s.color=c;}, 0f,1f)); 

(Wenn Sie Unity neu sind und nicht mit dem Grundkonzept von Erweiterungen vertraut, hier ist ein intro.)

+0

Ja, Sie könnten ein generisches Argument 'T' (' float'/'Vector3') hinzufügen und dann' Lerp/SmoothStep' als 'Func ' – Rob

+0

ah, im Grunde ein weiteres Argument hinzufügen. Es wäre toll, wenn die Erweiterung es herausfinden würde. Ich denke, man könnte diese Division auf eine andere generische Funktion drücken. – Fattie

Antwort

3

Sie können es tun, wenn Sie eine zusätzliche Func<T,T> machen, die vor dem Aufruf der Aktion var eine Umwandlung durchführt (die Sie umbenennen sollten, da var ein C# -Schlüsselwort ist).Hier

ist ein Ansatz, dass Sie könnte nehmen:

public static IEnumerator Tweeng<T>(
    this float duration 
, System.Action<T> varAction 
, T aa 
, T zz 
) { 
    Func<T,T,float,T> transform = MakeTransform<T>(); 
    float sT = Time.time; 
    float eT = sT + duration; 
    while (Time.time < eT) { 
     float t = (Time.time-sT)/duration; 
     varAction(transform(aa, zz, t)); 
     yield return null; 
    } 
    varAction(zz); 
} 

private static Func<T,T,float,T> MakeTransform<T>() { 
    if (typeof(T) == typeof(float)) { 
     Func<float, float, float, float> f = Mathf.SmoothStep; 
     return (Func<T,T,float,T>)(Delegate)f; 
    } 
    if (typeof(T) == typeof(Vector3)) { 
     Func<Vector3, Vector3, float, Vector3> f = Vector3.Lerp; 
     return (Func<T,T,float,T>)(Delegate)f; 
    } 
    throw new ArgumentException("Unexpected type "+typeof(T)); 
} 

Es kann sogar inline erfolgen:

public static IEnumerator DasTweeng<T>(this float duration, System.Action<T> vary, T aa, T zz) 
    { 
    float sT = Time.time; 
    float eT = sT + duration; 

    Func<T,T,float,T> step; 

    if (typeof(T) == typeof(float)) 
     step = (Func<T,T,float,T>)(Delegate)(Func<float, float, float, float>)Mathf.SmoothStep; 
    else if (typeof(T) == typeof(Vector3)) 
     step = (Func<T,T,float,T>)(Delegate)(Func<Vector3, Vector3, float, Vector3>)Vector3.Lerp; 
    else 
     throw new ArgumentException("Unexpected type "+typeof(T)); 

    while (Time.time < eT) 
     { 
     float t = (Time.time-sT)/duration; 
     vary(step(aa,zz, t)); 
     yield return null; 
     } 
    vary(zz); 
    } 

Vielleicht eine natürlichere Idiom ist

Delegate d; 

    if (typeof(T) == typeof(float)) 
     d = (Func<float, float, float, float>)Mathf.SmoothStep; 
    else if (typeof(T) == typeof(Vector3)) 
     d = (Func<Vector3, Vector3, float, Vector3>)Vector3.Lerp; 
    else 
     throw new ArgumentException("Unexpected type "+typeof(T)); 

    Func<T,T,float,T> step = (Func<T,T,float,T>)d; 
+0

@JoeBlow Sie können den Typ einschalten, indem Sie 'if (typeof (T) == typeof (float)) eingeben. .. (Es tut mir leid wegen des Fehlers in meinem Code oben, es ist jetzt behoben). Sie könnten es sicherlich innerhalb der Schleife tun, aber dann müssten Sie bei jeder Iteration die gleiche Entscheidung treffen. Wenn Sie einen 'Func' im Voraus speichern, können Sie einmal ein' if' machen und das Ergebnis bei jeder Iteration wiederverwenden. – dasblinkenlight

+0

richtig, aber was ich meine ist, kann man den Code von 'MakeTransform' einfach in' Tweeng' setzen (sicher, an der Spitze zu vermeiden, es zu tun, jede Schleife) ... Ich werde es versuchen! – Fattie

+0

@JoeBlow Absolut, man könnte den Code innerhalb von 'Tweeng 'inline einreihen. Ich habe es getrennt, weil für mich die Bedingung auf 'typeof (T)' etwas "unrein" ist, während der Rest von 'Tweeng ' nett und sauber ist. – dasblinkenlight

3

Sie können festlegen, Ihre Methode wie folgt:

public static IEnumerator Tweeng<T>(this float duration, 
     System.Action<T> var, T aa, T zz, Func<T,T,float,T> thing) 
{ 
    float sT = Time.time; 
    float eT = sT + duration; 

    while (Time.time < eT) 
    { 
     float t = (Time.time - sT)/duration; 
     var(thing(aa, zz, t)); 
     yield return null; 
    } 

    var(zz); 
} 

Und dann mit es:

float a = 5; 
float b = 0; 
float c = 0; 
a.Tweeng(q => {}, b, c, Mathf.SmoothStep); 

Oder:

float a = 0; 
Vector3 b = null; 
Vector3 c = null; 
a.Tweeng(q => {}, b, c, Vector3.Lerp); 

Alternativ, wenn Sie die Methode übergeben loswerden wollen, können Sie einfach Überlastungen müssen damit umgehen:

public static IEnumerator Tweeng(this float duration, System.Action<float> var, float aa, float zz) 
{ 
    return Tweeng(duration, var, aa, zz, Mathf.SmoothStep); 
} 
public static IEnumerator Tweeng(this float duration, System.Action<Vector3> var, Vector3 aa, Vector3 zz) 
{ 
    return Tweeng(duration, var, aa, zz, Vector3.Lerp); 
} 

private static IEnumerator Tweeng<T>(this float duration, 
     System.Action<T> var, T aa, T zz, Func<T,T,float,T> thing) 
{ 
    float sT = Time.time; 
    float eT = sT + duration; 

    while (Time.time < eT) 
    { 
     float t = (Time.time - sT)/duration; 
     var(thing(aa, zz, t)); 
     yield return null; 
    } 

    var(zz); 
} 

und dann mit:

012.351.
float a = 5; 
float b = 0; 
float c = 0; 
a.Tweeng(q => {}, b, c); 

Oder:

float a = 0; 
Vector3 b = null; 
Vector3 c = null; 
a.Tweeng(q => {}, b, c); 


Stub Methoden in LINQPad/ohne Einheit zu kompilieren:

public class Mathf { public static float SmoothStep(float aa, float zz, float t) => 0; } 
public class Time { public static float time => DateTime.Now.Ticks; } 
public class Vector3 { public static Vector3 Lerp(Vector3 aa, Vector3 zz, float t) => null; } 
+0

Ich kann bestätigen, das funktioniert perfekt! thx – Fattie

0

ich die Tweeng Sache mochte, aber warum Schwimmer zu verlängern, wenn die Coroutine nur verwendet werden kann, auf MonoBehaviours? Sie sollten die Erweiterungen für MonoBehaviour tun so, zum Beispiel ich eine Verlängerung tat zu tun Interpolation:

public static void _Interpolate(this MonoBehaviour monoBehaviour, float duration, 
    Action<float, bool> callback, float from, float to, Interpolator interpolator) 
{ 
    monoBehaviour.StartCoroutine(ExecuteInterpolation(interpolator, duration, callback, from, to)); 
} 

Also habe ich nur einen Coroutine in der Verlängerung gestartet:

private static IEnumerator ExecuteInterpolation(Interpolator interpolator, float duration, 
    Action<float, bool> callback, float from, float to) 
{ 
    float sT = Time.time; 
    float eT = sT + duration; 
    bool hasFinished = false; 
    while (Time.time < eT) 
    { 
     float t = (Time.time - sT)/duration; 
// ----> my logic here with callback(to, false) 
     yield return null; 
    } 

    hasFinished = true; 
    callback(to, hasFinished); 
} 

Bitte beachte, dass ich einen boolean haben sagen, dass die Interpolation beendet ist, geschieht dies, weil es nicht die beste Methode ist, sich auf float-Vergleich zu verlassen, um das Ende eines Flusses zu überprüfen, wenn es für das maximale Ergebnis das Ergebnis vor dem letzten ist, wir den Rückruf für die letzte aufgerufene Interaktion haben zweimal.

+0

hey Fernando! Natürlich kann man jede Erweiterung nach Belieben neu aufbauen, es ist nur syntaktische Süßigkeit - es hat keine Realität. Wirklich "Tweeng" ist sehr weit verbreitet wie es ist. (Beispiel: https://stackoverflow.com/a/37228628/294884) Da es sich um eine Animation handelt, handelt es sich um eine Zeit, die (für mich) die logische "Basis" der Syntax ist. Wie Sie erwähnen, kann es nur in einem MonoBehavior verwendet werden - das gilt natürlich für viele/die meisten Dinge in Unity. Wirklich, es ist nur eine Konvention, um die Sekunden zu verlieren. Sie können die Argumente sicherlich in einer anderen Reihenfolge haben, wenn sie bevorzugt werden. Genießen! – Fattie

+0

Hallo Fattie, "Tweeng" selbst ist eine wirklich nützliche Idee, mein Code ist nichts anderes als ein Tweeng für Monobehaviour statt Float, und ich könnte das Tweeng mit MonoB umgestalten. Dennoch ist nicht nur die Syntax nicht gut zu stellen .75.Tweeng (params []), aber ist nicht richtig, wenn die Aktion nur in MonoB ausgeführt werden kann. Die bessere eine Funktion beschreibt sich selbst am besten der Code ist, eine Erweiterung ohne Definition, schlechte Syntax und außerhalb des Kontexts ist nicht schön, ist ein schneller Trick, aber keine gute Lösung. (Hochachtungsvoll, ich will nicht streiten Sie mit Ihnen, aber zeigen Sie nur die Schwäche auf diese Lösung, die ich entdeckte) –

+0

für sicher @ fernanobonet, ich verstehe völlig, was du meinst !! Ich stimme respektvoll zu, die Operation passiert "on" Sekunden, also scheint es mir natürlich - aber ich verstehe, was du sagst !!! Ich würde einfach sagen, dass es im Einheitscode ziemlich üblich ist, Erweiterungen auf Float zu sehen, also auch wenn Sie nicht zustimmen - Sie können es in anderen Code-Basen sehen! Heh! Prost!!! – Fattie

Verwandte Themen