Я запускаю его с собственной циклом, как показано ниже. Однако у меня возникают проблемы с рисунком игры. Перед тем, как добраться до объяснения, вот мой код ниже: < /p>
Код: Выделить всё
function gameLoop(currentTime) {
const elapsed = currentTime - state.lastTime;
state.lastTime = currentTime;
state.accumulator += elapsed;
while (state.accumulator >= state.physicsDelta) {
Engine.update(state.engine, state.physicsDelta);
state.accumulator -= state.physicsDelta;
}
requestAnimationFrame(workerGameLoop);
}
requestAnimationFrame(workerGameLoop)
< /code>
state.lasttime инициализируется для производительности. now
и currenttime обрабатывается requestAnimationFrame. < /p>
state.physicsDelta установлено значение 1000/60 или 16.666 ... < /p>
, если игра будет. Дважды для поддержания темпа.
Однако после тестирования вызов Engine.update на самом деле не «прогрессирует» симуляцию больше, называя его 100 раз в одном и том же цикле, кажется, не продвигает его дальше. Я что -то упускаю? Он пингит основной поток с помощью данных физики < /p>
// Add all dynamic bodies
for (const [id, body] of state.dynamicBodies.entries()) {
bodiesData.push({
id,
x: body.position.x,
y: body.position.y,
offset: body.offset,
angle: body.angle,
velocity: body.velocity,
});
updatedIds.add(id);
}
if (bodiesData.length > 0) {
postMessage({ type: "update", payload: bodiesData });
}
< /code>
У меня есть работающая демонстрация HTML, вы можете проверить, где я использую тот же принцип для поддержания физического темпа, но только здесь он работает на основном потоке. Проверьте стимуляцию физики, изменив свой дисплей на более низкую частоту, или позволив своему браузеру переключаться на сохранение питания. < /p>
Matter.js Top-Down Player Demo
/* Basic styling to center the canvas and remove scrollbars */
body {
margin: 0;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #1a1a1a;
}
canvas {
display: block;
background-color: #2c2c2c;
}
.instructions {
position: absolute;
top: 20px;
left: 50%;
transform: translateX(-50%);
color: #fff;
font-family: Arial, sans-serif;
font-size: 16px;
background-color: rgba(0, 0, 0, 0.5);
padding: 10px 20px;
border-radius: 10px;
pointer-events: none; /* Make it non-interactive */
}
Use WASD to move
function createRectWithOrigin(
x,
y,
width,
height,
origin = { x: 0.5, y: 0.5 },
options = {}
) {
// Calculate the center offset from the desired origin
const offsetX = (0.5 - origin.x) * width;
const offsetY = (0.5 - origin.y) * height;
// Create the rectangle at the world position plus the offset
const rect = Bodies.rectangle(
x + offsetX,
y + offsetY,
width,
height,
options
);
// Shift vertices so the physics center aligns with the origin
//Body.translate(rect, Vector.create(-offsetX, -offsetY));
return rect;
}
/**
* Create thin rectangles along a line path to approximate a line for collisions.
* @param {Vector[]} points - Array of points [{x, y}, ...] defining the line path
* @param {number} thickness - The thickness of the rectangles
* @param {object} options - Optional Matter.js body options
* @param {World} world - Matter.World to add the rectangles to
* @returns {Body[]} - Array of rectangle bodies created
*/
function createLineBodies(
x,
y,
points,
thickness = 2,
options = {},
world
) {
const bodies = [];
for (let i = 0; i < points.length - 1; i++) {
const p1 = points[i];
const p2 = points[i + 1];
// Compute segment vector
const delta = Vector.sub(p2, p1);
const length = Vector.magnitude(delta);
// Compute angle
const angle = Math.atan2(delta.y, delta.x);
const wallOptions = {
...options,
isStatic: true,
friction: 0.01, // Low friction to encourage sliding
render: {
fillStyle: "#a1a1a1",
},
};
// Create thin rectangle for this segment
const rect = Bodies.rectangle(
x + (p1.x + p2.x) / 2,
y + (p1.y + p2.y) / 2,
length,
thickness,
Object.assign({}, wallOptions, { angle })
);
bodies.push(rect);
if (world) World.add(world, rect);
}
return bodies;
}
/**
* Create a compound Matter.js body from pre-decomposed convex polygons
* @param {number} x - world x position of the origin
* @param {number} y - world y position of the origin
* @param {Array[]} convexParts - array of convex vertex sets, e.g. [[{x,y},...], ...]
* @param {object} options - Matter.js body options applied to all parts
* @param {Vector} origin - optional offset vector to shift origin (default {x:0,y:0})
* @param {World} world - optional Matter.World to add the body to
* @returns {Body} compound Matter.js body
*/
function createCompoundBody(
x,
y,
convexParts,
options = {},
origin = { x: 0, y: 0 },
world
) {
const parts = convexParts.map((vertices) => {
// Create each convex body at origin; options can include render or physics settings
return Bodies.fromVertices(x, y, [vertices], options, true);
});
// Combine all parts into a single compound body
const compound = Body.create({
parts: parts,
...options,
});
// Adjust origin if requested
if (origin.x !== 0 || origin.y !== 0) {
Body.translate(compound, { x: -origin.x, y: -origin.y });
}
return compound;
}
// --- MODULE ALIASES ---
// These aliases make the code easier to read
const { Engine, Render, Runner, World, Bodies, Body, Events, Vector } =
Matter;
// --- SCENE SETUP ---
const sceneWidth = 800;
const sceneHeight = 600;
// 1. Create the physics engine
const engine = Engine.create();
// Disable gravity for a top-down view
engine.world.gravity.y = 0;
// 2. Create the renderer
const render = Render.create({
element: document.body,
engine: engine,
options: {
width: sceneWidth,
height: sceneHeight,
wireframes: false, // Set to false for solid colors
background: "#333",
showPerformance: true,
},
});
// 3. Create a runner to step the engine forward
// --- PLAYER SETUP ---
const playerRadius = 15;
const player = Bodies.circle(
sceneWidth / 2,
sceneHeight / 2,
playerRadius,
{
label: "player",
// Physics properties for a "slidey" feel with acceleration
frictionAir: 0.08, // Increased air friction for a smoother stop
friction: 0.0, // Very low friction against other bodies
restitution: 0.0, // A little bounciness
inertia: Infinity, // Prevents the player from rotating on collision
render: {
fillStyle: "#4287f5", // Player color
},
}
);
// --- CHASER SETUP ---
const chasers = [];
const chaserCount = 0;
const chaserRadius = 14;
const chaserForce = 0.000511;
const chaserOptions = {
label: "chaser",
frictionAir: 0.07,
friction: 0.0,
density: 0.00051,
restitution: 0.0, // Make them a bit bouncy
render: {
fillStyle: "#f55a42", // Chaser color
},
};
for (let i = 0; i < chaserCount; i++) {
// Spawn them in random locations away from the center
const x = Math.random() * sceneWidth;
const y = Math.random() * sceneHeight;
const chaser = Bodies.circle(x, y, chaserRadius, chaserOptions);
chasers.push(chaser);
}
// --- WALLS & OBSTACLES ---
// The walls are static, meaning they don't move
const wallOptions = {
isStatic: true,
friction: 0.01, // Low friction to encourage sliding
render: {
fillStyle: "#a1a1a1",
},
};
const wallThickness = 50;
const walls = [
...createLineBodies(200, 100, [
{ x: 0, y: 0 },
{ x: 500, y: 5 },
{ x: 500, y: 30 },
{ x: 0, y: 35 },
{ x: 0, y: 200 },
{ x: 100, y: 200 },
{ x: 400, y: 210 },
]), // Top
Bodies.rectangle(sceneWidth / 2, 0, sceneWidth, wallThickness, {
...wallOptions,
}), // Top
// Outer boundaries
Bodies.rectangle(
sceneWidth / 2,
sceneHeight,
sceneWidth,
wallThickness,
{ ...wallOptions }
), // Bottom
Bodies.rectangle(0, sceneHeight / 2, wallThickness, sceneHeight, {
...wallOptions,
}), // Left
Bodies.rectangle(
sceneWidth,
sceneHeight / 2,
wallThickness,
sceneHeight,
{ ...wallOptions }
), // Right
];
// --- CORNER OBSTACLES ---
// These are made of two static rectangles to test multi-body collisions
const cornerSize = 150;
const cornerThickness = 20;
// Top-left corner
const topLeftCorner = [
createRectWithOrigin(
100,
wallThickness / 2,
10,
500,
{ x: 0.5, y: 0 },
{
...wallOptions,
render: {
fillStyle: "#9217f5",
},
}
),
];
// Bottom-right corner
const bottomRightCorner = [
Bodies.rectangle(
sceneWidth - cornerSize / 2,
sceneHeight - 100 - cornerThickness / 2,
cornerSize,
cornerThickness,
{ ...wallOptions }
),
Bodies.rectangle(
sceneWidth - cornerSize + cornerThickness / 2,
sceneHeight - 100 - cornerSize / 2,
cornerThickness,
cornerSize,
{ ...wallOptions }
),
];
World.add(engine.world, [
player,
...walls,
...topLeftCorner,
...bottomRightCorner,
...chasers, // Add this line
]);
const keys = {};
const playerForce = 0.0015; // A small force value for gradual acceleration
window.addEventListener("keydown", (event) => {
keys[event.code] = true;
});
window.addEventListener("keyup", (event) => {
keys[event.code] = false;
});
// This event runs just before the engine updates each frame
Events.on(engine, "beforeUpdate", (event) => {
const force = Vector.create(0, 0);
if (keys["KeyW"]) {
force.y -= playerForce;
}
if (keys["KeyS"]) {
force.y += playerForce;
}
if (keys["KeyA"]) {
force.x -= playerForce;
}
if (keys["KeyD"]) {
force.x += playerForce;
}
// Apply the calculated force to the player's center
Body.applyForce(player, player.position, force);
chasers.forEach((chaser) => {
// Calculate vector from chaser to player
const direction = Vector.sub(player.position, chaser.position);
// Normalize the vector (get a unit vector)
const normalizedDirection = Vector.normalise(direction);
// Create a force vector and apply it to the chaser
const force = Vector.mult(normalizedDirection, chaserForce);
Body.applyForce(chaser, chaser.position, force);
});
});
const physicsDelta = 1000 / 600;
let lastTime = performance.now();
let accumulator = 0;
// This function will be our main game loop.
function gameLoop(currentTime) {
debugger;
// Calculate how much time has passed since the last frame.
const elapsed = currentTime - lastTime;
lastTime = currentTime;
// Add the elapsed time to an accumulator.
accumulator += elapsed;
// Trigger the 'beforeUpdate' event where all your movement logic lives.
// The default runner does this automatically, so we must do it manually here.
Events.trigger(engine, "beforeUpdate", {
timestamp: engine.timing.timestamp,
});
// While the accumulator has enough time for one or more physics steps,
// update the engine. This loop ensures the physics simulation "catches up"
// if rendering falls behind, maintaining a consistent simulation speed.
while (accumulator >= physicsDelta) {
// Update the engine by a fixed amount.
Engine.update(engine, physicsDelta);
accumulator -= physicsDelta;
}
// Render the current state of the world.
// This is done once per frame, regardless of how many physics steps were taken.
Render.world(render);
// Request the next animation frame from the browser to continue the loop.
requestAnimationFrame(gameLoop);
}
// Start the custom game loop!
requestAnimationFrame(gameLoop);
Подробнее здесь: https://stackoverflow.com/questions/797 ... -deltatime
Мобильная версия