Jump to content
Unity Insider Forum

GameObject für immer zerstören


Thanny08

Recommended Posts

Moin liebe Com,

 

Und zwar habe ich ein Level in welchem Münzen aufgesammelt werden können. Die Münzen werden zerstört und der CoinCounter geht hoch. Bis jetzt alles gut. 

Wenn ich aber das Ende erreiche sollen die Münzen für immer zerstört sein, also nicht bei Neustart wieder Sammelbar sein.

private void OnTriggerEnter2D(Collider2D other)             
    { 
        if (other.CompareTag("Coin"))                           
        {
            münzenAnzahl++;                                     
            Destroy(other.gameObject);
            Münzenanzahl.text = münzenAnzahl.ToString();
        }

        if (other.CompareTag("Finish"))
        {
            PlayerPrefs.SetInt("NumberOfCoins", + münzenAnzahl);
           //Und hier sollte noch übergeben werden das diese GameObjects für immer zerstört sind.
        }
    }         

Für Hilfe wäre ich dankbar!

LG

Link zu diesem Kommentar
Auf anderen Seiten teilen

Moin!

Wenn du eine Szene lädst, dann werden alle GameObjects darin in dem Zustand geladen, den du im Editor eingestellt hast. Wenn da dieses oder jenes Objekt nicht dabei sein soll, dann hast du nur zwei Möglichkeiten:

  1. Das Objekt war gar nicht in der Szene, sondern wird nach dem Laden durch Code erstellt. Und dieser Code macht das dann eben nicht, wenn der Spielstand nein sagt. Das ist etwas doof, weil du da standardmäßig erstmal keine Münzen im Editor erstellst - das macht ja der Code. Aber im Editor zu sehen, wo man seine Dinge hinpackt, ist schon eine der schönen Sachen am Arbeiten mit Unity. Da kann man zwar drumherum arbeiten, aber das ist ein Stückchen mehr Arbeit als Variante 2.
  2. Das Objekt schaut in Start(), ob es laut Spielstand bereits eingesammelt ist, und zerstört sich ggf. selbst direkt wieder.

In beiden Fällen brauchst du einen Weg, deine Münzen eindeutig zu identifizieren. Die ID einer Münze kann dann in den Spielstand geschrieben werden, um anzugeben, dass sie eingesammelt ist. Leider gibt Unity dir nicht für jedes Objekt eine eindeutige, persistente (!) ID, die bei jedem Spielstart gleich wäre. Die brauchst du aber, damit die Münze schauen kann, ob sie irgendwo im Spielstand drin steht.

Man kann da ein bisschen kreativ werden: Wenn dein Spiel auf einem Grid ist, kannst du z.B. die Position der Münze auf dem Grid nehmen.

Wenn du für so etwas keine Möglichkeit siehst, wirst du nicht drumherum kommen, deinem Münz-Prefab eine Komponente zu geben, die eine ID speichert. Diese Komponente kann dann auch gleich den Check in Start implementieren. Du musst dann sichergehen, dass jede Münze auch wirklich eine eindeutige ID hat, also keine ID doppelt belegt ist. Zumindest innerhalb einer Szene.

Dann benutzt du diese eindeutige ID und speicherst einen Wert dazu über PlayerPrefs. Zum Beispiel so:

PlayerPrefs.SetInt($"Coin-{sceneName}-{coinId}", 1);

sceneName wäre der Name der aktuellen Szene - so musst du nicht über mehrere Szenen hinweg deine IDs eindeutig halten. coinId ist dann die zugewiesene ID.

Die Coin-Komponente macht dann etwa sowas:

private void Start()
{
  if (PlayerPrefs.GetInt($"Coin-{sceneName}-{coinId}", 0) == 1)
  {
    Destroy(gameObject);
  }
}

Das Zuweisen der ID kannst du mit der Hand machen, oder du baust dir da ein bisschen Editor-Code. Dieser könnte einmal alle Coins in der Szene anschauen und die kleinstmögliche ID, die noch nirgendwo verwendet wird, an den jeweils nächsten Coin vergeben, dessen ID noch 0 ist. Das kann man auch so bauen, dass das beim Platzieren eines neuen Coins automatisch passiert. Ist dann aber zunehmend kein Anfängerkram mehr.

Als Randnotiz: Man muss bei sowas immer ein bisschen aufpassen. Wenn du z.B. irgendwann eine Szene umbenennst, sind alle Spielstände vor dieser Änderung erstmal kaputt.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Moin

 

Erstmal vielen vielen Dank für die schnelle Antwort!

 

