SDL 2.0 Tutorial-03: Metasprites

So last week we were able to generate a collection of textures and draw them to the screen. In terms of memory this isn’t very efficient. This week we will be creating one texture from a meta SDL_Surface* . This will be my last part of this basic tutorial, if there are any topics you would like discussed please email me at “info@stephenmeier.net”. If you do occasionally read these posts please let me know which posts you find interesting!


/* Stephen's Tutorials (https://stephenmeier.net/)
gcc main.c -o game.exe -I./include -L./lib -lSDL2main -lSDL2
running on gcc 4.8.1, SDL 2.0.1
*/
//----------------------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>

#include "game.h"

//----------------------------------------------------------------------
int main(int argc, char* argv[]) {

	Game.init();

	int x = Game.screen.w/2-8, y = Game.screen.h/2-8;
	SDL_Rect rect = {0, 0, 8*2, 8*2};

	int i, j;
	int** grid = (int**) malloc(sizeof(int*)*2);
	for(j=0; j<2; j++) {
		grid[j] = (int*) malloc(sizeof(int)*2);
		for(i=0; i<2; i++) {
			grid[j][i] = 2;
		}
	}

	SDL_Texture* spr = Game.gfx.buildSpr(2, 2, grid);

	SDL_Event event;
	while(Game.running) {
		while(SDL_PollEvent(&event)) {
			switch(event.type) {
				case SDL_QUIT: {
					Game.running = SDL_FALSE;
				} break;
			}
		}

		SDL_RenderClear(Game.screen.renderer);

		rect.x = 0+x, rect.y = 0+y;
		Game.screen.drawSpr(spr, &rect);

		SDL_RenderPresent(Game.screen.renderer);
	}

	Game.gfx.destroySpr(spr);

	Game.quit();

	return 0;
}

// Stephen's Tutorials (https://stephenmeier.net/)
//----------------------------------------------------------------------
#ifndef _GAME_H_
#define _GAME_H_

//----------------------------------------------------------------------
#define SCREEN_w 640
#define SCREEN_H 480
#define SCREEN_NAME "Prototype"
#define SCREEN_SCALE 1
#define SDL_MAIN_HANDLED

//----------------------------------------------------------------------
#include "SDL2/SDL.h"

//----------------------------------------------------------------------
void game_init(void);
void game_quit(void);

SDL_Texture* gfx_buildSpr(int w, int h, int** grid);
void gfx_destroySpr(SDL_Texture* spr);

void screen_drawSpr(SDL_Texture* spr, SDL_Rect* rect);

struct {
	// define "attributes"
	SDL_bool running;

	struct {
		unsigned int w;
		unsigned int h;
		const char* name;
		SDL_Window* window;
		SDL_Renderer* renderer;
		void (*drawSpr)(SDL_Texture* spr, SDL_Rect* rect);
	} screen;

	struct {
		unsigned int n;
		SDL_Surface** spritesheet;
		SDL_Texture* (*buildSpr)(int w, int h, int** grid);
		void (*destroySpr)(SDL_Texture* spr);
	} gfx;

	// define "methods"
	void (*init)(void);
	void (*quit)(void);
} Game = {
	SDL_FALSE,
	{
		SCREEN_SCALE*SCREEN_w,
		SCREEN_SCALE*SCREEN_H,
		SCREEN_NAME,
		NULL, NULL,
		screen_drawSpr
	},
	{
		0,
		NULL,
		gfx_buildSpr,
		gfx_destroySpr
	},
	game_init,
	game_quit
};

