Highlighting a tile with transparency - java

I am making turn based strategy based on a tiled map. My map contains two layers - first one is visible all the time and is used as a background, the second one is used for highlighting the tiles. Highlighted tiles shows possible movement for the player. For now the progress looks like this:
My border boxes pic
To make a highlighting possible I use this for loop:
for(int s = 0;s<7;){
for (int i = 0;i<7;i++) {
if ((inField(clickx + i,clicky + s)) && !(tileWithPlayer(clickx + i,clicky + s)))
{
highlight_layer.getCell(clickx + i, clicky+s).setTile(mark);
}
}
s++;
}
In this I use one tile from second layer of the map to make a border for a specific tiles. For now this tile is only 2px border of 32x32 transparent tile. To make highlighting disappear, I use similar for loop, where I set the tile to null. Selecting/deselecting tiles works great, but it is probably not the most efficient way to do this. I would like to exchange the blue border boxes into colored transparent boxes, something like in Advance wars during the movement, or as it can be seen below:
Desired look pic
I tried to make a semi transparent tile on the second layer in tiled, but without luck. It could be nice if there is some possibility to apply only semi-transparent color on the specific tiles, without using the second layer tile. So, my question is - how can I exchange those ugly borders, into fully colored, (semi)transparent tiles, to achieve similar result as in above picture?

Well...okay. I'm not an expert with Tiled. I've never really worked with it much but doesn't it support PNG tilemap imports? As in...make a tile texture that is semi transparent and then use that in your map on the second layer and only activate the ones that you really need?
Alternatively you could do it programmatically. Where for every tile that you need to highlight you just create a new TextureRegion with the coortinates of your tile:
float x = tileWidth * col;
float y = tileHight * row;
final TextureRegion region = Assets.getRegion(...);
This way you load a texture region to be displayed at the right position whenever you need it to. You could even (if the range is never going to change) store them in a collection and then with every move shift their coordinates by one in whatever direciton.
This would at least be my approach, not really knowing anything about Tiled.
I have used a HexagonTile map before, however I had to write my own implementation (based on LibGDX). Here is the code, in case it helps you to further understand the idea: https://github.com/2RSoftworks/interstellarempires/blob/master/IEMaster/IEClient/src/main/de/r2soft/empires/client/maps/hex/R2HexMapRenderer.java#L167
Hope this helped.

Related

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

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:

Setting a spherical area in a BufferedImage to be a certain opacity efficently

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 :):

Rotating a Sprite in Java

While working on Projectiles I thought that it would be a good idea to rotate the sprite as well, to make it look nicer.
I am currently using a 1-Dimensional Array, and the sprite's width and height can and will vary, so it makes it a bit more difficult for me to figure out on how to do this correctly.
I will be honest and straight out say it: I have absolutely no idea on how to do this. There have been a few searches that I have done to try to find some stuff, and there were some things out there, but the best I found was this:
DreamInCode ~ Rotating a 1-dimensional Array of Pixels
This method works fine, but only for square Sprites. I would also like to apply this for non-square (rectangular) Sprites. How could I set it up so that rectangular sprites can be rotated?
Currently, I'm attempting to make a laser, and it would look much better if it didn't only go along a vertical or horizontal axis.
You need to recalculate the coordinate points of your image (take a look here). You've to do a matrix product of every point of your sprite (x, y) for the rotation matrix, to get the new point in the space x' and y'.
You can assume that the bottom left (or the bottom up, depends on your system coordinate orientation) of your sprite is at (x,y) = (0,0)
And you should recalculate the color too (because if you have a pure red pixel surrounded by blue pixel at (x,y)=(10,5) when you rotate it can move for example to (x, y)=(8.33, 7.1) that it's not a real pixel position because pixel haven't float coordinate. So the pixel at real position (x, y)=(8, 7) will be not anymore pure red, but a red with a small percentage of blue)... but one thing for time.
It's easier than you think: you only have to copy the original rectangular sprites centered into bigger square ones with transparent background. .png files have that option and I think you may use them.

How can I get a certain RGB value from a canvas painted with LinearGradientPaint?

