How to get a region of rotated Actor in libgdx - java

In a small game I am developing using libgdx, I have spears as game objects.
I implemented them using Scene2D, as a subclass of Actor. The spears can be rotated by multitudes of 90° . I want to have just the "speartip" part of my image as a part that "hurts" the Player, the shaft shall be harmless. So when I construct my Spear object, I also construct 2 rectangles that cover the tip and the shaft of the spear. But when my actor gets rotated using setRotation() , the rectangles obviously don't, cause they aren't "attached" to the spear object.
Do you have some suggestions on how to handle this kind of stuff? Code below.
public TrapEntity(Texture texture, float pos_x, float pos_y, float width, float height, Vector2 center,
float rotation, Rectangle hurtZone, Rectangle specialZone) {
super(texture, pos_x, pos_y, width, height, center, rotation);
this.hurtZone = new Rectangle(hurtZone.getX(), hurtZone.getY(), hurtZone.getWidth(), hurtZone.getHeight());
this.specialZone = new Rectangle(specialZone.getX(), specialZone.getY(), specialZone.getWidth(), specialZone.getHeight());
}
And the render method in the same class. I use it to render the bounds of the "hurtZone" rectangle:
#Override
public void draw(Batch batch, float alpha){
super.draw(batch, alpha);
batch.end();
sr.setProjectionMatrix(batch.getProjectionMatrix());
sr.setTransformMatrix(batch.getTransformMatrix());
sr.begin(ShapeRenderer.ShapeType.Line);
sr.setColor(Color.RED);
sr.rect(hurtZone.getX(), hurtZone.getY(), hurtZone.getWidth(), hurtZone.getHeight());
sr.end();
batch.begin();
}

Rectangles can't be rotated, I had to do a similar thing and struggled with finding a solution. The best solution I have found yet is to use a Polygon. You would do something like this;
//Polygon for a rect is set by vertices in this order
Polygon polygon = new Polygon(new float[]{0, 0, width, 0, width, height, 0, height});
//properties to update each frame
polygon.setPosition(item.getX(), item.getY());
polygon.setOrigin(item.getOriginX(), item.getOriginY());
polygon.setRotation(item.getRotation());
Then, to check if a point is within the rotated Polygon use;
polygon.contains(x, y);

Related

Why Do Graphics Get Smaller Using Affine Transform in Java

I'm pretty new to Affine Transformations and am finding a few things quite frustrating..
I've been working on a personal project that draws a laser (think Star Wars) on the screen. I use the RadialGradientPaint class to draw the glow for the laser, and then use scale and rotate Affine Transformations to change the oval's shape and turn the laser in a specific direction.
When I try to rotate the graphic (without the scale), it appears to shrink it, and I'm not sure why this is. I've tried looking this up to no avail. Perhaps I misinterpreted the Java Doc on this class. Why is this?
I'm also curious as to how I can get the transformed object to move to the same location as an un-transformed graphic. Both graphics move to different places even with the same coordinates. I know this might be due to an Affine Transformation shifting the whole graph, and that I might have to translate the transformed graphic to get it to the same exact place as the un-transformed graphic, but I'm not sure how to approach this.
Here is the portion of my code that draws the laser:
public void render(Graphics g) {
// If the graphic is visible on the frame, draw it
if(onScreen()) {
Graphics2D g2d = (Graphics2D) g;
// Preserves the original presets of the program's graphics
AffineTransform ogTransform = g2d.getTransform();
Shape ogClip = g2d.getClip();
Point2D center = new Point2D.Float(x, y);
float[] dist = {0.3f, 0.9f};
Color[] colors = {new Color(1.0f, 1.0f, 1.0f, 0.9f), new Color(0.0f, 1.0f, 0.0f, 0.1f)};
g2d.setPaint(new RadialGradientPaint(center, radius, dist, colors));
AffineTransform scaleTransform = AffineTransform.getScaleInstance(scaleX, scaleY);
AffineTransform rotationTransform = AffineTransform.getRotateInstance(angle, x + width/2f, y + height/2f);
// Transforms the shape to look more like a laser
rotationTransform.concatenate(scaleTransform);
g2d.setTransform(rotationTransform);
// Draws a "cut-out" of the rectangle
g2d.setClip(new Ellipse2D.Double(x - radius, y - radius, radius * 2, radius * 2));
g2d.fillRect(x - radius, y - radius, radius * 2, radius * 2);
// Default transformation and clip are set
g2d.setTransform(ogTransform);
g2d.setClip(ogClip);
// Outlines where the transformed graphic should be
g.setColor(Color.orange);
g.drawRect(x - 10, y - 10, 20, 20);
}
}
Any help is appreciated!

