Moteur de rebond : Une Bille pratique !

Programmation avancée de jeux en PureBasic
Avatar de l’utilisateur
graph100
Messages : 1318
Inscription : sam. 21/mai/2005 17:50

Moteur de rebond : Une Bille pratique !

Message par graph100 »

Bon je le post maintenant, sinon je n'y penserais jamais :P

le code ci-dessous est un Module à inclure dans un projet pour disposer d'un moteur de rebond tout fait !
Il faut juste respecter la chose suivante pour ne pas avoir de bug : en aucun cas une bille ne doit toucher le bords de l'écran, elle doit rebondir avant.

le code contient une partie exemple, qui montre comment s'en servir. Cette partie est automatiquement enlevée lors de l'inclusion dans un autre fichier.

Parmi les trucs intéressant du code, il y a notamment ce dont je parlais dans le post à falsam :

- la détection des collisions est gérée de sorte que jamais, quelque que soit la vitesse de la bille, elle ne puisse manquer un obstacle sur son chemin !
- le rebond est calculé en fonction de ce contre quoi la bille s'est cognée, avec la normale au contact. C'est donc assez réaliste.

à faire : ajouter la rotation de la bille au contact, et les effets que ça entraine lors d'une collision.

Avec ce module, j'ai fais un petit jeu sur windows : un casse-brique simple.
Ce jeu ajoute une notion un peu plus complexe : une callback lors de la collision avec certaine partie (ici les briques). C'est assez pratique.
Image

A priori on pourrait programmer n'importe quoi avec une bille 2D dedans. Comme un flipper, ou autre (j'ai pas d'idée là).

Un des problèmes qui reste, est l'ajout d'éléments qui bougent dans la détection de la collision.
On peut remarquer que dans le casse brique, lorsque le curseur va sur la bille, le comportement est bizarre. Il faut que je corrige ce point.

Le Module :

Code : Tout sélectionner


