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.
I was able to achieve something similar to what I'm looking for by drawing the black layer in between when all game entities are drawn and when the HUD is drawn. I draw it with 80% opacity, and then I draw my flashlight polygon with 5% opacity and blend mode SRC_OVER. It took a lot of playing around with the different blend modes, but MULTIPLY was definitely not the way to go.
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:
Related
first of all I have scoured Google and SO for this answer, finding only how to change the actual pixels to be of a certain alpha value, which would be incredibly slow or actually making a part of the BufferedImage completely transparent via the use of lwg2.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR)). This is the exact functionality I need, however, I need to have the value to be less than 1f, which you cannot do with this specific instance of AlphaComposite.CLEAR.
What I want this implementation for is to make a wall inside my 2.5d game become transparent when the player goes behind it, like so:
The logic behind my game is that the terrain is one BufferedImage which is only updated when called, and then having the rest of the walls, etc, being drawn onto another BufferedImage where entities are also drawn, so the opacity transformation would only affect the trees (or walls).
This is the code I am using atm, but as I said I don't want the circle that I am drawing to make a part of the image completely transparent, but only slightly (about 50%):
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, 0.5f));
g2.fillOval(x - (int) (TILE_WIDTH * 1), y - (int) (TILE_HEIGHT * 1.5), TILE_WIDTH * 2, TILE_HEIGHT * 3);
(The 0.5f in the AlphaComposite constructor does nothing).
The reason I need this to be efficient is because I am updating this image 30 times a second, so efficiency > quality.
So, I ended up solving the issue by not manipulating the image directly via making a part of the image translucent, but manipulating the opacity of the images I am drawing with. As #user343760 and #NESPowerGlove mentioned, I could just make the assets I am using translucent when the player is behind it. Since I am using a underlying grid array to back my game, I could do this by working out if the tile.x - 1 == (int) player.x and tile.y - 1== (int) player.y. In isometry, this meant that the player was on the tile directly above it in our perspective. Then I had to solve the issue if the wall.z is bigger than 0 or 1, hence having the problem where a tile 5 blocks "bellow" the player could obstruct him if the walls extended z = 5 above the tile. For this problem, I implemented the following solution:
for(int i = 0; i < wall.getAsset(1f).getHeight()/TILE_HEIGHT; i++) {
if((tile.x - i - wall.z == (int) world.player.getX() && tile.y - i -wall.z == (int) world.player.getY())) {
lwg2.drawImage(wall.getAsset(0.5f), x, y, this);
}
}
This also ensures that the image is transparent even if the player is "above" the tile "above" the tile where the wall is situated, in terms of the image extending above that limit. I have fixed this via using the for loop which looks above for i number of times, depending on the image.height/tile_height, which is an universal constant.
If you require to make a part of the image transparent, I have not found solutions which would work fault free, except for manipulating the pixels in the low-levels of BufferedImage. If you also want to erase a part of an image directly, use the code g2.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR)); and draw as you would normally. Remember to switch back to a normal composite via g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));.
You could also draw with a certain opacity in the first place using the Composite g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, opacity));, where opacity is a float with values from 0f to 1f, 0f being completely transparent and 1f being completely opaque.
I hope this helped anyone out there. If you find a better way of doing this, please leave a comment for future readers.
This is what my solution looks like :):
I wanted to ask if you can use box2d lights so you can see only objects that are in the lights area. For example i have a flashlight and only want to see game objects in the light. I managed to do something like this but the problem is that the sprites of the game objects lose their color intensity because I render lights on top of sprites and the game itself doesn't look good because of this (even though it is the effect that i want).I used box2d bodies with user data containing sprites. I can't figure any way out. Is there any proper way to use box2d lights library to make these objects visible and with their real color? (I am setting lights to X-rays to do this; also I am using it with libgdx in java).
It may be because the default setting is to not use diffuseLight. You have to set rayHandler.useDiffuseLight(true).
Libgdx and Box2DLights - too bright + colors grayed out
I write simple game with libGdx. I have a hero, which always is in screen center and I must move my background sprite (or region?) to make move illusion. But my background sprite isn't infinity.
How can I create illusion of seamless infinity world?
Of course I can add several background sprites to try to cover all empty space of screen. But I must to draw out of the sceen a lot of all another objects: Houses, monsters, others heroes, etc. So I have a second question:
When I try to draw other object (a lot of objects!) out of the screen, how badly it affects memory? How to draw it correctly?
I know that OrthographicCamera in libgdx draw only viewportWidth-viewportHeight area. If it's right, then I must to move my camera and all my sprites too. I think it's not correctly.
How can I render infinity world in libgdx with OrthographicCamera?
How can I create illusion of seamless infinity world?
Create a tile background. Tile background means that if it was besides or top or bottom of itself, the edges of sticking line will not be visible to viewer.
To do this open your background image in photoshop and go to Filters > Other > Offset.
Set the offset filter to offset the background to center then try using photoshop tools to hide the edges (the + shape in image). Now again go to offset and return to 0, 0 and save your background.
When I try to draw other object (a lot of objects!) out of the screen,
how badly it affects memory? How to draw it correctly?
I have checked this and that was not much fps loosing on my test. So don't worry about it.
How can I render infinity world in libgdx with OrthographicCamera?
Move camera where-ever you want any x, y. Every time see where is camera and calculate needing tile backgrounds to draw (for example every time draw 3x3=9 backgrounds sticking together).
I am currently working on making a Java videogame with a few friends. It is a top-down RPG that is drawn with tiles of different types. I just added a day/night cycle to the game, and found that using transparent colors to draw to the screen is very slow. I recently learned how to make colors transparent by adding an alpha-value after the RGB values, and it works very well, but as I said, it is slow. I tried switching between transparent colors and opaque colors and discovered that the lag is because of the transparency. Basically, after a tile is drawn, it calls the method for getting the desired transparency factor, makes a new color, and calls g.fillRect() over the tile. This is done for every tile on screen.
I would like to know if there is another way to have a transparent overlay without just drawing the overlay over the whole window, because we want to be able to add light sources that replace the transparency for specific tiles. The tiles are drawn to the screen from a spritesheet. Any suggestions are welcomed.
I'm currently developing a game for Android.
As part of the game, I need to have a circle that increases and decreases in size, but not with a simple single-colour paint fill.
Instead, is there a way I could dynamically draw a circle onto a canvas then fill it with a given image (probably another bitmap - tiled, in order to fill the image)?
Many thanks in advance,
Will.
You want to do a simple Canvas.drawCircle(), but using a Paint with a BitmapShader.