iceshrimp/packages/client/src/scripts/physics.ts

164 lines
3.9 KiB
TypeScript
Raw Normal View History

2023-01-13 13:40:33 +09:00
import * as Matter from "matter-js";
2020-12-06 12:51:00 +09:00
export function physics(container: HTMLElement) {
const containerWidth = container.offsetWidth;
const containerHeight = container.offsetHeight;
const containerCenterX = containerWidth / 2;
// サイズ固定化(要らないかも?)
2023-01-13 13:40:33 +09:00
container.style.position = "relative";
container.style.boxSizing = "border-box";
2020-12-06 12:51:00 +09:00
container.style.width = `${containerWidth}px`;
container.style.height = `${containerHeight}px`;
// create engine
2020-12-06 17:35:21 +09:00
const engine = Matter.Engine.create({
constraintIterations: 4,
positionIterations: 8,
velocityIterations: 8,
});
const world = engine.world;
2020-12-06 12:51:00 +09:00
// create renderer
const render = Matter.Render.create({
engine: engine,
//element: document.getElementById('debug'),
options: {
width: containerWidth,
height: containerHeight,
2023-01-13 13:40:33 +09:00
background: "transparent", // transparent to hide
wireframeBackground: "transparent", // transparent to hide
},
2020-12-06 12:51:00 +09:00
});
// Disable to hide debug
Matter.Render.run(render);
// create runner
const runner = Matter.Runner.create();
Matter.Runner.run(runner, engine);
2020-12-06 17:35:21 +09:00
const groundThickness = 1024;
2023-01-13 13:40:33 +09:00
const ground = Matter.Bodies.rectangle(
containerCenterX,
containerHeight + groundThickness / 2,
containerWidth,
groundThickness,
{
isStatic: true,
restitution: 0.1,
friction: 2,
},
);
2020-12-06 12:51:00 +09:00
//const wallRight = Matter.Bodies.rectangle(window.innerWidth+50, window.innerHeight/2, 100, window.innerHeight, wallopts);
//const wallLeft = Matter.Bodies.rectangle(-50, window.innerHeight/2, 100, window.innerHeight, wallopts);
Matter.World.add(world, [
ground,
//wallRight,
//wallLeft,
]);
const objEls = Array.from(container.children);
const objs = [];
for (const objEl of objEls) {
2023-01-13 13:40:33 +09:00
const left = objEl.dataset.physicsX
? parseInt(objEl.dataset.physicsX)
: objEl.offsetLeft;
const top = objEl.dataset.physicsY
? parseInt(objEl.dataset.physicsY)
: objEl.offsetTop;
2020-12-06 23:54:58 +09:00
2020-12-06 12:51:00 +09:00
let obj;
2023-01-13 13:40:33 +09:00
if (objEl.classList.contains("_physics_circle_")) {
2020-12-06 12:51:00 +09:00
obj = Matter.Bodies.circle(
2023-01-13 13:40:33 +09:00
left + objEl.offsetWidth / 2,
top + objEl.offsetHeight / 2,
2020-12-06 12:51:00 +09:00
Math.max(objEl.offsetWidth, objEl.offsetHeight) / 2,
2020-12-28 21:59:59 +09:00
{
2023-01-13 13:40:33 +09:00
restitution: 0.5,
},
2020-12-06 12:51:00 +09:00
);
} else {
const style = window.getComputedStyle(objEl);
obj = Matter.Bodies.rectangle(
2023-01-13 13:40:33 +09:00
left + objEl.offsetWidth / 2,
top + objEl.offsetHeight / 2,
2020-12-06 12:51:00 +09:00
objEl.offsetWidth,
objEl.offsetHeight,
{
2023-01-13 13:40:33 +09:00
chamfer: { radius: parseInt(style.borderRadius || "0", 10) },
restitution: 0.5,
},
2020-12-06 12:51:00 +09:00
);
}
objEl.id = obj.id;
objs.push(obj);
}
Matter.World.add(engine.world, objs);
// Add mouse control
const mouse = Matter.Mouse.create(container);
const mouseConstraint = Matter.MouseConstraint.create(engine, {
mouse: mouse,
constraint: {
2020-12-28 21:59:59 +09:00
stiffness: 0.1,
2020-12-06 12:51:00 +09:00
render: {
2023-01-13 13:40:33 +09:00
visible: false,
},
},
2020-12-06 12:51:00 +09:00
});
Matter.World.add(engine.world, mouseConstraint);
// keep the mouse in sync with rendering
render.mouse = mouse;
for (const objEl of objEls) {
2023-01-13 13:40:33 +09:00
objEl.style.position = "absolute";
2020-12-06 12:51:00 +09:00
objEl.style.top = 0;
objEl.style.left = 0;
objEl.style.margin = 0;
}
window.requestAnimationFrame(update);
let stop = false;
function update() {
for (const objEl of objEls) {
2023-01-13 13:40:33 +09:00
const obj = objs.find((obj) => obj.id.toString() === objEl.id.toString());
2020-12-06 12:51:00 +09:00
if (obj == null) continue;
2023-01-13 13:40:33 +09:00
const x = obj.position.x - objEl.offsetWidth / 2;
const y = obj.position.y - objEl.offsetHeight / 2;
2020-12-06 12:51:00 +09:00
const angle = obj.angle;
objEl.style.transform = `translate(${x}px, ${y}px) rotate(${angle}rad)`;
}
if (!stop) {
window.requestAnimationFrame(update);
}
}
2020-12-06 17:35:21 +09:00
// 奈落に落ちたオブジェクトは消す
2022-01-16 10:14:14 +09:00
const intervalId = window.setInterval(() => {
2020-12-06 17:35:21 +09:00
for (const obj of objs) {
2023-01-13 13:40:33 +09:00
if (obj.position.y > containerHeight + 1024)
Matter.World.remove(world, obj);
2020-12-06 17:35:21 +09:00
}
}, 1000 * 10);
2020-12-06 12:51:00 +09:00
return {
stop: () => {
stop = true;
Matter.Runner.stop(runner);
2022-01-16 10:14:14 +09:00
window.clearInterval(intervalId);
2023-01-13 13:40:33 +09:00
},
2020-12-06 12:51:00 +09:00
};
}