Jump to content
Unity Insider Forum

John

Members
  • Gesamte Inhalte

    418
  • Benutzer seit

  • Letzter Besuch

  • Tagessiege

    15

John hat zuletzt am 8. November 2017 gewonnen

John hat die beliebtesten Inhalte erstellt!

Über John

  • Geburtstag 4. August

Contact Methods

  • AIM
    https://www.patreon.com/JohnMau

Profile Information

  • Gender
    Not Telling
  • Location
    Hamburg
  • Interests
    Technical Artist

Letzte Besucher des Profils

29.814 Profilaufrufe

John's Achievements

Advanced Member

Advanced Member (3/3)

85

Ansehen in der Community

  1. Was ist mit dem Horror Genre los? Fast jedes neue Horror-Game hat nen Waffe wo man sich durch Horden von Gegner durchschiessen muss :D ? Oder habe ich nur das Gefühl das es so ist? 

  2. Graphic-Weekend Environment-White-Blocking Ich hatte am Wochenende eine Idee jeweils für ein Level gehabt ein Beach/Steg (White-Blocking) (White-Blocking integriert in die Scene um die Max-Borders zu überprüfen, sprich wen man ein Größeren Screen hat sieht man nur mehr vom Level) Unit Captain Pirate Update Ich hatte angefangen jeweils zu überlegen ob ich bei jeder Unit Augen hinzufüge und mehr Detail rein packe soll. Hier ist ein zwischen Ergebnis (Augen, Arme, Augenbrauen hinzugefügt) Trap Prototypen Ebenfalls hatte ich begonnen die ersten Prototypen für Traps zu designen und zu blocken die man als Player jeweils auf seine Felder setzten kann. Summary: Dieses Wochenende hab ich mehr Graphic's gemacht. Aber habe auch eine sehr interessante Möglichkeit gefunden sein Game sehr Pluggable zu machen, sodass man sein Spiel mit jeweiligen Features zusammen stöpselt kann. Dazu nächste Woche mehr Mfg. -John
  3. Die Camera richtig einzustellen ist echt schwierig. :D Was haltet ihr davon? 

    (Das Lila Feld ist jeweils die Spielfläche wo der Spieler seine Traps drauflegen kann)

    https://www.imagebanana.com/s/883/p8oAr4nP.html

    1 / 2 / 3 oder gar keines?

    zum Topic )

    1. Mauri

      Mauri

      3 - das hat so'n bisschen was Pinball-mäßiges.

    2. Felix K.

      Felix K.

      Wäre auch für die 3. Grund: Da kommt das Tiefengefühl besser zur Geltung.

    3. Damon93

      Damon93

      Bin für das erste haha Bei dem 3 hab ich nach längerem hinsehn gedacht ich hab nen Sehfehler weil die rechte kante höher geht als die linke^^

  4. Heyo , ich brauche euer Feedback zu meiner Game-Idee von Pirate with Huge Hats. Gerne: Tower Defense Du als Spieler musst deinen Rum gegen bösartige Piraten verteidigen. Die Piraten kommen jeweils auf 5 Lines in Wellen um dein Rum zu stehlen. Du als Spieler hast die Möglichkeit sie mit deinen Fallen daran zu hindern bevor Sie zu deinen Rum-Vorrat kommen. Falls es jedoch ein Pirate schafft zu deinen Rum-Vorrat vorzukommen trägt er jeweils diese Rum-fass zurück zu seiner Base falls er auf dem Weg zurück sterben sollte lässt er das Rum-fass an der Stelle fallen wo er gestorben ist und so besteht aber noch die Gefahr das andere Piraten das Rum-fass von dem Ort aufheben und zurück tragen. Falls ein Rum-fass die Grenze Überschreitet ist das Spiel vorbei und du hast verloren, aber wen du alle Wave durchhaltest ohne das ein Rum-fass gestohlen wird hast du gewonnen. Würde mich über Feedback/Bedenken oder Anregungen sehr freuen Mfg. -John
  5. Heyo am Wochenende bin ich auf eine spannende Architecture gestoßen, die ein Spiel gut Configurable macht. Wo ich mit Hilfe dieses Flow später gut neuen Content hinzustöpseln kann oder Änderungen ohne Probleme vornehmen kann. Der Ansatz für ein solches System liegt im "ScriptableObject". Man mag es gar nicht auf den ersten Blick glauben, aber dieses kleine Schmuckstück erleichtert dir in vielen Hinsichten das Dev.-Leben und auch das abändern von Value geht super schnell von der Hand. Doch wie sieht das ganze aus? Auf den ersten Blick mag diese Grafik unverständlich sein bzw. man kann sich schwer was darunter Vorstellen deshalb gehen wir kurz durch jedes einzelne Element durch und klären deren Funktion und anschließend wen wir wieder zurück zu dieser Grafik springen wird sie einem hoffentlich besser schlüssig. Fangen wir mit einer Generellen Frage an: Was sind Models und Components Models Sind reine Daten-holder sie halten in unserem Beispiel eine Card die von uns Definiert wird: CardModel: - Type <- Eine definition welche Type es ist (Block/Trigger) - Cost <- Die kosten um diese Card zu erzeugen... - BattleObjectStates <- Wichtige Informationen über das Object (Health/Damage) - DisplayContent <- Ein Bundle an Display Information (Rendermesh/Name) - Rarity <- Ebenfalls eine defintion welche Rarity diese Card hat (Common, Rare, Epic, Legendary) - Ability <- Eine Ability die wir jeweils der Karte geben können (z.B. Verlangsamt Units um 15%) - ObjectBehaviours <- Hier können wir jeweils defenieren was die Card alles kann (Shooten, Movement...) Components Sind jeweils Elemente die für uns Information halten oder auch Behaviour. Was genau bedeutet das? Greifen wir wieder das Card Beispiel auf wir besitzen im CardModel eine Field das nennt sich ObjectBehaviours. Dieses Field in eine Collection von Behaviours die wir der Card hinzufügen können. Ein ObjectBehaviour ist eine Component wo wir definieren können wie sich jeweils diese Card verhält. Wen diese Karte ein bestimmtes Behaviour haben sollte erstellen wir dieses Behaviour unabhängig von dieser Card damit wir ggf. dieses Behaviour wo anderes wiederverwenden können. Im Grund das gleiche Component Prinzip von Unity, nur ein wenig spezifizierter. Ein Beispiel wäre dafür ein Shoot-Behaviour das in einem Regelmäßigen Intervall ein Schuss in eine Richtung abgibt. //... public class ObjectBehaviour : ScriptableObject, IObjectBehaviour { public virtual void Execute() { //... } } //... //... public class ShootDirectionComponent : ObjectBehaviour { IFloatComponent Time { get { return _time; } } VectorDirection Direction { get { return _direction; } } [SerializeField] FloatComponent _time; [SerializeField] VectorDirection _direction; public override void Execute() { base.Invoke(); //... Here we invoke the default Behaviour that for example track actions or log something //... execute ShootDirection Behaviour } //... Die Idee ist dabei diese Shoot-Behaviour (ObjectBehviour) später auch einer Unit zu verweisen ohne direkt für die Unit nochmal das Ganze zu schreiben. Die Frage ist inwiefern dieses Konstrukt einem Hilft in Verbindung mit ScriptableObject sein Game gut Configurable zu machen. Die Lösung sitzt direkt in dem ScriptableObject-Prinzip mit diesem ich in der Lage bin mir jeweils verschiedene Variationen anzulegen und diese jeweils weiter zu verwenden. Let's jump in Code: Wir haben jeweils hier das ICardModel-Interface dieses wir an die Außenwelt geben womit andere Klassen damit arbeiten können. namespace Pwhh.Game.Model { public interface ICardModel : IModel { IConstantIntComponent Cost { get; } //... } } Hier haben wir jeweils die Base Implementation wir extends jeweils vom ScriptableObject und von dem ICardModel. Wichtig: Da Unity nicht mit Interface-Typen (Getter und Setter) im Inspector umgehen kann benutzen wir jeweils hier einen Trick. Wir nutzen die Base Implementation von unserem gewünschten Objekt und setzen dieses jeweils auf private damit wir keinen Zugriff von außen drauf haben und weisen das jeweils dem Getter von dem Interface zu. Zu guter Letzt setzten wir über das private Field das [SerializeField]-Attribute das uns erlaubt im Editor diese Value zu verändern. namespace Pwhh.Game.Model { [CreateAssetMenu(menuName = "Models/Player/Card", fileName = "Card")] public class CardModel : ScriptableObject, ICardModel { public IConstantIntComponent Cost { get { return _cost; } } [SerializeField] private ConstantIntComponent _cost; } } Hier sehen wir noch ein Use-Case wo wir jeweils das ICardModel verwenden. Das gute ist das wir nur mit dem jeweiligen Interface arbeiten können ohne eine direkte Verbindung zum Objekt. So ist es im Grunde egal welche Version wir verwenden es bleibt immer gleich, aufgrund des interfaces. namespace Assets.Source.Serivces.CardLoader { public class CardLoader : IService { public void Load(ICardModel[] cards) { SetupUICard(cards); //... } //... ( Unity Training | Unity Docu ) Advantages Technics Da ScriptableObjects im Inspector angezeigt werden und wir mit dem SerializeField einfach im Editor values abändern oder hinzufügen können ist das schon mal ein großer Vorteil. Um diesen Vorteil noch zu vergrößern und die Benutzbarkeit des System so einfach wie möglich zu gestalten kann man auf [CustomeEditor(typeof(type)] zurückgreifen. Wie sieht das ganze aus? Dazu möchte ich gerne ein Situation aufgreifen womit ich am Wochenende konfrontiert wurde. Ich hatte jeweils anhand dieses design-flow den wir oberhalb besprochen haben mein GameSession aufgebaut. Diese beinhaltet alle relevanten Daten die wichtig sind für meine Levels. Das bedeutet wen ich ein neues Level hinzufügen möchte erstelle ich ein GameSession Object (ScriptableObject) und konfiguriere jeweils meine Level. Für uns ist jetzt speziell der Wave Aspekt interessant anzuschauen. Hier ist ein Überblick über den Aufbaue meines GameSession Objektes: Zu Erklärung was Wave sind: Ein Level besteht aus eine Anzahl aus Unit Wave die kommen und du musst jeweils diese Waves bekämpfen. Eine Wave hat jeweils eine bestimmte Zeit und jeweils Angaben zu welcher Zeit welche Units in dieser Wave spawnen. IWaveModel - Time <- Ist jeweils dauerer diese Wave bis die nächste Wave kommt - UnitSpawnComponent <- Beinhaltet welche Unit und zu welcher Zeit diese gespawnt wird So jetzt stellt sich nur folgende Frage wen ich jeweils in meiner Liste mehrere Wave Objecte reingezogen habe habe ich leider kein Überblick darüber wie lang die ganze GameSession dauert. Hier kommt unser CustomeEditor zum Einsatz mit dem ich jeweils die ganze Liste durchgehen kann und mir die GameSession Zeit zusammenrechnen kann. [CustomEditor(typeof(GameSessionModel))] public class GameSessionModelEditor : Editor { private int _totalGameSessionTime; private void OnEnable() { _totalGameSessionTime = GetTotalWaveTime(); } public override void OnInspectorGUI() { base.OnInspectorGUI(); EditorGUILayout.LabelField("Total Game-Wave Time:" + _totalGameSessionTime); } //... ( Unity Training | Unity Docu ) Mit dieser Hilfe kann mit sich ein sehr gutes Inspector-UI-System zusammenbasteln das einen schnell Information darüber gibt was alles abgeht ohne sich durch die ganzen ScriptableObjects zu clicken und sich diese heraus zu suchen. Ebenfalls ein Überlegung für später ist ggf. sich die Units per Icon in der GameSession anzeigen zu lassen um ein schnellen Überblick zu bekommen welche Units in dieser Session verwendet werden. Ich hoffe ihr habt ein Einblick bekommen in den Flow mit ScriptableObject und vll. für euch ein Use-Case entdeckt. Mfg. -John
  6. Hi, ein anderer cooler Trick für Szenenübergreifende Variablen ist das Verwenden von Scriptable-Objects. Dadurch vermeidet man jeweils das benutzen von Static-Variablen und erhöht jeweils die Flexibilität: Hier wird jeweils das ScriptableObject angelegt: //... [CreateAssetMenu] public class FloatVariable : ScriptableObject { public float Value; } Der Vorteil ist im Project kann man mehr ScriptableObjects angelegen und die beliebig wo man sie benötigt reinlegen oder switchen: Hier ist jeweils der benötigte Wert mit dem man weiter arbeitet. Der kann vom Inspector gesetzt werden. using UnityEngine; public class UnitAttackBehaviour : MonoBehaviour { [SerializeField] private FloatVariable _damage; //... } Mfg. -John
  7. Hallo Leute, mir ist schon oftmals passiert oder habe es in anderen Projekten mitbekommen. Das ein wichtiges Thema von Projekten nicht beachtet wird oder definiert wird ohne den tieferen Sinn zu hinterfragen. Es handelt sich hierbei um die Folder-Struktur in Unity Projekten. Der ein oder andere definiert eine wohlgeformte Definition seiner Folder-Struktur und in Laufe der Zeit wird Sie immer unaufgeräumter da man sich am Anfang nicht ausreichend Gedanken gemacht hat was man alles benötigt. So kommt es schnell dazu das man Dateien dort hinterlegt wo sie eigentlich nicht hinkommen und schnell gereht die ganze Folder-Struktur aus dem Ruder. Ich möchte in diesem Guide euch meine Folder-Struktur erklären die sich aus dem laufenden von Projekten her raus entwickelt hat und eine gute Verbindung zu Unity bietet. Fangen wir mit dem Grundlegendsten an das wir uns stellen müssen: Was habe ich alles für Daten? Dies ist ein bisschen schwierig auf dem ersten Anhieb zu beantworten, deshalb deferieren wir wohlgeformte Kategorien die uns dabei helfen unsere Dateien besser einzuordnen. Das wären zu einem folgende: Assets/Files/... Assets/UnityAssets/... Assets/Source/... Files In diesem Folder finden alle unsere importierten Dateien die wir von außen holen Platz. Das wären zum einen z.B. .../Files/Meshes/... .../Files/Textures/... .../Files/Audios/... .../Files/Fonts/... .../Files/BalancingSheets/... .../Files/.. Unter Files finden wirklich alle Dateien Platz die von anderen Applikation kommen und keine Verbindung zu Unity haben. Direkt unter dem Files-Folder folgen weiter Folders die jeweils eine Unter-Kategorie bilden die mit einer Mehrzahl angeben wird. (z.b. Meshes, Textures, BalancingSheets) Ich möchte hier nochmal rauspicken es finden auch solche Dateien Platz wie zum Beispiel Xml/Json die eigentlich nur Balancing-Daten beinhalten. Man Trifft schnell auch auf Edge-Cases wie zum Beispiel: Ich besitze jeweils folgende Dateien wo soll ich die am besten hin packen? CaptainPirate.fbx CaptainPirate@idle.fbx CaptainPirate@attack.fbx CaptainPirate@die.fbx CaptainPirate@walk.fbx CaptainPirate@hit.fbx ... Im Grunde ist es wie folgt CaptainPirate.fbx ist jeweils ein reines mesh ohne jegliche Animationen. Die anderen jeweiligen Files sind welche die im besten Fall nur die reine Animation Information besitzen ohne tatsächlich das ganze Mesh. Man könnte jeweils dafür eine eigene Kategorie anlegen unter Files die das Ganze für einen handelt das würde dann wie folgt aussehen: Hier sind alle CaptainPirates@*.fbx hinterlegt: Assets/Files/Animations/Characters/CaptainPirate/... Die Tatsächlich Mesh-Datei ist unter folgen Folder platziert: Assets/Files/Meshes/Characters/CaptainPirate/CaptainPirate.fbx So hat man jeweils eine saubere Definition was jeweils animierte Meshes sind und welche Statische. Ein anderer Ansatz der durch aus möglich wäre und den ich sogar bevorzuge ist jeweils den Unterschied direkt unter dem Meshes-Folder zu machen das würde dann wie folgt aussehen: Dynamic Meshes die jeweils eine Animation besitzen: .../Meshes/Dynamic/... Rein Statische .../Meshes/Static/... Wieso ich hier vermeide jeweils Animations zu benutzen zum einen wen man in Unity Animationen angelegt ist es jeweils schwer diese zu differenzieren in welchen Folder man diese packen soll. (dazu aber mehr im UnityAssets Abschnitt) Wie wir bereits gesehen habe legen wir unter Files direkt die Meshes Kategorien und versuchen Sie jeweils nach Sinn zu Bündeln so ist das später navigieren im Project-Window viel leichter, wenn wir gebündelte Daten haben. In den Files Ordner werden alle Files hinterlegt die außerhalb von Unity kommen, dazu wird jeweils eine Unterkategorie erstellt wo die Files dort in ihrem eigenen Folder Platz finden. UnityAssets Die UnityAssets-Kategorie beinhaltet alle Unity-Assets Files (*ausgenommen .cs-, .js-, .boo oder shader-files). Das wären z.B. wie folgt: Assets/UnityAssets/Materials/... Assets/UnityAssets/Prefabs/... Assets/UnityAssets/Animations/... (Animations die mithilfe von Unity erstellt worden sind!) Assets/UnityAssets/Skyboxs/... Assets/UnityAssets/PhyicalMaterials/... Assets/UnityAssets/Scenes/... Assets/UnityAssets/ScriptableObjects/... Assets/UnityAssets/AnimatorController/... Assets/UnityAssets/... Tatsächlich wird hier alles Unity-Basierendes liegen und hat durchaus hier seine Berechnung. Hier gilt genau das gleich wie bereits in dem Files-Folder, jeweils Unter-Kategorien zu bilden und für die jeweilige Datei einen Folder anlegen. Wir greifen das Example von vorhin vom CaptainPirate auf: Assets/UnityAssets/Materials/Characters/CaptainPirate.mat Assets/UnityAssets/Prefabs/Characters/CaptainPirate.prefab Assets/UnityAssets/AnimatorController/Characters/BasicCharacterController.controller Wie man hier sieht alles ist jeweils in seinem Sub-Folder. Ich hatte jetzt bewusst für das CaptainPirate.prefab kein weitern Folder angelegt, da man diese beruhigt in dem Characters Folder verweilen kann. Eine gute Überlegung wäre falls man mehrere Typen von Characters hat diese zu unterteilen wie z.B. Assets/UnityAssets/Prefabs/Characters/BossCharacters/CaptainPirate.prefab Assets/UnityAssets/Prefabs/Characters/RegularCharacters/Pirate.prefab Man sollte hier abwägen ab welchen grad es Sinn macht einen weitere Unter-Kategorie einzuführen. In dem falle mit verschieden Character-Typen sehe ich durch aus einen Sinn hinter diese jeweils in einzelne Folder aufzuteilen. Der UnityAssets Folder beinhaltet alle Dateien die von Unity direkt kommen ausgenommen die jeweiligen Source-Files (.cs-, .js-, .boo oder shader-files). Source Der Source-Folder ist der Ort an dem man seine ganzen Source/Script-Files hinterlegen kann. Hier schaut man auch das man jeweils unterscheidet von System, Game, Assets/Source/Game/Shop/Provider/ShopItemProvider.cs Assets/Source/System/Crashlytics/Log/CrashLogProvider.cs Assets/Source/System/HttpRest/Get/Interface/IGet.cs Im Grunde gelten hier genau die gleichen Regeln wie bereits oben schon genannt. Versuchen jeweils alles Zusammenzufassen eine Kategorie dafür zu finden und falls es geht diese nochmal in einer Unter-Kategorie aufzuteilen um die Übersicht und das Verständnis zu verbessern. Ein Wichtiger Teil der Oftmals zu kurz kommt sind "Namespaces" mit Hilfe einer Sauber Folder-Struktur ergibt sich das von ganz allein: Assets/Source/System/Crashlytics/Log/CrashLogProvider.cs using Pwhh.System.Log namespace Pwhh.System.Crashlytics.Log { public class CrashLogProvider : Log { //... } } Conclusion Ein sauber Folder-Struktur ist sehr wichtig, das oftmals unterschätz wird, egal ob in einem kleinen Projekt oder einem großen. Man sollte sich Gedanken darübermachen wie man was benennt und wo man es hinterlegt. Mit Kategorien wird es einem erleichtert seine jeweilige Datei an den passenden Ort zu hinterlegen. Files/.. & UnityAssets/... & Source/... ist ein guter Anfang seine Kategorien darauf aufzubauen. Wichtige Hinweise Unity-Folders Unity hat jeweils schon Vordefinierte Folder-Names die Funktionalitäten bieten: Assets Editor Resources Editor Default Resources Gizmos Plug-ins Standard Assets StreamingAssets Hidden Assets Wichtig ist ihr jeweils sich über den genauen nutzen dieses Folder bewusst zu werden und in jeweils dort immer verwenden welchen Content er beinhaltet. Ein Beispiel wäre es wen man ein Tool für Unity schreibt und dort gerne Custom Icons verwenden möchte die man von außerhalb reinimportiert. Das würde folgendermaßen gelöst: Assets/Files/Icons/Editor Default Resources/TranslationIcon.png Unity bietet durch aus die Möglichkeit mehrere dieser Folders in seinem Projekt zu haben. Dieses sollte man dazu nutzen auch dort Ordnung reinzubekommen. Externe Tools/Packages Oftmals ist es so, dass man externe Package verwendet diese nicht die gleiche Folder-Struktur aufweist. Hier fällt mir nur eine Lösung ein. Es hängt jeweils von der Größe des externen Packages ab, wenn es ein kleines kann man dieses per Hand selbst einpflegen und in die entsprechenden Folder reinlegen. Falls es ein durchaus größeres ist wäre es vll. die Überlegung eine weitere Kategorie zu Files, UnityAssets und Source hinzufügen. Das wäre dann ein Third-Party Folder wo alle diejenigen Packages hinterlegt werden die größer sind und wo es schwierig ist diese jeweils in die Folder-Struktur zu integrieren. Hier besonders Aufpassen das kann der Indikator dafür sein das seine Folder-Struktur schnell mal aus den Ruder läuft! Schnelle Navigation und Zugriff Unity bietet die Funktion jeweils jedes Objekt im Project-Windows mit Labels zu versehen, durch aus mehreren. Dies bietet die Möglichkeit neben der Searchbar direkt nach den Labels zu filtern und man bekommt eine Overview von allen Objekten die dieses Label besitzen. ( Unity Docs , Assets Workflow Labels Docu ) Oftmals habe ich gehört und gesehen das viele Leute gerne ein Folder Erstellen von zum Beispiel einem Character wo sie direkt im Unter-Folder den Sub-Folder: Material Texture Prefabs Scripts Dies Struktur kann sehr schnell unsauber werden vor allem wen sich andere Objekte das gleich Material benutzen das in diesem einen Folder verwendet wird. Deshalb würde ich dieses sehr bewusst vermeiden, alles in einen Folder rein zu verfrachten. Ich hoffe ihr habt einen guten Überblick bekommen wie wichtig eine gute Folder-Struktur. Falls ihr Anregungen habt oder euer Erfahrungen teilen möchtet mit euer Folder Struktur. Fühlt euch freie ebenfalls ist Feedback sehr erwünscht. Mfg. -John
  8. Heyo, ich hatte heute eine coole Sache gelernt was Game-Design/UX angeht. "Weniger ist oftmals besser!" Es kommt schnell vor das man sehr viel in sein Game reinbauen möchte wie z.b. ich mit meinen Traps und Spells. Ich hatte mir gedacht das es echt cool wäre noch gleichzeitig zu den Fallen Spells zu haben die natürlich eine eigene InGame Ressourcen braucht die sich Aufladet... und schnell kommt man dazu viel mehr logic bzw. Funktionen einzubauen. Um das alles zu kommunizieren und dem Spieler zu verstehen zu geben was etwas ist ist sehr schwer und wirkt schnell negativ auf den User. Der User fühlt sich dabei schnell überfordert, zwar macht es für uns Entwickler alles Sinn da wir es entwickeln und uns öfters damit beschäftigen, aber für außenstehende person ist das genau das Gegeteil. Aus diesen Grund habe ich mein Game-Design nochmal überdacht und angepasst. Die Spells wurden komplett rausgenommen und die Resourcen die man collecten muss auch. Der Spieler geniert die Coins (unterhalb die blaue bar) über Zeit mit der er dann die traps (von seinem Deck) setzten kann die sich in seinen Slots befinden. Oberhalb sieht der Player jeweils wann die nächste Wave kommt und bei welcher er aktuell ist (zurzeit mit mockup hüten gekennzeichnet "rechts") Auf der linken Seite sieht er jeweils sein Leben (zurzeit mockups heart graphics) falls ein Gegner es durch schafft zieht es im ein Leben ab. In der Version ist das UI auch nicht mehr so noise und gut lesbar auf dem ersten Blick. Oftmals ist weniger mehr!
  9. Heyo, in der letzten Zeit bin ich ein Stück weit vorangekommen. Eine kleine Zusammenfassung: Art: Ich hatte bereits letzten anfangen das Main-Menu zu design. Hier ist der Zwischenstand der sich aus mehreren Versionen ergeben hat. Main Battle-Menu: Game-Session kann man hier starten Seine Quests kann man hier jeweils aufrufen Schatztruhen die man aus Game-Session bekommt kann man hier öffnen und den Process verfolgen Ebenfalls kann man hier die Daily-Pirate Chest öffnen Man hat unterandern Obenhalb des Screens seine Resource in einem überblick (Left: Exp/Level, Middle: Gold, Right: Beads) Unterhalb des Screens hat man eine Navigations-bar mit der man in andere Kategorien wechseln kann wie z.B. Shop, Deck Programming: Ich arbeite zurzeit an einer Grundbasic von Core-System die als fundamental für das Front- und Backend dient. Cache-Package Das Cache-Package collected jeweils bestimmte Typen und du kannst dir jeweils egal wo du sie grade brauchst holen und damit arbeiten. Ein Beispiel wäre man möchte alle Units in einem Cache speichern dazu legt man wie folgt eine neue Klasse erbt jeweils von der abstract-base-class Cache<T> und gibt jeweils einen Typen an dem man in diesem Cache speichern möchte. Das könnten GameObjects, Scriptable-Objects, Scripts, ints... egal wovon du grade eine Collection benötigst. Dan kann man den jeweiligen Cache im CacheContainer hinterlegen und später sich wieder holen oder den jeweiligen gewünschten cache removen. //... Custome Class that extends form abstract Cach<T> Class public class UnitCache : Cache<UnitComponent> { //... you custome filter methode } //... Also, you have a Cache Container that store all Cache so you can easily inject this class over Dependency Injection and you have access to all Caches or inject your specific Cache //... Add _cacheContainer.Add<UnitCache, UnitComponent>(_unitCache); //... Get UnitCache unitCache = _cacheContainer.Get<UnitCache, UnitComponent>(); //... Remove _cacheContainer.Remove<UnitCache, UnitComponent>(_unitCache); //... Game-Architecture: Ich habe schon bereits ein paar Projekte davor mir eine gut Scalable Feature Archtiecture überlegt die man für Spiele einsetzten kann. Die ebenfalls die Dependency zu Unity auf das geringste minimiert. Model Das Model ist ein Ort zum Speichern von Daten (nur speichern / keine Modifizierung oder Konvertierung) Was du setzt ist was du bekommst! Wen sich was ändert das Benachrichtig. View / Display: View-Display ist nur für die Grafische Darstellung auf dem Screen oder Output vom Spieler z.B. Audio (Keine Modifizierungen oder Input Lister) View / Input: View Input ist hört auf den Input vom User und Push anschließend direkt das Event. (Keine Modifizierungen oder graphische Darstellung) Command: Command reagiert auf events von der View oder von einem Model Change und führt jeweils danach die Services aus (Keine Modifizierungen, Services-Logic oder Änderungen auf das Model) Service: Ein Service führt jeweils eine bestimmte Modifizierungen auf Models durch. Es kann auf Model zugreifen und diese jeweils verändern. (Falls man Größere Prozesse hat kann man die ruhig in kleiner services unterteilen) Signal-Package Das Signal Package ist ein Event-System. Man kann sich jeweils auf bestimmte Ereignisse Subscriben oder unsubscriben. Man kann jeweils Signale auch mit Guards versehen, dass heißt fals dieses Signal published wird der jeweilige Guard vorher ausgeführt und falls er fehl schlägt wird das Signal jeweils nicht gefeuert und die subscribe bekommen jeweils nichts mit. //... public class PlayerLevelUpSignal : Signal<int> { } //.. //... SignalConfig.cs _playerLevelUpSignal.Add(_resourceBarVfxHandler.LevelUp) .Add(_postProcessVfxHandler.LevelUp); //... //...PlayerResourceHandler.cs private void AddExp(int amount) { _expAmount += amount; if(IsPlayerLevelUp()) { _playerLevelUpSignal.Signal(_playerLvl) } } //... //...ResourceBarVfxHandler.cs public void LevelUp(int level) { //play LevelUp-Vfx } //... ASP.Net Backend Ich habe mich dazu entschieden das Backend jeweils auf ASP.Net Core aufzubauen. Ich hatte bereits die ersten kleinen Tests damit gemacht und bin positiv davon überrascht. Ebenfalls finde ich den Fakt sehr gut das ich meine Front- und Backend jeweils in C# schreiben kann was mir die Arbeit erleichtert und ich manche Prozesse nicht neue schreiben muss in einer anderen Sprache, sondern das bereits vorhanden wiederverwenden kann. ToDo Current Task: Die Registrierung von einem User und Login mit einer Public/Private-Key-Verschlüsselung.
  10. #4. Das Zwiebel-Prinzip / News-Paper Before: //... private void OnGUI() { EditorGUILayout.HelpBox(_infoMessage, MessageType.Info); _displayAdvancedSettings = EditorGUILayout.Foldout(_displayAdvancedSettings, "Advance Settings:"); if (_displayAdvancedSettings) { _customizeXValue = EditorGUILayout.IntField(_customizeXValue, "Important Value"); _selectedColor = EditorGUILayout.ColorField("Selected Color", _selectedColor); _customizeYValue = EditorGUILayout.FloatField(_customizeYValue, "Important Value"); } if (GUILayout.Button("Execute... Something")) { ExecuteCalculation(); } GUILayout.Label("Process Time" + _processTime); } //... After: //... private void OnGUI() { DrawHelperHeader(); DrawSettingsActivator(); if (_displayAdvancedSettings) { DrawAdvancedSettings(); } DrawExecuteArea(); } private void DrawHelperHeader() { EditorGUILayout.HelpBox(_infoMessage, MessageType.Info); } private void DrawSettingsActivator() { _displayAdvancedSettings = EditorGUILayout.Foldout(_displayAdvancedSettings, "Advance Settings:"); } private void DrawAdvancedSettings() { _customizeXValue = EditorGUILayout.IntField(_customizeXValue, "Important Value"); _selectedColor = EditorGUILayout.ColorField("Selected Color", _selectedColor); _customizeYValue = EditorGUILayout.FloatField(_customizeYValue, "Important Value"); } private void DrawExecuteArea() { if (GUILayout.Button("Execute... Something")) { ExecuteCalculation(); } GUILayout.Label("Process Time" + _processTime); } //... Detail-Erklärung: Der Aufbau einer Kasse und deren Struktur ist ein wichtiger Bestandteil der oftmals in Vergessenheit gerat. Eine geordnete und saubere Klasse ist das fundamental für einen guten Lesefluss und Verständnis für eine Klasse. Doch wie sieht eine saubere Struktur eigentlich aus? Ein seltsames Phänomen das mir immer über den Weg läuft ist das öfters in der "OnGUI", "Update", "Start"... MonoBehaviour-Klassen auftreten. Dort wird oftmals alles was man möchtet in diese Methode reingeschrieben. Hier ist es von enormen Vorteil dieses in unterschiedliche Methoden zu Verlagen, da falls man Änderungen vornehmen will sich nicht erst durch den gewaltigen GUI-Code durchkämpfen muss, sondern man schaut sich jeweils einmal die OnGUI-Klasse an wo jeweils das UI-Behaviour beschrieben wird und springt dann anschließend in die jeweilige Methode in der man eine Änderung vornehmen will. Man erspart sich Zeit und erleichtert das Lesen und das Verständnis von Methoden. Diese Concept kann man auch jeweils für seinen Klassen-Aufbau anwenden. //... public class ... { //Public-Variablen //...(static, const,...) //Private-Variablen //...(static, const,...) //Constructor //...(init, setup) //Public-Methoden //...(static, override, virtual,...) //Private-Methoden //...(static, override, virtual,...) //Destructor //...(cleanup, destory) } //... Oftmals wird auch der Vergleich vom Zeitung-Lesen genommen. Das man jeweils seine Header hat darunter folgen die Header2 danach folgt Letzt endlich der Artikel-Content. So kann man schon vorab abklären was einem gefällt bzw. was man sucht und dann zu dem jeweiligen Artikel-Content schneller navigieren ohne von Anfang sich durch jeden Artikel Content zu kämpfen bis man zu seiner passenden Stelle trifft. Tipp: Ich verfolge stets das Ziel den Leser meiner Klasse Oberhalb meiner Klasse darüber Aufzuklären was in dieser Klasse passiert und unterhalb findet er jeweils die tatsächliche Implementierung. //... // Top public bool IsObjectInRange(object obj) { return _playerRange < GetPlayerDistanceFromTarget(obj); } // Bottom private float GetPlayerDistanceFromTarget(object obj) { return Vector3.Distance(_playerObj, obj); } //... Eine weitere Faustregel die man verfolgen kann in Public-Methoden beschreibe ich nur jeweils das Ausgeführte Behaviour das ich tatsächlich in den Private-Methoden ausführe. So bekomme ich schneller einen Überblick und Verständnis der Klasse. Wen ich von außen in diese Klasse springe und falls mich wirklich die Implementierung interessiert spring ich jeweils dann ein schritt tiefer zu den Private-Methoden.
  11. #3. Simple Namings: Keine Type Beschreibung oder Falsch Information Before: //... private GameObject[] _unitList; //... private List<int> _scores; //... private GameObject _player01; //... private GameObject _player02; //... After: //... private GameObject[] _units; //... private List<int> _lastScoreCollection; //... private PlayerContainer _playerContainer; //... Detail-Erklärung: Ein richtiger Variablen-Name zu finden kann oftmals eine riesige Hürde sein und dann passiert es sehr schnell das man seinen Variablen-Name verkompliziert. Doch wie kann man dagegen angehen und welchen Trick gibt es für eine schnellere Findung von Variablen-Namen. Oftmals hilft es sogar wie im Falle von den units statt "-List" hinschreibt könnte man das ganze einfach sauber gestellten in dem man den Namen unit in die Mehrzahl verwandet units damit hat man schon Information geben das es sich hier nicht um eine einzelne Unit, sondern um eine Ansammlung von Units handelt. In dem Falle von scores ist es zwar jetzt nicht aussagekräftig nur ein "-s" dran zu hängen. Hier kann man auch auf Hilf Wörter zurückgreifen die das ganze mehr unterstreichen. Wie zum Beispiel "-Collection" das gibt darüber Auskunft das die letzte Score Zusammen Collected werden und man mit Hilfe dieser die letzte Score sich holen kann. Wichtiger Hinweis: Sicherlich wird sich jetzt der ein oder andere fragen: Was jetzt ein Unterschied macht wen ich „List“ hinschreibe anstatt von einer Collection? Es könnte schnell dazu führen das andere Leser oder man selbst verwirrt ist. Falls man jeweils mit dieser variable arbeitet und diese aus besonderen Gründen von einer List<int> in ein Array von int’s ändern müsste könnte der Leser erwarten, dass er eine Liste bekommt. Darum ist es ideal wen man einen Namen nimmt der jetzt nicht auf einen Typen zurückschließt lässt. Container ist ebenfalls ein oftmals Benutzer Name den einen öfters über den Weg läuft. Falls man zum Beispiel mehrere Spieler besitzt und diese gerne zusammenfassen will kann man dafür sogar eine extra Klasse anfertigen die diese hält und verwaltet. Ich benutze gerne den Namen Container wen ich in dieser Klasse eine Ansammlung von verschieden Typen habe. Bei einer Collection erwarte ich jeweils eine Ansammlung exakt nur vom einem Typen und nichts Anderes. Im Gegenteil zu einem Container wo ich alles reinpacken kann und diese dort auf eine bestimmte Zeit lagern kann. Tipp: Oftmals sollte man variablen nicht verkomplizieren nur, weil sie cool klingen oder glatt unnötige Information liefern wie z.B. den Typen den in den Namen reinschreiben das ist total überflüssig da fast jede der IDE bereits diesen Job übernehmen und dadurch nur mehr Verwirrung gestiftet werden kann.
  12. @Zer0Cool Du hast völlig recht das wäre der Ideal Fall. Die jeweilige evaluierung in die Klassen (bzw. Model ) selbst zu verfrachten Super punkt nochmal den du mit dem Example hervorgebracht hast.
  13. #2. Extract If-Condition in Methods Before: //... private void OnRequestJoinSession() { if (_playerData.HasEquipKey && _playerData.EquipKeyId == _sessionData.RequiredKeyId && _playerData.Level > _sessionData.RequiredPlayerLevel) { //JoinSession... } } //... After: //... private void OnRequestJoinSession() { if (IsPlayerValid()) { //JoinSession... } } private bool IsPlayerValid() { return _playerData.HasEquipKey && _playerData.EquipKeyId == _sessionData.RequiredKeyId && _playerData.Level > _sessionData.RequiredPlayerLevel; } //... Detail-Erklärung: Ich habe schon oft erlebt das man meistens eine If-Condition hat die ein wenig mehr Logik erfordert. Oftmals schleichen sich hier schnell Fehler ein oder das lesen dieser Condition ist meistens mühselig. Für diese Fälle ist es praktisch diese in eine separate Methode zu verlagern um einerseits den Lesefluss zu erleichtern und auf der anderen Seite Fehler vorzubeugen. Falls man in seiner Klasse zum Beispiel öfters diese Condition überprüfen muss lohnt es sich auf jedenfalls diese in eine separate Methode zu Verlagen. Tipp: Ich benutze die Faustregel oft falls ich eine Condition habe die mehr als ein State überprüft verlagere ich diese in eine andere Methode. Oftmals macht es auch Sinn selbst bei eine Condition diese in einer separaten Methode zu verlagern z.B. bei größeren Überprüfungen. Es erleichtert das ab ändern später falls sich die Condition ändert und verbessert den Lesefluss des source-codes Zusatz-Beispiel: //... private bool IsNumberNotZero() { return int.Parse(_uiText.text) != 0; } //...
  14. @Zer0Cool & LifeIsGood ihr hab völlig recht in fall das man von dem gleichen Object eine Collection besitzt sollte man auf ein Array zurückgreifen und das jeweils mit eine SerializeField versehen wird. Aber ich gibt auch fälle wo man keine Collection jeweils davon besitzen möchte Aber der beste Hinweis ist wie @Zer0Cool gesagt hat "item01, item02, item3..." wen das auftaucht sollte man darüber nachdenken eine Collection zu machen. Ich habe nochmal ein Beispiel erstellt um es besser zu erklären //... [SerializeField] private int _goldAmount; [SerializeField] private int _diamontAmount; [SerializeField] private int _rubyAmount; [SerializeField] private int _perlAmount; //... var amounts = new int[] {_goldAmount, _diamontAmount, _rubyAmount, _perlAmount}; foreach (var amount in amounts) { if(amount < 0) { signal.Publish<NegativAmountEvent>(); } } //... In der Klasse selbst hat man jeweils noch andere Provider oder Behaviours die direkt mit den Variable z.b. individuell arbeiten.
×
×
  • Neu erstellen...