You should use floating point precision and radian.
It might have been wise 10 years ago to use integer instead of float for CPU saving but you shouldn't bother now. Floating point calculation are really fast now and so much more natural to use.
JC
Some useful angle functions:
GetAngle: Returns the angle between point x,y - x2,y2 (360º = 360000)
GetDist: Returns euclidian distance between 2 points
GetDistX: X increment for given angle in given distance
GetDistY: Same for GetDistX, but for Y.
The only issue i'm having is that GetAngle gives a slightly incorrect angle, anyone trigonometric-wise can give me some advice about this?
EDIT: Removed some useless casting o_O i dont know what the heck was i thinking...Code:int GetAngle(int x, int y, int x2, int y2) { int distx = x2 - x; int disty = y2 - y; int ang; if ( distx == 0 ) { if (disty < 0) ang = 270000; else ang = 90000; return ang; } else { ang = ( int )( atan( disty / distx ) * 180000 / M_PI); if (distx > 0) ang = -ang + 180000; else ang = -ang; return ang; } } int GetDist(int x, int y, int x2, int y2) { int distx = x2 - x; int disty = y2 - y; return ( int )sqrt( ( distx * distx ) + ( disty * disty ) ); } float GetDistX(int angle, float distance) { return (float)(cos(angle * M_PI / 180000) * distance); } float GetDistY(int angle, float distance) { return (float)(-sin(angle * M_PI / 180000) * distance); }
EDIT2: Added this function
NearAngle: Does a smooth transition between 2 angles, returning the next logical angle in the transition, given the proper "smoothness" (i.e.: 10 is a good number)
Code:int NearAngle(int angle, int dest, int smoothness) { if (smoothness < 1) smoothness = 1; int temp; if (angle < dest && dest-angle > 180000) { angle += 360000; } if (angle > dest && angle-dest > 180000) { angle -= 360000; } if (angle < dest) { temp = dest-angle; angle += temp / smoothness; if (angle > dest) angle = dest; } else { temp = angle-dest; angle -= temp / smoothness; if (angle < dest) angle = dest; } if (angle < 0) return angle + 360000 ; if (angle >= 360000) return angle - 360000 ; return angle ; }
Hope someone finds this useful...
If you find a bug don't hesitate to tell me!
Thanks!
Last edited by Therion; 04-18-2007 at 08:34 AM.
You should use floating point precision and radian.
It might have been wise 10 years ago to use integer instead of float for CPU saving but you shouldn't bother now. Floating point calculation are really fast now and so much more natural to use.
JC
Thanks for the advice!... I tend to make these useless optimizations often, i think i should start thinking the fact that my code will run in something faster than a 486.
Another useful function is a function to return the distance between two angles.
For instance, what's the distance between 5 degrees and 355 degrees? 10 degrees. The way I figured out (which seems to work, although it's probably not the simplest way) is:
angle_difference = abs((angle1 mod 360) - (angle2 mod 360));
if (angle_difference > 180)
then angle_difference = 360 - angle_difference;
return (angle_difference);
Paul Eres - Radical Poesis Games & Creations | http://immortaldefense.com
it's also kinda dangerous to be dividing ints, passing ints into functions that take floats/doubles, etc... at any moment the compiler could round your values to integers and you'd lose precision (which would cause errors)
for example: atan( disty / distx ) is going to have problems if disty and distx are integers and the compiler passes an int into atan() -- imagine what happens when distx and disty are small
Depends on what you're doing actually. If you do lots of muls and divs, using floating point is faster, but if you do subs and adds using fixed point arithmetic is not only faster but more precise. You might have more precise results using doubles or long doubles instead of floats -and internally the CPU converts all floating point numbers to 80bit anyway- but moving doubles and long doubles around in memory is slower than moving floats and from experiments i've found that although computationally they might be the same in cycles, due to the memory bottleneck they're slower.
The ideal method is to break your operations in batches of integers and floats where needed, and convert between these between the batches.
Of course you have to worry about all these only if you experience slowdowns in your game because of the CPU usage and only if you cannot manage to figure out a better algorithm :-).
God yeah, the trouble I remember having trying to figure this one out with no conditionals! Gave up in the end and have something very similar looking to yours now. It's one of those bits of code I make sure to never lose!Another useful function is a function to return the distance between two angles.
Right, but a float is the same size as an int... it's fairly rare to have to use doubles unless there is an explicit need for super-precision (i.e. physics engines)
In general, I couldn't imagine that there's any reason to go through the hassle of fixed point these days unless you are SERIOUSLY cpu-bound. The algorithm itself should always be the first place to look for speedup. For these sorts of simple 2D math functions, unless you're calling them 100000000 times a second, there's not much point in making your life difficult...
Also, you're probably losing more time doing float to int casts than the actual math takes... (with code like: x = (int) atan2(some_int / some_int2)).
Does anyone know of code that can approximate tangents? I use a simplified language that doesn't have any trig functions. It doesn't need to be very precise, it's just used for situations like "is ego standing close to X." Right now I just use a long series of "if" statements but there must surely be a better way?
I think it's the best approach to make or get a 2d Vector library and put all this things in-there (distances, angles, inclusions, addition, scalar, rotate...) and move it from the core up. Working with vectors to move, accelerate, position..... stuff is so much neat than allways changing x and y to me. I had such library in c++ that I got on net and then added my stuff. I can post it if anyone wishes...
Yeah, that's why i said that the ideal case (if you really need this sort of optimization) is to split the operations in int and float batches.
But really, with the emerge of multi-core CPUs, it's much better to find parallelizing algorithms and work with threads than try to get yet another cycle. At the future, the CPU speed won't be measured in how many operations per sec a single thread can do, but how many cores it will have any how many operations can per second can be performed in all these cores. So the ideal code in such systems will be the code that takes advantage of the multicore environment.
@tolworthy:
Isn't something like abs((ego.x - other.x)*(ego.x - other.x) + (ego.y - other.y)*(ego.y*other.y)) < min_distance*min_distance enough?
That's more or less what I'd use, though I'd modify it to (ego.x-other.x)*(ego.x-other.x) + (ego.y-other.y)*(ego.y-other.y) < min_distance*min_distance. (You had a multiplication instead of a subtraction, and the absolute value isn't necessary since the LHS will always be positive anyway, assuming there are no complex numbers involved.)
Our engine is all fixed point basedvery useful if your not just targeting desktop computers
![]()
That's good for distances, but something I forgot to mention (sorry!) is that this is a 2D game with a different sprite for each direction that ego might face. So the angle is important. Using real trig (or at least a fake version) is not essential for my game, but would just be more elegant and would avoid those irritating "face the wrong way" or "spin around" bugs.![]()
I'm not sure exactly what you mean, but if you mean that you want to test "is ego facing the object?" as well as "is ego close to the object?", then look into the dot product. You need to obtain two vectors: One that points from ego to the object (I'll call it A) and one that has the direction that the ego is facing (I'll call it B). The length of these vectors is not important.
Then compute: A_x*B_x + A_y*B_y. If the result is zero, then the vectors are perpendicular (so ego is facing exactly 90 degrees to the left or the right of the object). If the result is positive, then ego is facing more-or-less towards the object (within a 180-degree arc). If it's negative, then ego is facing away from the object.
Note that the X component of A is just (object.x-ego.x), and similarly the Y component of A is (object.y-ego.y). So the above is equivalent to (object.x-ego.x)*B_x + (object.y-ego.y)*B_y. How you calculate the vector B will depend on how you're encoding the sprite's direction.
This happens because the dot product of two vectors is closely related to the cosine between those two vectors. The MathWorld page I linked to above has further explanation (if you don't mind that it's a bit technical).
Thanks, that's interesting. I'll have to experiment. Ego can face in eight possible directions, and usually being one direction off is not a huge problem. But occasionally, in close-up shots, facing 45 degrees to the wrong direction looks completely wrong. I'll have a look at dot product stuff. Thanks again.
My schtuphh: http://iki.fi/sol/
this is the library I said... it has dot product amongst things.. it's very simple but gives a lot of power.
Code:#ifndef NVECTOR_NOMIND_TECH_001_INCLUDED_ #define NVECTOR_NOMIND_TECH_001_INCLUDED_ /* The author of this Vector class in Olivier renault who made exceptionaly great polycolly tutorial/demo. I (janko metelko) used this lib and changed it to what I needed / renamed it and stuff... but the 99.9% of thanks is to **Olivier Renault**. This NVector is made to work with PTK (phelios) */ #include <stdio.h> #include <stdlib.h> #include <math.h> #include "NMath.h" #include "KInput.h" #define PI 3.141592f // the venerable pi #define PITIMES2 6.283185f // 2 * pi #define PIOVER2 1.570796f // pi / 2 #define E 2.718282f // the venerable e #define SQRT2 1.414214f // sqrt(2) #define SQRT3 1.732051f // sqrt(3) #define GOLDEN 1.618034f // the golden ratio #define DEGTORAD 0.017453f // convert degrees to radians #define RADTODEG 57.29578f // convert radians to degrees typedef unsigned char u_char; typedef unsigned short u_short; typedef unsigned int u_int; typedef unsigned long u_long; //#define for if(0); else for #define for if(0){} else for class NMatrix; class NVector { public: float x,y; static const NVector& Blank() { static const NVector V(0, 0); return V; } public: inline NVector(void) {} inline NVector(float Ix,float Iy) : x(Ix) , y(Iy) {} inline NVector &operator /=(const float Scalar) { x /= Scalar; y /= Scalar; return *this; } inline NVector &operator *=(const float Scalar) { x *= Scalar; y *= Scalar; return *this; } inline NVector &operator +=(const NVector &Other) { x += Other.x; y += Other.y; return *this; } inline NVector &operator -=(const NVector &Other) { x -= Other.x; y -= Other.y; return *this; } inline float operator ^ (const NVector &V) const { return (x * V.y) - (y * V.x); } // cross product inline float operator * (const NVector &V) const { return (x*V.x) + (y*V.y); } // dot product inline NVector operator * (float s) const { return NVector(x*s, y*s); } inline NVector operator / (float s) const { return NVector(x/s, y/s); } inline NVector operator + (const NVector &V) const { return NVector(x+V.x, y+V.y); } inline NVector operator - (const NVector &V) const { return NVector(x-V.x, y-V.y); } friend NVector operator * (float k, const NVector& V) { return NVector(V.x*k, V.y*k); } NVector operator * (const NMatrix& M) const; NVector operator ^ (const NMatrix& M) const; NVector& operator *=(const NMatrix& M); NVector& operator ^=(const NMatrix& M); inline NVector operator -(void) const { return NVector(-x, -y); } inline float Length(void) const { return (float) sqrt(x*x + y*y); } float Normalise(void) { float fLength = Length(); if (fLength == 0.0f) return 0.0f; (*this) *= (1.0f / fLength); return fLength; } NVector Direction(void) const { NVector Temp(*this); Temp.Normalise(); return Temp; } float Angle(const NVector& xE) { float dot = (*this) * xE; float cross = (*this) ^ xE; // angle between segments float angle = (float) atan2(cross, dot); return angle; } NVector& Rotate(float angle) { float tx = x; x = (float)x * (float)cos(angle) - y * (float)sin(angle); y = (float)tx * (float)sin(angle) + y * (float)cos(angle); return *this; } NVector& Rotate(const NVector& xCentre, float fAngle) { NVector D = *this - xCentre; D.Rotate(fAngle); *this = xCentre + D; return *this; } void Clamp(const NVector& min, const NVector& max) { x = (x < min.x)? min.x : (x > max.x)? max.x : x; y = (y < min.y)? min.y : (y > max.y)? max.y : y; } void Randomise(const NVector& xMin, const NVector& xMax) { x = NMath::frand(xMax.x - xMin.x) + xMin.x; y = NMath::frand(xMax.y - xMin.y) + xMin.y; } static NVector getRandom(const NVector& xMin, const NVector& xMax){ NVector Temp(0, 0); Temp.Randomise(xMin, xMax); return Temp; } static NVector getMouse(){ //i return mouse pos as NVector (this func is bind with PTK) return NVector(KInput::getMouseX(), KInput::getMouseY()); } bool isInRect(NVector edge, NVector size){ //i return mouse pos as NVector (this func is bind with PTK) if (x > edge.x && y > edge.y){ //in two steps so it is a little faster if (x < edge.x + size.x && y < edge.y + size.y) return true; } return false; } /*void Render(void) const //TODO: make it render with PTK { glColor3f(1.0f, 1.0f, 0.1f); glPointSize(3.0f); glBegin(GL_POINTS); glVertex2f(x, y); glEnd(); }*/ }; #endif
You know you don't need all those inline keywords if the function is defined in the class (like the operators)![]()
</pedant>
99.9% thanks fo to Olivier Renault... so does the blame. I stopped programing in c++ since then and went to java. I wasn't too good in c++. The language was quite ok, but I hated the compiler "bureaucracy".
Now I remember what I got stuck on, coz I'm here again.Quote:
Another useful function is a function to return the distance between two angles.
God yeah, the trouble I remember having trying to figure this one out with no conditionals! Gave up in the end and have something very similar looking to yours now. It's one of those bits of code I make sure to never lose!
What I'd kill for is for someone to post code that actually gives the shortest direction to move in, to get to a given angle.
My actual problem: I want to rotate from a given angle to another one in the correct (shortest) direction, ie by determining to add on some scalar or subtract it, to reach my desired angle, iyswim.
This is my angle class, it keeps all the values beetween -pi and pi.
The function your are looking for is interpolate, it will interpolate beetween 2 angles and the result is what you expect: the shortess direction.
class Angle
{
public:
static const float NOTANANGLE;
float value;
float SetBounds(float f)
{
if (f>0)
{
float k = float(int(f/(2*PI)));
f = (f-k*2*PI);
if (f>PI)
f = f-2*PI;
return f;
}
else
{
f = -f;
float k = float(int(f/(2*PI)));
f = (f-k*2*PI);
if (f>PI)
f = f-2*PI;
return -f;
}
}
Angle(){value=0;};
Angle(float f) {value = SetBounds(f);};
operator float () const {return value;};
void operator+=(const Angle &a) {value = SetBounds(value+a.value);};
static Angle Interpolate(Angle aa1, Angle aa2, float k)
{
float a1 = aa1;
float a2 = aa2;
if (fabsf(a1-a2)>=PI)
{
if (a1<a2) a2-=2*PI;
else a1-=2*PI;
}
float angle = a1*(1-k)+a2*k;
return angle;
}
};
JC
But I still can't get my hack and slash of that to work.
I'm working in degrees and I can't seem to port it. This seems like it should be so damned easy, but I just get a mental block.
My angles are also any-sized, so I call a clip routine to get them back into 0-360 range beforehand. It's really sophisticated, not!
Code:tF32 CMath::KeepAnglePositive (tF32 Angle) { while (Angle<0) Angle+=360; return Mod(Angle,360.0F); } tF32 CMath::AngleInterpolate (tF32 Angle1,tF32 Angle2,tF32 Scalar) { Angle1=KeepAnglePositive(Angle1); Angle2=KeepAnglePositive(Angle2); return Angle1*(1-Scalar)+Angle2*Scalar; }
to works, it has to be between -PI and PI,
so I guess it need to be beetween -180 and 180 degrees not 0 and 360
JC
Take off 180 from your angle and use my function : just change PI into 180.
In theroy, it should work. Then put it back into your units [0-360].
JC
D'oh, thanks again. Now I look again I see I trimmed off to much stuff - thought it was all bounds checking.
This is damned handy, thanks. The solution I came up with wayback was nothing like as elegant as this iirc.
On a related note, something I find very useful is to have a separate 'degrees' and 'radians' class, rather than one 'angle'. That way you can have comprehensible angles in your data, the system libraries can take their radians, opengl can take its degrees, and the compiler type checking handles any converting that needs to take place.
I'm coming around to that way of thinking myself tbh. All my engine is classed up with this exception as I don't really use angles much - I'm more of a vector man at heart.
Doing what I'm doing now though, I'm starting to feel the need.