Trackball mouse Rotation in OpenGL - opengl

I'm trying to implement a basic solution for trackball with openGL.
There are 2 rotations :
- around x axis (right direction) , linked with up/down mouse movement.
- around y axis (up direction) , linked with right/left mouse movement.
The cube is centered on 0, camera is on z axis. Axis are drawn : x in blue, y in green, z in red.
2 different groups of rotations:
90° around x by moving the mouse down. Z toward down, x right, y in front.
Then another rotation: 90° around z (z as displayed), mouse movement on the right. z towards the right direction, x up and y in front.
I process again, position start from scratch.
90° around y. y : up , z: right , x: in front
Another rotation of 90° around z. x: up , z: right : y : in front .
I identify a different behaviour between the second rotations.
For coherence, I was waiting that for the first operation, the second rotation was around z
Or for the second operation, the second rotation was around x axis.
Why ?
For me, the second operation has the goood rotations, that's the behaviour i would like to have.
Thank you for your help.
Below the code
cube.cpp :
#include <SDL/SDL.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <cstdlib>
#include <iostream>
#include "sdlglutils.h"
#include "trackballcamera.h"
using namespace std;
void Dessiner();
double angleZ = 0;
double angleX = 0;
double angleY = 0;
TrackBallCamera * camera;
int main(int argc, char *argv[])
{
cout<<"in main cube"<<endl;
freopen("CON", "w", stdout);
freopen("CON", "r", stdin);
freopen("CON", "w", stderr);
SDL_Event event;
SDL_Init(SDL_INIT_VIDEO);
atexit(SDL_Quit);
SDL_WM_SetCaption("SDL GL Application", NULL);
SDL_SetVideoMode(640, 480, 32, SDL_OPENGL);
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
gluPerspective(70,(double)640/480,1,1000);
glEnable(GL_DEPTH_TEST);
camera = new TrackBallCamera();
camera->setScrollSensivity(10);
Dessiner();
Uint32 last_time = SDL_GetTicks();
Uint32 current_time,ellapsed_time;
Uint32 start_time;
GLUquadric* params;
params = gluNewQuadric();
for (;;)
{
start_time = SDL_GetTicks();
while (SDL_PollEvent(&event))
{
switch(event.type)
{
case SDL_QUIT:
exit(0);
break;
case SDL_MOUSEBUTTONUP:
cout<<"SDL_MOUSEBUTTONUP"<<endl;
camera->OnMouseButton(event.button);
break;
case SDL_MOUSEBUTTONDOWN:
cout<<"SDL_MOUSEBUTTONDOWN"<<endl;
camera->OnMouseButton(event.button);
break;
case SDL_MOUSEMOTION:
camera->OnMouseMotion(event.motion);
break;
}
}
current_time = SDL_GetTicks();
ellapsed_time = current_time - last_time;
last_time = current_time;
//angleZ += 0.05 * ellapsed_time;
//angleX += 0.05 * ellapsed_time;
Dessiner();
ellapsed_time = SDL_GetTicks() - start_time;
if (ellapsed_time < 10)
{
SDL_Delay(10 - ellapsed_time);
}
}
return 0;
}
void dessinerRepere(unsigned int echelle = 10)
{
//glPushMatrix();
glScalef(echelle,echelle,echelle);
glBegin(GL_LINES);
glColor3ub(0,0,255);
glVertex3i(0,0,0);
glVertex3i(1,0,0);
glColor3ub(0,255,0);
glVertex3i(0,0,0);
glVertex3i(0,1,0);
glColor3ub(255,0,0);
glVertex3i(0,0,0);
glVertex3i(0,0,1);
glEnd();
//glPopMatrix();
}
void Dessiner()
{
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity( );
//gluLookAt(20,20,20,0,0,0,0,0,1);
camera->look(0,0,camera->getDistance()
,0,0,0
,0,1,0);
//glRotated(angleZ,0,0,1);
//glRotated(angleX,1,0,0);
glBegin(GL_QUADS);
glColor3ub(255,0,0); //face rouge
glVertex3d(1,1,1);
glVertex3d(1,1,-1);
glVertex3d(-1,1,-1);
glVertex3d(-1,1,1);
glColor3ub(0,255,0); //face verte
glVertex3d(1,-1,1);
glVertex3d(1,-1,-1);
glVertex3d(1,1,-1);
glVertex3d(1,1,1);
glColor3ub(0,0,255); //face bleue
glVertex3d(-1,-1,1);
glVertex3d(-1,-1,-1);
glVertex3d(1,-1,-1);
glVertex3d(1,-1,1);
glColor3ub(255,255,0); //face jaune
glVertex3d(-1,1,1);
glVertex3d(-1,1,-1);
glVertex3d(-1,-1,-1);
glVertex3d(-1,-1,1);
glColor3ub(0,255,255); //face cyan
glVertex3d(1,1,-1);
glVertex3d(1,-1,-1);
glVertex3d(-1,-1,-1);
glVertex3d(-1,1,-1);
glColor3ub(255,0,255); //face magenta
glVertex3d(1,-1,1);
glVertex3d(1,1,1);
glVertex3d(-1,1,1);
glVertex3d(-1,-1,1);
glEnd();
//glLoadIdentity( );
dessinerRepere();
glFlush();
SDL_GL_SwapBuffers();
}
trackballcamera.cpp :
#include "trackballcamera.h"
#include <GL/gl.h>
#include <GL/glu.h>
#include <cmath>
#include "trackballcamera.h"
#include "sdlglutils.h"
void TrackBallCamera::setDistance(const double & newDistance)
{
_distance = newDistance;
}
const double & TrackBallCamera::getDistance() const
{
return _distance;
}
TrackBallCamera::TrackBallCamera()
{
const char *hand1[] =
{
/* width height num_colors chars_per_pixel */
" 16 16 3 1 ",
/* colors */
"X c #000000",
". c #ffffff",
" c None",
/* pixels */
" XX ",
" XX X..XXX ",
" X..XX..X..X ",
" X..XX..X..X X ",
" X..X..X..XX.X",
" X..X..X..X..X",
" XX X.......X..X",
"X..XX..........X",
"X...X.........X ",
" X............X ",
" X...........X ",
" X..........X ",
" X.........X ",
" X.......X ",
" X......X ",
" X......X ",
"0,0"
};
const char *hand2[] =
{
/* width height num_colors chars_per_pixel */
" 16 16 3 1 ",
/* colors */
"X c #000000",
". c #ffffff",
" c None",
/* pixels */
" ",
" ",
" ",
" ",
" XX XX XX ",
" X..X..X..XX ",
" X........X.X ",
" X.........X ",
" XX.........X ",
" X...........X ",
" X...........X ",
" X..........X ",
" X.........X ",
" X.......X ",
" X......X ",
" X......X ",
"0,0"
};
_hand1 = cursorFromXPM(hand1);
_hand2 = cursorFromXPM(hand2);
SDL_SetCursor(_hand1);
_holdRotation = false;
_holdTranslation = false;
_angleX = 0;
_angleY = 0;
_angleZ = 0;
_transX = 0;
_transY = 0;
_transZ = 0;
_distance = 50;
_motionSensivity = 0.3;
//_scrollSensivity = 1;
_scrollSensivity = 10;
}
void TrackBallCamera::OnMouseMotion(const SDL_MouseMotionEvent & event)
{
printf("OnMouseMotion; _holdRotation:%d; _holdTranslation:%d\n", _holdRotation, _holdTranslation);
if (_holdRotation)
{
_angleY += event.xrel*_motionSensivity;
_angleX += event.yrel*_motionSensivity;
printf("_angleX:%lf; _angleY:%lf\n",_angleX,_angleY);
// if (_angleY > 90)
// _angleY = 90;
// else if (_angleY < -90)
// _angleY = -90;
}
if (_holdTranslation)
{
_transX += event.xrel*_motionSensivity;
_transY += event.yrel*_motionSensivity;
printf("_transZ:%lf, _transY:%lf\n",_transZ, _transY);
}
}
void TrackBallCamera::OnMouseButton(const SDL_MouseButtonEvent & event)
{
printf("OnMouseButton\n");
if (event.button == SDL_BUTTON_LEFT)
{
if ((_holdRotation)&&(event.type == SDL_MOUSEBUTTONUP))
{
_holdRotation = false;
SDL_SetCursor(_hand1);
}
else if ((!_holdRotation)&&(event.type == SDL_MOUSEBUTTONDOWN))
{
_holdRotation = true;
SDL_SetCursor(_hand2);
}
printf("_holdRotation:%d\n",_holdRotation);
}
else if (event.button == SDL_BUTTON_RIGHT)
{
if ((_holdTranslation)&&(event.type == SDL_MOUSEBUTTONUP))
{
_holdTranslation = false;
SDL_SetCursor(_hand1);
}
else if ((!_holdTranslation)&&(event.type == SDL_MOUSEBUTTONDOWN))
{
_holdTranslation = true;
SDL_SetCursor(_hand2);
}
}
else if ((event.button == SDL_BUTTON_WHEELUP)&&(event.type == SDL_MOUSEBUTTONDOWN))
{
printf("OK WHEELUP, _scrollSensivity:%lf; _distance:%lf\n", _scrollSensivity,_distance);
_distance -= _scrollSensivity;
//if (_distance < 0.1)
//_distance = 0.1;
printf("APRES WHEELUP, _distance:%lf\n", _distance);
}
else if ((event.button == SDL_BUTTON_WHEELDOWN)&&(event.type == SDL_MOUSEBUTTONDOWN))
{
printf("OK WHEELDOWN, _distance:%lf\n",_distance);
_distance += _scrollSensivity;
}
}
void TrackBallCamera::OnKeyboard(const SDL_KeyboardEvent & event)
{
if ((event.type == SDL_KEYDOWN)&&(event.keysym.sym == SDLK_HOME))
{
_angleY = 0;
_angleX = 0;
}
}
void TrackBallCamera::setMotionSensivity(double sensivity)
{
_motionSensivity = sensivity;
}
void TrackBallCamera::setScrollSensivity(double sensivity)
{
_scrollSensivity = sensivity;
}
TrackBallCamera::~TrackBallCamera()
{
SDL_FreeCursor(_hand1);
SDL_FreeCursor(_hand2);
SDL_SetCursor(NULL);
}
//camera->look(cloud.pointPosition[lastElement*3],0,75,cloud.pointPosition[lastElement*3],0,0,0,1,0);
void TrackBallCamera::look(const GLfloat& xPointOfView
,const GLfloat& yPointOfView
,const GLfloat& zPointOfView
,const GLfloat& xCenter
,const GLfloat& yCenter
,const GLfloat& zCenter
,const GLfloat& xVerticalVector
,const GLfloat& yVerticalVector
,const GLfloat& zVerticalVector
)
//void TrackBallCamera::look()
{
//gluLookAt(_distance,0,0,
//0,0,0,
//0,0,1);
//printf("look, xPointOfView:%lf\n",xPointOfView);
gluLookAt(xPointOfView,yPointOfView,zPointOfView,
xCenter,yCenter,zCenter,
xVerticalVector,yVerticalVector,zVerticalVector);
glRotated(_angleX,1,0,0);
glRotated(_angleY,0,1,0);
//glRotated(_angleZ,0,0,1);
//glTranslated(_transX,0,0);
//glTranslated(0,_transY,0);
//glTranslated(0,0,_transZ);
}
trackballcamera.h :
#ifndef TRACKBALLCAMERA_H
#define TRACKBALLCAMERA_H
#include <SDL/SDL.h>
#include <GL/gl.h>
class TrackBallCamera
{
public:
TrackBallCamera();
virtual void OnMouseMotion(const SDL_MouseMotionEvent & event);
virtual void OnMouseButton(const SDL_MouseButtonEvent & event);
virtual void OnKeyboard(const SDL_KeyboardEvent & event);
//virtual void look();
virtual void look(const GLfloat& xPointOfView
,const GLfloat& yPointOfView
,const GLfloat& zPointOfView
,const GLfloat& xCenter
,const GLfloat& yCenter
,const GLfloat& zCenter
,const GLfloat& xVerticalVector
,const GLfloat& yVerticalVector
,const GLfloat& zVerticalVector
);
virtual void setMotionSensivity(double sensivity);
virtual void setScrollSensivity(double sensivity);
void setDistance(const double & newDistance);
const double & getDistance() const ;
virtual ~TrackBallCamera();
protected:
double _motionSensivity;
double _scrollSensivity;
//bool _hold;
bool _holdRotation;
bool _holdTranslation;
double _distance;
double _angleX;
double _angleY;
double _angleZ;
double _transX;
double _transY;
double _transZ;
SDL_Cursor * _hand1;
SDL_Cursor * _hand2;
};
#endif //TRACKBALLCAMERA_H
sdlglutils.cpp :
#include "sdlglutils.h"
#include <SDL/SDL.h>
//#include <SDL/SDL_image.h>
#include <GL/glu.h>
#include <cstring>
#include <cstdlib>
SDL_Cursor * cursorFromXPM(const char * xpm[])
{
int i, row, col;
int width, height;
Uint8 * data;
Uint8 * mask;
int hot_x, hot_y;
SDL_Cursor * cursor = NULL;
sscanf(xpm[0], "%d %d", &width, &height);
data = (Uint8*)calloc(width/8*height,sizeof(Uint8));
mask = (Uint8*)calloc(width/8*height,sizeof(Uint8));
i = -1;
for ( row=0; row<height; ++row )
{
for ( col=0; col<width; ++col )
{
if ( col % 8 )
{
data[i] <<= 1;
mask[i] <<= 1;
}
else
{
++i;
data[i] = mask[i] = 0;
}
switch (xpm[4+row][col])
{
case 'X':
data[i] |= 0x01;
mask[i] |= 0x01;
break;
case '.':
mask[i] |= 0x01;
break;
case ' ':
break;
}
}
}
sscanf(xpm[4+row], "%d,%d", &hot_x, &hot_y);
//printf("data :%" PRIu8 "; mask:%" PRIu8 ";width:%d; height:%d; hot_x:%d; hot_y:%d\n", *data, *mask, width, height, hot_x, hot_y);
cursor = SDL_CreateCursor(data, mask, width, height, hot_x, hot_y);
free(data);
free(mask);
return cursor;
}
sdlglutils.h :
#ifndef SDLGLUTILS_H
#define SDLGLUTILS_H
#include <GL/gl.h>
#include <SDL/SDL.h>
SDL_Cursor * cursorFromXPM(const char * xpm[]);
#endif //SDLGLUTILS_H

