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.
Related
I have a Rectangle object that is placed on the screen and rendered using paintComponent.
I also have a rotation variable that determines the rotation of the object (using right and left keys to rotate) and repaints the object on the screen using Affine Transform.
In the keyPressed method, I have this which allows me to shoot bullets:
else if(key == KeyEvent.VK_SPACE) {
Bullet b = new Bullet(handler, player.x + 10, player.y - 10, ID.Bullet);
handler.addObject(b);
b.setDY(-3*Math.cos(player.rotation));
b.setDX(3*Math.sin(player.rotation));
}
If you see where I create a new bullet, in the second line, the second and third arguments in the new Bullet() are what determines where the bullets are created. Currently they just shoot from the same position on the rectangle regardless of the rotation.
I have failed at allowing the player to shoot a bullet from the direction they are facing, so if anyone has any suggestions that would be very helpful.
So basically you are trying to find the offset Vector from the player's position.
Let's assume, that you have the offset Vector, when you haven't rotated the object.
Then your task would be to rotate the given Vector by the rotation variable.
To do this we first need to look into geometry a bit. Let's think of a 2d-Vector as a triangle, that we can split into it's x and y components. We know, that the rotated Vector should have the same hypothenuse length as the original offset or in other terms the same magnitude.
This means that:
x₀² + y₀² = x₁² + y₁²
Since:
x² + y² = Vector-Magnitude
Your rotation variable keeps track of the inner angle of that "triangle vector" and hence we can describe the lengths as:
x = sin(rotation) * Vector Magnitude
y = cos(rotation) * Vector Magnitude
Now the only thing left to do is evalute those values. To do that, let us jump into the code, shall we?
float magnitude = offset.x*offset.x + offset.y*offset.y;
float x = Math.sin(rotation) * magnitude;
float y = Math.cos(rotation) * magnitude;
Bullet b = new Bullet(handler, player.x + x, player.y + y, ID.Bullet);
possible Errors and fixes
Make sure that you have the correct angle-format. The Java-Math class uses radiens for trigonometric methods.
There could be a constant rotation deviation, depending on what exactly you want your result to look like. This is a rather easy fix, as you just have to add this constant to the rotation variable. In unlikely instances you maybe also need to negate the rotation variable simply by using -rotation in the places I used rotation in my code.
If you know the rotation angle and the width of the rectangle then use this information to rotate the start position of the bullet too. For example to let the start position at the right side:
x = rectMiddleX + width/2 * cos(angle)
y = rectMiddleY + width/2 * sin(angle)
If angle is 0 it will start at the right side
else if(key == KeyEvent.VK_SPACE) {
Bullet b = new Bullet(handler, player.x + width/2 * cos(player.rotation), player.y - width/2 * sin(player.rotation), ID.Bullet);
//if player.x/y is in corner, add width/2 / height/2
handler.addObject(b);
b.setDY(-3*Math.cos(player.rotation));
b.setDX(3*Math.sin(player.rotation));
}
Purpose
I'm implementing a polygon rotation with Java AWT.
I'm already able to draw polygons on the screen, and I'd like to apply a rotation matrix manually upon my polygons coordinates (rotation is done around the lookAt point of the user).
What I've already done
In order to rotate the world, the user first clicks on the screen and then drags the mouse around to perform the rotation.
Let's note the first click point as S, the following point from the drag event as L, and the center of the screen as C.
In order to calculate the rotation angle, when first clicking the screen, I keep a vector from C to S: C-S.
Then, when a drag event occurs, I calculate the vector from C to L: C-L.
I then calculate the angle in radians between C-S to C-L, and that's what I apply on my world.
This works well, and the polygon is indeed rotation around the lookAt point.
My problem
The problem occurs when the user finishes a rotation of PI, and then the polygon is rotating backward.
e.g. When the user starts rotating, the angle starts from 0.1.... 0.2... 1.. 2.. 3.. and in value ~3.1 (I assume PI), the values are starting to go down: 3... 2.. 1.. until 0, and vice versa.
This makes sense since the radians range is [0, PI].
I assume the base vector C-S lies on the right side of X axis, and when the rotation goes down below the X axis the polygon is rotating backwards.
However, I have no idea how to keep the polygon rotating in the same direction all the time (when the user performs a full rotation around the polygon).
Edit
Angle function is:
public final double angle(Vector2D v1)
{
double vDot = this.dot(v1) / ( this.length()*v1.length() );
if( vDot < -1.0) vDot = -1.0;
if( vDot > 1.0) vDot = 1.0;
return ((double) (Math.acos( vDot )));
}
This is a problem of the arcus cosine, acos(cos(x)) is a periodic hat function moving up and down in the range of 0 to pi.
In higher dimensions that can not be avoided, as there is no preferred frame of reference, so there is no way to say that phi should really be -phi. In 2 dimensions there is a prefered orientation of the plane so that one can say what is the first and what the second vector and define a unique angle in positive orientation. Rotate the situation so that the first vector comes to lay on the positive real half axis to get the angle and correct quadrant from the coordinates of the rotated second vector.
Easiest to reconstruct is the complex picture, to compute the angle from a=a.x+i*a.y to b=b.x+i*b.y rotate b back by multiplying with the conjugate of a to get an angle from the zero angle resp. the positive real axis,
arg((a.x-i*a.y)*(b.x+i*b.y))
=arg((a.x*b.x+a.y*b.y)+i*(a.x*b.y-a.y*b.x))
=atan2( a.x*b.y-a.y*b.x , a.x*b.x+a.y*b.y )
Note that screen coordinates use the opposite orientation to the cartesian/complex plane, thus change atan2(y,x) to atan2(-y,x) to get an angle in the usual direction.
public Point rotate(Point original, Point vertex, double angle){
Point translated = new Point(original.x - vertex.x, original.y - vertex.y);
int x = (int)Math.round(translated.x * Math.cos(angle) - translated.y * Math.sin(angle));
int y = (int)Math.round(translated.x * Math.sin(angle) + translated.y * Math.cos(angle));
return new Point(vertex.x+x,vertex.y+y);
}
This is a simple rotation method that you can use to rotate a point around a given vertex.
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.
I believe this is more of a logic question than a java question, sorry.
My intent is rather straightforward, i want the ship to move and rotate with a matrix, with the bitmap ship1 being the center pivot of the rotation. The code works great except the pivot is off by a strange offset. (picture of conundrum linked at bottom)
The default value rotation at 0 works but all the other values seem to slide away from the center, with 180 being the furthest from the center.
centerX = playerValues[Matrix.MTRANS_X] + ship1.getWidth()/2;
centerY = playerValues[Matrix.MTRANS_Y] + ship1.getHeight()/2;
newRotation = ((float) Math.toDegrees(Math.atan2(fingery1 - centerY, fingerx1 - centerX)));
matrix.postRotate((newRotation - prevRotation), centerX, centerY);
prevRotation = newRotation;
if (fingerx1 > playerX) {
xspeed = 1;
} else
if (fingerx1 < playerX) {
xspeed = 0;
} else
if (fingery1 > playerY) {
yspeed = 1;
} else
if (fingery1 < playerY) {
yspeed = 0;
}
matrix.postTranslate(xspeed, yspeed);
matrix.getValues(playerValues);
I tried to draw how the relation of the bitmap looks at different angles. (the blue dot is where I intend to rotate the bitmap around, the arrow pointing right is the only correct one).
http://i.stack.imgur.com/2Yw76.png
Please let me know if you see any errors or any feedback helps! I just need a second pair of eyes on this because mine are going to explode soon.
Consider studying a good computer graphics text re matrix math. Foley and Van Dam is always a safe bet.
The matrix A is applied to point x with multiplication Ax. You have A = RT a rotation with translation post multiplied. The result is RTx which is R (T x) meaning the point is translated then rotated, when you probably meant the opposite.
Additionally it appears you are concatenating incremental changes repeatedly. Floating point errors will accumulate, visible as worsening distortions. Instead maintain orientation parameters x, y, theta for each ship. These are controlled by the UI. Set the matrix from these in each rendering. The transform will be rotation about the point (w/2, h/2) followed by translation to (x, y). But the matrix to effect this is the translation post multiplied by the rotation! Also you must reset the matrix for each ship.
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