Elastic physics algorithm

Blitz3D Forums/Blitz3D Programming/Elastic physics algorithm

Hi,

I was wondering if anyone would know how to do "elastic physics" (in 2D - well actually, 1D would suffice :))?

I would have one object (x1,y1) and another (x2,y2) that's "hanging" from it with a virtual wire. It would fall down, but start bouncing back once it reaches a certain distance, then fall again and come up again, and eventually stop... and when the first object moves, it would have to either fall down again or be pulled up (depending if the object moved up or down).

I can't find a way to do this realistically, but then again, I suck at physics.

Anyone wanna help me?

Thanks!

I can take no credit for this as you can see .. it should help you though. Use the arrow keys ...

; quick thrust game, by Wiebo de Wit
; Using verlet integration for movement and stick constraints for the ball and chain =]
; green line is your ship. I'm sorry but i didn't have the strength to include my new
; vector object engine. a and s to rotate, r-shift to thrust

Const TIMESTEP# = 0.05
Const GRAVITYANGLE = 0			; 0 = straight down, 90 = right, etc
Const GRAVITYFORCE# = 10
Const DRAG# = 0.995
Const THRUST# = 40
Const NUM_PARTICLES = 5
Const RESTLENGTH# = 20

Type Particle
	Field x#, y#
	Field old_x#, old_y#
	Field a_x#, a_y#
	Field mass#
	Field nxt.particle
End Type

Type Player
	Field angle#
	Field thrust#
	Field point.particle
End Type

Global player.player, p.particle

Graphics 640,480
SetBuffer BackBuffer()

; create player
player.player = New player

; player is a particle too, but you can control it
player\point = New particle
p.particle = player\point
p\x = 200 : p\y = 200
p\old_x = 200 : p\old_y = 200
p\a_x = 0 : p\a_y = 0

; make ship heavy
p\mass =1

For count = 1 To NUM_PARTICLES

	p\nxt = New particle
	p = p\nxt

	p\x = 200 : p\y = 200
	p\old_x = Rand(200,205) 
	p\old_y = Rand(200,205)
	p\a_x = 0 : p\a_y = 0
	p\mass = 60
Next

; last particle is ball. make heavy
p\mass = 1

; loop
While KeyHit( 1 ) = False

	GetInput()
	ApplyPhysics()
	Verlet()
	ConstrainParticlesMass()
	DrawParticles()
	Flip
	Cls

Wend

; ------------------------------------------------------------

Function GetInput()

	If KeyDown( 203 )
		;rotate
		player\angle = player\angle + 5
	EndIf

	If KeyDown( 205 )
		;rotate
		player\angle = player\angle - 5
	EndIf

	If KeyDown( 200 )
		;thrust, adjust player particle acceleration
		p.particle = player\point
		p\a_x = p\a_x - Sin( player\angle ) * THRUST
		p\a_y = p\a_y - Cos( player\angle ) * THRUST
	EndIf

End Function


Function ApplyPhysics()

	; apply gravity to particles
	For p.particle = Each particle
		p\a_x = p\a_x + Sin( GRAVITYANGLE ) * GRAVITYFORCE / p\mass
		p\a_y = p\a_y + Cos( GRAVITYANGLE ) * GRAVITYFORCE / p\mass
	Next

End Function


Function ConstrainParticlesMass()

	p.particle = player\point

	While p\nxt <> Null
		dx# = p\nxt\x - p\x
		dy# = p\nxt\y - p\y

		deltalength# = Sqr( dx*dx + dy*dy )
		diff# = ( deltalength - RESTLENGTH ) / (deltalength * ( -p\mass + -p\nxt\mass ) )

		p\x = p\x + -p\mass * dx * diff
		p\y = p\y + -p\mass * dy * diff
		p = p\nxt
		p\x = p\x - -p\mass * dx * diff
		p\y = p\y - -p\mass * dy * diff
	Wend

End Function


Function DrawParticles()

	p.particle = player\point

	; draw player, green line

	Color 0,255,0
	dx# = Sin( player\angle) 
	dy# = Cos( player\angle)
	Line p\x-dx*20, p\y-dy*20 , p\x + dy*5, p\y - dx*5
	Line p\x-dx*20, p\y-dy*20 , p\x - dy*5, p\y + dx*5
	Line p\x + dy*5, p\y - dx*5, p\x - dy*5, p\y + dx*5

	; draw line
	Color 255,0,0
	While p\nxt <> Null
		Line p\x, p\y, p\nxt\x, p\nxt\y
		p = p\nxt
	Wend

	; draw ball at end
	Color 255,255,0
	Oval p\x -14, p\y-14, 28,28, 0

	; white dots, for clarity
	Color 255,255,255
	For p.particle =Each particle
		Plot p\x, p\y
	Next

End Function


Function Verlet()

	; eg: move particles, introducing drag

	For p.particle = Each particle

		x# = p\x
		y# = p\y

		tempx# = p\x
		tempy# = p\y

		oldx# = p\old_x
		oldy# = p\old_y

		p\x = p\x + DRAG * x - DRAG * oldx + p\a_x * TIMESTEP * TIMESTEP
		p\y = p\y + DRAG * y - DRAG * oldy + p\a_y * TIMESTEP * TIMESTEP

		p\old_x = tempx
		p\old_y = tempy

		; reset acceleration after moving particle
		p\a_x = 0 : p\a_y = 0
	Next

End Function


Stevie G is a poet, and he doesn't know it!

You do know that I'm Pam Ayres second cousin twice removed!!

