Been coding for a bit, self taught, and this question has been nagging for a while and finally decided to ask. Why is "Mathf" so poor out of the box?

How I kinda feel asking this question:
image

A few years back I created a new class for all my math because I got fed up with always mixing up “Mathf” and “MathF”, and that started me on a journey of developing better named and more powerful math functions and other little handy bits of logic that I find were sorely lacking in Unity.

And I began to ask myself, why is everything so poorly named and organized?

Aren’t these fundamental tools we use to generate our logic?

Here’s one example, “Mathf.clamp”. Why doesn’t this default to a value of 01? Why do we have to type in 0 and 1 if we want a 01 calp, or have to use “Mathf.clamp01”?

And why don’t we have more cool stuff to make our life easier?

I made this function called “FireAtZero” that I find super handy

public static bool FireAtZero(ref float value, float resetTimeLength = 0f, float timeScale = 1f)
{
    if (value == -1) return false;
    if (value == 0) { if (resetTimeLength > 0) value = resetTimeLength; else value = -1; return true; } //Check the single frame where value is 0

    if (value > 0) value -= Time.deltaTime * timeScale;//Tickdown
    else value = 0; //If not firing or counting down, defaults to -1 state

    return false;
}

That I use all over the place:

if (zz.FireAtZero(ref _pulseTimer, timeBetweenPulse)) ResetPulse();

And what it does is sets off a function and then resets the timer to whatever it needs to be and it can be fired again.

Here’s one I just added now, often times I need to rescale or normalize a value around a new min/max, 9 times out of ten around a new 0 to 1 scale so i created this new function:

   public static float Normalize(float val, float oldMin, float oldMax, float newMin = 0, float newMax = 1)
   {
       return newMin + (val - oldMin) / (oldMax - oldMin) * (newMax - newMin);
   }

I guess my question is, is there some kind of library or script that has tons of useful gamedev features like this one that I don’t know about?

Or more at the topic at hand, why aren’t the included math functions out of the box easier to access, better named, and better polished towards the needs of gamedevs?

I could give a lot more examples of what could be improved, but it always seemed so odd to me that such core functionality that is so simple and easily upgraded is so… dated? Mediocre?

If there is a more polished, feature complete set of math functions with better naming conventions and usability and use cases, why isn’t it included with Unity out of the box?

I dunno. I’m pretty new to code obviously so this might be a really dumb question.

1 Like

Have you ever noticed how the Unity Editor tends to replace whole values in the Transform component with lots of decimals? And how they never “fixed” that? Well, your function is a good candidate for a Heisenbug because it expects floats to be equal to whole values, which will work only 99.99999% of the time. Now, there you could create a comparison function that takes some epsilon as range and call it to compare those floats. But that will be an overkill. And that’s a good example of why those Math libs are so barebones and we all end collecting and carrying them around. There are sooo many ways to solve math problems.

1 Like

Just feel like there could be a bit more game logic friendly things included in Unity. And there are plenty of rock solid things with no edge cases that could easily be improved.

Just seems like such low hanging fruit.

Unity cannot change how binary arithmetic and notation works.

Floating (float) point imprecision:

Never test floating point (float) quantities for equality / inequality. Here’s why:

https://starmanta.gitbooks.io/unitytipsredux/content/floating-point.html

“Think of [floating point] as JPEG of numbers.” - orionsyndrome on the Unity3D Forums

Scientific notation (numbers that look like 1.2345e-07 for instance):

Literal float / double issues:

And thanks to halley for this handy little visual floating point converter:

https://www.h-schmidt.net/FloatConverter/IEEE754.html

3 Likes

Thanks for these resources! I’ve noticed that floats set to 0 tend to be pretty reliable, but i’ve admittedly not looked into more foolproof methods of making my code less prone to edge cases. Will look this over later after finishing up dev.

Yup, that’s my point, it can’t be “fixed”.

Right, my code is imperfect, hence why i’m curious why there isn’t a more robust and battletested grouping of gamedev friendly bits of logic out of the box. I’m just curious why there aren’t more out of the box, rock solid and usable functions for developers to use.

Good lord these conversations always gloss over the point and end up revolving around some edge case to make people feel stupid.

I never said "my code is amazing’ i was saying, why aren’t there more exposed and functional bits of logic with better naming conventions and well integrated ergonomic use?

I feel like this is why we can’t have nice things.

That is this:

https://www.pyragraph.com/wp-content/uploads/2013/04/GOOD-FAST-CHEAP.jpg

(agh, how to embed an image here)

image
Agree to disagree. Like for instance changing the Mathf.Clamp to default to a 0 - 1 value is incredibly easy to do.

Seems like Unity is the perfect company to create a free and easy to use and improved set of math functions that all devs using their software could enjoy.

Wait till you see the naming in the Unity.Mathematics package.

But really they’re named to be brief and clear.

And everyone’s needs are different to everyone else’s needs. Math libs are meant to just be the core, simple, maths. Then we can build our own libraries to our own preferences on top of those.

Terrible idea. Clamp01 is more clear than a default overload of Clamp being 0 to 1. That would effectively be hiding the behaviour, which you would have to read the documentation to find out as opposed to it being right there in the method name.