Also im Prinzip habe ich das alles verestanden, ich weiß nur nicht wie ich denn jetzt die eindeutige ID festlege. Gibt es da irgendwelche Components die extra dafür da sind?

Und wie kann ich dann diese ID in meinem Script aufrufen? ist das dann sowas in der Art oder liege ich damit ganz falsch?


coinId = //Die Zahl oder eben Id die auf der Componente liegt

private void Start()
{
  if (PlayerPrefs.GetInt($"Coin-{sceneName}-{coinId}", 0) == 1)
  {
    Destroy(gameObject);
  }
}


 private void OnTriggerEnter2D(Collider2D other)            
    { 
        if (other.CompareTag("Coin"))                          
        {
            münzenAnzahl++;                                     
            Destroy(other.gameObject);
            Münzenanzahl.text = münzenAnzahl.ToString();
            PlayerPrefs.SetInt($"Coin-{sceneName}-{coinId}", 1);
        }

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Die ID muss als Teil der Komponente serialisiert werden. "Serialisiert" heißt hier, dass ein Wert dafür in der Szene gespeichert wird, sodass er dann im laufenden Spiel beim Laden der Szene "deserialisiert", also geladen werden kann.

Kennst du evtl. mit public, aber sauberer ist's so:

[SerializeField]
private int coinId;

Dann kannst du direkt per Hand eindeutige IDs an deine Coins im Editor vergeben. Automatischer Kram würde darauf aufbauen, das serialisierte Feld bleibt dabei dann.

Noch als Hinweis: Dieser Code müsste auf die Coins - wenn da also

if (other.CompareTag("Coin"))  

steht, dann würde nur Kollision zwischen Coins und... anderen Coins das auslösen. Ich glaube nicht, dass du das willst.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Perfekt! Vielen Dank es hat alles geklappt!

Nur zwei Fragen hätte ich noch: 

Wenn ich eine Münze aufsammel wird +1 gerechnet. Das im Moment leider nur einmal. Wie kann ich diesen kleinen Fehler noch beheben?

 if (other.CompareTag("Player"))                            
        {
            PlayerPrefs.SetInt("münzenAnzahl", +1);   //Habe hier gerade noch den Fehler gefunden das es nur 1x + 1 macht... wie kann ich das beheben                            
        
        }

 

Und meine andere Frage ist: ich würde gerne auch Leuten helfen die vieleicht noch Anfängerlicher sind als ich, aber alles was ich im Forum finde, was ich beantworten könnte, wurde bereits beantwortet. Liegt es vlt daran das es weniger Unity Anfänger irgendwie gibt? Wobei ich mir das nicht vorstellen kann. Na ja wenn du vlt einen Tipp hast...

 

Liebe Grüße und nochmals Danke!!

Link zu diesem Kommentar
Auf anderen Seiten teilen

Uiii!
Da muss ich mal schnell reingrätschen. ;)

Die Playerprefs sind nicht dafür da, dass du sie bei jedem möglichen Event beschreibst oder ausliest. Egal was man macht, aber man will nicht dauernd auf ein Speichermedium schreiben oder von ihm auslesen. Das ist das unperformanteste, was man machen kann!

Wenn man Playerprefs nutzt, muss man außerdem bedenken, dass sie nur Werte "ablegen" oder "auslesen". Denen kann man nichts dazu addieren, so wie du das willst.
Wenn du da +1 schreibst, dann bekommt der Schlüssel "münzenAnzahl" eine 1. Wenn du da -1 schreiben würdest wäre es die -1. Verstehst du?

Immer wenn du PlayerPrefs mit SetInt ausführst, wird direkt ein Schlüssel in die Registry geschrieben und der bekommt dann einen int Wert. Es ist dem Playerprefs komplett egal, ob da vorher ein Schlüssel mit gleichem Name und evtl. schon Werten drin existiert hatte. Es wird einfach geschrieben. Alles ohne Nachfrage oder Warnung!

Du brauchst also eine Variable, die fürs Münzenzählen da ist.
Diese Variable kannst du so:  coins=coins+1;  oder in der Kurzform  coins+=1;  oder noch kürzer coins++; um 1 erhöhen.
Wenn es dann später zum Abspeichern mit Playerprefs kommt, schreibst du PlayerPrefs.SetInt("münzenAnzahl", coins);

Die Variable coins hat ja die Anzahl der Münzen gespeichert und diese Anzahl übergibst du damit.

