I'm making a 2d game in libgdx and I would like to know what the standard way of moving (translating between two known points) on the screen is.
On a button press, I am trying to animate a diagonal movement of a sprite between two points. I know the x and y coordinates of start and finish point. However I can't figure out the maths that determines where the texture should be in between on each call to render. At the moment my algorithm is sort of like:
textureProperty = new TextureProperty();
firstPtX = textureProperty.currentLocationX
firstPtY = textureProperty.currentLocationY
nextPtX = textureProperty.getNextLocationX()
nextPtX = textureProperty.getNextLocationX()
diffX = nextPtX - firstPtX
diffY = nextPtY - firstPtY
deltaX = diffX/speedFactor // Arbitrary, controlls speed of the translation
deltaX = diffX/speedFactor
renderLocX = textureProperty.renderLocX()
renderLocY = textureProperty.renderLocY()
if(textureProperty.getFirstPoint() != textureProperty.getNextPoint()){
animating = true
}
if (animating) {
newLocationX = renderLocX + deltaX
newLocationY = renderLocY + deltaY
textureProperty.setRenderPoint(renderLocX, renderLocY)
}
if (textureProperty.getRenderPoint() == textureProperty.getNextPoint()){
animating = false
textureProperty.setFirstPoint(textureProperty.getNextPoint())
}
batch.draw(texture, textureProperty.renderLocX(), textureProperty.renderLocY())
However, I can foresee a few issues with this code.
1) Since pixels are integers, if I divide that number by something that doesn't go evenly, it will round. 2) as a result of number 1, it will miss the target.
Also when I do test the animation, the objects moving from point1, miss by a long shot, which suggests something may be wrong with my maths.
Here is what I mean graphically:
Desired outcome:
Actual outcome:
Surely this is a standard problem. I welcome any suggestions.
Let's say you have start coordinates X1,Y1 and end coordinates X2,Y2. And let's say you have some variable p which holds percantage of passed path. So if p == 0 that means you are at X1,Y1 and if p == 100 that means you are at X2, Y2 and if 0<p<100 you are somewhere in between. In that case you can calculate current coordinates depending on p like:
X = X1 + ((X2 - X1)*p)/100;
Y = Y1 + ((Y2 - Y1)*p)/100;
So, you are not basing current coords on previous one, but you always calculate depending on start and end point and percentage of passed path.
First of all you need a Vector2 direction, giving the direction between the 2 points.
This Vector should be normalized, so that it's length is 1:
Vector2 dir = new Vector2(x2-x1,y2-y1).nor();
Then in the render method you need to move the object, which means you need to change it's position. You have the speed (given in distance/seconds), a normalized Vector, giving the direction, and the time since the last update.
So the new position can be calculated like this:
position.x += speed * delta * dir.x;
position.y += speed * delta * dir.y;
Now you only need to limit the position to the target position, so that you don't go to far:
boolean stop = false;
if (position.x >= target.x) {
position.x = target.x;
stop = true;
}
if (position.y >= target.y) {
position.y = target.y;
stop = true;
}
Now to the pixel-problem:
Do not use pixels! Using pixels will make your game resolution dependent.
Use Libgdx Viewport and Camera instead.
This alows you do calculate everything in you own world unit (for example meters) and Libgdx will convert it for you.
I didn't saw any big errors, tho' i saw some like you are comparing two objects using == and !=, But i suggest u to use a.equals(b) and !a.equals(b) like that. And secondly i found that your renderLock coords are always being set same in textureProperty.setRenderPoint(renderLocX, renderLocY) you are assigning the same back. Maybe you were supposed to use newLocation coords.
BTW Thanks for your code, i was searching Something that i got by you <3
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.
I have a Rectangle object that is placed on the screen and rendered using paintComponent.
I also have a rotation variable that determines the rotation of the object (using right and left keys to rotate) and repaints the object on the screen using Affine Transform.
In the keyPressed method, I have this which allows me to shoot bullets:
else if(key == KeyEvent.VK_SPACE) {
Bullet b = new Bullet(handler, player.x + 10, player.y - 10, ID.Bullet);
handler.addObject(b);
b.setDY(-3*Math.cos(player.rotation));
b.setDX(3*Math.sin(player.rotation));
}
If you see where I create a new bullet, in the second line, the second and third arguments in the new Bullet() are what determines where the bullets are created. Currently they just shoot from the same position on the rectangle regardless of the rotation.
I have failed at allowing the player to shoot a bullet from the direction they are facing, so if anyone has any suggestions that would be very helpful.
So basically you are trying to find the offset Vector from the player's position.
Let's assume, that you have the offset Vector, when you haven't rotated the object.
Then your task would be to rotate the given Vector by the rotation variable.
To do this we first need to look into geometry a bit. Let's think of a 2d-Vector as a triangle, that we can split into it's x and y components. We know, that the rotated Vector should have the same hypothenuse length as the original offset or in other terms the same magnitude.
This means that:
x₀² + y₀² = x₁² + y₁²
Since:
x² + y² = Vector-Magnitude
Your rotation variable keeps track of the inner angle of that "triangle vector" and hence we can describe the lengths as:
x = sin(rotation) * Vector Magnitude
y = cos(rotation) * Vector Magnitude
Now the only thing left to do is evalute those values. To do that, let us jump into the code, shall we?
float magnitude = offset.x*offset.x + offset.y*offset.y;
float x = Math.sin(rotation) * magnitude;
float y = Math.cos(rotation) * magnitude;
Bullet b = new Bullet(handler, player.x + x, player.y + y, ID.Bullet);
possible Errors and fixes
Make sure that you have the correct angle-format. The Java-Math class uses radiens for trigonometric methods.
There could be a constant rotation deviation, depending on what exactly you want your result to look like. This is a rather easy fix, as you just have to add this constant to the rotation variable. In unlikely instances you maybe also need to negate the rotation variable simply by using -rotation in the places I used rotation in my code.
If you know the rotation angle and the width of the rectangle then use this information to rotate the start position of the bullet too. For example to let the start position at the right side:
x = rectMiddleX + width/2 * cos(angle)
y = rectMiddleY + width/2 * sin(angle)
If angle is 0 it will start at the right side
else if(key == KeyEvent.VK_SPACE) {
Bullet b = new Bullet(handler, player.x + width/2 * cos(player.rotation), player.y - width/2 * sin(player.rotation), ID.Bullet);
//if player.x/y is in corner, add width/2 / height/2
handler.addObject(b);
b.setDY(-3*Math.cos(player.rotation));
b.setDX(3*Math.sin(player.rotation));
}
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.
Is it possible to calculate distance between two HitResult `s ?
Or how we can calculate real distance (e.g. meters) using ARCore?
In Java ARCore world units are meters (I just realized we might not document this... aaaand looks like nope. Oops, bug filed). By subtracting the translation component of two Poses you can get the distance between them. Your code would look something like this:
On first hit as hitResult:
startAnchor = session.addAnchor(hitResult.getHitPose());
On second hit as hitResult:
Pose startPose = startAnchor.getPose();
Pose endPose = hitResult.getHitPose();
// Clean up the anchor
session.removeAnchors(Collections.singleton(startAnchor));
startAnchor = null;
// Compute the difference vector between the two hit locations.
float dx = startPose.tx() - endPose.tx();
float dy = startPose.ty() - endPose.ty();
float dz = startPose.tz() - endPose.tz();
// Compute the straight-line distance.
float distanceMeters = (float) Math.sqrt(dx*dx + dy*dy + dz*dz);
Assuming that these hit results don't happen on the same frame, creating an Anchor is important because the virtual world can be reshaped every time you call Session.update(). By holding that location with an anchor instead of just a Pose, its Pose will update to track the physical feature across those reshapings.
You can extract the two HitResult poses using getHitPose() and then compare their translation component (getTranslation()).
The translation is defined as
...the position vector from the destination (usually
world) coordinate frame to the local coordinate frame, expressed in
destination (world) coordinates.
As for the physical unit of this I could not find any remark. With a calibrated camera this should be mathematically possible but I don't know if they actually provide an API for this
The answer is: Yes, you definitely can calculate distance between two HitResult's. The working units of ARCore, as well as ARKit, are meters. Sometimes, it's more useful to use centimetres. Here are a few ways how you do it with Java and great old Pythagorean theorem.
import com.google.ar.core.HitResult
MotionEvent tap = queuedSingleTaps.poll();
if (tap != null && camera.getTrackingState() == TrackingState.TRACKING) {
for (HitResult hit : frame.hitTest(tap)) {
// some logic...
}
}
// Here's the principle how you can calculate the distance
// between two anchors in 3D space using Java:
private double getDistanceMeters(Pose pose0, Pose pose1) {
float distanceX = pose0.tx() - pose1.tx();
float distanceY = pose0.ty() - pose1.ty();
float distanceZ = pose0.tz() - pose1.tz();
return Math.sqrt(distanceX * distanceX +
distanceY * distanceY +
distanceZ * distanceZ);
}
// Convert Meters into Centimetres
double distanceCm = ((int)(getDistanceMeters(pose0, pose1) * 1000))/10.0f;
// pose0 is the location of first Anchor
// pose1 is the location of second Anchor
Alternatively, you can use the following math:
Pose pose0 = firstAnchor.getPose() // first pose
Pose pose1 = secondAnchor.getPose() // second pose
double distanceM = Math.sqrt(Math.pow((pose0.tx() - pose1.tx()), 2) +
Math.pow((pose0.ty() - pose1.ty()), 2) +
Math.pow((pose0.tz() - pose1.tz()), 2));
double distanceCm = ((int)(distanceM * 1000))/10.0f;
I have a character in my game that must rotate smoothly to get to a desired angle. Consider angle as the current angle and touchAngle as the desired angle which is always between 0 to 360. I want to add +1/-1 to current angle in every game update to get to the desired touchAngle. The problem is first it must chose direction and it must be between 0 to 360. this is my pseudo code:
int touchAngle;
float angle;
public void update()
{
if ((int)angle != touchAngle) angle += ???
}
Since you have values that are always normalized in the interval [0 360] this should not be too hard.
You just need to distinguish two different cases:
angle < touchAngle
angle > touchAngle
in the first case we want to rotate counterclockwise so the update has to be angle =+ 1 (assuming that you want to turn of 1 every update cycle).
In the second case we want to turn clockwise so the update should be angle -= 1.
The problem is that this is not always the shortest way to rotate. For instance if:
angle == 359
touchAngle == 1
we don't want to make all the way 358, 357, 356...instead we want to rotate counterclockwise for just 2 units: 360, 1.
This can be achieved comparing the distance between the angles abs(angle - touchAngle).
If this value is bigger than 180 it means we are going the wrong way, so we have to do the way around so
if(angle < touchAngle) {
if(abs(angle - touchAngle)<180)
angle += 1;
else angle -= 1;
}
else {
if(abs(angle - touchAngle)<180)
angle -= 1;
else angle += 1;
}
of course all of this until ((int)angale != touchAngle).
I might have made mistakes with the cases but this is the principle.
Generally you want to bring in time to the equation, so that you can smoothly change the angle over time. Most setups have a way to get a time it took to render the previous frame and the typical way to do this is to say..
int touchAngle;
float angle;
float deltaTime; //Time it took to render last frame, typically in miliseconds
float amountToTurnPerSecond;
public void update()
{
if((int)angle != touchAngle) angle += (deltaTime * amountToTurnPerSecond);
}
This will make it so that each second, your angle is changed by amountToTurnPerSecond, but changed slowly over each frame the correct amount of change so that it is smooth. Something to note about this is that you wont evenly end up at touchAngle most of the time, so checking to see if you go over and instead setting to touchAngle would be a good idea.
Edit to follow up on comment:
I think the easiest way to attain the correct direction for turn is actually not to use angles at all. You need to get the relative direction from your touch to your character in a 2d space. Typically you take the touch from screen space to world space, then do the calculations there (at least this is what I've done in the past). Start out by getting your touch into world space, then use the vector cross product to determine direction. This looks kind of like the following...
character position = cx, cy
target position = tx, ty
current facing direction of character = rx, ry
First we take the distance between the character and the target position:
dx = tx - cx
dy = ty - cy
This not only gives us how far it is from us, but essentially tells us that if we were at 0, 0, which quadrant in 2d space would the target be?
Next we do a cross product:
cross_product = dx * ry - dy * rx
If this is positive you go one way, if it's negative you go the other. The reason this works out is that if the distance is for instance (-5, 2) then we know that if we are facing directly north, the point is to our left 5 and forward 2. So we turn left.