Merry Multiplatform-mas!

It’s been a while since I wrote anything heavily technical, so here comes some words to fix that!  Black Annex is going to launch for Windows, MacOS and Linux simultaneously now, thanks to some large amounts of progress I’ve made lately.

Let’s start by talking a little about how I make Black Annex.

Black Annex is written in BASIC, it’s almost pure QBASIC, but to make it work on >16bit operating systems, it’s translated to C at compile-time, links SDL for its interface work, and then compiles using GCC. This was, originally, handled by an off-the-shelf product called QB64. It worked really well at first, but I started to find bugs in QB64 as I started to push for higher framerates, modern features (Steam Overlay Support) and slightly unusual input methods (relative mouse movement, for one). QB64 really broke down at a few key points, and I knew I would have to fork my own version of it and change a lot of it to make it work. I know almost zero about programming in C, so the scope of doing this was very scary.

This was the reason I decided to focus on Windows first, and then worry about a custom compiler for Black Annex for MacOS and Linux post-release. Programming in C is scary enough for me, but targeting operating systems that I’ve never worked with would also make the job even bigger.

So, it was obvious that I’d need to use a custom fork of QB64 to compile Black Annex, but I wanted to make sure that the off-the-shelf version of QB64 would still compile it. I wan’t to make sure the game would “work” to some degree if people didn’t have access to my compiler, only the game source.

The first thing I wanted to change about the compiler was the way that the PAUSE key, and the ALT+ENTER keys worked. The PAUSE key, in QBASIC, makes the entire program execution just pause. This really messed with my delta-timing in-game, and I wanted to just disable it so I can control game pausing myself. ALT+ENTER toggles full-screen modes, which crashes the game if you do it while the game is loading in sprite sheets. I wanted to just completely disable that and make a menu option for full-screen.

Changing those two options would be my first adventure in the C source of QB64. QBASIC 4.5 for DOS is basically this library file called qb.lib, which contains all the information for translating your BASIC code into DOS interrupts and such. QB64 works the same, with a file called qbx.lib. All the changes I wanted to make live in a file called qbxlib.cpp, which I can modify, recompile QB64, and then recompile Black Annex to use the new libraries.

I spent many hours exploring qbxlib.cpp, it’s a big file and it’s really messy. There’s no tab-spacing or anything, it’s just a massive flat text document. I tried to get Sublime Text to do the “Auto Spacing” on it to make it a bit more readable, but it didn’t have an amazing effect. I navigated about and found some parts where keyboard input is handled and disabled the ALT+ENTER and PAUSE key behaviors. With that done, I left it at that. That was the only thing I really needed to do at that time.

Six months passed, and I found a bug. The first major change I had to make to QB64 was the way mouse movement is handled.

The bug wasn’t in QB64, or Black Annex, the bug is in SDL 1.2 (or, some would argue, Windows). QB64 uses SDL for keyboard and mouse input. SDL has a bug involving relative mouse movement. When using relative mouse movement with SDL, you’ll typically enable a flag called SDL_GRAB, which forces the mouse cursor to stay inside the program window. The bug is as follows: When you enable SDL_GRAB, then begin holding a key on your keyboard, the mouse movements reported by SDL will skip a few messages, resulting in pulses of “lag” in the mouse movement. Release a key and the issue goes away. Hold two keys and the issue returns, release one key (while still holding another) and the issue goes away. If you disable SDL_GRAB, the issue does not appear at all.

The issue appears because SDL_GRAB works by constantly warping the mouse to the center of the window after reporting mouse movements. It does this warp as fast as it possibly can. Windows can only record so many messages about inputs, and while holding a key, moving the mouse, and warping the mouse, information gets lost (or SDL doesn’t read the messages fast enough, I am not sure).

