Someone showed me a bug in my recent game, Freefall, whereby you can get as far as you like, by just changing speeds continuously, you don’t even need to look at the screen, just tap with a consistent tempo to keep changing speeds. This works because the projectile speeds are set with integers which means they have:
- A base speed at which they will hit you with the parachute
- Fast (double base) speed they will you with if in free fall
- Anything faster than this will miss you because it will get off the screen before it reaches you and is therefore only in there for decoration / to confuse the player
I used integers for practically everything in the game because the low resolution meant that even moving one pixel at a time is a pretty big step (there are only 48px in the vertical axis) and you can’t draw smaller than that.
During testing I focussed on checking that 1 & 2 would hit you at their respective speeds and that you could dodge them if you changed at the last moment. If you change speeds so infrequently the problem is less apparent. If you ignore the projectiles and just tap continuously you will reach a new speed which is between 1 & 2: Because half the time you’re at moving speed 1 and half the time at speed 2, on average you are moving at 1.5 and all the projectiles miss you because none of them move at that speed. You are effectively moving at a “float” speed despite everything being “int”, the game counting movement, speed, position in “int” and being unable to draw anything more high-resolution than an “int”. If you know about this problem then the game because no fun at all, as reaching a high score only depends on how long it takes you to get bored of carelessly tapping.
The solution is that the projectiles should also move at float speeds, ignoring the fact that on the screen you only have 0 to 48 discrete positions you can draw them at. This is also a bit annoying because, seeing as everything is “int” I leaned on the standard library’s image types like Point, Rect etc. for position, drawing, collision detection etc. and I didn’t feel like rewriting all of those. Instead I changed only the projectile’s Coordinates and Velocity to float64. Coordinates was an image.Point, so I introduced a new internal type Point that uses float64 instead of int. Of course, all the other code that uses these immediately broke because it’s expecting a real image.Point, so I added a Pt() method that converts the values to int (rounding to the nearest whole number) and returns a Point of that. This was easy to plug in at the places that needed it without having to tediously convert all of the game code to floats. Now, even though the game can really only draw at integer values, its internal model has a higher resolution that lets it use more intermediate speeds and catch you if you change speeds frequently.
As a result, instead of the previous speeds randomly selected from the integers 1, 2 & 3 the speed can be any random float64 value in the range from 0.8 to 3.0. I kept the 3.0 speed in there because with only the slow ones the game looked a bit boring. I might lower the upper limit a bit if this has made the game too easy now. The increased resolution in between 1.0 and 2.0 adds a lot of projectiles that will hit you while you’re changing speed and the problem is solved: You can now no longer win the game by mindlessly tapping at a constant tempo.
Another lesson from this is that when choosing between int or float64 for numeric types in your game, if the value is about how “much” of something (like here the speed) then use float64 and if it’s about how “many” then use int. Okay, you may be able to count the 48 pixels to the top of the screen and say “how many” but that’s just how it is being rendered. In fact I’d recommend using float64 for basically all of your game logic related numberic types and only using ints for things like the index of items in a list you’re looping through or picking items from, and nothing more than that.