i've been working on a game project, its a slider platformer and i've been having problem with getting the movement working, the movement is time based instead of being fixed to the frames per secound so the movement is consistent. the movement was working when i was testing out the algorithm lazily in the main file but i've put it all into a class that handles movement and collision detection and now it doesn't want to work, instead of object moving smoothly around the screen it goes flying right off the screen instead.
i've tried to find the root cause of the problem with no luck, with some help from tnbforum i've found that commenting out "Clock.Start()" in the main loop it all works, the problem with doing this is that the framerate is no longer regulated. in a frustrating effort to get to the cause of the problem i've been logging the values of the movement speed and object position which hasn't really helped, the calculations are all working but it seems to be breaking around when drawing the charcter but i'm not sure where abouts it is failing, is it with returning a reference to the collisionbox or is it when updating the collision box position?
i have done a lot of searching with no real luck on results.
source, going to be abit long:
timer.h:
//timer.h
#ifndef TIMER_H
#define TIMER_H
#include "SDL/SDL.h" //for Uint32
class Timer
{
public:
Timer();
~Timer();
bool Started();
bool Paused();
void Stop();
void Start();
void Pause();
void Continue();
int GetTime();
private:
int ticks;
int Pticks;
bool started;
bool paused;
};
#endif // TIMER_H
timer.cpp:
//timer.cpp
#include "timer/timer.h"
#include "SDL/SDL.h"
Timer::Timer() : ticks(0), Pticks(0), started(false), paused(false)
{
//if the SDL subsystem timer isn't active then activate it.
if(SDL_WasInit(SDL_INIT_TIMER) == 0) SDL_InitSubSystem(SDL_INIT_TIMER);
}
Timer::~Timer(){/* */}
bool Timer::Started(){ return started; }
bool Timer::Paused(){ return paused; }
void Timer::Start()
{
started = true;
paused = false;
ticks = SDL_GetTicks();
}
void Timer::Stop()
{
started = false;
paused = false;
ticks = 0;
Pticks = 0;
}
void Timer::Pause()
{
if(started == true){
paused = true;
Pticks = SDL_GetTicks() - ticks;
}
}
void Timer::Continue()
{
if(started == true && paused == true){
paused = false;
ticks = SDL_GetTicks() - Pticks;
}
}
int Timer::GetTime()
{
int time = 0;
if(started == true){
if(paused == true) time = Pticks;
else time = SDL_GetTicks() - ticks;
}
return time;
}
inputevent.h:
//InputEvent.h
#ifndef INPUT_HANDLER
#define INPUT_HANDLER
#include "SDL/SDL.h"
class KeyBoardInput{
public:
KeyBoardInput();
~KeyBoardInput();
void RefreshKeyState();
bool WindowClose();
bool GetKeyState(SDLKey Key);
private:
SDL_Event Input;
Uint8* KeyState;
};
#endif // INPUT_HANDLER
inputevent.cpp
//InputEvent.cpp
#include "InputEvent.h"
KeyBoardInput::~KeyBoardInput(){ /* destructor */ }
KeyBoardInput::KeyBoardInput() : KeyState(SDL_GetKeyState(NULL)) { /* constructor */}
void KeyBoardInput::RefreshKeyState()
{
//update input information
SDL_PollEvent(&Input);
}
bool KeyBoardInput::WindowClose()
{
if(Input.type == SDL_QUIT) return true;
return false;
}
bool KeyBoardInput::GetKeyState(SDLKey Key)
{
if(KeyState[Key] == 1) return true;
return false;
}
graphics.h:
//Graphics.h
#ifndef GRAPHICS_CONTROL
#define GRAPHICS_CONTROL
#include "SDL/SDL.h"
#include "SDL/SDL_image.h"
struct Gposition{
int X;
int Y;
};
struct Gclip{
int X, Y;
int H, W;
};
struct Gsprite{
Gclip* Crop;
Gposition* Pos;
SDL_Surface* Sprite;
};
class Graphics{
public:
enum{SUCCESS, ERROR};
~Graphics();
Graphics();
int Load(int ScreenWidth, int ScreenHeight, int BPP, const char* WindowTitle, const char* WindowIcon);
void SetBackgroundColour(int Red, int Green, int Blue);
int LoadImage(Gsprite& Image, const char* ImagePath);
void CloseImage(Gsprite& Image);
void DrawImage(const Gsprite& Image);
void StartFrame();
void ShowFrame();
//group camera set up or adjust
void SetCameraPosition(int X, int Y);
void SetCameraDimensions(int W, int H);
void SetUpCamera(int X, int Y, int W, int H);
//return a copy of the camera
Gclip GetCamera();
private:
//variables to store, modify and display the screen
SDL_Surface* Screen;
SDL_Rect ImagePos;
SDL_Rect Crop;
//camera to adjust image positions based on camera position
Gclip Camera;
//red, green, blue variables to hold the background colour
int BackgroundR;
int BackgroundG;
int BackgroundB;
};
#endif // GRAPHICS_CONTROL
graphics.cpp:
//Graphics.cpp
#include "Graphics.h"
Graphics::~Graphics(){ SDL_Quit(); }
Gclip Graphics::GetCamera(){ return Camera; }
void Graphics::ShowFrame(){ SDL_Flip(Screen); }
Graphics::Graphics()
{
//initialize SDL video and set defaults
Screen = NULL;
SDL_Init(SDL_INIT_VIDEO);
Camera.X = 0;
Camera.Y = 0;
Camera.H = 0;
Camera.W = 0;
}
int Graphics::Load(int ScreenWidth, int ScreenHeight, int BPP,
const char* WindowTitle, const char* WindowIcon)
{
//create the window
Screen = SDL_SetVideoMode(ScreenWidth, ScreenHeight, BPP, SDL_HWSURFACE | SDL_DOUBLEBUF);
//if the window failed to create then exit
if(Screen == NULL) return ERROR;
SDL_WM_SetCaption(WindowTitle, WindowIcon);
//set camera width and height to the screens dimensions
Camera.W = ScreenWidth;
Camera.H = ScreenHeight;
return SUCCESS;
}
void Graphics::SetBackgroundColour(int Red, int Green, int Blue)
{
BackgroundR = Red;
BackgroundG = Green;
BackgroundB = Blue;
}
int Graphics::LoadImage(Gsprite& Image, const char* ImagePath)
{
//load images using the alpha channel
Image.Sprite = IMG_Load(ImagePath);
if(Image.Sprite != NULL) Image.Sprite = SDL_DisplayFormatAlpha(Image.Sprite);
else return ERROR;
return SUCCESS;
}
void Graphics::CloseImage(Gsprite& Image)
{
//wrapper for SDL freesurface to close images
SDL_FreeSurface(Image.Sprite);
}
void Graphics::DrawImage(const Gsprite& Image)
{
//adjusts image position based on the camera position
//the camera doesn't actually move but the world does
//which works by subtracting camera position from the image position
ImagePos.x = Image.Pos->X - Camera.X;
ImagePos.y = Image.Pos->Y - Camera.Y;
//if there image is cropped then
//convert my Gclip struct to SDL_rect struct
//and then attach the image to the screen else
//attach the image to the screen without the crop
if(Image.Crop != NULL){
Crop.x = Image.Crop->X;
Crop.y = Image.Crop->Y;
Crop.h = Image.Crop->H;
Crop.w = Image.Crop->W;
SDL_BlitSurface(Image.Sprite, &Crop, Screen, &ImagePos); //with crop
}else SDL_BlitSurface(Image.Sprite, NULL, Screen, &ImagePos); //without crop
}
void Graphics::StartFrame()
{
//fill the screen with the background colour
//best for erasing what was on the screen in the previous frame
SDL_FillRect(Screen, NULL,
SDL_MapRGB(Screen->format, BackgroundR, BackgroundG, BackgroundB));
}
void Graphics::SetCameraPosition(int X, int Y)
{
Camera.X = X;
Camera.Y = Y;
}
void Graphics::SetCameraDimensions(int W, int H)
{
Camera.W = W;
Camera.H = H;
}
void Graphics::SetUpCamera(int X, int Y, int W, int H)
{
Camera.X = X;
Camera.Y = Y;
Camera.W = W;
Camera.H = H;
}
physics.h:
//Physics.h
#ifndef PHYSICS_H
#define PHYSICS_H
#include <iostream>
#include <fstream>
#include "SDL/SDL.h" //for Unit32
struct CollBox{
float MinX, MinY;
float MaxX, MaxY;
};
class Physics{
public:
enum Dir{LEFT, RIGHT, UP, DOWN};
Physics();
~Physics();
void SetSpeed(int Left, int Right, int Up, int Down);
void SetUpPolygon(int MinX, int MinY, int MaxX, int MaxY);
void SetAcceleration(int Left, int Right, int Up, int Down);
void Update();
void Move(Dir Direction, Uint32 Dtime);
void StopMoving(Uint32 Dtime);
void StopMovingUp(Uint32 Dtime);
void StopMovingLeft(Uint32 Dtime);
void StopMovingDown(Uint32 Dtime);
void StopMovingRight(Uint32 Dtime);
/*void MoveVertical(Dir, Uint32);
void MoveHorizontal(Dir, Uint32);*/
void SpringBack(const CollBox& Object);
bool CheckCollision(const CollBox& Object);
CollBox& GetPolygon();
private:
CollBox Polygon;
float Xvel, Yvel;
int MaxL, MaxR;
int MaxU, MaxD;
int AccelL, AccelR;
int AccelU, AccelD;
int SpeedHoriz, SpeedVertic;
static const float Secs = 1000.0f;
std::ofstream Log;
};
#endif // PHYSICS_H
physics.cpp:
//Physics.h
#include <iostream>
#include <fstream>
#include "Physics/Physics.h"
#include "SDL/SDL.h" //for Uint32
Physics::~Physics(){ if(Log.is_open()) Log.close(); }
CollBox& Physics::GetPolygon(){ return Polygon; }
Physics::Physics() : Xvel(0.0f), Yvel(0.0f),
MaxL(0), MaxR(0), MaxU(0), MaxD(0),
AccelL(0), AccelR(0), AccelU(0), AccelD(0), SpeedHoriz(0), SpeedVertic(0)
{
Polygon.MinX = 0.0f;
Polygon.MinY = 0.0f;
Polygon.MaxX = 0.0f;
Polygon.MaxY = 0.0f;
Log.open("Log.txt");
}
void Physics::SetSpeed(int Left, int Right, int Up, int Down)
{
MaxL = Left;
MaxR = Right;
MaxU = Up;
MaxD = Down;
}
void Physics::SetUpPolygon(int MinX, int MinY, int MaxX, int MaxY)
{
Polygon.MaxX = MaxX;
Polygon.MaxY = MaxY;
Polygon.MinX = MinX;
Polygon.MinY = MinY;
}
void Physics::SetAcceleration(int Left, int Right, int Up, int Down)
{
AccelL = Left;
AccelR = Right;
AccelU = Up;
AccelD = Down;
}
void Physics::Update()
{
Log << "Before:" << std::endl
<< "Xvel: " << Xvel << " " << "Yvel: " << Yvel << std::endl
<< "MinX: " << Polygon.MinX << " " << "MinY: " << Polygon.MinY << std::endl;
//left or right
Polygon.MinX += Xvel;
Polygon.MaxX += Xvel;
//up or down
Polygon.MinY += Yvel;
Polygon.MaxY += Yvel;
Log << "After:" << std::endl
<< "Xvel: " << Xvel << " " << "Yvel: " << Yvel << std::endl
<< "MinX: " << Polygon.MinX << " " << "MinY: " << Polygon.MinY << std::endl << std::endl;
//Log << Xvel << " " << Yvel << std::endl;
}
void Physics::Move(Dir Direction, Uint32 Dtime)
{
if(Dtime == 0) return;
switch(Direction){
case UP : {
//move up, if speed is to high reduce to max
if(SpeedVertic != MaxU) SpeedVertic += AccelU;
if(SpeedVertic < MaxU) SpeedVertic = MaxU;
Yvel = SpeedVertic * (Dtime / Secs);
/*Yvel += AccelU * (Dtime / Secs);
if(Yvel < (MaxU * (Dtime / Secs))) Yvel = MaxU * (Dtime / Secs);*/
break;
}
case DOWN : {
//move down, if speed is to high reduce to max
if(SpeedVertic != MaxD) SpeedVertic += AccelD;
if(SpeedVertic > MaxD) SpeedVertic = MaxD;
Yvel = SpeedVertic * (Dtime / Secs);
/*Yvel += AccelD * (Dtime / Secs);
if(Yvel > (MaxD * (Dtime / Secs))) Yvel = MaxD * (Dtime / Secs);*/
break;
}
case LEFT : {
//move left, if speed is to high reduce to max
if(SpeedHoriz != MaxL) SpeedHoriz += AccelL;
if(SpeedHoriz < MaxL) SpeedHoriz = MaxL;
Xvel = SpeedHoriz * (Dtime / Secs);
/*Xvel += AccelL * (Dtime / Secs);
if(Xvel < (MaxL * (Dtime / Secs))) Xvel = MaxL * (Dtime / Secs);*/
break;
}
case RIGHT : {
//move right, if speed is to high reduce to max
if(SpeedHoriz != MaxR) SpeedHoriz += AccelR;
if(SpeedHoriz > MaxR) SpeedHoriz = MaxR;
Xvel = SpeedHoriz * (Dtime / Secs);
/*Xvel += AccelR * (Dtime / Secs);
if(Xvel > (MaxR * (Dtime / Secs))) Xvel = MaxR * (Dtime / Secs);*/
break;
}
default : break;
}
//Log << "Xvel: " << Xvel << " " << "Yvel: " << Yvel << std::endl;
}
void Physics::StopMoving(Uint32 Dtime)
{
if(Xvel == 0.0f && Yvel == 0.0f) return;
if(Dtime == 0) return;
StopMovingUp(Dtime);
StopMovingLeft(Dtime);
StopMovingDown(Dtime);
StopMovingRight(Dtime);
}
void Physics::StopMovingUp(Uint32 Dtime)
{
//if already not moving then return
if(Yvel == 0.0f) return;
if(Dtime == 0) return;
//if moving up decelerate down
if(SpeedVertic < 0){
SpeedVertic += AccelD;
Yvel = SpeedVertic * (Dtime / Secs);
if(Yvel > 0.0f) Yvel = 0.0f;
if(SpeedVertic > 0) SpeedVertic = 0;
}
/*if(Yvel < 0.0f){
Yvel += AccelD * (Dtime / Secs);
if(Yvel > 0.0f) Yvel = 0.0f;
}*/
}
void Physics::StopMovingLeft(Uint32 Dtime)
{
//if already not moving then return
if(Xvel == 0.0f) return;
if(Dtime == 0) return;
//if moving left decelerate right
if(SpeedHoriz < 0){
SpeedHoriz += AccelR;
Xvel = SpeedHoriz * (Dtime / Secs);
if(Xvel > 0.0f) Xvel = 0.0f;
if(SpeedHoriz > 0) SpeedHoriz = 0;
}
/*if(Xvel < 0.0f){
Xvel += AccelR * (Dtime / Secs);
if(Xvel > 0.0f) Xvel = 0.0f;
}*/
}
void Physics::StopMovingDown(Uint32 Dtime)
{
//if already not moving then return
if(Yvel == 0.0f) return;
if(Dtime == 0) return;
//if moving down decelerate up
if(SpeedVertic > 0){
SpeedVertic += AccelU;
Yvel = SpeedVertic * (Dtime / Secs);
if(Yvel < 0.0f) Yvel = 0.0f;
if(SpeedVertic < 0) SpeedVertic = 0;
}
/*if(Yvel > 0.0f){
Yvel += AccelU * (Dtime / Secs);
if(Yvel < 0.0f) Yvel = 0.0f;
}*/
}
void Physics::StopMovingRight(Uint32 Dtime)
{
//if already not moving then return
if(Xvel == 0.0f) return;
if(Dtime == 0) return;
//if moving right decelerate left
if(SpeedHoriz > 0){
SpeedHoriz += AccelL;
Xvel = SpeedHoriz * (Dtime / Secs);
if(Xvel < 0.0f) Xvel = 0.0f;
if(SpeedHoriz < 0) SpeedHoriz = 0;
}
/*if(Xvel > 0.0f){
Xvel += AccelL * (Dtime / Secs);
if(Xvel < 0.0f) Xvel = 0.0f;
}*/
}
void Physics::SpringBack(const CollBox& Object)
{
float X = 0;
float Y = 0;
//if the object is to the left or right else its above or below
if(Polygon.MinY <= Object.MaxY && Polygon.MaxY >= Object.MinY){
if(Xvel < 0.0f && Polygon.MinX < Object.MaxX) X = Object.MaxX - Polygon.MinX; //spring right
if(Xvel > 0.0f && Polygon.MaxX > Object.MinX) X = Object.MinX - Polygon.MaxX; //spring left
Xvel = 0;
}else if(Polygon.MinX <= Object.MaxX && Polygon.MaxX >= Object.MinX){
if(Yvel < 0.0f && Polygon.MinY < Object.MaxY) Y = Object.MaxY - Polygon.MinY; //spring down
if(Yvel > 0.0f && Polygon.MaxY > Object.MinY) Y = Object.MinY - Polygon.MaxY; //spring up
Yvel = 0;
}
Polygon.MinX += X;
Polygon.MaxX += X;
Polygon.MinY += Y;
Polygon.MaxY += Y;
}
bool Physics::CheckCollision(const CollBox& Object)
{
if(Polygon.MinX > Object.MaxX || Polygon.MaxX < Object.MinX) return false;
if(Polygon.MinY > Object.MaxY || Polygon.MaxY < Object.MinY) return false;
return true;
}
main.cpp:
//man.cpp
#include <iostream>
#include <fstream>
#include "SDL/SDL.h"
#include "timer/timer.h"
#include "SDL/SDL_image.h"
#include "Physics/Physics.h"
#include "Graphics/Graphics.h"
#include "input_handler/InputEvent.h"
class Character{
public:
Character(Graphics* Display, KeyBoardInput* input);
~Character();
void UpdatePos();
void Draw();
private:
Timer Delta;
Physics Box;
Gsprite Sprite;
Graphics* Screen;
KeyBoardInput* Input;
std::ofstream imgpos;
std::ofstream boxpos;
};
Character::Character(Graphics* Display, KeyBoardInput* input) : Screen(Display), Input(input)
{
Sprite.Crop = NULL;
Sprite.Sprite = NULL;
Sprite.Pos = NULL;
Sprite.Crop = new Gclip;
Sprite.Pos = new Gposition;
Sprite.Crop->H = 50;
Sprite.Crop->W = 40;
Sprite.Crop->Y = 1;
Sprite.Crop->X = 143;
Sprite.Pos->X = 0;
Sprite.Pos->Y = 0;
Box.SetSpeed(-300, 300, -300, 300);
Box.SetAcceleration(-50, 50, -50, 50);
Box.SetUpPolygon(0, 0, 40, 50);
Screen->LoadImage(Sprite, "images/grunt walking alpha.png");
imgpos.open("SpritePos.txt");
boxpos.open("BoxPos.txt");
}
Character::~Character()
{
delete Sprite.Crop;
delete Sprite.Pos;
Screen->CloseImage(Sprite);
imgpos.close();
boxpos.close();
}
void Character::UpdatePos()
{
Uint32 Dtime = Delta.GetTime();
//if(Dtime > 0) Log << Dtime << std::endl;
if(Input->GetKeyState(SDLK_UP) == true) Box.Move(Physics::UP, Dtime);
if(Input->GetKeyState(SDLK_UP) == false) Box.StopMovingUp(Dtime);
if(Input->GetKeyState(SDLK_LEFT) == true) Box.Move(Physics::LEFT, Dtime);
if(Input->GetKeyState(SDLK_LEFT) == false) Box.StopMovingLeft(Dtime);
if(Input->GetKeyState(SDLK_RIGHT) == true) Box.Move(Physics::RIGHT, Dtime);
if(Input->GetKeyState(SDLK_RIGHT) == false) Box.StopMovingRight(Dtime);
if(Input->GetKeyState(SDLK_DOWN) == true) Box.Move(Physics::DOWN, Dtime);
if(Input->GetKeyState(SDLK_DOWN) == false) Box.StopMovingDown(Dtime);
Box.Update();
Delta.Start();
}
void Character::Draw()
{
const CollBox& ImgPos = Box.GetPolygon();
Sprite.Pos->X = (int)ImgPos.MinX;
Sprite.Pos->Y = (int)ImgPos.MinY;
Screen->DrawImage(Sprite);
imgpos << "X: " << Sprite.Pos->X << " " << "Y: " << Sprite.Pos->Y << std::endl;
boxpos << "MinX: " << ImgPos.MinX << " " << "MinY: " << ImgPos.MinY << std::endl;
}
int main(int argC, char** argV)
{
Graphics* Screen = new Graphics;
if(Screen->Load(640, 480, 0, "Game Project", NULL) == Graphics::ERROR){
delete Screen;
return 1;
}
Screen->SetBackgroundColour(255, 255, 255);
KeyBoardInput* Input = new KeyBoardInput();
Character Player(Screen, Input);
Screen->StartFrame();
Player.Draw();
Screen->ShowFrame();
//30 frames per second
const int FPS = 1000 / 30;
Timer Clock;
Clock.Start();
do{
Input->RefreshKeyState();
Player.UpdatePos();
while(Clock.GetTime() < FPS){}
if(Clock.GetTime() >= FPS){
Screen->StartFrame();
Player.Draw();
Screen->ShowFrame();
Clock.Start();
}
}while(Input->WindowClose() != true);
delete Screen;
delete Input;
return 0;
}
thats my full source code, when i comment out "Clock.Start();" in main.cpp it works but it doesn't regulate framerate so its trying to draw a frame as soon as possible, without it commented the object just goes straight off the screen. i've also tried putting in a awhile loop just to pause things untill its time to draw a frame and it all works again so i wonder if this has to do with how fast the program is running? just a wild guess.
once the movement is working i'll be testing the collision detection then figuring out a map set up and file structure.
i posted the full source just incase there is something i'm missing, thanks for the help in advance.