🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

Vector based unit movement

posted in DreamLand editor
Published June 17, 2024
Advertisement

Up to this point I have been using a unique code snippet for each direction of movement. These days I managed to switch to using vectors for movement. I have the same code for all directions now

float VectorX = NextNodeX - UpixPosX;
float VectorY = NextNodeY - UpixPosY;
float VecLength = sqrt(VectorX * VectorX + VectorY * VectorY);
float SpeedPercent = (0.016 * 100) / VecLength;
float AdvanceX = (VectorX * SpeedPercent) / 100;
float AdvanceY = (VectorY * SpeedPercent) / 100;

UpixPosX = UpixPosX + AdvanceX;
UpixPosY = UpixPosY + AdvanceY;

Now I have to figure out steering given this new approach

0 likes 2 comments

Comments

JoeJ

Promising. : )
But it's over complicated. You can remove the percentage factor of 100, which is just wasted instructions and precision.
Instead you could make the 0.016 a variable ‘timestep’ or ‘deltatime’, and then use smaller timesteps instead a ‘percentage’ if necessary.

I would also recommend to write 0.016f and 100.f.
It's annoying, but without the f the compiler treats the constants as doubles, causing compiler warnings and eventually even reducing performance.
After years of resistance i have personally settled at typing this damn ‘f’ everywhere, now still editing old code where i was too lazy.

There is also a potential bug:

float VecLength = sqrt(VectorX * VectorX + VectorY * VectorY); 
float SpeedPercent = (0.016 * 100) / VecLength;

Length could become zero, so the division causes a infinite number, causing bugs or crashes later on.

To prevent this, we need extra checks, which is annoying as well but necessary.

Personally i use 3 constants for this:

#define FP_TINY 1.0e-12f
#define FP_EPSILON 1.0e-6f
#define FP_EPSILON2 FP_EPSILON*FP_EPSILON

I use the tiny one to prevent div by zero like so:

float SpeedPercent = (0.016 * 100) / (VecLength + FP_TINY);

It's cheap because it requires no branch. If VecLength would be zero, result of SpeedPercent would be zero too, which is what we want here.
But i'm no floating point expert and idk how this precisely works, so take it with a grain of salt.

Alternatively, this is a standard way of doing it:

float VecLength = sqrt(VectorX * VectorX + VectorY * VectorY); 
float SpeedPercent = (VecLength > FP_EPSILON ? (0.016 * 100) / VecLength : 0.f);

Or, if you're not familiar with ternary operator, it's like that:

float VecLength = sqrt(VectorX * VectorX + VectorY * VectorY); 
float SpeedPercent = 0.f;
if (VecLength > FP_EPSILON) SpeedPercent = (0.016 * 100) / VecLength;

Basically we check against a small epsilon to avoid divisions by zero.
You better make this a habit right now. Otherwise potential issues pile up and are hard to find once problems occur.

We can eventually optimize the square root away by doing the check earlier:

float SpeedPercent = 0.f;
float squaredLength = (VectorX * VectorX + VectorY * VectorY);
if (squaredLength > FP_EPSILON2)
{
	float VecLength = sqrt(squaredLength); 
	SpeedPercent = (0.016 * 100) / VecLength;
}

That's why i also need a squared epsilon constant.

Sometimes you can be sure your value can't be zero, so no checks are needed.
But mostly you can't be sure, and in doubt, better care about it.

There is however a problem about those arbitrary epsilon values, which i need to mention.
If you use huge numbers, you need larger epsilons, and if you use tiny numbers, you need smaller epsilons.
The given numbers are meant to work for numbers which have a size of about one.
In practice they mostly worked for me so far. But sometimes we need to dig deeper.

The main difference between floating point and integer is this:
With integers, you always have the same precision, no matter if the unit is at point 0 or point 100.
But with floating point, precision is higher near zero but decreases with distance from the origin.
So you want to have the origin in the middle of the map, not at a corner, or even outside.

June 20, 2024 07:26 AM
Calin

Thanks for your feedback JoeJ. Alway interesting to see what a pro has to say about my latest. As far as the redundant percentage thing is concerned this is just my current level of understanding. I know there is a better way to code what I did. There is always place for improvement I guess. There are so many things to keep in mind though if you try to do things better.

June 20, 2024 02:24 PM
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Profile
Author
Advertisement
Advertisement