I have a sprite that I want to rotate smoothly and face the opposite direction:
This is my render function:
public void render() {
Gdx.gl.glClearColor(0.7f, 0.7f, 0.2f, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
spriteBatch.begin();
if(bug.getX() >= (int)(Gdx.graphics.getWidth() - 100) && bug.getY() >= (int)(Gdx.graphics.getHeight() - 100)){
bug.rotate(rotDeg);
turn = !turn;
}
else if(bug.getX() <= 0 && bug.getY() <= 0){
bug.rotate(rotDeg);
turn = !turn;
}
if(!turn){
bug.translate(v.x * Gdx.graphics.getDeltaTime(), v.y * Gdx.graphics.getDeltaTime());
}
else{
bug.translate(-(v.x * Gdx.graphics.getDeltaTime()), -(v.y * Gdx.graphics.getDeltaTime()));
}
bug.draw(spriteBatch);
spriteBatch.end();
}
What my code does is that when it reaches the destination (top right corner, or bottom left corner), it immediatly turns facing the opposite direction. How would I make this turn smoothly?
I assume that "smoothly" would mean for you that the turning direction does not flip directly when reaching the border, but instead, it should stop directly and accelerates over a little time.
Therefore use three float values instead of rotDeg: currentRotation, rotationStep and maxRotation. With them you can model a smooth acceleration:
You call bug.rotate(currentRotation) in every render call. When reaching the border, set currentRotation = 0. Now everytime you call bug.rotate() increase currentRotation by rotationStep as long as currentRotation < maxRotation. You have to search for appropriate values for them depending on your application speed.
In case you want to let your bug fade out the screen a little, meaning not stopping the rotation immediately, use a further flag, that indicates whether the rotation speed is increasing or decreasing in the case |currentRotation| < maxRotation. Depending on this flag either increment or decrement currentrotation by rotationStep. This would make the border feel like rubber for the user, where the sprite fades in, stops and smoothly accelerates out.
Related
I'm trying to get this Vector3 to accelerate when it's to the right and decelerate when it's not. This is my update method. Why doesn't it accelerate? It has a constant speed. I know you could just add -5 to vel.xif it's to the left and +5 if it's to the right and then stop it when vel.x is 0 but I'm trying to make this work. I really don't understand what it is I'm doing wrong.
In the class I have declared two Vector3s (used in update)
public void update(float delta) {
timePassed += Gdx.graphics.getDeltaTime(); //irrelevant
if (pos.x > Kiwi.WIDTH / 2)
vel.add(-5, 0, 0);
vel.x *= 0.8f;
vel.scl(delta);
pos.add(vel.x, 0, 0);
vel.scl(1 / delta);
}
New answer
public void update(float delta) {
if (pos.x > Kiwi.WIDTH / 2)
vel.add(-5, 0, 0);
vel.x *= 0.8f;
vel.scl(delta);
pos.add(vel.x, 0, 0);
vel.scl(1 / delta);
}
The update method is called once for every frame. So what happens with your code above is that you reduce the velocity by 20% every frame, meaning if you have 60FPS, you will reduce your velocity to 0.8^60 = 1.eE-6 or practically zero in one second when you're to the left half of the screen.
You also want to to accelerate when it is to the right... Accelerate in which direction? What you're doing now is that if the position is to the RIGHT half of the screen, then it accelerates to the LEFT meaning as soon as you get to the RIGHT half it moves back to the LEFT! Change -5 to +5 and then it will fly off because you're increasing speed by 5*60=300 px/s^2. You need to scale the acceleration by the time delta.
Also you will need to scale the deacceleration by the time delta too. The divide by delta (1/delta) doesn't make any sense either so try this:
public void update(float delta) {
if (pos.x > Kiwi.WIDTH / 2)
vel.add(5*delta, 0, 0); // Acceleration is now 5 px/s^2 to the right
vel.x -= vel.x*0.2f*delta; // Decay speed by 20% every second
pos.add(vel.x*delta, 0, 0); // Move position by speed.
}
Old answer below
Without more code to go on, all I can do is show you the correct way to do the calculations in the general case.
If you want to use a Euler's method for solving the motion ODEs this is how you do it:
// Object state
speed = ...; // Unit is [m/s]
position = ...; // Unit is [m]
// Inputs
frameDelta = ...; // Unit is [s]
acceleration = ...; // Unit is [m/s^2]
// Update step
speed += acceleration * frameDelta; // Unit [m/s^2 * s] = [m/s];
position += speed * frameDelta; // Unit [m/s * s] = [m]
If you want the speed to "decay" you can do it by adding a suitable deacceleration (or drag/wind resistance/friction whatever you want to call it):
decayRate = ...; // Unit is [m/(m*s^2) = 1/s^2] ((de)acceleration per meter)
acceleration -= speed * decayRate * frameDelta; // Unit is [m/s*1/s^2*s = m/s]
Note: that you do not store the acceleration, you compute it each frame from other inputs (keyboard, mouse, forces, collisions etc).
Note2: Euler's method is the easiest solver you can use to compute the position and speed from acceleration. It comes with some problems, it is not as accurate as for example Runge-Kutta and it can also exhibit some stability issues with the oscillation.
You know when you see something shaking like it's crazy when it is stuck in a game? Yeah, that's probably Euler's method becoming unstable.
Here is a link to a video I recorded of my issue: https://sendvid.com/rjpi6vnw
You'll notice that initially, I start at tile (0, 0) then after moving my character up and down multiple times, the screen will only go up to (1,0). So I lost a whole row of playable map. I only lose part of the map when my screen adjusts itself. You'll understand what I mean in a moment. I have a class called Player, and in it I have methods called moveRight(), moveLeft(), moveUp(), and moveDown(). I'm excluding all useless classes and methods in order to not waste your time. Here are my moveDown() and moveUp() methods:
public void moveUp(){
locY1 -= defaultMoveAmount;
if(viewShouldMoveVertically(locX1, locY1) == true){ //locX1 and locY1 refers to the player's bounds location as set by setBounds()
Display.uni.moveMapDown(defaultMoveAmount); //Display.uni just means in the Display class
}
}
public void moveDown(){
locY1 += defaultMoveAmount;
if(viewShouldMoveVertically(locX1, locY1) == true){
Display.uni.moveMapUp(defaultMoveAmount); //defaultMoveAmount is the # of pixels the player moves each time the program updates
}
}
So I have KeyListeners that decide when these methods are called. The viewShouldMoveVertically() method is as follows:
public boolean viewShouldMoveVertically(int X1, int Y1){
if(Y1 < screenCenterY){ //screenCenterY is the number of vertical pixels on my screen/2
return false;
}
return true;
}
The moveMapUp() or moveMapDown() method is then called in the Display class:
int backgroundX1 = 0;
int backgroundY1 = 0;
public void moveMapUp(int moveAmt){
backgroundY1 -= moveAmt;
background.setBounds(backgroundX1, backgroundY1, backgroundX2 , backgroundY2);
}
public void moveMapDown(int moveAmt){
backgroundY1 += moveAmt;
background.setBounds(backgroundX1, backgroundY1, backgroundX2 , backgroundY2);
}
So if you can't view the video at the link I posted, I'll describe the issue. When my character moves close to the edge of the map, I obviously wouldn't want the camera to show areas off of the map. So the camera stops, but my character may continue walking up to the border of the map. If my character is within 540 pixels of the top of the map, the camera won't move(I'm running on a 1920x1080 display). This is intended. When I move the character more than 540 pixels from the top of the map, the camera will now move with the player since he's in the center of the screen. But the issue is that IF and ONLY IF the camera ends up moving away from the top of the map, then I now lose exactly "defaultMoveAmount" pixels from the viewable area when I return to the top again. I can't seem to figure out how to fix this issue. Now, a little more you may end up wanting to know: I have the same issue moving horizontally as I have moving vertically. It is set up in the same way, so there was no point in making you guys read extra code. When viewing the video at the link, I have to click on the play button at the bottom left, or else it tries to make me add an extension to Chrome or something. The solution to my program's issue may end up being quite simple, but I just can't seem to figure it out. I ended up getting sick of it and decided getting a little help would be better than giving up for now. I am a beginner to programming, as I've only had 1 year of programming experience from an AP Compute Science class. I'm sure you may see a few things that seem dumb, and I welcome any suggestions or comments you may have, but please be aware that I am fairly new to this stuff. Primarily motion. And finally, I did not post a compile-able section of code due to things such as the graphics that are required. While I'm on here, if you have any suggestions or good references for figuring out whether a character is within an area in a large tile-like map, such as a door that can be opened, it would be appreciated.
I have solved the issue. Here is my updated viewShouldMoveVertically() method:
public boolean viewShouldMoveVertically(int X1, int Y1){
if( Y1 <= screenCenterY && previousY1 > screenCenterY ){ //if just touched highest camera point from below
return true;
}else if( Y1 >= mapPixelTotalY-screenCenterY && previousY1 < mapPixelTotalY-screenCenterY ){ //if just touched lowest camera point from above
return true;
} else if( Y1 <= screenCenterY){ //if just touched highest camera point from above or is just above
return false;
} else if( Y1 >= mapPixelTotalY-screenCenterY ){ //If touched lowest camera point from below or is just below
return false;
}
return true;
}
It turns out that when the player touches the "highest camera point from below" or the "highest camera point from above", different results are required. If touching the highest camera view from below, the camera still needs to move upwards just one more time. But when touching that same point from above, the camera shouldn't move, since the player was coming from an area that the camera wouldn't be vertically moving at.You can see my solution in the code. Also, I added a "previousY1" that records what the last Y1 value was, so that I can tell whether the player came from above or below.
I'm having some difficulty implementing very basic collision within libGDX. The update code for the "player" is as so:
private void updatePosition(float _dt)
{
Vector2 _oldPos = _pos;
_pos.add(_mov.scl(_dt*50));
_bounds.setPosition(_pos.x-8, _pos.y-12);
if(_game.getMap().checkCollision(_bounds))
{
System.out.println("Collision Detected");
_pos = _oldPos;
_bounds.setPosition(_pos.x-8, _pos.y-12);
}
}
So, initially _oldPos is stored as the values of _pos position vector before applying any movement.
Movement is then performed by adding the movement vector _mov (multiplied by the delta-time * 50) to the position vector, the player's bounding rectange _bounds is then updated to this new position.
After this, the player's new bounding Rectangle is checked for intersections against every tile in the game's "map", if an intersection is detected, the player cannot move in that direction, so their position is set to the previous position _oldPos and the bounding rectangle's position is also set to the previous position.
Unfortunately, this doesn't work, the player is able to travel straight through tiles, as seen in this image:
So what is happening here? Am I correct in thinking this code should resolve the detected collision?
What is strange, is that replacing
_pos = _oldPos;
with (making the same move just made in reverse)
_pos.sub(_mov.scl(_dt*50));
Yields very different results, where the player still can travel through solid blocks, but encounters resistance.
This is very confusing, as the following statement should be true:
_oldPos == _pos.sub(_mov.scl(_dt*50));
A better solution for collision detection would be to have a Vector2 velocity, and every frame add velocity to position. You can have a method that tests if Up arrow key is pressed, add 1 (or whatever speed you would like) to velocity. And if down is pressed, subtract 1 (or whatever speed). Then you can have 4 collision rectangles on player, 1 on top of player, bottom, left, and right. You can say
if(top collides with bounds){
if(velocity.y > 0){
set velocity.y = 0.
}
}
And do the same for down, left and right (eg... for bottom, make sure to test if(velocity.y < 0) instead of if(velocity.y > 0).
EDIT: You code is not working because you set oldPos = pos without instantiating a new Vector2. Which means when you add onto pos, it also changes oldPos. So say oldPos = new Vector2(pos);
try to test future position before move. If collision, don't move.
I have a body following my player in a game I made and the player is of type Actor (scene2D). In his overriding act method he uses libgdx input to move. For example,(pseudo) if(gdxInput.keys(keys.up)){ applyForceToCenter(VElocity) xCoor = body.x yCoor = body.y
How do I make sure the body doesn't slide all over the place? The world has a gravity of 0,0 so if i click the up arrow the player will never stop so in my huge if else if block statement of input i put else linearVelocity = 0 this works however if the player is holding the right arrow key then holds the up arrow key the player moves right more then up as if he is sliding on ice. Please tell me how to turn all gravity off of the player in general. I can't just set the position of the body because im using the bodies as a way of collision and to set the position of the body is to turn off collision.
As soon as you want to stop you will set the velocity to 0 for example
if(!canFall){
velocity.y = 0;
}else
velocity.y += MainScreen.GRAVITY.y;
if(Gdx.input.isKeyPressed(Keys.A)){
velocity.x -= 10 * Gdx.graphics.getDeltaTime();
}
else if(Gdx.input.isKeyPressed(Keys.D)){
velocity.x += 10 * Gdx.graphics.getDeltaTime();
}else
velocity.x = 0;
So once you stop ur velocity will be equal to 0!
Have you got any damping set on your player body?
playerBody.SetLinearDamping(0.2f);
This will reduce positional forces to 0 over time if you're not applying anything to your playerBody.
I'm developing a 2D video game using libgdx. I ran into a problem when I try to make jumping a body.
It does not jump as expected after making it moving to the right.(I can only move to the right or Jump)
If the body jumps before it's moving to the right everything goes fine. But If I decide to make jumping the body after moving it to the right. The body no longer jumps to the same height (It jumps less high). And I don't figure out why..
My method to jump the body :
if (player.isPlayerOnGround()) {
body.applyForceToCenter(0, 200, true);
}
My method to move the body right
if (player.isPlayerOnGround()) {
body.setLinearDamping(0f);
body.setLinearVelocity(1f,0f);
isMoving = true;
}
My method to stop the body moving right :
body.setLinearDamping(5f);
isMoving = false;
The world use a -9.81f gravity and the body 1f for the Mass.
P.S : Sorry for me bad english, it's not my native language.
Thank you.
First thing: never use forces to jump. Force has different effects based on how long that force acted. Second: don't use linearDamping. It makes your physics floaty and not real. You could use impulse instead of force in jumping method (it doesn't work very well actually). I'm using this method and it works perfectly
public void jump() {
if (jumpDelta >= Constants.PLAYER_JUMP_RATE) {
grounded = level.getContactListener().numFootContacts > 0;
if (grounded) {
body.setLinearVelocity(body.getLinearVelocity().x, 7);
jumpDelta = 0;
}
}
}
Where if (jumpDelta >= Constants.PLAYER_JUMP_RATE) prevents from too fast jumping (like two or more jumps at once), grounded = level.getContactListener().numFootContacts > 0; checks if player is on platform and finally this body.setLinearVelocity(body.getLinearVelocity().x, 7); changes body's vertical velocity. Changing velocity works better than applying impulse because impulse doesn't set velocity, it increases velocity. So if player was moving down with vertical velocity -3 m/s then its velocity will become 4, not 7 as we wanted.
P.S. Instead of linear damping i use this method
public void stopMoving() {
if (grounded) {
if (Math.abs(body.getLinearVelocity().x) <= 0.5f)
body.setLinearVelocity(0, body.getLinearVelocity().y);
else
body.applyLinearImpulse(-direction * 0.5f, 0,
body.getPosition().x, body.getPosition().y, true);
} else if (Math.abs(body.getLinearVelocity().x) <= 0.1f)
body.setLinearVelocity(0, body.getLinearVelocity().y);
else
body.applyLinearImpulse(-direction * 0.1f, 0, body.getPosition().x,
body.getPosition().y, true);
}
This method can seem too complex but it's really simple. First part handles body's movement on ground and second in the air. if-statements prevent from changing body's direction while stopping and direction variable can be 1 if body's moving right, -1 if body's moving left and 0 if body isn't moving.