Log In  

Hi all,

I was wondering if anyone might know how to make an alpha mask like you might do in After Effects. A good example that I've been trying to replicate with no success is the classic Star Wars transition where you have a circle that gradually gets smaller, until the screen is black.

I know that I can draw a black rectangle and a circle, then scale the circle down but I cant figure out how to cut the circle from the rectangle to create the mask.

I hope I'm making sense.. it would basically be a black rectangle with a scalable transparent circle in the middle that cuts through the black revealing whatever is drawn onto the screen below it.

Any help would be much appreciated. Thanks!!

P#67479 2019-09-10 13:55

[Edit: 2022, January 31st]

Anyone coming across this should take a look at Krystman's post on url Circular Clipping Masks. It looks in a thorough manner at different ways of achieving the effect described, and similar effects.

[Post pre-2022]

[Some of the original discussion of the original poster's idea hidden. Click 'show' for details]

These are my initial thoughts on this. Someone else may have different ideas, or come to this with pre-existing-knowledge of how to achieve this.

Expensive operations

The way you have described it - cutting a circle from the rectangle to create a mask - might be an expensive operation.

For example:

Expensive: pixel by pixel examination

You could fill the screen with a black rectangle, fill the centre with a white circle, save that to memory somewhere, put the image you actually want to show onto screen (as per normal with _draw), then go back over the memory saved mask and if the pixel you are looking at is black, pset it to the screen.

The reason you have to examine your mask pixel by pixel is that "Transparency is [only] observed by spr(), sspr() and map()" (source pico-8.txt). Because this would not use any of them, it won't observe transparency except by selecting not to put certain pixels there (well, you could repeatedly set a sprite to the next 8 by 8 chunk of memory then put that out to screen I guess).

That pixel by pixel examination will make it expensive in terms of time and commands.

Expensive: Use the sprite sheet to save the mask

Trying to use the transparency by saving the mask to the spritesheet, setting the map to the various sprites that compose the mask, then recalling it with the map command would be expensive in terms of your graphics storage space.

It is possible I have missed something in the way that pico-8 works and that someone else could tell you something in the above is wrong and demonstrate a way to create a mask - in the manner you describe - without impacting performance or graphics space.

Redefine the question

Let's re-examine the effect you're asking about.

What you're asking for is a screen sized area of black with a protected space in the middle. That space happens to be a circle. So what you want to do is draw black on the screen except for at the central circle.

Decreasing circles with a still image

This is reasonably easy to implement. It only needs 4 calls to circ per frame.

After drawing the image, we signal to _draw (via a variable called state) that we should now switch to a different function, one that doesn't use cls. That function, for the reducing circle effect, just decreases the radius of the 4 calls to circ (not circfill) by a little every frame.

I use 4 calls to cover a few gaps that otherwise would occur due to the exact positions of the pixels that get put on screen by circ, and to give it symmetry (so it's not a rectangle when it gets down to the last few pixels). This is done by having origins at {{63,63},{64,63},{63,64},{64,64}}.

The example I give here lets you experiment with different values for the reduction of the radius of the circle. Reducing 2 pixels a frame is reasonably fast and covers all the gaps; larger values will leave gaps, smaller values will be slower.

Cart #remcode_deccircint01-0 | 2019-09-16 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA

Decreasing circles with animation going on

While there is animation going on you need a slightly more complex algorithm, in that you need to put the background back on the screen every time, and overwrite it with whatever is outside your circle. You could just do this with circs again, drawing them all in from the outside of the screen to your inner circle every frame, or you could try blocking in the edge with rectfills - which will hopefully be more efficient - then the circs don't have to come in all the way from the edge of the screen.

I hope these comments give you the idea of what needs to be done. I encourage you to try it out. :-)

[Edited last couple of paragraphs to remove items - as better solutions have been posted by others below - while still keeping the fundamentals of the advice I gave. Additionally, the following:]

It's good to see that others have commented too - you seem to be getting plenty of help.

Also, inspired by your question I now have this as a work in progress:

P#67730 2019-09-16 10:12 ( Edited 2022-01-31 15:20)

I think what PLS wants is the reverse, guys.

Here is both. Watch as one scene vanishes from the shrinking iris circle and then is RECOVERED with an entirely different scene in a reverse-direction expanding iris circle.

Cart #kenaniwogi-0 | 2019-09-16 | Code ▽ | Embed ▽ | No License

P#67743 2019-09-16 16:44 ( Edited 2019-09-16 17:38)

Thank you both for the in-depth info and carts! I'm on lunch break at work right now so I'm hoping to be able to dive into the code a little more once I get home. Thank you both again for your time and answers!

DW I have a question about copying the screen to sprites using memcpy. Lets say I have every single cell in the sprite book filled up with game assets, will using memcpy overwrite those sprites?

Also this technique would not work with an active, animated background right?

P#67751 2019-09-16 18:02

I think you can, PLS. Understand the method I am using is only copying a screen that did not earlier record all the locations and elements of sprites and tiles.

In a complete program you would have those draw automatically and you would not need to use MEMCPY() or RELOAD() at all.

Let me see, try this ...

Cart #zigidaforo-0 | 2019-09-16 | Code ▽ | Embed ▽ | No License

Here you've got some bobs traveling and bouncing from left to right.

Use the following keys to experiment:

LEFT and RIGHT to decrease and increase the iris view

the (O) key to swap between sprites viewed and their animation pattern.

So in your game you would show your sprites and animation, then for an upcoming next level close the iris on it. Then press (O) (z key) to change scenes and then expand again to show an entirely different animated view.


P#67755 2019-09-16 18:48 ( Edited 2019-09-16 21:31)

Simpler version - without memcpy nor weird circle proportions!

Cart #kuriwonago-0 | 2019-09-16 | Code ▽ | Embed ▽ | No License

P#67765 2019-09-16 19:50

Freds, I'm using CIRC(). I can't get any weirder than that. :) You can change the thickness to 1 instead of 2 for the white optional visual outline or leave it out completely ... if you think it's weird.

Also last version (above an hour ago) does not use MEMCPY() but does effect of iris expansion or compression for two animated scenes.

Yours is nifty at that with sqrt() to develop an inverted circle. That's pretty neat to see.

TIME() is flawed though. If you use it, press ESC and RESUME, the clock messes up the timing breaking all timed events.

function _draw()
P#67776 2019-09-16 21:03 ( Edited 2019-09-16 21:17)

@freds72, thanks for the beautiful iris effect. Code very simple to use, too. Will be using it in an upcoming game.

P#90806 2021-04-19 17:30

[Please log in to post a comment]