Jump to content
Unity Insider Forum

StopCoroutine(FunktioniertNicht())


Singular

Recommended Posts

Hallo zusammen,

ganz kurze Frage: Warum ist es nicht möglich folgendes zu tun: (Vorausgesetzt ich habe Coroutine jetzt auch immer richtig geschrieben ;) )

StartCoroutine(NameCoroutine());
StopCoroutine(NameCoroutine()); //Übergabe als IEnumerator

Genauso wie das hier:

Coroutine coroutine = StartCoroutine(NameCoroutine());
StopCoroutine(coroutine); //übergabe als Coroutine Objekt

Oder so:

StartCoroutine(NameCoroutine());
StopCoroutine("NameCoroutine"); // übergabe als String

Alles drei laut Dokumentation von Unity möglichkeiten eine einzelne Coroutine zu stoppen. (https://docs.unity3d.com/ScriptReference/MonoBehaviour.StopCoroutine.html)

Wie soll ich vorgehen, wenn ich möchte, dass eine Coroutine, sollte sie ein weiteres mal aufgerufen werden, die laufende beendet wird und die Zeit wieder von vorne beginnt? Also in etwa das:

public void ShieldRegeneration()
{
  StopCoroutine(ShieldRegeneration());
  StartCoroutine(ShieldRegeneration());
}

Problem ist derzeit, dass das StopCoroutine nicht ausgeführt wird aber ich nicht auf die StopAllCoroutines zurück greifen möchte, weil ich noch nicht weiß ob noch andere dazu kommen werden. 

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Achso der mein Code oben ist nicht ganz richtig da in der Dokumentation das hier steht:

Zitat

Note: Do not mix the three arguments. If a string is used as the argument in StartCoroutine, use the string in StopCoroutine. Similarly, use the IEnumerator in both StartCoroutine and StopCoroutine. Finally, use StopCoroutine with the Coroutine used for creation.

Aber das ändert dennoch nichts an der Tatsache.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Wenn du ShieldRegeneration() schreibst, dann rufst du die Methode auf. Wenn du es dann noch einmal schreibst, dann rufst du es noch einmal auf. Was auch immer beim zweiten Aufruf heraus kommt - es kann sicherlich nicht genutzt werden, um die Coroutine zu stoppen, die du in der vorherigen Zeile gestartet hast.

Um's kurz zu machen: Du musst das von StartCoroutine() zurückgegebene Coroutine-Objekt abspeichern und dann das als Parameter für StopCoroutine übergeben.

Coroutine myCoroutine = StartCoroutine(MyCoroutineMethod());

StopCoroutine(myCoroutine);

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

vor 2 Stunden schrieb Singular:

Genauso wie das hier:

Coroutine coroutine = StartCoroutine(NameCoroutine());
StopCoroutine(coroutine); //übergabe als Coroutine Objekt

 

vor 1 Stunde schrieb Sascha:

Um's kurz zu machen: Du musst das von StartCoroutine() zurückgegebene Coroutine-Objekt abspeichern und dann das als Parameter für StopCoroutine übergeben.

Coroutine myCoroutine = StartCoroutine(MyCoroutineMethod());

StopCoroutine(myCoroutine);

...hab ich versucht. Funktioniert nicht.

Das einzige was tatsächlich klappt ist das StopAllCoroutines() bei allem anderen passiert nichts. In diesem Fall kann ich nicht mal ein Debug einbauen um zu schauen was passiert, weil es da nichts zwischen gibt. Es ist eigentich genauso Stumpf, wie ich es oben geschrieben habe, dass die beiden zeilen direkt hintereinadner kommen. Erst Stop, dann Start und das Stop hat eine if abfrage ob es null ist, damit ich hier keinen Fehler bekomme. Alles andere ist wie oben geschrieben...

Edit: UND (nicht, dass ich für bekloppt gehalten werde^^) die Coroutine wird natürlich nicht innerhalb der Methode abgespeichert, sonder selbstverständlich innerhalb der Klasse!

Link zu diesem Kommentar
Auf anderen Seiten teilen

Eeeh, sorry, voll überlesen.

vor einer Stunde schrieb Singular:

...hab ich versucht. Funktioniert nicht.

Ist aber richtig. Wenn das nicht funktioniert, hast du es irgendwie falsch verbaut. Allerdings klingt

vor einer Stunde schrieb Singular:

Erst Stop, dann Start und das Stop hat eine if abfrage ob es null ist, damit ich hier keinen Fehler bekomme. Alles andere ist wie oben geschrieben...

sehr gut.

Wenn's geht, poste mal den ganzen Code. Oder zumindest den ganzen relevanten.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Also ich habe eben mal schnell einen Test geschrieben, um mit 3 public Variablen (zum anklicken) die Coroutine zu starten, stoppen und zu restarten.
Funktioniert! (Jedenfalls mit nem UnityEngine.Coroutine - Objekt)
Die Variable ShieldRoutine vom Typ Coroutine wird in der Klasse deklariert und bekommt seine Coroutine immer in der Update beim startIt zugewiesen.
Jetzt hat die Variable genau diese Coroutine gespeichert und kann mit StopCoroutine(variable); gestoppt werden.
Beim restartIt stoppe ich sie und starte sie auch gleich wieder neu. Geht auch.

Ich habe das auch mit einer Variable vom Typ IEnumerator getestet. Eigentlich geht das auch, aber:

Wenn man z.B. in der Start() der Variable die entsprechende Coroutine übergibt, dann geht es exakt nur ein einziges Mal!

Nur startIt gedrückt und gewartet: Beide Coroutinen werden gestartet und beide laufen auch ab.
Jetzt wieder startIt gedrückt: Nur noch die Shieldroutine startet und läuft ab.

Neustart des Spiels und beide CR wieder mit startIt gestartet und gleich danach mit stopIt gestoppt: Beide CR starten und halten auch an.
Wenn ich nun aber ein weiteres Mal startIt drücke, bekomme ich sofort die Meldung, dass Regain abgelaufen ist, dass Regain gestartet wurde bekommme ich nicht.
Shield läuft ganz normal.
Das bedeutet also, dass die Regain CR irgendwie nur pausiert wurde und nach nem neuen Start einfach fertig abläuft. Seltsam!
Ein weiterer Start von Regain geht dann nicht mehr.

Man muss also vor jedem Start der CR auch ersteinmal die CR als neues Objekt der Variable übergeben.

So wie ich es hier unten gemacht habe, funktionieren beide Möglichkeiten.

public class coroutineTest : MonoBehaviour
{
    bool gestoppt; 
    public bool startIt;
    public bool stopIt;
    public bool restartIt;
    Coroutine ShieldRoutine;
    IEnumerator HPRoutine;

    void Update()
    {
        if (startIt)
        {
            //coroutine
            ShieldRoutine = StartCoroutine(LoadShield());
            // IEnumerator
            HPRoutine = RegainHP();
            StartCoroutine(HPRoutine);
            startIt = false;
            gestoppt = false;
        }
        if (stopIt)
        {
            if(ShieldRoutine != null)
            	StopCoroutine(ShieldRoutine);
            if(HPRoutine != null)
            	StopCoroutine(HPRoutine);
            stopIt = false;
            gestoppt = true;
            Debug.Log("LoadShield und RegainHP gestoppt");

        }
        if (restartIt)
        {
            StopCoroutine(ShieldRoutine);
            StopCoroutine(HPRoutine);
            restartIt = false;
            gestoppt = false;
            ShieldRoutine = StartCoroutine(LoadShield());
            HPRoutine = RegainHP();
            StartCoroutine(HPRoutine);
            Debug.Log("LoadShield und RegainHP neu gestartet");
        }

    }
    IEnumerator LoadShield()
    {
        Debug.Log("Loadshield gestartet");
        yield return new WaitForSeconds(2);
        Debug.Log("LoadShield abgelaufen und gestoppt war: " + gestoppt);
    }
    IEnumerator RegainHP()
    {
        Debug.Log("HP gestartet");
        yield return new WaitForSeconds(2.5f);
        Debug.Log("Regain abgelaufen und gestoppt war: " + gestoppt);
    }
}

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Hallo zusammen,

vor 19 Stunden schrieb Sascha:

Wenn's geht, poste mal den ganzen Code. Oder zumindest den ganzen relevanten.

Das ist jetzt die ganze Schild Klasse. wie gesagt, die Coroutine wird gestartet und das Stop wird immer übersprungen:

Damage ist ein Struct der neben ein paar Methoden die drei int werte normalDamage, projectileDamage und energyDamage hat

Und falls ihr fragt Shield : ShipComponent und ShipComponent : MonoBehaviour -> IEnumerator wird also unterstützt ;) (Das möchte ich aber noch ändern. Gefällt mir nämlich nicht^^) aber Details...

