PDA

View Full Version : Crashes on run, works on debugging



FlyDoodle
4th January 2020, 00:29
Hello!
So the game works just fine but when i add the code which creates hp, hunger and thirst bar for my game, the game doesn't load on normal run (and it crashes), but it does fully load when i run the debugger and it works just fine.

this is the code that i run in game.cpp:

void Game::drawBars()
{
bars = new Bars();
scene->addItem(bars->hp);
scene->addItem(bars->hunger);
scene->addItem(bars->thirst);
}

This is Bars.h file

#ifndef BARS_H
#define BARS_H

#include <QGraphicsRectItem>
#include <QGraphicsItem>
#include <QObject>

class Bars: public QObject{
Q_OBJECT
public:
Bars();
QGraphicsPolygonItem *hp;
QGraphicsPolygonItem *hunger;
QGraphicsPolygonItem *thirst;
QLineF line;
QLineF dist;
void setPosition();
private:
int hp_amount;
int hunger_amount;
int thirst_amount;
float size;
float hp_height;
float hunger_height;
float thirst_height;
};

#endif // BARS_H

This is Bars.cpp file

Bars::Bars()
{
hp_amount = 1400;
hunger_amount = 1400;
thirst_amount = 1400;
QVector<QPointF> hp_points,hunger_points,thirst_points;
hp_points << QPointF(1,0) << QPointF(2,0) << QPointF(2.33,1) << QPointF(2.66,2) << QPointF(3,3) << QPointF(3,4) << QPointF(2.66,5) << QPointF(2.33,6) << QPointF(2,7) << QPointF(1,7) << QPointF(0.66,6) << QPointF(0.33,5) << QPointF(0,4) << QPointF(0,3) << QPointF(0.33,2) << QPointF(0.66,1);
hunger_points << QPointF(-1,0) << QPointF(0,0) << QPointF(-0.33,1) << QPointF(-0.66,2) << QPoint(-1,3) << QPoint(-1,4) << QPointF(-0.66,5) << QPointF(-0.33,6) << QPointF(0,7) << QPoint(-1,7) << QPointF(-1.33,6) << QPointF(-1.66,5) << QPoint(-2,4) << QPointF(-2,3) << QPointF(-1.66,2) << QPointF(-1.33,1);
thirst_points << QPoint(3,0) << QPoint(4,0) << QPointF(4.33,1) << QPointF(4.66,2) << QPoint(5,3) << QPoint(5,4) << QPointF(4.66,5) << QPointF(4.33,6) << QPointF(4,7) << QPointF(3,7) << QPointF(3.33,6) << QPointF(3.66,5) << QPointF(4,4) << QPointF(4,3) << QPointF(3.66,2) << QPointF(3.33,1);

line.setPoints(hunger_points[0], hp_points[0]);
dist.setPoints(hp_points[0], hp_points[1]);
size = game->getViewHeight()/57;

for(int i=0;i<hp_points.size();i++){
hp_points[i] *= size;
hunger_points[i] *= size;
thirst_points[i] *= size;
}

hp = new QGraphicsPolygonItem(QPolygonF(hp_points));
hunger = new QGraphicsPolygonItem(QPolygonF(hunger_points));
thirst = new QGraphicsPolygonItem(QPolygonF(thirst_points));

hp_height = hp->boundingRect().height();
hunger_height = hunger->boundingRect().height();
thirst_height = thirst->boundingRect().height();

setPosition();

hp->setBrush(QBrush("#f44047"));
hunger->setBrush(QBrush("#efbc12"));
thirst->setBrush(QBrush("#00a8f3"));
//hp->setPen(Qt::NoPen);
//hunger->setPen(Qt::NoPen);
//thirst->setPen(Qt::NoPen);
hp->setZValue(16);
}

void Bars::setPosition()
{
game->scene->removeItem(hp);
game->scene->addItem(hp);
game->scene->removeItem(hunger);
game->scene->addItem(hunger);
game->scene->removeItem(thirst);
game->scene->addItem(thirst);

hp->setPos(game->hotbarSlots->slot[8]->x()+game->hotbarSlots->slot[8]->boundingRect().width()+(game->getViewWidth()*0.175)/2-dist.length()/2, game->hotbarSlots->slot[8]->y()+game->hotbarSlots->slot[8]->boundingRect().width()-hp_height);
hunger->setPos(hp->x()-line.length()/2,game->hotbarSlots->slot[8]->y()+game->hotbarSlots->slot[8]->boundingRect().width()-hunger_height);
thirst->setPos(hp->x()+line.length()/2,game->hotbarSlots->slot[8]->y()+game->hotbarSlots->slot[8]->boundingRect().width()-thirst_height);
}



Any help or advice is appreciated !!!

ChrisW67
4th January 2020, 07:19
Where is the variable "game" at line 13 of Bars.cpp defined and initialized? It is not a member variable and has not been passed in to the constructor but it is used extensively. If unused unintialized, or its member scene etc. has not been initialized, then segfaults will be your friend.

FlyDoodle
4th January 2020, 10:14
i defined game class globally in main.cpp like this:

#include "game.h"

#include <QApplication>
#include <QTextCodec>

Game *game;

