I'm making a simple game that will have a space ship being gravitationally attracted to a star at the center of the screen. The code I have so far moved the rectangle (soon to be a rocket) in a way that is not smooth at all. The rectangle also will not stop when it reaches the star but rather keeps moving well beyond the star before turning back.
Here's my code:
public void move() {
// Deals with gravity towards the center star
if (x > game.getWidth() / 2) {
xVel -= xa;
}
if (x < game.getWidth() / 2) {
xVel += xa;
}
if (y > game.getHeight() / 2) {
yVel -= ya;
}
if (y < game.getHeight() / 2) {
yVel += ya;
}
x += xVel;
y += yVel;
}
I start by calculating where the rocket is located on the screen relative to the center, and then change its acceleration values, xa and ya, as needed. I then add the acceleration values to the Velocity variables, xVel and yVel.
Obviously you change the speed by
yVel += ya;
etc. and thats an acceptable thing to you as it seems from your wording. But that will create a constantly increasing speed (i.e. flying by at the end).
If you want to keep the speed constant you do
if (x > game.getWidth() / 2) {
xVel = -xa;
}
if (x < game.getWidth() / 2) {
xVel = xa;
}
and equivalent. That will have the rectangle oscilate probably around the center. Please update and come up with new questions.
Your spaceship's movement doesn't look smooth, because it is moving with no respect to time. What happens if you open up a CPU intensive application in the background, and your Java process gets less computing time? Now you are not calling move() as often, so your spaceship suddenly slows down.
You need to keep track of how much time has elapsed since the last call to move() - I'm sure you remember the high-school physics formula, distance = velocity * time. There are multiple ways to get the time - see here for a discussion specifically for games.
This will smooth out the movement a bit, but if you want the movement to look like gravity, then you can't just use velocity on its own - you also need to consider acceleration. Your space ship is accelerated towards the star due to its' gravity. The closer your spaceship gets to the star, the stronger the gravitational acceleration becomes. This is another high-school physics formula, which you should be able to find on the web.
You have not supplied all the code but to me it sounds like you are using integers where you should be using floats or doubles.
If you decalre the variables like the following
int x;
int velX;
You will not get the precision needed for smooth animation (there is nothing after the full stop). With integers 5 / 2 == 2 and not 2.5 as integers can only store whole numbers. This is why you are getting jerky motion.
You need to use floating point variables by decalring them as such.
float x = 0.0f;
float vecX;
or
double x = 0.0;
double vecX;
That will give you the smooth motion you want.
Related
I'm making a simple test program to try and develop a bullet hell game, but my problem is that I want to make an entity move in a desired path, and I don't know where to start.
Their movement system works by having an angle, and making it's cos and sin the x and y vectors for it's movement (that's the method my teacher taught me). Here's the part of the code that involves that
this.dx = Math.cos(Math.toRadians(angle));
this.dy = Math.sin(Math.toRadians(angle));
}
public void tick() {
x+=dx*speed;
y+=dy*speed;
//this makes it reverse when the window's wall
if(x > Game.WIDTH || x < 0) {
dx = -dx;
}
if(y > Game.HEIGHT || y < 0) {
dy = -dy;
}
//this is here so angles are stored correctly
if(angle < 0) {
angle += Math.PI*2;
}else if(angle > Math.PI*2) {
angle -= Math.PI*2;
}
}
But as I've said, I want it to follow a path I choose. I could specify it with an equation or another method (I really don't know what's more practical), but I don't know how to manipulate the angle to make it follow my desired path.
While I'm making this question, I've figured how to make a circle
angle -= 0.05;
this.dx = Math.cos(angle);
this.dy = Math.sin(angle);
but I'd like to have a way to feed in an equation and manipulate the angle in the right way as to make them follow the path the equation provides on a graph
I have a few different potential ideas that might be what you're looking for:
Option 1:
If you want it to follow a smooth path defined by a line on a graph, you could try to parameterise the equation, e.g. have both x and y depend on a variable t, rather than having x and y depend on one another. Then, you can use time as this parameter t, and set the entity's x and y directly based on plugging the time into the parametric equations rather than setting an angle.
Option 2:
If you're okay with the path not being a perfectly smooth curve, you could store the path as just a sequence of points in space. The entity stores which point it's currently heading towards and once that point is within a certain distance it moves onto heading towards the next point instead
Note:
With option 1, it involves updating the entity's x and y directly instead of setting an angle. If you do still want to update the angle so that the entity faces / fires in the direction it's moving, then you could keep track of where the entity is in the previous tick. Then using it's current position and previous position you can get a vector of the direction it's moving in, and use that to set the angle.
My gravity simulation acts more like a gravity slingshot. Once the two bodies pass over each other, they accelerate far more than they decelerate on the other side. It's not balanced. It won't oscillate around an attractor.
How do other gravity simulators get around it? example: http://www.testtubegames.com/gravity.html, if you create 2 bodies they will just oscillate back and forth, not drifting any further apart than their original distance even though they move through each other as in my example.
That's how it should be. But in my case, as soon as they get close they just shoot away from each other to the edges of the imaginary galaxy never to come back for a gazillion years.
edit: Here is a video of the bug https://imgur.com/PhhRhP7
Here is a minimal test case to run in processing.
//Constants:
float v;
int unit = 1; //1 pixel = 1 meter
float x;
float y;
float alx;
float aly;
float g = 6.67408 * pow(10, -11) * sq(unit); //g constant
float m1 = (1 * pow(10, 15)); // attractor mass
float m2 = 1; //object mass
void setup() {
size (200,200);
a = 0;
v = 0;
x = width/2; // object x
y = 0; // object y
alx = width/2; //attractor x
aly = height/2; //attractor y
}
void draw() {
background(0);
getAcc();
applyAcc();
fill(0,255,0);
ellipse(x, y, 10, 10); //object
fill(255,0,0);
ellipse(alx, aly, 10, 10); //attractor
}
void applyAcc() {
a = getAcc();
v += a * (1/frameRate); //add acceleration to velocity
y += v * (1/frameRate); //add velocity to Y
a = 0;
}
float getAcc() {
float a = 0;
float d = dist(x, y, alx, aly); //distance to attractor
float gravity = (g * m1 * m2)/sq(d); //gforce
a += gravity/m2;
if (y > aly){
a *= -1;}
return a;
}
Your distance doesn't include width of the object, so the objects effectively occupy the same space at the same time.
The way to "cap gravity" as suggested above is add a normal force when the outer edges touch, if it's a physical simulation.
You should get into the habit of debugging your code. Which line of code is behaving differently from what you expected?
For example, if I were you I would start by printing out the value of gravity every time you calculate it:
float gravity = (g * m1 * m2)/sq(d); //gforce
println(gravity);
You'll notice that your gravity value skyrockets as your circles get closer to each other. And this makes sense, because you're dividing by sq(d). Ad d gets smaller, your gravity increases.
You could simply cap your gravity value so it doesn't go off the charts anymore:
float gravity = (g * m1 * m2)/sq(d);
if(gravity > 100){
gravity = 100;
}
Alternatively you could cap d so it never goes below a certain value, but the result is the same.
In the end you'll find that this is not going to be as easy as you expected. You're going to have to tune the parameters quite a bit so your simulation works how you want.
Working demo here: https://beta.observablehq.com/#shaunlebron/1d-gravity
I followed the solution posted by the author of the sim that inspired this question here:
-First off, shrinking the timestep is always helpful. My simulation runs, as a baseline, about 40 ‘steps’ per frame, and 30 frames per second.
-To deal with the exact issue you talk about, I think modeling the bodies not as pure point masses - but rather spherical masses with a certain radius will be vital. That prevents the force of gravity from diverging to infinity. So, for instance, if you drop an asteroid into a star in my simulation (with collisions turned off), the force of gravity will increase as the asteroid gets closer, up until it reaches the surface of the star, at which point the force will begin to decrease. And the moment it’s at the center of the star (or nearby), the force will be zero (or nearly zero) - instead of near-infinite.
In my demo, I just completed turned off gravity when two objects are close enough together. Seems to work well enough.
i am trying to develop a game in libgdx in which i want to use explode effect with sprite when i clicked on it. is it possible or i should use BOX2D physics to apply the explode effect.?
You should use the API that handles Animations for you, or if you want to use particles you must use the API that provides Emmiters for you.
I'm doing explosion effects on my own. For every particle object I have variables that hold X and Y coords and in VX and VY I'm holding particle speed.
When explosion happens I'm creating some number of those objects (I'm storing them in a list). For every one of those particles I set X and Y coords with the coords of explosion it self. So they all start at same point. But for every particle I set different, random VX and VY (initial speed). So they will start moving in different directions.
I'm also implementing some gravity effect - in every frame I'm decreasing VY for some constant (multiplied with that delta time - time since last frame rendering, to get constant moving).
At end you should check somehow when to remove particle object. I.e. when it's coordinates are out of screen or after some time or something...
To make it even nice you can animate particles...use transparency...
One simple way you can achieve what you wanted is through the use of particles
I am guessing that your game is 2d so here is the official wiki about 2d particles
As MilanG stated the easiest way ist to create a Particle class
public class Particle{
public float x;
public float y;
public float vx;
public float vy;
public Particle(float x, float y, float vx, float vy){...}
}
Then when the user clicks on the Texture you would instantiate a lot of these particles and add them to an ArrayList. X and Y coordinates should be the center of your texture. vx can be a random number (negative and positive). vy can also be a random number depending on the visual effect of your particles. Experiment with the values a bit
for(int i = 0; i < 100; i++){
arrayList.add(new Particle(textureX,
textureY,
MathUtils.randomFloat(-200, 200),
MathUtils.randomFloat(0, 200)));
}
In the update loop you would change the x and y coordinates according to vx and vy values. The vy value should be decrased because of gravity:
for(Particle p : arrayList){
p.x += p.vx * delta;
p.y += p.vy * delta;
p.vy += -200 * delta; // -200 is gravity value
}
In you render code you draw the particle texture for every element in the arraylist:
for(Particle p : arrayList){
batch.draw(particleTexture, p.x, p.y);
}
Important:
You also need to implement some logic to remove particles from the arrayList (like checking for x and y coordinate boundings together with a CopyOnWriteArrayList or a second deleteParticles ArrayList)
Important as well:
Another point to mention is that this technique generates a lot of objects causing a heavy GC to follow. You can reduce that by using a Pooling technique. LibGDX has some pretty nice classes for that.
I am currently creating a 2D space game in Java, in which you control a ship in, well, space.
The game does not use any external libraries.
The ship is supposed to move towards the cursor. However, when moving the cursor, the old force does not magically disappear; the ship changes its course, over time, to eventually move in the desired direction.
However, I have run into an issue regarding the movement of the ship.
Basically, what I want to achieve is crudely illustrated by this image:
The image shows how the ship is supposed to move during one game tick.
To explain further:
The Ship's max speed is illustrated by the circle.
The Target Angle is where the cursor currently is.
The Current Angle is the direction that the ship is currently traveling.
The Current Angle should move closer and closer to the Target Angle until it reaches the point where these two angles are the same.
The ship should change direction toward the target angle taking the shortest route possible; it can turn both left and right, not just left or right.
Now I've explained what I want to achieve, now I will instead describe what I so far have achieved and how it works.
Basically, the "ship" is an image sitting in the center of the screen. When you "move" the ship, the ship stays put; what moves is the rest of the play area.
The current "position" of the ship relative to the coordinate system that represents the play area are the integers xPos and yPos.
Now for some sample code that shows how the system works:
int xPos;
int yPos;
public void updateMovement() {
xPos += xSpeed;
yPos += ySpeed;
}
public void moveForward() {
double yTempSpeed = ySpeed;
double xTempSpeed = xSpeed;
yTempSpeed += 0.01 * Math.sin(Math.toRadians(targetAngle));
xTempSpeed += 0.01 * Math.cos(Math.toRadians(targetAngle));
double resultVector = Math.sqrt(xTempSpeed * xTempSpeed + yTempSpeed * yTempSpeed);
if (resultVector < 2) {
ySpeed += 0.01 * Math.sin(Math.toRadians(targetAngle));
xSpeed += 0.01 * Math.cos(Math.toRadians(targetAngle));
}
This code successfully sets the Ship's max speed to the desired value, however, this does not work (the ships' course does not change) in the event where the resulting "vector" is larger than 2, i.e. when the speed is already at it's maximum and the targetAngle is too close to the angle which the ship is currently traveling (+- Pi / 2).
How would I go about changing the current angle based on this implementation?
public void moveForward() {
ySpeed += 0.01 * Math.sin(Math.toRadians(targetAngle));
xSpeed += 0.01 * Math.cos(Math.toRadians(targetAngle));
double currentSpeed = Math.sqrt(xTempSpeed * xTempSpeed + yTempSpeed * yTempSpeed);
if (currentSpeed > maxSpeed) {
//the resulting speed is allways <= maxspeed (normed to that)
ySpeed *= maxSpeed/currentSpeed;
xSpeed *= maxSpeed/currentSpeed;
}
hope this is what you needed... although it is quite unrealistic, that a spacecraft has a maximum Speed, but in terms of "playability" i would do the same.
What about normalizing speed of the ship, not to let it actually exceed your speed limit (=2):
//it's good to put all constants out of a function in one place
//to make it easier if you ever wanted to change it
private final int MAX_SPEED = 2;
private final double ACCEL_FACTOR = 0.01;
public void moveForward() {
ySpeed += ACCEL_FACTOR * Math.sin(Math.toRadians(targetAngle));
xSpeed += ACCEL_FACTOR * Math.cos(Math.toRadians(targetAngle));
//normalize ship speed, i.e. preserve ratio of xSpeed/ySpeed
//but make sure that xSpeed^2 + ySpeed^2 <= MAX_SPEED^2
//your code goes here
//...
}
Read about vector normalization. This way, the changes of ship speed will be applied normally (at this moment speed can be >= MAX_SPEED), but after normalization it will never get greater than MAX_SPEED, so your if instruction is not even needed.
You may find the following (Swift) code useful, although you would need to handle the per-frame integration of the ship's linear and angular velocities yourself:
func moveShipTowards(location: CGPoint) {
if let ship = shipNode? {
let distanceVector = CGVector(origin: ship.position, point: location)
let targetAngle = distanceVector.angle
let shipAngle = ship.zRotation
var dø = targetAngle - shipAngle
// convert to shortest arc
if dø > π {
dø -= 2.0 * π
} else if dø < -π {
dø += 2.0 * π
}
// resulting angular velocity
ship.physicsBody.angularVelocity = 12 * dø.clampedTo(π)
var velocityUnitVector = CGVector(cosf(ship.zRotation), sinf(ship.zRotation))
var magnitude = distanceVector.length.clampedTo(400)
ship.physicsBody.velocity = velocityUnitVector * magnitude
}
}
It handles having the ship deccelerate as the target point is approached. Looking at it just now, it doesn't appear to handle acceleration properly though.
This is my first post, so please bear with me. I'll try and be as clear as I can here about my problem.
First off, let me say that I suck at math. I failed it in school, and it pains me to no end that I can't grasp simple math concepts, especially since I love to code. Someone who wants to program, and can't perform basic math operations? Bad combo.
Aaaanyway, on to the problem.
Some context. I am writing an asteroids-type game. It's coming along quite nicely, and I've overcome all my hurdles so far thanks to this site (thank you!), and google. I've searched high and low to a solution to my problem, but it always seems like I run into a solution that either doesn't work, or I just don't understand and can't incorporate it into my code.
The issue involves rotation of the ship. I have an onscreen joystick class that returns the angle that the joystick is being pushed. I use that angle to point the ship in that same direction.
What I want to do is gradually turn the ship towards the angle the user wants to go, using the shortest turn, left or right. In my mind I'm thinking "How the hell do I go to say.. 350 degrees from 5 degrees, going left?". I don't know...
Here is my draw code:
public void draw(Canvas canvas){
canvas.save();
canvas.rotate((float) (fAngle + 90), (float) (dX + (mShip.getIntrinsicWidth() / 2)), (float) (dY + (mShip.getIntrinsicHeight() / 2)));
mShip.setBounds((int)dX, (int)dY, (int)dX + mShip.getIntrinsicWidth(), (int)dY + mShip.getIntrinsicHeight());
mShip.draw(canvas);
canvas.restore();
}
The angle is passed to the fAngle variable from the joystick getangle method. The angle is then increased by 90 degrees because of the image facing.
user.fAngle = oJoystick.getAngle();
So on each game tick, I want to turn the ship towards whatever direction the player wants to go, degree by degree. Any help with this would be greatly appreciated!
Thanks for reading!
In your tick function, instead of:
user.fAngle = oJoystick.getAngle();
Use this (adjust increment to your liking - this controls the rotation speed):
const float increment = 1.0;
float direction;
float joy = oJoystick.getAngle();
float ang = user.fAngle;
float fudge = 5.0;
if (abs (joy - ang) > fudge) {
if (joy > ang) {
if (joy - ang < 180)
direction = 1;
else
direction = -1;
} else if (joy < ang) {
if (ang - joy < 180)
direction = -1;
else
direction = 1;
}
} else // already pointing right direction
direction = 0;
user.fAngle = ang + direction * increment;
if (0 > user.fAngle)
user.fAngle += 360;
if (360 < user.fAngle)
user.fAngle -= 360;