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.
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));
}
I have tried to make an algorithm in java to rotate a 2-d pixel array(Not restricted to 90 degrees), the only problem i have with this is: the end result leaves me with dots/holes within the image.
Here is the code :
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
int xp = (int) (nx + Math.cos(rotation) * (x - width / 2) + Math
.cos(rotation + Math.PI / 2) * (y - height / 2));
int yp = (int) (ny + Math.sin(rotation) * (x - width / 2) + Math
.sin(rotation + Math.PI / 2) * (y - height / 2));
int pixel = pixels[x + y * width];
Main.pixels[xp + yp * Main.WIDTH] = pixel;
}
}
'Main.pixels' is an array connected to a canvas display, this is what is displayed onto the monitor.
'pixels' and the function itself, is within a sprite class. The sprite class grabs the pixels from a '.png' image at initialization of the program.
I've tried looking at the 'Rotation Matrix' solutions. But they are too complicated for me. I have noticed that when the image gets closer to a point of 45 degrees, the image is some-what stretched ? What is going wrong? And what is the correct code; that adds the pixels to a larger scale array(E.g. Main.pixels[]).
Needs to be java! and relative to the code format above. I am not looking for complex examples, simply because i will not understand(As said above). Simple and straight to the point, is what i am looking for.
How id like the question to be answered.
Your formula is wrong because ....
Do this and the effect will be...
Simplify this...
Id recommend...
Im sorry if im asking to much, but i have looked for an answer relative to this question, that i can understand and use. But to always either be given a rotation of 90 degrees, or an example from another programming language.
You are pushing the pixels forward, and not every pixel is hit by the discretized rotation map. You can get rid of the gaps by calculating the source of each pixel instead.
Instead of
for each pixel p in the source
pixel q = rotate(p, theta)
q.setColor(p.getColor())
try
for each pixel q in the image
pixel p = rotate(q, -theta)
q.setColor(p.getColor())
This will still have visual artifacts. You can improve on this by interpolating instead of rounding the coordinates of the source pixel p to integer values.
Edit: Your rotation formulas looked odd, but they appear ok after using trig identities like cos(r+pi/2) = -sin(r) and sin(r+pi/2)=cos(r). They should not be the cause of any stretching.
To avoid holes you can:
compute the source coordinate from destination
(just reverse the computation to your current state) it is the same as Douglas Zare answer
use bilinear or better filtering
use less then single pixel step
usually 0.75 pixel is enough for covering the holes but you need to use floats instead of ints which sometimes is not possible (due to performance and or missing implementation or other reasons)
Distortion
if your image get distorted then you do not have aspect ratio correctly applied so x-pixel size is different then y-pixel size. You need to add scale to one axis so it matches the device/transforms applied. Here few hints:
Is the source image and destination image separate (not in place)? so Main.pixels and pixels are not the same thing... otherwise you are overwriting some pixels before their usage which could be another cause of distortion.
Just have realized you have cos,cos and sin,sin in rotation formula which is non standard and may be you got the angle delta wrongly signed somewhere so
Just to be sure here an example of the bullet #1. (reverse) with standard rotation formula (C++):
float c=Math.cos(-rotation);
float s=Math.sin(-rotation);
int x0=Main.width/2;
int y0=Main.height/2;
int x1= width/2;
int y1= height/2;
for (int a=0,y=0; y < Main.height; y++)
for (int x=0; x < Main.width; x++,a++)
{
// coordinate inside dst image rotation center biased
int xp=x-x0;
int yp=y-y0;
// rotate inverse
int xx=int(float(float(xp)*c-float(yp)*s));
int yy=int(float(float(xp)*s+float(yp)*c));
// coordinate inside src image
xp=xx+x1;
yp=yy+y1;
if ((xp>=0)&&(xp<width)&&(yp>=0)&&(yp<height))
Main.pixels[a]=pixels[xp + yp*width]; // copy pixel
else Main.pixels[a]=0; // out of src range pixel is black
}
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.
Im developing simple game. I have cca. 50 rectangles arranged in 10 columns and 5 rows. It wasn't problem to put them somehow to fit the whole screen. But when I rotate the canvas, let's say about 7° angle, the old coordinates does't fit in the new position of the coordinates. In constructor I already create and define the position of that rectangles, in onDraw method I'm drawing this rectangles (of course there are aready rotated) bud I need some method that colliding with the current rectangle. I tried to use something like this (i did rotation around the center point of the screen)
int newx = (int) ((x * Math.cos(ROTATE_ANGLE) - (y * Math.sin(ROTATE_ANGLE))) + width / 2);
int newy = (int) ((y * Math.cos(ROTATE_ANGLE) + (x * Math.sin(ROTATE_ANGLE))) + height / 2);
but it doesn't works (becuase it gives me absolute wrong new coordinates). x and y are coordinates of the touch that I'm trying to calculate new position in manner of rotation. ROTATE_ANGLE is the angle of rotation the screen.
Does anybody know how to solve this problem, I already go thorough many articles, wiki, wolframalpha categories but not luck. Maybe I just need some link to understand problem more.
Thank you
You use a rotation matrix.
Matrix mat = new Matrix(); //mat is identity
mat.postRotate(ROTATE_ANGLE); //mat is a rotation matrix of ROTATE_ANGLE degrees
float point[] = {10.0, 20.0}; //create a new float array representing the point (10, 20)
mat.mapPoints(point); //rotate the point by the requested amount
Ok, find the solution.
First it is important to convert from angle to radian
Then I personly need to negate that radian value.
That's all, this solution is correct
I just started playing arround with 3D in libgdx. I allready know how to draw basic Models and i tryed to play arround with the CameraController. Now i want to create a FirstPersonCamera or FirstPersonCameraController. I thought about extending PerspectiveCamera and adding a MyMovingObject target to it. The MyMovingObject would hold a x, y, z position, where y is a constant value, cause i can't move up/down at the moment. So my movement is basicly in 2D. The MyMovingObject would also store the left/right rotation, needed for its moving direction/ xSpeed, zSpeed. But the Player should also be able to look up and down, and this up/down rotation is not really needed for the MyMovingObject, as it only changes the view and no other properties. So i am not sure if i go the right way.
I want to be able to go forward, left, right, backward by using W,A,S,D and rotate left right by using the mouse. Also i want to look up and down by using the mouse, like in most First Person games.
Should i use another way, not creating my own camera by extending PerspectiveCamera?
Or is this approach good and i just have to store the up/down rotation in the MyMovingObject to, also if it is only needed for the view?
Or would it be better to controll the camera with W,A,S,D and mouse and update the MyMovingObjects position, depending on cameras position and rotation?
I hope you understand what I mean. It seems a bit complicated to explain it (at least for me).
EDIT: I am now using Vector3 direction, Vector3 position and Vector3 size for my NPCs and the player. I calculate the speed by doing: xSpeed = direction.x / (direction.x + direction.z) * speed; the same for zSpeed. By doing this i "filter" the y value out of it and i get only the percent of x and y. The only problem is, that when i look straight up x and z are 0. I could fix this by using an UpVecotr, which gets rotated when i do a "Pitch-rotation". But how do i rotate him? I need to rotate it arround the sideway Vector. Thanks
EDIT: The rotation and movement work now (see my answer), but i have really big problems with the limitation of the "Pitch-rotation". I am using: if (direction.y < 0.9 && angle > 1) doPitchRotation(); else if (direction.y > -0.9 && angle < 1) doPitchRotation(); so if i rotate down and i still look down at least at -0.9 y it just does not perform the rotation. But what really happens: I rotates to - 0.9 then it rotates arround the Y-Axis and at the other side it rotates up, even if i move my mous down. Can you explain why? Why does the Y-Axis flip when i turn arround by looking down?
EDIT: It works now. It seems like my upVector got some wrong values sometimes. For landbased cams you can also use crossproduct of Y-Axis and direction Vector. No need for upVector.
Hey thanks for sharing this link. I found it very useful.
Here's my code on rotating a land based camera and it seems to work without problems.
private int mouseX = 0;
private int mouseY = 0;
private float rotSpeed = 0.2f;
#Override
public boolean mouseMoved(int screenX, int screenY) {
int magX = Math.abs(mouseX - screenX);
int magY = Math.abs(mouseY - screenY);
if (mouseX > screenX) {
cam.rotate(Vector3.Y, 1 * magX * rotSpeed);
cam.update();
}
if (mouseX < screenX) {
cam.rotate(Vector3.Y, -1 * magX * rotSpeed);
cam.update();
}
if (mouseY < screenY) {
if (cam.direction.y > -0.965)
cam.rotate(cam.direction.cpy().crs(Vector3.Y), -1 * magY * rotSpeed);
cam.update();
}
if (mouseY > screenY) {
if (cam.direction.y < 0.965)
cam.rotate(cam.direction.cpy().crs(Vector3.Y), 1 * magY * rotSpeed);
cam.update();
}
mouseX = screenX;
mouseY = screenY;
return false;
}
This works for landbased cameras. If you want to make a flightcontroll camera, you have to do a pitch rotation arround the cam.direction.crs(cam.up). Instead of using the Vector3.cpy() i would store a Vector3 help, which gets those temporary values, because Vector3.cpy() creates a new Vector3 and this operation is performed every render loop.
For flightcontroll cameras you also need to add a roll rotation and do the yaw rotation arround the cam.up Vector.
This article is really helpful in my opinion. I have found a solution which should work, but i haven't tryed it yet. My MovingObjects all have a Vector3 position, Vector3 direction, Vector3 size and Vecotr3 upVector. The Player class extends this MovingObject class and adds Mouse and Keycontroll to the movement.
In the MovingObject class i have the moethods:
rotateYaw(float degrees): rotates the Vector3 direction arround the Y-Axis by the given degrees (libgdx has a rotate function for Vector3)--> Simple
rotatePitch(float degrees): rotates the Vector3 direction arround the: direction.cross(Vector3.Y), which is the rotated side Vector of your MovingObject, by the given degrees. Also a Pitch-Rotation has to rotate the upVector, so you rotate the upVector arround the same axis, by the given degrees. As soon as you understand this it is simple.
move(delta) moves your MovingObject in x,z direction by doing:
if (direction.y == 1) {
// You are looking straight up, no x,z direction, move in the opposite
// direction of upVector
xSpeed = upVector.x / (Math.abs(upVetor.x) + Math.abs(upVector.z)) * (-speed);
zSpeed = upVector.z / (Math.abs(upVetor.x) + Math.abs(upVector.z)) * (-speed);
position.add(xSpeed * delta, 0, ySpeed * delta);
} else if (direction.y == -1) {
// You are looking straight down, no x,z direction, move in the direction of
// upVector
xSpeed = upVector.x / (Math.abs(upVetor.x) + Math.abs(upVector.z)) * speed;
zSpeed = upVector.z / (Math.abs(upVetor.x) + Math.abs(upVector.z)) * speed;
position.add(xSpeed * delta, 0, ySpeed * delta);
} else {
// You are not looking straight up or down, so you have x,z direction. Use
// that.
xSpeed = direction.x / (Math.abs(direction.x) + Math.abs(direction.z)) * speed;
zSpeed = direction.z / (Math.abs(direction.x) + Math.abs(direction.z)) * speed;
position.add(xSpeed * delta, 0, ySpeed * delta);
}
I did not test this until now, but i think it should work. Note, that in the Pitch-rotation you should also limit it to straight up/ straight down. Do this by checking the signum of x and z. If they change while you are doing a Pitch-rotation you rotated over 90 degrees.
I am stil waiting for other answers and if i am wrong please correct me!
EDIT: I tested it. It works like this, but there are a few things to take care of:
direction.cross(upVector) changes the direction Vector. So store that data somewhere first! After using it reset the direction Vector.
The Pitch limitation has a problem: If you controll signum change, as i suggested the following happens: you look straight up, signum x and signum z are 0. You look down, signum changes and your action (limiting) starts. So take care that you also check, if it is not zero.
I stil don't know how to do the pitch limitation and i edit my question to explain my issue.
Think about normalizing your direction and upVector whenever you change something!
I think this should work pretty good. If you have any improvements let me know and i will update this here. If you have another solution please add an answer! Thanks
I know that this question already has good answers. But I had some issues with the selected answer. And I just want to help someone who is looking for the same solution.
I noticed some strange behaviour with the selected answer. I doesn't keep the Y exis as up. Which is of course very important on a fps. So this one is not perfect but I wanted to put this here.
// put into the create() method.
Gdx.input.setInputProcessor(new InputProcessor() {
private int dragX, dragY;
float rotateSpeed = 0.2f;
// dont' forget to override other methods.
#Override
public boolean mouseMoved(int screenX, int screenY) {
Vector3 direction = cam.direction.cpy();
// rotating on the y axis
float x = dragX -screenX;
// change this Vector3.y with cam.up if you have a dynamic up.
cam.rotate(Vector3.Y,x * rotateSpeed);
// rotating on the x and z axis is different
float y = (float) Math.sin( (double)(dragY -screenY)/180f);
if (Math.abs(cam.direction.y + y * (rotateSpeed*5.0f))< 0.9) {
cam.direction.y += y * (rotateSpeed*5.0f) ;
}
cam.update();
dragX = screenX;
dragY = screenY;
return true;
}
});
NOTE: Don't use any camera controllers.
NOTE2: This might come handy: Gdx.input.setCursorCatched(true);
EDIT: I just wanted to share the walking function I use to change position of the camera with wasd keys. When W key is down the forward is true. And when the key is up forward is false. Other directions have the same principle. To detect the key down and up, please use the InputProcessor in the above code.
This creates movement in the two dimensional space (X-Z axises). The direction of the camera will not change the direction of the movement as we are eliminating the Y axis. of the direction.
One must now even if camera is directed to the sky (at a non 90 degree angle with the ground), the length of the direction vector is not fixed to 1.0f. So there will not be any loss of movement speed.
To test this I rotated camera up and down (moved the mouse forward and backward) and the x and z values of the direction vector didn't change. So when the y axis of the direction vector is eliminated, we have a 2d direction vector which doesn't effected by the Y angle of the camera.
private void walking(float timeElapsed) {
float speed = movementSpeed;
if ((forward | back) & (right | left)) {
speed /= Math.sqrt(2);
}
System.out.println(speed);
if (forward) {
Vector3 v = cam.direction.cpy();
v.y = 0f;
v.x *= speed * timeElapsed;
v.z *= speed * timeElapsed;
cam.translate(v);
cam.update();
}
if (back) {
Vector3 v = cam.direction.cpy();
v.y = 0f;
v.x = -v.x;
v.z = -v.z;
v.x *= speed * timeElapsed;
v.z *= speed * timeElapsed;
cam.translate(v);
cam.update();
}
if (left) {
Vector3 v = cam.direction.cpy();
v.y = 0f;
v.rotate(Vector3.Y, 90);
v.x *= speed * timeElapsed;
v.z *= speed * timeElapsed;
cam.translate(v);
cam.update();
}
if (right) {
Vector3 v = cam.direction.cpy();
v.y = 0f;
v.rotate(Vector3.Y, -90);
v.x *= speed * timeElapsed;
v.z *= speed * timeElapsed;
cam.translate(v);
cam.update();
}
}