I was completely unable to fix this issue inside my BASIC code, I had to go back to qbxlib.cpp and write some hacks into the keyboard/mouse handlers there. I tried so many things, sending fake KEYUP messages to SDL, sending fake KEYUP messages to the windows even queue, and all sorts of terribly ugly hacks. Disabling SDL_GRAB and just constantly using SDL_Warp to warp the mouse myself. Using the Windows API to warp the mouse. None of these worked. The issue always returned.

In the end, I had to disable SDL_GRAB, and every time the mouse reaches past the inner 25% of the program window, warp it back to the center myself. This much slower warping (as opposed to warping constantly as fast as possible) made the issue go away. This was about one week’s work, between learning C, learning how to handle events in Windows and SDL, and actually coming up with a solution.

With this work done, I remembered the main roadblock I had originally encountered with the MacOS and Linux builds of Black Annex. Every time you moved the mouse, the entire game would just lock-up. Execution would resume as soon as you stopped moving the mouse. In the past week, I’d learned a lot about how mouse movement works in QB64, and I was confident that maybe I could actually make some headway in the Linux and MacOS forks of QB64 I had planned.

But, I had something else I’d rather work on with my new knowledge of C and the qbxlib.cpp file. I wanted to get the Steam overlay working. The Steam overlay only works if you game renders using OpenGL and DirectX, but QB64 just uses software “Surfaces”, so it was out of the question. I had been talking to Simon Roth a little bit about my issues with mouse movement earlier, and I mentioned the Steam Overlay roadblock. He said to me: “You can just render an SDL surface to an OpenGL texture, that’s what I did with VVVVVV”.

I understood the theory of what he was talking about, but I had zero concept of how to actually do what he was saying. The second major change I had to make to QB64 was the way SDL surfaces end up on your PC screen.

Googling “Render SDL surface to OpenGL texture” comes up with lots of results, and I had to spend a lot of time reading about what these people were doing, and how to make it work. It wasn’t too long because I had completely broken QB64, and ended up with it always just outputting a square window with an OpenGL-rendered triangle on it. Black Annex would be running in the background; you should still hear the music, but the rendering was just an OpenGL loop thingie now.

With loads of help from the goons in #sagamedev, I got the SDL surface that normally appears on-screen in QB64 to instead render to an OpenGL texture in a quad on-screen instead. Black Annex launched via Steam, and the Steam Overlay appeared. I had to do some work as far as communicating to OpenGL what resultions to use versus the size of the SDL surface I had initialized, so I hijacked QBASIC’s “OCT$” function (who even uses that, anyway). By using an existing function, I could ensure my code would still compile using an off-the-shelf compiler, but if OCT$ returned some very specific results, Black Annex would know that OpenGL was available and switch to using that instead. I changed the code for OCT$ in qbxlib.cpp so that it would always return a blank string of 22 spaces no matter what you sent to it, and then it would begin a sequence of initializing OpenGL. Making a call to OCT$ would first turn OpenGL on, then you need to send the width of your screen to OCT$, then the height of the screen. It looks funny to see these unexplained calls to OCT$ in the code for Black Annex, but there’s comments explaining what they do.

So I’d ended up with a build of QB64 that’s very different than the off-the-shelf version, and also supports OpenGL scaling, instead of the very slow scaling method I had used previously. Full-screen Black Annex is a lot faster now.

So next I turned to Linux with my new knowledge of how all this stuff works. I figured “I can probably get Linux working”. I installed Ubuntu on a Dell XPS17 I have spare (it’s not mine, I borrowed it for exhibiting at PAX). The XPS17 has an Optimus video card, so it took two instances of “Whoops, I broke everything, format” for me to get into Ubuntu (I don’t know anything about Linux so it was easy for me to break stuff).

