Realistic 'rope' physics?
Blitz3D Forums/Blitz3D Programming/Realistic 'rope' physics?
| ||
Hi, I was messing around the other night practicing my maths work, etc., and I came up with this almost by accident:Graphics 640, 400, 32, 1 SetBuffer BackBuffer() Type dat Field x#, y# Field h#, v# End Type Global ball.dat ; Create 100 links in the chain For n=0 To 99 ball.dat = New dat ball\x = Rand(GraphicsWidth()) ball\y = Rand(GraphicsHeight()) Next Repeat Cls ; Draw blue oval for the mouse Color 0, 0, 255 Oval MouseX()-5, MouseY()-5, 10, 10 ; set coords for first link in chain to current mouse coords lastx# = MouseX() lasty# = MouseY() lastv# = MouseYspeed() lasth# = MouseXspeed() For ball.dat = Each dat ball\v = ball\v + .5 ; Increase Y velocity by 1 to simulate gravity ; ball\h = ball\h + (lastx - ball\x) * .05 ; Calcuate current velocity by comparing current link to previous links positions ; ball\v = ball\v + (lasty - ball\y) * .05 ball\v = (ball\v + lastv) * .5 ; Average the velocities between the current link and the previous link ball\h = (ball\h + lasth) * .5 ball\v = ball\v * .95 ; Apply friction from air resistance ball\h = ball\h * .95 ball\x = ball\x + ball\h ; Move link in the new direction ball\y = ball\y + ball\v ; Stop chain link from dropping below screen or off to the sides If ball\y > GraphicsHeight() - 10 Then ball\y = GraphicsHeight() - 10: ball\v = -ball\v If ball\x < 0 Then ball\x = 0: ball\h = - ball\h If ball\x > GraphicsWidth() Then ball\x = GraphicsWidth(): ball\h = -ball\h ; Calculate the distance between current & previous links xdiff# = lastx - ball\x ydiff# = lasty - ball\y dist# = Sqr(xdiff * xdiff + ydiff * ydiff) ; Calculate the vector normal of where the link should be If Abs(dist#) > 0 Then xd# = (lastx - ball\x) / dist# yd# = (lasty - ball\y) / dist# Else xd# = 0 yd# = 0 End If ; Set the distance between current link and previous link xd = xd * 5 yd = yd * 5 ; Calculate new position ball\x = (lastx - xd) ball\y = (lasty - yd) ; Draw the link Color 255, 0, 0 Oval ball\x-2, ball\y-2, 4, 4 Line lastx, lasty, ball\x, ball\y ; Save current links coords and velocities lastx# = ball\x lasty# = ball\y lasth# = ball\h lastv# = ball\v Next Flip Until KeyHit(1) End Now, I have *no idea* if this is the 'proper' way of simulating this sort of thing, I just worked it out as best I could. I can't help feeling that I can improve on this though. Is there anybody who might be able to give me any pointers as to how I might improve on this? the biggest problem I have at the moment is that the links 'concertina' up when the gravity is set below a certain amount. I don't want that to happen basically, I just want the rope to move a little more realistically. EDIT: Just edited out a couple of lines and altered a couple. EDIT 2: Ha! That's seems to have done the trick. Well, sort of. |
| ||
hey that is fun. nice work! |
| ||
The proper way would be to use verlet integration where the velocity is implicit. Much more stable than Euler. You could look up Jakobsens paper on Gamasutra. Here's an old example by Weibo .....; 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 |
| ||
Thanks for that, I'm going to have a closer look at this today and try to understand it. Great stuff! |
| ||
Essentially it uses change in location as an integral towards velocity. This change in location comes from a spring limiter between "particles" in the solution. Basically, you sum all forces(including the spring force from any particles the particle in question is linked to). After suming the forces on this particle, you integrate them into its acceleration. I'm not going to go into it any more as there are some great papers on the subject with formulas. All in all, when the different principles of the integration scheme are working together, it creates a simulation applicable for rope, non deforming objects, and cloth.(other things as well but these are the common uses.) |
| ||
I'm just going through the Gamasutra article Stevie G suggested. I'm beginning to see how the system works, it's quite versatile. Ironically I began to write something similar to that in the program above, but ultimately used the Euler system instead. I'll rewrite this little program and post a new version of it when I've got to grips with Verlets more. Thanks for both your help! |
| ||
@Dev : Ah, so that's how they did swirling dresses in Shrek. Which software do the pros us to create films like that? |
| ||
John: Maya (probably). |
| ||
And they probably use something alot more advanced :). |