PlaydateはLuaの他にC言語での開発が可能です。
C言語は主にパフォーマンスが必要な場合に利用するのですが、今回紹介する物理演算エンジンPlaybox2Dはこれを利用しています。
Playbox2D
Box2D-Liteをベースにしており、矩形リジッドボディやaddForce、摩擦、ジョイントなど制限されたBox2Dとなります。
サンプルを見ながら確認してみます。
main.luaのsetup関数でplaybox2dの設定を行い、draw関数で描画を行っています。
function setup()
-- 略 --
-- World
world = playbox.world.new(0.0, 9.81, 10)
world:setPixelScale(WORLD_PIXEL_SCALE)
-- 地面
floor = playbox.body.new(FLOOR_WIDTH, FLOOR_HEIGHT, 0)
floor:setCenter(WORLD_WIDTH/2, WORLD_HEIGHT)
floor:setFriction(FLOOR_FRICTION)
floor:setRotation(0.1)
world:addBody(floor)
-- 天井
ceiling = playbox.body.new(CEILING_WIDTH, CEILING_HEIGHT, 0)
ceiling:setCenter(WORLD_WIDTH/2, CEILING_HEIGHT/2)
ceiling:setFriction(CEILING_FRICTION)
world:addBody(ceiling)
-- 箱
for i = 1, BOX_COUNT do
local box_mass <const> = math.random(MASS_MIN, MASS_MAX)
local box = playbox.body.new(math.random() * 0.3 + 0.2, math.random() * 0.3 + 0.2, box_mass)
box:setCenter(math.random() * WORLD_WIDTH, CEILING_HEIGHT)
box:setFriction(0.8)
box:setRotation(math.random() * 0.8 + 0.1)
world:addBody(box)
boxes[#boxes + 1] = box
-- 箱の模様は質量と連動。明るい模様 = 軽い
box_patterns[#box_patterns + 1] = math.max(math.min(1.0 - box_mass/MASS_MAX, 0.9), 0.1)
end
-- 揺れる板
swing_box = playbox.body.new(0.1, 1.0, 80)
swing_box:setCenter(0, CEILING_HEIGHT)
swing_box:setFriction(1.0)
world:addBody(swing_box)
-- 揺れる板と結ばれている紐
swing_joint = playbox.joint.new(ceiling, swing_box, WORLD_WIDTH/4, 0)
swing_joint:setBiasFactor(0.3)
swing_joint:setSoftness(0.0)
world:addJoint(swing_joint)
end
playbox.world.new([X軸の重力加速度], [Y軸の重力加速度], [イテレーション])
でWorld生成し、
floor、ceiling、box、swing_boxはplaybox.body.new([横幅], [縦幅], [質量])
で生成しています。
playbox.body
の質量を0にすると固定できます。
余談ですが、box同士がぶつかり合う際、この質量に大きい差があると、めり込みが起こってしまいます。
なぜそうなるのか理解していないので知見ある方教えていただけますとありがたいです🙏
ceilingとswing_boxをつなぐジョイントはplaybox.joint.new([対象A], [対象B], [worldからみたジョイントのX座標], [worldからみたジョイントのY座標])
となります。
function draw()
-- 略 --
-- 床描画
local floor_polygon = geometry.polygon.new(floor:getPolygon())
floor_polygon:close()
graphics.fillPolygon(floor_polygon)
-- 天井描画
local ceiling_polygon = geometry.polygon.new(ceiling:getPolygon())
ceiling_polygon:close()
graphics.fillPolygon(ceiling_polygon)
-- 箱描画
graphics.setStrokeLocation(graphics.kStrokeInside)
for i, box in ipairs(boxes) do
local box_polygon = geometry.polygon.new(box:getPolygon())
box_polygon:close()
graphics.setDitherPattern(box_patterns[i])
graphics.fillPolygon(box_polygon)
graphics.setColor(graphics.kColorBlack)
if i == selected_box then
graphics.setLineWidth(3)
else
graphics.setLineWidth(1)
end
graphics.drawPolygon(box_polygon)
end
graphics.setLineWidth(1)
graphics.setDitherPattern(0.5)
-- 揺れる板描画
local swing_box_polygon = geometry.polygon.new(swing_box:getPolygon())
swing_box_polygon:close()
graphics.fillPolygon(swing_box_polygon)
-- 揺れる板と結ばれている紐描画
graphics.setStrokeLocation(graphics.kStrokeCentered)
local _, _, px1, py1, x2, y2, _, _ = swing_joint:getPoints()
graphics.setDitherPattern(0.5)
graphics.drawLine(x2, y2, px1, py1)
-- 略 --
end
draw関数ではplaybox.body:getPolygon()でポリゴンを取得し、各boxを描画しています。
ジョイントについてはplaybox.joint:getPoints()で座標を取得し、drawLineで線を描画しています。