Okay, I think I sortof understand, though I still have some confusion.
Have you checked out
Sol's SDL tutorials?
In any case, let's establish some points first.
- When drawing anything to the screen, it is permanently "there" until you draw something over it.
- There is no such thing as "erasing" pixels. You can only draw over pixels with a different color.
- Every frame you should re-draw everything currently on-screen - it's the easiest way to do computer graphics.
Also, your previous code was doing alot of things wrong, now that I look over it a second time.
Here's a list of serious errors:
- You never freed your resources.
- You initialized Screen at a global scope.
- You never called SDL_Init.
- You never called SDL_Quit.
- You #undef main, apparently deciding SDL didn't know what it was doing.
That's the really bad stuff that could potentially break your program without you knowing why. There's also several bad practices (tons of global variables, for example), but those aren't actual errors like the list above.
[hr]
So, let's do a bit of code. First, our pixel putting and pixel getting functions shouldn't assume we are reading or writing pixels from the screen - we want to be able to read and write to any SDL_Surface (also, we don't want global variables, so the read and write pixel functions shouldn't know about the screen surface anyway).
My SDL is a bit rusty, but (editing your original code) this should work:
typedef unsigned int PixelColor;
void WritePixel(unsigned int x, unsigned int y, PixelColor color, SDL_Surface *surface)
{
PixelColor *pixels = static_cast<PixelColor*>(surface->pixels);
int offset = y * (surface->pitch / sizeof(unsigned int));
pixels[offset + x] = color;
}
unsigned int ReadPixel(unsigned int x, unsigned int y, SDL_Surface *surface)
{
PixelColor *pixels = static_cast<PixelColor*>(surface->pixels);
int offset = y * (surface->pitch / sizeof(unsigned int));
return pixels[offset + x];
}
//Make sure you lock surfaces before editing their pixels.
void LockSurface(SDL_Surface *surface)
{
if(SDL_MUSTLOCK(surface))
{
SDL_LockSurface(surface);
}
}
//Make sure you unlock surfaces once you are done editing their pixels.
void UnlockSurface(SDL_Surface *surface)
{
if(SDL_MUSTLOCK(surface))
{
SDL_UnlockSurface(surface);
}
}
I actually don't have SDL on my computer, and so I can't test anything I'm writing here. You might need to tweak some of the code to get it to work.
We'll also want these, for convenience and code clarity:
//Stolen from Lazy Foo's SDL tutorial.
//See here: http://lazyfoo.net/SDL_tutorials/lesson03/windows/devcpp/index.php
//And here: http://lazyfoo.net/SDL_tutorials/lesson05/index.php
SDL_Surface *LoadSurface(const std::string &filepath, const SDL_Color &color = SDL_Color(0, 255, 255))
{
//The optimized image that will be used
SDL_Surface* optimizedImage = NULL;
//Load the image using SDL_image
SDL_Surface* loadedImage = IMG_Load( filename.c_str() );
//If the image loaded
if( loadedImage != NULL )
{
//Create an optimized image
optimizedImage = SDL_DisplayFormat( loadedImage );
//Free the old image
SDL_FreeSurface( loadedImage );
//If the image was optimized just fine
if( optimizedImage != NULL )
{
//Map the color key
Uint32 colorkey = SDL_MapRGB(optimizedImage->format, color.r, color.g, color.b);
//Set all pixels of color R 0, G 0xFF, B 0xFF to be transparent
SDL_SetColorKey( optimizedImage, SDL_SRCCOLORKEY, colorkey );
}
}
//Check if everything went fine.
if(optimizedImage == NULL)
{
//Report an error.
std::cerr << "LoadSurface() - Something went wrong loading '" << filepath << "'.\n"
<< " Error: " << SDL_GetError() << std::endl;
}
//Return the optimized image
return optimizedImage;
}
//Also stolen from Lazy Foo's SDL tutorial.
//See here: http://lazyfoo.net/SDL_tutorials/lesson02/index.php
void DrawSurface(int x, int y, SDL_Surface *source, SDL_Surface *destination)
{
//Make a temporary rectangle to hold the offsets
SDL_Rect offset;
//Give the offsets to the rectangle
offset.x = x;
offset.y = y;
//Blit the surface
SDL_BlitSurface( source, NULL, destination, &offset);
}
They are slightly modified versions of the functions found at
Lazy Foo's SDL tutorials; a set of tutorials you should go.
These two functions we'll also find useful:
void DrawRectangle(SDL_Surface *destination, const SDL_Color &color, SDL_Rect *rect = NULL)
{
SDL_FillRect(destination, rect, SDL_MapRGB(destination->format, color.r, color.g, color.b));
}
SDL_Surface *CreateEmptySurface(int width, int height, SDL_Surface *surfaceToCompareTo)
{
SDL_Surface *newSurface = SDL_CreateRGBSurface(surfaceToCompareTo->flags, int width, int height, surfaceToCompareTo->format->BitsPerPixel,
surfaceToCompareTo->format->Rmask, surfaceToCompareTo->format->Gmask,
surfaceToCompareTo->format->Bmask, surfaceToCompareTo->format->Amask);
if(!newSurface)
{
std::cerr << "CreateEmptySurface() - Something went wrong creating a " << width << "x" << height << " surface.\n"
<< " Error: " << SDL_GetError() << std::endl;
}
//Let's start off filling it with a solid color, like solid white.
DrawRectangle(newSurface, SDL_Color(255, 255, 255));
return newSurface;
}
[hr]
Here's what your code should actually look like, if done properly:
int main(int argc, char *argv[])
{
//----------------------------------
//Start up SDL.
SDL_Init(SDL_INIT_EVERYTHING);
//----------------------------------
//----------------------------------
//Create the window.
SDL_Surface *screen = SDL_SetVideoMode(800, 600, 32, SDL_SWSURFACE);
//----------------------------------
//----------------------------------
//Load resources.
//Here we are loading an image just to show how.
//'exampleImage.png' is being looked for in the same folder that the program's executable is in.
SDL_Surface *exampleImage = LoadSurface("./exampleImage.png");
//----------------------------------
//----------------------------------
//Main loop:
bool done = false;
//Loop until we are done.
while(!done)
{
//----------------------------------
//Handle user input.
while(SDL_PollEvent(&event))
{
switch(event.type)
{
case SDL_QUIT:
{
//Don't do return 0 here, set the 'done' bool to true, so we exit properly and free our resources.
done = true;
}
break;
}
}
//----------------------------------
//Handle time-based updating.
//----------------------------------
//Handle logic/thinking.
//----------------------------------
//Draw everything.
{
//Reset the screen to all black.
DrawRectangle(screen);
//Draw any images.
DrawSurface(50, 50, exampleImage, screen);
//Update the screen. Anything drawn this frame now takes effect.
SDL_Flip(screen);
}
//----------------------------------
//----------------------------------
//Delay a little, so we don't take 100% CPU usage.
SDL_Delay(0);
//----------------------------------
}
//----------------------------------
//----------------------------------
//Free resources. Anything we load _must_ be freed - it's one of the marks of a good programmer.
//Any surfaces we load or create (except for 'screen'!) needs to be freed.
SDL_FreeSurface(exampleImage);
//----------------------------------
//----------------------------------
//Shut down SDL.
SDL_Quit(); //SDL_Quit() also frees 'screen' for us, so we don't need to.
//----------------------------------
return 0;
}
[hr]
Moving on to the next portion: Proper timing.
Really, I'll just link to another Lazy Foo tutorial:
Frame indpendant movement (lesson 32).
Here's the code I'm adding for time management. I find time is easier to manipulate if it's in a float or double, but that's a matter of preference.
float SecondsSinceLastFrame()
{
static Uint32 lastFrameTime = 0;
//Get the current time.
Uint32 currentTime = SDL_GetTicks();
Uint32 timeDifference = (currentTime - lastFrameTime);
if(timeDifference > 0)
{
//Store the new current time.
lastFrameTime = currentTime;
//Convert from millaseconds to seconds, and return as a float.
return (float(timeDifference) / 1000.0f);
}
else
{
return 0.0f;
}
}
[hr]
Now we can manipulate pixels based on time.
I'm going to create a new surface to hold what we are manipulating, rather than drawing on the screen directly.
SDL_Surface *waterImage = CreateEmptySurface(50, 50, screen);
Every 1/10th of a second, I'm going to update the water image with this function:
void MakeWaterSurface(SDL_Surface *surface, unsigned int frame)
{
//We only have 20 frames, so keep within range.
frame %= 20;
int offset = frame;
if(offset >= 10)
{
//0 1 2 3 4 5 6 7 8 9 10 9 8 7 6 5 4 3 2 1
offset = (10 - (offset - 10));
}
SDL_Color colors[] = {SDL_Color(100, 150, 190),SDL_Color(100, 160, 180),SDL_Color(100, 170, 170),SDL_Color(100, 180, 160),SDL_Color(100, 190, 150),
SDL_Color(100, 190, 170),SDL_Color(100, 190, 190),SDL_Color(100, 190, 210),SDL_Color(100, 190, 230),SDL_Color(100, 220, 250)};
for(int y = 0; y < surface->height; y++)
{
for(int x = 0; x < surface->height; x++)
{
//I'm coding blind without a compiler, so I have completely no idea if this will work or not.
int waveDepth = ((y + offset) % 10);
int waveHorizontalOffset = ((x + (offset/3)) % 20);
int waveVerticalOffset = ((x + waveHorizontalOffset) + offset);
unsigned colorIndex = (waveDepth + waveVerticalOffset) % 10;
WritePixel(x, y, colors[colorIndex], surface);
}
}
}
Not the best looking water in the world, I'm just showing you the basics of how it could be done.
I'm assuming it's not the best looking water in the world, anyway, since I can't compile it to see how it looks.
Not only that, but I'm also really sleepy and not thinking straight, so double the chance of mistakes.
Here's the whole main body again, with the additions:
const int NumWaterFrames = 20;
const int HalfOfWaterFrames = (NumWaterFrames/2);
const int NumColors = 10;
void MakeWaterSurface(SDL_Surface *surface, unsigned int frame)
{
//We only have 20 frames, so keep within range.
frame %= NumWaterFrames;
int offset = frame;
if(offset >= HalfOfWaterFrames)
{
//0 1 2 3 4 5 6 7 8 9 10 9 8 7 6 5 4 3 2 1
offset = (HalfOfWaterFrames - (offset - HalfOfWaterFrames));
}
SDL_Color colors[] = {SDL_Color(100, 150, 190),SDL_Color(100, 160, 180),SDL_Color(100, 170, 170),SDL_Color(100, 180, 160),SDL_Color(100, 190, 150),
SDL_Color(100, 190, 170),SDL_Color(100, 190, 190),SDL_Color(100, 190, 210),SDL_Color(100, 190, 230),SDL_Color(100, 220, 250)};
for(int y = 0; y < surface->height; y++)
{
for(int x = 0; x < surface->height; x++)
{
//I'm coding blind without a compiler, so I have completely no idea if this will work or not.
int waveDepth = ((y + offset) % NumColors);
int waveHorizontalOffset = ((x + (offset/3)) % NumWaterFrames);
int waveVerticalOffset = ((x + waveHorizontalOffset) + offset);
unsigned colorIndex = (waveDepth + waveVerticalOffset) % NumColors;
WritePixel(x, y, colors[colorIndex], surface);
}
}
}
int main(int argc, char *argv[])
{
//----------------------------------
//Start up SDL.
SDL_Init(SDL_INIT_EVERYTHING);
//----------------------------------
//----------------------------------
//Create the window.
SDL_Surface *screen = SDL_SetVideoMode(800, 600, 32, SDL_SWSURFACE);
//----------------------------------
//----------------------------------
//Load resources.
//Here we are loading an image just to show how.
//'exampleImage.png' is being looked for in the same folder that the program's executable is in.
SDL_Surface *exampleImage = LoadSurface("./exampleImage.png");
SDL_Surface *waterImage = CreateEmptySurface(40, 40, screen);
//----------------------------------
//----------------------------------
//Main loop:
bool done = false;
float accumulatedWaterTime = 0.0f;
unsigned int currentWaterFrame = 0;
//Loop until we are done.
while(!done)
{
//----------------------------------
//Handle user input.
while(SDL_PollEvent(&event))
{
switch(event.type)
{
case SDL_QUIT:
{
//Don't do return 0 here, set the 'done' bool to true, so we exit properly and free our resources.
done = true;
}
break;
}
}
//----------------------------------
//Handle time-based updating.
float delta = SecondsSinceLastFrame();
if(delta > 0.0001f)
{
accumulatedWaterTime += delta;
bool updateSurface = false;
while(accumulatedWaterTime > 0.1f) //One tenth of a second.
{
currentWaterFrame++;
if(currentWaterFrame >= NumWaterFrames)
{
currentWaterFrame %= NumWaterFrames;
}
updateSurface = true;
}
if(updateSurface)
{
MakeWaterSurface(waterImage, currentWaterFrame);
}
}
//----------------------------------
//----------------------------------
//Handle logic/thinking.
//----------------------------------
//----------------------------------
//Draw everything.
{
//Reset the screen to all black.
DrawRectangle(screen);
//Draw any images.
DrawSurface(50, 50, exampleImage, screen);
//Update the screen. Anything drawn this frame now takes effect.
SDL_Flip(screen);
}
//----------------------------------
//----------------------------------
//Delay a little, so we don't take 100% CPU usage.
SDL_Delay(10);
//----------------------------------
}
//----------------------------------
//----------------------------------
//Free resources. Anything we load _must_ be freed - it's one of the marks of a good programmer.
//Any surfaces we load or create (except for 'screen'!) needs to be freed.
SDL_FreeSurface(exampleImage);
//----------------------------------
//----------------------------------
//Shut down SDL.
SDL_Quit(); //SDL_Quit() also frees 'screen' for us, so we don't need to.
//----------------------------------
return 0;
}
[hr]
Any questions about any of that? It's alot to chew at once, I'm sure.
I strongly suggest reading
Lazy Foo's SDL tutorials, for starters, and following it up with
Sol's SDL tutorials.