PDA

View Full Version : First QT - C++ Game



Sean11
22nd September 2017, 02:53
Hey guys im building my first QT C++ game and i hope ill get some help in a problem i have encountered
im trying to make group of spaceships in a line, that moves from one side of the screen to the other.
now when i built it i have defined that if spaceship x() != 1000 keep moving right if it does go left. but they cross eachother and each one of them has to reach the x() == 1000 to change direction.
now i want to them to move as a group if the most left spaceship reached the maximum space of the right side of the screen they all start move left and so..
https://github.com/SeanR11/Spaceship---first-game
here is my code on github hope u can read and come with a solution ty.!

d_stranz
22nd September 2017, 04:07
The problem is in your design. As it stands now, when you create your game scene, it goes from x = 0 - 1000. You put 10 spaceships in a line separated by 100 units, going from 0 - 900. The problem is this: you start timers in each spaceship that move each one independently of all the others. So when one spaceship reaches the end of the line, none of the others know about it. So even if that last spaceship changes direction, the other 9 keep on marching towards 1000 (or 0) until each one also makes it to the end and turns around. So instead of a line of space invader style ships marching back and forth together, you have a conga line.

What you need is to add another piece to the design - a spaceship controller. This class is the only one that needs a timer. With each timeout, it advances the position of -all- of the spaceships. When spaceship[9] (the tenth one) reaches 1000 or spaceship[0] (the first one) reaches 0, the controller changes the direction of the line and everyone turns around at the same time.

An easy place to do this is instead of creating a new class, let the game class do it. As you create each spaceship and add it to the scene, also add the pointer to a QVector<Spaceship*> that is a member variable of the game class. When you are ready to start playing, start the QTimer and then update the positions of every spaceship when it times out by retrieving each one from the QVector and updating pos()..

Sean11
22nd September 2017, 09:55
First of all thanks for your answer
i really liked your idea and got the main point of what u wanted to explain to me.
but as i said im new to QT and it is my first game and i dont know how to use QVector or even what it does.
now i know the whole point of writing a game is writing it alone but because im still learning i be glad if u can help me write a code that i can learn from him to next times/games
i know it is a work for you but if you could help me ill be more than happy to receive your help.

**EDITED**
another problem i have encountered is that i cant create spaceship[10] i have tried it and i saw you wrote spaceship[0]->spaceship[9] but i couldnt create this object
there is any solution for that?

again tnx for your helping.

ado130
22nd September 2017, 10:54
Do you know C++? Or do you want to learn C++? Maybe you can start with a little bit easier application than a game.

Sean11
22nd September 2017, 15:58
I have programmed many games and softwarers on c++ on visual studio, on cmd platform...
im learning QT.. its a bit different for me and i didnt programmed at all for like 1-1.5 years and i just wanted a help with the code and if u can help me and show me the way to get better ill be much thanksful for that.
i also have another question if i want to add class that in-charge of changing levels. What should the class inherits from?

d_stranz
22nd September 2017, 16:30
spaceship[10]

I meant this is a figurative sense, not as C++ code. "spaceship[0]" is the first spaceship you create in your loop. "spaceship[9]" is the last one. I agree with ado130 - you really need to learn a little more about C++. QVector is the Qt version of std:: vector, and if you don't know what that is either, then you need to learn some more. It's hard to help you with example code if you don't understand the code we are providing.

Remove the QTimer stuff from the Spaceship class. Here is your game.[h, cpp] with some added code to help you get started:



// game.h

#ifndef GAME_H
#define GAME_H

#include <QGraphicsView>
#include <QGraphicsScene>
#include <QVector>

class Spaceship;
class QTimer;

class Game: public QGraphicsView
{
Q_OBJECT; // <<< Absolutely need this

public:
// constructor
Game(QWidget* parent=NULL);

// public method
void start();

private slots:
void onSpaceshipTimeout();

// Attributes should ALWAYS be private or protected, never public
// If you need access to them from outside the Game class, then provide
// Game class methods to get or set their values.

private:
QGraphicsScene * scene;
//Map* map; TODO
QString Score;
QString Life;

QTimer * pSpaceshipTimer;
QVector < Spaceship * > spaceships;
int direction;
};

#endif // GAME_H




#include "game.h"
#include "player.h"
#include <QTimer>
#include "spaceship.h"

Game::Game(QWidget *parent)
: QGraphicsView( parent ) // <<< NEED THIS
, direction( 1 )
{

// set screen
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOf f);
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff) ;
setFixedSize(1024,800);

// set scene
scene = new QGraphicsScene();
scene->setSceneRect(0,0,1000,800);
setScene(scene);

// set player
Player * player = new Player();

// add player to scene
scene->addItem(player);

// add enemy to scene
for(int lol = 0; lol < 10; lol++)
{
Spaceship * spaceship = new Spaceship(x, 10);
scene->addItem(spaceship);
x = x + 100;

// add a copy of the spaceship POINTER to the vector for use later
spaceships.push_back( spaceship );
}

