Is it possible to add Graphic objects into a staticGroup?

Hello,

I have drawn a line. That’s not very spectacular, I know :wink:
I wanted to get that line into a staticGroup. But all I managed was to generate a texture out of it and then use that in the staticGroup. Then it was able to use the overlap function. (this.physics.add.overlap(player, spriteLine):wink:

Here is my code:
lines = this.add.graphics();
lines.lineStyle(3, ‘black’, 1);
lines.lineBetween(0, 0, 0, 150);
lines.generateTexture(‘myLine’, 3, 150);
spriteLine = this.physics.add.staticGroup();
spriteLine.create(200, 500, ‘myLine’);
lines.destroy();

My question: is it possible to make that line get an overlap without that texture generation?

Since Graphics objects are just a set of commands that draw something, they don’t have an intrinsic size and you can’t put a physics body on them. You can work around this by adding x, y, displayWidth, and displayHeight properties to the Graphics object to let the physics engine work with it.

My recommendation, however, would be to use one of the shape objects - such as a line or a rectangle. They’re more efficient than Graphics objects and you don’t need workarounds to add physics to them.

2 Likes

Sure. You don’t need physics at all.
Since Graphics implements getBounds you can just use:

Phaser.Rectangle.intersects(boundsA, boundsB);

I would suggest sticking with your texture though, much easier to get the color data. You can then use something like the following example.

1 Like

That interesect looks interesting.
And I found a phaser function that should work even with a circle.
I just have to figure out how to use it.( Especially with my physical melon that phaser thinks is a rectangle … because phaser is seeing transparent pixels! :stuck_out_tongue_closed_eyes:)

<static> LineToCircle(line, circle [, nearest])
Checks for intersection between the line segment and circle.

You seem to dread the obvious pixel checking solution.
If you want to use shapes, look at this tutorial.

I STRONGLY advise against this route. The pixel routine is extremely easy, much faster, and WAY more accurate…

I want to, but I simply cannot do it at the moment.
I just cannot code it.

It makes no sense for me and therefore I have a block in my head.
When I have a function that can detect an overlap, why doesn’t it work only for non-transparent pixels? It seems to be a flaw of the whole framework …

In my head it makes much more sense to use inline svg, too.

All any overlap does is a rectangle intersect. So it is not interested in color per pixel.
It has nothing to do with the framework. Per pixel collision is rarely used, because it’s rarely necessary. In your case however…

Let’s try to have it make more sense.
From the example (in C#):

   static bool PerPixelCollision(Sprite a, Sprite b)
{
    // Get Color data of each Texture
    Color[] bitsA = new Color[a.Texture.Width * a.Texture.Height];
    a.Texture.GetData(bitsA);
    Color[] bitsB = new Color[b.Texture.Width * b.Texture.Height];
    b.Texture.GetData(bitsB);

    // Calculate the intersecting rectangle
    int x1 = Math.Max(a.Bounds.X, b.Bounds.X);
    int x2 = Math.Min(a.Bounds.X + a.Bounds.Width, b.Bounds.X + b.Bounds.Width);

    int y1 = Math.Max(a.Bounds.Y, b.Bounds.Y);
    int y2 = Math.Min(a.Bounds.Y + a.Bounds.Height, b.Bounds.Y + b.Bounds.Height);

     // For each single pixel in the intersecting rectangle
     for (int y = y1; y < y2; ++y)
     {
         for (int x = x1; x < x2; ++x)
         {
             // Get the color from each texture
             Color a = bitsA[(x - a.Bounds.X) + (y - a.Bounds.Y)*a.Texture.Width];
             Color b = bitsB[(x - b.Bounds.X) + (y - b.Bounds.Y)*b.Texture.Width];

             if (a.A != 0 && b.A != 0) // If both colors are not transparent (the alpha channel is not 0), then there is a collision
             {
                 return true;
             }
         }
     }
    // If no collision occurred by now, we're clear.
    return false;
}

This is all you need.
What part do you not get?

I implemented the collision code in your game, now you have pixel perfect collision again:

Alles-Melone

I’ve changed the sprite origins to make the calculations a bit more obvious, so the images are a bit out of place.
This is what’s new:

player = this.physics.add.sprite(p_x, p_y, 'melon').setOrigin(0);
barrier = this.physics.add.sprite(barrier_x, barrier_y, barrier_pic).setOrigin(0);
this.physics.add.overlap(player, barrier,
	function overlap(player, barrier) {
		this.scene.start('sceneA');
	},
	function process(player, barrier) {
		// Calculate the intersecting rectangle
		x1 = Math.max(Math.round(player.x), barrier.x);
		x2 = Math.min(Math.round(player.x) + player.width, barrier.x + barrier.width);
		y1 = Math.max(Math.round(player.y), barrier.y);
		y2 = Math.min(Math.round(player.y) + player.height, barrier.y + barrier.height);
		// For each pixel in the intersecting rectangle
		for (y = y1; y < y2; ++y)
			for (x = x1; x < x2; ++x) {
				// Get the color from both textures
				colorPlayer = canvasPlayer.getPixel(x - Math.round(player.x), y - Math.round(player.y));
				colorBarrier = canvasBarrier.getPixel(x - barrier.x, y - barrier.y);
				// If both colors are not transparent (the alpha channel is not 0), then there is a collision
				if (colorPlayer.a != 0 && colorBarrier.a != 0) return true;
			}
		return false;
	},
	this
);

if (typeof canvasPlayer == "undefined") {
	src_player = this.textures.get('melon').getSourceImage();
	canvasPlayer = this.textures.createCanvas('canvasPlayer', src_player.width, src_player.height);
	canvasPlayer.draw(0, 0, src_player);
}
src_barrier = this.textures.get(barrier_pic).getSourceImage();
this.textures.remove('canvasBarrier');
canvasBarrier = this.textures.createCanvas('canvasBarrier', src_barrier.width, src_barrier.height);
canvasBarrier.draw(0, 0, src_barrier);

I create 2 canvases, draw the player and level images on them, and then use getPixel to check the alpha (transparency) for each corresponding pixel in the intersecting rectangle.

1 Like

Wow!

Thanks a lot!

I will go through that in detail and try to find out what each thing does.
I mean I have a hunch about most things, but I need to look at that in detail.

Thanks again. That’s awesome of you :smiley:

Okay. I am through now.

There are two things I wouldn’t have done:

  • creating those 2 canvases
  • using the slots for those callback things in “add.overlap” to define own functions

If you use getPixel directly on a texture, this happens for every pixel:

ctx.clearRect(0, 0, 1, 1);
ctx.drawImage(textureFrame.source.image, x, y, 1, 1, 0, 0, 1, 1);
var rgb = ctx.getImageData(0, 0, 1, 1);

So for performance reasons, you want to get all the pixel data only once, by creating a canvas, and drawing the texture on it. You can then directly access the data as an array.
This is all a canvas getPixel does:

var data = this.data;
var r = data[index + 0];
var g = data[index + 1];
var b = data[index + 2];
var a = data[index + 3];

1 Like

I try to add a rectangle to a static group. How to make it?

function create() {
    const ground = this.add.rectangle(361, 348, 721.35, 48.4, 0xcccccc);
    ground.setStrokeStyle(1, 0xff0000);

    platforms = this.physics.add.staticGroup();
    // How to add a ground to a static Group?
}

It works:

function create() {
    const ground = this.add.rectangle(361, 348, 721.35, 48.4, 0xcccccc);
    ground.setStrokeStyle(1, 0xff0000);
    this.physics.world.enable(ground);
    ground.body.immovable = true;
    ground.body.allowGravity = false;