Beiträge von Tomura

    Die logik im Blueprint scheint mir nicht ganz so sinnvoll.

    Über Add World offset, gibst du den ganzen ja ein Positions Delta vor, d.h. es ist so als ob du Geschwindigkeit vorgibst, die jedoch je nach Framerate anders ist, da DeltaWeg= Geschwindigkeit * Zeit und die Zeit ist ja variabel da UE4/5 variable frame rate hat.


    Folgendes:

    A) Die Kurve soll eine Geschwindigkeit sein --> Multiplizier den Wert mit dem DeltaTime bevor du es in Add World Offset gibst, dann ist es physikalisch korrekt denn DeltaWeg= Geschwindigkeit * Zeit

    B) Die Kurve soll eine Positionsangabe sein -> Nimm lieber Set Position (absolut) or Set Relative Position. Bei letzterem solltest du sicherstellen, dass dein Plattform Actor ein Rootcomponent hat (z.B. ein SceneComponent als Root) welches sich nicht bewegt und die Plattform selbst (Mesh und Collider) Child Components sind. Du könntest dann über die RelativPosition der Components die Verschiebung zum RootComponent steuern.

    Genauso wie du bei Clean Coding Dinge z.B. in Funktionen packst um selbst-dokumentierenden Code zu schreiben kannst du auch in Blueprint gewisse Dinge einfach in Funktionen verpacken und etwas aufräumen.


    Versuche einfach mal die Prinzipien von Clean Code auf Blueprint zu übertragen. Also

    • Erklärt sich der Blueprint von selbst
    • Wie ist die kognitive Komplexität?
    • Sind Variablen und Funktionen gut benannt?
    • Zu viele Zweige?
    • Zu viele exit points (zu viele returns in einer funktion)
    • etc.

    Das hier wäre z.B. das Blueprint Gegenstück zu Spagetti Code.

    1) ist sauberer und du kannst mittels Polymorphie gut abstrahieren (d.h. der Actor der die Waffe trägt muss nicht wissen um welche waffe es sich handelt, sondern nur dass es sich um eine Waffe handelt) und somit bleibt dein Code simpel. Das Attachment System von UE ist auch gut genug um so dinge wie physik und collider einfach zu halten.

    Das hinzufügen neuer Waffen wäre im Idealfall- also wenn du gute Vorarbeit geleistet hast, also eine gute Abstraktion in Form einer Basis Klasse geschaffen hast, von der alle anderen erben - nur eine Sache vom Hinzufügen eines neuen Waffen Blueprints.

    Wenn du keine OOP erfahrung hast, dann solltest du dir zuerst aber mal die Grundlagen beibringen.


    2) ist i.O. wenn du nur wenige Waffen hast, skaliert aber schlecht wenn es um Maintanability geht.

    Wie du sagst, würde das hinzufügen einer neuen Waffe in deinem Spiel hier bedeuten, dass du die Data Table ändern musst, die Funktionen im Actor, etc. D.h. viele Änderungen an verschiedenen Stellen, die zusammenspielen müssen.

    Was genau ist denn dein Use-case? Vielleicht wäre eine BitMask besser als ein Array, wenn du eh nur bools hast?

    Bitmask Blueprint Variables
    Blueprint integer variables can be declared as bitmasks to store binary flags in a more compact fashion.
    docs.unrealengine.com


    Vorteile:

    • Sehr einfache Datenstruktur (Integer, statt Array)
    • Setzen von mehreren Bits auf einen Schlag ist einfach
    • Benennung der Einzelnen Bits über Unreal Enums
    • ForEach Iterator für den Enum der die Bits benennt wird von Blueprint generiert

    Nachteile:

    • Ungewohnt für High-Level Programmierer (bit-wise zeug ist eher etwas womit sich low-level Programmierer auseinandersetzen, die Hardwarenahen Code schreiben), somit komplexer zu lesen und komplexer sich einzudenken.



    Das letzte Beispiel könnte falsch sein, habe in unsigned int gedacht, weiß nicht genau was im Hintergrund passiert bei UE5, aber sollte sich rausfinden lassen.


    Das hier wäre die sichere Variante bei der man gegen einen 0000000... usw. prüft

    Wie kyodai sagt, ergibt sich das aus der Logik (ich will einen Kegel, von daher benötige ich ja die Richtung des Kegels und den Winkel) und aus den Variablen Namen.

    Zu C++ in UE4:

    Epic hat bei C++ die Philosophie, dass Code der keine Kommentare braucht, auch keine Kommentare bekommt. Das hält natürlich den Dokumentationsaufwand auf niedrigen Niveau und sorgt für einen konsistenten Programmierstil.


    Es hilft nicht direkt, aber du kannst dir mal den Coding Standard anschauen.

    https://docs.unrealengine.com/…mentSetup/CodingStandard/


    Hier mal ein Beispiel für Code der sich selber Dokumentiert. Da Epic so programmiert, genügt es oft, wenn man einfach in die Definition der Funktion reinschaut. Natürlich gegeben, dass man z.B. die Mathematik versteht, bei Mathematik Funktionen.

    Code
    // Bad:
    t = s + l - b;
    
    // Good:
    TotalLeaves = SmallLeaves + LargeLeaves - SmallAndLargeLeaves;

    Ich habe das EQS für Deckung genutzt, dabei habe ich über spezielle Volumes die ich selber programmiert habe das Navmesh modifiziert, so dass die Fläche eine Deckungs Navigation class hat. Über ein EQS habe ich dann erreichbare Deckungspunkte in der Nähe gesucht und bewertet.


    Eine Sache die definitiv besser gelöst werden musste:

    Um zu prüfen ob eine Deckung auch Deckung gibt, um diese raus zu filtern habe ich in der Neuen Position über Traces geprüft, ob diese Position auch Deckung für den Gegnern gibt, welche die AI kennt. Es wäre wahrscheinlich besser irgendwie Deckungspunkte zu setzen (oder zu generieren) welche diese Information enthalten ("gibt Deckung für Beschuss aus Richtung X"). Das würde die Abfrage performanter machen (z.B. Winkelvergleich oder Skalarprodukt mit Einheitsrichtungsvektoren, anstelle von Traces).

    Das wichtige hierbei ist, dass Waffe Feuern eine Task ist, die Dauerhaft läuft, bis diese beendet ist (Succeeded) oder fehlschläft (Failed)

    Deine Feuern Task benötigt nur einen Tick, d.h. dein Subtree wird sofort abgebrochen.


    Die Lösung wäre eine Waffe Feuern Task, welche läuft, bis eine andere Aufgabe ansteht. Also eher eine "Zielen und Feuern wenn Gegner Anvisiert ist, bis ich nicht mehr kann oder will"-Task ist.

    Simple Parallel wäre schon ein weg das zu machen:

    Simple Parallel, hat ein Anbindung zu einer Task Node und eine zu eine zu einem sub tree.

    Ein wenig Hintergrund: Das Konzept der Behavior Tree's sieht keine Parallele ausführung von Tree's innerhalb eines Behavior Tree's vor. Von daher solltest du es dir gut überlegen, denn zu weichst von der Theorie der Behavior Trees ab.

    Meistens sehe ich das als Warnzeichen, dass du Behavior Tree's nutzt, aber deine Überlegungen zur Implementierung eigentlich kein Behavior Tree ist. (Habe den Fehler selbst schon am Anfang gemacht)


    Der Parallel Node ist von daher ein Node der eine Main Task Ausführt, und parallel dazu einen Sub Tree. Die Main Task ist die Primäre Task, die den Node Steuert. Wenn diese Task mit einem success oder failure beendet wird, so wird entsprechend des drüber liegenden Nodes (z.B. Sequence oder Select) weiter gemacht.


    Bei mir sah es wie folgt aus:


    In diesem Fall ist es so dass es einen Combat Node gab, in dem die Main Task das Feuern und Zielen übernimmt, und parallel dazu die Bewegung lief. Wenn diese Task fehlschlägt, so kommt man durch den Select in den nächsten Node eine Art Reload Task. Diese initiert das nachladen, und es findet parallel eine Bewegung in Deckung statt. Schlägt dies fehl (keine Munition, oder kein Nachladen nötig), so kommt man in die Verfolgen Task, etc. Bei einem Success sorgt der Select dafür dass der ganze Baum neugestartet wird. D.h. wenn der Combat Node sich erfolgreich beendet (z.B.) Ziel ist eliminiert, dann wird der Baum neugestartet ein neues Ziel gesucht und die möglichen Aktionen werden abgearbeitet.

    Was ich heutzutage anders machen würde ist, dass ich als Decorator keine Perception hatte. Dies ist aber viel zu Performancehungrid und ich hätte dies als Task machen sollen, die ausgeführt wird, wenn ein neues Ziel gesucht wird.


    ist aber bei weitem nicht perfekt und ich würde einiges anders machen.

    Mir ist am Ende auch aufgefallen, dass Gegner die sich bewegen und schießen, etwas zu schwer waren, gerade in Kombination mit einem sehr fiesen Recoil Modell.

    In diesem Fall musst du noch this innerhalb der eckigen Klammern schreiben, damit das this, mit in das Lambda reingenommen wird.

    Die eckigen Klammern, sind dazu da, um Variablen die außerhalb der Lambda funktion stehen, in das Lambda rein zu bringen.


    Edit: Wie Tomar sagt, gibt es bessere Sortier Algorithmen, aber meistens lohnt es sich erst so richtig, wenn man viele Daten sortieren muss. Was vielleicht wichtig ist, bevor du sortierst ist Filtern immer gut.

    Da ich glaube dass du dich gerade mit AI beschäftigst, wäre auch das EQS system einen Blick Wert, hier werden viele Operationen auch Asynchron ausgeführt, von daher hast du somit oft weniger Last auf den Main Thread.

    Wie immer ist aber alles immer eine Sache des Use-Case, sorg Erstmal dafür dass die Dinge funktionieren, du bist ja zur Zeit am Lernen. Von daher eins nach dem anderen.

    Edit: Ich bin doof und habe nicht bis nach unten gescrollt. Sorry




    Leider musst du folgendes machen:

    1. Du machst ein UMG Widget welches nur einen Button enthält (+ Styling, Text, etc.)

    Hat auch den Vorteil, dass du den Style für jeden Button innerhalb eines Widgets managen kannst.

    Natürlich solltest du alles parametrierbar machen, was parametrierbar sein soll.


    2. Du erstellst einen EventDispatcher innerhalb des Widgets mit entsprechender Call-Signature


    Jetzt kannst du deinen Button innerhalb deines anderen UMG Widgets nutzen und hast das neue Event verfügbar

    Es gäbe Möglichkeiten über das Animation Blueprint Events in Animationen zu erfassen. Es ist jedoch meiner Meinung nach ein schlechtes Design Gameplay relevante Dinge an Animationen zu koppeln, da nicht garantiert ist, dass diese abgespielt werden (z.B. dedicated Server)


    Ich würde auch eher Timer statt delays nutzen. Delays sind übersichtlich, aber mit Timern hast bildest du besser ab was unter der Haube passiert und hast somit ein besseres Verständnis was den Programflow angeht. Bei Timern hast du auch den Vorteil dass du entsprechende Events hast, wenn dieser endet.


    Was die RPCs angeht, ist ja dein Problem einfach. Du solltest den RPC im Grunde aufrufen, wenn der Angriff initiiert wird und nicht erst wenn getroffen wird.

    Dieses Plugin ist dein Freund wenn es darum geht: https://vreue4.com/


    Hier mal wie es bei mir aussieht.

    Externer Inhalt www.youtube.com
    Inhalte von externen Seiten werden ohne Ihre Zustimmung nicht automatisch geladen und angezeigt.
    Durch die Aktivierung der externen Inhalte erklären Sie sich damit einverstanden, dass personenbezogene Daten an Drittplattformen übermittelt werden. Mehr Informationen dazu haben wir in unserer Datenschutzerklärung zur Verfügung gestellt.

    Bei komplexen Dingen nutze ich jedoch definierte Posen (z.B. Waffengriff), bei anderen wo die Pose nicht klar ist, ist es kollisionsbasiert.

    Tomura Mit C++ hab ich eigentlich nix am Hut. In der Engine nutze ich nur die BluePrints. Das muss man doch beim Erstellen des Projektes festlegen, ob man mit BluePrints oder mit C++ arbeiten möchte. Richtig?

    Aber ich werde deinen Vorschlag mal ausprobieren. Vlt. klappt es ja.

    Ja, man muss oder eher kann es festlegen. Jedes UE4 Projekt kann aber auch nachher noch (mit etwas verständnis von der UE4 Projekt struktur) in ein C++ Projekt umgewandelt werden.


    Was bei dir passiert, ist dass bei der Konvertierung versucht wird C++ Codes zu kompilieren, welche in deinem Projekt sind.

    Die Fehlermeldung scheint ein Problem mit Funktionen aus dem HeadMountedDisplay Modul zu haben, was direkt etwas mit VR zu tun hat.

    Die Fehlermeldung scheint seinen Ursprung im Intermediate Ordner zu haben wo, objekte und generierte C++ Dateien gerne landen. Bei einem reinen Blueprint Projekt sollte da kaum was sein. Es scheint hier auch ein Binary Verzeichnis zu erstellen.


    Wenn es kein C++ Projekt wäre sollte eigentlich kein kompilieren notwending sein.


    Kannst du mal Screenshots von deinem Projektverzeichnis machen, dass man genau sieht was du hast?

    Du hast einen Fehler beim Kompilieren der C++ Codes.


    Falls es sich um ein C++ Projekt handelt würde mal vorschlagen, dass du den ganzen Intermediate Ordner löscht und die Visual Studio Projekt Dateien neu generierst:


    Das wird den Fehler nicht lösen, sorgt aber dafür, dass dort schon Mal alle Probleme eliminiert sind.

    Der Fehler selbst ist, dass sich hier einige Symbole nicht mehr auflösen lassen (hier Head Mounted Display also VR). Das kann sehr gut sein, denn UE4 ist deutlich Modularer geworden und viele Codes sind vom Engine Modul in eigene Module oder Plugins gewandert, dazu musst du vielleicht auch deine *.build.cs files etwas anpassen, dazu gehört auch das HMD zeugs.


    Edit: Anbei noch einmal eine Build.cs in der das HeadMountedDisplay Modul hinzugefügt wurde:

    Wenn dies der Fall ist lässt sich der Inhalt des Moduls über includes innerhalb deines eigenen Moduls nutzen.


    Du wirst aber falls du weitere C++ Codes hast, mehr Fehler bekommen, da sich zwischen UE4 releases gerne mal einiges ändert.


    Und bevor ich es vergesse:

    Immer eine Sicherheitskopie erstellen oder Source Control nutzen, denn ein Engine switch ist eine riesige Änderung bei der gut was kaputt gehen kann.

    Danke für eure Lösungsvorschläge. Um ein eigene Damage Logik zu schreiben müsste ich das in C++ umsetzen oder?

    Sollte auch in Blueprint gehen.

    Was genau willst du erreichen? Sagen wir mal du hast Static Meshes die irgendwie Rüstungsteile darstellen, die kaputt gehen können, würde ich z.B. anfangen einen Blueprint zu erstellen mit dem StaticMeshComponent als Parent, dieses Blueprint Programmierst du dann so, dass es Schaden nehmen kann und Leben hat.

    Diese Kannst du dann anstelle des Normalen StaticMeshComponent einsetzen, für Meshes die Schaden nehmen sollen.


    Genauso kannst du dann auch z.B. in Waffen eine entsprechende Logik erzeugen die beim Treffer (also Kollision, Trace, Overlap, oder wie auch immer du es gelöst hast) in dem Fall dass das getroffenene Component vom entsprechenden Typ ist, dann die "Schaden Nehmen" Funktion aufruft. Am besten schreibst du die Treffer logik auch so, dass auch nur die Komponenten getroffen werden können, bei denen es Sinn macht (also am besten einen eigenen Object oder Trace Channel und entsprechende Collision Settings)