I installed the vanilla QB64 onto my Ubuntu computer and compiled Black Annex. As it had always done, it crashed as soon as you moved the mouse. I jumped straight into the qbxlib_lnx.cpp file and got to work on finding what the hell the mouse was doing that made everything crash. It was a straight-up bug. The fellow who made QB64 must have just never tested relative mouse movement on Linux, because it just had a line that was telling program execution to literally pause whenever the mouse moved. I have no idea what the point of it was meant to be, so I just commented it out. Blam, Black Annex worked on Linux. I don’t fully understand Linux, so I’m not 100% sure how to deploy the game (it has these SDL dependencies), but with the game working, I ported all my OpenGL code over to the Linux libraries and I had Steam Overlay working on Black Annex in Ubuntu. The entire Linux effort took a single afternoon. It was an amazing result.

But then there’s Apple.

I have a really bad time trying to use MacOS. I get frustrated and fumble around and can’t find folders and get confused and end up installing Windows 8 on my iMac and breathe a sigh of relief. That is my relationship with MacOS up to this point. I recall the issues that I had with Black Annex on MacOS.

Firstly, there was the mouse issue, the same as Linux, game would crash the moment you moved the mouse. Then there was the ugly-as-hell terminal that would also run alongside the game reporting some kind of errors or something the whole time the game ran. Then there was this stupid .command file you had to run to actually start the game so that the working-directory would change before running the game so that it could find the SDL framework in its subdirectory. All these issues compounded with that fact that I struggle massively with MacOS made this seem like the scariest endeavor.

So I installed vanilla QB64 on MacOS and compiled Black Annex. It was exactly as I described. I quickly fixed the mouse issue, it was the same bug that had been in the Linux build, so the game worked at this point, but this horrible terminal window and the .command file you needed to use to start it was really ugly.

The reason it had this .command file was because all the SDL framework lives in a subfolder next to Black Annex called “common”, but in MacOS, when you run an executable (unlike in Windows), the program considers ./ to be the hard disk root, as opposed to the executable program’s folder. So when Black Annex looks for ./common, it wouldn’t find them. This .command file performs as chdir to the executable’s folder, and then launches the game so it can find all of its frameworks. I Googled the issue.

I found this program called otool, which you can use to find a list of every framework your executable relies on, and where it expects to find it on the hard disk. As expected, Black Annex was spending a lot of time searching for ./common. A little more searching and I found a program called “install_name_tool” which lets you change the URLs that your program expects to find its dependencies at. In fact, you can use “@executable” to direct these changes to areas on the hard disk relative to where the executable is located! So I wrote a little .command file of my own to change all the dependencies on an executable after it’s been complied by QB64, so the .command file wasn’t needed anymore.

Well, this worked all well-and-good for the dependencies, but what about the actual game content? It lives in a folder called “campaign” next to the Black Annex executable, and there’s no way in QBASIC for me to say CHDIR(“@executable”) or anything like that. I had to go back into qbxlib.cpp and perform a chdir in there by discovering the executable URL manually. This was a real fuckaround, with a lot of conflicting information as to whether it was even possible outside of ObjectiveC. Without delving into actual source code, I eventually found someone who had written a few lines of code to do what I needed, and worked it into my own uses.

So Black Annex would work now without a .command file to start it, the last thing was just this ugly terminal window that always insisted on being there while the game ran. It was just reporting “Warnings” constantly from the C source of Black Annex (which I never touch, considering I deal in the BASIC side of it only), so I wasn’t interested in fixing all the warnings hoping the terminal might go away if I did.

Well, I got distracted by something else…

I wanted to make a .app bundle for Black Annex. You know how on MacOS games are usually just these .app folder things that you just double-click on and the game runs? Well, I packaged up Black Annex into one of those and the terminal window I was arguing with just disappeared completely! I guess it’s just some magical thing where .app bundles just assume you’re not interested in debugging your software and hide that stuff.

I had to make one last change to libqbx.cpp’s new chdir code so that it would change directory to one inside the .app bundle and still find everything, but with that done, Black Annex would launch happily!

I sent the .app bundle to a friend who also has a Mac to test it out. He replied: “It says I need to install x11″. What the fuck is that? I have no idea what this means. It’s something to do with making Linux stuff work on MacOS. But Black Annex just uses SDL for everything and that works on Mac. Why is it using this x11 thing?

I went back to otool and checked the dependancies and saw this “x11″ thing in the list, it was loading something called “freetype”. I guess the default font that QBASIC uses is emulated in QB64 using this stuff. I don’t even use that font, I have my own text renderer I built for Black Annex. Sometimes I use “PRINT” while debugging, but I don’t plan on doing any actual QBASIC development on MacOS. So I went through libqbx.cpp, commented out every line that made reference to “TTF” font stuff, and remove every reference to SDL_TTF in the compilation process, and I was done.

I haven’t ported the OpenGL stuff to MacOS yet, but I now have Black Annex deployable on Windows, Mac and Linux. I still need to re-visit the Linux pipeline to make sure I’m not requiring the user to jump through unrealistic hoops in regards to dependencies, but the major work is done.

Black Annex will launch on all three platforms simultaneously.

Annexation

At certain times, extra rooms are added to Black Annex during game-play. I actually cut this from the design doc about six months ago, but I was finally able to add it back in by piggy-backing on the system I build for spawning in enemies. It works in a much more complex way than the enemy spawn-in. With enemy spawn, there’s just an AI entity that’s invisible until it hears someone call for help.

When creating a new room, the clipping plane needs to be dramatically modified, and the zBuffer needs to be rebuilt as the room is spawning-in as well (the zBuffer typically only considers walls and static objects once when the level loads, and then never again because they don’t change.). There’s also the issue of “joining” tiles needing to appear to connect old rooms to the new ones, so certain wall tiles need to “listen” for a spawning room, and switch to a different type to connect to the new room.

In the video below, see how the new room actually “connects” to the old room, and a new window appears in one of the offices which wasn’t there before.

Where We At

I’m at a stage with Black Annex at the moment where most of the development time is spent creating content, rather than working on the technical side of things. The features are nailed down (although some still not implemented) and most of the technical hurdles are overcome (except optimisation, there’s always room for more optimisations) so I don’t really get to just play around with code all that much in Black Annex right at this time.

So, one week ago I opened a blank new project and started creating something. This blog is always about the more technical side of what I’m doing, so here’s some video of something totally new.

Maybe one day it’ll turn in to something more.

Framerate++

A lot of the jobs on my to-do list are variations of “Make [x] not run like shit”, so I’ve been spending a lot of time making things run a lot faster, and the overall system requirements for Black Annex a lot lower.

So one of the first things I managed to do was fix an issue paging the video buffer meaning that even when the game was running at 60fps, the screen would only update at 30fps. I didn’t even realize this was happening until I fixed it so now the game is literally running twice as smoothly as it was in the PAXAus AIS demo.

Performance MenuAfter this, I needed to seriously think about getting the game able to “Stretch to full screen”. I mean, I’ve had it working, but the “stretch” was slowing the game down massively so it would drop about 20fps when doing it. I had been just treating the screen like a rectangle and scaling it in a really basic way before blitting it to the computer screen. It was running horrible, so I started working on a way to actually map the screen to a couple of texture-mapped triangles and scale those up to the screen size instead. I ended up getting this working, so the screen is sliced into two triangles, then the two triangles are drawn back onto the screen at a new size to make the game full-screen. This turned out to be almost no slower than drawing the screen at the original size. Dealing in polygons seems to be pretty snappy, it turns out!

When I figured that out, I went back and changed almost every sprite-blitting routine to use texture-mapped triangles instead, and this sped up the game massively, especially if any form of scaling was being used on any sprite. The problem is, there’s no filtering, so I can’t smooth the screen or anything. Scaling square pixels to funny numbers can get some ugly results. I’m going to let the player run the game in any resolution they want, but they’ve got a few options on scaling. They can just go straight-up “Full screen” and just put up with any ugly pixels, or they can “Scale to nearest square pixels” which leaves some black borders at some resolutions (It will scale to 100%, 150%, 200%, 250%, etc. Keeping things fairly nice), and they can turn on “allow 25% extra scaling”, which allows 100%, 125%, 150%, 275%, etc. It’s a little uglier, but it’s up to the player to decide what they’re happy with.

A Pixel ShaderSo that’s done! Although I had at least tried to put some kind of screen filter in, it was just too slow to actually use. During that process, I did actually make a pixel shader for the game, which a lot of people though was pretty cool. It’s just a tilt-shift thing which gives everything a kind-of “Miniature” look. It will just be optional in the game as it runs like trash, but I like leaving things like that in for people to fiddle with.

But THAT lead to me deciding to work on some absolutely massive performance tweaking for the engine. I decided it was about time to actually go ahead and work on “Frame Skip”. “Frame Skip” is where you measure the time it’s taking to render the game’s graphics and, if it takes too long, turn off rendering until the game catches back up again. This results in “choppy” gameplay, instead of “slow motion” game play when things start getting too heavy for the CPU. The good thing about frame skip is that it lets people with computers just a little below requirements still play the game at the same “speed”, they just notice the game looks a tiny bit worse. It’s much less frustrating than having the game run at a snail’s pace.

I made it so Frame Skip comes on gradually, so if the game only slows down only for 1 second, it will give you a little mix of slow-motion and maybe just skip 2 frames or so. This means that if a GUI element takes a little bit too long to populate (Some of them have elements that stream from the hard drive), the GUI animation will still remain smooth instead of just jumping in. If the entire game runs slow, the frameskip will quickly go to full effect and the whole game will just be a bit jumpy.

So that’s great! The game runs way better on this Core Duo Mobility laptop I have for testing, which it was kinda unplayable on before. It still needs some tweaks but the technology is there and working so I’m super happy with that. The next item on my to-do list said “Make Pathfinding faster, a LOT faster” so I decided I really needed to finally wrestle with a* Pathfinding and make it work. Doing this in BASIC is extremely hard because you can not create arrays of arrays, and there’s no pointers either. I had to create a ton of different arrays with a bunch of arbitrary reference keeping arrays between them so they can point back and forth to each other. I also ran into a massive issue calculating the heuristic for my map because I use a horrible skewed coordinate system. A goon by the name of “testsubject” eventually figured out the math for it and I’ve had awesome success now implementing a*.

I uploaded a video showing the game running with the old pathfinding, and then I pull down a menu and change it to a* in real-time and the game instantly returns to 60fps with no frames dropping at all. Have a little read of the video description on youtube if you want a bit more detail on exactly what I’m doing in the video.

So that’s what’s new. Oh, I’m also going to E3! I won’t be exhibiting there or anything, just hanging out and hopefully meeting some cool people. I’ve never been to America before, tweet at me if you want to meet up or something.

Touchy Feely

I spent all of New Year’s Day making the drag and drop mechanics feature-complete for one of the in-game menus.

It’s been amazingly more complicated than expected to implement something that I use in my OS and other games every day without even thinking about it. When you pick up a sprite to drag it somewhere, you have to record such a huge amount of information about what you’re doing and what you might possibly plan to do with that sprite.

fasCn

I use the mouse cursor as an entity which records what it’s carrying, where it started carrying it, where it’s considering throwing it to, what it’s doing with it at the moment, and about fifteen other things. When you release the mouse button, the game has to check so many things regarding “Is this a good place to drop this?” that it’s amazingly easy to leave loop-holes that can be exploited and result in duped items or creatures.

Another important thing is to only check certain “drag and drop” conditions if you actually need to – because you’re looking at doing a huge number of checks on a per-pixel basis, you can’t do that every frame all the time; you need to turn certain features on or off depending on whether they might possibly apply at any given time.

