Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

21

13.02.2018, 11:39

Hallo Tiles.

Vielen Dank für die Antwort.

SO, hilft das ideal weiter. In das Script fuchse ich mich ein sobald ich dafür den Kopf frei habe, erst einmal soll in meinem kleinem Adventure der
Held auch neue Herzen und Munition sammeln können, nur mit dem StartAmount steht er da bissel einsam da :)

Vielen Dank.

Und ja: das Learning by Doing, ist für mich persönlich auch die beste Möglichkeit etwas zu lernen.
Nur habe ich mich halt gefragt wo der Fehler lag, das Script oben (bzw. der Tip) hat überhaupt nicht weiter geholfen.

so schlimm ist es mit dem C# dann auch nicht, etwas Vorerfahrung in Programmierung ist ja auch vorhanden,
nur bei manchen Dingen braucht es etwas, den Zusammenhang zu verstehen.
Schlimmer wird mein Herz System einzubauen, da nutze ich derzeit nur eine einzige Variable, später soll das mal currentHearts maxHearts StartHearths und ownHearts werden.

d.h. own für die die ich habe
max für das maximum der Lebensleiste die man bekommen kann
current für die die derzeit aktiv sind (voll oder leer)
und own für das maximum was ich derzeit besitze.


aber erst mal Stück für Stück rantasten, es hat jeder mal angefangen.
Vorsicht! Tante Gretel darf man nicht hänseln.

Jar

Treue Seele

Beiträge: 197

Wohnort: Lübeck

Beruf: Softwareentwickler

  • Private Nachricht senden

22

13.02.2018, 12:30

Eine gängige Methode für dein Problem ist es, eine Elternklasse zu erstellen die man nicht instanziieren kann, von der du aber erben kannst.
Hier bei diesem Beispiel mal auf den Player zugeschnitten.

Deine Powerups bekommen einfach ein HealthPowerup Script, welches du im Editor konfigurieren kannst. Du könntest jetzt zur Übung anfangen noch andere Powerups zu erstellen.

C#-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
public abstract class PowerUp : MonoBehaviour
 {
     public abstract void OnPickup(Player player);
     void OnTriggerEnter(Collider collider)
     {
         Player player = collider.GetComponent<Player>();
         if (player != null) {
             OnPickup(player);
             Destroy(this.gameObject);
         }    
     }
 }

C#-Quelltext

1
2
3
4
5
6
7
8
9
 public class HealthPowerup : PowerUp
 {
     public int healthValue;

     public override void OnPickup(Player player)
     {
         player.health += healthValue;
     }
 }


Ich habe bewusst einige Fachbegriffe verwendet um zu zeigen, das eine gewissen Grundlage doch hilfreich sein kann um sich auf einer Ebene über ein Problem zu unterhalten :)
Bei Fragen einfach fragen^^

P.S.: Ich habe diesen Code nicht getestet.

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »Jar« (14.02.2018, 11:11)


Tiles

Treue Seele

Beiträge: 168

Wohnort: none

  • Private Nachricht senden

23

13.02.2018, 13:07

Ja, so geht es natürlich auch. Aber wie gesagt, von GetComponent() sollte man in Unity die Griffel lassen wo es irgend geht. Das ist sehr langsam. Die Methode das über eine Variable im Inspektor zu referenzieren ist deutlich performanter. Und einfacher :)

Das ist imho auch ein schönes Beispiel wieso man für Unity nicht erst C# lernen sollte. Unity ist keine C# IDE. Das ist eine Game Engine. Die als Scriptsprache C# verwendet. Und Unity verwendet nur einen klitzekleinen Bruchteil von C#. Vieles lässt sich natürlich Eins zu Eins übernehmen. Aber vieles auch nicht. Weil es eben immer auch den Unity Weg gibt.
Free Game Graphics, Freeware Games https://www.reinerstilesets.de

Garzec

Alter Hase

Beiträge: 693

Wohnort: Gießen

  • Private Nachricht senden

24

13.02.2018, 21:49

Um einfach mal einen anderen Weg in den Raum zu werfen (mal ohne Inspector, mit Inspector wäre natürlich 0.2 Nanosekunden performanter, aber man ist auch stärker auf Unity angewiesen)

Vielleicht habt ihr ja auch Verbesserungsvorschläge...

Kollisionsabfragen habe ich fertig in einer globalen Klasse, weil die Abfrage ja immer die gleiche ist.

C#-Quelltext

1
2
3
4
5
6
7
8
9
    public static bool CheckCollision(Collision col, GameObject collidingGameObject)
    {
        return col.gameObject == collidingGameObject;
    }

    public static bool CheckCollision(Collider col, GameObject collidingGameObject)
    {
        return col.gameObject == collidingGameObject;
    }


Durch die Signatur muss ich dann nur noch an eine Methode denken ;)

Ebenso brauche ich immer wieder den Spieler. Den kann ich im Inspector setzen oder per Code, aber dann in Start, nicht im Trigger Event. Ebenfalls eine globale Methode, die ich mir in Start aufrufe