//----------------------------------------------------------------------
void game_init(void) {
	if(SDL_Init(SDL_INIT_EVERYTHING)!=0) {
		printf("SDL error -> %sn", SDL_GetError());
		exit(1);
	}

	unsigned int w = Game.screen.w;
	unsigned int h = Game.screen.h;
	const char* name = Game.screen.name;

	Game.screen.window = SDL_CreateWindow(
		name,
		SDL_WINDOWPOS_CENTERED,
		SDL_WINDOWPOS_CENTERED,
		w, h, 0
	);

	Game.screen.renderer = SDL_CreateRenderer(
		Game.screen.window, -1,
		SDL_RENDERER_ACCELERATED|SDL_RENDERER_PRESENTVSYNC
	);

	SDL_Surface* surface = SDL_LoadBMP("spritesheet.bmp");
	int n = ((surface->w/8)*(surface->h/8)+1);

	Game.gfx.n = n;
	Game.gfx.spritesheet = (SDL_Surface**) malloc(sizeof(SDL_Surface*)*n);

	int i, x, y;
	SDL_Rect rect = {0, 0, 8, 8};
	for(i=0; i<n; i++) {
		Game.gfx.spritesheet[i] = SDL_CreateRGBSurface(0, 8, 8, 24, 0x00, 0x00, 0x00, 0x00);
		SDL_SetColorKey(Game.gfx.spritesheet[i], 1, 0xFF00FF);
		SDL_FillRect(Game.gfx.spritesheet[i], 0, 0xFF00FF);
		if(i!=0) {
			x = (i-1)%(surface->w/8);
			y = (i-x)/(surface->w/8);
			rect.x = x*8;
			rect.y = y*8;
			SDL_BlitSurface(surface, &rect, Game.gfx.spritesheet[i], NULL);
		}
	}

	SDL_FreeSurface(surface);

	Game.running = SDL_TRUE;
}

//----------------------------------------------------------------------
void game_quit(void) {
	int i;
	for(i=0; i<Game.gfx.n; i++)
		SDL_FreeSurface(Game.gfx.spritesheet[i]);
	free(Game.gfx.spritesheet);
	Game.gfx.spritesheet = NULL;

	SDL_DestroyRenderer(Game.screen.renderer);
	Game.screen.renderer = NULL;

	SDL_DestroyWindow(Game.screen.window);
	Game.screen.window = NULL;

	SDL_Quit();
}

//----------------------------------------------------------------------
SDL_Texture* gfx_buildSpr(int w, int h, int** grid) {
	SDL_Surface* surface = SDL_CreateRGBSurface(0, 8*w, 8*h, 24, 0x00, 0x00, 0x00, 0x00);
	SDL_SetColorKey(surface, 1, 0xFF00FF);
	SDL_FillRect(surface, 0, 0xFF00FF);

	int i, j;
	SDL_Rect rect = {0, 0, 8, 8};
	for(j=0; j<h; j++) {
		for(i=0; i<w; i++) {
			rect.x = i*8;
			rect.y = j*8;
			SDL_BlitSurface(Game.gfx.spritesheet[grid[j][i]], NULL, surface, &rect);
		}
	}

	SDL_Texture* tex = SDL_CreateTextureFromSurface(Game.screen.renderer, surface);
	SDL_FreeSurface(surface);

	return tex;
}

//----------------------------------------------------------------------
void gfx_destroySpr(SDL_Texture* spr) {
	SDL_DestroyTexture(spr);
}

//----------------------------------------------------------------------
void screen_drawSpr(SDL_Texture* spr, SDL_Rect* rect) {
	SDL_RenderCopy(Game.screen.renderer, spr, NULL, rect);
}

#endif

There are many ways to structure game logic, and for a simple 2D game being written in C it comes down to mostly personal preference.

9 thoughts on “SDL 2.0 Tutorial-03: Metasprites

  1. BionicWave

    I think you forgot to add functions to the functionpointers, like:
    Game.gfx.buildSpr = gfx_buildSpr;
    Game.gfx.destroySpr = gfx_destroySpr;
    Game.screen.drawSpr = screen_drawSpr;

    Without these lines in game_init(void) i can´t run the program successfully. It just crashes on the first use of the functions.

    Like

    Reply
  2. Stephen Meier Post author

    The function pointers are being set on line 60/61 of the second code block but I realized after posting this that styling the code that way may only work with GCC (i.e. the compiler I was using at the time). So that is worth noting.

    Like

    Reply
  3. BionicWave

    I´m sorry, Stephen. Didn´t see the lines you mentioned. It works for me for the “buildSpr” and “destroySpr” functions, but i still have to manually assign the function pointer for the “Game.screen.drawSpr” function in game_init().
    I think this was the initial problem. I just played with the code and placed all three functions into init without realising that only the drawSpr function was never assigned in your code ( at least i couldn´t find the assignment on this website).

    But thanks for your code. Very easy to follow and thus easy to learn from.

    Like

    Reply
  4. 0864

    Thanks! For a beginner like me, this was very useful. I especially like how neatly each step in the tutorial merged into the next.

    Like

    Reply
    1. Stephen Meier Post author

      Thank you so much! I really need to find time to work on more posts. Thank you for your support, greatly appreciated!

      Like

      Reply

Leave a comment