How to get to an angle choosing the shortest rotation direction - java

I have a character in my game that must rotate smoothly to get to a desired angle. Consider angle as the current angle and touchAngle as the desired angle which is always between 0 to 360. I want to add +1/-1 to current angle in every game update to get to the desired touchAngle. The problem is first it must chose direction and it must be between 0 to 360. this is my pseudo code:
int touchAngle;
float angle;
public void update()
{
if ((int)angle != touchAngle) angle += ???
}

Since you have values that are always normalized in the interval [0 360] this should not be too hard.
You just need to distinguish two different cases:
angle < touchAngle
angle > touchAngle
in the first case we want to rotate counterclockwise so the update has to be angle =+ 1 (assuming that you want to turn of 1 every update cycle).
In the second case we want to turn clockwise so the update should be angle -= 1.
The problem is that this is not always the shortest way to rotate. For instance if:
angle == 359
touchAngle == 1
we don't want to make all the way 358, 357, 356...instead we want to rotate counterclockwise for just 2 units: 360, 1.
This can be achieved comparing the distance between the angles abs(angle - touchAngle).
If this value is bigger than 180 it means we are going the wrong way, so we have to do the way around so
if(angle < touchAngle) {
if(abs(angle - touchAngle)<180)
angle += 1;
else angle -= 1;
}
else {
if(abs(angle - touchAngle)<180)
angle -= 1;
else angle += 1;
}
of course all of this until ((int)angale != touchAngle).
I might have made mistakes with the cases but this is the principle.

