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
Related
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.
All my sprites are reversed when I try to draw my isometric map.
Here is the tileset.png mentionned in the following code :
Object.h/Object.cpp
I can use them to draw tiles, UI element, etc. ...
#pragma once
class Object {
public:
//FUNCTIONS
Object();
void addComponent(float value);
int getComponent(float index);
void editComponent(float index, float value);
void deleteComponent(float index);
private:
vector<int> components;
};
#include "Object.cpp"
-
#pragma once
//FUNCTIONS
Object::Object() {
//...
}
void Object::addComponent(float value) {
components.push_back(value);
}
int Object::getComponent(float index) {
return components[index];
}
void Object::editComponent(float index, float value) {
components[index] = value;
}
void Object::deleteComponent(float index) {
components.erase(components.begin() + index);
}
Note: I may have weird includes, I'm struggling with visual studio ha ha.
Scene.h/Scene.cpp
Handle data & graphics
#pragma once
class Scene {
public:
Scene(float w, float h, int mapx, int mapy, int tilesize, int mapwidth, int mapheight);
void run();
void addLayer();
void loadTileset(sf::String url);
void loadUiTileset(sf::String url);
//functions
//...
//getters
//...
//setters
//...
private:
sf::RenderWindow window;
float width;
float height;
int nb_layers;
int map_x;
int map_y;
int map_width;
int map_height;
int tile_size;
int selected_tile_index;
sf::RenderTexture texture;
sf::Sprite tile;
sf::Sprite map;
sf::Texture tileset;
vector<Object> tiles;
sf::Texture uiTileset;
//private functions
void updateMap();
//...
void processEvent();
void update(sf::Time deltaTime);
void render();
//...
};
#include "Scene.cpp"
-
#pragma once
//functions
Scene::Scene(float w, float h, int mapx, int mapy, int tilesize, int mapwidth, int mapheight) : window(sf::VideoMode(w, h), "Editor") {
width = w;
height = h;
map_x = mapx;
map_y = mapy;
map_width = mapwidth;
map_height = mapheight;
tile_size = tilesize;
selected_tile_index = 0;//default
nb_layers = 0;
}
void Scene::run() {
sf::Clock clock;
sf::Time timeSinceLastUpdate = sf::Time::Zero;
sf::Time TimePerFrame = sf::seconds(1.f / 60.f);
while (window.isOpen()) {
processEvent();
timeSinceLastUpdate += clock.restart();
while (timeSinceLastUpdate > TimePerFrame) {
timeSinceLastUpdate -= TimePerFrame;
processEvent();
update(TimePerFrame);
}
render();
}
}
void Scene::addLayer() {
nb_layers += 1;
int tile_x = map_x,
tile_y = map_y,
num_layer = nb_layers - 1,
layer_pos = (num_layer * tile_size) / 2,
tile_zOrder = -1;
tile_y -= layer_pos;
int x = map_x,
y = map_y;
for (int h = 0; h < map_height; h++) {
for (int w = 0; w < map_width; w++) {
tile_zOrder = (w * (h + 1)) + (num_layer * 10);
x = carthesianToIsometric(tile_x, tile_y)[0];
y = carthesianToIsometric(tile_x, tile_y)[1] - layer_pos;
cout << x << ", " << y << endl;
Object tile;
tile.addComponent(selected_tile_index);
tile.addComponent(x);
tile.addComponent(y);
tile.addComponent(tile_zOrder);
tile.addComponent(num_layer);
tiles.push_back(tile);
tile_x += tile_size;
}
tile_x = 0;
tile_y += tile_size;
}
updateMap();
}
void Scene::loadTileset(sf::String url) {
if (!tileset.loadFromFile(url))
{
cout << std::string(url) << "couldn't be loaded..." << endl;
}
}
void Scene::loadUiTileset(sf::String url) {
if (!uiTileset.loadFromFile(url))
{
cout << std::string(url) << "couldn't be loaded..." << endl;
}
}
//getters
//...
//setters
//...
//private functions
void Scene::updateMap() {
int tile_position_x = 0,
tile_position_y = 0;
int tile_x = 0,
tile_y = 0;
if (!texture.create(map_width * tile_size, (map_height * tile_size) / 2))
cout << "Texture couldn't be loaded... " << endl;
texture.clear(sf::Color(133, 118, 104, 255));
sf::Sprite image;
image.setTexture(tileset);
int tileset_width = image.getGlobalBounds().width,
tileset_height = image.getGlobalBounds().height;
tile.setTexture(tileset);
for (int tile_index = 0; tile_index < tiles.size(); tile_index++) {
tile_position_x = getTilePosition(tileset_width, tileset_height, tiles[tile_index].getComponent(0), tile_size)[0];
tile_position_y = getTilePosition(tileset_width, tileset_height, tiles[tile_index].getComponent(0), tile_size)[1];
tile.setTextureRect(sf::IntRect(tile_position_x, tile_position_y, tile_size, tile_size));
tile_x = tiles[tile_index].getComponent(1);
tile_y = tiles[tile_index].getComponent(2);
tile.setPosition(sf::Vector2f(tile_x, tile_y));
texture.draw(tile);
}
map.setTexture(texture.getTexture());
}
void Scene::processEvent() {
sf::Event event;
while (window.pollEvent(event)) {
switch (event.type) {
case sf::Event::Closed:
window.close();
break;
case sf::Event::KeyPressed:
if (event.key.code == sf::Keyboard::Escape)
window.close();
break;
}
}
}
void Scene::update(sf::Time deltaTime) {
//REMEMBER: distance = speed * time
//MOVEMENT, ANIMATIONS ETC. ..
}
void Scene::render() {
window.clear();
window.draw(map);
window.display();
}
main.cpp
#pragma once
//global functions + main headers + class headers =>
#include "globalfunctions.h"
int main() {
int map_width = 15,
map_height = 15,
tile_size = 64;
float scene_width = map_width * tile_size,
scene_height = (map_height * tile_size) / 2;
Scene engine(scene_width, scene_height, 0, 0, tile_size, map_width, map_height);
engine.loadTileset("tileset.png");
//engine.loadUiTileset("menu.png");
engine.addLayer();
//...
engine.run();
return EXIT_SUCCESS;
}
globalfunctions.h
Some utility functions.
getTilePosition(...) allow me to get x, y on a texture with a given tile index. Example : if I want to draw the tile n°0 of the tileset texture.
#pragma once
#include <SFML/System.hpp>
#include <SFML/Graphics.hpp>
#include <SFML/Window.hpp>
#include <iostream>
#include <math.h>
#include <vector>
using namespace std;
vector<float> getTilePosition(float tileset_width, float tileset_height, float tile_index, float tile_size) {//In a tileset
float tileX = 0,
tileY = 0,
tilePerLine = 0;
tilePerLine = tileset_width / tile_size;
tileY = floor(tile_index / tilePerLine);
tileX = ((tile_index + 1) - (tileY * tilePerLine)) - 1;
tileX *= tile_size;
tileY *= tile_size;
vector<float> coords;
coords.push_back(tileX);
coords.push_back(tileY);
return coords;
}
vector<int> carthesianToIsometric(int x, int y) {
vector<int> coords;
float isoX = (x - y) / 2,
isoY = (x + y) / 4;
coords.push_back(isoX);
coords.push_back(isoY);
return coords;
}
#include "Object.h"
#include "Scene.h"
//...
And here, the WTF result I get :
Thanks for reading all that weird code !
Edit :
When I change
tile.setPosition(sf::Vector2f(tile_x, tile_y));
to
tile.setPosition(sf::Vector2f(0, 0));
in updateMap() from scene.cpp :
Unfortunatly, I cannot explain why. Maybe it will help you to understand the problem.
In case someone encounter the same problem :
As #Spectre suggested it was a problem of the sfml function draw().
http://en.sfml-dev.org/forums/index.php?topic=6903.0
You need to use display on the sf::renderTexture after your cleared it.
this is my first post on SO, please forgive me if I don't do it right.
I am trying to draw some a simple hexagon grid using C++ SFML. The hex grid is created in it'sown class and called in main.
Hex class Header
class Terrain : public sf::Drawable, public sf::Transformable
{
public:
explicit Terrain(sf::Texture texture, int x, int y);
sf::Texture texture;
sf::CircleShape hexMethod(float x, float y, float Radius);
virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const;
void cameraAngle();
void setTexture(sf::Texture);
void setX(int xint);
void setY(int yint);
void update(sf::Time deltaTime);
sf::Time T_currentTime;
private:
sf::Texture m_terrain;
int xcoord, ycoord;
sf::Vertex m_verticeList[6];
bool isOdd(int integer)
{
if (integer % 2 == 0)
return true;
else
return false;
}
//virtual ~Terrain()
//{
//}
};
Hex class cpp
Terrain::Terrain(sf::Texture texture, int x, int y) :
m_terrain(texture), xcoord(x), ycoord(y)
{
}
void Terrain::setTexture(sf::Texture newText)
{
m_terrain = newText;
}
void Terrain::setX(int xint)
{
xcoord = xint;
}
void Terrain::setY(int yint)
{
ycoord = yint;
}
sf::CircleShape Terrain::hexMethod(float x, float y, float Radius){
float Width = Radius * 2, Height = Width *sqrt(3) / 2;
sf::CircleShape shape(Radius, 6);
float origo = 200;
float yrsize;
float normalDistanceX = origo + Width * 3 / 4 * x;
float normalSeparationY = origo + Height*y;
float isEven = Height / 2;
shape.setTexture(&texture, false);
shape.setOutlineThickness(2);
shape.setOrigin(Radius, Radius);
shape.setRotation(30);
if (Terrain::isOdd(x))
yrsize = normalSeparationY;
else
yrsize = normalSeparationY + isEven;
shape.setPosition(normalDistanceX, yrsize);
m_verticeList[0] = shape.getPoint(0);
m_verticeList[1] = shape.getPoint(1);
m_verticeList[2] = shape.getPoint(2);
m_verticeList[3] = shape.getPoint(3);
m_verticeList[4] = shape.getPoint(4);
m_verticeList[5] = shape.getPoint(5);
return shape;
}
void Terrain::draw(sf::RenderTarget& target, sf::RenderStates states) const
{
for (size_t i = 0; i < 3; i++)
{
for (size_t j = 0; j < 3; j++)
{
states.transform *= getTransform();
states.texture = &m_terrain;
target.draw(m_verticeList, 4, sf::Points, states);
}
}
}
How I call it in main:
int main()
{
sf::RenderWindow window(sf::VideoMode(800, 800), "Puppet Masters!");
if (!tTexture.loadFromFile("Green.jpg"))
return EXIT_FAILURE;
Terrain newterrain(tTexture, 3,3);
newterrain.hexMethod(3, 3, 70);
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
if (event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::P)
{
}
window.clear();
window.draw(newterrain);
window.display();
}
return 0;
}
I get no errors when running the code, however I also get no result, just a blank black screen. My intention is to have the hexes drawn from calling the class and defining it's variables. The Hexfunction I used in main at first to get a hex grid working, I don't think know how to translate that into a class properly.
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);
}