Jan 1, 2013

IntervalTimer


Introduction
When programming a game you'll depend on timers heavily. Whether you have a damage effect, that needs to be calculated every x seconds, or a visual special effect to be controlled, you'll steer all that by adding time to variable and checking it against a limit.
Like with every work that gets repetitive, you'll get sloppy and you'll make mistakes.
And it's precicely for this reason that I wrote this class.

Description
It should have a constructor where you would have to declare the timeout in milliseconds you want to measure.
Then you would have to update it every game-cycle, giving it the current gameTime object. Its update method will do the appropriate math with the appropriate values hidden in the gameTime object and return true, if the timeout has been reached.
It has some convenience-properties, which you may use to determine the percentage of time passed, to determine the time that is still left, or to reset or change the interval itself.
You may use a defaulted field in the update method, if you wish to add the excess time, that was not used after "triggering" the timer, to the next run of the timer (in case you have some sort of a cyclic timer because otherwise you would just "lose" this excess time).
 
References

Interval<T>

Introduction
When programming a game or, frankly speaking, when programming anything, you will be confronted with intervals.
I remember reaching the point, where I began thinking about a separate class, when I was working on the particle engine. I had about 30 values to track per particle (velocity, starting position, rotation, acceleration...) and each one of them had an upper- and lower boundary. As I'm not paid per line of code, I searched for a way to somehow reduce the length and improve the readability of it (int minNumberOfParticles, maxNumberOfParticles; float minRotation, maxRotation...).
The Interval class is a handy way to produce shorter and more readable code while introducing a new level of consistency and, as I will explain right now, accuracy as well.
 
Accuracy
We don't always take care about it, but intervals don't only have upper- and lower bounds. They also have the property to include or exclude both bounds as well.
And when we have to take care about that, we almost certainly treat it differently each time.
 
Think about it. What do you mean by: "Pick a number between 0 and 100!".
It could be one of the following:

Most of the time we mean to include both boundaries, but we should be able to override that behavior.
 
The requirements
So we want a generic class that you may instantiate using
  • Only an upper and lower bound (you would use that constructor most of the time) and it would default the treatment of the boundaries as inclusive.
  • A full constructor, specifying the treatment of the boundaries as well
 
It should have convenience-methods:
  • IsInBetween, that takes a value and tells us if this given value lies within the interval
  • An overload of this method, specifying the treatment of the boundaries as well, overriding the given treatment, but only for this single call.
  • We would want it to adjust the values in a way, that the interval doesn't become irregular
    Consider you built one:
Interval<int> i = new Interval(3, 8);
And now we set the lower bound to 9. That may actually happen during the lifetime of such an object and it may happen the other way round as well. Our object should, without having any other useful information, do the most sensitive thing there is to do and automatically set the upper bound to 9 as well.
 
When we think about the functionality, we should restrict the generic type-usage of this class to the interface IComparable, which allows us to do all the proper comparisons.
 
The class/struct
After writing it I saw that I used it almost everywhere and since I was programming a game at the moment, the effort for the garbage collector was some concern. So I found that I could change it from a class to a struct (= no garbage collector overhead), without any side effects.
It doesn't change the way you use it at all.
 
The consequences
After using it for a while now I found that the usage of this class helps making my code a lot leaner.
For example it reduced the LOC of my Particle class almost to half and it reduced the number of overloads for a few different classes as well .
Think about a randomizer-class, that gives you a random number between two given values (which I was very lucky to have when trying to change my code to support several multiplayer modes... Deterministic Lockstep is the keyword... But that is another story waiting to be told).
 
References

Feb 12, 2012

Radian - Degree - Conversion


Introduction    
When we are talking about the value of angles most of us are used to think in degree.
In common language the phrase "to do a 180" just sounds better than "to do a 3.141592 radian" or even "to do a PI".
   
But game-engines, due to their unavoidable background in geometry, are somewhat different.
They use radian instead and that is the standard for almost all areas of mathematics.
   
What are radians?    
According to Wikipedia, "radian is the ratio between the length of an arc and its radius".
   
The meaning of this is that on a circle where the radius is 1 unit (the Unit Circle), the angle in radian IS the length of the arc.
The following diagram illustrates this thought.
   
    
Pic 1: This picture illustrates the connection between radian and degree. The length of the arc is the angle in radian.
   
One full turn please    
As we all learned at school in times past, the circumference (c) of a circle is ...
   
    
   
So the circumference of a circle with the radius r = 1 (the Unit Circle) would be...
   
    
   
   
    
Pic 2: This diagram illustrates the connection of the circumference of the Unit Circle and 2*PI.
Pretend that the circle unrolls the red line while rolling in the direction of the orange arrow. The purple angle-value has the unit degree (°) the red one's is radian (rad).
   
A little help converting    
Now everybody knows that two PI are 360 degree but in my experience the debugging of such values can be difficult.
So either you calculate the degree-value on the fly within the code or the debug-environment, or you could use the following handy table, that, in most cases, suffices.
   
  
Pic 3: We used a 8-direction compass that displays "east" for 0 degree rotation.
   
For all of you still searching for the right conversion-methods to use within your code:
   
(Angle in degree) = (Angle in radian) * PI / 180
(Angle in radian) = (Angle in degree) * 180 / PI
   
Clamping / Constraining    
In some cases, like when memorizing the rotation within a sprite-class it may be suitable to clamp the value according to its domain since there is no point in memorizing the number of turns in a single direction that brought you to this specific rotation and there always is the problem of a possible value-overflow of a variable.
   
You don't want to use the method "Clamp" in the XNA-MathHelper - Class because it returns the max-value if the upper boundary is exceeded (and the min-value vice versa).
   
All is good as long as you set the bounds for your valid angle to [-π, +π] as you will get the following output for the calculation: value % MathHelper.Pi
    
Pic 4: This output shows that a simple modulo-Pi - Calculation is sufficient if you want to constrain your angle to [-π, +π].
   
Nevertheless most programmers want the angle to be between zero and 2π, just like this:
    
Pic 5: The angle constrained to [0, +2π].
   
Here are two of our methods that we are actually using in production-code.
    
Pic 6: The first method constrains to a value within -Pi and +Pi by using a simple modulo-operation. The second one does a modulo-operation and always returns a positive value. This takes into account that a quarter-twist in the negative direction is the same as a three-quarter-twist into the positive one.
   
There are better methods out there using only one modulo-operation instead of two, which is pretty expensive, but we have not yet reached the performance-limits using this one and you know what they say.
Don't solve performance issues that have not yet occurred.
   
References