Gravity trajectory with already known destination - libgdx

I have been spending several days trying to solve this problem and I am about to go mad. Your help would be very much appreciated.
I am using a 2D stage in libgdx. I want to move actors (or sprite) to this stage with a "gravity" display effect: for example for actor1, his initial coordinates are (0, 0), destination coordinates would be (100, 50), and I want to move this actor to this destination with a gravity trajectory effect. Then I want to use the same gravity for actor2 moving from (0, 0) to (25, 75), then actor3 from (0, 0) to (200, 75) etc.
I managed to apply a gravity trajectory to any actor based on this well known loop:
setX(getX() + velocity.x);
setY(getY() + velocity.y);
velocity.y += getGravity().y * delta;
So tweaking the gravity value would modify the trajectory. It works fine.
Now, as I said earlier I want to give every actors a unique trajectory given their predetermined destination.
So I have tried to find a formula to determine the x and y for each actor at every frame of their trajectory
I am using the following static parameters:
gravity.y : same for all actors
delay : the amount of frames during which each actor moves between his initial coordinates and his destination coordinates. Same value for all actors too
First I calculate the velocity with this SUPER UGLY formula that I am absolutely not proud of:
velocity = new Vector2 ( (destinationx - b.getX() )/time, initdisty/time + ( Math.sqrt(delta*1000)*time / ( 500/Math.abs(gravity) ) ));
where delta = Gdx.graphics.getDeltaTime();
Then I apply this velocity each frame to calculate the x and y of each actor:
public void act(float delta){
for (int i=0; i<delay; i++) {
setX(getX() + velocity.x);
setY(getY() + velocity.y);
velocity.y += gravity.y * delta;
}
}
It KIND OF work, but of course, this can not be a long term solid solution. Calculating the x and y for each frame for each actor (there can be 5-6 actors moving at the same time) doesn't look good at all.
The main problem is that the trajectories are good on computer with consistent 60FPS, they are okay on a tablet, but on a phone with limited memory and 30 < fps < 60, the trajectories become terribly wrong.
After reading several blog posts, it seems like I could avoid the multi device memory fps problems by removing the delta parameter from my formulas, but I haven't found how. And it still doesn't give me a strong long term solution to calculate the trajectory with predetermined destination coordinates.
Thanks for reading and for your time, please let me know if this is unclear I'll do my best to explain better.

Cause of the problem
Maintaining both position and velocity leads to discretization of the system resulting in quantization error. So you will experience inconsistent behavior by your current method when fps fluctuates.
Solution
All you need is to reduce your number of state variables to only two i.e. don't store current velocity. It is causing the errors in final position.
In stead use parametric form of the trajectory.
v = u + at
and
s = ut + ½at²
Implementation
Suppose you want to go from (sourceX, sourceY) to (targetX, targetY) in time 'totalTime'.
Calculate initial velocity.
Vector2 initialVelocity = new Vector2((targetX - sourceX) / totalTime,
(targetY - sourceY) / totalTime - gravity * totalTime / 2);
float currentTime = 0;
In each iteration, calculate position directly and keep track of currentTime.
public void act(float delta){
if (currentTime < totalTime) {
currentTime += delta;
setX((initialVelocity.x + gravity.x * currentTime / 2) * currentTime);
setY((initialVelocity.y + gravity.y * currentTime / 2) * currentTime);
} else {
setX(targetX);
setY(targetY);
}
}

Related

LWJGL Picking - Select Certain Block When Hovering ( gluUnProject() )