You seem to be running into a classic problem with Euler Angles, the order of incremental rotations matters.
Furthermore for an intuitive Trackball behavior for 3D rotation, you need to consider that the model and global rotation axes are not aligned, and thus when you apply the trackball deltas to the global axes, the rotation might be in an unexpected direction. To remedy this, you need to do a coordinate transformation of the user input into global space, or compute a rotation matrix in user space, and transform that rotation matrix from model space into global space, so that the rendering does the right thing.

Related

SDL2 Texture wont move when main loop event calls w, a, s, or d in c++

so i've been working on a game system/engine, for my 2D Platformer, and when i press the w, a, s, or d keys it won't move when called in the main loop event.
Here is all of my project files and everything that i've written:
main.cpp:
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <iostream>
#include <vector>
#include "RenderWindow.hpp"
#include "Entity.hpp"
#include "Utils.hpp"
int main(int argc, char const *argv[])
{
if (SDL_Init(SDL_INIT_VIDEO) > 0)
std::cout << "ERROR: SDL_Init() HAS FAILED: SDL_ERROR => " << SDL_GetError() << std::endl;
if (!(IMG_Init(IMG_INIT_PNG)))
std::cout << "ERROR: IMG_Init() HAS FAILED: SDL_ERROR => " << SDL_GetError() << std::endl;
RenderWindow window("GAME v1.0", 1280, 720);
SDL_Texture* grassTexture = window.loadTexture("res/gfx/ground_grass.png");
SDL_Texture* playerTexture = window.loadTexture("res/gfx/ghost.png");
std::vector<Entity> platforms = {Entity(Vector2f(0, 30), grassTexture),
Entity(Vector2f(30, 30), grassTexture),
Entity(Vector2f(30, 30), grassTexture),
Entity(Vector2f(60, 30), grassTexture)};
Entity player(Vector2f(30, 8), playerTexture);
bool gameRunning = true;
SDL_Event event;
const float timeStep = 0.01f;
float accumulator = 0.0f;
float currentTime = utils::hireTimeInSeconds();
while(gameRunning)
{
int startTicks = SDL_GetTicks();
float newTime = utils::hireTimeInSeconds();
float frameTime = newTime - currentTime;
currentTime = newTime;
accumulator += frameTime;
while(accumulator >= timeStep)
{
// Get out controls and events
while(SDL_PollEvent(&event))
{
if (event.type == SDL_QUIT)
{
gameRunning = false;
break;
// window.freeTexture(grassTexture);
// window.freeTexture(playerTexture);
}
// Add code to move the player texture
const Uint8* currentKeyStates = SDL_GetKeyboardState(NULL);
Vector2f& playerPos = player.getPos();
if(currentKeyStates[SDL_SCANCODE_W]) {
playerPos.y -= 1;
break;
}
if(currentKeyStates[SDL_SCANCODE_S]) {
playerPos.y += 1;
break;
}
if(currentKeyStates[SDL_SCANCODE_A]) {
playerPos.x -= 1;
break;
}
if(currentKeyStates[SDL_SCANCODE_D]) {
playerPos.x += 1;
break;
}
}
window.clear();
for (Entity& e : platforms)
{
window.render(e);
window.render(player);
}
window.display();
// // Add code to move the player texture
// const Uint8* currentKeyStates = SDL_GetKeyboardState(NULL);
// Vector2f& playerPos = player.getPos();
// if(currentKeyStates[SDL_SCANCODE_W]) {
// playerPos.y -= 1;
// }
// if(currentKeyStates[SDL_SCANCODE_S]) {
// playerPos.y += 1;
// }
// if(currentKeyStates[SDL_SCANCODE_A]) {
// playerPos.x -= 1;
// }
// if(currentKeyStates[SDL_SCANCODE_D]) {
// playerPos.x += 1;
// }
//playerPos.print();
accumulator -= timeStep;
// std::cout << accumulator << std::endl;
}
// const float alpha = accumulator / timeStep; // 50%?
// window.freeTexture(grassTexture);
// window.freeTexture(playerTexture);
int frameTicks = SDL_GetTicks() - startTicks;
if (frameTicks < 1000 / window.getRefreshRate())
SDL_Delay(100 / window.getRefreshRate() - frameTicks);
}
window.cleanUp();
SDL_Quit();
return 0;
}
renderwindow.hpp:
#pragma once
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include "Entity.hpp"
class RenderWindow
{
public:
RenderWindow(const char* p_title, int p_w, int p_h);
SDL_Texture* loadTexture(const char* p_filePath);
int getRefreshRate();
void cleanUp();
void clear();
void render(Entity& p_entity);
//void freeTexture(SDL_Texture* p_tex);
void display();
private:
SDL_Window* window;
SDL_Renderer* renderer;
};
renderwindow.cpp:
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <iostream>
#include "RenderWindow.hpp"
#include "Entity.hpp"
RenderWindow::RenderWindow(const char* p_title, int p_w, int p_h)
:window(NULL), renderer(NULL)
{
window = SDL_CreateWindow(p_title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, p_w, p_h, SDL_WINDOW_SHOWN);
if (window == NULL)
{
std::cout << "ERROR: Window has failed to init! SDL_Error: " << SDL_GetError() << std::endl;
}
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
}
SDL_Texture* RenderWindow::loadTexture(const char* p_filePath)
{
SDL_Texture* texture = NULL;
texture = IMG_LoadTexture(renderer, p_filePath);
if (texture == NULL)
std::cout << "ERROR: Failed to load texture! SDL_Error: " << SDL_GetError() << std::endl;
return texture;
}
int RenderWindow::getRefreshRate()
{
int displayIndex = SDL_GetWindowDisplayIndex(window);
SDL_DisplayMode mode;
SDL_GetDisplayMode(displayIndex, 0, &mode);
return mode.refresh_rate;
}
void RenderWindow::cleanUp()
{
SDL_DestroyWindow(window);
}
void RenderWindow::clear()
{
SDL_RenderClear(renderer);
}
void RenderWindow::render(Entity& p_entity)
{
SDL_Rect src;
src.x = p_entity.getCurrentFrame().x;
src.y = p_entity.getCurrentFrame().y;
src.w = p_entity.getCurrentFrame().w;
src.h = p_entity.getCurrentFrame().h;
SDL_Rect dst;
dst.x = p_entity.getPos().x * 4;
dst.y = p_entity.getPos().y * 4;
dst.w = p_entity.getCurrentFrame().w * 4;
dst.h = p_entity.getCurrentFrame().h * 4;
SDL_RenderCopy(renderer, p_entity.getTex(), &src, &dst);
}
// void RenderWindow::freeTexture(SDL_Texture* p_tex) {
// SDL_DestroyTexture(p_tex);
// }
void RenderWindow::display()
{
SDL_RenderPresent(renderer);
}
Entity.hpp:
#pragma once
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include "Math.hpp"
class Entity
{
public:
Entity(Vector2f p_pos, SDL_Texture* p_tex);
Vector2f& getPos()
{
return pos;
}
void setPos(Vector2f p_pos)
{
pos = p_pos;
}
SDL_Texture* getTex();
SDL_Rect getCurrentFrame();
private:
Vector2f pos;
SDL_Rect currentFrame;
SDL_Texture* tex;
};
entity.cpp:
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include "Entity.hpp"
#include "Math.hpp"
Entity::Entity(Vector2f p_pos, SDL_Texture* p_tex)
:pos(p_pos), tex(p_tex)
{
currentFrame.x = 0;
currentFrame.y = 0;
currentFrame.w = 32;
currentFrame.h = 32;
}
SDL_Texture* Entity::getTex()
{
return tex;
}
SDL_Rect Entity::getCurrentFrame()
{
return currentFrame;
}
math.hpp:
#pragma once
#include <iostream>
struct Vector2f
{
Vector2f()
:x(0.0f), y(0.0f)
{}
Vector2f(float p_x, float p_y)
:x(p_x), y(p_y)
{}
void print()
{
std::cout << x << ", " << y << std::endl;
}
float x, y;
};
Utils.hpp:
#pragma once
#include <SDL2/SDL.h>
namespace utils
{
inline float hireTimeInSeconds()
{
float t = SDL_GetTicks();
t *= 0.001f;
return t;
}
}
Thanks!
This is an updated version of my code, i've figured it out.
It turns out the problem was:
if (frameTicks < 1000 / window.getRefreshRate())
SDL_Delay(100 / window.getRefreshRate() - frameTicks);
was actually delaying it by 100 instead of 1000.

