Mechwarrior-inspired game (code critique requested)

Hello, ive been working on a simple Mechwarrior/Battlezone-inspired game for the last month and a half or so… i’m trying to keep the gameplay as simple as possible (so my to do list is more a list of ideas than planned features) but so far ive implemented a 2D minimap (which you can toggle with the A button), two point perspective projection for the grid (and everything in world space), and coordinate wrapping once you reach/cross the world boundaries (using modulo)… I’ve tried holding off on asking questions (or asking for code critique) until i refined it as much as possible (trying to reuse functions/variables while cutting down on functions/variables in the global namespace as much as possible) but since my working knowledge of this stuff is still quite limited i was hoping i might ask for a second (or third, or fourth) opinion before moving on… the repository can be found here

And a video here

Thanks!

4 Likes

Implemented different sized enemy sprites and redrew the cockpit, and ive already finished most of the other sprites (so all i need to do is draw up smaller sizes and animate them)… then ill be moving on to shooting mechanics (which should work pretty similarly to the 3D projection logic ive already implemented) and the main game engine will be done (then the rest will just be adding menus/missions)… updated visuals can be found here

2 Likes

Nice. This game with one color: gratz

Its looking really nice!

Do you think performance will be an issue?

I wonder if you should look at using @pharap’s fixed point library instead of using floats. It will result (probably) in smaller, faster code.

Thanks! And thanks for reminding me @filmote, i was actually planning on using that initially then got so wrapped up in it i forgot :sweat_smile: but now would be the best time to do that before i start adding things like bullets (since the memory/performance savings would really start to add up then), plus otherwise id have to refactor more code later on (so i really appreciate the reminder lol)

So far memory and performance has been looking pretty good though… honestly i could even see the basic 3D engine being used for different sorts of games (maybe by storing level, player, and sprite data on the FX) but I’ve been trying to work my way up to see how much RAM will limit the scope as things start to add up… i was also hoping to use the FX stuff later on to store as much data as possibie to fit things like mission/save data and more mechs into the game though, if possible :grin:

1 Like

Started implementing the shooting mechanics and a progress update can be seen here… sorry for the beeping in the background, im at work so its just a CMM running behind me.

Although im probably going to stop for a moment and try replacing all my floats with fixed points before i get too much further… actually meant to do that before moving on but i was just curious to see how much more RAM it would take up and while i was playing around with it managed to get it working a lot quicker/easier than i thought :man_shrugging: at the moment i still have 1201 bytes of RAM left, but id imagine this will quickly shrink as i add collision detection and enemy bullets as well. The repository hasnt been updated yet though (since i wasnt sure if anyone would even be interested in looking at it)

1 Like

Playing on an Arduboy at work? I salute you.

1 Like

Smaller is likely, particularly if SQ7x8 or UQ8x8 will be sufficient.

Faster seems to be a ‘your mileage may vary’ scenario. I’m never sure why though. I suspect it might just be that the compiler knows how to optimise floats in ways it won’t for fixed points, or simply because of the amount of bit-shifting involved for multiplication and division.

Either way, it’s easy to use and works in a constexpr context, which were always the main objectives.

@Ard_Flamingo Of course not, that would be crazy… I hide in the bathroom when i need a fix! :wink: but noo, luckily we’ve just been a bit slow lately so its allowed me a little time in between to work on other things

I’m more concerned with size than speed at this point… honestly i hadn’t even thought of speed just because I was so focused on the former :sweat_smile: but as i add more (with all of it interacting) thats definitelya valid concern lol… i was curious just to see how both would look if i changed the number of max possible bullets (and extended their lifespan) and i maxed out the available RAM without seeing any drop in performance… the number far exceeded the amount of objects id even need in the world at any givin time, but considering i have a few more things to add (like collisions, etc) im sure both memory and speed will start to add up pretty quickly as things progress.

All that being said, ive been looking into your fixed point library the last few days (and have tried to read up on it a few times in the past), but im having a bit of trouble understanding the format… i think i kind of get the concept (using smaller data types to store the integer and fractional sides as smaller integers) just not so much how to actually implement it… ive tried reading up on that as well, but for some reason it just hasnt clicked yet. But i wanted to read more into it before asking for help (since there’s already a wealth of information out there)… will probably pop up with a few questions once i get to trying to implement it though lol

There is an awesome article on FixedPoints on page 35 of this magazine :slight_smile:

1 Like

You don’t have to ‘implement’ it, that’s what the library is for.

You just bring in the library and treat it 99% the same as you would floats or integers.

For the most part the only time you have to care about the difference is when you need to convert them to integers (e.g. for feeding them to one of the Sprites drawing functions), which can be done either through the .getInteger() member function or a plain old static_cast<int>.

Start by reading “A Fixed Point Primer”, which is included with the library.

You don’t really need to understand fixed points in great depth to use them though, all you really need to understand is how to think of them as mixed fractions and (as a result of that) how to understand their range limits.

For example:

  • UQ8x8 has an 8-bit unsigned integer part and an 8-bit fractional part.
    • An 8-bit integer part can represent 2^8 = 256 values, and being unsigned makes that range 0 to 255 (i.e. 0 to 2^8-1).
    • An 8-bit fractional part can also represent 2^8 = 256 values, but those values are fractional values, so the range is \frac{0}{256} to \frac{255}{256} (i.e. \frac{0}{2^8} to \frac{2^8-1}{2^8}).
    • Together that makes the range of UQ8x8 0 + \frac{0}{256} to 255 + \frac{255}{256} (i.e. 0 + \frac{0}{2^8} to {2^8-1} + \frac{2^8-1}{2^8})
  • SQ7x8 has an 8-bit signed integer part and an 8-bit fractional part.
    • An 8-bit integer part can represent 2^8 = 256 values, and being signed makes that range -128 to 127 (i.e. -2^7 to 2^7-1).
    • An 8-bit fractional part can also represent 2^8 = 256 values, but those values are fractional values, so the range is \frac{0}{256} to \frac{255}{256} (i.e. \frac{0}{2^8} to \frac{2^8-1}{2^8}).
    • Together that makes the range of SQ7x8 {-128} + \frac{0}{256} to 127 + \frac{255}{256} (i.e. {-2^7} + \frac{0}{2^8} to {2^7-1} + \frac{2^8-1}{2^8})
