I am making use of what looks like a bit of a cheat of SDL2's SDL_Rect struct. In my program I wanted a rectangle to "grow" from the right to the left side of the screen. To do this I tried to decrement the width attribute of the rect below zero and, in fact, it has the correct effect. However, I am left with the question of is this correct, portable behaviour. I understand that in the real world it does not make sense to have a rectangle with a negative width. However, it seems that the SDL developers may have intended for this to be the case. In SDL1.2 the SDL_Rect structure was defined as using an unsigned integer to represent the width and height whereas in SDL2 the SDL_Rect uses signed integers. Included below is code that exhibits this "cheat", error checking omitted for brevity.
#include <stdio.h>
#include <stdbool.h>
#include <SDL2/SDL.h>
#define SCREEN_W 640
#define SCREEN_H 480
int main(int argc, char** argv)
{
SDL_Init(SDL_INIT_VIDEO)
SDL_Window* window = SDL_CreateWindow("test", 110, 110, SCREEN_W, SCREEN_H, 0);
SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_PRESENTVSYNC);
SDL_Rect rects[5];
for(int i = 0; i < 5; i++)
{
rects[i].w = 0;
rects[i].h = (SCREEN_H / 5);
rects[i].x = (i % 2) * SCREEN_W;
rects[i].y = i * (SCREEN_H / 5);
}
SDL_Event e;
bool running = true;
while(running)
{
while (SDL_PollEvent(&e)) {
if (e.type == SDL_QUIT)
{
running = false;
}
}
SDL_SetRenderDrawColor(renderer, 255, 255, 255, SDL_ALPHA_OPAQUE);
SDL_RenderClear(renderer);
SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
for(int i = 0; i < 5; i++)
{
SDL_RenderFillRect(renderer, &rects[i]);
if (i % 2 == 0)
{
rects[i].w++;
}
else
{
rects[i].w--;
}
}
if (rects[0].w == SCREEN_W)
{
for(int i = 0; i < 5; i++)
{
rects[i].w = 0;
}
}
SDL_RenderPresent(renderer);
}
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
return EXIT_SUCCESS;
}
I will reiterate, I do not think this invokes any undefined behaviour but I am unsure if this is portable behaviour within the context of SDL2. Should I expect my code to work on everyone's machine today and going forward?
Related
I'm just trying to write a program to draw to a window using SDL. I'm trying to use rects to draw, and I thought that if I just don't clear the screen then I could leave a trail of where a rect has been whilst changing its position. However this causes the window to glitch out like mad and so I came up with another solution wherein I would have an array of Rects that is added to as the program draws and then each frame it will go through the array and render each rect to the window in the correct place. However, when trying to render the rects from the array it seems that they are all at the same position, and not in the position they were set to before being added to the array?
#include <SDL2/SDL.h>
#define SCREEN_WIDTH 1920
#define SCREEN_HEIGHT 1080
SDL_Rect *rects[200] = {};
int get_rect_count()
{
int i;
for (i = 0; i < sizeof(rects) / sizeof(rects[0]); i++)
{
if (rects[i] == NULL)
break;
}
return i;
}
void draw_rects(SDL_Renderer *renderer)
{
SDL_SetRenderDrawColor(renderer, 0, 255, 255, 255);
SDL_RenderClear(renderer);
SDL_SetRenderDrawColor(renderer, 241, 194, 125, 255);
for (int i = 0; i < get_rect_count(); i++)
{
SDL_RenderDrawRect(renderer, rects[i]);
SDL_RenderFillRect(renderer, rects[i]);
}
SDL_RenderPresent(renderer);
}
void ball(int sp_x, int sp_y, SDL_Renderer *renderer)
{
for (int i = 0; i < 3; i++)
{
SDL_Rect rect;
int offset = i * 10;
rect.x = sp_x + offset;
rect.y = sp_y - offset;
rect.w = 10;
rect.h = 10;
rects[i] = ▭
draw_rects(renderer);
SDL_Delay(200);
}
}
int main(int argc, char *args[])
{
SDL_Window *window = NULL;
SDL_Renderer *renderer = NULL;
if (SDL_Init(SDL_INIT_VIDEO) < 0)
printf("sdl failed lol");
else
{
window = SDL_CreateWindow("window", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, 0);
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
SDL_SetRenderDrawColor(renderer, 0, 255, 255, 255);
SDL_RenderClear(renderer);
SDL_RenderPresent(renderer);
SDL_Delay(750);
ball(SCREEN_WIDTH / 8, SCREEN_HEIGHT * 0.75, renderer);
SDL_Delay(1000);
}
return 0;
}
One major problem is here:
for (int i = 0; i < 3; i++)
{
SDL_Rect rect;
Here the variable rect is local inside the loop. Once the loop iterates, the life-time of rect ends and it ceases to exist. Any pointers to it that you have saved will become invalid, and dereferencing these pointers will lead to undefined behavior.
Now that's the theory. What probably happens in reality is that the compiler will reuse the memory for rect, meaning that all pointers will be to one single SDL_Rect structure, which you overwrite each iteration in the loop. So you end up with all rectangles being set to the values of the very last rectangle.
One possible way to solve your problem is to allocate rectangles dynamically using malloc. You of course have to remember to free them once you're finished.
Another solution is to use an array of SDL_Rect structure objects, not pointers, and keep track of its "size" (number of used elements) with another variable.
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??
Having trouble blitting an image in SDL2. I built an array of SDL_Rect using malloc and attributed to each member the usual x,y,w (width), and h (height) variables, but the images simply won't show up. Curiously, when I try blitting outside of a for loop by directly referencing the index of the element, it seems to work. Any idea what's going on?
//Not Working
int number =5;
for(int i = 0; i < number; i++){
SDL_BlitScaled(myImage, NULL, mySurface, &(myElements[i]));
}
//Works Perfectly
SDL_BlitScaled(myImage, NULL, mySurface, &(myElements[0]));
Edit: Ended up forgetting this part. Here's how the array is being generated:
SDL_Rect *myElements;
myElements = (SDL_Rect * ) malloc (number * sizeof(SDL_Rect));
generateGroup(myElements, number, 10, 10, 180, 30);
The function I am calling there looks like this:
void generateGroup(SDL_Rect* elementArray, int numberElements, int posX, int posY, int width, int height) {
int i;
for(i = 0; i < numberElements; i++) {
elementArray[i].x = posX;
posX = posX + width + 1;
elementArray[i].y = posY;
elementArray[i].w = width;
elementArray[i].h = height;
}
}
To describe the issue more precisely, the image literally only appears when I blit outside the for loop. As for the index, in addition to 0, I have also tried 1 and 2, and both worked.
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, ¤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;
}
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.)
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.