Example of Snake game from NoobTuts tutorial rewritten in Qt C++ OpenGL
-
wrote on 23 Nov 2020, 20:11 last edited by 8Observer8 3 May 2021, 15:57
Example of Snake game from NoobTuts tutorial (Python Snake Game) rewritten in Qt C++ OpenGL
Demo for Windows: Snake2DNoobTuts_OpenGLES20_Qt5Cpp (11 MB)
There are two versions of sources:
- OpenGL 3.3 for Desktop: https://rextester.com/MMC15477
- OpenGL ES 2.0 for Desktop, Android, and iOS: https://rextester.com/YHXB28198
main.cpp (OpenGL 3.3)
// Add this line to .pro: // win32: LIBS += -lopengl32 #ifdef _WIN32 #include <windows.h> extern "C" __declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001; extern "C" __declspec(dllexport) DWORD AmdPowerXpressRequestHighPerformance = 0x00000001; #endif #include <QtWidgets/QApplication> #include <QtWidgets/QWidget> #include <QtWidgets/QOpenGLWidget> #include <QtWidgets/QVBoxLayout> #include <QtWidgets/QHBoxLayout> #include <QtWidgets/QLabel> #include <QtGui/QOpenGLShaderProgram> #include <QtGui/QOpenGLBuffer> #include <QtGui/QMatrix4x4> #include <QtGui/QKeyEvent> #include <QtCore/QList> #include <QtCore/QMutableListIterator> #include <QtCore/QTimer> #include <QtCore/QRandomGenerator> class OpenGLWidget : public QOpenGLWidget { Q_OBJECT public: OpenGLWidget(QWidget *parent = nullptr) : QOpenGLWidget(parent) { setFocusPolicy(Qt::StrongFocus); } signals: void updateScore(QString score); void updateLives(QString lives); private slots: void onUpdate() { // Move snake // Insert new position in the beginning of the snake list m_snake.insert(0, m_snake[0] + m_snakeDir); m_snake.removeLast(); // Collision with itself int hx = m_snake[0].x(); int hy = m_snake[0].y(); for (int i = 0; i < m_snake.length(); i++) { if (i == 0) continue; if (hx == m_snake[i].x() && hy == m_snake[i].y()) { m_food.clear(); m_snake.clear(); m_snake.append(m_startPos); m_snakeDir = m_startDir; emit updateLives("Lives: " + QString::number(--m_lives)); update(); return; } } // Spawn food // Spawn food with 5% chance int r = QRandomGenerator::global()->bounded(0, 20); if (r == 0) { int x = QRandomGenerator::global()->bounded(0, m_fieldWidth); int y = QRandomGenerator::global()->bounded(0, m_fieldHeight); m_food.append(QVector2D(x, y)); } // Let the snake eat the food // Get the snake's head x and y position QMutableListIterator<QVector2D> i(m_food); while (i.hasNext()) { QVector2D f = i.next(); if (hx == f.x() && hy == f.y()) { // Is the head where the food is? m_snake.append(QVector2D(f.x(), f.y())); // Make the snake longer i.remove(); // Remove the food m_score += 10; emit updateScore("Score: " + QString::number(m_score)); } } // Collisions with borders if (hx < 0 || m_fieldWidth <= hx || hy < 0 || m_fieldHeight <= hy) { m_lives--; m_food.clear(); m_snake.clear(); m_snake.append(m_startPos); m_snakeDir = m_startDir; if (m_lives == 0) { m_lives = 3; m_score = 0; emit updateScore("Score: " + QString::number(m_score)); } emit updateLives("Lives: " + QString::number(m_lives)); } update(); } private: QOpenGLShaderProgram m_program; QOpenGLBuffer m_vertPosBuffer; float m_fieldWidth = 20.f; // Internal resolution float m_fieldHeight = 20.f; // Internal resolution QMatrix4x4 m_projMatrix; QMatrix4x4 m_modelMatrix; QList<QVector2D> m_snake; // Snake list of (x, y) positions QList<QVector2D> m_food; QVector2D m_startPos = QVector2D(5.f, 10.f); QVector2D m_startDir = QVector2D(1, 0); QVector2D m_snakeDir = m_startDir; // Snake movement direction QTimer m_timer; int m_score = 0; int m_lives = 3; void initializeGL() override { // qDebug() << QString("w = %1, h = %2").arg(width()).arg(height()); glClearColor(0.f, 0.f, 0.f, 1.f); glEnable(GL_DEPTH_TEST); const char *vertShaderSrc = "#version 330 core\n" "in vec3 aPosition;" "uniform mat4 uMvpMatrix;" "void main()" "{" " gl_Position = uMvpMatrix * vec4(aPosition, 1.0);" "}"; const char *fragShaderSrc = "#version 330 core\n" "uniform vec4 uColor;" "out vec4 fragColor;" "void main()" "{" " fragColor = uColor;" "}"; m_program.addShaderFromSourceCode(QOpenGLShader::Vertex, vertShaderSrc); m_program.addShaderFromSourceCode(QOpenGLShader::Fragment, fragShaderSrc); m_program.link(); m_program.bind(); float vertPositions[] = { 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 1.f, 0.f, 1.f, 1.f, 0.f }; m_vertPosBuffer.create(); m_vertPosBuffer.bind(); m_vertPosBuffer.allocate(vertPositions, sizeof(vertPositions)); m_program.bindAttributeLocation("aPosition", 0); m_program.setAttributeBuffer(0, GL_FLOAT, 0, 3); m_program.enableAttributeArray(0); m_projMatrix.ortho(0.f, m_fieldWidth, 0.f, m_fieldHeight, -100.f, 100.f); m_snake.append(m_startPos); emit updateScore("Score: " + QString::number(m_score)); emit updateLives("Lives: " + QString::number(m_lives)); connect(&m_timer, &QTimer::timeout, this, &OpenGLWidget::onUpdate); m_timer.start(200); } void paintGL() override { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); drawFood(); drawSnake(); } void resizeGL(int w, int h) override { glViewport(0, 0, w, h); } void keyPressEvent(QKeyEvent *e) override { if (e->key() == Qt::Key_W || e->key() == Qt::Key_Up) if (m_snakeDir != QVector2D(0, -1)) m_snakeDir = QVector2D(0, 1); if (e->key() == Qt::Key_S || e->key() == Qt::Key_Down) if (m_snakeDir != QVector2D(0, 1)) m_snakeDir = QVector2D(0, -1); if (e->key() == Qt::Key_A || e->key() == Qt::Key_Left) if (m_snakeDir != QVector2D(1, 0)) m_snakeDir = QVector2D(-1, 0); if (e->key() == Qt::Key_D || e->key() == Qt::Key_Right) if (m_snakeDir != QVector2D(-1, 0)) m_snakeDir = QVector2D(1, 0); } void drawRect(float x, float y, float width, float height, QColor color) { m_modelMatrix.setToIdentity(); m_modelMatrix.translate(QVector3D(x, y, 0.f)); m_modelMatrix.scale(width, height, 1.f); m_program.bind(); m_program.setUniformValue("uMvpMatrix", m_projMatrix * m_modelMatrix); m_program.setUniformValue("uColor", color); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); } void drawSnake() { foreach (const QVector2D &cell, m_snake) { drawRect(cell.x(), cell.y(), 1, 1, QColor(255, 255, 255, 255)); } } void drawFood() { foreach (const QVector2D &f, m_food) { drawRect(f.x(), f.y(), 1, 1, QColor(0, 0, 255, 255)); } } }; class Window : public QWidget { public: Window(QWidget *parent = nullptr) : QWidget(parent) { setWindowTitle("C++ OpenGL"); setFixedSize(239, 268); QFont font = QFont("Areal", 14); m_labelScore.setFont(font); m_labelScore.setText(""); m_labelScore.setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); m_labelLives.setFont(font); m_labelLives.setText(""); m_labelLives.setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); QHBoxLayout *hboxOutput = new QHBoxLayout(); hboxOutput->addWidget(&m_labelScore); hboxOutput->addWidget(&m_labelLives); QHBoxLayout *hbox = new QHBoxLayout(); hbox->addWidget(&m_openGLWidget); QVBoxLayout *vbox = new QVBoxLayout(this); vbox->addLayout(hboxOutput); vbox->addLayout(hbox); connect(&m_openGLWidget, &OpenGLWidget::updateScore, [this](const QString &s){ m_labelScore.setText(s); }); connect(&m_openGLWidget, &OpenGLWidget::updateLives, [this](const QString &s){ m_labelLives.setText(s); }); } private: OpenGLWidget m_openGLWidget; QLabel m_labelScore; QLabel m_labelLives; }; #include "main.moc" int main(int argc, char *argv[]) { QApplication a(argc, argv); Window w; w.show(); return a.exec(); }
main.cpp (OpenGL ES 2.0)
// Add this line to .pro: // win32: LIBS += -lopengl32 #ifdef _WIN32 #include <windows.h> extern "C" __declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001; extern "C" __declspec(dllexport) DWORD AmdPowerXpressRequestHighPerformance = 0x00000001; #endif #include <QtWidgets/QApplication> #include <QtWidgets/QWidget> #include <QtWidgets/QOpenGLWidget> #include <QtWidgets/QVBoxLayout> #include <QtWidgets/QHBoxLayout> #include <QtWidgets/QLabel> #include <QtGui/QOpenGLShaderProgram> #include <QtGui/QOpenGLBuffer> #include <QtGui/QMatrix4x4> #include <QtGui/QKeyEvent> #include <QtCore/QList> #include <QtCore/QMutableListIterator> #include <QtCore/QTimer> #include <QtCore/QRandomGenerator> class OpenGLWidget : public QOpenGLWidget { Q_OBJECT public: OpenGLWidget(QWidget *parent = nullptr) : QOpenGLWidget(parent) { setFocusPolicy(Qt::StrongFocus); } signals: void updateScore(QString score); void updateLives(QString lives); private slots: void onUpdate() { // Move snake // Insert new position in the beginning of the snake list m_snake.insert(0, m_snake[0] + m_snakeDir); m_snake.removeLast(); // Collision with itself int hx = m_snake[0].x(); int hy = m_snake[0].y(); for (int i = 0; i < m_snake.length(); i++) { if (i == 0) continue; if (hx == m_snake[i].x() && hy == m_snake[i].y()) { m_food.clear(); m_snake.clear(); m_snake.append(m_startPos); m_snakeDir = m_startDir; emit updateLives("Lives: " + QString::number(--m_lives)); update(); return; } } // Spawn food // Spawn food with 5% chance int r = QRandomGenerator::global()->bounded(0, 20); if (r == 0) { int x = QRandomGenerator::global()->bounded(0, m_fieldWidth); int y = QRandomGenerator::global()->bounded(0, m_fieldHeight); m_food.append(QVector2D(x, y)); } // Let the snake eat the food // Get the snake's head x and y position QMutableListIterator<QVector2D> i(m_food); while (i.hasNext()) { QVector2D f = i.next(); if (hx == f.x() && hy == f.y()) { // Is the head where the food is? m_snake.append(QVector2D(f.x(), f.y())); // Make the snake longer i.remove(); // Remove the food m_score += 10; emit updateScore("Score: " + QString::number(m_score)); } } // Collisions with borders if (hx < 0 || m_fieldWidth <= hx || hy < 0 || m_fieldHeight <= hy) { m_lives--; m_food.clear(); m_snake.clear(); m_snake.append(m_startPos); m_snakeDir = m_startDir; if (m_lives == 0) { m_lives = 3; m_score = 0; emit updateScore("Score: " + QString::number(m_score)); } emit updateLives("Lives: " + QString::number(m_lives)); } update(); } private: QOpenGLShaderProgram m_program; QOpenGLBuffer m_vertPosBuffer; float m_fieldWidth = 20.f; // Internal resolution float m_fieldHeight = 20.f; // Internal resolution QMatrix4x4 m_projMatrix; QMatrix4x4 m_modelMatrix; QList<QVector2D> m_snake; // Snake list of (x, y) positions QList<QVector2D> m_food; QVector2D m_startPos = QVector2D(5.f, 10.f); QVector2D m_startDir = QVector2D(1, 0); QVector2D m_snakeDir = m_startDir; // Snake movement direction QTimer m_timer; int m_score = 0; int m_lives = 3; void initializeGL() override { // qDebug() << QString("w = %1, h = %2").arg(width()).arg(height()); glClearColor(0.f, 0.f, 0.f, 1.f); glEnable(GL_DEPTH_TEST); const char *vertShaderSrc = "attribute vec3 aPosition;" "uniform mat4 uMvpMatrix;" "void main()" "{" " gl_Position = uMvpMatrix * vec4(aPosition, 1.0);" "}"; const char *fragShaderSrc = "uniform vec4 uColor;" "void main()" "{" " gl_FragColor = uColor;" "}"; m_program.addShaderFromSourceCode(QOpenGLShader::Vertex, vertShaderSrc); m_program.addShaderFromSourceCode(QOpenGLShader::Fragment, fragShaderSrc); m_program.link(); m_program.bind(); float vertPositions[] = { 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 1.f, 0.f, 1.f, 1.f, 0.f }; m_vertPosBuffer.create(); m_vertPosBuffer.bind(); m_vertPosBuffer.allocate(vertPositions, sizeof(vertPositions)); m_program.bindAttributeLocation("aPosition", 0); m_program.setAttributeBuffer(0, GL_FLOAT, 0, 3); m_program.enableAttributeArray(0); m_projMatrix.ortho(0.f, m_fieldWidth, 0.f, m_fieldHeight, -100.f, 100.f); m_snake.append(m_startPos); emit updateScore("Score: " + QString::number(m_score)); emit updateLives("Lives: " + QString::number(m_lives)); connect(&m_timer, &QTimer::timeout, this, &OpenGLWidget::onUpdate); m_timer.start(200); } void paintGL() override { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); drawFood(); drawSnake(); } void resizeGL(int w, int h) override { glViewport(0, 0, w, h); } void keyPressEvent(QKeyEvent *e) override { if (e->key() == Qt::Key_W || e->key() == Qt::Key_Up) if (m_snakeDir != QVector2D(0, -1)) m_snakeDir = QVector2D(0, 1); if (e->key() == Qt::Key_S || e->key() == Qt::Key_Down) if (m_snakeDir != QVector2D(0, 1)) m_snakeDir = QVector2D(0, -1); if (e->key() == Qt::Key_A || e->key() == Qt::Key_Left) if (m_snakeDir != QVector2D(1, 0)) m_snakeDir = QVector2D(-1, 0); if (e->key() == Qt::Key_D || e->key() == Qt::Key_Right) if (m_snakeDir != QVector2D(-1, 0)) m_snakeDir = QVector2D(1, 0); } void drawRect(float x, float y, float width, float height, QColor color) { m_modelMatrix.setToIdentity(); m_modelMatrix.translate(QVector3D(x, y, 0.f)); m_modelMatrix.scale(width, height, 1.f); m_program.bind(); m_program.setUniformValue("uMvpMatrix", m_projMatrix * m_modelMatrix); m_program.setUniformValue("uColor", color); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); } void drawSnake() { foreach (const QVector2D &cell, m_snake) { drawRect(cell.x(), cell.y(), 1, 1, QColor(255, 255, 255, 255)); } } void drawFood() { foreach (const QVector2D &f, m_food) { drawRect(f.x(), f.y(), 1, 1, QColor(0, 0, 255, 255)); } } }; class Window : public QWidget { public: Window(QWidget *parent = nullptr) : QWidget(parent) { setWindowTitle("C++ OpenGL"); setFixedSize(239, 268); QFont font = QFont("Areal", 14); m_labelScore.setFont(font); m_labelScore.setText(""); m_labelScore.setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); m_labelLives.setFont(font); m_labelLives.setText(""); m_labelLives.setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); QHBoxLayout *hboxOutput = new QHBoxLayout(); hboxOutput->addWidget(&m_labelScore); hboxOutput->addWidget(&m_labelLives); QHBoxLayout *hbox = new QHBoxLayout(); hbox->addWidget(&m_openGLWidget); QVBoxLayout *vbox = new QVBoxLayout(this); vbox->addLayout(hboxOutput); vbox->addLayout(hbox); connect(&m_openGLWidget, &OpenGLWidget::updateScore, [this](const QString &s){ m_labelScore.setText(s); }); connect(&m_openGLWidget, &OpenGLWidget::updateLives, [this](const QString &s){ m_labelLives.setText(s); }); } private: OpenGLWidget m_openGLWidget; QLabel m_labelScore; QLabel m_labelLives; }; #include "main.moc" int main(int argc, char *argv[]) { QApplication a(argc, argv); Window w; w.show(); return a.exec(); }
-
This post is deleted!