1 Like

Well that’s scary enough to scare most people off!

And nice use of the mathematics addition @bateske added to the forum.

Ahh thanks @filmote, that article makes things much clearer!

Id actually already read that a few times over, but reading it again after reading the article I can definitely visualize things a lot better and that makes perfect sense now, thanks!

Well ideally :laughing: but my code relies quite a bit on using sin and cos for rotations, and fmod for my wprld wrapping, and its my understanding those can only take floats (so ill have to look into making my own)… and I’ll have to go through my code a bit more thoroughly to make sure the values i use (for things like rotation and movement speed) dont cause any rounding errors due to precision. Plus at the moment my world size is fairly small (40x40), but i use floats for a lot of stuff within it, so it also might make more sense to make it larger in order to rely less on floats or allow less precision (but this is forseeing issues i may not even have yet, just want to take the time to make sure)… either way though, thanks for the info/help, that definitely makes things much clearer. Well, besides the maths part, filmote was kinda right about that :laughing: but your Fixed Point Primer combined with the table in that Arduboy magazine article definitely helps clear things up, thanks again!

You could possibly have the sin and cos values in a lookup table - this might speed up the game.

Did nobody else do mixed fractions at school?
1.5 = 1 \frac{1}{2} and that sort of thing?

Another way to look at it is to say the whole thing is a fraction: 1 \frac{128}{256} = \frac{384}{256},
which conviniently explains why UQ8x8(1.5).getInternal() happens to be 384.
(Or at least it should be, I haven’t actually checked.)


Yeah, that’s going to be a problem.

You’d pretty much have to implement your own sin and cos, which is easier said than done.

An easier way to do it is to use brads for measuring angle, that way you only need to store a 64 element lookup table. (Or 16,383 elements if you go for 16-bit brads, at which point you’d probably have to store it on the FX chip.)

If you opt for those, you could just adapt this old code:

If you don’t use brads…
It’s probably going to be CORDIC or a Taylor series scenario, so good luck with that…

A better way, if possible, would be to make your world size a suitable power of two (ideally 256) so that either the wrapping happens implicitly or you can just use masking instead.

I’m not sure how one would go about replicating fmod for fixed points. I would hope it would be as simple as replicating operator / and reimplementing it using % instead of /. If not then x - (x / y) * y should work according to cppreference, but that’s bound to be quite expensive.

Consider this: 1.5 kg of flour is the same thing as 1500 g of flour, with the difference being that the latter doesn’t require a decimal point.

Or to put it another way, making your units smaller lessens the need for fractional precision.

Having a 256x256 grid of cells where each cell can be further divided into 256x256 is effectively the same thing as just having a 65,536x65,536 grid of cells.

Sure, I had a slide rule.

I was going to say ‘so did most people over…’, but then I started wondering when slide rules actually stopped being used, and vexingly I can’t find a source that gives a decent answer. The best guess I’ve managed to dig up is ‘80s’.

Incidentally, I happen to have possession of an old O-level maths textbook (decimal currency edition), which also happens to be as dull as ditchwater. Chapter 1 is about logarithms.

I finished high school in '88 and I never saw a slide rule in the 70s or 80s. Maybe Australia ditched them early?

Or, on the other side of the coin, perhaps Britain clinged to them for longer than it really ought to have done? Both seem plausible.

Incidentally, Chinese students still use suanpans (Chinese abaci) from what I hear, but far from being a historical relic, they actually improve mathematical skill.

Actually there are quite a few calls to it, considering the player has opposite rotation on the minimap and in 3D space, the bullets have their own angle to keep track of, and the enemies will eventually need it as well… so any savings might actually add up to be a decent amount, thanks!

I did, but graduated almost 14 years ago so I’ve forgotten more than I’ll remember at this point, I think :laughing: same with a lot of math in general, unfortunately… I still actively use geometry and a bit of trig at work though

That definitely helps to further clarify things (since visualizing how the number to the right works as the numerator takes a bit of getting used to), thanks!

I’ll definitely look more into that, it would just take a bit of adapting since I’m currently using radians for my rotations. But seems like it might be a bit more worth it in the long run for sure.

Sorry, I’m not quite sure what you mean by either.

Ahh, I thought it might work the opposite way (making my units larger might lessen the need for fractional precision)… since having more cells could mean using less subdivisions (and lessening the need for more precise fixed points) but I think I see what you mean.

So as of right now I think my plan of action will be reverting back to just the basic framework (what I have in the github repository), start integrating the fixed points library (except for the trig and fmod functions), and try to move everything over to using lookup tables (and using brads) instead… I only use fmod for wrapping world coordinates and rotation so could probably replace this with another approach, but converting from radians to brads might be a bit trickier… then once I get all of that stuff done I’ll try to re-merge each change back in one at a time (enemies, shooting, etc)… meanwhile, I’ll have to play around with different parts and see how this affects memory/speed.

And once I get the basic framework converted over we can actually compare the memory/speed savings between both versions side by side before i put the other parts back in (just because id imagine it’ll probably be a decent bit and I’m curious lol)