Generally you want to bring in time to the equation, so that you can smoothly change the angle over time. Most setups have a way to get a time it took to render the previous frame and the typical way to do this is to say..
int touchAngle;
float angle;
float deltaTime; //Time it took to render last frame, typically in miliseconds
float amountToTurnPerSecond;
public void update()
{
if((int)angle != touchAngle) angle += (deltaTime * amountToTurnPerSecond);
}
This will make it so that each second, your angle is changed by amountToTurnPerSecond, but changed slowly over each frame the correct amount of change so that it is smooth. Something to note about this is that you wont evenly end up at touchAngle most of the time, so checking to see if you go over and instead setting to touchAngle would be a good idea.
Edit to follow up on comment:
I think the easiest way to attain the correct direction for turn is actually not to use angles at all. You need to get the relative direction from your touch to your character in a 2d space. Typically you take the touch from screen space to world space, then do the calculations there (at least this is what I've done in the past). Start out by getting your touch into world space, then use the vector cross product to determine direction. This looks kind of like the following...
character position = cx, cy
target position = tx, ty
current facing direction of character = rx, ry
First we take the distance between the character and the target position:
dx = tx - cx
dy = ty - cy
This not only gives us how far it is from us, but essentially tells us that if we were at 0, 0, which quadrant in 2d space would the target be?
Next we do a cross product:
cross_product = dx * ry - dy * rx
If this is positive you go one way, if it's negative you go the other. The reason this works out is that if the distance is for instance (-5, 2) then we know that if we are facing directly north, the point is to our left 5 and forward 2. So we turn left.

Related

Make an entity follow a path manipulating the angle

I'm making a simple test program to try and develop a bullet hell game, but my problem is that I want to make an entity move in a desired path, and I don't know where to start.
Their movement system works by having an angle, and making it's cos and sin the x and y vectors for it's movement (that's the method my teacher taught me). Here's the part of the code that involves that
this.dx = Math.cos(Math.toRadians(angle));
this.dy = Math.sin(Math.toRadians(angle));
}
public void tick() {
x+=dx*speed;
y+=dy*speed;
//this makes it reverse when the window's wall
if(x > Game.WIDTH || x < 0) {
dx = -dx;
}
if(y > Game.HEIGHT || y < 0) {
dy = -dy;
}
//this is here so angles are stored correctly
if(angle < 0) {
angle += Math.PI*2;
}else if(angle > Math.PI*2) {
angle -= Math.PI*2;
}
}
But as I've said, I want it to follow a path I choose. I could specify it with an equation or another method (I really don't know what's more practical), but I don't know how to manipulate the angle to make it follow my desired path.
While I'm making this question, I've figured how to make a circle
angle -= 0.05;
this.dx = Math.cos(angle);
this.dy = Math.sin(angle);
but I'd like to have a way to feed in an equation and manipulate the angle in the right way as to make them follow the path the equation provides on a graph
I have a few different potential ideas that might be what you're looking for:
Option 1:
If you want it to follow a smooth path defined by a line on a graph, you could try to parameterise the equation, e.g. have both x and y depend on a variable t, rather than having x and y depend on one another. Then, you can use time as this parameter t, and set the entity's x and y directly based on plugging the time into the parametric equations rather than setting an angle.
Option 2:
If you're okay with the path not being a perfectly smooth curve, you could store the path as just a sequence of points in space. The entity stores which point it's currently heading towards and once that point is within a certain distance it moves onto heading towards the next point instead
Note:
With option 1, it involves updating the entity's x and y directly instead of setting an angle. If you do still want to update the angle so that the entity faces / fires in the direction it's moving, then you could keep track of where the entity is in the previous tick. Then using it's current position and previous position you can get a vector of the direction it's moving in, and use that to set the angle.

Animating translation between two fixed points (Libgdx)

I'm making a 2d game in libgdx and I would like to know what the standard way of moving (translating between two known points) on the screen is.
On a button press, I am trying to animate a diagonal movement of a sprite between two points. I know the x and y coordinates of start and finish point. However I can't figure out the maths that determines where the texture should be in between on each call to render. At the moment my algorithm is sort of like:
textureProperty = new TextureProperty();
firstPtX = textureProperty.currentLocationX
firstPtY = textureProperty.currentLocationY
nextPtX = textureProperty.getNextLocationX()
nextPtX = textureProperty.getNextLocationX()
diffX = nextPtX - firstPtX
diffY = nextPtY - firstPtY
deltaX = diffX/speedFactor // Arbitrary, controlls speed of the translation
deltaX = diffX/speedFactor
renderLocX = textureProperty.renderLocX()
renderLocY = textureProperty.renderLocY()
if(textureProperty.getFirstPoint() != textureProperty.getNextPoint()){
animating = true
}
if (animating) {
newLocationX = renderLocX + deltaX
newLocationY = renderLocY + deltaY
textureProperty.setRenderPoint(renderLocX, renderLocY)
}
if (textureProperty.getRenderPoint() == textureProperty.getNextPoint()){
animating = false
textureProperty.setFirstPoint(textureProperty.getNextPoint())
}
batch.draw(texture, textureProperty.renderLocX(), textureProperty.renderLocY())
However, I can foresee a few issues with this code.
1) Since pixels are integers, if I divide that number by something that doesn't go evenly, it will round. 2) as a result of number 1, it will miss the target.
Also when I do test the animation, the objects moving from point1, miss by a long shot, which suggests something may be wrong with my maths.
Here is what I mean graphically:
Desired outcome:
Actual outcome:
Surely this is a standard problem. I welcome any suggestions.
Let's say you have start coordinates X1,Y1 and end coordinates X2,Y2. And let's say you have some variable p which holds percantage of passed path. So if p == 0 that means you are at X1,Y1 and if p == 100 that means you are at X2, Y2 and if 0<p<100 you are somewhere in between. In that case you can calculate current coordinates depending on p like:
X = X1 + ((X2 - X1)*p)/100;
Y = Y1 + ((Y2 - Y1)*p)/100;
So, you are not basing current coords on previous one, but you always calculate depending on start and end point and percentage of passed path.
First of all you need a Vector2 direction, giving the direction between the 2 points.
This Vector should be normalized, so that it's length is 1:
Vector2 dir = new Vector2(x2-x1,y2-y1).nor();
Then in the render method you need to move the object, which means you need to change it's position. You have the speed (given in distance/seconds), a normalized Vector, giving the direction, and the time since the last update.
So the new position can be calculated like this:
position.x += speed * delta * dir.x;
position.y += speed * delta * dir.y;
Now you only need to limit the position to the target position, so that you don't go to far:
boolean stop = false;
if (position.x >= target.x) {
position.x = target.x;
stop = true;
}
if (position.y >= target.y) {
position.y = target.y;
stop = true;
}
Now to the pixel-problem:
Do not use pixels! Using pixels will make your game resolution dependent.
Use Libgdx Viewport and Camera instead.
This alows you do calculate everything in you own world unit (for example meters) and Libgdx will convert it for you.
I didn't saw any big errors, tho' i saw some like you are comparing two objects using == and !=, But i suggest u to use a.equals(b) and !a.equals(b) like that. And secondly i found that your renderLock coords are always being set same in textureProperty.setRenderPoint(renderLocX, renderLocY) you are assigning the same back. Maybe you were supposed to use newLocation coords.
BTW Thanks for your code, i was searching Something that i got by you <3

