产生C ++的敌人波浪

时间:2015-10-29 13:09:09

标签: c++ qt

我正在使用qt 5.0.1创建一个简单的游戏。这就像Warblade。 我有制造敌人波浪的问题。

int k;
int pos = 100;
for (k = 0; k < 5; k++)
{
    pos = 100;
    for (int i = 0; i < 9; i++)
    {
        player->spawn_in_pos(pos);
        pos += 100;
    }
    //QThread::sleep(2);
}

当我使用sleep()函数时,我的游戏无法运行。它正在等待循环结束然后显示。

我也在处理第二种选择:

QTimer * timer = new QTimer();
QObject::connect( timer, SIGNAL(timeout()), player, SLOT(spawn_in_pos(pos)) );
timer->start(450);

但看起来SLOT无法获得这个位置。

编辑: 我只是做了@ddriver所说的,这对我帮助很大。 现在我得到了一些'迟滞'式的敌人运动。

enter image description here

EDIT2:

我正在这样移动我的敌人:

setPos(x(),y()+1);

用那个计时器:

// connect
QTimer * timer = new QTimer(this);
connect(timer,SIGNAL(timeout()),this,SLOT(move()));
// start the timer
timer->start(10);

它看起来非常平滑,但可能会降低+1像素,而10个计时器会减少:((

3 个答案:

答案 0 :(得分:1)

我不确定你想要达到的目标,但是在你的第二个选项中,你无法获得该位置,因为超时不会发送它。 信号是超时(无效),您的插槽需要一个参数。我猜你对信号/插槽机制缺乏一些基本的了解。 QT文档非常简洁: http://doc.qt.io/qt-5/signalsandslots.html

如果你只想创造一个无中生有的游戏,你可以在这里找到一个小教程,如何在QT中编写游戏: https://www.youtube.com/watch?v=8ntEQpg7gck

答案 1 :(得分:0)

调用sleep将阻止线程处理任何事情,这不是你想要做的事情。

Using C++ 11,您可以将QTimer与lambda函数一起使用,如下所示: -

int pos = 100;
int nextWaveTime = 2000; // 2 seconds per wave
for (k = 0; k < 5; k++) // 5 waves of enemies
{       
    for (int i = 0; i < 9; i++) // 9 enemies per wave
    {
        QTimer * timer = new QTimer(); 
        timer->setSingleShot(true);

        pos = pos + (100*i); // set the pos, which is captured by value, in the lambda function

        QObject::connect( timer, QTimer::timeout, [=](){
           player->spawn_in_pos(pos); 
           timer->deleteLater(); // must cleanup the timer
        });

        timer->start(450 + (k*nextWaveTime));
     } 
 }

答案 2 :(得分:0)

为了传递Qt中信号和槽的参数,信号参数必须与槽的参数(或Qt 5以来的功能)相匹配。

解决问题的一种方法是在TheDarkKnight的答案中使用lambda。

我建议使用封装 - 您可以创建一个Spawner对象,专门用于产生敌人并保持其内部位置。这样生成器将管理位置,并且您可以使用Spawner::createWave()槽而不带参数,因为位置是内部的。然后设置计时器并将其连接到createWave()并进行设置。

对这样的东西进行硬编码也是一个非常糟糕的主意,你真的需要更多的灵活性,更改敌人和波浪计数的选项,波浪时间以及屏幕宽度,这样你的游戏就可以改变这些东西它变得更难。

class Spawner : public QObject {
    Q_OBJECT
public:
    Spawner(int wCount = 5, int eCount = 9, int time = 2000, int sWidth = 1000)
        : waveCount(wCount), enemyCount(eCount), currentWave(0), screenWidth(sWidth) {
        timer.setInterval(time);
        connect(&timer, SIGNAL(timeout()), this, SLOT(createWave()));
    }
    void set(int wCount, int eCount, int time) {
        timer.setInterval(time);
        waveCount = wCount;
        enemyCount = eCount;
    }
    void changeWidth(int w) { screenWidth = w; }
public slots:
    void start() { timer.start(); }
    void stop() {
        timer.stop();
        currentWave = 0;
    }    
private slots:
    void createWave() {
        int pos = screenWidth / (enemyCount + 1);
        int step = pos;
        for (int i = 0; i < enemyCount; ++i) {
            Game::spawnEnemyAt(pos);
            pos += step;
        }
        if (++currentWave >= waveCount) stop();
    }
private:
    QTimer timer;
    int waveCount, enemyCount, currentWave, screenWidth;
};

创建一个Spawner对象并将游戏新关卡连接到start() - 它将在游戏画面中均匀地跨越给定数量的敌人波,当你完成关闭波浪时,你调整生成器设置并开始一个新的水平。

随着游戏变得不那么简单,更像是真正的游戏 - 随着难度的增加,更改产卵和攻击模式等等,这种封装将会在以后派上用场。因此,从一开始就实施它是一个好主意,并建立一个良好而灵活的设计,而不是回头和改变周围的东西,这可能会打破其他代码。你真的不想在没有好的设计的情况下开始,并在以后进行设计更改。因此需要封装功能和责任,只需连接各个部分而不是构建在一堆意大利面条代码上。在这一思路中,我注意到你正在使用player->spawn_in_pos(pos); - 这是一个糟糕设计的例子,因为产生应该是Game类的责任,而不是Player类。一个好的设计不仅灵活,而且干净。 Spawner对象仅负责产生敌人的波浪,其可见界面仅限于start()stop()set()

编辑:

class Game : public QObject {
    Q_OBJECT
public:
    Game() {
        if (!scene) scene = new QGraphicsScene(this);
        connect(this, SIGNAL(newLevel()), &spawner, SLOT(start()));
    }
    static void spawnEnemyAt(int x = 0) {
        scene->addItem(new Enemy(x, 0));
        qDebug() << "enemy created";
    }
public slots:
    void newGame() {
        // initialize game
        emit newLevel(); // begin spawning
    }
    void onLevelEnd() {
        // spawner.set(new level settings); 
        emit newLevel();
    }
    void onGameEnded() {
        // ...
    }
signals:
    void newLevel();
private:
    Spawner spawner;
    static QGraphicsScene * scene;
};

// in game.cpp
QGraphicsScene * Game::scene = nullptr;

如果您不想使用静态成员,可以创建spawnEnemyAt()scene实例成员,但是您必须将Game实例传递给{{1在构造函数中,您可以引用spawner操作的游戏并使用Spawner代替。这样您就可以使用自己的专用场景创建多个游戏。或者将产生者生成游戏,并将产卵者的game->spawnEnemyAt()投射到parent()以访问游戏实例,这有点黑客,但通过重用父母来节省额外成员。