This video will show my current situation, and I currently can't find any answers to it online.
https://www.youtube.com/watch?v=O8Mh-1Emoc8&feature=youtu.be
My Code:
public Vector3D pickBlock() {
glDisable(GL_TEXTURE);
IntBuffer viewport = BufferUtils.createIntBuffer(16);
FloatBuffer modelview = BufferUtils.createFloatBuffer(16);
FloatBuffer projection = BufferUtils.createFloatBuffer(16);
FloatBuffer winZ = BufferUtils.createFloatBuffer(1);
float winX, winY;
FloatBuffer position = BufferUtils.createFloatBuffer(3);
glGetFloat(GL_MODELVIEW_MATRIX, modelview);
glGetFloat(GL_PROJECTION_MATRIX, projection);
glGetInteger(GL_VIEWPORT, viewport);
winX = (float)Display.getWidth() / 2;
winY = (float)viewport.get(3) - (float)Display.getHeight() / 2;
glReadPixels(Display.getWidth() / 2, (int)winY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, winZ);
gluUnProject(winX, winY, winZ.get(), modelview, projection, viewport, position);
glEnable(GL_TEXTURE);
return new Vector3D(position.get(0) / 2 + 0.5f, position.get(1) / 2 + 0.5f, position.get(2) / 2 + 0.5f);
}
It returns "/ 2 + 0.5f" because that is needed because of the offsets I have for the blocks (if I removed the 0.5f, the offset would be in the center instead of the corner)
I seams to me that the error, based on the video, comes from when you are facing in the positive z direction (or whatever your back direction is). My guess is that you aren't taking the facing direction into account as I see in your code that you are just adding a constant 0.5F to the position of your cursor.
Therfore, when you are facing backwards, it adds 0.5 which makes it be behind the wall (since back is negative Z). one simple check would be weather the Z component of your forward vector is positive or negative, and deciding the factor added to the cursor based on that, then doing the same for the X.
Depending on how you implemented your camera (IE: if you used Euler angles (rx, ry, rz) or if you used Quaternions / forward vectors), the way you would do that check would vary, feel free to ask me for examples based on your system if you need.
hope this helped!
PS: if you're using angles, you can either check for the range of the y-axis rotation value and determine which direction you are facing and thus weather to add or subtract, OR you can calculate the forward vector based on your angles, and then check the for sign of the component.

How to make an object randomly spawn inside of the stage only

My stage size is 640 x 1136
I want to spawn my objects randomly but only inside of the stage.
I used this
object.x = Math.random() * stage.stageHeight;
How can I make the x position spawn within the stage area?
You would want a "random in range" function like the one here
(included below).
However, if you are doing a game you may not be satisfied with the result of randomly spawning objects (e.g. objects piling up on each other, etc.) and want to exert more control. Lots of ways to be clever with that – like defining a set of regions or quadrants, picking one of those randomly and then generating random coordinates within that region, or having an array of defined "good" points and randomly selected from it. Actual randomness is often less interesting than the idea or appearance of randomness.
public function randRange(minNum:Number, maxNum:Number):Number {
return (Math.floor(Math.random() * (maxNum - minNum + 1)) + minNum);
}
TOMATO answer is the same as what you actually do so it's not really helpful. What you need to do is account for the object size. If you have an object that is at maximum 100 pixel wide then:
var size:Number = object.width;
object.x = size + Math.random() * (stage.stageWidth - size * 2);
This make sure the object spawn on the x axis inside the stage x axis. Same operation needs to be repeated for the y axis of course.

LibGdx Bow and arrow game physics

I'm working on a new game written with LibGdx Engine and Java.
I've got a problem with some of the physics in this game.
I want to shoot the arrow in a ballistic trajectory (angry bird style)
and can't find the equation to do so .
I am using these velocity equations:
float velx = (float) (Math.cos(rotation) * spd);
float vely = (float) (Math.sin(rotation) * spd);
I add this to the current position and the arrow shoots in one direction - straight.
I thought maybe changing the rotation would help me achieve what I want (a ballistic path).
It does help, but I want to have the trajectory as well.
I saw this
ProjectileEquation class that someone already posted but didn't know how to work with it:
public class ProjectileEquation
{
public float gravity;
public Vector2 startVelocity = new Vector2();
public Vector2 startPoint = new Vector2();
public Vector2 gravityVec = new Vector2(0,-10f);
public float getX(float n) {
return startVelocity.x * (n ) + startPoint.x;
}
public float getY(float n) {
float t = n;
return 0.5f * gravity * t * t + startVelocity.y * t + startPoint.y;
}
}
I'm looking for some help to help me use this class for ballistic trajectories.
This is how I tried using it:
for(int i =0;i<30;i++)
{
Texture f = ResData.Square_1;
ProjectileEquation e= new ProjectileEquation();
e.gravity = 1;
e.startPoint = new Vector2(bow.getX(),bow.getY());//new Vector2(-bow.getX(),-bow.getY()); //My bow is opposite so it suppose to work fine
e.startVelocity = getVelocityOf(bow.getRotation());
Vector3 touchpos = new Vector3();
s.draw(f,e.getX(i) ,e.getX(i),5,5);
}
The ProjectileEquation class you post looks like it'll calculate the X and Y position given a time delta, so the float you pass in should be the time delta since you started the arrow moving (in seconds).
That code will not give you the angle of the arrow though. To find that, I would suggest you keep hold of the previous X and Y, then you can use Math.atan2() to calculate the angle based on the previous XY and the current XY. Google atan2 for a load of info on how to use it.
The very best way to do this however would be to use Box2d and model the scene correctly. Then you wouldn't have to get involved in the maths at all. I read somewhere that that's what Angry Birds uses, and is an excellent choice for modelling these sorts of physics games.
I hope your game goes well.