int main(int argc, char *argv[])
{
QApplication a(argc, argv);

srand(time(0));
game = new Game;
game->show();
return a.exec();
}


And in the Bars.cpp its initialized like this:

#include "game.h"

extern Game *game;

And if it helps here is the game.h file

#ifndef GAME_H
#define GAME_H
#include "player.h"
#include "button.h"
#include "sea.h"
#include "land.h"
#include "hotbarslots.h"
#include "soundeffects.h"
#include "story.h"
#include "rabbithole.h"
#include "tree.h"
#include "rock.h"
#include "bars.h"

#include <QGraphicsView>
#include <QGraphicsScene>
#include <QObject>
#include <QMouseEvent>
#include <QGraphicsSceneMouseEvent>
#include <QGraphicsScene>
#include <QWidget>
#include <QKeyEvent>

class Game: public QGraphicsView{
Q_OBJECT
public:
// this class stuff
static QString in_room;
static QString day_or_night;
static int language; // 0~ENG, 1~SLO,
Game(QWidget *parent=NULL);
QGraphicsScene *scene;
void languageMenu();
void menu();
void createMap();
void createPlayer();
void createHotbarSlots();
void spawnAnimals();
void spawnTrees();
void spawnRocks();
void drawBars();
void setStopRectCreated(bool t);
bool getStopRectCreated();
int getDesktopHeight();
int getDesktopWidth();
int getViewHeight();
int getViewWidth();
void setViewWidthToDesktop();
void setViewHeightToDesktop();
void setViewWidthToNormal();
void setViewHeightToNormal();
void changeColorOfButton();
QGraphicsRectItem *stopRect;
void wheelEvent(QWheelEvent *event);
// dark screen stuff
QGraphicsRectItem *darkScreen;
QTimer *darkScreenTimer;
void deleteDarkScreen();
// other class stuff
RabbitHole *rabbitHole;
Story *story;
Player *player;
Land *land;
Sea *sea;
HotbarSlots *hotbarSlots;
SoundEffects *soundEffects;
Tree *tree[3];
Rock *rock[3];
Bars *bars;
// static stuff
private:
// dark screen stuff
double darking_opacity;
bool dark_screen_on;
// this class stuff
int curr_pos_in_menu;
bool stop_rect_created;
int desktop_height;
int desktop_width;
int current_height;
int current_width;
int starting_width;
int starting_height;
public slots:
void setLanguageToSlovenian();
void setLanguageToEnglish();
void startStory();
void start();
// dark screen stuff
void screenGoesDark();
void screenComesFromDark();
};

#endif // GAME_H

d_stranz
4th January 2020, 17:26
There is a big difference between code running in the debugger vs. code running in release mode. To help you debug, the debugger will initialize variables and pointers to known but invalid values. Usually when you try to access something using one of these variables or pointers, it will result in a crash and you can use the debugger to find out where and why. So if your program doesn't crash in debug mode, then it is probably purely by accident that the debugger's initialization lets it run correctly (maybe - probably at some point it will crash - you just haven't tested that part of the code yet).

In release mode, nothing is initialized. Pointers and variables have whatever random values your program memory has when it starts up. So if you haven't initialized something yourself, if you have mistakenly "double declared" a pointer or other variable (like as a class member, then again as a local variable in a method, hiding the class member), if you access a position in a vector or array that is out of bounds, any of these could cause a crash.

In your code, you do no error checking at all that I can see. It just assumes that every pointer is valid, every array index is within bounds, and every function call will work and return a valid result. You have calls with three or four levels of pointer indirection (a->b->c->d) and your code assumes every one of those will result in a valid pointer.

So the error could be anywhere. The first thing you should do is to initialize EVERY variable, either where it is declared (Game * game = nullptr;) or in class constructors, or in methods where local variables are created. Then pay attention to compiler warnings. If you see a warning that a variable is being used before it is assigned a value, hint, hint... And when you run the code in the debugger, if you have initialized all of your variables yourself instead of relying on the debugger to do it, your program should crash and you can find out what is wrong. The debugger doesn't just crash and exit, it tells you what went wrong (index out of range in an array, invalid pointer, etc.). Pay attention to that.

stryga42
6th January 2020, 10:20
Fully agree with d_stranz - initializing data is your friend :-)
But: Debug-code is pre-initilized to fixed but to - for your application - usually meaningless values. If you have an uninitialized pointer it doesn't make much difference if it is a true null pointer or some garbage, pointing anywhere. In both cases your program will usually crash. (Since you do not check for null pointers...)
So for fast fixing your problem I would focus on data structures which contain array sizes, indices pointing to elements in containers and so on. If these are zero-ed your code may work but if they index something outside your container the program will crash.
Good luck and work on your coding style :-)

ChrisW67
6th January 2020, 11:34
Everything above is good advice IMHO.

I cannot see all your code but I suspect...
At line 13 of main.cpp you create a new Game object.
During constructing the Game object you more than likely try to create a Bar object (in drawBars()).
Constructing the Bar object relies on the global Game object pointer which has not been initialised yet (because you are still evaluating the RHS of line 13).


You need to rethink how objects get a pointer to the Game object, and whether it is needed at all. Why does the Bars object need to know about the Game object? Is there a better way to achieve the same thing?