Rotation & Déplacement d'un sprite vers une souris

Programmation avancée de jeux en PureBasic
Avatar de l’utilisateur
falsam
Messages : 7244
Inscription : dim. 22/août/2010 15:24
Localisation : IDF (Yvelines)
Contact :

Rotation & Déplacement d'un sprite vers une souris

Message par falsam »

N'ayant pas la place d'écrire le titre en entier je vais reformuler ma demande.

Rotation & Déplacement d'un sprite vers le curseur d'une souris. Voila qui est plus clair.

J'ai pu résoudre la première partie de ma demande qui consiste à demander à un sprite de faire une rotation en direction du curseur de la souris.

L'objectif était de trouver l'angle existant entres deux objets (vecteurs) placés respectivement en x et y.

J'ai appliqué la fonction arc tangente(x0 - x1, y0 - y1) que j'ai multiplié par 180 / PI + 180 pour avoir un angle de 0 à 360°.

Le code pour illustrer ce que je viens de dire.

Code : Tout sélectionner

EnableExplicit

;Quelques variables 
Structure Sprite
  id.i
  x.d
  y.d
  Speed.d
EndStructure

Global Background, LadyBug.Sprite, Cursor, Event, Angle.f

;Initialisation diverses
InitNetwork() ;Pour le téléchargement des images

InitSprite()
InitKeyboard()
InitMouse()

UseJPEGImageDecoder()
UsePNGImageDecoder()

;Téléchargement des images 
If ReceiveHTTPFile("http://www.falsam.com/Download/images/sprites/i2.png", "i2.png") : EndIf
If ReceiveHTTPFile("http://www.falsam.com/Download/images/sprites/blur1.jpg", "blur1.jpg") : EndIf
If ReceiveHTTPFile("http://www.falsam.com/Download/images/sprites/cursordrag.png", "cursordrag.png") : EndIf

;Creation du screen
OpenWindow(0, 0, 0, 630, 410, "test")
OpenWindowedScreen(WindowID(0), 0, 0, 800, 600)

