Development/Tutorials/Games/Palapeli Slicers: Difference between revisions

From KDE TechBase
No edit summary
(first version is ready)
Line 5: Line 5:
|reading=[http://api.kde.org/playground-api/games-apidocs/palapeli/lib/html/classPalapeli_1_1Pattern.html Palapeli::Pattern], [http://api.kde.org/playground-api/games-apidocs/palapeli/lib/html/classPalapeli_1_1PatternConfiguration.html Palapeli::PatternConfiguration]
|reading=[http://api.kde.org/playground-api/games-apidocs/palapeli/lib/html/classPalapeli_1_1Pattern.html Palapeli::Pattern], [http://api.kde.org/playground-api/games-apidocs/palapeli/lib/html/classPalapeli_1_1PatternConfiguration.html Palapeli::PatternConfiguration]
}}
}}
{{Box|Warning to editors|I'm currently creating this tutorial. You should not edit this page in the mean time. -- [[User:Majewsky|Majewsky]] 18:19, 20 July 2008 (CEST)}}


== Abstract ==
== Abstract ==
Line 156: Line 154:
</code>
</code>


Everything of this is pretty straightforward if you have previous experience with CMake (and this is what I assume). Make sure you in
Everything of this is pretty straightforward if you have previous experience with CMake (and this is what I assume). Make sure you compile everything as a plugin, and install it into the plugin directory.

Revision as of 17:06, 20 July 2008

Creating a Palapeli pattern
Tutorial Series   Programming with the Palapeli API
Previous   Introduction to KDE4 programming
What's Next   n/a
Further Reading   Palapeli::Pattern, Palapeli::PatternConfiguration

Abstract

This tutorial shows you how to create a pattern for Palapeli, that is: a plugin for the Palapeli libraries that describes an algorithm to split an image into pieces.

The pattern we will be constructing is quite easy: It splits an image into two equally sized pieces.

Structure

A pattern plugin consists of two classes. The first one (derived from Palapeli::PatternConfiguration) tells Palapeli what features this pattern plugin has and which configuration values it needs. The second one (derived from Palapeli::Pattern) does the actual slicing.

The code: mypattern.h

  1. ifndef MYPATTERN_H
  2. define MYPATTERN_H
  1. include <Palapeli/Pattern>
  2. include <Palapeli/PatternConfiguration>

class MyPattern : public Palapeli::Pattern { public: MyPattern(); virtual ~MyPattern() {}

virtual int estimatePieceCount() const; protected: virtual void doSlice(const QImage& image); };

class MyPatternConfiguration : public Palapeli::PatternConfiguration { public: MyPatternConfiguration(QObject* parent = 0, const QVariantList& args = QVariantList()); virtual ~MyPatternConfiguration() {} virtual Palapeli::Pattern* createPattern() const; };

  1. endif // MYPATTERN_H

As described above, we have declared two classes deriving from the base classes Palapeli::Pattern and Palapeli::PatternConfiguration. For this simple example, we do only reimplement constructors, destructors and some pure virtual functions.

The code: mypattern.cpp

  1. include "mypattern.h"
  1. include <QImage>
  2. include <KPluginFactory>
  3. include <KPluginLoader>

K_PLUGIN_FACTORY(MyPatternFactory, registerPlugin<MyPatternConfiguration>();) K_EXPORT_PLUGIN(MyPatternFactory("mypattern"))

MyPattern::MyPattern() : Palapeli::Pattern() { }

int MyPattern::estimatePieceCount() const {

   return 2;

}

void MyPattern::doSlice(const QImage& image) {

   //construct pixmaps for the pieces
   const int pieceWidth = image.width() / 2, pieceHeight = image.height();
   QImage leftPiece = image.copy(QRect(0, 0, pieceWidth, pieceHeight));
   QImage rightPiece = image.copy(QRect(pieceWidth, 0, pieceWidth, pieceHeight));
   //add pieces; define a neighborship relation between them
   addPiece(leftPiece, QRectF(0, 0, pieceWidth, pieceHeight));
   addPiece(rightPiece, QRectF(pieceWidth, 0, pieceWidth, pieceHeight));
   addRelation(0, 1, QPointF(pieceWidth, 0)); //0 and 1 are the consecutive indices of the pieces

}

MyPatternConfiguration::MyPatternConfiguration(QObject* parent, const QVariantList& args)

   : Palapeli::PatternConfiguration(parent, args)

{

   Q_UNUSED(parent)
   Q_UNUSED(args)
   setSizeDefinitionMode(Palapeli::PatternConfiguration::CustomSizeDefinition);

}

Palapeli::Pattern* MyPatternConfiguration::createPattern() const {

   return new MyPattern;

}

We will start with the MyPatternConfiguration class, because this is the logical entry point. The pattern configuration class is created when Palapeli is started. (The constructor arguments are necessary for the plugin loader, you will not need to bother with them as long as you pass them to the base class constructor.) Its job is to manage the pattern's settings. In this case, there is nothing to configure. If you want to implement a size definition by the number of pieces in horizontal and in vertical direction (as for the standard rectangular piece pattern), you can simply set the size definition mode to Palapeli::PatternConfiguration::CountSizeDefinition. Arbitrary configuration can be achieved by adding widgets through the Palapeli::PatternConfiguration::addWidget method.

When the user has configured its game (and therefore the pattern), the Palapeli game engine will call the createPattern method to create a Palapeli::Pattern object. If you have added configuration values, pass them to the constructor of your pattern object, and save them in private variables. If you have chosen the CountSizeDefinition, you can use the xCount and yCount methods to retrieve the configured values. See the implementation of the standard rectangular pattern for details on how to use this size definition mode.

Now we have a Palapeli::Pattern instance. Its main purpose is the doSlice method which takes a method. In this implementation, we copy the left and the right part of the image to get two pieces. To add these pieces to Palapeli's puzzle scene, use the base class' addPiece method. It will properly report it to Palapeli.

After we have added all pieces, we need to define neighbor relations between pieces. Neighbor relations are used to snap pieces together when they're near enough. Consider three pieces in a row: If piece 1 is near piece 3, nothing should snap because they do not have a common edge. If piece 1 is moved near piece 2, these should snap together. Therefore, we define a relation between piece 1 and piece 2 (and piece 2 and piece 3, respectively). The relation is added through the addRelation function. In our case (line 29), we define a relation between the first piece (index 0) and the second piece (index 1). The piece indices are based on the order in which you added the pieces to the scene, and start at zero.

Integrate into Palapeli: mypattern.desktop

[Desktop Entry] X-KDE-Library=mypattern X-KDE-PluginInfo-Author=The best KDE hacker [email protected] X-KDE-PluginInfo-Name=mypattern X-KDE-PluginInfo-Version=1.0 X-KDE-PluginInfo-Website=http://kde-hackers.example.org/palapelipatterns X-KDE-PluginInfo-Category= X-KDE-PluginInfo-Depends= X-KDE-PluginInfo-License=GPL X-KDE-PluginInfo-EnabledByDefault=true X-KDE-ParentApp=libpalapelipattern X-KDE-ServiceTypes=Palapeli/PatternPlugin Type=Service Icon=palapeli-pattern-mypattern PatternIdentifier=mypattern Name=My pattern Name[de]=Mein Schnittmuster Comment=The best pattern in the world Comment[de]=Das beste Schnittmuster auf der ganzen Welt

We need to tell the Palapeli library that there is a new plugin out there. We need a desktop entry file like shown here. You can mostly copy everything you see here; as long as you keep the fields X-KDE-Library and X-KDE-PluginInfo-Name in sync with the library name (see next section). You should not change the PatternIdentifier field because that will invalidate saved games created with previous versions. Note also that the name is used in the "New puzzle" dialog to identify this pattern. (It can be translated; for example, a German translation has been added in the above example.)

Build everything: CMakeLists.txt

project(mypattern)

find_package(KDE4 REQUIRED) find_package(Palapeli REQUIRED)

add_definitions(${QT_DEFINITIONS} ${KDE4_DEFINITIONS}) include_directories(${KDE4_INCLUDES} ${PALAPELI_INCLUDE_DIR})

set(mypattern_SRCS mypattern.cpp )

kde4_add_plugin(mypattern ${mypattern_SRCS}) target_link_libraries(mypattern ${KDE4_KDEUI_LIBS} palapelipattern)

install(TARGETS mypattern DESTINATION ${PLUGIN_INSTALL_DIR}) install(FILES mypattern.desktop DESTINATION ${SERVICES_INSTALL_DIR})

Everything of this is pretty straightforward if you have previous experience with CMake (and this is what I assume). Make sure you compile everything as a plugin, and install it into the plugin directory.