This is my first post, so please bear with me. I'll try and be as clear as I can here about my problem.
First off, let me say that I suck at math. I failed it in school, and it pains me to no end that I can't grasp simple math concepts, especially since I love to code. Someone who wants to program, and can't perform basic math operations? Bad combo.
Aaaanyway, on to the problem.
Some context. I am writing an asteroids-type game. It's coming along quite nicely, and I've overcome all my hurdles so far thanks to this site (thank you!), and google. I've searched high and low to a solution to my problem, but it always seems like I run into a solution that either doesn't work, or I just don't understand and can't incorporate it into my code.
The issue involves rotation of the ship. I have an onscreen joystick class that returns the angle that the joystick is being pushed. I use that angle to point the ship in that same direction.
What I want to do is gradually turn the ship towards the angle the user wants to go, using the shortest turn, left or right. In my mind I'm thinking "How the hell do I go to say.. 350 degrees from 5 degrees, going left?". I don't know...
Here is my draw code:
public void draw(Canvas canvas){
canvas.save();
canvas.rotate((float) (fAngle + 90), (float) (dX + (mShip.getIntrinsicWidth() / 2)), (float) (dY + (mShip.getIntrinsicHeight() / 2)));
mShip.setBounds((int)dX, (int)dY, (int)dX + mShip.getIntrinsicWidth(), (int)dY + mShip.getIntrinsicHeight());
mShip.draw(canvas);
canvas.restore();
}
The angle is passed to the fAngle variable from the joystick getangle method. The angle is then increased by 90 degrees because of the image facing.
user.fAngle = oJoystick.getAngle();
So on each game tick, I want to turn the ship towards whatever direction the player wants to go, degree by degree. Any help with this would be greatly appreciated!
Thanks for reading!
In your tick function, instead of:
user.fAngle = oJoystick.getAngle();
Use this (adjust increment to your liking - this controls the rotation speed):
const float increment = 1.0;
float direction;
float joy = oJoystick.getAngle();
float ang = user.fAngle;
float fudge = 5.0;
if (abs (joy - ang) > fudge) {
if (joy > ang) {
if (joy - ang < 180)
direction = 1;
else
direction = -1;
} else if (joy < ang) {
if (ang - joy < 180)
direction = -1;
else
direction = 1;
}
} else // already pointing right direction
direction = 0;
user.fAngle = ang + direction * increment;
if (0 > user.fAngle)
user.fAngle += 360;
if (360 < user.fAngle)
user.fAngle -= 360;
Related
I'm making a simple game that will have a space ship being gravitationally attracted to a star at the center of the screen. The code I have so far moved the rectangle (soon to be a rocket) in a way that is not smooth at all. The rectangle also will not stop when it reaches the star but rather keeps moving well beyond the star before turning back.
Here's my code:
public void move() {
// Deals with gravity towards the center star
if (x > game.getWidth() / 2) {
xVel -= xa;
}
if (x < game.getWidth() / 2) {
xVel += xa;
}
if (y > game.getHeight() / 2) {
yVel -= ya;
}
if (y < game.getHeight() / 2) {
yVel += ya;
}
x += xVel;
y += yVel;
}
I start by calculating where the rocket is located on the screen relative to the center, and then change its acceleration values, xa and ya, as needed. I then add the acceleration values to the Velocity variables, xVel and yVel.
Obviously you change the speed by
yVel += ya;
etc. and thats an acceptable thing to you as it seems from your wording. But that will create a constantly increasing speed (i.e. flying by at the end).
If you want to keep the speed constant you do
if (x > game.getWidth() / 2) {
xVel = -xa;
}
if (x < game.getWidth() / 2) {
xVel = xa;
}
and equivalent. That will have the rectangle oscilate probably around the center. Please update and come up with new questions.
Your spaceship's movement doesn't look smooth, because it is moving with no respect to time. What happens if you open up a CPU intensive application in the background, and your Java process gets less computing time? Now you are not calling move() as often, so your spaceship suddenly slows down.
You need to keep track of how much time has elapsed since the last call to move() - I'm sure you remember the high-school physics formula, distance = velocity * time. There are multiple ways to get the time - see here for a discussion specifically for games.
This will smooth out the movement a bit, but if you want the movement to look like gravity, then you can't just use velocity on its own - you also need to consider acceleration. Your space ship is accelerated towards the star due to its' gravity. The closer your spaceship gets to the star, the stronger the gravitational acceleration becomes. This is another high-school physics formula, which you should be able to find on the web.
You have not supplied all the code but to me it sounds like you are using integers where you should be using floats or doubles.
If you decalre the variables like the following
int x;
int velX;
You will not get the precision needed for smooth animation (there is nothing after the full stop). With integers 5 / 2 == 2 and not 2.5 as integers can only store whole numbers. This is why you are getting jerky motion.
You need to use floating point variables by decalring them as such.
float x = 0.0f;
float vecX;
or
double x = 0.0;
double vecX;
That will give you the smooth motion you want.
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.
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();
}
}
This is developed in JavaFX.
There are 2 balls - a dynamic ball and a stationary one. The first ball(dynamic) bounces off the walls and anything that comes in its way.
The second ball's purpose is to be an obstacle for the first ball. So, whenever the first ball touches the second ball, the first ball should instantly bounce away. Currently, the bouncing has very bad accuracy, and I don't know exactly how to fix it. Sometimes the first ball will bounce properly, but usually it will go INSIDE the second ball, get stuck in there for a moment, and then bounce away. The picture below goes over this exact issue.
Here's my code for detecting collision and responding to it:
//Pythagorean Theorem, to detect collision, by estimating the distance between the two circles
double dx = circle.getLayoutX() - circle2.getLayoutX();
double dy = circle.getLayoutY() - circle2.getLayoutY();
double radii = circle.getRadius() + circle2.getRadius();
double distance = (dx * dx) + (dy * dy);
double minDistance = radii * radii;
// I believe something is missing in the lines below, which is causing the problem.
if (distance < minDistance) { //If circle1(dynamic) collides with circle2(stationary)
c1SpeedX = c1SpeedX * -1; //Inverts direction.
}
I spent hours on google, but I was not able to find an answer. I hope somebody can provide a solution and explain the issue. Thanks a lot in advance everybody!
It's probably because the ball don't always have time to exit the bigger ball when its direction is reversed again and again.
If distance == minDistance:
Just do as you do now.
If distance < minDistance:
The ball is inside the larger one. Then it should already have bounced off and be a bit away. The ball should already have moved sqrt(distance-minDistance) away from the larger ball.
Mmm...
I think you have some formulas errors.
By example, distance and minDistance should be
double distance = Math.sqrt((dx * dx) + (dy * dy));
double minDistance = radii;