Inventar Array -> richtig gestalten

  • Hallo zusammen!


    Ich möchte gerne mein Inventarsystem überarbeiten und da sind mir ein paar Fehler aufgefallen.

    Es geht dabei vor allem um die stapelbaren Gegenstände!


    Habe das so gemacht das meine Gegenstände ein Struct haben und darin befindet sich die Werte; "StapelbarAktuell" für wie viele es aktuell sind und "StapelbarMax" wie groß der Stapel maximal sein kann.

    Also 10 Erz zu je 5 StapelMax = 2x 5 Erz (so sollte es dann im Inventar sein).


    Das ganze als Integer Werte, um das Komma zu vermeiden.


    Nun muss ich also 11 Erze zu je 5 Stapel in letztendlich 3 Stapel umrechnen.

    11 durch 5 = 2,2 Stapel.

    2,2 sollte zu Integer 3 gerundet werden, aber das tut die Unreal Engine nicht.

    Float 2,2 RoundToInteger ist immer 2 und nicht 3! Wie kann man das machen das IMMER aufgerundet wird?



    Dazu hätte ich noch eine allgemeine Frage:
    Macht es mehr Sinn wenn man weniger Anzahl in einem Array hat und dafür im Array (im Struct) die Werte erhöht?

    Z.B. 10 Erze sind im Array 1 Gegenstand (1x10), anstatt 10x1.

    Wobei es bei einer StapelmengeMax also 2x5 wären, wenn man nach dem System geht was ich geplant habe.


    Diese Frage bezieht sich darauf wie man die Gegenstände wieder aus dem Inventar bekommt und vor allem wie man das ganze im Inventar umgestalten kann.


    Danke für eure Infos. Ich wollte eigentlich einen Code hier einfügen aber irgendwie geht das nicht.

    • Offizieller Beitrag

    Was du auf jeden Fall vermeiden musst, ist wenn deine Berechnung 0/10 lautet. Wenn sich in deiner Variable 0 befindet und du versuchst dies durch irgend eine andere Zahl zu teilen, dann gibts einen Fehler. zb 0/10 ist NICHT 0 und auch NICHT 10.


    Es gibt einen guten Grund warum man bei spielen eigentlich immer Zahlen verwendet die durch 2 Teilbar sind.

    Ich glaube mit der komma Rechnerei wirst du nicht glücklich.


    Aber zu deiner Frage. Ich vermute mal du hast mit Round gerundet ? Round macht das was es soll, es rundet die Zahl auf oder ab je nach Kommastelle.

    Was du im grunde willst, ist einen Float wert auf die nächste ganze Zahl zu runden. Und das geht mit Ceil


    Ceil
    Ceil
    docs.unrealengine.com

  • Aber natürlich kann man 0 / 10 teilen, bleibt dann halt 0. Und mit durch 2 teilbar hat es auch nichts zu tun.


    Du musst schlichtweg aufrunden. Nun kenne ich nicht deinen genauen Code. Du kannst natürlich den kleinsten Stack überprüfen, ob dieser Max_StackSize erreicht hat. Wenn ja, eröffnest du einen neuen Stack.


    Oder du rundest einfach auf, auf die nächste Ganzzahl. Wenn du zwei Stacks hast, er braucht dann aber drei, dann halt überprüfen, wie viele Stacks sind vorhanden und braucht es einen neuen.


    Wie gesagt, ich weiß ja nicht, wie du es bisher angegangen bist. Aber so in die Richtung würde ich erstmal gehen.


    Momentan bin ich leider in Blender total vertieft und habe die Nodes jetzt nicht alle so im Kopf, wenn ich nicht selber davor sitze. Aber vielleicht probiere ich nachher auch noch was aus, dann kann ich das Problem ja auch mal genauer anschauen.

    • Offizieller Beitrag

    Aber natürlich kann man 0 / 10 teilen, bleibt dann halt 0. Und mit durch 2 teilbar hat es auch nicht

    Probiers doch mal mit dem Windows Taschenrechner aus.

    In der Mathematik ist das nicht zulässig.


    Ich hatte in Maya schon öfter das Division by Zero Problem, vor allem wenn Pixel kleiner als 0,00xxx waren. Diese Werte werden dann auf 0 Gerundet und wenn dann eine Zahle die größer ist durch 0 geteilt wird, dann knallt es.

    In Python kannst du auch nicht 3/0 rechnen, das führ zu einer Fehlermeldung.

    Deswegen würde ich das persönlich nicht machen. Dieses Problem nennt ist Division by Zero im Internet findest eine Menge Informationen dazu.



    Und mit durch 2 teilbar hat es auch nichts zu tun.

    Ja doch weil wenn eine Zahl durch 2 Teilbar ist, dann braucht sie weniger Speicherplatz. Deswegen gibt es 32 Bit Systeme, 64 Bitsysteme. bei MInecraft einen Stack mit 2,4,8 16, 32. 64 usw.

    Es macht mehr Sinn Zahlen zu verwenden die durch zwei Teilbar sind und vor allem Zahlen die gerade sind.

    Man nennt diese Zahlen ja auch Power of Two. Grundsätzlich können 2er Potenzen leichter gespeichert werden.

    Man kennt das Problem auch von Texturgrößen die ebenfalls Power of Two sein sollten.

    Zahlen müssen nicht durch 2 teilbar sein es ist aber definitiv effizienter und leichter zu rechnen.

  • Aber natürlich kann man 0 / 10 teilen, bleibt dann halt 0....

    Dann versuche mal 0 mit einer Zahl zu multiplizieren und die Zahl 10 zu erhalte ;)


    Wenn du versuchst 0 durch eine Zahl zu teilen ist das Ergebnis undefiniert.


    EDIT: oppss...sorry die Frage wurde schon von Sleepy beantwortet :D

  • Ja, dann teilt doch beide mal auf irgendeinem Taschenrechner die 0 / 10. Überraschung, es geht. Weil 0 Teile in 10 Teile aufzuteilen, bleibt 0 für jeden. Ganz einfach.


    Das, was ihr meint, ist 10 / 0. Weil wenn du eine Zahl durch null teilst, ergibt das unendlich. Was auch die Fehlermeldung von Spleepy "Division by Zero" genau aussagt. Witzigerweise gibt er dann ja sogar noch das Beispiel in Python 3 / 0 geteilt zu haben.

  • Danke für die Infos!


    Das mit Ceil... immer solche versteckten Dinger ^^


    Für welche Art von Inventarstrukturierung habt ihr euch entschieden und vor allem WARUM?


    Variante A: "Array Length: 10"

    10 x 1 Eisenbarren (oder was auch immer)


    Variante B: "Array Length: 1"

    1x 10 Eisenbarren


    oder gibt es noch eine andere Form?


    Bin derzeit bei Variante A aber mit Vorbereitung bei Variante B...

    Es ist halt bequemer wenn man ein Array nach der Anzahl durchsucht anstatt nach einzeln mit einer Anzahl als Variable darin.

  • Du könntest auch zwei geteilte Inventarsysteme machen. Eins, welches man nicht sieht, also ein Array, wo alles reinkommt, also 50 Eisenbarren, 20 Schaufeln, 25 Äpfel, halt für jedes Objekt ein Array. Und in dem Inventarsystem, welches der Spieler dann sieht, werden die Stacks dann halt in die möglichen Stackgrößen aufgeteilt. 4x5 Schaufeln, 5x10 Eisenbarren usw. Das macht es vielleicht auch beim Speichern ein wenig einfacher.

    • Offizieller Beitrag

    Ich frage mich ob du später dein Inventar erweitern willst ?


    Was ist wenn zb ein neues Schwert ins das Spiel eingebunden werden soll ?

    Was ist wenn du das Balancing der Objekte im im Inventar verändern willst ? zb soll das Schwer statt 100 Energie, 120 Energie abziehen ?


    Ich meine du hast ja sicherlich später keine Lust alles Händisch zu ändern und bei jedem Spieler die Schwertstärke von 100 auf 120 zu ändern?


    Meine Überlegung ist, ob du dir nicht ein datatable erstellt wo du alle Assets reinhämmerst. Diese Liste kannst du jederzeit erweitern und dort auch Werte ändern. So könntest zb Geschenke machen oder wenn ein Player ein Asset kauft, dort das gekaufte Asset publishen.

    Startet ein Player das Spiel. Wird ein neues Array aus dem Inhalt dieser Tabelle initialisiert.


    Bei jedem Start des Spiels, werden die Werte aktualisiert und mit dem Player local synchronisiert.


    Auch beispielsweise Kisten bzw deren Inhalt könntest du mit diesem Datatable synchronisieren.


    Aus dem Datatable kannst du nur auslesen nix speichern. Deswegen würde ich das Inventar in Localen Inventarklasse speichern und zwar so, das bei Internet disconnect die Daten Local gespeichert werden, und sie jederzeit bei einer Datenbank im Internet abgeglichen werden können.

  • Ich glaube, du verwechselst da verschiedene Dinge mit einem Inventarsystem. Das Balancing hat gar keinen Einfluss auf das Inventar oder umgekehrt. Ein Inventar ist eine reine Liste mit Gegenständen, die man besitzt. Es ist lediglich eine Frage, wie man es verwalten und/oder darstellen will.

    • Offizieller Beitrag

    Es ging mir darum wie man dass von Anfang an dynamisch aufbauen kann.

    Über ein Datatable, kannst du neue Waffen zum Spiel hinzufügen.


    Dinge wie das Inventarbild, wie stark die Waffe ist, wieviel Schaden sie aushält, wieviel Gesundheit die Waffe hat usw.
    All dass sind Informationen die man im Datatable hinterlegen kann. Auf diese Informationen könntest du aus einem Inventar zugreifen.

    Im Grunde könntest bei einer Waffe nur eine ID und ne Menge speichern alle anderen Informationen einschließlich Inventarbild kämen aus dem Datatable und nicht aus dem Array.


    Das hat den Vorteil, das du Werte, Namen, Icons etc schnell ändern kannst und du musst auch nicht bei jedem Spieler alle Informationen in einem Array speichen.


    Wenn du ein RPG machst und 500 Spieler hast und du merkst dass der Zauberstab zu stark ist, willst du dann bei allen 500 Spieler den Wert Manuell im Array ändern ?

    Du müsstest den Wert dann nur im Datatable ändern und alle Zauberstäbe in allen Inventaren hätten den neuen Wert.

    DInge wie der jetzige Zustand des Zauberstab (Leben) müsstest du dann zustätzlich im Array speichern. Aber du müsstest zb nicht 50 mal Zauberstab in ein Array schreiben und auch nicht 50mal das der Zauberstab 100 Schaden macht usw.

  • Ich habe das ganze so gemacht das ich mich auf eine Graphen-Datenbank hinarbeite. So das ich nur für die erste Gegenstandsart eine Datenbank brauche und alles weitere wird in sich selbst gespeichert, modifiziert und angepasst vererbt usw.. Man soll die Sachen im Spiel verändern können (innerhalb einer gewissen Möglichkeit) die wiederum neue Dinge ermöglichen die die bisherigen vererben können, "Parent/Child" System aber eben erweitert. Natürlich innerhalb ihrer Zugehörigkeit (bezüglich der Inventarorientierung).


    Dabei habe ich das Inventarsystem und allgemein so gemacht das 6 Inventar-Systeme vorhanden sind; Waffen, Nahrung, Fahrzeuge usw.

    Dadurch kann ich die allgemeine Last verschieben (im späteren Verlauf kommen erst die großen Probleme), in anderen Spielen habe ich gemerkt das wenn man alles über ein System laufen lässt das die Performance darunter enorm leidet (weil ja immer alle Informationen ((auch wenn leer)) mitgeliefert werden).


    Natürlich hoffe ich das es so funktioniert wie gedacht. Muss nur noch gucken wie ich die ganzen Sachen in der Welt speichern kann (Persistenz). So kann ich z.B. gewisse Dinge deaktivieren ohne das davon alle betroffen sind. Als Beispiel wäre da die Gebäude im Spiel, dort könnte man die Informationen deaktivieren (Sleep) bis diese gebraucht werden (Awake). Es macht einen Unterschied ob ich jetzt 1000 Häuser habe die voll mit Informationen sind (weil das im Laufe der Zeit ja mehr an Informationen wird) oder 1000 Häuser bei der ich die Informationsdichte selbst regulieren kann, ohne das darin Informationen sind die nicht dazugehören (als Beispiel Fahrzeugeigenschaften usw.).


    Ich möchte das die Spieler selber Entscheidungen treffen können und dürfen legendäre Gegenstände zu erzeugen, ganz nach ihren Wünschen. Das wird genau den Nerf der Zeit treffen wenn Kreativität der Spieler gefördert wird, natürlich wird das Balancing berücksichtigt. Alles im Spiel ist endlich. Sollte eine Waffe zu übermächtig sein, kann diese wieder zerstört werden. Darum die Aufteilung weil ich so mehr Spielraum innerhalb der Bereiche habe. Falls mal was kaputt gehen sollte (was ja immer mal passieren kann) ist nur der Bereich davon betroffen und nicht alles andere. Ausgenommen das was damit zusammenhängt könnte auch etwas fehlerhaft sein.


    ---

    Zu meinem Fehlerproblem:


    Derzeit habe ich diese Fehlermeldungen (gelbe Schrift) wie kann ich das beheben? Es scheint so als ob ein Array leer ist und dann doch nicht (habe es gegengeprüft). Muss ich da einfach nur überall ein "IsValidIndex" machen oder so um das zu beheben, oder ist das harmlos?

    Es funktioniert ja alles wunderbar. Im Internet schreiben einige das es harmlose Fehler sind, trotzdem würde ich verhindern wollen das diese Fehlermeldung erscheint (um zukünftig andere Fehlermeldungen besser analysieren zu können).

  • Ich habe noch eine zusätzliche Frage:

    Bezüglich Multiplayer und Delay.


    Wie kann man das machen das eine Übertragung von Informationen dann erfolgt wenn es möglich ist ohne den Verlust durch Ping?


    Bezüglich dem "Inventar", benutze ich das Langschwert daraus muss im anderen "Ausrüstung-Inventar" (wo es hineingeführt wird) die Widgets aktualisiert werden. Da der Transfer von Server zu Server erfolgt, können keine Lokalen Widgets durchgeführt werden (es geht allgemein nicht). Setzte ich die Aktualisierung des "Ausrüstung-Inventar" an das Ende, so das es aktualisiert werden soll wenn alles andere davor erledigt wurde, funktioniert es nicht.

    Erst wenn ich ein Delay von 0,2 Sekunden davor hänge funktioniert es.


    Aber wenn einmal der Ping/Verzögerung größer ist, tritt der gleiche Fehler auf.

    Daher meine Frage wie kann man etwas Ausführen wenn es tatsächlich ausführbar ist?

    • Offizieller Beitrag

    LogScript: Warning: Script Msg: Attempted to access index 4 from array 'Anzahl_13_E31372784E01254EC23564AC9F641901' of length 0 in '/Game/EXARAN/Blueprints/Actors/Items/ItemDataBaseStructs/Ausruestung/Stapelbar/ItemDataBaseAusruestungStapelbar.ItemDataBaseAusruestungStapelbar'!

    In deinem Blueprint:

    /Game/EXARAN/Blueprints/Actors/Items/ItemDataBaseStructs/Ausruestung/Stapelbar/ItemDataBaseAusruestungStapelbar.ItemDataBaseAusruestungStapelbar

    Versuchst du auf den Index 4 zuzugreifen. Das Array ist aber leer oder existiert nicht.


    Im Grunde erst ne Index mit einer Zahl befüllen und dann darauf zugreifen. Alles andere wäre nicht sauber Initialisiert. Du kannst als Default auch 0 reinmachen sonst steht eben nichts drin und es knallt ^^


    Ich glaube die anderen Fehler haben die selben Ursache. Teste halt mit einem Print welche Zahl sich beim Lesen im Array befindet.



    Zitat

    Ich habe noch eine zusätzliche Frage:

    Was soll den Passieren wenn kein Internet mehr da ist oder wenn ein Spieler das Spiel verlässt ?

    Was soll passieren wenn keine Verbindung zum Server möglich ist ?


    Bei vielen Spielen, fliegst man dann direkt aus dem Spiel. Bei manchen spielen wird eine gewisse Zeit gewartet ob die Verbindung wieder hergestellt werden kann. (TImeout wird abgewartet)


    Du müsstest doch nur Synchronisieren wenn es eine Veränderung gibt ? Du speicherst ja alles auf dem Server? was ist wenn der Server abstürzt und nicht verfügbar ist ?


    Was ich mich frage ob du Veränderungen lokal speichern willst und diese Informationen mit dem Server Synchronisieren möchtest.


    Bei Counterstrike zb macht es keinen Sinn Informationen Lokal zu speichern da die Runde vorbei ist wenn der Server Crasht oder alle das Spiel verlassen.

    Bei Statisfactory zb werden Informationen Lokal gespeichert und du kannst die Spielstände auch Manuel mit der Cloud oder umgekehrt Synchronisieren. So können lokale Spielstände von Cloudspielstand abweichen.


    Ich glaube da hat jeder ein anderes Szenario im Kopf.


    Ich weiß nicht wie du dir dein Spiel vorstellst. Aber so grundlegend brauchst du doch eine Routine die Prüft ob ein Spieler noch da ist. Verlässt ein Spieler dass spiel, müsste doch ein Boolean von True auf false gesetzt werden. Dann kommt eine Meldung Spieler xyz hat das Spiel verlassen.


    Da selbe gilt für den Server. Du prüfst ob eine Verbindung um Server besteht und nur dann speicherst du. Ist der Server Boolean = Ture, dann kannst du speichern. Dann brauchst du vom Server eine Antwort dass die Speicherung erfolgreich war. Nur dann kannst du dem Spieler eine Nachricht ausgeben wie "Autosave erfolgreich gespeichert"

    Bei der Datenbank würde das eher still im Hintergrund passieren aber für als Entwickler wäre es denke ich nicht schlecht das erstmal anzuzeigen.


  • Das wundert mich etwas weil das Array nicht leer ist... komische Sache.

    Darum verstehe ich nicht warum die Fehlermeldung erscheint.




    Gedacht habe ich das so das Dinge in der Welt nur auf dem Server gespeichert werden, nach dem "Object-Streaming" Prinzip.

    Dinge im Spiel werden rein und raus gestreamt wenn diese nicht oder benötigt werden.


    Der erste Schritt dafür ist die Physik der Objekte nach einer Ruhephase deaktiviert werden, um so die Performance zu verbessern. Also diese werden zu temporär Statischen Objekten und dabei sollen auch die Informationen entfernt werden, bis diese wieder gebraucht werden. Etwa wenn sich ein Spieler einem gewissen Bereich nähert.