C#-Quelltext

1
2
3
4
    public static GameObject GetPlayerObject()
    {
        return GameObject.FindGameObjectWithTag(StringCollection.PLAYER); // StringCollection ist eine Sammlung von strings, damit diese nicht wild im Projekt rumfliegen
    }


Das Pickup selbst benötigt den Spieler ja nur für die Kollisionsabfrage. Der HealthController selbst liegt ja bei den UI Elementen, also der Healthbar. Die kann ich also wieder im Inspector setzen oder mir in der Startmethode holen.

Das TriggerEvent sieht bei mir dann so aus

C#-Quelltext

1
2
3
4
5
6
7
8
    private void OnTriggerEnter(Collider col)
    {
        if (Globals.CheckCollision(col, playerObject))
        {
            healthController.ChangeHealth(healthToAdd);
            Destroy(gameObject);
        }
    }


Da kann man auch wieder einiges in Basisklassen auslagern.

Naja und

C#-Quelltext

1
healthController.ChangeHealth(healthToAdd);


macht bei der Healthbar eigentlich nur folgendes

C#-Quelltext

1
2
3
4
5
6
7
    public void ChangeHealth(float healthToAdd)
    {
        currentHealth = Mathf.Clamp(currentHealth + healthToAdd, 0, maxHealth);
        UpdateHealthBar(); // UI updaten
        if (currentHealth <= 0)
            Destroy(playerObject);
    }


Wobei das jetzt mehr Pseudo Code ist, weil ich mir die Spielerwerte natürlich vom "Spielstand" hole, ein Objekt, das zwischen den Szenen nicht zerstört wird und den Speicherstand darstellt, die Daten erzeugt und beim Beenden natürlich alle Daten abwickelt.

25

14.02.2018, 06:56

Nun habe ich mich ein Stück eingelesen in die abstract Class, jetzt werd ich heute mal etwas damit herumprobieren.

die Idee "healthValue" ist gar nicht so schlecht.

aber zu aller erst einmal: Vielen Dank für Eure Antworten, es ist viel interessantes dabei, was man natürlich zuerst auch mal
"sacken" lassen muss.

Es ist ja am ende auch das 1. Projekt, das ist immer zum lernen da - und gleichzeitig auch zum sich ausprobieren.
Für alles andere, öffne ich dann meist aber erst mal eine neue Scene, und probiere etwas herum :)
Vorsicht! Tante Gretel darf man nicht hänseln.

Tiles

Treue Seele

Beiträge: 168

Wohnort: none

  • Private Nachricht senden

26

14.02.2018, 09:00

Japp, so könnte eine mögliche Lösung über Tags aussehen. Top :)

Zitat

(mal ohne Inspector, mit Inspector wäre natürlich 0.2 Nanosekunden performanter, aber man ist auch stärker auf Unity angewiesen)


Du bist doch schon in Unity. Du baust ja ein Spiel damit. Dann würde ich auch alle Möglichkeiten ausschöpfen die das Ding zu bieten hat ;)

Über eine Inspectorvariable zu gehen ist einfach eine kostenlose und schnelle Möglichkeit der Mikrooptimierung, also Best Practice. Du sparst da schon einige Milisekunden. Und je komplexer das Rig ist um so mehr Zeit brauch es um durch die Hierarchie zu gehen. Das mag im Einzelfall tatsächlich nicht grossartig was ausmachen. Aber wenn du das mit der Inspectorvariable nicht weisst, dann ballerst du beim Entwickeln erst mal alles mit GetComponent voll. Das summiert sich dann auf. Und im schlimmsten Fall kommt es dann zu Stotterern wenn alle GetComponent Dinger mal gleichzeitig am Rödeln sind. Und das dann nachträglich zu optimieren ist dann doch recht mühsam.

Und in unserem Fall mit dem Health Pack würden wir auch Code einsparen. Das wäre einfach nur oncollisionenter + if other.tag is player, add to health variable vom referenzierten Player, destroy game object. Das Script ans Healthpack, und gut is.

Oder noch geschickter, du gehst vom Player aus, dann gibts das Collision Script nur einmal at Runtime. Pseudocode: ontriggerenter (other:Collider): + if (other.gameObject.CompareTag ("healthpack")), add to health variable vom referenzierten Player, destroy other.game object. Und in das OnTriggerEnter kannst du jetzt auch alles andere reinpacken was der Player so aufsammeln können soll. Musst halt nur andere Tags nehmen für die Objekte.

Aber ja, über ein einziges GetComponent würde ich mir auch keinen Kopf machen :)
Free Game Graphics, Freeware Games https://www.reinerstilesets.de

Dieser Beitrag wurde bereits 12 mal editiert, zuletzt von »Tiles« (14.02.2018, 10:07)


Tiles

Treue Seele

Beiträge: 168

