Java moving an object on a straight line/vector - java

(I am working in libgdx and my arrows are polygons)
I have some programming issue. I'm currently working on a level editor in my game, I've come very far, but now I have a scaling issue. I am working with 8 arrows. Picture below.
I want to drag the the arrow, or when hovered a huge square in the direction, on a straight line. (Orange line). Every arrow has a direction. The arrows should scale the green square.
My code, which is shown above, looks like this:
public void onDragged(int prevX, int prevY, int cX, int cY) {
super.onDragged(prevX, prevY, cX, cY);
int dX = cX - prevX;
int dY = cY - prevY;
int pX = x;
int pY = y;
Vector2f vector = new Vector2f((float) Math.cos(directionAngle + Math.PI / 2), (float) Math.sin(directionAngle + Math.PI / 2));
float sum = new Point2f(prevX, prevY).distance(new Point2f(cX, cY));
x += sum * Integer.signum(dX) * vector.x;
y -= sum * Integer.signum(dY) * vector.y;
polygon.setPosition(x, y);
gui.onArrowDragged(getID(), pX, pY, x, y);
}
This solution works very well at the 4 arrows on the squares side, but my problem is, at the other ones, I can still move the arrow in any direction, and not on the vector of the direction.

Related

Java - get x,y coordinates after rotating a rectangle

I can't seem to get the standard answer to work. I'm trying to find the new x,y coordinates of a point inside a larger rectangle, after rotating the larger rectangle around its center. (The end goal is to calculate the 4 corners of a collision box inside the larger rectangle.)
The rectangle is 50W x 128H, and the point inside the rectangle is at 15,111. If I rotate this same rectangle 90 degrees clockwise in Photoshop around its center, the point becomes 17,15 inside the rectangle that is now 128W and 50H.
However, if I use the formula I've found 100 times around here for the same question, I get a completely different answer.
import java.awt.*;
public class MyClass {
public static void main(String args[]) {
int width=50;
int height=128;
int cx = width/2;
int cy = height/2;
// should get 17,15
rotateXY(15,111,cx,cy,-90D); // returns 61,85, which is not even inside the image anymore
rotateXY(15,111,cx,cy,90D); // returns -11,66.
}
public static void rotateXY(int x, int y, int cx, int cy, double degrees) {
// x, y - coordinates of a corner point of the square
// cx, cy - center of square coordinates
double angle = Math.toRadians(degrees);
double x1 = x - cx;
double y1 = y - cy;
double x2 = x1 * Math.cos(angle) - y1 * Math.sin(angle);
double y2 = y1 * Math.cos(angle) + x1 * Math.sin(angle);
int rx = (int) x2 + cx;
int ry = (int) y2 + cy;
System.out.println(rx + "," + ry);
}
}
The method I used to rotate the image in java:
public static BufferedImage rotate(BufferedImage bimg, Double angle) {
double sin = Math.abs(Math.sin(Math.toRadians(angle))),
cos = Math.abs(Math.cos(Math.toRadians(angle)));
int w = bimg.getWidth();
int h = bimg.getHeight();
int neww = (int) Math.floor(w*cos + h*sin),
newh = (int) Math.floor(h*cos + w*sin);
BufferedImage rotated = new BufferedImage(neww, newh, bimg.getType());
Graphics2D graphic = rotated.createGraphics();
graphic.translate((neww-w)/2, (newh-h)/2);
graphic.rotate(Math.toRadians(angle), w/2, h/2);
graphic.drawRenderedImage(bimg, null);
graphic.dispose();
return rotated;
}
I assume you created an AffineTransform. With that you need three transformations:
translate the rectangle so it's center point is the origin
rotate the rectangle by some angle
translate the rectangle back to the position where it came from
Now with this you were able to transform the rectangle's coordinates (corners) into new screen coordinates. What you need is a transformation to get from new screen coordinates back to coordinates inside your rectangle. I think it looks like this:
translate the point the same as in step 1
rotate by the negative angle
undo step 1
These AffineTransforms can be applied quite efficiently, compared to your algorightm using trigonometry which even may have a limited range of validity.
See also Rotating Image with AffineTransform

Accounting for gravity when shooting an arrow at a coordinate

So I am making a 2D android game where you aim with your cursor and your character shoots where your cursor clicks.
When the arrow is created this method is called
private final float GRAVITY = 100, SPEED = 50f;
public Arrow(float dx, float dy, float x1, float y1, float x2, float y2,)
{
destination = new Vector2(dx, dy);//mouse clicked here
//x1 and y1 are the starting coordinates
bounds = new Polyline(new float[]{x1, y1, x2, y2});
double r = Math.atan2(dy-y1, dx-x1);//calculate angle
velocity = new Vector2();
velocity.x = (float)(Math.cos(r) * SPEED);
velocity.y = (float)(Math.sin(r) * SPEED) + ACCOUNT FOR GRAVITY;
acceleration= new Vector2(0, GRAVITY);
}
and this is the update method, pretty straight forward
public void update(float delta)
{
velocity.add(acceleration.cpy().scl(delta));
position.add(velocity.cpy().scl(delta));
}
How do I account for gravity? If gravity is set to 0 the arrow travels in a straight line to the coordinates the mouse clicked, but with gravity it always falls short. Im not sure how to account for gravity. I think delta might be screwing me up.
This is more of a math / physics question than a programming question. So first of all, you know the horizontal velocity of the arrow is constant (unless you have air resistance, in which case it is a lot more complicated). You can calculate the time it will take for the arrow to reach it's destination's x coordinate.
let (dx, dy) = displacement from launcher to destination
let c = cos(angle), s = sin(angle), vx = c * speed, vy = s * speed
vx * t = dx
t = dx / vx
With this value, you can compute the vertical displacement
dy = 0.5*acc * t^2 + V0 * t
dy = 0.5*acc * (dx/vx)^2 + vy*t
dy = 0.5*acc * (dx/(c*speed))^2 + (s*speed)*(dx/(c*speed))
since sin = sqrt(1 - cosine^2),
dy = 0.5*acc * (dx/(c*speed))^2 + (sqrt(1-c^2)*speed)*(dx/c*speed))
Now you have an equation with only known values (acc, dy, dx, speed) and c. If you solve for c, you know the cosine and you can find the sin.

Rendering 3-d lines in Java?

Ok, I have a little problem: I'm trying to code an engine capable of rendering 3d lines onto a 2d plane, but I'm having some trouble with it.
I have a point3d class, which takes x y and z coords, a line3d class, which takes two point3d endpoints, and I have a world object containing an arraylist of line3d's. I also have point2d and line2d, which are just like their 3d counterparts, except that they lack a z coordinate.
Here's the render method:
public void render(){
for(Line3d line : world.lines){ //for every 3d line in the world
panel.l3d(line, world.main); //render that line
}
panel.repaint(); //repaint the graphic
}
Which calls upon the l3d method:
public void l3d(Line3d line, Camera view){
Point2d start = p3Top2(line.start, view), end = p3Top2(line.end, view); //convert 3d points to 2d points
if(start==null || end==null)return; //if either is null, return
lineAbs(new Line2d(start, end)); //draw the line
}
Which calls upon p3Top2:
public Point2d p3Top2(Point3d point, Camera view){ //convert 3d point to 2d point on screen
int relativeZed = point.z - view.z; //difference in z values between camera and point
if(relativeZed <= 0)return null; //if it's behind the camera, return
int sx, sy, ex, ey, tx, ty; //begin declaring rectangle formed from extending camera angle to the z coord of the point(angle of 1 = 90 degrees)
sx = (int) (view.x - (width / 2) - relativeZed * (width * view.angle)); //starting x of rectangle
ex = (int) (view.x + (width / 2) + relativeZed * (width * view.angle)); //ending x
sy = (int) (view.y - (height / 2) - relativeZed * (height * view.angle)); //starting y
ey = (int) (view.y + (height / 2) + relativeZed * (height * view.angle)); //ending y
tx = point.x - sx; //difference between point's x and start of rectangle
ty = point.y - sy; //same for y
float px = (float)tx / (float)(ex - sx), py = (float)ty / (float)(ey - sy); //px and py are the ratios of the point's x/y coords compared to the x/y of the rectangle their in
return new Point2d((int)(px * width), (int)(py * height)); //multiply it by the width and height to get positions on screen
}
And also on lineAbs:
public void lineAbs(Line2d line){
Point2d start = line.start;
Point2d end = line.end;
if(start.x>end.x){
start = line.end;
end = line.start;
}
int y = start.y; //starting y point
int white = 0xffffff;
for(int x = start.x; x<end.x; x++){ //for each x in the line
if(x < 0 || y < 0 || x > canvas.getWidth() || y > canvas.getHeight())continue; //if the point is outside of the screen, continue
y += line.getSlope().slope; //increment y by the slope
canvas.setRGB(x, y, white); //draw the point to the canvas
}
}
'canvas' is a BufferedImage being drawn to the screen. With an arbitrary camera and angle of 1, as well as a few arbitrary lines thrown in, I do see each line, but they don't appear to be rendered properly. For example, when I have three point3d's as vertices, and three lines, each with a different combination of two of the points, the lines don't appear to meet at all, although each one is visible.
I suspect the issue is in my p3Top2, but I'm not sure where, can you tell?

Shoot to the mouse direction

The problem:
I've got this "Shot" class. In the code, the target variables are the mouseX and mouseY.
So when i click the mouse button, my player class will create a new shot object.
But the shooting is inaccurate.
How can i calculate the correct dx and dy?
If i add the dx and dy to the "bullet's" x and y, the bullet will move to the mouse's direction.This is what i want. The mouse position is stored in targetX and targetY, when the object is created. This is the point what the oval wants to reach.
Links:
The game (finished)
The code (from Shot.java):
public class Shot extends Entity {
private float targetX, targetY;
public Shot(World world, float x, float y, int width, int height, Color color, float targetX, float targetY) {
super(world, x, y, width, height, color);
this.targetX = targetX;
this.targetY = targetY;
}
#Override
public void render(GameContainer gc, Graphics g, Camera camera) {
g.setColor(color);
g.fillOval(x - camera.getX(), y - camera.getY(), width, height);
}
#Override
public void update(GameContainer gc, int delta) {
float dx = targetX - x;
float dy = targetY - y;
x += dx * delta * .001f;
y += dy * delta * .001f;
}
}
I tried this, but still not work:
#Override
public void update(GameContainer gc, int delta) {
float length = (float) Math.sqrt((targetX - x) * (targetX - x) + (targetY - y) * (targetY - y));
double dx = (targetX - x) / length * delta;
double dy = (targetY - y) / length * delta;
x += dx;
y += dy;
}
I did it! Here is my solution:
The problem was that, the target was the window's mouse position, and not the world's mouse position.
This is how i calculated the world's mouse positions:
float mouseWorldX = x + (mouseX - screen_width / 2); // x = player's x position
float mouseWorldY = y + (mouseY - screen_height / 2); // y = player's y position
This is code from my game at the moment is used to move a unit to the mouse when the right mouse button is pressed:
length = Math.sqrt((target_X - player_X)*(target_X - player_X) + (target_Y - player_Y)*(target_Y - player_Y)); //calculates the distance between the two points
speed_X = (target_X - player_X) /length * player_Speed;
speed_Y = (target_Y - player_Y) /length * player_Speed;
This will move an object to the target in a line at a set speed.
Edit: this is the actual code right from my game
if(input.isMouseButtonDown(Input.MOUSE_RIGHT_BUTTON))
{
length = (float) Math.sqrt((player_waypoint_X - player_X)*(player_waypoint_X - player_X) + (player_waypoint_Y - player_Y)*(player_waypoint_Y - player_Y));
velocityX = (float) (player_waypoint_X - player_X) /length * (float) PlayerStats.player.db_player_Speed;
velocityY = (float) (player_waypoint_Y - player_Y) /length * (float) PlayerStats.player.db_player_Speed;
player_waypoint_X = input.getMouseX() - 2;
player_waypoint_Y = input.getMouseY() - 2;
}
For testing purposes the velocity's are defined in the init method along with length. Every time the right mouse is pressed the waypoints's X and Y are changed to the mouse location.
I learned this from this question
velocity calculation algorithm.
in order to make the bullets not all change direction every shot, create an array list so that each bullet fired has its own x and y velocity

Move Minute hand in Android clock

I am trying to write a simple proof of concept app that allows a user to rotate minute hand of a clock. I am having hard time coming up with the right logic for OnTouchEvent.
So far I have the following code:
public boolean onTouchEvent(MotionEvent e) {
float x = e.getX();
float y = e.getY();
switch (e.getAction()) {
case MotionEvent.ACTION_MOVE:
//find an approximate angle between them.
float dx = x-cx;
float dy = y-cy;
double a=Math.atan2(dy,dx);
this.degree = Math.toDegrees(a);
this.invalidate();
}
return true;
}
protected void onDraw(Canvas canvas) {
super .onDraw(canvas);
boolean changed = mChanged;
if (changed) {
mChanged = false;
}
int availableWidth = getRight() - getLeft();
int availableHeight = getBottom() - getTop();
int x = availableWidth / 2;
int y = availableHeight / 2;
cx = x;
cy = y;
final Drawable dial = mDial;
int w = dial.getIntrinsicWidth() + 100;
int h = dial.getIntrinsicHeight() + 100;
boolean scaled = false;
if (availableWidth < w || availableHeight < h) {
scaled = true;
float scale = Math.min((float) availableWidth / (float) w, (float) availableHeight / (float) h);
canvas.save();
canvas.scale(scale, scale, x, y);
}
if (changed)
{
dial.setBounds(x - (w / 2), y - (h / 2), x + (w / 2), y + (h / 2));
}
dial.draw(canvas);
canvas.save();
float hour = mHour / 12.0f * 360.0f;
canvas.rotate(hour, x, y);
final Drawable hourHand = mHourHand;
if (changed) {
w = hourHand.getIntrinsicWidth() + 30;
h = hourHand.getIntrinsicHeight() + 30;
hourHand.setBounds(x - (w / 2), y - (h / 2), x + (w / 2), y + (h / 2));
}
hourHand.draw(canvas);
canvas.restore();
canvas.save();
float minute = mMinutes / 60.0f * 360.0f;
if (bearing == 0)
{
canvas.rotate(minute, x, y);
}
else
{
canvas.rotate((float)bearing, x, y);
}
final Drawable minuteHand = mMinuteHand;
if (changed) {
w = minuteHand.getIntrinsicWidth() + 30;
h = minuteHand.getIntrinsicHeight() + 30;
minuteHand.setBounds(x - w, y - h, x + w, y + h);
}
minuteHand.draw(canvas);
canvas.restore();
if (scaled) {
canvas.restore();
}
}
Then based on that, my OnDraw method rotates the minute hand to the specified "this.degree"(just calls canvas.rotate). I am assuming my math is off here. I tried to follow the example here: Calculate angle for rotation in Pie Chart, but that's still not rotating the minute hand correctly. Any help would be appreciated.
The math looks correct. Your calculations should give you the angle of the touch event, where a touch that is to the exact right of the center point should give you 0 degrees.
A few things to watch out for
Make sure that you're rotating in the correct direction. It is hard to keep this straight, and thus easy to screw it up
Make sure that you're taking into account that a value of 0 means that the minute hand should be pointing to the right. For example, if you start out with a minute hand that is pointing upwards, you would have to add/subtract 90 degrees to the result of your calculation (depending on the direction of rotation - not sure which is correct offhand)
Make sure that (cx, cy) is the center point around which you want to calculate the angle
When rotating, you'll need to either use the 3 arg Canvas.rotate(float, float, float) method, or add an additional translation seperately, to make sure that you are rotating around the correct point. Without any translation, it will rotate around (0,0) (the top left corner of the view)
More on rotation:
Rotation always happens around the "current" (0,0) point. By "current", I mean the (0,0) point after the current matrix has been applied. When you first enter onDraw, the (0,0) point should be the upper-left corner of the view. Whenever you apply a translation/scaling/etc, you will potentially change where the (0,0) point is, relative to the view.
I think something like the following should work, in regards to setting the correct center of rotation:
//first we save the initial matrix, so we can easily get
//back to this state after we're done rotating
canvas.save();
//I *think* you need to negate the center offsets here,
//because you are conceptually moving the canvas, rather
//than moving the center directly
canvas.translate(-cx, -cy);
//<perform the rotation and draw the clock hand>
//...
//and now restore the matrix back to the initial state
canvas.restore();
Your calculation is good for measuring angle for minutes hand to
rotate in corresponding quadrants in analog clock... here with little
bit changes can make either minutes or hours hand to rotate at the
touch position....call the below method in onTouch() method for action move
public float getRotationAngle(float x, float y) {
float dx = x - cx;
float dy = y - cy;
double a = Math.atan2(dy, dx);
double degree = Math.toDegrees(a)+90;
if(angle<0){
degree=degree+360;
}
return (float) degree;
}
i have this approach with as vectors concept for calculating the angle but if little bit more than your code if u want i will give that logic....

Categories

Resources