I'm in the process of making a mobile game and am having trouble predicting the future location of a body/sprite pair. The method I am using to do this is below.
1 --futureX is passed in as the cocos2d sprite's location, found using CCSprite.getPosition().x
2 -- I am using b2Body values for acceleration and velocity, so I must correct the futureX coordinate by dividing it by PTM_RATIO (defined elsewhere)
3 -- the function solves for the time it will take for a the b2Body to reach the futureX position (based off of its x-direction acceleration and velocity) and then uses that time to determine where the futureY position for the body should be. I multiply by PTM_RATIO at the end because the coordinate is meant to be used for creating another sprite.
4 -- when solving for time I have two cases: one with x acceleration != 0 and one for x acceleration == 0.
5 -- I am using the quadratic formula and kinematic equations to solve my equations.
Unfortunately, the sprite I'm creating is not where I expect it to be. It ends up in the correct x location, however, the Y location is always too large. Any idea why this could be? Please let me know what other information is helpful here, or if there is an easier way to solve this!
float Sprite::getFutureY(float futureX)
{
b2Vec2 vel = this->p_body->GetLinearVelocity();
b2Vec2 accel = p_world->GetGravity();
//we need to solve a quadratic equation:
// --> 0 = 1/2(accel.x)*(time^2) + vel.x(time) - deltaX
float a = accel.x/2;
float b = vel.x;
float c = this->p_body->GetPosition().x - futureX/PTM_RATIO;
float t1;
float t2;
//if Acceleration.x is not 0, solve quadratically
if(a != 0 ){
t1 = (-b + sqrt( b * b - 4 * a * c )) / (2 * a);
t2 = (-b - sqrt( b * b - 4 * a * c )) / (2 * a);
//otherwise, solve linearly
}else{
t2 = -1;
t1 = (c/b)*(-1);
}
//now that we know how long it takes to get to posX, we can tell the y location on the sprites path
float time;
if(t1 >= 0){
time = t1;
}else{
time = t2;
}
float posY = this->p_body->GetPosition().y;
float futureY = (posY + (vel.y)*time + (1/2)*accel.y*(time*time))*PTM_RATIO;
return futureY;
}
SOLVED:
The issue was in this line:
float futureY = (posY + (vel.y)*time + (1/2)*accel.y*(time*time))*PTM_RATIO;
I needed to explicitley cast the (1/2) as a float. I fixed it with this:
float futureY = (posY + (vel.y)*time + (float).5*accel.y*(time*time))*PTM_RATIO;
Note, otherwise, the term with acceleration was evaluated to zero.
Related
I've been scratching my head for a couple of days on this one.
In my 2D top down game I have an array of FloatRects which represents walls.
I want to shoot a bullet represented by a point at X distance from the player (X being the weapon's range).
What I want to do is to check if there is a wall on the bullet's trajectory and if there is set the new bullet target to the collision point.
To do this I've tried to check if a side of a rectangle intersects with my bullet's trajectory line using this solution:
https://stackoverflow.com/a/1968345/10546991
I've used the program linked above but it gives me strange results, when the target is higher than the player the bullet just shoot behind.
Take a look at the following example :
Note : In SFML the Y axis points down
A is the player
B is the bullet's impact point or target, call it however you want.
C is the rectangle's Top Left side
D is the rectangle's Top Right side
(2500,2000) = a
(2500, 1300) = b
(2400, 2500) = c
(2600, 2500) = d
S1 = (0, -700)
S2 = (200, 0)
numeratorS = 700 * -100
numeratorT = 200 * -500
denominator = -200 * 700
s = 700 * - 100 / -200 * 700 = -70 000 / -140 000 = 7/14 = 1/2
t = -100 000 / -140 000 = 10 / 14 = 5 / 7
intersectionPoint.x = 2500 + (1/2 * 0) = 2500
intersectionPoint.y = 2000 + (1/2 * -700) = 1650
intersectionPoint (2500,1650)
So as you can see when testing if there is an intersection between the line between the player and the bullet's impact point and the top side of the rectangle (which is below the player) the program finds an intersection in between the player and the bullet!
I'm also providing a part of the code I use to detect collisions
Vector2f intersectionPoint = target;
//Check if bullet hits a wall and calculate new target
for (int i = 0; i < arraySize; i++)
{
float intersectionPointMagnitude = sqrt(intersectionPoint.x * intersectionPoint.x + intersectionPoint.y * intersectionPoint.y);
//Storing rect corner positions
Vector2f rectTopLeft;
rectTopLeft.x = collisions[i].left;
rectTopLeft.y = collisions[i].top;
Vector2f rectBottomLeft;
rectBottomLeft.x = collisions[i].left;
rectBottomLeft.y = collisions[i].top + collisions[i].height;
Vector2f rectBottomRight;
rectBottomRight.x = collisions[i].left + collisions[i].width;
rectBottomRight.y = collisions[i].top + collisions[i].height;
Vector2f rectTopRight;
rectTopRight.x = collisions[i].left + collisions[i].width;
rectTopRight.y = collisions[i].top;
Vector2f intersectionLeft = intersectionPoint;
if (Maths::vectorsIntersect(start, target, rectTopLeft, rectBottomLeft, intersectionLeft))
{
//We want to set a new target only if the detected collision is closer than the previous one
intersectionLeft = intersectionLeft - start;
float intersectionLeftMagnitude = sqrt(intersectionLeft.x * intersectionLeft.x + intersectionLeft.y * intersectionLeft.y);
if (intersectionLeftMagnitude < intersectionPointMagnitude)
{
intersectionPoint = intersectionLeft;
target = start + intersectionPoint;
}
}
I just can't understand where the issue is coming from so if anyone could help me out it'll be greatly appreciated.
Edit : Here is Maths::vectorsIntersect which is really similar to the function in the link above
bool vectorsIntersect(Vector2f A, Vector2f B, Vector2f C, Vector2f D, Vector2f& intersectionPoint)
{
Vector2f S1 = B - A;
Vector2f S2 = D - C;
//Calculate scalar parameters
float denominator = (S1.x * S2.y - S1.y * S2.x);
//We can't divide by 0!
if (denominator == 0.0f)
return false;
//S & T have the same denominator
float numeratorS = (S1.x * (A.y - C.y) - S1.y * (A.x - C.x));
float numeratorT = (S2.x * (A.y - C.y) - S2.y * (A.x - C.x));
float s, t;
s = numeratorS / denominator;
t = numeratorT / denominator;
//Check for intersection point
if (abs(s) > 0.0f && abs(s) < 1.0f && abs(t) > 0.0f && abs(t) < 1.0f)
{
//Return intersection point
intersectionPoint.x = A.x + (t * S1.x);
intersectionPoint.y = A.y + (t * S1.y);
return true;
}
return false;
}
EDIT 2 :
Alright I feel stupid now, in my Maths::vectorIntersect function I was checking if the absolute values of both s and t are between 0 and 1, which is why when the scalar parameters where between -0 and -1 the function returned true and led to unexpected behaviors.
Problem solved thank you for the help
hello guys i am game programmer from korea.
and just today i found a some code that use the QUADRATIC formula for calculating something. here is code
hduVector3Dd p = startPoint;
hduVector3Dd v = endPoint - startPoint;
// Solve the intersection implicitly using the quadratic formula.
double a = v[0]*v[0] + v[1]*v[1] + v[2]*v[2];
double b = 2 * (p[0]*v[0] + p[1]*v[1] + p[2]*v[2]);
double c = p[0]*p[0] + p[1]*p[1] + p[2]*p[2] - m_radius * m_radius;
double disc = b*b - 4*a*c;
// The scale factor that must be applied to v so that p + nv is
// on the sphere.
double n;
if(disc == 0.0)
{
n = (-b)/(2*a);
}
else if(disc > 0.0)
{
double posN = (-b + sqrt(disc))/(2*a);
double negN = (-b - sqrt(disc))/(2*a);
n = posN < negN ? posN : negN;
}
else
{
return false;
}
// n greater than one means that the ray defined by the two points
// intersects the sphere, but beyond the end point of the segment.
// n less than zero means that the intersection is 'behind' the
// start point.
if(n > 1.0 || n < 0.0)
{
return false;
}
this is the part of function that checking SOMETHING for sphere shape.
and i couldn't figure out why use QUADRATIC FORMULA for calculating SOMETHING.
any idea, will be preciated
i just really wanna know, understand and reuse it for my code in the future^^
It looks like it is doing a line-sphere collision check.
I'm not exactly sure where one would use this though(I could be dumb xD)
Objects can often times pass through each other? Additionally when calculating momentum, occasionally the sprites will form blobs upon collision, moving together instead of bouncing off.
The code does work for most collisions, but it often fails. Any ideas?
xV = X Velocity. yV = Y Velocity. Every frame this velocity values are added to the X and Y positions of the quad.
bool Quad::IsTouching(Quad &q)
{
float distance = 0;
float combinedRadius = (size/2) + (q.GetSize()/2);
distance = sqrt(pow(q.GetX() - GetX(), 2) + pow(q.GetY() - GetY(), 2));
if(distance < combinedRadius)
{
return true;
}
return false;
}
void Quad::Collide(Quad &q)
{
float mX, mY, mX2, mY2, mXTmp, mYTmp;
mX = mass * xV;
mY = mass * yV;
mXTmp = mX;
mYTmp = mY;
mX2 = q.GetMass() * q.GetxV();
mY2 = q.GetMass() * q.GetyV();
mX = mX2;
mY = mY2;
mX2 = mXTmp;
mY2 = mYTmp;
xV = mX/mass;
yV = mY/mass;
q.SetxV(mX2/q.GetMass());
q.SetyV(mY2/q.GetMass());
}
I had the same issue and here is a quick video I made to demonstrate the problem.
The method to solve this is to calculate the exact time of the collision, so the particles would move the remaining time of the iteration/time-step with the new velocity. To do this you would have to check whether the will be a collision before updating the position, so: sqrt((x1 - x2 + dt * (vx1 - vx2))^2 + (y1 - y2 + dt * (vy1 - vy2))^2) <= distance.
You might also be able to get away with a simpler solution, in which you move both object slightly so that they aren't colliding anymore. This would yield a creator inaccuracy but does needs less calculations:
dx = x1 - x2;
dy = y1 - y2;
d = sqrt(dx^2 + dy^2);
D = r1 + r2;
if(d < D)
{
s = (d - D) / (2 * d);
x1 = x1 + s * dx;
x2 = x2 - s * dx;
y1 = y1 + s * dy;
y2 = y2 - s * dy;
}
What type of collisions are you referring to? Elastic or inelastic? For an elastic collision, the code would fail, and you would have to create an additional property to prevent the two objects from sticking together on contact. You would also have to ensure, with a loop or if statement, that if one object is crossing the position of another object at the same time as the other object, that the two will separate with an angle proportional to the collision speed. Use the appropriate physics formulae.
As a deduced, potential, issue (there are no values to the velocities, sizes, etc. supplied so I can't say for sure), you are not accounting that the quads are exactly touching. That is, distance == combinedRadius. Therefore, when this is true the check fails then the objects continue moving on the next tick...right through each other.
Change your check to distance <= combinedRadius. In addition, you may simply be getting a tunneling effect because the objects are small enough and moving fast enough that on each tick they pass through each other. There are multiple ways to fix this some of which are: impose a maximum velocity and a minimum size; increase your frame rate for physics checks; use continuous collision checks versus discrete checks: see wikipedia article on subject
my question is regarding working out the direction of the smallest angle between two vectors in 2D. I am making a game in C++ where one of the obstacles is a heat seeking missile launcher. I have it working by calculating the vector between the target and bullet, normalising the vector and then multiplying it by its speed. However, I am now coming back to this class to make it better. Instead of instantly locking onto the player I want it to only do so only when the bullets vector is within a certain angle (the angle between the bullets vector and the vector bulletloc->target). Otherwise I want it to slowly pan towards the target by a degrees thus giving the player enough space to avoid it. I have done all this (in a vb.net project so i could simplify the problem, work it out then re write in in C++). However the bullet always rotates clockwise towards the target even if the quickest route would be counter clockwise. So the problem is working out the direction to apply the rotation in so the smallest angle is covered. Here is my code so you can try and see what I am describing:
Function Rotate(ByVal a As Double, ByVal tp As Point, ByVal cp As Point, ByVal cv As Point)
'params a = angle, tp = target point, cp = current point, cv = current vector of bullet'
Dim dir As RotDir 'direction to turn in'
Dim tv As Point 'target vector cp->tp'
Dim d As Point 'destination point (d) = cp + vector'
Dim normal As Point
Dim x1 As Double
Dim y1 As Double
Dim VeritcleResolution As Integer = 600
tp.Y = VeritcleResolution - tp.Y 'modify y parts to exist in plane with origin (0,0) in bottom left'
cp.Y = VeritcleResolution - cp.Y
cv.Y = cv.Y * -1
tv.X = tp.X - cp.X 'work out cp -> tp'
tv.Y = tp.Y - cp.Y
'calculate angle between vertor to target and vecrot currntly engaed on'
Dim tempx As Double
Dim tempy As Double
tempx = cv.X * tv.X
tempy = cv.Y * tv.Y
Dim DotProduct As Double
DotProduct = tempx + tempy 'dot product of cp-> d and cp -> tp'
Dim magCV As Double 'magnitude of current vector'
Dim magTV As Double 'magnitude of target vector'
magCV = Math.Sqrt(Math.Pow(cv.X, 2) + Math.Pow(cv.Y, 2))
magTV = Math.Sqrt(Math.Pow(tv.X, 2) + Math.Pow(tv.Y, 2))
Dim VectorAngle As Double
VectorAngle = Acos(DotProduct / (magCV * magTV))
VectorAngle = VectorAngle * 180 / PI 'angle between cp->d and cp->tp'
If VectorAngle < a Then 'if the angle is small enough translate directly towards target'
cv = New Point(tp.X - cp.X, tp.Y - cp.Y)
magCV = Math.Sqrt((cv.X ^ 2) + (cv.Y ^ 2))
If magCV = 0 Then
x1 = 0
y1 = 0
Else
x1 = cv.X / magCV
y1 = cv.Y / magCV
End If
normal = New Point(x1 * 35, y1 * 35)
normal.Y = normal.Y * -1
cv = normal
ElseIf VectorAngle > a Then 'otherwise smootly translate towards the target'
Dim x As Single
d = New Point(cp.X + cv.X, cp.Y + cv.Y)
a = (a * -1) * PI / 180 'THIS LINE CONTROL DIRECTION a = (a*-1) * PI / 180 would make the rotation counter clockwise'
'rotate the point'
d.X -= cp.X
d.Y -= cp.Y
d.X = (d.X * Cos(a)) - (d.Y * Sin(a))
d.Y = (d.X * Sin(a)) + (d.Y * Cos(a))
d.X += cp.X
d.Y += cp.Y
cv.X = d.X - cp.X
cv.Y = d.Y - cp.Y
cv.Y = cv.Y * -1
End If
Return cv
End Function
One idea I had was to work out the bearing of the two vectors and if the difference is greater than 180 degrees, rotate clockwise otherwise rotate counter clockwise, any ideas would be helpful. Thanks.
EDIT: I would like to add that this site is very helpful. I often use questions posed by others to solve my own problems and I want to take the chance to say thanks.
As you've written in your code, the angle between two (normalized) vectors is the inverse cosine of their dot product.
To get a signed angle, you can use a third vector representing the normal of the plane that the other two vectors lie on -- in your 2D case, this would be a 3D vector pointing straight "up", say (0, 0, 1).
Then, take the cross-product of the first vector (the one you want the angle to be relative to) with the second vector (note cross-product is not commutative). The sign of the angle should be the same as the sign of the dot product between the resulting vector and the plane normal.
In code (C#, sorry) -- note all vectors are assumed to be normalized:
public static double AngleTo(this Vector3 source, Vector3 dest)
{
if (source == dest) {
return 0;
}
double dot; Vector3.Dot(ref source, ref dest, out dot);
return Math.Acos(dot);
}
public static double SignedAngleTo(this Vector3 source, Vector3 dest, Vector3 planeNormal)
{
var angle = source.AngleTo(dest);
Vector3 cross; Vector3.Cross(ref source, ref dest, out cross);
double dot; Vector3.Dot(ref cross, ref planeNormal, out dot);
return dot < 0 ? -angle : angle;
}
This works by taking advantage of the fact that the cross product between two vectors yields a third vector which is perpendicular (normal) to the plane defined by the first two (so it's inherently a 3D operation). a x b = -(b x a), so the vector will always be perpendicular to the plane, but on a different side depending on the (signed) angle between a and b (there's something called the right-hand rule).
So the cross product gives us a signed vector perpendicular to the plane which changes direction when the angle between the vectors passes 180°. If we know in advance a vector perpendicular to the plane which is pointing straight up, then we can tell whether the cross product is in the same direction as that plane normal or not by checking the sign of their dot product.
Based on #Cameron's answer, here is the python translation i've used:
As bonus, i've added the signed_angle_between_headings function to directly return the 'quickest' turn angle between two north-referenced headings.
import math
import numpy as np
def angle_between_vectors(source, dest):
if np.array_equal(source, dest):
return 0
dot = np.dot(source, dest)
return np.arccos(dot)
def signed_angle_from_to_vectors(source, dest, plane_normal):
angle = angle_between_vectors(source, dest)
cross = np.cross(source, dest)
dot = np.dot(cross, plane_normal)
return -angle if dot < 0 else angle
def signed_angle_between_headings(source_heading, destination_heading):
if source_heading == destination_heading:
return 0
RAD2DEGFACTOR = 180 / math.pi
source_heading_rad = source_heading / RAD2DEGFACTOR
dest_heading_rad = destination_heading / RAD2DEGFACTOR
source_vector = np.array([np.cos(source_heading_rad), np.sin(source_heading_rad), 0])
dest_vector = np.array([np.cos(dest_heading_rad), np.sin(dest_heading_rad), 0])
signed_angle_rad = signed_angle_from_to_vectors(source_vector, dest_vector, np.array([0,0,1]))
return signed_angle_rad * RAD2DEGFACTOR
I have a 2D bitmap-like array of let's say 500*500 values. I'm trying to create a linear gradient on the array, so the resulting bitmap would look something like this (in grayscale):
(source: showandtell-graphics.com)
The input would be the array to fill, two points (like the starting and ending point for the Gradient tool in Photoshop/GIMP) and the range of values which would be used.
My current best result is this:
alt text http://img222.imageshack.us/img222/1733/gradientfe3.png
...which is nowhere near what I would like to achieve. It looks more like a radial gradient.
What is the simplest way to create such a gradient? I'm going to implement it in C++, but I would like some general algorithm.
This is really a math question, so it might be debatable whether it really "belongs" on Stack Overflow, but anyway: you need to project the coordinates of each point in the image onto the axis of your gradient and use that coordinate to determine the color.
Mathematically, what I mean is:
Say your starting point is (x1, y1) and your ending point is (x2, y2)
Compute A = (x2 - x1) and B = (y2 - y1)
Calculate C1 = A * x1 + B * y1 for the starting point and C2 = A * x2 + B * y2 for the ending point (C2 should be larger than C1)
For each point in the image, calculate C = A * x + B * y
If C <= C1, use the starting color; if C >= C2, use the ending color; otherwise, use a weighted average:
(start_color * (C2 - C) + end_color * (C - C1))/(C2 - C1)
I did some quick tests to check that this basically worked.
In your example image, it looks like you have a radial gradient. Here's my impromtu math explanation for the steps you'll need. Sorry for the math, the other answers are better in terms of implementation.
Define a linear function (like y = x + 1) with the domain (i.e. x) being from the colour you want to start with to the colour your want to end with. You can think of this in terms of a range the within Ox0 to OxFFFFFF (for 24 bit colour). If you want to handle things like brightness, you'll have to do some tricks with the range (i.e. the y value).
Next you need to map a vector across the matrix you have, as this defines the direction that the colours will change in. Also, the colour values defined by your linear function will be assigned at each point along the vector. The start and end point of the vector also define the min and max of the domain in 1. You can think of the vector as one line of your gradient.
For each cell in the matrix, colours can be assigned a value from the vector where a perpendicular line from the cell intersects the vector. See the diagram below where c is the position of the cell and . is the the point of intersection. If you pretend that the colour at . is Red, then that's what you'll assign to the cell.
|
c
|
|
Vect:____.______________
|
|
I'll just post my solution.
int ColourAt( int x, int y )
{
float imageX = (float)x / (float)BUFFER_WIDTH;
float imageY = (float)y / (float)BUFFER_WIDTH;
float xS = xStart / (float)BUFFER_WIDTH;
float yS = yStart / (float)BUFFER_WIDTH;
float xE = xEnd / (float)BUFFER_WIDTH;
float yE = yEnd / (float)BUFFER_WIDTH;
float xD = xE - xS;
float yD = yE - yS;
float mod = 1.0f / ( xD * xD + yD * yD );
float gradPos = ( ( imageX - xS ) * xD + ( imageY - yS ) * yD ) * mod;
float mag = gradPos > 0 ? gradPos < 1.0f ? gradPos : 1.0f : 0.0f;
int colour = (int)( 255 * mag );
colour |= ( colour << 16 ) + ( colour << 8 );
return colour;
}
For speed ups, cache the derived "direction" values (hint: premultiply by the mag).
There are two parts to this problem.
Given two colors A and B and some percentage p, determine what color lies p 'percent of the way' from A to B.
Given a point on a plane, find the orthogonal projection of that point onto a given line.
The given line in part 2 is your gradient line. Given any point P, project it onto the gradient line. Let's say its projection is R. Then figure out how far R is from the starting point of your gradient segment, as a percentage of the length of the gradient segment. Use this percentage in your function from part 1 above. That's the color P should be.
Note that, contrary to what other people have said, you can't just view your colors as regular numbers in your function from part 1. That will almost certainly not do what you want. What you do depends on the color space you are using. If you want an RGB gradient, then you have to look at the red, green, and blue color components separately.
For example, if you want a color "halfway between" pure red and blue, then in hex notation you are dealing with
ff 00 00
and
00 00 ff
Probably the color you want is something like
80 00 80
which is a nice purple color. You have to average out each color component separately. If you try to just average the hex numbers 0xff0000 and 0x0000ff directly, you get 0x7F807F, which is a medium gray. I'm guessing this explains at least part of the problem with your picture above.
Alternatively if you are in the HSV color space, you may want to adjust the hue component only, and leave the others as they are.
void Image::fillGradient(const SColor& colorA, const SColor& colorB,
const Point2i& from, const Point2i& to)
{
Point2f dir = to - from;
if(to == from)
dir.x = width - 1; // horizontal gradient
dir *= 1.0f / dir.lengthQ2(); // 1.0 / (dir.x * dir.x + dir.y * dir.y)
float default_kx = float(-from.x) * dir.x;
float kx = default_kx;
float ky = float(-from.y) * dir.y;
uint8_t* cur_pixel = base; // array of rgba pixels
for(int32_t h = 0; h < height; h++)
{
for(int32_t w = 0; w < width; w++)
{
float k = std::clamp(kx + ky, 0.0f, 1.0f);
*(cur_pixel++) = colorA.r * (1.0 - k) + colorB.r * k;
*(cur_pixel++) = colorA.g * (1.0 - k) + colorB.g * k;
*(cur_pixel++) = colorA.b * (1.0 - k) + colorB.b * k;
*(cur_pixel++) = colorA.a * (1.0 - k) + colorB.a * k;
kx += dir.x;
}
kx = default_kx;
ky += dir.y;
}
}