How do i tell java its closer to go 350 -> 355 -> 360 -> 05 degrees instead of all the way around

I'm creating a racing game, in which I run into some problems while creating the AI. What I need to do is get the AI from a X,Y position to a "checkpoint" with another X,Y position. The AI driving track will be created by placing different invisible checkpoints around the map that is will go to.
I can get the direction to the checkpoint by calculating the difference in X,Y from me to it and then use tan(Deg) = Y/X, witch gives me the direction I have to go from my current position.
distanceToMoveX = checkpoint1X - this.getX();
distanceToMoveY = checkpoint1Y - this.getY();
newDirection = ((double)distanceToMoveY / distanceToMoveX) * 100;
So now I have the direction in which I have to go from my current position, and I also have a direction in which I'm currently moving. What I want to do now is get my AI to realize its closer to turn right if my current direction is 350 and I want to go to 10. As it is right now I can only get it to turn right or left all the time.
Well I guess somewhere you compute the difference between the actual angle and the next one, something like turn = newDirection - actualDirection.
Doing 10 - 350 = -340 tells you AI it needs to turn -340°.
However, (10 - 350 + 360) % 360 = 20 tells your AI it needs to turn +20°. Adding the 360 is to make sure your angle is positive, so the modulo will work as expected (because -340 % 360 = -340).
What you really want, is never turn more than 180°. Thus, add another 180 before reducing between 0 and 360, and then remove them, that will shift your result in [-180,180] while staying module 360° (thus the same angle) :
turn = (newDirection - actualDirection + 540) % 360 - 180
Then you breakup this turn however you want if you don't want it to do in one go, for example 5 degrees at a time like you seem to be already doing.
This is what you need for choosing the direction (this is an example with hardcoded values):
private int correctedDirectionCalculation(int calculation){
return calculation >= 0 ? calculation : calculation + 360;
}
public void chooseDirection() {
int targetDirection = 5;
int currentDirection = 360;
int goingRightDistance = correctedDirectionCalculation(targetDirection - currentDirection);
int goingLeftDistance = correctedDirectionCalculation(currentDirection - targetDirection);
System.out.println(goingLeftDistance < goingRightDistance ? "Go Left!" : "Go Right!");
}
As for the inverted values on the Y axis when rendering, I guess you just need to invert the Y value to fix it (multiply by -1).
You can use degrees outside the range of (0;360), so you can f.e. go from 360 to 365 and it works as expected. If you want later to, say, compare two angles, just say angle % 360

Cannot understand AffineTransformation, bounding rectangle