libGDX Draw viewport only partly while cutting off the rest

This problem seemed very obvious for me to solve, but whatever I try, it doesn't work. What I'm trying to do is to incorporate a mini-version of my PlayScreen in a ScrollPane as a tutorial where you can read text and try it out immediately.
Because I didn't find any better solution to add this to the Table inside the ScrollPane, I edited the draw() method of the PlayScreen to take the ScrollPane.getScrollPercentY() and offset the camera of the PlayScreen accordingly.
What I want to do now is to only render only part of the viewport that would be normally visible in the real game. Subsequently, I want to be able to control the size and position of this "window".
I also want to be able to resize and move the content, while cutting off the edges that are not visible to the camera. This is what I tried inside the PlayScreenDraw:
public void draw(final float yOffset,
final int xTiles,
final int yTiles) {
view.getCamera().position.y = yTiles / 2f - yOffset * yTiles / HEIGHT; // HEIGHT = 800
view.getCamera().position.x = xTiles / 2f;
view.setWorldSize(xTiles, yTiles); //Do i even need to change the world size?
b.setProjectionMatrix(view.getCamera().combined);
b.begin();
...
b.end();
view.update(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
}
What this gives me, in terms of the picture above, is this
How do I need to change the viewport and/or the camera? Btw., this is how i set the two up:
cam = new OrthographicCamera();
cam.setToOrtho(false, WIDTH, HEIGHT); // WIDTH = 8, HEIGHT = 16
batch.setProjectionMatrix(cam.combined);
view = new FitViewport(WIDTH, HEIGHT, cam);
The Pixmap class can help you achieve what you want since you stated that you wanted to "cut off" the parts outside of the green selection box.
You need to render what the camera sees to an FBO and then get the pixmap from the FBO itself.
Framebuffer Objects are OpenGL Objects, which allow for the creation of user-defined Framebuffers. With them, one can render to non-Default Framebuffer locations, and thus render without disturbing the main screen.
-- OpenGL wiki
// Construct an FBO and keep a reference to it. Remember to dispose of it.
FrameBuffer fbo = new FrameBuffer(Format.RGBA8888, width, height, false);
public void render() {
//Start rendering to the fbo.
fbo.begin();
//From the camera's perspective.
batch.setProjectionMatrix(camera.combined);
batch.begin();
//Draw whatever you want to draw with the camera.
batch.end();
// Finished drawing, get pixmap.
Pixmap pixmap = ScreenUtils.getFrameBufferPixmap(0, 0, width, height);
//Stop drawing to your fbo.
fbo.end();
}
After getting the pixmap you can iterate through the pixels and set the alpha of the pixels outside your green selection window to 0 making them invisible or "cutting them off"

Scale up small bitmap pixel perfectly

I am making a pixel game for android. I am using 32x32 images. To make the game appear the same regardless of screen-dimensions i dynamically scale up the images. My issue is that when scaling up, parts of the images does not keep their original color:
6 tiles, originally 32x32. As you can see, just before the black edges there is an unwanted shadowy line (presumably the average of the black and the redish color).
This is the code i use for scaling:
public abstract class Drawable {
protected int x;
protected int y;
protected Bitmap image;
Drawable(Bitmap image, int x, int y, float scale) {
this.x = x;
this.y = y;
this.image = Bitmap.createScaledBitmap(image, (int)(image.getWidth()*scale), (int)(image.getHeight()*scale), false);
}
abstract void draw(Canvas canvas);
}
As you can see i am not using the filter. This would make the edge-area more blurry. Are there another filter that in contrast to if i where to use true when scaling up actually perserves the crispness of the image?
Edit:
I now tried this approach instead:
scaledRect = new RectF(x, y, x+image.getWidth()*scale, y+image.getHeight()*scale);
paint = new Paint();
paint.setAntiAlias(false);
paint.setDither(false);
paint.setFilterBitmap(false);
And in the draw call:
canvas.drawBitmap(this.image, null, scaledRect, paint);
With no success...
Android processes bitmap scaling with bilinear interpolation algorithm by default. What you're trying to do is nearest neighbor interpolation.
Make a Paint, turn off dither and anti-alias, don't draw by createScaledBitmap and try this:
paint.setDither(false);
paint.setAntiAlias(false);
canvas.drawBitmap(bitmap, null, new RectF(left, top, width, height), paint);

LibGDX drawing circles scales somehow

I try to draw 2 circles using textures in Java with LibGDX.
My classes are pretty straightforward, GameObject has a circle (for logic) and a draw method.
Here is my draw method:
#Override
public void draw(Batch batch, float alpha){
batch.draw(sprite, circle.x-circle.radius, circle.y-circle.radius, circle.x+circle.radius, circle.y+circle.radius);
}
This is the code I execute:
GameObject go = new GameObjectBuilder().createNew()
.withPosition(32, 32)
.withRadius(32)
.withTexture(new Texture(Gdx.files.internal("ball.png")))
.withId(0)
.build();
GameObject go2 = new GameObjectBuilder().createNew()
.withPosition(64, 64)
.withRadius(32)
.withTexture(new Texture(Gdx.files.internal("ball.png")))
.withId(1)
.build();
stage.addActor(go);
stage.addActor(go2);
And this is the result:
Somehow using circles with the same size, scales them when placed further away from the origin. Currently my main class extends ApplicationAdapter and that's it.
Here is the stack of calls I make in my builder.
circle.setPosition(x, y); // LibGDX.Circle class call
circle.setRadius(radius); // LibGDX.Circle class call
sprite = new Sprite(texture);
Using a ShapeRenderer, calling the same objects and drawing a filled circle yields these results:
shapeRenderer.setColor(Color.BLACK);
shapeRenderer.begin(ShapeRenderer.ShapeType.Filled);
for(GameObject goo : objects.values()) {
shapeRenderer.circle(goo.getX(), goo.getY(), goo.getRadius());
}
shapeRenderer.end();
Final edit:
batch.draw(sprite, circle.x-circle.radius, circle.y-circle.radius, circle.radius*2, circle.radius*2);
Did the trick!
Been a while since I used libGDX and I can tell some things have changed. However...
void draw(TextureRegion region,
float x,
float y,
float width,
float height)
Draws a rectangle with the bottom left corner at x,y and stretching the region to cover the given width and height.
Batch.draw draws a rectangle from x,y to x + width, y + height. What you have done is to scale the shape based on the objects internal position.
#Override
public void draw(Batch batch, float alpha){
batch.draw(sprite, circle.x-circle.radius, circle.y-circle.radius, circle.x+circle.radius, circle.y+circle.radius);
}
for circle 1 we have
batch.draw(sprite, 32-32, 32-32, 32+32, 32+32);
This draws the sprite from 0,0 to 64,64
for circle 2 we have
batch.draw(sprite, 64-32, 64-32, 64+64, 64+64);
This draws the sprite from 32,32 to 128,128
This should fix your code:
#Override
public void draw(Batch batch, float alpha){
batch.draw(sprite, circle.x, circle.y, circle.radius, circle.radius);
}
This will scale the objects base on the circle's radius.
Reference: http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/graphics/g2d/Batch.html#draw-com.badlogic.gdx.graphics.Texture-float-float-float-float-

2D clip area to shape

I'm quite new to graphics in java and I'm trying to create a shape that clips to the bottom of another shape. Here is an example of what I'm trying to achieve:
Where the white line at the base of the shape is the sort of clipped within the round edges.
The current way I am doing this is like so:
g2.setColor(gray);
Shape shape = getShape(); //round rectangle
g2.fill(shape);
Rectangle rect = new Rectangle(shape.getBounds().x, shape.getBounds().y, width, height - 3);
Area area = new Area(shape);
area.subtract(new Area(rect));
g2.setColor(white);
g2.fill(area);
I'm still experimenting with the clip methods but I can't seem to get it right. Is this current method ok (performance wise, since the component repaints quite often) or is there a more efficient way?
I think your original idea about using the clip methods was the right way to do it. This works for me:
static void drawShapes(Graphics2D g, int width, int height,
Shape clipShape) {
g.setPaint(Color.BLACK);
g.fillRect(0, 0, width, height);
g.clip(clipShape);
int centerX = width / 2;
g.setPaint(new GradientPaint(
centerX, 0, Color.WHITE,
centerX, height, new Color(255, 204, 0)));
g.fillRect(0, 0, width, height);
g.setPaint(Color.WHITE);
int whiteRectHeight = height * 4 / 5;
g.fillRect(0, whiteRectHeight,
width, height - whiteRectHeight);
}
Is this current method ok (performance wise, since the component repaints quite often) ..
Subtracting shapes is how I'd go about it. The objects could be a few instances or (possibly) a single instance that is transformed as needed.
A text demo., using scaling & fading.
Here's one with simple lines (..and dots, ..and it is animated).
Of course, if the image is purely additive, use a BufferedImage as the canvas & display it in a JLabel/ImageIcon combo. As in both of those examples.

Categories

Resources