Showing posts with label Graphics view. Show all posts
Showing posts with label Graphics view. Show all posts

Saturday, November 13, 2010

Sprite animation with SVG sprite sheet in Qt Graphics View

Recently I started to port my game to Symbian platform,
In my previous post I showed how to make graphics work with different resolution.But if you are using images in application, you need to use SVG image to make sure that images looks good in all resoltion.

When I started to replace my game images with SVG image, I need to make changes in my sprite class.With normal images I was drawing only certain portion of image from Sprite sheet like code in this post, but I could not make that thing work with SVG. Then to make my sprite class work I made following changes.

In following code, I am using SVG file which contain each frame with unique id and in code on next frame I am rendering SVG element with id for particular frame.

Code for animatedsvgitem.h
#ifndef ANIMATEDSVGITEM_H
#define ANIMATEDSVGITEM_H

#include <QGraphicsSvgItem>
#include <QTimer>

class AnimatedSvgItem : public QGraphicsSvgItem
{
    Q_OBJECT
public:
    AnimatedSvgItem();
    QRectF boundingRect() const;

private slots:
    void nextFrame();

private:
    QTimer *timer;
    QStringList frames;
    int currentFrame;
};
#endif // ANIMATEDSVGITEM_H

Code for animatedsvgitem.cpp
#include "animatedsvgitem.h"

AnimatedSvgItem::AnimatedSvgItem()
    :QGraphicsSvgItem( QString(":/egg.svg") )
{
    //svg element id of frame in svg file
    frames << "egg1" << "egg2" << "egg3" << "egg4" << "egg5" << "egg6" << "egg7" << "egg8";
    currentFrame = 0;
    this->setElementId( frames[currentFrame] );
    timer = new QTimer(this);
    QObject::connect(timer,SIGNAL(timeout()),this,SLOT(nextFrame()));
    timer->start(400);
}

QRectF AnimatedSvgItem::boundingRect() const
{
    return QRectF(0,0,20,20);
}

void AnimatedSvgItem::nextFrame()
{
    currentFrame = ++currentFrame % 8;
    this->setElementId( frames[currentFrame]);
    this->moveBy(5,0);
    update();
}
So this was all, Sprite sheet used for above code is like this but in SVG format.





Here video from sample code.

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.



Monday, August 16, 2010

Creating custom graphics item in Qt's graphics view

Now a days I started to learn Qt's Graphics view. Initially I was struggling with it but once I started programming with it I found that its quite easy and powerful. What I like most about it is, while creating custom item we can perform paint opration with items local coordinate and need not worry about scene coordinate.

While creating custom graphics item, Your class needs to be derived from QGraphicsItem or QGraphicsObject. If your object needs to use signal/slot than derive your class from QGraphicsObject else derive it from QGraphicsItem and override paint and boundingRect methods.

For test application I tried to create sprite with custom graphics item that shows rolling ball animation. Following is code for my custom graphics item.
#include <QGraphicsObject>
#include <QPixmap>

class BallGraphicsItem : public QGraphicsObject
{
    Q_OBJECT
public:

    BallGraphicsItem();
    ~BallGraphicsItem();

    void paint ( QPainter * painter, 
const QStyleOptionGraphicsItem * option, 
QWidget * widget = 0 );

    QRectF boundingRect() const;

public slots:
    void move();
    void nextFrame();
private:

    QPixmap mBallImage;    
    int mCurrentRow;
    int mCurrentColumn;    
};

#endif // BALLGRAPHICSITEM_H

#include "ballgraphicsitem.h"
#include <QPainter>
#include <math.h>
#include <QBitmap>


BallGraphicsItem::BallGraphicsItem()
    :QGraphicsObject( ),mBallImage(":sphere.bmp"),
mCurrentRow(0),mCurrentColumn(0)
{
    //masking image to make image transperent
    mBallImage.setMask( 
mBallImage.createMaskFromColor(QColor(255,0,255)));    
}

BallGraphicsItem::~BallGraphicsItem()
{}

QRectF BallGraphicsItem::boundingRect() const
{
    return QRectF(0,0,32,32);
}

void BallGraphicsItem::paint ( QPainter *painter, 
const QStyleOptionGraphicsItem */*option*/, QWidget */*widget*/ )
{
    painter->drawPixmap(0,0,mBallImage, 
mCurrentColumn* 32 ,mCurrentRow*32, 32,32);
}

void BallGraphicsItem::nextFrame()
{
    mCurrentColumn = ++mCurrentColumn % 8;
    if( mCurrentColumn == 0 ) {
        mCurrentRow = ++mCurrentRow %4;
    }
}

void BallGraphicsItem::move()
{    
    setPos( x() + 2, y());
    nextFrame();
}

And my main.cpp file looks like following.
#include <QApplication>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QTimer>
#include "ballgraphicsitem.h"

int main( int argc, char* argv[] )
{
    QApplication app(argc,argv);

    QGraphicsScene scene( 0, 0, 840, 480 );
    QGraphicsView view(&scene);
    view.setRenderHint(QPainter::Antialiasing);
    view.show();

    BallGraphicsItem* ball = new BallGraphicsItem();
    ball->setPos(10,10);
    scene.addItem( ball );

    QTimer *timer = new QTimer(qApp);
    timer->start(1000/30);
    QObject::connect(timer,SIGNAL(timeout()),ball,SLOT(move()));

    return app.exec();
} 
For above code I am using following image, don't know from where I downloaded image.