├── .gitignore ├── AppConfig.cpp ├── AppConfig.h ├── Ball.cpp ├── Ball.h ├── Barrier.cpp ├── Barrier.h ├── Brick.cpp ├── Brick.h ├── CaptureDevice.cpp ├── CaptureDevice.h ├── CaptureWindow.cpp ├── CaptureWindow.h ├── CaptureWindow.ui ├── ContactListener.cpp ├── ContactListener.h ├── GameField.cpp ├── GameField.h ├── GameOverPoster.cpp ├── GameOverPoster.h ├── GameOverPoster.ui ├── GameWindow.cpp ├── GameWindow.h ├── GameWindow.ui ├── GraphicsPhysicsItem.cpp ├── GraphicsPhysicsItem.h ├── MainWindow.cpp ├── MainWindow.h ├── MainWindow.ui ├── MathAn.cpp ├── MathAn.h ├── MoveDetector.cpp ├── MoveDetector.h ├── MoveNoid.pro ├── MoveNoid.pro.user ├── ReadyPoster.cpp ├── ReadyPoster.h ├── ReadyPoster.ui ├── RocketBit.cpp ├── RocketBit.h ├── SettingsWindow.cpp ├── SettingsWindow.h ├── SettingsWindow.ui ├── bin └── pic │ └── GameView.png ├── license ├── licenseBSD.txt └── licenseGPL.txt ├── main.cpp ├── main.h ├── misc ├── pic │ ├── GameView01.png │ ├── GameView01.svg │ ├── Marker01.png │ └── Marker01.svg └── release │ └── 01 │ └── sample.zip └── resource.qrc /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore IDE files 2 | *.Debug 3 | *.Release 4 | 5 | *.pro.user -------------------------------------------------------------------------------- /AppConfig.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "main.h" 5 | #include "AppConfig.h" 6 | 7 | 8 | AppConfig::AppConfig(QObject *pobj) 9 | { 10 | Q_UNUSED(pobj); 11 | 12 | // Создается имя файла конфигурации 13 | QString configFileName="./config.ini"; 14 | 15 | // Проверяется, есть ли файл конфигурации 16 | QFile configFile(configFileName); 17 | if(!configFile.open(QIODevice::ReadWrite)) 18 | criticalError("Can't using file "+configFileName); 19 | configFile.close(); 20 | 21 | // Создается указатель на объект хранилища конфигурации 22 | conf=new QSettings(configFileName, QSettings::IniFormat); 23 | conf->sync(); 24 | } 25 | 26 | 27 | AppConfig::~AppConfig() 28 | { 29 | qDebug() << "Save config file before exit"; 30 | conf->sync(); 31 | delete conf; 32 | } 33 | 34 | 35 | // Получение параметра по имени в виде строки с проверкой его существования 36 | QString AppConfig::getParameter(const QString name) const 37 | { 38 | // Если в конфиге есть запрашиваемый параметр 39 | if(conf->contains(name)) 40 | return conf->value(name).toString(); 41 | else { // Иначе в файле конфигурации небыло нужного параметра 42 | QString paramName; 43 | QString paramValue; 44 | 45 | if( name=="captureDeviceFileName" ) { 46 | paramName=name; 47 | paramValue="default"; 48 | } 49 | if( name=="brigthnessThreshold" ) { 50 | paramName=name; 51 | paramValue="50"; 52 | } 53 | 54 | // Если запрашиваемый параметр имеет допустимое имя, его надо проинициализировать в файле конфигурации 55 | if(paramName.length()>0) { 56 | conf->setValue(paramName, paramValue); 57 | conf->sync(); 58 | 59 | return paramValue; 60 | } else { 61 | criticalError("Get unknown parameter: "+name); 62 | } 63 | 64 | } 65 | 66 | return QString(); 67 | } 68 | 69 | 70 | void AppConfig::setParameter(const QString name, const QString value) const 71 | { 72 | conf->setValue(name, value); 73 | conf->sync(); 74 | } 75 | 76 | 77 | -------------------------------------------------------------------------------- /AppConfig.h: -------------------------------------------------------------------------------- 1 | #ifndef __APPCONFIG_H__ 2 | #define __APPCONFIG_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | 9 | // Конфигурация программы (переделать на синглтон) 10 | class AppConfig : public QObject 11 | { 12 | Q_OBJECT 13 | 14 | public: 15 | AppConfig(QObject *pobj=0); 16 | ~AppConfig(); 17 | 18 | QString getParameter(const QString name) const; 19 | void setParameter(const QString name, const QString value) const; 20 | 21 | protected: 22 | QSettings *conf; 23 | 24 | }; 25 | 26 | #endif // __APPCONFIG_H__ 27 | -------------------------------------------------------------------------------- /Ball.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "main.h" 4 | #include "Ball.h" 5 | 6 | Ball::Ball(QGraphicsItem *parent) 7 | { 8 | QGraphicsItem::setParentItem(parent); 9 | 10 | radius=0.0; 11 | } 12 | 13 | 14 | void Ball::setRadius(const qreal iRadius) 15 | { 16 | radius=iRadius; 17 | } 18 | 19 | 20 | void Ball::putToPhysicsWorld() 21 | { 22 | // Общие параметры тела 23 | b2BodyDef bodyDef; 24 | bodyDef.type=b2_dynamicBody; 25 | bodyDef.position.Set(this->x(), this->y()); 26 | 27 | // Создается тело в физическом движке 28 | b2Body *body=physicsWorld->CreateBody(&bodyDef); 29 | 30 | // Создается форма тела 31 | b2CircleShape circleShape; 32 | circleShape.m_radius=radius; 33 | 34 | // Настройка физических параметров тела 35 | b2FixtureDef fixture; 36 | fixture.shape = &circleShape; // Форма 37 | fixture.density = 1.0; // Плотность (через нее и форму движек узнает массу тела) 38 | fixture.friction = 0.0; // Коэффициент трения ( 0.0 - нет трения, 1.0 - максимальное трение. Трение расчитывается "среднее" для двух контачащих тел) 39 | fixture.restitution = 1.0; // Коэффициент упругости (0.0 - нет отскока, 1.0 - максимальный отскок) 40 | body->CreateFixture(&fixture); 41 | 42 | b2Vec2 velocityVector(MOVE_NOID_START_BALL_VELOCITY_X, MOVE_NOID_START_BALL_VELOCITY_Y); 43 | body->SetLinearVelocity(velocityVector); 44 | 45 | qDebug() << "Ball mass: " << body->GetMass(); 46 | 47 | // Запоминается настроенное тело 48 | physicsBody=body; 49 | } 50 | 51 | 52 | void Ball::updatePosByPhysicsWorld() 53 | { 54 | this->setX( physicsBody->GetPosition().x ); 55 | this->setY( physicsBody->GetPosition().y ); 56 | 57 | // qDebug() << "Ball coordinats: " << this->x() << this->y(); 58 | } 59 | 60 | 61 | void Ball::moveToDefaultPos() 62 | { 63 | // Если мячик уже есть, его надо удалить перед перемещением 64 | if(physicsBody!=nullptr) { 65 | physicsWorld->DestroyBody(physicsBody); 66 | } 67 | 68 | this->setX(MOVE_NOID_START_BALL_POS_X); 69 | this->setY(MOVE_NOID_START_BALL_POS_Y); 70 | 71 | putToPhysicsWorld(); 72 | } 73 | 74 | 75 | b2ContactEdge *Ball::getContactList() const 76 | { 77 | return physicsBody->GetContactList(); 78 | } 79 | 80 | 81 | QRectF Ball::boundingRect() const 82 | { 83 | return QRectF(-radius, -radius, radius*2.0, radius*2.0); 84 | } 85 | 86 | 87 | QPainterPath Ball::shape() const 88 | { 89 | QPainterPath path; 90 | path.addEllipse(-radius, -radius, radius*2.0, radius*2.0); 91 | return path; 92 | } 93 | 94 | 95 | void Ball::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) 96 | { 97 | // Filling 98 | painter->setBrush(QColor(199, 104, 2)); 99 | 100 | // Edges 101 | QPen pen; 102 | pen.setWidth(0.1); 103 | pen.setBrush(Qt::white); 104 | painter->setPen(pen); 105 | 106 | QRectF rectangle(-radius, -radius, radius*2.0, radius*2.0); 107 | painter->drawEllipse(rectangle); 108 | } 109 | -------------------------------------------------------------------------------- /Ball.h: -------------------------------------------------------------------------------- 1 | #ifndef BALL_H 2 | #define BALL_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "GraphicsPhysicsItem.h" 9 | 10 | class Ball : public GraphicsPhysicsItem 11 | { 12 | public: 13 | Ball(QGraphicsItem *parent = Q_NULLPTR); 14 | 15 | QRectF boundingRect() const override; 16 | QPainterPath shape() const override; 17 | void paint(QPainter *painter, 18 | const QStyleOptionGraphicsItem *option, 19 | QWidget *widget) override; 20 | 21 | void setRadius(const qreal iRadius); 22 | void updatePosByPhysicsWorld(); 23 | void moveToDefaultPos(); 24 | void putToPhysicsWorld(); 25 | 26 | b2ContactEdge* getContactList() const; // Точки столкновения мячика и других тел 27 | 28 | protected: 29 | 30 | qreal radius; 31 | }; 32 | 33 | #endif // BALL_H 34 | -------------------------------------------------------------------------------- /Barrier.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "Barrier.h" 3 | #include "main.h" 4 | 5 | Barrier::Barrier(QGraphicsItem *parent) 6 | { 7 | QGraphicsItem::setParentItem(parent); 8 | 9 | polygon.clear(); 10 | } 11 | 12 | 13 | void Barrier::setPolygon(const QPolygonF &iPolygon) 14 | { 15 | polygon=iPolygon; 16 | } 17 | 18 | 19 | void Barrier::putToPhysicsWorld() 20 | { 21 | b2BodyDef bodyDef; 22 | bodyDef.type=b2_staticBody; 23 | bodyDef.position.Set(0.0, 0.0); 24 | b2Body *body=physicsWorld->CreateBody(&bodyDef); 25 | 26 | QPolygonF scenePolygon=mapToScene(polygon); 27 | 28 | b2Vec2 vertices[10]; 29 | for(int i=0; i=0; i--) 30 | vertices[i].Set(scenePolygon.at(i).x(), scenePolygon.at(i).y()); 31 | 32 | qDebug() << "Item coordinats:" << polygon.at(i).x() << polygon.at(i).y() << "Scene coordinats:" << scenePolygon.at(i).x() << scenePolygon.at(i).y() ; 33 | } 34 | b2PolygonShape polygonShape; 35 | polygonShape.Set(vertices, scenePolygon.size()); 36 | 37 | // body->CreateShape(&polygonShape); 38 | body->CreateFixture(&polygonShape, 0.0); 39 | 40 | // Запоминается настроенное тело 41 | physicsBody=body; 42 | } 43 | 44 | 45 | QRectF Barrier::boundingRect() const 46 | { 47 | return polygon.boundingRect(); 48 | } 49 | 50 | 51 | QPainterPath Barrier::shape() const 52 | { 53 | QPainterPath path; 54 | path.addPolygon( polygon ); 55 | return path; 56 | } 57 | 58 | 59 | void Barrier::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) 60 | { 61 | // Filling 62 | painter->setBrush(QColor(87, 113, 140)); 63 | 64 | // Edges 65 | QPen pen; 66 | pen.setWidth(0.1); 67 | pen.setBrush(Qt::white); 68 | painter->setPen(pen); 69 | 70 | painter->drawPolygon( polygon ); 71 | } 72 | -------------------------------------------------------------------------------- /Barrier.h: -------------------------------------------------------------------------------- 1 | #ifndef BARRIER_H 2 | #define BARRIER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "GraphicsPhysicsItem.h" 11 | 12 | class Barrier : public GraphicsPhysicsItem 13 | { 14 | public: 15 | Barrier(QGraphicsItem *parent = Q_NULLPTR); 16 | 17 | QRectF boundingRect() const override; 18 | QPainterPath shape() const override; 19 | void paint(QPainter *painter, 20 | const QStyleOptionGraphicsItem *option, 21 | QWidget *widget) override; 22 | 23 | void setPolygon(const QPolygonF &iPolygon); 24 | void putToPhysicsWorld(); 25 | 26 | protected: 27 | 28 | QPolygonF polygon; 29 | 30 | }; 31 | 32 | #endif // BARRIER_H 33 | -------------------------------------------------------------------------------- /Brick.cpp: -------------------------------------------------------------------------------- 1 | #include "Brick.h" 2 | 3 | Brick::Brick(QGraphicsItem *parent) 4 | { 5 | QGraphicsItem::setParentItem(parent); 6 | 7 | width=0.8; 8 | height=0.4; 9 | 10 | color=QColor(156, 44, 44); // QColor(qrand() % 256, qrand() % 256, qrand() % 256); 11 | 12 | flagToRemove=false; 13 | } 14 | 15 | QRectF Brick::boundingRect() const 16 | { 17 | qreal halhWidth=width/2.0; 18 | qreal halhHeight=height/2.0; 19 | return QRectF(-halhWidth, -halhHeight, width, height); 20 | } 21 | 22 | 23 | QPainterPath Brick::shape() const 24 | { 25 | QPainterPath path; 26 | path.addRect( boundingRect() ); 27 | return path; 28 | } 29 | 30 | 31 | void Brick::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) 32 | { 33 | // Заливка 34 | painter->setBrush(color); 35 | 36 | // Граница 37 | QPen pen; 38 | pen.setWidth(0.1); 39 | pen.setBrush(Qt::white); 40 | painter->setPen(pen); 41 | 42 | painter->drawRect( boundingRect() ); 43 | } 44 | 45 | 46 | void Brick::putToPhysicsWorld() 47 | { 48 | b2BodyDef bodyDef; 49 | bodyDef.type=b2_staticBody; 50 | bodyDef.position.Set(this->x(), this->y()); 51 | b2Body *body=physicsWorld->CreateBody(&bodyDef); 52 | 53 | b2PolygonShape polygonShape; 54 | polygonShape.SetAsBox(width/2.0, height/2.0); 55 | 56 | body->CreateFixture(&polygonShape, 0.0); 57 | 58 | // Запоминается настроенное тело 59 | physicsBody=body; 60 | } 61 | 62 | 63 | void Brick::setSize(const qreal iWidth, const qreal iHeight) 64 | { 65 | width=iWidth; 66 | height=iHeight; 67 | } 68 | 69 | 70 | void Brick::setToRemove() 71 | { 72 | flagToRemove=true; // Кирпич подготавливается к уничтожению с игрового поля 73 | } 74 | 75 | 76 | bool Brick::isToRemove() const 77 | { 78 | return flagToRemove; 79 | } 80 | 81 | -------------------------------------------------------------------------------- /Brick.h: -------------------------------------------------------------------------------- 1 | #ifndef BRICK_H 2 | #define BRICK_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | class Brick : public GraphicsPhysicsItem 12 | { 13 | public: 14 | Brick(QGraphicsItem *parent = Q_NULLPTR); 15 | 16 | QRectF boundingRect() const override; 17 | QPainterPath shape() const override; 18 | void paint(QPainter *painter, 19 | const QStyleOptionGraphicsItem *option, 20 | QWidget *widget) override; 21 | 22 | void putToPhysicsWorld(); 23 | 24 | void setSize(const qreal iWidth, const qreal iHeight); 25 | void setToRemove(); 26 | bool isToRemove() const; 27 | 28 | protected: 29 | qreal width; 30 | qreal height; 31 | 32 | QColor color; 33 | 34 | bool flagToRemove; 35 | }; 36 | 37 | #endif // BRICK_H 38 | -------------------------------------------------------------------------------- /CaptureDevice.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "CaptureDevice.h" 7 | 8 | 9 | CaptureDevice::CaptureDevice() 10 | { 11 | deviceFileName="default"; 12 | brigthnessThreshold=50; 13 | } 14 | 15 | 16 | // Инициализация устройства видеозахвата 17 | void CaptureDevice::init(const QString iDeviceFileName) 18 | { 19 | qDebug() << "Start capture device init time: "<< QDateTime::currentDateTime(); 20 | 21 | deviceFileName=iDeviceFileName; 22 | 23 | if(iDeviceFileName=="default") { 24 | videoCaptureFlow.open(0); // Default камера 25 | if(!videoCaptureFlow.isOpened()) { 26 | QMessageBox msgBox; 27 | msgBox.setText("Can't open default camera"); 28 | msgBox.exec(); 29 | return; 30 | } 31 | } else { 32 | videoCaptureFlow.open( iDeviceFileName.toLocal8Bit().constData() ); // Камера на указанном файле устройства 33 | if(!videoCaptureFlow.isOpened()) { 34 | QMessageBox msgBox; 35 | msgBox.setText("Can't open camera device file: "+deviceFileName); 36 | msgBox.exec(); 37 | return; 38 | } 39 | } 40 | 41 | qDebug() << "Finish capture device init time: "<< QDateTime::currentDateTime(); 42 | } 43 | 44 | 45 | void CaptureDevice::setBrigthnessThreshold(const int iBrigthnessThreshold) 46 | { 47 | brigthnessThreshold=iBrigthnessThreshold; 48 | } 49 | 50 | 51 | QSize CaptureDevice::getFrameSize(void) 52 | { 53 | return QSize(videoCaptureFlow.get(CV_CAP_PROP_FRAME_WIDTH), videoCaptureFlow.get(CV_CAP_PROP_FRAME_HEIGHT)); 54 | } 55 | 56 | 57 | cv::Mat* CaptureDevice::getBwFrame(void) 58 | { 59 | // Из потока берется один кадр 60 | videoCaptureFlow >> currentFrame; 61 | if (currentFrame.empty()) 62 | return nullptr; 63 | 64 | double thresholdLevel=(double) brigthnessThreshold; 65 | 66 | // Преобразование в Ч/Б и превращение в контрастное изображение с порогом brigthnessThreshold 67 | cv::cvtColor(currentFrame, currentBwFrame, CV_BGR2GRAY); 68 | cv::GaussianBlur(currentBwFrame, currentBwFrame, cv::Size(7,7), 1.5, 1.5); 69 | cv::threshold(currentBwFrame, currentBwFrame, thresholdLevel, 254.0, cv::THRESH_BINARY); 70 | cv::flip(currentBwFrame, currentBwFrame, 1); 71 | 72 | // cv::imshow("Source frame", currentBwFrame); 73 | 74 | return ¤tBwFrame; 75 | } 76 | -------------------------------------------------------------------------------- /CaptureDevice.h: -------------------------------------------------------------------------------- 1 | #ifndef CAPTUREDEVICE_H 2 | #define CAPTUREDEVICE_H 3 | 4 | #include 5 | #include 6 | #include "opencv2/opencv.hpp" 7 | 8 | class CaptureDevice 9 | { 10 | public: 11 | CaptureDevice(); 12 | void init(const QString iDeviceFileName); 13 | void setBrigthnessThreshold(const int iBrigthnessThreshold); 14 | 15 | QSize getFrameSize(void); 16 | 17 | cv::Mat* getBwFrame(void); 18 | 19 | protected: 20 | QString deviceFileName; 21 | cv::VideoCapture videoCaptureFlow; 22 | cv::Mat currentFrame; 23 | cv::Mat currentBwFrame; 24 | 25 | int brigthnessThreshold; 26 | }; 27 | 28 | #endif // CAPTUREDEVICE_H 29 | -------------------------------------------------------------------------------- /CaptureWindow.cpp: -------------------------------------------------------------------------------- 1 | #include "CaptureWindow.h" 2 | #include "ui_CaptureWindow.h" 3 | 4 | CaptureWindow::CaptureWindow(QWidget *parent) : 5 | QDialog(parent), 6 | ui(new Ui::CaptureWindow) 7 | { 8 | ui->setupUi(this); 9 | } 10 | 11 | 12 | CaptureWindow::~CaptureWindow() 13 | { 14 | delete ui; 15 | } 16 | 17 | 18 | void CaptureWindow::setImage(QImage *img) 19 | { 20 | int enabledWidth=this->ui->capturePixmapLabel->sizeHint().width(); 21 | this->ui->capturePixmapLabel->setPixmap(QPixmap::fromImage( *img ).scaledToWidth(enabledWidth, Qt::FastTransformation)); 22 | } 23 | 24 | -------------------------------------------------------------------------------- /CaptureWindow.h: -------------------------------------------------------------------------------- 1 | #ifndef CAPTUREWINDOW_H 2 | #define CAPTUREWINDOW_H 3 | 4 | #include 5 | #include 6 | 7 | namespace Ui { 8 | class CaptureWindow; 9 | } 10 | 11 | class CaptureWindow : public QDialog 12 | { 13 | Q_OBJECT 14 | 15 | public: 16 | explicit CaptureWindow(QWidget *parent = 0); 17 | ~CaptureWindow(); 18 | 19 | void setImage(QImage *img); 20 | 21 | private: 22 | Ui::CaptureWindow *ui; 23 | }; 24 | 25 | #endif // CAPTUREWINDOW_H 26 | -------------------------------------------------------------------------------- /CaptureWindow.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | CaptureWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 658 10 | 518 11 | 12 | 13 | 14 | Capture 15 | 16 | 17 | 18 | 19 | 20 | 21 | 0 22 | 0 23 | 24 | 25 | 26 | Input video frame 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 0 35 | 0 36 | 37 | 38 | 39 | 40 | 640 41 | 480 42 | 43 | 44 | 45 | Video Capture 46 | 47 | 48 | Qt::AlignCenter 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /ContactListener.cpp: -------------------------------------------------------------------------------- 1 | #include "ContactListener.h" 2 | 3 | 4 | ContactListener::ContactListener() 5 | { 6 | ball=nullptr; 7 | bricks=nullptr; 8 | } 9 | 10 | void ContactListener::setBall(Ball *iBall) 11 | { 12 | ball=iBall; 13 | } 14 | 15 | void ContactListener::setBricks(QList *iBricks) 16 | { 17 | bricks=iBricks; 18 | } 19 | 20 | 21 | // Метод обработки события когда два тела перестают пересекаться 22 | // Отскок мяча должен происходить только после того, как контакт пропал 23 | void ContactListener::EndContact(b2Contact* contact) 24 | { 25 | b2Body *bodyA=contact->GetFixtureA()->GetBody(); 26 | b2Body *bodyB=contact->GetFixtureB()->GetBody(); 27 | 28 | // Нахождение кирпича, с которым перестал пересекаться мячик 29 | foreach( Brick* brick, *bricks ) 30 | if( bodyA==brick->getPhysicsBody() || bodyB==brick->getPhysicsBody()) 31 | brick->setToRemove(); // Кирпич помачается на удаление. Сразу здесь в обработчике удалять нельзя 32 | } 33 | -------------------------------------------------------------------------------- /ContactListener.h: -------------------------------------------------------------------------------- 1 | #ifndef CONTACTLISTENER_H 2 | #define CONTACTLISTENER_H 3 | 4 | #include 5 | #include "Ball.h" 6 | #include "Brick.h" 7 | 8 | // Данный класс подключается к физическому движку 9 | // для отслеживания событий контакта тел друг с другом 10 | 11 | class ContactListener : public b2ContactListener 12 | { 13 | public: 14 | ContactListener(); 15 | 16 | void setBall(Ball *iBall); 17 | void setBricks(QList *iBricks); 18 | 19 | void EndContact(b2Contact* contact); 20 | 21 | protected: 22 | 23 | Ball *ball; 24 | QList *bricks; // В арканоидах имеет смысл отслеживать только события контакта мяча с кирпичами, чтобы кирпичи убирать 25 | 26 | }; 27 | 28 | #endif // CONTACTLISTENER_H 29 | -------------------------------------------------------------------------------- /GameField.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "main.h" 5 | #include "ReadyPoster.h" 6 | #include "GameOverPoster.h" 7 | #include "GameField.h" 8 | 9 | GameField::GameField(QObject *parent) : QGraphicsScene(parent) 10 | { 11 | // Параметры сцены 12 | this->setSceneRect(0.0, 0.0, MOVE_NOID_FIELD_WIDTH, MOVE_NOID_FIELD_HEIGHT); 13 | 14 | // Инициализация физического движка 15 | b2Vec2 gravity(0.0, 0.2); // Сила гравитации на игровом поле 16 | physicsWorld=new b2World(gravity); 17 | 18 | // Инициализация обработчика пересечений 19 | contactListener=new ContactListener; 20 | contactListener->setBall(&ball); 21 | contactListener->setBricks(&bricks); 22 | physicsWorld->SetContactListener(contactListener); 23 | 24 | // Инициализация определителя положения ракетки (GameField наследуется от QObject, и здесь работает connect) 25 | connect(&moveDetectorThread, &QThread::started, &moveDetector, &MoveDetector::run); // При старте треда запустится основной цикл в методе run() 26 | connect(&moveDetector, SIGNAL(finished()), &moveDetectorThread, SLOT(quit())); // Соединение для корректного завершения потока 27 | moveDetector.moveToThread(&moveDetectorThread); // Определитель положения ракетки переносится в тред 28 | moveDetectorThread.start(); // Тред запускается, при этом в нем автоматически будет запущен объект moveDetector 29 | 30 | // Таймер обновления сцены 31 | connect(&updateWorldTimer, SIGNAL(timeout()), this, SLOT(updateWorld())); 32 | 33 | level=0; 34 | } 35 | 36 | GameField::~GameField() 37 | { 38 | // Удаляются все динамические объекты, отображаемые на текущем уровне 39 | clearLevel(); 40 | 41 | // Удаление обработчика пересечений 42 | delete contactListener; 43 | 44 | // Удаление движка физического моделирования 45 | delete physicsWorld; 46 | 47 | // Отключение физических объектов на стеке 48 | ball.setPhysicsWorld(nullptr); 49 | rocketBit.setPhysicsWorld(nullptr); 50 | 51 | // Отключение определителя положения ракетки 52 | moveDetector.doExit(); 53 | moveDetectorThread.quit(); 54 | moveDetectorThread.wait(); 55 | while(!moveDetectorThread.isFinished()) { 56 | qDebug() << "Wait finished move detector..."; 57 | } 58 | qDebug() << "Success move detector finished."; 59 | } 60 | 61 | 62 | void GameField::clearLevel() 63 | { 64 | // Удаляются препятствия 65 | foreach( Barrier *currentBarrier, barriers ) { 66 | this->removeItem(currentBarrier); 67 | delete currentBarrier; 68 | } 69 | barriers.clear(); 70 | 71 | // Удаляются кирпичи 72 | foreach( Brick *currentBrick, bricks ) { 73 | this->removeItem(currentBrick); 74 | delete currentBrick; 75 | } 76 | bricks.clear(); 77 | 78 | rocketBit.setPos(MOVE_NOID_START_BALL_POS_X, MOVE_NOID_START_BALL_POS_Y); 79 | ball.setPos(MOVE_NOID_START_ROCKET_BIT_POS_X, MOVE_NOID_START_ROCKET_BIT_POS_Y); 80 | } 81 | 82 | 83 | // Создание стандартных препятствий, ограничивающих экран игры 84 | void GameField::initBarriers() 85 | { 86 | QPolygonF polygon; 87 | 88 | // Левая стена 89 | Barrier *barrierLeft=new Barrier(); 90 | polygon << QPointF(0.0, 0.0) << QPointF(0.0, MOVE_NOID_FIELD_HEIGHT) << QPointF(0.05, MOVE_NOID_FIELD_HEIGHT) << QPointF(0.05, 0.0); 91 | barrierLeft->setPolygon(polygon); 92 | barrierLeft->setPos(0.0, 0.0); 93 | barriers.append( barrierLeft ); // Запоминается указатель на препятствие 94 | this->addItem(barrierLeft); // Препятствие кладется на поле 95 | barrierLeft->setPhysicsWorld(physicsWorld); 96 | 97 | // Правая стена 98 | Barrier *barrierRight=new Barrier(); 99 | barrierRight->setPolygon(polygon); 100 | barrierRight->setPos(MOVE_NOID_FIELD_WIDTH-0.049, 0.0); 101 | barriers.append( barrierRight ); 102 | this->addItem(barrierRight); 103 | barrierRight->setPhysicsWorld(physicsWorld); 104 | 105 | // Верхняя стена 106 | Barrier *barrierTop=new Barrier(); 107 | polygon.clear(); 108 | polygon << QPointF(0.0, 0.0) << QPointF(0.0, 0.05) << QPointF(MOVE_NOID_FIELD_WIDTH, 0.05) << QPointF(MOVE_NOID_FIELD_WIDTH, 0.0); 109 | barrierTop->setPolygon(polygon); 110 | barrierTop->setPos(0.0, 0.0); 111 | barriers.append( barrierTop ); 112 | this->addItem(barrierTop); 113 | barrierTop->setPhysicsWorld(physicsWorld); 114 | 115 | // Временная балка снизу 116 | /* 117 | Barrier *barrierBottom=new Barrier(); 118 | polygon.clear(); 119 | polygon << QPointF(0.0, 0.0) << QPointF(0.0, 0.15) << QPointF(8.0, 0.15) << QPointF(8.0, 0.0); 120 | barrierBottom->setRotation(-10.0); 121 | barrierBottom->setPolygon(polygon); 122 | barrierBottom->setPos(1.5, 9.8); 123 | barriers.append( barrierBottom ); 124 | this->addItem(barrierBottom); 125 | barrierBottom->setPhysicsWorld(physicsWorld); 126 | */ 127 | } 128 | 129 | 130 | // Установки мячика 131 | void GameField::initBall() 132 | { 133 | ball.setRadius(0.15); 134 | ball.setPos(MOVE_NOID_START_BALL_POS_X, MOVE_NOID_START_BALL_POS_Y); 135 | this->addItem(&ball); // Мячик кладется на поле 136 | ball.setPhysicsWorld(physicsWorld); 137 | } 138 | 139 | 140 | // Установки ракетки 141 | void GameField::initRocketBit() 142 | { 143 | rocketBit.setPos(MOVE_NOID_START_ROCKET_BIT_POS_X, MOVE_NOID_START_ROCKET_BIT_POS_Y); 144 | this->addItem(&rocketBit); // Ракетка кладется на поле 145 | rocketBit.setPhysicsWorld(physicsWorld); 146 | rocketBit.setMoveDetector(&moveDetector); 147 | } 148 | 149 | 150 | // Загрузка уровня 151 | void GameField::loadLevel(const int levelNum) 152 | { 153 | clearLevel(); 154 | initBarriers(); 155 | 156 | if(levelNum==1){ 157 | 158 | score=0; 159 | emit setScore(score); 160 | lives=5; 161 | emit setLives(lives); 162 | 163 | initBall(); 164 | initRocketBit(); 165 | 166 | // Создание кирпичей 167 | for(int i=0; i<5; i++) { 168 | qreal x=i*2+1.0; 169 | qreal y=4.0; 170 | createBrick(x,y); 171 | } 172 | for(int i=0; i<5; i++) { 173 | qreal x=i*2+1.5; 174 | qreal y=3.0; 175 | createBrick(x,y); 176 | } 177 | for(int i=0; i<5; i++) { 178 | qreal x=i*2+1.0; 179 | qreal y=2.0; 180 | createBrick(x,y); 181 | } 182 | } 183 | 184 | if(levelNum==2){ 185 | for(int j=0; j<7; j++) { 186 | for(int i=0; i<12-j; i++) { 187 | qreal x=i*0.8+0.5; 188 | qreal y=4.5-j*0.5; 189 | createBrick(x,y); 190 | } 191 | } 192 | } 193 | 194 | } 195 | 196 | 197 | // Создание кирпича по игровым координатам 198 | void GameField::createBrick(const qreal x, const qreal y) 199 | { 200 | Brick *brick=new Brick; 201 | brick->setPos(x, y); 202 | bricks.append( brick ); // Запоминается указатель на кирпич 203 | this->addItem(brick); // Кирпич кладется на поле 204 | brick->setPhysicsWorld(physicsWorld); 205 | } 206 | 207 | 208 | // Метод удаляет кирпичи, с которыми столкнулся мячик 209 | void GameField::destroyBricks() 210 | { 211 | // Перебор кирпичей и удаление тех, с которым столкнулся мячик 212 | QList::iterator i = bricks.begin(); 213 | while (i != bricks.end()) { 214 | Brick* brick=*i; 215 | 216 | if( brick->isToRemove() ) { 217 | // this->removeItem(brick); // Кирпич убирается с графического игрового поля 218 | delete brick; // Кирпич удаляется как объект 219 | i = bricks.erase(i); // Кирпич удаляется из списка кирпичей 220 | 221 | score+=10; 222 | emit setScore(score); 223 | } 224 | else 225 | ++i; 226 | } 227 | } 228 | 229 | 230 | void GameField::runGame() 231 | { 232 | level=1; 233 | loadLevel(level); 234 | 235 | updateWorldTimer.start(1000/60); 236 | } 237 | 238 | 239 | // Обновление мира, этот слот срабатывает по таймеру updateWorldTimer 240 | void GameField::updateWorld() 241 | { 242 | // Обновляется физический мир 243 | physicsWorld->Step(1.0/60.0, 6, 2); 244 | 245 | // Удаляются кирпичи, помеченные на удаление в обрабочике пересечений 246 | destroyBricks(); 247 | 248 | ball.updatePosByPhysicsWorld(); // Обновляется месторасположение мяча 249 | rocketBit.updatePosByMovieDetector(); // Обновляется месторасположение ракетки 250 | 251 | checkBallPosition(); // Проверка, не улетел ли мяч 252 | checkBricksCount(); // Проверка, не выбиты ли все кирпичи 253 | 254 | // Обновляется сцена 255 | this->update(); 256 | } 257 | 258 | 259 | // Проверка местоположения мяча 260 | void GameField::checkBallPosition() 261 | { 262 | // Если мячь улетел 263 | if(ball.y()>MOVE_NOID_FIELD_HEIGHT+1.0) { 264 | if(lives>0) { // Если еще есть попытки 265 | emit setLives(--lives); 266 | 267 | ReadyPoster readyPoster; 268 | readyPoster.setLine1Text("...READY..."); 269 | readyPoster.exec(); 270 | 271 | ball.moveToDefaultPos(); 272 | rocketBit.moveToDefaultPos(); 273 | } else { 274 | GameOverPoster gameOver; 275 | gameOver.setScore(score); 276 | gameOver.exec(); 277 | 278 | emit closeGame(); 279 | } 280 | } 281 | } 282 | 283 | 284 | // Проверка выбитых кирпичей 285 | void GameField::checkBricksCount() 286 | { 287 | // Если все кирпичи выбиты 288 | if(bricks.count()==0) { 289 | level++; 290 | 291 | ReadyPoster readyPoster; 292 | readyPoster.setLine1Text("Congratulation!"); 293 | readyPoster.setLine2Text("Go to next level "+QString::number(level)+"!"); 294 | readyPoster.exec(); 295 | 296 | ball.moveToDefaultPos(); 297 | rocketBit.moveToDefaultPos(); 298 | loadLevel(level); 299 | } 300 | 301 | } 302 | -------------------------------------------------------------------------------- /GameField.h: -------------------------------------------------------------------------------- 1 | #ifndef GAMEFIELD_H 2 | #define GAMEFIELD_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "ContactListener.h" 12 | #include "Barrier.h" 13 | #include "Brick.h" 14 | #include "RocketBit.h" 15 | #include "Ball.h" 16 | #include "MoveDetector.h" 17 | 18 | class GameField : public QGraphicsScene 19 | { 20 | Q_OBJECT 21 | 22 | public: 23 | GameField(QObject *parent = Q_NULLPTR); 24 | ~GameField(); 25 | 26 | void clearLevel(); 27 | void loadLevel(const int levelNum); 28 | 29 | void runGame(); 30 | 31 | private slots: 32 | void updateWorld(); 33 | 34 | signals: 35 | void setScore(int iScore); 36 | void setLives(int iLives); 37 | void closeGame(); 38 | 39 | protected: 40 | void createBrick(const qreal x, const qreal y); 41 | void destroyBricks(); 42 | 43 | void checkBallPosition(); // Проверка позиции мяча на предмет выхода его за игровое поле 44 | void checkBricksCount(); // Проверка количества оставшихся кирпичей 45 | 46 | void initBarriers(); 47 | void initBall(); 48 | void initRocketBit(); 49 | 50 | QList barriers; 51 | QList bricks; 52 | RocketBit rocketBit; 53 | Ball ball; 54 | 55 | b2World *physicsWorld; 56 | ContactListener *contactListener; 57 | 58 | QTimer updateWorldTimer; 59 | 60 | QThread moveDetectorThread; 61 | MoveDetector moveDetector; 62 | 63 | int score; 64 | int lives; 65 | int level; 66 | }; 67 | 68 | #endif // GAMEFIELD_H 69 | -------------------------------------------------------------------------------- /GameOverPoster.cpp: -------------------------------------------------------------------------------- 1 | #include "GameOverPoster.h" 2 | #include "ui_GameOverPoster.h" 3 | 4 | GameOverPoster::GameOverPoster(QWidget *parent) : 5 | QDialog(parent), 6 | ui(new Ui::GameOverPoster) 7 | { 8 | ui->setupUi(this); 9 | 10 | // Убирается рамка окна 11 | this->setWindowFlags(Qt::Window | Qt::FramelessWindowHint); 12 | } 13 | 14 | 15 | GameOverPoster::~GameOverPoster() 16 | { 17 | delete ui; 18 | } 19 | 20 | 21 | void GameOverPoster::setScore(const int score) 22 | { 23 | ui->scoreLabel->setText(QString::number(score)); 24 | } 25 | -------------------------------------------------------------------------------- /GameOverPoster.h: -------------------------------------------------------------------------------- 1 | #ifndef GAMEOVERPOSTER_H 2 | #define GAMEOVERPOSTER_H 3 | 4 | #include 5 | 6 | namespace Ui { 7 | class GameOverPoster; 8 | } 9 | 10 | class GameOverPoster : public QDialog 11 | { 12 | Q_OBJECT 13 | 14 | public: 15 | explicit GameOverPoster(QWidget *parent = 0); 16 | ~GameOverPoster(); 17 | 18 | void setScore(const int score); 19 | 20 | private: 21 | Ui::GameOverPoster *ui; 22 | }; 23 | 24 | #endif // GAMEOVERPOSTER_H 25 | -------------------------------------------------------------------------------- /GameOverPoster.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | GameOverPoster 4 | 5 | 6 | 7 | 0 8 | 0 9 | 471 10 | 300 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 255 20 | 255 21 | 255 22 | 23 | 24 | 25 | 26 | 27 | 28 | 239 29 | 226 30 | 160 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 255 40 | 255 41 | 255 42 | 43 | 44 | 45 | 46 | 47 | 48 | 239 49 | 226 50 | 160 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 239 60 | 226 61 | 160 62 | 63 | 64 | 65 | 66 | 67 | 68 | 239 69 | 226 70 | 160 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | Dialog 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 0 88 | 0 89 | 90 | 91 | 92 | 93 | DejaVu Sans Mono 94 | 50 95 | 96 | 97 | 98 | GAME OVER 99 | 100 | 101 | Qt::AlignCenter 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | Qt::Horizontal 111 | 112 | 113 | 114 | 40 115 | 20 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 0 125 | 0 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 16 135 | 117 136 | 249 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 16 146 | 117 147 | 249 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 190 157 | 190 158 | 190 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | DejaVu Sans 168 | 32 169 | 170 | 171 | 172 | Your score: 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 222 185 | 41 186 | 60 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 222 196 | 41 197 | 60 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 190 207 | 190 208 | 190 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | DejaVu Sans 218 | 32 219 | 220 | 221 | 222 | 0 223 | 224 | 225 | 226 | 227 | 228 | 229 | Qt::Horizontal 230 | 231 | 232 | 233 | 40 234 | 20 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 100 248 | 16777215 249 | 250 | 251 | 252 | ОК 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | pushButton 266 | clicked() 267 | GameOverPoster 268 | accept() 269 | 270 | 271 | 234 272 | 277 273 | 274 | 275 | 235 276 | 149 277 | 278 | 279 | 280 | 281 | 282 | -------------------------------------------------------------------------------- /GameWindow.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "GameWindow.h" 7 | #include "ui_GameWindow.h" 8 | 9 | GameWindow::GameWindow(QWidget *parent) : 10 | QDialog(parent), 11 | ui(new Ui::GameWindow) 12 | { 13 | ui->setupUi(this); 14 | 15 | this->showFullScreen(); 16 | 17 | // Устанавливается градиент заднего плана 18 | QLinearGradient gradient(0, 0, 0, ui->graphicsView->size().height()); 19 | gradient.setColorAt(0, QColor::fromRgbF(0.87, 0.89, 0.94, 1)); 20 | gradient.setColorAt(1, QColor::fromRgbF(0.69, 0.79, 0.97, 1)); 21 | QBrush brush(gradient); 22 | ui->graphicsView->setBackgroundBrush(brush); 23 | 24 | // Задается сцена в виде игрового поля 25 | gameField=new GameField(); 26 | 27 | // Сцена размещается в графическом виде 28 | ui->graphicsView->setScene(gameField); 29 | ui->graphicsView->fitInView(0.0, 0.0, 5.0, 5.0, Qt::KeepAspectRatio); 30 | // ui->graphicsView->fitInView(0.0, 0.0, 1.0, 1.0, Qt::KeepAspectRatio); 31 | 32 | connect(gameField, SIGNAL(setScore(int)), this, SLOT(onSetScore(int))); 33 | connect(gameField, SIGNAL(setLives(int)), this, SLOT(onSetLives(int))); 34 | connect(gameField, SIGNAL(closeGame()), this, SLOT(reject())); 35 | 36 | gameField->runGame(); 37 | } 38 | 39 | 40 | GameWindow::~GameWindow() 41 | { 42 | delete gameField; 43 | delete ui; 44 | } 45 | 46 | 47 | void GameWindow::reject() 48 | { 49 | qDebug() << "Reject game window"; 50 | QDialog::reject(); 51 | } 52 | 53 | 54 | void GameWindow::onSetScore(const int iScore) 55 | { 56 | ui->lcdScreenScore->display(iScore); 57 | } 58 | 59 | 60 | void GameWindow::onSetLives(const int iLives) 61 | { 62 | ui->lcdScreenLives->display(iLives); 63 | } 64 | -------------------------------------------------------------------------------- /GameWindow.h: -------------------------------------------------------------------------------- 1 | #ifndef GAMEWINDOW_H 2 | #define GAMEWINDOW_H 3 | 4 | #include 5 | #include 6 | 7 | #include "GameField.h" 8 | 9 | namespace Ui { 10 | class GameWindow; 11 | } 12 | 13 | class GameWindow : public QDialog 14 | { 15 | Q_OBJECT 16 | 17 | public slots: 18 | void onSetScore(const int iScore); 19 | void onSetLives(const int iLives); 20 | void reject(); 21 | 22 | public: 23 | explicit GameWindow(QWidget *parent = 0); 24 | ~GameWindow(); 25 | 26 | private: 27 | Ui::GameWindow *ui; 28 | 29 | GameField *gameField; 30 | }; 31 | 32 | #endif // GAMEWINDOW_H 33 | -------------------------------------------------------------------------------- /GameWindow.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | GameWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 598 10 | 444 11 | 12 | 13 | 14 | MoveNoid 15 | 16 | 17 | background-color: rgb(70, 111, 136); 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | true 26 | 27 | 28 | Qt::ScrollBarAlwaysOff 29 | 30 | 31 | Qt::ScrollBarAlwaysOff 32 | 33 | 34 | 35 | 0.000000000000000 36 | 0.000000000000000 37 | 0.000000000000000 38 | 0.000000000000000 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | Score 49 | 50 | 51 | Qt::AlignCenter 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | Lives 62 | 63 | 64 | Qt::AlignCenter 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 100 77 | 62 78 | 216 79 | 80 | 81 | 82 | 83 | 84 | 85 | 70 86 | 111 87 | 136 88 | 89 | 90 | 91 | 92 | 93 | 94 | 93 95 | 255 96 | 120 97 | 98 | 99 | 100 | 101 | 102 | 103 | 193 104 | 44 105 | 121 106 | 107 | 108 | 109 | 110 | 111 | 112 | 70 113 | 111 114 | 136 115 | 116 | 117 | 118 | 119 | 120 | 121 | 70 122 | 111 123 | 136 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 100 133 | 62 134 | 216 135 | 136 | 137 | 138 | 139 | 140 | 141 | 70 142 | 111 143 | 136 144 | 145 | 146 | 147 | 148 | 149 | 150 | 93 151 | 255 152 | 120 153 | 154 | 155 | 156 | 157 | 158 | 159 | 193 160 | 44 161 | 121 162 | 163 | 164 | 165 | 166 | 167 | 168 | 70 169 | 111 170 | 136 171 | 172 | 173 | 174 | 175 | 176 | 177 | 70 178 | 111 179 | 136 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 190 189 | 190 190 | 190 191 | 192 | 193 | 194 | 195 | 196 | 197 | 70 198 | 111 199 | 136 200 | 201 | 202 | 203 | 204 | 205 | 206 | 93 207 | 255 208 | 120 209 | 210 | 211 | 212 | 213 | 214 | 215 | 190 216 | 190 217 | 190 218 | 219 | 220 | 221 | 222 | 223 | 224 | 70 225 | 111 226 | 136 227 | 228 | 229 | 230 | 231 | 232 | 233 | 70 234 | 111 235 | 136 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | Qt::Vertical 248 | 249 | 250 | 251 | 20 252 | 5000 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | Qt::Vertical 261 | 262 | 263 | QDialogButtonBox::Close 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | buttonBox 277 | rejected() 278 | GameWindow 279 | reject() 280 | 281 | 282 | 531 283 | 432 284 | 285 | 286 | 286 287 | 274 288 | 289 | 290 | 291 | 292 | 293 | -------------------------------------------------------------------------------- /GraphicsPhysicsItem.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "GraphicsPhysicsItem.h" 3 | 4 | GraphicsPhysicsItem::GraphicsPhysicsItem(QGraphicsItem *parent) 5 | { 6 | QGraphicsItem::setParentItem(parent); 7 | 8 | physicsWorld=nullptr; 9 | physicsBody=nullptr; 10 | } 11 | 12 | 13 | GraphicsPhysicsItem::~GraphicsPhysicsItem() 14 | { 15 | // Элемент убирается из физического симулятора 16 | if(physicsBody!=nullptr && physicsWorld!=nullptr) 17 | physicsWorld->DestroyBody(physicsBody); 18 | } 19 | 20 | 21 | void GraphicsPhysicsItem::setPhysicsWorld(b2World *iPhysicsWorld) 22 | { 23 | physicsWorld=iPhysicsWorld; 24 | 25 | if(physicsWorld!=nullptr) 26 | putToPhysicsWorld(); 27 | } 28 | 29 | 30 | void GraphicsPhysicsItem::putToPhysicsWorld() 31 | { 32 | qDebug() << "It is empty method putToPhysicsWorld()"; 33 | } 34 | 35 | 36 | b2Body *GraphicsPhysicsItem::getPhysicsBody() const 37 | { 38 | return physicsBody; 39 | } 40 | -------------------------------------------------------------------------------- /GraphicsPhysicsItem.h: -------------------------------------------------------------------------------- 1 | #ifndef GRAPHICSPHYSICSITEM_H 2 | #define GRAPHICSPHYSICSITEM_H 3 | 4 | #include 5 | #include 6 | 7 | class GraphicsPhysicsItem : public QGraphicsItem 8 | { 9 | public: 10 | GraphicsPhysicsItem(QGraphicsItem *parent = Q_NULLPTR); 11 | virtual ~GraphicsPhysicsItem(); 12 | 13 | void setPhysicsWorld(b2World *iPhysicsWorld); 14 | virtual void putToPhysicsWorld(); 15 | b2Body* getPhysicsBody() const; 16 | 17 | protected: 18 | b2World *physicsWorld; 19 | b2Body *physicsBody; 20 | 21 | }; 22 | 23 | #endif // GRAPHICSPHYSICSITEM_H 24 | -------------------------------------------------------------------------------- /MainWindow.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "MainWindow.h" 4 | #include "ui_MainWindow.h" 5 | #include "SettingsWindow.h" 6 | #include "GameWindow.h" 7 | 8 | using namespace std; 9 | 10 | MainWindow::MainWindow(QWidget *parent) : 11 | QMainWindow(parent), 12 | ui(new Ui::MainWindow) 13 | { 14 | ui->setupUi(this); 15 | 16 | ui->gameVersion->setText("v."+ 17 | QString::number(APPLICATION_RELEASE_VERSION)+"."+ 18 | QString::number(APPLICATION_RELEASE_SUBVERSION)+"."+ 19 | QString::number(APPLICATION_RELEASE_MICROVERSION)); 20 | } 21 | 22 | 23 | MainWindow::~MainWindow() 24 | { 25 | delete ui; 26 | } 27 | 28 | 29 | void MainWindow::onSettingsButtonClicked() const 30 | { 31 | SettingsWindow settingsWindow; 32 | settingsWindow.exec(); 33 | } 34 | 35 | 36 | void MainWindow::onPlayButtonClicked() const 37 | { 38 | GameWindow gameWindow; 39 | gameWindow.exec(); 40 | } 41 | -------------------------------------------------------------------------------- /MainWindow.h: -------------------------------------------------------------------------------- 1 | #ifndef MAINWINDOW_H 2 | #define MAINWINDOW_H 3 | 4 | #include 5 | 6 | namespace Ui { 7 | class MainWindow; 8 | } 9 | 10 | class MainWindow : public QMainWindow 11 | { 12 | Q_OBJECT 13 | 14 | public: 15 | explicit MainWindow(QWidget *parent = 0); 16 | ~MainWindow(); 17 | 18 | private slots: 19 | void onSettingsButtonClicked() const; 20 | void onPlayButtonClicked() const; 21 | 22 | private: 23 | Ui::MainWindow *ui; 24 | }; 25 | 26 | #endif // MAINWINDOW_H 27 | -------------------------------------------------------------------------------- /MainWindow.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 800 10 | 480 11 | 12 | 13 | 14 | MoveNoid 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | Qt::Horizontal 24 | 25 | 26 | 27 | 40 28 | 20 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | Qt::Vertical 41 | 42 | 43 | 44 | 20 45 | 40 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | :/bin/pic/GameView.png 57 | 58 | 59 | 60 | 61 | 62 | 63 | Qt::Vertical 64 | 65 | 66 | 67 | 20 68 | 40 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | <b>MoveNoid</b> 77 | 78 | 79 | Qt::RichText 80 | 81 | 82 | Qt::AlignCenter 83 | 84 | 85 | 86 | 87 | 88 | 89 | v.0.1 90 | 91 | 92 | Qt::AlignCenter 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 250 101 | 16777215 102 | 103 | 104 | 105 | Play 106 | 107 | 108 | true 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 250 117 | 16777215 118 | 119 | 120 | 121 | Settings 122 | 123 | 124 | true 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 250 133 | 16777215 134 | 135 | 136 | 137 | Exit 138 | 139 | 140 | true 141 | 142 | 143 | 144 | 145 | 146 | 147 | Qt::Vertical 148 | 149 | 150 | 151 | 20 152 | 40 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | Qt::Horizontal 165 | 166 | 167 | 168 | 40 169 | 20 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | settingsButton 186 | clicked() 187 | MainWindow 188 | onSettingsButtonClicked() 189 | 190 | 191 | 391 192 | 385 193 | 194 | 195 | 593 196 | 387 197 | 198 | 199 | 200 | 201 | exitButton 202 | clicked() 203 | MainWindow 204 | close() 205 | 206 | 207 | 444 208 | 414 209 | 210 | 211 | 546 212 | 419 213 | 214 | 215 | 216 | 217 | playButton 218 | clicked() 219 | MainWindow 220 | onPlayButtonClicked() 221 | 222 | 223 | 476 224 | 357 225 | 226 | 227 | 561 228 | 357 229 | 230 | 231 | 232 | 233 | 234 | onSettingsButtonClicked() 235 | onPlayButtonClicked() 236 | 237 | 238 | -------------------------------------------------------------------------------- /MathAn.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "MathAn.h" 4 | 5 | MathAn::MathAn() 6 | { 7 | 8 | } 9 | 10 | 11 | // Преобразование радианы в градусы 12 | qreal MathAn::radToDeg(const qreal rad) 13 | { 14 | return (rad * 180.0) / PI; 15 | } 16 | 17 | 18 | // Преобразование градусы в радианы 19 | qreal MathAn::degToRad(const qreal deg) 20 | { 21 | return (PI * deg) / 180.0; 22 | } 23 | 24 | 25 | // Функция преобразует угол angle с шагом 90 градусов так, чтобы он 26 | // примерно соответствовал углу wayMarkAngle, на который он должен ориентироваться 27 | qreal MathAn::selectNearestAngle(qreal angle, const qreal wayMarkAngle, const qreal angleDispersion) 28 | { 29 | // На вход может быть подан отрицательный угол, его надо нормировать 30 | if(angle < 0.0) angle+=360.0; 31 | if(angle > 360.0) angle-=360.0; 32 | 33 | for(int i=0; i<=270; i=i+90) { 34 | qreal a=angle+(qreal)(i); 35 | if(a < 0.0) a+=360.0; // Нормировка угла a 36 | if(a > 360.0) a-=360.0; 37 | if(qAbs(a-wayMarkAngle) 5 | 6 | #define PI 3.14159265358979323846264338327950288419717 7 | 8 | class MathAn 9 | { 10 | public: 11 | MathAn(); 12 | 13 | static qreal radToDeg(const qreal rad); 14 | static qreal degToRad(const qreal deg); 15 | static qreal selectNearestAngle(qreal angle, const qreal wayMarkAngle, const qreal angleDispersion); 16 | static qreal getAngleByPoints(const qreal xA, const qreal yA, const qreal xB, const qreal yB); 17 | }; 18 | 19 | #endif // MATHAN_H 20 | -------------------------------------------------------------------------------- /MoveDetector.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "MoveDetector.h" 6 | #include "MathAn.h" 7 | #include "AppConfig.h" 8 | #include "main.h" 9 | 10 | using namespace std; 11 | 12 | extern AppConfig appConfig; 13 | 14 | MoveDetector::MoveDetector(QObject *parent) : QObject(parent) 15 | { 16 | // Для отладки можно сделать вывод в консоль и будет появляться дополнительное окно 17 | // с обнаруженными объектами (окно игры надо сделать не на весь экран) 18 | enableDebug=false; 19 | 20 | // Для устновки всех коэффициентов надо сделать отдельное окно настроек 21 | rocetBitXY=QPointF(5.0, 8.0); 22 | rocetBitAngle=0.0; 23 | 24 | enableBoxMinArea=0.0008333; 25 | enableBoxMaxArea=0.25; 26 | enableDispersionBoxBtwContour=0.25; 27 | 28 | enableAspectRatio << 1.0/1.0 << 2.0/1.0 << 10.0/3.0; // Прямоугольники 3x3, 6x3, 10x3 29 | enableAspectRatioDispersion=0.46; 30 | 31 | dynamicAngleDispersion=11.0; 32 | 33 | borderSafetyX=0.15; 34 | borderSafetyY=0.12; 35 | 36 | // Инициализируется устройство захвата изображения 37 | captureDevice.init( appConfig.getParameter("captureDeviceFileName") ); 38 | captureDevice.setBrigthnessThreshold( appConfig.getParameter("brigthnessThreshold").toInt() ); 39 | 40 | // Открытие окна с отлаживаемым изображением с камеры 41 | debugWindow=nullptr; 42 | if(enableDebug) { 43 | debugWindow=new CaptureWindow; 44 | debugWindow->show(); 45 | } 46 | } 47 | 48 | 49 | MoveDetector::~MoveDetector() 50 | { 51 | delete debugWindow; 52 | } 53 | 54 | 55 | void MoveDetector::run() 56 | { 57 | exitFlag=false; 58 | 59 | for(;;){ 60 | update(); 61 | 62 | if(exitFlag) { 63 | emit finished(); 64 | return; 65 | } 66 | } 67 | } 68 | 69 | 70 | void MoveDetector::doExit() 71 | { 72 | exitFlag=true; 73 | } 74 | 75 | 76 | void MoveDetector::update() 77 | { 78 | // Этап 1 - Получение упрощенных данных о контурах на картинке, дальше работа только с этими данными 79 | QVector contoursData=getSimplificatedContourData(); 80 | if(enableDebug) qDebug() << "Detect object stage 1: " << contoursData.size(); 81 | 82 | // Этап 2 - удаление контуров со слишком большим или слишком маленьким ограничивающим прямоугольником 83 | // и контуров у которых разница с ограничивающим прямоугольником слишком большая 84 | contoursData=removeTooSmallBigCrookedContour(contoursData); 85 | if(enableDebug) qDebug() << "Detect object stage 2: " << contoursData.size(); 86 | 87 | // Этап 3 - удаление контуров с недопустимыми пропорциями 88 | contoursData=removeBadAspectRatioContour(contoursData); 89 | if(enableDebug) qDebug() << "Detect object stage 3: " << contoursData.size(); 90 | 91 | // Этап 4 - в массиве должны остаться только два самых больших контура, остальные отбрасываются как помехи 92 | contoursData=removeNoiseContour(contoursData); 93 | if(enableDebug) qDebug() << "Detect object stage 4: " << contoursData.size(); 94 | 95 | // Метод определяет местоположение и угол маркера на основе его частей и запоминает его как игровые координаты 96 | detectMarkerLocation( getMarker(contoursData) ); 97 | } 98 | 99 | 100 | // Конструирование маркера, описание его частей 101 | Marker MoveDetector::getMarker(const QVector contours) 102 | { 103 | Marker marker; 104 | 105 | if(contours.size()==0) { 106 | marker.chunks=0; 107 | return marker; 108 | } 109 | 110 | qreal angle; 111 | 112 | // Данные части A 113 | marker.chunks=1; 114 | marker.verticlesA=getBoxVertex(contours[0]); 115 | marker.massCenterA=QPointF(contours[0].box.center.x, contours[0].box.center.y); 116 | 117 | angle=contours[0].box.angle; // Нормализация угла 118 | if(angle < 0.0) angle+=360.0; 119 | if(angle > 360.0) angle-=360.0; 120 | marker.angleA=angle; 121 | 122 | marker.sizeA=QSizeF(contours[0].box.size.width, contours[0].box.size.height); 123 | marker.areaA=contours[0].box.size.width*contours[0].box.size.height; 124 | // marker.boundingBox=contours[0].box.boundingRect(); 125 | 126 | if(contours.size()==1) 127 | return marker; 128 | 129 | // Данные части B 130 | marker.chunks=2; // Количество частей изменяется на 2. Далее заполняется только часть B, потому что часть A уже заполнена 131 | marker.verticlesB=getBoxVertex(contours[1]); 132 | marker.massCenterB=QPointF(contours[1].box.center.x, contours[1].box.center.y); 133 | 134 | angle=contours[1].box.angle; // Нормализация угла 135 | if(angle < 0.0) angle+=360.0; 136 | if(angle > 360.0) angle-=360.0; 137 | marker.angleB=angle; 138 | 139 | marker.sizeB=QSizeF(contours[1].box.size.width, contours[1].box.size.height); 140 | marker.areaB=contours[1].box.size.width*contours[1].box.size.height; 141 | 142 | return marker; 143 | } 144 | 145 | 146 | // Получиение Qt-совместимых координат минимального прямоугольника 147 | QList MoveDetector::getBoxVertex(const ContourData contour) 148 | { 149 | QList v; 150 | 151 | cv::Point2f vertices[4]; 152 | contour.box.points(vertices); // В массив vertices засовываются точки углов минимального прямоугольника 153 | for (int i = 0; i < 4; i++) { // Из массива vertices значения записываются в итоговый список 154 | v << QPointF(vertices[i].x, vertices[i].y); 155 | } 156 | 157 | return v; 158 | } 159 | 160 | 161 | // Этап 1 162 | // Метод обрабатывает контуры и извлекает обобщенную информацию - минимальный прямоугольник и площадь контура 163 | QVector MoveDetector::getSimplificatedContourData() 164 | { 165 | cv::Mat* currentBwFrame=captureDevice.getBwFrame(); 166 | 167 | vector > contours; // Массив для нахождения вершин минимальных прямоугольников 168 | vector hierarchy; // Массив иерархии, передается как формальный параметр потому что иерархия в данном случае не используется 169 | 170 | // Поиск контуров 171 | cv::findContours(*currentBwFrame, 172 | contours, 173 | hierarchy, 174 | CV_RETR_LIST, 175 | CV_CHAIN_APPROX_SIMPLE); 176 | 177 | QVector contoursData; 178 | 179 | // Найденные контуры нужно обработать - получить минимальные прямоугольники и площадь под контурами 180 | // Все контуры верхнего уровня (без иерархии), поэтому обходится только верхний уровень 181 | // for(unsigned int idx = 0; idx >= 0; idx = hierarchy[idx][0] ) - это код для иерархических контуров 182 | for(unsigned int idx = 0; idx < contours.size(); idx++) 183 | { 184 | ContourData data; 185 | 186 | // Для каждого контура определяется минимальный прямоугольник 187 | vector points=contours[idx]; // Очередной контур "помещается" в массив points 188 | data.box=minAreaRect(points); // Определяется минимальный прямоугольник для контура 189 | data.area=contourArea(points); // Площадь текущего произвольного контура 190 | 191 | contoursData.append(data); 192 | 193 | if(enableDebug) { 194 | cv::Scalar color( rand()&255, rand()&255, rand()&255 ); 195 | 196 | cv::Point2f vertices[4]; 197 | data.box.points(vertices); // В массив vertices засовываются точки углов минимального прямоугольника 198 | for (int i = 0; i < 4; i++) 199 | cv::line(*currentBwFrame, vertices[i], vertices[(i+1)%4], color); 200 | } 201 | } 202 | 203 | // Картинка для отладки 204 | if(enableDebug) { 205 | QImage img((uchar*)currentBwFrame->data, currentBwFrame->cols, currentBwFrame->rows, currentBwFrame->step, QImage::Format_Grayscale8); 206 | debugWindow->setImage(&img); 207 | } 208 | 209 | return contoursData; 210 | } 211 | 212 | 213 | // Этап 2 214 | QVector MoveDetector::removeTooSmallBigCrookedContour(QVector contoursData) 215 | { 216 | QMutableVectorIterator iterator(contoursData); 217 | while(iterator.hasNext()) { 218 | ContourData currentData=iterator.next(); 219 | 220 | // Относительная площадь ограничивающего прямоугольника 221 | qreal relationArea=( (qreal)(currentData.box.size.width*currentData.box.size.height) )/( (qreal)(captureDevice.getFrameSize().width()*captureDevice.getFrameSize().height()) ); 222 | 223 | // Разница между площадью контура и площадью ограничивающего прямоугольника 224 | qreal dispersion=qAbs(1.0 - ((qreal) currentData.area)/((qreal)(currentData.box.size.width*currentData.box.size.height))); 225 | 226 | if( relationAreaenableBoxMaxArea ) { // Очень большой 230 | // qDebug() << "Stage 2. Remove if relationArea" << relationArea << "more than" << "enableBoxMaxArea" << enableBoxMaxArea; 231 | iterator.remove(); 232 | } else if( dispersion > enableDispersionBoxBtwContour ) { // Площадь контура далека от площади ограничивающего прямоугольника 233 | // qDebug() << "Stage 2. Remove if dispersion " << dispersion << "more than" << "enableDispersionBoxBtwContour" << enableDispersionBoxBtwContour; 234 | iterator.remove(); 235 | } 236 | } 237 | 238 | return contoursData; 239 | } 240 | 241 | 242 | // Этап 3 243 | QVector MoveDetector::removeBadAspectRatioContour(QVector contoursData) 244 | { 245 | QMutableVectorIterator iterator(contoursData); 246 | while(iterator.hasNext()) { 247 | ContourData currentData=iterator.next(); 248 | 249 | // Соотношение сторон - это ширина к высоте, причем ширина всегда должна быть больше высоты 250 | qreal w=qMax( (qreal)currentData.box.size.width, (qreal)currentData.box.size.height ); 251 | qreal h=qMin( (qreal)currentData.box.size.width, (qreal)currentData.box.size.height ); 252 | qreal aspectRatio=w/h; 253 | 254 | // Перебираются допустимые соотношения сторон 255 | bool isFind=false; 256 | foreach(qreal currentEnableAspectRatio, enableAspectRatio) { 257 | // Если соотношения сторон соответсвуют (с погрешностью) допустимым 258 | if(qAbs(currentEnableAspectRatio-aspectRatio) < enableAspectRatioDispersion) { 259 | isFind=true; 260 | } 261 | } 262 | 263 | if(!isFind) { 264 | if(enableDebug) qDebug() << "Stage 3. Remove object with bad adpect ratio" << aspectRatio; 265 | iterator.remove(); 266 | } 267 | 268 | } 269 | 270 | return contoursData; 271 | } 272 | 273 | 274 | // Этап 4 275 | // Данный метод должен оставить только два самых больших контура, остальное считается шумом 276 | QVector MoveDetector::removeNoiseContour(QVector contoursData) 277 | { 278 | // Сортировка по убыванию 279 | qSort(contoursData.begin(), contoursData.end(), contourMoreThan); 280 | 281 | // foreach(ContourData contourData, contoursData) 282 | // qDebug() << "Stage 4. Sorted area:" << contourData.area; 283 | 284 | // Оставляется только два элемента 285 | if(contoursData.size()>2) 286 | contoursData.remove(2, contoursData.size()-2); 287 | 288 | return contoursData; 289 | } 290 | 291 | 292 | // Для обычной сортировки от малых значений к большим 293 | bool MoveDetector::contourLessThan(const ContourData &c1, const ContourData &c2) 294 | { 295 | return c1.area < c2.area; 296 | } 297 | 298 | 299 | // Для обратной сортировки от больших значений к малым 300 | bool MoveDetector::contourMoreThan(const ContourData &c1, const ContourData &c2) 301 | { 302 | return c1.area > c2.area; 303 | } 304 | 305 | 306 | void MoveDetector::detectMarkerLocation(const Marker marker) 307 | { 308 | static Marker previousMarker=marker; 309 | 310 | if(enableDebug) qDebug() << "Marker chunks: " << marker.chunks; 311 | 312 | if(marker.chunks==1) { 313 | // Приведение координат к виртуальному экрану 314 | QPointF centerMass=convertToSafetyCoord( QPointF(marker.massCenterA.x(), marker.massCenterA.y()) ); 315 | 316 | // Приведение координат к игровому полю 317 | qreal x=centerMass.x()*MOVE_NOID_FIELD_WIDTH/(qreal)captureDevice.getFrameSize().width(); 318 | qreal y=centerMass.y()*MOVE_NOID_FIELD_HEIGHT/(qreal)captureDevice.getFrameSize().height(); 319 | rocetBitXY=QPointF(x, y); 320 | 321 | // Угол подбирается так, чтобы он был ближайшим к предыдущему значению 322 | // В момент вызова объект еще хранит предыдущее значение угла 323 | qreal previousAngle=rocetBitAngle; 324 | rocetBitAngle=MathAn::selectNearestAngle(marker.angleA, previousAngle, dynamicAngleDispersion); 325 | 326 | if(enableDebug) qDebug() << "Marker angle: " << marker.angleA << "prev:" << previousAngle << "disp:" << dynamicAngleDispersion << "res:" << rocetBitAngle; 327 | 328 | } else if(marker.chunks==2) { 329 | qreal xA=marker.massCenterA.x(); 330 | qreal xB=marker.massCenterB.x(); 331 | qreal yA=marker.massCenterA.y(); 332 | qreal yB=marker.massCenterB.y(); 333 | 334 | qreal xMass=(xA+xB)/2.0; 335 | qreal yMass=(yA+yB)/2.0; 336 | 337 | // Приведение координат к виртуальному экрану 338 | QPointF centerMass=convertToSafetyCoord( QPointF(xMass, yMass) ); 339 | 340 | // Приведение координат к игровому полю 341 | qreal x=centerMass.x()*MOVE_NOID_FIELD_WIDTH/(qreal)captureDevice.getFrameSize().width(); 342 | qreal y=centerMass.y()*MOVE_NOID_FIELD_HEIGHT/(qreal)captureDevice.getFrameSize().height(); 343 | 344 | rocetBitXY=QPointF(x, y); 345 | 346 | rocetBitAngle=MathAn::getAngleByPoints(xA, yA, xB, yB); 347 | } 348 | 349 | previousMarker=marker; 350 | } 351 | 352 | 353 | QPointF MoveDetector::convertToSafetyCoord(const QPointF iPos) 354 | { 355 | qreal x=iPos.x(); 356 | qreal y=iPos.y(); 357 | 358 | qreal borderPixX=(qreal)captureDevice.getFrameSize().width()*borderSafetyX; 359 | qreal borderPixY=(qreal)captureDevice.getFrameSize().height()*borderSafetyY; 360 | 361 | qreal newScrSizeX=(qreal)captureDevice.getFrameSize().width()-borderPixX*2.0; 362 | qreal newScrSizeY=(qreal)captureDevice.getFrameSize().height()-borderPixY*2.0; 363 | 364 | // Координаты точки в уменьшенном прямоугольнике 365 | x=x-borderPixX; 366 | y=y-borderPixY; 367 | 368 | // Нормальзация 369 | if(x<0.0)x=0.0; 370 | if(x>newScrSizeX)x=newScrSizeX; 371 | if(y<0.0)y=0.0; 372 | if(y>newScrSizeY)y=newScrSizeY; 373 | 374 | // Получение нормированных на единицу координат 375 | x=x/newScrSizeX; 376 | y=y/newScrSizeY; 377 | 378 | // Получение координат в размере захватываемого фрейма 379 | x=x*(qreal)captureDevice.getFrameSize().width(); 380 | y=y*(qreal)captureDevice.getFrameSize().height(); 381 | 382 | return QPointF(x, y); 383 | } 384 | 385 | 386 | // Координаты ракетки 387 | QPointF MoveDetector::getRocketBitPos() const 388 | { 389 | // return getFakeRocketBitPos(); 390 | return rocetBitXY; 391 | } 392 | 393 | 394 | // Наклон ракетки, в градусах 395 | qreal MoveDetector::getRocketBitAngle() const 396 | { 397 | // return getFakeRocketBitAngle(); 398 | return rocetBitAngle; 399 | } 400 | 401 | 402 | QPointF MoveDetector::getFakeRocketBitPos() const 403 | { 404 | static qreal angleShiftX=0.0; 405 | static qreal angleShiftY=0.0; 406 | static qreal anglePhase=0.0; 407 | 408 | angleShiftX+=0.025; 409 | angleShiftY+=0.015; 410 | anglePhase+=0.0005; 411 | 412 | qreal x=5.0+sin(angleShiftX+anglePhase)*4.0; 413 | qreal y=8.0+cos(angleShiftX+anglePhase)*1.5; 414 | 415 | // qDebug() << "RocketBit fake coordinats: " << x << y; 416 | 417 | return QPointF(x, y); 418 | } 419 | 420 | 421 | qreal MoveDetector::getFakeRocketBitAngle() const 422 | { 423 | static qreal angle=0.0; 424 | static int direction=1; 425 | qreal delta=0.01; 426 | qreal maxAngle=0.5; 427 | 428 | angle=angle+(qreal)direction*delta; 429 | 430 | if(fabs(angle)>maxAngle) 431 | direction=-direction; 432 | 433 | return angle; 434 | } 435 | 436 | -------------------------------------------------------------------------------- /MoveDetector.h: -------------------------------------------------------------------------------- 1 | #ifndef MOVEDETECTOR_H 2 | #define MOVEDETECTOR_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "opencv2/opencv.hpp" 8 | #include "CaptureDevice.h" 9 | #include "CaptureWindow.h" 10 | 11 | 12 | // Итоговые сведения об обнаруженном маркере 13 | // Физически маркер состоит из двух белых прямоугольников 14 | // Первый - квадрат 3x3, второй - прямоугольник 6x3. Между ними расстояние 1. 15 | // Общая площадь этих двух прямоугольников с учетом расстояния между ними 10x3 16 | // Размеры даны в условных единицах, реально маркер можно сделать в сантиметрах и размещать его в 50-70 см. от камеры 17 | struct Marker{ 18 | 19 | int chunks; // Количество частей 20 | // 0 - нет маркера 21 | // 1 - одна большая часть A (10:3) - для движущегося смазанного и потому "слипшегося" изображения 22 | // 2 - две части A и B (1:1 и 2:1) - для четкого изображения 23 | QList verticlesA; // Вершины ограничивающего прямоугольника A 24 | QList verticlesB; // Вершины ограничивающего прямоугольника B 25 | 26 | QPointF massCenterA; 27 | QPointF massCenterB; 28 | 29 | qreal angleA; 30 | qreal angleB; 31 | 32 | QSizeF sizeA; // Размер ограничивающего прямоугольника 33 | QSizeF sizeB; 34 | 35 | qreal areaA; // Площадь ограничивающего прямоугольника (чтобы повторно не высчитывать) 36 | qreal areaB; 37 | 38 | QRectF boundingRect; // Ограничивающий прямоугольник с вертикальными и горизонтальными гранями 39 | }; 40 | 41 | 42 | struct ContourData { 43 | cv::RotatedRect box; // Ограничивающий прямоугольник 44 | qreal area; // Площадь произвольного контура 45 | }; 46 | 47 | 48 | class MoveDetector : public QObject 49 | { 50 | Q_OBJECT 51 | 52 | public: 53 | explicit MoveDetector(QObject *parent = 0); 54 | virtual ~MoveDetector(); 55 | 56 | public slots: 57 | void run(); // Метод, используемый для запуска в треде 58 | QPointF getRocketBitPos() const; 59 | qreal getRocketBitAngle() const; 60 | void doExit(); 61 | 62 | signals: 63 | void finished(); // Сигнал о реальном завершении работы (метод run() останавливается и происходит выход из него) 64 | 65 | protected: 66 | void update(); 67 | QVector getSimplificatedContourData(); 68 | QVector removeTooSmallBigCrookedContour(QVector contoursData); 69 | QVector removeBadAspectRatioContour(QVector contoursData); 70 | QVector removeNoiseContour(QVector contoursData); 71 | 72 | static bool contourLessThan(const ContourData &c1, const ContourData &c2); 73 | static bool contourMoreThan(const ContourData &c1, const ContourData &c2); 74 | 75 | static QList getBoxVertex(const ContourData contour); 76 | static Marker getMarker(const QVector contours); 77 | void detectMarkerLocation(const Marker marker); 78 | QPointF convertToSafetyCoord(const QPointF iPos); 79 | 80 | QPointF getFakeRocketBitPos() const; 81 | qreal getFakeRocketBitAngle() const; 82 | 83 | QPointF rocetBitXY; 84 | qreal rocetBitAngle; 85 | 86 | CaptureDevice captureDevice; 87 | 88 | // Значения в процентах от площади картинки, где 1% записывается как 0.01 89 | // Например, для картинки с камеры 640x480 pix минимальный распознаваемый объект будет примерно 16x16 pix 90 | // 640x480=307200, 16x16=256, значит размер такого объекта относительно площади картинки будет 256/307200=0.0008333 91 | qreal enableBoxMinArea; // Граничное значение для минимально допустимой площади ограничивающего прямоугольника 92 | qreal enableBoxMaxArea; // Граничное значение для максимально допустимой площади ограничивающего прямоугольника 93 | 94 | qreal enableDispersionBoxBtwContour; // Допустимая разница в площади между ограничивающим прямоугольником и контуром 95 | // Если площади абсольно равны, отношение их площадей 1 96 | // Пример: площадь A=100, площадь B=115. Отношение 100/115=0.86956 97 | // Разница площади fabs(1 - 0.86956)=0.13043 - это значение и проверяется 98 | 99 | QList enableAspectRatio; // Допустимые соотношения сторон для ограничивающих прямоугольников 100 | qreal enableAspectRatioDispersion; // Допустимая погрешность при проверке соотношения сторон ограничивающих прямоугольников 101 | 102 | qreal dynamicAngleDispersion; // Величина для подбора угла на основе предыдущего значения, 103 | // с учетом того, что opencv может дать значение кратное 90 104 | 105 | qreal borderSafetyX; // Границы безопасности от края кадра, за которыми не должно срабатывать распознавание маркера 106 | qreal borderSafetyY; 107 | 108 | CaptureWindow *debugWindow; 109 | 110 | bool enableDebug; 111 | bool exitFlag; 112 | 113 | }; 114 | 115 | #endif // MOVEDETECTOR_H 116 | -------------------------------------------------------------------------------- /MoveNoid.pro: -------------------------------------------------------------------------------- 1 | #------------------------------------------------- 2 | # 3 | # Project created by QtCreator 2017-11-22T14:24:20 4 | # 5 | #------------------------------------------------- 6 | 7 | #QMAKE_CXXFLAGS_DEBUG += -pg -no-pie 8 | #MAKE_LFLAGS_DEBUG += -pg -no-pie 9 | 10 | QT += core gui 11 | 12 | greaterThan(QT_MAJOR_VERSION, 4): QT += widgets 13 | 14 | TARGET = MoveNoid 15 | TEMPLATE = app 16 | 17 | # The following define makes your compiler emit warnings if you use 18 | # any feature of Qt which has been marked as deprecated (the exact warnings 19 | # depend on your compiler). Please consult the documentation of the 20 | # deprecated API in order to know how to port your code away from it. 21 | DEFINES += QT_DEPRECATED_WARNINGS 22 | 23 | # You can also make your code fail to compile if you use deprecated APIs. 24 | # In order to do so, uncomment the following line. 25 | # You can also select to disable deprecated APIs only up to a certain version of Qt. 26 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 27 | 28 | 29 | SOURCES += \ 30 | main.cpp \ 31 | MainWindow.cpp \ 32 | SettingsWindow.cpp \ 33 | AppConfig.cpp \ 34 | GameWindow.cpp \ 35 | Ball.cpp \ 36 | GameField.cpp \ 37 | Barrier.cpp \ 38 | RocketBit.cpp \ 39 | Brick.cpp \ 40 | GraphicsPhysicsItem.cpp \ 41 | ContactListener.cpp \ 42 | CaptureDevice.cpp \ 43 | MoveDetector.cpp \ 44 | CaptureWindow.cpp \ 45 | ReadyPoster.cpp \ 46 | GameOverPoster.cpp \ 47 | MathAn.cpp 48 | 49 | HEADERS += \ 50 | MainWindow.h \ 51 | SettingsWindow.h \ 52 | AppConfig.h \ 53 | main.h \ 54 | GameWindow.h \ 55 | Ball.h \ 56 | GameField.h \ 57 | Barrier.h \ 58 | RocketBit.h \ 59 | Brick.h \ 60 | GraphicsPhysicsItem.h \ 61 | ContactListener.h \ 62 | CaptureDevice.h \ 63 | MoveDetector.h \ 64 | CaptureWindow.h \ 65 | ReadyPoster.h \ 66 | GameOverPoster.h \ 67 | MathAn.h 68 | 69 | FORMS += \ 70 | MainWindow.ui \ 71 | SettingsWindow.ui \ 72 | GameWindow.ui \ 73 | CaptureWindow.ui \ 74 | ReadyPoster.ui \ 75 | GameOverPoster.ui 76 | 77 | CONFIG += mobility 78 | MOBILITY = 79 | 80 | RESOURCES += \ 81 | resource.qrc 82 | 83 | # add open CV 84 | unix { 85 | CONFIG += link_pkgconfig 86 | PKGCONFIG += opencv 87 | PKGCONFIG += box2d 88 | } 89 | -------------------------------------------------------------------------------- /ReadyPoster.cpp: -------------------------------------------------------------------------------- 1 | #include "ReadyPoster.h" 2 | #include "ui_ReadyPoster.h" 3 | 4 | ReadyPoster::ReadyPoster(QWidget *parent) : 5 | QDialog(parent), 6 | ui(new Ui::ReadyPoster) 7 | { 8 | ui->setupUi(this); 9 | 10 | // Убирается рамка окна 11 | this->setWindowFlags(Qt::Window | Qt::FramelessWindowHint); 12 | 13 | ui->line1Label->hide(); 14 | ui->line2Label->hide(); 15 | 16 | count=3; 17 | ui->countDownLabel->setText(QString::number(count)); 18 | 19 | connect(&updateTimer, SIGNAL(timeout()), this, SLOT(updateCountDown())); 20 | 21 | runCountDown(); 22 | } 23 | 24 | 25 | ReadyPoster::~ReadyPoster() 26 | { 27 | delete ui; 28 | } 29 | 30 | 31 | void ReadyPoster::setLine1Text(const QString iText) 32 | { 33 | ui->line1Label->setText(iText); 34 | ui->line1Label->show(); 35 | } 36 | 37 | 38 | void ReadyPoster::setLine2Text(const QString iText) 39 | { 40 | ui->line2Label->setText(iText); 41 | ui->line2Label->show(); 42 | } 43 | 44 | 45 | void ReadyPoster::runCountDown() 46 | { 47 | updateTimer.start(1000); 48 | } 49 | 50 | 51 | // Слот, срабатывающий по таймеру updateWorldTimer 52 | void ReadyPoster::updateCountDown() 53 | { 54 | count--; 55 | 56 | if(count==0) 57 | this->close(); 58 | else 59 | ui->countDownLabel->setText(QString::number(count)); 60 | } 61 | -------------------------------------------------------------------------------- /ReadyPoster.h: -------------------------------------------------------------------------------- 1 | #ifndef READYPOSTER_H 2 | #define READYPOSTER_H 3 | 4 | #include 5 | #include 6 | 7 | namespace Ui { 8 | class ReadyPoster; 9 | } 10 | 11 | class ReadyPoster : public QDialog 12 | { 13 | Q_OBJECT 14 | 15 | public: 16 | explicit ReadyPoster(QWidget *parent = 0); 17 | ~ReadyPoster(); 18 | 19 | void setLine1Text(const QString iText); 20 | void setLine2Text(const QString iText); 21 | 22 | private: 23 | Ui::ReadyPoster *ui; 24 | 25 | private slots: 26 | void updateCountDown(); 27 | 28 | protected: 29 | 30 | void runCountDown(); 31 | 32 | int count; 33 | QTimer updateTimer; 34 | }; 35 | 36 | #endif // READYPOSTER_H 37 | -------------------------------------------------------------------------------- /ReadyPoster.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | ReadyPoster 4 | 5 | 6 | 7 | 0 8 | 0 9 | 400 10 | 300 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 255 20 | 255 21 | 255 22 | 23 | 24 | 25 | 26 | 27 | 28 | 239 29 | 226 30 | 160 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 255 40 | 255 41 | 255 42 | 43 | 44 | 45 | 46 | 47 | 48 | 239 49 | 226 50 | 160 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 239 60 | 226 61 | 160 62 | 63 | 64 | 65 | 66 | 67 | 68 | 239 69 | 226 70 | 160 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | Dialog 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | DejaVu Sans Condensed 88 | 50 89 | 90 | 91 | 92 | ...READY... 93 | 94 | 95 | Qt::AlignCenter 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 16 108 | 117 109 | 249 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 16 119 | 117 120 | 249 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 190 130 | 190 131 | 190 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 32 141 | 142 | 143 | 144 | TextLabel 145 | 146 | 147 | Qt::AlignCenter 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 200 160 | 71 161 | 62 162 | 163 | 164 | 165 | 166 | 167 | 168 | 76 169 | 99 170 | 150 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 200 180 | 71 181 | 62 182 | 183 | 184 | 185 | 186 | 187 | 188 | 76 189 | 99 190 | 150 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 190 200 | 190 201 | 190 202 | 203 | 204 | 205 | 206 | 207 | 208 | 190 209 | 190 210 | 190 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 100 220 | 75 221 | true 222 | 223 | 224 | 225 | 0 226 | 227 | 228 | Qt::AlignCenter 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | -------------------------------------------------------------------------------- /RocketBit.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "main.h" 4 | #include "MathAn.h" 5 | #include "RocketBit.h" 6 | 7 | RocketBit::RocketBit(QGraphicsItem *parent) 8 | { 9 | QGraphicsItem::setParentItem(parent); 10 | 11 | width=1.1; 12 | height=0.45; 13 | color=QColor(31, 56, 94); 14 | 15 | moveDetector=nullptr; 16 | mouseJoint=nullptr; 17 | } 18 | 19 | 20 | RocketBit::~RocketBit() 21 | { 22 | 23 | } 24 | 25 | 26 | void RocketBit::setMoveDetector(MoveDetector *iMoveDetector) 27 | { 28 | moveDetector=iMoveDetector; 29 | } 30 | 31 | 32 | QRectF RocketBit::boundingRect() const 33 | { 34 | qreal halhWidth=width/2.0; 35 | qreal halhHeight=height/2.0; 36 | return QRectF(-halhWidth, -halhHeight, width, height); 37 | } 38 | 39 | 40 | QPainterPath RocketBit::shape() const 41 | { 42 | QPainterPath path; 43 | path.addRect( boundingRect() ); 44 | return path; 45 | } 46 | 47 | 48 | void RocketBit::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) 49 | { 50 | QPen pen; // Обводка 51 | 52 | // Корпус ракетки 53 | painter->setBrush(color); // Заливка 54 | pen.setWidth(0.1); 55 | pen.setBrush(Qt::white); 56 | painter->setPen(pen); 57 | painter->drawRect( boundingRect() ); 58 | 59 | 60 | // Огонь из левой дюзы 61 | painter->setBrush(QColor(150+(qrand()%(254-150)), 50, 50)); // Заливка 62 | pen.setWidth(0.2); 63 | pen.setBrush(Qt::yellow); 64 | 65 | QPolygonF polygon; // Полигон пламени 66 | qreal flameWidth=0.2; 67 | qreal flameHeight=0.25; 68 | polygon << QPointF(-width/2.0*0.8, height/2+0.1) 69 | << QPointF(-width/2.0*0.8-flameWidth/2.0, height/2+0.1+flameHeight) 70 | << QPointF(-width/2.0*0.8+flameWidth/2.0, height/2+0.1+flameHeight); 71 | painter->drawPolygon( polygon ); 72 | 73 | // Огонь из правой дюзы 74 | polygon.translate(width/2.0*0.8 * 2, 0.0); 75 | painter->drawPolygon( polygon ); 76 | } 77 | 78 | 79 | // Ракетка при каждом обращении удаляется и снова создается в физическом мире 80 | void RocketBit::putToPhysicsWorld() 81 | { 82 | static qreal previousX=5.0; 83 | static qreal previousY=5.0; 84 | 85 | // Если ракетка уже была проинициализирована, она удаляется (чтобы произошло пересоздание) 86 | if(physicsBody!=nullptr) { 87 | physicsWorld->DestroyBody(physicsBody); 88 | } 89 | 90 | // Вычисляется вектор движения ракетки, чтобы преобразовать его в вектор скорости 91 | b2Vec2 velocityVector(this->x()-previousX, this->y()-previousY); 92 | previousX=this->x(); 93 | previousY=this->y(); 94 | 95 | // Ракетка создается 96 | b2BodyDef bodyDef; 97 | bodyDef.type=b2_staticBody; // b2_dynamicBody 98 | bodyDef.position.Set(this->x(), this->y()); 99 | bodyDef.angle=MathAn::degToRad(this->rotation()); 100 | b2Body *body=physicsWorld->CreateBody(&bodyDef); 101 | 102 | b2PolygonShape polygonShape; 103 | polygonShape.SetAsBox(width/2.0, height/2.0); 104 | 105 | b2FixtureDef fixtureDef; 106 | fixtureDef.shape = &polygonShape; 107 | fixtureDef.density = 1.0; // Плотность 108 | fixtureDef.restitution=0.99; // Коэфициент отскока? 109 | 110 | body->CreateFixture(&fixtureDef); 111 | body->SetLinearVelocity(velocityVector); 112 | 113 | // Запоминается настроенное тело 114 | physicsBody=body; 115 | } 116 | 117 | 118 | /* 119 | void RocketBit::putToPhysicsWorld() 120 | { 121 | // Создается тело ракетки 122 | b2BodyDef bodyDef; 123 | bodyDef.type=b2_dynamicBody; 124 | bodyDef.position.Set(this->x(), this->y()); 125 | bodyDef.angle=MathAn::degToRad(this->rotation()); 126 | b2Body *body=physicsWorld->CreateBody(&bodyDef); 127 | 128 | b2MouseJointDef mouseJointDef; 129 | 130 | // Получение ground body в C++ в box2d 2.x.x, чтобы прикрепиться к миру 131 | b2BodyDef groundBodyDef; 132 | b2Body groundBody = CreateBody(&groundBodyDef); 133 | 134 | mouseJointDef.bodyA=groundBody; 135 | } 136 | 137 | } 138 | */ 139 | 140 | 141 | void RocketBit::moveToDefaultPos() 142 | { 143 | this->setX(MOVE_NOID_START_ROCKET_BIT_POS_X); 144 | this->setY(MOVE_NOID_START_ROCKET_BIT_POS_Y); 145 | 146 | putToPhysicsWorld(); 147 | } 148 | 149 | 150 | void RocketBit::updatePosByMovieDetector() 151 | { 152 | // moveDetector.update(); // Теперь этот вызов ненужен, потому что moveDetector работает в треде 153 | 154 | // В дальнейших вычислениях принимается, что MoveDetector выдает координаты в виде игровых координат x=0...10, y=0...10 155 | // пропорционально растягивая прямоугольную картинку камеры до квадрата 156 | 157 | // Местоположение оси в игре, на которой должна отображаться горизонтальная центральная ось картинки с камеры 158 | qreal yOpticAxis=MOVE_NOID_FIELD_HEIGHT*3.0/4.0; // В координатах сцены 159 | 160 | qreal yKoeff=1.5; // Коэффициент сплющивания координат по вертикали 161 | 162 | qreal yShift=MOVE_NOID_FIELD_HEIGHT-(MOVE_NOID_FIELD_HEIGHT*yKoeff)/2.0; // Зазор от верхнего края экрана до сплющенного пространства 163 | yShift=yShift+(MOVE_NOID_FIELD_HEIGHT-yOpticAxis); // Зазор увеличиватся в соответствии с местоположением оси картинки с камеры 164 | 165 | // Местоположение ракетки 166 | QPointF pos=moveDetector->getRocketBitPos(); 167 | 168 | // Координаты, полученные с камеры, сплющиваются по вертикали и сдвигаются до оси в игре 169 | qreal yPos=pos.y()/yKoeff+yShift; 170 | 171 | // Ракетка не должна подниматься выше середины поля 172 | if(yPos<(MOVE_NOID_FIELD_HEIGHT/2.0)) 173 | yPos=MOVE_NOID_FIELD_HEIGHT/2.0; 174 | 175 | // Устанавливаются координаты ракетки 176 | this->setPos( QPointF(pos.x(), yPos) ); 177 | 178 | // Поворот ракетки 179 | this->setRotation( moveDetector->getRocketBitAngle() ); 180 | 181 | putToPhysicsWorld(); // Чтобы ракетка пересоздавалась в новом месте 182 | 183 | // qDebug() << "RocketBit coordinats 1: " << moveDetector.getRocketBitPos(); 184 | // qDebug() << "RocketBit coordinats 2: " << this->x() << this->y(); 185 | } 186 | -------------------------------------------------------------------------------- /RocketBit.h: -------------------------------------------------------------------------------- 1 | #ifndef ROCKETBIT_H 2 | #define ROCKETBIT_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "opencv2/opencv.hpp" 10 | 11 | #include "GraphicsPhysicsItem.h" 12 | #include "MoveDetector.h" 13 | 14 | class RocketBit : public GraphicsPhysicsItem 15 | { 16 | public: 17 | RocketBit(QGraphicsItem *parent = Q_NULLPTR); 18 | virtual ~RocketBit(); 19 | 20 | QRectF boundingRect() const override; 21 | QPainterPath shape() const override; 22 | void paint(QPainter *painter, 23 | const QStyleOptionGraphicsItem *option, 24 | QWidget *widget) override; 25 | 26 | void putToPhysicsWorld(); 27 | 28 | void updatePosByMovieDetector(); 29 | void moveToDefaultPos(); 30 | 31 | void setMoveDetector(MoveDetector *iMoveDetector); 32 | 33 | protected: 34 | 35 | qreal width; 36 | qreal height; 37 | QColor color; 38 | 39 | MoveDetector *moveDetector; 40 | 41 | b2MouseJoint *mouseJoint; 42 | }; 43 | 44 | #endif // ROCKETBIT_H 45 | -------------------------------------------------------------------------------- /SettingsWindow.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "SettingsWindow.h" 11 | #include "ui_SettingsWindow.h" 12 | #include "AppConfig.h" 13 | #include "main.h" 14 | 15 | extern AppConfig appConfig; 16 | 17 | SettingsWindow::SettingsWindow(QWidget *parent) : 18 | QDialog(parent), 19 | ui(new Ui::SettingsWindow) 20 | { 21 | ui->setupUi(this); 22 | 23 | init(); 24 | 25 | // Таймер обновления картинки 26 | timer = new QTimer(); 27 | connect(timer, SIGNAL(timeout()), this, SLOT(onTimerFinish())); 28 | timer->start(50); 29 | } 30 | 31 | SettingsWindow::~SettingsWindow() 32 | { 33 | delete ui; 34 | delete timer; 35 | } 36 | 37 | 38 | void SettingsWindow::init(void) 39 | { 40 | // На экране устанавливается значение файла устройства захвата, считанное из конфига 41 | ui->captureDeviceFileName->setText(appConfig.getParameter("captureDeviceFileName")); 42 | 43 | // На экране устанавливается положение движка границы отсечения яркости 44 | ui->brigthnessThresholdSlider->setMinimum(0); 45 | ui->brigthnessThresholdSlider->setMaximum(254); 46 | ui->brigthnessThresholdSlider->setSliderPosition(appConfig.getParameter("brigthnessThreshold").toInt()); 47 | 48 | // Инициализируется устройство захвата изображения 49 | captureDevice.init( ui->captureDeviceFileName->text() ); 50 | captureDevice.setBrigthnessThreshold( ui->brigthnessThresholdSlider->sliderPosition() ); 51 | 52 | updateCaptureImage(); // Устанавливается первая картинка с камеры 53 | } 54 | 55 | 56 | QImage SettingsWindow::getCurrentImage(void) 57 | { 58 | cv::Mat* currentBwFrame=captureDevice.getBwFrame(); 59 | QImage img((uchar*)currentBwFrame->data, currentBwFrame->cols, currentBwFrame->rows, currentBwFrame->step, QImage::Format_Grayscale8); 60 | return img; 61 | } 62 | 63 | 64 | void SettingsWindow::updateCaptureImage(void) 65 | { 66 | int enabledWidth=ui->mainVerticalLayout->sizeHint().width(); 67 | ui->graphicsPixmapLabel->setPixmap(QPixmap::fromImage( getCurrentImage() ).scaledToWidth(enabledWidth, Qt::FastTransformation)); 68 | } 69 | 70 | 71 | // Слот, срабатывающий по таймеру 72 | void SettingsWindow::onTimerFinish(void) 73 | { 74 | updateCaptureImage(); 75 | } 76 | 77 | 78 | // Клик по кнопке Reset to default 79 | void SettingsWindow::onCaptureDeviceDefaultButtonClicked() 80 | { 81 | QString deviceFileName="default"; 82 | 83 | ui->captureDeviceFileName->setText(deviceFileName); 84 | appConfig.setParameter("captureDeviceFileName", deviceFileName); 85 | 86 | timer->stop(); 87 | captureDevice.init( deviceFileName ); 88 | timer->start(); 89 | } 90 | 91 | 92 | // Клик по кнопке Apply 93 | void SettingsWindow::onCaptureDeviceApplyButtonClicked() 94 | { 95 | QString deviceFileName=ui->captureDeviceFileName->text(); 96 | appConfig.setParameter("captureDeviceFileName", deviceFileName); 97 | 98 | timer->stop(); 99 | captureDevice.init( deviceFileName ); 100 | timer->start(); 101 | } 102 | 103 | 104 | void SettingsWindow::onBrigthnessThresholdSliderMoved(const int position) 105 | { 106 | qDebug() << "In onBrigthnessThresholdSliderMoved slot"; 107 | 108 | captureDevice.setBrigthnessThreshold( position ); 109 | 110 | QString n=QString::number( position ); 111 | appConfig.setParameter("brigthnessThreshold", n); 112 | } 113 | -------------------------------------------------------------------------------- /SettingsWindow.h: -------------------------------------------------------------------------------- 1 | #ifndef SETTINGSWINDOW_H 2 | #define SETTINGSWINDOW_H 3 | 4 | #include 5 | #include 6 | 7 | #include "CaptureDevice.h" 8 | #include "main.h" 9 | 10 | 11 | namespace Ui { 12 | class SettingsWindow; 13 | } 14 | 15 | class SettingsWindow : public QDialog 16 | { 17 | Q_OBJECT 18 | 19 | public: 20 | explicit SettingsWindow(QWidget *parent = 0); 21 | ~SettingsWindow(); 22 | 23 | private: 24 | Ui::SettingsWindow *ui; 25 | void init(void); 26 | QImage getCurrentImage(void); 27 | void updateCaptureImage(void); 28 | 29 | protected: 30 | 31 | CaptureDevice captureDevice; 32 | QTimer *timer; 33 | 34 | private slots: 35 | void onCaptureDeviceDefaultButtonClicked(); 36 | void onCaptureDeviceApplyButtonClicked(); 37 | void onTimerFinish(); 38 | void onBrigthnessThresholdSliderMoved(const int position); 39 | }; 40 | 41 | #endif // SETTINGSWINDOW_H 42 | -------------------------------------------------------------------------------- /SettingsWindow.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | SettingsWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 527 10 | 499 11 | 12 | 13 | 14 | 15 | 0 16 | 0 17 | 18 | 19 | 20 | Settings 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | Capture device file 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | Apply 49 | 50 | 51 | 52 | 53 | 54 | 55 | Reset to default 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | Markers threshold 67 | 68 | 69 | 70 | 71 | 72 | 73 | Qt::Horizontal 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | Qt::Horizontal 85 | 86 | 87 | 88 | 40 89 | 20 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 0 101 | 0 102 | 103 | 104 | 105 | VideoCapture 106 | 107 | 108 | Qt::AlignCenter 109 | 110 | 111 | 112 | 113 | 114 | 115 | Qt::Horizontal 116 | 117 | 118 | QDialogButtonBox::Close 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | buttonBox 130 | rejected() 131 | SettingsWindow 132 | reject() 133 | 134 | 135 | 262 136 | 477 137 | 138 | 139 | 262 140 | 249 141 | 142 | 143 | 144 | 145 | captureDeviceDefaultButton 146 | clicked() 147 | SettingsWindow 148 | onCaptureDeviceDefaultButtonClicked() 149 | 150 | 151 | 453 152 | 26 153 | 154 | 155 | 520 156 | 31 157 | 158 | 159 | 160 | 161 | captureDeviceApplyButton 162 | clicked() 163 | SettingsWindow 164 | onCaptureDeviceApplyButtonClicked() 165 | 166 | 167 | 318 168 | 23 169 | 170 | 171 | 487 172 | 5 173 | 174 | 175 | 176 | 177 | brigthnessThresholdSlider 178 | sliderMoved(int) 179 | SettingsWindow 180 | onBrigthnessThresholdSliderMoved(int) 181 | 182 | 183 | 367 184 | 51 185 | 186 | 187 | 479 188 | 47 189 | 190 | 191 | 192 | 193 | 194 | on_buttonBox_rejected() 195 | onCaptureDeviceDefaultButtonClicked() 196 | onCaptureDeviceApplyButtonClicked() 197 | onBrigthnessThresholdSliderMoved(int) 198 | 199 | 200 | -------------------------------------------------------------------------------- /bin/pic/GameView.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xintrea/movenoid/d924794bc2fb7cebf764ee6432cf9626a8fdd3bd/bin/pic/GameView.png -------------------------------------------------------------------------------- /license/licenseBSD.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017, Sergey M. Stepanov 2 | Russia, Volgodonsk city, Pionerskaya st. 103/26. 3 | All rights reserved. 4 | 5 | 6 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that 7 | the following conditions are met: 8 | 9 | 1) Redistributions of source code must retain the above copyright notice, this list of conditions and 10 | the following disclaimer. 11 | 12 | 2) Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 13 | the following disclaimer in the documentation and/or other materials provided with the distribution. 14 | 15 | 3) Neither the names of its contributors may be used to endorse 16 | or promote products derived from this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS 19 | OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 20 | FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 21 | OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 22 | OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 24 | OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /license/licenseGPL.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, Sergey M. Stepanov 2 | Russia, Volgodonsk city, Pionerskaya st. 103/26. 3 | All rights reserved. 4 | 5 | 6 | GNU GENERAL PUBLIC LICENSE 7 | Version 3, 29 June 2007 8 | 9 | Copyright (C) 2007 Free Software Foundation, Inc. 10 | Everyone is permitted to copy and distribute verbatim copies 11 | of this license document, but changing it is not allowed. 12 | 13 | Preamble 14 | 15 | The GNU General Public License is a free, copyleft license for 16 | software and other kinds of works. 17 | 18 | The licenses for most software and other practical works are designed 19 | to take away your freedom to share and change the works. By contrast, 20 | the GNU General Public License is intended to guarantee your freedom to 21 | share and change all versions of a program--to make sure it remains free 22 | software for all its users. We, the Free Software Foundation, use the 23 | GNU General Public License for most of our software; it applies also to 24 | any other work released this way by its authors. You can apply it to 25 | your programs, too. 26 | 27 | When we speak of free software, we are referring to freedom, not 28 | price. Our General Public Licenses are designed to make sure that you 29 | have the freedom to distribute copies of free software (and charge for 30 | them if you wish), that you receive source code or can get it if you 31 | want it, that you can change the software or use pieces of it in new 32 | free programs, and that you know you can do these things. 33 | 34 | To protect your rights, we need to prevent others from denying you 35 | these rights or asking you to surrender the rights. Therefore, you have 36 | certain responsibilities if you distribute copies of the software, or if 37 | you modify it: responsibilities to respect the freedom of others. 38 | 39 | For example, if you distribute copies of such a program, whether 40 | gratis or for a fee, you must pass on to the recipients the same 41 | freedoms that you received. You must make sure that they, too, receive 42 | or can get the source code. And you must show them these terms so they 43 | know their rights. 44 | 45 | Developers that use the GNU GPL protect your rights with two steps: 46 | (1) assert copyright on the software, and (2) offer you this License 47 | giving you legal permission to copy, distribute and/or modify it. 48 | 49 | For the developers' and authors' protection, the GPL clearly explains 50 | that there is no warranty for this free software. For both users' and 51 | authors' sake, the GPL requires that modified versions be marked as 52 | changed, so that their problems will not be attributed erroneously to 53 | authors of previous versions. 54 | 55 | Some devices are designed to deny users access to install or run 56 | modified versions of the software inside them, although the manufacturer 57 | can do so. This is fundamentally incompatible with the aim of 58 | protecting users' freedom to change the software. The systematic 59 | pattern of such abuse occurs in the area of products for individuals to 60 | use, which is precisely where it is most unacceptable. Therefore, we 61 | have designed this version of the GPL to prohibit the practice for those 62 | products. If such problems arise substantially in other domains, we 63 | stand ready to extend this provision to those domains in future versions 64 | of the GPL, as needed to protect the freedom of users. 65 | 66 | Finally, every program is threatened constantly by software patents. 67 | States should not allow patents to restrict development and use of 68 | software on general-purpose computers, but in those that do, we wish to 69 | avoid the special danger that patents applied to a free program could 70 | make it effectively proprietary. To prevent this, the GPL assures that 71 | patents cannot be used to render the program non-free. 72 | 73 | The precise terms and conditions for copying, distribution and 74 | modification follow. 75 | 76 | TERMS AND CONDITIONS 77 | 78 | 0. Definitions. 79 | 80 | "This License" refers to version 3 of the GNU General Public License. 81 | 82 | "Copyright" also means copyright-like laws that apply to other kinds of 83 | works, such as semiconductor masks. 84 | 85 | "The Program" refers to any copyrightable work licensed under this 86 | License. Each licensee is addressed as "you". "Licensees" and 87 | "recipients" may be individuals or organizations. 88 | 89 | To "modify" a work means to copy from or adapt all or part of the work 90 | in a fashion requiring copyright permission, other than the making of an 91 | exact copy. The resulting work is called a "modified version" of the 92 | earlier work or a work "based on" the earlier work. 93 | 94 | A "covered work" means either the unmodified Program or a work based 95 | on the Program. 96 | 97 | To "propagate" a work means to do anything with it that, without 98 | permission, would make you directly or secondarily liable for 99 | infringement under applicable copyright law, except executing it on a 100 | computer or modifying a private copy. Propagation includes copying, 101 | distribution (with or without modification), making available to the 102 | public, and in some countries other activities as well. 103 | 104 | To "convey" a work means any kind of propagation that enables other 105 | parties to make or receive copies. Mere interaction with a user through 106 | a computer network, with no transfer of a copy, is not conveying. 107 | 108 | An interactive user interface displays "Appropriate Legal Notices" 109 | to the extent that it includes a convenient and prominently visible 110 | feature that (1) displays an appropriate copyright notice, and (2) 111 | tells the user that there is no warranty for the work (except to the 112 | extent that warranties are provided), that licensees may convey the 113 | work under this License, and how to view a copy of this License. If 114 | the interface presents a list of user commands or options, such as a 115 | menu, a prominent item in the list meets this criterion. 116 | 117 | 1. Source Code. 118 | 119 | The "source code" for a work means the preferred form of the work 120 | for making modifications to it. "Object code" means any non-source 121 | form of a work. 122 | 123 | A "Standard Interface" means an interface that either is an official 124 | standard defined by a recognized standards body, or, in the case of 125 | interfaces specified for a particular programming language, one that 126 | is widely used among developers working in that language. 127 | 128 | The "System Libraries" of an executable work include anything, other 129 | than the work as a whole, that (a) is included in the normal form of 130 | packaging a Major Component, but which is not part of that Major 131 | Component, and (b) serves only to enable use of the work with that 132 | Major Component, or to implement a Standard Interface for which an 133 | implementation is available to the public in source code form. A 134 | "Major Component", in this context, means a major essential component 135 | (kernel, window system, and so on) of the specific operating system 136 | (if any) on which the executable work runs, or a compiler used to 137 | produce the work, or an object code interpreter used to run it. 138 | 139 | The "Corresponding Source" for a work in object code form means all 140 | the source code needed to generate, install, and (for an executable 141 | work) run the object code and to modify the work, including scripts to 142 | control those activities. However, it does not include the work's 143 | System Libraries, or general-purpose tools or generally available free 144 | programs which are used unmodified in performing those activities but 145 | which are not part of the work. For example, Corresponding Source 146 | includes interface definition files associated with source files for 147 | the work, and the source code for shared libraries and dynamically 148 | linked subprograms that the work is specifically designed to require, 149 | such as by intimate data communication or control flow between those 150 | subprograms and other parts of the work. 151 | 152 | The Corresponding Source need not include anything that users 153 | can regenerate automatically from other parts of the Corresponding 154 | Source. 155 | 156 | The Corresponding Source for a work in source code form is that 157 | same work. 158 | 159 | 2. Basic Permissions. 160 | 161 | All rights granted under this License are granted for the term of 162 | copyright on the Program, and are irrevocable provided the stated 163 | conditions are met. This License explicitly affirms your unlimited 164 | permission to run the unmodified Program. The output from running a 165 | covered work is covered by this License only if the output, given its 166 | content, constitutes a covered work. This License acknowledges your 167 | rights of fair use or other equivalent, as provided by copyright law. 168 | 169 | You may make, run and propagate covered works that you do not 170 | convey, without conditions so long as your license otherwise remains 171 | in force. You may convey covered works to others for the sole purpose 172 | of having them make modifications exclusively for you, or provide you 173 | with facilities for running those works, provided that you comply with 174 | the terms of this License in conveying all material for which you do 175 | not control copyright. Those thus making or running the covered works 176 | for you must do so exclusively on your behalf, under your direction 177 | and control, on terms that prohibit them from making any copies of 178 | your copyrighted material outside their relationship with you. 179 | 180 | Conveying under any other circumstances is permitted solely under 181 | the conditions stated below. Sublicensing is not allowed; section 10 182 | makes it unnecessary. 183 | 184 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 185 | 186 | No covered work shall be deemed part of an effective technological 187 | measure under any applicable law fulfilling obligations under article 188 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 189 | similar laws prohibiting or restricting circumvention of such 190 | measures. 191 | 192 | When you convey a covered work, you waive any legal power to forbid 193 | circumvention of technological measures to the extent such circumvention 194 | is effected by exercising rights under this License with respect to 195 | the covered work, and you disclaim any intention to limit operation or 196 | modification of the work as a means of enforcing, against the work's 197 | users, your or third parties' legal rights to forbid circumvention of 198 | technological measures. 199 | 200 | 4. Conveying Verbatim Copies. 201 | 202 | You may convey verbatim copies of the Program's source code as you 203 | receive it, in any medium, provided that you conspicuously and 204 | appropriately publish on each copy an appropriate copyright notice; 205 | keep intact all notices stating that this License and any 206 | non-permissive terms added in accord with section 7 apply to the code; 207 | keep intact all notices of the absence of any warranty; and give all 208 | recipients a copy of this License along with the Program. 209 | 210 | You may charge any price or no price for each copy that you convey, 211 | and you may offer support or warranty protection for a fee. 212 | 213 | 5. Conveying Modified Source Versions. 214 | 215 | You may convey a work based on the Program, or the modifications to 216 | produce it from the Program, in the form of source code under the 217 | terms of section 4, provided that you also meet all of these conditions: 218 | 219 | a) The work must carry prominent notices stating that you modified 220 | it, and giving a relevant date. 221 | 222 | b) The work must carry prominent notices stating that it is 223 | released under this License and any conditions added under section 224 | 7. This requirement modifies the requirement in section 4 to 225 | "keep intact all notices". 226 | 227 | c) You must license the entire work, as a whole, under this 228 | License to anyone who comes into possession of a copy. This 229 | License will therefore apply, along with any applicable section 7 230 | additional terms, to the whole of the work, and all its parts, 231 | regardless of how they are packaged. This License gives no 232 | permission to license the work in any other way, but it does not 233 | invalidate such permission if you have separately received it. 234 | 235 | d) If the work has interactive user interfaces, each must display 236 | Appropriate Legal Notices; however, if the Program has interactive 237 | interfaces that do not display Appropriate Legal Notices, your 238 | work need not make them do so. 239 | 240 | A compilation of a covered work with other separate and independent 241 | works, which are not by their nature extensions of the covered work, 242 | and which are not combined with it such as to form a larger program, 243 | in or on a volume of a storage or distribution medium, is called an 244 | "aggregate" if the compilation and its resulting copyright are not 245 | used to limit the access or legal rights of the compilation's users 246 | beyond what the individual works permit. Inclusion of a covered work 247 | in an aggregate does not cause this License to apply to the other 248 | parts of the aggregate. 249 | 250 | 6. Conveying Non-Source Forms. 251 | 252 | You may convey a covered work in object code form under the terms 253 | of sections 4 and 5, provided that you also convey the 254 | machine-readable Corresponding Source under the terms of this License, 255 | in one of these ways: 256 | 257 | a) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by the 259 | Corresponding Source fixed on a durable physical medium 260 | customarily used for software interchange. 261 | 262 | b) Convey the object code in, or embodied in, a physical product 263 | (including a physical distribution medium), accompanied by a 264 | written offer, valid for at least three years and valid for as 265 | long as you offer spare parts or customer support for that product 266 | model, to give anyone who possesses the object code either (1) a 267 | copy of the Corresponding Source for all the software in the 268 | product that is covered by this License, on a durable physical 269 | medium customarily used for software interchange, for a price no 270 | more than your reasonable cost of physically performing this 271 | conveying of source, or (2) access to copy the 272 | Corresponding Source from a network server at no charge. 273 | 274 | c) Convey individual copies of the object code with a copy of the 275 | written offer to provide the Corresponding Source. This 276 | alternative is allowed only occasionally and noncommercially, and 277 | only if you received the object code with such an offer, in accord 278 | with subsection 6b. 279 | 280 | d) Convey the object code by offering access from a designated 281 | place (gratis or for a charge), and offer equivalent access to the 282 | Corresponding Source in the same way through the same place at no 283 | further charge. You need not require recipients to copy the 284 | Corresponding Source along with the object code. If the place to 285 | copy the object code is a network server, the Corresponding Source 286 | may be on a different server (operated by you or a third party) 287 | that supports equivalent copying facilities, provided you maintain 288 | clear directions next to the object code saying where to find the 289 | Corresponding Source. Regardless of what server hosts the 290 | Corresponding Source, you remain obligated to ensure that it is 291 | available for as long as needed to satisfy these requirements. 292 | 293 | e) Convey the object code using peer-to-peer transmission, provided 294 | you inform other peers where the object code and Corresponding 295 | Source of the work are being offered to the general public at no 296 | charge under subsection 6d. 297 | 298 | A separable portion of the object code, whose source code is excluded 299 | from the Corresponding Source as a System Library, need not be 300 | included in conveying the object code work. 301 | 302 | A "User Product" is either (1) a "consumer product", which means any 303 | tangible personal property which is normally used for personal, family, 304 | or household purposes, or (2) anything designed or sold for incorporation 305 | into a dwelling. In determining whether a product is a consumer product, 306 | doubtful cases shall be resolved in favor of coverage. For a particular 307 | product received by a particular user, "normally used" refers to a 308 | typical or common use of that class of product, regardless of the status 309 | of the particular user or of the way in which the particular user 310 | actually uses, or expects or is expected to use, the product. A product 311 | is a consumer product regardless of whether the product has substantial 312 | commercial, industrial or non-consumer uses, unless such uses represent 313 | the only significant mode of use of the product. 314 | 315 | "Installation Information" for a User Product means any methods, 316 | procedures, authorization keys, or other information required to install 317 | and execute modified versions of a covered work in that User Product from 318 | a modified version of its Corresponding Source. The information must 319 | suffice to ensure that the continued functioning of the modified object 320 | code is in no case prevented or interfered with solely because 321 | modification has been made. 322 | 323 | If you convey an object code work under this section in, or with, or 324 | specifically for use in, a User Product, and the conveying occurs as 325 | part of a transaction in which the right of possession and use of the 326 | User Product is transferred to the recipient in perpetuity or for a 327 | fixed term (regardless of how the transaction is characterized), the 328 | Corresponding Source conveyed under this section must be accompanied 329 | by the Installation Information. But this requirement does not apply 330 | if neither you nor any third party retains the ability to install 331 | modified object code on the User Product (for example, the work has 332 | been installed in ROM). 333 | 334 | The requirement to provide Installation Information does not include a 335 | requirement to continue to provide support service, warranty, or updates 336 | for a work that has been modified or installed by the recipient, or for 337 | the User Product in which it has been modified or installed. Access to a 338 | network may be denied when the modification itself materially and 339 | adversely affects the operation of the network or violates the rules and 340 | protocols for communication across the network. 341 | 342 | Corresponding Source conveyed, and Installation Information provided, 343 | in accord with this section must be in a format that is publicly 344 | documented (and with an implementation available to the public in 345 | source code form), and must require no special password or key for 346 | unpacking, reading or copying. 347 | 348 | 7. Additional Terms. 349 | 350 | "Additional permissions" are terms that supplement the terms of this 351 | License by making exceptions from one or more of its conditions. 352 | Additional permissions that are applicable to the entire Program shall 353 | be treated as though they were included in this License, to the extent 354 | that they are valid under applicable law. If additional permissions 355 | apply only to part of the Program, that part may be used separately 356 | under those permissions, but the entire Program remains governed by 357 | this License without regard to the additional permissions. 358 | 359 | When you convey a copy of a covered work, you may at your option 360 | remove any additional permissions from that copy, or from any part of 361 | it. (Additional permissions may be written to require their own 362 | removal in certain cases when you modify the work.) You may place 363 | additional permissions on material, added by you to a covered work, 364 | for which you have or can give appropriate copyright permission. 365 | 366 | Notwithstanding any other provision of this License, for material you 367 | add to a covered work, you may (if authorized by the copyright holders of 368 | that material) supplement the terms of this License with terms: 369 | 370 | a) Disclaiming warranty or limiting liability differently from the 371 | terms of sections 15 and 16 of this License; or 372 | 373 | b) Requiring preservation of specified reasonable legal notices or 374 | author attributions in that material or in the Appropriate Legal 375 | Notices displayed by works containing it; or 376 | 377 | c) Prohibiting misrepresentation of the origin of that material, or 378 | requiring that modified versions of such material be marked in 379 | reasonable ways as different from the original version; or 380 | 381 | d) Limiting the use for publicity purposes of names of licensors or 382 | authors of the material; or 383 | 384 | e) Declining to grant rights under trademark law for use of some 385 | trade names, trademarks, or service marks; or 386 | 387 | f) Requiring indemnification of licensors and authors of that 388 | material by anyone who conveys the material (or modified versions of 389 | it) with contractual assumptions of liability to the recipient, for 390 | any liability that these contractual assumptions directly impose on 391 | those licensors and authors. 392 | 393 | All other non-permissive additional terms are considered "further 394 | restrictions" within the meaning of section 10. If the Program as you 395 | received it, or any part of it, contains a notice stating that it is 396 | governed by this License along with a term that is a further 397 | restriction, you may remove that term. If a license document contains 398 | a further restriction but permits relicensing or conveying under this 399 | License, you may add to a covered work material governed by the terms 400 | of that license document, provided that the further restriction does 401 | not survive such relicensing or conveying. 402 | 403 | If you add terms to a covered work in accord with this section, you 404 | must place, in the relevant source files, a statement of the 405 | additional terms that apply to those files, or a notice indicating 406 | where to find the applicable terms. 407 | 408 | Additional terms, permissive or non-permissive, may be stated in the 409 | form of a separately written license, or stated as exceptions; 410 | the above requirements apply either way. 411 | 412 | 8. Termination. 413 | 414 | You may not propagate or modify a covered work except as expressly 415 | provided under this License. Any attempt otherwise to propagate or 416 | modify it is void, and will automatically terminate your rights under 417 | this License (including any patent licenses granted under the third 418 | paragraph of section 11). 419 | 420 | However, if you cease all violation of this License, then your 421 | license from a particular copyright holder is reinstated (a) 422 | provisionally, unless and until the copyright holder explicitly and 423 | finally terminates your license, and (b) permanently, if the copyright 424 | holder fails to notify you of the violation by some reasonable means 425 | prior to 60 days after the cessation. 426 | 427 | Moreover, your license from a particular copyright holder is 428 | reinstated permanently if the copyright holder notifies you of the 429 | violation by some reasonable means, this is the first time you have 430 | received notice of violation of this License (for any work) from that 431 | copyright holder, and you cure the violation prior to 30 days after 432 | your receipt of the notice. 433 | 434 | Termination of your rights under this section does not terminate the 435 | licenses of parties who have received copies or rights from you under 436 | this License. If your rights have been terminated and not permanently 437 | reinstated, you do not qualify to receive new licenses for the same 438 | material under section 10. 439 | 440 | 9. Acceptance Not Required for Having Copies. 441 | 442 | You are not required to accept this License in order to receive or 443 | run a copy of the Program. Ancillary propagation of a covered work 444 | occurring solely as a consequence of using peer-to-peer transmission 445 | to receive a copy likewise does not require acceptance. However, 446 | nothing other than this License grants you permission to propagate or 447 | modify any covered work. These actions infringe copyright if you do 448 | not accept this License. Therefore, by modifying or propagating a 449 | covered work, you indicate your acceptance of this License to do so. 450 | 451 | 10. Automatic Licensing of Downstream Recipients. 452 | 453 | Each time you convey a covered work, the recipient automatically 454 | receives a license from the original licensors, to run, modify and 455 | propagate that work, subject to this License. You are not responsible 456 | for enforcing compliance by third parties with this License. 457 | 458 | An "entity transaction" is a transaction transferring control of an 459 | organization, or substantially all assets of one, or subdividing an 460 | organization, or merging organizations. If propagation of a covered 461 | work results from an entity transaction, each party to that 462 | transaction who receives a copy of the work also receives whatever 463 | licenses to the work the party's predecessor in interest had or could 464 | give under the previous paragraph, plus a right to possession of the 465 | Corresponding Source of the work from the predecessor in interest, if 466 | the predecessor has it or can get it with reasonable efforts. 467 | 468 | You may not impose any further restrictions on the exercise of the 469 | rights granted or affirmed under this License. For example, you may 470 | not impose a license fee, royalty, or other charge for exercise of 471 | rights granted under this License, and you may not initiate litigation 472 | (including a cross-claim or counterclaim in a lawsuit) alleging that 473 | any patent claim is infringed by making, using, selling, offering for 474 | sale, or importing the Program or any portion of it. 475 | 476 | 11. Patents. 477 | 478 | A "contributor" is a copyright holder who authorizes use under this 479 | License of the Program or a work on which the Program is based. The 480 | work thus licensed is called the contributor's "contributor version". 481 | 482 | A contributor's "essential patent claims" are all patent claims 483 | owned or controlled by the contributor, whether already acquired or 484 | hereafter acquired, that would be infringed by some manner, permitted 485 | by this License, of making, using, or selling its contributor version, 486 | but do not include claims that would be infringed only as a 487 | consequence of further modification of the contributor version. For 488 | purposes of this definition, "control" includes the right to grant 489 | patent sublicenses in a manner consistent with the requirements of 490 | this License. 491 | 492 | Each contributor grants you a non-exclusive, worldwide, royalty-free 493 | patent license under the contributor's essential patent claims, to 494 | make, use, sell, offer for sale, import and otherwise run, modify and 495 | propagate the contents of its contributor version. 496 | 497 | In the following three paragraphs, a "patent license" is any express 498 | agreement or commitment, however denominated, not to enforce a patent 499 | (such as an express permission to practice a patent or covenant not to 500 | sue for patent infringement). To "grant" such a patent license to a 501 | party means to make such an agreement or commitment not to enforce a 502 | patent against the party. 503 | 504 | If you convey a covered work, knowingly relying on a patent license, 505 | and the Corresponding Source of the work is not available for anyone 506 | to copy, free of charge and under the terms of this License, through a 507 | publicly available network server or other readily accessible means, 508 | then you must either (1) cause the Corresponding Source to be so 509 | available, or (2) arrange to deprive yourself of the benefit of the 510 | patent license for this particular work, or (3) arrange, in a manner 511 | consistent with the requirements of this License, to extend the patent 512 | license to downstream recipients. "Knowingly relying" means you have 513 | actual knowledge that, but for the patent license, your conveying the 514 | covered work in a country, or your recipient's use of the covered work 515 | in a country, would infringe one or more identifiable patents in that 516 | country that you have reason to believe are valid. 517 | 518 | If, pursuant to or in connection with a single transaction or 519 | arrangement, you convey, or propagate by procuring conveyance of, a 520 | covered work, and grant a patent license to some of the parties 521 | receiving the covered work authorizing them to use, propagate, modify 522 | or convey a specific copy of the covered work, then the patent license 523 | you grant is automatically extended to all recipients of the covered 524 | work and works based on it. 525 | 526 | A patent license is "discriminatory" if it does not include within 527 | the scope of its coverage, prohibits the exercise of, or is 528 | conditioned on the non-exercise of one or more of the rights that are 529 | specifically granted under this License. You may not convey a covered 530 | work if you are a party to an arrangement with a third party that is 531 | in the business of distributing software, under which you make payment 532 | to the third party based on the extent of your activity of conveying 533 | the work, and under which the third party grants, to any of the 534 | parties who would receive the covered work from you, a discriminatory 535 | patent license (a) in connection with copies of the covered work 536 | conveyed by you (or copies made from those copies), or (b) primarily 537 | for and in connection with specific products or compilations that 538 | contain the covered work, unless you entered into that arrangement, 539 | or that patent license was granted, prior to 28 March 2007. 540 | 541 | Nothing in this License shall be construed as excluding or limiting 542 | any implied license or other defenses to infringement that may 543 | otherwise be available to you under applicable patent law. 544 | 545 | 12. No Surrender of Others' Freedom. 546 | 547 | If conditions are imposed on you (whether by court order, agreement or 548 | otherwise) that contradict the conditions of this License, they do not 549 | excuse you from the conditions of this License. If you cannot convey a 550 | covered work so as to satisfy simultaneously your obligations under this 551 | License and any other pertinent obligations, then as a consequence you may 552 | not convey it at all. For example, if you agree to terms that obligate you 553 | to collect a royalty for further conveying from those to whom you convey 554 | the Program, the only way you could satisfy both those terms and this 555 | License would be to refrain entirely from conveying the Program. 556 | 557 | 13. Use with the GNU Affero General Public License. 558 | 559 | Notwithstanding any other provision of this License, you have 560 | permission to link or combine any covered work with a work licensed 561 | under version 3 of the GNU Affero General Public License into a single 562 | combined work, and to convey the resulting work. The terms of this 563 | License will continue to apply to the part which is the covered work, 564 | but the special requirements of the GNU Affero General Public License, 565 | section 13, concerning interaction through a network will apply to the 566 | combination as such. 567 | 568 | 14. Revised Versions of this License. 569 | 570 | The Free Software Foundation may publish revised and/or new versions of 571 | the GNU General Public License from time to time. Such new versions will 572 | be similar in spirit to the present version, but may differ in detail to 573 | address new problems or concerns. 574 | 575 | Each version is given a distinguishing version number. If the 576 | Program specifies that a certain numbered version of the GNU General 577 | Public License "or any later version" applies to it, you have the 578 | option of following the terms and conditions either of that numbered 579 | version or of any later version published by the Free Software 580 | Foundation. If the Program does not specify a version number of the 581 | GNU General Public License, you may choose any version ever published 582 | by the Free Software Foundation. 583 | 584 | If the Program specifies that a proxy can decide which future 585 | versions of the GNU General Public License can be used, that proxy's 586 | public statement of acceptance of a version permanently authorizes you 587 | to choose that version for the Program. 588 | 589 | Later license versions may give you additional or different 590 | permissions. However, no additional obligations are imposed on any 591 | author or copyright holder as a result of your choosing to follow a 592 | later version. 593 | 594 | 15. Disclaimer of Warranty. 595 | 596 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 597 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 598 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 599 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 600 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 601 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 602 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 603 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 604 | 605 | 16. Limitation of Liability. 606 | 607 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 608 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 609 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 610 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 611 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 612 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 613 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 614 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 615 | SUCH DAMAGES. 616 | 617 | 17. Interpretation of Sections 15 and 16. 618 | 619 | If the disclaimer of warranty and limitation of liability provided 620 | above cannot be given local legal effect according to their terms, 621 | reviewing courts shall apply local law that most closely approximates 622 | an absolute waiver of all civil liability in connection with the 623 | Program, unless a warranty or assumption of liability accompanies a 624 | copy of the Program in return for a fee. 625 | 626 | END OF TERMS AND CONDITIONS 627 | 628 | How to Apply These Terms to Your New Programs 629 | 630 | If you develop a new program, and you want it to be of the greatest 631 | possible use to the public, the best way to achieve this is to make it 632 | free software which everyone can redistribute and change under these terms. 633 | 634 | To do so, attach the following notices to the program. It is safest 635 | to attach them to the start of each source file to most effectively 636 | state the exclusion of warranty; and each file should have at least 637 | the "copyright" line and a pointer to where the full notice is found. 638 | 639 | 640 | Copyright (C) 641 | 642 | This program is free software: you can redistribute it and/or modify 643 | it under the terms of the GNU General Public License as published by 644 | the Free Software Foundation, either version 3 of the License, or 645 | (at your option) any later version. 646 | 647 | This program is distributed in the hope that it will be useful, 648 | but WITHOUT ANY WARRANTY; without even the implied warranty of 649 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 650 | GNU General Public License for more details. 651 | 652 | You should have received a copy of the GNU General Public License 653 | along with this program. If not, see . 654 | 655 | Also add information on how to contact you by electronic and paper mail. 656 | 657 | If the program does terminal interaction, make it output a short 658 | notice like this when it starts in an interactive mode: 659 | 660 | Copyright (C) 661 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 662 | This is free software, and you are welcome to redistribute it 663 | under certain conditions; type `show c' for details. 664 | 665 | The hypothetical commands `show w' and `show c' should show the appropriate 666 | parts of the General Public License. Of course, your program's commands 667 | might be different; for a GUI interface, you would use an "about box". 668 | 669 | You should also get your employer (if you work as a programmer) or school, 670 | if any, to sign a "copyright disclaimer" for the program, if necessary. 671 | For more information on this, and how to apply and follow the GNU GPL, see 672 | . 673 | 674 | The GNU General Public License does not permit incorporating your program 675 | into proprietary programs. If your program is a subroutine library, you 676 | may consider it more useful to permit linking proprietary applications with 677 | the library. If this is what you want to do, use the GNU Lesser General 678 | Public License instead of this License. But first, please read 679 | . 680 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | #include "MainWindow.h" 9 | #include "AppConfig.h" 10 | 11 | #include "main.h" 12 | 13 | 14 | using namespace std; 15 | 16 | // Указатель на основное окно программы 17 | QObject *pMainWindow; 18 | 19 | // Конфигурация программы (считанная из файла конфигурации) 20 | AppConfig appConfig; 21 | 22 | 23 | void criticalError(const QString message) 24 | { 25 | qDebug() << " "; 26 | qDebug() << "---------------"; 27 | qDebug() << "Critical error!"; 28 | qDebug() << "---------------"; 29 | qDebug() << message; 30 | qDebug() << "---------------"; 31 | qDebug() << " "; 32 | 33 | QMessageBox::critical(qobject_cast(pMainWindow), "Critical error", 34 | message+"\n\nProgramm was closed.", 35 | QMessageBox::Ok); 36 | 37 | exit(1); 38 | } 39 | 40 | 41 | int main(int argc, char *argv[]) 42 | { 43 | qDebug() << "MoveNoid start..."; 44 | 45 | QApplication a(argc, argv); 46 | MainWindow w; 47 | pMainWindow=&w; 48 | w.show(); 49 | 50 | return a.exec(); 51 | } 52 | -------------------------------------------------------------------------------- /main.h: -------------------------------------------------------------------------------- 1 | #ifndef MAIN_H 2 | #define MAIN_H 3 | 4 | #include 5 | #include "opencv2/opencv.hpp" 6 | 7 | // ---------------------------------------------------------- 8 | // MoveNoid - Арканоид с бесконтактным управлением 9 | // Волгодонск, 2017 г. 10 | // Контакты: xintrea@gmail.com, www.webhamster.ru 11 | // Данный исходный код распространяется под лицензиями: 12 | // - GPL v.3 13 | // - Modified BSD 14 | // © С. М. Степанов, 2017 15 | // ---------------------------------------------------------- 16 | 17 | // Версия программы 18 | #define APPLICATION_RELEASE_VERSION 0 19 | #define APPLICATION_RELEASE_SUBVERSION 2 20 | #define APPLICATION_RELEASE_MICROVERSION 0 21 | 22 | #define MOVE_NOID_FIELD_WIDTH 10.0 23 | #define MOVE_NOID_FIELD_HEIGHT 10.0 24 | 25 | #define MOVE_NOID_START_BALL_POS_X 5.0 26 | #define MOVE_NOID_START_BALL_POS_Y 7.0 27 | #define MOVE_NOID_START_BALL_VELOCITY_X -2.0 28 | #define MOVE_NOID_START_BALL_VELOCITY_Y -6.0 29 | 30 | #define MOVE_NOID_START_ROCKET_BIT_POS_X 5.0 31 | #define MOVE_NOID_START_ROCKET_BIT_POS_Y 8.5 32 | 33 | void criticalError(const QString message); 34 | 35 | #endif // MAIN_H 36 | -------------------------------------------------------------------------------- /misc/pic/GameView01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xintrea/movenoid/d924794bc2fb7cebf764ee6432cf9626a8fdd3bd/misc/pic/GameView01.png -------------------------------------------------------------------------------- /misc/pic/GameView01.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 20 | 22 | 30 | 35 | 36 | 44 | 49 | 50 | 53 | 57 | 61 | 62 | 71 | 81 | 91 | 101 | 102 | 124 | 127 | 128 | 130 | 131 | 133 | image/svg+xml 134 | 136 | 137 | 138 | 139 | 140 | 144 | 152 | 162 | 172 | 180 | 189 | 198 | 207 | 214 | 222 | 227 | 235 | 243 | 248 | 253 | 261 | 266 | 274 | 282 | 287 | 288 | 297 | 305 | 312 | 313 | 314 | -------------------------------------------------------------------------------- /misc/pic/Marker01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xintrea/movenoid/d924794bc2fb7cebf764ee6432cf9626a8fdd3bd/misc/pic/Marker01.png -------------------------------------------------------------------------------- /misc/pic/Marker01.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 29 | 34 | 35 | 43 | 48 | 49 | 57 | 63 | 64 | 72 | 78 | 79 | 87 | 93 | 94 | 102 | 108 | 109 | 117 | 123 | 124 | 132 | 138 | 139 | 147 | 153 | 154 | 162 | 168 | 169 | 177 | 183 | 184 | 192 | 198 | 199 | 207 | 213 | 214 | 222 | 228 | 229 | 237 | 243 | 244 | 252 | 258 | 259 | 267 | 273 | 274 | 282 | 288 | 289 | 290 | 308 | 311 | 312 | 314 | 315 | 317 | image/svg+xml 318 | 320 | 321 | 322 | 323 | 324 | 329 | 341 | 352 | 363 | 374 | 382 | 390 | 398 | 406 | 414 | 422 | 430 | 438 | 446 | 454 | 462 | 470 | 479 | 488 | 497 | 506 | 515 | 524 | 533 | 542 | 12 cm 556 | 1 cm 570 | 3 cm 584 | 6 cm 598 | 3 cm 613 | 5 cm 628 | 629 | 630 | -------------------------------------------------------------------------------- /misc/release/01/sample.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xintrea/movenoid/d924794bc2fb7cebf764ee6432cf9626a8fdd3bd/misc/release/01/sample.zip -------------------------------------------------------------------------------- /resource.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | bin/pic/GameView.png 4 | 5 | 6 | --------------------------------------------------------------------------------