Das Debug.Log("Stop") wird mir übrigens auch angezeigt, aber die Coroutine nicht beendet.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Shield : ShipComponent
{
    private Equipment health;

    public int shieldCurrent;//gibt an, wieviele Schildpunkte der Schild derzeit hat.

    public int shieldMax; //gibt an, wieviele Schildpunkte der Schild maximal haben kann.
    public float energyUsage; //gibt an, wieviel Energie gebraucht wird um einen Schildpunkt zu regenerieren

    public int regenerationValue; //gibt an, wieviele Schildpunkte regeneriert werden pro Coroutine
    public float regenerationRate; //gibt an, wie lange es dauert einen Schildpunkt zu regenerieren

    private Coroutine regenerationRoutine;

    void Start()
    {
        health = GetComponent<Equipment>();
        health.Equip(this);

        shieldCurrent = shieldMax;
    }

    public Damage TakeDamage(Damage damage)
    {
        // Wird aufgerufen, wenn das Objekt Schaden erleidet.

        Damage damageTest = damage;

        if (damage.EnergyDamage() > 0)
        {
            //EnergieSchaden Abfangen:
            if (shieldCurrent > 0)
            {
                if (damage.energyDamage >= shieldCurrent)
                {
                    damage.energyDamage -= shieldCurrent;
                    shieldCurrent = 0;
                }
                else
                {
                    shieldCurrent -= damage.energyDamage;
                    damage.energyDamage = 0;
                }

                //NormalenSchaden abfangen.

                if (damage.normalDamage >= shieldCurrent)
                {
                    damage.normalDamage -= shieldCurrent;
                    shieldCurrent = 0;
                }
                else
                {
                    shieldCurrent -= damage.energyDamage;
                    damage.normalDamage = 0;
                }
            }

            if(regenerationRoutine != null)
            {
                Debug.Log("Stop");
                StopCoroutine(regenerationRoutine);
            }
            regenerationRoutine = StartCoroutine(RegenerationShield());
        }

        return damage;
    }

    IEnumerator RegenerationShield()
    {
        if (shieldCurrent < shieldMax)
        {
            yield return new WaitForSeconds(regenerationRate);
        
            if (health.PayEnergy(energyUsage))
            {
                shieldCurrent += regenerationValue;

                if(shieldCurrent >= shieldMax)
                {
                    int diff = shieldCurrent - shieldMax;
                    shieldCurrent = shieldMax;
                    health.ReturnEnergy(diff * (energyUsage / regenerationValue));
                }
            }

            if(shieldCurrent < shieldMax)
            {
                regenerationRoutine = StartCoroutine(RegenerationShield());
            }
        }
    }
}

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Hmmm...
Wenn du das Debug.Log siehst, sollte auch die Coroutine gestoppt werden. (Wenn sie denn überhaupt läuft und nicht schon durchgelaufen ist).

