Jump to content
Unity Insider Forum
Singular

Lebenspunkte und Schaden berechnen

Recommended Posts

Ich habe mal zunächst eine allgemeinere Frage.

Da ich im Moment einen Onlinekurs mache und in diesem Kurs jedes mal (insgesamt 3 Mal bisher) diese Art gewählt wird bin ich doch etwas stutzig geworden. Folgendes Problem.

Ein Gegner wird von einer Kugel getroffen und erleidet Schaden. In dem Onlinekurs hält das Script, das entscheidet wie viel Schaden er nimmt, immer der Gegner, also der "Schadensnehmer". Heißt das Script reagiert bei der Kollision, zerstört die Kugel und reduziert die LeP. Soweit so klar.

Das kann man mit Sicherheit so machen aber diese Herangehensweise halte ich für etwas umständlich, wenn ich andere Munitionsarten oder Schadensarten habe. Für mein Spiel beispielsweise habe ich jetzt 2 Arten von Waffen entwickelt. Einen normalen Laser und Raketen. Der Laser soll etwas mehr Schaden an den Schilden und die Raketen mehr Schaden am Schiffsrumpf machen. Anstatt also 2 unterschiedliche Kollisionen für meinen Player zu schreiben, schreibe ich eine Methode für den Player [IncomingDamage(int schaden, int schildschaden, int rumpfschaden)] der nun 3 Werte erwartet. Sowie natürlich die Scripts für die Munition, die dann diese Werte übergibt. Diese Werte werden natürlich auf den Scripts der Munitionsarten hinterlegt. Das macht es meiner Meinung nach auch einfacher in Zukunft weitere Arten von Waffen o.ä. einzufügen.

Sehe ich das Richtig, das diese Herangehensweise besser ist oder übersehe ich hier etwas entscheidendes und verbaue mir damit andere Wege?

 

Im Zuge dessen noch eine Frage zu meinem Script für die Munition. 

Nachdem eine Kollision Stattgefunden hat, wird die Munition nicht zerstört sondern fliegt einfach weiter. Debug.Log habe ich reingehängt und er erkennt auch die Kollision.

    void OnCollisionEnter(Collision collision)
     {
        if (collision.gameObject.tag == "Planet" || collision.gameObject.tag == "Munition")
        {
            Debug.Log("Kollision Planet/Munition");
            Destroy(gameObject);
        }
        else if (collision.gameObject.tag == "Player" || collision.gameObject.tag == "Enemy")
        {
            Debug.Log("Kollision mit Spieler/Gegner");
            collision.gameObject.GetComponent<LebenspunkteUndSchaden>().IncomingDamage(schaden, schildschaden, rumpfschaden);
            Destroy(gameObject);
        }
    }

Share this post


Link to post
Share on other sites
vor 27 Minuten schrieb Singular:

Sehe ich das Richtig, das diese Herangehensweise besser ist oder übersehe ich hier etwas entscheidendes und verbaue mir damit andere Wege?

Ich verstehe gerade gar nicht, wie man das groß anders machen will? Zwei Klassen für zwei Arten von Schaden, die bis auf einige wenige Zahlenwerte genau gleich funktionieren? Wer macht denn sowas? Auch für deine Munitionsarten solltest du genau ein Script haben und nicht mehrere quasi gleiche.

vor 30 Minuten schrieb Singular:

Nachdem eine Kollision Stattgefunden hat, wird die Munition nicht zerstört sondern fliegt einfach weiter.

Moment, welches Objekt hat dieses Script? Was auch immer dieses Script hat, zerstört sich selbst und sonst nichts.

Davon abgesehen: Tag-Vergleiche sind doof. Ich benutze nie welche und vermisse nichts. Stattdessen lieber so etwas:

var health = collision.gameObject.GetComponent<Health>();
if (health)
{
  health.ApplyDamage(baseDamage, shieldDamage, hullDamage);
}

 

Share this post


Link to post
Share on other sites
Zitat

Ich verstehe gerade gar nicht, wie man das groß anders machen will? Zwei Klassen für zwei Arten von Schaden, die bis auf einige wenige Zahlenwerte genau gleich funktionieren? Wer macht denn sowas? Auch für deine Munitionsarten solltest du genau ein Script haben und nicht mehrere quasi gleiche.