Jetzt ist wahrscheinlich deine Frage, wer denn die Coins verwaltet. Ich gehe ganz stark davon aus, dass du die Playerprefs als globale Möglichkeit dafür gesehen hast.
Du musst jetzt für dich überdenken, wie es am Besten funktioniert. Momentan sind es die Münzen ja selber. Die müssen also irgendwas/irgendwem Bescheid geben, dass ein Coin dazu gekommen ist.

Wenn jetzt z.B. dein Player die Coins zählen würde, könnte er es im selben Moment für sich tun, denn ein Triggerevent geht immer in beide Richtungen, oder aber die Münze sendet kurz was an das entsprechende Script des Players. Das es der Player ist, hast du ja schon abgefragt. Wenn der Player jetzt ein Script drauf hätte, welches z.B. MyCoins heisst, könnte sich der Coin schnell das Script verknüpfen und ihm dann in eine public Methode was senden oder gleich eine public Variable erhöhen.

if (other.comparTag("Player")){
  MyCoins TheCoinscript = other.GetComponent<MyCoins>();
  if(myCoins!=null){
    TheCoinscript.AddACoin();  // eine public Methode im Script
    // oder
    TheCoin.coins ++; // coins wäre eine public Variable im Script.
  }
}

Eine weitere Möglichkeit wäre das Beschreiben einer static Variable. Dabei gibt es aber wichtige Dinge, die man beachten muss. Und da weiss ich nicht, ob du dafür schon bereit bist.  ;)

Versuch einfach mal einen Weg für dich zu finden, wer denn jetzt die Münzen zählen soll und wer dazu anregt.
Wann das passieren soll ist ja klar; Wenn der Player eine Münze berührt hat.
Der Player kann das tun wenn er den Tag Coin berührt hat und die Münze kann das tun wenn sie den Tag Player berührt hat. Beides passiert zur selben Zeit.
Wo jetzt aber die Variable zum Hochzählen liegt, musst du selbst entscheiden. Die könnte der Player haben und die könnte irgendein Objekt in der Szene haben, z.B. die Camera, die ja immer da ist, oder die UI, die es ja sowieso anzeigen soll, oder sogar ein extra Werte-Objekt, was selbst beim Szenenwechsel nicht zerstört wird.

Wegen deiner Frage wegen den Neulingen:
Es sind nicht soo viele, aber jede Woche kommen neue hinzu. Schau einfach öfter mal hier rein und wenn du eine unbeantwortete Frage siehst, die du beantworten kannst, dann los! :)


 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Hallo!

 

Ich habe verrsuch die Münzen einmal nur mit einem int einfach mal zu zählen um das dann als Text im Level anzeigen zu lassen.

Das hat nicht funktioniert. Dann habe ich versucht einfach jedes Mal wenn der Player den Coin berührt etwas in der Konsole ausgeben zu lassen. Das hat auch nicht geklappt. 

public class CoinSystem : MonoBehaviour
{
    public Text Münzenanzahl;                                  
                                     
    [SerializeField]
    private int coinId;
    [SerializeField]
    private string sceneName;
    private int coins;
    
    private void Start()
    {
        coins = 0;
        if (PlayerPrefs.GetInt($"Coin-{sceneName}-{coinId}", 0) == 1)
        {
            Destroy(gameObject);
        }
        Münzenanzahl.text = coins.ToString();
    }

   
    private void OnTriggerEnter2D(Collider2D other)             
    { 
        if (other.CompareTag("Player"))                          
        {
            Debug.Log("Hallo");
            coins++;
            PlayerPrefs.SetInt("münzenAnzahl", coins);
            Destroy(GameObject.FindWithTag("Coin"));
            Münzenanzahl.text = coins.ToString();
            PlayerPrefs.SetInt($"Coin-{sceneName}-{coinId}", 1);

        }

    }         
       

   

}

Es geht ja jetzt eigendlich alles über die Variable coins, trozdem wird mir immer nur eine 1 angezeigt. Ich denke mal das liegt an meinem ID System? Weil das vieleicht einfach das gleiche ist, also alle coins...? 

Ich bin gerade echt ziemlich Ratlos...

Link zu diesem Kommentar
Auf anderen Seiten teilen

Ich verstehe nicht ganz was du da machst.

Dieses Script soll die Coins zählen. Kann sie aber nur zählen, wenn es selber auf einem Coin drauf liegt, denn es wird ja schließlich getriggert und abgefragt, ob der Player berührt wurde. Ergo: Muss es auf dem Coin liegen, denn nur der kann den Player triggern.

Sobald du aber den Player berührst, wird einmal Hallo ausgegeben.
Die Variable coins wird um eins erhöht und danach wird ein GameObject mit dem Tag "coin" gesucht und dieses dann gefundene wird zerstört.
Währenddessen wird der Text vom UI Element Münzenanzahl mit der Zahl, die in coins drinne steht, übergeben.

