Jump to content
Unity Insider Forum

Bild schrittweise drehen


minuschki

Recommended Posts

Hallo zusammen

Ich möchte ein Bild um 90° drehen lassen, sobald man es anklickt. Dabei soll die Drehung animiert sichtbar sein und nicht direkt auf 90° springen. Wenn ich die schrittweise Rotation in eine for-Schleife einbaue, wird diese wohl 90 mal durchlaufen aber man sieht die Zwischenschritte der Rotation nicht. Gibt es einen Befehl, um innerhalb der for-Schleife ein frame update zu erzwingen, damit die Animation auch tatsächlich angezeigt wird oder löst man das ganze Drehproblem anders?

Danke für eure Hilfe!

for (int i=0; i=<90; i++)
   transform.rotation = i;
Link zu diesem Kommentar
Auf anderen Seiten teilen

Für so etwas sind Coroutinen super geeignet. Es geht auch in Update (und manchmal, je nach Situation, ist das sogar die bessere Variante), aber Coroutinen sollte man sich so oder so mal anschauen.

Kurz: Coroutinen sind Methoden, die an beliebigen Stellen in ihrem Code einen oder mehrere Frames warten können. Sie werden dann unterbrochen und beim nächsten Update weiter von der Zeile ausgeführt, wo sie unterbrochen wurden.

Das ganze funktioniert so:

public void StartRotation()
{
  // StartCoroutine ist wichtig, sonst hört die Coroutine beim ersten Warten auf und wird NICHT mehr fortgesetzt
  StartCoroutine(Rotation());
}

private IEnumerator Rotation() // IEnumerator als Rückgabetyp
{
  const float duration = 1f;
  // Ausgangsrotation
  var from = transform.localRotation;
  // Zielrotation (90° weiter)
  var to = from * Quaternion.Euler(0, 0, 90);
  
  // So eine Schleife ist praktsich, um etwas über x Sekunden laufen zu lassen, aber kein nötiger Teil einer Coroutine
  for (var time = Time.deltaTime; time < duration; time += Time.deltaTime)
  {
    var progress = time / duration; // Geht im Verlauf von 0 bis 1
    
    transform.localRotation = Quaternion.Slerp(from, to, progress);
    
    yield return null; // Einen Frame warten
  }
  
  transform.localRotation = to;
}

Wichtig sind hier:

  1. StartCoroutine um den Aufruf der Methode herum.
  2. IEnumerator als Rückgabetyp. Dafür musst du oben "using System.Collections;" drinhaben.
  3. "yield return null;" Das ist die Zeile, die einen Frame lang wartet - der Rest deines Spiels läuft weiter und beim nächsten Update wird die Coroutine hier fortgesetzt, was in diesem Fall heißt: Der nächste Durchlauf der For-Schleife beginnt.

Du kannst deiner Coroutine-Methode auch Parameter geben:

public void StartRotation()
{
  StartCoroutine(Rotation(90f, 1f));
}

private IEnumerator Rotation(float angle, float duration)
{
  var from = transform.localRotation;
  var to = from * Quaternion.Euler(0, 0, angle);
  // ...

Und statt "null" kannst du auch andere Dinge in "yield return" reinstecken. Bekanntestes Beispiel ist:

yield return new WaitForSeconds(2f); // Warte 2 Sekunden

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Es gibt doch noch ein Problem!

Wenn die Animation in 2 Sekunden ablaufen soll, der Anwender aber innerhalb der laufenden Animationen nochmals auf das Bild klick, wird die Coroutine neu gestartet, was zu unerwünschten Winkeln führt. Anstatt 90°, 180° oder 270° bleibt das Bild z.B. auf 127° stehen!

 

public void OnMouseDown()
	{
  
  // Hier müsste noch eine Bedingung stehen, die einen Neustart der Coroutine erst erlaubt, wenn eine 90° Drehung abgeschlossen ist!
		if ()
		{
		StartCoroutine(drehen(90f, 2f));//Drehwinkel und Dauer in Sekunden
		}

	}

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Jau, das ergibt Sinn, weil die Coroutine am Anfang den aktuellen Winkel nimmt und da 90° draufrechnet. Die erste Frage ist jetzt: Willst du, dass ein Klick, der während einer Laufenden Drehung kommt, ignoriert wird, oder willst du dann direkt noch einmal 90° dazu haben?

Link zu diesem Kommentar
Auf anderen Seiten teilen

Variante 2 wäre in diesem Fall erwünscht. Will heissen: Weitere Klicks innerhalb einer laufenden Animation sollen registriert werden und zu weiteren 90°-Drehungen führen. 3 Klicks würden also eine Drehung um 270° erzeugen.

Natürlich wäre es für mich auch interessant zu wissen, wie man einen Klick ignorieren könnte, falls das nicht zu viel Mühe bedeutet.

Danke für die Geduld!

Link zu diesem Kommentar
Auf anderen Seiten teilen

Hallo Sascha

Ich habe das nun mal versucht so zu lösen, wie es im Code steht. Das heisst: Ein Klick startet die Coroutine nur, wenn gerade keine Drehung ausgeführt wird. Bei Beendigung der Drehung wird aktion wieder auf false gesetzt. Das hat zur Folge, dass keine "ungeraden" Winkel entstehen, sondern immer 0°, 90°, 180° oder 270°.

Allerdings hat das den Nachteil, dass Klicks innerhalb der Drehbewegung ignoriert werden. Ziel wäre eigentlich, dass wenn der Anwender zum Beispiel einen Doppelklick innerhalb der 2 Sekunden ausführt, sich das Bild um 180° drehen würde, aber ohne direkten Spung auf das Ziel. So das eine fliessende Drehbewegung ablaufen würde. Meine Vermutung ist, dass man eine Art Klickzähler einbauen müsste, der dann "abgearbeitet" wird.

 

public void OnMouseDown()// Mit linker Maustaste drehen
	{
		if (aktion == false)
		{
			StartCoroutine (drehen (-90f, 2.0f));//Drehwinkel und Dauer in Sekunden
			aktion = true;
		}
	}

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Alles klar. Für das, was es aktuell macht, ist dein Ansatz genau richtig. Wenn du immer weiter drehen willst, würde ich eventuell von Coroutinen wieder weggehen, da die Vorteile etwas weniger werden; du hast dann keinen festen Ablauf mit bestimmtem Anfang und Ende mehr. Das kriegen Coroutinen zwar auch hin, aber du hast weniger davon. So oder so denke ich, dass es eine gute Idee war, dass du sie dir angeschaut hast ;)

