i want to use an acceleration algorithm to change the speed of a supposed aicraft to move it in a 2D environment. In example:
positionX = positionX + (speed based on acceleration);
positionY = positionY + (speed based on acceleration);
My problem is that if i do it like that the result, assuming the speed is 50, will be position += 50, which is entirely wrong since i don't want to use speed as the number of X it will move on the axis. I want the speed to be some sort of basis for the axis numbers.
In example, if lets say speed is 50 and 50 speed means 3 X per movement then that means
positionX + speed = positionX+3;
I want to create that in code along with an acceleration method that will increase the speed by a percentage.
So my question is how to make speed as a reference point kind of thing.
Keep it simple. The physics aren't difficult. The "math" is no more difficult than multiplying and adding.
You want to deal with changes in velocity and position over increments of time.
Position, velocity, and acceleration are vector quantities. In your 2D world, that means that each one has a component in the x- and y-directions.
So if you increment your time:
t1 = t0 + dt
Your position will change like this if the velocity is constant over that time increment dt:
(ux1, uy1) = (ux0, uy0) + (vx0*dt, vy0*dt)
The velocity will change like this if the acceleration is constant over that time increment dt:
(vx1, vy1) = (vx0, vy0) + (ax0*dt, ay0*dt)
Update your accelerations if there are forces involved using Newton's law:
(ax0, ay0) = (fx0/m, fy0/m)
where m is the mass of the body.
Update the positions, velocities, and accelerations at the end of the time step and repeat.
This assumes that using the values for acceleration and velocity at the start of the step is accurate enough. This will limit you to relatively smaller time steps. (It's called "explicit integration".)
Here's an example. You have a cannon at (x, y) = (0, 0) with a cannonball of mass 20 lbm inside it. The cannon is inclined up from the horizontal at 30 degrees. We'll neglect air resistance, so there's no force in the x-direction acting on the cannonball. Only gravity (-32.2 ft/sec^2) will act in the y-direction.
When the cannon goes off, it'll launch the cannonball with an initial speed of 40 ft/sec. The (vx, vy) components are (40*cos(30 degrees), 40*sin(30 degrees)) = (34.64 ft/sec, 20 ft/sec)
So if you plug into our equations for a time step of 0.1 second:
(ax0, ay0) = (0, -32.2 ft/sec^2)
(vx1, vy1) = (vx0, vy0) + (ax0, ay0)*dt = (34.64 ft/sec, 20 ft/sec) + (0, -3.22 ft/sec) = (34.64, 16.78)
(ux1, uy1) = (ux0, uy0) + (vx0, vy0)*dt = (3.464 ft, 1.678 ft)
Take another time step of 0.1 seconds with these values as the start. Rinse, repeat....
You do this individually for both x- and y- axes.
You can make this slightly more real by making the initial height of the cannon ball equal to half the diameter of your cannon wheels.
You can add a small negative acceleration in the x-direction to simulate wind resistance.
Assume your target is off to the right along the x-axis.
If you fire with the cannon pointing straight up the equations will show the ball going up, slowing down under it reaches its apex, and then coming straight down. No hit, except perhaps on your head and the cannon.
If you fire with the cannon horizontal, the equations say the ball with move with constant velocity in the x-direction and only fall the initial height of the cannon. Your enemies will taunt you: "Air ball! Air ball!"
So if you want the ball to intersect with the ground (a.k.a. reach position y = 0) within some blast radius of your target's location, you'll have to play with the initial speed and the angle of the cannon from the horizontal.
You just need to use the equations of movement for each axis:
x(t)= x0 + v0*t + 1/2*a*t^2 #where x0 is the initial position, v0 is the initial velocity and a is the acceleration you are considering, all relatively to an axis
Now you need to define the instants at which you are calculating the position, as well as writing the values of velocity and acceleration in the correct units as #markusw suggested.
like this:
double calculateSpeed(double value) {
return value / 16.66;
}
And call it like this:
positionX = positionX + calculateSpeed(50);
Related
If got a game project where I'm using box2d. Now in my MovementSystem (I'm using a Entity-Component-Based-Approach), I want Box2D to move my objects arround, according to the desired velocities which are set by the controls.
Unfortunately the velocities seems never to get high enough. Even when doing an applyLinearImpulse with a velocity-vector (the desired velocity) of 245044.23 for each axis for example, the resulting velocity of the body just became something about 90.0. What am I'm doing wrong? Is there a limitation or something?
Here's my code for running the velocity-update and world-step:
//************************
// physics-system
//************************
public void update(float deltaTime) {
float frameTime = Math.min(deltaTime, 0.25f);
accumulator += frameTime;
if (accumulator >= MAX_STEP_TIME) {
world.step(MAX_STEP_TIME, 6, 2);
accumulator -= MAX_STEP_TIME;
for (Entity entity : entities) {
TransformComponent transform = tim.get(entity);
BodyComponent bodyComp = bod.get(entity);
VelocityComponent velocity = vel.get(entity);
Vector2 bodyVelocity = bodyComp.body.getLinearVelocity();
float velChangeX = velocity.horizontalVelocity - bodyVelocity.x;
float velChangeY = velocity.verticalVelocity - bodyVelocity.y;
float impulseX = bodyComp.body.getMass() * velChangeX;
float impulseY = bodyComp.body.getMass() * velChangeY;
bodyComp.body.applyLinearImpulse(new Vector2(impulseX, impulseY), bodyComp.body.getWorldCenter(),
false);
// update transform
Vector2 position = bodyComp.body.getPosition();
transform.x = (int) position.x;
transform.y = (int) position.y;
// slowingdownVelocitys(velocity);
}
}
}
And here the definiton of my currently only entity with a box2D-Component (called a BodyComponent):
Entity entity = new Entity();
//...
BodyComponent bodyComponent = new BodyComponent();
BodyDef bodyDef = new BodyDef();
bodyDef.type = BodyDef.BodyType.DynamicBody;
bodyDef.position.set(transformComponent.getX(), transformComponent.getY());
bodyComponent.body = GameManager.getB2dWorld().createBody(bodyDef);
bodyComponent.body.applyAngularImpulse(50f, true);
CircleShape circle = new CircleShape();
circle.setRadius(2f);
FixtureDef fixtureDef = new FixtureDef();
fixtureDef.shape = circle;
fixtureDef.density = 10f;
fixtureDef.friction = 0.4f;
fixtureDef.restitution = 0.6f; // Make it bounce a little bit
bodyComponent.body.createFixture(fixtureDef);
circle.dispose();
entity.add(bodyComponent);
//...
Box2D does limit velocities yes. The limit is there basically to avoid inaccuracies of floating point arithmetic. A velocity of 245044.23 with a step time of 1/60th of a second is well above this limit as you've discovered. If you could lower your step time to say 1/200000th of a second you could simulate 245044.23 meters per second but I think most of us would have trouble getting that to run in real-time. At a step rate of 60 steps per second (each step being only 1/60 of a simulated second), the velocity you've stated is approximately 4084 meters per step since Box2D units are basically MKS units (meters, kilograms, seconds). The velocity limit meanwhile is limited per step to 2 meters per step (by b2_maxTranslation). This limit can be increased but as it's increased you're more likely to see less physical-like behaviors like tunneling.
As to what you're doing wrong, besides trying to use a velocity way higher than Box2D can handle, usually problems like you've described are the result of the visual scaling used. Keep in mind that Box2D positions are in units of meters while most of our monitors are significantly smaller (in either horizontal or vertical direction) than a meter. The Box2D FAQ has this to say about scaling in terms of pixels:
Suppose you have a sprite for a character that is 100x100 pixels. You
decide to use a scaling factor that is 0.01. This will make the
character physics box 1m x 1m. So go make a physics box that is 1x1.
Now suppose the character starts out at pixel coordinate (345,679). So
position the physics box at (3.45,6.79). Now simulate the physics
world. Suppose the character physics box moves to (2.31,4.98), so move
your character sprite to pixel coordinates (231,498). Now the only
tricky part is choosing a scaling factor. This really depends on your
game. You should try to get your moving objects in the range 0.1 - 10
meters, with 1 meter being the sweet spot.
The question that you'll want to answer is what scaling to use such that the physical velocities which Box2D can handle can translate to the visual effect in a way that can still be seen. At the step simulation of 1/60 of second (per step), the limit of velocity that Box2D will deal with is +/- 120 meters per second. But with clever use of scaling between world coordinates and graphical coordinates, that can be like 120 units-of-distance per second where you can sort of make the units-of-distance to be kilometers or tera-meters or whatever.
Beware that going really slow can pose problems too - like running into the velocity threshold for collision responses (b2_velocityThreshold).
Hope this helps!
Example: A cannon fired with the speed of ball 60 kpH/
Given: (60 kpH)
Distance = 60 kilometers,
Time = 1 hour
Libgdx: GameWorld
// Given that I am using 1/45.0f step time, the rest iteration velocity 6 and position 2
// Given that 60.00012 kilometres per hour = 16.6667 metres per second
float speed = 16.6667f; // 16.6667 metres per second
Vector2 bulletPosition = body.getPosition();
Vector2 targetPosition = new Vector2(touchpoint.x touchpoint.y);
Vector2 targetDirection = targetPosition.cpy().sub(bulletPosition).scl(speed);
Problem: but my problem is the cannon ball is not moving in my desired speed, also how do I log the body speed so I could check if the speed is correct. I just noticed it was wrong because the cannon ball is moving so slow, imagine 60 kpH
PS: assume that the above picture width is 5 meters and height is 3 meters
body.setLinearVelocity(targetDirection.scl(deltaTime));
Problem 2: I have no idea how do I compute force by the given speed and step time
// Given that F = ma
Vector2 acceleration = ???
float mass = body.getMass();
Vector2 force = ???
body.applyForces(force);
In Box2d, rather than directly applying a force instead you apply an impulse which is a force applied for a time, which effectively results in a near instantaneous acceleration. To calculate your impulse is just a bit of physics.
First we define some variables, F is the force in newtons, a is acceleration, I is the impulse (which we want to calculate to apply in Box2d), u is the initial velocity (metres per second), v is the final velocity and t is time in seconds.
Using Newtons laws and the definition for acceleration we start with:
Now we can calculate the impulse:
In your case, u is 0 because the cannon ball is initially at rest so it boils down to:
And that's it! So, in your code you would apply an impulse on the cannon ball equal to it's mass times the required velocity. The following code also contains how you would compute the direction to your touchPoint.
E.g:
float mass = body.getMass();
float targetVelocity = 16.6667f; //For 60kmph simulated
Vector2 targetPosition = new Vector2(touchpoint.x, touchpoint.y);
// Now calculate the impulse magnitude and use it to scale
// a direction (because its 2D movement)
float impulseMag = mass * targetVelocity;
// Point the cannon towards the touch point
Vector2 impulse = new Vector2();
// Point the impulse from the cannon ball to the target
impulse.set(targetPosition).sub(body.getPosition());
// Normalize the direction (to get a constant speed)
impulse.nor();
// Scale by the calculated magnitude
impulse.scl(impulseMag);
// Apply the impulse to the centre so there is no rotation and wake
// the body if it is sleeping
body.applyLinearImpulse(impulse, body.getWorldCentre(), true);
Edit: in response to the comment:
A normalized vector is a unit length vector meaning it has a size of 1 (irrespective of which angle it is at). A visual explanation (from Wikipedia):
Both vectorsd1 andd2 are unit vectors and so are called normalized. In Vector2, the nor function makes the vector normalized by keeping it at the same angle but giving it a magnitude of one. As the below diagram shows (blue are original vectors and green are after normalization):
For your game, the point of this is so that the cannon ball travels at the same speed whether the player touched the screen very close or very far from the cannon ball, all that matters is the angle to the cannon.
I'm creating a racing car game and am having trouble figuring out how to get steering to work. I have a basic race 2D race course that is built in a 3D environment. The program only uses x and y, with z being 0.
My race course consists of a road that is 29 units wide in the x-axis and two long tracks that are 120 units long in the y direction. At 120 units in the y-axis there is a 180 degree turn. You can think of the course as looking similar to a nascar styled race course.
I'm trying to set my car's steering so that it can turn realistically when I reach the 180 degree turns. I'm using two variables that separately control the x / y positions, as well as two variables for the x / y velocities. My code at the moment is as follows:
public void steering(){
double degreesPerFrame = 180 / (2*30); //180 degrees over 30 fps in 2s
velocityX = velocityX * -1 * Math.cos(degreesPerFrame);
velocityY = velocityY * -1 * Math.sin(degreesPerFrame);
double yChange = Math.sin(degreesPerFrame) * velocityY;
double xChange = Math.cos(degreesPerFrame) * velocityX;
x += xChange; //change x position
y += yChange; //change y position
}
I'm not completely sure how I can get my steering to turn properly. I'm stuck at the moment and not sure what I would need to change in my function to get steering working properly.
I think this would be easier if you don't use angles at all for you calculations. Simply operate with position, velocity, and acceleration vectors.
First, a quick reminder from physics 101. With a small time step dt, current position p, and current velocity vector v, and an acceleration vector a, the new position p' and velocity vector v' are:
v' = v + a * dt
p' = p + v' * dt
The acceleration a depends on the driver input. For example:
When moving ahead at a constant speed, it is 0.
When accelerating, it is limited by engine power and tire grip in longitudinal direction.
When turning, it is limited by tire grip in lateral direction.
When braking, it is limited by tire grip (mostly, the brakes are typically strong enough to not be a limit).
For a relatively simple model, you can assume that grip in longitudinal and lateral direction are the same (which is not entirely true in reality), which limits the total acceleration to a vector within a circle, which is commonly called the friction circle.
That was mainly just background. Back to the more specific case here, which is turning at a steady speed. The result of steering the car can be modeled by a lateral force, and corresponding acceleration. So in this case, we can apply an acceleration vector that is orthogonal to the current velocity vector.
With the components of the velocity vector:
v = (vx, vy)
a vector that points orthogonally to the left (you mentioned NASCAR, so there's only left turns...) of this is:
(-vy, vx)
Since we want to control the amount of lateral acceleration, which is the length of the acceleration vector, we normalize this vector, and multiply it by the maximum acceleration:
a = aMax * normalize(-vy, vx)
If you use real units for your calculations, you can apply a realistic number for the maximum lateral acceleration aMax. Otherwise, just tweak it to give you the desired maneuverability for the car in your artificial units.
That's really all you need. Recapping the steps in code form:
// Realistic value for sports car on street tires when using
// metric units. 10 m/s^2 is slightly over 1 g.
static const float MAX_ACCELERATION = 10.0f;
float deltaTime = ...; // Time since last update.
float accelerationX = -veclocityY;
float accelerationY = velocityX;
float accelerationScale = MAX_ACCELERATION /
sqrt(accelerationX * accelerationX + accelerationY * accelerationY);
accelerationX *= accelerationScale;
acceleration *= accelerationScale;
velocityX += accelerationX * deltaTime;
velocityY += accelerationY * deltaTime;
x += velocityX * deltaTime;
y += velocityY * deltaTime;
Inside my game, I have a spinner, just like the ones in Mario.
When the game doesn't lag, the spinner functions perfectly and rotates in a full 360 degrees circle at constant speed.
However, when it lags (which happens a lot for Android version), gaps start appearing between the different projectiles and it doesn't even rotate in a circle anymore, it just rotates in a distorted elliptical pattern.
Here is my Java code for the Spinner
helper += speedVariable * 1f;
speedY = (float) (speedVariable *helper2 * Math.cos(Math.toRadians(helper)));
speedX = (float) (speedVariable * -1 * helper2 * Math.sin(Math.toRadians(helper)));
setX(getX() + (speedX * delta) + ((Background.bg.getSpeedX() * 5) * delta));
setY(getY() + (speedY * delta));
float helper is the field that is inside the cosine and sine functions that make the spinner spin
speedVariable controls the speed of revolution
helper2 sets the radius of which it rotates
Am I doing anything wrong? Thanks!
Don't set the new position relative to the previous one. You want to stay on a circle around a central point, so it's a lot easier to reliably calculate the position from that point:
setX(getCenterX() + xFromCenter + (Background.bg.getSpeedX() * 5));
setY(getCenterY() + yFromCenter);
(I'm not sure what the Background part is about, I just assumed it should be kept.)
xFromCenter and yFromCenter are the cosine and sine of the rotation. In your original code, you had the rotation in helper, so let's keep it like that. But you should apply delta to its increment, since you want to rotate the same amount of degrees in the same amount of seconds.
helper += speedVariable * delta;
Think of it like this: The delta should directly influence how much it rotates, not how much it moves on the x and y axis (as those values don't change linearly with time).
Then you can just get xFromCenter as you got speedX, except there's no need for speed there now, and do the same for Y. Though I'd set the X by cos and Y by sin, not the other way around - that's the typical usage, with angles measured from 0 at the right side. (The difference is just a 90 degrees rotation.)
Also, a small matter: you could use the MathUtils.sinDeg() and cosDeg() functions when calculating, so you don't need to convert to radians and cast the result back to float. It's a bit more readable.
The entire code:
helper += speedVariable * delta;
xFromCenter = helper2 * MathUtils.cosDeg(helper));
yFromCenter = -1 * helper2 * MathUtils.sinDeg(helper));
setX(getCenterX() + xFromCenter + (Background.bg.getSpeedX() * 5));
setY(getCenterY() + yFromCenter);
You might need to play around with the constants (helper2, speedVariable, -1 multiplication) and possibly undo the swapping of X and Y to get exactly what you want, but this should keep the objects on a circle under all conditions.
I've been working on a top down car game for quite a while now, and it seems it always comes back to being able to do one thing properly. In my instance it's getting my car physics properly done.
I'm having a problem with my current rotation not being handled properly. I know the problem lies in the fact that my magnitude is 0 while multiplying it by Math.cos/sin direction, but I simply have no idea how to fix it.
This is the current underlying code.
private void move(int deltaTime) {
double secondsElapsed = (deltaTime / 1000.0);// seconds since last update
double speed = velocity.magnitude();
double magnitude = 0;
if (up)
magnitude = 100.0;
if (down)
magnitude = -100.0;
if (right)
direction += rotationSpeed * (speed/topspeed);// * secondsElapsed;
if (left)
direction -= rotationSpeed * (speed/topspeed);// * secondsElapsed;
double dir = Math.toRadians(direction - 90);
acceleration = new Vector2D(magnitude * Math.cos(dir), magnitude * Math.sin(dir));
Vector2D deltaA = acceleration.scale(secondsElapsed);
velocity = velocity.add(deltaA);
if (speed < 1.5 && speed != 0)
velocity.setLength(0);
Vector2D deltaP = velocity.scale(secondsElapsed);
position = position.add(deltaP);
...
}
My vector class emulates vector basics - including addition subtraction, multiplying by scalars... etc.
To re-iterate the underlying problem - that is magnitude * Math.cos(dir) = 0 when magnitude is 0, thus when a player only presses right or left arrow keys with no 'acceleration' direction doesn't change.
If anyone needs more information you can find it at
http://www.java-gaming.org/index.php/topic,23930.0.html
Yes, those physics calculations are all mixed up. The fundamental problem is that, as you've realized, multiplying the acceleration by the direction is wrong. This is because your "direction" is not just the direction the car is accelerating; it's the direction the car is moving.
The easiest way to straighten this out is to start by considering acceleration and steering separately. First, acceleration: For this, you've just got a speed, and you've got "up" and "down" keys. For that, the code looks like this (including your threshold code to reduce near-zero speeds to zero):
if (up)
acceleration = 100.0;
if (down)
acceleration = -100.0;
speed += acceleration * secondsElapsed;
if (abs(speed) < 1.5) speed = 0;
Separately, you have steering, which changes the direction of the car's motion -- that is, it changes the unit vector you multiply the speed by to get the velocity. I've also taken the liberty of modifying your variable names a little bit to look more like the acceleration part of the code, and clarify what they mean.
if (right)
rotationRate = maxRotationSpeed * (speed/topspeed);
if (left)
rotationRate = maxRotationSpeed * (speed/topspeed);
direction += rotationRate * secondsElapsed;
double dir = Math.toRadians(direction - 90);
velocity = new Vector2D(speed * Math.cos(dir), speed * Math.sin(dir));
You can simply combine these two pieces, using the speed from the first part in the velocity computation from the second part, to get a complete simple acceleration-and-steering simulation.
Since you asked about acceleration as a vector, here is an alternate solution which would compute things that way.
First, given the velocity (a Vector2D value), let's suppose you can compute a direction from it. I don't know your syntax, so here's a sketch of what that might be:
double forwardDirection = Math.toDegrees(velocity.direction()) + 90;
This is the direction the car is pointing. (Cars are always pointing in the direction of their velocity.)
Then, we get the components of the acceleration. First, the front-and-back part of the acceleration, which is pretty simple:
double forwardAcceleration = 0;
if (up)
forwardAcceleration = 100;
if (down)
forwardAcceleration = -100;
The acceleration due to steering is a little more complicated. If you're going around in a circle, the magnitude of the acceleration towards the center of that circle is equal to the speed squared divided by the circle's radius. And, if you're steering left, the acceleration is to the left; if you're steering right, it's to the right. So:
double speed = velocity.magnitude();
double leftAcceleration = 0;
if (right)
leftAcceleration = ((speed * speed) / turningRadius);
if (left)
leftAcceleration = -((speed * speed) / turningRadius);
Now, you have a forwardAcceleration value that contains the acceleration in the forward direction (negative for backward), and a leftAcceleration value that contains the acceleration in the leftward direction (negative for rightward). Let's convert that into an acceleration vector.
First, some additional direction variables, which we use to make unit vectors (primarily to make the code easy to explain):
double leftDirection = forwardDirection + 90;
double fDir = Math.toRadians(forwardDirection - 90);
double ldir = Math.toRadians(leftDirection - 90);
Vector2D forwardUnitVector = new Vector2D(Math.cos(fDir), Math.sin(fDir));
Vector2D leftUnitVector = new Vector2D(Math.cos(lDir), Math.sin(lDir));
Then, you can create the acceleration vector by assembling the forward and leftward pieces, like so:
Vector2D acceleration = forwardUnitVector.scale(forwardAcceleration);
acceleration = acceleration.add(leftUnitVector.scale(leftAcceleration));
Okay, so that's your acceleration. You convert that to a change in velocity like so (note that the correct term for this is deltaV, not deltaA):
Vector2D deltaV = acceleration.scale(secondsElapsed);
velocity = velocity.add(deltaV).
Finally, you probably want to know what direction the car is headed (for purposes of drawing it on screen), so you compute that from the new velocity:
double forwardDirection = Math.toDegrees(velocity.direction()) + 90;
And there you have it -- the physics computation done with acceleration as a vector, rather than using a one-dimensional speed that rotates with the car.
(This version is closer to what you were initially trying to do, so let me analyze a bit of where you went wrong. The part of the acceleration that comes from up/down is always in a direction that is pointed the way the car is pointed; it does not turn with the steering until the car turns. Meanwhile, the part of the acceleration that comes from steering is always purely to the left or right, and its magnitude has nothing to do with the front/back acceleration -- and, in particular, its magnitude can be nonzero even when the front/back acceleration is zero. To get the total acceleration vector, you need to compute these two parts separately and add them together as vectors.)
Neither of these computations are completely precise. In this one, you compute the "forward" and "left" directions from where the car started, but the car is rotating and so those directions change over the timestep. Thus, the deltaV = acceleration * time equation is only an estimate and will produce a slightly wrong answer. The other solution has similar inaccuracies -- but one of the reasons that the other solution is better is that, in this one, the small errors mean that the speed will increase if you steer the car left and right, even if you don't touch the "up" key -- whereas, in the other one, that sort of cross-error doesn't happen because we keep the speed and steering separate.