Я делаю простую баскетбольную игру типа щелчка. Игра 2D, но после запуска мяч уменьшается в размерах, чтобы создать 3D-эффект.
В месте появления мяча край кольца находится прямо над ним на экране. Однако мне нужно запустить мяч выше обода, не сталкиваясь с ним, а затем, когда мяч опускается вниз, обод становится объектом, с которым мяч может столкнуться. В противном случае я запускаю мяч, и по его восходящей траектории он просто ударяется об обод снизу и отскакивает на землю.
Вот код, который у меня есть:
export default class PlayScene extends Phaser.Scene {
constructor({ sceneKey, nextSceneKey }) {
super({ key: sceneKey, active: false });
this.nextSceneKey = nextSceneKey;
this.MIN_LAUNCH_VELOCITY = 10; // Minimum velocity required to count as a valid shot
this.isBallFalling = false; // Track the ball's downward movement
this.isGameRunning = true;
}
init({ score, sessionToken, config }) {
this.config = config;
this.sessionToken = sessionToken;
this.score = score || 0;
this.isDown = false;
}
preload() {}
create() {
const { height, width } = this.game.config;
// Define collision categories
const BALL_CATEGORY = 0x0001;
const RIM_CATEGORY = 0x0002;
this.matter.world.setBounds(
0,
0,
width,
height,
32,
true,
true,
false,
true
); // Disabling the top boundary
this.background = this.add
.tileSprite(0, 0, width, height, 'global', 'background')
.setOrigin(0, 0)
.setAlpha(0.6);
setHeightAndScale(this.background, height);
this.hoop = this.add.sprite(width / 2, 400, 'global', 'hoop');
setHeightAndScale(this.hoop, 250);
// Create small circular collision bodies for the sides of the rim
this.leftRim = this.matter.add.circle(width / 2 - 80, 485, 1, {
isStatic: true,
label: 'leftRim',
});
this.rightRim = this.matter.add.circle(width / 2 + 80, 485, 1, {
isStatic: true,
label: 'rightRim',
});
// Add debug graphics to see the collision bodies
this.debugGraphics = this.add.graphics();
this.debugGraphics.lineStyle(1, 0x00ff00);
this.debugGraphics.fillStyle(0x00ff00, 0.5);
this.drawDebug(this.leftRim);
this.drawDebug(this.rightRim);
this.ball = this.matter.add.image(
width / 2,
height - 50,
'global',
'basketball'
);
setHeightAndScale(this.ball, 150);
this.ball.setCircle();
this.ball.setBounce(0.5);
this.ball.setInteractive({ useHandCursor: true });
this.matter.add.mouseSpring({ length: 1, stiffness: 0.05 }); // Reduce stiffness for less sensitivity - experiment with
this.matter.world.setGravity(0, 4);
this.ball.on('pointerup', () => {
const velocityX = this.ball.body.velocity.x * 0.5;
const velocityY = this.ball.body.velocity.y * 0.5;
// Check if the ball was actually launched
if (
Math.abs(velocityX) < this.MIN_LAUNCH_VELOCITY &&
Math.abs(velocityY) < this.MIN_LAUNCH_VELOCITY
) {
this.resetBall(this.ball);
return;
}
this.ball.setVelocity(velocityX, velocityY);
// Add scale tween to simulate "3D" effect
this.tweens.add({
targets: this.ball,
scaleX: 0.5,
scaleY: 0.5,
duration: 150,
ease: 'Power1',
onComplete: () => {
if (
this.ball.body.velocity.x === 0 &&
this.ball.body.velocity.y === 0
) {
this.ball.setScale(1, 1);
}
},
});
// reset the ball if it goes out of bounds
this.time.delayedCall(1000, () => {
if (
this.ball.y > height ||
this.ball.y < 0 ||
this.ball.x > width ||
this.ball.x < 0
) {
this.resetBall(this.ball);
}
});
});
// Periodic check to reset the ball if it goes out of bounds
this.time.addEvent({
delay: 500,
callback: () => {
if (
this.ball.y > height ||
this.ball.y < 0 ||
this.ball.x > width ||
this.ball.x < 0
) {
this.resetBall(this.ball);
}
},
loop: true,
});
// Pre-collision detection to manage ball and rim collision dynamically
this.matter.world.on('collisionactive', (event) => {
event.pairs.forEach((pair) => {
const { bodyA, bodyB } = pair;
// Handle rim collisions only when the ball is falling
if (
(bodyA.label === 'basketball' &&
(bodyB.label === 'leftRim' ||
bodyB.label === 'rightRim')) ||
(bodyB.label === 'basketball' &&
(bodyA.label === 'leftRim' ||
bodyA.label === 'rightRim'))
) {
if (!this.isBallFalling) {
pair.isActive = false;
}
}
});
});
// Collision detection for scoring
this.matter.world.on('collisionstart', (event) => {
event.pairs.forEach((pair) => {
const { bodyA, bodyB } = pair;
// Handle rim collisions only when the ball is falling
if (
(bodyA.label === 'basketball' &&
(bodyB.label === 'leftRim' ||
bodyB.label === 'rightRim')) ||
(bodyB.label === 'basketball' &&
(bodyA.label === 'leftRim' ||
bodyA.label === 'rightRim'))
) {
if (this.isBallFalling) {
this.handleRimCollision();
}
}
// Check if the ball hits the ground
if (
(bodyA === this.ball.body && bodyB.label === 'ground') ||
(bodyB === this.ball.body && bodyA.label === 'ground')
) {
this.resetBall(this.ball);
}
});
});
// Create the ground as a static body
this.ground = this.matter.add.rectangle(width / 2, height, width, 10, {
isStatic: true,
label: 'ground',
});
}
drawDebug(body) {
this.debugGraphics.fillCircle(body.position.x, body.position.y, 5);
this.debugGraphics.strokeCircle(body.position.x, body.position.y, 5);
}
handleRimCollision() {
this.score += 1;
console.info('Score:', this.score);
}
resetBall(ball) {
const { height, width } = this.game.config;
ball.setPosition(width / 2, height - 50); // Reset to bottom center of the screen
ball.setVelocity(0, 0);
ball.setScale(1, 1);
this.isBallFalling = false; // Reset the falling state
}
update() {
if (!this.isGameRunning) {
return;
}
// Check if the ball is falling
this.isBallFalling = this.ball.body.velocity.y > 0;
const { height, width } = this.game.config;
if (
this.ball.y > height ||
this.ball.y < 0 ||
this.ball.x > width ||
this.ball.x < 0
) {
this.resetBall(this.ball);
}
}
}
Основные части, которые я считаю важными, — это проверка перед столкновением collisionactive и фактическая логика столкновения collisionstart. Я думал, что мяч мог столкнуться с краями обода только в том случае, если бы он двигался по нисходящей траектории (т. е. скорость y больше 0). Однако ничего из того, что я сделал до этого момента, не заставило его работать.
Наличие проверки перед столкновением и проверки методов начала столкновения. Управляйте столкновением только в том случае, если траектория мяча движется вниз.



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Решением может быть использование/установка свойства isSensor, когда мяч падает.
Я создал короткую демонстрацию на основе вашего кода (я просто сохраняю соответствующие части), если скорость больше 0 («падение»), обод является всего лишь датчиком, столкновения не будет. Это может сработать в большинстве случаев.
Короткая демонстрация:
«бросить» (перетащить и отпустить) «шар», но небольшими движениями, иначе вы не сможете его увидеть.
document.body.style = 'margin:0;';
class PlayScene extends Phaser.Scene {
create() {
const { height, width } = this.game.config;
this.add.text(15, 10, 'Demo for "isSensor"')
.setColor('#ffffff');
this.matter.world.setBounds(0, 0, width, height, 32, true, true, false, true);
this.matter.add.mouseSpring({ length: 1, stiffness: 0.05 });
this.matter.world.setGravity(0, 4);
// Create small circular collision bodies for the sides of the rim
this.leftRim = this.matter.add.circle(width / 2 - 80, 120, 8, { isStatic: true, label: 'leftRim', });
this.rightRim = this.matter.add.circle(width / 2 + 80, 120, 8, { isStatic: true, label: 'rightRim', });
this.ball = this.matter.add.image( width / 2 - 79, 30, 'global', 'basketball')
.setCircle()
.setInteractive({ useHandCursor: true });
// Create the ground as a static body
this.ground = this.matter.add.rectangle(width / 2, height, width, 10, {
isStatic: true,
label: 'ground',
});
}
update() {
// lower than zero, to prevent rounding errors
this.rightRim.isSensor = this.leftRim.isSensor = (this.ball.body.velocity.y < -.05);
}
}
var config = {
width: 500,
height: 180,
physics: {
default: 'matter',
matter: {
debug: {
staticFillColor: 0xff0000,
staticLineColor: 0xff0000,
renderFill: true,
}
}
},
scene: [PlayScene],
};
new Phaser.Game(config);
console.clear();<script src = "//cdn.jsdelivr.net/npm/phaser/dist/phaser.min.js"></script>Информация: для тестирования также можно просто перетащить мяч через обод, и вы увидите, как он меняется на датчик и обратно. (Если «обод» синий, то это датчик, если красный, то это предмет, с которым можно столкнуться).
Совет: Вы также можете рассмотреть возможность использования размера шара, чтобы определить, должно ли произойти столкновение.
Если бы вы могли предоставить небольшой пример, объясняющий вашу проблему, это могло бы облегчить нам (вам) понимание и помочь вам с решением. Как упоминалось в ТАК- статье. Ваш код работоспособен, непонятно, что должно произойти.