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 :PThis 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 :PIt 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.
Hopefully you'll share some portions of your code someday. It really interests me to see C++ written by other people (I assume your code is much more professional than mine, but that wouldn't be a surprise since I usually focus on art and ignore the quality of the code).
I noticed you mentioned AngelScript. It really surprises me someone is actually using it. I once considered using it by myself, but I changed my mind when I realized how bad a documentation it had.@SpectreNectar
Now I understand why it's recommend to use comments in code… You have different naming habits, but otherwise the code (or coding style) is not so far from my code as I had thought.Everywhere. It would help understanding what that code does, instead of "running" the code in your head and imagining the effects.
But I don't think I'm the right person to tell other people to add comments since I don't do that by myself, either…Aw man, that looks like PS1! :D
Ah, yes. those are valid points that I'm aware of. :) I'm arguing that treating faces as quads in the engine, before sending the stuff of to GL (whether you or the driver turn it into triangles), can be more efficient (for the CPU) and elegant (if your entire world are quads). But now we're talking niche low-poly retro style 3D games intended to work on a computer from 1998. Probably don't do dis for anything else.
Just a note here instead of making a new blog about it: No log this week, between work being hectic, Dark Souls 3 being distracting as hell and the sudden onset of a bad cold yesterday, I've hardly had the project open this entire week.
Not to say that I didn't get anything done. Basic player movement is in (Including dashing). It's bugging out a bit because I 'ported' it at something like 3 in the morning while unable to sleep, so for some reason that I still can't figure out, I can dash around perfectly, and walk normally so long as I hold a direction key after the dash… but try move from a standstill without the dash? Nothing happens.The above movement also includes all the correct animations (Sans animation dash trail); things are looking pretty good for about 2 hours of on-off work this week.