2 Likes

I guess it just ends up being like hotkeys, you’re not going to make everyone happy no matter what you do. Wont we see that it defaults to those values by hovering over the function?
image

1 Like

There is, it’s called fixed point arithmetic!!

It was how we did everything fractional until floating point unit (FPU) processors came long and became cheap and ubiquitous and let us dispense with fixed point.

But those FPUs also imply that languages expose them in the most performant way possible, which is to say without binding extra logic on top, like rounding, or “nearing,” etc.

If you need that, write it out. Side benefit: you know and control exactly how it works and if there’s an issue, you can fix / reason about it.

I think the answer is they had to stop somewhere and chose arbitrarily.

For instance, while Clamp01() is a pretty handy function, what about something like ClampOne(), which is:

public static float ClampOne( float x)
{
  if (x < -1) x = -1;
  if (x > +1) x = +1;
  return x;
}

I reach for that 10x more than Clamp01. There’s just always gonna be “Well, it’s done, ship it” point.

1 Like

That’s still an extra step over just looking at the method name.

In any case, this is why we can code our own helper functions to our preferences. Like you said, you won’t make everyone happy. So just make yourself happy.

2 Likes

I’d like to make sure people are familiar:

https://docs.unity3d.com/Packages/com.unity.mathematics@1.3/manual/index.html
If anything, take up the naming aspect of it with the people behind shader languages, or Unity for deciding this was the way to go for their math library to go with Burst.

1 Like

That’s a good one! Going to gnab it…

Yeah, I use it all the time for control inputs, or really any kind of signed normalized quantity.

The other one that I use in EVERY game that has analog control inputs is a deadband expander… something that ignores deflections below a certain amount, but rescales them back to zero at their minimum to avoid input discontinuities at the minimum deflection (deadband).

Let me yank one out of my Jetpack Kurt Space Flight game:

Function looks like the green line here:

In other “it’s hard to define functions” news, back in the day on many Windows C compilers we had a method called sign() and it functioned like this:

public static int sign( int a)
{
  if (a < 0) return -1;
  if (a > 0) return +1;
  return 0;
}

SUPER useful! I used sign(); everywhere in logic, direction, control inputs, movement, you name it. I mean it was SO handy it became (as far as my mental model of it was) as common as addition or subtraction. NOTE: it is NOT an actual official C spec function, or at least it certainly wasn’t back then. Sometimes it was __sign(); for instance (I think the MSFT compilers did that, IIRC), or a macro SIGN();

One day a while back I saw Unity3D had a Mathf.Sign();… whoa, how cool is that!?

So I started using it without closer inspection (so I’m saying this failure is 100% on ME for not reading the documentation first!) and after some time I saw some really weird behaviour near zero.

Turns out the Unity one works like this, from the docs:

Returns a value of 1 when f is 0 or greater. Returns a value of -1 when f is negative.

So it looks like:

public static int Mathf_Sign( int a)
{
  if (a < 0) return -1;
  return +1;
}

WHOA! That was a tricky bug to track down. Serves me right for not reading carefully.

EDIT: I’ll just put one final note here that good user input signal processing is SO critical to games, to getting that “oh my goodness this game feels SO GOOD!” feeling. Game companies like Nintendo absolutely NAIL this stuff down, they always have. That’s one of the major reasons why Nintendo first party games just feel SO good. Nintendo really gets it.

3 Likes

I would have never thought about that deadband expander, yeah I can see that being super helpful for things like physics where the difference between 0, -. 01, and 1 are all hugely important. And my fire at zero script would be a good use for this!

I’m seeing why having a standardized list of math functions could be a bit of a headache, because the smallest bit of change would radically break many games in ways no one could understand.

I STILL think though that there could be some sort of improved list that people could share around, if only to give people ideas for cool gameplay functions they could adopt for their own function list that they keep unmodified.

These functions are just so darned useful and like the ones you linked, just seeing the function gives a deeper knowledge of gamedev and the sorts of things that can break!

1 Like

While it is widely considered bad practice to do equality comparisons between floating-point values, it does work when comparing constant values.

float value = 0f;
if(value == 0f) // <- true :)

But if you’ve performed any kinds of arithmetic operations to get a float value, then comparing it against a constant value can give unintuitive results, due to the inherent inaccuracy of floating-point numbers.

float value = 1f;
if(value == 1f * 123f * (1f / 123f)) // <- false :o

Because equality comparisons between floats can be so unintuitive and error-prone, it’s arguably best to avoid them altogether, even if is is valid in some cases. Some code analysis packages can show warnings if you have float comparisons in your code, for this reason.

Your code could quite easily be rewritten to not contain any equality comparisons:

public static bool AutoFire(ref float timer, float roundsPerSecond, float timeScale = 1f)
{
    if((timer -= Time.deltaTime * timeScale) > 0f)
    {
        return false;
    }

    timer += 1f / roundsPerSecond;
    return true;
}

In more complex situations you could consider using float?, or defining a custom struct/class, instead of relying on magic numbers like 0f and 1f to represent special states.

1 Like

How about this?
x = x < -1 ? -1 : x > 1 ? 1 : x;

1 Like

I’ve been meaning to use this if syntax more often