;{ MODULE BILLE


DeclareModule BILLE
	
	Structure V2D
		x.d
		y.d
	EndStructure
	
	Structure _bille_contact_
		x.d
		y.d
		a.d ; angle
		
		p.V2D
		vec.V2D
		
		C.b
	EndStructure
	
	Structure _bille_
		; position et Mouvement du centre de gravité
		x.d
		y.d
		a.d ; angle
		
		o.V2D ; ancienne position de la bille
		
		r.d ; rayon
		
		v.d ; vitesse horizontale
		u.d ; vitesse verticale
		w.d ; vitesse de rotation
		
		m.d ; masse
		I.d ; moment d'inertie
		
		nb_contact.l
		Color.l
		
		Array contact._bille_contact_(0)
		List trace._bille_contact_()
		
		max_trace.l
		trace_step.b
		Count.l
	EndStructure
	
	Structure _Limit_
		w.l
		h.l
		
		Array Limit.i(0,0)
		Array info.l(0,0)
	EndStructure
	
	
	Global _Gravite_.V2D
	
	Declare.i Init_Limit(Output.l, Color.l) ; Color is where the ball can go
	Declare.i Init_Bille(x, y, rayon, Color.l, Taille_Trace.l = 0)
	
	Declare.i Add_ActionOnRebound(*Limit._Limit_, Output.l, *Function)
	
	Declare Process_Bille(*Limit._Limit_, *Bille._bille_, dT.d)
	Declare DrawBille(*Bille._bille_)
	
EndDeclareModule

Module BILLE
	
	#Distance_Pt_detection_contact = 2
	#MAX_trace = 10
	
	Procedure.i Init_Limit(Output.l, Color.l) ; Color is where the ball can go
		
		If StartDrawing(Output)
			
			*limit._Limit_ = AllocateMemory(SizeOf(_Limit_))
			InitializeStructure(*limit, _Limit_)
			
			*limit\w = OutputWidth() - 1
			*limit\h = OutputHeight() - 1
			
			Dim *limit\Limit(*limit\w, *limit\h)
			Dim *limit\info(*limit\w, *limit\h)
			
			For x = 0 To *limit\w
				For y = 0 To *limit\h
					If Point(x, y) <> Color
						*limit\Limit(x, y) = 1
					EndIf
				Next
			Next
			
			StopDrawing()
			
			ProcedureReturn *limit
		Else
			ProcedureReturn #False
		EndIf
		
	EndProcedure
	
	Procedure.i Add_ActionOnRebound(*Limit._Limit_, Output.l, *ptr_ptr_Function)
		
		If *Limit And StartDrawing(Output)
			
			For x = 0 To *limit\w
				For y = 0 To *limit\h
					color = Point(x, y)
					
					If color
						*limit\Limit(x, y) = *ptr_ptr_Function
						*limit\info(x, y) = color
					EndIf
				Next
			Next
			
			StopDrawing()
			
			ProcedureReturn *limit
		Else
			ProcedureReturn #False
		EndIf
		
	EndProcedure
	
	Procedure.i Init_Bille(x, y, rayon, Color.l, Taille_Trace.l = 0)
		
		*bille._bille_ = AllocateMemory(SizeOf(_bille_))
		InitializeStructure(*bille, _bille_)
		
		*bille\x = x
		*bille\y = y
		*bille\r = rayon
		
		*bille\o\x = x
		*bille\o\y = y
		
		_tmp_.d = *bille\r * *bille\r
		
		*bille\m	= #PI * _tmp_
		*bille\I	= *bille\m * _tmp_  / 2
		
		*bille\Color = Color
		*bille\max_trace = Taille_Trace
		
		*bille\nb_contact = Round(2 * #PI * *bille\r / #Distance_Pt_detection_contact, #PB_Round_Up)
		
		Dim *bille\contact(*bille\nb_contact)
		
		_tmp_.d = 2 * #PI / (*Bille\nb_contact + 1)
		For i = 0 To *Bille\nb_contact
			*Bille\contact(i)\a = i * _tmp_
			
			*Bille\contact(i)\vec\x = Cos(*Bille\contact(i)\a)
			*Bille\contact(i)\vec\y = Sin(*Bille\contact(i)\a)
			
			*Bille\contact(i)\p\x = *Bille\r * *Bille\contact(i)\vec\x
			*Bille\contact(i)\p\y = *Bille\r * *Bille\contact(i)\vec\y
		Next
		
		ProcedureReturn *bille
	EndProcedure
	
	
	Procedure Process_Bille(*Limit._Limit_, *Bille._bille_, dT.d)
		*Bille\Count + 1
		
		;{ physique
		
		; 		_ax_.d = 0 ; sert à stocker les forces x
		; 		_ay_.d = 0 ; sert à stocker les forces y
		;			_cz_.d = 0 ; sert à stocker le couple
		
		
		; Forces
		_ax_ = _Gravite_\x ;+ _ax_ / *Bille\m
		_ay_ = _Gravite_\y ;+ _ay_ / *Bille\m
		;_cz_ = _cz_ / *Bille\I
		
		; ajout acc --> vitesse
		*Bille\u + _ax_ * dT
		*Bille\v + _ay_ * dT
		;*Bille\w + _cz_ * dT
		
		If KeyboardPushed(#PB_Key_B)
			*Bille\u * 1.02
			*Bille\v * 1.02
		EndIf
		
		If KeyboardPushed(#PB_Key_N)
			*Bille\u / 1.02
			*Bille\v / 1.02
		EndIf
		
		*Bille\o\x = *Bille\x
		*Bille\o\y = *Bille\y
		
		; Ajout vitesse --> position
		*Bille\x + *Bille\u * dT
		*Bille\y + *Bille\v * dT
		;*Bille\a + *Bille\w * dT
		
		;}
		
		
		;{ détection de contact
		
		direction.V2D\x = *Bille\x - *Bille\o\x
		direction\y = *Bille\y - *Bille\o\y
		
		Taille.d = Sqr(direction\x * direction\x + direction\y * direction\y) / 2
		
		If Taille < 1 : Taille = 1 : EndIf
		
		direction\x	/ Taille
		direction\y	/ Taille
		
		pos.V2D\x = *Bille\o\x
		pos\y = *Bille\o\y
		
		For j = 1 To Taille
			pos\x + direction\x
			pos\y + direction\y
			
			_nb_contact = 0
			
			For i = 0 To *Bille\nb_contact
				*Bille\contact(i)\x = pos\x + *Bille\contact(i)\p\x
				*Bille\contact(i)\y = pos\y + *Bille\contact(i)\p\y
				
				x.l = *Bille\contact(i)\x
				y.l = *Bille\contact(i)\y
				
				*Bille\contact(i)\C = #False
				
				If *Limit\Limit(x, y)
					*Bille\contact(i)\C = #True
					
					_nb_contact + 1
					
					If *Limit\Limit(x, y) <> 1
						
						CallFunctionFast(PeekI(*Limit\Limit(x, y)), *Limit\Limit(x, y), *Limit, *Bille, x, y)
						
					EndIf
				EndIf
			Next
			
			If _nb_contact
				;{ contact
				
				*Bille\x = pos\x - direction\x
				*Bille\y = pos\y - direction\y
				
				_u_.d = 0
				_v_.d = 0
				
				For i = 0 To *Bille\nb_contact
					If *Bille\contact(i)\C = #True
						
						_u_ - *Bille\contact(i)\vec\y
						_v_ + *Bille\contact(i)\vec\x
						
					EndIf
				Next
				
				_tmp_.d = Sqr(_u_ * _u_ + _v_ * _v_)
				
				_u_ / _tmp_; / 1.05
				_v_ / _tmp_; / 1.05
				
				*Bille\x - _v_; * 2
				*Bille\y + _u_; * 2
				
				_tmp_c_x.d = *Bille\u * _u_ + *Bille\v * _v_
				_tmp_c_y.d = *Bille\v * _u_ - *Bille\u * _v_
				
				_tmp_d_x.d = _tmp_c_x * _u_ + _tmp_c_y * _v_
				_tmp_d_y.d = _tmp_c_x * _v_ + _tmp_c_y * (-_u_)
				
				*Bille\u = _tmp_d_x
				*Bille\v = _tmp_d_y
				
				;}
				
				Break
			EndIf
			
		Next
		
		;}
		
		
		;{ trace
		
		If *Bille\max_trace
			
			*Bille\trace_step = 0
			If *Bille\Count % 1 = 0 Or _REBOND_
				LastElement(*Bille\trace())
				AddElement(*Bille\trace())
				*Bille\trace()\x = *Bille\x
				*Bille\trace()\y = *Bille\y
				*Bille\trace()\C = *Bille\max_trace
				
				*Bille\trace_step = 1
				
				If ListSize(*Bille\trace()) > *Bille\max_trace
					FirstElement(*Bille\trace())
					DeleteElement(*Bille\trace())
				EndIf
			EndIf
			
		EndIf
		
		;}
		
	EndProcedure
	
	Procedure DrawBille(*Bille._bille_)
		
		If *Bille\max_trace
			ForEach *Bille\trace()
				
				_tmp_.d = *Bille\trace()\C / *Bille\max_trace
				Circle(*Bille\trace()\x, *Bille\trace()\y, *Bille\r * _tmp_, $ff * _tmp_)
				
				*Bille\trace()\C - *Bille\trace_step
			Next
		EndIf
		
		Circle(*Bille\x, *Bille\y, *Bille\r, *Bille\Color)
		;LineXY(*Bille\x, *Bille\y, *Bille\x + *Bille\r * Cos(*Bille\a), *Bille\y + *Bille\r * Sin(*Bille\a), 0)
	EndProcedure
	
	
EndModule


;}

CompilerIf #PB_Compiler_IsMainFile
	
;{ Initialisation & fenetre

InitSprite()
InitKeyboard()
UsePNGImageDecoder()

OpenWindow(0, 0, 0, 800, 660, "Flipper", #PB_Window_ScreenCentered|#PB_Window_SystemMenu)
OpenWindowedScreen(WindowID(0), 0, 0, 800, 660)
KeyboardMode(1)

;LoadSprite(0, "flipper.png")

;{ decors
CreateSprite(0, 800, 600)

NewList *bille.BILLE::_bille_()


StartDrawing(SpriteOutput(0))

Box(0, 0, 800, 600, $FFFFFF)

For i = 0 To 250
	rayon = Random(35)+20
	
	
	x.d = rayon + Random(800 - 2 * rayon)
	y.d = rayon + Random(600 - 2 * rayon)
	
	If i%100=0
		AddElement(*bille())
		*bille() = BILLE::Init_Bille(x, y, Random(10)+5, $FF, 20)
		*bille()\u = 200
	EndIf
	
	Circle(x, y, rayon - 10, 0)
Next

Line(0, 0, 800, 1, $FFFFFF)
Line(0, 599, 800, 1, $FFFFFF)
Line(0, 0, 1, 600, $FFFFFF)
Line(799, 0, 1, 600, $FFFFFF)

StopDrawing()

;}

*Limit = BILLE::Init_Limit(SpriteOutput(0), 0)

; *bille = BILLE::Init_Bille(100, 300, 10, $FF)
; *bille2 = BILLE::Init_Bille(400, 300, 5, $FF)

_time_ = ElapsedMilliseconds()-5 ; evite que _dt_ = 0 au début

;}

;{ boucle principale

Repeat
	
	;{ Event
	
	Repeat
		_event_ = WindowEvent()
	Until _event_ = 0 Or #PB_Event_CloseWindow
	
	ExamineKeyboard()
	
	If KeyboardPushed(#PB_Key_Escape)
		_event_ = #PB_Event_CloseWindow
	EndIf
	
	BILLE::_Gravite_\x = 0
	BILLE::_Gravite_\y = 0
	
	If KeyboardPushed(#PB_Key_Left)
		BILLE::_Gravite_\x = -500
	EndIf
	
	If KeyboardPushed(#PB_Key_Right)
		BILLE::_Gravite_\x = 500
	EndIf
	
	If KeyboardPushed(#PB_Key_Up)
		BILLE::_Gravite_\y = -500
	EndIf
	
	If KeyboardPushed(#PB_Key_Down)
		BILLE::_Gravite_\y = 500
	EndIf
	
	
	;}
	
	
	_dt_.d = (ElapsedMilliseconds() - _time_) / 1000
	_time_ = ElapsedMilliseconds()
	
	
	; 	BILLE::Process_Bille(*Limit, *bille, _dt_)
	; 	BILLE::Process_Bille(*Limit, *bille2, _dt_)
	
	ForEach *bille()
		BILLE::Process_Bille(*Limit, *bille(), _dt_)
	Next
	
	;{ dessin
	
	ClearScreen(0)
	DisplaySprite(0, 0, 0)
	
	
	StartDrawing(ScreenOutput())
	
	DrawText(0, 0, "dT = " + _dt_, 0, $FFFFFF)
	
	; 	BILLE::DrawBille(*bille)
	; 	BILLE::DrawBille(*bille2)
	
	ForEach *bille()
		BILLE::DrawBille(*bille())
	Next
	
	DrawText(10, 610, "[B] / [N] : Accélérer / Ralentir toutes les billes", $FFFFFF, 0)
	DrawText(10, 630, "[Flèches] : Applique une force dans la direction de la flèche", $FFFFFF, 0)
	
	StopDrawing()
	
	
	FlipBuffers()
	
	;}
	
Until  _event_ = #PB_Event_CloseWindow

;}

CompilerEndIf
_________________________________________________
Mon site : CeriseCode (Attention Chantier perpétuel ;))
comtois
Messages : 5172
Inscription : mer. 21/janv./2004 17:48
Contact :

Re: Moteur de rebond : Une Bille pratique !

Message par comtois »

Je viens de tester le casse brique, bravo, c'est excellent.
Tu peux faire un billard maintenant :)
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.
Avatar de l’utilisateur
graph100
Messages : 1318
Inscription : sam. 21/mai/2005 17:50

Re: Moteur de rebond : Une Bille pratique !

Message par graph100 »

Pour le billard, il y a un autre soucis : les interactions entre les billes...
Et ça, ça n'a jamais été ma tasse de thé. Pour le nombre de fois que je m'y suis cassé les dents ! :mrgreen:
Dernière modification par graph100 le sam. 09/août/2014 8:26, modifié 1 fois.
_________________________________________________
Mon site : CeriseCode (Attention Chantier perpétuel ;))
Avatar de l’utilisateur
venom
Messages : 3072
Inscription : jeu. 29/juil./2004 16:33
Localisation : Klyntar
Contact :

Re: Moteur de rebond : Une Bille pratique !

Message par venom »

Excellent ce code, merci graph100. Je compte pas coder de casse brique pour le moment mais ce code est a garder de coté. 8)






@++
Windows 10 x64, PureBasic 5.73 x86 & x64
GPU : radeon HD6370M, CPU : p6200 2.13Ghz
Avatar de l’utilisateur
graph100
Messages : 1318
Inscription : sam. 21/mai/2005 17:50

Re: Moteur de rebond : Une Bille pratique !

Message par graph100 »

Merci :D

Tu remarqueras qu'il contient aussi ce dont a parlé G-Rom sur la vitesse des objets :

Code : Tout sélectionner

	_dt_.d = (ElapsedMilliseconds() - _time_) / 1000 
	_time_ = ElapsedMilliseconds()
Je l'utilise quasiment dans tout les projets de ce type. C'est un peu plus compliqué pour régler la vitesse de ces objets, mais ça se fait !
_________________________________________________
Mon site : CeriseCode (Attention Chantier perpétuel ;))
Avatar de l’utilisateur
venom
Messages : 3072
Inscription : jeu. 29/juil./2004 16:33
Localisation : Klyntar
Contact :

Re: Moteur de rebond : Une Bille pratique !

Message par venom »

Oui, j'ai regarder le code sans trop m'y attarder mais je regarderais a ça de plus près :P






@++
Windows 10 x64, PureBasic 5.73 x86 & x64
GPU : radeon HD6370M, CPU : p6200 2.13Ghz
Répondre