Right now my code is as follows:
if(Gdx.input.justTouched()) {
if(cl.isPlayerOnGround()) {
playerBody.applyForceToCenter(0, B2DVars.jumpForce, true);
}
}
It works for the most part, however if you click both mouse buttons at the same time on desktop, or multitouch on android, the jumpForce gets multiplied. I need to ignore all clicks/taps but the first until the player touches the ground again.
(cl is the contact listener.)
There are a number of ways to solve this but in my option the best way to solve this is to limit how often a jumpForce can be applied. This will likely prevent other problems down the road.
//class var
float lastJumpForceTime = 0.5f; // start at 0.5f to enable jumping from the start
float jumpForceDelay = 0.5f; // delay between jumps, 0.5f = half second
//method that calls your code
//delta time is the time since last update LibGDX provides this but you will need to pass it
public void yourMethod(float deltaTime, ...){
lastJumpForceTime += deltaTime; // update last jump time
// check if player is on ground and if time since last jump is > delay
if(cl.isPlayerOnGround() && lastJumpForceTime >= jumpForceDelay){
lastJumpForceTime = 0.0f;
playerBody.applyForceToCenter(0, B2DVars.jumpForce, true);
}
}
Related
I made a body which I want to move when a button is clicked, I have been able to move the body using body.setLinearVelocity() but it's not accurate. Let's say I want to move my body for seven meters with a linear X velocity of 40, how do I do that?
//BODY MOVEMENT
//timer = I try to move the body for a certain amount of time
/*isMoveRight and isMoveLeft are Just booleans for activating and deactivating movement*/
if(Gdx.input.isKeyJustPressed(Input.Keys.RIGHT)){
timer = 0f;
isMoveRight = true;
isMoveLeft = false;
}else if(Gdx.input.isKeyJustPressed(Input.Keys.LEFT)){
timer = 0f;
isMoveLeft = true;
isMoveRight = false;
}
if(isMoveRight == true && timer < 0.1f){
timer += 1f * delta; //activate timer
body.setLinearVelocity(52f, 0f);
}else if(isMoveLeft == true && timer < 0.1f){
timer += 1 * delta;
body.setLinearVelocity(-52f, 0f);
}
I could just use body.setTransform() but I need the body to actually move and not to teleport. Thank you in advance
I don't know how complete your code sample is but from waht I see here, you are at least missing the part where you reset the velocity to 0 after 0.1 seconds.
else {
body.setLinearVelocity(0F, 0F);
}
Apart from that your method has a slight vagueness in the moved distance because dependent on your frame rate your check timer < 0.1f is not very exact:
timer = 0.099 -> distance = 5.148
timer = 0.132 -> distance = 6.881 (one frame later with 30 frames/second)
Some (untested) ideas how to handle this problem:
Use a RopeJoint to enforce a maximum distance between the body and the starting point (which has to be a body as well in this case)
Measure the distance (use the Vector2 class) and teleport it back to the point at max distance if needed. (You should use the distance instead of the timer anyway)
Slow your body down if it gets close to the target, this will at least help to reduce the error
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.
I recently started playing around with android and decided to try make a basic physics simulator, but I have encountered a small issue.
I have my object of Ball, each ball has a velocity vector, and the way I move it is by adding said vector to the ball's location with each tick.
It worked quite well until I noticed an issue with this approach.
When I tried applying gravity to the balls I noticed that when two balls got close to each other one of the balls gets launched great velocity.
After some debugging I found the reason for that happening.
Here is an example how I calculate force of gravity and acceleration:
//for each ball that isn't this ball
for (Ball ball : Ball.balls)
if (ball != this) {
double m1 = this.getMass();
double m2 = ball.getMass();
double distance = this.getLocation().distance(ball.getLocation());
double Fg = 6.674*((m1*m2)/(distance * distance));
Vector direction = ball.getLocation().subtract(this.getLocation()).toVector();
Vector gravity = direction.normalize().multiply(Fg / mass);
this.setVeloctity(this.getVelocity().add(gravity));
}
Here's the problem - when the balls get really close, the force of gravity becomes really high (as it should) thus the velocity also becomes incredibly high, but because I add vector to location each tick, and the vector's value is so high, one of the balls gets ejected.
So that brings me to my questions - is there a better way to move your objects other than just adding vectors? Also, is there a better way to handle gravity?
I appreciate any help you guys could offer.
you can try this :
acceleration.y = force.y / MASS; //to get the acceleration
force.y = MASS * GRAVITY_Constant; // to get the force
displacement.y = velocity.y * dt + (0.5f * acceleration.y * dt * dt); //Verlet integration for y displacment
position.y += displacement.y * 100; //now cal to position
new_acceleration.y = force.y / MASS; //cau the new acc
avg_acceleration.y = 0.5f * (new_acceleration.y + acceleration.y); //get the avg
velocity.y += avg_acceleration.y * dt; // now cal the velocity from the avg
(acceleration,velocity,displacment ,and position) are vectors for your ball .
*note (dt = Delta Time which is the difference time between current frame and the previous one.
You need to study the physics of the problem. Obviously it is a bad idea to add a force or acceleration to a velocity, the phyisical units can not match.
Thus in the most primitive case, with a time step dt, you need to implement
velocity = velocity + acceleration * dt
The next problem to consider is that you need to first accumulate all the forces resp. the resulting accelerations for the state (positions and velocities) of all objects at a given time before changing the the whole state simultaneously for their (approximate) state at the next time step.
I'm making a 2d game in libgdx and I would like to know what the standard way of moving (translating between two known points) on the screen is.
On a button press, I am trying to animate a diagonal movement of a sprite between two points. I know the x and y coordinates of start and finish point. However I can't figure out the maths that determines where the texture should be in between on each call to render. At the moment my algorithm is sort of like:
textureProperty = new TextureProperty();
firstPtX = textureProperty.currentLocationX
firstPtY = textureProperty.currentLocationY
nextPtX = textureProperty.getNextLocationX()
nextPtX = textureProperty.getNextLocationX()
diffX = nextPtX - firstPtX
diffY = nextPtY - firstPtY
deltaX = diffX/speedFactor // Arbitrary, controlls speed of the translation
deltaX = diffX/speedFactor
renderLocX = textureProperty.renderLocX()
renderLocY = textureProperty.renderLocY()
if(textureProperty.getFirstPoint() != textureProperty.getNextPoint()){
animating = true
}
if (animating) {
newLocationX = renderLocX + deltaX
newLocationY = renderLocY + deltaY
textureProperty.setRenderPoint(renderLocX, renderLocY)
}
if (textureProperty.getRenderPoint() == textureProperty.getNextPoint()){
animating = false
textureProperty.setFirstPoint(textureProperty.getNextPoint())
}
batch.draw(texture, textureProperty.renderLocX(), textureProperty.renderLocY())
However, I can foresee a few issues with this code.
1) Since pixels are integers, if I divide that number by something that doesn't go evenly, it will round. 2) as a result of number 1, it will miss the target.
Also when I do test the animation, the objects moving from point1, miss by a long shot, which suggests something may be wrong with my maths.
Here is what I mean graphically:
Desired outcome:
Actual outcome:
Surely this is a standard problem. I welcome any suggestions.
Let's say you have start coordinates X1,Y1 and end coordinates X2,Y2. And let's say you have some variable p which holds percantage of passed path. So if p == 0 that means you are at X1,Y1 and if p == 100 that means you are at X2, Y2 and if 0<p<100 you are somewhere in between. In that case you can calculate current coordinates depending on p like:
X = X1 + ((X2 - X1)*p)/100;
Y = Y1 + ((Y2 - Y1)*p)/100;
So, you are not basing current coords on previous one, but you always calculate depending on start and end point and percentage of passed path.
First of all you need a Vector2 direction, giving the direction between the 2 points.
This Vector should be normalized, so that it's length is 1:
Vector2 dir = new Vector2(x2-x1,y2-y1).nor();
Then in the render method you need to move the object, which means you need to change it's position. You have the speed (given in distance/seconds), a normalized Vector, giving the direction, and the time since the last update.
So the new position can be calculated like this:
position.x += speed * delta * dir.x;
position.y += speed * delta * dir.y;
Now you only need to limit the position to the target position, so that you don't go to far:
boolean stop = false;
if (position.x >= target.x) {
position.x = target.x;
stop = true;
}
if (position.y >= target.y) {
position.y = target.y;
stop = true;
}
Now to the pixel-problem:
Do not use pixels! Using pixels will make your game resolution dependent.
Use Libgdx Viewport and Camera instead.
This alows you do calculate everything in you own world unit (for example meters) and Libgdx will convert it for you.
I didn't saw any big errors, tho' i saw some like you are comparing two objects using == and !=, But i suggest u to use a.equals(b) and !a.equals(b) like that. And secondly i found that your renderLock coords are always being set same in textureProperty.setRenderPoint(renderLocX, renderLocY) you are assigning the same back. Maybe you were supposed to use newLocation coords.
BTW Thanks for your code, i was searching Something that i got by you <3
I'm working in J2ME, I have my gameloop doing the following:
public void run() {
Graphics g = this.getGraphics();
while (running) {
long diff = System.currentTimeMillis() - lastLoop;
lastLoop = System.currentTimeMillis();
input();
this.level.doLogic();
render(g, diff);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
stop(e);
}
}
}
So it's just a basic gameloop, the doLogic() function calls for all the logic functions of the characters in the scene and render(g, diff) calls the animateChar function of every character on scene, following this, the animChar function in the Character class sets up everything in the screen as this:
protected void animChar(long diff) {
this.checkGravity();
this.move((int) ((diff * this.dx) / 1000), (int) ((diff * this.dy) / 1000));
if (this.acumFrame > this.framerate) {
this.nextFrame();
this.acumFrame = 0;
} else {
this.acumFrame += diff;
}
}
This ensures me that everything must to move according to the time that the machine takes to go from cycle to cycle (remember it's a phone, not a gaming rig). I'm sure it's not the most efficient way to achieve this behavior so I'm totally open for criticism of my programming skills in the comments, but here my problem: When I make I character jump, what I do is that I put his dy to a negative value, say -200 and I set the boolean jumping to true, that makes the character go up, and then I have this function called checkGravity() that ensure that everything that goes up has to go down, checkGravity also checks for the character being over platforms so I will strip it down a little for the sake of your time:
public void checkGravity() {
if (this.jumping) {
this.jumpSpeed += 10;
if (this.jumpSpeed > 0) {
this.jumping = false;
this.falling = true;
}
this.dy = this.jumpSpeed;
}
if (this.falling) {
this.jumpSpeed += 10;
if (this.jumpSpeed > 200) this.jumpSpeed = 200;
this.dy = this.jumpSpeed;
if (this.collidesWithPlatform()) {
this.falling = false;
this.standing = true;
this.jumping = false;
this.jumpSpeed = 0;
this.dy = this.jumpSpeed;
}
}
}
So, the problem is, that this function updates the dy regardless of the diff, making the characters fly like Superman in slow machines, and I have no idea how to implement the diff factor so that when a character is jumping, his speed decrement in a proportional way to the game speed. Can anyone help me fix this issue? Or give me pointers on how to make a 2D Jump in J2ME the right way.
Shouldn't you be adjusting the jumpSpeed based on the elapsed time? That is, perhaps the speed changes by -75/sec, so your diff should be a weight for the amount of change applied to the jumpSpeed.
So pass in diff to checkGrav and do something like... jumpSpeed += (diff * (rate_per_second)) / 1000;
(assuming diff in milliseconds)
(Ideally, this would make it just like real gravity :D)
Why not just scale all constants by diff?
By the way, I'm embarrassed to say this, but I worked on a commercial game where gravity was twice as strong on characters going down as going up. For some reason, people preferred this.
This seems to be more of a question about game design than the math of a jump. It is a common problem that in games running on different processors one game will be executed faster and on other games it will be executed slower (thus changing the entire speed of the game). I'm not sure what common practice is in games, but whenever I made home-brewed 2D games (they were fun to make) I would have the concept of a game-tick. On faster machines
long diff = System.currentTimeMillis() - lastLoop;
lastLoop = System.currentTimeMillis();
Would be lower. A wait time would be derived from the diff so that the game would run at the same speed on most machines. I would also have the render method in a separate thread so that the game speed isn't dependent on the graphics.
I can give a formula like this (I use it everywhere). The X is the parameter of it starting from zero and ending on the length of jump.
if you want someone to jump at some Height (H) and at some Length (L), then function of the jump will look like this (and it won't' be able to ever look different):
y = minus(power(x - Length of Jump divided by two) multiply by 4 and
multiply by Height of the jump) divide by power of Length and add
Height of jump in the very end.
y = -(x-l/2)(x-l/2)*4*h/(l*l) + h
And if you want the jumping object to land on something, then you can check every new X if it's approximately standing on a platform and if it is standing on something, then don't make it just stop, make it's Y position exactly equal to the Y of platform.
If you're using something like Flash or other base which has inverted y axis, then multiply the function output by -1;