Jump to content
Unity Insider Forum

Leaderboard


Popular Content

Showing content with the highest reputation since 03/23/2019 in all areas

  1. 2 points
    Moinsen! Ich habe mich vorhin nach langer Zeit dann doch dazu aufgerafft, mir einen Blog aufzusetzen, in dem ich einfach mal beliebige Themen behandeln kann... geht aber schon noch um Unity Ich habe heute Nacht zwei Artikelchen geschrieben, und mit der Zeit sollen es mehr werden. Die beiden sind soweit auf englisch. Deutsche Übersetzungen sind nicht ausgeschlossen, aber im Moment nicht sonderlich weit oben auf der Liste. Wer Lust hat, kann ja mal reinschauen. http://blog.13pixels.de/
  2. 2 points
    Sehr gute Idee! Coroutinen funktioneren so: Du hast eine Methode, die ganz normal abläuft mit dem kleinen Unterschied, dass du eine Anweisung zur Hand hast, mit der du eine beliebige Anzahl an Frames warten kannst. Du kannst damit also dasselbe machen wie mit Update, aber eben als zusammenhängender Ablauf. Wenn du z.B. diesen Code hast: private int frameCounter = 0; private void Update() { if (frameCounter < 60) { frameCounter++; if (frameCounter == 60) { Debug.Log("Ich habe 60 Frames abgewartet."); } } } dann hast du genau die Probleme, die du bereits angemerkt hast. Deine Zählervariable ist irgendwo abseits des Codes, der damit arbeitet, und darüber hinaus läuft da eine Update-Methode, die die meiste Zeit über gar nichts sinnvolles macht. So sieht das Äquivalent in einer Coroutine aus: private IEnumerator CountToSixtyFrames() { for (var frameCounter = 0; frameCounter < 60; frameCounter++) { yield return null; // warte einen Frame } Debug.Log("Ich habe 60 Frames abgewartet."); } Es gibt hier nur zwei Dinge zu bemerken: Die Methode hat als Rückgabetyp "IEnumerator". Mit "yield return null" wird die Methode unterbrochen und im nächsten Schritt des Update-Zyklus an dieser stelle fortgesetzt. Statt null kann man auch andere Dinge zurück geben, Z.B. ein WaitForSeconds-Objekt, dann wird nicht einen Frame lang gewartet, sondern so viele Frames, dass die angegebene Zeit in Sekunden verstrichen ist. Gestartet wird eine Coroutine so: StartCoroutine(CountToSixtyFrames()); In deinem Fall kannst du also etwa so etwas machen: private IEnumerator AnimateRotationSpeed(float targetValue, float duration) { var startValue = rotationSpeed; for (var time = 0f; time < duration; time += Time.deltaTime) { var progress = time / duration; rotationSpeed = Mathf.Lerp(startValue, targetValue, progress); yield return null; } rotationSpeed = targetValue; } und dann in Start aufrufen: private void Start() { StartCoroutine(AnimateRotationSpeed(targetRotationSpeed, 1f)); } Kann man natürlich auch noch etwas modularer machen, statt fest rotationSpeed zu beeinflussen einfach einen ref-Parameter oder ein Callback anbieten und schon hat man sein eigenes Tweening-System
  3. 2 points
  4. 2 points
    Naja, wenn's um Kollisionen geht, dann ist das mit Layern schon sinnvoll. Irgendwo muss man seine Grenze ziehen. Natürlich könnte man jetzt versuchen, alles über Layer zu machen. Theoretisch gibt's performancemäßige Vorteilchen, wenn du Trigger, in denen nur der Player etwas auslösen soll, auch nur mit dem Player kollidieren lässt. Aber irgendwann wird's auch einfach albern und unübersichtlich. Bei Performance gilt immer: Mach dich erst krumm, wenn's zum Problem wird. Ich bevorzuge es, wenn ich eine einfache Änderung vornehme, wenn ich eine einfache Änderung haben will. Wenn ich ein neues System baue und dann doch Trigger sein sollen, wo Player und Gegner drin etwas auslösen, will ich nicht erstmal die gesamte Kollisionsmatrix überarbeiten. Physik-Engines haben sowieso Beschleunigungsstrukturen und Optimierungen des Todes - da geht dem Rechner erst sehr spät die Puste aus. Da sind sind Bedenken wie "Ich will einen Raycast haben der durch Dinge durch kann ohne dafür mein Erstgeborenes zu verkaufen" schon viel besser. Also... lange Rede, kurzer Sinn: Guter Code und sauberes Projekt > Performance, solange bis die Performance ein Problem wird. Und Layer managen ist nicht so ganz geil, also würd ich's vermeiden, solange dein Code nicht drunter leidet, wie er's beim Raycast tun würde.
  5. 1 point
    Ich glaube, hier liegt ein grundlegendes Missverständnis vor. In deinem Video sehe ich, dass Dinge sich von oben nach unten in einer geraden Linie bewegen, sonst passiert nichts. Das ist exakt eine Zeile Code, mit festem Start- und Zielpunkt macht das etwa drei, plus Klammern. Ein Script für eine einzelne Aufgabe, z.B. "von oben nach unten bewegen" schreibst du genau ein Mal. Und dann benutzt du das für alle deine Objekte. Du kannst ja dasselbe Script auf beliebig viele Objekte ziehen. Und wenn die Objekte alle etwas unterschiedlich sind (z.B. unterschiedliche Geschwindigkeit), dann baust du ein Feld in dein Script ein, mit dem du das einstellen kannst. Es gibt nirgendwo mehr Kontrolle als beim Scripten. Für Animationen gibt es natürlich sehr wohl einen Haufen hervorragender Anwengunszwecke - ich würde sagen, das hier ist keiner davon.
  6. 1 point
    Die Frage verstehe ich nicht so ganz, aber ich nehme an, die Antwort "du kannst alles in deine Liste reinstecken was dich glücklich macht" wird schon passen Richtig. Kannste erstmal machen. Ein struct hast du, wenn du statt "class" "struct" schreibst. Der Unterschied ist die fehlende Referenzsemantik. Bei Objekten einer Klasse redest du von Dingen mit einer Identität (z.B. deinen Vater). Zwei Objekte können gleich sein (z.B. sein Zwilling) und trotzdem nicht dasselbe. Und du redest von deinem Vater, aber deine Mutter von ihrem Mann - zwei reden mit unterschiedlichen Bezeichnungen von derselben Person. structs sind dagegen für Werte. Eine 5 ist immer eine 5, und wenn zwei Leute von einer 5 reden, reden sie von demselben Wert. Einen Unterschied zwischen "das gleiche" und "dasselbe" bei Werten. Wenn du deine 5 erhöhst hast du keine 5 mehr, sondern eine 6. Referenzsemantik spiegelt das wieder. Hast du eine Klasse "Auto", sieht das so aus: var meinAuto = new Auto(); var dasAuto = meinAuto; meinAuto.farbe = blau; Debug.Log(dasAuto.farbe); // blau In diesem Beispiel gibt es exakt ein Auto-Objekt, und zwei Variablen, die dieses Objekt referenzieren. Hast du allerdings ein Struct, wie z.B. Vector3: var meinePosition = new Vector3(1, 2, 3); var diePosition = meinePosition; diePosition.x = 3; Debug.Log(meinePosition); // (1, 2, 3) Hier wird bei der Zuweisung in der zweiten Zeile keine Referenz kopiert, sondern der Wert selbst. Deshalb steht in den beiden Variablen zwar derselbe Wert, aber es gibt keine Verbindung, wie ein gemeinsam referenziertes Objekt. Wenn du dann den Wert der einen Variable änderst, dann hast du eben einen neuen Wert, und änderst nicht den alten. So wie 5+1 nicht "erhöhte 5" ergibt, sondern eben 6.
  7. 1 point
    Ein Konstruktor ist eine Methode ohne Rückgabetyp in der Signatur, die genauso heißt wie der Typ selbst: public struct Person { public string name; public int age; public Person(string name, int age) { this.name = name; this.age = age; } } Wenn du dann eine neue Person erstellst, kannst du das über diesen Konstruktor tun: new Person("Klaus", 42) Wie jede andere Methode auch kann der Konstruktor kann beliebige Dinge tun, er ist nicht darauf beschränkt, Feldern Parameterwerte zuzuweisen. Bei structs ist allerdings Regel, dass ein Konstruktor jedes Feld initialisieren muss. Es gibt halt noch die andere Schreibweise, mit der du Werte von structs oder Objekten direkt initialisierst: new Person { name = "Klaus", age = 42 } Bemerke die geschweiften anstatt der normalen Klammern. Ganz alternativ kannst du natürlich auch alles in einzelne Zeilen schreiben, ob das so hübsch ist, sei jedem selbst überlassen. var person = new Person(); person.name = "Klaus"; person.age = 42; Am Ende hast du jedenfalls immer eine Person, die du dann mit Add in die Liste hinzufügen kannst.
  8. 1 point
    Ich merke gerade, dass du Sachen über Index in eine Collection packst, die eventuell gar nicht so groß ist. Wenn du in ein Array laden würdest, müsstest du das hier machen: ThemesCount = PlayerPrefs.GetInt("Themes"); saveTheme = new SaveTheme[ThemesCount]; Da du aber deine Daten in eine Liste packst, solltest du nicht über Index hinzufügen, sondern einfach mit Add. Statt saveThemeList[i].themeName = PlayerPrefs.GetString("theme_name" + i); saveThemeList[i].themeIndex = PlayerPrefs.GetInt("theme_index" + i); also var themeName = PlayerPrefs.GetString("theme_name" + i); var themeIndex = PlayerPrefs.GetInt("theme_index" + i); saveThemeList.Add(new SaveTheme(themeName, themeIndex)); Diese Schreibweise benötigt natürlich einen Konstruktor im SaveTheme-Typ.
  9. 1 point
    Du liest doch die Itemnamen aus, um sie anzuzeigen. Schieb sie einfach alle in eine Liste rein. Wenn etwas in der Liste drin ist, egal wieviele Einträge, wird das oberste Objekt für eine gewisse Zeit angezeigt und dann aus der liste gelöscht. Ist in der Liste immer noch etwas drin, wird das neue Oberste Objekt angezeigt und gelöscht. Solange, bis die Liste leer ist, also keine Einträge mehr hat. Hier alles über eine Liste: https://docs.microsoft.com/de-de/dotnet/api/system.collections.generic.list-1?view=netframework-4.7.2
  10. 1 point
    Du speicherst die werte unter "theme_name"+i lädst dann aber nur "theme_name" /edit Und 'Themes Count' war oben im screenshot 0, wenn du das noch nicht geändert hast..
  11. 1 point
    Hallo @Sascha und @Jomnitech, danke für die Tipps. Ich denke mir, dass der Tipp von Jomnitech auch funktioniert, nur schmirt mit in letzter Zeit bei einer "while" Schleife immer mein Unity (Version 2018.3.0f2) ab. Das hatte ich schon an anderer Stelle gehabt. Sascha: beim Aufrufen der Coroutine meinte Visual Studio, dass eine Klammer am Ende fehlt: StartCoroutine(AnimateRotationSpeed(backRotSpeed, slowRotDuration)); Die habe ich nachgetragen. Zudem hat Visual Studio bei "time += Time.deltaTime" gemeckert, weil der Typ "float" nicht in "int" konvertiert werden kann. Somit habe ich diese Zeile wie folgt abgeändert: for (var time = 0.0f; time < duration; time += Time.deltaTime) Der Unterschied ist im vordersten "time = 0.0f". Klappt wunderbar. Ich spare mir einige Variablen am Anfang (ist somit übersichtlicher), und die "Zeitschleife" ist aus der FixedUpdate Methode raus. Sollte der Code noch erweitert werden bleibt das somit übersichtlicher. Danke. So, und mit "Mathf.Lerp" muss ich mich nun dringend doch einmal auseinander setzen.
  12. 1 point
    habs jetzt nicht getestet, aber ich meine du kannst auch einfach math.lerp verwenden: public class myScript : MonoBehaviour { public float speed = 1f; float backRotSpeed = 0.3f; float realBackRotSpeed = 0.0f; private void FixedUpdate() { while (realBackRotSpeed < backRotSpeed) { realBackRotSpeed = Mathf.Lerp(realBackRotSpeed, backRotSpeed, speed * Time.deltaTime); } } }
  13. 1 point
    Das ist keine gute Lösung. Das Material-Array eines Renderers ist nicht zum Einlagern von irgendwelchen Materials, sondern erfüllt einen bestimmten Zweck. Was in jedem Fall funktionieren sollte: using UnityEngine; public class ChangeMaterial : MonoBehaviour { public Material[] materials; void Start() { GetComponent<Renderer>().sharedMaterial = materials[1]; } } Achte auch darauf, dass du deine eigenen Klassen nicht nach Typen benennst, die es schon gibt - "Material" zum Beispiel solltest du deine Komponente nicht nennen.
  14. 1 point
    Sorry habe das Problem grade gelöst. Das funktioniert: using UnityEngine; public class Material : MonoBehaviour { public Material[] materials; void Start() { GetComponent<MeshRenderer>().material = GetComponent<MeshRenderer>().materials[1]; } }
  15. 1 point
    Ein GetKeyDown ist das lomplette Update über aktiv und kann so nicht ausgewertet werden. An sich ist deine If-Else Abfrage schon soweit ok. Ob du da aber else If nutzt oder nur ganz viele If hintereinander oder eben über Switch die Auswertung machst, ist in deinem Codefall egal. Aber trotzdem nochmal zu den Basics. Eine If Abfrage ist eine boolsche Überprüfung ob eine gewisse Bedingung wahr ist. Diese Bedingung kann aus mehreren Teilen bestehen. Also in deinem Fall fragst du ja z.B. ganz oben ab, ob du im Menü bist. Ist das Ergebnis true, wird alles innerhalb dieser If-Abfrage ausgeführt. Wenn nicht, wird der kompeltte Block übersprungen . Würdest du jetzt noch ein Else nutzen, könntest du im Falle, dass das Ergebnis false ist, etwas anderes ausführen. Also auf deutsch: Wenn der Zustand wie gewünscht ist, dann mache etwas, wenn er nicht so ist, dann mache etwas anderes. Ein else if ist eine weitere Abfrage, bei der du wieder eine Bedingung einfügst. Also wenn die Grundbedingung nicht erfüllt ist, dann mache etwas anderes, aber nur wenn die weitere Bedingung auch erfüllt ist. In deinem Falle geht es um die menüPosition. Wenn in Position 0 dann mache etwas, wenn nicht dann mache etwas anderes, aber nur wenn ich in Position 1 eins bin. Das kann man zwar mit else if abbilden, aber in Endeffekt kann man ja nur auf einer Position sein, deswegen brauchst du kein else und könntest einfach nur lauter ifs nutzen. Aber wie gesagt, dein Problem sind nicht die else ifs sondern der Zustand der Taste, der komplett bis zum nächsten Frame erhalten bleibt. Du hast jetzt 2 Möglichkeiten. Entweder dein Submenü (und der Tastaturbefehl da drin) wird im Code vor dem Hauptmenü abgefragt, was zwangsläufig erst im nächsten Frame ausgewertet werden kann, nachdem weiter unten ja erst erkannt wird, dass du im Menü bist, oder aber du baust eine Variable für die Taste ein, die den Code erkennen lässt, dass es immer noch der Erste Tastendruck ist. Also beim setzen des activeSubmenu setzt du auch die Variable isPressed für den Tastendruck. Mit GetKeyUp setzt du diese Variable wieder auf false. in deinem Submenü fragst du zum Tastendruck zusätzlich noch ab, ob isPressed denn false ist. Und nur dann wird der 2te Tastendruck ausgewertet. Deine ganze Routine braucht jetzt also 2 Frames.
  16. 1 point
    Da brauchst du wohl ein kleines Script. Setze die Emission komplett auf 0. Dann packst du noch so ein Script auf das GameObject: new private ParticleSystem particleSystem; private void Awake() { particleSystem = GetComponent<ParticleSystem>(); } private void OnEnable() { StartCoroutine(SpawnParticlesRandomly()); } private IEnumerator SpawnParticlesRandomly() { while (enabled) { particleSystem.Emit(Random.Range(10, 50)); yield return new WaitForSeconds(Random.Range(0.5f, 2f)); } }
  17. 1 point
    Passend dazu, und auch zu Unity's ECS, lass ich mal diesen exzellenten Artikel hier liegen https://www.gamedev.net/blogs/entry/2265481-oop-is-dead-long-live-oop/
  18. 1 point
    Guten Morgen, ich konnte den Fehler gestern noch finden und beheben. Durch den Import von irgendeinem Asset (vermutlich Gaia), wurden meine Quality Settings komplett verstellt und Default auf Low gestellt. LightCount stand dort auf 0. Interessant ist allerdings, dass alle auf Low gestellt wurden und die Quality Settings nach Good fehlen (was nun nicht so tragisch ist). Beim Import gab es leider keinen Hinweis darauf...
  19. 1 point
    Im nachhinein sicher. Aber meine gesamten anderen Level basieren auf der Logik, dass der Untergrund unter meiner statischen Camera hinweggleitet. Nun alle Beweggungen, Einstellungen von Partikel Systemen (zum Generieren von Wolken und Asteroiden) etc. umzustellen wirft mich doch weit zurück. Ich mach es anders: meine Basis setzten ich auf eine Platte mit anderen technischen Aufbauten. Diese Platte inkl. der Basis kann ich drehen. Mein Terrain ist komplett ausserhalb des Sichtbereich der Camera und bereitet mir somit keine Probleme. Sicher nicht die schönste Lösung, aber als "Alleinkämpfer" möchte man doch immer wieder kleine Fortschritte und Erfolge sehen, um den Spaß nicht zu verlieren.
  20. 1 point
    Ich denke weitere Infos sind nicht nötig. Es funktioniert Einwand frei. Besten Dank Jomnitech. LG Samuel
  21. 1 point
    Und das sogar doppelt. GetComponent ist schneller (und eben string-los und damit sauberer) als CompareTag. Aber selbst, wenn das nicht so wäre, vergleiche mal die CompareTag-Variante private void OnTriggerEnter(Collider other) { if (other.CompareTag("Some Tag")) { var componentOfInterest = other.GetComponent<ComponentOfInterest>(); if (componentOfInterest) { componentOfInterest.DoStuff(); } } } mit der Variante ohne CompareTag: private void OnTriggerEnter(Collider other) { var componentOfInterest = other.GetComponent<ComponentOfInterest>(); if (componentOfInterest) { componentOfInterest.DoStuff(); } } Ist einfach eine if-Abfrage weniger, es sei denn, du nimmst oben die Abfrage raus und verlässt dich darauf, dass jedes Objekt mit dem Tag auch diese Komponente hat. Ist irgendwie albern, oder? Wenn es um die Kollisionsmatrix oder Raycasts geht, dann solltest du bei Layern bleiben. Ich gehe sehr stark davon aus, dass tatsächlich nur Kollisionschecks gemacht werden mit Objekten, die auf den richtigen Layern sind. Und mit "sehr" meine ich "so sicher wie man sich nur sein kann, ohne den Source Code gesehen zu haben".
  22. 1 point
    Das scheint echt ein Problem zu sein. Es gibt zwar wege mit Scripts, die das können, aber ohne weiteres scheint es nicht möglich zu sein. https://answers.unity.com/questions/15043/terrain-rotation.html Exportieren kann man glaube ich nur die hight Map, allerdings hast du dann keine Texturen. Ich denke du tust dir leichter wenn du die Kamera und dein Raumschiff bewegst statt das Terrain. Das scrollen des Terrains wird ja Ende des lvls null sein? da könntest du doch dann deine Rotationsanimation beginnen?
  23. 1 point
    Super, für dich, schade für Diejenigen, die früher oder später das gleiche Problem haben und nur wissen, dass du es gelöst hast, aber nicht wie! Lg Ricky-W
  24. 1 point
    Das ist schon richtig. Es wird einfach in dem Bereich nach dem Schlüssel Banane gesucht und true ausgegeben, wenn er da ist oder eben false, wenn er nicht da ist. Aber in deiner Abfrage steht if (!PlayerPrefs.HasKey("Bananen")) Mit einem ! vorneweg. Und ! bedeutet nicht . Deine Bedingung ist also erfüllt wenn es den Key Bananen NICHT gibt. Und dann willst du den Bananenwert auslesen. Das ist aber doch nicht möglich! Denn wenn etwas nicht da ist, kannst du auch keinen Wert auslesen. Du hast deinen Code komplett verdreht. Denn in der Awake fragst du ständig ab, ob die Keys NICHT da sind. Und wenn die nicht da sind willst du etwas auslesen. Anstatt die Keys überhaupt ersteinmal zu erzeugen. Du hast den Code scheinbar irgendwie zusammengebastelt ohne genau zu verstehen wie man Playerprefs behandelt. Ich hatte vor 4 Jahren mal ein kleines Texttutorial dazu geschrieben. Das solltest du dir mal anschauen:
  25. 1 point
    @Denni173: Wenn zwischen 2 Frames mehr Weg zurück gelegt wird, als das Objekt lang ist (bzw. soviel Weg, dassder eine Collider komplett in den anderen eintaucht), dann tunnelt das bewegte Objekt den Collider und wird nicht aufgehalten. Also im Frame davor war es noch knapp for dem anderen Objekt und im Frame danach ist es schon dahinter (oder mittendrin). Bewegt man das Objekt über die Physik, dann wird das ja auch über die Physikzeit gesteuert und die Kollision wird viel besser ausgewertet. Aber auch dann, wird irgendwann der Tunneleffekt eintreten. Aber eben erst viel später. Ob der Collider statisch ist oder nicht, ist total egal. Zum Verständnis hab ich hier ein Tutorial:

Announcements

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

Weiterleitung zum Entwickler "daubit"



×