Jump to content
Unity Insider Forum

Rotation um einen Pivotpunkt


Garzec

Recommended Posts

Ich möchte eine Tür um beispielsweise 90 Grad öffnen, danach soll diese beispielsweise 3 Sekunden warten und sich wieder schließen. Für die Rotation der Tür würde ich einen Pivotpunkt setzen. Für den Beginn der Rotation rufe ich im Spiel die Methode "Interact()" auf.

public class RotatingDoor : Door, IInteractable
{
    [SerializeField]
    private Vector3 targetRotation; // Rotation, wenn offen

    [SerializeField]
    private float duration; // Geschwindigkeit

    [SerializeField]
    private bool closeAgain; // Tür wieder schließen oder offen lassen?

    [SerializeField]
    private float waitInterval; // Wartezeit bis zum Schließen

    private Vector3 defaultRotation; // Ursprungsrotation bei Start
    private bool isActive = false; // Tür grade "beschäftigt" ?

    private void Start()
    {
        defaultRotation = transform.eulerAngles;
    }

    private IEnumerator DoorRotation()
    {
        if (isActive) // Abbrechen, wenn sich die Tür bereits öffnet / schließt
            yield break;

        isActive = true;

        yield return StartCoroutine(RotateDoor(targetRotation)); // Tür öffnen

        if (!closeAgain) // Script zerstören, falls die Tür nicht geschlossen werden muss
            Destroy(this);

        yield return new WaitForSeconds(waitInterval); // X Sekunden warten

        yield return StartCoroutine(RotateDoor(defaultRotation)); // Tür zur Ursprungsrotation schließen

        isActive = false;
    }

    private IEnumerator RotateDoor(Vector3 newRotation) // Tür rotieren
    {
        float counter = 0;
        newRotation = transform.eulerAngles + newRotation;
        while (counter < duration)
        {
            counter += Time.deltaTime;
            transform.eulerAngles = Vector3.Lerp(transform.eulerAngles, newRotation, counter / duration);
            yield return null;
        }
    }

    public void Interact()
    {
        StartCoroutine(DoorRotation()); // Tür öffnen und anschließend wieder schließen
    }
}

Dabei habe ich zwei Probleme:

1. Setze ich im Inspector eine niedrige Dauer, so rotiert die Tür sehr schnell. Setze ich also einen größeren Wert ein, rotiert sie zwar langsamer aber der Counter innerhalb der Methode benötigt dann eben auch X Sekunden, bis er bei dieser Dauer angekommen ist. Obwohl er bei einer Rotationsdauer von 3 Sekunden nach 3 Sekunden größer als Dauer sein sollte.

2. Die Tür rotiert zwar auf, aber nicht mehr zu. Es wird zwar die Coroutine aufgerufen, wieder zurück zu rotieren, aber die Tür bleibt starr. Rotiere ich die Tür danach noch einmal, rotiert sie natürlich erstmal weiter von 90 bis 180 Grad, da sie ja nicht zurück rotiert ist.

Hilfe wäre super :)

Link zu diesem Kommentar
Auf anderen Seiten teilen

Ich muss zugeben, dass ich Frage 1 nicht verstehe. Wenn du 3 Sekunden Dauer eingibst kriegst du 3 Sekunden Dauer. Ist doch richtig?

2 ist allerdings relativ simpel. Deine RotateDoor-Funktion braucht als Parameter einen Offset, also eine Rotation, um die sich gedreht wird - nicht eine Zielrotation. Dein Schließen-Aufruf ist aber

yield return StartCoroutine(RotateDoor(defaultRotation));

Das heißt, dass bei defaultRotation = Quaternion.identity die Tür gar nicht gedreht wird. Ändere also am besten deine RotateDoor-Funktion, dass sie eine Zielrotation braucht statt eines offsets.

Diese Zeile kommt dann einfach raus:

newRotation = transform.eulerAngles + newRotation;

und dafür sieht der Öffnen-Aufruf dann so aus:

yield return StartCoroutine(RotateDoor(transform.eulerAngles + targetRotation)); // Tür öffnen

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

ok super, Frage 2 gelöst :)

Zu Frage 1:

Ich nehme mal als Duration den Wert 5. Die Rotation der Tür ist bereits in unter 2 Sekunden fertig. Die `RotateDoor` Funktion selbst benötigt aber die vollen 5 Sekunden, da der Counter dann erst bei 5 Sekunden ankommt und die while-Schleife verlässt.

Ich möchte im Prinzip mit der Duration angeben, wie lange die Rotation dauern soll.

Würde ich nämlich beispielsweise bei der Duration 20 setzen, wäre die Rotation nach ca. 3 Sekunden fertig aber der Counter muss ca. 20 Sekunden lang hochzählen, bis es weiter geht.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Kann eigentlich nicht sein.

vor einer Stunde schrieb Garzec:

counter / duration

Das hier ist ja kleiner als 1 solange counter < duration ist. Das heißt, dass dein Lerp auch noch läuft solange counter < duration ist.

