Welcome to the first in what is probably going to be a moderately long string of relatively boring logs, as I hammer my C++ code into shape. :P
I actually got very little done this week (For me), because like clockwork my days went like this:
> Wake up
> At 9AM, start coding
> At 9:22AM get phone-call from boss
> Arrive back home at 6PM and wonder what just happened
> Code a bit then get too tired to do much.
As a result of this, I've not really had time to tweak the prototype or work on art this week.
I'm pretty happy that the shop is getting so much work though, means things are becoming sustainable and quickly.
On the other hand, I'm probably going to have to start sacrificing more sleep to get stuff done; I've got another project I'm working on for local business - an extension of my inventory management tool that is being built on top of Electron.
People in town are willing to pay me money for a polished version of that, so I'll probably make a bit of cash off that if I can pull it together in between repairing their computers.
And some good news: Intel finally payed out the prize money for the competition this game won. I've got a shopping list that needs clearing (Including items like extra winter clothing, Dark Souls 3 and my Driver's License).
Now, onto the meat of the blog. It's a bit dry and hard to chew this time, but still represents a good deal of progress:
Some time last year, probably in November, I started writing a little renderer while bored. I wanted to try representing 3D objects in a purposefully low-res setting, to emulate the style a lot of 3D DOS games used, or N64/PS1 titles.
Here's a screenshot of the renderer in action:
The render code has a few useful features, but most importantly: It can handle any desired "native" render mode and scale them correctly to the current desktop resolution while preserving the aspect ratio and maximizing screen usage at the same time.
To render the scene at a lower resolution, I do a two-pass render. First into a texture, then render the texture scaled correctly to the display.
The way the renderer works is that it can operate in either Legacy OpenGL mode or in 2.0+ mode, where it uses GLSL to do most of the legwork.
Performance is pretty much the same as rendering a scene normally; copying the framebuffer to memory and drawing it doesn't really take much out of a system, old or new.
The advantage to allowing for old GL and new GL is that the game works effectly on basically anything with a graphics card from 2000 and up (Tested on an old PC I have in the house that has an Nvidia RIVA TNT2 with 16MB of RAM in it).
The broad process of doing this is:
> Create a blank texture to use
> On each draw frame:
- Render scene as normal, setting viewport and projection to "native" sizes
- Copy framebuffer to texture
- Calculate scaling and positioning of resulting viewport
- Clear framebuffer
- Draw scaled and positioned texture
This results in consistently "perfect" pixels on pretty much any display, with the only exception being if the monitor resolution is lower than the resolution you're using in game (In which case the renderer will lower the scale to fit. Looks a bit worse, since information is lost, but works in a pinch).
As mentioned in my previous blog, I've chosen to use rapidxml so I can load tilemaps from various editors. Most of the good 2D tile editors I've bothered using can export to an XML-style format, and I happen to be using Ogmo, which saves to XML by default.
I did a quick test of how I might load and render the tiledata, and managed to get a test level up on screen using some quick-'n-dirty tile-rendering code (Hard coded and using GL_QUADS).
Everything checked out, so I've stashed that code for later (Want to clean it up properly before committing to using it).
Another minor feature I decided to add quickly while testing was the ability to parse and check command line options. Adding debug switches to a game is a really good way of sectioning off certain bits of code during runtime or jumping into a level/feature that you want to test without having to hard-code it into the gameloop.
This is mostly a system-level design feature, something I decided to add because I'm trying to minimize the game's resource-footprint for no reason other than that it's the kind of thing I do I guess :P
This is basically a case of me pre-allocating my own Heap to use for resources. Instead of allocating a giant monolithic chunk of memory and hoping it'll be enough, I'm assigning blocks of contiguous memory to related blocks of textures/sounds/whatever.
Essentially, each game state has a block assigned to it that contains any unique resources it needs for it to function. The top-level game's data structure contains any common resources that can be used anywhere (Things like bitmap fonts, UI, UI sounds, player sprite, etc), the forest level will probably load the forest tilesets, enemy sprites, etc.
The usual reason for doing this in an engine is to help prevent alloc fragmentation:
If you're frequently grabbing and releasing varying fragments of memory, you're often left with small gaps that can't fit the new stuff you might try to load, so the OS just hands you a few extra pages of memory; so even if you're only using 1% of the new chunk of memory, you've just committed it and the OS won't take it back 'til you're done.
A lot of older game engines did this, and while it's not as immediately useful in my exact situation, it's the kind of thing I felt like doing :P
It might also have uses in the future where I'm potentially working with larger resources; I want to eventually revisit making an FPS, something like a PS1 title: Models, textures, terrain, BSP data, etc. Those can end up taking up quite a large bit of memory, especially once you've got them "unpacked" into usable data.
Heck, I won't underestimate the scope of this game, I might end up needing to use a load-transition between some areas if things get big enough.
I guess that's actually pretty decent for the 2-3 hours I actually got to spend on things.
A lot of this code is actually coming from other places. As I mentioned before, I have a ridiculously huge development folder full of project fragments. Most of them have some useful code I can reuse or recycle, saving a ton of development time.
Some of the things I'm working on integrating soon:
An AngelScript framework I wrote for some version of Exile or other. Basically provides interfaces for linking script to native code.
I may or may not even use this, but having the option to potentially implement the game's logic and gameplay in script is very appealing to me.
The third iteration of my GME wrapper. If I'm going to use NES-style music and sound effects, I may as well use them via an emulator like this one :P
Back when I made Exile, I made an Asset Packer called MPK2 that basically just stored a flat file table as ASCII with starting offsets and lengths, then sequentially dumped each file in order.
I got a bit more sophisticated last year with a few extra features: zlib integration, virtual paths, editing existing PAKs and selective loading (E.G: Load entire /data/textures/level2 folder contents into contiguous block of memory).
This basically allows me to obfuscate my resource files a bit and reduce final filesize.
Math library I started back when I made Exile. Started out as a messy Vector implementation, now has cleaner Vectors, basic Collision functions (Line, AABB, Circle, 2D Raycast) and a bunch of interpolation functions.
Also an implementation of Fixed Point numbers in case I ever want to port my games to systems without a Floating Point Unit...
Portions of a few engines
Most didn't even have names. I basically just went over a bunch of my old engines, found a few decent implementations of the gameloop and hybridized something out of them.
The Gameloop is already fully implemented, and consists of three major parts: The "Engine" (Part that runs each cycle of the loop), the State and the Renderer.
The Game State is the part that contains the current "stack" for the game. It holds resources used by whatever situation is currently playing out, executes entity code, dispatches events to listeners and a bunch of other stuff.
Entities are stored as pointers, and are stored in an associative array of singly-link lists.
Each list contains only entities of one specific type (As reported by the entity's data); this is done to make it easy to iterate over specific kinds of entities for situations like collision checking, deletion, etc.
The system I'm working on currently for things like collision is based on the Listener pattern. The GameState is basically the Listener. Entire types of Entities can be bound to specific events, along with parameters.
So, for instance, the Player entity can register to listen for all Collision events that come from any Pickup type.
The loop tightens up nicely when you're only considering things that can
collide with each other instead of the naive double-dispatch system I implemented in Exile (Basically everything is looped through twice and checked to see if they're colliding).
Of course, the Listener system isn't just for that kind of thing. Generic messages can also be sent and triggered between various entities; sounds, music, transitions and other things can be triggered by code condition and propagated to anything that wants
to be notified about that event...
Next week, I'll hopefully be showing a partially working game (I've got the player partially implemented, just need to get my tile renderer polished up and I'll actually be able to do something with it).