Jump to content
Unity Insider Forum

Objekt automatisch fahren lassen


Haji17
 Share

Recommended Posts

Gerade eben schrieb Haji17:

  case UnityWebRequest.Result.Success:
                    var json = webRequest.downloadHandler.text;
                    var downloadedThings = JsonUtility.FromJson<ManyThings>(json);

                    var x_aktuell = downloadedThings.things[0].x;
                    var y_aktuell = downloadedThings.things[0].y;
                    var d_x = downloadedThings.things[0].dx;
                    var d_y = downloadedThings.things[0].dy;
                    var aktueller_Winkel = Mathf.Atan(d_y / d_x);
                    var aktueller_Vektor = new Vector3(x_aktuell, 0F, y_aktuell);
                    var aktuelle_Richtung = new Vector3(aktueller_Winkel, 0F, 0F);
                    
                    Debug.Log("Aktueller Vektor ist = " + aktueller_Vektor + "Aktueller Winkel beträgt " + aktueller_Winkel );
                    
                    StartCoroutine(RunAround(aktueller_Vektor));

                    break;
            }
        }
    }

    private IEnumerator RunAround(Vector3 Position)
    {
        while (enabled)
        {
            yield return TurnTowards(Position);
            yield return RunTowards(Position * walkDistance);

        }
    }

    private IEnumerator TurnTowards(Vector3 direction)
    {
        var targetRotation = Quaternion.LookRotation(direction);

        while (transform.rotation != targetRotation)
        {
            transform.rotation = Quaternion.RotateTowards(transform.rotation, targetRotation, ROTATE_SPEED * Time.deltaTime);
            yield return null;
        }
    }

    private IEnumerator RunTowards(Vector3 direction)
    {
        var targetPosition = transform.position + direction;

        while (transform.position != targetPosition)
        {
            transform.position = Vector3.MoveTowards(transform.position, targetPosition, WALK_SPEED * Time.deltaTime);
            yield return null;
        }
    }

Hier habe ich die Daten mir rausgezogen und mir dann daraus einen vector3 (var aktueller_Vektor) gebastelt, um durch diesen die aktuelle Position darzustellen. Variable  aktueller_Winkel und aktuelle_Rotation wollte ich zuerst nutzen für die Drehung, aber dann kam mir dein Tipp in den Sinn dass ich dafür "Quaternion.Lookrotation" in Kombination mit meinem aktueller_Vektor- Variable nutzen kann. Ich denke, das sollte so möglich sein ohne Trigonometrie oder?

Jetzt frage ich mich gerade, ob das Sinn macht, was in der Coroutine RunAround mache. Mit "walkdistance" lege ich ja die Strecke fest, die mein Object sich bewegen soll, aber angenommen ich fahre vom Startpunkt (0,0,0) zu (5,0,0) , dann beträgt walkdistance ja 5 (angenommen Bewegung nur in x-Richtung). Wenn der jetzt aber von (5,0,0) nach (2,0,0) sich zurückbewegt, dann wäre walkdistance doch -4. Wenn ich mir das richtig überlegt habe, wird das in meinem Script doch nicht beachtet? In der Coroutine RunTowards schon mit der Variable targetPosition. Bin ich richtig mit meiner Überlegung? Und wenn ja, wie könnte ich das anpassen?

Link to comment
Share on other sites

vor 5 Stunden schrieb Haji17:

aber dann kam mir dein Tipp in den Sinn dass ich dafür "Quaternion.Lookrotation" in Kombination mit meinem aktueller_Vektor- Variable nutzen kann. Ich denke, das sollte so möglich sein ohne Trigonometrie oder?

Genau! Naja, aktuelle_Richtung, nicht aktueller_Vektor. Aber sonst ja.

vor 5 Stunden schrieb Haji17:

dann wäre walkdistance doch -4

Nur, wenn du falsch rechnest. Vektordistanz ist kommutativ. Das heißt, ob du die Distanz von 0,0,0 zu 5,0,0 oder von 5,0,0 zu 0,0,0 ausrechnest - da kommt dasselbe heraus.

Link to comment
Share on other sites

vor 6 Stunden schrieb Haji17:

yield return RunTowards(Position * walkDistance);