Sie wird danach aber sofort wieder gestartet.

Die Coroutine wartet aber nur, wenn dein Schild < dem MaxSchild ist. Ist das nicht so, dann passiert garnichts und sie ist sofort fertig.
Ist es aber so und die Zeit ist abgelaufen, startet sie sich direkt selber wieder, wenn denn der Schild immer noch kleiner als der MaxSchild ist.

Beim Schild und MaxSchild könnte also ein Problem sein.
Vielleicht liegt es auch am eigenen Starten in der CR selbst...

Von der Logik her sollte die CR starten und auch stoppen. Und nach dem ersten Durchlauf ist deine regenerationRoutine auch nie mehr null.

Ich persönlich würde die Abfrage shield < maxShield nicht in der CR machen sondern da, wo du die CR startest. Dann weisst du auch, dass sie ihre Wartezeit abläuft, wenn sie denn gestartet wurde.

Vielleicht sieht @Saschaetwas Entscheidendes, was mir entgangen sein könnte.

 


 

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

vor 2 Stunden schrieb malzbie:

Die Coroutine wartet aber nur, wenn dein Schild < dem MaxSchild ist.

Genau. Der Sinn ist, dass du nicht mehr Schild aufladen sollst, wenn du schon auf oder über Maximum bist. Dann ruft sie sich aber auch selbst nicht auf. die kommt dann gar nicht ins "if" rein und beendet sich sofort. Das soll sie auch.

 