Oh man!! That code is awesome! I mean it's a really simple program but the elastic effect is fantastic. It would be great to convert this to a 3d demo.

Could someone who has a good understanding of this sort of physics perhaps comment the code a bit for us. I would really love to understand how the code does what it does.

max version (just a quick replace of some bits, no point ooping it up just for this lil example)

Const TIMESTEP# = 0.05
Const GRAVITYANGLE = 0			'0 = straight down, 90 = Right, etc
Const GRAVITYFORCE# = 10
Const DRAG# = 0.995
Const THRUST# = 40
Const NUM_PARTICLES = 5
Const RESTLENGTH# = 20

Type Particle
	Field x#, y#
	Field old_x#, old_y#
	Field a_x#, a_y#
	Field mass#
	Field nxt:particle
End Type

Type Player
	Field angle#
	Field thrust#
	Field point:particle
End Type

Global particlelist:TList = New TList
Global tmpplayer:player, p:particle

Graphics 640,480,0

' create player
tmpplayer:player = New player

' player is a particle too, but you can control it
tmpplayer.point = New particle
particlelist.addlast(tmpplayer.point)

p:particle = tmpplayer.point
p.x = 200
p.y = 200

p.old_x = 200
p.old_y = 200

p.a_x = 0
p.a_y = 0

' make ship heavy
p.mass =1

For count = 1 To NUM_PARTICLES

	p.nxt = New particle
	particlelist.addlast(p.nxt)
	p = p.nxt

	p.x = 200
	p.y = 200
	
	p.old_x = Rand(200,205) 
	p.old_y = Rand(200,205)
	
	p.a_x = 0
	p.a_y = 0
	
	p.mass = 60
Next

' last particle is ball. make heavy
p.mass = 1

' loop
While Not KeyHit(key_escape)

	GetInput()
	ApplyPhysics()
	Verlet()
	ConstrainParticlesMass()
	DrawParticles()
	Flip
	Cls

Wend

' ------------------------------------------------------------

Function GetInput()

	If KeyDown(key_a)
		'rotate
		tmpplayer.angle = tmpplayer.angle + 5
	EndIf

	If KeyDown(key_d)
		'rotate
		tmpplayer.angle = tmpplayer.angle - 5
	EndIf

	If KeyDown(key_w)
		'thrust, adjust player particle acceleration
		p:particle = tmpplayer.point
		p.a_x = p.a_x - Sin( tmpplayer.angle ) * THRUST
		p.a_y = p.a_y - Cos( tmpplayer.angle ) * THRUST
	EndIf

End Function


Function ApplyPhysics()

	' apply gravity To particles
	For p:particle = EachIn particlelist
		p.a_x = p.a_x + Sin( GRAVITYANGLE ) * GRAVITYFORCE / p.mass
		p.a_y = p.a_y + Cos( GRAVITYANGLE ) * GRAVITYFORCE / p.mass
	Next

End Function


Function ConstrainParticlesMass()

	p:particle = tmpplayer.point

	While p.nxt <> Null
		dx# = p.nxt.x - p.x
		dy# = p.nxt.y - p.y

		deltalength# = Sqr( dx*dx + dy*dy )
		diff# = ( deltalength - RESTLENGTH ) / (deltalength * ( -p.mass + -p.nxt.mass ) )

		p.x = p.x + -p.mass * dx * diff
		p.y = p.y + -p.mass * dy * diff
		p = p.nxt
		p.x = p.x - -p.mass * dx * diff
		p.y = p.y - -p.mass * dy * diff
	Wend

End Function


Function DrawParticles()

	p:particle = tmpplayer.point

	' draw player, green line

	SetColor 0,255,0
	dx# = Sin( tmpplayer.angle) 
	dy# = Cos( tmpplayer.angle)
	DrawLine p.x-dx*20, p.y-dy*20 , p.x + dy*5, p.y - dx*5
	DrawLine p.x-dx*20, p.y-dy*20 , p.x - dy*5, p.y + dx*5
	DrawLine p.x + dy*5, p.y - dx*5, p.x - dy*5, p.y + dx*5

	' draw line
	SetColor 255,0,0
	While p.nxt <> Null
		DrawLine p.x, p.y, p.nxt.x, p.nxt.y
		p = p.nxt
	Wend

	' draw ball at End
	SetColor 255,255,0
	DrawOval p.x-14, p.y-14, 28,28

	' white dots, For clarity
	SetColor 255,255,255
	For p:particle = EachIn particlelist
		Plot p.x, p.y
	Next

End Function


Function Verlet()

	' eg: move particles, introducing drag

	For p:particle = EachIn particlelist

		x# = p.x
		y# = p.y

		tempx# = p.x
		tempy# = p.y

		oldx# = p.old_x
		oldy# = p.old_y

		p.x = p.x + DRAG * x - DRAG * oldx + p.a_x * TIMESTEP * TIMESTEP
		p.y = p.y + DRAG * y - DRAG * oldy + p.a_y * TIMESTEP * TIMESTEP

		p.old_x = tempx
		p.old_y = tempy

		' reset acceleration after moving particle
		p.a_x = 0
		p.a_y = 0
	Next

End Function


I added this to GetInput()

B3D
	If KeyDown( 57 )
		p.particle = player\point
		p\x = 512	;because screen is 1024 x 768
		p\y = 384
	EndIf

BMax
	If KeyDown(key_space)
		p:particle = tmpplayer.point
		p.x = 512
		p.y = 384
	EndIf


In case the triangle gets lost (happens often enough when playing with different parameters) keep space pressed until things have calmed down.

Very useful Code snippet that you found, Stevie G - thanks!