Hide Entity Behind A Specific Area In The World


(Zouhair Elamrani Abou Elassad) #1

Hi,

I would like to hide an entity behind a specific transparent area (Example: Rectangle) in the world, and once i tween the entity, it will slowly appear after it leaves the area that was hiding it.

I tried to use the drawMask property, but i was only able to show parts of the image inside the entity boundaries and not at a specific location in the world :

                 clippingMask = new BitmapData(200, 200, true, 0x00ffffff);
	         soundImage.drawMask = clippingMask;

Lets say that the entity is the red filled circle, i want to place it in my world, but i want it to be invisible, it will be hidden behind an area (Striped Rectangle here), the area will also be invisible, but at the same time it hides the entity, once i start moving my Entity ( x += 300 * FP.elapsed), the entity will start to appear slowly, like it’s coming from nowhere, this is the Image :

Thank You.


(Martí Angelats i Ribera) #2

I’m not sure i understood it right. Could you draw a fast example so i (and maybe other people) don’t misunderstand your question?


(Martí Angelats i Ribera) #4

I think you have two options here:

  1. That rectangle is an Entity that redraws the background on top after the other entity had been rendered (using layers).
  2. You can try to make a dynamic clippingMask.

What is best will depend on your specific case. The second one is really simple if it’s a single simple shape but gets harder really fast.

PS: This effect is not an easy thing to do.


(Zouhair Elamrani Abou Elassad) #5

I see what you mean, i don’t think i’ll go for the first one cue i have a moving parallax in the background, i’ll have to change the image continuously. I’ll try to go with the second one, and see if it gives me anything.

Thank you.


(rostok) #6

I assume you want to take a snapshot of screen buffer at some depth (probably lower than red circle) then draw red circle and finally paste the buffer on top of this. If that’s the deal I would create two types of rectangle entity for this. The lower one would have render overidden so it could store the screen part using BitmapData.copyPixels from FP.buffer. The top one should have a pointer to the previous entity in order to read part of the screen (render overidden as well). Also this one should adjust position of the lower entitiy in update. You can have two classes or just put this into one to keep the code clean. Hope this pseudocode will help:

