I'm working on some AI stuff in Java. When an entity moves towards a point, the movement does not look natural. If its starting point and target are not equal X and Y distances away from each other, the movement looks like this:
What I want it to look move like is this:
Currently, movement is done like so:
int tX = target.x;
int tY = target.y;
if(tX> oX){
getOwner().addX(getOwner().getMoveSpeed());
}
if(tX < oX){
getOwner().addX(-getOwner().getMoveSpeed());
}
if(tY> oY){
getOwner().addY(getOwner().getMoveSpeed());
}
if(tY< oY){
getOwner().addY(-getOwner().getMoveSpeed());
}
I'm guessing that there is a much better method for handling movement than this.
So what I want to know is probably how to work out the angle I need to move along, and then the x and y velocitys needed to do so.
You just need to scale the x and y speeds according to the total distance to be traveled in each direction.
It will help to do the calculations in floating point and round to an int only when you need to assign a position.
int tX = target.x;
int tY = target.y;
float speed = getOwner.getMoveSpeed();
float dX = tX - oX;
float dY = tY - oY;
float dist = Math.sqrt(dX * dX + dY * dY);
float sX = speed * dX / dist;
float sY = speed * dY / dist;
getOwner().addX((int) (sX + 0.5));
getOwner().addY((int) (sY + 0.5));
You're describing the process of drawing a line between two points.
There are relatively simple integer-only algorithms, such as Bresenham that may help.
Related
Okay. My problem is kinda mind-blowing. Let's say that I have a constructor which looks like this:
public Ball(Random r, float halfphonewidth, float halfphoneheight, float cx, float cy){
//te wartosci odpowiadaja za losowe polozenie i losowe wektory
x = (halfphonewidth-48)*0.1f;
y = (halfphoneheight-48)*0.1f;
vx = -0.2f + r.nextFloat();
vy = -0.2f + r.nextFloat();
Log.i("", "\n\n" + this.vx + " " +this.vy+"\n\n");
health = 3;
}
Now let's say that I have a step() method declared somewhere - it works. Step:
public void step(){
x += vx;
y += vy;
if(x<2f || x > 98f)
vx =- vx;
if(y<2f || y > 98f)
vy =- vy;
}
I have an onTouch event that listens and checks the 'click coords'. I catch them and pass to the constructor (float cx, float cy).
The first position of the ball is static - I set it permanently in the constructor and move by 48 pixels (cause of image size). It starts from the bottom-center and floats to the random direction + when it collides with a wall it reverses it's vx & vy.
So! I need to point the ball in a direction I click! :D
I tried by scaling screen w/h with coordinate system, tried with implementing Vector2D class (cause it's missing in the newest java I think) but im not that good with linear algebra, tried with trygonometry(even cyclometry) (but I may be blind).
Is there anyone that can point me a solution? Which is the best way and which should I try to implement?
You need to calculate the difference between the coordinates of the current position and the click position:
double xDiff = clickPoint.x - ball.x;
double yDiff = clickPoint.y - ball.y;
then you move the ball with a velocity proportional to that difference, in your case you have to set the vx and vy like:
vx = xDiff * n;
vy = yDiff * n;
where the bigger n is, the faster the ball will reach the target. You need to update the xDiff and yDiff variables as the ball move, so when the difference is 0 the movement will stop.
I started learning java just over a year ago so i'm still fairly new.
I'm trying to make an object travel from one point to another at a constant net velocity no matter where the second point is in the frame. Currently it's working fairly well as long as I run the method every few frames.
The only problem is that it it will only move horizontally unless the second point is approximately between 45 and 135 degrees or between 225 and 315 degrees (1/2π and 3/2π or 5/2π and 7/2π).
It may be because of the 'if' statements meant to stop it from dividing by 0 but it doesn't seem like it. Also if there is any way to simplify those equations or remove 'if' statements I wouldn't mind some advice there too.
Note: vel is the net velocity the objects travel at and Prime.mx and Prime.my is the location of the target point.
public void target()
{
if (Prime.mx > x)
{
if (Math.abs(x-Prime.mx) != 0)
x = Math.round(Math.round((x + (vel*Math.cos(Math.atan(Math.abs(y-Prime.my)/Math.abs(x-Prime.mx)))))));
}
if (Prime.mx < x)
{
if (Math.abs(x-Prime.mx) != 0)
x = Math.round(Math.round((x - (vel*Math.cos(Math.atan(Math.abs(y-Prime.my)/Math.abs(x-Prime.mx)))))));
}
if (Prime.my > y)
{
if (Math.abs(x-Prime.mx) != 0)
y = Math.round(Math.round((y + (vel*Math.sin(Math.atan(Math.abs(y-Prime.my)/Math.abs(x-Prime.mx)))))));
}
if (Prime.my < y)
{
if (Math.abs(x-Prime.mx) != 0)
y = Math.round(Math.round((y - (vel*Math.sin(Math.atan(Math.abs(y-Prime.my)/Math.abs(x-Prime.mx)))))));
}
}
I use Math.round twice because the first brings it to a float from a double and the second makes it an int. I need the x and y as ints so the paint method can draw the objects.
I found a few simillar problems on the site but the closest one was in python and and the anwer didn't seem applicable to my problem.
I believe you are overcomplicating this. If your starting point is (sx, sy) and your destination is (dx, dy) then you can easily calculate any point (x, y) that is p distance along the line (0.0 <= p <= 1.0). You can use this to move at velocity v. So I suggest finding your end point and then using simple arithmetic to move on the x and y axis.
float dx = dist * Math.cos(angle);
float dy = dist * Math.sin(angle);
for (float p = 0.0; p <= 1.0; p = Math.min(1.0, p + dist / v) {
x = sx + p * (dx - sx);
y = sy + p * (dy - sy);
}
The Math.min expression in the for loop ensures that you end up exactly at the destination point.
If you already have the destination point then it's just as easy. Instead of finding dx and dy from dist and angle you find dist from dx and dy using pythagoras.
More than solution these are some advices.
First, implement all you coordinate variables as floats to prevent rounding precision loss errors and round only right before painting.
Second, define float dx = Prime.mx - x; float dy = Prime.my - y; distance to target from current point (to use later). I would use Math.atan2(dy,dx) to compute angle between current point and target. Then use that angle to increment coordinates like this:
x += Math.cos(angle)*vel;
y += Math.sin(angle)*vel;
Third, check if your object is at target using (dx*dx + dy*dy <= radius*radius) for suitable radius (can be 1).
Also note that if the y axis goes down, then the angle will be CW (clock-wise) instead of CCW (counter-clock-wise).
I need to be able to move my player x and y pixels in the same direction as a point to a point. It's hard to explain, but this is what I am trying to do:
Angles 1 and 2 have to be the same. Point 1 stays the same at (100, 100), however point 2 constantly changes and so must the angles. I have tried this:
moveRectangle.setX(touchEvent.getX());
moveRectangle.setY(touchEvent.getY());
float theta = (float) Math.toDegrees(Math.atan2(touchEvent.getY() - 100,touchEvent.getY() - 100));
float velX = (float) (getSpeed() * Math.cos(theta));
float velY = (float) (getSpeed() * Math.sin(theta));
player.move(velX, velY);
The above code is constantly run when the user puts his finger on moveRectangle (Point 2) and moves it. But the above code does not work. The player just moves in one of two directions. player.move just adds velX and velY velocity. So how can I get the two angles and move the player in the right direction? Thanks.
Would it be easier to approach this problem using a cartesian approach (vectors) versus polar approach (angle and magnitude)? So, if the player is at point p0 and the "finger" is at point p1, then the direction the player should be moving v is given by the vector p1-p0. You can then scale the resulting vector v by the player's speed, and add the player's speed to his position. You can do this easily on a frame-by-frame basis.
Do you need just to know velocity on X and Y axis? You can do it without using trigonometry (just use Pythagorean theorem).
final float deltaX = touchEvent.getX() - player.x; // player.x is point1.x
final float deltaY = touchEvent.getY() - player.y; // player.y is point1.y
final float length = Maths.sqrt((deltaX)^2 + (deltaY)^2);
final float itterations = length / getSpeed();
final float velX = deltaX / itterations;
final float velY = deltaY / itterations;
player.move(velX, velY);
Or you need a code of player moving in cycle?
Remove Math.toDegrees().
From the Math.sin() / cos() Javadoc:
Parameters:
a - an angle, in radians.
Returns:
the sine of the argument.
I got x and y (My position) and also destination.x and destination.y (where I want to get). This is not for homework, just for training.
So what I did already is
float x3 = x - destination.x;
float y3 = y - destination.y;
float angle = (float) Math.atan2(y3, x3);
float distance = (float) Math.hypot(x3, y3);
I got angle and distance but don't know how to make it move directly.
Please help!
Thanks!
Maybe using this will help
float vx = destination.x - x;
float vy = destination.y - y;
for (float t = 0.0; t < 1.0; t+= step) {
float next_point_x = x + vx*t;
float next_point_y = y + vy*t;
System.out.println(next_point_x + ", " + next_point_y);
}
Now you have the coordinates of the points on the line. Choose step to small enough according to your need.
To calculate the velocity from a given angle use this:
velx=(float)Math.cos((angle)*0.0174532925f)*speed;
vely=(float)Math.sin((angle)*0.0174532925f)*speed;
*speed=your speed :) (play with the number to see what is the right)
I recommend calculating the x and y components of your movement independently.
using trigonometric operations slows your program down significantly.
a simple solution for your problem would be:
float dx = targetX - positionX;
float dy = targetY - positionY;
positionX = positionX + dx;
positionY = positionY + dy;
in this code example, you calculate the x and y distance from your position to your target
and you move there in one step.
you can apply a time factor (<1) and do the calculation multiple times, to make it look like your object is moving.
Note that + and - are much faster than cos(), sin() etc.
I am making a small game, 2D, and I have a player.
EDIT:
This is what I have right now:
int oldX = player.x;
int oldY = player.y;
int newX = oldX - player.getSpeedX();
int newY = oldY - player.getSpeedY();
if(player.getBounds().intersects(brush1.getBounds())){
player.x = newX;
player.y = newY;
}else{
player.x = oldX;
player.y = oldY;
}
But, it is acting really weird, it changes speed when I go in from one side, etc.
For a formula and code that checks the intersection of a line segment and a circle, have a look at this SO answer.
The rest of the code should be quite clear, before you make a move, check if a collision occurs, if it would, don't move.
Depending on the behaviour you prefer, you could also move as close to the wall as possible and then move parallel to it to let the circle "slide" along the wall. This can be done by projecting the movement vector on a line with the same direction as the wall.
EDIT: Some comments on how to use the code from the answer to check for collisions:
The code uses a Dot function that computes the dot product of two vectors. You can either create a Vector class (a good exercise and it is useful for such projects) or compute just the dot product directly using the formulas here.
A vector class will make some of the code easier to read, but you can also operate on floats directly. For example, let's have a look at the computation of d (Direction vector of ray) and f (Vector from center sphere to ray start) as described at the start of the answer. With a vector class, this will be as easy as the formulas used there:
// L, E, C, d and f are of some vector type
d = L - E;
f = E - C;
Without a vector class, you'll have seperate x/y coordinates for all of the variables, bloating the code a bit but doing just the same:
// Twice as much variables, but all of them are usual floats.
dx = Lx - Ex;
dy = Ly - Ey;
fx = Ex - Cx;
fy = Ey - Cy;
I think your code snippet has a few bugs. Suggested fixes:
int oldX = player.x;
int oldY = player.y;
// *add* speed to old pos to get new pos
int newX = oldX + player.getSpeedX();
int newY = oldY + player.getSpeedY();
if(player.getBounds().intersects(brush1.getBounds())){
// Collision! Keep old position.
// Reverse speed (just a suggestion).
player.setSpeedX(-player.getSpeedX());
player.setSpeedY(-player.getSpeedY());
}else{
// No collision. Set position to *new* pos, not old pos.
player.x = newX;
player.y = newY;
}