Premake is a tool that generates build configuration files for different build systems, such as Visual Studio and gmake. It comes in the form of a command-line tool and some okaytutorials and documentation are available. I’ve started using it in my spare-time game engine project.
I chose to try Premake instead of a similar tool called CMake because while CMake looks like it has more features and is more stable than Premake, using it requires learning a custom scripting language1. In Premake you write your build instructions in Lua. I’m not some kind of Lua expert – in fact, when I started on writing my Premake script I found I’d forgotten quite a lot about the language – but I have enough experience with it that I can get things done.
The Premake workflow is pretty simple: you write a Lua script which specifies what files are part of your project, the include directories, library directories and linker inputs, and so on. Premake imbibes your script and spits out .vsxproj files, makefiles or whatever your target build system uses.
For example, I have a file named ‘premake5.lua’ in the root directory of my project. It looks a bit like this. When I run Premake like so:
C:\Made\Up\Path> premake5 vs2017
It automatically finds the Lua file (because of its special name) and generates a .sln file and a bunch of .vcxproj files for my target build system (vs2017 – Visual Studio 2017). The solution is set up the way I specified, with different configurations (Debug/Release), platforms (x86/x86-64), compiler switches and so on.
Premake solves a number of problems I have:
Visual Studio’s GUI for modifying build settings is impressively bad, and the format for .sln and .vcxproj files is eye-destroying XML, so if you want to change something there’s a lot that can go wrong. With Premake, I make changes to one easy-to-read Lua script, run the tool, and can get straight back to the real work.
As a result of the above problem, changing the way my solution/project is set up is a massive pain in the butt. With Premake it’s so much easier to move files around in folders, split things apart into sub-projects, etc.
Eventually I will be going cross-platform with this project. With Premake I don’t have to set up ten different kinds of build configurations for every different build system or IDE and I don’t have to remember all the differences between the compiler and linker options on each toolchain. People working on the project on different systems can just run Premake and get going. At the moment I’m only using one build system and not taking advantage of the cross-platform capabilities of Premake, but I still make gains in that I no longer have to think about the settings of my various projects because Premake abstracts all that away.
Using Premake it was pretty easy to do the long-overdue work of reorganizing files in my mess of a project. I wish I’d known enough about Premake to use it from the beginning and in past projects. Oh well2!
In the game engine I’m working on, an Entity (or one of an Entity’s components) can sometimes be in a state where it needs to have some extra processing done. Rather than having a branch in Entity::Update, we can just store a pointer to the Entity/Component in a container along with all the others that meet the same criteria. This is existence-based processing.
For example, RenderComponents can be in a state where they own a fixture (shape) in the physics world which needs to be rotated to face the camera before rendering happens (don’t ask me why at the moment; it’s complicated). We refer to these special RenderComponents as ‘Flat Sprites’, and keep track of all of them in a std::vector of pointers:
std::vector<RenderComponent*>mFlatSprites;
It’s safe to do this because RenderComponent instances are dynamically allocated and do not move around in memory.
Just before rendering, we call RenderComponent::UpdateFlatSprite on each of the instances pointed to by the pointers in the container, rotating their fixture to face the camera. When adding or removing a RenderComponent from the set of Flat Sprites, we can use std::find to check for whether the pointer we’re trying to add or remove is already in the array:
// flatSprite is of type RenderComponent*constautoit=std::find(mFlatSprites.begin(),mFlatSprites.end(),flatSprite);if(it!=mFlatSprites.end())// the pointer was found in mFlatSprites
Easy! Yet, there’s something a little bit smelly about all this: we’re using pointers instead of references.
(In case you’re unaware, references in C++ are just pointers with special semantics: they cannot (without coercion) point to null – they must always point to an actual instance. They also behave syntactically like values, so you can act upon them as you would a normal instance using the . operator instead of the -> operator (and dereference operator *) that you have to use with normal pointers.)
The problem with references is that this doesn’t compile:
std::vector<RenderComponent&>mFlatSprites;
This is (partly) because references cannot be default-initialised. That is, a reference must always be assigned a value in the place where it is initialised.
But I want an array of references, because I want to represent the idea that elements can never be null. Fortunately, I can do this by writing a structure that ‘wraps’ a reference, and creating an array of that. I don’t even have to write that wrapper myself, because it’s already a template in the standard library: std::reference_wrapper.
Stay with me now, because I’m closing in on the point of this post. This solution creates a new problem: what should the following code do?
// flatSprite is of type RenderComponent&constautoit=std::find(mFlatSprites.begin(),mFlatSprites.end(),flatSprite);
Should it compare the instance each element in the array refers to with that referred-to by flatSprite? Or should it compare the values of the underlying pointers involved – the addresses at which the RenderComponents reside – which is what I want?
Turns out the implementers of std::reference_wrapper didn’t want to make that call for you, which is a good thing. The code does not compile because bool operator==(const std::reference_wrapper<RenderComponent>, const RenderComponent&) is undefined.
I could just write a definition for that operator myself and make it compare the addresses, but I decided not to. There may be cases down the line where I want that operator to compare the values rather than the addresses, and I want to make it explicit what I’m trying to do here. Finally we’re back to that first line of code.
constautoit=FindByAddress(specialEntities,specialEntity);// except in this case it's actually:constautoit=FindByAddress(mFlatSprites,flatSprite);
In fact we could make it even more generic and not constrain it to only working with std::vectors by having it take two iterators, a begin and an end. Or, in the bright future, a range. I’ll leave that as an exercise for the reader.
I’ll also leave as an exercise for the reader the task of pointing out mistakes I’ve made, or why an array of references is a bad idea, or generally letting me know what you think of the code I’ve shared here either on Twitter or in the comments below. Can I really refer to my cakes and eat them?
Shout out to Elias Daler for his recent dev log, which alerted me to the existence of reference wrappers and started all of this.
Your friends die. You die. The planet dies. You don’t exactly ‘win the game’ at the end of Halo: Reach’s single-player campaign.
Watching the latest Star Wars film, Rogue One, I was struck by the similarity of its finale to that of Reach1. In Rogue One (spoilers, by the way), the heroes’ self-sacrifice allows Princess Leia to escape with the plans for the Death Star – the film ends almost immediately before A New Hope begins. In Reach, the heroes’ self-sacrifice allows the Pillar of Autumn to escape, with Cortana and the Master Chief aboard, and therefore find the first Halo ring. The last lines of Reach’s script are the first of Halo: Combat Evolved:
Captain Keyes: Cortana, all I need to know is did we lose them?
Cortana: I think we both know the answer to that.
Stories which deploy this conceit have to be prequels – at least, I can’t think of any examples which aren’t – because the only way you can get away with such a bleak ending is if the audience knows how it all works out okay in the end.
I was recently in San Francisco for five-ish days to attend Game Developer’s Conference. It was my first time in an American city – previous trips to the US have taken me only to skiing resorts. We spent most of our time in and around the conference, and only really had time for a bit of proper sightseeing on Saturday.
It’s a nice place, but very different to what I’m used to. In Scotland we barely have any tall buildings, so when I’m surrounded on all sides by skyscrapers I felt a little bit like I was going to fall into the sky, which is the most country-bumpkin thing about myself I’ve ever admitted (on this blog).
Game Developers Conference is an annual gathering of people involved in the game industry from all over the world. It takes place in and around the Moscone Centre in San Francisco, California. I’ve always wanted to go, but it’s expensive to get out there and get accommodation and very expensive for the kind of pass that gets you into lots of interesting talks and events, which is one of the main reasons to go.
Very luckily, and to my great surprise, this year I got to go for free (for reasons I’ll explain). I was in San Francisco from Tuesday 28th February until Sunday 5th March and have spent most of my free time since I got back recovering. Here are some words about my trip.