Developing the Kolobok Game Piece

In my previous post, I developed the Kolobok game board for
Windows Phone 7 from the QML Bounce game posted on the
projects.developer.nokia.com site. In this blog posting, I will discuss how my
team developed the main Kolobok character along with the Cacti, Fire, and Brick
Wall obstacles. In my next posting, I will discuss the input buttons and
methods and how the game components are integrated into the final game.

The Kolobok character is the central component of the game.
Upon input from the user, the Kolobok will roll across the current tier or
bounce upward to a new tier. If the Kolobok encounters an obstacle, such as a
brick wall or cactus, the Kolobok will bounce backwards to a previous
position.  Fire will impede the
characters progress while reducing its health.

As we discussed in the previous posting, the Kolobok
character is defined in the class Kolobok in the file Kolobok.cs.  The class Kolobok is derived from the
GraphicItem class that holds important common properties such as object width,
height, position, texture, rotation angle, and opacity. All game pieces,
including characters and obstacles, are derived from this GraphicItem class.
The GraphicItem class also supports rudimentary animations for the moment of
the Kolobok or the flicker of the flame obstacle.

The original QML file kolobok.qml is an Ellipse Element that
contains the “Kolobok’s” properties and its Image, a non visible Timer element,
Key handlers, and javascript methods that respond to user events, moves the Kolobok
character, detects collisions, and updates the game statistics based on the
collisions with obstacles.

import QtQuick 1.0

import Shapes 1.0 

Ellipse
{

    id: kolobok

    // properties
   

    Image {

        id: kolobok_image

        objectName: "kolobok_image"

        x: 0; y: 0

        width: 100; height: 100

        source: ":/images/face-smile.png"

        smooth: true

        opacity: 1.0

    }

   

    Timer {

        id: timer

        interval: 25; running: false; repeat: true

        onTriggered: doMovement()
    }

       Keys.onPressed: { 

        if (event.isAutoRepeat)

            return;

        keyPressed(event.key);

    }

    Keys.onReleased: {

        if (event.isAutoRepeat)

            return;

        keyReleased(event.key);

        }

        // methods
       
}

QML does
not have an Ellipse Item defined, so we have defined an EllipseItem class that
draws an Ellipse in the C file level.cpp and registered this class in the
“Shapes” library as an Item named Ellipse.

The
KeyPressed and KeyReleased methods referenced above will respond to use input
events. If the input is one of the arrow keys, then the doMovement() method is
called, which will move the Kolobok character accordingly.

Collision
detection in the QML application is handled by the Qt Graphics View built-in
collision detection mechanism. The doMovement() method in kolobok.qml will call
into a method in the level.cpp file that will extract the list of items that
collide with the Kolobok and then update the state of the Kolobok accordingly.

The newly
created Kolobok.cs file will also contain the Ellipse, the Kolobok’s properties
and its image, key handlers, and support methods. The timer is an integral part
of the main event loop in XNA and so it is not explicitly defined in C#.
Because graphics objects are not implicitly self drawn or updated in XNA, we
will need to implement the Kolobok’s Initialize, Draw, and Update methods. The
remaining support methods are based on the original Javascript code, but
rewritten in C# using the XNA libraries.

The Initialize
method is very simple and merely initializes the Kolobok’s properties to their
starting values. The Update method will call doMovement() which will check the
user input for an arrow key press and will move the Kolobok character
accordingly. The update() and Draw() methods are shown here:

        public override void Update(GameTime gameTime)

        {

            base.Update(gameTime);

 

            // TODO: Add your update logic here

            doMovement();

 

            if (health <= 0)

                health = 100;

        }

 

        public override void Draw(SpriteBatch spriteBatch)

        {

            // Set up the color

            Color color = new Color(opacity, opacity, opacity, opacity);

 

            // Set up the origin point

            Vector2 origin = new Vector2();

            origin.X = Texture.Width / 2;

            origin.Y = Texture.Height / 2;

 

            // Set up the render rectangle

            Rectangle rect = new Rectangle(Rect.X + Rect.Width / 2,

                                           Rect.Y + Rect.Height / 2,

                                           Rect.Width, Rect.Height);

 

            spriteBatch.Draw(Texture, rect, null, color, RotationAngle, origin,

                              SpriteEffects.None, 0f);

 

            // Draw the health

            spriteBatch.DrawString(healthFont, "Health:" + health,

                                    new Vector2(20, 20), Color.White);

        }  

Key input
is handled through the KeyPressed() in KeyReleased() called by the input
handler in the game object’s update method. The remainder of the method, called
collisionDetection(), uses the XNA collision detection library to determine if
the Kolobok character is colliding with an obstacle and then updates the Kolobok
state.

The
doMovement(), keyPressed() and keyReleased(), and updateCollisions() methods
from kolobok.qmls javascript code are simply re-implemented in C#.

In QML,
the code to read the game layout database and present it to the game board is
written in C in the file level.cpp. The method load() in the C file uses
the Qt QXmlStreamReader to parse an XML database file and for each vertical
level on the game board stores a string containing a character for each
obstacle. The vertical level strings are stored in QVector container called
“map”. These level strings are used to build the game board as described in the
previous blog. The load method is shown below:

bool Level::load()

{

   

    m_rowCount = m_columnCount = 0;

    m_map.clear();

   

    QFile file(":/level/level.xml");

    if (!file.open(QFile::ReadOnly | QFile::Text)) {

        qDebug() << Q_FUNC_INFO << file.errorString();

        return false;

    }

   

    QXmlStreamReader xml(&file);

    if (xml.readNextStartElement()) {

        if (xml.name() == "level") {

            while (xml.readNextStartElement()) {

                if (xml.name() == "layer") {

                    const QString text = xml.readElementText();

                    m_map.append(text);

                    m_rowCount;

                    m_columnCount = qMax(m_columnCount, text.count());

                }

            }

        }

    }

In the
Windows Phone version of the game, level.cs contains a very similar load function.
Here we use the XmlReader class to read the XML data file and a List container
class to store the level strings. Note the similarities between the two
methods:

public static bool load(string
filePath)

        {

            // Clear the level’s old data.

           
mRowCount = 0;

           
mColumnCount = 0;

           
mMap.Clear();

 

            // Open the level xml file

            XmlReader reader = XmlReader.Create(filePath);

            while (reader.Read())

            {

               
if (reader.Name == "layer")

               
{

                   
string text = reader.ReadInnerXml();

                   
mMap.Add(text);

                   
mRowCount ;

                   
mColumnCount = Math.Max(mColumnCount,
text.Count<char>());

                }

            }

            return true;

        }

 

Now that
we implemented the Kolobok character and the movement logic behind the game,
the only remaining parts are to process input from the user and play background
music and sounds. In my next blog, I will pull the rest of the application
together and show how I completed the port.