Thursday, October 28, 2010

Chopper game with Qt Quick (QML)

Today, I created my first Qt Quick (QML) game named Chopper, game is simple and was created just for learning process.

UI Is implemented using QML and logic part is done using JavaScript and tested on Nokia Qt SDK emulator.

If you want to look at code then here is link to my gitorious repository.

Following is snap from game,


And below is video from game.

Saturday, October 23, 2010

Creating Qt game for mobile device with differnt screen size

Till now I have created few Qt games for Maemo 5 (N900) device, and now I wanted to run same games on Symbian device which support Qt.

First problem I encounter doing so is different screen size from N900, in my current game implementation I have hard coded screen size and coordination of game object.

After playing with graphics framework from some time, I created a small prototype application that shows two rectangle one on top left other on bottom right corner and can run on different Symbian device and N900 as well. So first thing I did is to remove all hard coded coordinate and implemented resize event, then scaled all graphics item which are added to graphics scene.

Following is my prototype code.

In below code, I have added QGraphicsView as QMainWindow's central widget and also reimplemented resizeEvent of QMainWindow.
GraphicsWidget::GraphicsWidget(QWidget *parent)
    : QMainWindow(parent)
{    
    _scene = new MyScene();
    _scene->setItemIndexMethod(QGraphicsScene::NoIndex);
    _view = new QGraphicsView(_scene,this);
    _view->setDragMode(QGraphicsView::NoDrag);
    _view->setCacheMode(QGraphicsView::CacheBackground);
    _view->setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate);

    setCentralWidget(_view);

    // Disable scrollbars
    _view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    _view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
}

void GraphicsWidget::resizeEvent(QResizeEvent* event)
{
    _scene->setSceneRect(_view->rect());
    QWidget::resizeEvent(event);
}

And then in MyScene class, derived from QGraphicsScene, on sceneRectChanged signal i am scaling all item to scale factor according to my reference screen size, which is of n900 device's screen size.
MyScene::MyScene()
    :QGraphicsScene()
{
    item1 = new MyGraphicsItem();
    addItem( item1 );

    item2 = new MyGraphicsItem();
    addItem( item2 );
    QObject::connect(this,SIGNAL(sceneRectChanged(QRectF)),
                    this,SLOT(resize(QRectF)));
}

void MyScene::resize(const QRectF& newRect)
{
    qreal w = (qreal)newRect.width()/800 ;
    qreal h =  (qreal)newRect.height()/480;

    item1->resetMatrix();
    item1->scale(w,h);
    item1->setPos(newRect.topLeft());

    item2->resetMatrix();
    item2->scale(w,h);
    item2->setPos(newRect.width()-(item2->boundingRect().width()*w),
            newRect.height() - (item2->boundingRect().height()*h));
}
Finally in main function, I am showing QMainWindow in full screen mode.
int main(int argc, char* argv[] )
{
    QApplication app(argc,argv);
    GraphicsWidget widget;
    widget.showFullScreen();
    return app.exec();
}
Following are snaps from prototype running on emulator for different device.



Thursday, October 21, 2010

Using QStateMachine with sprite animation

In 4.6 Qt introduced new State machine framework. Framework looks quite easy to use and extensible for complex use-case.
To explore Qt's State machine framework I thought to use it with sprite animation, as a complex sprite also holds many state and maintaining sprite state and transition from one state to another can become pain if not done properly.

So following is my sample code for prince sprite which use QStateMachine to maintain sprite's state and transition from one state to another. From somewhere in Internet I found sprite sheet for Prince of Persia, to make it easy for sample code I created another simple version of original sprite sheet.
PrinceSprite::PrinceSprite(QGraphicsItem * parent)
    :QGraphicsObject(parent)
{
    mSpriteImage = new QPixmap(":/prince.png");
    changeDir(this);

    //creating different state object , 
    //which are derived from QState and l
    //istening to timer's timeout signal 
    //and also modify frame according to animation
    StandingState* standing = new StandingState(this);
    RunningState* running = new RunningState(this);
    PrepareForAttack* prepareForAttack = 
                        new PrepareForAttack(this);
    Attack* attack = new Attack(this);
    InAttackMode* inAttackMode = new InAttackMode(this);

    //adding transition to state
    //goto running state from stating state at left click
    standing->addTransition(this,SIGNAL(leftClick()),running);
    running->addTransition(this,SIGNAL(leftClick()),standing);
    standing->addTransition(this,SIGNAL(rightClick())
                                    ,prepareForAttack);
    prepareForAttack->addTransition(prepareForAttack,
                        SIGNAL(exited()),inAttackMode);
    inAttackMode->addTransition(this,SIGNAL(leftClick()),attack);
    attack->addTransition(attack,SIGNAL(exited()),inAttackMode);
    inAttackMode->addTransition(this,
                     SIGNAL(rightClick()),standing);

    //adding all state to QStateMachine
    _stateMachine.addState(standing);
    _stateMachine.addState(running);
    _stateMachine.addState(prepareForAttack);
    _stateMachine.addState(attack);
    _stateMachine.addState(inAttackMode);

    //setting initial state and then starting state machine
    _stateMachine.setInitialState( standing);
    _stateMachine.start();
}