Die größte Herausforderung an der Sache ist, zu verhindern, dass dein Pfeil nicht den kürzesten Weg nimmt und dafür einfach gegen den Uhrzeigersinn dreht, und auch mehrere Runden läuft wenn oft genug gedrückt wurde. Basisidee, jetzt wieder mit Update, wäre so etwas:

(weg damit, besserer Code weiter unten)

Ist leider etwas komplizierter Code für so etwas scheinbar einfaches, aber wie gesagt... soll sich ja nicht rückwärts drehen oder eine ganze Runde zu früh Schluss machen oder so.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Bin nach wie vor nicht dazu gekommen, das auch mal zu testen, hatte jetzt aber ein paar Minuten in Unity dafür. Hab's übern Haufen geworfen und was ganz neues gebaut. Das hier ist wesentlich kürzer... und funktioniert sogar ;)

    private float remainingAngle = 0f;
    public float speed = 10f;

    private void OnMouseDown()
    {
        remainingAngle += 90f;
    }

    private void Update()
    {
        if (remainingAngle <= 0f) return;

        var amount = speed * Time.deltaTime;
        remainingAngle -= amount;

        transform.Rotate(0, 0, amount);

        if (remainingAngle <= 0f)
        {
            remainingAngle = 0f;
            var euler = transform.localEulerAngles;
            euler.z = 90f * Mathf.Round(euler.z / 90f);
            transform.localEulerAngles = euler;
        }
    }

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Yep! Jetzt funktioniert es super!

Ich habe nun versucht aus der Linksdrehung eine Rechtsdrehung zu machen. Könntest du mal einen Blick drauf werfen, ob die vorgenommenen 4 Änderungen (kommentiert) korrekt sind?

Ansonsten besten Dank für deine Bemühungen und den Tipp mit der Coroutine!

 

 

private float remainingAngle = 0f;
	private float speed = 200f;
	
	private void OnMouseDown()
	{
		remainingAngle += -90f;			// Hier einen negativen Winkel eingeben
	}

	private void Update()
	{
		if (remainingAngle >= 0f) return;	// Hier grössergleich anstatt kleinergleich

		var amount = -speed * Time.deltaTime;	// Hier speed auf minus setzen
		remainingAngle -= amount;

		transform.Rotate(0, 0, amount);

		if (remainingAngle >= 0f)		// Hier grössergleich anstatt kleinergleich
		{
			remainingAngle = 0f;
			var euler = transform.localEulerAngles;
			euler.z = 90f * Mathf.Round(euler.z / 90f);
			transform.localEulerAngles = euler;
		}
	}

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Ich würde ehrlich gesagt alle Änderungen streichen, da der Code aktuell einfach sagt "es sind noch x° zu drehen". "-x° übrig" ergibt dabei keinen Sinn. Und obwohl es funktionieren dürfte, sind Dinge, die nicht für einen normal Denkenden unlogisch sind, immer potentielle Fehlerquellen.

Als einzige Änderung würde ich daher machen:

transform.Rotate(0, 0, -amount);

und der Rest bleibt, wie es ist: runterzählend.

vor 5 Stunden schrieb minuschki:

Ansonsten besten Dank für deine Bemühungen und den Tipp mit der Coroutine!

Gerne :) Früher oder später wird das sowieso nützlich :)

Link zu diesem Kommentar
Auf anderen Seiten teilen

Archiviert

Dieses Thema ist jetzt archiviert und für weitere Antworten gesperrt.

×
×
  • Neu erstellen...