I am making a little game, and I want my enemy in the game to move and spawn bullet automatically at the same time.
So far, my enemy can move and spawn bullet automatically, but the problem is that
my bullet cannot follow the enemy's movement and spawn at the different start point of the enemy.
To be more specific,
I want to catch the very position of the enemy every time the enemy spawn bullet. However, I cannot setPos my bullet dynamically.
Now, I design enemy-moving function(enemyMove()) and spawning-bullet function(MakeBall()) separately. And I set a different timer for each function in my mainwindow constructor in order to evoke the function automatically every time window.
My attempts and thoughts so far:
Any sharing or advice will be beyond thankful. Thank you very very much.
main window.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent):
QMainWindow(parent),
ui(new Ui::MainWindow),
enemyMoveTimer(new QTimer),
balldrop(new QTimer),
makeballtimer(new QTimer)
{
ui->setupUi(this);
//scene
scene = new QGraphicsScene(0, 0, 1050, 600);
//view
view = new QGraphicsView;
view ->setScene(scene);
setCentralWidget(view);
//enemy
Enemy * enemy = new Enemy();
enemy->setPixmap(QPixmap(QPixmap(":/img/ghost.gif").scaled(100,100)));
enemy->setPos(0,0);
scene->addItem(static_cast<QGraphicsPixmapItem*>(enemy));
enemyMoveTimer->start(100);
enemy->connect(enemyMoveTimer, SIGNAL(timeout()), enemy, SLOT(enemyMove()));
//enemy's bullet
makeballtimer->start(1000);
//int & x=enemy->x();
//connect(makeballtimer, SIGNAL(timeout()), this, SLOT(MakeBall(int & x)));
connect(makeballtimer, SIGNAL(timeout()), this, SLOT(MakeBall(QGraphicsPixmapItem * enemy)));
}
void MainWindow::MakeBall(QGraphicsPixmapItem * enemy)
{
EnemyBullet * ball = new EnemyBullet();
ball->setPixmap(QPixmap(":/img/yellowblankball.png").scaled(50, 30));
ball->setPos(enemy->x()+50, 100);//the problem is here
balldrop->start(100);
ball->connect(balldrop, SIGNAL(timeout()), ball, SLOT(fall()));
scene->addItem(static_cast<QGraphicsPixmapItem*>(ball));
}
MainWindow::~MainWindow()
{
delete ui;
}
main window.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsPixmapItem>
#include <QTimer>
#include <QKeyEvent>
#include <QtGui>
#include "enemy.h"
#include "ball.h"
#include "enemybullet.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
public slots:
//virtual void MakeBall(int &x);
virtual void MakeBall( QGraphicsPixmapItem * enemy);
//virtual void MakeBall();
private:
Ui::MainWindow *ui;
QGraphicsScene *scene;
QTimer *enemyMoveTimer;
QTimer *balldrop;
QTimer *makeballtimer;
QGraphicsItem *enemy;
QGraphicsView * view;
};
#endif // MAINWINDOW_H
enemybullet.cpp
#include "enemybullet.h"
#include <QTimer>
#include <QGraphicsScene>
#include <QList>
EnemyBullet::EnemyBullet()
{
}
void EnemyBullet::fall()
{
setPos(x(), y() +10 );
}
enemybullet.h
#ifndef ENEMYBULLET_H
#define ENEMYBULLET_H
#include <QObject>
#include <QGraphicsPixmapItem>
#include <QGraphicsScene>
class EnemyBullet: public QObject, public QGraphicsPixmapItem
{
Q_OBJECT
public:
EnemyBullet();
public slots:
void fall();
};
#endif// ENEMYBULLET_H
enemy.cpp (trimmed to the essential. it can actually move in the straight line left to right and right to left repeatedly. So we cannot count the specific time to spawn bullet, because every position will be revisited again and again. )
#include "enemy.h"
#include "enemybullet.h"
#include <QDebug>
Enemy::Enemy()
{
}
void Enemy::enemyMove()
{
setPos(x() + 10,y());
}
enemy.h
#ifndef ENEMY_H
#define ENEMY_H
#include <QObject>
#include <QGraphicsPixmapItem>
#include <QTimer>
class Enemy: public QObject,public QGraphicsPixmapItem
{
Q_OBJECT
public:
Enemy();
public slots:
void enemyMove();
};
#endif // ENEMY_H
Generally you want to avoid mixing UI code with data structures, it makes it much easier to scale so in your case I would really suggest creating backing data classes that will hold just data and then UI.
Your problem is however that you are mixing signal and slot - you are connecting QTimer::timeout() signal to your slot that takes parameter of type QGraphicsPixmapItem *
. While this compiles I think at least in Debug mode you will get message saying something like signal and slots are not compatible. It's much better to use modern connect style like this:
connect(makeballtimer, &QTimer::timeout, this, &MainWindow::MakeBall);
Since this notation will fail already in compile time.
My suggestion to you will be to do following: create model classes (or structures, it's really up to you) which will hold just the data associated with each element; for example:
struct Enemy {
QPoint position;
};
struct BulletModel {
std::shared_ptr<Enemy> enemy;
QPoint position;
};
Your list of enemies will then be stored as vector of std::shared_ptr
to make sure their lifetimes are managed:
std::vector<std::shared_ptr<Enemy>> m_enemies;
Then you can create enemy and bullet:
auto enemy = std::make_shared<EnemyModel>();
auto bullet = std::make_shared<BulletModel>();
bullet->enemy = enemy;
Then you can create widgets that will be using the models to draw the elements into their positions.