Firstly, thanks for all the help and support. Much appreciated.
Secondly, sorry for a ridiculously long post. Feel free to skim.
@ Stevie
I'm not exactly sure which method you're talking about. I've scrapped (for now) the adjustX#/adjustY# method, but its remains can still be seen in the code. Could you explain what you mean by the "standard" method? I don't appear to have any "accumulate forces" part of my code... perhaps I should? Or is that what I was trying (unsuccessfully) to accomplish with my adjustX#/adjustY# code? I'm a bit confused here...
My blob is (I believe) set up identically to the one in
this article. Run the code to see.
I may have to implement angular or distance constraints, but I think there's an underlying problem that needs to get dealt with first.
Thanks
a ton for all of your suggestions! I haven't tried them all, but changing the number of relaxations and time step doesn't seem to help too much. I have drag implemented (just a basic *1.99 sort of thing), but friction wouldn't seem to matter at this point as I have no level!
@ Nate the Great
Thanks so much... great idea! I implemented it and it helped the stability somewhat. Blobs with 10 outer joints used to collapse/fold in half. With your trick, they did much better, but blobs would still collapse when I got around 12 outer joints, so clearly there is a deeper problem.
@ Pongo
Thanks for the advice. My shape is the same as that of the article I mentioned, and you can always run my code to see. I still could be setting it up incorrectly, because it seems like there is an excess of energy in the system at the start - and this is magnified by the number of joints in the system.
@ All
Without any further ado, I present my code! Even though some suggestions (Nate the Great's neat trick, Stevie's - and others' - advice to increase the number of relaxation iterations and decrease the time step, etc.) helped some, none solved the underlying problem, so here is the code
as I originally wrote it. I apologize in advance for any unreadability. Ask me if you have any questions.
Another problem I forgot to mention is every blob's tendency to spin counter-clockwise during its initial freefall. Could this have to do with the problem? I have absolutely no clue why it happens, so maybe...
Also good to know:
-Blobs are created by clicking the mouse.
-Blobs can be controlled by the arrow keys and the spacebar.
-To change the size of a blob, you must alter the constant
NUM_OUTER_BLOB_JOINTS at the top of the code. Because of Blitz arrays, I couldn't implement a way to change the number of blob joints during the running of the program.
And here it is.
Graphics 800, 600, 0, 2
AppTitle "Verlet Demonstration"
SeedRnd MilliSecs()
Const NUM_OUTER_BLOB_JOINTS = 6
Const BLOB_OUTER_DISTANCE = 100, BLOB_INNER_DISTANCE = 80
Type blob
Field joints.joint[NUM_OUTER_BLOB_JOINTS*2]
Field centerPoint.point
End Type
Type joint
Field x#, y#
Field lastX#, lastY#
Field adjustX#, adjustY#
Field ax#, ay#
Field inverseMass
Field otherJoint1.joint, otherJoint2.joint
Field correctDistance1#, correctDistance2#
End Type
Type point
Field x#, y#
Field lastX#, lastY#
Field adjustX#, adjustY#
Field ax#, ay#
Field inverseMass
Field otherJoints.joint[NUM_OUTER_BLOB_JOINTS]
Field correctDistance#
End Type
Const GRAVITY# = .2
Const TIME_STEP = 1
Global startX, startY, endX, endY
Global timePassed
SetBuffer BackBuffer()
MainLoop()
Function CheckMouse()
If MouseHit(1)
CreateBlob(MouseX(), MouseY())
EndIf
End Function
Function CheckKeys()
If KeyDown(205)
For p.point = Each point
p\x# = p\x# + 1
Next
ElseIf KeyDown(203)
For p.point = Each point
p\x# = p\x# - 1
Next
EndIf
If KeyDown(200)
For p.point = Each point
p\y# = p\y# - 10
Next
EndIf
If KeyHit(57)
For p.point = Each point
p\lastY# = p\y# + 100
Next
EndIf
End Function
Function CreateBlob(x, y)
b.blob = New blob
;START CREATE OUTER JOINTS
For n = 0 To NUM_OUTER_BLOB_JOINTS - 1
b\joints[n] = New joint
b\joints[n]\x# = Cos#(360.0/NUM_OUTER_BLOB_JOINTS*n)*BLOB_OUTER_DISTANCE + x
b\joints[n]\lastX# = b\joints[n]\x#
b\joints[n]\y# = Sin#(360.0/NUM_OUTER_BLOB_JOINTS*n)*BLOB_OUTER_DISTANCE + y
b\joints[n]\lastY# = b\joints[n]\y#
b\joints[n]\inverseMass = 1
b\joints[n]\ay# = GRAVITY#/b\joints[n]\inverseMass
Next
For n = 0 To NUM_OUTER_BLOB_JOINTS - 2
b\joints[n]\otherJoint1 = b\joints[n + 1]
Next
b\joints[NUM_OUTER_BLOB_JOINTS - 1]\otherJoint1 = b\joints[0]
;END CREATE OUTER JOINTS
;START CREATE INNER JOINTS
For n = NUM_OUTER_BLOB_JOINTS To NUM_OUTER_BLOB_JOINTS*2 - 1
b\joints[n] = New joint
b\joints[n]\x# = Cos#(360.0/NUM_OUTER_BLOB_JOINTS*n)*BLOB_INNER_DISTANCE + x
b\joints[n]\lastX# = b\joints[n]\x#
b\joints[n]\y# = Sin#(360.0/NUM_OUTER_BLOB_JOINTS*n)*BLOB_INNER_DISTANCE + y
b\joints[n]\lastY# = b\joints[n]\y#
b\joints[n]\inverseMass = 1
b\joints[n]\ay# = GRAVITY#/b\joints[n]\inverseMass
Next
For n = NUM_OUTER_BLOB_JOINTS To NUM_OUTER_BLOB_JOINTS*2 - 2
b\joints[n]\otherJoint1 = b\joints[n + 1]
Next
b\joints[NUM_OUTER_BLOB_JOINTS*2 - 1]\otherJoint1 = b\joints[NUM_OUTER_BLOB_JOINTS]
;END CREATE INNER JOINTS
;set correct joint distances
For n = 0 To NUM_OUTER_BLOB_JOINTS*2 - 1
b\joints[n]\correctDistance1# = GetDistance#(b\joints[n]\x#, b\joints[n]\y#, b\joints[n]\otherJoint1\x#, b\joints[n]\otherJoint1\y#)
Next
;attach outer joints to inner joints
For n = 0 To NUM_OUTER_BLOB_JOINTS - 1
;attach outer joint to corresponding inner joint
b\joints[n]\otherJoint2 = b\joints[n + NUM_OUTER_BLOB_JOINTS]
;attach inner joint to diagonal outer joint
b\joints[n + NUM_OUTER_BLOB_JOINTS]\otherJoint2 = b\joints[n + 1]
Next
;attach last inner joint to last diagonal outer joint
b\joints[NUM_OUTER_BLOB_JOINTS*2 - 1]\otherJoint2 = b\joints[0]
;set correct 2-layer distances
For n = 0 To NUM_OUTER_BLOB_JOINTS*2 - 1
b\joints[n]\correctDistance2# = GetDistance#(b\joints[n]\x#, b\joints[n]\y#, b\joints[n]\otherJoint2\x#, b\joints[n]\otherJoint2\y#)
Next
;START CREATE CENTER POINT
b\centerPoint.point = New point
b\centerPoint\x# = x
b\centerPoint\y# = y
b\centerPoint\lastX# = b\centerPoint\x#
b\centerPoint\lastY# = b\centerPoint\y#
b\centerPoint\ax# = 0
b\centerPoint\inverseMass = 1
b\centerPoint\ay# = GRAVITY#/b\centerPoint\inverseMass
;END CREATE CENTER POINT
;attach center point to inner joints
For n = 0 To NUM_OUTER_BLOB_JOINTS - 1
b\centerPoint\otherJoints[n] = b\joints[n + NUM_OUTER_BLOB_JOINTS]
Next
b\centerPoint\correctDistance# = GetDistance#(b\centerPoint\x#, b\centerPoint\y#, b\centerPoint\otherJoints[0]\x#, b\centerPoint\otherJoints[0]\y#)
End Function
Function DrawBlobs()
Color 255, 0, 0
; For b.blob = Each blob
;
; For n = 0 To NUM_OUTER_BLOB_JOINTS - 1
;
; Line b\joints[n]\x#, b\joints[n]\y#, b\joints[n]\otherJoint1\x#, b\joints[n]\otherJoint1\y#
;
; Next
;
; Next
For j.joint = Each joint
Line j\x#, j\y#, j\otherJoint1\x#, j\otherJoint1\y#
Line j\x#, j\y#, j\otherJoint2\x#, j\otherJoint2\y#
Next
For p.point = Each point
For n = 0 To NUM_OUTER_BLOB_JOINTS - 1
Line p\x#, p\y#, p\otherJoints[n]\x#, p\otherJoints[n]\y#
Next
Next
Color 0, 255, 0
For j.joint = Each joint
Oval j\x# - 5, j\y# - 5, 10, 10, 1
Next
;START TEST
Color 255, 0, 0
For b.blob = Each blob
For n = 0 To NUM_OUTER_BLOB_JOINTS*2 - 1
Text b\joints[n]\x#, b\joints[n]\y#, n, True, True
Next
Next
;END TEST
Color 0, 0, 255
For p.point = Each point
Oval p\x# - 5, p\y# - 5, 10, 10, 1
Next
Color 255, 255, 255
End Function
Function GetDistance#(x1#, y1#, x2#, y2#)
Return Sqr#((x1# - x2#)*(x1# - x2#) + (y1# - y2#)*(y1# - y2#))
End Function
Function MainLoop()
Local thisTime = MilliSecs(), lastTime = MilliSecs()
Local FPSTimer = MilliSecs()
Local FPSCounter, FPS
While Not KeyDown(1)
thisTime = MilliSecs()
If thisTime > lastTime
;START FPS CALCULATIONS
FPSCounter = FPSCounter + 1
If thisTime > FPSTimer + 1000
FPS = FPSCounter
FPSCounter = 0
FPSTimer = thisTime
EndIf
;END FPS CALCULATIONS
timePassed = thisTime - lastTime
lastTime = thisTime
;quickTimer = MilliSecs()
UpdateBlobs()
;DebugLog "UpdateChains: " + (MilliSecs() - quickTimer)
;quickTimer = MilliSecs()
CheckMouse()
;DebugLog "CheckMouse: " + (MilliSecs() - quickTimer)
;quickTimer = MilliSecs()
CheckKeys()
;DebugLog "CheckKeys: " + (MilliSecs() - quickTimer)
Cls
;quickTimer = MilliSecs()
DrawBlobs()
;DebugLog "DrawChains: " + (MilliSecs() - quickTimer)
Text 0, 0, "FPS: " + FPS
Text 0, 10, "Time Passed: " + timePassed
VWait
Flip False
EndIf
Wend
End Function
Function UpdateBlobs()
Local numIterations = 1
For b.blob = Each blob
For n = 0 To NUM_OUTER_BLOB_JOINTS*2 - 1
;move joint one timestep
Local temp# = b\joints[n]\x#
b\joints[n]\x# = 1.99*(b\joints[n]\x#) - .99*b\joints[n]\lastX# + b\joints[n]\ax#*TIME_STEP*TIME_STEP
b\joints[n]\lastX# = temp#
temp# = b\joints[n]\y#
b\joints[n]\y# = 1.99*(b\joints[n]\y#) - .99*b\joints[n]\lastY# + b\joints[n]\ay#*TIME_STEP*TIME_STEP
b\joints[n]\lastY# = temp#
For i = 1 To numIterations
;keep joint in bounds
If b\joints[n]\x# > 799
b\joints[n]\x# = 799
ElseIf b\joints[n]\x# < 0
b\joints[n]\x# = 0
EndIf
If b\joints[n]\y# > 599
b\joints[n]\y# = 599
EndIf
;joint constraints for first pair
Local dx# = b\joints[n]\otherJoint1\x# - b\joints[n]\x#
Local dy# = b\joints[n]\otherJoint1\y# - b\joints[n]\y#
Local separation# = Sqr(dx#*dx# + dy#*dy)
Local extension# = separation# - b\joints[n]\correctDistance1#
Local springForce# = extension#/(separation#*(b\joints[n]\inverseMass + b\joints[n]\otherJoint1\inverseMass))
b\joints[n]\x# = b\joints[n]\x# + b\joints[n]\inverseMass*dx#*springForce#
b\joints[n]\y# = b\joints[n]\y# + b\joints[n]\inverseMass*dy#*springForce#
b\joints[n]\otherJoint1\x# = b\joints[n]\otherJoint1\x# - b\joints[n]\otherJoint1\inverseMass*dx#*springForce#
b\joints[n]\otherJoint1\y# = b\joints[n]\otherJoint1\y# - b\joints[n]\otherJoint1\inverseMass*dy#*springForce#
;joint constraints for second pair
dx# = b\joints[n]\otherJoint2\x# - b\joints[n]\x#
dy# = b\joints[n]\otherJoint2\y# - b\joints[n]\y#
separation# = Sqr(dx#*dx# + dy#*dy)
extension# = separation# - b\joints[n]\correctDistance2#
springForce# = extension#/(separation#*(b\joints[n]\inverseMass + b\joints[n]\otherJoint2\inverseMass))
b\joints[n]\x# = b\joints[n]\x# + b\joints[n]\inverseMass*dx#*springForce#
b\joints[n]\y# = b\joints[n]\y# + b\joints[n]\inverseMass*dy#*springForce#
b\joints[n]\otherJoint2\x# = b\joints[n]\otherJoint2\x# - b\joints[n]\otherJoint2\inverseMass*dx#*springForce#
b\joints[n]\otherJoint2\y# = b\joints[n]\otherJoint2\y# - b\joints[n]\otherJoint2\inverseMass*dy#*springForce#
Next
Next
Next
;updates center point
For p.point = Each point
;move center point one timestep
temp# = p\x#
p\x# = 1.99*(p\x#) - .99*p\lastX# + p\ax#*TIME_STEP*TIME_STEP
p\lastX# = temp#
temp# = p\y#
p\y# = 1.99*(p\y#) - .99*p\lastY# + p\ay#*TIME_STEP*TIME_STEP
p\lastY# = temp#
For n = 0 To NUM_OUTER_BLOB_JOINTS - 1
For i = 1 To numIterations
;keep center point in bounds - don't need to check?
If p\x# > 799
p\x# = 799
ElseIf p\x# < 0
p\x# = 0
EndIf
If p\y# > 599
p\y# = 599
EndIf
;constraints for center point and inner joints
dx# = p\otherJoints[n]\x# - p\x#
dy# = p\otherJoints[n]\y# - p\y#
separation# = Sqr(dx#*dx# + dy#*dy)
extension# = separation# - p\correctDistance#
springForce# = extension#/(separation#*(p\inverseMass + p\otherJoints[n]\inverseMass))
p\x# = p\x# + p\inverseMass*dx#*springForce#
p\y# = p\y# + p\inverseMass*dy#*springForce#
p\otherJoints[n]\x# = p\otherJoints[n]\x# - p\otherJoints[n]\inverseMass*dx#*springForce#
p\otherJoints[n]\y# = p\otherJoints[n]\y# - p\otherJoints[n]\inverseMass*dy#*springForce#
Next
Next
Next
;START MOVING EVERYTHING - this is currently unused, as all constraint updates directly move joints
For j.joint = Each joint
j\x# = j\x# + j\adjustX#
j\y# = j\y# + j\adjustY#
j\adjustX# = 0
j\adjustY# = 0
Next
For p.point = Each point
p\x# = p\x# + p\adjustX#
p\y# = p\y# + p\adjustY#
p\adjustX# = 0
p\adjustY# = 0
Next
;END MOVING EVERYTHING
End Function