Pixelmasks and Spritemaps


(Ricesteam) #1

First, I want to thank the community for the help provided so far!

Now, how do I get the two working for pixel perfect collisions?


(billy2000) #2

If you are trying to say if its possible to make pixelmask with a spritemap the answer is yes. I once did that in my older projects. you do it like this:

private var TheMask:Pixelmask;
public function TheEntity():void 
	{
		TheSpritemap.add("map", [nr. of frames], speed, true/false);
		graphic = TheSpritemap;
		TheMask = new Pixelmask(TheSpritemap.getBuffer());
        mask=TheMask;
	}

The problem is that ,if i remember it right u had to add something to NET(forgot the class sry >.<). If someone still remember what should be changed pls tell it would help :D. In any case…i still have the NET of that project wich should be like the normal NET cuz it was a pretty recent project.So you can give me a pm with your mail and ill send it to you.


(kgbkgb) #3

I think it was adding

public function getBuffer():BitmapData
	{
		return _buffer;
	}

Into Image.as


(billy2000) #4

you are right THX :slight_smile:


(Zouhair Elamrani Abou Elassad) #5

… I’m trying to use pixemasks and spritemaps as well, i added the getBuffer() function in net.flashpunk.graphics.image , i used it in : TheMask = new Pixelmask(TheSpritemap.getBuffer()) , but i still get the error : Invalid Pixelmask source image, am i missing something ?


(Mike Evmm) #6

The pixelmask class takes a bitmapdata and sort of makes a hitbox, but with the shape of that bitmapdata (hence pixelperfect collisions). By doing

TheMask = new Pixelmask(TheSpritemap.getBuffer());

You’re getting the current bitmapdata of your entity’s spritemap (the current frame’s bitmapdata), and applying it as a Pixelmask.
I just wanted to make sure everyone understands what’s going on behind (in front of?) the curtains.
What’s your spritemap like?


P.S. If you’re using an embeded image, not a spritemap, you can just pass your embeded class to the pixelmask, like so:

private var TheMask:Pixelmask;
private var img:Image
public function TheEntity():void 
{
	img = new Image(A.MyEmbededPNG);
	this.graphic = img;
	TheMask = new Pixelmask(A.MyEmbededPNG);
        this.mask=TheMask;
}

In A.as:

package  
{
    public class A
    {
	[Embed(source = "MY_IMAGE_PATH.PNG")]public static const MyEmbededPNG:Class;
    }
}

(Zouhair Elamrani Abou Elassad) #7

… That’s exactly what i did, the problem is i have a large sprite sheet for my player, i embed it and use scale it and use it for the spritemap, but when i create my mask it’s created based on the large spritesheet and not the scaled one which gives me trouble with collisions , i want the mask to be created based on the player scaled size, is there a way to do that ?


(Mike Evmm) #8

Not sure, but

var mask:Pixelmask = new Pixelmask(A.MyEmbeddedPNG);
mask.scale = image.scale;
this.mask = mask;

I don’t have a compiler with me, could you please confirm it works?


(Zouhair Elamrani Abou Elassad) #9

… There is no mask.scale : Access of possibly undefined property scale through a reference with static type net.flashpunk.masks:Pixelmask


(Mike Evmm) #10

Okay, one last shot:

 var imageBmd:BitmapData = image.getBuffer();
 var scale:Number = image.scale;
 var width:int = (imageBmd.width * scale) || 1;
 var height:int = (imgeBmd.height * scale) || 1;
 var transparent:Boolean = imageBmd.transparent;
 var result:BitmapData = new BitmapData(width, height, transparent);
 var matrix:Matrix = new Matrix();
 matrix.scale(scale, scale);
 result.draw(bitmapData, matrix);
 mask = new Pixelmask(result);

This edit thing can be kind of silly.


(Zouhair Elamrani Abou Elassad) #11

… Man that’s awesome it’s working, thank’s a lot, could please explain a bit what you did that would great :slight_smile:


(Mike Evmm) #12

I can write a more detailed explanation later if you want me to (I’m on my phone!) but basically, I’m taking your image’s current bitmapdata:
var imageBmd:BitmapData = image.getBuffer();
your image’s scale:
var scale:Number = image.scale;
getting the width and height of the scaled image/its bitmap:
var width:int = (imageBmd.width * scale) || 1;
var height:int = (imgeBmd.height * scale) || 1;
whether the original bmp was transparent
var transparent:Boolean = imageBmd.transparent;
create a new bitmap with those properties (to put the scaled copy on), with those properties
var result:BitmapData = new BitmapData(width, height, transparent);
scale the original bitmap by the image’s scale using a Matrix:
var matrix:Matrix = new Matrix();
matrix.scale(scale, scale);
and copy the original bitmapdata with that scale applied to the new bmd. Then pass the scaled bmd to the pixelmask:

result.draw(bitmapData, matrix);
mask = new Pixelmask(result);

(Zouhair Elamrani Abou Elassad) #13

… Thanks for the feedback, that’s a very interesting way of doing it, i tried to apply the same thing for a scaled image (PNG) but i get a rectangular shaped mask instead of mask that only fills the non transparent part


(Mike Evmm) #14

What do you mean? (Cuold you provide some code?)


(Zouhair Elamrani Abou Elassad) #15

… I created a class that i want to use to generate a top and bottom obstacle at the same time, and i want to create a mask for every obstacle using MaskList to test collisions :

  _stalacImage = new Image(GameConstants.TOP_ROCK);
  _stalagImage = new Image(GameConstants.BOTTOM_ROCK); 

  var gapTop:Number = FP.rand(FP.screen.height - 2 * _maxOffse) + _maxOffset;

		_stalacImage.scaledHeight = gapTop;
		_stalagImage.scaledHeight = FP.screen.height - gap - gapTop
		_stalagImage.y = FP.screen.height - _stalagImage.scaledHeight;

               _stalacMask = generateMask(_stalacImage);
	       _stalagMask = generateMask(_stalagImage, _stalagImage.x , _stalagImage.y);

                graphic = new Graphiclist(_stalacImage, _stalagImage);
		mask = new Masklist(_stalacMask, _stalagMask);



	public static function generateMask(image:Object, x:int = 0, y:int = 0 ):Pixelmask {
		
		 var imageBmd:BitmapData = image.getBuffer();
		 var scale:Number = image.scale;
		 var width:int = (imageBmd.width * scale) || 1;
		 var height:int = (imageBmd.height * scale) || 1;
		 var transparent:Boolean = imageBmd.transparent;
		 var result:BitmapData = new BitmapData(width, height, transparent);
		 var matrix:Matrix = new Matrix();
		 matrix.scale(scale, scale);
		 result.draw(imageBmd, matrix);
		 var mask:Pixelmask = new Pixelmask(result, x, y);
		 
		 return mask;

	}

… It was a great work what you did with the player spritemap, now the mask is scaled based on the player size but it’s still in a rectangular shape, i think the calculated mask doesn’t recognize the transparent part, i guess the same thing is happening with the rocks with also the problem of scaled image, i was kinda hoping the have a mask for every rock so when my player touches one of them he falls (Flappy Bird Like Game)


(Zouhair Elamrani Abou Elassad) #16

… i updated the code, i’ve changed the generateMask function to use scaledHeight and scaledWidth of the images, now the mask takes the size of the scaled images :

as you can see there are two masks on the top of the screen, one for every rock, the thing is even if the mask of the bottom rock is up there (on the top of the screen), collisions still being launched if i touch the bottom rock, and if i go through the gap between the rocks nothing happens even if there is a part of a mask in there because basically it belongs to the bottom rock and collisions will start if i touched the area that the mask supposed to take around the bottom rock, so now the problem is about the transparency of the calculated mask, although the created image (result in the generateMask function) has a transparence = true, tha mask is still has a rectangular form based on the scaled height and width, and not being transparent in the parts where the scaled image is