Wohnort: none

  • Private Nachricht senden

27

14.02.2018, 09:32

Für alles andere, öffne ich dann meist aber erst mal eine neue Scene, und probiere etwas herum :)


Das ist ein sehr guter Ansatz. Problem vereinzeln, lösen, dann wieder einbauen. Ich hatte damals hunderte einzelne Problemlösungsfiles :)
Free Game Graphics, Freeware Games https://www.reinerstilesets.de

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »Tiles« (14.02.2018, 09:49)


Jar

Treue Seele

Beiträge: 197

Wohnort: Lübeck

Beruf: Softwareentwickler

  • Private Nachricht senden

28

14.02.2018, 11:06

Man brauch sich keine großen Gedanken um GetComponent<>() zu machen. Die Geschwindigkeitseinbußen sind nicht mal richtig messbar. Laut folgenden Post, sind selbst bei 10+ Millionen Aufrufen keine signifikanten Unterschiede zu erkennen. https://answers.unity.com/questions/1851…my-scripts.html

Außerdem ist die Faustregel, löse Probleme erst wenn sie auftreten, Überoptimierung ist ein Zeitfresser.

Tiles

Treue Seele

Beiträge: 168

Wohnort: none

  • Private Nachricht senden

29

14.02.2018, 12:22

Das stimmt natürlich. Viele Wege führen nach Rom, und ein funktionierender langt :)
Free Game Graphics, Freeware Games https://www.reinerstilesets.de

Schorsch

Supermoderator

Beiträge: 5 145

Wohnort: Wickede

Beruf: Softwareentwickler

  • Private Nachricht senden

30

14.02.2018, 13:02

Allgemein sollte man Probleme dann lösen wenn sie auftreten. Ich fand es auch eher lästig Dinge im Inspector zuzuweisen. Was einem da jetzt lieber ist bleibt ja jedem selbst überlassen. Aber überlegen wir doch mal von welchem Aufwand wir reden, welcher optimiert werden soll. Ein Objekt hat in Unity eine Liste von Komponenten. Mit GetComponent<> hole ich mir jetzt eben die passende Komponente. Im schlechtesten Fall wird das ganze lineare Laufzeit haben, wobei die Komponenten eben einfach alle unsortiert in einer linearen Datenstruktur liegen und GetComponent<> Komponente für Komponente durchgeht. Selbst wenn dein Objekt jetzt 20 Komponenten hätte dann würde man GetComponent<> in etwa auf 20 Vergleiche runter brechen können. Dazu kommt natürlich noch etwas Overhead, das sollte am Ende aber nicht viel ausmachen.
Wenn du eh gern den Inspektor nutzt und darüber zuweist, dann mach das weiter. Damit aber anzufangen um die Performance zu optimieren ist wohl quatsch. Wenn dein Spiel schlechte Performance hat guckst du mit einem Profiler wo deine Bottlenecks sind. Wenn du die gefunden hast kannst du anfangen zu optimieren. Es gibt da ein schönes Zitat von Donald Knuth: "The real problem is that programmers have spent far too much time worrying about efficiency in the wrong places and at the wrong times; premature optimization is the root of all evil (or at least most of it) in programming."

Das ist imho auch ein schönes Beispiel wieso man für Unity nicht erst C# lernen sollte. Unity ist keine C# IDE. Das ist eine Game Engine. Die als Scriptsprache C# verwendet. Und Unity verwendet nur einen klitzekleinen Bruchteil von C#. Vieles lässt sich natürlich Eins zu Eins übernehmen. Aber vieles auch nicht. Weil es eben immer auch den Unity Weg gibt.

Das ist so eine Sache. Ein Bekannter von mir meint auch in vielen Fällen nicht schön Arbeiten zu können, weil er dann eben "gegen" Unity arbeiten müsste. Es gibt am Ende viele Möglichkeiten ein Problem zu lösen. Unity schreibt dir dabei nicht vor einen schmutzigen zu nehmen. Eben weil vielen Leuten bestimmtes Grundlagenwissen fehlt kursieren allerlei Gerüchte im Internet. Ein Beispiel dafür hatten wir ja hier mit GetComponent<>. Das wird verrufen ohne zu wissen warum es jetzt schlecht ist. Noch so eine Sache ist das mit den Structs. Wie oft ließt man im Internet dass Structs in C# für kleinere Dinge da sind da sie die lightweight Klassen sind. Das wird von vielen Leuten so übernommen und am Ende weißt keiner was jetzt der genaue Unterschied eigentlich ist.
Natürlich muss ich kein C# Profi sein um mit Unity irgendwie arbeiten zu können. Es ist aber sinnvoll ein gewisses Grundwissen zu haben um eben vernünftig damit arbeiten zu können.
„Es ist doch so. Zwei und zwei macht irgendwas, und vier und vier macht irgendwas. Leider nicht dasselbe, dann wär's leicht.
Das ist aber auch schon höhere Mathematik.“

Werbeanzeige