Ja, dass das kommutativ gerechnet wird versteh ich. Aber mich stört dieser Faktor walkDistance, den benötige ich doch eigentlich nicht? Oder habe ich gerade einen Denkfehler 😅

Link to comment
Share on other sites

So ganz verstehe ich das System halt immer noch nicht. Du kriegst eine Position und eine Richtung. Dann kriegst du irgendwann die nächste Position. Was, wenn diese Position nicht da liegt, wo die letzte Richtung hingezeigt hat?

Link to comment
Share on other sites

vor 11 Stunden schrieb Sascha:

So ganz verstehe ich das System halt immer noch nicht. Du kriegst eine Position und eine Richtung. Dann kriegst du irgendwann die nächste Position. Was, wenn diese Position nicht da liegt, wo die letzte Richtung hingezeigt hat?

Also, so wie ich mir das vorstelle soll das wie folgt ablaufen.

Das Object steht am Punkt (x1,0,z1) und bekommt dann einen neuen Zielvektor (x2,0,z2). Jetzt dreht er sich in die Richtung des neuen Zielvektors und bewegt sich dann translatorisch vom x1 nach x2. Wenn er den Punkt erreicht hat, könnte er einen neuen Zielvektor (x3,0,z3) bekommen und wiederholt den vorherigen Schritt um dann da anzukommen. Konnte dir diese Erklärung helfen? :)

Link to comment
Share on other sites

vor 2 Stunden schrieb Sascha:

In dieser Erklärung ist ja nun kein Platz für einen vorgegebenen Richtungsvektor mehr drin :)

Das ist mir auch gerade aufgefallen :D 

Das würde bedeuten, ich brauch diesen Richtungsvektor aus dx und dy nicht, sondern nehmen immer meinen neuen Positionsvektor, nach dem sich der alte ausrichtet. Sprich

vor 21 Stunden schrieb Sascha:

Genau! Naja, aktuelle_Richtung, nicht aktueller_Vektor. Aber sonst ja.

Hier nehme ich doch den aktueller_Vektor oder?

Verstehst du jetzt aber auch, was mich an der Variable walkdistance stört? Der Wert ist ja eigentlich nicht konstant, sondern ich müsste die Strecke zwischen neuen Vektor und altem Vektor immer wieder neu berechnen. Aber wie kann ich die Werte vom alten Vektor beibehalten, nachdem der neue schon da ist? Ich bin mir aber gerade unsicher ob ich mich nicht gerade selber verwirrt habe :D

Link to comment
Share on other sites

vor 3 Stunden schrieb Haji17:

Das würde bedeuten, ich brauch diesen Richtungsvektor aus dx und dy nicht, sondern nehmen immer meinen neuen Positionsvektor, nach dem sich der alte ausrichtet.

Genau! Zum Starten brauchst du also schon einmal zwei Positionen, damit du von der ersten zur zweiten kannst. Ab dann brauchst du immer noch eine weitere Position, damit es weitergehen kann. Distanz und Ausrichtung ergeben sich aus den Positionen von alleine.

  • Like 1
Link to comment
Share on other sites

  • 2 weeks later...

Hallo,

ich hätte noch ein Mal eine Frage bzgl. der Json Serialization. Ich möchte gerne einen solchen Json-String in Unity verwenden.

{"[0]": {"x": 148.0, "y": 162.0, "dx": 3, "dy": -43}, "[1]": {"x": 477.0, "y": 118.0, "dx": 3, "dy": 43}}

JsonUtility kann ich nur auf Objekte anwenden (also geschweifte Klammern, wenn ich das richtig aus den Beiträgen hier gelernt hab :D ). Dahingehend erfüllt mein Json-String ja diese Bedingung. Ich tue mich gerade bei der Definition eines Structs schwer, so dass ich dann irgendwann die Eintrage x (148.0) und y (162.0) von meinem Array [0] auswählen kann. 

Ausgehend von einem solchen Struct:

public struct Thing
    {
        public float x;
        public float y;
        public float dx;
        public float dy;
    }

    [System.Serializable]
    public struct ManyThings
    {
        public Thing[] things;
    }