Moment, bin ich da jetzt auf einem guten Weg oder nicht? Ich habe ein script für beide Munitionsarten (bzw es sind zwei unterschiedliche, da der Laser nur geradeaus fliegt und die Rakete ihr Ziel verfolgt wenn der/bzw ein Gegner in Reichweite ist) beide greifen aber auf das Script ihres Ziels zu und sagen dem Gegner, dass er Schaden nehmen soll. Übergeben werden dann wie du auch schon geschrieben hast die drei Schadensarten. 

(baseDamage, shieldDamage, hullDamage)

 

Zitat

Moment, welches Objekt hat dieses Script? Was auch immer dieses Script hat, zerstört sich selbst und sonst nichts.

Meine Munition hat das Script. Mein Healthscript bzw ich habe es LebenspunkteUndSchaden genannt, berechnet selbst ob der Spieler bzw Gegener zerstört wird. Es müssen ja die LeP erst auf Null fallen und dafür reicht nicht eine Rakete oder ein Laser.

Zitat

Davon abgesehen: Tag-Vergleiche sind doof. Ich benutze nie welche und vermisse nichts. Stattdessen lieber so etwas:

Gut das du das sagst^^. Das das nicht Optimal ist habe ich mir schon gedacht, wusste aber nicht, wie ich es sonst machen soll, da mein Kurs (auf dem derzeitigen stand) nur diesen Vergleich nutzt.

Verstehe ich deinen Code richtig?

var health = collision.gameObject.GetComponent<Health>(); // das Healthscript von dem Object, mit dem die Kollision stattgefunden hat wird in Health gespeichert.
if (health) // es wird geprüft ob etwas in health gespeichert wurde (also != null ist)
{
  health.ApplyDamage(baseDamage, shieldDamage, hullDamage); // das ist klar.
  //muss hier nicht noch ein "health = null" hin da ansonsten bei einer anderen Kollision wieder das selbe script, was ja noch in health drin ist aufgerufen wird?
  //oder wird "null" gespeichert, wenn das Objekt kein solches Script hat?
}

 