steering behavior and passed time

I'm trying to implement steering behaviors but I have a problem with "including" the passed time in the calculation and then allowing me to control the speed of the game. I have seen various sources of steering behaviors and I've come up with this (Arrive behavior):
var toTarget:Vector2D = new Vector2D( mEntity.targetPosition.x, mEntity.targetPosition.y );
toTarget.subtract( mEntity.position );
var dist:Number = toTarget.length;
toTarget.normalize().scale( mEntity.maxSpeed );
if( dist < slowDownDist ) {
toTarget.scale( dist / slowDownDist );
}
return toTarget.subtract( mEntity.velocity );
And here's the advanceTime method of MovingEntity:
var steeringForce:Vector2D = mSteering.calculate();
steeringForce.x /= mMass;
steeringForce.y /= mMass;
steeringForce.scale( time );
mVelocity.x += steeringForce.x;
mVelocity.y += steeringForce.y;
x += mVelocity.x * time;
y += mVelocity.y * time;
The steering force should be (at some point) directly opposite to the entity's velocity and thus making it stop. The problem I see and do not understand is that in order to simulate acceleration the force needs to be divided by mass and also to consider the passed time it needs to be multiplied by that time - but this scales the steering force down a lot and causes the entity to overshoot the target spot instead of stopping there so then it returns back which effectively causes oscillation. If I do not multiply the force with the time then the moving entity behaves slightly differently depending on the game speed.
By second (?) Newton's law F = m * a where F is force, m is mass and a is acceleration. Force is measured in newtons, mass in kilograms and acceleration in meters per second per second. So, you just need to apply bigger force, and lave the calculation as is.

rotation problems when firing

I have a big problem when I try to rotate my projectiles along with the ship's rotation.
I want my spaceship shoot two shots at once. So I used my first array of projectiles and placed them at the right side of the ship (one shot on the left and one on the right). So far so good.
But when I shoot with my spaceship and I turn around the rotations of the projectiles look very strange. It's hard to describe so here's an image that shows the error in action.
http://imageshack.us/photo/my-images/841/projectilebug.jpg
I have huge problems rotating the projectiles ALONG with the spaceship so that it looks really good. Currently there is one single shot only. My shooting gets screwed up when I rotate and fire at once.
The goal is to create a dual firing cannon like in the picture shown.
Here is some code that places the projectile at the left of the ship (that doesn't really work):
var projectileRadians:Number = (player.frame / 180) * 3.14159;
tempProjectile.x = (player.point.x + 6) + Math.cos(projectileRadians);
tempProjectile.y = (player.point.y + 3) + Math.sin(projectileRadians);
The velocity code:
tempProjectile.nextX = tempProjectile.x;
tempProjectile.nextY = tempProjectile.y;
tempProjectile.dx = rotationVectorList[player.frame].x;
tempProjectile.dy = rotationVectorList[player.frame].y;
This updates the position:
nextX += (dx * (speed + Math.abs(xAdjust))) * step;
nextY += (dy * (speed + Math.abs(yAdjust))) * step;
The velocity code looks ok. You don't seem to be calculating the gun position correctly.
Edit: we're not using the standard trig frame of reference; instead, we're using one where angle=0 means up and angles increase as you rotate clockwise. So we take the standard usage and swap sin and cos.
Here, gx and gy mean the coordinates of a gun relative to the coordinates around which the ship moves. We need to rotate the vector <gx, gy> by the angle before adding it to the ship coords:
cs = Math.cos(projectileRadians);
sn = Math.sin(projectileRadians);
tempProjectile.x = player.point.x + gx * sn - gy * cs;
tempProjectile.y = player.point.y + gx * cs + gy * sn;
It would be a good optimization to restrict the ship's angle to certain values (perhaps every 5 degrees), and put all the trig functions into a table ahead of time. Trig functions are expensive to compute.
Note that this also assumes that positive Y is "up". Computer graphics often start with (0, 0) in the upper left corner and increase Y as you move down, so you may have to switch the sign of Y.