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.
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.
LikeLike
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.
LikeLike
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.
LikeLike
Thanks! For a beginner like me, this was very useful. I especially like how neatly each step in the tutorial merged into the next.
LikeLike
@0864 Thanks!
LikeLike
Thanks for the great tutorial! The way you structured your code was so clear, and it was easy to follow along.
LikeLike
@klin Thank you so much!
LikeLike
Hi Steve, Just wanted to say THANKS! for this nice tutorial. Please keep em coming 🙂
LikeLike
Thank you so much! I really need to find time to work on more posts. Thank you for your support, greatly appreciated!
LikeLike