I’ve always been more interested in how game engines work than I was about actually using them. These days you simply can’t make a market-ready game without one. They’re these big machines which have all these different systems working together which on their own are these complex beasts which may even have their own sub-systems. At first, when you look at a game engine from afar it can be a bit tricky to list its parts. You might think to yourself “Well it has a rendering system that’s for sure”, and then you might go “Ah and it must have to manage the levels and states of the games some how, and of course player input - duh!”. But then you come closer and all of a sudden we have this lasagna of layers of systems upon systems and before you know it you’re overwhelmed by the sheer complexity and so you walk away and lie down somewhere quiet and safe.

So with all that being said I’m going to start and try to write my own game engine. Though I might have painted a scary picture previously, game engines are really whatever you make them. They can be simple with just a few systems which cover your needs precisely. And if all goes awry then there’s no shame in simply giving up and moving on. The mere action of attempting to write your own game engine can be an extremely rewarding and learning-rich excersise.

Another thing that’s worth mentioning before kicking off this series of blog posts is that I will be refraining from the use of an IDE for as long as possible. The reason for this is fairly simple: I feel that Visual Studio has held my hand for the vast majority of my experience a programmer. I want to go it alone this time around, using the G++ compiler and text-editing with Visual Code.

Let’s Make it happen

Make is a build automation tool which uses a project’s makefile to compile and link a project’s source. To compile with the G++ you might enter the following command: g++ code.cpp -o target. This is fine if we’re writing a very small application, we just need to pass the compiler all the translation units we want our program to comprise of and it will spit out a lovely executable for us. But what if we have more than a few source files? What if we are nice, neat, and tidy and organise our project in to multiple directories? What if we want to compile only the code that’s been modified? Well then you better find yourself a build-automation tool like Make! Here’s a very rough list of steps to get a project set up:

  • Download and install MinGW 64 for a Windows distrobution of GCC
  • (Optional) Add MinGW’s /bin folder to the PATH system environment variable
  • Download the GNU Make binary files and dependencies
  • Add make.exe, libiconv2.dll, libintl3.dll to MinGW’s /bin folder
  • Download and install Visual Code
  • Make a new folder for the project somewhere and open it up in Visual Code
  • Create the following folders within the project’s root:
    • /bin - Executable and any .DLLs
    • /include - Project and 3rd party header files
    • /lib - The libraries we’re going to link to our project
    • /obj - Object files (compiled code) and dependency files (generated via our makefile to help detect when we should recompile source)
    • /res - Any project resources like shaders, textures, meshes, etc
    • /src - The good stuff: our source code to be compiled

Now we have an empty project, we should create our makefile! Writing a makefile deserves a whole article of its own, and if you’ve never had much experience with build automation tools it can take quite a bit of research to get the hang of it. Luckily I’ve already done said research and have prepared my own! It’s not perfect, but it seems to do the job in a relatively flexible manner:

OUTNAME		:= alvere
CC		:= g++
LIBDIR		:= lib
INCDIR		:= include
SRCDIR		:= src
OUTDIR		:= bin
BUILDDIR	:= obj
SRCEXT		:= cpp
DEPEXT		:= d
OBJEXT		:= o
LIB             := opengl32 glew32 glfw3dll

###########################################
# FURTHER MODIFICATION MAY BREAK MAKEFILE #
###########################################

OUTPATH		:= $(OUTDIR)/$(OUTNAME)
LIBDIRP		:= $(foreach d, $(LIBDIR), -L$d)
INCDIRP		:= $(foreach d, $(INCDIR), -I$d)
SRCS		:= $(wildcard $(SRCDIR)/*.cpp) $(wildcard $(SRCDIR)/*/*.cpp)
OBJS		:= $(patsubst $(SRCDIR)/%,$(BUILDDIR)/%,$(SRCS:.$(SRCEXT)=.$(OBJEXT)))
DEPS		:= $(OBJS:.o=.d)
LIBP		:= $(foreach l, $(LIB), -l$l)
ECHO		:= echo

# Default make rule
all: $(OUTNAME)

# Build main executable
$(OUTNAME): $(OBJS)
	@$(ECHO) Linking object files...
	@$(CC) $(LIBDIRP) -o $(OUTDIR)/$@ $^ $(LIBP)

# Build object files from source
$(BUILDDIR)/%.$(OBJEXT): $(SRCDIR)/%.$(SRCEXT)
	@if not exist "$(@D)" mkdir "$(@D)"
	@$(ECHO) Compiling: "$<" ...
	@$(CC) $(INCDIRP) -c $< -o $@

# Generate dependency files
$(BUILDDIR)/%.$(DEPEXT): $(SRCDIR)/%.$(SRCEXT)
	@if not exist "$(@D)" mkdir "$(@D)"
	@$(ECHO) Generating dependency file: "$@" ...
	@$(CPP) $(CFLAGS) $(INCDIRP) $< -MM -MT $(@:.$(DEPEXT)=.$(OBJEXT)) >$@

# Clean build; delete BUILD directory contents and main executable
.PHONY: clean
clean:
	@$(ECHO) Deleting "$(BUILDDIR)/*.$(OBJEXT)", "$(BUILDDIR)/*.$(DEPEXT)", \
	and main executable: "$(OUTPATH).exe" ...
	@del /s /q "$(BUILDDIR)\*.$(OBJEXT)" 2>NUL
	@del /s /q "$(BUILDDIR)\*.$(DEPEXT)" 2>NUL
	@del /q "$(OUTDIR)\$(OUTNAME).exe" 2>NUL

# Clean and then re-make
rebuild:
	@make clean
	@make all

# Include dependency files
-include $(DEPS)

# Run the main executable
run:
	if not exist $(OUTPATH).exe make all
	@$(ECHO) Running ($(OUTPATH).exe) ...
	@$(OUTPATH).exe

.PHONY: run rebuild

So there it is. If you’d like to learn how to write your own makefiles, I’m afraid you’re reading the wrong article. If you run in to any issues related to this makefile then feel free to get in touch. To actually use this makefile we need to open up the command prompt and navigate to the project directory root where the makefile should be. Here when we run Make we can pass it one of the rule name’s that have been defined. For example to completely recompile all project source code and then run the compiled code we can type make rebuild all.