Jump to content
Unity Insider Forum

Maurice94

Members
  • Content Count

    20
  • Joined

  • Last visited

  • Days Won

    2

Maurice94 last won the day on January 12

Maurice94 had the most liked content!

Community Reputation

4 Neutral

About Maurice94

Profile Information

  • Gender
    Male
  • Location
    Germany
  • Interests
    C#

Recent Profile Visitors

239 profile views
  1. 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
  2. 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 abstrakten Klasse "Directory" ist. Directory.CreateDirectory(Application.persistentDataPath + "/deinOrdnerName"); // Und schon hast du einen Ordner erstellt. // Angenommen, wir haben deine gewünschten Speicherobjekte als Childs vom Panel 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. Genauer gesagt springen wir an die Stelle, an der wir die Datei erstellt haben. 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. Bedeutet, dass wir dem StreamWriter den soeben erstellen FileStream als Parameter geben. 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, 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].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 DirectoryInfo meinOrdner = new DirectoryInfo(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. FileInfo[] alleDateien = meinOrdner.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 DirectoryInfo meinOrdner = new DirectoryInfo(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. FileInfo[] alleDateien = meinOrdner.GetFiles(); foreach(FileInfo info in alleDateien) { GameObject obj = Instantiate(deinPrefab, irgendeinParent); ObjektInfo info = obj.GetComponent<ObjektInfo>(); // wir erstellen einen Reader und geben Ihm als Parameter ein Objekt vom Typ FileInfo... alleDateien ist ja lediglich eine Ansammlung von FileInfos. StreamReader reader = new StreamReader(alleDateien[i]); // hier bin ich mir unsicher, aber ich glaube das sollte funktionieren. info.nameZumBeispiel = reader.ReadToEnd(); // wir speichern das Ergebnis der Datei, die wir auslesen (reader.ReadToEnd() returned einen String) in einer Variable des Scripts der auf deinem Objekt liegt. 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 🙂
  3. 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
  4. @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
  5. @YungCaedo Du könntest einfach die Szene neu laden, sodass der Spieler immer an seiner Ausgangsposition spawnt? Ansonsten poste mal den Code 🙂
  6. 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; }
  7. 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);
  8. Und musst du die Klasse nicht mit [System.Serializable] kennzeichnen?
  9. 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.
  10. Stimme dir zu in dem Punkto - ich hätte direkt die saubere Variante aufführen sollen.
  11. ....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:
  12. @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.
  13. @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
  14. Nur weil du zwei Spieler hast bedeutet das nicht, dass die Variablen automatisch static sein müssen. Wenn zwei Spieler dein Spiel zur gleichen Zeit im gleichen Raum / Netzwerk spielen, teilen beide Spieler die gleichen lokalen Daten, da sie ja beide das gleiche Programm öffnen. Du schickst dann einfach Funktionen übers Netzwerk (auch RPC genannt), die an eine gewisse Logik gebunden sind. Diese Funktionen sind in der Lage, bei allen Spielern im Netzwerk zum gleichen Zeitpunkt ausgeführt zu werden. Sprich, selbst wenn deine Variable private wäre, könntest du über RPC's die lokalen Variablen aller Spieler im gleichen Netzwerk verändern / beeinflussen, so wie du es willst.
  15. Das Problem lässt sich relativ leicht lösen. Du musst einfach das TMPro Namespace importieren. Schreib einfach oben bei deinen Namespaces: using TMPro; Dadurch bekommst du Zugriff auf die Klassen vom TextMesh. Darunter auch auf die TextMeshProUGUI-Klasse, die du benötigst. Du deklarierst dann einfach anstelle vom normalen Text ein TextMeshProUGUI Feld. Sollte ohne Probleme funktionieren! Hier nochmal ein Bild 🙂
×
×
  • Create New...