pSpaceshipTimer = new QTimer( this );
connect( pSpaceshipTimer, SIGNAL( timeout() ), this, SLOT( onSpaceshipTimeout() ) );
}

void Game::start()
{
pSpaceshipTimer->start( 500 ); // 50 is too short.
}

void Game::onSpaceshipTimeout()
{
// Note that "direction" has the values 1 or -1 to indicate movement to the right or left, respectively
int nShips = spaceships.size();
if ( direction > 0 )
{
if ( spaceships[ nShips - 1 ]->x() >= 950 )
direction = -1;
}
else
{
if ( spaceships[ 0 ]->x() <= 50 )
direction = 1;
}

int distance = 10 * direction;
for ( nShip = 0; nShip < nShips; ++nShip )
{
Spaceship * spaceship = spaceships[ nShip ];
spaceship->setX( spaceship->x() + distance );
}
}


Not compiled or tested so there may be typos or bugs.

Sean11
22nd September 2017, 16:46
i understand that it is harder to learn if i dont know what the code means but i read the code and it i tested your code fixed some typos issue and the code still does the same
they are moving left and then each one of them have to hit the wall by himself they are not moving as 1 line of spaceships.

d_stranz
22nd September 2017, 17:26
they are not moving as 1 line of spaceships.

Did you remove the QTimer from your Spaceship class?

Sean11
22nd September 2017, 19:29
Oh i forgot to do it.
i did it and it is working now..
thanks for your help i will read it few times in order to understand what each line says and means.
meanwhile i have another small question if i want to create class that controls the levels in this class there will be void for each level and in each void it will create spaceships in the order the level require to:
1.what should the class inherits from? QGraphicsView?
2.should i create QVector on the level class so i can make my for loop that will .push_back the spaceship POINTER into the Vector and then method inside game.cpp that will transfer the POINTER from Level.cpp Vector to game.cpp Vector("spaceships" thats the name you gave the vector)

d_stranz
22nd September 2017, 21:16
I'm sorry, but I don't really understand what you are asking. What is a "level"? Is it a type of difficulty of play? Or do you mean additional rows of spaceships?

If you mean a "level" is a row of spaceships, then I think a Level class (derived from QObject if you want it to receive signals to slots) would be the best solution. In that case, each Level should own a QVector of spaceship pointers, the spaceships in that row of the screen. The Game should have a QVector of Level pointers instead of the QVector of spaceship pointers.

The Level class probably should have these methods, slots, and signals:

- addSpaceship( Spaceship * ) // pushes a spaceship onto the QVector

// a slot - this is the same code as is now in Game:: onSpaceshipTimout(), except you also need to decrease the y() position when the spaceships reach the end of the line in addition to changing direction
- void step()

// a signal emitted each time the row reaches the end of the line and changes direction. yPos is the new y position when the row drops down. Connected to the Game's "onEndOfLine( int yPos )" slot. In this slot, the Game decides whether to add a new Level at the top of the screen or if the game should end (when yPos == 0)
- void endOfLine( int yPos )


You should change the Game class so that instead of creating a row of spaceships in the constructor, it calls an "addLevel( int yPos ) method:


void Game::addLevel( int yPos )
{
Level * level = new Level( this );

// add enemy to scene
for(int lol = 0; lol < 10; lol++)
{
Spaceship * spaceship = new Spaceship(x, yPos );
scene->addItem(spaceship);
x = x + 100;

// add the spaceship to the level
level->addSpaceship( spaceship );
}
connect( level, SIGNAL( endOfLine( int ) ), this, SLOT( onEndOfLine( int ) ) );

levels.push_back( level ); // "levels" is QVector< Level * >, a member of the Game class
}

void Game::onSpaceshipTimeout()
{
int nLevels = levels.size();
for ( int nLevel = 0; nLevel < nLevels; ++nLevel )
{
Level * level = levels[ nLevel ];
level->step();
}
}

void Level::step()
{
// Note that "direction" has the values 1 or -1 to indicate movement to the right or left, respectively
int nShips = spaceships.size();
bool bChangedDirection = false;
if ( direction > 0 )
{
if ( spaceships[ nShips - 1 ]->x() >= 950 )
{
direction = -1;
bChangedDirection = true;
}
}
else
{
if ( spaceships[ 0 ]->x() <= 50 )
{
direction = 1;
bChangedDirection = true;
}
}

int yPos = spaceships[ 0 ]->y();
if ( bChangedDirection ) // have to decrease y also now
yPos -= 10;

int distance = 10 * direction;
for ( nShip = 0; nShip < nShips; ++nShip )
{
Spaceship * spaceship = spaceships[ nShip ];
spaceship->setPos( spaceship->x() + distance, yPos );
}

if ( bChangedDirection )
emit endOfLine( yPos );
}

Sean11
23rd September 2017, 10:17
the levels it is type of difficulty i want to make class called Levels(int cLvl)
in the Levels method there is:
if(cLvl == 1)
Level1(); // opens level 1 method
in the level1() method there is for loop that spawn and place all the spaceship for this level