Hey, thanks for the quick reply! Yes, that's what I thought too... but in the constructors I set the values, and printing them to the console after initializing them shows the values I would expect - it's during the draw method that they seem to go random... could it be how SDL_Rect is initialized? Maybe it isn't allowed as a class property or something (though I couldn't find that in the documentation anywhere). Or maybe it should be a pointer to an SDL_Rect and created in the constructor with "new"?
Either way, here's what I'm guessing is the code involved:
// --------------------------------------------------------
// GameObject.h
// --------------------------------------------------------
#include "SDL.h"
#include "Animation.h"
#ifndef GAMEOBJECT_H_
#define GAMEOBJECT_H_
//////////////////////////////////////////////////////////////////////////////////////////////////////
// DECLARATION
//////////////////////////////////////////////////////////////////////////////////////////////////////
class GameObject
{
public:
int x, y, w, h;
GameObject(int x, int y, int w, int h);
void set_animation(Animation * a);
void Draw(SDL_Surface * destination);
bool collision(GameObject * other);
void destroy();
protected:
SDL_Surface * sprite;
SDL_Rect rect;
Animation * animation;
bool _destroyed;
};
//////////////////////////////////////////////////////////////////////////////////////////////////////
// DEFINITION / IMPLEMENTATION
//////////////////////////////////////////////////////////////////////////////////////////////////////
GameObject::GameObject(int x, int y, int w, int h)
{
x = x;
y = y;
w = w;
h = h;
rect.x = x;
rect.y = y;
rect.w = w;
rect.h = h;
_destroyed = false;
}
void GameObject::Draw(SDL_Surface * destination)
{
if (!_destroyed)
{
// Update the object's position
rect.x = x;
rect.y = y;
// In an effort to figure out why the heck new objects added to the vector were not being drawn,
// I created this code
std::cout << rect.x << " ";
std::cout << rect.y << " ";
std::cout << rect.w << " ";
std::cout << rect.h << std::endl;
// And update the animation
animation->update(destination, rect);
}
}
void GameObject::set_animation(Animation * a)
{
animation = a;
}
bool GameObject::collision(GameObject * other)
{
return ((x > other->x && x < other->x + other->w && y > other->y && y < other->y + other->h)
|| (x + w > other->x && x + w < other->x + other->w && y > other->y && y < other->y + other->h)
|| (x > other->x && x < other->x + other->w && y + h > other->y && y + h < other->y + h + other->h)
|| (x + w > other->x && x + w < other->x + other->w && y + h > other->y && y + h < other->y + h + other->h));
}
void GameObject::destroy()
{
_destroyed = true;
delete this;
}
#endif
// --------------------------------------------------------
// Animation.h
// --------------------------------------------------------
#ifndef ANIMATION_H_
#define ANIMATION_H_
#include "SDL.h"
#define ANIMATION_HORIZONTAL 0
#define ANIMATION_VERTICAL 1
//////////////////////////////////////////////////////////////////////////////////////////////////////
// DECLARATION
//////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* This class handles the math and SDL stuff involved in looping through an animation
* @todo Add events like pause(), stop() etc. If possible, add a setter for speed, and
* maybe a way to set the pic (for objects that will use multiple instances of this).
*/
class Animation
{
public:
/**
* Constructor: init instance variables
* @param[in] The image file and/or path
* @param[in] The width of a single pic in the animation
* @param[in] The height of a single pic
* @param[in] One of the constants #defined above - Tells the Update function whether to move the X or Y
* @param[in] The total number of pics in the animation
* @remarks This class assumes a single "strip" of images, either horizontal or vertical, with no gaps
* between them (for now). This is what the direction and total are for, so it "knows" how to run
* the animation. If either of these is off, the image will flicker as class loops through the pics.
*/
Animation(const char * sprite_file, int width, int height, int direction, int total, int speed);
/**
* Shows the next pic in the animation
* @param[in] The surface we're drawing to
* @param[in] The rectangle where the animation should be positioned on the screen
*/
void update(SDL_Surface * destination, SDL_Rect object_rect);
private:
SDL_Surface * sprite;
SDL_Rect rect;
int direction, total, index, speed, next;
};
//////////////////////////////////////////////////////////////////////////////////////////////////////
// DEFINITION / IMPLEMENTATION
//////////////////////////////////////////////////////////////////////////////////////////////////////
Animation::Animation(const char * sprite_file, int width, int height, int direction, int total, int speed)
{
// Save references to the ints
direction = direction;
total = total;
speed = speed;
next = speed; // Used in update
// Set the starting index to 0;
index = 0;
// Init the rect property
rect.x = 0;
rect.y = 0;
rect.w = width;
rect.h = height;
// Load the sprite
sprite = NULL;
sprite = SDL_LoadBMP(sprite_file);
if (this->sprite == NULL)
std::cout << "Oh crap, something went wrong. Fight with this later.\n";
}
void Animation::update(SDL_Surface * destination, SDL_Rect object_rect)
{
// Another part of the debugging attempt
// When I create the first object, its index starts at 0, where I expect it
// But when I create the second, the index is 4741476 (for only God and Bjourne Stroustrup know why)
// So clearly it's undefined behavior, but why? The index is initialized to 0 in the constructor, and
// so is the total
//std::cout << index << " " << std::endl;
// Update the index
index++; // The debugger points to this line as the problem
// I've tried commenting out all this - the problem is not here.
if (index >= total * speed)
{
index = 0;
rect.x = 0;
rect.y = 0;
next = speed;
}
else if (index == next)
{
next += this->speed;
if (direction == ANIMATION_HORIZONTAL)
rect.x += rect.w;
else
rect.y += rect.h;
}
// And update the animation
SDL_BlitSurface(sprite, &rect, destination, &object_rect);
}
#endif
// --------------------------------------------------------
// Main.cpp
// --------------------------------------------------------
#include <iostream>
#include "Game.h"
#include <iostream>
#include "SDL.h"
#include "Window.h"
#include "GameObject.h"
#include "Game.h"
#include "Animation.h"
#include "Controller.h"
// Global variables
bool left_pressed = false, right_pressed = false, up_pressed = false, down_pressed = false;
Animation player_left("bin\\debug\\graphics\\player_left.bmp", 32, 32, ANIMATION_VERTICAL, 8, 10);
Animation player_right("bin\\debug\\graphics\\player_right.bmp", 32, 32, ANIMATION_VERTICAL, 8, 10);
Animation player_up("bin\\debug\\graphics\\player_up.bmp", 32, 32, ANIMATION_HORIZONTAL, 8, 10);
Animation player_down("bin\\debug\\graphics\\player_down.bmp", 32, 32, ANIMATION_HORIZONTAL, 8, 10);
GameObject player(468, 544, 32, 32);
void GameLoop()
{
// Update the player's position
if (left_pressed)
player.x -= 2;
else if (right_pressed)
player.x += 2;
if (up_pressed)
player.y -= 2;
else if (down_pressed)
player.y += 2;
// And slow things down a sec
SDL_Delay(10); //For now - I'll have to come up with another way to make characters move slowly later
}
void KeyDownEventHandler(int key)
{
if (key == LEFT)
{
player.set_animation(&player_left);
left_pressed = true;
}
else if (key == RIGHT)
{
player.set_animation(&player_right);
right_pressed = true;
}
else if (key == UP)
{
player.set_animation(&player_up);
up_pressed = true;
}
else if (key == DOWN)
{
player.set_animation(&player_down);
down_pressed = true;
}
}
void KeyUpEventHandler(int key)
{
if (key == LEFT)
left_pressed = false;
else if (key == RIGHT)
right_pressed = false;
else if (key == UP)
up_pressed = false;
else if (key == DOWN)
down_pressed = false;
else if (key == SPACE)
{
// This is where I'm creating the instances at runtime - the player presses Space and creates a new GameObject instance
// Same constructor, same Game object, different GameObject and Animation objects.
Game * game = Game::Get();
Animation player_left2("bin\\debug\\graphics\\player_left.bmp", 32, 32, ANIMATION_VERTICAL, 8, 10);
GameObject test(32, 32, 32, 32);
test.set_animation(&player_left2);
game->AddObject(&test);
}
}
int WinMain() // I'm on Windows, and apparently SDL wants WinMain, as calling it "main" causes compiler tantrums
{
Controller::OnKeyUp = KeyUpEventHandler;
Controller::OnKeyDown = KeyDownEventHandler;
Game * game = Game::Get();
game->CreateWindow("Game Title", 800, 600);
// Create our player animation
player.set_animation(&player_left);
game->AddObject(&player);
game->Start(GameLoop);
return 0;
}
So after doing all kinds of experiments with this code to try and guess out what possible reason there could be for the randomness, the only guess I got is maybe the object's SDL_Rect... can you see anything else it might be? Thanks.