In above code I have create different state object for different sprite state, like for StandingState or Attack state.
This state classes are derived from QState class and listen to QTimer's timeout signal to change frame according to animation.

For example following code for Attack state.
#include <QState>

class Attack : public QState
{
    Q_OBJECT
public:
    Attack(PrinceSprite* prince);
    void onEntry ( QEvent * event );
    void onExit ( QEvent * event );

signals:
    void exited();

private slots:
    void nextFrame();
};

#include "attack.h"

Attack::Attack(PrinceSprite* prince)
    :QState()
{}

void Attack::nextFrame()
{    
    _prince->_x += 1;
    _prince->setX(_prince->x() + 7);
    if (_prince->_x >= 4 ) {
        emit exited();
    }
}

void Attack::onEntry ( QEvent * event )
{
    QObject::connect(_prince,SIGNAL(tick()),
                     this,SLOT(nextFrame()));
    _prince->_x = 0;
    _prince->_y = 2;
}

void Attack::onExit ( QEvent * event )
{
    QObject::disconnect(_prince,SIGNAL(tick()),
                        this,SLOT(nextFrame()));
}

So finally, I am not sure if this is correct approach to implement sprite or not, but using Qt's State machine framework is helping a lot to make it simpler.

Follwing is demo of my implementation.

Saturday, October 9, 2010

Crazy chickens on Qt Ambassador Showcase

Finally yesterday my game Crazy Chickens successfully gone through Qt Ambassador Showcase process and now it is show cased in Qt Ambassador Showcase

Coincidently I received my Ambassador T-shirt also on same day. Following is snap of T shirt if you wonder how it looks.

Sunday, October 3, 2010

Detecting Swipe gesture in Qt

In continuation of my previous post of long press gesture in Qt, I created another simple gesture class to detect swipe gesture.

In my implementation, I am storing initial coordinate on mouse press event and comparing initial coordinate with coordinate on mouse release event.In this code I have not considered speed of swipe but we can easily measure speed of swipe by measuring time difference between two event.

myswipegesture.h file
#ifndef MYSWIPEGESTURE_H
#define MYSWIPEGESTURE_H

#include <QObject>
#include <QPoint>

class MySwipeGesture : public QObject
{
    Q_OBJECT
public:
    explicit MySwipeGesture(QObject *parent = 0);
    void handleEvent( QEvent *event);
public:
    enum SwipeDirection {
        Left = 0,
        Right,
        Up,
        Down
    };
signals:
    void handleSwipe( MySwipeGesture::SwipeDirection direction );
private:
    QPoint _startPoint;
    QPoint _endPoint;
};
#endif // MYSWIPEGESTURE_H
myswipegesture.cpp file
#include "myswipegesture.h"
#include <QEvent>
#include <QMouseEvent>

MySwipeGesture::MySwipeGesture(QObject *parent)
    :QObject(parent),_startPoint(0,0),_endPoint(0,0)
{}

void MySwipeGesture::handleEvent( QEvent *event)
{
    if( event->type() == QEvent::MouseButtonPress ) {
    QMouseEvent* mouseEvent = static_cast<QMouseEvent*> (event);
        _startPoint = mouseEvent->pos();
    } else if( event->type() == QEvent::MouseButtonRelease ) {
    QMouseEvent* mouseEvent = static_cast<QMouseEvent*> (event);
        _endPoint = mouseEvent->pos();

        //process distance and direction
        int xDiff = _startPoint.x() - _endPoint.x();
        int yDiff = _startPoint.y() - _endPoint.y();
        if( qAbs(xDiff) > qAbs(yDiff) ) {
            // horizontal swipe detected, now find direction
            if( _startPoint.x() > _endPoint.x() ) {
                emit handleSwipe( Left);
            } else {
                emit handleSwipe( Right);
            }
        } else {
            // vertical swipe detected, now find direction
            if( _startPoint.y() > _endPoint.y() ) {
                emit handleSwipe( Up);
            } else {
                emit handleSwipe( Down);
            }
        }
    } else if( event->type() == QEvent::MouseMove ) {
        //ignore event
    }
}
Some test code.
#include "myswipegesture.h"

TestWidget::TestWidget(QWidget *parent) : QWidget(parent)
{
    _swipeGesture = new MySwipeGesture(this);
    connect(_swipeGesture,SIGNAL(handleSwipe(MySwipeGesture::SwipeDirection)),this,SLOT(swipe(MySwipeGesture::SwipeDirection)));
}

bool TestWidget::event(QEvent *event)
{
    _swipeGesture->handleEvent(event);
    return QWidget::event(event);
}

void TestWidget::swipe(MySwipeGesture::SwipeDirection direction)
{
    qDebug() << "swipe" << direction;
}
Hope this helps.