物理エンジンを使う 8, ボディー同士を一定の距離で拘束する, PinJoint


-- 物理エンジンを使う 8, ボディー同士を一定の距離で拘束する, PinJoint
-- 拘束条件を定義して、それを addConstraint(constraint) メソッドでスペースへ投入する。
require "physics"

dt = 0.01
ZERO = physics.Vect(0, 0)

pos1x, pos1y = 55, 5   -- ボール 1 の座標
pos2x, pos2y = 120, 55 -- ボール 2 の座標
mass         = 1      -- ボールの重さ
radius       = 10      -- ボールの半径
angle1       = 0       -- ボール 1 の角度
angle2       = 0       -- ボール 2 の角度
leftx,  lefty  = 70, 100  -- 床の左端座標
rightx, righty = 250, 120 -- 床の右端座標

gravity    = 150
elasticity = 0.8
friction   = 0.9

inertia = physics.misc.momentForCircle(mass, 0, radius, ZERO)

-- ボールを作る。
ballBody1 = physics.Body(mass, inertia)
   :setVel(ZERO)
   :setPos(physics.Vect(pos1x, pos1y))
   :setAngle(angle1)
ballShape1 = physics.CircleShape(ballBody1, radius, ZERO)
   :setRestitution(elasticity)
   :setFriction(friction)
   
-- もうひとつ同じボールを作る。
ballBody2 = physics.Body(mass, inertia)
   :setVel(ZERO)
   :setPos(physics.Vect(pos2x, pos2y))
   :setAngle(angle2)
ballShape2 = physics.CircleShape(ballBody2, radius, ZERO)
   :setRestitution(elasticity)
   :setFriction(friction)
   
-- static な壁を作る。   
wallShape = physics.SegmentShape(nil, physics.Vect(leftx, lefty), physics.Vect(rightx, righty), 1)
   :setRestitution(elasticity)
   :setFriction(friction)
   
-- PinJoint の拘束条件を定義する。
-- 引数は(ボディー a, ボディー b, ボディー a のどこ, ボディー b のどこ)
-- ここでは両方のボールの重心同士を拘束する。   
constraint = physics.PinJoint(ballBody1, ballBody2, ZERO, ZERO)

space = physics.Space()
   :setGravity(physics.Vect(0, gravity))
   
   :addBody(ballBody1)
   :addShape(ballShape1)
   
   :addBody(ballBody2)
   :addShape(ballShape2)
   
   :addShape(wallShape)
   
   -- 拘束条件をスペースへ投入する。
   :addConstraint(constraint)

function on.paint(gc)
   gc:drawLine(leftx, lefty, rightx, righty)
   fillCircleCenter(pos1x, pos1y, radius, angle1, 0x71C671, gc)
   fillCircleCenter(pos2x, pos2y, radius, angle2, 0x71C671, gc)
   gc:setColorRGB(0x8E8E38)
   gc:setPen("thin", "dashed")
   gc:drawLine(pos1x, pos1y, pos2x, pos2y)
end
function on.timer()
   space:step(dt)
   pos1 = ballBody1:pos()
   pos1x, pos1y = pos1:x(), pos1:y()
   pos2 = ballBody2:pos()
   pos2x, pos2y = pos2:x(), pos2:y()
   angle1 = ballBody1:angle()
   angle2 = ballBody2:angle()
   platform.window:invalidate()
end
function on.enterKey()
   timer.start(dt)
end

-----------------------
-- general functions --
-----------------------
function fillCircleCenter(cx, cy, radius, angle, color, gc)
   gc:setColorRGB(color)
   gc:fillArc(cx - radius, cy - radius, radius + radius, radius + radius, 0, 360)
   gc:setColorRGB(0x000000)
   gc:drawArc(cx - radius, cy - radius, radius + radius, radius + radius, 0, 360)
   gc:drawLine(cx, cy, cx + radius * math.cos(angle), cy + radius  * math.sin(angle))
end