Hands-On Game Development with WebAssembly
上QQ阅读APP看书,第一时间看更新

Using SDL in WebAssembly

At this point, I could roll my own system for interaction between the WebAssembly module and the JavaScript WebGL library. That would involve using a function table to call the JavaScript WebGL functions from within C++. Luckily for us, the Emscripten team has done most of this work. They have created a port of a popular 2D C++ graphics library that does this for us. SDL is a 2D graphics Application Programming Interface (API) built on top of OpenGL in most implementations. There is an Emscripten port that is used to help us render our 2D graphics on top of WebGL. If you would like to know what other libraries have been integrated into Emscripten, use the following emcc command:

emcc --show-ports

If you run this command, you will notice that several different SDL libraries are displayed. These include SDL2, SDL2_image, SDL2_gfx, SDL2_ttf, and SDL2_net. SDL was created with a modular design to allow the user to include only the parts of SDL that they need, allowing the core SDL library to remain small. This is very helpful if your goal is to create a web game where download size is limited.

The first thing we will do is get familiar with SDL by creating a simple "Hello World" application that writes some text to the HTML5 canvas element. To do this, we will need to include two of the Emscripten libraries listed when we ran the emcc --show-ports command. We will need to add the core SDL library to our Emscripten compiled with the USE_SDL=2, flag, and we will need to add the SDL TrueType font library by adding the USE_SDL_TTF=2 flag.

The .c source code that will display a message such as "HELLO SDL!" inside an HTML canvas is relatively simple:

#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
#include <emscripten.h>
#include <stdio.h>

#define MESSAGE "HELLO SDL!"
#define FONT_SIZE 16
#define FONT_FILE "font/Roboto-Black.ttf"

int main() {
SDL_Window *window;
SDL_Renderer *renderer;

SDL_Rect dest = {.x = 160, .y = 100, .w = 0, .h = 0 };

TTF_Font *font;
SDL_Texture* texture;

SDL_Init( SDL_INIT_VIDEO );
TTF_Init();

SDL_CreateWindowAndRenderer( 320, 200, 0, &window, &renderer );

SDL_SetRenderDrawColor( renderer, 0, 0, 0, 255 );
SDL_RenderClear( renderer );

font = TTF_OpenFont( FONT_FILE, FONT_SIZE );

SDL_Color font_color = {255, 255, 255, 255 }; // WHITE COLOR
SDL_Surface *temp_surface = TTF_RenderText_Blended( font,
MESSAGE,
font_color );

texture = SDL_CreateTextureFromSurface( renderer, temp_surface );

SDL_FreeSurface( temp_surface );
SDL_QueryTexture( texture,
NULL, NULL,
&dest.w, &dest.h ); // query the width and
height

dest.x -= dest.w / 2;
dest.y -= dest.h / 2;

SDL_RenderCopy( renderer, texture, NULL, &dest );
SDL_RenderPresent( renderer );

return EXIT_SUCCESS;
}

Let me walk you through exactly what is going on here. The first four lines of code are the SDL header files, as well as the Emscripten header file:

#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
#include <emscripten.h>
#include <stdio.h>

Following this, there are three preprocessor defines. If we wanted to change the message or font size quickly, we would modify these first two lines. The third define is a little less clear. We have something called FONT_FILE, which is a string that appears to be a filesystem location. That is a little bit weird, because WebAssembly does not have access to the local filesystem. To give the WebAssembly module access to the TrueType font file in the fonts directory, we will use the --preload-file flag when we compile the WASM file. This will generate a .data file from the contents of the font directory. The web browser loads this data file into the virtual filesystem, which is accessed by the WebAssembly module. That means that the C code that we are writing will have access to this file as if it were accessing it inside a local filesystem:

#define MESSAGE "HELLO SDL!"
#define FONT_SIZE 16
#define FONT_FILE "font/Roboto-Black.ttf"