vor 2 Stunden schrieb malzbie:

Von der Logik her sollte die CR starten und auch stoppen. Und nach dem ersten Durchlauf ist deine regenerationRoutine auch nie mehr null.

Genau. Das würde ich auch erwarten. Also starten tut er, stoppen nicht. Das Stop sehe ich auch, wie erwartet, auch nur ab dem zweiten Mal.

vor 2 Stunden schrieb malzbie:

Ich persönlich würde die Abfrage shield < maxShield nicht in der CR machen sondern da, wo du die CR startest. Dann weisst du auch, dass sie ihre Wartezeit abläuft, wenn sie denn gestartet wurde.

Ja, hab ich mal versucht... ändert leider nichts am Verhalten. Also sie startet wie erwartet und stopp nicht.

 

Wenn euch nichts mehr einfällt, werde ich das Script mal einfach neu schreiben und schauen ob es dann funktioniert.

Link zu diesem Kommentar
Auf anderen Seiten teilen

vor 6 Stunden schrieb Singular:

Und falls ihr fragt Shield : ShipComponent und ShipComponent : MonoBehaviour -> IEnumerator wird also unterstützt ;)

Sonst würde er StartCoroutine und StopCoroutine auch nicht finden ;)

vor 4 Stunden schrieb malzbie:

Vielleicht sieht @Saschaetwas Entscheidendes, was mir entgangen sein könnte.

Das einzige, was mir bisher ebenfalls auffällt, ist, dass am Ende von RegenerationShield regenerationRoutine nicht auf null gesetzt wird. Heißt nur, dass da eine abgelaufene Coroutine in StopCoroutine gefüttert werden kann, was vermutlich leise failed und gut is. So oder so sollte das aber kein Problem mit StopCoroutine bedeuten.

Ich hab das halt mal kopiert und das meiste rausgenommen. Sieht bei mir so aus und funktioniert einwandfrei. Wenn ich regenerationRate auf 5 hab und 4 Sekunden nach dem letzten Tick auf shieldCurrent wieder TakeDamage aufrufe, dann muss ich neue 5 Sekunden warten, bis ich wieder ein Stück Schild zurück kriege.

Link zu diesem Kommentar
Auf anderen Seiten teilen

 

Am 28.9.2022 um 23:20 schrieb Sascha:

Ich hab das halt mal kopiert und das meiste rausgenommen. Sieht bei mir so aus und funktioniert einwandfrei. Wenn ich regenerationRate auf 5 hab und 4 Sekunden nach dem letzten Tick auf shieldCurrent wieder TakeDamage aufrufe, dann muss ich neue 5 Sekunden warten, bis ich wieder ein Stück Schild zurück kriege.

Somit liegt das Problem ganz woanders... wo allerding kann ich euch nicht sagen, denn: Ich habe, wie schon angedroht das Script neu geschrieben und getestet... läuft...
ohne Probleme gleich beim ersten versuch. Vielleicht habe ich schon vorher irgendo was mit dem Script gemacht, was ihm nicht geschmeckt hat und dann wollte er einfach nicht mehr. Keine Ahnung.

Am 28.9.2022 um 23:20 schrieb Sascha:

Das einzige, was mir bisher ebenfalls auffällt, ist, dass am Ende von RegenerationShield regenerationRoutine nicht auf null gesetzt wird. Heißt nur, dass da eine abgelaufene Coroutine in StopCoroutine gefüttert werden kann, was vermutlich leise failed und gut is. So oder so sollte das aber kein Problem mit StopCoroutine bedeuten.

Das habe ich jetzt noch mit hinzugefügt aber das ändert nichts an der funktionalität^^

Danke euch.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Archiviert

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

×
×
  • Neu erstellen...