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.
Related
So, my problem is that I'm currently working on a path-finding technique in an open world with tiles of various sizes. The object needs to find an optimal path to a destination inside an infinite world (it generates on the fly), which is filled with tiles of various sizes (which are not located on a set grid - they can have any location and size - and neither have to be integers). (The object has access to the data of all the tiles via and ArrayList). Now some factors that make this problem more difficult:
The objects itself has a size and cannot move through tiles. Therefore, it is possible for a path to exist that is too narrow for the object to move through.
The target destination may itself be a moving object.
It is possible for there to be dozens of such objects at the same time - so it is necessary for the algorithm to either be light on the system or for the path to be calculated in a few separate ticks of the program.
I tried implementing solutions for maze-solving techniques, but the main problem is the in most mazes, the tiles can only have very specific coordinates (such as whole numbers) and are always the same size.
I also tried rendering the scene as a giant conventional maze where tiles are actually pixels of tiles (so if i have a 20x40 tile it becomes a 20x40 block of 1x1 tiles), but ran into performance issues and the still didn't solve the issue with a path potentially being to narrow for the object to fit through.
EDIT:
Terribly sorry for my poor wording before, that happens when I'm trying to rush to a solution without fully understanding the question. So what I'm using the algorithm for at the moment is for NPC enemies to find their way to the player around obstacles. Here is an example of a scene:
The black circle with an arrow is the player, the black bush-like blobs are the NPC enemies. So this my my current algorithm I'm using for the enemy AI:
void move() { //part of the Enemy class, this function is called once each tick for every enemy
PVector velocity = new PVector(speed*game.dt, 0); //speed is a pre-set float denoting the enemy's speed, game.dt is deltatim
velocity.rotate(atan2(game.player.location.y-location.y, game.player.location.x-location.x)); //game.player.location is a PVector of the player's position, location is a PVector of this enemy's position
boolean init_collision = getTileCollision(); //getTileCollision is a boolean of whether this enemy is colliding with any tile
location.add(velocity);
boolean collision = getTileCollision();
if (!init_collision && collision) { //if the enemy happens to spawn inside a tile, let is move out of it before checking for collision
location.sub(velocity);
if (desired_heading != -1) { //desired heading is the angle, in radians, of which 90-degree angle the enemy wants to move in, by default set to -1 (see my descrition of this algorithm below)
velocity = new PVector(speed*game.dt, 0);
velocity.rotate(desired_heading);
location.add(velocity);
if (getTileCollision()) {
location.sub(velocity);
velocity = new PVector(speed*game.dt, 0);
velocity.rotate(current_heading); //current heading the an angle, in radians, of which 90-degree angle the enemy is currently moving in. set to -1 by default but can not equal -1 if desired_heading is not -1
location.add(velocity);
if (getTileCollision()) {
location.sub(velocity);
desired_heading = -1;
current_heading = -1;
}
} else {
desired_heading = -1;
current_heading = -1;
}
} else {
float original_heading = velocity.heading();
desired_heading = radians(round(degrees(velocity.heading())/90.0)*90.0); //round to the nearest 90 degrees
velocity = new PVector(speed*game.dt, 0);
velocity.rotate(desired_heading);
location.add(velocity);
if (getTileCollision()) {
location.sub(velocity);
}
float turn = radians(90);
while (true) { //if it cant move, try rotating 90 degrees and moving
velocity.rotate(turn);
location.add(velocity);
if (!getTileCollision() && abs(round(degrees(current_heading)) - round(degrees(velocity.heading()))) != 180) {
current_heading = velocity.heading();
break;
} else {
location.sub(velocity);
}
}
}
} else {
desired_heading = -1;
current_heading = -1;
}
}
So what my terrible code hopes to accomplish is the the enemy first tries to move directly at the player. If it encounters an obstacle, it will round its angle to the nearest 90 degrees, set desired_heading to this and try to move through. If it cant, it will rotate another 90 degrees and so forth, always keeping the original rounded angle in mind.
This doesn't work remotely well as first of all, rotating 90 degrees has a 50% chance to go in the exact wrong diretion, so I tried adding
if (abs(original_heading - velocity.heading()+turn) < abs(original_heading - velocity.heading()-turn)) {
turn = radians(-90);
}
right before the while (true) but that broke the algorithm completely (sometimes the enemy will freeze in deep thought and not move ever again).
What am I doing terribly wrong? Should I try a different algorithm or does this one have potential?
I hope this is a better question now...
I am currently making a 2D Platformer in Java. I am a beginner so take it easy.
I have a problem with the gravity in the game. I am using different variables for falling and jumping. I am using a tiled map. So lets get to the point.
My fall method works like this -
if(collisionDown == false) {
characterY += fall;
fall ++;
{
fall is equal to 4. and If collisionDown is true it resets back to 4.
My jump method is almost the same:
if(key.E == true && collisionDown == true) {
characterY -= jump;
jump --;
}
jump is equal to 16. and If collisonDown is true it resets back to 16.
Now problem is: imagine the character is jumping. its in the air and while going down characterY += fall; lets say characterY = 250 and fall is equal - 15 at that exact moment. The next solid tile below the character starts at Y position 255. character is at 250 and does NOT detect collision so next frame it adds 15 to characterY which is 250 + 15 = 265. At that point the character has "entered" the solid tile which was at positionY 255.
I have "fixed" that so the character gets back ON TOP of the solid tile (and that is visible and annoying.) That is not the perfect solution because it slows the character 1 frame each time it enters a solid tile(which is because it detects left and right collision and the character can't move). The character visibly stutters if I can say it like that.
I need a solution for that problem but can't think of any.
So if you make a suggestion I would be happy. Thank you.
I would probably use something like the following.
if(collisionDown == false) {
characterYnext = characterY + fall; //Get Next Position
if(NextMovementCollides()){ //Basically if next position is too far.
characterYnext += difference_between(CharacterY,Ground); //This should move the character to the ground state.
fall = 0; //No longer falling so reset the value.
}
else{characterY += fall; fall++;} //Otherwise continue falling like normal.
}
DISCLAIMER: I'm not a java programmer so my syntax might be a bit off.
This should work, just plug in your game logic where it would make sense.
The way I usually handle this, is to pre-check movement:
private int moveDown(int fall){
if (isSolidBlock(characterX, characterY + fall)){
//Knowing the height of your block, calculate some kind of reduction. If your block height is 40, it's probably something like (characterY + fall)%40
fall = 4;
collisionDown = true;
return maxFallUntilSolidBlockReached();
}
fall++;
return fall;
}
private boolean isSolidBlock(int x, int y){
//Implement some kind of check if at (x,y) there's a solid block.
}
Then just do this for the fall calculation:
if(collisionDown == false) {
characterY += moveDown(fall);
}
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 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.
Here is my code:
/*
Scott Landau
Robot Lab Assignment 1
*/
// Standard Java Libs
import java.io.*;
// Player/Stage Libs
import javaclient2.*;
import javaclient2.structures.*;
import javaclient2.structures.sonar.*;
// Begin
public class SpinningRobot
{
public static Position2DInterface pos = null;
public static LaserInterface laser = null;
public static void main(String[] args)
{
PlayerClient robot = new PlayerClient("localhost", 6665);
laser = robot.requestInterfaceLaser(0, PlayerConstants.PLAYER_OPEN_MODE);
pos = robot.requestInterfacePosition2D(0,PlayerConstants.PLAYER_OPEN_MODE);
robot.runThreaded (-1, -1);
pos.setSpeed(0.5f, -0.25f);
// end pos
float x, y;
x = 46.0f;
y = -46.0f;
boolean done = false;
while( !done ){
if(laser.isDataReady()) {
float[] laser_data = laser.getData().getRanges();
System.out.println("== IR Sensor ==");
System.out.println("Left Wall Distance: "+laser_data[360]);
System.out.println("Right Wall Distance: " +laser_data[0]);
// if laser doesn't reach left wall, move to detect it
// so we can guide using left wall
if ( laser_data[360] < 0.6f ) {
while ( laser_data[360] < 0.6f ) {
pos.setSpeed(0.5f, -0.5f);
}
} else if ( laser_data[0] < 0.6f ) {
while(laser_data[0<0.6f) { pos.setSpeed(0.5f, 0.5f);
}
}
pos.setSpeed(0.5f, -0.25f);
// end pos?
done = ( (pos.getX() == x) && (pos.getY() == y) );
}
}
}
} // End
I was trying to have the Roomba go continuously at a slight right curve, quickly turning away from each wall it came to close to if it recognized it with it's laser. I can only use laser_data[360] and laser_data[0] for this one robot. I think this would eventually navigate the maze.
However, I am using the Player Stage platform, and Stage freezes when the Roomba comes close to a wall using this code, I have no idea why.
Also, if you can think of a better maze navigation algorithm, please let me know.
Thank you!
It seems like you get stuck against a wall because you are oscillating around distance 0.6.
For instance:
Drive angling toward wall, eventually reaching < 0.6f
Rotate away from wall, stop when > 0.6f. In other words 0.6000001f (for instance)
Resume normal trajectory.
Almost immediately < 0.6f
Goto 2
You will get stuck angling slightly toward and slightly away from the wall.
You need a buffer zone. Once you get within MIN_DIS distance from the wall you need to rotate away until you reach some BUFFER_DIS from the wall where BUFFER_DIS > MIN_DIS by a fair amount.
EDIT:
Looks like you have a few issues. First you are checking if the laser_data is close to a wall, and if it is you are rotating toward the wall. You need to check if it is NOT close to a wall, and rotate toward it.
Second, you are in a very tight while loop, adjusting wheel speeds. This will probably result in bad things. You need to call a trackWall() function when you are too far from the wall, which will poll laser.isDataReady() in a separate loop. Either that, or you need a state-machine in your main polling loop. If not, you are pointlessly adjusting wheel speed with stale laser data.
Third, can you guarantee that the wall will always be within 0.6f? If not, your robot will spin infinitely if it gets to far from the wall.