Intelligentes Umdrehen bzw. Winkel zwischen 2 Objekten (Gott ist das kompliziert)

  • GM 8

Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

  • Intelligentes Umdrehen bzw. Winkel zwischen 2 Objekten (Gott ist das kompliziert)

    Also gut. Ich habe ein Objekt, welches wild in einem mit Mauern abgegrenzten, viereckigen Raum umher schwirrt. (->im create event: direction = floor(random360)) ) Dabei schaut es immer in die direction. Nun finde ich aber, dass es doof aussieht, wenn dieses objekt an den Wänden des Raumes einfach bounced, weil es dann auf einmal in eine andere Richtung schaut. Ich dachte mir, man könne es sich langsam umdrehen lassen, wenn es in die nähe einer Mauer kommt.

    Ich brauche jetzt die möglichkeit den winkel zwischen dem objekt und der Mauer zu berechnen, damit es weiß, ob es sich nach links oder nach rechts (-> immer zum größeren winkel(siehe bild) ) drehen soll, um der mauer bestmöglich ausweichen zu können.

    Meine bisherige, aber nur in manchen fällen funktionierende Lösung ist einfach zu schwer zu erklären... außerdem funktioniert sie nicht richtig und nach einer besseren Lösung ist ja in diesem Thema gefragt...
  • Wenn du es über Winkel machen willst hätte die Mauer in deinem Bild also 0° bzw. 180°. Egal ob dein Objekt sich von oben oder unten an diese Waagerechte Wand nähert, es müsste sich auf einen der beiden Winkel angleichen.
    In deinem Bild hätte das Objekt ca die direction 320°. Es müsste sich also im Restklassenring 360° auf 0° oder 180° einstellen.
    Da 360° (= 0° in diesem Restklassenring) näher ist, willst du dich dorthin drehen, also gegen den Uhrzeigersinn.

    Ich würde den relativen Winkel zwischen Objekt und Wand bestimmen, also (0° - 320° = -320° bzw. 360° - 320° = 40°) oder (180° - 320° = -140°).
    Von diesen Werten brauchst du nur noch den Wert mit dem kleinsten Betrag nehmen, hier also 40° und dein Objekt also um 40° drehen (Vorzeichen ist schon an den GameMaker angepasst).
    Natürlich müssen alle Winkelwerte auch im Restklassenring liegen. Also nimm vorher alle Werte modulo 360, wenn du dir nicht sicher bist, ob sie schon stimmen.
    Ich denke das müsste so passen. Wenn du Probleme mit Restklassen oder Modulo hast, kannst du dich ja nochmal melden.
  • In der Theorie sieht es einfach so aus:

    Quellcode

    1. winkel += angle_difference( goto_winkel, winkel)/masse

    goto_winkel ist der Winkel, in den das Objekt gucken sollte, Winkel der, in den dein Objekt aber tatsächlich guckt. Masse ist wie langsam es sich drehen sollte.

    In deinem Fall würde es dann so aussehen (habe es nicht getestet, sollte aber gehen)

    GML-Quellcode

    1. image_angle += (((direction-image_angle+540) mod 360) - 180)/10;
    Ich habe mal 10 als Masse genommen. Wenn es dir zu langsam wird, kleinerer Wert.

    MfG SDX
  • Also erst mal Entschuldigung, dass ich erst so spät antworte, mein Internet ist im Moment ein wenig widerspenstig.

    @BadToxic
    Ich benötige eine Erklärung bezüglich der Restklassen. Modulo ist doch teilen mit Rest, wobei der Rest der Rückgabewert ist oder? (also 4/2=2 R0, 5/2 = 2 R1)

    @SDX
    Warum image_angle += [...] ? man will doch, dass es in die Richtung geht oder? Muss es dann nicht direction sein? Außerdem weiß ich nicht genau in welchem zusammenhang ich das einbauen muss... (ich habs erst mal so versucht, weil es mir am klügsten vor kam if (distance_to_object(Wall) <= 50) {[dein code]} aber das funktioniert mit image_angle gar nicht und mit direction absolut nicht richtig... )

    @J-L
    Hm... also erst mal Respekt. Das Problem ist, dass ich noch nicht genug Erfahrung mit dem GM habe um deinen Code ohne weiteres zu versthen, um dann den für mich wichtigen Teil zu verwenden... mit copy und paste bekomme ich es wohl hin, dass mein Objekt tut, was es soll, allerdings zappelt es bevor es sich für eine Richtung entscheidet immer noch etwas herum, was blöd aussieht. Ich wüsste gern, wie ich eine Höchstgeschwindigkeit festlege, wie ich festlege wie schnell oder langsam sich das Objekt dreht etc.
    Schön, wirklich schön, auch für andere wäre eine voll durch-kommentierte Version deines Codes.

  • Shizaso schrieb:

    Ich benötige eine Erklärung bezüglich der Restklassen. Modulo ist doch teilen mit Rest, wobei der Rest der Rückgabewert ist oder? (also 4/2=2 R0, 5/2 = 2 R1)

    Ja genau, "5 mod 2 = 1". Den Mathematischen Hintergrund zu Restklassen musst du dann auch nicht kennen. Das wissen sollte reichen.

    Shizaso schrieb:

    Muss es dann nicht direction sein?

    Ja, mit direction steuerst du die Richtung, in die sie ein Objekt per speed bewegt. image_angle muss nur an direction angepasst werden (image_angle=direction) damit dein Sprite in die Bewegungsrichtung zeigt.

    Wenn du nun noch willst, dass es sich langsam in die Richtung dreht speichere den berechneten Winkel in eine extra Variable, z.B. angle=...
    Dann kannst du in jedem Stepevent z.B.

    GML-Quellcode

    1. if(angle>direction) direction+=1;
    2. else if(angle<direction) direction-=1;
    schreiben. Hierbei kannst du die Geschwindigkeit angeben (Die Zahl 1).
  • ... Ich verstehe es immernoch nicht...
    Warum arbeiten alle immer mit image_angle, wenn image_angle = driection ist? Außerdem lasse ich den sprite sowieso mit mit draw_sprite_ext drawen, und nutze die direction als rotation...
    folgender Code funktioniert nicht richtig, aber das ist alles, was ich mit den informationen aus dem thread zustande gebracht habe... Es dreht sich nur nach rechts, nicht nach links.
    Außerdem frage ich mich warum direction-image_angle+540. Warum genau diese Zahl?

    Quellcode

    1. image_angle = direction;
    2. angle = (((direction-image_angle+540) mod 360) - 180);
    3. if (distance_to_object(Wall) <= 200)
    4. {
    5. if(angle > direction) {direction+=1;}
    6. if(angle <= direction) {direction-=1;}
    7. }
  • Shizaso schrieb:

    Warum arbeiten alle immer mit image_angle, wenn image_angle = driection ist? Außerdem lasse ich den sprite sowieso mit mit draw_sprite_ext drawen, und nutze die direction als rotation...

    Das wussten wir ja nicht, dann ignorier die Geschichte mit image_angle einfach.

    Shizaso schrieb:

    Außerdem frage ich mich warum direction-image_angle+540. Warum genau diese Zahl?

    Das gibt hier auch keinen Sinn mehr. (Und 540 = 360 + 180)

    Shizaso schrieb:

    image_angle = direction;

    Das kannst du also weglassen, arbeite nur mit direction.

    Shizaso schrieb:

    angle = (((direction-image_angle+540) mod 360) - 180);

    Diese Zeile muss ersetzt werden. Du kannst die Variante aus meinem ersten Post benutzen.
    Neben der direction brauchst du noch den Winkel der Wand...
  • [resigation]... Ich habe in diesem thread bisher mit keinem einzigen post, außer meinen eigenen etwas anfangen können...
    Das wirkt alles so, als wäre es ganz einfach aber ich bekomme keinen funktionierenden Code hin.[/resigation]

    BadToxic schrieb:

    Diese Zeile muss ersetzt werden.

    durch was?

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von Shizaso ()

  • Shizaso schrieb:

    durch was?

    Wenn du schon willst, dass wir alles für dich machen, verrat uns wenigstens in welcher Variablen der Winkel deiner Wand steckt. Also ich geh mal davon aus, in der Variablen Wall steckt dessen Winkel auch in direction, also eine waagerechte Wand hat die direction=0 oder direction=180 und eine senkrechte direction=90 oder direction=270. Und hab mir mal das Hirn verrenkt... versuch mal ob das so etwa passt:

    GML-Quellcode

    1. if(distance_to_object(Wall) <= 200){
    2. direction = direction mod 360; //Stellt sicher dass Wert nur zwischen 0 und 360 liegt.
    3. relative=(Wall.direction-direction) mod 180;
    4. if(relative < 90) direction+=5;
    5. else if(relative > 90) direction-=5;
    6. }

    Ich muss zugeben, das ist gar nicht so einfach...
  • Erstmal danke, aber

    BadToxic schrieb:

    Also ich geh mal davon aus, in der Variablen Wall steckt dessen Winkel auch in direction, also eine waagerechte Wand hat die direction=0 oder direction=180 und eine senkrechte direction=90 oder direction=270.
    Das stimmt nicht.
    Wenn ein objekt einfach bewegungslos im raum ist, dann ist die direction 0. Das ist aber nicht so wichtig, man kann ja so tun, als wäre sie es nicht, indem man anhand des x und y wertes im raum eine variabel bestimmen lässt, in diesem falle nenne ich sie dir

    Quellcode

    1. dir = 0
    2. if (y == 0) {dir = 180;}
    3. if (y == 784) {dir = 180;}
    4. if (x == 0) {dir = 90;}
    5. if (x == 1040) {dir = 90;}

    (mein raum ist 1056*800, das Wall sprite 16*16 groß.)
    dann muss man deinen code nur noch so verändern:

    Quellcode

    1. if(distance_to_object(Wall) <= 200)
    2. {
    3. direction = direction mod 360; //Stellt sicher dass Wert nur zwischen 0 und 360 liegt.
    4. relative=(Wall.dir-direction) mod 180;
    5. if(relative < 90) {direction+=5;}
    6. if(relative >= 90) {direction-=5;} // >= weil das Objekt ja auch direkt mit 90 grad auf die Wand zukommen kann
    7. }

    Ich hoffe mal, dass das die Wirkung, die deiner Meinung nach eintreten sollte nicht verändert.

    Nichts desto trotz funktioniert das ganze noch immer nicht... wer sich anschauen will was passiert:
    gmk
    exe

  • Shizaso schrieb:

    Wenn ein objekt einfach bewegungslos im raum ist, dann ist die direction 0.
    Das stimmt so nicht. Du kannst die direction eines Objektes auch verändern, ohne dass es sich bewegen muss. Statt dir hättest du also bedenkenlos direction nehmen können, macht aber nix.

    Shizaso schrieb:

    // >= weil das Objekt ja auch direkt mit 90 grad auf die Wand zukommen kann

    Das ist falsch, es ist der Relative Winkel zur Wand, 90° bedeutet also im rechten Winkel zu Wand...

    Dein Problem war, dass du nicht die Wand nimmst, die vor dir ist, sondern eine x-beliebige. Statt Wall musst du z.B. instance_nearest(x,y,Wall) nehmen. Wie du sehen wirst, macht er nun alles so, wie du beschrieben hast, aber es macht noch anderweitig Probleme. Wie z.B. dass er sich dreht, wenn er zu nah an einer Wand ist oder verwirrt wird, wenn er in eine Ecke läuft, aber das bekommst du schon hin, mit ein paar Überprüfungen.

    GML-Quellcode

    1. if(distance_to_object(Wall) <= 200){
    2. direction = direction mod 360; //Stellt sicher dass Wert nur zwischen 0 und 360 liegt.
    3. relative=(instance_nearest(x,y,Wall).dir-direction) mod 180;
    4. if(relative < 90) direction+=5;
    5. if(relative > 90) direction-=5;
    6. }
  • Ja, bei mir hats soweit geklappt, dachte ich. Wie gesagt, wenn er genau in eine Ecke läuft, klappt es nicht, weil er dann zwischen zwei senkrecht zueinander stehenden Wänden oszilliert, das muss abgefangen werden. Dann reagiert er ja auch auf die Wand, wenn er von ihr wegläuft, dadurch vibriert er so, hier kann man so eine Art Einschränkung auf den Blickwinkel machen, z.B. mit instance_place... so dass es nur die Wand vor einem benutzt. Ich hab noch ne Weile mit den Werten rumgespielt... so richtig gute Ergebnise bekomm ich auch nicht locker hin.
    Aber da haste dir auch was anstrengendes rausgesucht. Was genau soll es denn werden?
  • Soweit ich weiß "spiegelt" sie einfach die Geschwindigkeiten.
    Also wenn ich z.B. von links an eine Wand kommte hspeed*=-1; und analog die vertikale Geschwindigkeit vspeed*=-1;...
    Und um zu unterscheiden, von welcher Richtung es kommt, wird vermutlich einmal nur die x-Koordinate, einmal die y-Koordinate und danach bei bedarf beide verändert und auf Kollision geprüft.
  • Ich habe jetzt mal aus der Fahrzeug-KI ein Script gemacht, dass du nur noch im step event des Objectes einfügen musst:

    GML-Quellcode

    1. avoid_wall(reaction_distance,obj_wall,reaction_sharpness,enabled)


    reaction_distance: wie weit entfernt von der mauer soll er reagieren; entweder ein fester wert oder besser z.B. "speed" oder "speed*1.5" (also geschwindigkeitsabhängig) (achtung: != Entfernung in pixeln!)
    obj_wall: Name der Wand
    reaction sharpness: soll er eher träge oder sehr "zackig" reagieren? Je höher der wert, desto schärfer die Wendung! ( empfohlen zwischen 1 und 5)
    enabled: soll er auch vorher abbremsen und bei freier Bahn Beschleunigen? (Achtung: wenn ja, dann unbedingt eine geschwindigkeitsabhängige reibung einbaun ala

    GML-Quellcode

    1. friction=speed*speed/200

    ansonsten wird er zu schnell)

    P.S.: ich darf leider keine .gml datei hochladen, deswegen hab ich sie verpackt

    EDIT:UPDATE: BUG GEFIXT
    Dateien
    • avoid_wall.rar

      (397 Byte, 119 mal heruntergeladen, zuletzt: )

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von J-L ()