Jump to content
Unity Insider Forum

Maurice94

Members
  • Content Count

    22
  • Joined

  • Last visited

  • Days Won

    3

Maurice94 last won the day on March 31

Maurice94 had the most liked content!

Community Reputation

5 Neutral

About Maurice94

  • Rank
    Member
  • Birthday 06/15/1994

Profile Information

  • Gender
    Male
  • Location
    Germany
  • Interests
    C#

Recent Profile Visitors

339 profile views
  1. @Hans Ulrich Grüß Gott. Nur mal angenommen du hast irgendwo in deiner Szene ein Gameobjekt rumstehen. Dieses Objekt soll eine Kiste darstellen, deren Inhalt von Wert X ist. Diese Kiste ist jedoch nicht die einzige Kiste, die du in der Szene rumstehen hast. Du hast vielleicht noch 2 weitere Kisten unter einem Felsen, eine im Wasser, eine auf einer Wolke und weiß der Henker wo nicht noch alles. Ich weiß nicht, ob du es schon gemacht hast, oder wie breit dein Wissensstand generell gefächert ist - aber du musst jetzt eine neue Klasse schreiben (einen neuen Script anlegen) und diesen Script dann als Komponente auf deine Kiste legen. Du könntest deine Klasse z.B wie folgt schreiben: using UnityEngine; using UnityEngine.UI; using System; public class Schatztruhe : MonoBehaviour { public int GoldWert; } Wenn du nun einen derartigen Script als Komponente auf deine Kiste legst, wird der Script immer als eine eigene Instanz der Klasse behandelt. Heißt, auch wenn du 10 dieser Scripts auf 10 verschiedenen Kisten liegen hast, kannst du bei jeder Instanz den GoldWert individuell für das Objekt im Inspector festlegen. Das würde nicht für statische Klassen funktionieren, aber davon ist jetzt nicht die Rede. Mit diesem Wissen könntest du nun also Gebrauch von den Methoden OnTriggerEnter / OnTriggerExit machen - diese Methoden feuern, wann immer du den Trigger betrittst / verlässt: using UnityEngine; using UnityEngine.UI; using System; public class Schatztruhe : MonoBehaviour { public int GoldWert; public GameObject infoPanel; // Referenz zu deinem Panel public Text infoText; // Referenz zu deinem Text void OnTriggerEnter() { infoPanel.SetActive(true); infoText.text = GoldWert.ToString(); } void OnTriggerExit() { infoPanel.SetActive(false); infoText.text = string.Empty; } } Und schon hättest du auf relativ einfache Art und Weise die Informationen deiner Kiste in dein Panel übertragen. Das hier sollte dir auf jeden Fall einen Einblick gewähren
  2. @ShV Erstell dir eine Zählervariable die du ShotsFired nennst. Schreib deine Attackfunktion in einen If-Block der prüft, ob ShotsFired kleiner als maximaleMunition ist und erhöhe am Ende jeder Ausführung des Attack-Blocks die Variable ShotesFired um 1. Im Else-Teil returnst du einfach, sodass nichts passiert, sofern der Zähler gleich oder größer als die Anzahl der maximalen Munition ist. Wenn du dann z.B nachladen möchtest, könntest du eine neue Funktion schreiben, die den Zähler wieder auf 0 zurücksetzt. private readonly int maximaleMunition = 3; private int ShotsFired; void Attack() { if(ShotesFired < maximaleMunition) { clone = Instantiate(bulletPrefap, Bulletspawn.position, Bulletspawn.rotation); clone.AddForce(Bulletspawn.transform.right * bulletSpeed); ShotsFired ++; } else return; }
  3. Wo übst du denn Force auf die Kugeln aus? Alles was ich aus dem Code oben entnehmen kann ist, dass du Kugeln an der Position deines Spielers erzeugst. Danach passiert aber nichts mehr. Wie würde, bzw. wie hat dein Code denn ausgesehen als du versucht hast, Force auf die Kugeln auszuüben? @elson
  4. Guten Morgen @Kevin28 - direkt zu Beginn: ICH BIN KEIN EXPERTE! Aber ich kann dich in die richtige Bahn lenken. Du musst dann jedoch selbst recherchieren Ich denke, dass man hier mit dem System.IO Namespace an die Lösung kommen kann. System.IO gibt dir Zugriff auf verschiedene Klassen wie z.B File, DirectoryInfo, usw. die in C# Dateien, Ordner und Pfade auf dem Computer repräsentieren. Mein Lösungsansatz würde wie folgt aussehen: Wenn du dein Spiel schließt (oder von mir aus auch direkt nach der Erstellung der besagten Objekte) erstellst du im persistentDataPath (das ist der Ordner des Programms im Verzeichnis %AppData%) z.B einen Ordner der "Objekte" heißt. Das wäre der erste Schritt, um zu garantieren, dass auch alle Objekte von einem Typen ihr eigenes Verzeichnis besitzen. Dann könntest du über die Childs von deinem "Panel1" drüber loopen und für jedes Objekt eine individuelle Datei mit einer von dir gewählten Endung (z.B .Objekt1.txt oder Objekt1.dat) in diesem Verzeichnis erstellen. All das ist Teil vom System.IO Namespace. // Um ein Verzeichnis zu erstellen, benutz einfach die CreateDirectory-Funktion, die Teil der Klasse "Directory" ist. Directory.CreateDirectory(Application.persistentDataPath + "/deinOrdnerName"); // Und schon hast du einen Ordner erstellt. // Angenommen, wir haben deine gewünschten Speicherobjekte in einem GameObject[] (Array) liegen. GameObject[] alleObjekte = new GameObject[]{10}; // 10 Objekte for(int i = 0; i < alleObjekte.Length ; i++) // erstelle 10 Dateien { string dateiPfad = Application.persistentDataPath + "/deinOrdnerName/"; // in diesem Ordner FileStream neueDatei = File.Create(dateiPfad + alleObjekte[i].GetComponent<ObjektInfo>().objektName); // mit diesem Namen // setzt aber voraus, dass das Objekt irgendwo ein public Field / eine Property namens "objektName" hat, das/die wir uns holen können, um die Datei im Order zu benennen. } Jetzt haben wir das Verzeichnis erstellt und für jedes Objekt eine eigene externe Datei angelegt. Problem ist nur, dass wir jetzt noch die Werte des Objektes in diese Datei schreiben müssen, da es ansonsten ja nur leere Dateien wären. Sprich wir springen nochmal zurück in den Loop. Da du offensichtlich nur mit Strings / Ints / Floats arbeitest, sollten wir in der Lage sein die Werte über einen sogenannten StreamWriter in die Datei zu schreiben, ohne einen BinaryFormatter oder ähnliches zu benutzen. Ein StreamWriter ist gewissermaßen ein Modul, dass wie eine (Achtung: Abstraktion) "Schreibmaschine" funktioniert. Alles, was der StreamWriter von dir verlangt ist ein FileStream. Und ein FileStream ist im Grunde nicht mehr, als eine Repräsentation der Datei, in die der Writer etwas hinein schreiben soll. Wobei genauer gesagt: das FileInfo Objekt ist eher die GENAUE Repräsentation der Datei, wo hingegen der Stream lediglich eine Art "Verbindungsstück" / "Überbrückung" ist, das die Daten empfängt und sie in kleinen Päckchen an den Writer / Reader weiterleitet. Schwierig zu beschreiben, sollte aber ausreichen Jede Instanz vom StreamWriter hat Methoden, auf die du zugreifen kannst. Mit writer.WriteLine(string deinText) kannst du jetzt den von dir gewünschten Inhalt in die externe Datei schreiben, die wir davor noch erstellt haben. Das machst du dann für alle Werte des Objektes, die du später wieder laden möchtest. Müsste dann irgendwie so aussehen: // wir sind wieder im Loop von eben! for(int i = 0; i < alleObjekte.Length ; i++) // erstelle 10 Dateien { string dateiPfad = Application.persistentDataPath + "/deinOrdnerName/"; // in diesem Ordner FileStream neueDatei = File.Create(dateiPfad + alleObjekte[i].GetComponent<ObjektInfo>().objektName); // mit diesem Namen // das ist der Stream, in den wir nun was schreiben wollen... lass uns also einen StreamWriter erstellen StreamWriter writer = new StreamWriter(neueDatei); // neueDatei geben wir als Stream an den Writer writer.WriteLine("Dies ist ein Testtext, den ich in meine Datei schreibe."); // Jetzt haben wir in die Datei geschrieben. writer.Close(); // Und am Ende schließen wir den Writer noch. Gehört zur Prozedur dazu! } Nachdem wir all das gemacht haben, existieren nun mehrere Dateien im Verzeichnis - losgelöst von deiner Anwendung. Wir haben jetzt 10 Dateien erstellt und in jede Datei etwas hinein geschrieben. Wenn du nun dein Spiel beendest und das Spiel neu lädst, werden die Daten jedoch nach wie vor verschwinden. Das ist dann der Punkt, an dem du vom StreamWriter zum StreamReader überspringen musst. Der StreamReader ermöglicht dir nämlich wie der Name schon sagt, Text aus Dateien auszulesen. Wichtig ist also, dass du nun in irgendeiner Start-Funktion in deinem Programm eine Funktion hast, die zunächst einmal prüft, ob das Verzeichnis für die Objekte existiert und ob innerhalb dieses Verzeichnisses auch Dateien (Objekte) liegen. Ich glaube in C# kannst du die Dateien in einem Ordner in einem FileInfo[] (Array) speichern. Folgendes Beispiel: private void ErstelleGameobjekte() { // Referenz zum Pfad string meinOrdner = Application.persistentDataPath + "/deinOrdnerName"; // Prüfen, ob ein Ordner überhaupt existiert machst du so: if(Directory.Exists(meinOrder) { DirectoryInfo ordnerInfo = new DirectoryInfo(meinOrdner); // hier nun ein Array mit allen Files in diesem Ordner... beachte, dass wir eine Funktion vom DirectoryObject benutzen, die einen FileInfo Array returned. FileInfo[] alleDateien = ordnerInfo.GetFiles(); } } Nachdem du das gemacht hast, hast du nun alle Dateien in diesem Ordner in deinem Array liegen. Sprich, wir loopen nun über diesen Array und erstellen für jede Datei in diesem Array eine Instanz deines Prefabs und synchronisieren die Daten von diesem Prefab (die wir uns mit GetComponent greifen) dann mit den Daten aus der externen Datei. Das machst du mit dem eben von mir erwähnten StreamReader, der dich nun die Zeilen aus der Datei lesen lässt. Ich glaube, dass wie beim Writer die Funktion dafür einfach Reader.ReadLine(); / Reader.ReadToEnd(); oder so ähnlich sein sollte. Das ist die Richtung in die du gehen kannst. // Gleicher Code wie eben, nur weiter ausgeführt. public GameObject deinPrefab; public Transform irgendeinParent; private void ErstelleGameobjekte() { // Referenz zum Pfad - dieses Mal als String string meinOrdner = Application.persistentDataPath + "/deinOrdnerName"; // Prüfen, ob ein Ordner überhaupt existiert machst du so: if(Directory.Exists(meinOrder) { // hier nun ein Array mit allen Files in diesem Ordner... beachte, dass wir eine Funktion vom DirectoryObject benutzen, die einen FileInfo Array returned. DirectoryInfo ordnerInfo = new DirectoryInfo(meinOrdner); FileInfo[] alleDateien = ordnerInfo.GetFiles(); foreach(FileInfo info in alleDateien) { GameObject obj = Instantiate(deinPrefab, irgendeinParent); ObjektInfo data = obj.GetComponent<ObjektInfo>(); FileStream stream = info.Open(FileMode.Open); // Du musst nun einen FileStream auf diese Art und Weise erstellen. StreamReader reader = new StreamReader(stream); // ...und ihn auf diese Art und Weise an den Reader weitergeben! data.Name = reader.ReadToEnd(); reader.Close(); // Auch hier wieder den Reader schließen! } } Und im Grunde solltest du an dieser Stelle Informationen vom Computer in dein Spiel geladen haben. Solltest du mit Datentypen arbeiten, die nicht primitiv sind, wird es komplizierter, da du dann BinaryFormatter benutzen musst, um Daten zu serializen, usw. Ein wichtiger Punkt, den ich nicht angemerkt habe ist: JSONUtility. Schau dir auf jeden Fall auch mal Beiträge zu JSON an, da du höchstwahrscheinlich früher oder später Sachen zu JSON konvertieren musst um von dort aus dann weiter zu arbeiten. Ich selbst arbeite z.B größtenteils nur mit Scriptable Objects, demnach weiß ich nicht wie du speziellen Zugriff auf Zeile 3 oder so in einer externen Datei kriegst, um irgendeiner Variable genau diesen Wert zuzuweisen. Aber auch das kannst du durch Recherche easy lösen! Beim Scriptable Object kann ich mir die Werte dann nämlich immer direkt aus den jeweiligen Feldern holen. Nochmals: ich bin kein Experte und wollte dir lediglich die Richtung vorgeben! Sorry, dass ich am Ende etwas schlampig geworden bin. Ich hoffe, ich konnte dir dennoch etwas helfen! LG, Maurice 🙂
  5. Hey @Hans Ulrich - es gibt verschiedene Möglichkeiten, dies zu erreichen. Eine Variante hat Sascha bereits genannt: Raycasting. Du schickst per Mausklick einen "unsichtbaren Strahl" von der aktiven Kamera aus in die Szene. Dieser Strahl kann dann z.B basierend auf den Objekten die er trifft, verschiedene logische Abläufe ausführen. Sollte es sich bei dem Objekt was du angeklickt hast z.B um eine Kiste handeln, möchtest du die Informationen in einem seperaten UI-Panel angezeigt bekommen. Wie mein Vorgänger bereits erwähnt hat, könntest du in diesem Falle einfach ein Panel einblenden, was dann die Informationen aus dem "Kisteninhalt-Script" für dich ausliest und visualisiert. Neben dem Raycasting könntest du ua. auch mit Distanz oder Triggern arbeiten, wenn dein Spiel eher auf Tastaturnavigation aufbaut. Die Funktion Vector3.Distance(float a, float b) returned einen Float, dessen Wert die exakte Distanz zwischen Punkt A und B ist. Du könntest also permanent prüfen, ob die Distanz zwischen der Kiste und dem Player einer gewissen Reichweite (Range) entspricht. Sollte dies gegeben sein, könntest du ebenfalls ein Panel einblenden und auch hier die Daten ans Panel transferieren. Ob du dafür eine extra Taste drücken müsst, oder ob es sich direkt öffnet wenn du in Reichweite bist, liegt dabei ganz bei dir. Für die Trigger-Variante würdest du dem Objekt einfach einen Collider geben und ihn als Trigger kennzeichnen. Dann schreibst du deine Logik in der OnTriggerEnter oder OnTriggerStay Function, die auf der Kiste in einem Script liegt. Die Funktion prüft dann permanent, ob du dich noch im Radius des Triggers befindest. Anmerkung für diese Lösung: OnTriggerStay könnte Mucken machen und nur dann feuern, wenn du dich bewegst. Um dies zu vermeiden, ändere den sleeping Mode vom RigidBody. Nur als letzten Tipp. Das wären drei simple Lösungen, die dich an dein Ziel bringen könnten. Solltest du nicht wissen, wie du allgemein eine Verbindung zwischen GUI und Script herstellen kannst, würde ich dir raten, zunächst einmal das Grundkonzept von Referenzen zu verinnerlichen um zu verstehen, wie du die Werte von Feldern, bzw. von Objekten quer durch dein Projekt schicken kannst. Hoffe, ich habe dich nicht zu sehr verwirrt :D
  6. @YungCaedo Hey! Ich habe nun mal eben schnell ein ähnliches Szenario nachgebaut... der Spieler trifft auf einen Gegner und der Gegner fügt dem Spieler bei jeder Collision 10 Schaden zu... dann wird geprüft ob das Leben des Spielers 0 oder weniger beträgt. Falls dies der Fall sein sollte, spawnt der Spieler an einer bestimmen Position und ich resette das Leben. Wie du unten im Video sehen kannst funktioniert das einwandfrei. Ich weiß nicht, was in deiner Coroutine passiert.... aber irgendwo musst du einen Fehler gemacht haben. Wenn du mir den Rest vom Code zeigst, kann ich dir helfen. 2020-03-25 18-02-25.mp4
  7. @YungCaedo Du könntest einfach die Szene neu laden, sodass der Spieler immer an seiner Ausgangsposition spawnt? Ansonsten poste mal den Code 🙂
  8. Hey @Kojote, du könntest doch im Update eine Art Check laufen lassen der prüft, ob das von dir genannte PopUp-Fenster aktiv ist, oder nicht. Falls es aktiv ist, kannst du dem Button doch einfach seine Interaktivität nehmen. Das könntest du natürlich für alle UI-Elemente in einem Modul laufen lassen, dass du UI Controller nennst. Also das ist so die Grundrichtung, in die ich denke. Hilft dir das vielleicht? private GameObject PopUpFenster; private Button buttonImHintergrund; private void Update() { PopUpCheck(); } private void PopUpCheck() { if(PopUpFenster.activeInHierarchy) { buttonImHintergrund.interactable = false; } else { buttonImHintergrund.interactable = true; }
  9. Du musst genauer beschreiben, was nicht funktioniert. Rein von der Logik her, sollte der Code funktionieren. Ich würde dir jedoch raten, deinen Variablen und Funktionen aussagekräftige Namen zu geben. Wieso gibst du einer OnClick()-Function die du an den Button weitergibst den Namen OnClick2? Wähle stattdessen einen Namen, der beschreibt, was die Funktion macht. Die Funktion erhöht offensichtlich einen Zähler, bzw. einen Wert. Also gib ihr einen Namen wie z.B IncreaseValue() oder DecreaseValue() - mach es dir nicht unnötig kompliziert. Das gleiche gilt für die Variable. Wieso x1? Was ist die Variable? Ist die Variable ein x1 oder ist die Variable ein Punktestand/Display für einen Wert? Namensgebung ist extrem wichtig. Wähle logische und lesbare Namen, die etwas aussagen. Um zurück auf deine Frage zu kommen: was genau funktioniert denn nicht? Denk daran, dass du dir in deinem Script die Referenz zum Text vom gleichen Objekt holst, auf dem DIESER Script liegt, da du mit GetComponent<Type> danach greifst. Das setzt voraus, dass der Text eine Komponente von dem Objekt ist, auf dem dieser Script liegt. Der Script sollte außerdem nicht auf zwei Objekten liegen, da er permanent einen Wert updatet und jeder Script eine eigene Instanz ist - sprich jede Instanz wird versuchen, den Text für sich zu beanspruchen. Es gibt außerdem eine praktische Funktion für das, was du da manuell zusammen gebaut hast. Versuch mal: Laserleft.text = string.Format("Der Wert ist: {0}", x1);
  10. Und musst du die Klasse nicht mit [System.Serializable] kennzeichnen?
  11. Ich hatte neulich auch noch ein ähnliches Problem. Bei mir ging es jedoch darum, Listen vom Typen Scriptable Object zu speichern. Es lief ähnlich ab wie bei dir - ich konnte meine Dateien im Savefile speichern, hatte jedoch Schwierigkeiten die Dateien wieder ordnungsgemäß zu importieren, bzw. zu laden. Ich habe mich dazu entschieden, das Problem erstmal bei Seite zu schieben, da ich mich zu lange mit dem Thema aufgehalten habe und schon fast die Freude am Projekt verloren habe. Meine Recherche hat mir zumindest die Antwort gegeben, dass bestimmte Typen nicht serializable sind und man einen kleinen Umweg gehen muss, um das Resultat zu erzielen, was man sich wünscht. Es wurde häufiger vom "Wrapping" und von Helferklassen gesprochen. Ich kann dir leider keine Antwort auf deine Frage geben, wollte dich aber fragen, ob du schon versucht hast, deine Dateien mit JsonUtility zu speichern? Mit JSON habe ich bisher immer gute Erfahrungen gesammelt.
  12. Stimme dir zu in dem Punkto - ich hätte direkt die saubere Variante aufführen sollen.
  13. ....im Grunde wollte ich ihm ja auch nur das Prinzip von Verschachtelung näher bringen, weil er meinte, dass er mehr Informationen benötigen würde und der Typ alleine nicht ausreicht. Deine Variante würde so aussehen:
  14. @Sascha Klar, man kann die Informationen auch direkt ins Scriptable Object integrieren und dann mit Referenz dazu Verknüpfungen machen. Im Grunde läuft es ja auf das gleiche Resultat hinaus, oder nicht? Ob das Objekt nun die Informationen aus Quelle A, oder Quelle B erhält spielt doch keine direkte Rolle? Ich selbst trenne bestimmte Sachen gerne voneinander - aber das ist wohl einfach meine eigene, chaotische Art und Weise. Bisher hat immer alles bestens funktioniert. Zum anderen Punkt möchte ich sagen, dass ich niemals die Liste als Referenz für andere Aufgaben nehmen würde. Ich würde die Liste lediglich in einer Art Preload-Szene als einmalige Referenz nutzen, um Informationen ins Spiel zu laden. Alles weitere passiert unabhängig zu dieser Liste, indem sich Objekte ganz normal Referenzen zueinander holen. Meistens leite ich von Liste zu Liste weiter.
  15. @Herbstmond Zunächst einmal würde ich deinem Scriptable Object ein Enum für die Typen hinzufügen. Dann prüfst du im Objekt, was Gebrauch von deinem Datencontainer machen will, von welchem Gentypen es ist. Sollte das Objekt dann also im Awake erfahren haben, von welchem Gentypen es ist, kannst du doch im Start direkt individuelle Logik für jeden Typen dazu schreiben. Alles was das Objekt vom Enum wissen will ist doch, von welchem Typen es ist, um dann seine seine eigene Identität definieren zu können. Hier ein Beispiel für dich: Ich habe es jetzt hier im Beispiel nicht verdeutlicht, aber du solltest auf jeden Fall Gebrauch von Switches machen, wenn du auf diese Art und Weise prüfst. Ich hoffe aber du verstehst, dass das Enum nicht so viele Informationen halten muss. Es genügt, den Grundtypen vorzugeben um dann alles andere davon abzuleiten. Wie @devandart bereits erwähnt hat, könnte ein leeres GameObject eine Liste vom Typen deines Scriptable Objects halten. In dieser Liste speicherst du ganz einfach alle Objekte, die im Spiel existieren. Du kannst es so einrichten, dass sich das Objekt nicht zerstört, wenn du eine neue Szene lädst. Das gibt dir den Vorteil, dass du von überall und zu jedem Zeitpunkt Informationen aus dieser Liste entnehmen kannst, bzw. immer eine Referenz zur Liste findest, da das Objekt in jeder Szene existiert. Grüße, Maurice
×
×
  • Create New...