Add lava lamp scene with soft-body blobs
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
(() => {
|
||||
const { Bodies } = Matter;
|
||||
const scenes =
|
||||
(window.PhysilinksSceneDefs = window.PhysilinksSceneDefs || []);
|
||||
const scenes = (window.PhysilinksSceneDefs =
|
||||
window.PhysilinksSceneDefs || []);
|
||||
|
||||
scenes.push({
|
||||
id: "scene1",
|
||||
@@ -12,6 +12,12 @@
|
||||
minChain: 3,
|
||||
palette: ["#ff595e", "#ffca3a", "#8ac926", "#1982c4", "#6a4c93"],
|
||||
ballRadius: 38,
|
||||
winCondition: {
|
||||
type: "score",
|
||||
target: 10000,
|
||||
onWin: { shoveBalls: true },
|
||||
nextSceneId: "scene2",
|
||||
},
|
||||
link: {
|
||||
stiffness: 0.85,
|
||||
lengthScale: 1.05,
|
||||
@@ -41,16 +47,10 @@
|
||||
render: { fillStyle: "#0ea5e9", strokeStyle: "#0ea5e9" },
|
||||
},
|
||||
),
|
||||
Bodies.rectangle(
|
||||
-wallThickness / 2,
|
||||
h / 2,
|
||||
wallThickness,
|
||||
wallHeight,
|
||||
{
|
||||
isStatic: true,
|
||||
render: { fillStyle: "#f97316", strokeStyle: "#f97316" },
|
||||
},
|
||||
),
|
||||
Bodies.rectangle(-wallThickness / 2, h / 2, wallThickness, wallHeight, {
|
||||
isStatic: true,
|
||||
render: { fillStyle: "#f97316", strokeStyle: "#f97316" },
|
||||
}),
|
||||
Bodies.rectangle(
|
||||
w + wallThickness / 2,
|
||||
h / 2,
|
||||
|
||||
97
scenes/scene-lavalamp.js
Normal file
97
scenes/scene-lavalamp.js
Normal file
@@ -0,0 +1,97 @@
|
||||
(() => {
|
||||
const { Bodies, Body } = Matter;
|
||||
const scenes = (window.PhysilinksSceneDefs =
|
||||
window.PhysilinksSceneDefs || []);
|
||||
|
||||
const makeCurveSegments = (cx, h, amp, thickness, segments) => {
|
||||
const segs = [];
|
||||
const stepY = h / segments;
|
||||
let prevX = cx;
|
||||
let prevY = 0;
|
||||
for (let i = 0; i < segments; i += 1) {
|
||||
const y = stepY * (i + 1);
|
||||
const t = y / h;
|
||||
const x = cx + Math.sin(t * Math.PI * 1.5) * amp;
|
||||
const dx = x - prevX;
|
||||
const dy = y - prevY;
|
||||
const len = Math.max(1, Math.sqrt(dx * dx + dy * dy));
|
||||
const angle = Math.atan2(dy, dx);
|
||||
segs.push(
|
||||
Bodies.rectangle((prevX + x) / 2, (prevY + y) / 2, thickness, len, {
|
||||
isStatic: true,
|
||||
angle,
|
||||
render: { fillStyle: "#14213a", strokeStyle: "#14213a" },
|
||||
plugin: { curve: true },
|
||||
}),
|
||||
);
|
||||
prevX = x;
|
||||
prevY = y;
|
||||
}
|
||||
return segs;
|
||||
};
|
||||
|
||||
scenes.push({
|
||||
id: "scene-lava",
|
||||
name: "Lava drift",
|
||||
config: {
|
||||
gravity: -0.1,
|
||||
spawnIntervalMs: 180,
|
||||
spawnFrom: "bottom",
|
||||
autoSpawn: true,
|
||||
minChain: 3,
|
||||
palette: ["#f472b6", "#38bdf8", "#fbbf24", "#a855f7", "#22c55e"],
|
||||
ballRadius: 26,
|
||||
blobBalls: true,
|
||||
winCondition: {
|
||||
type: "score",
|
||||
target: 25000,
|
||||
onWin: { setGravity: -0.55, removeCurves: true },
|
||||
nextSceneId: "scene-grid",
|
||||
},
|
||||
link: {
|
||||
stiffness: 0.7,
|
||||
lengthScale: 1.05,
|
||||
damping: 0.12,
|
||||
lineWidth: 3,
|
||||
rope: true,
|
||||
renderType: "line",
|
||||
maxLengthMultiplier: 4.8,
|
||||
},
|
||||
},
|
||||
createBodies: (w, h) => {
|
||||
const wallThickness = Math.max(24, w * 0.05);
|
||||
const amp = Math.max(40, w * 0.08);
|
||||
const segments = 12;
|
||||
const curves = [
|
||||
...makeCurveSegments(w * 0.33, h, amp, wallThickness, segments),
|
||||
...makeCurveSegments(w * 0.67, h, amp * 0.95, wallThickness, segments),
|
||||
];
|
||||
|
||||
// Gentle paddles that sway slightly
|
||||
/*
|
||||
const paddleWidth = Math.max(120, w * 0.18);
|
||||
const paddleHeight = Math.max(12, h * 0.018);
|
||||
const paddles = [
|
||||
Bodies.rectangle(w * 0.45, h * 0.65, paddleWidth, paddleHeight, {
|
||||
isStatic: true,
|
||||
angle: -0.08,
|
||||
render: { fillStyle: "#0ea5e9", strokeStyle: "#0ea5e9" },
|
||||
plugin: {
|
||||
oscillate: { axis: "x", amplitude: w * 0.05, speed: 0.7 },
|
||||
},
|
||||
}),
|
||||
Bodies.rectangle(w * 0.58, h * 0.4, paddleWidth * 0.85, paddleHeight, {
|
||||
isStatic: true,
|
||||
angle: 0.12,
|
||||
render: { fillStyle: "#f59e0b", strokeStyle: "#f59e0b" },
|
||||
plugin: {
|
||||
oscillate: { axis: "x", amplitude: w * 0.04, speed: 1.0 },
|
||||
},
|
||||
}),
|
||||
];
|
||||
*/
|
||||
|
||||
return [...curves];
|
||||
},
|
||||
});
|
||||
})();
|
||||
@@ -1,7 +1,7 @@
|
||||
(() => {
|
||||
const { Bodies } = Matter;
|
||||
const scenes =
|
||||
(window.PhysilinksSceneDefs = window.PhysilinksSceneDefs || []);
|
||||
const { Bodies, Body } = Matter;
|
||||
const scenes = (window.PhysilinksSceneDefs =
|
||||
window.PhysilinksSceneDefs || []);
|
||||
|
||||
scenes.push({
|
||||
id: "scene2",
|
||||
@@ -12,6 +12,12 @@
|
||||
minChain: 3,
|
||||
palette: ["#fb7185", "#fbbf24", "#34d399", "#38bdf8"],
|
||||
ballRadius: 22,
|
||||
winCondition: {
|
||||
type: "score",
|
||||
target: 50000,
|
||||
nextSceneId: "scene3",
|
||||
onWin: { shoveBalls: true },
|
||||
},
|
||||
link: {
|
||||
stiffness: 0.6,
|
||||
lengthScale: 1,
|
||||
@@ -26,10 +32,45 @@
|
||||
const floorHeight = Math.max(70, h * 0.12);
|
||||
const wallThickness = Math.max(32, w * 0.05);
|
||||
const wallHeight = h * 1.8;
|
||||
const ledgeHeight = Math.max(14, h * 0.022);
|
||||
const leftWidth = Math.max(160, w * 0.18);
|
||||
const midWidth = Math.max(190, w * 0.26);
|
||||
const rightWidth = Math.max(150, w * 0.18);
|
||||
const cogRadius = Math.max(40, Math.min(w, h) * 0.085);
|
||||
const cogRadiusSmall = Math.max(30, Math.min(w, h) * 0.065);
|
||||
const bumperRadius = Math.max(20, Math.min(w, h) * 0.05);
|
||||
const stickyWidth = Math.max(90, w * 0.12);
|
||||
const stickyHeight = Math.max(14, h * 0.02);
|
||||
const stickyAmplitude = w * 0.06;
|
||||
const bumperAmplitude = h * 0.08;
|
||||
|
||||
const makeGear = (cx, cy, outerRadius, teeth, color, rotSpeed) => {
|
||||
const coreRadius = outerRadius * 1.5;
|
||||
const toothLength = outerRadius * 0.5;
|
||||
const toothWidth = Math.max(outerRadius * 0.14, 10);
|
||||
const parts = [
|
||||
Bodies.circle(cx, cy, coreRadius, {
|
||||
isStatic: true,
|
||||
render: { fillStyle: color, strokeStyle: color },
|
||||
}),
|
||||
];
|
||||
const step = (Math.PI * 2) / teeth;
|
||||
for (let i = 0; i < teeth; i += 1) {
|
||||
const angle = step * i;
|
||||
const tx = cx + Math.cos(angle) * (coreRadius + toothLength / 2);
|
||||
const ty = cy + Math.sin(angle) * (coreRadius + toothLength / 2);
|
||||
parts.push(
|
||||
Bodies.rectangle(tx, ty, toothWidth, toothLength, {
|
||||
isStatic: true,
|
||||
angle,
|
||||
render: { fillStyle: color, strokeStyle: color },
|
||||
}),
|
||||
);
|
||||
}
|
||||
const gear = Body.create({
|
||||
isStatic: true,
|
||||
parts,
|
||||
plugin: { rotSpeed },
|
||||
});
|
||||
return gear;
|
||||
};
|
||||
|
||||
return [
|
||||
Bodies.rectangle(
|
||||
w / 2,
|
||||
@@ -42,16 +83,10 @@
|
||||
render: { fillStyle: "#0ea5e9", strokeStyle: "#0ea5e9" },
|
||||
},
|
||||
),
|
||||
Bodies.rectangle(
|
||||
-wallThickness / 2,
|
||||
h / 2,
|
||||
wallThickness,
|
||||
wallHeight,
|
||||
{
|
||||
isStatic: true,
|
||||
render: { fillStyle: "#7c3aed", strokeStyle: "#7c3aed" },
|
||||
},
|
||||
),
|
||||
Bodies.rectangle(-wallThickness / 2, h / 2, wallThickness, wallHeight, {
|
||||
isStatic: true,
|
||||
render: { fillStyle: "#7c3aed", strokeStyle: "#7c3aed" },
|
||||
}),
|
||||
Bodies.rectangle(
|
||||
w + wallThickness / 2,
|
||||
h / 2,
|
||||
@@ -62,20 +97,77 @@
|
||||
render: { fillStyle: "#7c3aed", strokeStyle: "#7c3aed" },
|
||||
},
|
||||
),
|
||||
Bodies.rectangle(w * 0.2, h * 0.45, leftWidth, ledgeHeight, {
|
||||
// Rotating cogs with teeth
|
||||
makeGear(w * 0.32, h * 0.38, cogRadius, 10, "#f97316", 1.2),
|
||||
makeGear(w * 0.64, h * 0.5, cogRadiusSmall, 12, "#a855f7", -1.6),
|
||||
makeGear(w * 0.5, h * 0.32, cogRadius * 0.85, 14, "#fb7185", 1.9),
|
||||
// Oscillating bumpers
|
||||
Bodies.circle(w * 0.2, h * 0.46, bumperRadius, {
|
||||
isStatic: true,
|
||||
angle: 0.08,
|
||||
render: { fillStyle: "#f97316", strokeStyle: "#f97316" },
|
||||
}),
|
||||
Bodies.rectangle(w * 0.5, h * 0.6, midWidth, ledgeHeight, {
|
||||
isStatic: true,
|
||||
angle: -0.04,
|
||||
restitution: 1.08,
|
||||
friction: 0.01,
|
||||
render: { fillStyle: "#14b8a6", strokeStyle: "#14b8a6" },
|
||||
plugin: {
|
||||
oscillate: { axis: "y", amplitude: bumperAmplitude, speed: 1.4 },
|
||||
},
|
||||
}),
|
||||
Bodies.rectangle(w * 0.8, h * 0.42, rightWidth, ledgeHeight, {
|
||||
Bodies.circle(w * 0.5, h * 0.62, bumperRadius * 0.9, {
|
||||
isStatic: true,
|
||||
angle: 0.14,
|
||||
restitution: 1.08,
|
||||
friction: 0.01,
|
||||
render: { fillStyle: "#fbbf24", strokeStyle: "#fbbf24" },
|
||||
plugin: {
|
||||
oscillate: { axis: "x", amplitude: stickyAmplitude, speed: 1.1 },
|
||||
},
|
||||
}),
|
||||
Bodies.circle(w * 0.8, h * 0.38, bumperRadius * 1.05, {
|
||||
isStatic: true,
|
||||
restitution: 1.08,
|
||||
friction: 0.01,
|
||||
render: { fillStyle: "#38bdf8", strokeStyle: "#38bdf8" },
|
||||
plugin: {
|
||||
oscillate: {
|
||||
axis: "y",
|
||||
amplitude: bumperAmplitude * 0.7,
|
||||
speed: 1.8,
|
||||
},
|
||||
},
|
||||
}),
|
||||
// Sticky moving pads
|
||||
Bodies.rectangle(w * 0.32, h * 0.72, stickyWidth, stickyHeight, {
|
||||
isStatic: true,
|
||||
angle: -0.08,
|
||||
friction: 1.3,
|
||||
render: { fillStyle: "#0ea5e9", strokeStyle: "#0ea5e9" },
|
||||
plugin: {
|
||||
oscillate: { axis: "x", amplitude: stickyAmplitude, speed: 0.9 },
|
||||
},
|
||||
}),
|
||||
Bodies.rectangle(w * 0.72, h * 0.7, stickyWidth * 0.9, stickyHeight, {
|
||||
isStatic: true,
|
||||
angle: 0.06,
|
||||
friction: 1.3,
|
||||
render: { fillStyle: "#f472b6", strokeStyle: "#f472b6" },
|
||||
plugin: {
|
||||
oscillate: {
|
||||
axis: "x",
|
||||
amplitude: stickyAmplitude * 0.8,
|
||||
speed: 1.3,
|
||||
},
|
||||
},
|
||||
}),
|
||||
// Random polygon obstacles
|
||||
Bodies.polygon(w * 0.18, h * 0.28, 5, bumperRadius * 0.9, {
|
||||
isStatic: true,
|
||||
angle: 0.2,
|
||||
render: { fillStyle: "#22c55e", strokeStyle: "#22c55e" },
|
||||
plugin: { rotSpeed: -0.6 },
|
||||
}),
|
||||
Bodies.polygon(w * 0.86, h * 0.62, 7, bumperRadius * 0.95, {
|
||||
isStatic: true,
|
||||
angle: -0.25,
|
||||
render: { fillStyle: "#c084fc", strokeStyle: "#c084fc" },
|
||||
plugin: { rotSpeed: 0.9 },
|
||||
}),
|
||||
];
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user