I have used the LinearGradientPaint class to draw a rectangle dynamically filled with user-defined colors. This happens by overriding the paintComponent method.
Here is how it looks like:
.
You can see the small thumbs/ handle on top. The user can move these around, delete them, and add new ones. They can also change the color. As a result the user can completely customize how the gradient looks like. This works just fine so far, no issue.
What I need now, and I tried searching for this info, is to get RGB-values anywhere on this gradient.
I only know the x-amount of colors that LinearGradientPaint uses to generate the gradient. I know at what point (fraction) these colors are located (the number below the gradient box, corresponding with the 'thumbs' on top.
Is there anyway to get the colors in between the RGB-values which are used to generate the gradient? In my example above I mean the darkened red or green areas.
If this really is a linear gradient between new Color(r1,g1,b1) and new Color(r2,g2,b2), then the colour at x, where 0 <= x <= 1 is
new Color((int)(r1*(1-x)+r2*x),(int)(g1*(1-x)+g2*x),(int)(b1*(1-x)+b2*x));
Of course, I have no idea whether this is the formula that is actually used inside LinearGradientPaint - but it ought to be something equivalent to this.
A way you might be able to do this, is to create a 1 pixel high BufferedImage of the same width of your component, render the gradient to it and simple use something like BufferedImage#getRGB.
This will return a int packed color value, which you can then use Color(int) to return a Color object, which makes it easier to extract the color components of the pixel.
Of course, this would all be easier if you used the BufferedImage as your primary output as well, then you would only have to create it once and because you'd be updating the image so it could be rendered to the screen, it would also be up-to-date

how to increase opacity in gaussian blur

I have a Java application where I need to draw text on top of an image. The text, the font, and the image are all determined at run time. The text needs to look nice, yet be readable (sufficiently contrastive) on top of the image.
To meet these requirements, I create a drop shadow. This is done by drawing the text in opaque black, on a blank/transparent BufferedImage, then applying a Gaussian blur filter. I then draw the text again, in opaque white, on top of the drop shadow. So I have opaque white text, with a black blurred shadow around it that quickly fades to full transparency. I can then draw this image on top of the background image.
The problem I'm trying to solve is that the drop shadow seems too transparent. So against bright, busy backgrounds, it doesn't give the white text enough separation.
So how to increase the opacity of the shadow? I've tried increasing the radius of the gaussian blur, and that makes the shadow wider, but doesn't make it more opaque.
The code I'm using is based on this DropShadowDemo by Romain Guy. I use his createDropShadow() and gaussianBlurFilter(). But instead of painting the drop shadow and the text separately during paintComponent(), I draw them both onto a BufferedImage in advance; and I draw this single BufferedImage on top of the background during paintComponent(). Maybe that's my problem? But I don't see how that would decrease the opacity of the shadow. I'm not using g2.setComposite() during paintComponent().
I've looked at adjusting the opacity of the drop shadow using some kind of BufferedImageOp, such as a LookupOp. But it seems like a lot of work for a simple adjustment (creating four arrays of numbers, I guess). I don't think a RescaleOp would work, since I want the result alpha to fall in the same range (0 to 1) as the source alpha. If I could specify a BufferedImageOp that sets new alpha = sqrt(old alpha), or something like that, that would be ideal. But I don't know an easy way to do that.
Details of the code can be seen here:
ShadowedText.java (creates the text-with-drop-shadow image)
SetBeforeMe.java (implements paintComponent() that draws the image)
I would include relevant code blocks here, but it seems like the relevant amount of code is too big (wall of code)... might as well just give links to the source files.
Update
It looks like Change the alpha value of a BufferedImage? would be a way to change the opacity of the drop shadow... basically recalculating the alpha value of each pixel, one by one. TBD: whether it's portable (to 64-bit machines, e.g.), and whether it's fast enough. If I do a = sqrt(a) or a = sin(a * pi * 0.5) on every pixel (thinking of a in the range 0 to 1), will that be slow? I would be happy to know if there's a simpler way that takes advantage of available optimizations, like the built-in BufferedImageOps presumably do. Maybe the answer is building arrays for LookupOp after all. Anybody know of some example code for that?
Final update
Solved using a LookupOp; see code below.
Below is the code I ended up with to make a BufferedImage more opaque. I decided to go ahead and use a LookupOp, rather than a potentially unportable and slow loop over getRGB / setRGB on each pixel. The work to set up Lookup arrays wasn't so bad.
/* Make alpha channel more opaque.
* Modify the alpha (opacity) channel so that values are higher, but still
* continuous and monotonically increasing.
*/
private static void adjustOpacity(BufferedImage shadowImage) {
// Use a lookup table with four arrays;
// the three for RGB are identity arrays (no change).
byte identityArray[] = new byte[256];
for (int i=0; i < 256; i++)
identityArray[i] = (byte)i;
byte alphaArray[] = new byte[256];
// map the range (0..256] to (0..pi/2]
double mapTo90Deg = Math.PI / 2.0 / 256.0;
for (int i=0; i < 256; i++) {
alphaArray[i] = (byte)(Math.sin(i * mapTo90Deg) * 256);
}
byte[][] tables = {
identityArray,
identityArray,
identityArray,
alphaArray
};
ByteLookupTable blut = new ByteLookupTable(0, tables);
LookupOp op = new LookupOp(blut, null);
// With LookupOp, it's ok for src and dest to be the same image.
op.filter(shadowImage, shadowImage);
}
It seems to work (though I haven't taken before-and-after screenshots for comparison).

Categories