Jump to content
Unity Insider Forum

All Activity

This stream auto-updates

  1. Yesterday
  2. Last week
  3. Eigentlich wollte ich nur einen kleinen Retro-Plattformer erstellen. Jetzt wo ich damit begonnen habe, stellte sich schnell heraus, das es wohl doch ein etwas umfangreicheres Projekt werden wird. Daher bekommt es auch einen eigenen Kanal. Ich werde das Projekt diesmal englischsprachig halten. Da bekomme ich schon hin. Bekanntlicherweise halten sich ja die Dialoge in einem Plattformer in Grenzen. Nichtsdestotrotz werde ich die Spielbeschreibungen erstmal deutsch halten. Dann habe ich immer die Möglichkeit mich evtl. doch nur für eine rein deutsche Version zu entscheiden. Name: The cunning Fox Story: In einem dichten Wald voller Magie und Kreaturen lebte ein listiger Fuchs namens Max. Eines Tages hörte Max ein Gerücht über ein monströses Ungeheuer, das im Wald sein Unwesen trieb und alles verschlang, was ihm in den Weg kam. Max beschloss, das Ungeheuer zu finden und zu besiegen, aber er erkannte schnell, dass es nur eine Illusion war, die von einem Zauberer des Waldes erschaffen wurde. Max schmiedete einen Plan, um den Zauberer zu überlisten und seine Schätze zu stehlen. Hier ein Screen vom ersten Level:
  4. Ja, die Zeit vergeht. Ich denke du hast, auch wenn Einiges deiner Ideen gestrichen wurde, immer noch genug Arbeit vor dir. Jedenfalls ist deine Projektübersicht noch nicht allzu vollständig wie ich das sehe. Das Kampfsystem schein aber schon fast fertig zu sein. Wirst du in Zukunft etwas mehr Zeit für dein Projekt finden?
  5. Oha, wow. Sascha, ich danke dir vielmals für diesen hilfreichen Ansatz. Das werde ich morgen mal in Angriff nehmen.
  6. Korrekt! Aber du kannst Typen eben nicht nur über generische Parameter kommunizieren, sondern auch über einen normalen System.Type-Parameter. Statt public void Foo<T>() machst du public void Foo(System.Type type) Du kannst generische Parameter auch problemlos mit typeof() zu einem System.Type-Objekt umwandeln. So als Beispiel: public bool Foo<T>(System.Type type) { return typeof(t) == type; } Das gibt dann true zurück, wenn man das so aufruft: Foo<Light>(typeof(Light)) // oder Foo<Light>(GetComponent<Light>().GetType()) Umgekehrt braucht man halt leider Reflection und sollte das vermeiden. Aber man kann oft von der anderen Seite ankommen, z.B. so: class MyGenericClass<T> { public System.Type GenericType => typeof(T); } Objekte dieser Klasse geben die mit der Property "GenericType" den Typ zurück, den du beim Erstellen für T übergeben hast: var thing = new MyGenericClass<Light>(); Debug.Log(thing.GenericType.Name); // "Light" Damit geht eine ganze Menge. Du könntest zum Beispiel beim Start alle deine Objekte laden und sie in ein Dictionary packen. Das Dictionary kann so aussehen: private Dictionary<System.Type, List<MyGenericClass<>> genericClassObjectsByGenericType = new Dictionary<...>(); Und dann lädst du da alle geladenen Objekte rein und sortierst sie nach ihrem generischen Typ: foreach (var loadedObject in allLoadedObjects) { var type = loadedObject.GenericType; List<MyGenericClass<>> list; if (genericClassObjectByType.TryGetValue(type, out list)) { list.Add(loadedObject); } else { list = new List<MyGenericClass<>>(); list.Add(loadedObject); genericClassObjectByType.Add(type, list); } } Und wenn du dann alle Objekte eines bestimmten generischen Type haben willst, holst du sie dir einfach aus dem Dictionary: public List<MyGenericClass<>> GetAllObjectsForType(System.Type type) { return genericClassObjectsByType[type]; } Da fehlt noch die Ausnahmebehandlung, falls der Typ nicht gefunden wird. Und ich hab das jetzt so runtergetippt, kann sein dass die Syntax mit <> nicht ganz richtig verwendet wird. Aber ich hoffe, die Idee ist einigermaßen klar. Ja schon, aber eine Datenstruktur wachsen zu lassen ist besserer Stil als ein switch-case, das ständig erweitert werden muss. Vor allem, weil du bei einem Dictionary einfach mit der Schleife über alle Typen gehen kannst (siehe oben), anstatt deinen switch-case-Code jedes Mal anzufassen.
  7. Wäre das nicht das selbe nur in Grün? Ich müsste ja auch dort den Datentypen angeben.
  8. Der Parameter gibt den Typ des Datanbankobjekts an, also z.B. Item, Skill, Slot, usw.. Und davon wird dann jeweils die Liste an verfügbaren Objekten geladen und sobald ich eines aus der Liste anklicke, wird der entsprechende Inspector aufgerufen. Nur muss dafür ja der genaue Type bekannt sein, sonst bekomme ich die Daten ja nicht? Die Datenbank ist so aufgebaut, das im Db Objekt die verschiedenen Datenbanken liegen, also z.B. ItemDatabase, SettingDatabase, usw.. Und darin sind dann mehrere Listen. Im Fall ItemDatabase wären das Item, Slot, Currency. Die einzelnen Listen generieren zum einen einen neuen Menüpunkt und sobald man einen davon klickt, wird die entsprechende Liste geladen und angezeigt, sowie der erste Eintrag der Liste geladen. Wenn man nun in der Liste einen anderen eintrag wählt, wird halt dieser geladen, usw. Ps: Das Bild nicht auf die Goldwaage legen, das ist alles noch nicht fertig. 😄
  9. Hmm... müssen tut man das nie. Ich bin mir gerade nicht ganz sicher, aber mir fällt gerade kein Fall ein, wo man etwas mit einer generischen Methode machen kann, was man nicht auch mit einem System-Type-Parameter machen könnte. Außer Boxing bei Structs vermeiden... Was machst du denn am Ende mit deinem generischen Typparameter, dass du nicht auch ein System.Type-Objekt dafür nehmen kannst? Und so nebenbei: switch-case kannst du üblicherweise mit einem Dictionary vermeiden. Sollte man machen, wenn das switch-case potentiell immer weiter wachsen würde.
  10. Hey Sascha, danke dir für deine Antwort. Ja, habe schon befürchtet, bzw. gelesen und gesehen, dass es so nicht funktioniert, wie ich es mir wünsche. Aber hatte die Hoffnung, dass wenn ich direkt jemanden danach Frage, derjenige evtl. doch einen Tipp, oder Idee hat. Schade, dann werde ich wohl doch mit switches arbeiten müssen. Wäre anders ja auch zu einfach gewesen^^
  11. Moin! Ich bin gerade nicht so ganz fit, daher verstehe ich die Frage vielleicht nur so halb. Aber wenn ich die Hälfte immerhin richtig verstehe, dann geht es darum, ein System.Type zu haben und diesen als generischen Parameter zu nehmen. Das ist nicht vorgesehen, da Generics ein "statisches" Konzept ist, das zur Compile Time feststeht und dadurch mehr Sicherheit gibt als dynamisches Umherwerfen von Typen zur Laufzeit. Der korrekte Weg ist daher eine Überladung der Methode mit einem normalen Parameter vom Typ System.Type. Macht GetComponent auch so (wobei "Unity macht das auch so" natürlich kein starkes Argument ist ). Alternativ geht das theoretisch auch mit Reflection. Man kann da aber den generischen Typ (typeof(DatabaseCodeWindow)) nehmen und den mit MakeGenericType() inflaten (also generische Parameter setzen). Diesen Typ kannst du dann in den Activator stecken, um eine Instanz zu erzeugen. Reflection ist aber nur in einigen wenigen Sonderfällen eine gute Idee, z.B. wenn dein Code mit anderem Code funktionieren soll, den du jetzt noch nicht kennst, z.B. weil du ein Paket schreibst, das andere dann weiter verwenden. Wenn das nicht der Fall ist, solltest du Reflection eher meiden.
  12. Hey Leute, ich würde gerne wissen, ob es möglich ist einen Parameter einer generischen Klassen dynamisch zu erstellen? Als Beispiel: internal class DatabaseEditorSubWindow<DB> : IDatabase where DB : ScriptableObject {} //Wird aufgerufen, wenn ein bestimmter Button geklickt wird. Der Typ der Datenbank wird mitgegeben Hier wird nun geschaut, welche Daten die Datenbank enthält. Dies können z.B. Listen verschiedenen Typs sein. Diese werden geladen und deren Inhalte in eine Variable gepackt. Anschließend werden Buttons generiert, mit denen die Einzelnen Inhalte geladen werden sollen. Dies geschieht dann über eine neue Klasse. internal class DatabaseCoreWindow<DB, TType> : IDatabase where DB : ScriptableObject where TType ScriptableObject, IName {} // Zeigt die Daten an, erstellt neue Inhalte, usw.. Der Aufruf der Klasse sieht folgendermaßen aus m_Child = new DatabaseCoreWindow<DB, (Type)toolbarTypes[i]>(m_Database, toolbarList); // Hier soll TType dynamisch gesetzt und durch den Typ der Liste ersetzt werden, z.B. Item, oder Skill Ich habe dazu verschiedenste Ansätze versucht, aber nichts hat funktioniert. Entweder es kommen Fehler, oder es werden die mitgegebenen Parameter nicht gesetzt. Hat evtl. jemand eine Idee, wie ich das umsetzten kann? Vielen Dank für die Hilfe. LG
  13. Danke malzbie, habe ich auch mal ausprobiert, sehr interessant hat mir neue Kenntnisse gebacht! Ich habe meiner Rakete im ersten Versuch erstmal mal nur eine Drehung um den Forward-Vector gegeben. Wahrscheinlich muss ich den Radius noch abhängig von der Enfernung zum Ziel machen und dann mal sehen wie das aussieht. Aber danke für die Tipps!
  14. An dieser Aussage hat sich für mich jetzt leider nicht wirklich was geändert...
  15. Hallo @malzbie Du hattest Recht. Ich habe nun ein Delay von 0.1 Sec zwischen Destroy und Neuladen eingebaut und es funktioniert! Absolut super. Danke! Christoph
  16. Dein Problem könnte sein, dass ein Destroy nicht sofort passiert, sondern erst nach ablauf des Update-Loops. Du zerstörst also den letzten Level, welcher aber bis zum Ende des Loops noch da ist, instanzierst einen neuen Level, hast jetzt also 2 Level gleichzeitig drin, suchst nach deinen Punkten, die jetzt 2 Mal drin sind, und findest die alten, schon gefundenen, Punkte. Jetzt erst wird der alte Level aus der Szene raus genommen und deine gefundenen Punkte sind weg. Was dir helfen könnte, wäre ein DestroyImmediate. Das wird aber ausdrücklich nicht empfohlen! https://docs.unity3d.com/ScriptReference/Object.DestroyImmediate.html Suchen die Punkte doch einfach mal im LateUpdate und guck ob dann alles in Ordnung ist.
  17. Moin, Ich vermute, dass es an der von dir genannten Ursache liegt. Wie verhindere ich das jetzt? Oder gibt es noch eine bessere Möglichkeit als einen Collider?
  18. Hallo EndPoint hat nix dergleichen und SpawnPoint ist nicht einmal ein script. Christoph
  19. Moin! Doch, das kann es schon sein. Objekte sind nicht von sich aus undurchlässig. Auch Collidern ist es egal, wenn sie ineinander stecken. Es muss immer irgendein Stück Code, z.B. der der Rigidbody-Komponente, ankommen und Kollisionschecks machen, damit Objekte nicht ineinander landen. Wenn du in deinem anderen Projekt einen Rigidbody benutzt, diesen mit der Tastatur bewegst und er in eine Wand gerät, dann stellt er eine Kollision fest und bewegt sich wieder heraus. Hältst du die Taste in Richtung der Wand weiter gedrückt, dann passiert im nächsten Frame noch einmal dasselbe: Geht in die Wand hinein, stellt Kollision fest, geht wieder heraus. Diesen Zwischenstand in der Wand drin sieht man auch nicht, weil das alles zwischen zweimal rendern passiert. Ich vermute jetzt: In deinem neuen Projekt bewegst du dich aber nicht jeden Frame ein Stück weiter (und dann im Kollisionsfall wieder ein bisschen zurück), sondern "teleportierst" dein Objekt in jedem Frame zur Mausposition. Vielleicht ist das nicht der Fall, oder vielleicht doch aber es ist nicht das Problem - aber der Punkt ist, dass in diesem Fall das Verhalten ganz anders sein kann, weil dein Rigidbody vielleicht tief in der Wand drinsteckt und nicht weiß, wo er hin soll. Rigidbodys haben die Angewohnheit, dann irgendwo™ hinzugehen, und deshalb gibt es so etwas. Jetzt, wo das gesagt ist... ich weiß überhaupt nicht, was bei dir genau schief läuft. Gibt ja leider keine Info außer des implizierten "Geht nicht". Vielleicht ist der Absatz mit dem Rigidbody aber ja schon genug.
  20. Hi zusammen, ich hab mal wieder eine Frage: Ich habe ein 2d gameobject, erstmal einfach nur ein Quadrat, welches ich mit der Maus bewegen kann. Jetzt möchte ich eine Art Feldbegrenzung am Bildschirmrand machen. Das habe ich in einem anderen Spiel schonmal mit einem leeren Gameobject und einem Collider gemacht, da hat das auch funktioniert. Der einzige unterschied zwischen den beiden projekten ist meiner meinung nach, dass ich das jameobject jetzt mit der Maus steuere und in dem andren Projekt mit der Tastatue, aber das macht doch eigentlich keinen Unterschied oder? Der collider an dem 2d object funktioniert mit anderen gameobjects, aber halt nicht an dem am dem leeren gameobjekt.
  21. Moin! Dafür, dass das gar nicht so einfach passieren dürfte, habe ich dieses Problem, dass beim zweiten Mal Laden etwas anders ist als beim ersten Mal, schon echt oft gesehen. Hast du irgendwo DontDestroyOnLoad drin, z.B. in deinem Spawn- und Endpunkt-Scripts oder so?
  22. Hallo Ich hänge seit Tagen über einem komischen Problem. Habe mittlerweile einen totalen Knoten im Kopf. Eventuell sieht ja jemand von euch, was hier schief läuft. In meiner Szene liegt ein Ball. Un die Klasse LevelLoader ist dafür zuständig, Level zu laden. Diese sind einfache Tiled-Karten. Aus Testzwecken habe ich drei Karten erstellt und diese kann ich genau so laden, wie ich es mir wünsche. Dazu nutze ich folgende Funktion: public void LoadLevel(int levelIndex) { if (_currentLevel) { Destroy(_currentLevel); _currentLevel = null; } if (_currentEndPoint) { Destroy(_currentEndPoint); _currentEndPoint = null; } _ball.Stop(); _currentLevel = Instantiate(_levels[levelIndex]); var spawnPoint = GameObject.Find("SpawnPoint"); if (spawnPoint == null) { Log.WriteDebug("cant find spawn point", name); } else { _ball.transform.Translate(spawnPoint.transform.position); } var polygonCollider2D= _currentLevel.GetComponentInChildren<PolygonCollider2D>(); if (polygonCollider2D) { polygonCollider2D.gameObject.AddComponent<LevelBorder>(); } else { Log.WriteDebug("cant find polygon collider on current level", name); } var endPointFromTiled = GameObject.Find("EndPoint"); if (endPointFromTiled == null) { Log.WriteDebug("cant find end point", name); } else { _currentEndPoint = Instantiate(_endPoint); _currentEndPoint.transform.position = endPointFromTiled.transform.position; } } Wie bereits geschrieben, funktioniert das wunderbar. Wenn ich nun aber aus dem Spiel heraus ein neues Level laden möchte (weil der Spieler den Endpunkt erreicht hat), werden SpwanPoint und EndPoint falsch gesetzt (bzw. gar nicht neu gesetzt). Das Laden des nächsten Levels ist über folgende Funktion gelöst: public void StartNextLevel() { _currentLevelIndex++; if (_currentLevelIndex < _levels.Count) { LoadLevel(_currentLevelIndex); } } Ich habe absolut null Ahung, warum beim Wechsel des Levels der Ball nicht am SpawnPoint liegt und warum auch der EndPoint nicht neu gesetzt wird. Hat jemand von euch eine Idee? Welche Informationen brauch ihr noch von mir? Danke Christoph
  23. Oje, wie ist die Zeit vergangen... Nachdem das Projekt eingeschlafen ist, habe ich nun endlich wieder Zeit gefunden mich aktiver darum zu kümmern. Ich habe einiges an refactoring betrieben. Einige arbeiten komplett gestrichen und allgemein den Umfang was ich im ersten Schritt umsetzten will reduziert. Dazu habe ich Mal einen Plan für einen vertical slice als Orientierung gemacht.
  24. Mal ein anderer Ansatz. Du nutzt ja LookAt für die Ausrichtung. Was spricht dagegen, dass du dem "Ziel", zu dem sich die Rakete ja ausrichten soll, einfach einen Offset hinzufügst. Je weiter weg vom Ziel, desto größer kann der Offset sein. Dafür könntest du dann den Sinus nutzen. Also: Position des Ziels abfragen, Entfernung zum Ziel ermitteln, dann dieser Position auf der X-Achse, Y-Achse oder beiden über den Sinus einen Offset hinzufügen. Der Ausschlag des Offsets, ist abhängig von der Entfernung und wird immer kleiner, je näher du kommst. Die nun neu berechnete Position nutzt du als LookAt Target. Somit würde die Rakete zu Beginn recht stark schlingern und sich beim näher kommen immer mehr fokusieren. Sollte die Rakete an dem Ziel vorbei fliegen, würde sie sich weiterhin in Richtung Ziel (+Offset) ausrichten. Ich habe in meinem Spiel Cavatus ja auch zielsuchende Raketen drin, die eine gewisse Trägheit haben. Sie richten sich dem Ziel aus, aber eben nicht augenblicklich, sondern langsam. Dafür wird ständig ein Zwischenziel berechnet. Da könntest du ansetzen und den Sinus mit einbauen. Den Codeschnipsel kann ich dir ja mal anhängen: // das alles findet in der FixedUpdate statt myRigid.AddRelativeForce(0, 0, force * Time.deltaTime); // es wird dauernd in eigene Z-Richtung beschleunigt if (Player != null ) { Vector3 targetDirection = Player.transform.position - transform.position; // hier berechne ich die Richtung wo es hin gehen soll float step = rotationSpeed * Time.deltaTime; // hier berechne ich einen Winkel, den sich die Rakete pro Sekunde drehen darf Vector3 newDirection = Vector3.RotateTowards(transform.forward, targetDirection, step, 0.0f); // hier berechne ich ein neues Ziel, das ein Stück in Richtung des echten Ziels zeigt transform.rotation = Quaternion.LookRotation(newDirection); // in diese neue Richtung drehe ich mich nun } Ach so: Meine Raketen haben während des Fluges ( solange sie Treibstoff haben) die Gravity ausgeschaltet und einen recht hohen Drag, der die Rakete nicht unendlich beschleunigen lässt. Ja, ich nutze auch in der FixedUpdate die DeltaTime. Somit sind die Werte, die ich einstelle, immer pro Sekunde.
  25. Earlier
  26. Vielen Dank für die Antworten! Ich glaube ich muss diese Lookat-Funktion und translate etc. erst mal richtig verstehen. Die Sinus-Funktion habe ich getestet, die funktioniert auch insofern, dass sie die Rakete schon hinundher bewegt. Es sieht aber eher aus das sie Rutsch bzw trifftet, sie müsste aber einlenken also der Sinuskurve eher folgen. Und den Trägheitsansatz habe ich auch mal getestet. Die Rakete fliegt, aber wenn sich das Target bewegt und die Rakete vorbei fliegt folgt sie dann nicht mehr. Aber Sacha du hattest ja geschrieben, dass ich das mit der Geschwindikeit regeln müsste und das klappt noch nicht. Ich glaube ich mache erst mal einfachere Tests. Wenn ihr spontan noch ideen habt gebt mir bescheid, ansonsten melde ich mich bals wieder.
  1. Load more activity

Announcements

Hy, wir programmieren für dich Apps(Android & iOS):

Weiterleitung zum Entwickler "daubit"



×
×
  • Create New...