Habe ich das richtig verstanden, dass ich hiermit nur diesen Teil "[0]": {"x": 148.0, "y": 162.0, "dx": 3, "dy": -43} meines Json Strings berücksichtigt habe? Das heißt, eigentlich müsste ich doch jetzt ein übergreifendes Struct konstruieren, welches die zwei Teile [0] und  [1] beinhaltet oder? Aber angenommen, ich nehme jetzt ein übergreifendes Struct und nenne es "InitialThing", dann würde das ja aus den zwei Einträgen [0] und [1] bestehen. Aber jetzt bin ich gerade verwirrt, mit welchem Datentyp ich die beiden deklariere. Das sind doch eigentlich Strings? 

Kann man verstehen womit ich gerade verwirrt bin? Oder habe ich das schlecht erklärt? 😅

Link to comment
Share on other sites

vor 11 Stunden schrieb Haji17:

JsonUtility kann ich nur auf Objekte anwenden [...] Dahingehend erfüllt mein Json-String ja diese Bedingung.

 

vor 11 Stunden schrieb Haji17:

[...] von meinem Array [0] auswählen kann. 

Merkste, oder? Wenn dein Root-Element ein Objekt ist... dann ist es nicht gleichzeitig ein Array. Du hast da auch kein Element Nummer 0 ([0]), du hast da ein Feld mit dem Namen "[0]". Wenn du eine Json-Bibliothek benutzt, wo du alles per Hand ausliest, dann kannst du das Element mit dem Namen "[0]" natürlich auslesen. Wenn du aber JsonUtility nutzt, dann musst du Klassen und/oder Structs erstellen, die dein Json wiederspiegeln. Und "[0]" ist kein gültiger Name für eine Eigenschaft:

struct Foo
{
  public int [0]; // Nö.
  public int number; // Jo.
}

Lies dir das hier bitte nochmal durch.

Du kannst wunderbar Arrays benutzen, wenn du das willst. Du musst sie halt nur in ein Objekt rein packen. Und genau dafür ist das ManyThings-Struct schon da.

Link to comment
Share on other sites

vor 57 Minuten schrieb Sascha:

Merkste, oder? Wenn dein Root-Element ein Objekt ist... dann ist es nicht gleichzeitig ein Array

Oh sorry. Ja hast natürlich Recht 😅

vor 58 Minuten schrieb Sascha:

Wenn du aber JsonUtility nutzt, dann musst du Klassen und/oder Structs erstellen, die dein Json wiederspiegeln. Und "[0]" ist kein gültiger Name für eine Eigenschaft:

struct Foo
{
  public int [0]; // Nö.
  public int number; // Jo.
}

Genau, das "[0]" bringt mich jedes Mal aus der Spur. Das müsste ich ja eigl als "public int [0];" deklarieren, aber das geht mit JsonUtility nicht. In der dieser Form des Json-Strings  kann ich also JsonUtility nicht nutzen, sondern muss eine Json Bibliothek auswählen. Hab ich das richtig verstanden?

Die verlinkten Beispiele habe ich mir auch nochmals angeschaut, auch da wird ja nicht etwas in der Form "[0]" genutzt, sondern wie zum Beispiel {points: [0:{...}]}.

Das Points- Beispiel finde ich am verständlichsten für mich, weil ich da auch genau verstehe wie ich was als Struct definiere :D

Link to comment
Share on other sites

vor 12 Minuten schrieb Haji17:

auch da wird ja nicht etwas in der Form "[0]" genutzt

Korrekt. Du sollst halt nicht ein Array definieren dürfen, das die Nummern 0, 1, 2, 3 und 5 hat. Du kannst einfach nur eine Liste von Elementen aufschreiben, und dir Nummerierung ist dabei implizit. Die Form für ein Array ist:

[
  element,
  element,
  element
]

Das ist ein Array mit 3 Elementen ([0], [1] und [2]), wobei "element" natürlich durch korrektes Json ersetzt werden müsste. Z.B.

[
  {"x": 10, "y": 20},
  {"x": 10, "y": 30},
  {"x": 10, "y": 40}
]

Hier haben wir 3 Objekte, die jeweils die Eigenschaften "x" und "y" besitzen. Passt also z.B. auf Vector2. Das Äquivalent zu diesem gesamten Json-String wäre also z.B.

Vector2[]

wenn man es in C# definiert.

Du kannst aber eben kein Array als Wurzel haben, also muss da ein Objekt drumherum:

{ "points":
  [
    {"x": 10, "y": 20},
    {"x": 10, "y": 30},
    {"x": 10, "y": 40}
  ]
}

