Simple problem moving enemy in game (C / SDL) - c

I'm hacking away at a simple game to teach myself C and I've come up against an infuriatingly simple problem that I haven't been able to Google an answer to.
Code follows, apologies for its noobie terribleness (criticisms appreciated!):
#include <stdio.h>
#include <stdlib.h>
#include <SDL/SDL.h>
#define AMOUNT_OF_ENEMIES 10
#define AMOUNT_OF_PIXELS_TO_MOVE 50.0
struct enemy
{
int alive;
SDL_Rect rect;
};
void create_enemy(struct enemy *position)
{
// Take a pointer to an array. Iterate through array looking for any 'dead' instances.
// (Re)initialise when found, ignore entirely if array is full of alive instances.
int j = 0;
while(position[j].alive == 1 && j < AMOUNT_OF_ENEMIES)
{
++j;
}
if(position[j].alive == 0)
{
position[j].alive = 1;
position[j].rect.y = 0;
}
}
void update_enemies(struct enemy *position)
{
// Iterate through a passed array looking for alive instances. If found increment vertical position,
// unless instance is at bottom of screen in which case it's marked as dead.
int j = 0;
while(j < AMOUNT_OF_ENEMIES)
{
if(position[j].alive == 1)
{
position[j].rect.y += 1;
if(position[j].rect.y > 570)
{
position[j].alive = 0;
}
}
++j;
}
}
int main(void)
{
// INITS *********************************************************************
int k;
int current_time = 0;
int previous_time = 0;
float difference_in_time = 0.0;
// Load SDL library
if(SDL_Init(SDL_INIT_EVERYTHING) != 0)
{
printf("Problem, yo\n");
return 1;
}
// Setup event queue
SDL_Event event;
// Create array to store enemys, initialise it
struct enemy *enemy_array = malloc(sizeof(struct enemy) * AMOUNT_OF_ENEMIES);
int j;
for(j = 0; j < AMOUNT_OF_ENEMIES; ++j)
{
enemy_array[j].alive = 0;
enemy_array[j].rect.x = 150;
enemy_array[j].rect.y = 0;
}
// Create an array to flag keypresses, initialise it
int pressed_keys[323];
int l;
for(l = 0; l < 323; ++l)
{
pressed_keys[l] = 0;
}
// Create surfaces
SDL_Surface *screen = SDL_SetVideoMode(300, 600, 0, SDL_HWSURFACE);
int black = SDL_MapRGB(screen->format, 0, 0, 0);
SDL_Surface *tower = SDL_LoadBMP("tower.bmp");
SDL_Rect tower_rect;
tower_rect.x = 50;
tower_rect.y = 0;
tower_rect.w = 200;
tower_rect.h = 600;
SDL_Surface *dude = SDL_LoadBMP("dude.bmp");
float dude_x = 0.0;
SDL_Rect dude_rect;
dude_rect.x = 120;
dude_rect.y = 500;
dude_rect.w = 60;
dude_rect.h = 100;
SDL_Surface *enemy = SDL_LoadBMP("enemy.bmp");
// GAME LOOP *****************************************************************
while(1)
{
current_time = SDL_GetTicks();
difference_in_time = (float)(current_time - previous_time) / 1000;
previous_time = current_time;
if(SDL_PollEvent(&event))
{
if(event.key.keysym.sym == SDLK_DOWN)
{
create_enemy(enemy_array);
}
else
{
switch(event.type)
{
case SDL_QUIT:
printf("NOOOOOO\n");
SDL_FreeSurface(screen);
SDL_FreeSurface(tower);
SDL_FreeSurface(enemy);
free(enemy_array);
SDL_Quit();
return 0;
case SDL_KEYDOWN:
pressed_keys[event.key.keysym.sym] = 1;
break;
case SDL_KEYUP:
pressed_keys[event.key.keysym.sym] = 0;
break;
}
}
}
if(pressed_keys[SDLK_LEFT] && dude_rect.x > 50)
{
dude_rect.x -= (AMOUNT_OF_PIXELS_TO_MOVE * difference_in_time);
}
if(pressed_keys[SDLK_RIGHT] && dude_rect.x < 190)
{
dude_rect.x += (AMOUNT_OF_PIXELS_TO_MOVE * difference_in_time);
}
update_enemies(enemy_array);
SDL_FillRect(screen, NULL, black);
SDL_BlitSurface(tower, NULL, screen, &tower_rect);
for(k = 0; k < AMOUNT_OF_ENEMIES; ++k)
{
if(enemy_array[k].alive == 1)
{
SDL_BlitSurface(enemy, NULL, screen, &enemy_array[k].rect);
}
}
SDL_BlitSurface(dude, NULL, screen, &dude_rect);
SDL_Flip(screen);
}
return 0;
}
The issue arises at this part:
if(pressed_keys[SDLK_LEFT] && dude_rect.x > 50)
{
dude_rect.x -= (AMOUNT_OF_PIXELS_TO_MOVE * difference_in_time);
}
if(pressed_keys[SDLK_RIGHT] && dude_rect.x < 190)
{
dude_rect.x += (AMOUNT_OF_PIXELS_TO_MOVE * difference_in_time);
}
The 'dude' object moves to the left correctly, but nothing happens when the right arrow key is pressed.
Adding a printf tells me the if statement is being executed correctly. Removing difference_in_time makes it work, so it's either something to do with that variable or the operation of it and AMOUNT_OF_PIXELS_TO_MOVE.
I just can't for the life of me figure out why the former block executes correctly and the latter (which is essentially the same thing) doesn't. I'm sure it's something simple I've overlooked but I'm going insane trying to find it.

Your problem is due to rounding.
For your "dude" you are using a SDL_Rect, that uses integer coordinates (short int if I remember correct).
You configured your dude speed to 50 and if your game is running at 60fps (probably due to its simplicity and it may be much more if vsync is off) you will get each frame a movement value of 0.83333.
This value will be truncated to a int and the result will be zero, for example, if dude.x is 10 and you press right, the calculated value will be 10.83 and when truncated this will result in 10.
For left, it works because the value is rounded down, assuming again dude.x is 10, when left is pressed, on the first iteration the calculated value would be 9.17, truncating this will give you 9.
Simple, bad and Hack Solution
Increase AMOUNT_OF_PIXELS_TO_MOVE to a higher value that forces the int to increase, this will fix the problem.
Good Solution
Does not use SDL_Rect for storing your characters position, create a "MyRect" and use float values in it and only does rounding when drawing the character. Actually you only need to store the character position, so I would create a Point2D struct with only x and y and use this to keep track of characters position.

Related

Snake Game implementation in C

I need some help with some homework. I am supposed to make a Snake Game by using mostly "basic" c, only simple concepts.
To explain my thought process: Controls are the usual WASD. The grid we re playing in is an array. Head is represented by '#' and the tail by "*". What I am trying to do is implement this thinking:
Get input from user, move the head. Check in 4 possible directions of head's initial position, find the " * ", move it to the initial position of the head. Repeat till all '*' have been moved.
Obviously, I am doing something wrong while checking and/or moving, but I cannot quite figure out what parts I am treating wrong and after 2 days I am stuck with this as my closest to working version. I know I have been looking at it for too long, but I cant afford to take more time with it as it needs to be turned in.
Here is my code. I hope my error is very obvious to "fresh eyes".
main:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
int main(int argc, char *argv[]){
//get size of grid from args
int size;
size = atoi(argv[1]);
while(size < 6){
printf("Please insert a number greater than 6 so the game is
playable\n");
scanf("%d\n", &size);
}
char grid[size][size];
int sx,sy; //coordinates of snake head
int length = 2; //length of tail (without head)
int score = 0;
sx = 1;
sy = size/2;
//generate empty grid
int x, y;
for (y = 0; y < size; ++y) {
for (x = 0; x < size; ++x) {
if (y == 0 || y == size - 1) {
grid[y][x] = '-';
} else if (x == 0 || x == size - 1) {
grid[y][x] = '|';
} else {
grid[y][x] = ' ';
}
}
}
//initialize snake position
grid[sx][sy] = '#';
grid[sx+1][sy] = '*';
grid[sx+2][sy] = '*';
grid[sx+3][sy-6] = 'X';//just to test if it works, will be generated
randomly
//print grid
printGrid(size, grid);
play(sx,sy,size,grid,&length);
printf("*********GAME OVER********** \n final score: %d\n", score);
}
play() function. Practically all of the game.
int play(int x, int y, int size, char grid[size][size], int length){
int x0,y0,x1,y1,x2,y2,valid;//x0,y0 = current, x1,y1 next to check,
//x2,y2 = move there x0,y0
char end,dir; //what did the head land on
int score = 0;
int checkl = 0;
while(1){
scanf("%c", &dir);
switch(dir){
//move head, change head coordinates
case 'w': end = grid[x-1][y];
grid[x-1][y] = grid[x][y];
grid[x][y] = ' ';
x0 = x;
y0 = y;
x--;
break;
case 's': end = grid[x+1][y];
grid[x+1][y] = grid[x][y];
grid[x][y] = ' ';
x0 = x;
y0 = y;
x++;
break;
case 'a': end = grid[x][y-1];
grid[x][y-1] = grid[x][y];
grid[x][y] = ' ';
x0 = x;
y0 = y;
y--;
break;
case 'd': end = grid[x][y+1];
grid[x][y+1] = grid[x][y];
grid[x][y] = ' ';
x0 = x;
y0 = y;
y++;
break;
}
if ((end == '-') | (end =='|') | (end == '*')){
break;
}
printf("POINT 1\n");
do{
check(x0,y0,&x1,&y1, size, grid);
move(x0,y0, x2,y2, size, grid);
//if(valid == 1){
x2 = x0;
y2 = y0;
x0 = x1;
y0 = y1;
//}
checkl++;
printf("POINT 2\n");
}while(checkl <= length);
printf("POINT 3\n");
if (end == 'X'){
grid[x1][y1] = '*';
length++;
score += 10;
}
clearScreen();
printGrid(size, grid);
}
return score;
}
check() function. Implements the "look for the next '*'" process
int check(int x0, int y0, int *x1, int *y1, int size, char grid[size]
[size]){
if (grid[x0][y0-1] == '*'){
*x1 = x0;
*y1 = y0-1;
return 1;
}
else if (grid[x0][y0+1] == '*'){
*x1 = x0;
*y1 = y0+1;
return 1;
}
else if (grid[x0-1][y0] == '*'){
*x1 = x0-1;
*y1 = y0;
return 1;
}
else if (grid[x0+1][y0] == '*'){
*x1 = x0+1;
*y1 = y0;
return 1;
}
else
return 0;
}
move(). Self-explanatory
void move(int x0, int y0, int x2, int y2, int size, char grid[size]
[size]){
grid[x2][y2] = grid[x0][y0];
grid[x0][y0] = ' ';
}
A function to clear the window
void clearScreen()
{
const char *CLEAR_SCREEN_ANSI = "\e[1;1H\e[2J";
write(STDOUT_FILENO, CLEAR_SCREEN_ANSI, 12);
}
PrintGrid. Also self explanatory, have it as a function for convenience
void printGrid(int size, char grid[size][size]){
int y,x;
for (y = 0; y < size; ++y) {
for (x = 0; x < size; ++x) {
printf("%c", grid[y][x]);
}
printf("\n");
}
}
I believe the error is either in check, move, and in the do-while condition with the length in play. However, as I said, with a lot of trial and error, I am still here.
All of this should be in one file, but i could not align it properly all together. Thanks for any help, very appreciated.
Edit: Edited part of the code out. Now what happens after running the code is: I get the grid and the snake. The head moves just fine, along with the first '" of the tail but the rest does not follow. When it "eats" an 'X', it grows the tail where it should, but the '' added disappears in the next move. Also, the score goes up 20 instead of 10 so for some reason it enters the if twice for one move. The movement of the head + the first '*' as well as the "game over" scenarios work as they should.
Tidbit: This is only 10 actual characters, not 12 because the \ characters don't count:
const char *CLEAR_SCREEN_ANSI = "\e[1;1H\e[2J";
Better is to let the compiler count for you:
void clearScreen()
{
static const char CLEAR_SCREEN_ANSI[] = "\e[1;1H\e[2J";
write(STDOUT_FILENO, CLEAR_SCREEN_ANSI, sizeof(CLEAR_SCREEN_ANSI)-1);
}
Note that CLEAR_SCREEN_ANSI is now an array, not a pointer, and the -1 is for the trailing NUL byte.
Aha:
if ((end == '-') | (end =='|') | (end == '*')){
The | are bitwise OR and you almost certainly mean logical OR ||:
if ((end == '-') || (end =='|') || (end == '*')){
Edit: They actually do the same thing in this case, but it's a common-enough error that it's worth fixing.
Hat tip to #Sami Kuhmonen
I've spent some time looking at this, and it's clear this is entirely about program logic and less about C, so that makes it a lot more difficult to dive into something unfamiliar (and without a real spec).
The general idea is that the program draws a square playing field with a three-part snake: an # for a head and two * for the body, and the keyboard lets you move the snake around on the field with awds keys. You can't move on top of yourself, you can't go past the walls, and you want to find the food at location marked with X.
I believe your approach was to look for * characters, but I am not sure how it could tell a * from the middle of the snake from one at the end, so my approach is to maintain an array of all the positions of the snake, and in the main function it's initialized:
int ysnake[4] = { 1, 2, 3, 4 };
int xsnake[4] = { 15, 15, 15, 16 };
plus an obvious function to draw the snake given the position array:
static void drawsnake(
int snakelen, int xsnake[snakelen], int ysnake[snakelen],
int size, char grid[size][size])
{
char snakechar = '#'; // start with snake head
for (int i = 0; i < snakelen; i++)
{
grid[ ysnake[i] ]
[ xsnake[i] ] = snakechar;
snakechar = '*'; // all the rest are tails
}
}
So now we have the play() function, and this required quite a bit of refactoring, but it works for me.
int play(
int snakelen, int xsnake[snakelen], int ysnake[snakelen],
int size, char grid[size][size])
{
int score = 0;
while (1)
{
char dir;
scanf ("%c", &dir);
int proposed_xhead = xsnake[0];
int proposed_yhead = ysnake[0];
switch (dir)
{
case 'w': // up
proposed_yhead--;
break;
case 's': // down
proposed_yhead++;
break;
case 'a': // left
proposed_xhead--;
break;
case 'd': // right
proposed_xhead++;
break;
default: // ignore this invalid char
continue;
}
// first make sure we didn't go off the edge of the field
if (proposed_yhead <= 0 || proposed_yhead >= (size-1)
|| proposed_xhead <= 0 || proposed_xhead >= (size-1))
{
// would head off the maze, LOSE
break;
}
const char char_at_proposed_head = grid[proposed_yhead][proposed_xhead];
// now make sure we're not bumping into ourself
if (char_at_proposed_head == '#' // we shouldn't ever be able to bump into our head
|| char_at_proposed_head == '*') // we bumped into our tail
{
break; // would run into ourself: LOSE
}
// NOW we know it's a legal move. Did we find the food?
if (char_at_proposed_head == 'X')
{
// found the food!
// update score, set new food, etc.
score += 10;
}
// blank out the former tail; we're about to lose that position
grid[ ysnake[ snakelen-1 ] ]
[ xsnake[ snakelen-1 ] ] = ' ';
// shift all items down in the array, dropping the last one
for (int i = snakelen-1; i > 0; i--)
{
xsnake[i] = xsnake[i-1];
ysnake[i] = ysnake[i-1];
}
xsnake[0] = proposed_xhead;
ysnake[0] = proposed_yhead;
drawsnake(snakelen, xsnake, ysnake, size, grid);
clearScreen ();
printGrid (size, grid);
}
return score;
}
There's a clear separation of duties, where the decoding of the direction key translates to a change in the snake's head position, then checking for out-of-bounds or running into yourself, and finding if you found the food.
Then we move the snake by shifting the entire array of positions: the tail element drops off and the new one shifts onto the front. Then we re-draw the snake with new positions.
The main function is the same as yours at the top to set up the grid, but the snake configuration and play looks like:
int main(int argc, argv)
{
...
int length = 4;
int ysnake[4] = { 1, 2, 3, 4 };
int xsnake[4] = { 15, 15, 15, 16 };
drawsnake(length, xsnake, ysnake, size, grid);
// Put the food somewhere
grid[sx + 3][sy - 6] = 'X'; //just to test if it works, will be generated randomly
printGrid (size, grid);
score = play(length, xsnake, ysnake, size, grid);
printf ("*********GAME OVER********** \n final score: %d\n", score);
}
I didn't really do anything meaningful with the score and have only barely tested it, but it does let the little guy slither around the screen, and it might be a bit easier to follow.
Good luck.

Difficulties getting a pure sounding sine wave (currently getting a subtle frequency with it)

I'm attempting to play a pure sine wave in SDL2, but I'm finding that I can't seem to get a completely pure tone. It sounds pretty close to a true sine wave, but there is a slight secondary frequency behind it that sounds closer to a square wave. I've recorded the sound and verified that it is indeed incorrect when playing through two sets of speakers on two different systems (compared to a pure sine wave)
I've tried quite a few things at this point, including implementing multiple sine waves from stack overflow, and attempting to adapt the code from Handmade Hero. But each time, the same problem crops up. My suspicion is that there's something wrong with the bit precision, an incorrect cast somewhere, or that it has something to do with the specific way that SDL audio works that I'm not navigating around properly
Here's the main audio callback function that I'm currently working with along with my most recent attempt at writing a sine wave to the buffer:
#define Pi32 3.14159265359f
#define Tau32 (2.0f * Pi32)
void
AudioCallback(void* UserData, u8* Stream, int Length)
{
audio_user_data* AudioUserData = (audio_user_data*)UserData;
static u32 Count = 0;
u16* SampleBuffer = (u16*)Stream;
int SamplesToWrite = Length / AudioUserData->BytesPerSample;
for(int SampleIndex = 0; SampleIndex < SamplesToWrite; SampleIndex++)
{
u16 ToneValue = round((AudioUserData->ToneVolume * sin(Tau32 * (f32)Count / (f32)AudioUserData->WavePeriod)));
*SampleBuffer++ = ToneValue;
*SampleBuffer++ = ToneValue;
++Count;
}
}
I would be happy to provide more context if it might help
EDIT -- Additional Context:
#include "x:\SDL2-2.0.10\include\SDL.h"
#define Pi32 3.14159265359f
#define Tau32 (2.0f * Pi32)
#define INITIAL_SCREEN_WIDTH (8*80)
#define INITIAL_SCREEN_HEIGHT (8*60)
typedef struct audio_user_data audio_user_data;
struct audio_user_data
{
int SamplesPerSecond;
int BytesPerSample;
int SampleIndex;
int ToneHz;
int ToneVolume;
int WavePeriod;
u32 FileLength;
u16* BufferLocation;
};
void
AudioCallback(void* UserData, u8* Stream, int Length)
{
audio_user_data* AudioUserData = (audio_user_data*)UserData;
static u32 Count = 0;
u16* SampleBuffer = (u16*)Stream;
int SamplesToWrite = Length / AudioUserData->BytesPerSample;
for(int SampleIndex = 0; SampleIndex < SamplesToWrite; SampleIndex++)
{
u16 ToneValue = (0.5f + (AudioUserData->ToneVolume * sin(Tau32 * Count / AudioUserData->WavePeriod)));
*SampleBuffer++ = ToneValue;
*SampleBuffer++ = ToneValue;
++Count;
}
}
int
main(int argc, char* argv[])
{
SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO);
SDL_Window* Window = SDL_CreateWindow("Spell Checker", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, INITIAL_SCREEN_WIDTH*2, INITIAL_SCREEN_HEIGHT*2, 0);
SDL_Renderer* Renderer = SDL_CreateRenderer(Window, 0, SDL_RENDERER_SOFTWARE);
SDL_PixelFormat* Format = SDL_AllocFormat(SDL_PIXELFORMAT_RGB888);
SDL_Texture* Screen = SDL_CreateTexture(Renderer, Format->format, SDL_TEXTUREACCESS_STREAMING, INITIAL_SCREEN_WIDTH, INITIAL_SCREEN_HEIGHT);
audio_user_data AudioUserData = {0};
AudioUserData.SamplesPerSecond = 44100;
AudioUserData.BytesPerSample = 2 * sizeof(int16);
AudioUserData.SampleIndex = 0;
AudioUserData.ToneVolume = 3000;
AudioUserData.ToneHz = 440;
AudioUserData.WavePeriod = AudioUserData.SamplesPerSecond / AudioUserData.ToneHz;
SDL_AudioSpec Want, Have;
SDL_AudioDeviceID AudioDeviceID;
Want.freq = AudioUserData.SamplesPerSecond;
Want.format = AUDIO_S16;
Want.channels = 2;
Want.samples = 4096;
Want.callback = &AudioCallback;
Want.userdata = &AudioUserData;
AudioDeviceID = SDL_OpenAudioDevice(0, 0, &Want, &Have, 0);
SDL_PauseAudioDevice(AudioDeviceID, 0); // Start playing
u32* PixelMap = calloc(INITIAL_SCREEN_WIDTH * INITIAL_SCREEN_HEIGHT, sizeof(PixelMap));
int PixelMapLocation = 0;
int Running = 1;
while(Running)
{
SDL_Event Event;
while(SDL_PollEvent(&Event))
{
if(Event.type == SDL_QUIT)
{
Running = 0;
break;
}
}
// Test colors
PixelMapLocation = 0;
for(int Row = 0; Row < INITIAL_SCREEN_WIDTH; ++Row)
{
for(int Col = 0; Col < INITIAL_SCREEN_HEIGHT; ++Col)
{
PixelMap[PixelMapLocation++] = 0xFF00FF;
}
}
for(int Row = 0; Row < INITIAL_SCREEN_WIDTH; ++Row)
{
for(int Col = 0; Col < INITIAL_SCREEN_HEIGHT; ++Col)
{
PixelMap[PixelMapLocation++] = 0x00FFFF;
}
}
SDL_UpdateTexture(Screen, 0, PixelMap, INITIAL_SCREEN_WIDTH * sizeof(PixelMap));
SDL_RenderClear(Renderer);
SDL_RenderCopy(Renderer, Screen, 0, 0);
SDL_RenderPresent(Renderer);
}
return(0);
}
EDIT2:
I recorded the audio I'm hearing here (might need to turn the volume up to hear the problem):
https://www.youtube.com/watch?v=-V2IMhK2Zis&feature=youtu.be
I also ran a timing test, and got this back for the each run through of the AudioCallback function:
https://imgur.com/a/9pqCte0
EDIT3:
Oscilloscope readings --
My sine wave:
A pure sine wave:
(I can't see an appreciable difference, but maybe someone else can?) Edit: Oh wait, on the left side of the oscilloscope, there are clear differences between the two waveforms (though they don't appear in the main reading). Trying to figure out what the issue is -- at this point I am still unsure since I have tried several different algorithms
EDIT4:
Here's a picture of Want / Have to show that everything (other than size) is the same after calling SDL_OpenAudioDevice:
EDIT5:
Problem (sort of) solved! Setting AudioUserData.SamplesPerSecond to 48000 resulted in a pure sounding sine wave. But the question still stands: why does it only work at 48000??

C Space Invaders enemy movement

I have to write a clone of space invaders game for university in C language using SDL library for graphics.I am very new to C and programming in general so i struggle with it a lot. For now I have one row of enemies and I'm trying to make it move correctly. This are functions for movement of aliens, which check for the collision with right/left wall(which are SDL_Rects near window edges) and if it happens, enemies move one line lower in the opposite direction. The problem is it works okay for all ten enemies except the first one. Each time when collision with left wall occurs the first alien sort of moves away a bit from others instead of moving in one block as I want it to. I noticed that if I change the first for loop in move_aliens function to start from i=11 and i--, the same thing will happen to the enemy in last column. But I still dont know how to fix it.
I would appreciate if someone could tell me what I'm doing wrong, give me an idea or sollution :).
I uploaded a video of whats happening http://sendvid.com/dt1reizc
void move_down (GameState *game)
{
int i=0;
for(; i < HMALIENS; i++)
game->alien1[i].y += 25;
}
int collided(int a1, int b1, int a2, int b2, int width1,
int height1, int width2, int height2)
{
return (!((a1 > (a2+width2)) || (a2 > (a1+width1)) ||
(b1 > (b2+height2)) || (b2 > (b1+height1))));
}
void move_aliens(GameState *game)
{
int i=0;
for(; i < HMALIENS; i++)
{
if (game->alien1[i].dir==LEFT)
game->alien1[i].x -= 10;
if (game->alien1[i].dir==RIGHT)
game->alien1[i].x += 10;
if (collided(game->alien1[i].x, game->alien1[i].y,
game->leftwall.x, game->leftwall.y, 50, 50, 1, 600))
{
int i = 0;
for(; i < HMALIENS; i++)
game->alien1[i].dir=RIGHT;
move_down(game);
}
}
if(collided(game->alien1[i].x, game->alien1[i].y, game->rightwall.x,
game->rightwall.y, 50, 50, 1, 600))
{
int i = 0;
for(; i < HMALIENS; i++)
game->alien1[i].dir=LEFT;
move_down(game);
}
}
}
//edit
HMALIENS is just a constant (11), the number of living enemies at the start
GameState is a structure.
LEFT/RIGHT stand for direction of movement (quite obvious) [enum Direction {LEFT, RIGHT};]. I have in my alien1 structure enum Direction dir and in the function which load_game function i set it to RIGHT.
typedef struct
{
Player player;
Rightwall rightwall;
Leftwall leftwall;
Alien1 alien1[HMALIENS];
SDL_Texture *bulet;
SDL_Texture *ship;
SDL_Texture *Alien1;
SDL_Renderer *renderer;
} GameState;
Without rewriting your code, the problem is that you have already moved the first alien ([0]) left,
if (game->alien1[i].dir==LEFT)
game->alien1[i].x -= 10;
and then you are doing the collision test and flagging all of them to move right, but the loop continues with [1..10], while [0] has already moved.
typedef enum{
LEFT,
RIGHT
}GAME_DIRECTION;
int dir=LEFT;
#define SPRITE_WIDTH 50
#define SPRITE_HEIGHT 50
#define BOTTOM_LINE 600
void move_down (GameState *game)
{
int i;
for(i=0; i < HMALIENS; i++)
game->alien1[i].y += 25;
}
void do_slide(GameState *game)
{
int i;
for(i=0; i < HMALIENS; i++)
game->alien1[i].x += dir?10:-10;
}
int collided(GameState *game)
{
int i;
for(i=0; i < HMALIENS; i++){
if(
game->alien1[i].x <= game->leftwall.x ||
game->alien1[i].x >= game->rightwall.x +SPRITE_WIDTH ||
game->alien1[i].y >= BOTTOM_LINE - SPRITE_HEIGHT
)
return true;
}
return false;
}
void move_aliens(GameState *game)
{
if(collided(game)){
move_down (game);
dir=!dir; // update direction;
}else{
do_slide (game);
}
}
Maybe something like:
void move_aliens(GameState *game)
{
int i=0;
for(; i < HMALIENS; i++)
{
if (game->alien1[i].dir==LEFT)
game->alien1[i].x -= 10;
if (game->alien1[i].dir==RIGHT)
game->alien1[i].x += 10;
}
if (collided(
game->alien1[0].x, game->alien1[0].y,
game->leftwall.x, game->leftwall.y,
50, 50, 1, 600))
{
int i = 0;
for(; i < HMALIENS; i++)
game->alien1[i].dir=RIGHT;
move_down(game);
}
if(collided(
game->alien1[HMALIENS-1].x, game->alien1[HMALIENS-1].y,
game->rightwall.x, game->rightwall.y,
50, 50, 1, 600))
{
int i = 0;
for(; i < HMALIENS; i++)
game->alien1[i].dir=LEFT;
move_down(game);
}
}
I have tried to make the minimum changes. But #milevyo's more extensive rewrite looks good as well.
The problem, I think (just from looking), is you has the left collision test inside the loop so when that hits, the movement gets out of sync.
Also it was pretty subtle that the right collision test used i from the previous loop which happens to index the last element (the rightmost alien). I changed that to explicitly use HMALIENS - 1. When you start destroying aliens,
you'll have to track the first (leftmost) and last (rightmost) living aliens and use them for you collision tests.
Your indentation and formatting were a little off which makes the code much harder to read. Formatting is important, and will be even more so when your code gets more complicated.

SDL2: getting segmentation fault with TTF_RenderText after some time?

Well, I wasn't sure if that's for code review or for this site, but it has a problem, so I suppose I'm in the right place. I'm trying to create a game using C and SDL. Here's the code(explanation below):
//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, &current_pillar->pillar_up, angle = 0.0,
NULL, SDL_FLIP_VERTICAL);
SDL_RenderCopy(renderer, current_pillar->pillar_texture,
NULL, &current_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;
}
Quite a read, huh? It probably has many flaws, as it's my first try in game programming, but my main problem is that after some time of playing it I get a segmentation fault. With gdb, I found that it's the following command that's causing the problem:
count = TTF_RenderText_Solid(font,
count_text,
text_color);
Located in the draw_counter() function, at line 214(somewhere in the middle). The question is: why? I suppose it has to do with the memory allocation that I'm doing (I'm still learning C and that's a very tricky part, coming from Python). If anyone could give some useful info? Thanks
And BTW, please don't respond with the usual "don't use C, learn C++ or Java" or even "just use Unity", I want to learn C, and maybe learn some game programming along the way.
It must be the continuously calling of font = TTF_OpenFont("DALEK.ttf", 40); that makes it fail after a time.
SDL allocates memory for every opened font and at some time you are going to run out. (Perhaps the file stays open and your system runs out of file handles.) You need to close the font handle: add
TTF_CloseFont( font );
at the end of the routines that open it. Alternatively, open the fonts only once (in your main) routine and save their handles in a global variable.
By the way, I also notice you are as liberal with your surfaces. Use a similar function SDL_FreeSurface when you're done with one, again to prevent using resources only once but still having them allocated. (That is for SDL 1.0, which I'm used to. Check SDL 2 documentation.)

