Getting the x and y point of a transformed object - java

I am transforming the barrel of a turret with AffineTransform, and I want a bullet to shoot right out of the tip of the barrel. Is there a method in shape to get these coordinates or do I have to calculate it manually?
code for transform
AffineTransform rotate = AffineTransform.getRotateInstance(rotation, getX() + getWidth()/2, getY() + getHeight()/2);
barrel = rotate.createTransformedShape(new Rectangle(getX() + getWidth()/2, getY() - getHeight()/2, 2, getHeight()/2 + 1));
code for bulllet
int dx = getX() - o.getX();
int dy = o.getY() - getY();
bullets.add(new Bullet((int)barrel.getBounds2D().getX(), (int)barrel.getBounds2D().getY(), SPEED, new NVector(dx, dy)));

Use the same transform (rotate) to transform the coordinate of the tip of the barrel:
rotate.transform(tipOfTheBarrel, transformedTipOfTheBarrel);

When your barrel is a rotated rectangle, then you can't use it because your bullet should probably come out of the middle of the side of the rectangle and not one of the corners.
But if you create your own shape and you make sure the point where the bullets appear is the first in the shape, then you can use getPathIterator() to get the transformed point.

Related

LibGdx - Make sprite follow a line path

I have an isometric map drawn.
I take the current position of my sprite and the target position of where my sprite should be at after the move:
// region is my TextureRegion.
int x1 = getIsometricX(1,1,region);
int x2 = getIsometricX(1,2,region);
int y1= getIsometricY(1,1,region);
int y2 = getIsometricY(1,2,region);
And then I draw a simple line using ShapeRenderer to see if the local/target points are correctly set, and the rectangle so you can see where the sprite rendering starts.
renderer.setProjectionMatrix(camera.combined);
renderer.begin(ShapeRenderer.ShapeType.Line);
renderer.setColor(Color.RED);
renderer.line(x1 + location.getOffsetx(), y1 + location.getOffsety(), x2 + location.getOffsetx(), y2 + location.getOffsety());
renderer.rect(x1 + location.getOffsetx(), y1 + location.getOffsety(), region.getRegionWidth(), region.getRegionHeight());
renderer.end();
Every sprite of mine has set offsetX and offsetY to adjust its location on the isometric tile, because every sprite is different.
Output looks like this:
what you can see here, is the starting point of where the sprite starts to draw (you see that offsets adjusted it so the sprite looks like its on the 1,1 tile.
and you can see the line which starts at the starting draw point of the sprite, and ends at the target draw point of the sprite.
Now my question is, how can I make that sprite move on that line's path, so it will look like the ship is moving forward?
So the main concept of the question is.. How can you make a sprite move in a straight line, from local point to target point?
Some functions you might need to see:
public int getIsometricX(int x, int y, TextureRegion region) {
return (x * GameTile.TILE_WIDTH / 2) - (y * GameTile.TILE_WIDTH / 2) - (region.getRegionWidth() / 2);
}
public int getIsometricY(int x, int y, TextureRegion region) {
return (x * GameTile.TILE_HEIGHT / 2) + (y * GameTile.TILE_HEIGHT / 2) - (region.getRegionHeight() / 2);
}
Tiles are drawn using the same method, just with Tile's texture.
I would like to answer myself this question, because other people might have the same issue and just over-complicate it like me.
If you want to perform any move on your isometric map, do not follow my misunderstandings and calculate it on the isometric coordinates.
You have to calculate it on your flat screen matrix coordinates, and then convert it to isometric coordinates.
For example, I want to move up like this line, all I need to do is this:
ship.y += 0.1f // when it reaches 1, then it will be at 0, 1
So you know that you want to be at 0,1 on your non-isometric map.
So you do this increment, and then for last, you have to convert it to isometric coordinates before drawing:
float x = (ship.x * GameTile.TILE_WIDTH / 2) - (ship.y * GameTile.TILE_WIDTH / 2) - (textyure.getWidth() / 2);
float y = (ship.x * GameTile.TILE_HEIGHT / 2) + (ship.y * GameTile.TILE_HEIGHT / 2) - (texture.getHeight() / 2);
And that will draw it on the isometric map, exactly like on your screen-coordinates, but on an isometric format.
for(float x : new float[targetx - currentx]) {ship.setPosition(ship.getX + x, ship.getY); }
And you would do the same for y.
Edit:
I guess this is wrong since you're calling this every frame I assume.
Instead you would keep track of your distance traveled between target and ship and you would increment sip position by 1 each time to x and y until it reached the distance.

How to create an arc with Slick2D?

I cant seem to figure out how to create an arc with Slick2D. Basically I want to be able to save it as an object so I can do collicion detection with it, but Slick2D only has a g.drawArc() function.
I thought about using Arc2D, which is perfect for what I need, but I can't figure out how to get slick to draw the Arc2D.
Heres is a mockup of what I want to have:
This is pretty easy to do with Arc2D and then using jFrame to draw it, but I dont know how to create something like this with slick.
Edit: The solution I see that I can do, is just use Arc2D for all the collision detection, and then just use myArc.x, myArc.y, myArc.width, etc. when I need to draw the arc with g.drawArc(), but Arc2D cant use the .interesects() function with slick2d shapes, which are used everywhere else.
Ive created a method that creates an approximate arc based on a curve
public Curve createArc(float centerX, float centerY, float radius, float theta){
// creates a 180 degree arc around centerX and Y, theta (in degrees) is used for rotation
float x1,x2,x3,x4,y1,y2,y3,y4;
x1 = (float) (radius*(1-Math.cos(Math.toRadians(theta)))) + startX - radius;
y1 = (float) (radius*(1-Math.sin(Math.toRadians(theta)))) + startY - radius;
x2 = (float) (Math.sqrt(25*(radius*radius)/9)*(Math.cos(Math.toRadians(theta-36.87-90)))) + startX;
y2 = (float) (Math.sqrt(25*(radius*radius)/9)*(Math.sin(Math.toRadians(theta-36.87-90)))) + startY;
x3 = (float) (Math.sqrt(25*(radius*radius)/9)*(Math.cos(Math.toRadians(theta-53.13)))) + startX;
y3 = (float) (Math.sqrt(25*(radius*radius)/9)*(Math.sin(Math.toRadians(theta-53.13)))) + startY;
x4 = (float) (radius*(1-Math.cos(Math.toRadians(theta+180)))) - radius + startX;
y4 = (float) (radius*(1-Math.sin(Math.toRadians(theta+180)))) - radius + startY;
Vector2f vectorTest1 = new Vector2f(x1,y1);
Vector2f vectorTest2 = new Vector2f(x2,y2);
Vector2f vectorTest3 = new Vector2f(x3,y3);
Vector2f vectorTest4 = new Vector2f(x4,y4);
Curve curve = new Curve(vectorTest1,vectorTest2,vectorTest3,vectorTest4);
return curve;
}
Hey you can create a variety of different shapes with Slick2D. You can see them here:
http://slick.ninjacave.com/javadoc/org/newdawn/slick/geom/package-summary.html
They all inherit from shape and share the same methods (e.g. intersects());
There is no specific arc shape but you might be able to use a curve or maybe a polygon (or something else). So for example you simply could do:
org.newdawn.slick.geom.Curve myCurve = new org.newdawn.slick.geom.Curve(10f, 5f, 9f, 4f);
to create a weird looking curve. Just read the documentation and play a little bit around.
In your render method you could either call g.drawArc(myCurve) or you call the drawing method on myCurve directly (myCurve.draw());
I'm sure you will be able to do this if you read the documentation and play a little bit around with the coordinates.
Furthermore you could create your own Shapes in Slick2D if you create a new class and let it inherit from Shape.

How to calculate a point out of an angle and a distance?

I searched and implemented things from this forum, it doesn't come out right.
What I'm trying to achieve is to calculate a spawnPoint for player bullets relative to his position and rotation.
The spawnPoint should be and his X + his width (the player is set to point to the right by default) and y + height/2 (to spawn from his center on the Y axis).
This is what I got from this forum:
this.bulletSpawn.x = (float)(this.position.x + this.width/2 + this.width * Math.cos(rotation));
this.bulletSpawn.y = (float)(this.position.y + this.height/2 + this.height/2 * Math.sin(rotation));
The rotation is in Radians. The this is the Player class.
Images showing what I expect to happen:
Original Position
Expected Behaviour
The red dot is the spawnPoint I'm trying to calculate knowing the player position and rotation.
The player Sprite is what rotates, and it rotates related to his center x and y, which is done with a lib, i do not have these variables. The entire arrow would be the player , the arrow direction is where the player is pointing at, and the red dot would be the bulletSpawn point (or the expected one)
Using the code I posted, the bullets seem to be spawning from somewhere else. Even at the beggining they have an offset and when I rotate the player the spawnPoint seems to be relative to a different origin than what I'm expecting.
This is the bullet position code:
position.x = holder.bulletSpawn.x - (float)(this.width/2 * holder.rotation);
position.y = holder.bulletSpawn.y - (float)(this.height/2 * holder.rotation);
This is inside the Bullet class. The position variable is a Vector2 of bullet, and holder is the player instance. This code is merely to give an offset for the bullet to spawn at the center of its own size
I added some fixes related to the comments, but the bullets still have a tiny offset that looks wrong at certain angles.
Basically the distance i want to get is the width of the player, and his center y which is height/2.
Let's initial position is X0, Y0, rotation is about center point CX, CY, and rotation angle is Theta. So new position after rotation is:
NX = CX + (X0-CX) * Cos(Theta) - (Y0-CY) * Sin(Theta)
NY = CY + (X0-CX) * Sin(Theta) + (Y0-CY) * Cos(Theta)
This equations describe affine transformation of rotation of arbitrary point about center point, and affine matrix is combination of translation, rotation, and back translation matrices.
About center CX, CY - you wrote
it rotates related to his x and y origin at his bottom left
About initial point coordinate - for bullet it seems to be
X + Width, Y + Height/2
Swift extension:
extension CGSize {
static func offsetFrom(angle:CGFloat, distance:CGFloat) -> CGSize {
let rad = angle * CGFloat.pi / 180
return CGSize(width: sin(rad) * distance, height: cos(rad) * distance)
}
}

How to rotate a line based on a given number of degrees

I have a line drawn with a Graphics object. I want to rotate this line a certain amount of degrees based on how much the mouse is dragged. I can get the number of degrees i need to rotate it but how do i then rotate the line based on that?
Thank You!
You can create a Line2D object for your original line. Then you can use AffineTransform#getRotateInstance to obtain an AffineTransform that does the rotation about a certain angle, around a certain point. Using this AffineTransform, you can create a rotated Line2D object to paint. So your painting code could roughly look like this:
protected void paintComponent(Graphics gr) {
super.paintComponent(gr);
Graphics2D g = (Graphics2D)gr;
// Create the original line, starting at the origin,
// and extending along the x-axis
Line2D line = new Line2D.Double(0,0,100,0);
// Obtain an AffineTransform that describes a rotation
// about a certain angle (given in radians!), around
// the start point of the line. (Here, this is the
// origin, so this could be simplified. But in this
// form, it's more generic)
AffineTransform at =
AffineTransform.getRotateInstance(
Math.toRadians(angleInDegrees), line.getX1(), line.getY1());
// Draw the rotated line
g.draw(at.createTransformedShape(line));
}
Alright, you will need to compute the length of the line, assuming that the ends of the line are (x0,y0) and (x1,y1), and (x,y) are the mouse coordinates, what you want is the point (x2,y2) that's on the line between (x0,y0) and (x,y), the distance between (x0,y0) and (x2,y2) must be the same as the one between (x0,y0) and (x1,y1).
The distance between (x0,y0) and (x1,y1) is:
double dx = x1-x0;
double dy = y1-y0;
double length = Math.sqrt(dx*dx, dy*dy);
The distance between (x0,y0) and (x,y) is:
double dx1 = x-x0;
double dy1 = y-y0;
double mouseDist = Math.sqrt(dx1*dx1, dy1*dy1);
And (x2,y2) are:
int x2 = x0 + (int)(dx1*length/mouseDist);
int y2 = y0 + (int)(dy1*length/mouseDist);
I suppose you are talking about Java AWT Graphics class. Graphics can be thought of as a canvas. It's an array of pixel values and "drawing a line" is just a utility function that changes the values of some of these pixels - there is no "line object" to speak of, from it's point of view. Normally you should erase the whole thing and draw a new line with the angle you want. However for that, you may want to look at Graphics2D (http://docs.oracle.com/javase/7/docs/api/java/awt/Graphics2D.html) and in particular at setTransform and the AffineTransform class.
static Point rotateLineClockWise(Point center, Point edge, int angle) {
double xRot = (int) center.x + Math.cos(Math.toRadians(angle)) * (edge.x - center.x) - Math.sin(Math.toRadians(angle)) * (edge.y - center.y);
double yRot = (int) center.y + Math.sin(Math.toRadians(angle)) * (edge.x - center.x) + Math.cos(Math.toRadians(angle)) * (edge.y - center.y);
return new Point((int) xRot, (int) yRot);
}

draw ellipse using center point (not upper left corner)

I try to find a solution for drawing ellipses based on the center point, not the upper left corner as it is specified in the constructor of Ellipse2D.Double. As seen in the picture the ellipses should have the same center point and scale, is that somehow possible?
Thanks in advance for your help.
If (x,y) is the center you want to use and you can only specify the upper left corner, then use the following:
private Ellipse2D getEllipseFromCenter(double x, double y, double width, double height)
{
double newX = x - width / 2.0;
double newY = y - height / 2.0;
Ellipse2D ellipse = new Ellipse2D.Double(newX, newY, width, height);
return ellipse;
}
If called with the center point and the width and height, this will "transform" your center point to the upper left corner and create an Ellipse2D which is located just as you want it to be.
The 'Upper' coordinate is misleading , it only works assuming y >=0 ( which works fine for a screen referential , bur not if you use the primitive with y <0 , for instance calculating object collisions )
With the usual math referential , where y<0 is possible , up is at the bottom
so it lacks a general definition not to get confused
The exact definition is that x and y are the min coordinates of the bounding rectangle.
It can be 'up' or 'down' ( relatively to your screen i suppose ) depending on the y axis orientation and y coordinate sign

Categories