Daher ist dein Code ist richtig, vielleicht hast du nur die falschen Werte drin?

Link zu diesem Kommentar
Auf anderen Seiten teilen

Hmm..

also ich habs jetzt mal mit verschiedenen Werten getestet. Nehme ich den Wert 1, ist die Rotation natürlich schon in 1 Sekunde fertig. Nehme ich den Wert 10, dauert es ca. 2 Sek.

Bei 50 ca. 4 Sek. usw. Also die Rotation wird schon langsamer, aber sie ist eben sehr viel schneller als die eigentliche Dauer.:huh:

Link zu diesem Kommentar
Auf anderen Seiten teilen

@Sascha wenn du magst kann ich dir ein Video oder Gif erstellen. Oder du erstellst ein Projekt, nimmst einen Würfel und kopierst den Code drauf.

 

Edit: Speichere ich mir die eulerAngles vor der Schleife zwischen, also

    private IEnumerator RotateDoor(Vector3 newRotation)
    {
        float counter = 0;
        Vector3 defaultAngles = transform.eulerAngles; // Zwischenspeichern
        while (counter < duration)
        {
            counter += Time.deltaTime;
            transform.eulerAngles = Vector3.Lerp(defaultAngles, newRotation, counter / duration);
            yield return null;
        }
    }

funktioniert das Ganze. ^_^

Link zu diesem Kommentar
Auf anderen Seiten teilen

Ich bin doof. Hier ist das Problem. Falsch:

float counter = 0;
while (counter < duration)
{
  counter += Time.deltaTime;
  transform.eulerAngles = Vector3.Lerp(transform.eulerAngles, newRotation, counter / duration);
  yield return null;
}

Richtig:

float counter = 0;
var oldRotation = transform.eulerAngles;
while (counter < duration)
{
  counter += Time.deltaTime;
  transform.eulerAngles = Vector3.Lerp(oldRotation, newRotation, counter / duration);
  yield return null;
}

Du nimmst ja immer die aktuelle Rotation als Ausgangspunkt für dein Lerp. Damit geht dir die exakte Kontrolle über die Zeit flöten und die Interpolation ist auch nicht mehr linear.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Hallo, 

für die Vollständigkeit würde ich mal das finale Script hier lassen, vielleicht kann es ja jemand gebrauchen. Vielleicht findet auch jemand Verbesserungen. Mit dem Script ist es jetzt ebenfalls möglich, die Tür so einzustellen, dass sie immer vom Spieler weg rotiert. 

Beispielsweise eine Saloon Tür.

 [SerializeField]
    private Vector3 targetRotation;

    [SerializeField]
    private float duration;

    [SerializeField]
    private bool closeAgain;

    [SerializeField]
    private float waitInterval;

    [SerializeField]
    private Vector3 pivotPosition;

    [SerializeField]
    private bool opensAwayFromPlayer;

    private Vector3 defaultRotation;

    private bool isActive = false;

    private Transform doorPivot;

    private Transform playerTransform;

    private void Start()
    {
        playerTransform = Globals.GetPlayerObject().transform;
        doorPivot = new GameObject().transform;
        doorPivot.position = pivotPosition;
        transform.SetParent(doorPivot);
        defaultRotation = doorPivot.eulerAngles;
    }

    private IEnumerator DoorRotation()
    {
        if (isActive)
            yield break;

        isActive = true;

        float counter = 0;
        Vector3 defaultAngles = doorPivot.eulerAngles;

        if (PlayerIsBehindDoor())
            targetRotation = -targetRotation;

        Vector3 openRotation = transform.eulerAngles + targetRotation;

        while (counter < duration)
        {
            counter += Time.deltaTime;
            LerpDoor(defaultAngles, openRotation, counter);
            yield return null;
        }

        if (!closeAgain)
            Destroy(this);

        yield return new WaitForSeconds(waitInterval);

        while (counter > 0)
        {
            counter -= Time.deltaTime;
            LerpDoor(defaultAngles, openRotation, counter);
            yield return null;
        }

        isActive = false;
    }

    private void LerpDoor(Vector3 defaultAngles, Vector3 targetRotation, float counter)
    {
        doorPivot.eulerAngles = Vector3.Lerp(defaultAngles, targetRotation, counter / duration);
    }

    private bool PlayerIsBehindDoor()
    {
        Vector3 doorTransformDirection = transform.TransformDirection(Vector3.forward);
        Vector3 playerTransformDirection = playerTransform.position - transform.position;
        return Vector3.Dot(doorTransformDirection, playerTransformDirection) < 0;
    }

    public void Interact()
    {
        StartCoroutine(DoorRotation());
    }

Die Einstellungen im Inspector sind sehr überschaubar. Zusätzlich muss man die Tür nicht unbedingt um ihre y-Achse drehen. Man kann sie drehen und winden, wie man möchte. ^_^

Link zu diesem Kommentar
Auf anderen Seiten teilen

Archiviert

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

×
×
  • Neu erstellen...