SDL2 C++ Snake Game Self Collision

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.

2D Isometric(diamond shape) game engine - Reversed sprites

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.

C++ - Can't get if statement to work using states

I'm currently creating a project which involves switching between different game states such as main menu to play state and play state to battle state etc. I'm currently struggling with getting my battle state to only draw the enemy the player collides with inside the play state. I tried doing this using a very simply "enemyNo =;" where the first enemy was 1, second was 2, and so on. then in battle state, tell it "if(enemyNo =1) then draw enemy 1 and it's corresponding stats. Can't for the life of me figure it out, pretty sure im not inheriting the enemyNo value across states properly. Any and all help would be appreciated.
StatePlay.cpp
#include "StatePlay.h"
#include <iostream>
// stringstream and string
#include <sstream>
#include <string>
#include "Game.h"
StatePlay::StatePlay(void)
{
}
StatePlay::~StatePlay(void)
{
}
void StatePlay::init(Game &context)
{
std::cout << "Initialising play state." << std::endl;
player = new Rect(0.0f, 0.0f, 0.15f, 0.15f);
//player stats
health = 10;
maxHealth = 10;
strength = 10;
speed = 10;
dollars = 0;
noTargets = false;
targetCount = 5;
bruteCount = 3;
raiderCount = 1;
//target
for(int i = 1; i <=5; i++)
{
target[i] = new Rect();
target[i]->setX((float)rand()/RAND_MAX - 0.75f);
target[i]->setY((float)rand()/RAND_MAX - 0.75f);
targetAlive[i] = true;
}
//brute
for(int i = 1; i <=3; i++)
{
brute[i] = new Rect();
brute[i]->setX((float)rand()/RAND_MAX - 0.75f);
brute[i]->setY((float)rand()/RAND_MAX - 0.75f);
bruteAlive[i] = true;
}
//raider
for(int i = 1; i <=1; i++)
{
raider[i] = new Rect();
raider[i]->setX((float)rand()/RAND_MAX - 0.75f);
raider[i]->setY((float)rand()/RAND_MAX - 0.75f);
raiderAlive[i] = true;
}
score = 0;
}
void StatePlay::enter()
{
std::cout << "Entering play state. Press ESCAPE to return to menu" << std::endl;
}
void StatePlay::exit()
{
}
void StatePlay::draw(SDL_Window * window)
{
// Replace this with OpenGL draw calls to draw the main menu screen
TTF_Font *textFont = TTF_OpenFont("MavenPro-Regular.ttf", 24);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glColor3f(1.0,1.0,1.0);
player->draw();
std::stringstream strStream2;
std::stringstream strStream3;
strStream2 << "Player" << std::endl;
strStream3 << "Target" << std::endl;
Label :: displayString(player->x() + player->w()/2.0,player->y() + player->h(), strStream2.str().c_str(), textFont);
//enemy
for(int i = 1; i <=5; i++)
{
if(targetAlive[i] == true)
{
Label :: displayString(target[i]->x() + target[i]->w()/2.0,target[i]->y() + target[i]->h(), strStream3.str().c_str(), textFont);
glColor3f(1.0,0.0,0.0);
targetAlive[i] = target[i];
target[i]->draw();
}
}
std::stringstream strStream4;
strStream4 << "Brute" << std::endl;
//brute
for(int i = 1; i <=3; i++)
{
if(bruteAlive[i] == true)
{
Label :: displayString(brute[i]->x() + brute[i]->w()/2.0,brute[i]->y() + brute[i]->h(), strStream4.str().c_str(), textFont);
glColor3f(0.6,0.3,0.1);
bruteAlive[i] = brute[i];
brute[i]->draw();
}
}
std::stringstream strStream6;
strStream6 << "Raider" << std::endl;
//raider
for(int i = 1; i <=1; i++)
{
if(raiderAlive[i] == true)
{
Label :: displayString(raider[i]->x() + raider[i]->w()/2.0,raider[i]->y() + raider[i]->h(), strStream6.str().c_str(), textFont);
glColor3f(0.2,0.9,0.4);
raiderAlive[i] = raider[i];
raider[i]->draw();
}
}
//information
std::stringstream strStream;
std::stringstream strStream1;
strStream << "Score:" << score;
strStream1 << "Health:" << health;
Label :: displayString(-0.9,-0.9, strStream.str().c_str(), textFont);
Label :: displayString(-0.4,-0.9, strStream1.str().c_str(), textFont);
SDL_GL_SwapWindow(window); // swap buffers
}
void StatePlay::handleSDLEvent(SDL_Event const &sdlEvent, Game &context)
{
if (sdlEvent.type == SDL_KEYDOWN)
{
//std::cout << "Scancode: " << sdlEvent.key.keysym.scancode ;
//std::cout << ", Name: " << SDL_GetKeyName( sdlEvent.key.keysym.sym ) << std::endl;
switch( sdlEvent.key.keysym.sym )
{
case SDLK_UP:
case 'w': case 'W':
player->setY(player->y() + 0.05f);
break;
case SDLK_DOWN:
case 's': case 'S':
player->setY(player->y() - 0.05f);
break;
case SDLK_LEFT:
case 'a': case 'A':
player->setX(player->x() - 0.05f);
break;
case SDLK_RIGHT:
case 'd': case 'D':
player->setX(player->x() + 0.05f);
break;
default:
break;
}
}
if (sdlEvent.type == SDL_KEYDOWN)
{
//std::cout << "Scancode: " << sdlEvent.key.keysym.scancode ;
//std::cout << ", Name: " << SDL_GetKeyName( sdlEvent.key.keysym.sym ) << std::endl;
switch( sdlEvent.key.keysym.sym )
{
case SDLK_ESCAPE:
context.setState(context.getMainMenuState());
default:
break;
}
}
//target
for(int i = 1; i <=5; i++)
{
if ( (target[i]->x() >= player->x()) && (target[i]->x()+target[i]->w() <= player->x()+player->w()) // cursor surrounds target in x
&& (target[i]->y() >= player->y()) && (target[i]->y()+target[i]->h() <= player->y()+player->h() ) && targetAlive[i] == true ) // cursor surrounds target in y
{
score += 100;
enemyNo = 1;
context.setState(context.getBattleState());
delete target[i];
targetAlive[i] = false;
targetCount -= 1;
}
}
//brute
for(int i = 1; i <=3; i++)
{
if ( (brute[i]->x() >= player->x()) && (brute[i]->x()+brute[i]->w() <= player->x()+player->w()) // cursor surrounds target in x
&& (brute[i]->y() >= player->y()) && (brute[i]->y()+brute[i]->h() <= player->y()+player->h() ) && bruteAlive[i] == true ) // cursor surrounds target in y
{
score += 100;
enemyNo = 2;
context.setState(context.getBattleState());
delete brute[i];
bruteAlive[i] = false;
bruteCount -= 1;
}
}
//raider
for(int i = 1; i <=1; i++)
{
if ( (raider[i]->x() >= player->x()) && (raider[i]->x()+raider[i]->w() <= player->x()+player->w()) // cursor surrounds target in x
&& (raider[i]->y() >= player->y()) && (raider[i]->y()+raider[i]->h() <= player->y()+player->h() ) && raiderAlive[i] == true ) // cursor surrounds target in y
{
score += 100;
enemyNo = 3;
context.setState(context.getBattleState());
delete raider[i];
raiderAlive[i] = false;
raiderCount -= 1;
}
}
}
StatePlay.h
#ifndef STATE_PLAY
#define STATE_PLAY
#include "gameState.h"
#include "Label.h"
// C stdlib and C time libraries for rand and time functions
#include <cstdlib>
#include <ctime>
#include "rect.h"
class StatePlay : public GameState
{
public:
StatePlay(void);
~StatePlay(void);
void init(Game &context);
void draw(SDL_Window * window);
void handleSDLEvent(SDL_Event const &sdlEvent, Game &context);
void enter();
void exit();
int enemyNo;
int targetCount;
private:
//player Stats
int score;
int health;
int maxHealth;
int strength;
int speed;
int dollars;
int getHealth() {return health;}
int getMaxHealth() { return maxHealth;}
int getStrength() { return strength;}
int getSpeed() { return speed;}
int getDollars() { return dollars; }
void setHealth(int newHealth) { health = newHealth; }
void damageTaken(int damage) { health = health - damage; }
void setMaxHealth(int newHealth) { maxHealth = maxHealth + newHealth; }
void setStrength(int newStrength) { strength = strength+newStrength; }
void setSpeed(int newSpeed) { speed = speed+newSpeed; }
void setDollars(int newDollars) { dollars = dollars+newDollars; }
// Another *strong* candidate for creating a new class type
// is character/agent, to encapsulate player and target data,
// potentially to include an Agent::draw() function and
// perhaps also generalise collision detection
// This is left as a further exercise
float xpos;
float ypos;
float xsize;
float ysize;
//target
float targetXPos;
float targetYPos;
float targetXSize;
float targetYSize;
bool targetAlive[5];
bool noTargets;
//brute
float bruteXPos;
float bruteYPos;
float bruteXSize;
float bruteYSize;
int bruteCount;
bool bruteAlive[5];
bool noBrutes;
//raider
float raiderXPos;
float raiderYPos;
float raiderXSize;
float raiderYSize;
int raiderCount;
bool raiderAlive[1];
bool noRaider;
clock_t lastTime; // clock_t is an integer type
clock_t currentTime; // use this to track time between frames
Label * playerLabel;
Label * targetLabel;
Label * bruteLabel;
Label * raiderLabel;
TTF_Font * textFont; // SDL type for True-Type font rendering
Rect * player;
Rect * target[5];
Rect * brute[3];
Rect * raider[1];
};
#endif
StateBattle.cpp
#include "StateBattle.h"
#include <iostream>
#include "Game.h"
#include "StatePlay.h"
#include "rect.h"
StateBattle::StateBattle(void)
{
}
StateBattle::~StateBattle(void)
{
}
void StateBattle::init(Game &context)
{
fighting = false;
}
void StateBattle::enter()
{
std::cout << "Entering main menu state: Press RETURN to play or ESCAPE to quit " << std::endl;
}
void StateBattle::exit()
{
}
void StateBattle::draw(SDL_Window * window)
{
glClearColor(0.0f,0.0f,0.0f,0.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glColor3f(1.0,1.0,1.0);
TTF_Font *textFont = TTF_OpenFont("MavenPro-Regular.ttf", 24);
std::stringstream strStream;
strStream << "FIGHT!" <<std::endl;
Label :: displayString(-0.8 ,0.8, strStream.str().c_str(),textFont);
//player
Rect * player;
player = new Rect(-0.4f, 0.0f, 0.15f, 0.15f);
player ->draw();
std::stringstream playerStrStream1;
playerStrStream1 << "Health:" << std::endl;
Label :: displayString(-0.4 ,0.5, playerStrStream1.str().c_str(),textFont);
std::stringstream playerStrStream2;
playerStrStream2 << "Strength:" << std::endl;
Label :: displayString(-0.4 , -0.2, playerStrStream2.str().c_str(),textFont);
std::stringstream playerStrStream3;
playerStrStream3 << "Speed:" << std::endl;
Label :: displayString(-0.4 , -0.3, playerStrStream3.str().c_str(),textFont);
std::stringstream playerStrStream4;
playerStrStream4 << "Dollars:" << std::endl;
Label :: displayString(-0.4 , -0.4, playerStrStream4.str().c_str(),textFont);
//Enemy
if(enemyNo = 1)
{
Rect * target;
glColor3f(1.0,0.0,0.0);
target = new Rect(0.4f, 0.0f, 0.15f, 0.15f);
target->draw();
std::stringstream targetStrStream1;
targetStrStream1 << "Health:" << std::endl;
Label :: displayString(0.4f,0.5f, targetStrStream1.str().c_str(),textFont);
std::stringstream targetStrStream2;
targetStrStream2 << "Strength:" << std::endl;
Label :: displayString(0.4, -0.2, targetStrStream2.str().c_str(),textFont);
std::stringstream targetStrStream3;
targetStrStream3 << "Speed:" << std::endl;
Label :: displayString(0.4, -0.3, targetStrStream3.str().c_str(),textFont);
std::stringstream targetStrStream4;
targetStrStream4 << "Dollars:" << std::endl;
Label :: displayString(0.4, -0.4, playerStrStream4.str().c_str(),textFont);
}
//brute
if(enemyNo = 2)
{
Rect * brute;
glColor3f(0.6,0.3,0.1);
brute = new Rect(0.6f, 0.0f, 0.15f, 0.15f);
brute->draw();
std::stringstream bruteStrStream1;
bruteStrStream1 << "Health:" << std::endl;
Label :: displayString(0.4, 0.5, bruteStrStream1.str().c_str(),textFont);
std::stringstream bruteStrStream2;
bruteStrStream2 << "Strength:" << std::endl;
Label :: displayString(0.4, -0.2, bruteStrStream2.str().c_str(),textFont);
std::stringstream bruteStrStream3;
bruteStrStream3 << "Speed:" << std::endl;
Label :: displayString(0.4, -0.3, bruteStrStream3.str().c_str(),textFont);
std::stringstream bruteStrStream4;
bruteStrStream4 << "Dollars:" << std::endl;
Label :: displayString(0.4, -0.4, bruteStrStream4.str().c_str(),textFont);
}
//raider
if(enemyNo = 3)
{
Rect * raider;
glColor3f(0.2,0.9,0.4);
raider = new Rect(0.8f, 0.0f, 0.15f, 0.15f);
raider->draw();
std::stringstream raiderStrStream1;
raiderStrStream1 << "Health:" << std::endl;
Label :: displayString(0.4, 0.5, raiderStrStream1.str().c_str(),textFont);
std::stringstream raiderStrStream2;
raiderStrStream2 << "Strength:" << std::endl;
Label :: displayString(0.4, -0.2, raiderStrStream2.str().c_str(),textFont);
std::stringstream raiderStrStream3;
raiderStrStream3 << "Speed:" << std::endl;
Label :: displayString(0.4, -0.3, raiderStrStream3.str().c_str(),textFont);
std::stringstream raiderStrStream4;
raiderStrStream4 << "Dollars:" << std::endl;
Label :: displayString(0.4, -0.4, raiderStrStream4.str().c_str(),textFont);
}
SDL_GL_SwapWindow(window); // swap buffers
}
void StateBattle::handleSDLEvent(SDL_Event const &sdlEvent, Game &context)
{
// Add additional input checks for any other actions carried out in this state
std::cout << "Event handling in main menu state." << std::endl;
if (sdlEvent.type == SDL_KEYDOWN)
{
//std::cout << "Scancode: " << sdlEvent.key.keysym.scancode ;
//std::cout << ", Name: " << SDL_GetKeyName( sdlEvent.key.keysym.sym ) << std::endl;
switch( sdlEvent.key.keysym.sym )
{
case SDLK_RETURN:
context.setState(context.getPlayState());
break;
case SDLK_ESCAPE:
// Create a SDL quit event and push into the event queue
SDL_Event quitEvent;
quitEvent.type = SDL_QUIT;
SDL_PushEvent(&quitEvent);
default:
break;
}
}
}
StateBattle.h
#ifndef STATE_BATTLE
#define STATE_BATTLE
#include "GameState.h"
#include "Label.h"
#include "StatePlay.h"
class StateBattle : public GameState, public StatePlay
{
public:
StateBattle(void);
~StateBattle(void);
void init(Game &context);
void draw(SDL_Window * window);
void handleSDLEvent(SDL_Event const &sdlEvent, Game &context);
void enter();
void exit();
bool fighting;
};
#endif
I've included all the .cpp and .h files involved, hopefully this is enough. If not i can upload more.
Thanks again
You are missing a =, you should write
if(enemyNo == 1)
instead of
if(enemyNo = 1)
But for the next time, please provide a smaller example (as already suggested) with the exact error message.
You should declare the dstor in StatePlay virtual, because it acts as a base class for the StateBattle class. And in the StateBattle implementation file your are initializing enemyNo to 1 instead of comparing it to 1:
//Enemy
if(enemyNo == 1) // <-- missing ==
Inside your states you should only hold state variables: variables that are bound to only this specific state. Enemies could be a state variable if you have just one game state and all other states are complelty independent (i.e. menu state, setting state...). But your states kind of having cross dependencies.
To solve this problem you should extract first everything that doesn't belong to one specific state into another class. I.e. You could have a 'World' class, which manages the list of enemies, terrain, ... Or a class for a List of your Entities. It's a design decision you have to take yourself.
However, your StatePlay shouldn't handle the enemies - and get it inherited by the StateBattle. StateBattle overrides the init(Game &context) function from StatePlay - so it get's never called if you don't call it yourself. The enemyNo in StateBattle is never initalized this way (in fact it is, but just because of another mistake)!
You could write:
void StateBattle::init(Game &context)
{
StatePlay::init(context);
fighting = false;
}
to do so. But like I pointed out, there are some things in your class design, that makes handling these things hard and redundant.
Also you are not checking the enemyNo - you're setting it. = is the assignment Operator - you want to use the compare Operator ==
So better use:
if(enemyNo == 1)
instead of
if(enemyNo = 1)
Otherwise it will set your variable enemyNo to 1 instead of compare it against 1.

SDL software rendering faster than hardware rendering

I've just found out a strange behavior of SDL.
I've written a simple particle renderer, and for some reason it runs about 6 times faster with the software renderer than with the hardware renderer.
Here's the source code:
main.cpp
#define _USE_MATH_DEFINES
#include <iostream>
#include <cstdlib>
#include <Windows.h>
#include <vector>
#include <math.h>
#include <time.h>
#include <SDL.h>
#include "Particle.h"
const int SCREEN_WIDTH = 1024;
const int SCREEN_HEIGHT = 600;
const int PARTICLE_NUMBER = 50000;
const int MAX_SPEED = 200;
const int MIN_SPEED = 5;
long long getMs (void) {
SYSTEMTIME stime;
GetLocalTime(&stime);
long long ms = stime.wMilliseconds +
stime.wSecond * 1000 +
stime.wMinute * 60000 +
stime.wHour * 3600000 +
stime.wDay * 86400000 +
stime.wMonth * 2592000000 +
(stime.wYear - 1970) * 31104000000;
return ms;
}
int main(int argc, char *argv[])
{
bool hardwareAccelerated = true;
if (argc == 2)
{
if (strncmp(argv[1], "-software", 9) == 0)
{
hardwareAccelerated = false;
}
}
char title [100];
sprintf(title, "Particles: %d - (%s)", PARTICLE_NUMBER, (hardwareAccelerated ? "HARDWARE ACCELERATED" : "SOFTWARE RENDERING"));
Particle<double> *particles = (Particle<double>*) malloc(sizeof(Particle<double>) * PARTICLE_NUMBER);
for (int i = 0; i < PARTICLE_NUMBER; i++)
{
double x = rand() % SCREEN_WIDTH;
double y = rand() % SCREEN_HEIGHT;
double direction = (((double) rand() / (double) RAND_MAX) - 0.5f) * 2 * M_PI;
double speed = rand() % (MAX_SPEED - MIN_SPEED) + MIN_SPEED;
(particles+i)->setPos(x, y);
(particles+i)->setDirection(direction);
(particles+i)->setSpeed(speed);
// std::cout << (particles+i) << std::endl;
}
if (SDL_Init(SDL_INIT_EVERYTHING) != 0) {
return 1;
}
SDL_Window *window = SDL_CreateWindow(title,
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
if (window == nullptr) {
return 2;
}
SDL_RendererFlags flags = (hardwareAccelerated ? SDL_RENDERER_ACCELERATED : SDL_RENDERER_SOFTWARE);
SDL_Renderer *renderer = SDL_CreateRenderer(window, -1,
flags);
if (renderer == nullptr) {
return 3;
}
bool quit = false;
SDL_Event evt;
long long lastFrame = getMs();
double delta = 0.f;
while (!quit)
{
long long currentTime = getMs();
delta = currentTime - lastFrame;
lastFrame = currentTime;
std::cout << "delta: " << delta << std::endl;
while(SDL_PollEvent(&evt) != 0)
{
if (evt.type == SDL_QUIT)
{
quit = true;
}
}
SDL_SetRenderDrawColor(renderer, 0,0,0,1);
SDL_RenderClear(renderer);
SDL_SetRenderDrawColor(renderer, 255,0,0,255);
for (int i = 0; i < PARTICLE_NUMBER; i++)
{
(particles+i)->tick(delta);
double *pos = (particles+i)->getPos();
SDL_RenderDrawPoint(renderer, pos[0], pos[1]);
}
SDL_RenderPresent(renderer);
}
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}
particle.h
#ifndef _H_PARTICLE
#define _H_PARTICLE
#include <math.h>
template <class T>
class Particle
{
public:
Particle(void);
void tick(double);
void setPos(T, T);
T* getPos(void);
void setDirection(double);
double getDirection(void);
void setSpeed(T);
T getSpeed(void);
~Particle(void);
private:
T x;
T y;
T speed;
double direction;
};
template <class T>
Particle<T>::Particle(void)
{
}
template <class T>
void Particle<T>::tick(double delta)
{
double dt = delta / 1000;
T d_speed = this->speed * dt;
// std::cout << d_speed << std::endl;
this->x += cos(this->direction) * d_speed;
this->y += sin(this->direction) * d_speed;
if (this->x > SCREEN_WIDTH) this->x = 0;
if (this->y > SCREEN_HEIGHT) this->y = 0;
if (this->x < 0) this->x = SCREEN_WIDTH;
if (this->y < 0) this->y = SCREEN_HEIGHT;
}
template <class T>
void Particle<T>::setPos(T x, T y)
{
this->x = x;
this->y = y;
}
template <class T>
T* Particle<T>::getPos(void)
{
T pos[2];
pos[0] = this->x;
pos[1] = this->y;
return pos;
}
template <class T>
void Particle<T>::setDirection(double direction)
{
this->direction = direction;
}
template <class T>
double Particle<T>::getDirection(void)
{
return this->direction;
}
template <class T>
void Particle<T>::setSpeed(T speed)
{
this->speed = speed;
}
template <class T>
T Particle<T>::getSpeed(void)
{
return this->speed;
}
template <class T>
Particle<T>::~Particle(void)
{
}
#endif
Why is this happening? Shouldn't the hardware renderer be a lot faster than the software one?
SDL_RenderDrawPoint() calls SDL_RenderDrawPoints() but wiht a count of 1.
SDL_RenderDrawPoints() calls SDL_stack_alloc() before rendering the number of points it needs to, and SDL_stack_free() when it's finished. That's probably your problem. You're doing a malloc and free for every single particle in your system, each frame.
I reckon Retired Ninja has the right idea - use SDL_RenderDrawPoints() instead and just do the malloc and free once each frame.
Alternatively - use a different paradigm. Create an SDL_Surface once. Each frame, you blit all the pixels you need to ( by directly manipulating the pixel memory of the SDL_Surface by doing SDL_MapRGB() on a particular pixel ) and then when it comes to rendering, convert the SDL_Surface to a an SDL_Texture and present that to the renderer.
Some example code - if a Particle is a class and contains a pointer to an SDL_Surface, then you could have a draw function that looks like this:
void Particle::draw()
{
Uint32 x = m_position.getX();
Uint32 y = m_position.getY();
Uint32 * pixel = (Uint32*)m_screen->pixels+(y*(m_pitch/4))+x;
Uint8 r1 = 0;
Uint8 g1 = 0;
Uint8 b1 = 0;
Uint8 a1 = 0;
GFX_RGBA_FROM_PIXEL(*pixel, m_screen->format, &r1, &g1, &b1, &a1);
Uint32 * p = (Uint32*)m_screen->pixels+(y*(m_pitch/4))+x;
*p = SDL_MapRGB(m_screen->format, m_r, m_g, m_b);
}
Where GFX_RGBA_FROM_PIXEL is (stolen from Andreas Schiffler's SDL2_gfx library) and is defined as:
///////////////////////////////////////////////////////////////////
void GFX_RGBA_FROM_PIXEL(Uint32 pixel, SDL_PixelFormat * fmt, Uint8* r, Uint8* g, Uint8* b, Uint8* a)
{
*r = ((pixel&fmt->Rmask)>>fmt->Rshift)<<fmt->Rloss;
*g = ((pixel&fmt->Gmask)>>fmt->Gshift)<<fmt->Gloss;
*b = ((pixel&fmt->Bmask)>>fmt->Bshift)<<fmt->Bloss;
*a = ((pixel&fmt->Amask)>>fmt->Ashift)<<fmt->Aloss;
}
It might work faster. I haven't done any time tests but it might be worth it, as you're directly manipulating the colour of pixel memory, and then simply blitting it each frame. You're not doing any mallocs or frees.