Avant toute chose voici le code.
Pour voir l'algo entier non-modifié, voir l'adresse dans le code.
Si vous récupérez le code là-bas, il faudra remplacer les 2 back-slashs par un seul :
Code : Tout sélectionner
; Algo tiré d'un code de Cpl.Bator : http://www.game-corp.net/topic-744-purebasic-poursuite-d-039-un-point-par-un-autre.html
EnableExplicit
Enumeration
#Voiture
#Voiture3D_IA
EndEnumeration
#SW = 640
#SH = 480
UsePNGImageDecoder()
InitSprite() : InitKeyboard() : InitMouse()
If InitSprite3D() = 0
MessageRequester("Erreur", "La librairie Sprite3D n'a pas pu être initialisée", 0)
End
EndIf
;OpenWindow(0, 0, 0, 640, 480, "Un écran dans une fenêtre...", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
;OpenWindowedScreen(WindowID(0), 0, 0, #SW, #SH, 0, 0, 0)
OpenScreen(#SW, #SH, 32, "")
CompilerIf #PB_Compiler_OS = #PB_OS_Linux
Structure point
x.f
y.f
EndStructure
CompilerEndIf
;-Structures
; Structure VECTOR2
; x.f
; y.f
; EndStructure
Structure ENTITY
;Position.VECTOR2
x.f
y.f
Angle.f
EndStructure
;-Declarations des fonctions
Declare.f curveangle(newangle.f,oldangle.f,increments.f)
Declare.f ReturnDegAngle(x1.f,y1.f,x2.f,y2.f)
Declare track_SmoothPoint(*A.ENTITY,*B.ENTITY, turnspeed.f=1.0)
#turnspeed = 4.0
; ************************************************************************************
; Arrow (En réalité la voiture).
; Ses coordonnées pour la position de départ
Global Arrow.ENTITY
Arrow\x = 100
Arrow\y = 250
Arrow\Angle = 0
; Tableau pour stocker les différents checkpoint à atteindre
Global Dim Target.ENTITY(5)
;Target(1)\x = MouseX()
;Target(1)\y = MouseY()
Target(1)\x = 500
Target(1)\y = 50
Target(2)\x = 550
Target(2)\y = 200
Target(3)\x = 300
Target(3)\y = 250
Target(4)\x = 300
Target(4)\y = 50
Target(5)\x = 10
Target(5)\y = 10
Global checkpoint = 1
Global Quit
;LoadSprite(#Voiture, "voitureFin.png", #PB_Sprite_Texture)
; Super voiture de sport rouge dont on ne citera pas la marque.
CreateSprite(#Voiture, 16, 16, #PB_Sprite_Texture)
StartDrawing(SpriteOutput(#Voiture))
Box(3, 0, 16, 10, RGB(255, 0, 0))
StopDrawing()
; Création du Sprite 3d
CreateSprite3D(#Voiture3D_IA, #Voiture)
; Ci-dessous si on utilise le programme dans le mode fenêtré.
;ReleaseMouse(1)
Repeat
ClearScreen(0)
ExamineKeyboard()
ExamineMouse()
StartDrawing(ScreenOutput())
; Le 1er checkpoint à atteindre
If checkpoint = 1
;++++++++++++++++++++++++++++++++++++++++++++++++++++
;+ Si on est en X à + ou - 5 px et + ou - 5 px en Y alors la condition
;+ est bonne et donc on passe au prochain checkpoint
;++++++++++++++++++++++++++++++++++++++++++++++++++++
;If Int(Arrow\x) - 5 <= Target(1)\x And Int(Arrow\x) + 5 >= Target(1)\x And Int(Arrow\y) - 5 <= Target(1)\y And Int(Arrow\y) + 5 >= Target(1)\y
If Target(1)\x >= Int(Arrow\x) - 5 And Target(1)\x <= Int(Arrow\x) + 5 And Target(1)\y >= Int(Arrow\y) - 5 And Target(1)\y <= Int(Arrow\y) + 5
; Delay(2000)
checkpoint = 2
Else
Circle(Target(1)\x, Target(1)\y, 2, RGB(255, 255, 255))
DrawText(Target(1)\x - 10, Target(1)\y + 10, "Checkpoint 1")
track_SmoothPoint(@Arrow,@Target(1), #turnspeed)
EndIf
EndIf
; Le 2ème checkpoint à atteindre
If checkpoint = 2
;If Int(Arrow\x) - 5 <= Target(2)\x And Int(Arrow\x) + 5 >= Target(2)\x And Int(Arrow\y) - 5 <= Target(2)\y And Int(Arrow\y) + 5 >= Target(2)\y
If Target(2)\x >= Int(Arrow\x) - 5 And Target(2)\x <= Int(Arrow\x) + 5 And Target(2)\y >= Int(Arrow\y) - 5 And Target(2)\y <= Int(Arrow\y) + 5
; Delay(2000)
checkpoint = 3
Else
Circle(Target(2)\x, Target(2)\y, 2, RGB(255, 255, 255))
DrawText(Target(2)\x - 10, Target(2)\y + 10, "Checkpoint 2")
track_SmoothPoint(@Arrow,@Target(2), #turnspeed)
EndIf
EndIf
If checkpoint = 3
;If Int(Arrow\x) - 5 <= Target(3)\x And Int(Arrow\x) + 5 >= Target(3)\x And Int(Arrow\y) - 5 <= Target(3)\y And Int(Arrow\y) + 5 >= Target(3)\y
If Target(3)\x >= Int(Arrow\x) - 5 And Target(3)\x <= Int(Arrow\x) + 5 And Target(3)\y >= Int(Arrow\y) - 5 And Target(3)\y <= Int(Arrow\y) + 5
;If Int(Arrow\x) = Target(1)\x Or Int(Arrow\y) = Target(1)\y
; Delay(2000)
checkpoint = 1
Else
Circle(Target(3)\x, Target(3)\y, 2, RGB(255, 255, 255))
DrawText(Target(3)\x - 10, Target(3)\y + 10, "Checkpoint 3")
track_SmoothPoint(@Arrow,@Target(3), #turnspeed)
EndIf
EndIf
If checkpoint = 4
;If Int(Arrow\x) - 5 <= Target(4)\x And Int(Arrow\x) + 5 >= Target(4)\x And Int(Arrow\y) - 5 <= Target(4)\y And Int(Arrow\y) + 5 >= Target(4)\y
If Target(4)\x >= Int(Arrow\x) - 5 And Target(4)\x <= Int(Arrow\x) + 5 And Target(4)\y >= Int(Arrow\y) - 5 And Target(4)\y <= Int(Arrow\y) + 5
; Delay(2000)
checkpoint = 1
Else
Circle(Target(4)\x, Target(4)\y, 2, RGB(255, 255, 255))
DrawText(Target(4)\x - 10, Target(4)\y + 10, "Checkpoint 4")
track_SmoothPoint(@Arrow,@Target(4), #turnspeed)
EndIf
EndIf
DrawText(Arrow\x - 70, Arrow\y + 10, "{"+ Str(Target(checkpoint)\x) +" | "+ Str(Int(Arrow\x)) +", "+ Str(Target(checkpoint)\y) +" | "+ Str(Int(Arrow\y)) +"}" )
StopDrawing()
;++++++++++++++++++++++++++++++++++++++++++++++++++++
;+ La voiture se dirige vers le chemin du checkpoint à la vitesse 1
;++++++++++++++++++++++++++++++++++++++++++++++++++++
Arrow\x + 1 *Cos(Arrow\Angle*#PI/180)
Arrow\y + 1 *Sin(Arrow\Angle*#PI/180)
; Define vitesse.b
; vitesse = Random(3)
; Arrow\x + vitesse *Cos(Arrow\Angle*#PI/180)
; Arrow\y + vitesse *Sin(Arrow\Angle*#PI/180)
; Affichage des sprites
If Start3D()
; Affichage Car IA
DisplaySprite3D(#Voiture3D_IA, Arrow\x - 8, Arrow\y - 8)
RotateSprite3D(#Voiture3D_IA, Arrow\Angle, 0)
Stop3D()
EndIf
If KeyboardPushed(#PB_Key_Escape)<>#Null : Quit=1 : EndIf
FlipBuffers()
Until Quit=1
End
Procedure.f curveangle(newangle.f,oldangle.f,increments.f)
If increments > 1
If (oldangle + 360) - newangle < newangle - oldangle
oldangle = 360 + oldangle
EndIf
If (newangle + 360) - oldangle < oldangle - newangle
newangle = 360 + newangle
EndIf
oldangle = oldangle - (oldangle - newangle) / increments
EndIf
If increments <= 1
;Debug "newangle : "+ StrF(newangle)
ProcedureReturn newangle
EndIf
;Debug "oldangle : "+ StrF(oldangle)
ProcedureReturn oldangle
EndProcedure
Procedure.f ReturnDegAngle(x1.f,y1.f,x2.f,y2.f) ; DEGREE
Protected A.f, b.f, c.f, Angle.f
A.f = x1-x2
b.f = y1-y2
c.f = -Sqr(A*A+b*b)
Angle.f = ACos(A / c) * 180 / #PI
If y1 < y2 : Angle = 360 - Angle : EndIf
ProcedureReturn Abs(Angle - 360)
EndProcedure
Procedure track_SmoothPoint(*A.ENTITY,*B.ENTITY, turnspeed.f=1.0)
Protected AngDif.f, temp_yaw.f
AngDif = ReturnDegAngle(*A\x,*A\y,*B\x,*B\y)
temp_yaw.f = curveangle(AngDif, *A\Angle, turnspeed)
*A\Angle = temp_yaw
;*A\Angle = AngDif
EndProcedure
Faire une petite IA pour un jeu de voiture.
La voiture se déplacera de checkpoint en checkpoint.
INFO : Le checkpoint 4 n'est pas utilisé, on passe du 3ème au 1er
1) 1ère question un peu honteuse.
A force d'essais pour arriver à quelque chose qui fonctionne, j'en suis arrivé à ne plus savoir quoi mettre à gauche et à droite dans le if.
Surtout que les 2 solutions au final fonctionnent
Si on veut savoir si on est à + ou - 5 px en X, j'ai donc le choix entre :
If Target(1)\x >= Int(Arrow\x) - 5 And Target(1)\x <= Int(Arrow\x) + 5
ou
If Int(Arrow\x) - 5 <= Target(1)\x And Int(Arrow\x) + 5 >= Target(1)\x
Target(1)\x est la valeur fixe, ici de 500 et Target(1)\x la valeur qui varie.
Donc quel est la solution la plus logique.
J'ai laissé l'autre solution en commentaire.
2) Est ce qu'il y aurait une technique plus subtile pour vérifier que l'on à bien atteint le checkpoint
Parce qu'actuellement si je mets une valeur supérieure à + ou - 5px la voiture tourne trop tôt et si l'on vire le + ou - 5px la condition n'est pas remplie et la voiture ne va plus au prochain checkpoint.
Bref, ce n'est pas très fiable comme vérification.
3) Lors du passage aux différents checkpoints la 1ère fois, tout va bien, mais au second tour lorsque la voiture atteint de nouveau le checkpoint 1, la voiture fait un tour sur elle même.
J'aurais voulu savoir d'où venait le problème, mais je suis nul en math.
J'aurais voulu que la voiture prenne toujours le checkpoint dans le sens des aiguilles d'une montre, ce qui conviendrait pour un circuit en rond, mais je viens de penser qu'il pourrait y avoir un problème avec un circuit en forme de "T" où il y aurait des virages à droite mais aussi à gauche.
Je me dis qu'en plus des coordonnées du checkpoint, je pourrais ajouter l'info sur le type de virage, genre D, G, ou TD (Droite, Gauche ou ToutDroit lorsque qu'il faut dévier de quelques degrés)
Bref, est-ce qu'il y aurait un matheux charitable qui pourrait solutionner la chose.
Si on modifie la procédure "track_SmoothPoint()" comme ceci, on a un pivot basique :
Code : Tout sélectionner
Procedure track_SmoothPoint(*A.ENTITY,*B.ENTITY, turnspeed.f=1.0)
Protected AngDif.f, temp_yaw.f
AngDif = ReturnDegAngle(*A\x,*A\y,*B\x,*B\y)
temp_yaw.f = curveangle(AngDif, *A\Angle, turnspeed)
;*A\Angle = temp_yaw
*A\Angle = AngDif
EndProcedure
Bye.