HaxeFlixel / flixel

Free, cross-platform 2D game engine powered by Haxe and OpenFL
https://haxeflixel.com/
MIT License
1.97k stars 434 forks source link

FlxSprite.draw(FlxCamera.canvas) does not render the Alpha channel #2675

Open Paracosm-Daemon opened 1 year ago

Paracosm-Daemon commented 1 year ago

Code snippet reproducing the issue:

package;

import flixel.group.FlxSpriteGroup;
import flixel.util.FlxColor;
import openfl.display.Sprite;
import openfl.display.BitmapData;
import flixel.FlxG;
import flixel.FlxSprite;
import flixel.FlxCamera;
import flixel.FlxState;

class PlayState extends FlxState
{
    inline private static var screenHeight:Int = 600;
    inline private static var screenWidth:Int = 400;

    var camMain:FlxCamera;
    var camSource:FlxCamera;

    var camSquares:FlxSpriteGroup;
    var camMirror:FlxSprite;

    var squareTime:Float = 0;
    var squareSize:Int = 50;

    override public function create()
    {
        camMain = new FlxCamera();
        camSource = new FlxCamera(0, 0, screenWidth, screenHeight, 1);

        camMain.bgColor.alpha = 0;
        camSource.bgColor = FlxColor.GRAY;

        camSource.active = false;

        FlxG.cameras.reset(camMain);
        FlxG.cameras.add(camSource, false);

        camMirror = new FlxSprite().makeGraphic(screenWidth, screenHeight, FlxColor.TRANSPARENT);
        camMirror.cameras = [ camMain ];

        camMirror.screenCenter();
        add(camMirror);

        var squareX:Float = (screenWidth - squareSize) / 2;
        var squareY:Float = squareSize * 2;

        for (index in 0...3)
        {
            var camSquare:FlxSprite = new FlxSprite(squareX, squareY + (index * squareSize * 2)).makeGraphic(squareSize, squareSize, FlxColor.RED);

            camSquare.alpha = (index + 1) / 3;
            camSquare.cameras = [ camSource ];

            add(camSquare);
        }
        super.create();
    }
    override public function update(elapsed:Float)
    {
        squareTime = (squareTime + (elapsed * 4)) % (Math.PI * 2);

        camMirror.screenCenter();
        camMirror.x += Math.sin(squareTime) * 10;

        @:privateAccess
        if (camMirror != null && camSource != null)
        {
            // i basically reinvent the wheel
            var buffer:BitmapData = camSource.buffer;

            var screen:FlxSprite = camSource.screen;
            var canvas:Sprite = camSource.canvas;

            var useBufferLocking:Bool = FlxG.cameras.useBufferLocking;

            var renderBlit:Bool = FlxG.renderBlit;
            var renderTile:Bool = FlxG.renderTile;

            camSource.render();
            camSource.update(elapsed);

            camSource.drawFX();
            if (renderBlit)
            {
                if (useBufferLocking)
                    buffer.unlock();
                screen.dirty = true;
            }

            var mirrorPixels:BitmapData = camMirror.pixels;
            mirrorPixels.fillRect(mirrorPixels.rect, FlxColor.TRANSPARENT);

            switch (renderBlit)
            {
                case true:
                    mirrorPixels.copyPixels(buffer, buffer.rect, mirrorPixels.rect.topLeft);
                default:
                    mirrorPixels.draw(canvas, null, canvas.transform.colorTransform);
            }
            if (renderBlit)
            {
                camSource.checkResize();
                if (useBufferLocking)
                    buffer.lock();
            }
            if (renderTile)
            {
                camSource.clearDrawStack();
                canvas.graphics.clear();
                // Clearing camera's debug sprite
                #if FLX_DEBUG
                camSource.debugLayer.graphics.clear();
                #end
            }
            if (renderBlit)
            {
                camSource.fill(camSource.bgColor, camSource.useBgAlphaBlending);
                screen.dirty = true;
            }
            else { camSource.fill(camSource.bgColor.to24Bit(), camSource.useBgAlphaBlending, camSource.bgColor.alphaFloat); }
        }
        super.update(elapsed);
    }
}

Making a new Lime project and pasting this code into the PlayState will give the same results as seen below.


I have been attempting to make an arcade machine for a game that I am currently developing, and as of right now, I've come up with the solution of copying an FlxCamera's pixels to an FlxSprite to mask it behind the arcade machine's body. With the code above, what I do is render the camera manually, and then draw the canvas (since renderBlit is disabled) to the sprite. The issue comes with this, as the alpha channel does not seem to be rendering. I cannot seem to find a workaround to this issue, as there seem to be no other properties or functions I can use to draw the canvas with the alpha channel.

Observed behavior:

https://user-images.githubusercontent.com/98495978/202837813-b08ec8f3-7187-4be2-92f5-8a6241f11f76.mp4

Expected behavior:

https://user-images.githubusercontent.com/98495978/202837809-daf29dbb-f73e-46db-b311-58a5ce175092.mp4

For reference, here are the two side-by-side:

FlxProject_eXwgKmPEHP

The one on the left is the FlxCamera itself, and the one in the center is the FlxSprite that has copied the pixels. This is an issue I cannot seem to bypass, or fix, unless I were to try using renderBlit. I'm also unsure if this is intended behavior or a bug.

For context, here is the issue, but presented on the 'game' I have mentioned:

https://user-images.githubusercontent.com/98495978/202837949-a0c0eec6-dd44-4563-8ade-cbb1deabe193.mp4

The top-left is the camera, and the center is the sprite copying the camera's pixels. You can see that the static fades out on the camera, but not on the sprite. This is a hinderance to the effects that I want to add to this, seeing as it limits the ability to make sprites translucent. I will be grateful for any help on this issue.

DigiEggz commented 1 year ago

There's something going on here. I don't see any immediate issues with the code you posted and I was able to reproduce the problem. I dug into HaxeFlixel's rendering code a bit and I'm not sure if the problem is with HaxeFlixel or OpenFL.

Paracosm-Daemon commented 1 year ago

There's something going on here. I don't see any immediate issues with the code you posted and I was able to reproduce the problem. I dug into HaxeFlixel's rendering code a bit and I'm not sure if the problem is with HaxeFlixel or OpenFL.

Yeah, I was thinking if I should've actually put this as an OpenFL issue instead of a HaxeFlixel issue since it seems to root from that instead