;Création des sprite
Background = LoadSprite(#PB_Any, "blur1.jpg")
LadyBug\id = LoadSprite(#PB_Any, "i2.png", #PB_Sprite_AlphaBlending)
Cursor = LoadSprite(#PB_Any, "cursordrag.png", #PB_Sprite_AlphaBlending)

;Position initiale de la coccinelle (LadyBug)
LadyBug\x = 315
LadyBug\y = 205

;Boucle evenementielle
Repeat  
  Repeat
    Event = WindowEvent()
     
    Select Event    
      Case #PB_Event_CloseWindow
        End
    EndSelect  
  Until Event=0
    
  FlipBuffers()
  ClearScreen(RGB(0,0,0))
  ExamineKeyboard()
  ExamineMouse()
    
  ;Affichage du background 
  DisplaySprite(Background, 0, 0)
        
  ;Affichage du curseur de la souris
  DisplayTransparentSprite(Cursor, MouseX(), MouseY())
   
  ;Calcul de l'angle en degre (0 à 360)
  Angle = ATan2(MouseX() - LadyBug\x, MouseY() - LadyBug\y) * 180 / #PI + 180
    
  ;Rotation de la cocinelle avec l'angle calculé
  ;La cocinelle initiale étant affiché à 90°, on retire 90 au résultat obtenu
  RotateSprite(LadyBug\id, Angle - 90, #PB_Absolute)
          
  ;Affichage de la cocinelle
  DisplayTransparentSprite(LadyBug\id, LadyBug\x, LadyBug\y)
    
Until KeyboardPushed(#PB_Key_Escape)
Dernière modification par falsam le mer. 17/juin/2015 0:50, modifié 4 fois.
Configuration : Windows 11 Famille 64-bit - PB 6.03 x64 - AMD Ryzen 7 - 16 GO RAM
Vidéo NVIDIA GeForce GTX 1650 Ti - Résolution 1920x1080 - Mise à l'échelle 125%
Avatar de l’utilisateur
falsam
Messages : 7244
Inscription : dim. 22/août/2010 15:24
Localisation : IDF (Yvelines)
Contact :

Re: Rotation & Déplacement d'un sprite vers une souris

Message par falsam »

J'ai un souci par contre pour la deuxième partie de ce code qui consiste à déplacer un sprite vers le curseur de la souris quand je clique sur le bouton gauche de la souris. Merci pour votre aide.

Code : Tout sélectionner

EnableExplicit

;Quelques variables 
Structure Sprite
  id.i
  x.d
  y.d
  Speed.d
EndStructure

Global Background, LadyBug.Sprite, Cursor, Event, Angle.d

;Initialisation diverses
InitNetwork() ;Pour le téléchargement des images

InitSprite()
InitKeyboard()
InitMouse()

UseJPEGImageDecoder()
UsePNGImageDecoder()

;Téléchargement des images 
If ReceiveHTTPFile("http://www.falsam.com/Download/images/sprites/i2.png", "i2.png") : EndIf
If ReceiveHTTPFile("http://www.falsam.com/Download/images/sprites/blur1.jpg", "blur1.jpg") : EndIf
If ReceiveHTTPFile("http://www.falsam.com/Download/images/sprites/cursordrag.png", "cursordrag.png") : EndIf

;Creation du screen
OpenWindow(0, 0, 0, 630, 410, "test")
OpenWindowedScreen(WindowID(0), 0, 0, 800, 600)

;Création des sprite
Background = LoadSprite(#PB_Any, "blur1.jpg")
LadyBug\id = LoadSprite(#PB_Any, "i2.png", #PB_Sprite_AlphaBlending)
Cursor = LoadSprite(#PB_Any, "cursordrag.png", #PB_Sprite_AlphaBlending)

;Position initiale de la coccinelle (LadyBug)
LadyBug\x = 315
LadyBug\y = 205

;Vitesse de la coccinelle
LadyBug\Speed = 2

;Boucle evenementielle
Repeat  
  Repeat
    Event = WindowEvent() 
    Select event    
      Case #PB_Event_CloseWindow
        End
    EndSelect  
  Until event=0
    
  FlipBuffers()
  ExamineKeyboard()
  ExamineMouse()
    
  ;Affichage du background 
  DisplaySprite(Background, 0, 0)
       
  ;Affichage du curseur de la souris
  DisplayTransparentSprite(Cursor, MouseX(), MouseY())
    
  ;Calcul de l'angle en degre (0 à 360)
  Angle = ATan2(MouseX() - LadyBug\x, MouseY() - LadyBug\y) * 180 / #PI + 180
    
  ;Rotation de la cocinelle avec l'angle calculé
  ;La cocinelle initiale étant affiché à 90°, on retire 90 au résultat obtenu
  RotateSprite(LadyBug\id, Angle - 90, #PB_Absolute)
        
  If MouseButton(#PB_MouseButton_Left )    
              
    LadyBug\x + (LadyBug\Speed * Cos(Angle))
    LadyBug\y + (LadyBug\Speed * Sin(Angle))
       
  EndIf 
    
  ;Affichage de la cocinelle
  DisplayTransparentSprite(LadyBug\id, LadyBug\x, LadyBug\y)
     
Until KeyboardPushed(#PB_Key_Escape)
Configuration : Windows 11 Famille 64-bit - PB 6.03 x64 - AMD Ryzen 7 - 16 GO RAM
Vidéo NVIDIA GeForce GTX 1650 Ti - Résolution 1920x1080 - Mise à l'échelle 125%
Demivec
Messages : 90
Inscription : sam. 18/sept./2010 18:13

Re: Rotation & Déplacement d'un sprite vers une souris

Message par Demivec »

Code : Tout sélectionner

  If MouseButton(#PB_MouseButton_Left )    
              
    LadyBug\x - (LadyBug\Speed * Cos(Radian(Angle)))
    LadyBug\y - (LadyBug\Speed * Sin(Radian(Angle)))
       
  EndIf
Plus:

Code : Tout sélectionner

EnableExplicit

;Quelques variables 
Structure Sprite
  id.i
  x.d
  y.d
  Speed.d
EndStructure

Procedure.f angleDiff(currentDir.f, wantDir.f, maxTurn.f)
  ;from http://stackoverflow.com/questions/20109394/rotate-angle-to-target-angle-via-shortest-side
  Protected directionDiff.f

  ;wantDir - votre direction de la cible
  ;maxTurn - le nombre maximum de degrés pour allumer
  ;currentDir - votre direction actuelle
  
  If wantDir >= (currentDir + 180)
    currentDir + 360
  ElseIf wantDir < (currentDir - 180)
    wantDir + 360
  EndIf
  
  directionDiff = wantDir - currentDir
  
  If directionDiff < -maxTurn
    directionDiff = -maxTurn
  ElseIf directionDiff > maxTurn
    directiondiff = maxTurn
  EndIf
  
  ProcedureReturn Mod(directionDiff + currentDir, 360)  ;retourner la direction résultante
EndProcedure

Global Background, LadyBug.Sprite, Cursor, Event, Angle.d, dernierAngle.f

;Initialisation diverses
InitNetwork() ;Pour le téléchargement des images

InitSprite()
InitKeyboard()
InitMouse()

UseJPEGImageDecoder()
UsePNGImageDecoder()

;Téléchargement des images 
If ReceiveHTTPFile("http://www.falsam.com/Download/images/sprites/i2.png", "i2.png") : EndIf
If ReceiveHTTPFile("http://www.falsam.com/Download/images/sprites/blur1.jpg", "blur1.jpg") : EndIf
If ReceiveHTTPFile("http://www.falsam.com/Download/images/sprites/cursordrag.png", "cursordrag.png") : EndIf

;Creation du screen
OpenWindow(0, 0, 0, 630, 410, "test")
OpenWindowedScreen(WindowID(0), 0, 0, 800, 600)

;Création des sprite
Background = LoadSprite(#PB_Any, "blur1.jpg")
LadyBug\id = LoadSprite(#PB_Any, "i2.png", #PB_Sprite_AlphaBlending)
Cursor = LoadSprite(#PB_Any, "cursordrag.png", #PB_Sprite_AlphaBlending)

;Position initiale de la coccinelle (LadyBug)
LadyBug\x = 315
LadyBug\y = 205

;Vitesse de la coccinelle
LadyBug\Speed = 2

;Boucle evenementielle
Repeat  
  Repeat
    Event = WindowEvent() 
    Select event    
      Case #PB_Event_CloseWindow
        End
    EndSelect  
  Until event=0
    
  FlipBuffers()
  ExamineKeyboard()
  ExamineMouse()
    
  ;Affichage du background 
  DisplaySprite(Background, 0, 0)
       
  ;Affichage du curseur de la souris
  DisplayTransparentSprite(Cursor, MouseX(), MouseY())
    
  ;Calcul de l'angle en degre (0 à 360)
  Angle = ATan2(MouseX() - LadyBug\x, MouseY() - LadyBug\y) * 180 / #PI + 180
  
  Angle = angleDiff(dernierAngle, Angle, 10)
  dernierAngle = Angle
  
  ;Rotation de la cocinelle avec l'angle calculé
  ;La cocinelle initiale étant affiché à 90°, on retire 90 au résultat obtenu
  RotateSprite(LadyBug\id, Angle - 90, #PB_Absolute)
        
  If MouseButton(#PB_MouseButton_Left )    
    If Abs(MouseX() - LadyBug\x) > 1              
      LadyBug\x - (LadyBug\Speed * Cos(Radian(Angle)))
    EndIf
    If Abs(MouseY() - LadyBug\y) > 1 
      LadyBug\y - (LadyBug\Speed * Sin(Radian(Angle)))
    EndIf
  EndIf  
   
  ;Affichage de la cocinelle
  DisplayTransparentSprite(LadyBug\id, LadyBug\x, LadyBug\y)
     
Until KeyboardPushed(#PB_Key_Escape)
:)
G-Rom
Messages : 3626
Inscription : dim. 10/janv./2010 5:29

Re: Rotation & Déplacement d'un sprite vers une souris

Message par G-Rom »

Pour le déplacement de sprite sur l'écran , j'utilise toujours le deltatime.
il se calcul sur le temps de rendu d'une boucle :
deltaTime.f = TempMS / 1000

puis pour le déplacement en lui même , tu as les 2 coordonnées ( position & cible )
on peu calculer le vecteur direction :
direction = cible - position
& tu normalises se vecteur , ensuite tu update la position :
position + (direction * vitesse) * deltatime
(direction * vitesse) est donc la vélocité.

cela t'assure une vitesse identique sur n'importe quel pc.
Avatar de l’utilisateur
microdevweb
Messages : 1798
Inscription : mer. 29/juin/2011 14:11
Localisation : Belgique

Re: Rotation & Déplacement d'un sprite vers une souris

Message par microdevweb »

@Falsam,

Je ne vois pas trop ton soucis? cela fonctionne !
Windows 10 64 bits PB: 5.70 ; 5.72 LST
Work at Centre Spatial de Liège
Marc56
Messages : 2146
Inscription : sam. 08/févr./2014 15:19

Re: Rotation & Déplacement d'un sprite vers une souris

Message par Marc56 »

Code : Tout sélectionner

LadyBug\x + (LadyBug\Speed * Cos(Angle)) 
...
LadyBug\x - (LadyBug\Speed * Cos(Radian(Angle))) 
Ça me rappelle quand je me suis pris la tête pendant 2 jours a essayer de faire une pendule à aiguilles et que l’extrémité de l'aiguille délirait systématiquement après la dixième position 8O :|

Pourtant mes vieux souvenirs de math étaient bon (sinus = opposé/hypoténuse et cosinus = adj/hypo) donc facile de calculer la position de l'extrémité de l'aiguille quand on connait l'angle (avance de 6°) et la longueur de l'aiguille.
Sur le papier ou avec un tableur, ça marchait très bien, les 60 positions x, y de l’extrémité de l'aiguille étaient bonnes.

Après m'être tapé la tête contre les murs, pris une aspirine, réparé le mur, j'ai relu doucement la doc PB, et vu que les fonctions utilisant les angles les expriment par défaut le résultat en radian :idea: et non pas en degré :D (comme le font les calculatrices et les tableurs)

PS. Oui, bon, il existe un tas d'exemple de pendules à aiguille, mais j'aime bien comprendre comment ça marche plutôt que de commencer par utiliser une lib ou un coupé/collé :wink:

PS. Merci Falsam pour ce joli exemple didactique, ça donne envie de se mettre au graphisme :wink:
Avatar de l’utilisateur
falsam
Messages : 7244
Inscription : dim. 22/août/2010 15:24
Localisation : IDF (Yvelines)
Contact :

Re: Rotation & Déplacement d'un sprite vers une souris

Message par falsam »

Whaou, que de réponses rapides. Café en main, j'ai pris le temps de comprendre et de faire quelques tests.
microdevweb a écrit :je ne vois pas trop ton soucis? cela fonctionne !
Bonjour microdevweb. Non mon code ne fonctionne pas correctement.

Maintiens le clic gauche de la souris et déplace la coccinelle sur plusieurs points. Le résultat n'est pas propre car la coccinelle tremblote.


@Demivec: Merci beaucoup pour ton code, les rotations durant le déplacement sont trés fluides. C'est génial :)
Marc56 a écrit :près m'être tapé la tête contre les murs, pris une aspirine, réparé le mur, j'ai relu doucement la doc PB, et vu que les fonctions utilisant les angles les expriment par défaut le résultat en radian et non pas en degré (comme le font les calculatrices et les tableurs)
Et voila !!! Je n'ai pas pensé à la conversion degrés -> radian.

Dans le code que je propose, la variable Angle est en degrés et je me sers de cette variable pour mon calcul de Cosinus et Sinus.

Alors pourquoi ne pas juste faire la conversion en radian ?

C'est ce que j'ai fais. J'ai modifié mon code en remplaçant

Code : Tout sélectionner

;Déplacement de la cocinelle
If MouseButton(#PB_MouseButton_Left )        
    LadyBug\x + (LadyBug\Speed * Cos(Angle))
    LadyBug\y + (LadyBug\Speed * Sin(Angle))
EndIf
par

Code : Tout sélectionner

;Déplacement de la cocinelle
If MouseButton(#PB_MouseButton_Left)        
    LadyBug\x - (LadyBug\Speed * Cos(Radian(Angle)))
    LadyBug\y - (LadyBug\Speed * Sin(Radian(Angle)))
EndIf 
Effectivement les rotations durant les déplacement sont fluides mais ce n'est qu'en apparence. De temps en temps il y a des imperfections durant les rotations.

Je vais donc adopter le code de Demivec.
G-Rom a écrit :Pour le déplacement de sprite sur l'écran , j'utilise toujours le deltatime.
il se calcul sur le temps de rendu d'une boucle
J'ai éliminé de mon code tout ce qui était inutile à ma demande. Mais je suis d'accord avec toi.

Pour ceux qui se demandent de quoi parlent G-Rom, quelques explications sur ce lien dont il est l'auteur
:arrow: http://www.purebasic.fr/french/viewtopi ... 48#p165348

Comment ça G-Rom radote ? Oui il radote mais il a raison d'insister.
Configuration : Windows 11 Famille 64-bit - PB 6.03 x64 - AMD Ryzen 7 - 16 GO RAM
Vidéo NVIDIA GeForce GTX 1650 Ti - Résolution 1920x1080 - Mise à l'échelle 125%
Mesa
Messages : 1093
Inscription : mer. 14/sept./2011 16:59

Re: Rotation & Déplacement d'un sprite vers une souris

Message par Mesa »

Un code de comtois ici http://forums.purebasic.com/english/vie ... 13&t=62245
qui est intéressant.

Code : Tout sélectionner

;PB 4.02 6 mai 2007
;Updated 15/05/2015 for PB 5.31
;http://forums.purebasic.com/english/viewtopic.php?f=13&t=62245
;comtois

EnableExplicit ; Force la déclaration de toutes les variables 

Structure Vecteur
	x.f
	y.f
EndStructure


;Declare les procédures
Declare.f atan2f(y.f, x.f)
Declare Norme(*V.Vecteur)

;Initialise l'environnement nécessaire au fonctionnement des sprites et pour ouvrir un écran.
InitSprite()
;Initialise l'environnement propre à la gestion du clavier. 
InitKeyboard()
;Initialise l'environnement propre à la gestion de la souris. 
InitMouse()
;Ouvre un nouvel écran avec les caractéristiques Largeur, Hauteur et Profondeur. 
OpenScreen(800,600,32,"Tut Sprite3D et Souris")

;-Déclaration des variables
Define Angle, dist, Vitesse.f = 3
Define.Vecteur Direction, Sprite, Destination

Macro DISTANCE(p1, p2)
	Sqr((p1\x - p2\x) * (p1\x - p2\x) + (p1\y - p2\y) * (p1\y - p2\y))
EndMacro

;Creation d'une texture
CreateSprite(0,64,64)

;On dessine un triangle plein dans la texture
StartDrawing(SpriteOutput(0))
LineXY(1, 1, 1, SpriteHeight(0)-2,RGB(255,255,0))
LineXY(1, 1, SpriteWidth(0)-2, SpriteHeight(0)/2,RGB(255,255,0))
LineXY(1, SpriteHeight(0)-2, SpriteWidth(0)-2, SpriteHeight(0)/2,RGB(255,255,0))
FillArea(SpriteWidth(0)/2,SpriteHeight(0)/2,RGB(255,255,0),RGB(255,155,0))
StopDrawing()

;Sprite pour la souris
CreateSprite(1,5,5)
StartDrawing(SpriteOutput(1))
Box(0,0,5,5,RGB(0,255,0))
StopDrawing() 

Repeat
	;Inverse le buffer d'arrière plan avec le buffer visible à l'écran. 
	;La partie invisible du buffer remplace alors complètement La partie visible. 
	FlipBuffers()
	
	;Efface l'écran courant avec la couleur specifiée. 
	ClearScreen(RGB(0,0,100))
	
	;Met à jour l'état du clavier.Cette fonction doit être appelée avant d'utiliser les commandes 
	;KeyboardInkey(), KeyboardPushed() et KeyboardReleased(). 
	ExamineKeyboard()
	
	
	;Destination 
	If ExamineMouse()
		Destination\x = MouseX()
		Destination\y = MouseY()
	EndIf
	
	;Direction
	Direction\x = Destination\x - Sprite\x 
	Direction\y = Destination\y - Sprite\y
	Angle=Degree(ATan2(Direction\x, Direction\y))
	Norme(@Direction)
	
	;Deplacement sprite
	dist = DISTANCE(Destination, Sprite)
	
	If dist < Vitesse
		Sprite\x + Direction\x * dist
		Sprite\y + Direction\y * dist   
	Else 
		Sprite\x + Direction\x * Vitesse
		Sprite\y + Direction\y * Vitesse
	EndIf
	
	;Affiche le sprite 3D
	
	DisplayTransparentSprite(0, Sprite\x, Sprite\y)
	RotateSprite(0,Angle,0)
	
	
	;Affiche la souris
	DisplaySprite(1, Destination\x, Destination\y)
	
Until KeyboardPushed(#PB_Key_Escape)
End

Procedure Norme(*V.Vecteur)
	Define.f Norme
	
	Norme.f = Sqr(*V\x * *V\x + *V\y * *V\y)
	If Norme 
		*V\x / Norme
		*V\y / Norme
	EndIf
EndProcedure
M.
Répondre