Edit: Achso, ich habe die Änderung übernommen. Nur mein Spieler selbst nimmt Schaden. Und auch nur bei ihm wird auch die Kollision erkannt. Die Schüsse fliegen aber weiterhin durch die Gegner und Planeten durch. Anders herum kann mein Spieler aber mit ihnen Kollidieren. (Ich habe natürlich noch ein "Destroy(gameObject);" in die if Abfrage eingefügt und auch ein else mit Destoy.

Share this post


Link to post
Share on other sites

Mein Script sieht jetzt so aus:

public class Kugeln : MonoBehaviour
{
  [...]
  
      void OnCollisionEnter(Collision collision)
    {
        var health = collision.gameObject.GetComponent<LebenspunkteSchaden>();
        if (health)
        {
            Debug.Log("Kollision erkannt Player/Enemy");
            health.IncomingDamage(schaden, schildschaden, rumpfschaden);
            Destroy(gameObject);
        }
        else
        {
            Debug.Log("Kollision erkannt Planet/Munition");
            Destroy(gameObject);
        }
    }
}

Sowohl Spieler als auch Munition und Planeten und Gegner haben alle einen Collider.

Der Spieler hat einen MeshCollider, die Planeten und Gegner einen SphereCollider, die Munitionen einen CapsuleCollider.

Wie gesagt, der Spieler kann auch mit allem anderen Kollidieren.

Share this post


Link to post
Share on other sites
vor 2 Stunden schrieb Singular:

Moment, bin ich da jetzt auf einem guten Weg oder nicht?

Öh, ja, klingt gut.

vor 2 Stunden schrieb Singular:

Verstehe ich deinen Code richtig?

Ja, bis auf den letzten Teil. "var health" bedeutet, dass hier ein lokale Variable deklariert wird. Die existiert nur solange wie der Block, in dem sie deklariert wurde, läuft. Spätestens, wenn die Methode durch ist, ist das Ding weg.

Darüber hinaus teilen sich, sofern man nicht "static" benutzt, Klassen nicht ihre Felder. Wenn du zwei Lichter hast, das eine blau und das andere rot färbst, dann sind danach ja auch nicht beide rot. Jede Instanz einer Klasse, also in diesem Fall jede Komponente, die du auf ein GameObject packst, hat ihren eigenen Zustand. Wenn du also eine Methode in deiner Klasse hast, dann wird da, wenn die aufgerufen wird, der eigene Zustand des Objekts benutzt und nicht mit dem von irgendeinem anderen verwechselt.

vor 2 Stunden schrieb Singular:

Ich habe natürlich noch ein "Destroy(gameObject);" in die if Abfrage eingefügt und auch ein else mit Destoy.

Nur so nebenbei: Ist etwas unintuitiver (und schlechter lesbar, weniger robust) als die Alternative. In einem Gespräch sagst du ja auch nicht "wenn ich heute gut drauf bin, dann höre ich Musik, und wenn ich heute schlecht drauf bin, dann höre ich Musik". Daher einfach:

var health = collision.gameObject.GetComponent<LebenspunkteSchaden>();
if (health)
{
  health.IncomingDamage(schaden, schildschaden, rumpfschaden);
}

Destroy(gameObject);

Für die Debug.Logs kannst du das else drinbehalten, aber wenn du etwas sowieso machen willst, warum dann mehrfach hinschreiben.

vor 2 Stunden schrieb Singular:

Die Schüsse fliegen aber weiterhin durch die Gegner und Planeten durch.

Wenn sie nicht daran abprallen, dann hast du entweder einen Collider zu wenig, oder einer davon ist auf "Is Trigger" gestellt. Denn durchfliegen passiert bei Objekten nicht, die sich mit Rigidbody bewegen und Collider haben, Script hin oder her.

Alternativ benutzt du irgendwie Transform.Translate, was dir keine Garantie für eine Kollision gibt - das machen nur Rigidbodys (oder, auf andere Weise, der CharacterController).

Share this post


Link to post
Share on other sites
vor 5 Stunden schrieb Sascha:

Alternativ benutzt du irgendwie Transform.Translate, was dir keine Garantie für eine Kollision gibt - das machen nur Rigidbodys (oder, auf andere Weise, der CharacterController).

Tatsache. Meine Raketen und der Laser bewegen sich über transform.Translate.

So jetzt stehe ich vor dem nächsten Problem... mein Laser bewegt sich keinen milimeter... Ich habe sogar das Script für "AddForce" von der Offiziellen Unity Seite übernommen. Nix.

Dein erster Gedanke ist deswegen: Klar er hat den rb auf Kinematisch gestellt... nö! Und die Masse ist auch auf 1.

public class Kugeln : MonoBehaviour
{
    Movement mov;
    private float playerSpeed;
    public float bulletSpeed = 20f;
    private Rigidbody rb;
    public int schaden = 1;
    public int rumpfschaden = 0;
    public int schildschaden = 1;


    void Start()
    {
        rb = GetComponent<Rigidbody>();
        playerSpeed = mov.speed + 1f;
        Destroy(gameObject, 5f);
    }

    void FixedUpdate()
    {
        rb.AddForce(transform.forward * bulletSpeed * playerSpeed);
        //transform.Translate(tr.forward * bulletSpeed * speedie * Time.deltaTime, Space.World);
    }

Ich glaube nicht, dass es am Script liegt. Eher an den einstellungen am Rigidbody. Ich habe aber sogar neue Elemente eingefügt denen das Script und einen RB gegeben und es passiert nichts. Das einzige was ich ändere, dass sie keine Gravitation haben. Wenn die dafür aber notwendig ist, dann brauche ich eine andere Lösung.

Ein positives hat es ... sie Kollidieren jetzt mit allem was sie sollen und machen auch Schaden! :D Aber stehende Raketen sind nicht so geil. Das sind dan eher Minen. Die wirft man aber eher selten VOR seinem Schiff raus xD.

Share this post


Link to post
Share on other sites

Okay, ich habe den Fehler gefunden und er war doch im Script. Wobei ich nicht verstehe, warum es nicht geklappt hat.

Ich habe vergessen GetComponent für mov in die StartMethode einzutragen. Deswegen war dieser Wert 0. Da ich ihn aber +1 gerechnet habe (um zu verhindern, dass er *0 gerechnet wird) ist scheinbar am ende des Tages doch irgendwie 0 herausgekommen.

Mit der AddForce habe ich jedoch ein Problem. Die Kugel steht erst einmal in der Luft bis sie die Geschwindigkeit bekommt die sie haben soll. Auch die einzelnen "ForceModes" helfen da leider nicht. Ich habe es meiner meinung nach aber etwas unschön gelöst, dass de Collider erst etwas später aktiviert wird.

Gibt es vielleicht die Möglichkeit, dass ich bei der Instatiate Methode noch weitere Werte übergeben kann. In meinem Fall würde ich dem Laser das GameObject übergeben welches diesen Laser erstellt hat und dieses GameObject soll bei Collision ignoriert werden. Oder gibt es vielleicht andere Möglichkeiten?

Share this post


Link to post
Share on other sites
vor 6 Stunden schrieb Singular:

Ich habe vergessen GetComponent für mov in die StartMethode einzutragen

Wenn du mit einem Objekt arbeitest, das gar nicht da ist, dann nimmt C# aber nicht irgendwelche Default-Werte. Dann hagelt's die NullReferenceException. Immer in der Konsole nach Fehlermeldungen schauen.

vor 6 Stunden schrieb Singular:

Mit der AddForce habe ich jedoch ein Problem. Die Kugel steht erst einmal in der Luft bis sie die Geschwindigkeit bekommt die sie haben soll. Auch die einzelnen "ForceModes" helfen da leider nicht. Ich habe es meiner meinung nach aber etwas unschön gelöst, dass de Collider erst etwas später aktiviert wird.

AddForce ist auch meistens Quatsch, das wird viel zu viel benutzt.

Einmalig in Start rb.velocity auf nen Vektor setzen und fertig.

vor 6 Stunden schrieb Singular:

Gibt es vielleicht die Möglichkeit, dass ich bei der Instatiate Methode noch weitere Werte übergeben kann.

Nein, aber keine Sorge, kannst du direkt im Anschluss machen. Ich habe mal mit Überladungen für Instantiate experimentiert, aber da es keine Möglichkeit gibt, sicherzustellen, dass auch wirklich Parameter übergeben werden und nicht nur das normale Instantiate ohne Parameter aufgerufen wird, hab ich's wieder eingestampft.

Was du machen kannst ist eine Methode zum Initialisieren der Komponente, nenne sie daher gerne z.B. "Initialize( ... )". Dann machst du

var spawnee = Instantiate(prefab);
spawnee.Initialize(parameters);

Ich frage mich gerade... sind die Laser langsam oder eher blitzschnell? Wenn die nach Sekundenbruchteilen schon ankommen, ist es eine Überlegung wert, stattdessen Raycasts zu benutzen und die Laser nur visuell darzustellen, anstatt ihn als kollidierendes Objekt zu instanziieren.

  • Thanks 1

Share this post


Link to post
Share on other sites

Ich werde es mal glaube ich lassen jeden einzelnen Punkte zu zitieren 🤪

rb.velocity wurde in meinem Kurs nie angesprochen. Damit sollte es gehen.

Die Übergabe Methode klingt gut. Die werde ich denke ich nachher mal ausprobieren. 

Die Laser sind nicht gerade "Blitzschnell" aber sie sind nach ca 2 Sekunden aus dem Bild,  weswegen sie auch nach 5 Sekunden zerstört werden wenn sie nichts getroffen haben. Mit Raycasts kenne ich mich (noch) nicht aus. Das werde ich mir gleich mal Anschauen. Klingt nach einer gesunden sauberen Alternative.

Share this post


Link to post
Share on other sites

Sorry das ich mich nicht mehr gemeldet habe. Also, mit Raycast hab ich mich jetzt noch nicht beschäftigen können aber dank deiner Tipps habe ich meinen Code ordentlich entschlacken können. ein Script konnte ich direkt löschen und zwei weitere brauche ich nicht mal mehr erst zu schreiben.

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...

×
×
  • Create New...