recently I started a project on OpenGL in C++ and I kind of hit a snag. The player is a cube measured at 32x32 and the platforms at 32x32. When gravity comes in to place, the player falls as they should, it hits the platform, and continuously bounces up and down.
Anyways, here's my C++ code:
Player.h:
typedef void *(*ccallbackv)(void);
typedef void *(*ccallbackc)(const std::string);
const float g_gravity = 0.52f;
class Player :
public CBaseObject
{
// Variables
int m_health, m_stealth, m_maxHealth, m_maxStamina;
float jumpForce;
bool mEnabled;
ccallbackv* c_damageCallback;
ccallbackc* c_killedCallback;
bool m_isOnGround;
// Movement Variables
Vector2f velocity;
BoundingBox2D<float>* m_tilePosition, *m_oldTilePosition;
GLuint m_texture;
Level* level;
public:
Player(void);
virtual ~Player(void);
virtual void SetDamageCallbackFunc(ccallbackv* func) { this->c_damageCallback = func; }
virtual void SetKilledCallbackFunc(ccallbackc* func) { this->c_killedCallback = func; }
virtual void Kill(CBaseObject* killer = NULL);
virtual void Reset();
virtual void Tick();
virtual void Render();
virtual void KeyPress(char);
virtual void Enabled(bool v) { this->mEnabled = v; }
virtual bool isEnabled() { return this->mEnabled; }
virtual void Collision();
virtual bool OnGround(bool r) { if(r != m_isOnGround) { m_isOnGround = r; } return m_isOnGround; }
virtual void SetLevel(Level* level) { this->level = level; }
virtual Level* GetLevel() { return level; }
virtual Vector2f* getPosition(){
return &this->m_tilePosition->getVector();
}
virtual BoundingBox2D<float>* getBoundries() { return this->m_tilePosition; }
virtual void setPosition(const Vector2f position){
return this->m_tilePosition->setVector(position);
}
virtual bool IsAlive(){
return m_health > 0;
}
virtual bool HasStamina(){
return m_stealth > 0;
}
virtual void SetJumpForce(float force){ this->jumpForce = force; }
virtual float getJumpForce() { return this->jumpForce; }
};
Player.cpp:
int movement_position = 0;
float old_y = 0;
Player::Player(void)
{
this->SetTypeString("PLAYER_NON_NPC");
m_texture = ImportTexture("Player.png");
this->velocity = Vector2f(0,0);
this->m_tilePosition = new BoundingBox2D<float>(64,0,32,32);
this->mEnabled = true;
this->OnGround(false);
old_y = -5;
}
Player::~Player(void)
{
}
void Player::Kill(CBaseObject* killer){
if( this->c_killedCallback != NULL )
(*this->c_killedCallback)( std::string( "Player was killed by entity " + ( !killer->GetTypeString().empty() ) ? killer->GetTypeString()
: "unknown."));
this->m_health = 0;
}
void Player::Reset(){
this->m_health = 100;
this->m_stealth = 100;
}
void Player::Tick()
{
if(!this->mEnabled) return;
float ticks = std::toFloat(Time::getSystemTicksSeconds());
m_oldTilePosition = this->m_tilePosition;
if( GLOBALS::pressedKey == 'd' ){
velocity.setX(clamp<float>(velocity.getX() + (1.0f / 10000) * ticks, -0.05f, 0.05f));
}
else if( GLOBALS::pressedKey == 'a' ){
velocity.setX(clamp<float>(velocity.getX() - (1.0f / 10000) * ticks, -0.05f, 0.05f));
}
else {
velocity.setX(0);
}
this->Collision();
if( !m_isOnGround OR ( old_y != this->getBoundries()->getY() ) ){
velocity.addY(clamp<float>(0.0001f * ticks, -0.005f, 0.005f));
}
if(m_oldTilePosition != this->m_tilePosition)
glutPostRedisplay();
if( ( old_y == this->getBoundries()->getY() ) ) {
this->m_tilePosition->setY(old_y);
velocity.setY(0);
}
this->m_tilePosition->addVector(this->velocity);
}
void Player::KeyPress(char i){
}
void Player::Collision(){
if( this->level == NULL ) return;
float old_yc = 0;
std::vector<Tile*> mapData = this->level->GetMap();
OnGround(false);
for(unsigned int i = 0; i < mapData.size(); ++i){
BoundingBox2D<float>* source = this->getBoundries();
BoundingBox2D<float>* mapTile = mapData.at(i)->getBoundries();
Vector2f mapTileVector = mapTile->getIntersectionDepth(source);
Vector2f abs = mapTileVector.Absolute();
if( mapTileVector != Vector2f(0,0) && mapTile->IntersectsWith(source)){
if( abs.getX() > abs.getY() ){
if( old_y <= mapTile->top() )
OnGround(true);
if(m_isOnGround){
this->getBoundries()->setVector(Vector2f(this->m_tilePosition->getX(), this->m_tilePosition->getY() + mapTileVector.getY()));
old_yc = this->getBoundries()->getVector().getY();
}
}
}
}
old_y = old_yc;
}
void Player::Render()
{
if( this->m_texture == 0x0 ) return;
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, this->m_texture);
glBegin(GL_QUADS);
::glTexCoord2f(0.0f, 0.0f); ::glVertex2f(this->m_tilePosition->getX(),this->m_tilePosition->getY());
::glTexCoord2f(0.0f, 1.0f); ::glVertex2f(this->m_tilePosition->getX(),this->m_tilePosition->getY() + this->m_tilePosition->getH());
::glTexCoord2f(1.0f, 1.0f); ::glVertex2f(this->m_tilePosition->getX() + this->m_tilePosition->getW(),this->m_tilePosition->getY() + this->m_tilePosition->getH());
::glTexCoord2f(1.0f, 0.0f); ::glVertex2f(this->m_tilePosition->getX() + this->m_tilePosition->getW(),this->m_tilePosition->getY());
glEnd();
glDisable(GL_TEXTURE_2D);
}
Related
I have a Player class that inherits from RectangleShape and I have created a function to move my player left and right on the A & D keys, but when enabling and disabling VSync I find that the movement speeds up rapidly, my guess is that my delta time implementation is incorrect but it seems to be the same as most tutorials online:
class Player : public sf::RectangleShape {
private:
sf::Vector2f velocity;
sf::Vector2f acceleration;
const float maxSpeed = 20.f;
public:
Player(sf::Vector2f size) : sf::RectangleShape(size) {
velocity = sf::Vector2f(0.f, 0.f);
acceleration = sf::Vector2f(0.05f, 27.f);
}
void Move(int& dt) {
if (Keyboard::isKeyPressed(Keyboard::D)) {
if (velocity.x < maxSpeed) {
velocity.x += acceleration.x * dt;
}
}
else if (Keyboard::isKeyPressed(Keyboard::A)) {
if (velocity.x > -maxSpeed) {
velocity.x -= acceleration.x * dt;
}
}
else if (velocity.x < 0.2f && velocity.x > - 0.2f) {
velocity.x = 0;
}
else {
if (velocity.x > 0.f) {
velocity.x -= acceleration.x * dt;
}
else {
velocity.x += acceleration.x * dt;
}
}
}
This is my main loop (stripped down)(I also have a custom Window class):
sf::Clock dtClock;
int dt;
int main() {
Window win("Test Window", sf::VideoMode::getDesktopMode().width, sf::VideoMode::getDesktopMode().height, false);
Player player(sf::Vector2f(50.f, 50.f));
player.setFillColor(sf::Color::Blue);
player.move(sf::Vector2f(200.f, 150.f));
sf::Clock clock;
float lastTime = 0;
while (win.IsOpen()) {
dt = dtClock.restart().asMilliseconds();
player.Move(dt);
win.SetCenter(player.getPosition());
float currentTime = clock.restart().asSeconds();
short int fps = 1 / currentTime;
lastTime = currentTime;
win.Clear();
win.Draw(player);
win.Disp(fps);
}
}
EDIT: here is the minimal reproducible code:
#include <string>
#define BAR_SIZE 16.f
//this is where scale will be 1:1
#define STANDARD_WIDTH 1920
sf::Font firaCode;
enum class RenderObjectType {
Rectangle = 0,
Circle,
Sprite,
count
};
typedef RenderObjectType TROT;
struct TestRenderObject { //node
RenderObjectType type;
void* val;
TestRenderObject(void* val, RenderObjectType type) { this->val = val; this->type = type; }
};
typedef TestRenderObject TRO;
class Window {
private:
std::string title;
sf::RenderWindow win;
sf::View view;
sf::View staticView; //for use with GUI and cinematic bars
sf::Event evnt;
sf::Color background;
bool vsyncEnabled = true;
public:
Window(std::string name = "", unsigned pWidth = 1920, unsigned pHeight = 1080, bool fullscreen = false, sf::Color backgroundColour = sf::Color(135, 206, 235)) {
this->title = name;
this->win.create(sf::VideoMode(pWidth, pHeight), name, (fullscreen ? sf::Style::Fullscreen : sf::Style::Default));
this->win.setVerticalSyncEnabled(this->vsyncEnabled);
this->view.setSize(sf::Vector2f(pWidth, pHeight));
this->view.setCenter(pWidth / 2, pHeight / 2);
this->staticView.setSize(sf::Vector2f(pWidth, pHeight));
this->staticView.setCenter(pWidth / 2, pHeight / 2);
this->background = backgroundColour;
}
inline void toggleVSync() {
this->vsyncEnabled = !this->vsyncEnabled;
this->win.setVerticalSyncEnabled(this->vsyncEnabled);
}
inline void Clear() {
this->win.clear(background);
this->win.setView(this->view);
}
inline void Draw(sf::RectangleShape& r) {
this->win.draw(r);
}
inline void Draw(sf::CircleShape& c) {
this->win.draw(c);
}
inline void Draw(sf::Sprite& s) {
this->win.draw(s);
}
inline void Draw(std::initializer_list<TestRenderObject>& renderObjects) { //multiple types for debugging
for (auto& ele : renderObjects) {
switch (ele.type)
{
case(RenderObjectType::Rectangle):
this->Draw(*((sf::RectangleShape*)ele.val));
break;
case(RenderObjectType::Circle):
this->Draw(*((sf::CircleShape*)ele.val));
break;
case(RenderObjectType::Sprite):
this->Draw(*((sf::Sprite*)ele.val));
break;
default:
break;
}
}
}
inline void Draw(std::initializer_list<sf::Sprite> renderObjects) {
for (auto& ele : renderObjects)
this->win.draw(ele);
}
inline void Disp() {
this->win.setView(this->staticView); //UI elements
this->win.setView(this->view); //game elements
this->win.display();
}
inline bool IsOpen() {
return this->win.isOpen();
}
inline bool PollEvnt() {
return this->win.pollEvent(this->evnt);
}
inline sf::Event& GetEvnt() {
return this->evnt;
}
inline sf::Vector2f GetPosition() {
return sf::Vector2f(this->win.getPosition().x, this->win.getPosition().y);
}
inline void Shift(sf::Vector2f amount) {
this->view.move(amount);
} inline void Move(sf::Vector2f amount) { this->Shift(amount); }
inline void SetCenter(sf::Vector2f position) {
this->view.setCenter(position);
}
inline void Rotate(float amount) {
this->view.rotate(amount);
}
inline void Zoom(float amount) {
this->view.zoom(amount);
}
inline void ResetRotation() {
this->view.setRotation(0);
}
inline void ResetZoom() {
this->view.setSize(sf::Vector2f(this->win.getSize()));
} inline void ResetScale() { this->ResetZoom(); }
inline void ResetViewRatio() {
this->ResetZoom();
}
void Close() {
this->win.close();
}
};
typedef Window Win;
using Keyboard = sf::Keyboard;
using WinEvnt = sf::Event;
const float gravity{ 1.f };
class Player : public sf::RectangleShape {
private:
sf::Vector2f velocity;
sf::Vector2f acceleration;
const float maxSpeed = 20.f;
const float maxFallSpeed = 30.f;
public:
Player(sf::Vector2f size) : sf::RectangleShape(size) {
velocity = sf::Vector2f(0.f, 0.f);
acceleration = sf::Vector2f(0.05f, 27.f);
}
void Move(const int& dt) {
if (Keyboard::isKeyPressed(Keyboard::D)) {
if (velocity.x < maxSpeed) {
velocity.x += acceleration.x * dt;
}
}
else if (Keyboard::isKeyPressed(Keyboard::A)) {
if (velocity.x > -maxSpeed) {
velocity.x -= acceleration.x * dt;
}
}
else if (velocity.x < 0.2f && velocity.x > -0.2f) {
velocity.x = 0;
}
else {
if (velocity.x > 0.f) {
velocity.x -= acceleration.x * dt;
}
else {
velocity.x += acceleration.x * dt;
}
}
if (this->getPosition().y >= 500) {
velocity.y = 0;
if (this->getPosition().y >= 500)
this->setPosition(this->getPosition().x, 500);
if (Keyboard::isKeyPressed(Keyboard::Space) || Keyboard::isKeyPressed(Keyboard::W)) {
velocity.y -= acceleration.y;
}
}
else {
if (velocity.y < maxFallSpeed)
velocity.y += gravity;
}
this->move(velocity);
}
};
sf::Clock dtClock;
int dt;
int main() {
Window win("Test Window", sf::VideoMode::getDesktopMode().width, sf::VideoMode::getDesktopMode().height, false);
sf::CircleShape c(100.f);
c.setFillColor(sf::Color::Green);
Player player(sf::Vector2f(50.f, 50.f));
player.setFillColor(sf::Color::Blue);
player.move(sf::Vector2f(200.f, 150.f));
sf::RectangleShape rect(sf::Vector2f(100.f, 50.f));
rect.setFillColor(sf::Color::Magenta);
rect.move(sf::Vector2f(600.f, 200.f));
sf::CircleShape circle(200.f);
circle.setFillColor(sf::Color::Yellow);
circle.setOutlineThickness(-15);
circle.setOutlineColor(sf::Color(255, 99, 200));
circle.move(sf::Vector2f(1000.f, 1000.f));
sf::RectangleShape big(sf::Vector2f(6000.f, 3000.f));
big.setFillColor(sf::Color::Red);
big.move(sf::Vector2f(900.f, 600.f));
std::initializer_list<TestRenderObject> objects =
{
TRO((void*)&c, TROT::Circle),
TRO((void*)&rect, TROT::Rectangle),
TRO((void*)&big, TROT::Rectangle),
TRO((void*)&circle, TROT::Circle)
};
sf::Clock clock;
float lastTime = 0;
while (win.IsOpen()) {
while (win.PollEvnt()) {
if (win.GetEvnt().type == WinEvnt::KeyReleased && win.GetEvnt().key.code == Keyboard::V) {
win.toggleVSync();
}
else if (win.GetEvnt().type == sf::Event::Resized) {
win.ResetViewRatio();
}
else if (win.GetEvnt().type == WinEvnt::Closed) {
win.Close();
}
}
dt = dtClock.restart().asMilliseconds();
player.Move(dt);
win.SetCenter(player.getPosition());
float currentTime = clock.restart().asSeconds();
short int fps = 1 / currentTime;
lastTime = currentTime;
win.Clear();
win.Draw(objects);
win.Draw(player);
win.Disp();
}
}
With basic drawing code and modern GPUs you can easily get up to 2-3k frames per second, which can then turn into a frame time of 1f / 3000fps = 0.00033s which is less than one millisecond, thus when you call asMilliseconds() you may end up with 0.
An easy workaround here is to always work with seconds (asSeconds()) and floats, that way you should never hit 0. Keep in mind though, that you should always set a limit, either by enabling VSync or calling setFramerateLimit().
As for a design point of view, I recommend to not derive from sf::RectangleShape and instead use composition over inheritance. You can then derive from the more interface-like classes such as `sf
As a school project, I've made the classic snake game using SDL2 and C++.
I've already implemented the growing, moving features for the Snake but it was required to make the movement based on a grid, but when I implemented the grid feature, the self-collision was always triggering whenever grow one part, so every time I start the game, and eat the first fruit, the snake dies.
I've been trying for a while now, from placing a delay to the adding of the tail and delaying the collision check, but to no avail, it's always colliding with itself even though it is not.
I can't see what is wrong with the self collision, I would gladly appreciate it if someone can point out what's wrong.
Snake.h
#include "GameObject.h"
#include "common.h"
#include "draw.h"
#include "Food.h"
#include "util.h"
#include <vector>
struct Segment {
int x;
int y;
Segment(int posx, int posy) {
x = posx;
y = posy;
}
};
class Snake :
public GameObject
{
public:
~Snake();
void start();
void update();
void draw();
void outOfBoundsCheck();
void move();
void addSegment(int x, int y);
void selfCollisionCheck(bool hasEaten);
void setHasMoved(bool a);
void setIsAlive(bool a);
int getX();
int getY();
int getWidth();
int getHeight();
bool getIsAlive();
bool getHasMoved();
std::vector<Segment*> const& getV() const;
private:
std::vector<Segment*> body;
SDL_Texture* headTexture;
SDL_Texture* bodyTexture;
int x;
int y;
int width;
int height;
int dx;
int dy;
int tempX;
int tempY;
bool isAlive;
bool hasMoved;
};
Snake.cpp
Snake::~Snake()
{
}
void Snake::start()
{
// Load Texture
headTexture = loadTexture("gfx/player.png");
bodyTexture = loadTexture("gfx/body.png");
tempX = 0;
tempY = 0;
x = 0;
y = 0;
dx = 0;
dy = 0;
isAlive = true;
hasMoved = false;
width = 0;
height = 0;
SDL_QueryTexture(headTexture, NULL, NULL, &width, &height);
addSegment(x, y);
}
void Snake::update()
{
std::cout << "Head" << body[0]->x << std::endl;
if (body.size() > 1) {
std::cout << "2nd Segment" << body[1]->x << std::endl;
if (body.size() > 2) {
std::cout << "3nd Segment" << body[2]->x << std::endl;
}
}
move();
outOfBoundsCheck();
}
void Snake::draw()
{
if (!isAlive) return; // Cancel the render if player dies
for (int i = 0; i < body.size(); i++) {
blit(headTexture, body[i]->x, body[i]->y);
}
}
void Snake::outOfBoundsCheck()
{
for (int i = 0; i < body.size(); i++) {
if (body[i]->x > SCREEN_WIDTH) {
body[i]->x = 0;
}
if (body[i]->x < 0) {
body[i]->x = SCREEN_WIDTH;
}
if (body[i]->y > SCREEN_HEIGHT) {
body[i]->y = 0;
}
if (body[i]->y < 0) {
body[i]->y = SCREEN_HEIGHT;
}
}
}
void Snake::move()
{
if (app.keyboard[SDL_SCANCODE_W] && dy != 5) {
dx = 0;
dy = -5;
}
if (app.keyboard[SDL_SCANCODE_A] && dx != 5) {
dx = -5;
dy = 0;
}
if (app.keyboard[SDL_SCANCODE_S] && dy != -5) {
dx = 0;
dy = 5;
}
if (app.keyboard[SDL_SCANCODE_D] && dx != -5) {
dx = 5;
dy = 0;
}
Segment* snakeHead = *(body.begin()); //Grid
tempX += dx;
tempY += dy;
if (tempX % 25 == 0) {
snakeHead->x += tempX;
tempX = 0;
}
if (tempY % 25 == 0) {
snakeHead->y += tempY;
tempY = 0;
}
for (int i = body.size() - 1; i > 0; i--) { //For the other parts to follow
body[i]->x = body[i - 1]->x;
body[i]->y = body[i - 1]->y;
}
}
void Snake::addSegment(int x, int y)
{
Segment* seg = new Segment(x, y );
body.push_back(seg);
}
void Snake::selfCollisionCheck(bool hasEaten) // Fail
{
Segment* head = body[0];
if (hasEaten == false) {
for (int i = 1; i < body.size(); i++) {
if (head->x == body[i]->x && head->y == body[i]->y) {
isAlive = false;
break;
}
}
}
else {
return;
}
}
void Snake::setHasMoved(bool a)
{
hasMoved = a;
}
void Snake::setIsAlive(bool a)
{
isAlive = a;
}
int Snake::getX()
{
return x;
}
int Snake::getY()
{
return y;
}
int Snake::getWidth()
{
return width;
}
int Snake::getHeight()
{
return height;
}
bool Snake::getIsAlive()
{
return isAlive;
}
bool Snake::getHasMoved()
{
return hasMoved;
}
std::vector<Segment*> const& Snake::getV() const
{
// TODO: insert return statement here
return body;
}
GameScene.h
#include "Scene.h"
#include "GameObject.h"
#include "Snake.h"
#include "Food.h"
#include "util.h"
#include "text.h"
#include "SoundManager.h"
class GameScene : public Scene
{
public:
GameScene();
~GameScene();
void start();
void draw();
void update();
void spawnFood();
void collisionLogic();
void selfCollision();
void despawnFood(Food* food);
private:
Snake* snake;
Food* food;
int points;
std::vector<Food*> spawnedFood;
};
GameScene.cpp
#include "GameScene.h"
GameScene::GameScene()
{
// Register and add game objects on constructor
snake = new Snake();
this->addGameObject(snake);
points = 0;
}
GameScene::~GameScene()
{
delete snake;
delete food;
}
void GameScene::start()
{
Scene::start();
// Initialize any scene logic here
initFonts();
spawnFood();
}
void GameScene::draw()
{
Scene::draw();
drawText(110, 20, 255, 255, 255, TEXT_CENTER, "POINTS: %03d", points);
if (snake->getIsAlive() == false) {
drawText(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, 255, 255, 255, TEXT_CENTER, "GAME OVER!");
}
}
void GameScene::update()
{
Scene::update();
if (spawnedFood.size() == 0 && spawnedFood.size() != 1) {
spawnFood();
}
collisionLogic();
selfCollision();
}
void GameScene::spawnFood()
{
int random = rand() % 720;
if (random % 25 != 0) {
random = rand() % 720;
}
else {
Food* food = new Food();
this->addGameObject(food);
food->setPosition(rand() % SCREEN_WIDTH, rand() % SCREEN_HEIGHT);
spawnedFood.push_back(food);
}
}
void GameScene::collisionLogic()
{
Segment* head = snake->getV()[0];
std::vector<Segment*> snakeBody = snake->getV();
for (int i = 0; i < objects.size(); i++) {
Food* food = dynamic_cast<Food*>(objects[i]);
if (food != NULL) {
int collision = checkCollision(
head->x, head->y, snake->getWidth(), snake->getHeight(),
food->getX(), food->getY(), food->getWidth(), food->getHeight()
);
if (collision == 1) {
despawnFood(food);
snake->addSegment(snakeBody[snakeBody.size() - 1]->x, snakeBody[snakeBody.size() - 1]->y); //Adds a part to the snake
points++;
break;
}
}
}
}
void GameScene::selfCollision()
{
std::vector<Segment*> body = snake->getV();
Segment* head = snake->getV()[0];
for (int i = 1; i < snake->getV().size(); i++) {
if (head->x == body[i]->x && head->y == body[i]->y) {
snake->setIsAlive(false);
break;
}
}
}
void GameScene::despawnFood(Food* food)
{
int index = -1;
for (int i = 0; i < spawnedFood.size(); i++) {
if (food == spawnedFood[i]) {
index = i;
break;
}
}
if (index != -1) {
spawnedFood.erase(spawnedFood.begin() + index);
delete food;
}
}
It seems that I had some logical errors when it comes to the grid movement because when I re-coded everything and changed the grid movement into cell based instead of using modulo condition by dividing the screen width and height to the pixel size of my snake and using that as the coordinates for my movement, everything went back to normal and the collision bug disappeared.
Instead of doing this for the grid movement
Old Grid Movement Code
tempX += dx;
tempY += dy;
if (tempX % 25 == 0) {
snakeHead->x += tempX;
tempX = 0;
}
if (tempY % 25 == 0) {
snakeHead->y += tempY;
tempY = 0;
}
I defined this as a permanent value in my defs.h
defs.h
#define CELL_SIZE 25 // Size of the segment
#define CELL_WIDTH SCREEN_WIDTH / CELL_SIZE
#define CELL_HEIGHT SCREEN_HEIGHT / CELL_SIZE
After that, since I'm still going to render the picture with the default resolution, I multiplied CELL_SIZE to the dest variable of my blit function
draw.cpp
void blit(SDL_Texture* texture, int x, int y)
{
SDL_Rect dest;
dest.x = x * CELL_SIZE;
dest.y = y * CELL_SIZE;
SDL_QueryTexture(texture, NULL, NULL, &dest.w, &dest.h);
SDL_RenderCopy(app.renderer, texture, NULL, &dest);
}
This results to the snake and any other thing that I'm going to render to follow a grid and by assigning the x and y values with the CELL_WIDTH and CELL_HEIGHT as substitution to the resolution, I accomplished the grid movement with no conflict with my collision check.
I have a base class cGameObject which has a virtual update function. I wish to have many derived game objects each with their own specific update function.
I want to add the derived gameobjects to a vector and iterate through them to call each of their update methods.
What is wrong with my iterator?
//In Header file
std::vector <cGameObject*> vGameObjs;
std::vector <cGameObject*>::iterator Iter;
cGameObject *AGameObj;
/In cpp file - add object pointer to vector
AGameObj = new BrickBat(128, 32, (1024 - 128) / 2, 768 - 64, 0);
vGameObjs.push_back(AGameObj);
AGameObj = new BrickBall(64, 512, 384, 1, 1, 0);
vGameObjs.push_back(AGameObj);
//Iterator Crashing??
for (Iter = vGameObjs.begin(); Iter != vGameObjs.end(); ++Iter)
{
//Call Each Objects Update() method here?
}
When I run this, it throws an exception: read access violation. _Mycont was nullptr.
Not sure what to do.
error thrown
Header of Class App Class:
#ifndef _H_AGK_TEMPLATE_
#define _H_AGK_TEMPLATE_
// Link to GDK libraries
#include "AGK.h"
#include "Brickbat.h"
#include "BrickBall.h"
#include "cGameObject.h"
#include <vector>
#define DEVICE_WIDTH 1024
#define DEVICE_HEIGHT 768
#define DEVICE_POS_X 32
#define DEVICE_POS_Y 32
#define FULLSCREEN false
// used to make a more unique folder for the write path
#define COMPANY_NAME "BitMaNip:Play"
// Global values for the app
class app
{
public:
// constructor
app() { memset ( this, 0, sizeof(app)); }
// main app functions - mike to experiment with a derived class for this..
void Begin( void );
int Loop( void );
void End( void );
private:
//Vector of GameObject Pointers;
std::vector <cGameObject*> vGameObjs;
//Iterator of GameObjects
std::vector <cGameObject*>::iterator Iter;
BrickBat *MyBat;
BrickBall *MyBall;
cGameObject *AGameObj;
};
extern app App;
#endif
// Allow us to use the LoadImage function name
#ifdef LoadImage
#undef LoadImage
#endif
cpp file for Header
// Includes
#include "template.h"
#include "cGameObject.h"
#include
// Namespace
using namespace AGK;
app App;
void app::Begin(void)
{
agk::SetVirtualResolution (DEVICE_WIDTH, DEVICE_HEIGHT);
agk::SetClearColor( 151,170,204 ); // light blue
agk::SetSyncRate(60,0);
agk::SetScissor(0,0,0,0);
//Test
/*BrickBall = agk::CreateSprite(0);
agk::SetSpriteSize(BrickBall, 64, 64);
agk::SetSpriteColor(BrickBall, 255, 255, 255, 255);
xPos = (DEVICE_WIDTH - 64) / 2;
yPos = (DEVICE_HEIGHT - 64) / 2;
agk::SetSpritePosition(BrickBall, xPos , yPos );
xDir = 1;
yDir = 1;
iSpeed = 8;*/
MyBat = new BrickBat(128, 32, (1024-128)/2, 768-64, 0);
AGameObj = new BrickBat(128, 32, (1024 - 128) / 2, 768 - 64, 0);
vGameObjs.push_back(AGameObj);
MyBall = new BrickBall(64, 512, 384, 1, 1, 0);
AGameObj = new BrickBall(64, 512, 384, 1, 1, 0);
vGameObjs.push_back(AGameObj);
}
int app::Loop (void)
{
agk::Print( agk::ScreenFPS() );
if (agk::GetRawKeyState(37) == 1)
{
MyBat->MoveLeft();
}
if (agk::GetRawKeyState(39) == 1)
{
MyBat->MoveRight();
}
MyBat->Update();
MyBall->Update();
MyBall->Collided(MyBat->iGetBatID());
for (Iter = vGameObjs.begin(); Iter != vGameObjs.end(); ++Iter)
{
//(*Iter)->Update();
//(*Iter)->Collided(MyBat->iGetBatID());
}
Derived class
include "cGameObject.h"
class BrickBall: public cGameObject
{
private:
bool bHoriDir;
bool bVertDir;
int iSpeed;
bool bPause; //in case game is paused
public:
BrickBall(int iSize, int xPos, int yPos, bool bHori, bool bVert, int ImageID);
virtual void Update() override;
virtual void Collided(int OtherSpriteToCheck) override;
};
agk::Sync();
return 0; // return 1 to close app
}
void app::End (void)
{
}
Base Class:
#pragma once
class cGameObject
{
protected:
bool bInit = false;
int iSprID;
int iImageID;
int iXPos;
int iYPos;
int iAngle;
int iAlpha;
int iWidth;
int iHeight;
int iColour;
//Movement
float fDeltaX;
float fDeltaY;
//Animation
int iAniType; //Type of animation
//0 = No Animation
//1 = Repeating Loop of Frames (All image is animation)
//2 =
int iFrameW; //Width of Animation Frame
int iFrameH; //Height of Animation frame
int iFPS; //Animation Delay
int iNumFrames; //Number of animation frames
int iAniCount; //Frame Counter
public:
// set up default for constructor
cGameObject(int width = 16, int height = 16, int xPos = 0, int yPos = 0, int ImageID = 0);
void SetPosition(int ixPos, int iyPos);
void SetSize(int iWidth, int iHeight);
void SetWidth(int Width);
void SetAngle(int iAngle);
void SetTransparency(int iAlpha);
void SetAnimation(int Type, int FrameW, int FrameH, int FPS, int NumFrames);
virtual void Collided(int OtherSpriteToCheck) {};
virtual void Update() {};
int iGetWidth();
int iGetHeight();
int iGetX();
int iGetY();
int iGetSprID();
~cGameObject();
};
Base Class cpp
#include "cGameObject.h"
#include "agk.h"
void cGameObject::SetAngle(int iAngle)
{
if (bInit == true)
{
if (iAngle > 0 && iAngle < 359)
{
agk::SetSpriteAngle(iSprID, iAngle);
}
}
}
//cGameObject::cGameObject(int width, int height, int xPos, int yPos, const char * szImageFile)
cGameObject::cGameObject(int width, int height, int xPos, int yPos, int ImageID)
{
bInit = true;
iImageID = 0;
/*if (std::strlen(szImageFile) > 0)
{
iImageID = agk::LoadImage(szImageFile);
if (iImageID < 1)
{
bInit = false;
}
}*/
iColour = agk::MakeColor(255, 255, 255);
//init animation code
iAniType = 0; //Type of animation
iFrameW = 64; //Width of Animation Frame
iFrameH = 64; //Height of Animation frame
iFPS = 10; //Animation Delay
iNumFrames = 1; //Number of animation frames
iAniCount = 0; //Frame Counter
iSprID = agk::CreateSprite(iImageID);
if (iSprID < 1)
{
bInit = false;
}
else
{
agk::SetSpriteSize(iSprID, width, height);
agk::SetSpritePosition(iSprID, xPos, yPos);
fDeltaX = 4.0;
fDeltaY = 4.0;
}
}
void cGameObject::SetPosition(int ixPos, int iyPos)
{
if (bInit == true)
{
agk::SetSpritePosition(iSprID, ixPos, iyPos);
}
}
void cGameObject::SetSize(int iWidth, int iHeight)
{
if (bInit == true)
{
if (iWidth > 0 && iWidth < 1024 && iHeight > 0 && iHeight < 1024)
{
agk::SetSpriteSize(iSprID, iWidth, iHeight);
}
}
}
void cGameObject::SetWidth(int Width)
{
if (bInit == true)
{
agk::GetSpriteWidth(Width);
}
}
void cGameObject::SetTransparency(int iAlpha)
{
if (bInit == true)
{
if (iAlpha > 0 && iAlpha < 256)
{
agk::SetSpriteTransparency(iSprID, iAlpha);
}
}
}
void cGameObject::SetAnimation(int Type, int FrameW, int FrameH, int FPS, int NumFrames)
{
//Animation
iAniType = Type;
iFrameW = FrameW; //Width of Animation Frame
iFrameH = FrameH; //Height of Animation frame
iFPS = FPS; //Animation Delay
iNumFrames = NumFrames; //Number of animation frames
iAniCount = 0; //Frame Counter
agk::SetSpriteAnimation(iSprID, iFrameW, iFrameH, iNumFrames);
if (iAniType > 0)
{
agk::PlaySprite(iSprID, iFPS);
}
}
int cGameObject::iGetWidth()
{
if (bInit == true)
{
return agk::GetSpriteWidth(iSprID);
}
else
{
return 0;
}
}
int cGameObject::iGetHeight()
{
if (bInit == true)
{
return agk::GetSpriteHeight(iSprID);
}
else
{
return 0;
}
}
int cGameObject::iGetX()
{
if (bInit == true)
{
return agk::GetSpriteX(iSprID);
}
else
{
return 0;
}
}
int cGameObject::iGetY()
{
if (bInit == true)
{
return agk::GetSpriteY(iSprID);
}
else
{
return 0;
}
}
int cGameObject::iGetSprID()
{
if (bInit == true)
{
return iSprID;
}
else
{
return 0;
}
}
cGameObject::~cGameObject()
{
if (bInit == true)
{
agk::DeleteSprite(iSprID);
}
}
Derived Class Header:
#include "cGameObject.h"
class BrickBall: public cGameObject
{
private:
bool bHoriDir;
bool bVertDir;
int iSpeed;
bool bPause; //in case game is paused
public:
BrickBall(int iSize, int xPos, int yPos, bool bHori, bool bVert, int ImageID);
virtual void Update() override;
virtual void Collided(int OtherSpriteToCheck) override;
};
Derived Class cpp
#include "BrickBall.h"
BrickBall::BrickBall(int Size, int xPos, int yPos, bool bHori, bool bVert, int ImageID):cGameObject(Size, Size, xPos, yPos, ImageID)
{
iWidth = Size;
iHeight = Size;
iXPos = xPos;
iYPos = yPos;
bHoriDir = bHori;
bVertDir = bVert;
/*iSprID = agk::CreateSprite(0);
agk::SetSpriteColor(iSprIdx, 255, 255, 255, 255);
//agk::SetSpriteSize(iSprIdx, iSize, iSize);
agk::SetSpriteSize(iSprIdx, 64, 64);
//agk::SetSpritePosition(iSprIdx, ixPos, iyPos);
agk::SetSpritePosition(iSprIdx, ixPos, iyPos);
bInit = true;*/
iSpeed = 8;
}
void BrickBall::Update()
{
if (bInit==true)// && BatID > 0)
{
//Move Ball
agk::SetSpriteColor(iSprID, 100, 100, 100, 255);
agk::PrintC("BallX:");
agk::Print(iXPos);
if (bHoriDir == 1) //Right
{
iXPos = iXPos + iSpeed;
if (iXPos > 1024 - iWidth)
{
bHoriDir = 0;
}
}
else
{
iXPos = iXPos - iSpeed;
if (iXPos < 0)
{
bHoriDir = 1;
}
}
if (bVertDir == 1) //down
{
iYPos = iYPos + iSpeed;
if (iYPos > 768 - 64)
{
bVertDir = 0;
}
}
else
{
iYPos = iYPos - iSpeed;
if (iYPos < 0)
{
bVertDir = 1;
}
}
agk::SetSpritePosition(iSprID, iXPos, iYPos);
//END Move Ball
//Bat2Ball Collisions
/*if (agk::GetSpriteCollision(iSprID, BatID))
{
//We have collided.
//As Ball is bigger than the gap below the bat must have hit top or sides
//so just flip the vertcal direction
if (bVertDir == 1)
{
bVertDir = 0;
}
}*/
}
}
void BrickBall::Collided(int OtherSpriteToCheck)
{
if (agk::GetSpriteCollision(iSprID, OtherSpriteToCheck))
{
if (bVertDir == 1)
{
bVertDir = 0;
}
}
}
I don't know the cause of your error, but I got this exact error because I had an iterator to a vector, then I updated the vector, then I tried to dereference the iterator (e.g. access *myIterator). I'm new to C++, but it seems that if you alter a collection after getting an iterator for it, your iterator is no longer valid. You need to repoint your iterator to wherever it was.
I got a memory-leak, caused by a function convert.
The function converts a very big vector of 3D-pointdata to another
vector of other 3D-pointdata.
here is my code:
IPoint3D.h
struct IPoint3D
{
virtual ~IPoint3D()
{
}
float x, y, z;
virtual std::string toString() = 0;
};
RealSenseCamera.h
class RealSenseCamera : public IRealSenseCamera
{
public:
RealSenseCamera();
~RealSenseCamera();
CameraInitializationResult CreateSession(int width, int height, int fps) override;
SetSensorConfigurationResult SetSensorConfiguration(pxcBool dsAutoExposure, pxcBool dsCropping, pxcI32 dsGain, pxcI32 laserPower, PXCCapture::Device::IVCAMAccuracy accuracy) override;
bool IsReady() override;
int GetFrameSize() override;
void Destroy() override;
std::vector<IPoint3D*> QueryVertices() override;
private:
int _width, _height, _fps;
PXCSession* _session;
PXCSenseManager* _senseManager;
PXCProjection* _projection;
PXCCapture::Device* _device;
std::vector<IPoint3D*> convert(std::vector<PXCPoint3DF32> source);
};
RealSenseCamera.cpp
RealSenseCamera::RealSenseCamera() : _width(0), _height(0), _fps(0), _session(nullptr), _senseManager(nullptr), _projection(nullptr), _device(nullptr)
{
}
RealSenseCamera::~RealSenseCamera()
{
}
CameraInitializationResult RealSenseCamera::CreateSession(int width, int height, int fps)
{
_width = width;
_height = height;
_fps = fps;
_session = PXCSession::CreateInstance();
if (_session == nullptr)
return CameraInitializationResult(false, "Failed to create camera session instance.");
_senseManager = _session->CreateSenseManager();
if (_senseManager == nullptr)
return CameraInitializationResult(false, "Failed to create SenseManager.");
_senseManager->EnableStream(PXCCapture::STREAM_TYPE_DEPTH, width, height, fps);
_senseManager->Init();
PXCCaptureManager* captureManager = _senseManager->QueryCaptureManager();
if (captureManager == nullptr)
return CameraInitializationResult(false, "Failed to create CaptureManager.");
_device = captureManager->QueryDevice();
if (_device == nullptr)
return CameraInitializationResult(false, "Failed to create device.");
return CameraInitializationResult(true, "Initialization successful.");
}
bool RealSenseCamera::IsReady()
{
return _session != nullptr && _device != nullptr && _senseManager != nullptr && _senseManager->IsConnected();
}
int RealSenseCamera::GetFrameSize()
{
return _width * _height;
}
SetSensorConfigurationResult RealSenseCamera::SetSensorConfiguration(pxcBool autoExposure, pxcBool cropping, pxcI32 gain, pxcI32 laserPower, PXCCapture::Device::IVCAMAccuracy accuracy)
{
if (_device == nullptr)
return SetSensorConfigurationResult(false, "Failed to find device.");
_device->SetDSLeftRightAutoExposure(autoExposure);
_device->SetDSLeftRightCropping(cropping);
_device->SetDSLeftRightGain(gain);
_device->SetIVCAMLaserPower(laserPower);
_device->SetIVCAMAccuracy(accuracy);
return SetSensorConfigurationResult(true, "New Configuration ist set.");
}
std::vector<IPoint3D*> RealSenseCamera::QueryVertices()
{
std::vector<PXCPoint3DF32> vertices;
if (_senseManager->AcquireFrame(true) >= pxcStatus::PXC_STATUS_NO_ERROR) {
PXCCapture::Sample* _sample = _senseManager->QuerySample();
PXCImage* depthImage = _sample->depth;
vertices.resize(_width * _height);
_projection = _device->CreateProjection();
_projection->QueryVertices(depthImage, &vertices[0]);
}
_senseManager->ReleaseFrame();
// konvertierung leider bisher nötig
return convert(vertices);
}
// Neuer vector wird für Punktdaten angelegt
std::vector<IPoint3D*> RealSenseCamera::convert(std::vector<PXCPoint3DF32> source)
{
int size = GetFrameSize();
std::vector<IPoint3D*> target(size);
for (int i = 0; i < size; i++)
{
target[i] = new PXCPoint3DF32Adapter(source[i]);
}
return target;
}
void RealSenseCamera::Destroy()
{
if (_device != nullptr)
_device->Release();
if (_senseManager != nullptr)
{
_senseManager->Close();
_senseManager->Release();
}
if (_session != nullptr)
_session->Release();
}
Can anyone see the problem?
I am making a TBS game, at least im trying to.
So i've started off by following a tutorial on lazyfoo.net (lesson 29) on how to make a tiled environment and making my own changes along the way.
Every time i try to compile it it gives me two main errors:
No. 1: 255 error: expected primary-expression before ',' token
No. 2: 267 error: expected ';' before 'myUnit'
Here is the source code, i am fairly certain the that the problem is not with the images or the map.
CODE:
#include "SDL/SDL_image.h"
#include "SDL/SDL.h"
#include <string>
#include <fstream>
const int SCREEN_WIDTH = 600;
const int SCREEN_HEIGHT = 600;
const int SCREEN_BPP = 32;
const int FPS = 30;
const int UNIT_WIDTH = 50;
const int UNIT_HEIGHT = 50;
const int LEVEL_WIDTH = 600;
const int LEVEL_HEIGHT = 600;
const int TILE_WIDTH = 60;
const int TILE_HEIGHT = 60;
const int TOTAL_TILES = 100;
const int TILE_SPRITES = 3;
const int TILE_GRASS = 0;
const int TILE_WATER = 1;
const int TILE_MOUNTAIN = 2;
SDL_Surface *screen = NULL;
SDL_Surface *Unit = NULL;
SDL_Surface *tileMap = NULL;
SDL_Rect clips[ TILE_SPRITES ];
SDL_Event occur;
class Tile
{
private:
SDL_Rect box;
int type;
public:
Tile(int x, int y, int tileType);
void show();
int get_type();
SDL_Rect get_box();
};
class Unit
{
private:
SDL_Rect Box;
bool movement;
public:
Unit();
void handle_input();
void move( Tile *tiles[]);
void show();
};
SDL_Surface *load_image(std::string filename)
{
SDL_Surface* loadedImage = NULL;
SDL_Surface* optimizedImage = NULL;
loadedImage = IMG_Load(filename.c_str());
if(loadedImage != NULL)
{
optimizedImage = SDL_DisplayFormat( loadedImage );
SDL_FreeSurface( loadedImage );
if(optimizedImage != NULL)
{
SDL_SetColorKey(optimizedImage, SDL_SRCCOLORKEY, SDL_MapRGB(optimizedImage->format, 0,0xff,0xff));
}
}
return optimizedImage;
}
void apply_surface(int x, int y, SDL_Surface* source, SDL_Surface* destination, SDL_Rect* clip = NULL)
{
SDL_Rect offset;
offset.x = x;
offset.y = y;
SDL_BlitSurface(source, clip, destination, &offset);
}
bool init()
{
if(SDL_Init(SDL_INIT_EVERYTHING) == -1)
{
return false;
}
screen = SDL_SetVideoMode(SCREEN_WIDTH,SCREEN_HEIGHT,SCREEN_BPP,SDL_SWSURFACE);
if(screen == NULL)
{
return false;
}
SDL_WM_SetCaption("Strategy Game", NULL);
return true;
}
bool load_files()
{
Unit = load_image("infantry_red.png");
if(Unit == NULL)
{
return false;
}
tileMap = load_image("tilemap.png");
if( tileMap == NULL)
{
return false;
}
return 0;
}
void clean_up(Tile *tiles[])
{
SDL_FreeSurface(Unit);
SDL_FreeSurface(tileMap);
for(int t = 0;t < TOTAL_TILES; t++)
{
delete tiles[ t ];
}
SDL_Quit();
}
void clip_tiles()
{
clips[TILE_GRASS].x = 0;
clips[TILE_GRASS].y = 0;
clips[TILE_GRASS].w = TILE_WIDTH;
clips[TILE_GRASS].h = TILE_HEIGHT;
clips[TILE_WATER].x = 60;
clips[TILE_WATER].y = 0;
clips[TILE_WATER].w = TILE_WIDTH;
clips[TILE_WATER].h = TILE_HEIGHT;
clips[TILE_MOUNTAIN].x = 120;
clips[TILE_MOUNTAIN].y = 0;
clips[TILE_MOUNTAIN].w = TILE_WIDTH;
clips[TILE_MOUNTAIN].h = TILE_HEIGHT;
}
bool set_tiles( Tile *tiles[])
{
int x = 0, y = 0;
std::ifstream map("strategy_game.map");
if(map == NULL)
{
return false;
}
for(int t = 0; y < TOTAL_TILES; t++)
{
int tileType = -1;
map >> tileType;
if(map.fail() == true)
{
map.close();
return false;
}
if( (tileType >= 0) && (tileType < TILE_SPRITES))
{
tiles[t] = new Tile(x,y,tileType);
}
else
{
map.close();
return false;
}
x += TILE_WIDTH;
if(x >= LEVEL_WIDTH)
{
x = 0;
y += TILE_HEIGHT;
}
}
map.close();
return true;
}
Tile::Tile(int x, int y, int tileType)
{
box.x = x;
box.y = y;
box.w = TILE_WIDTH;
box.h = TILE_HEIGHT;
type = tileType;
}
void Tile::show()
{
apply_surface(box.x, box.y, tileMap, screen, &clips[type]);
}
int Tile::get_type()
{
return type;
}
SDL_Rect Tile::get_box()
{
return box;
}
Unit::Unit()
{
Box.x = 0;
Box.y = 0;
Box.w = UNIT_WIDTH;
Box.h = UNIT_HEIGHT;
}
SDL_Rect rect;
int mouseX,mouseY;
void Unit::handle_input()
{
if(occur.type == SDL_MOUSEBUTTONDOWN)
{
mouseX = occur.button.x;
mouseY = occur.button.y;
}
}
void Unit::move(Tile *tiles[])
{
Box.x += mouseX;
if( Box.x < 0 || Box.x + UNIT_WIDTH > LEVEL_WIDTH )
{
Box.x -= mouseX;
}
Box.y -= mouseY;
if( Box.y < 0 || Box.y + UNIT_HEIGHT > LEVEL_HEIGHT)
{
Box.y -= mouseY;
}
}
void Unit::show()
{
int BoxX;
int BoxY;
Box.x = BoxX;
Box.y = BoxY;
SDL_Rect unitOffset;
unitOffset.x = BoxX;
unitOffset.y = BoxY;
SDL_BlitSurface(Unit,NULL,screen,unitOffset);
}
Uint32 start;
int main(int argc, char* args[])
{
bool quit = false;
Unit myUnit;
Tile *tiles[TOTAL_TILES];
if(init() == false)
{
return 1;
}
if(load_files() == false)
{
return 1;
}
clip_tiles();
if( set_tiles(tiles) == false)
{
return 1;
}
while(quit == false)
{
start = SDL_GetTicks();
while(SDL_PollEvent(&occur));
{
myUnit.handle_input();
switch(occur.type)
{
case SDL_QUIT:
quit = false;
break;
}
}
myUnit.move(tiles);
for(int t = 0;t<TOTAL_TILES;t++)
{
tiles[t]->show();
}
myUnit.show();
//render
SDL_FillRect(screen,&screen->clip_rect,0);
SDL_Flip(screen);
if(1000/FPS > SDL_GetTicks() - start ){
SDL_Delay(1000/FPS - (SDL_GetTicks()-start));
}
}
clean_up( tiles );
return 0;
}
Remove semi-colon from while(SDL_PollEvent(&occur))
Change: SDL_Surface *Unit = NULL; to `SDL_Surface *unitSurf = NULL;'
Change:
Unit = load_image("infantry_red.png");
if(Unit == NULL)
{
return false;
}
to
unitSurf = load_image("infantry_red.png");
if(unitSurf == NULL)
{
return false;
}
Change: SDL_BlitSurface(Unit,NULL,screen,unitOffset); to SDL_BlitSurface(unitSurf,NULL,screen,unitOffset);
Remove the semicolon after while(SDL_PollEvent(&occur))