Search code examples
javascriptcollision-detectiongame-physicsphaser-framework

Phaser Complex Collisions


How can I detect the collisions of multiple children from different groups?

enter image description here

similar

enter image description here

Imagine something like this, but with square hitboxes the pink object is from one group, the red object from another, and the orange objects from another group

(also, this exact order/position is not necessary, but as long as each orange object is touching two bigger objects and they are near each other)

how can I get an collision like this in arcade physics? (matter.js solutions still work if its not possible in arcade, but arcade is preferred)

EDIT, all hitboxes are square


Solution

  • Update: I misunderstud your question

    In Arcade Physics this is not really possible you could work around it setting the hitbox to circles ( checkout this example). It would not fix prefect, but could be a easy solution if it doesn't have to be perfect. If you need exact hitboxes, you need matterjs (or create your own physics-engine).

    Well I think it is difficult to achevie this with arcade physics, since each impact will push the object away and this will push the other touching away, so rarely all 6 will be touching.

    That said depending on you usecase, you could iterate through all gameobject and check if it matches your desired pattern. Depending on how many gameObjects it might cause performance issues.

    Basically your have to programm the matching yourself

    Small Demo, for three Object:
    (the objects have to overlap for this demo to work)

    UPDATE CODE for complex model

    document.body.style = 'margin:0;';
    
    var config = {
        type: Phaser.AUTO,
        width: 536,
        height: 183,
        physics: {
            default: 'arcade',
            arcade: { gravity: { y: 0 } }
        },
        scene: { create }
    };
    
    function create() {
        this.add.text(10, 10, 'Target ')
            .setScale(1.5)
            .setOrigin(0)
            .setStyle({ fontStyle: 'bold', fontFamily: 'Arial' });
    
        this.add.text(180, 10, 'Build yourself')
            .setScale(1.5)
            .setOrigin(0)
            .setStyle({ fontStyle: 'bold', fontFamily: 'Arial' });
    
        this.result = this.add.text(400, 10, 'No Match')
            .setScale(1.5)
            .setColor('#ff0000')
            .setOrigin(0)
            .setStyle({ fontStyle: 'bold', fontFamily: 'Arial' });
    
        let graphics = this.make.graphics();
        graphics.fillStyle(0xffffff);
        graphics.fillRect(0, 0, 20, 20);
        graphics.generateTexture('big', 20, 20);
    
        graphics.fillStyle(0xff0000);
        graphics.fillRect(0, 0, 10, 10);
        graphics.generateTexture('small', 10, 10);
    
        // DEMO
        let demoTop = this.add.image(60, 60, 'big');
        let demoMid = this.add.image(60, 60, 'small').setDepth(10);
        let demoBottom = this.add.image(60, 60, 'big');
    
        Phaser.Display.Align.To.RightCenter(demoMid, demoTop, -2, 0);
        Phaser.Display.Align.To.RightCenter(demoBottom, demoMid, -2, 0);
        
         this.add.image(75, 85, 'big');
         this.add.image(66, 72, 'small').setDepth(10);
         this.add.image(83, 72, 'small').setDepth(10);
    
        this.add.line(150, 0, 0, 0, 0, config.height, 0xffffff).setOrigin(0);
    
        this.big = this.physics.add.group({
            key: "big",
            repeat: 2,
            setXY: { x: 240, y: 65, stepX: 26 },
        });
    
        this.small = this.physics.add.group({
            key: "small",
            repeat: 2,
            setXY: { x: 290, y: 65, stepY: 32 },
        });
    
        this.big.children.iterate(function (child) {
            child.setInteractive({ draggable: true });
            child.on('pointerdown', () => {
                this.selectedGameObject = child;
            });
        }, this); // <-- don't forget to pass the context
    
        this.small.children.iterate(function (child) {
            child.setInteractive({ draggable: true });
            child.on('pointerdown', () => {
                this.selectedGameObject = child;
            });
        }, this); // <-- don't forget to pass the context    
    
        this.input.on('drag', pointer => {
            if (this.selectedGameObject) {
                this.selectedGameObject.setPosition(pointer.x, pointer.y);
            }
        });
    
        this.input.on('dragend', pointer => {
            if (this.selectedGameObject) {
                this.selectedGameObject.setPosition(pointer.x, pointer.y);
                this.selectedGameObject = null
            }
        })
    
        this.physics.add.overlap([this.small, this.big], [this.small, this.big], (p1, p2) => {
            // Target is BIG - SMALL - BIG
            this.result.setText('No Match').setColor('#ff0000');
    
            if (p1.texture.key == 'big') {
                if (p2.texture.key == 'small') {
                    this.big.children.iterate(function (p3) {
                        if (p3 != p1 && this.physics.overlap(p2, p3)) { 
                            this.small.children.iterate(
                                function (p4) {
                                    if (p4 != p2 && this.physics.overlap(p3, p4)) {
                                        this.big.children.iterate(function (p5) {
                                            if([p1,p3].indexOf(p5) == -1 && this.physics.overlap(p4, p5)){
                                                this.small.children.iterate(
                                                    function (p6) { 
                                                        if([p2,p4].indexOf(p6) == -1 && 
                                                            this.physics.overlap(p5, p6) &&
                                                            this.physics.overlap(p6, p1)
                                                        ){
                                                                this.result.setText('Match').setColor('#00ff00');
                                                        }
                                                    }, this);
                                            }
                                        }, this);
                                    }
                                }, this);
                        }
                    }, this);
                }
            }
        });
    }
    
    new Phaser.Game(config);
    <script src="//cdn.jsdelivr.net/npm/phaser/dist/phaser.min.js"></script>

    If you opt for matterjs, I recommend checking out this example for matterjs, it is straight forward, but how it is done depnece on your specific usecase.

    Here is the link all official phaser matterjs examples