Как условно включить/выключить коллизии в Phaser 3?

Я делаю простую баскетбольную игру типа щелчка. Игра 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). Однако ничего из того, что я сделал до этого момента, не заставило его работать.

Наличие проверки перед столкновением и проверки методов начала столкновения. Управляйте столкновением только в том случае, если траектория мяча движется вниз.

Если бы вы могли предоставить небольшой пример, объясняющий вашу проблему, это могло бы облегчить нам (вам) понимание и помочь вам с решением. Как упоминалось в ТАК- статье. Ваш код работоспособен, непонятно, что должно произойти.

winner_joiner 01.06.2024 06:46
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
1
57
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Решением может быть использование/установка свойства 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>

Информация: для тестирования также можно просто перетащить мяч через обод, и вы увидите, как он меняется на датчик и обратно. (Если «обод» синий, то это датчик, если красный, то это предмет, с которым можно столкнуться).

Совет: Вы также можете рассмотреть возможность использования размера шара, чтобы определить, должно ли произойти столкновение.

Другие вопросы по теме