--[[ x 軸、y 軸、補助線を引く(Grid クラスを追加した)。 --]] require "physics"; require "color" function pV(x, y) return physics.Vect(x, y) end function on.resize() W, H = platform.window:width(), platform.window:height() X0, Y0, UNIT = math.floor(W/2), math.floor(H/2), math.floor(H/10) end TICKING = false ZERO = pV(0, 0) LARGE = physics.misc.INFINITY() DT = 1/(2^6) GRAVITY = -9.8 -- 重力加速度の符号は鉛直方向下向きをマイナスにする。 COLOR = { -- 参考: http://www.colordic.org --[[パステルカラー]]0xFF7F7F, 0xFF7FBF, 0xFF7FFF, 0xBF7FFF, 0x7F7FFF, 0x7FBFFF, 0x7FFFFF, 0x7FFFBF, 0x7FFF7F, 0xBFFF7F, 0xFFFF7F, 0xFFBF7F, --[[ビビッドカラー--]]0xFF6060, 0xFF60AF, 0xFF60FF, 0xAF60FF, 0x6060FF, 0x60AFFF, 0x60FFFF, 0x60FFAF, 0x60FF60, 0xAFFF60, 0xFFFF60, 0xFFAF60, --[[モノトーン--]]0x5E5E5E, 0x7E7E7E, 0x9E9E9E, 0xBEBEBE, 0xDEDEDE, 0xFEFEFE, --[[メトロカラー--]]0x0078BA, 0x0079C2, 0x009944, 0x00A0DE, 0x00A7DB, 0x00ADA9, 0x019A66, 0x522886, 0x6CBB5A, 0x814721, 0x9B7CB6, 0x9CAEB7, 0xA9CC51, 0xB6007A, 0xBB641D, 0xD7C447, 0xE44D93, 0xE5171F, 0xE60012, 0xE85298, 0xEE7B1A, 0xF39700, } ----------------- -- pBall class -- ----------------- pBall = class() function pBall:init(mass, radius, elasticity, friction, color, group) self.color = color or color.black local inertia = physics.misc.momentForCircle(mass, 0, radius, ZERO) self.body = physics.Body(mass, inertia) self.shape = physics.CircleShape(self.body, radius, ZERO) :setRestitution(elasticity or 0.8) :setFriction(friction or 0.8) if group then self.shape:setGroup(group) end end function pBall:paint(gc) local pos = self.body:pos() local cx, cy = X0 + UNIT * pos:x(), Y0 - UNIT * pos:y() -- 円の中心座標をスクリーン座標に変換する。 local radius = UNIT * self.shape:radius() -- 円の半径も数学座標系に合わせて変える。 local diameter = radius + radius local angle = self.body:angle() local x0, y0 = cx - radius, cy - radius gc:setColorRGB(self.color) gc:fillArc(x0, y0, diameter, diameter, 0, 360) gc:setColorRGB(color.black) gc:setPen("thin") gc:drawArc(x0, y0, diameter, diameter, 0, 360) gc:drawLine(cx, cy, cx + radius * math.cos(angle), cy - radius * math.sin(angle)) end ---------------- -- pBox class -- ---------------- pBox = class() function pBox:init(mass, width, height, elasticity, friction, color, group) self.color = color or color.black local inertia = physics.misc.momentForBox(mass, width, height) self.body = physics.Body(mass, inertia) local verts = {pV(-width/2, -height/2), pV(-width/2, height/2), pV(width/2, height/2), pV(width/2, -height/2)} -- {左上, 左下, 右下, 右上} self.shape = physics.PolyShape(self.body, verts, ZERO) :setRestitution(elasticity or 0.8) :setFriction(friction or 0.8) if group then self.shape:setGroup(group) end end function pBox:paint(gc) local numVerts = self.shape:numVerts() local verts = {} for i = 1, numVerts + 1 do local j = i; if i > numVerts then j = 1 end -- drawPolyLine() は自動的には閉じてくれないので頂点座標リストの末尾にもう 1 回始点座標を持ってくる。 local points = self.shape:points()[j] table.insert(verts, X0 + UNIT * points:x()) -- 矩形の頂点座標をスクリーン座標に変換する。 table.insert(verts, Y0 - UNIT * points:y()) -- 〃 end gc:setColorRGB(self.color) gc:fillPolygon(verts) gc:setPen("thin") gc:setColorRGB(color.black) gc:drawPolyLine(verts) end ---------------------- -- pStaticSeg class -- ---------------------- pStaticSeg = class() function pStaticSeg:init(x1, y1, x2, y2, elasticity, friction, color, group) self.color = color or color.black local avec, bvec = pV(x1, y1), pV(x2, y2) self.shape = physics.SegmentShape(nil, avec, bvec, 0.05) -- セグメントの厚みも数学座標系に合わせて変える。 :setRestitution(elasticity or 0.8) :setFriction(friction or 0.8) if group then self.shape:setGroup(group) end end function pStaticSeg:paint(gc) local pos1 = self.shape:a() local x1, y1 = X0 + UNIT * pos1:x(), Y0 - UNIT * pos1:y() -- 始点座標をスクリーン座標に変換する。 local pos2 = self.shape:b() local x2, y2 = X0 + UNIT * pos2:x(), Y0 - UNIT * pos2:y() -- 終点座標をスクリーン座標に変換する。 gc:setColorRGB(self.color) gc:setPen("thin") gc:drawLine(x1, y1, x2, y2) end ------------------ -- pSpace class -- ------------------ pSpace = class() function pSpace:init(GRAVITY) self.space = physics.Space() :setGravity(pV(0, GRAVITY)) self.objects = {} end function pSpace:step(DT) self.space:step(DT) end function pSpace:addObj(obj, velx, vely, cx, cy) obj.body:setVel(pV(velx or 0, vely or 0)) :setPos(pV(cx or W/2, cy or H/2)) self.space:addBody(obj.body) :addShape(obj.shape) table.insert(self.objects, obj) end function pSpace:addStatic(obj, cx, cy) if obj.body then obj.body:setPos(pV(cx or W/2, cy or H/2)) end self.space:addStaticShape(obj.shape) table.insert(self.objects, obj) end function pSpace:paint(gc) ---[[ gc:setColorRGB(color.gray) gc:drawString(string.format("acceleration of gravity: %g", GRAVITY), 5, -3, "top") -- %g は小数(小数点以下も表示する) --]] for _, v in ipairs(self.objects) do v:paint(gc) end end ---------------- -- Grid class -- ---------------- Grid = class() function Grid:init() end function Grid:paint(gc) gc:setPen("thin") gc:setColorRGB(0x6495ED) gc:drawLine(0, Y0, W, Y0) -- x 軸を引く。 gc:drawLine(X0, 0, X0, H) -- y 軸を引く。 gc:fillPolygon({W, Y0, W - 7, Y0 - 4, W - 3, Y0, W - 7, Y0 + 4, W, Y0}) -- x 軸の矢印を描く。 gc:fillPolygon({X0, 0, X0 - 4, 7, X0, 3, X0 + 4, 7, X0, 0}) -- y 軸の矢印を描く。 gc:drawString("x", W - 10, Y0 - 21) -- x 軸のラベルを表示する。 gc:drawString("y", X0 + 5, -6) -- y 軸のラベルを表示する。 gc:setColorRGB(0xCAE1FF) local i1 = 1; -- 第 1、第 2 象限に補助線(横線)を引く。 while Y0 - UNIT * i1 > 0 do gc:drawLine(0, Y0 - UNIT * i1, W, Y0 - UNIT * i1); i1 = i1 + 1 end local i2 = 1; -- 第 3、第 4 象限に補助線(横線)を引く。 while Y0 + UNIT * i2 < H do gc:drawLine(0, Y0 + UNIT * i2, W, Y0 + UNIT * i2); i2 = i2 + 1 end local i3 = 1; -- 第 1、第 4 象限に補助線(縦線)を引く。 while X0 + UNIT * i3 < W do gc:drawLine(X0 + UNIT * i3, 0, X0 + UNIT * i3, H); i3 = i3 + 1 end local i4 = 1; -- 第 2、第 3 象限に補助線(縦線)を引く。 while X0 - UNIT * i4 > 0 do gc:drawLine(X0 - UNIT * i4, 0, X0 - UNIT * i4, H); i4 = i4 + 1 end end ------------------ -- 確認してみる -- ------------------ function reset() grid = Grid() space = pSpace(GRAVITY) -- スペースを実体化する。 -- 何かを実体化してスペースへ投入する。位置、寸法は数学座標系で指定する。 ball1 = pBall(10, 0.5, 1, 1, COLOR[math.random(#COLOR)]) -- (質量, 半径, 反撥係数, 摩擦力, 色 [,グループ]) space:addObj(ball1, 0, 0, -2, 0) -- (オブジェクト, 初速ベクトル x, y, 初期座標 x, y) ball2 = pBall(10, 0.5, 1, 1, COLOR[math.random(#COLOR)]) space:addObj(ball2, 0, 0, 2, 2.8) box1 = pBox(30, 6, 0.3, 1, 1, COLOR[math.random(#COLOR)]) -- (質量, 幅, 高さ, 反撥係数, 摩擦力, 色 [,グループ]) space:addObj(box1, 0, 0, 0, 4) -- (オブジェクト, 初速ベクトル x, y, 初期座標 x, y) box2 = pBox(20, 1, 2, 1, 1, COLOR[math.random(#COLOR)]) space:addObj(box2, 0, 0, 0, .5) line1 = pStaticSeg(-5, -3, 5, -3, 1, 1, color.black) -- (始点座標 x, y, 終点座標 x, y, 反撥係数, 摩擦力, 色 [,グループ]) space:addStatic(line1) end ---------------------------------------------------------- ---------------------------------------------------------- ---------------------------------------------------------- function on.construction() reset() end function on.escapeKey() reset() end function on.paint(gc) ---[[ grid:paint(gc) --]] space:paint(gc) end function on.timer() space:step(DT) platform.window:invalidate() end function on.enterKey() -- enter キーで timer の動きを toggle する。 if TICKING == false then timer.start(DT); TICKING = true elseif TICKING == true then timer.stop(); TICKING = false end end
参考:
- 作者: 山村富太郎
- 出版社/メーカー: アスキー・メディアワークス
- 発売日: 2008/11/20
- メディア: 大型本
- この商品を含むブログを見る