Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Context Opacity Not Working as Intended #2440

Closed
robly18 opened this issue Jul 30, 2022 · 3 comments · Fixed by #2449
Closed

Context Opacity Not Working as Intended #2440

robly18 opened this issue Jul 30, 2022 · 3 comments · Fixed by #2449

Comments

@robly18
Copy link

robly18 commented Jul 30, 2022

Steps to Reproduce

Minimal working example:

import "regenerator-runtime/runtime";

import * as ex from 'excalibur';
import {vec, Actor, Color, CollisionType, Engine} from 'excalibur';



const paddle = new Actor({
x: 150, y: 150,
width: 200, height: 200,
color: Color.Chartreuse
});

const paddle2 = new Actor({
x: 170, y: 170,
width: 200, height: 200,
color: Color.White
});


class MyScene extends ex.Scene {
    
    public onPreDraw(ctx : ex.ExcaliburGraphicsContext) {
        ctx.save();
        ctx.opacity = 0.2;
    }
    
    public onPostDraw() {
        this.engine.graphicsContext.restore();
    }
}


class CustomDraw extends ex.System<ex.GraphicsComponent> {
    public readonly types = ['ex.graphics'] as const;
    public readonly systemType = ex.SystemType.Draw;
    
    private _graphicsContext? : ex.ExcaliburGraphicsContext;
    private _engine? : ex.Engine;
    
    public initialize(scene: ex.Scene) : void {
        this._graphicsContext = scene.engine.graphicsContext;
        this._engine = scene.engine;
    }
    
    public preupdate() : void {
        // Graphics context could be switched to fallback in a new frame
        if (this._engine == null) {
            throw new Error("Uninitialized ObjectSystem");
        }
        this._graphicsContext = this._engine.graphicsContext;
    }
    
    public update(entities : ex.Entity[], delta : number) {
        if (this._graphicsContext == null) {
            throw new Error("Uninitialized ObjectSystem");
        }
        const ctx = this._graphicsContext;
        
        ctx.save();
        ctx.translate(0,0);
        ctx.drawRectangle(vec(0,0),100,100,ex.Color.White);
        ctx.restore();
    }
    
}

const game = new Engine({
  width: 800,
  height: 600,
});

const thescene = new MyScene();
thescene.world.add(new CustomDraw());
game.addScene('test', thescene);
game.goToScene('test');

game.add(paddle);
game.add(paddle2);

game.start();

Expected Result

Notice the definition of onPreDraw in the previous example:

    public onPreDraw(ctx : ex.ExcaliburGraphicsContext) {
        ctx.save();
        ctx.opacity = 0.2;
    }

Then, it would be expected that everything subsequently drawn would have its opacity multiplied by 0.2, creating a collection of transparent rectangles.

Actual Result

The two rectangle actors (paddle and paddle2) are drawn with no opacity. This could be attributed to thinking "if everything is transparent, nothing is transparent", but that's where the CustomDraw system comes in. It draws a rectangle on screen using the graphics context, and indeed this rectangle is drawn with transparency.

image

Potential Cause

I didn't rife through the source code, so I may be off-base, but I suspect that somewhere in the actor drawing code there is a line which sets the opacity instead of multiplying it. I can tell this is not intended behavior, as in Graphics.ts we have the following code:

  protected _preDraw(ex: ExcaliburGraphicsContext, x: number, y: number): void {
    ex.save();
    //...
    // it is important to multiply alphas so graphics respect the current context
    ex.opacity = ex.opacity * this.opacity;
    //...
  }

My suspicion is that somewhere is something along the lines of ex.opacity = this.opacity instead.

Partial Workaround

The intended behavior (transparency inside a certain scope) can be partially obtained by using a transparent tint, e.g. ctx.tint = ex.Color.Transparent. This is only a partial solution, as it does not work at all on black objects (is this intended?) and appears to wash out the color of everything else.

@eonarheim
Copy link
Member

Hi @robly18 thanks for the issue!

Definitely seems like a bug.

I'll have some time this week to dig in, I appreciate the detailed issue and workaround👍

@eonarheim
Copy link
Member

I think I've figured out what is wrong here, I'll have a PR up soon 👍

@eonarheim
Copy link
Member

@robly18 You definitely nailed the likely code issue! We weren't respecting the current context's opacity
image

eonarheim added a commit that referenced this issue Aug 5, 2022
Closes #2440

## Changes:

- Multiplies the current context opacity in the `GraphicsSystem` to respect opacity set on the context outside of the `GraphicsComponent`
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants