Lenkrakete



  • Tach Leute,

    möchte gerne eine Art Lenkrakete machen, und zwar habe ich einen festen Punkt (m_target) und eine Rakete. Die Rakete hat eine konstante Geschwindigkeit von 2 Pixel / Frame. Das habe ich schon so realisiert :
    Code:

    float speed = 2;
    float tempx = sin(m_rot)*speed;  //m_rot = direction
    float tempy = cos(m_rot)*speed;
    m_xPos += tempy;     // m_xPos x Position der Rakete
    m_yPos += tempx;
    

    Jetzt suche ich einen Algoryhtmus, der mir zeigt, ob die Rakete nach links lenken oder nach Rechts lenken soll. Die Rakete einen eine Rotationsgeschwindigkeit von 4° pro Frame, also 2pi*(4/360) radians.
    Alles spielt sich in 2D ab.
    Hat irgendwer nen Tipp oder ne Lösung ?

    lg



  • Was ich an meinem (leider kürzlich verstorbenen) Bruder immer bewundert habe:
    Er hat grundsätzlich alle Jobangebote der Rüstungsindustrie abgelehnt.

    Ansonsten sollte man "Algoryhtmus" erst mal einigermaßen richtig schreiben können, bevor man nach einem fragt.

    Ich weiß, daß ich mich mit meinen posts immer unbeliebter mache...



  • EOP schrieb:

    Was ich an meinem (leider kürzlich verstorbenen) Bruder immer bewundert habe:
    Er hat grundsätzlich alle Jobangebote der Rüstungsindustrie abgelehnt.

    Ansonsten sollte man "Algoryhtmus" erst mal einigermaßen richtig schreiben können, bevor man nach einem fragt.

    Ich weiß, daß ich mich mit meinen posts immer unbeliebter mache...

    Danke für den hilfreichen Post.
    Und damit du heute schlafen kannst : algorithmus.

    gute Nacht.

    lg



  • Du kannst zunächst die relativen Zielkoordinaten ausrechnen

    float dx = targetx - raketex;  
      float dy = targety - raketey;
    

    diese dann in das Raketen-eigene Koordinatensystem umrechnen (drehen). Die Rakete hat eine "nach vorne"-Achse und eine "zur Seite"-Achse:

    float vorne = dx*sin(m_rot) + dy*cos(m_rot);  
      float seite = dx*cos(m_rot) - dy*sin(m_rot);
    

    Wenn die Rakete jetzt schon direkt auf das Ziel zufliegt sollte seite gleich Null sein und vorne gleich dem Abstand zwischen Rakete und Ziel sein. Aus dem Quetienten seite/vorne lässt sich über den Arcustangens der Korrekturwinkel bestimmen, der dann noch auf +/-4 Grad beschränkt werden muss:

    float kwinkel = atan(seite/vorne);
      if (kwinkel<-4*pi/180) kwinkel=-4*pi/180; else  
      if (kwinkel>+4*pi/180) kwinkel=+4*pi/180;
      m_rot += kwinkel
    

    Danach sollte prüfen, ob m_rot nicht unnötig groß oder klein geworden ist:

    if (m_rot> 2) m_rot -= 2*pi; else
      if (m_rot<-2) m_rot += 2*pi;
    

    Denn sonst kannst Du noch Ärger mit Rundungsfehlern bekommen.

    Das ganze lässt sich auch komplett ohne sin , cos und atan regeln. Dazu kannst Du statt m_rot einfach einen normalisierten Richtungsvektor speichern und diesen dann verändern.

    Alle Angaben ohne Gewähr (Ich habe den Code nicht getestet).

    HTH,
    SP



  • cirbi schrieb:

    EOP schrieb:

    Was ich an meinem (leider kürzlich verstorbenen) Bruder immer bewundert habe:
    Er hat grundsätzlich alle Jobangebote der Rüstungsindustrie abgelehnt.

    Ansonsten sollte man "Algoryhtmus" erst mal einigermaßen richtig schreiben können, bevor man nach einem fragt.

    Ich weiß, daß ich mich mit meinen posts immer unbeliebter mache...

    Danke für den hilfreichen Post.
    Und damit du heute schlafen kannst : algorithmus.

    gute Nacht.

    lg

    Na es geht doch. :p



  • Tach,

    leider klappt dein Code auch nicht 😕
    Sprich er fliegt in die Richtung, dann dreht er und is weg 😞

    Hier nochma alles code :

    void CProjectil::Update(float time)
    {
    	float speed = 2;
    	float tempx = sin(m_rot)*speed;
    	float tempy = cos(m_rot)*speed;
    
    	float dx = m_targetX - m_xPos;  
    	float dy = m_targetY - m_yPos;
    	float vorne = dx*sin(m_rot) + dy*cos(m_rot);  
    	float seite = dx*cos(m_rot) - dy*sin(m_rot);
    	float kwinkel = atan(seite/vorne);
    	if (kwinkel<=-4.0f*3.1415f/180) kwinkel=-0.8f*3.1415f/180.0f; else  
    	if (kwinkel>=+4.0f*3.1415f/180) kwinkel=+0.8f*3.1415f/180.0f;
    	m_rot += kwinkel ;
    	if (m_rot> 2.0f) m_rot -= 2.0f*3.1415f; else
    	if (m_rot<-2.0f) m_rot += 2.0f*3.1415f; 
    
    	m_xPos += tempy;
    	m_yPos += tempx;
    }
    
    void CProjectil::setTarget(float x, float y)
    {
    	m_targetX = x;
    	m_targetY = y;
    }
    


  • Nimm doch atan2() , wenn du den Quadranten berücksichtigen willst. Ausserdem nimmt man normalerweise eher den Cosinus für die x-Achse, aber das hängt natürlich davon ab, wo dein Winkel 0 ist. Und muss das Folgende so sein? Falls ja, solltest du die Bezeichner überdenken.

    m_xPos += tempy; 
        m_yPos += tempx;
    

    Ich hab dir doch auf spieleprogrammierer.de bereits gesagt, du solltest dir mal Klassen und Funktionen überlegen. Plötzlich hast du ein fast gleiches Problem, aber brauchst dich wieder stundenlang abzumühen und Code zu kopieren. Alleine schon wie oft du Pi ausschreibst oder zwischen Grad und Bogenmass umrechnest...



  • Tach,

    kenne mich mit Vektoren nicht aus, darum schreibe ich mir keine Klasse dafür.

    Wieso funktioniert die oben genannte Funktion nicht ?
    Also bei 0° zeigt das Objekt nacht rechts.
    Bei 90° nach unten.
    180° links.
    270° oben.

    Vllt hilft das 😕

    lg



  • cirbi schrieb:

    kenne mich mit Vektoren nicht aus, darum schreibe ich mir keine Klasse dafür.

    Ein 2-dimensionaler Vektor sollte kein Problem sein, da das dann nur x und y besitzt und eigentlich nichts weiteres ist, als das, was du hier darstellst. Allerdings einfacher zu verstehen und man kann dann auch einfacher damit operieren, als immer ein x und y mitzutragen.

    Alleine schon das sollte helfen:

    struct vec2d
    {
     float x;
     float y;
    };
    

    Wenn du hier noch ein paar Operatoren, wie +,- usw. beibringst, dann ist das schon sehr viel Wert.



  • cirbi schrieb:

    Wieso funktioniert die oben genannte Funktion nicht ?
    Also bei 0° zeigt das Objekt nacht rechts.
    Bei 90° nach unten.
    180° links.
    270° oben.

    Das passt aber nicht ganz zu Deinem sin / cos -Kram. Es sei denn, deine Y-Achse zeigt nach rechts und Deine X-Achse zeigt nach unten.

    Hier ist mal eine recht kurze Lösung mit komplexen Zahlen:

    #include <iostream>
    #include <complex>
    #include <cmath>
    #include <algorithm>
    
    using namespace std;
    
    typedef complex<float> cfloat;
    
    class rakete
    {
    	cfloat richtung; //< immer auf 1 normiert
    public:
    	cfloat position; //< Potition der Rakete
    	cfloat ziel;     //< Zielposition
    	float speed;
    	float wendigkeit;
    
    	explicit rakete(float x=0, float y=0, float rot=0,
    			float zielx=0, float ziely=0)
    		: richtung(polar(1.f,rot)), position(x,y),
    		  ziel(zielx,ziely), speed(1), wendigkeit(0.07f) {}
    
    	void set_rot(float rot) {richtung = polar(1.f,rot);}
    
    	float get_rot() const {return arg(richtung);}
    	float get_x() const {return position.real();}
    	float get_y() const {return position.imag();}
    
    	void update();
    };
    
    void rakete::update()
    {
    	float k = arg( (ziel-position)*conj(richtung) );
    	k = max(-wendigkeit,min(k,wendigkeit));
    	richtung *= polar(1.f,k);
    	richtung /= abs(richtung);
    	position += richtung * speed;
    }
    
    ostream& operator<<(ostream& os, rakete const& r)
    {
    	os << "(x=" << r.get_x() << ", y=" << r.get_y() << ", rot=";
    	os << r.get_rot() << ')';
    	return os;
    }
    
    int main()
    {
    	// Rakete startet bei (0,0), fliegt in Richtung +X
    	// mit Geschwindigkeit 1 soll auf das Ziel (15,5) lenken.
    	rakete r (0,0, 0, 15,5);
    	for (int zeit=0; zeit<20; ++zeit) {
    		cout << r << '\n';
    		r.update();
    	}
    }
    

    Die "Wendigkeit" von 0.07 ergibt sich aus 4°*pi/180.

    Gruß,
    SP



  • Tach,

    hab jetz den Winkel rausbekommen.
    Aber ich weiß nicht ob er nach link oder rechts lenken soll, immer nur die zahlen von 0 bis pi ausgibt, egal ob target oben oder unten ist.

    Hier der relevante Code :

    void cplayer::update(float vx, float vy)
    {
        float speed = 4;
        float tempx = cos(rot)*speed;
        float tempy = sin(rot)*speed;
    
        float lineB = distance(vx,vy,x,y);
        float lineA = distance(vx,vy,x+tempx,y+tempy);
    
        float deg = innerRad(lineA, lineB, speed); // hier von 0 bis pi
    
        if(deg <= DegToRad(2.0f))
        {
            rot+= deg;
        }
        else if(deg >= 3.1415f)
        {
            rot+= DegToRad(2.0f);
        }
        else if(deg <= 3.1415f)
        {
            rot-= DegToRad(2.0f);
        }
    
        x += tempx;
        y += tempy;
    }
    
    #define pi 3.1415926f
    
    inline float DegToRad(float deg)
    {
        return 2.0f*pi*(deg/360.0f);
    }
    
    inline float distance(float x1, float y1, float x2, float y2)
    {
        return sqrt(pow(x1-x2,2)+pow(y1-y2,2));
    }
    
    inline float innerRad(float lineA, float lineB, float lineC)
    {
        return acos( ( pow(lineB,2)+pow(lineC,2)-pow(lineA,2) )/ (2*lineB*lineC ) );
    }
    

    lg



  • Kennst Du das Skalarprodukt? Das ist extrem praktisch, wenn man wissen will, ob zwei gegebene Richtungen ungefähr gleich sind (Skalaprodukt ist dann größer Null), ob sie senkrecht zueinander sind (Skalarprodukt ist dann Null), oder ob sie in die entgegengesetzte Richtung zeigen (Skalarprodukt ist dann negativ).

    Die Richtung, in die Deine Rakete fliegt ist (tempx,tempy). Die Richtung in der das Ziel liegt -- relativ zur Rakete -- ist (vx-x,vy-y). Eine Richtung senkrecht zur Flugrichtung ist die Flugrichtung um 90° gedreht: (-tempy,tempx). Diese liegt von der Rakete aus gesehen auf einer bestimmten Seite. Wenn also Deine X/Y Achse nach rechts und unten zeigt, dann entspricht diese 90° Drehung einer Drehung im Uhrzeigersinn -- es ist also von der Rakete ausgesehen "rechts". Wenn Deine Y-Achse nach oben zeigt ist es "links" und eine Drehung entgegen dem Uhrzeigersinn.

    Berechne nun das Skalarprodukt zwischen (-tempy,tempx) und (vx-x,vy-y). In diesem Fall ist es (vy-y)*tempx-(vx-x)*tempy. Ist der Wert größer als Null, musst Du rot vergrößern -- sonst verkleinern.

    Gruß,
    SP



  • da du die lösung jz schon kennst, nur noch einige punkte:

    drakon schrieb:

    Alleine schon das sollte helfen:

    struct vec2d
    {
     float x;
     float y;
    };
    

    Wenn du hier noch ein paar Operatoren, wie +,- usw. beibringst, dann ist das schon sehr viel Wert.

    #define pi 3.1415926f
    ->
    const float pi (3.141592681);

    die inline`s brauchst du imho nicht

    if(deg >= 3.1415f)
    hat dir pi dann doch nicht mehr gefallen? ;P

    cplayer::update(float vx, float vy)
    {
      float speed = 4;
    

    klingt so, als ob die rakete ein spieler wäre
    wenn das falsch ist, solltest du mal überlegen, warum du die geschwindigkeit nicht als member hast
    außerdem ist vx bzw vy evtl nicht der allerbeste variablen-name... ich könnte damit zumindest nix anfangen

    tempx
    wie wärs statt dessen mit next_step_x oder act_x oder move_x oder keine Ahnung...

    würd ich die fkt wahrscheinlich in move und calculate oder so was in der richtung aufteilen...
    wenn ich das richtig verstehe, willst du ja die rakete mit dem aktuellen winkel fliegen lassen und für das nächste stück den winkel berechnen...
    (würd ich auch nich so machen - wieso nicht gleich den neuen wert nehmen?)

    usw...

    sieht irgendwie ein wenig so aus, als ob du dir das leben unnötig kompliziert machen würdest, in dem du deine fkt so groß und unübersichtlich werden lässt...

    bb


Anmelden zum Antworten