Grundlagen zur Logik:
Wenn du ein Script wie dieses erstellest, dann passiert erstmal nichts, wenn dieses Script auch nicht auf einem Gameobject in der Szene liegt.
Es ist quasi eine Vorlage für etwas.
Wenn du dieses Script aber einem Coin gibst, dann läuft der Code da drin auch ab. Aber! Dieses Script ist jetzt eine Instanz (grob gesagt eine Kopie) des original Scripts, welches immer noch in deinem Projekt Ordner liegt.
Wenn du jetzt 2 Coins in der Szene hast und auch beide Coins dieses Script bekommen haben, dann agieren beide Scripts unabhängig voneinander.
Wenn also ein Coin vom Player berührt wird, dann wird das Script, was auf diesem einen Coin liegt seinen coins Wert um 1 erhöhen. Das Script, welches auf dem anderen Coin liegt, macht gar nichts, denn es wurde ja nicht berührt. Das ist genauso, als wenn du 2 Computer hättest, die komplett gleich installiert sind. Wenn du bei dem einen Computer ins Internet gehst, ist der andere Computer ja auch nicht im Internet. Klar?

So. Coin 1 wurde getroffen, erhöht seine coins von 0 auf 1 und "übergibt" diese 1 an das Textelement und zerstört sich vielleicht. (Zum vielleicht komme ich gleich)
Wird danach ein anderer Coin getroffen, dann erhöht der andere Coin seine Variable von 0 auf 1 und übergibt wiederum eine 1 an das Textelement.
Die beiden Scripts sind unabhängig voneinander und somit fängt jeder Coin von 0 an zu zählen. Du kannst also nur einen 1 sehen.

Das ulkige ander Sache ist, dass dieses Script ja auf einem Gameobject liegt, welches ein Coin ist. Da es nur etwas macht, wenn der Player diesen Coin berührt, brauchst du nicht nach irgeneinem Coin in der Szene suchen und diesen dann zerstören. Nein. Es kann sich gleich selbst zerstören. Dafür gibt es das gameObject mit kleinem g.
Also einfach Destroy(gameObject). Das eigene GameObject wird zerstört und alle Komponenten (auch dieses Script) mit ihm. Denn es kann keine Komponente alleine in einer Szene weiterleben.
Die große Gefahr beim Suchen nach einem Objekt ist die, dass du nie weißt, welches GameObject mit einem gewissen Tag denn als erstes gefunden wurde. Das Erste was gefunden wurde, wird zerstört. Das muss noch nicht einmal das eigene GameObject sein. Keine Ahnung was da innerhalb von Unity abläuft, jedenfalls kann man nie sagen, welches Object zuerst gefunden wird.

Nochmal zum verstehen.
Das Objekt, welches die Coins zählen soll, kann nicht auf einem Coin liegen, weil dieser ja zerstört wird. Es muss also auf einem Objekt liegen, welches die ganze Zeit über am leben bleibt, bzw. nicht zerstört werden darf.
Da es nicht der Coin sein kann , weil der der sich ja zerstört, könnte es der Player sein. Aber der könnte sich ja auch zerstören, wenn er alle Leben verloren hat, also sollte es ein Objekt sein, was immer da ist und mit dem Spielgeschehen nichts zu tun hat. z.B. die Camera.

Diesem Objekt musst du etwas senden und dafür musst du wissen wie es heisst und wo es zu finden ist. Die MainCamera hat von hause aus einen Tag und deswegen kann man den gut nutzen. Camera.main.
Also Münze hat eine Variable vom Typ des gesuchten Scriptes. So wie das Script fürs Münzenzählen eben heisst.
In der Start() oder der Awake() sucht es nach der Camera und dessen Scriptkomponente (wie oben im Beispiel zu sehen) und verknüpft das mit der Variable.
Jetzt kann an dieses Script gesendet werden, wenn eine Münze aufgenommen wurde. (Wieder so wie oben beschrieben)

Das Script auf der Camera hat natürlich eine Variable, welche die Coins zählt, und immer wenn sich der Wert erhöht, würde es dem Textelement den neuen Wert übergeben.


OK. Jetzt hast du was zu tun. Und sei so lieb und lass das ersteinmal mit den Playerprefs. Die nutzt man mal zu beginn eines Spiels und auch mal wenn gespeichert werden soll, aber nicht ständig. Habe ich auch oben erklärt.
 

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Archiviert

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

×
×
  • Neu erstellen...