Creating a "shadow" layer for the canvas in JavaFX?

Viewed 431

I'm currently remaking a game I made several years ago in Swing/AWT, this time using JavaFX. My current dilemma is that the original game had a "flashlight", in which I first created a blank black layer which I would then create a polygon on and subtract it from that layer using a blend mode. From there, the layer was drawn with transparency to give the appearance that everything was dark and the player had a flashlight.

I'm having trouble figuring out how to implement this in JavaFX. I figured I could create a blank black image and that there would be someway to create a GraphicsContext out of that and I could set a blend mode to subtract from the image, but images provide no support for this type of rendering in JavaFX, and in fact, WritableImage is a separate class which only allows the use of a PixelWriter, where I have to set each pixel manually.

I know there's probably plenty I still don't understand about JavaFX, because I've only made a few applications with it before. Does anyone have any recommendations about how to go about implementing this feature? It would be nice if I could make it look better than the original.

Here's a picture from the old game for reference.

Generic Zombie Shooter

2 Answers

I created two Canvas, the "bright-world"-canvas and a second "night-overlay"-canvas with just a night-grey rectangle and light-images. If I want night in my game I add the night canvas with Blendmode.MULTIPLY.

Here I create the night-overlay: (simplified)

private void renderLightEffect()
{
        ShadowMaskGc.setFill(shadowColor);
        ShadowMaskGc.fillRect(0, 0, CAMERA_WIDTH, CAMERA_HEIGHT);
        for (Sprite sprite : passiveCollisionRelevantSpritesLayer)
           {
           Image lightImage = lightsImageMap.get(lightSpriteName);
           ShadowMaskGc.drawImage(lightImage, sprite.getPositionX() + sprite.getHitBoxOffsetX() + sprite.getHitBoxWidth() / 2 - lightImage.getWidth() / 2 - camX, sprite.getPositionY() + sprite.getHitBoxOffsetY() + sprite.getHitBoxHeight() / 2 - lightImage.getHeight() / 2 - camY);
           }

}

After creating the night-layer a add it to the Pane every render cycle:

root.getChildren().clear();//root-Pane
root.getChildren().add(worldCanvas);//The world with all sprites
//LightMap
if (shadowColor != null) {//To switch on/off night
    renderLightEffect(currentNanoTime);//update of the positions of the light points
root.getChildren().add(shadowMask);//grey Canvas with some light points
shadowMask.setBlendMode(BlendMode.MULTIPLY);//Here is the magic merge the colors
    }

EDIT: Later I realized, that the two-canvas approach is bad for performance. I recommend to draw the shadow layer directly on the world canvas.

Result:

enter image description here

Related