我正在使用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所说的,这对我帮助很大。 现在我得到了一些'迟滞'式的敌人运动。
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个计时器会减少:((
答案 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()
以访问游戏实例,这有点黑客,但通过重用父母来节省额外成员。