Iterator and Pointer on Matrix in C

For a JPEG image compression, I manipulate image in grey levels and 8bits by pixels
I have this type of matrix I dynamically allocated :
typedef char pixel_t;
pixel_t ** pix_matrix;
after allocating and filling it, I have a bidimensional array with the values (from -128 to +127) of the luminance of the picture.
For the JPEG compression, I need to iterate this array in zigzag like this:
So I want to create an Iterator structure for this type. This iterator must have 'current' and 'begin' members and I want those members to be pointers to the current element and first one of the matrix. In other words, I want to store the addresses and not the indexes. But after hours of tests, prints and researches, I couldn't find the way to make that possible. What type of pointer do I have to use? how make it point to the first address of my matrix? Is my request simply possible?
And if all of this is possible, how can I get the next element, and the value of the current one?
You can write an interator structure:
struct zigzag_t {
int width; // width, must be initialised
int height; // height, must be initialised
int x; // current x index
int y; // current y index
int underway; // dummy value to start at (0, 0)
};
which you must initialise with the width and height of your image. Write an interator function, so that you can use this iterator like this:
struct zigzag_t zz = {8, 8};
while (zigzag_next(&zz)) {
printf("(%d, %d)\n", zz.y, zz.x);
}
The iterator itself is not too complicated: If the sum of the x and y indices is odd, you walk southwest until you hit either the west or south edge. If the sum is even, you walk northeast until you hit either the north or east wall. If you hit the ne or sw edges, the east and south edges get priority. The iteration ends after you have visited the se edge.
Because the struct starts off with x and y both zero, the first point is (0, 1). In order to fix this, the dummy field underway, which also is zero, is used.
The iterator must be reset if you want to use it a second time. better yet, define and initialise a fresh iterator.
The iterator function:
int zigzag_next(struct zigzag_t *zz)
{
int odd = (zz->x + zz->y) % 2;
if (zz->underway == 0) {
zz->x = zz->y = 0;
zz->underway = 1;
return 1;
}
if (odd) {
/* walk southwest */
int w_edge = zz->x == 0;
int s_edge = zz->y == zz->height - 1;
if (s_edge) {
zz->x++;
return zz->x < zz->width;
} else if (w_edge) {
zz->y++;
} else {
zz->x--;
zz->y++;
}
} else {
/* walk northeast */
int e_edge = zz->x == zz->width - 1;
int n_edge = zz->y == 0;
if (e_edge) {
zz->y++;
return zz->y < zz->height;
} else if (n_edge) {
zz->x++;
} else {
zz->x++;
zz->y--;
}
}
return 1;
}
This solution returns the x and y positions, which you can use as indices to your double pointer to pixel data. It would not be hard to extend the struct to hold the base pointer to your pixel data and have the iterator function return a pointer to a pixel or NULL if the iteration has run out.
An example solution with pointers is below.
#include <stdlib.h>
#include <stdio.h>
typedef char pixel_t;
struct zigzag_t {
pixel_t **p; // base data
int width; // width, must be initialised
int height; // height, must be initialised
int x; // current x index
int y; // current y index
int underway; // dummy value to start at (0, 0)
};
pixel_t *zigzag_next(struct zigzag_t *zz)
{
int odd = (zz->x + zz->y) % 2;
if (zz->underway == 0) {
zz->x = zz->y = 0;
zz->underway = 1;
return *zz->p;
}
if (odd) {
/* walk southwest */
int w_edge = zz->x == 0;
int s_edge = zz->y == zz->height - 1;
if (s_edge) {
zz->x++;
if (zz->x == zz->width) return NULL;
} else if (w_edge) {
zz->y++;
} else {
zz->x--;
zz->y++;
}
} else {
/* walk northeast */
int e_edge = zz->x == zz->width - 1;
int n_edge = zz->y == 0;
if (e_edge) {
zz->y++;
if (zz->y == zz->height) return NULL;
} else if (n_edge) {
zz->x++;
} else {
zz->x++;
zz->y--;
}
}
return zz->p[zz->y] + zz->x;
}
int main()
{
pixel_t *data[] = {
"abcde", "fghij", "klmno", "pqrst", "uvwxy"
};
struct zigzag_t zz = {data, 5, 5};
for (;;) {
pixel_t *p = zigzag_next(&zz);
if (p == NULL) break;
putchar(*p);
}
putchar('\n');
return 0;
}
This solution is a C solution. There is no begin member function; initialisation is done via simple struct initialisation. There is no increment operator and no end member function; moving the iterator forward and checking for the end is done in a plain old function.
You have tagged the question C, but iterators are more frequent in C++, where they can be implemented as classes. The above C example may serve as a base for such an implementation.
Something nice and simple.
Function next is the iterator; it returns true until all cells have been visited.
A variable of type POSITION holds the iterator state.
Function current returns a pointer to the current cell in the matrix.
Demo function sample_application puts it all together.
#define MAX_XY 7
typedef struct { int x, y; } POSITION;
static int sign_of(int i)
{
return i < 0 ? -1 : i > 0 ? 1 : 0;
}
static int get_direction(int a, int b, int odd_is_forward)
{
return sign_of(((a + b) % 2 == odd_is_forward || b >= MAX_XY ? MAX_XY : 0) - a);
}
int next(POSITION *pos)
{
int x = pos->x;
int y = pos->y;
pos->x += get_direction(x, y, 0);
pos->y += get_direction(y, x, 1);
return x < MAX_XY || y < MAX_XY;
}
pixel_t *current(POSITION *pos)
{
return &pix_matrix[pos->y][pos->x];
}
void sample_application() // just demonstrating the use of POSITION
{
POSITION pos = {-1, -1}; // always start from these dummy coordinates
while (next(&pos)) // this iterates through the matrix
{
int coord_x = pos.x; // this is how you get the current coordinates
int coord_y = pos.y;
*current(&pos) = 12; // this is how you access the current cell
}
}

Resources