Created
June 15, 2013 13:34
Corona SDK Verlet Ropes
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- Verlet class at bottom, save to separate file called "Verlet.lua" | |
require "physics" | |
local Verlet = require "Verlet" | |
display.setStatusBar( display.HiddenStatusBar ) | |
--physics.setDrawMode("hybrid") | |
physics.start() | |
physics.setGravity(0, 9.8) | |
physics.setPositionIterations( 10 ) | |
function getGrapplePoint() | |
local point = display.newRect(0, 0, 10, 10) | |
physics.addBody(point,"static", {density = 1, friction = .5, bounce = .1}) | |
return point | |
end | |
function getMetalSphere(r) | |
local sphere = display.newCircle(0, 0, r) | |
physics.addBody(sphere, "dynamic", {density=3, radius=r, bounce=0.0, friction=0.7}) | |
return sphere | |
end | |
local function testingRope2() | |
local point1 = getGrapplePoint() | |
point1.x = 100 | |
point1.y = 100 | |
local point2 = getGrapplePoint() | |
point2.x = 300 | |
point2.y = 200 | |
local sphere = getMetalSphere(20) | |
sphere.x = 0 | |
sphere.y = 200 | |
local mappedX, mappedY = sphere:contentToLocal(sphere.x + 2, sphere.y - sphere.height / 2) | |
local rope1 = physics.newJoint("rope", sphere, point1, mappedX, mappedY) | |
-- now watch the bounce (elsaticity more accurate) of a distance joint | |
local sphere2 = getMetalSphere(20) | |
sphere2.x = 200 | |
sphere2.y = 300 | |
local distance = physics.newJoint("distance", sphere2, point2, sphere2.x, sphere2.y, point2.x, point2.y) | |
distance.length = 200 | |
distance.frequency = 0.7 | |
distance.dampingRatio = 0 | |
local visualRope1 = Verlet:new(point1, sphere) | |
local visualRope2 = Verlet:new(point2, sphere2) | |
-- finally, MOVE 'EM UP! | |
local t = {} | |
t.speed = 5 | |
function t:timer() | |
if rope1.maxLength > 20 then | |
rope1.maxLength = rope1.maxLength - self.speed | |
end | |
if distance.length > 20 then | |
distance.length = distance.length - self.speed | |
end | |
end | |
-- t.id = timer.performWithDelay(10, t, 0) | |
local function startDrag( event ) | |
local t = event.target | |
local phase = event.phase | |
if "began" == phase then | |
display.getCurrentStage():setFocus( t ) | |
t.isFocus = true | |
-- Store initial position | |
t.x0 = event.x - t.x | |
t.y0 = event.y - t.y | |
-- Make body type temporarily "kinematic" (to avoid gravitional forces) | |
event.target.bodyType = "kinematic" | |
-- Stop current motion, if any | |
event.target:setLinearVelocity( 0, 0 ) | |
event.target.angularVelocity = 0 | |
elseif t.isFocus then | |
if "moved" == phase then | |
t.x = event.x - t.x0 | |
t.y = event.y - t.y0 | |
elseif "ended" == phase or "cancelled" == phase then | |
display.getCurrentStage():setFocus( nil ) | |
t.isFocus = false | |
t.bodyType = "dynamic" | |
end | |
end | |
-- Stop further propagation of touch event! | |
return true | |
end | |
sphere:addEventListener("touch", startDrag) | |
sphere2:addEventListener("touch", startDrag) | |
end | |
testingRope2() | |
-- if you want more bounce/slack, play with the stick's contract method, like changing the .5 to higher, or lower | |
-- or making the x and y values different. | |
-- Also, in the enterframe, you can change the 4 to different #'s to get a more slack wire | |
local Verlet = {} | |
function Verlet:new(startPoint, endPoint) | |
local verlet = display.newGroup() | |
verlet.startPoint = startPoint | |
verlet.endPoint = endPoint | |
function verlet:init() | |
local TOTAL = 10 | |
local points = {} | |
local sticks = {} | |
self.points = points | |
self.sticks = sticks | |
local w = TOTAL * 10 | |
local startPoint = self.startPoint | |
local i = 1 | |
while i < TOTAL do | |
local vpoint = self:getVPoint(startPoint.x, startPoint.y) | |
table.insert(points, vpoint) | |
if i > 1 then | |
local stick = self:getVStick(points[i - 1], points[i]) | |
table.insert(sticks, stick) | |
end | |
i = i + 1 | |
end | |
Runtime:addEventListener("enterFrame", self) | |
end | |
function verlet:clearLines() | |
local len = self.numChildren | |
while len > 0 do | |
local line = self[len] | |
line:removeSelf() | |
len = len - 1 | |
end | |
end | |
function verlet:getVPoint(x, y) | |
local point = {} | |
point.classType = "VPoint" | |
function point:setPos(x, y) | |
-- assert(x == x) | |
-- assert(y == y) | |
self.x = x | |
self.y = y | |
self.oldX = x | |
self.oldY = y | |
--print("initial:, x:", self.x, ", y:", self.y) | |
-- assert(self.x == self.x) | |
-- assert(self.y == self.y) | |
-- assert(self.oldX == self.oldX) | |
-- assert(self.oldY == self.oldY) | |
end | |
function point:refresh() | |
local tempX = self.x | |
local tempY = self.y | |
local oldOldX = self.oldX | |
local oldOrigX = self.x | |
local diffX = self.x - self.oldX | |
self.x = self.x + (self.x - self.oldX) | |
self.y = self.y + (self.y - self.oldY) | |
self.oldX = tempX | |
self.oldY = tempY | |
-- assert(self.x == self.x, "Failed, oldOldX: " .. oldOldX .. ", vs oldOrigX: " .. oldOrigX .. ", diffX: " .. diffX) | |
-- assert(self.y == self.y) | |
-- assert(self.oldX == self.oldX) | |
-- assert(self.oldY == self.oldY) | |
end | |
point:setPos(x, y) | |
return point | |
end | |
function verlet:getVStick(pointA, pointB) | |
local stick = {} | |
stick.pointA = pointA | |
stick.pointB = pointB | |
local dx = pointA.x - pointB.x; | |
local dy = pointA.y - pointB.y; | |
stick.hyp = math.sqrt(dx * dx + dy * dy); | |
function stick:contract() | |
local dx = self.pointB.x - self.pointA.x | |
local dy = self.pointB.y - self.pointA.y | |
local h = math.sqrt(dx * dx + dy * dy) | |
local diff = self.hyp - h | |
local offx = (diff * dx / h) * .5 | |
local offy = (diff * dy / h) * .5 | |
--print("dx:", dx, ", dy:", dy) | |
--print("h:", h, ", diff:", diff) | |
--print("offx:", offx, ", offy:", offy) | |
self.pointA.x = self.pointA.x - offx | |
self.pointA.y = self.pointA.y - offy | |
self.pointB.x = self.pointB.x + offx | |
self.pointB.y = self.pointB.y + offy | |
--Runtime:dispatchEvent({name="VPoint_changed", target=self}) | |
end | |
return stick | |
end | |
function verlet:enterFrame() | |
local points = self.points | |
local sticks = self.sticks | |
local startPoint = self.startPoint | |
local endPoint = self.endPoint | |
local startX = startPoint.x | |
local startY = startPoint.y | |
local endX = endPoint.x | |
local endY = endPoint.y | |
local t = #points | |
local i = 1 | |
points[1]:setPos(endX, endY) | |
points[#points]:setPos(startX, startY) | |
while points[i] do | |
local point = points[i] | |
point.y = point.y + 4 | |
point:refresh() | |
i = i + 1 | |
end | |
t = #sticks | |
i = 1 | |
while sticks[i] do | |
local stick = sticks[i] | |
stick:contract() | |
i = i + 1 | |
end | |
self:clearLines() | |
i = 1 | |
while i < t do | |
local stick = sticks[i] | |
local line = display.newLine(self, stick.pointA.x, stick.pointA.y, stick.pointB.x, stick.pointB.y) | |
line.width = 3 | |
line:setColor(255, 0, 0) | |
--print("pos:", stick.pointA.x, stick.pointA.y, stick.pointB.x, stick.pointB.y) | |
i = i + 1 | |
end | |
return true | |
end | |
verlet:init() | |
return verlet | |
end | |
return Verlet |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment