Sinus&Cosinus: Homing Missiles



  • Hallo zusammen,

    ich lese schon lange in diesem Forum mit, seit ich vor etwa einem Jahr mit C++ begonnen habe, heute poste das erste mal da ich ziemlich ratlos bin.

    Ich habe Amarillions ersten Teil seines Tutorials "Sin & Cos: The Programmer's Pals!"
    http://www.helixsoft.nl/articles/circle/sincos.htm
    übersetzt. Die Codes bin ich gerade dabei auf den neuesten Stand zu bringen. Ich werde verschiedene Versionen beilegen (SFML, SDL, Allegro 5) und noch einige mehr....!

    Angefangen habe ich mit graphics.h da diese Bibliothek sehr einfach und grundlegend ist. Mein Problem ist der Code im Teil "Using atan2(): homing missiles " (circ7.c). Die Rakete findet nur einmal ihr Ziel, wird danach ein neues Ziel vorgegeben, jagt sie daran vorbei 😕 . Gebe ich die neuen Zielkoordinaten in der If-Bedingung manuell fest vor z.B.:

    if (new_target == true)
            {
                target_x = 300;
                target_y = 400;
                new_target = false;
            }
    

    klappt gar nichts mehr! 😕

    Hier mein vollständiger Code:

    #include<windows.h>
    #include<graphics.h>
    #include<cmath>
    
    // circ7
    
    void home_in();
    
    int SCREEN_W = 600;
    int SCREEN_H = 400;
    
    main(int argc, char*argv[])
    {
       // Deklaration und Initialisierung des Grafiktreibers
        int graphdriver, graphmode;
        graphdriver = DETECT;
        initgraph(&graphdriver, &graphmode, "Draw_Sine"); // graphics.h kann keine Windowstitel setzen
    
        setbkcolor(0); // Hintergrundfarbe des Ausgabefensters schwarz
        cleardevice(); // Löschen des Ausgabefensters
    
        setcolor(15); // Textfarbe weiss (15)
        settextstyle(10, HORIZ_DIR, 2); // Fontsyle, Richtung und Grösse
        //outtextxy(20, 120, "Sinuskurve zeichnen: "); // Textausgabe
    
        home_in(); // Funktionsaufruf von draw_circle()
    
     return 0;
    }
    
    void home_in ()
    {
        // the x, y position of the homing missile
        int x = SCREEN_W / 2;
        int y = SCREEN_H / 2;
        // the angle and length of the missile's velocity vector
        int angle = 0;
        int length = 1;
        int angle_stepsize =  1;
        // determines whether the missile has reached
        // the target and a new one should be chosen
        bool new_target = true;
        // angle to the target
        int target_angle;
        // position of the target
        int target_x;
        int target_y;
    
        while (!GetAsyncKeyState(VK_ESCAPE))
        {
            setbkcolor(0); // Hintergrundfarbe des Ausgabefensters schwarz
            cleardevice(); // Löschen des Ausgabefensters
            // choose new target randomly when needed
            if (new_target == true)
            {
                target_x = ((SCREEN_W + rand() % (2 * SCREEN_W)) / 4);
                target_y = ((SCREEN_H + rand() % (2 * SCREEN_H)) / 4);
                new_target = false;
            }
    
            // draw a pixel where the target is
            putpixel ((target_x), (target_y),15);
    
            // draw the missile
            // (actually a circle with a line representing the angle)
            setcolor(WHITE);
            circle (x, y, 20);
            line (x, y, x +  (9 * cos (angle)), y +  (9 * sin (angle)));
    
            // move the missile
            x = x + length * cos (angle);
            y = y + length * sin (angle);
    
            // if we are very close to the target, set a new target
            if (abs (x - target_x) + abs (y - target_y) < 10)
            {
                 new_target = true;
            }
    
            // calculate the angle from the missile to the target
            target_angle = atan2 (target_y - y, target_x - x);
    
            // Determine whether we should turn left or right.
            // Note that itofix (128) represents half a circle.
            // We use & 0xFFFFFF as a trick to get an angle
            // between 0 and 256.
            if (((angle - target_angle) & 0xFFFFFF) < 128)
            {
                angle = (angle - angle_stepsize) & 0xFFFFFF;
            }
            else
            {
                angle = (angle + angle_stepsize) & 0xFFFFFF;
            }
            delay (20);
        }
    }
    

    Ich versuche nun schon seit mehreren Tagen auf die Lösung zu kommen aber irgendwie seh ich den Wald vor lauter Bäumen nicht!
    So schwer schienen mir die Codes nicht zu sein ich dachte ich wär da durchgestiegen!
    Ist ein Jahr C++ Erfahrung zu wenig?

    Wäre super wenn Ihe mir da helfen könntet.

    Viele Grüße und vielen Dank für Eure Mühe,
    Merlin



  • Das Problem wird wohl bei der Ganzzahlarithmetik liegen, d.h. für angle solltest du float oder double benutzen.
    Außerdem verlangen cos und sin den Parameter im Bogenmaß (also 0-PI).

    Oder hast du versucht die "Use fixed, not float"-Lösung zu implementieren? Dann mußt du auch die passenden Sinus- und Cosinus- und Arkustangens-Funktionen benutzen.



  • Hi Th69,

    ja ich habe versucht die "Use fixed, not float"-Lösung zu implementieren.
    Welches sind die korrekten Sinus-, Cosinus- und Arkustangens-Funktionen?
    Finde ich die in der cmath?

    Viele Grüße und danke für Deine Mühe,
    Merlin_MLN



  • Das scheinen ja die fsin, fcos etc. zu sein, und die sind in <allegro.h> definiert.

    Aber den Umweg über fixed würde ich nicht mehr gehen - bei heutigen Rechnern (und Grafikkarten) kannst du einfach float-Berechnungen benutzen (außer du benutzt spezielle Umgebungen).



  • Hi Th69,

    Das scheinen ja die fsin, fcos etc. zu sein, und die sind in <allegro.h> definiert.

    Ja, fsin(), fcos() sind in allegro.h.
    Aber ich möchte die graphics.h behalten!.

    Gibt es eine gute Mathelib etc. für C++ die ich für Festkommaarithmetik verwenden kann 😕 ? Ich kenne leider nur die cmath für C++ und die verlangt das Bogenmaß!

    Aber den Umweg über fixed würde ich nicht mehr gehen - bei heutigen Rechnern (und Grafikkarten) kannst du einfach float-Berechnungen benutzen (außer du benutzt spezielle Umgebungen).

    Klar heute sind wir schnell genug! 🙂

    Viele Grüße und danke für Deine Hilfe,
    Merlin



  • Hi,

    ich hab darüber nachgedacht. Th69 hat vollkommen recht es macht keinen Sinn an dem fixed-Gedöns festzuhalten weder aus lerntechnischer Hinsicht oder sonstwie!

    Ich habe den Code abgeändert aber leider trifft die Rakete ihr Ziel jetzt überhaupt nicht mehr! 😕

    Ich hab nicht den leisesten Schimmer wo der Fehler liegt. 😕

    void home_in ()
    {
        // the x, y position of the homing missile
        int x = SCREEN_W / 2;
        int y = SCREEN_H / 2;
        // the angle and length of the missile's velocity vector
        double angle = 0.0;
        int length = 1;
        double angle_stepsize = 1*  PI / 180;
        // determines whether the missile has reached
        // the target and a new one should be chosen
        bool new_target = true;
        // angle to the target
        double target_angle;
        // position of the target
        int target_x;
        int target_y;
    
        while (!GetAsyncKeyState(VK_ESCAPE))
        {
            setbkcolor(0); // Hintergrundfarbe des Ausgabefensters schwarz
            cleardevice(); // Löschen des Ausgabefensters
            // choose new target randomly when needed
            if (new_target == true)
            {
                target_x = ((SCREEN_W + rand() % (2 * SCREEN_W)) / 4);
                target_y = ((SCREEN_H + rand() % (2 * SCREEN_H)) / 4);
                new_target = false;
            }
    
            // draw a pixel where the target is
            putpixel ((target_x), (target_y),15);
    
            // draw the missile
            // (actually a circle with a line representing the angle)
            setcolor(WHITE);
            circle (x, y, 20);
            line (x, y, x +  (9 * cos (angle)), y +  (9 * sin (angle)));
    
            // move the missile
            x = x + length * cos (angle);
            y = y + length * sin (angle);
    
            // if we are very close to the target, set a new target
            if (abs (x - target_x) + abs (y - target_y) < 10)
            {
                 new_target = true;
            }
    
            // calculate the angle from the missile to the target
            target_angle = atan2 (target_y - y, target_x - x);
    
            if (fmod(angle - target_angle, 2*PI) < PI)
            {
                angle = (angle - angle_stepsize) ;
            }
            else
            {
                angle = (angle + angle_stepsize) ;
            }
            delay (20);
        }
    }
    

    Vielen Dank für eure Mühe,
    Merlin



  • Dann debugge es oder laß dir die Werte (x, y, angle, ...) in einer Konsole (oder Trace-Fenster oder Logdatei) ausgeben.



  • Atan2 gibt einen Wert zwischen - pi und pi. Was versprichst du dir von dem fmod? Ich denke, du kannst mit

    if(target_angle<0)
    ...
    else
    ....
    

    arbeiten



  • Hi Schlangenmensch,

    stimmt den fmod hab ich schon entfernt.
    Aber es muss heißen:

    if ((angle - target_angle) < PI)
            {
                angle = (angle - angle_stepsize) ;
            }
            else
            {
                angle = (angle + angle_stepsize) ;
            }
    

    Wenn ich auf 0 prüfe nimmt die Rakete einen anderen Weg und fliegt sogar etwas näher am Ziel vorbei aber sonst genauso chaotisch!

    Grüße und danke für Deine Hilfe,
    Merlin



  • So, irgendwie hat mich das jetzt etwas in den Fingern gejuckt.

    Mehrere Punkte:
    1: Du hast noch Typenunverträglichkeiten durch dein Wechsel auf floatingpoint. Grade bei Divisionen ist das blöd, da da schonmal Kommastellen abegschnitten werden. Auch solltest du von abs() auf fabs() wechseln.

    2: Wenn du mit einer Länge von 1 arbeitest schießt du schnell am Ziel vorbei, passt aber den Winkel nur sehr langsam an. Ich habe jetzt mal 0.1 als Länge genommen.

    3: Dein Winkel Vergleich oszilliert um PI:

    if ((angle - target_angle) < 0)
        {
          angle = (angle + angle_stepsize);
        }
        else
        {
          angle = (angle - angle_stepsize);
        }
    

    funktioniert mit der Länge 0.1.



  • Nur ein kleiner Tip am Rande: Um zu entscheiden ob die Rakete nach links oder rechts drehen soll braucht man keine Winkelfunktionen.
    Abstandsvektor Rakete->Ziel, 90° drehen, Flugvektor draufprojizieren (dot product), Vorzeichen prüfen. Fertig.
    Da man bloss am Vorzeichen interessiert ist kann man sich auch das Normalisieren sparen.


  • Mod

    LeftRight(U,V)
    {
    return U.x*V.y-U.y*V.x
    }
    


  • @Schlangenmensch
    Ich habe alles so abgeändert wie Du es gesagt hat aber jetzt fliegt die Rakete auf einer Kreisbahn!

    Könntest Du Deinen kompletten funktionierenden Code posten?
    Ich mach glaub ich einen Kardinalsfehler!

    #include<windows.h>
    #include<graphics.h>
    #include<cmath>
    
    #define PI 3.141592654
    
    // circ7
    
    void home_in();
    
    int SCREEN_W = 600;
    int SCREEN_H = 400;
    
    main(int argc, char*argv[])
    {
       // Deklaration und Initialisierung des Grafiktreibers
        int graphdriver, graphmode;
        graphdriver = DETECT;
        initgraph(&graphdriver, &graphmode, "Draw_Sine"); // graphics.h kann keine Windowstitel setzen
    
        setbkcolor(0); // Hintergrundfarbe des Ausgabefensters schwarz
        cleardevice(); // Löschen des Ausgabefensters
    
        setcolor(15); // Textfarbe weiss (15)
        settextstyle(10, HORIZ_DIR, 2); // Fontsyle, Richtung und Größe
        //outtextxy(20, 120, "Sinuskurve zeichnen: "); // Textausgabe
    
        home_in(); // Funktionsaufruf von home_in()
    
     return 0;
    }
    
    void home_in()
    {
        // the x, y position of the homing missile
        double x = SCREEN_W / 2;
        double y = SCREEN_H / 2;
        // the angle and length of the missile's velocity vector
        double angle = 0;
        double length = 0.1;
        double angle_stepsize =  1 * PI / 180;
        // determines whether the missile has reached
        // the target and a new one should be chosen
        bool new_target = true;
        // angle to the target
        double target_angle;
        // position of the target
        double target_x;
        double target_y;
    
        while (!GetAsyncKeyState(VK_ESCAPE))
        {
            setbkcolor(0); // Hintergrundfarbe des Ausgabefensters schwarz
            cleardevice(); // Löschen des Ausgabefensters
            // choose new target randomly when needed
            if (new_target == true)
            {
                target_x = ((SCREEN_W + rand() % (2 * SCREEN_W)) / 4);
                target_y = ((SCREEN_H + rand() % (2 * SCREEN_H)) / 4);
                new_target = false;
            }
    
            // draw a pixel where the target is
            putpixel ((target_x), (target_y),15);
    
            // draw the missile
            // (actually a circle with a line representing the angle)
            setcolor(WHITE);
            circle (x, y, 20);
            line (x, y, x +  (9 * cos (angle)), y +  (9 * sin (angle)));
    
            // move the missile
            x = x + length * cos (angle);
            y = y + length * sin (angle);
    
            if (fabs (x - target_x) + abs (y - target_y) < 10)
            {
                 new_target = true;
            }
    
            // calculate the angle from the missile to the target
            target_angle = atan2 (target_y - y, target_x - x);
    
            if ((angle - target_angle) < 0)
            {
                angle = (angle - angle_stepsize) ;
            }
            else
            {
                angle = (angle + angle_stepsize) ;
            }
            delay (1);
        }
    }
    

    Danke für Eure Mühe und Hilfe,
    Merlin_2



  • Ich poste meinen Code morgen früh, wenn ich wieder am Computer sitze.

    Edit: mach mal bei deinem angle stepsize: PI/180.
    Also floating point division.



  • @Schlangenmensch
    Du wolltest doch Deinen Code posen 😕 ;-))

    Gruß,
    Merlin_2



  • Stimmt...hatte gehofft, dass sich das mit der angepassten Winkel Stepgröße ergeben hätte.

    Ich habe den Grafikkram auskommentiert und verlasse die Schleife wenn das Ziel getroffen wird. Außerdem sind noch ein paar Debugausgaben drin

    #include <iostream>
    #include <math.h>
    
    #define SCREEN_W 800.
    #define SCREEN_H 600.
    #define SCREEN_Wi 800
    #define SCREEN_Hi 600
    
    #define PI 3.14159265359
    
    void home_in()
    {
      // the x, y position of the homing missile
      double x = SCREEN_W / 2.0;
      double y = SCREEN_H / 2.0;
      std::cout << x << std::endl;
      // the angle and length of the missile's velocity vector
      double angle = 0.0;
      double length = 0.1;
      double angle_stepsize = PI / 180.;
      // determines whether the missile has reached
      // the target and a new one should be chosen
      bool new_target = true;
      // angle to the target
      double target_angle;
      // position of the target
      double target_x;
      double target_y;
      target_x = ((SCREEN_W + rand() % (2 * SCREEN_Wi)) / 4);
      std::cout << target_x << std::endl;
      target_y = ((SCREEN_H + rand() % (2 * SCREEN_Hi)) / 4);
      new_target = false;
      int count = 0;
      while (!new_target && count < 10000)
      {
        //setbkcolor(0); // Hintergrundfarbe des Ausgabefensters schwarz
        //cleardevice(); // Löschen des Ausgabefensters
                       // choose new target randomly when needed
        ++count;
      /*
        if (new_target == true)
        {
          target_x = ((SCREEN_W + rand() % (2 * SCREEN_W)) / 4);
          target_y = ((SCREEN_H + rand() % (2 * SCREEN_H)) / 4);
          new_target = false;
        }
        */
        // draw a pixel where the target is
       // putpixel((target_x), (target_y), 15);
    
        // draw the missile
        // (actually a circle with a line representing the angle)
        //setcolor(WHITE);
       // circle(x, y, 20);
       // line(x, y, x + (9 * cos(angle)), y + (9 * sin(angle)));
    
        // move the missile
        x = x + length * cos(angle);
        y = y + length * sin(angle);
    
        // if we are very close to the target, set a new target
        if (fabs(x - target_x) + fabs(y - target_y) < 10)
        {
          new_target = true;
        }
    
        // calculate the angle from the missile to the target
        target_angle = atan2(target_y-y, target_x-x);
    
        if ((angle - target_angle) < 0)
        {
          angle = (angle + angle_stepsize);
        }
        else
        {
          angle = (angle - angle_stepsize);
        }
        //delay(20);
        if ((count % 1000) == 0) {
          std::cout << target_x << " " << target_y << " " << target_angle << std::endl;
          std::cout << x << " " << y << " " << angle << std::endl;
          std::cout << "dif: " << " " << angle - target_angle << std::endl << std::endl;
        }
      }
      if (new_target)
        std::cout << "hit \n";
      else
        std::cout << "no hit \n";
    
      std::cout << target_x <<" " << target_y << std::endl;
      std::cout << x << " " << y << std::endl;
    
    }
    
    int main() {
      home_in();
      std::cin.get();
    }
    

    Edit Da ich auf dem Sprung war, habe ich Den Code nicht aufgeräumt, ich bitte das zu verzeihen


Anmelden zum Antworten