I'm developing a 2D game in C using Allegro 5, where an enemy from a fixed position shoots a projectile at the player's current position. I know I will have to calculate the tangent of an imaginary triangle based on the player's position and the enemy's. However, how can I have the projectile follow a straight line based on that value?
This is a situation where can be easier to work with vectors than angles.
Some simple math computes the vector between the enemy and the player:
# Compute the x and y displacement from the enemy to the player
dx = player_x - enemy_x
dy = player_y - enemy_y
# normalize the displacement to get the direction vector
distance = sqrt(dx * dx + dy * dy)
projectile.dir_x = dx / distance
```
The projectile just needs to follow that vector during the update loop:
projectile.x += projectile.dir_x * projectile.speed * time_elapsed
projectile.y += projectile.dir_y * projectile.speed * time_elapsed
Related
Given theta angles in radians, width and height of the rotated image, how do I calculate the new width and height of the outer rectangle that contains the rotated image?
In other words how do I calculate the new bonding box width/height?
Note that the image could actually be circle and have transparent pixels on the edges.
That would be: x1, y1.
I am actually rotating a pixbuf with the origin at center using cairo_rotate() and I need to know the newly allocated area. What I tried is this:
double geo_rotated_rectangle_get_width (double a, double b, double theta)
{
return abs(a*cos(theta)) + abs(b*sin(theta));
}
And it will work in the sense of always returning sufficient space to contain the rotated image, but it also always returns higher values than it should, when image is not rotated in a multiple of 90o and is a fully opaque image (a square).
EDIT:
This is the image I am rotating:
Interestingly enough, I just tried with a fully opaque image with the same size and it was OK. I use gdk_pixbuf_get_width() to get width and it returns the same value for both regardless. So I assume the formula is correct and the problem is that the transparency is not accounted for. When rotated with a diagonal orientation there are edges from the rectangle of the rotated image that are transparent.
I'll leave the above so that it is helpful to others :)
Now the question becomes how to account for transparent pixels on the edges
To determine the bbox of the rotated rectangle, you can compute the coordinates of the 4 vertices and take the bbox of these 4 points.
a is the width of the unrotated rectangle and b its height ;
let diag = sqrt(a * a + b * b) / 2 the distance from the center to the top right corner of this rectangle. You can use diag = hypot(a, b) / 2 for better precision ;
first compute the angle theta0 of the first diagonal for theta=0: theta0 = atan(b / a) or better theta0 = atan2(b, a) ;
the 4 vertices are:
{ diag * cos(theta0 + theta), diag * sin(theta0 + theta) }
{ diag * cos(pi - theta0 + theta), diag * sin(pi - theta0 + theta) }
{ diag * cos(pi + theta0 + theta), diag * sin(pi + theta0 + theta) }
{ diag * cos(-theta0 + theta), diag * sin(-theta0 + theta) }
which can be simplified as:
{ diag * cos(theta + theta0), diag * sin(theta + theta0) }
{ -diag * cos(theta - theta0), -diag * sin(theta - theta0) }
{ -diag * cos(theta + theta0), -diag * sin(theta + theta0) }
{ diag * cos(theta - theta0), diag * sin(theta - theta0) }
which gives x1 and y1:
x1 = diag * fmax(fabs(cos(theta + theta0)), fabs(cos(theta - theta0))
y1 = diag * fmax(fabs(sin(theta + theta0)), fabs(sin(theta - theta0))
and the width and height of the rotated rectangle follow:
width = 2 * diag * fmax(fabs(cos(theta + theta0)), fabs(cos(theta - theta0))
height = 2 * diag * fmax(fabs(sin(theta + theta0)), fabs(sin(theta - theta0))
This is the geometric solution, but you must take into account the rounding performed by the graphics primitive, so it is much preferable to use the graphics API and retrieve the pixbuf dimensions with gdk_pixbuf_get_width() and gdk_pixbuf_get_height(), which will allow for precise placement.
I'd say "let cairo compute those coordinates". If you have access to a cairo_t*, you can do something like the following (untested!):
double x1, y1, x2, y2;
cairo_save(cr);
cairo_rotate(cr, theta); // You can also do cairo_translate() and whatever your heart desires
cairo_new_path(cr);
cairo_rectangle(cr, x, y, width, height);
cairo_restore(cr); // Note: This preserved the path!
cairo_fill_extents(cr, &x1, &y1, &x2, &y2);
cairo_new_path(cr); // Clean up after ourselves
printf("Rectangle is inside of (%g,%g) to (%g,%g) (size %g,%g)\n",
x1, y1, x2, y2, x2 - x1, y2 - y1);
The above code applies some transformation, then constructs a path. This makes cairo apply the transformation to the given coordinates. Afterwards, the transformation is "thrown away" with cairo_restore(). Next, we ask cairo for the area covered by the current path, which it provides in the current coordinate system, i.e. without the transformation.
can someone slap me an idea or math formula on how to make my enemies move in a sine wave
tried something like this but they just move at the same time so they just create a straight line of enemies moving left and right.
for(int i = 0; i < 5; i++){
float y = sinf( 100+delta_time*0.06f) * 75;
float x = game->enemy[i].base_x + y;
game->enemy[i].x = x ;
game->enemy[i].y += 1;
SDL_Rect rect = { game->enemy[i].x , game->enemy[i].y ,game->enemy[i].w, game->enemy[i].h};
SDL_RenderCopy(game->renderer , game->enemy[i].sprite , NULL , &rect);
}
Let v=(v_x,v_y) be the overall direction of the enemy. Let o be the vector o=(-v_y/||v||,v_x/||v||) where ||v||=sqrt(v_x.v_x+v_y.v_y) is the norm of v. The vector o is perpendicular to v. A sinusoidal motion is wanted in that direction. Consequently, the position p(t)=(x(t),y(t)) is defined as :
x(t)=v_x.t-A.v_y/||v||.sin(w.t)
y(t)=v_y.t+A.v_x/||v||.sin(w.t)
where A is the magnitude of the ossilations and w the pulsation of the ossilations. The corresponding frequency is f=w/(2pi).Then, the wavelength lambda=||v||/f corresponds to the length of ossilations.
If the enemy is moving in the x direction (v_y=0) then :
x(t)=v_x.t
y(t)=A.sin(w.t)
The length of ossilations is lambda=2pi.v_x/w.
I have a function that takes 3 points and I will use these points to draw a triangle, as if I were using the glVertex function.
But since I want to texture map this triangle while avoiding perspective distortion, I have to subdivide it, and use the vertices for texture mapping and calculation of normals.
I managed to do this for rectangles, spheres, cylinders and torus, but I can't, for the life of me, figure out how to do a triangle.
Every example of triangle mapping I've managed to find is only for 2D space and with predefined points, using glVertex.
As for rectangles, the code I'm using is this one:
void Rectangle::draw(float texS, float texT)
{
float x1, x2, y1, y2;
x1 = v.at(0); x2 = v.at(2);
y1 = v.at(1); y2 = v.at(3);
//glRectf(x1,y1,x2,y2);
int _numDivisions = 100;
float _xDim = abs(x2 - x1);
float _yDim = abs(y2 - y1);
float texMultiS, texMultiT;
texMultiS = _xDim / texS;// / _xDim;
texMultiT = _yDim / texT;// / _yDim;
glPushMatrix();
glTranslatef(x1, y1, 0);
glRotatef(-90.0,1,0,0);
glScalef( _xDim * (1.0/(double) _numDivisions), 1 , _yDim * (1.0/(double) _numDivisions));
glNormal3f(0,-1,0);
for (int bx = 0; bx<_numDivisions; bx++)
{
glBegin(GL_TRIANGLE_STRIP);
glTexCoord2f((bx * 1.0/_numDivisions) * texMultiS, 0.0 * texMultiT);
glVertex3f(bx+x1, 0, 0+y1);
for (int bz = 0; bz<_numDivisions; bz++)
{
glTexCoord2f(((bx+1) * 1.0/_numDivisions) * texMultiS, (bz * 1.0/_numDivisions) * texMultiT);
glVertex3f((bx + 1)+x1, 0, bz+y1);
glTexCoord2f(((bx+1) * 1.0/_numDivisions) * texMultiS, ((bz+1) * 1.0/_numDivisions) * texMultiT);
glVertex3f(bx+x1, 0, (bz + 1)+y1);
}
glTexCoord2f(((bx+1) * 1.0/_numDivisions) * texMultiS, 1.0 * texMultiT);
glVertex3d((bx+1)+x1, 0, _numDivisions+y1);
glEnd();
}
glPopMatrix();
}
And this I get. It's simple enough, since it's in 2D space. I was aiming for the same kind of logic, but for a 3D space triangle.
But I can't figure out the calculations needed for the points in a triangle in 3D space.
FOR EXAMPLE:
P1->(0,0,1); P2->(1,0,0); P3->(0,1,0);
My best idea so far is to draw it in 2D space with P1 as origin, I can just make every point in the P1->P2 line converge towards P3, and then calculate the angle to rotate according to the x-axis and then the angle for a rotation in the y-axis, but is really this the best way to go at it?
EDIT:
As sugested below, a way to rephrase the question might be:
"How do I subdivide a general triangle in three dimensions"?
Since the objective is to get an algorithm that builds a triangle in 3D space in sections (triangle strip or quad strip) so I can use the vertices for texture mapping and normal calculation.
As suggested in the comments, you could slice up a triangle very similar to a quad. With some fancy ASCII art:
/|
/ |
----
/| /|
/ |/ |
-------
/| /| /|
/ |/ |/ |
----------
/| /| /| /|
/ |/ |/ |/ |
-------------
Decomposing this into triangle strips that run bottom to top, we would have 4 strips. The triangle counts of the strips for 4 strips are:
1, 3, 5, 7
or 2 * i + 1 for strip i, with i from 0 to n - 1.
To calculate the vertices needed for the subdivision, you can calculate the points along the bottom edge by linear interpolation between the corresponding two vertices. Same thing for the points along the edge that goes diagonal in the diagram. Then along each strip, you again do a linear interpolation between the corresponding two points, splitting the left side of strip i into i pieces, and the right side into i + 1 pieces.
Given the following system:
Where:
A: Point that exists anywhere on the edge of a circle with radius r on the xz plane.
θ: The angle between the positive-x-axis and a vector from the origin to point A. This should range from -PI/2 to PI/2.
B1: A point at the intersection of the circle and the positive x-axis at a height of h1.
B2: A point at the intersection of the circle and the positive z-axis at a height of h2.
d1: Distance between B1 and A.
d2: Distance between B2 and A.
Assuming:
h1, h2, and r are known constants.
d1 and d2 are known variables.
How do I find θ?
This will eventually be implemented in C in an embedded system where I have reasonably fast functions for arctan2, sine, and cosine. As such, performance is definitely a priority, and estimations can be used if they are correct to about 3 decimal places (which is how accurate my trig functions are).
However, even given a mathematical algorithm, I'm sure I could work out the specific implementation.
For what it's worth, I got about as far as:
(d1^2 - h1^2) / r = (sin(θ))^2 + (cos(θ))^2
(d2^2 - h2^2) / r = (sin(PI/4 - θ))^2 + (cos(PI/4 - θ))^2
Before I realized that, mathematically, this is way out of my league.
This isn't a full answer but a start of one.
There are two easy simplifications you can make.
Let H1 and H2 be the points in your plane below B1 and B2.
Since you know h1 and d1, h2 and d2, you can calculate the 2 distances A-H1 and A-H2 (with Pythagoras).
Now you have reduced the puzzle to a plane.
Furthermore, you don't really need to look at both H1 and H2. Given the distance A-H1, there are only 2 possible locations for A, which are mirrored in the x-axis. Then you can find which of the two it is by seeing if the A-H2 distance is above or below the threshold distance H2-H1.
That seems to be a good beginning :-)
Employing #Rhialto, additional simplifications and tests for corners cases:
// c1 is the signed chord distance A to (B1 projected to the xz plane)
// c1*c1 + h1*h1 = d1*d1
// c1 = +/- sqrt(d1*d1 - h1*h1) (choose sign later)
// c1 = Cord(r, theta) = fabs(r*2*sin(theta/2))
// theta = asin(c1/(r*2))*2
//
// theta is < 0 when d2 > sqrt(h2*h2 + sqrt(2)*r*sqrt(2)*r)
// theta is < 0 when d2 > sqrt(h2*h2 + 2*r*r)
// theta is < 0 when d2*d2 > h2*h2 + 2*r*r
#define h1 (0.1)
#define h2 (0.25)
#define r (1.333)
#define h1Squared (h1*h1)
#define rSquared (r*r)
#define rSquaredTimes2 (r*r*2)
#define rTimes2 (r*2)
#define d2Big (h2*h2 + 2*r*r)
// Various steps to avoid issues with d1 < 0, d2 < 0, d1 ~= h1 and theta near pi
double zashu(double d1, double d2) {
double c1Squared = d1*d1 - h1Squared;
if (c1Squared < 0.0)
c1Squared = 0.0; // _May_ be needed when in select times |d1| ~= |h1|
double a = sqrt(c1Squared) / rTimes2;
double theta = (a <= 1.0) ? asin(a)*2.0 : asin(1.0)*2.0; // Possible a is _just_ greater than 1.0
if (d2*d2 > d2Big) // this could be done with fabs(d2) > pre_computed_sqrt(d2Big)
theta = -theta;
return theta;
}
I code a diagram editor and I draw some curved links that perfectly works from quadratic bezier segment (see picture) :
I search the best way (and if it's possible) to draw "spike" curved link. Approximatively like this (in blue) :
I have no idea where to start, I read few articles on drawingbrush or "how to draw a curved text" but it doesn't seems to be what I need...
Thanks for your help or advises ! :)
Just to complete few remarks, the bezier is made with quadrametricBezier class from 3 points. Thanks to all of you !
I don't think there's a built-in way in WPF to do that. You'll have to calculate the coordinates yourself and draw the lines yourself (e.g. using DrawingVisual).
To calculate the coordinates, you would have to:
Step 1 Sample points along the bezier curve.
A bezier curve with 4 control points has the formula:
curve(t) = t^3 p1 + 3 t^2 (1-t) p2 + 3 t (1-t)^2 p3 + (1-t)^3 p4
d/dt curve(t) = 3 p3 - 3 p4 + 6 p2 t - 12 p3 t + 6 p4 t + 3 p1 t^2 - 9 p2 t^2 + 9 p3 t^2 - 3 p4 t^2
With these formulas, you can calculate points on the curve, and their tangent directions. Rotating the tangent direction by 90° (i.e. swap X/Y and change the sign of Y) gives the normal direction.
However, these points are not equidistant:
So if you used these points directly, you'd get a curve where some of the "spikes" are shorter than others:
Step 2: Get equidistant points along the curve
You now have a list of points along the curve. You can calculate the euclidean distance between each point and the next. Summing all those distances up gives the total length of the curve.
Let's say you want spikes that are (roughly) 10 pixels wide. Then you need n=round(TotalLength / 10) points. The points are at s(i) = TotalLength / n * i.
So if you want to e.g. find the value of t for the 3rd equidistant point, you'd calculate s(3) = TotalLength / n * 3. Then you'd iterate over the set of sampled points, summing up the distances as you go, until you reach a point that has a total distance along the curve > s(3). Now you know the points immediately before and after the point you're looking for, and you can use the rule of three to calculate the t in between.
Now you have a set of points that are the same distance apart along the curve:
Step 3: Drawing the spikes
This is the easiest part: At each of your equidistant points, calculate the normal (using the derivative formula above). Divide that normal by its length to get the unit normal. Then add to each even point +d * UnitNormal and to each odd point -d * UnitNormal, where d is the "depth" of the spike, i.e. the distance of the tip to the curve.
Assuming that you already have computed Bezier curve, desired curve is a sum of triangle wave multiplied to normal vector to the Bezier curve with the Bezier curve. The only thing you should take into account that the Bezier curve is a parametric curve with parameter t in [0, 1]. Then you need Bezier curve length function L(t) and to plug that into triangle wave equation instead of t.
Triangle wave also can be expressed through modulo operation
TW(t) = M * abs(mod(q * L(t), n * 2 - 2) - n + 1) + 1
where
M - a magnitude of the wave,
q - a scaling factor along path,
n - period of a curve,
t - parameter of a Bezier curve,
L(t) - length function of Bezier curve.
Resulting curve:
C(t) = TW(t) * B_normal(t) + B(t)
where B_normal(t) is a normal vector to a Bezier curve at point t.
For those that can be interested by the WPF solution, I finally code this (not very optimized) based on the QuadraticBezierSegment and PathGeometry classes.
Thanks very much to all of you. :)
public partial class MainWindow : Window
{
int orientation = 1;
int compt = 0;
int SpikeWidth = 5;
int SpikeHeigth = 3;
public MainWindow()
{
InitializeComponent();
Polyline wave = new Polyline();
wave.Stroke = Brushes.Blue;
wave.StrokeThickness = 2;
PathGeometry pg = BezierPath.Data.GetFlattenedPathGeometry();
double CurveLenght = GetLength(pg, PathFigure.StartPoint);
double NbrPoint = (Math.Round(CurveLenght / SpikeWidth));
for (int i = 0; i <= NbrPoint; i++)
{
//Calcul de T
double t = SpikeWidth * i / CurveLenght;
Point TangentPoint;
Point PointToDraw;
pg.GetPointAtFractionLength(t, out PointToDraw, out TangentPoint);
// Calcul de l'angle
double a = Math.Atan2(TangentPoint.Y, TangentPoint.X);
a += Math.PI / 2;
//Alterner un point sur deux de chaque coté de la courbe
if (compt % 2 == 0)
orientation = 1;
else
orientation = -1;
//Calcul du point et ajout à la polyligne.
//Point calculation and added to the polyline.
wave.Points.Add(new Point(Math.Cos(a) * SpikeHeigth * orientation + PointToDraw.X, Math.Sin(a) * SpikeHeigth * orientation + PointToDraw.Y));
//Compte le nombre de passage pour l'orientation
compt += 1;
}
//Traçage sur la canvas
cv.Children.Add(wave);
}
private double GetLength(PathGeometry pg, Point startPoint)
{
PolyLineSegment pls = pg.Figures[0].Segments[0] as PolyLineSegment;
double distance = 0;
foreach (Point pt in pls.Points)
{
distance += Math.Sqrt((startPoint.X - pt.X).Pow(2) + (startPoint.Y - pt.Y).Pow(2));
startPoint = pt;
}
return distance;
}
}
<Canvas x:Name="cv">
<Path Stroke="Black" x:Name="BezierPath">
<Path.Data>
<PathGeometry>
<PathGeometry.Figures>
<PathFigureCollection>
<PathFigure x:Name="PathFigure" StartPoint="10,400">
<PathFigure.Segments>
<PathSegmentCollection>
<QuadraticBezierSegment x:Name="BezierSegment" Point1="50,80" Point2="400,400">
</QuadraticBezierSegment>
</PathSegmentCollection>
</PathFigure.Segments>
</PathFigure>
</PathFigureCollection>
</PathGeometry.Figures>
</PathGeometry>
</Path.Data>
</Path>
</Canvas>