(Zouhair Elamrani Abou Elassad) #17

… A huge progression, i edited my generateMask function to the following :

 public static function generateMask(image:Object, x:int = 0, y:int = 0 ):Pixelmask {
		
		 var imageBmd:BitmapData = image.getBuffer();
		 var scale:Number = image.scale;
		 
		 //var width:int = (imageBmd.width * scale) || 1;
		 //var height:int = (imageBmd.height * scale) || 1;
		 
		 var width:int = image.scaledWidth;
		 var height:int = image.scaledHeight;
		 var transparent:Boolean = imageBmd.transparent;
		
                 **var result:BitmapData = new BitmapData(width, height, transparent,0x00000000);**

		 var matrix:Matrix = new Matrix();
		 matrix.scale(scale, scale);
		 result.draw(imageBmd, matrix);

		 var mask:Pixelmask = new Pixelmask(result, x, y);
		 
		 return mask;

	}

0x00000000 is black(0x000000) with alpha equal to 0

as you can see the mask well calculted this time, except for the upper rock, there is always always a gap between the it and mask, still don’t know why.


(Mike Evmm) #18

So the problem is with the top rock, not the bottom one? It would seem like the mask for the bottom rock is not getting the correct Y coordinates. Maybe use FP.height instead of FP.screen.height?

_stalagMask = generateMask(_stalagImage, _stalagImage.x , _stalagImage.y);

to

_stalagMask = generateMask(_stalagImage, _stalagImage.x , FP.height - _stalacImage.scaledHeight);

Other than that I can’t spot anything. It’s also normal that you’d need to provide bgcolor as 0x000000 (btw, I believe it works if you just write 0 instead, as they’re the same), as I’ve had that issue before (though I’m not sure as to why it’s not assumed), but I have no ideia why that would be. I mean, if it’s transparent, there shouldn’t be a need for a bgcolor, and if it isn’t, I’d usually use white. Perhaps someone with more knowledge can help.


(Zouhair Elamrani Abou Elassad) #19

… I Just checked again, there is a gap also between the bottom rock and its mask, i guess the mask is not scaled to fit the scaled rock, except for the height may be.


(Mike Evmm) #20

I dunno, to me the issue here really looks like the masks’ xy coordinates:

_stalacImage = new Image(GameConstants.TOP_ROCK);
_stalagImage = new Image(GameConstants.BOTTOM_ROCK); 

var gapTop:Number = FP.rand(FP.screen.height - 2 * _maxOffse) + _maxOffset;

	_stalacImage.scaledHeight = gapTop;
	_stalagImage.scaledHeight = FP.height - gap - gapTop
	_stalagImage.y = FP.height - _stalagImage.scaledHeight;
       //This bit
           _stalacMask = generateMask(_stalacImage);
       _stalagMask = generateMask(_stalagImage,  0,  FP.height);
      //Till here
            graphic = new Graphiclist(_stalacImage, _stalagImage);
	mask = new Masklist(_stalacMask, _stalagMask);



public static function generateMask(image:Image, x:int = 0, y:int = 0 ):Pixelmask {

	 var imageBmd:BitmapData = image.getBuffer();
	 var scale:Number = image.scale;
	 var width:int = image.scaledWidth;
	 var height:int = image.scaledHeight;
	 var transparent:Boolean = imageBmd.transparent;
	 var result:BitmapData = new BitmapData(width, height, transparent, 0x000000);
	 var matrix:Matrix = new Matrix();
	 matrix.scale(scale, scale);
	 result.draw(imageBmd, matrix);
	 var mask:Pixelmask = new Pixelmask(result, x, y);

	 return mask;

}

I really don’t know, worst case scenario I’d just rewrite stuff to make two entities (top rock and bottom rock), each with their mask, instead of using a graphiclist and a masklist. Sorry I’m not helping.