好吧,我不确定这是为了代码审查还是为了这个网站,但它有一个问题,所以我想我在正确的地方。我试图用C和SDL创建一个游戏。下面是代码(解释如下):
//Includes
#include <stdio.h>
#include <stdlib.h>
#include <SDL.h>
#include <SDL_image.h>
#include <SDL_ttf.h>
#include <SDL2_gfxPrimitives.h>
//Create some global variables(is there some other way to do that?)
SDL_Window *screen;
SDL_Renderer *renderer;
SDL_Texture *tardis;
SDL_Surface *tardis_surface;
SDL_Texture *gallifrey;
SDL_Surface *gallifrey_surface;
SDL_Surface *pillar_surface;
SDL_Rect tardis_dest;
//Some variables I want to keep throughout the game
float speed = 0;
int pillars = 0;
int pause = 0;
//Those two could change in the future
int WIDTH = 640;
int HEIGHT = 480;
//Define a struct to express the pillars in a better way
typedef struct{
SDL_Rect pillar_up;
SDL_Rect pillar_down;
SDL_Texture *pillar_texture;
}pillar;
//OK, that might be silly, but I create a list of pointers so
//that I can keep track of them afterwards. Is that bad?
pillar *list_dest[4];
//Generate a pillar
void create_pillar(int pillars_no, int xpos)
{
//Generate random height
int height = rand() % 200;
if(height < 50)
height += 50;
pillar pillar_dest;
//Set the coordinates for the upper pillar
pillar_dest.pillar_up.x = xpos;
pillar_dest.pillar_up.y = 0;
pillar_dest.pillar_up.w = 70;
pillar_dest.pillar_up.h = HEIGHT - (height + 180);
//Set the coordinates for the pillar on the ground
pillar_dest.pillar_down.x = xpos;
pillar_dest.pillar_down.y = HEIGHT - height;
pillar_dest.pillar_down.w = 70;
pillar_dest.pillar_down.h = 200;
//Set the pillar texture
pillar_dest.pillar_texture = SDL_CreateTextureFromSurface(
renderer, pillar_surface);
//Allocate space for the pillar and save the address
//in the list_dest
pillar *pillar_destp = malloc(sizeof(pillar_dest));
*pillar_destp = pillar_dest;
list_dest[pillars_no % 3] = pillar_destp;
}
//Check if the Tardis collides with a pillar
int check_collision(SDL_Rect A, SDL_Rect B)
{
//The sides of the rectangles
int leftA, leftB;
int rightA, rightB;
int topA, topB;
int bottomA, bottomB;
//Calculate the sides of rect A
leftA = A.x;
rightA = A.x + A.w;
topA = A.y;
bottomA = A.y + A.h;
//Calculate the sides of rect B
leftB = B.x;
rightB = B.x + B.w;
topB = B.y;
bottomB = B.y + B.h;
//If any of the sides from A are outside of B
if(bottomA < topB)
{
return 0;
}
if(topA > bottomB)
{
return 0;
}
if(rightA < leftB)
{
return 0;
}
if(leftA > rightB)
{
return 0;
}
//Else
return 1;
}
//Render the pillars(shitty function name, whatever...)
void draw_ground()
{
//I always create 3 pillars, no more
if(pillars < 3)
{
create_pillar(pillars, WIDTH + pillars * 240);
pillars += 1;
}
else
{
int i;
for(i = 0; i < 3; i++)
{
//Free the previous pillars
pillar *current_pillar;
current_pillar = list_dest[i];
if(current_pillar->pillar_up.x < -70)
{
free(current_pillar->pillar_texture);
free(current_pillar);
create_pillar(pillars, WIDTH);
pillars += 1;
}
else
{
//Check if the Tardis has collided with a pillar and
//display a "game over" message.
if(check_collision(current_pillar->pillar_up, tardis_dest) ||
check_collision(current_pillar->pillar_down, tardis_dest))
{
SDL_Rect target;
target.x = (WIDTH / 2) - 100;
target.y = (HEIGHT / 2) - 50;
target.w = 200;
target.h = 100;
TTF_Font *font;
font = TTF_OpenFont("DALEK.ttf", 40);
SDL_Surface *text;
SDL_Color text_color = {255, 255, 0};
text = TTF_RenderText_Solid(font,
"GAME OVER",
text_color);
SDL_Texture *game_over;
game_over = SDL_CreateTextureFromSurface(renderer, text);
SDL_RenderCopy(renderer, game_over, NULL, &target);
pause = 1;
int j;
for(j = 0; j < 3; j++)
{
free(list_dest[j]);
}
pillars = 0;
break;
}
//Render the pillars
double angle = 180.0;
current_pillar->pillar_up.x -= 10;
current_pillar->pillar_down.x -= 10;
SDL_RenderCopyEx(renderer, current_pillar->pillar_texture,
NULL, ¤t_pillar->pillar_up, angle = 0.0,
NULL, SDL_FLIP_VERTICAL);
SDL_RenderCopy(renderer, current_pillar->pillar_texture,
NULL, ¤t_pillar->pillar_down);
}
}
}
}
//Render the pillar counter
void draw_counter()
{
SDL_Rect counter;
counter.x = WIDTH - 50;
counter.y = 10;
counter.w = 40;
counter.h = 50;
TTF_Font *font;
font = TTF_OpenFont("DALEK.ttf", 40);
SDL_Surface *count;
SDL_Color text_color = {0, 255, 255};
char count_text[3];
if(pillars >= 3)
sprintf(count_text, "%d", pillars - 3);
else
sprintf(count_text, "%d", pillars);
count = TTF_RenderText_Solid(font,
count_text,
text_color);
SDL_Texture *pillar_counter;
pillar_counter = SDL_CreateTextureFromSurface(renderer, count);
SDL_RenderCopy(renderer, pillar_counter, NULL, &counter);
//This is the last function of the game loop, so I suppose it's a good
//place for the SDL_RenderPresent
SDL_RenderPresent(renderer);
}
//Load the images used in the game
void load_images()
{
tardis_surface = IMG_Load("files/tardis.bmp");
gallifrey_surface = IMG_Load("files/gallifrey.bmp");
pillar_surface = IMG_Load("files/pipe.bmp");
}
//Create the background and the Tardis in it's initial place
void create_sprites()
{
//Create area for the TARDIS
tardis_dest.x=50;
tardis_dest.y=100;
tardis_dest.w=50;
tardis_dest.h=80;
//Color key the TARDIS and create texture
Uint32 colorkey = SDL_MapRGB(tardis_surface->format,0,0,0);
//SDL_DisplayFormat(tardis_surface);
SDL_SetColorKey(tardis_surface, SDL_SRCCOLORKEY, colorkey); // That doesn't work!!!!!
//Why? Any ideas?
tardis = SDL_CreateTextureFromSurface(renderer, tardis_surface);
//Create texture for the Gallifreyan background
gallifrey = SDL_CreateTextureFromSurface(renderer, gallifrey_surface);
//Clear the renderer
SDL_RenderClear(renderer);
//Add textures to renderer
SDL_RenderCopy(renderer, gallifrey, NULL, NULL);
SDL_RenderCopy(renderer, tardis, NULL, &tardis_dest);
//Update renderer
//SDL_RenderPresent(renderer);
}
//Update the Tardis' position
void update_sprites(float time)
{
speed += time * 28;
if(tardis_dest.y > 460)
{
tardis_dest.y = 460;
speed = 0;
}
else if(tardis_dest.y < 0)
{
tardis_dest.y = 0;
speed = 0;
}
else
{
tardis_dest.y += speed;
SDL_RenderCopy(renderer, gallifrey, NULL, NULL);
SDL_RenderCopy(renderer, tardis, NULL, &tardis_dest);
//SDL_RenderPresent(renderer);
}
}
//Change the speed when clicked
void ignite()
{
if(speed > 0)
speed = -10;
else
speed -= 10;
}
//The main function
int main(int argc, char *argv[])
{
SDL_Event ev;
int active = 1;
if(SDL_Init(SDL_INIT_EVERYTHING) != 0)
fprintf(stderr, "Could not initialize SDL. SDL Error: %s\n", SDL_GetError());
else
printf("SDL initialized.\n");
if(TTF_Init() == 0)
fprintf(stderr, "TTF initialized.\n");
else
fprintf(stderr, "Could not initialize TTF.\n");
//Open main window
screen = SDL_CreateWindow
(
"First Game", SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
WIDTH,
HEIGHT,
SDL_WINDOW_SHOWN
);
if(!screen)
fprintf(stderr, "Could not set video mode: SDL Error: %s\n", SDL_GetError());
renderer = SDL_CreateRenderer(screen, 0, SDL_RENDERER_ACCELERATED);
//Draw the environment
load_images();
create_sprites();
//Set time
Uint32 current_time, old_time;
float time_passed;
current_time = SDL_GetTicks();
while(active)
{
//Update time
old_time = current_time;
current_time = SDL_GetTicks();
time_passed = (current_time - old_time) / 1000.0f;
//Handle Events
while(SDL_PollEvent(&ev))
{
if(ev.type == SDL_QUIT)
active = 0;
if(ev.type == SDL_MOUSEBUTTONDOWN)
{
if(pause)
{
pause = 0;
}
else
ignite();
}
}
if(pause)
continue;
update_sprites(time_passed);
draw_ground();
draw_counter();
SDL_Delay(40);
}
//Exit
SDL_FreeSurface(tardis_surface);
SDL_FreeSurface(gallifrey_surface);
SDL_Quit();
printf("SDL terminated. Have a nice day...\n");
return 0;
}
很有意思,是吧?它可能有很多缺陷,因为这是我在游戏编程中的第一次尝试,但我的主要问题是,玩了一段时间后,我得到了一个分割错误。对于gdb,我发现是以下命令导致了问题:
count = TTF_RenderText_Solid(font,
count_text,
text_color);
位于draw_counter()函数中,第214行(中间的某个位置)。问题是:为什么?我想这与我正在做的内存分配有关(我仍然在学习C语言,这是一个非常棘手的部分,来自Python)。如果有人能提供一些有用的信息?谢谢
BTW,请不要用通常的“不使用C,学习C++或Java”或“仅仅使用Unity”来回应,我想学习C,并且可能沿路学习一些游戏编程。
最佳答案
一定是连续调用font = TTF_OpenFont("DALEK.ttf", 40);
导致它在一段时间后失败。
SDL为每一个打开的字体分配内存,在某个时候你会用完。(可能文件保持打开状态,而系统的文件句柄不足。)需要关闭字体句柄:add
TTF_CloseFont( font );
在打开它的程序结束时。或者,只打开一次字体(在
main
例程中)并将其句柄保存在全局变量中。顺便说一句,我也注意到你对自己的外表也很大方。使用完一个类似的函数
SDL_FreeSurface
后,再次防止只使用一次资源,但仍在分配它们。(这是SDL1.0,我已经习惯了。检查SDL 2文档。)