When working with creating games in Libgdx, I have not been using the physics engine as I dont have too many moving parts in the game. So, to have objects fall from the top of the screen to the bottom, I used what the documentation had, something like this:
projectile.y -= 200 * Gdx.graphics.getDeltaTime();
That example will make said projectile go down at 200 pixels per second (I believe). What I am trying to do is make it so after two seconds, the projectile will transition from negative 200 per second, to positive 200 per second. I've tried using loops and Thread.sleep, but that will just freeze the entire game and unfreeze with the projectiles going the other way. Any ideas?
Linear interpolation.
All you need to do is determine the start point: x1 = -200
Determine the end point: x2 = 200
Determine the amount of seconds that it takes to reach the end point: tmax = 2.0 sec
Determine the difference that you need to add to the original to reach the end point: v = (x2-x1) = (200 - (-200)) = 400
Use the linear interpolation function: x1 + t*v = x2 where t e [0...1] //must be normalized to 0..1 interval
Thus at t = 0, the value is at x1 + 0 = x1; and at t = (tn/tmax) [which is 1], the value is at x1 + v = x2.
So all you need is a timer from 0 to 2 and the following equation:
float interpolationTimer = 0.0f;
final float interpolationTimerMax = 2.0f;
public void render()
{
float delta = Gdx.graphics.getDeltaTime();
interpolationTimer += delta;
if(interpolationTimer > interpolationTimerMax )
{
interpolationTimer = interpolationTimerMax ;
}
velocity.y = -200 + (interpolationTimer/interpolationTimerMax) * (400); //x1 + t*v = x2
projectile.y -= velocity.y * delta;
}
To get a change in direction of y, you need a polynomial function of x. For a single direction change, use a binomial; try something like
projectile.y = projectile.y
- 200 * Gdx.graphics.getDeltaTime()
+ 20 * Math.pow(Gdx.graphics.getDeltaTime(), 2);
If you are looking for linear interpolation of the speeds, just keep track of time.
float timeElapsed = 0.0f;
void render() {
timeElapsed += Gdx.graphics.getDeltaTime();
projectile.y -= 200.0f * (1.0f - timeElapsed);
}
Be sure to stop after timeElapsed has reached 2 seconds (that's if(timeElapsed < 2.0f)). Time elapsed variable will start at 0.0f and will slowly increment. Until it reaches 1.0f, projectile.y will get substracted from. However, as soon as time elapsed is higher than 1.0f, projectile.y will get added to.
Related
I've been trying from hours to setup gravity and relate it to time or what we call frame independent bounce ball. I did everything correct I guess, and I tried to implement the system where height of ball would decrease after every bounce. I did not even start that, and my code is creating something absurd I don't understand why. Here's my code:
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
currentFrame = System.currentTimeMillis();
dt = currentFrame - lastFrame;
dt = dt/1000;
lastFrame = currentFrame;
myFreakinRect.set(0,0, canvas.getWidth(), canvas.getHeight());
freakinRed.setColor(Color.RED);
freakinRed.setStyle(Paint.Style.FILL);
canvas.drawRect(myFreakinRect, freakinRed);
//
// o yuea
if(goingDown) {
//velocityY = Math.sqrt(100 + 2*gravity*(posY));
velocityY = gravity*(currentFrame - runTime);
} else {
velocityY = downV - gravity*(currentFrame - runTime);
}
if(posX > w - ballRadius*2) {
goingRight = false;
}
if(posX < 0) {
goingRight = true;
}
if(posY > h - ballRadius*2) {
//initY = initY - 0.25F;
//if(initY < 0) initY = 0;
Log.i("xxx", String.valueOf(initY));
runTime = currentFrame;
downV = velocityY;
goingDown = false;
}
if(velocityY <= 0) {
goingDown = true;
runTime = currentFrame;
}
if(goingDown) posY += velocityY*dt;
else posY -= velocityY*dt;
if(goingRight) posX += velocityX*dt;
else posX -= velocityX*dt;
canvas.drawText(String.valueOf(posX)+" "+String.valueOf(posY), 10, 10, new Paint());
canvas.drawBitmap(rBall, (float)posX, (float)posY, myFreakingFaintPaint);
invalidate();
}
Here's a GIF what is happening:
UPDATE:
Here's my updated code which is clean, understandable and works perfect:
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
currentFrame = System.currentTimeMillis();
dt = currentFrame - lastFrame;
dt = dt/1000;
lastFrame = currentFrame;
velocityY = downV + gravity*(currentFrame - runTime);
posY += velocityY*dt;
posX += velocityX*dt;
if(posX > w - ballRadius*2 || posX < 0) {
velocityX = -velocityX;
}
if(posY >= h - ballRadius*2) {
posY = h - ballRadius*2 - 2;
runTime = currentFrame;
downV = -0.8*velocityY;
}
canvas.drawBitmap(rBall, (float)posX, (float)posY, null);
invalidate();
}
Here ...
if(goingDown) {
//velocityY = Math.sqrt(100 + 2*gravity*(posY));
velocityY = gravity*(currentFrame - runTime);
} else {
velocityY = downV - gravity*(currentFrame - runTime);
}
... you update the velocity (speed, actually) assuming that the ball will not bounce during this frame.
Then here ...
if(posY > h - ballRadius*2) {
//initY = initY - 0.25F;
//if(initY < 0) initY = 0;
Log.i("xxx", String.valueOf(initY));
runTime = currentFrame;
downV = velocityY;
goingDown = false;
}
... you have not yet updated posY, so you are determining whether the ball hit the floor as a result of the previous update. If it did, you reverse the direction of motion, but keep the speed you already computed for this frame. As a result, each time the ball bounces, its initial upward speed is one frame's worth of acceleration greater than the speed it was traveling when it hit the floor.
You have a similar effect at the top of the ball's motion, but it's smaller because the speed is small there.
There are a couple of ways you might solve this problem. The simplest is probably to perform the bounce check after the position update instead of before.
Additional notes:
use the signs of your X and Y speeds instead of separate direction-of-motion flags (thus making the names velocityY etc. accurate). Your code will be simpler, and you'll need to handle only one change of vertical direction, not two, because the equations of motion will handle the other automatically.
you have a bit of a precision problem because you assume that the ball travels in the same direction for a whole frame. This may become noticeable if you allow the ball to reach high speeds: it will appear to penetrate the floor before bouncing back up.
this computation is suspicious: dt = dt/1000. Since dt seems to be computed from System.currentTimeMillis(), I am inclined to guess that it, too, has type long. In that case, you are performing an integer division and thereby losing precision.
In general:
Split up into model and view. In that case the rendering still runs fine, because the calculations are pretty light-weight, but you shouldn't run code inside the rendering-routine that isn't directly related to painting something.
Next point:
Stay as close to reality as possible, if you simulate physics. You can always optimize afterwards, but first make sure your code is actually doing what it's supposed to do. I'm currently playing a bit around with projectile-motion, so I've got a basic idea of what the code is supposed to do. I've been attempting to understand yout code for 10 mins so far. Interim result: I'm confused and don't quite get it.
My suggestion:
Start off with clearer code and try to stick as close to physical rules as possible. This code isn't optimized as far as it could be, but it's readable, understandable and simulates close enough to the real life. That makes it a lot simpler to debug:
final double GRAVITY = -9.81;
final double BALL_ELASTICITY = 0.95;
double vx, vy;
double x, y;
//dt is delta-time in seconds!!!
void simulateBall(double dt){
//calculate when the ball will touch the floor the next time
double next_floor_touch = (-vy + Math.sqrt(vy * vy - 2 * GRAVITY * y)) / GRAVITY;
double after_floor_touch = dt - next_floor_touch;
boolean touches_floor = (next_floor_touch <= dt);
//calculate new y
if(touches_floor){
//predict the speed the ball will have, after it bounces from the floor
double vy_at_floor = vy + GRAVITY * next_floor_touch;
double vy_from_floor = vy_at_floor * (-1) * BALL_ELASTICITY;
//predict y starting from the floor at t = next_floor_touch until dt
y = 0 + vy_from_floor * after_floor_touch + 0.5 * GRAVITY * after_floor_touch * after_floor_touch;
}else{
//uniform acceleration
y = y + vy * dt + 0.5 * GRAVITY * dt * dt;
}
//calculate vy
if(touches_floor){
//see above
double vy_after_floor = (vy + GRAVITY * next_floor_touch) * (-1) * BALL_ELASTICITY;
vy = vy_after_floor + GRAVITY * after_floor_touch;
}else{
vy = vy + GRAVITY * dt;
}
... //that's already the hardest part
}
This uses the quadratic equation to predict when the ball will hit the floor and uniform acceleration to calculate the position from a given position, speed and acceleration. Unless I've made any mistakes in my calculation (this code is not tested), this should be physically precise. BALL_ELASTICITY represents how much of the speed is left, after the ball hits the floor. That's not physically precise - might be, IDK - , but should do for this purpose.
after creating a very simple animation using libGDX i have some questions I'd like to clarify in order to make sure I understand everything prior to start with any other development with more complexity.
I have a box created like this:
public void createBaseCube(Model modelCube) {
ModelBuilder modelBuilder = new ModelBuilder();
modelCube = modelBuilder.createBox(1f, 1f, 1f,
new Material(ColorAttribute.createDiffuse(Color.GREEN)),
VertexAttributes.Usage.Position | VertexAttributes.Usage.Normal);
this.cubeInstance.transform.translate(0.5f, 0.5f, 0.5f);
}
As it is centered in position (0,0,0) and a want its corner to be allocated in (0, 0, 0) I applied the translation in last line.
Here is how it looks like (I added a set of tiles to have some reference):
Afterwards, I create the animation. I want to overturn the box, so it will be positioned over the white tile, and rotating over its bottom-right edge
public class CubeAnimation {
...
<<definition of attributes>>
...
public CubeAnimation (ModelInstance cubeModel, float fromAngle, float toAngle, float speed, float alpha){
this.cubeInstance = cubeModel;
this.fromAngle = fromAngle; //value set to 0
this.toAngle = toAngle; //value set to 90f
this.speed = speed; //value set to 1f
this.alpha = alpha; //value set to 0
}
public void update(float delta) {
alpha += delta * speed;
if (alpha >= 1f) {
finished =true;
return;
}
float angle = alpha * (toAngle - fromAngle);
fromAngle = angle;
Vector3 t = new Vector3(0.5f, -0.5f, 0);
cubeInstance.transform.idt().translate(t).rotate(Vector3.Z, -angle).translate(t.scl(-1));
}
Everything seems to be fine, and code is quite simple, BUT (and here are the issues) when applying the animation, the box is translated to the center again (so first translate when box was created is undone), and -surprise- although I'm passing 90f as parameter to the animation, cube only rotates 45 degrees (when I set 180, it rotated as expected: 90).
Here how it looks like after the animation:
What is wrong here? Thanks in advance for your help!
You want to rotate the cube from angle fromAngle to angle toAngle
You are attempting to do so gradually by calculating the percentage completed over time, stored in your alpha variable.
alpha += delta * speed;
if (alpha >= 1f) {
finished =true;
return;
}
This part is fine for calculating the percentage as an angular-velocity multiplied by time passed. ie
angle_percentage/seconds * seconds_passed
You then get the distance between the start and stop angles in this line
float angle = alpha * (toAngle - fromAngle);
This code works for a starting angle of 0, but will fail for non zero starting points. The equation for a line is y = mx + b, so to correct this, you should include the b value:
float angle = fromAngle + alpha * (toAngle - fromAngle);
This will start the animation at fromAngle and push it over the distance required.
The extra line fromAngle = angle; changes your starting location on every iteration, so you end up with an unpredictable animation, which will be different depending on the speed you choose... I'm fairly certain the factor of two is merely a coincidence ;)
Finally this loop ends when the value is set to 100%, but never actually updates to 100%. Try this instead.
public void update(float delta) {
alpha += delta * speed;
if (alpha <= 1f) {
float angle = fromAngle + alpha * (toAngle - fromAngle);
Vector3 t = new Vector3(0.5f, -0.5f, 0);
cubeInstance.transform.idt().translate(t).rotate(Vector3.Z, -angle).translate(t.scl(-1));
} else {
finished = true;
}
}
I want to draw an arc using center point,starting point,ending point on opengl surfaceview.I have tried this given below code so far. This function draws the expected arc if we give the value for start_line_angle and end_line_angle manually (like start_line_angle=0 and end_line_angle=90) in degree.
But I need to draw an arc with the given co-ordinates(center point,starting point,ending point) and calculating the start_line_angle and end_line_angle programatically.
This given function draws an arc with the given parameters but not giving the desire result. I've wasted my 2 days for this. Thanks in advance.
private void drawArc(GL10 gl, float radius, float cx, float cy, float start_point_x, float start_point_y, float end_point_x, float end_point_y) {
gl.glLineWidth(1);
int start_line_angle;
double sLine = Math.toDegrees(Math.atan((cy - start_point_y) / (cx - start_point_x))); //normal trigonometry slope = tan^-1(y2-y1)/(x2-x1) for line first
double eLine = Math.toDegrees(Math.atan((cy - end_point_y) / (cx - end_point_x))); //normal trigonometry slope = tan^-1(y2-y1)/(x2-x1) for line second
//cast from double to int after round
int start_line_Slope = (int) (sLine + 0.5);
/**
* mapping the tiriogonometric angle system to glsurfaceview angle system
* since angle system in trigonometric system starts in anti clockwise
* but in opengl glsurfaceview angle system starts in clock wise and the starting angle is 90 degree of general trigonometric angle system
**/
if (start_line_Slope <= 90) {
start_line_angle = 90 - start_line_Slope;
} else {
start_line_angle = 360 - start_line_Slope + 90;
}
// int start_line_angle = 270;
// int end_line_angle = 36;
//casting from double to int
int end_line_angle = (int) (eLine + 0.5);
if (start_line_angle > end_line_angle) {
start_line_angle = start_line_angle - 360;
}
int nCount = 0;
float[] stVertexArray = new float[2 * (end_line_angle - start_line_angle)];
float[] newStVertextArray;
FloatBuffer sampleBuffer;
// stVertexArray[0] = cx;
// stVertexArray[1] = cy;
for (int nR = start_line_angle; nR < end_line_angle; nR++) {
float fX = (float) (cx + radius * Math.sin((float) nR * (1 * (Math.PI / 180))));
float fY = (float) (cy + radius * Math.cos((float) nR * (1 * (Math.PI / 180))));
stVertexArray[nCount * 2] = fX;
stVertexArray[nCount * 2 + 1] = fY;
nCount++;
}
//taking making the stVertextArray's data in reverse order
reverseArray = new float[stVertexArray.length];//-2 so that no repeatation occurs of first value and end value
int count = 0;
for (int i = (stVertexArray.length) / 2; i > 0; i--) {
reverseArray[count] = stVertexArray[(i - 1) * 2 + 0];
count++;
reverseArray[count] = stVertexArray[(i - 1) * 2 + 1];
count++;
}
//reseting the counter to initial value
count = 0;
int finalArraySize = stVertexArray.length + reverseArray.length;
newStVertextArray = new float[finalArraySize];
/**Now adding all the values to the single newStVertextArray to draw an arc**/
//adding stVertextArray to newStVertextArray
for (float d : stVertexArray) {
newStVertextArray[count++] = d;
}
//adding reverseArray to newStVertextArray
for (float d : reverseArray) {
newStVertextArray[count++] = d;
}
Log.d("stArray", stVertexArray.length + "");
Log.d("reverseArray", reverseArray.length + "");
Log.d("newStArray", newStVertextArray.length + "");
ByteBuffer bBuff = ByteBuffer.allocateDirect(newStVertextArray.length * 4);
bBuff.order(ByteOrder.nativeOrder());
sampleBuffer = bBuff.asFloatBuffer();
sampleBuffer.put(newStVertextArray);
sampleBuffer.position(0);
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glVertexPointer(2, GL10.GL_FLOAT, 0, sampleBuffer);
gl.glDrawArrays(GL10.GL_LINE_LOOP, 0, nCount * 2);
gl.glLineWidth(1);
}
To begin with the trigonometry you may not simply use the atan to find degrees of the angle. You need to check what quadrant the vector is in and increase or decrease the result you get from atan. Better yet use atan2 which should include both dx and dy and do the job for you.
You seem to create the buffer so that a point is created per degree. This is not the best solution as for large radius that might be too small and for small radius this is way too much. Tessellation should include the radius as well such that number of points N is N = abs((int)(deltaAngle*radius*tessellationFactor)) then use angleFragment = deltaAngle/N but make sure that N is greater then 0 (N = N?N:1). The buffer size is then 2*(N+1) of floats and the iteration if for(int i=0; i<=N; i++) angle = startAngle + angleFragment*i;.
As already pointed out you need to define the radius of the arc. It is quite normal to use an outside source the way you do and simply force it to that value but use the 3 points for center and the two borders. Some other options that usually make sense are:
getting the radius from the start line
getting the radius from the shorter of the two lines
getting the average of the two
interpolate the two to get an elliptic curve (explained below)
To interpolate the radius you need to get the two radiuses startRadius and endRadius. Then you need to find the overall radius which was already used as deltaAngle above (watch out when computing this one, it is more complicated as it seems, for instance drawing from 320 degrees to 10 degrees results in deltaAngle = 50). Anyway the radius for a specific point is then simply radius = startRadius + (endRadius-startRadius)*abs((angleFragment*i)/deltaAngle). This represents a simple linear interpolation in polar coordinate system which is usually used to interpolate vector in matrices and is the core functionality to get nice animations.
There are some other ways of getting the arc points which may be better performance wise but I would not suggest them unless and until you need to optimize your code which should be very late in production. You may simply keep stepping toward the next point and correcting the radius (this is only a concept):
vec2 start, end, center; // input values
float radius; // input value
// making the start and end relative to center
start -= center;
end -= center;
vec2 current = start/length(start) * radius; // current position starts in first vector
vec2 target = end/length(end) * radius; // should be the last point
outputBuffer[0] = current+center; // insert the first point
for(int i=1;; i++) { // "break" will need to exit the loop, we need index only for the buffer
vec2 step = vec2(current.y, -(current.x)); // a tangential vector from current start point according to center
step = step/length(step) / tessellationScale; // normalize and apply tessellation
vec2 next = current + step; // move tangentially
next = next/length(next) * radius; // normalize and set the
if(dot(current-target, next-target) > .0) { // when we passed the target vector
current = next; // set the current point
outputBuffer[i] = current+center; // insert into buffer
}
else {
current = target; // simply use the target now
outputBuffer[i] = current+center; // insert into buffer
break; // exit
}
}
Is there a way to move a bitmap from point1 to point 2 using the angle?
x += speed * Math.sin(getAngle(pointDestination));
y += speed * Math.cos(getAngle(pointDestination));
edit:
public double getAngle(Point target) {
double angle = (Math.atan2(target.y - y, target.x - x));
double angledeg = angle*0.0174532925;
return angledeg;
}
Should getAngle() be executed on every iteration or just once at the beginning?
Unfortunately the sprite moves to a wrong direction.
Your problem is that you increment the x value and when you go to increment the y too you are using the new x that you just incremented to calculate the angle.
Change it to:
float angle=getAngle(pointDestination);
x += speed * Math.cos(angle);
y += speed * Math.sin(angle);
public double getAngle(Point target) {
return Math.atan2(target.y - y, target.x - x);
}
Instead of doing a incremental update of the bitmap position, you better define a (mathematical) function that computes the position (x, y) over time. The advantage is that this will result in very exact and predictable movements independent of CPU speed / frames per second.
Assuming that the bitmap should move at constant speed from (x1, y1) to (x2, y2) in time milliseconds, so your (time dependent) position functions are as follows:
x(t) := x1 + (x2 - x1) * t / time // t in range [0, time]
y(t) := y1 + (y2 - y1) * t / time // t in range [0, time]
(Note: By doing some physics/maths, you can define more sophisticated functions that result in more complex movements).
This two functions can then be used in your animation thread to update the position of the bitmap:
bitmap.setX(x(currentTime - animationStartTime));
bitmap.setY(y(currentTime - animationStartTime));
Have a look at Trident animation library. It supports multiple UI frameworks and seems to be exactly what you're looking for!
Update: In case you really want to do a incremental update, e.g. based on your current frame rate, you don't need trigonometric functions (sin, cos, tan, ...) at all, just vectors:
// x, y is the current position of the bitmap
// calculate vector (dx, dy) to target:
double dx = x2 - x;
double dy = y2 - y;
// calculate length of this vector:
double l = Math.hypot(dx, dy); // calculates sqrt(dx² + dy²)
// calculate unit vector of (dx, dy):
double vx = dx / l;
double vy = dy / l;
// update bitmap position:
// distance is the number of pixels to travel in this iteration (frame)
x += distance * vx;
y += distance * vy;
Note that all values should be of type double. Otherwise, if int is used for x and y and the increment is lower than 1 (e.g. due to slow movement, i.e. distance is very low), the bitmap won't move at all due to rounding errors!
Also note that in this approach you have to measure the frame rate to adjust distance accordingly to compensate deviation. The formula could be something like:
double distance = (time elapsed since last frame in sec) * (distance to travel per sec)
I am trying to get my bullets to fire towards (input coords) at a constant speed.
So far I was able to get it to shoot at the direction but the farther I click (touch, android game) the faster the bullet goes. I have tried different methods by scaling but failed miserably, I have started coding just a month ago and using this as a project to increase my knowledge of how things work before I work on a full game but having too much trouble with this.
This is what I have been using to get the bullet to move towards the direction I want it to, the codes with // in front were other samples I got while browsing through the internet in hopes of getting what I wanted. I have thought of not using velocity to set the direction, but I have no clue of another method for this.
EDIT: All in short, I cannot get all the bullets to move in the same speed, farther I click, higher velocity bullet has.
Any help guys? Thanks a bunch
Player Class :
public void update(float delta) {
if (Gdx.input.isTouched()) {
if (System.currentTimeMillis() - lastShot >= FIRE_RATE) {
bullets.add(new Bullet(position.x + 6,position.y + 6,4,4,Gdx.input.getX() / 2,Gdx.input.getY() / 2));
lastShot = System.currentTimeMillis();
}
}
for (int i=0;i<bullets.size();i++) {
bullets.get(i).update(delta);
}
}
Bullet Class :
public Bullet(float x, float y, int width, int height, float targetX, float targetY) {
this.width = width;
this.height = height;
position = new Vector2( x , y );
velocity = new Vector2( 0 , 0 );
velocity.set(targetX - position.x,targetY - position.y);
//velocity.set(targetX - position.x, targetY - position.y).nor().scl(Math.min(position.dst(targetX, targetY), speedMax));
}
public void update(float deltaTime) {
//position.add(position.x + speedMax * deltaTime * ax,position.y + speedMax * deltaTime * ay);
position.add(velocity.x * deltaTime, velocity.y * deltaTime);
//velocity.scl(1 - (0.98f * deltaTime));
// Linear dampening, otherwise the ball will keep going at the original velocity forever
}
Well, normalizing vectors should be rather straightforward. Take your components, square them, and add them together (pythagorean theorem) and then divide each component by this result. I.e. vX = (targetX - position.x)/Math.sqrt(((targetX - position.x) * (targetX - position.x)) + ((targetY - position.y) *(targetY - position.y )))
Then you can multiply vX by some constant, and do the same for a vY and then set your velocity.