`

public class MagicMirror extends Entity
{
	public var slave:MagicMirror = null;
	public var saved:BitmapData = null;
	
	public function MagicMirror(x:Number=0, y:Number=0, w:Number, h:Number, topLayer:int, bottomLayer:int, master:Boolean=true) 
	{
		// store position, dimensions and layers, and is it master
		this.layer = master ? topLayer : bottomLayer;
	}
	
	override public function added():void 
	{
		super.added();
		world.add( slave = new MagicMirror(x, y, w, h, topLayer, bottomLayer, false);
	}
	
	override public function update():void 
	{
		// copy position and dimensions to slave		       	
		slave.x = x;
		slave.y = y;
		slave.w = w;
		slave.h = h;
		super.update();
	}

	override public function render():void 
	{
		if (master)
		{
			// paste stored part of the buffer
			FP.buffer.draw(slave.saved, new Matrix( 1, 0, 0, 1, x, y ));
		}
		else
		{
			// store part of the buffer
			saved = new BitmapData(w, h, true, 0);
			// copy part of the screen 
			saved.copyPixels(FP.buffer, new Rectangle(x, y, w, h), new Point());
		}
	}
}

(Zouhair Elamrani Abou Elassad) #7

Hi,

I try to use the class, but it doesn’t seem to work, the project won’t even launch, what should i replace the ‘shape’ in the render function ?


(Martí Angelats i Ribera) #8

I did this:

public class MagicMirror extends Entity
{
	public var img:BitmapData; //the updated one to be shown.

	public MagicMirror(x:Number, y:Number, w:int, h:int)
	{
		//make it the size you want
		img = new BitmapData(w, h, true, 0);
		super(x, y, new Stamp(img));
	}

	override public function added():void
	{
		world.add(_slave);
	}
	override public function removed():void
	{
		if (_slave.world) _slave.world.remove(_slave);
	}

	override public function set layer(top:int):void
	{
		_slave.layer = l - 2; //Change to the desired value
		super.layer = l;
	}

	private var _slave:MagicSlave = new MagicSlave(this);
}

public class MagicSlave extends Entity
{
	public MagicSlave(parent:MagicMirror)
	{
		_parent = parent;
	}

	override public function render():void
	{
		//Render the current screen (the one rendered so far) to the shwon by the parent.
		_rect.setTo(_parent.x, _parent.y, _parent.img.width, _parent.img.height);
		_parent.img.copyPixels(_parent.renderTarget || FP.buffer, _rect, FP.zero);
	}

	private var _parent:MagicMirror;
	private var _rect:Rectangle = new Rectangle; //We use this to avoid generating a lot of garbage.
}

The idea is that we update a BitmapData that is actually the rendered one (because Stamp [as well as image] won’t clone it; they’ll use the same object so we can play with that). Then we have a slave that makes that job for us.

We override the added and removed to add and remove the slave. We also override the layer setter to set a correct one for the slave.

PS: I don’t have anything to test it because i’ve recentlly updated to W10 so i’m still installing stuff.


(rostok) #9

Sorry I had this copied from come other class that was doing something similar yet different. The shape parameter of copyPixels is about alpha channel. So basically you can remove it (as I did in edited post) and keep the area rectangular. However if you will add some Image with alpha channel to this entity you could use it to copy pixels from the screen buffer with any peculiar shape.


(Zouhair Elamrani Abou Elassad) #10

Hi Again,

It’s quite interesting, your code is great, it copies the background into the slave, but i don’t understand why the background of the slave is not changing, it’s like the render not working.


(Zouhair Elamrani Abou Elassad) #11

Thanks buddy, i’ll check that out :smile:


(Martí Angelats i Ribera) #12

If you want to have a graphic in the slave, you need to add super.render(); at the end of the MagicSlave's render function. I thought you weren’t using it so i simply didn’t add it (makes the code a little faster).


(Zouhair Elamrani Abou Elassad) #13

Actually i wanted to copypixels every frame from the moving background, so the user won’t be able to see that there is an entity in there.


(Martí Angelats i Ribera) #14

Isn’t that what it does?


(Zouhair Elamrani Abou Elassad) #15

It only takes a clip for the first time, then when the parallax changes, the MagicMirror doesn’t change also, it always shows the first image copied from the bachground.


(Martí Angelats i Ribera) #16

Hmm… Could you try this?

Slave

	override public function render():void
	{
		//Render the current screen (the one rendered so far) to the shwon by the parent.
		_rect.setTo(_parent.x, _parent.y, _parent.img.width, _parent.img.height);
		_parent.img.fillRect(_parent.img.rect, 0);
		_parent.img.copyPixels(FP.buffer, _rect, FP.zero);
	}

(rostok) #17

Here’s the working code. Hope this is what you wanted: http://rostok.3e.pl/download/MagicMirror.zip

/**
 * MagicMirror creates two entangled entities at different layers. 
 * The one at top renders everything that is below the one at the bottom.
 * 
 * Master is at the bottom because it is processed first
 * @author rostok
 */
public class MagicMirror extends Entity
{
	public var slave:MagicMirror = null;
	public var master:MagicMirror = null;
	public var saved:BitmapData = null;
	public var topLayer:int;
	public var isMaster:Boolean;
	
	public function MagicMirror(x:Number=0, y:Number=0, w:Number=0, h:Number=0, layer:int=0, topLayer:int=0, isMaster:Boolean=true, g:Graphic = null) 
	{
		// store position, dimensions and layers, and is it master
		this.layer = isMaster ? layer : topLayer;
		this.width = w;
		this.height = h;
		this.topLayer = topLayer;
		this.isMaster = isMaster;
		this.x = x;
		this.y = y;
		
		if (g) this.graphic = g;
	}
	
	override public function added():void 
	{
		super.added();
		if (isMaster)
		{
			slave = new MagicMirror(x, y, width, height, layer, topLayer, false);
			slave.master = this;
			world.add( slave );
		}
	}
	
	override public function render():void 
	{
		//super.render();
		//return;
		
		if (isMaster)
		{
			// copy position and dimensions to slave		       	
			if (slave)
			{
				slave.x = x;
				slave.y = y;
				slave.width = width;
				slave.width = height;
			}

			// store part of the buffer
			saved = new BitmapData(width, height, true, 0);
			// copy part of the screen 
			if (graphic && graphic is Image)
				saved.copyPixels(FP.buffer, new Rectangle(x, y, width, height), new Point(), Image(graphic).getBuffer(), new Point(),  true);
			else
				saved.copyPixels(FP.buffer, new Rectangle(x, y, width, height), new Point());
		}
		else
		{
			// paste stored part of the buffer
			if (master && master.saved) FP.buffer.draw(master.saved, new Matrix( 1, 0, 0, 1, x, y ));
		}
	}
}

(Zouhair Elamrani Abou Elassad) #18

Thanks for the feedback, i tried it too, still it’s not working :stuck_out_tongue:


(Zouhair Elamrani Abou Elassad) #19

This is Great man, it’ working :wink:, can please explain a bit the code, that will be great ^^


(rostok) #20

Basically you create two entities of the same class, one is master another its slave. Master is placed at lower layer because it’s render function is called first. Then it captures the screen buffer of specified area and stores it. In case graphics is assigned copyPixels function uses its alpha channel to create mask. The slave which is above is rendered afterwards and it uses reference to master to paste previously stored buffer. Two more things: I forgot to overwrite the removed function to remove slave as well and also you should assign position to x and y as integers.


(Zouhair Elamrani Abou Elassad) #21

Thanks buddy, that was very helpful :wink: