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.
Related
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 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;
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);
I'm creating a small curling/shuffleboard game in java, where I'm emphasizing on the physics.
At the moment the game can shove the curlingstone along the x-axis using the following to calculate the x position. The player can decide the initial x-velocity.
xPos = xIniVel* time - 0.5 * mu * mass * g * time* time;
I'm using a gameTimer which runs as long as the ball is in motion.
double speed = xIniVel- mu * G * mass * time;
if (speed <= 0.0) {gameTimer.stop();}
The method updateDisplay() then redraws the ball/curlingstone at the new position.
int x = (int) (xPos* 100);
int y = (int) (yPos* 100);
g.setColor(Color.green);
g.fillOval(x, y, 22, 22);
The problem I'm having is how can I make the stone anywhere else than along the x-axis? Preferably I want the player to type in an angle, but an initial y-velocity will also work.
Edit: Screenshot of the game.
By adding exactly the same code for the y velocity as you have for the x
yPos = yIniVel* time - 0.5 * mu * mass * g * time* time;
A simple way to use an initial angle and velocity would be to use trigonometry to solve for the x velocity and y velocity components.
http://www.physicsclassroom.com/class/vectors/u3l2d.cfm
This webpage provides a basic understanding of doing so. Then simply copy the code you used for the x position and replace the x initial velocity with the y initial velocity.
Also, your equation for motion seems somewhat flawed. Even if there is no initial velocity, you still end up with an increasing negative position. This is because xIniVel * time = 0, then 0 - (0.5*mu*mass*g*time^2) = -(0.5*mu*mass*g*time^2).
So i've made my own FPS, graphics and guns and all of that cool stuff; When we fire, the bullet should take a random direction inside the crosshair, as defined by:
float randomX=(float)Math.random()*(0.08f*guns[currentWeapon].currAcc)-(0.04f*guns[currentWeapon].currAcc);
float randomY=(float)Math.random()*(0.08f*guns[currentWeapon].currAcc)-(0.04f*guns[currentWeapon].currAcc);
bulletList.add(new Bullet(new float[]{playerXpos, playerYpos, playerZpos}, new float[]{playerXrot+randomX, playerYrot+randomY}, (float) 0.5));
We calculate the randomness in X and Y (say you had a crosshair size (guns[currentWeapon].currAcc) of 10, then the bullet could go 0.4 to any side and it would remain inside the crosshair.
After that is calculated, we send the player position as the starting position of the bullet, along with the direction it's meant to take (its the player's direction with that extra randomness), and finally it's speed (not important atm, tho).
Now, each frame, the bullets have to move, so for each bullet we call:
position[0] -= (float)Math.sin(direction[1]*piover180) * (float)Math.cos(direction[0]*piover180) * speed;
position[2] -= (float)Math.cos(direction[1]*piover180) * (float)Math.cos(direction[0]*piover180) * speed;
position[1] += (float)Math.sin(direction[0]*piover180) * speed;
So, for X and Z positions, the bullet moves according to the player's rotation on the Y and X axis (say you were looking horizontally into Z, 0 degrees on X and 0 on Y; X would move 0*1*speed and Z would move 1*1*speed).
For Y position, the bullet moves according to the rotation on X axis (varies between -90 and 90), meaning it stays at the same height if the player's looking horizontally or moves completely up if the player is looking vertically.
Now, the problem stands as follows:
If i shoot horizontally, everything works beautifully. Bullets spread around the cross hair, as seen in https://dl.dropbox.com/u/16387578/horiz.jpg
The thing is, if i start looking up, the bullets start concentrating around the center, and make this vertical line the further into the sky i look.
https://dl.dropbox.com/u/16387578/verti.jpg
The 1st image is around 40ยบ in the X axis, the 2nd is a little higher and the last is when i look vertically.
What am i doing wrong here? I designed this solution myself can im pretty sure im messing up somewhere, but i cant figure it out :/
Basicly the vertical offset calculation (float)Math.cos(direction[0]*piover180) was messing up the X and Z movement because they'd both get reduced to 0. The bullets would make a vertical line because they'd rotate on the X axis with the randomness. My solution was to add the randomness after that vertical offset calculation, so they still go left and right and up and down after you fire them.
I also had to add an extra random value otherwise you'd just draw a diagonal line or a cross.
float randomX=(float)Math.random()*(0.08f*guns[currentWeapon].currAcc)-(0.04f*guns[currentWeapon].currAcc);
float randomY=(float)Math.random()*(0.08f*guns[currentWeapon].currAcc)-(0.04f*guns[currentWeapon].currAcc);
float randomZ=(float)Math.random()*(0.08f*guns[currentWeapon].currAcc)-(0.04f*guns[currentWeapon].currAcc);
bulletList.add(new Bullet(new float[]{playerXpos, playerYpos, playerZpos}, new float[]{playerXrot, playerYrot}, new float[]{randomX,randomY, randomZ},(float) 0.5));
And the moving code...
vector[0]= -((float)Math.sin(dir[1]*piover180) * (float)Math.cos(dir[0]*piover180)+(float)Math.sin(random[1]*piover180)) * speed;
vector[1]= ((float)Math.sin(dir[0]*piover180)+(float)Math.sin(random[0]*piover180)) * speed;
vector[2]= -((float)Math.cos(dir[1]*piover180) * (float)Math.cos(dir[0]*piover180)+(float)Math.sin(random[2]*piover180)) * speed;
You didn't need to bust out any complex math, your problem was that when you were rotating the bullet around the y axis for gun spread, if you were looking directly up (that is, through the y axis, the bullet is being rotated around the path which its going, which means no rotation whatsoever (imagine the difference between sticking your arm out forwards towards a wall and spinning in a circle, and sticking you arm out towards the sky and spinning in a circle. Notice that your hand doesn't move at all when pointed towards the sky (the y-axis)) and so you get those "diagonal" bullet spreads.
The trick is to do the bullet spread before rotating by the direction the player is looking in, because that way you know that when you are rotating for spread, that the vector is guaranteed to be perpendicular to the x and y axes.
this.vel = new THREE.Vector3(0,0,-1);
var angle = Math.random() * Math.PI * 2;
var mag = Math.random() * this.gun.accuracy;
this.spread = new THREE.Vector2(Math.cos(angle) * mag,Math.sin(angle) * mag);
this.vel.applyAxisAngle(new THREE.Vector3(0,1,0),this.spread.y / 100); //rotate first when angle gaurenteed to be perpendicular to x and y axes
this.vel.applyAxisAngle(new THREE.Vector3(1,0,0),this.spread.x / 100);
this.vel.applyAxisAngle(new THREE.Vector3(1,0,0),player.looking.x); //then add player looking direction
this.vel.applyAxisAngle(new THREE.Vector3(0,1,0),player.looking.y);
this.offset = this.vel.clone()
I don't use java but I hope you get the main idea of what im doing by this javascript. I am rotating a vector going in the negative z direction (default direction of camera) by the spread.y, and spread.x, and then I am rotating by the pitch and yaw of the angle at which the player is facing.