Hi I am new to java games programming and I was reading through some code and I couldn't understand a few things. I was hoping someone here would be able to clear a few things for me.
1) I know you use "bounding rectangle" to check for collision. But my question is that when you are creating for example a polygon or square object in java from the grahpics2d/shapes classes. Do they already contain a bounding rectangle? or do we have to create it. Also what is the following the code doing, is it creating a bounding rectangle and also what information is it taking in as arguments.
public Rectangle getBounds(){
Rectangle r;
r = new Rectangle((int)getX() -6,(int) getY() -6,50,50);
return r;
}
2)I know you use something called Affine transformation to transform, rotate and scale stuff in java. But the following code is a bit confusing. Can you expelling what this code is doing:
int rotation=0;
AffineTransformation identity = new AffineTransformation();
g2d.translate(width/2, height/2);
g2d.scale(20,20);
g2d.rotate(Math.toRadians(rotation));
public void keyReleased(KeyEvent k) { }
public void keyTyped(KeyEvent k) { }
public void keyPressed(KeyEvent k) {
switch (k.getKeyCode()) {
case KeyEvent.VK_LEFT:
rotation--;
if (rotation < 0) rotation = 359;
repaint();
break;
case KeyEvent.VK_RIGHT:
rotation++;
if (rotation > 360) rotation = 0;
repaint();
break;
}
}
Now I'm mostly confused about how the if statement is processing. The rotation variable is 0 in the beginning then its decremented to -1 by (rotation--) and it checks whether -1 < 0 which it is and it then sets rotation=359 but after break does rotation goes back to 0?
And whats happening in this line:
g2d.rotate(Math.toRadians(rotation));
Is the if statement sending the value to this rotate method and that method converts it into radians and does radians are the pixels on the screen and it rotates to those pixels. Is this right?
I'll be grateful if someone can explain this to me. And please don't link me to java docs I've already read them and they have not helped me Im looking for an explanation from someone who can make it easier.
Thank you in advance.
Here rotation is the angle in degrees, the value is 0 <= rotation < 360
g2d.rotate(angle) rotates an image. It requires angle to be specified in radians.
Math.toRadians(rotation) translates degrees to radians, the value 0 <= radians < 2Pi
Update:
The code rotates the image when cursor keys pressed. It rotates it by 1 degree to the right or 1 degree to the left.
The full turn is 360 degrees. When it reaches 360 it resets the rotation variable to zero, which is the same angle as 360. Hence :
rotation++;
if (rotation > 360) rotation = 0;
OK I am not familiar with the Java version but in general speaking
The "bounding rectangle" can be easily calculated even if it's not any method available. Of course if the rectangle is not rotated is just the rectangle itself but lets consider the general case where rotation exist and also polygon exist also. The "bounding rectangle"'s coordinated are the min x & y of every corner upto the max x & y of every corner. Just do a loop for all corners. So 2 points are (minX, minY) -> (maxX, maxY)
From the above code I just see a method increasing or decreasing the rotation angle as long a key is pressed. And of course if the angle if greater than 360 or less than 0 the whole circles are extracted from the degrees.
e.g. 360 degrees = 10 degrees
-10 degrees = 350 degrees etc
As for the g2d.rotate(Math.toRadians(rotation)); part, what you do not understand on this code? It just converts degrees to radians (I guess the rotate() methods needs rads) and it just rotates the g2d object.
Hope it helped.

Specific direction from a point: If 'slope' is not sufficient, then what is?

The plan was to calculate the slope between the two points(character and cursor), convert it to an angle to the horizontal, and depending on that angle, switch to a specific character sprite so that it appears to be pointing at the cursor(example: 0-30 degrees, one sprite. 30-60 degrees, another sprite. 60-90 another, etc). The problem I encountered afterward was that the slopes could mirror each other in a way.
I realized the (now seemingly obvious) problem was that having the character at point A and the cursor at point B would have the same slope/angle as the character point B and the cursor at point A. It had no way of knowing the direction to point.
I'm at a loss as to what to do from here. The simplest solution would be to have it so that the direction of the player wouldn't be found out through this way(whether it's facing right or left), but through the arrow keys, but I'm saving that as a last resort as it would cause problems with quickly aiming.
double deltaX = point1.x - point2.x;
double deltaY = point1.y - point2.y;
double angleInRadians = java.lang.Math.atan2(deltaX, deltaY);
double length = java.lang.Math.sqrt(deltaX * deltaX + deltaY * deltaY);
You should be able to use sprite/cursor location along with slope to solve this.
Pseudocode:
if(slope is positive and cursor is to the right of sprite)
{
sprite should face right (first quadrant, 0-90 degrees)
}
else if( slope is positive and cursor is to the left of sprite)
{
sprite should face left (third quadrant, 180-270 degrees)
}
//etc

Categories

Resources