How to build video games for Windows using Crystal
Crystal Programming Language does not yet support Windows, but the progress is ongoing, and programs that use isolated parts of the standard library can be properly cross-compiled. Note that, even though there are multiple mentions of Windows Subsystem for Linux here, it is used only for one of the build steps. The resulting binaries run natively on Windows, no strings attached.
Basic Crystal setup¶
You need a POSIX-compatible system that Crystal officially supports (like Linux) and also Windows. If you are already on Windows 10, the obvious choice is Bash on Ubuntu on Windows. If on a different OS, use a virtual machine with Windows — VirtualBox is a good choice. Microsoft offers virtual machine images for free.
Get the two systems running at the same time and set up a shared/synchronized (initially empty) folder between them. If your host is Windows, an easy choice would be "C:\crystal-windows", then the Windows Subsystem for Linux can see it as "/mnt/c/crystal-windows".
First, set up the folder in Bash. Save the path to it in a variable for later reference. Try to avoid whitespace in the path to the directory.
Keep in mind that if you need to open a new terminal window, you'll need to reapply this variable.
bashfolder=/full/path/to/crystal-windows cd "$folder"
Now that that's done, let's try a basic example.
bashcd "$folder" echo 'puts "Hello, World!"' > hello.cr crystal/bin/crystal run hello.cr
The compiler itself does not yet run on Windows, so in order to run this example on Windows later on, we need to cross-compile an object file first.
bashcrystal/bin/crystal build hello.cr --cross-compile --target x86_64-pc-windows-msvc
bashcd "$folder" wget https://crystal-lang.s3.amazonaws.com/windows/gc/gc-7.6.0.zip wget https://crystal-lang.s3.amazonaws.com/windows/pcre/pcre-8.40.zip for f in *.zip; do unzip -j "$f" && rm "$f"; done # -j to ignore subfolders
Now we move on to Windows.
Install Visual C++ 2015 Build Tools.
Open "Visual C++ 2015 x64 Native Build Tools Command Prompt" in the Start menu. We will be using this shell from now on. Find the same folder that you were working on in Bash and change to it here. Ensure that there is no whitespace in the path to the directory. Set a variable for further reference. Keep in mind that if you need to open a new terminal window (it needs to be the one from "Visual C++..."), you'll need to reapply this variable.
cmdset folder=C:\full\path\to\crystal-windows cd %folder%
Now we can build and run the program from earlier because we've stored the object file here.
cmdlink hello.o libevent.lib gc.lib pcre.lib kernel32.lib ws2_32.lib user32.lib advapi32.lib /NODEFAULTLIB:msvcrt hello.exe
So the two-step process of compiling on a POSIX system and linking on Windows allows us to run Crystal programs on Windows. At this early stage many programs will fail. But, as it turns out, CrSFML already works well, because it's an external framework and you rarely need to interact with Crystal's standard library while using it. So, it's finally time for some real game dev!
CrSFML is Crystal bindings to Simple and Fast Multimedia Library, which is most often used to make cross-platform games.
Remember, we're still using two systems at once: bash on a POSIX system and cmd on Windows.
Start by downloading CrSFML precompiled sources:
bashcd "$folder" git clone --branch sources https://github.com/oprypin/crsfml
Going back to Windows:
Download CMake Latest Release → Windows x64 Installer and install it. Make sure to select "Add CMake to the system PATH for the current user".
To apply this
PATH change, close and reopen the Visual C++ 2015 x64 Native Build Tools Command Prompt window. That means reapplying the
folder variable (see below).
Download SFML Latest stable version → Visual C++ 14 (2015) - 64-bit. Extract it to that same folder we've been using, but rename it from something like "SFML-2.4.2" to just "sfml" for easier reference. Get the full path to it in a variable (it is important for CMake to use forward slashes).
cmdset folder=C:\full\path\to\crystal-windows set sfml=C:/full/path/to/crystal-windows/sfml
The next step is compiling the intermediate C bindings that connect CrSFML to SFML:
cmdcd %folder%\crsfml\voidcsfml cmake . -DSFML_ROOT=%sfml% -DCMAKE_MODULE_PATH=%sfml%/cmake/Modules -G "Visual Studio 14 2015 Win64" cmake --build . --config Release
And we'll continue the unfortunate pattern of just dropping all compiled libraries into the same folder, it's easier this way.
cmdcd %folder% xcopy crsfml\voidcsfml\Release\* .
Sigh, let's even copy all the CrSFML examples and resources into that poor folder.
cmdcd %folder% xcopy /S crsfml\examples\* .
Now we're ready for the two-step cross-compilation dance again.
bashcd "$folder" crystal/bin/crystal build snakes.cr --cross-compile --target x86_64-pc-windows-msvc --no-debug
cmdcd %folder% link snakes.o libevent.lib gc.lib pcre.lib kernel32.lib ws2_32.lib user32.lib advapi32.lib voidcsfml-graphics.lib voidcsfml-window.lib voidcsfml-system.lib /NODEFAULTLIB:msvcrt snakes.exe
With any luck, now you should be playing a two-player snake game! You can try out other examples just as well, though not all of them will work.
By the way, all these demos do work on Windows.
If you're using Windows Subsystem for Linux, you can set up a script to initiate both compilation steps from Windows. Create the file build.cmd:
build.cmdset name=%1 bash -c "crystal/bin/crystal build %name%.cr --cross-compile --target x86_64-pc-windows-msvc --no-debug" link %name%.o libevent.lib gc.lib pcre.lib kernel32.lib ws2_32.lib user32.lib advapi32.lib voidcsfml-*.lib /NODEFAULTLIB:msvcrt
cmdbuild transformable & transformable
Refer to the documentation to learn CrSFML.