Pilotage automatique d'un avion 2D (hyper simplifié)

Partagez votre expérience de PureBasic avec les autres utilisateurs.
comtois
Messages : 5186
Inscription : mer. 21/janv./2004 17:48
Contact :

Pilotage automatique d'un avion 2D (hyper simplifié)

Message par comtois »

Pour donner suite à la demande de Slide voici la dernière version de mon code (avec l'aide de lutin), je l'améliorerai à l'occasion , c'est open , si vous avez des propositions, ça peut intéresser du monde :)

L'objectif : Pouvoir indiquer à l'aide d'une souris la destination que doit atteindre un avion.
Contrainte : L'avion doit se déplacer selon un rayon de courbure constant pour prendre un virage.
L'avion se déplace à vitesse constante , aussi bien en ligne droite que dans un virage.

La touche [F1] permet de visualiser le cercle autour duquel l'avion doit tourner.

Pour le fun j'ai aussi ajouté la modification de la taille de l'avion par les flèches haut et bas.

Code : Tout sélectionner

;Comtois et Lutin 12 mai 2007
;PB 4.02
EnableExplicit
;{ Fichier Include à créer éventuellement ici
Enumeration 
  #Souris
  #Destination
EndEnumeration
Structure s_Vecteur
  x.f
  y.f
EndStructure
Structure s_Avion
  Pos.s_Vecteur
  Vitesse.f
  Omega.f
  Rayon.f
  Direction.s_Vecteur
  NbSommet.l
  Scale.l
  Angle.f
  Cherche.l
  Sommet.s_Vecteur[12]
EndStructure
Macro COPIE_VECTEUR(V, V1)
  V\x = V1\x
  V\y = V1\y
EndMacro 
Macro ADDITION_VECTEUR(V, V1, V2)
  V\x = V1\x + V2\x
  V\y = V1\y + V2\y
EndMacro
Macro SOUSTRACTION_VECTEUR(V, V1, V2)
  V\x = V1\x - V2\x
  V\y = V1\y - V2\y
EndMacro
Macro PRODUIT_SCALAIRE(V1, V2)
  (V1\x * V2\x + V1\y * V2\y) 
EndMacro

Define.s_Vecteur Destination, AvionDestination, CercleDestination, Cercle, Normale, T1, Temp
Define.s_Vecteur MemoireDirectionTangente
Define.f DistanceCercleDestination, DistanceAvionDestination, Angle1, Angle2 
Define.f DistancePointDestination, ProduitScalaire, AngleAvion, AngleTangente, CosA, SinA 
Define.l i, CalculNormale, Affiche 
Define.s_Avion Avion 
Define.l ScreenX, ScreenY

Declare.f atan2f(y.f, x.f)
Declare InitAvion(*Avion.s_Avion)

;}
;{ Init et ouverture écran
If ExamineDesktops()
  ScreenX = DesktopWidth(0)
  ScreenY = DesktopHeight(0)
Else
  ScreenX = 1024
  ScreenY = 728
EndIf      
If InitSprite() = 0 Or InitKeyboard() = 0 Or InitMouse() = 0
  MessageRequester( "Erreur" , "Impossible d'initialiser DirectX 7 Ou plus" , 0 )
  End
ElseIf OpenScreen( ScreenX,ScreenY, 32, "Math" ) = 0
  MessageRequester( "Erreur" , "Impossible d'ouvrir l'écran " , 0 )
  End
EndIf
;}
;{ Les spritess
CreateSprite(#SOuris,16,16)
StartDrawing(SpriteOutput(#SOuris))
  Line(0,0,15,0,#White)
  Line(0,0,0,15,#White)
  LineXY(0,15,15,0,#White)
  FillArea(7,7,#White,#Blue)
StopDrawing()

;Sprite Destination
CreateSprite(#Destination,16,16)
StartDrawing(SpriteOutput(#Destination))
  LineXY(0,0,15,15,#Yellow)
  LineXY(0,15,15,0,#Yellow)
StopDrawing()
;}
;{ Les procédures

Procedure InitAvion(*Avion.s_Avion)
  Define i
  Define.f x, y
  *Avion\NbSommet = 12
  *Avion\Scale    = 40
  Restore sommets
  For i = 0 To 11
    Read *Avion\sommet[i]\x
    Read *Avion\sommet[i]\y
  Next i  
  ;Position de l'avion
  *Avion\Pos\x = 220 
  *Avion\Pos\y = 210 
  ;Rayon du cercle
  *Avion\Rayon = 5 * *Avion\Scale
  ;Vitesse
  *Avion\Vitesse = 3.5
  *Avion\Omega = *Avion\Vitesse / *Avion\Rayon 
  ;Direction
  *Avion\Direction\x = 1
  *Avion\Direction\y = 0 
  ;Angle
  *Avion\Angle = 0
EndProcedure
Procedure.f atan2f(y.f, x.f)
  !fld dword[p.v_y]
  !fld dword[p.v_x]
  !fpatan
  ProcedureReturn
EndProcedure
Procedure.f Distance(*A.s_Vecteur, *B.s_Vecteur)
  Define.f Dist
  Dist = Sqr((*A\x-*B\x)*(*A\x-*B\x)+(*A\y-*B\y)*(*A\y-*B\y))
  ProcedureReturn Dist
EndProcedure
Procedure.f Norme(*V.s_Vecteur)
  ProcedureReturn Sqr(*V\x * *V\x + *V\y * *V\y)
EndProcedure
Procedure Normalise(*V.s_Vecteur)
  Define.f Longueur
  Longueur = Sqr(*V\x * *V\x + *V\y * *V\y)
  ;Normalise
  If Longueur
    *V\x / Longueur 
    *V\y / Longueur 
  EndIf
EndProcedure
Procedure RotationAutourOrigine(*Point.s_Vecteur, Angle.f)
  ;Je pourrais utiliser la procédure RotationAutourPoint en utilisant un vecteur nul
  Define.f CosA, SinA
  Define.s_Vecteur Temporaire
  CosA = Cos(Angle)
  SinA = Sin(Angle)
  Temporaire\x = *Point\x * CosA - *Point\y * SinA
  Temporaire\y = *Point\x * SinA + *Point\y * CosA
  COPIE_VECTEUR(*Point, Temporaire)
EndProcedure
Procedure RotationAutourPoint(*Origine.s_Vecteur, *Point.s_Vecteur, Angle.f)
  Define.f CosA, SinA
  Define.s_Vecteur Temporaire
  CosA = Cos(Angle)
  SinA = Sin(Angle)
  Temporaire\x = *Origine\x + (*Point\x - *Origine\x) * CosA - (*Point\y - *Origine\y) * SinA
  Temporaire\y = *Origine\y + (*Point\x - *Origine\x) * SinA + (*Point\y - *Origine\y) * CosA
  COPIE_VECTEUR(*Point, Temporaire)
EndProcedure
Procedure Translation(*Point.s_Vecteur, *Direction.s_Vecteur, Distance.f)
   *Point\x + *Direction\x * Distance
   *Point\y + *Direction\y * Distance
EndProcedure

Procedure DrawAvion(*A.s_Avion)
  Define.l i, x, y, x1, y1
  Define.f CosA, SinA
  CosA = Cos(*A\Angle)
  SinA = Sin(*A\Angle)
  
  For i = 0 To *A\NbSommet-2
    x  = *A\Pos\x + *A\Sommet[i]\x   * *A\Scale * CosA - *A\Sommet[i]\y   * *A\Scale * SinA
    y  = *A\Pos\y + *A\Sommet[i]\x   * *A\Scale * SinA + *A\Sommet[i]\y   * *A\Scale * CosA
    x1 = *A\Pos\x + *A\Sommet[i+1]\x * *A\Scale * CosA - *A\Sommet[i+1]\y * *A\Scale * SinA
    y1 = *A\Pos\y + *A\Sommet[i+1]\x * *A\Scale * SinA + *A\Sommet[i+1]\y * *A\Scale * CosA
    LineXY(x, y, x1, y1, #White)
  Next i   
    x  = *A\Pos\x + *A\Sommet[*A\NbSommet-1]\x * *A\Scale * CosA - *A\Sommet[*A\NbSommet-1]\y * *A\Scale * SinA
    y  = *A\Pos\y + *A\Sommet[*A\NbSommet-1]\x * *A\Scale * SinA + *A\Sommet[*A\NbSommet-1]\y * *A\Scale * CosA
    x1 = *A\Pos\x + *A\Sommet[0]\x             * *A\Scale * CosA - *A\Sommet[0]\y             * *A\Scale * SinA
    y1 = *A\Pos\y + *A\Sommet[0]\x             * *A\Scale * SinA + *A\Sommet[0]\y             * *A\Scale * CosA
   LineXY(x, y, x1, y1, #White)  
   Line(*A\Pos\x,*A\Pos\y,*A\Direction\x* *A\Scale * 3, *A\Direction\y* *A\Scale * 3, #White)    
EndProcedure
;}
InitAvion(@Avion)
Repeat
  ClearScreen(0)
  If ExamineKeyboard()
    If KeyboardReleased(#PB_Key_F1)
      Affiche = 1 - Affiche
    EndIf  
    If KeyboardPushed(#PB_Key_Up) And Avion\Scale < 100 
      Avion\Scale + 1
    ElseIf KeyboardPushed(#PB_Key_Down) And Avion\Scale > 1 
      Avion\Scale - 1
    EndIf 
  EndIf
  If ExamineMouse()
    If MouseButton(#PB_MouseButton_Left) 
      Avion\Cherche = #True
      CalculNormale = #False
      Destination\x = MouseX()
      Destination\y = MouseY()
    EndIf 
  EndIf
      
  ;La cible est atteinte ?
  If Distance(@Avion\Pos, @Destination) <= Avion\Vitesse + 0.1
    COPIE_VECTEUR(Destination, Avion\Pos)
  EndIf
  
  
  If Avion\Cherche 
    ;On calcule la position du cercle et la tangente à chaque changement de destination  
    If CalculNormale = #False
      ;-Calcule la normale
      Normale\x = -Avion\Direction\y
      Normale\y = Avion\Direction\x
      Normalise(@Normale)
      ;-Calcul la position du cercle en fonction de la destination
      SOUSTRACTION_VECTEUR(AvionDestination, Destination, Avion\Pos)
      ProduitScalaire = PRODUIT_SCALAIRE(AvionDestination, Normale) 
      DistanceAvionDestination = Norme(AvionDestination)
      ProduitScalaire / Abs(ProduitScalaire) ; Vaut 1 ou -1 selon le côté où se trouve la destination
      If ProduitScalaire <> 0
        Cercle\x = Avion\Pos\x + Normale\x * ProduitScalaire * Avion\Rayon
        Cercle\y = Avion\Pos\y + Normale\y * ProduitScalaire * Avion\Rayon
    
        ;-Calcul la tangente
        SOUSTRACTION_VECTEUR(CercleDestination, Destination, Cercle)
        DistanceCercleDestination = Norme(CercleDestination) 
        Angle1 = ASin(Avion\Rayon / DistanceCercleDestination)
        DistancePointDestination = Cos(Angle1) * DistanceCercleDestination 
    
        Angle2 = #PI - atan2f(-CercleDestination\y, CercleDestination\x)
        ;Coordonnées du point tangent
        T1\x = Destination\x + Cos(Angle2 + Angle1 * ProduitScalaire) * DistancePointDestination 
        T1\y = Destination\y + Sin(Angle2 + Angle1 * ProduitScalaire) * DistancePointDestination 
        ;Avant je ne le mettais pas et ça fonctionnait quand même, mais j'avais un doute pour certains cas ! 
        SOUSTRACTION_VECTEUR(MemoireDirectionTangente, Destination, T1)
        Normalise(MemoireDirectionTangente)
        CalculNormale = #True
      EndIf  
    EndIf
    
    ;Il faut avancer pour se dégager si la destination se trouve dans le cercle
    If Distance(@Destination , @Cercle) < Avion\Rayon Or ProduitScalaire = 0

      CalculNormale = #False ; Pour autoriser le calcul de la nouvelle position du cercle
      ;Déplacement de l'avion en ligne droite
      Translation(@Avion\Pos, @Avion\Direction, Avion\Vitesse)
    Else
      ;Tourne sur le cercle jusqu'à trouver la tangente en direction de la destination 
      
      ;Mise à jour de la direction
      RotationAutourOrigine(@Avion\Direction, Avion\Omega*ProduitScalaire)
      
      ;Mise à jour de l'angle 
      Avion\Angle = atan2f(Avion\Direction\y, Avion\Direction\x)
      
      ;Repositionne l'avion autour du cercle
      RotationAutourPoint(@Cercle, @Avion\Pos, Avion\Omega*ProduitScalaire)

      ;LE point tangent est atteint      
      ;If Distance(@Avion\Pos, @T1) <= Avion\Vitesse/2 
      AngleAvion = atan2f(Cercle\y-Avion\Pos\y, Cercle\x-Avion\Pos\x)
      AngleTangente = atan2f(Cercle\y-T1\y, Cercle\x-T1\x)  
      If AngleAvion=AngleTangente Or (AngleAvion > AngleTangente - Avion\Omega/2 And AngleAvion < AngleTangente + Avion\Omega/2)
        CalculNormale = #False
        Avion\Cherche = #False
        COPIE_VECTEUR(Avion\Pos, T1)
        ;SOUSTRACTION_VECTEUR(Avion\Direction, Destination, T1)
        ;Normalise(@Avion\Direction)
        COPIE_VECTEUR(Avion\Direction, MemoireDirectionTangente);Remplace les 2 lignes commentées au dessus
        ;Mise à jour de l'angle 
        Avion\Angle = atan2f(Avion\Direction\y, Avion\Direction\x)
      EndIf
     
    EndIf
    
  Else
    ;Déplacement de l'avion en ligne droite
    Translation(@Avion\Pos, @Avion\Direction, Avion\Vitesse)
  EndIf
  
  StartDrawing(ScreenOutput())
    DrawingMode(#PB_2DDrawing_Outlined)
    BackColor(0)
    If Affiche
      ;Cercle retenu
      Circle(Cercle\x,Cercle\y, Avion\Rayon, #Yellow)   
      ;Tangente
      LineXY(Destination\x, Destination\y, T1\x, T1\y, #Green)
    EndIf
    DrawAvion(@Avion)
  StopDrawing()
  
  DisplayTransparentSprite(#Destination, Destination\x-8, Destination\y-8)
  DisplayTransparentSprite(#SOuris,MouseX(), MouseY())

  FlipBuffers()
Until KeyboardPushed(#PB_Key_Escape)


;Définition des sommets de l'avion
DataSection
sommets:
Data.f -3,  0.5 
Data.f  0,  0.5 
Data.f  0,  2.5 
Data.f  1,  2.5
Data.f  2,  0.5 
Data.f  3,  0.5
Data.f  3, -0.5
Data.f  2, -0.5
Data.f  1, -2.5
Data.f  0, -2.5 
Data.f  0, -0.5
Data.f -3, -0.5 
EndDataSection
Dernière modification par comtois le sam. 12/mai/2007 16:53, modifié 2 fois.
http://purebasic.developpez.com/
Je ne réponds à aucune question technique en PV, utilisez le forum, il est fait pour ça, et la réponse peut profiter à tous.
Frenchy Pilou
Messages : 2194
Inscription : jeu. 27/janv./2005 19:07

Message par Frenchy Pilou »

çà plane :)
Good07
Messages : 308
Inscription : ven. 23/avr./2004 18:08
Localisation : Hérault 34190 Laroque

Message par Good07 »

Excelent ! :D

J'ai pas pu le prendre en défaut...Une véritable tête chercheuse cet avion. 8O
lionel_om
Messages : 1500
Inscription : jeu. 25/mars/2004 11:23
Localisation : Sophia Antipolis (Nice)
Contact :

Message par lionel_om »

Super !!!

Par contre j'ai étais confronté à un bug : après lui avoir asigné un point de passage, l'avion bouclé sur lui même sans passé par le point. J'ai alors voulu faire des screens shots, mais à ce même moment, il a arrété de bouclé !

Je ne sais pas ce qui bug. J'ai essayé rapidement de reproduire le bug, mais pas moyen.
A voir...
Webmestre de Basic-univers
Participez à son extension: ajouter vos programmes et partagez vos codes !
comtois
Messages : 5186
Inscription : mer. 21/janv./2004 17:48
Contact :

Message par comtois »

Exact, je crois que le bug est dans ma façon de tester à quel moment on atteint le point tangent. Très mauvaise méthode.

J'y réfléchis mais si quelqu'un a une proposition...

Je pense qu'il faut faire comme pour les collisions, C'est à dire tester sur l'intervalle du déplacement (Omega) si on trouve le point tangent, si c'est le cas , il faut se placer au point tangent et calculer le reste de la course à partir de ce point.
En clair à chaque tour de cycle on se déplace d'omega sur le cercle, si on rencontre le point tangent à 1/3 du déplacement, on se place au point tangent et il faut avancer de 2/3 sur la tangente, de façon à garder un mouvement fluide et uniforme.
ALors que pour l'instant je me contente de replacer l'avion au point tangent.
Et surtout je teste très mal si je suis arrivé à ce point , donc je peux le louper dans certains cas, notamment quand le point tangent se trouve à 0° :?
http://purebasic.developpez.com/
Je ne réponds à aucune question technique en PV, utilisez le forum, il est fait pour ça, et la réponse peut profiter à tous.
Répondre