(LibGDX) Changing Color of Actor - java

So, I have a class that extends Actor, and I'm trying to change the alpha value of it; objectPreview is a type of that class:
#Override
public void display() {
...
// remove previous object preview from stage
objectPreview.remove();
...
// add a translucent preview of where the object will be added
objectPreview.getColor().a = 0.5f;
stage.addActor(objectPreview);
...
stage.draw();
}
And here's my draw method of my custom Actor:
#Override
public void draw(Batch batch, float alpha) {
batch.enableBlending();
batch.draw(texture, pos.x, pos.y);
}
The display method is called every frame, and the objectPreview is an Actor that was added to stage.
However, modifying the alpha value of objectPreview does not work.
Otherwise, this works as intended, placing a preview of the Actor on the screen and clearing / redrawing it every frame.
I have also tried the setColor() method, and that does not work. Even if I change the r, g, b vaulues, nothing happens; the object is still the original Actor's texture.
Why isn't the Color of the actor changing?

When you subclass Actor, it is left up to you to apply its own color in the draw method. I am not sure why they didn't build this into the Actor class, except maybe that there are so many possible ways that color might be used, or because some Actors don't have any visuals associated with them, so applying the color would waste time.
First of all, note that the second argument passed into the draw method is parentAlpha, not alpha as you have labeled it. This is because the parent's alpha should be multiplied by the child's alpha for proper fade effects.
So your updated draw method should look like this:
#Override
public void draw(Batch batch, float parentAlpha) {
batch.enableBlending(); //You can probably remove this line*
Color color = getColor(); //keep reference to avoid multiple method calls
batch.setColor(color.r, color.g, color.b, color.a * parentAlpha);
batch.draw(texture, pos.x, pos.y);
}
/* * It would only be useful if you have some custom Actors that disable blending.
I don't think any of the built-in actors disable blending. Since many actors will
require blending, it is usually best to leave it on even for fully opaque sprites, in
order to avoid ending up with many draw calls. */
Also note that if you wanted to take advantage of Actor's already existing scaleX and scaleY fields, it would also be up to you to modify the draw method accordingly to use them.

Related

Problem with alpha blending when there are many objects in OpenGL ES 2.0/3.0

The game 3D-scene has many objects (background, asteroids, rockets):
private Background background;
private Asteroid[] asteroids = new Asteroid[NUMBER_ASTEROIDS];
private Rocket[] rockets = new Rocket[NUMBER_ROCKETS];
...
public void onDrawFrame(GL10 glUnused) {
GLES20.glEnable(GLES20.GL_DEPTH_TEST);
...
background.draw();
for (Asteroid asteroid: asteroids) asteroid.draw(); // draw all asteroids
for (Rocket rocket: rockets) rocket.draw(); // draw all rockets
...
}
Objects of asteroids and rockets use alpha-blending:
public class IceAsteroid extends Object3D implements Asteroid {
...
GLES20.glEnable(GLES20.GL_BLEND);
GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
... // draw an object with a texture
GLES20.glDisable(GLES20.GL_BLEND);
...
}
public class Rocket extends Object3D {
...
GLES20.glEnable(GLES20.GL_BLEND);
GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
... // draw an object without a texture
GLES20.glDisable(GLES20.GL_BLEND);
...
}
In general, translucency works well for the 3D-scene except that when the rockets are behind the asteroids they (rockets) are not visible. It seems that at this moment the transparency of the asteroids does not work, although the background behind the asteroids is visible. Can anyone please suggest why the rockets are not visible behind the asteroids? Thanks in advance!
Note: I tried to do this:
background.draw();
GLES20.glEnable(GLES20.GL_BLEND);
GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
for (Asteroid asteroid: asteroids) asteroid.draw(); // draw all asteroids
for (Rocket rocket: rockets) rocket.draw(); // draw all rockets
GLES20.glDisable(GLES20.GL_BLEND);
But this did not solve the problem.
Solution: On the Rabbid76 advice, I sorted all the translucent objects in order from the back to the front:
Comparator<Object3D> comparatorByZ = (objectA, objectB) -> {
Float z1 = objectA.getZ();
Float z2 = objectB.getZ();
return z1.compareTo(z2);
};
...
background.draw();
Collections.sort(transparentObjects, comparatorByZ);
for (Object3D object3D: transparentObjects) object3D.draw();
In my case, that was enough.
You cannot have both, Blending and the benefits of the Depth Test. You have to do draw the scene in 2 passes. First draw the opaque objects with depth test on and blending off, then draw the transparent objects with depth test on but no writing to the depth buffer, and blending on, in sorted order from the back to the front. See Transparency Sorting.
enable depth test and disable blending
GLES20.glEnable(GLES20.GL_DEPTH_TEST);
GLES20.glDepthMask(GLES20.GL_TRUE);
GLES20.glDisable(GLES20.GL_BLEND);
draw opaque objects
enable depth test, disable writing to the depth buffer and enable blending
GLES20.glEnable(GLES20.GL_DEPTH_TEST);
GLES20.glDepthMask(GLES20.GL_FALSE);
GLES20.glEnable(GLES20.GL_BLEND);
draw transparent objects in sorted order from the back to the front.
If the transparent objects are not sorted, then the transparent objects will still be arranged correctly, in compare to the opaque objects, but the transparent object itself may not be arranged correctly. When you use blending, then the order matters. The blending function is not commutative, the result is different if the order of covering transparent objects is changed.