Das Äquivalent dazu wäre

[System.Serializable]
public struct PointArray
{
  public Vector2[] points;
}

 

Link to comment
Share on other sites

vor 8 Stunden schrieb Sascha:

Korrekt. Du sollst halt nicht ein Array definieren dürfen, das die Nummern 0, 1, 2, 3 und 5 hat. Du kannst einfach nur eine Liste von Elementen aufschreiben, und dir Nummerierung ist dabei implizit. Die Form für ein Array ist

Super danke, dann bin ich beruhigt. Im Grunde habe ich mir schon gedacht, dass das in der Form nicht möglich sein wird, aber manchmal denk ich mir, ich könnte etwas übersehen haben und der Fehler liegt bei mir :D

Link to comment
Share on other sites

Eine Frage hätte ich noch. Dank deiner Hilfe konnte ich meinen Json-String deserialisieren wie gewünscht. 

Jetzt habe ich das Problem, dass meine http-Request Anfrage nur ein Mal zu Beginn erfolgt. Ich will die Anfrage immer wieder neu stellen nach z.B. 5 Sekunden. Die einfachste aber auch ziemlich unelegante Variante wäre eine For-Schleife mit Zähler laufen zu lassen, bei dem ich den letzten Punkt sehr sehr hoch ansetze :D 

Dann kam mir die Idee, eine Coroutine mir zu basteln, in der die Coroutine mit der Serveranfrage (inkl. dem Bewegungsprozess usw.) ausgeführt wird, danach mit waitforseconds (5f) "pausiert" und dann nochmals die Coroutine startet. Also so etwas:

 void Start ()
    {

        StartCoroutine(Anfragen());


    }

    private IEnumerator Anfragen()
    {
       
            StartCoroutine(GetRequest("http"));
            yield return new WaitForSeconds(5f);
            StartCoroutine(GetRequest("http"));
       
        
    }

Wenn ich das so mache, dann habe ich ja nur zwei Anfragen mit einer Pause von 5 Sekunden. Wenn ich das ganze anstatt void Start() mit void update () machen würde, dann eskaliert das ganze etwas :D 

Kann ich das ganze regeln, in dem ich eine while (enabled)-Schleife in meiner Coroutine "Anfragen" einbaue? Ich kann das ganze erst am Montag erst ausprobieren, da es sich um einen lokalen Server handelt, aber ich möchte gerne bis dahin das Problem lösen.

 void Start ()
    {

        StartCoroutine(Anfragen());


    }

    private IEnumerator Anfragen()
    {
        while (enabled) 
        {
            StartCoroutine(GetRequest("http:"));
            yield return new WaitForSeconds(5f);
            StartCoroutine(GetRequest("http:"));
        }
        
    }

Mache ich hier einen Denkfehler oder passt das so mit einer sich immer wiederholenden Serveranfrage im Abstand von 5 Sekunden.

 

Link to comment
Share on other sites

vor 2 Stunden schrieb Haji17:

Dank deiner Hilfe konnte ich meinen Json-String deserialisieren wie gewünscht. 

Super! :)

vor 2 Stunden schrieb Haji17:

Kann ich das ganze regeln, in dem ich eine while (enabled)-Schleife in meiner Coroutine "Anfragen" einbaue?

Ja! Und das funktioniert nicht nur - es ist auch eine sehr gute Lösung.

Drei kleine Änderungen würde ich jedoch vorschlagen:

  1. Du hast jetzt die Schleife
    Anfrage - Warten - Anfrage - Nochmal
    Das heißt, dass zwischen der zweiten und der dritten Abfrage keine Wartezeit liegt. Entferne einfach die zweite Anfrage aus deinem Code - die Wiederholung hast du ja schon durch die Schleife.
    while (enabled)
    {
      // Anfrage
      // Warten
    }
  2. Du benutzt "enabled" als Schleifen-Bedingung. Das ist super! Wenn du deine Komponente deaktivierst, arbeitet sie nicht mehr weiter. Genau dafür ist enabled ja auch da. Wenn du jetzt allerdings deine Komponente wieder anschaltest, dann wird die Coroutine nicht noch einmal neu gestartet. Start wird nur einmal pro Komponente ausgeführt. Du könntest die StartCoroutine-Zeile aber auch statt in Start in OnEnabled packen. Dann wird die Coroutine jedes Mal wieder gestartet, wenn du die Komponente reaktivierst.
  3. Um es komplett abzurunden, würde ich die Coroutine noch in OnDisable stoppen wollen. Wenn die Komponente deaktiviert ist und der Code-Fluss am "while (enabled)" ankommt, dann wird die Schleife, und damit die Coroutine, abgebrochen. Wenn du die Komponente aber deaktivierst, bricht die Coroutine deshalb noch nicht automatisch ab. Deaktivierst du deine Komponente und aktivierst du sie wieder, bevor die Schleife einmal ihre Bedingung überprüft, dann wird die Coroutine nicht beendet. Und wegen 2. startet dann eine neue, und du hast zwei gleichzeitig laufen. Daher einmal ein StopAllCoroutines() in OnDisable - nur zur Sicherheit.
  • Like 1
