Jump to content

Infinite Matter tiles


ol-web
 Share

Recommended Posts

So I wrote something that would allow me to have a tilemap full of matter tiles without killing my CPU before it even has a chance to render a frame. I did a test with a 200x200 tiles tilemap with 8000 colliding matter tiles and it just works. In areas with no colliding tiles the game runs smoothly at 10% cpu usage. Of course the usage spikes and frames drop to unplayable levels when going to areas with like 500 tiles at once but the moment they're off the camera there are once again no collisions and therefore smooth framerate again. Sadly it can have some leaks in some scenarios so in an act of desperation I throttled a worldwide tile cleanup every 10 seconds.

The code isn't very good. It's pretty ugly too. And I don't know how performant it is compared to how it could be if it was made differently either. It gets the job done and actually allows for infinite bodies so at least there's that.

 

Here's the question though:

Does a feature like this have a chance to be implemented in Phaser in the future? I'd rather use a performant native solution than my own code. JS games need optimization more than any others so it'd be nice to have it in Phaser out of the box.

And maybe another question: what's the best way to temporarily disable a matter body? As you can see below in methods enableBodiesOnTiles and disableBodiesOnTiles I just copy matterBody.body to a different variable and then call removeBody(). Is there a better way?

 

    disableLeakedTileBodies() {
        const hasMatterBody = tile => (tile.physics.matterBody && tile.physics.matterBody.body);
        const worldTiles = this.getTilesWithinWorldXY(0, 0, this.widthInPixels, this.heightInPixels).filter(hasMatterBody);

        const cameraTiles = this.getTilesWithinWorldXY(
            this.scene.cameras.main.scrollX,
            this.scene.cameras.main.scrollY,
            this.scene.cameras.main.width,
            this.scene.cameras.main.height
        ).filter(hasMatterBody);

        const leakedTiles = worldTiles.length - cameraTiles.length;
        this.disableBodiesOnTiles(difference(worldTiles,cameraTiles));

        return leakedTiles;
    }
    enableBodiesOnTiles(tiles) {
        for (var i = 0; i < tiles.length; i++) {
            var matterBody = tiles[i].physics.matterBody;
            if (matterBody && !matterBody.body) matterBody.setBody(matterBody.bodyCopy);
        }
    }
    disableBodiesOnTiles(tiles) {
        for (var i = 0; i < tiles.length; i++) {
            var matterBody = tiles[i].physics.matterBody;
            if (matterBody && matterBody.body) matterBody.removeBody();
        }
    }
    update() {
        const tileWidth = this.tileWidth;
        const tileHeight = this.tileHeight;
        const paddingX = 2 * tileWidth;
        const paddingY = 2 * tileHeight;
        const cameraWidth = this.scene.cameras.main.width;
        const cameraHeight = this.scene.cameras.main.height;
        const cameraOffsetX = this.scene.cameras.main.scrollX;
        const cameraOffsetY = this.scene.cameras.main.scrollY;

        this.cameraOffsetBeforeUpdateX = this.cameraOffsetBeforeUpdateX || cameraOffsetX;
        this.cameraOffsetBeforeUpdateY = this.cameraOffsetBeforeUpdateY || cameraOffsetY;

        const cameraOffsetAfterUpdateX = this.cameraOffsetBeforeUpdateX - cameraOffsetX;
        const cameraOffsetAfterUpdateY = this.cameraOffsetBeforeUpdateY - cameraOffsetY;
        const shouldTilesRenderX = Math.abs(cameraOffsetAfterUpdateX) >= tileWidth;
        const shouldTilesRenderY = Math.abs(cameraOffsetAfterUpdateY) >= tileHeight;

        if (shouldTilesRenderX || shouldTilesRenderY) {
            let tilesPassedX = Math.ceil(cameraOffsetAfterUpdateX / tileWidth);
            let tilesPassedY = Math.ceil(cameraOffsetAfterUpdateY / tileHeight);

            // throttled leak cleanup every 10 seconds
            this.disableLeakedTileBodies();
            this.cameraOffsetBeforeUpdateX = 0;
            this.cameraOffsetBeforeUpdateY = 0;

            if (tilesPassedX > 0) {
                // CAMERA MOVES TO THE LEFT
                const newTiles = this.getTilesWithinWorldXY(
                    cameraOffsetX,
                    cameraOffsetY,
                    tilesPassedX * tileWidth + paddingX,
                    cameraHeight
                );
                const oldTiles = this.getTilesWithinWorldXY(
                    cameraOffsetX + cameraWidth,
                    cameraOffsetY - tilesPassedX * tileHeight,
                    tilesPassedX * tileWidth + paddingX,
                    cameraHeight + tilesPassedX * tileHeight
                );
                
                this.enableBodiesOnTiles(newTiles);
                this.disableBodiesOnTiles(oldTiles);
            } else if (tilesPassedX < 0) {
                // CAMERA MOVES TO THE RIGHT
                tilesPassedX = Math.abs(tilesPassedX);
                const newTiles = this.getTilesWithinWorldXY(
                    cameraOffsetX + cameraWidth - tilesPassedX * tileWidth - paddingX,
                    cameraOffsetY,
                    tilesPassedX * tileWidth + paddingX,
                    cameraHeight
                );
                const oldTiles = this.getTilesWithinWorldXY(
                    cameraOffsetX - tilesPassedX * tileWidth - paddingX,
                    cameraOffsetY - tilesPassedX * tileHeight,
                    tilesPassedX * tileWidth + paddingX,
                    cameraHeight + tilesPassedX * tileHeight
                );

                this.enableBodiesOnTiles(newTiles);
                this.disableBodiesOnTiles(oldTiles);
            }

            if (tilesPassedY > 0) {
                // CAMERA MOVES UP
                const newTiles = this.getTilesWithinWorldXY(
                    cameraOffsetX,
                    cameraOffsetY,
                    cameraWidth,
                    tilesPassedY * tileHeight + paddingY
                );
                const oldTiles = this.getTilesWithinWorldXY(
                    cameraOffsetX - tilesPassedY * tileWidth,
                    cameraOffsetY + cameraHeight,
                    cameraWidth + tilesPassedY * tileWidth,
                    tilesPassedY * tileHeight + paddingY
                );

                this.enableBodiesOnTiles(newTiles);
                this.disableBodiesOnTiles(oldTiles);
            } else if (tilesPassedY < 0) {
                // CAMERA MOVES DOWN
                tilesPassedY = Math.abs(tilesPassedY);
                const newTiles = this.getTilesWithinWorldXY(
                    cameraOffsetX,
                    cameraOffsetY + cameraHeight - tilesPassedY * tileHeight - paddingY,
                    cameraWidth,
                    tilesPassedY * tileHeight + paddingY
                );
                const oldTiles = this.getTilesWithinWorldXY(
                    cameraOffsetX - tilesPassedY * tileWidth,
                    cameraOffsetY - tilesPassedY * tileHeight - paddingY,
                    cameraWidth + tilesPassedY * tileWidth,
                    tilesPassedY * tileHeight + paddingY
                );

                this.enableBodiesOnTiles(newTiles);
                this.disableBodiesOnTiles(oldTiles);
            }
        }
    }

 

Link to comment
Share on other sites

 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...