Java game 2D overlapping shadows with Swing

I am currently developing a 2D Java game using Swing as my primary drawing component. Every object has a shadow (BufferedImage) but every shadow overlaps other shadows. Is it possible to only have the shadows not overlap each other? Because I still want the shadows to draw over the player if the object is beneath it, and not if the object is above of the player. Here is a picture for clarity:
I have looked at alpha compositing, I guess I need Source Out? I also thought of having all the shadows (with no transparency) draw on one layer and then draw it with transparency but then it won't draw over the player and other objects like before.
I have a Draw object which is a JPanel and overrides the paintComponent method. Within this method I draw the floor of the current room and then I iterate over the list of objects that belongs to the current room and call each objects' draw method to draw everything.
The object draw method:
public void draw(Graphics g) {
if (visible && checkInScreen()) {
// The required drawing location
int drawLocationX = getX() - globalCameraX;
int drawLocationY = getY() - globalCameraY;
if (shadow) {
g.drawImage(shadowImages.get(imageIndex),
drawLocationX + shadowOffset.x + (getImageWidth()/2),
drawLocationY + shadowOffset.y, null);
}
g.drawImage(images.get(imageIndex), drawLocationX, drawLocationY, null);
//Collisionbox
if (SHOW_COLLISION_BOXES){
g.setColor(Color.WHITE);
g.drawRect(drawLocationX + getCollBoxX(), drawLocationY + getCollBoxY(), getCollBoxW() - getCollBoxX(), getCollBoxH() - getCollBoxY());
}
}
}
My apologies if this question has already been asked but I couldn't find something similar like this.
What I would do to solve this is to have a shadow-layer bitmap. By which I mean:
have your shadow textures saved as a 2D array of boolean values (representing the position of a shadow pixel).
What you can do with this is to then logically or the shadow maps together to create a single layer, which can be layered behind the tree textures to create the shadows.
You may want to change the booleans to floats to represent the colour/intensity of the shadow, then have a larger calculation to merge the shadows together.
The below ShadowMap class is used to store the data for each shadow:
class ShadowMap {
public int xPos, yPos;
public boolean[][] array;
public ShadowMap(int xPos, int yPos, boolean[][] array) {
this.xPos = xPos;
this.yPos = yPos;
this.array = array;
}
}
The ShadowLayer class creates a 2D array for the entire screen, containing if a shadow is present for each pixel:
class ShadowLayer {
public static boolean[][] array = new boolean[SCREEN_WIDTH][SCREEN_HEIGHT];
public static makeNew(ShadowMap[] shadows) {
for (int x = 0; x < SCREEN_WIDTH; x++) {
for (int y = 0; y < SCREEN_HEIGHT; y++) {
array[x][y] = false;
}
}
for (ShadowMap map : shadows) {
for (int i = 0; i < SCREEN_WIDTH; i++) {
for (int j = 0; j < SCREEN_HEIGHT; j++) {
// Logical or such that the pixel at (x, y) has a shadow
// if any shadow map also has a shadow at pixel (x, y)
array[i + map.xPos][j + map.yPos] |= map.array[i][j];
}
}
}
}
}
Using this ShadowLayer class, you just need to darken each pixel on the screen if the ShadowMap has a shadow on the same pixel:
public static Color ajustPixelForShadows(int x, int y, Color pixel) {
return ShadowMap.array[x][y] ? pixel.darken() : pixel;
}
I admit I'm not familiar with Swing so I'm not sure it is possible with that specific interface but the below solution could be used in a variety of 2D graphics engines.
You'll need an off-screen "shadow layer" to draw to that matches the screen dimensions. Initialize the shadow layer to being pure white.
For each object you draw from back to front (y-sorting), do the following, in order, with the shadow layer:
Draw the object's shadow shape in a single solid dark grey color to the shadow layer
Draw the object itself to the shadow layer as a pure white sprite (i.e. all non-transparent pixels in the object's bitmap are white)
Of course, also draw the object itself to the screen.
Then, once all objects have been drawn to both the screen and the shadow layer, draw the shadow layer to the screen using multiply blending. The multiply blend guarantees shadows will darken whatever they are drawn over (unlike alpha blend which, with very light shadows, could potentially actually lighten the colors they are drawn over). It will also make the pure white portions of the layer do nothing, which is what you want.
The above steps mean that after each object draws a shadow, it erases any shadows that would be underneath it in the final scene when it draws itself in white to the shadow layer. Therefore it won't cast a shadow on itself, and objects won't cast shadows over other objects that are technically in front of them.
Objects will still cast shadows onto other objects that are behind them as you wanted, since any parts of the shadow that haven't been erased by an overlapping object will still apply (or if they are erased, will be potentially re-drawn by a later object). And, since you are drawing the shadows as a single non-translucent color to the shadow layer, multiple shadows overlapping won't affect each other either, which was of course the main point.
You could modify this technique depending on what you have available. For example, instead of white you could use a fully transparent shadow layer initially and an "erase" blend mode [(src * 0) + (dst * (1 - srcAlpha))] to draw the objects that erase shadows underneath them. You could then use alpha instead of multiply blend if you prefer for drawing the shadow layer to the screen.

LibGDX Scene2D: How to apply actions to actors which are not located at bottom left corner?

Here is the code from the Main class:
MyActor myActor = new MyActor;
moveAction = new MoveToAction();
moveAction.setPosition(600f, 750f);
myActor.addAction(moveAction);
And here is the code from the MyActor class
#Override
public void draw(Batch batch, float alpha){
batch.draw(texture,getX(),getY());
}
If it is written like this, the action will work but the texture starting position is in the bottom left corner and if I replace getX() and getY() with other coordinates, the actions will not work and the texture will just stay at the same position. So how exactly I set starting position for the actor?
Actors have their own position values. You probably know this considering you use them to draw their texture at the correct location. Therefore, what you need to do is set the Actor's initial position when you create it. Something like this:
MyActor myActor = new MyActor;
myActor.setPosition(100, 100);
Now, if you want to give the actor an action to move somewhere else, instead of creating a new MoveToAction, use the Actions convenience methods like this:
myActor.addAction(Actions.moveTo(600, 750));
This will move the Actor to that position instantaneously, so if you want the Actor to move there in a certain period of time you would have to write that line like this:
myActor.addAction(Actions.moveTo(600, 750, duration));
duration is a float that holds the number of seconds you want the actor to take to get to that specified location.

How do I draw an interactive Graph/Lines in Java?

Solved.
I had a question before but it was very badly posted so here it goes again, according to better rules.
I want to create some style of a graph such as this image:
.
It's based on a physics law, Snell's Law. As of now I've managed to paint the graph it self with a basic Line2D.Double which you can see here (Line.java). Then all I need to do is, in the interface class, add the lines to the array in that class as so:
LinesArray.addLine(150 , 25 , 150 , 275);
And every time it adds a new one, it repaints as you can see in the code sample.
But the problem is that I have no idea how to make this interactive. I wanted to make it interactive, as in that you could actually move those lines and at the same time you move the first line, the second would move accordingly to the Snell's Law formula, which is:
n1 * sin( a1 ) = n2 * sin ( a2 )
Considering that a1 is the first (left) angle, and a2 the second (right) angle in the first image posted.
A perfect example of what I'd hope to achieve is this one.
And if interactive movement is too hard (I'm on a 2 days schedule), this example is also a possibility.
Correct me if I'm wrong but for the second one, all that I'd need to do is calculate the mouse's coordinates and draw and calculate everything from there.
Here (menu_ui.java) is my interface class, in which the method I'm currently working with the lines is "menuSnell()" and here (Snell.java is my Snell class which contains the logic. Apologies for portuguese comments but it's fairly simple code which you don't really need comments to understand, plus I've separated it into readable methods.
So basically, my question is how do I make those lines interactive in the way I've described above.
Thanks!
I am not a graphic expert, but I had similar work a long time ago. I had an object that I need to repaint. I created my own JPanel, which holds my objects that should be paint. Whenever something changed, I call repaint method on JPanel. It looked like this
http://sourceforge.net/p/scribbler-cvut/code/132/tree/Tuzka/src/cz/cvut/scribbler/panel/RewritableGlassPane.java.
private LinkedList<ColoredArea> background = new LinkedList<ColoredArea>();
/**
* Vykreslí všechny položky v senamu vykreslených obrazců
* #param g2d grafika k vykreslení
*/
public void paintShape(Graphics2D g2d) {
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
for (ColoredArea area : background) {
area.fill(g2d);
}
if (mouse != null && !block) {
g2d.setColor(mouse_color);
g2d.draw(mouse);
}
if (point!=null){
SetPointsDialog.paintPoints(point, maxPoint, parent.getChildsSize(), g2d);
}
}
#Override
public void paint(Graphics g) {
paintShape((Graphics2D) g);
}
#Override
protected void paintComponent(Graphics g) {
paintShape((Graphics2D) g);
}
Everything I need to paint was stored in background variable. When something I LinkedList changed, I invoke repaint () method on the window.
I have a full source code store here: http://sourceforge.net/projects/scribbler-cvut/ but it was my long term project, so it is a little bit big.

How to paintComponent to display objects in Java?

Okay, so I have a GameField class, and a GameObject class and a Panel class.
The GameObject class describes an object, which has an x and y position, width and height, and x and y direction (in which it is currently moving). The GameField class has a few different instances of these objects, some stored by themselves, and some stored in primitive arrays.
The Panel class is supposed to display these objects on the screen. I used JPanel for this.
However, when it comes to actually displaying them on the screen, I'm a bit lost. I need to implement a function called paintComponent(Graphics graphics), which takes in a Graphics object.
To start, I want to display all the objects on the screen, and set their colour. Their size, position, etc. are handled elsewhere. How can I use these attributes to set the actual objects to have a size, position and direction?
I may need to override the paintComponent function to display all the objects in GameField.
If you could help me out with some code, that'd be great.
I'm not quite clear on what you mean by "their size, position, etc. are handled elsewhere". For now, let's assume that you have approximately the following structure (fields and other methods ommitted for clarity) :
class GameObject {
java.awt.Color getColor() { ... }
java.awt.Point getPosition() { ... }
java.awt.Point getDirection() { ... }
java.awt.Dimension getSize { ... }
}
class GameField {
List<GameObject> getGameObjects() { ... }
}
class Panel extends JPanel {
private GameField getGameField() { ... }
#Override
public void paintComponent(Graphics g) {
// this is where the GameObjects must be painted
}
}
The paintComponent method is responsible for the screen representation of the Panel class. If you override it, you have just won that responsibility from it. Luckily, drawing is - if tedious - rather simple. You asked about that Graphics parameter. Very simply put, it is set for you by the magic of Java and gives you a toolbox to use for drawing.
First, you will want to have a clean slate whenever the panel is repainted. You cannot delete anything once it is painted, but you can easily paint the entire panel in a background color of your choice.
g.setColor(Color.white); // everything that is now painted will be white
g.fillRect(0, 0, getWidth(), getHeight()); // fills the entire area with the set color
Now, for each GameObject you have, let's place rectangle in the objects' defined color and size on the screen, with it's center on the object's position.
for (GameObject object : getGameField().getGameObjects()) {
g.setColor(object.getColor());
g.fillRect(object.getPosition().x - (object.getSize().x / 2), object.getPosition().y - object.getSize().y / 2, object.getSize().x, object.getSize().y);
}
The fillRect method requires the first two arguments to be the top-left corner of the rectangle. So to have it centered on the object's position, we subtract half the size from the position for the x and y values respectively. Now you have, for every GameObject, a rectangle of the right diameter in the object's color at the right position.
You should read up on the javadoc on java.awt.Graphics to find out how to draw other stuff, maybe image sprites or lines for the direction or something. It is cumbersome but doable.

Categories