In the end, everything turns out very satisfyingly tactile and you can kinda “throw” objects around between windows in a smoothly rendered way. I like the result so I made a little video about it.

Happy New Year.

Make a Mess

Among some small adjustments in the way particles look (they were accidentally 2x as large as intended) I started testing them by turning off the “reload cooldown” on all the creature weapons.

I made a video, you can look at it if you’d like.

Floor is Lava

So I talked about particles the other day when I pushed the prototype into the game engine and got them working, but I had to do some work to make them really “exist” in the game world. The first thing to do was make sure you couldn’t see them through walls, and I’ve now given them the ability to “collide” with the floor.

As an example, I’ve made “Bullet Casings” eject from guns fired and bounce on the floor before coming to a resting stop and fading away. To do this, I had to make the X axis inertia inherit the direction the creature was facing when he fired the gun to make sure the bullet fires out of the “back” of the gun. On top of this, the particles are told, when they spawn, how high above the floor are they. The floor is defined as the bottom-most pixel of the sprite that emits them.

Using this information, the particle can tell when it has hit the floor, however, because the game camera is skewed isometrically, the floor is angled. If the creature is facing North or East, the floor behind the creature is tilted “downwards”, otherwise it tilts “upwards”. The further the particle has traveled horizontally, the further the floor has skewed.

With much fiddling, bullet casings now bounce along the floor as per the rules of the isometric camera, and any particle can follow the same rules with a simple “CollidesWithFloor” flag.

That was certainly a whole lot of words that probably make no sense out of context, but I’m still pretty happy with the result; I’m wondering if I should make particles bounce off walls now.

The Problem with Particles

So, last post I mentioned that I had finished off the particle emitter system in a prototype environment (literally a blank 2D canvas) but the next job was to take those particle emitters and drop them into the game environment. I had initially through that I would just overlay the particles over the top of the game scene and it wouldn’t be all that bad if you could see particles through walls; in-game walls aren’t taller than people anyway so it should be okay, surely.

But it wasn’t okay, it looked terrible.

In an “Isometric” scene, zSorting (the order things are rendered in so behind/in-front looks right) is done from the top of the screen to the bottom in linear order. In-engine at the moment, only 1 object (a wall tile, a person, a door) can be on 1 tile at a time, so the engine just renders whatever object is on whatever tile starting at the top of the screen down to the bottom. However, there can be 2000 particles on one tile, and incorporating them into the zSorting populator and renderer took some fairly heavy changes to the way it works.

Every particle has to remember what the y-axis of the tile was for the emitter that it came from and then gets inserted into a massive stack in order of y-axis, with up to 2000 other particles sharing the same y-axis. So there’s an array called ParticleSorter(200,2000), the first dimension is the y axis, and the second is the particular particle that’s on that axis. Once all the particles are added to that array stack, the stack is then read from during the zRenderer routine.

It’s not perfect yet; particles need to be given a concept of “floor” for them to land on, but the rendering is working and it seems to be fairly pretty so far. Here’s a video where you can see a fairly early implementation of breakable doors with particle effects.

The Procedural Bits

I’ve been working on lots of little mathematical bits of graphics for UI stuff and also simple in-game animations. You’ve probably seen the “Spawn In” animation where people seem to drop out of the sky, which is all animated procedurally, but I’ve now added a nice little line-graph UI element for a new game system and I’ve just finished the particle emitter system.

I uploaded a video showing the particle emitter system being built from scratch if you’re the sort of person who likes to watch that kind of thing. Also, an irrelevant animated gif.

Some Things Get Done

A productive weekend goes on by and lots of new graphics and some actual engine work get done. I spend almost 50% of my time lately between working on “the game” and talking to people about it. Some days I write emails for hours or introduce myself to people on twitter while looking for other game developers or people in the industry. Getting to know other people doing the same things as me, or people who are generally interested in what I am doing is a very rewarding experience.

Work keeps on chugging along. In the meantime, here’s three pictures of some new stuff.