Link to comment
Share on other sites

vor 7 Stunden schrieb Sascha:

Entferne einfach die zweite Anfrage aus deinem Code - die Wiederholung hast du ja schon durch die Schleife.

Ja, stimmt. Das zweite Mal Anfragen kann ich mir ersparen mit der Schleife.

 

vor 7 Stunden schrieb Sascha:

Du könntest die StartCoroutine-Zeile aber auch statt in Start in OnEnabled packen. Dann wird die Coroutine jedes Mal wieder gestartet, wenn du die Komponente reaktivierst.

Jetzt wird mir auch klar, warum bei meinem ersten Versuch die Anfrage nur ein Mal erfolgt ist. Wenn ich den Game-Mode starte, so soll die Anfrage direkt beim Start erfolgen und durchgehend immer wieder laufen. Das heißt, ich muss das OnEnabled so einbauen oder?

void OnEnabled ()
    {

        StartCoroutine(Anfragen());


    }

    private IEnumerator Anfragen()
    {
        while (enabled) 
        {
            StartCoroutine(GetRequest("http:"));
            yield return new WaitForSeconds(5f);
            StartCoroutine(GetRequest("http:"));
        }
        
    }

Das habe ich dann soweit (hoffentlich) richtig verstanden.

vor 7 Stunden schrieb Sascha:

Um es komplett abzurunden, würde ich die Coroutine noch in OnDisable

Warum ich zur Sicherheit diese Überprüfung machen soll, leuchtet mir ein.

Ich habe mir OnDisable mal im Manual aufgerufen (https://docs.unity3d.com/ScriptReference/MonoBehaviour.OnDisable.html), aber das Beispiel verwirrt mich etwas.  Muss ich vor meinem Void OnEnabled() ein Void OnDisable mit dem Inhalt StopAllCoroutines setzen? Aber das macht doch an der Stelle keinen Sinn. Wo und wie baue ich denn ich OnDisable- Funktion ein?

Link to comment
Share on other sites

vor 14 Stunden schrieb Haji17:

Das heißt, ich muss das OnEnabled so einbauen oder?

Jau!

vor 14 Stunden schrieb Haji17:

Muss ich vor meinem Void OnEnabled() ein Void OnDisable mit dem Inhalt StopAllCoroutines setzen? Aber das macht doch an der Stelle keinen Sinn. Wo und wie baue ich denn ich OnDisable- Funktion ein?

Die Reihenfolge, in der du deine Methoden definierst, ist egal. Eine Methode hat eine Signatur (für dich erstmal wichtig: der Name) und wird über diese aufgerufen. Unity hat jetzt diese etwas merkwürdige Eigenschaft, besondere Methodennamen (Start, Update, ...) selber aufzurufen. Wenn du also irgendwo in deine Komponente eine Methode namens OnEnable definierst, dann wird Unity diese Methode aufrufen, wann immer die Komponente aktiviert wird. Das ist zum einen immer, wenn das Häckchen der Komponente aus ist und wieder angemacht wird (bzw. enabled false ist und auf true gesetzt wird), aber zum anderen auch einmalig am Anfang des "Lebens" der Komponente. Und daran ändert sich nichts, wenn du OnEnable über oder unter eine andere Methode definierst. Und OnDisable wird halt aufgerufen, wann immer du eine Komponente ausschaltest. Das schließt auch den Moment ein, wo sie zerstört wird.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
 Share

×
×
  • Create New...