Search code examples
actionscript-3flashflash-cc

Masking in AS3 from below, keeping mask visible?


Okay, I'm pretty sure this isn't possible, but maybe someone can think of a neat combo of effects that can make this happen:

I want a [large, complex] movieclip to act as a mask to another movieclip above it, AND not disappear itself.

The point is to let a user drag various objects onto a character, and have them appear "on the skin", so only visible where the character movieclip is. Previously I've done this by creating a mask that is the same shape as the character, but this time the character MC is SO complex and dynamic that it's just not feasible.

Is there an easy way to dynamically duplicate the complex MC and make that the mask? Is there a way to make the Erase filter erase using negative space instead of content? Ideas?

Edit:

At first the answer below wouldn't work due to high pixellation and scaling issues, but I was able to resolve these by drawing the parent instead, allowing smoothing as suggested, and tinkering lots.

    // remove old bitmap to replace with new one as needed
    if(grl.tempContainer.numChildren >= 1){
        grl.tempContainer.removeChildAt(0);
    }
    // make hair disappear so only visible skin is part of mask
    grl.hair.cacheAsBitmap = true;
    grl.hair.blendMode = 'erase';
    grl.hair2.alpha = 0;
    grl.ponytail.alpha = 0;
    grl.body.braids.alpha = 0;
    grl.filters = [];
    grl.body.filters = [];

    // create bitmap copy of the character
    var bmd:BitmapData = new BitmapData(400, 600, true, 0);
    bmd.draw(this.parent);
    var clone:Bitmap = new Bitmap(bmd);
    clone.cacheAsBitmap = true;
    clone.smoothing = true;
    grl.tempContainer.addChild(clone);
    // undo the scaling effects that were on girl to revert to parent scaling/position
    clone.scaleX = 1/grl.scaleX;
    clone.scaleY = 1/grl.scaleY;
    clone.x = -(clone.width)/2;
    clone.y = -(75/(grl.scaleY));

    grl.draggieContainer5.cacheAsBitmap = true;
    grl.draggieContainer5.mask = clone;

    // reset character to look normal again
    grl.hair.blendMode = 'normal';
    grl.hair2.alpha = 1;
    grl.ponytail.alpha = 1;
    grl.body.braids.alpha = 1;
    grl.filters = filterHolder.filters;
    grl.body.filters = filterHolder.filters;

Solution

  • It sounds like you've already figured this out, but I think you would have to create a 2nd character object to use for the mask.

    Is there an easy way to dynamically duplicate the complex MC and make that the mask?

    If your mc is static, you could draw it to a bitmap first and use that as the mask.

    import flash.display.Bitmap;
    
    //Create Character
    var character:Character = new Character();
    character.x = 100;
    character.y = 50;
    stage.addChild(character);
    
    //Clone it
    var bmd:BitmapData = new BitmapData(character.width, character.height, false, 0);
    bmd.draw(character);
    var clone:Bitmap = new Bitmap(bmd);
    clone.x = character.x;
    clone.y = character.y;
    stage.addChild(clone);
    
    //Tattoo is masked to the clone
    var tattoo:Tattoo = new Tattoo();
    stage.addChild(tattoo);
    tattoo.mask = clone;
    stage.addEventListener(MouseEvent.MOUSE_MOVE, mousemove);
    function mousemove(e:MouseEvent):void
    {
        tattoo.x = mouseX;
        tattoo.y = mouseY;
    }
    

    EDIT:

    Since you're character most likely has transparent areas, you actually need to do a bit more work. In order to give your bitmap transparency, you need to set the third param to true, and the 4th param to 0.

    var bmd:BitmapData = new BitmapData(grl.body.width, grl.body.height, true, 0);
    

    In your character body, the registration point is in the center, but the draw function draws from the top-left. In order to draw correctly, you need to apply a matrix to the draw function:

    //Apply Matrix so the entire image gets drawn
    var matrix:Matrix = new Matrix();
    matrix.translate(grl.body.width/2, grl.body.height/2);
    bmd.draw(grl.body, matrix);
    
    //Draw the clone image
    var clone:Bitmap = new Bitmap(bmd);
    

    Now your bitmap should display correctly, but it will be positioned with its top-left aligned with the center of the original image. You need to move it back so it is positioned the same as before.

    var t:Transform = new Transform(clone);
    matrix = new Matrix();
    matrix.translate(grl.body.x-grl.body.width/2, grl.body.y-grl.body.height/2);
    t.matrix = matrix;
    clone.scaleX = grl.body.scaleX;
    clone.scaleY = grl.body.scaleY;
    

    Now there are 2 things you need to do in order for the mask to correctly blend with the alpha channels. You must set the blend mode to ALPHA and you must set cacheAsBitmap to true for both the displayobject and the mask object.

    clone.cacheAsBitmap = true;
    clone.blendMode = BlendMode.ALPHA;
    grl.addChild(clone);
    
    grl.draggieContainer.cacheAsBitmap = true;
    grl.draggieContainer.mask = clone;