The Worlds first 100% XML Mark-up Driven Puzzle Game made with XOML

Well we finally cracked it, we’ve been aiming to create a game in 100% XOML mark-up and it finally happened. We created a small logic puzzle game called 10.

The game itself has simple rules. The screen is filled with cards, each card having a value of 0 to 9. The user selects 3 cards, who’s total value must total 10. When the user selects 3 cards that total 10 they are removed from play. To complete the round the user must eliminate all cards on the play field. The idea is simple but the game can be become incredibly difficult.

The game is 100% XOML based using only actions and variables to create the logic and no additional C/C++. We will be providing the project as an example project when we release IwGame 0.36.

Note that the game will not work with version 0.35 of IwGame.

We are getting closer to bringing cross platform mobile game and app development to the hands of none programmers :)

Oh and For the sake of clarity, we are not stating a worldwide claim over all mark-up languages as that would be ludicrous, only the XOML mark-up language.

A Windows build of the Game of 10 is available for download at http://www.drmop.com/wp-content/uploads/2012/06/Game-of-10.zip

<?xml version="1.0"?>
<xml>

    <Style Name="GenericSceneStyle">
        <Set Property="CanvasSize" Value="-100, -100" />
        <Set Property="CanvasFit" Value="none" />
        <Set Property="CanvasOrigin" Value="centre" />
        <Set Property="Colour" Value="255, 255, 255, 255" />
        <Set Property="AllowSuspend" Value="false" />
        <Set Property="Physics" Value="false" />
    </Style>

    <!-- Load a UI style to style the user interface  -->
    <LoadXOML File="UIStyle.xml" />

    <!-- Create a scene to hold or game -->
    <Scene Name="GameScene" Style="GenericSceneStyle" Camera="Cam" Extents="-1000, -400, 2000, 800" Current="true" Batch="false" ClipStatic="true">

        <!-- Create a camera -->
        <Camera Name="Cam" Position="0, 0" Angle="0" Scale="1.0" />

        <!-- Score / round variables -->
        <Variable Name="Score" Type="int" Value="0" />
        <Variable Name="RoundScore" Type="int" Value="0" />
        <Variable Name="CurrentRound" Type="int" Value="0" />
        <!-- TargetValue determines the valuue that all 3 user selections should add up to gain a correct answer -->
        <Variable Name="TargetValue" Type="int" Value="10" />
        <!-- TotalCardsValue is the current value of all selected cards -->
        <Variable Name="TotalCardsValue" Type="int" Value="0" />
        <!-- CardValues is an array that holds the value of each card -->
        <Variable Name="CardValues" Type="arrayint" Size="15" Value="0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0" />
        <!-- CardsCount is the total number of cards in play  -->
        <Variable Name="CardsCount" Type="int" Value="15" />
        <!-- CardsFoundCount is the total number of cards that the user has successfully found  -->
        <Variable Name="CardsFoundCount" Type="int" Value="0" />
        <!-- SelectedCardNames is an array that holds the names of the currently selected cards  -->
        <Variable Name="SelectedCardNames" Type="arraystring" Size="3" Value="none, none, none" />
        <!-- CardIndex is the current selection index  -->
        <Variable Name="CardIndex" Type="int" Value="0" />

        <!-- CardIndexIs are conditions used to determine the currently selected index  -->
        <Variable Name="CardIndexIs0" Type="condition" Value="CardIndex == 0"/>
        <Variable Name="CardIndexIs1" Type="condition" Value="CardIndex == 1"/>
        <Variable Name="CardIndexIs2" Type="condition" Value="CardIndex == 2"/>
        <Variable Name="CardIndexIs3" Type="condition" Value="CardIndex == 3"/>
        <!-- CardsMatch is a condition used to determine if the total of all currently selected cards add up to the the target value of 10 -->
        <Variable Name="CardsMatch" Type="condition" Value="TotalCardsValue == TargetValue"/>
        <!-- NoCardsLeft is a condition used to determine if there are any cards left in play -->
        <Variable Name="NoCardsLeft" Type="condition" Value="CardsCount == CardsFoundCount"/>

        <!-- IsRound is a condition used to determine the current round -->
        <Variable Name="IsRound0" Type="condition" Value="CurrentRound == 0"/>
        <Variable Name="IsRound1" Type="condition" Value="CurrentRound == 1"/>
        <Variable Name="IsRound2" Type="condition" Value="CurrentRound == 2"/>

        <!-- Load the first set of cards -->
        <File Name="RoundFile" Location="Round1.txt" FileType="text" Preload="true" Blocking="true" Variable="CardValues" />

        <!-- Action that resets the round -->
        <Actions Name="RestartRound">
            <Action Method="SetVar" Param1="RoundScore" Param2="0" />
            <Action Method="SetVar" Param1="TotalCardsValue" Param2="0" />
            <Action Method="SetVar" Param1="CardsFoundCount" Param2="0" />
            <Action Method="CallActions" Param1="ResetSelection" />
            <Action Method="ShowActor" Param1="Card0" />
            <Action Method="ShowActor" Param1="Card1" />
            <Action Method="ShowActor" Param1="Card2" />
            <Action Method="ShowActor" Param1="Card3" />
            <Action Method="ShowActor" Param1="Card4" />
            <Action Method="ShowActor" Param1="Card5" />
            <Action Method="ShowActor" Param1="Card6" />
            <Action Method="ShowActor" Param1="Card7" />
            <Action Method="ShowActor" Param1="Card8" />
            <Action Method="ShowActor" Param1="Card9" />
            <Action Method="ShowActor" Param1="Card10" />
            <Action Method="ShowActor" Param1="Card11" />
            <Action Method="ShowActor" Param1="Card12" />
            <Action Method="ShowActor" Param1="Card13" />
            <Action Method="ShowActor" Param1="Card14" />
            <Action Method="SetProperty" Param1="Timeline" Param2="StillAnim" Param3="Card0" />
            <Action Method="SetProperty" Param1="Timeline" Param2="StillAnim" Param3="Card1" />
            <Action Method="SetProperty" Param1="Timeline" Param2="StillAnim" Param3="Card2" />
            <Action Method="SetProperty" Param1="Timeline" Param2="StillAnim" Param3="Card3" />
            <Action Method="SetProperty" Param1="Timeline" Param2="StillAnim" Param3="Card4" />
            <Action Method="SetProperty" Param1="Timeline" Param2="StillAnim" Param3="Card5" />
            <Action Method="SetProperty" Param1="Timeline" Param2="StillAnim" Param3="Card6" />
            <Action Method="SetProperty" Param1="Timeline" Param2="StillAnim" Param3="Card7" />
            <Action Method="SetProperty" Param1="Timeline" Param2="StillAnim" Param3="Card8" />
            <Action Method="SetProperty" Param1="Timeline" Param2="StillAnim" Param3="Card9" />
            <Action Method="SetProperty" Param1="Timeline" Param2="StillAnim" Param3="Card10" />
            <Action Method="SetProperty" Param1="Timeline" Param2="StillAnim" Param3="Card11" />
            <Action Method="SetProperty" Param1="Timeline" Param2="StillAnim" Param3="Card12" />
            <Action Method="SetProperty" Param1="Timeline" Param2="StillAnim" Param3="Card13" />
            <Action Method="SetProperty" Param1="Timeline" Param2="StillAnim" Param3="Card14" />
        </Actions>

        <!-- Action that moves to the next round -->
        <Actions Name="NextRound">
            <Action Method="CallActions" Param1="RestartRound" />
            <Action Method="AddVar" Param1="CurrentRound" Param2="1" />
            <Action Method="LoadFile" Param1="RoundFile" Param2="true" Param3="Round1.txt" Condition="IsRound0" />
            <Action Method="LoadFile" Param1="RoundFile" Param2="true" Param3="Round2.txt" Condition="IsRound1" />
            <Action Method="LoadFile" Param1="RoundFile" Param2="true" Param3="Round3.txt" Condition="IsRound2" />
        </Actions>

        <!-- Action that is called when a correct answer is given -->
        <Actions Name="CorrectAnswer">
            <Action Method="AddVar" Param1="RoundScore" Param2="1" />
            <Action Method="HideActor" Param1="SelectedCardNames:0" />
            <Action Method="HideActor" Param1="SelectedCardNames:1" />
            <Action Method="HideActor" Param1="SelectedCardNames:2" />
            <Action Method="AddVar" Param1="CardsFoundCount" Param2="3" />
            <Action Method="SetProperty" Param1="Text" Param2="Correct, well done" Param3="AnswerNotification" />
            <Action Method="SetTimeline" Param1="AnswerNotificationAnim" Param2="AnswerNotification" />
        </Actions>

        <!-- Action that is called when an incorrect answer is given -->
        <Actions Name="WrongAnswer">
            <Action Method="SetProperty" Param1="Text" Param2="Incorrect, try again" Param3="AnswerNotification" />
            <Action Method="SetTimeline" Param1="AnswerNotificationAnim" Param2="AnswerNotification" />
        </Actions>

        <!-- Action that is called when the user has finished selecting 3 cards -->
        <Actions Name="ResetSelection">
            <Action Method="SetVar" Param1="CardIndex" Param2="0" />
            <Action Method="SetVar" Param1="TotalCardsValue" Param2="0" />
        </Actions>

        <!-- Action that is used to check the answer -->
        <Actions Name="CheckAnswer">
            <Action Method="CallActions" Param1="CorrectAnswer" Condition="CardsMatch" />
            <Action Method="CallActions" Param1="WrongAnswer" Condition="!CardsMatch" />
            <Action Method="CallActions" Param1="ResetSelection" />
            <Action Method="CallActions" Param1="RoundComplete" Condition="NoCardsLeft" />
            <Action Method="SetProperty" Param1="Timeline" Param2="StillAnim" Param3="SelectedCardNames:0" />
            <Action Method="SetProperty" Param1="Timeline" Param2="StillAnim" Param3="SelectedCardNames:1" />
            <Action Method="SetProperty" Param1="Timeline" Param2="StillAnim" Param3="SelectedCardNames:2" />
        </Actions>

        <!-- Action that is called when the round is complete -->
        <Actions Name="RoundComplete">
            <Action Method="AddVar" Param1="Score" Param2="RoundScore" />
            <Action Method="CallActions" Param1="NextRound" />
        </Actions>

        <!-- Create a grid cell actor template to represent our card -->
        <Template Name="CardTemplate">
            <Label Name="Card$index$" Size="-18, -28" Background="Button1Brush" BackgroundColour="255, 255, 200, 255" SelectedColour="200, 255, 200, 255" Font="font1" GridPos="$gridpos$" HitTest="true" SelectType="normal" Selected="false" OnTapped="CardSelected">
                <Timeline Name="FlashAnim" AutoPlay="true">
                    <Animation Anim="Flashing" Repeat="0" Target="Scale"/>
                </Timeline>
                <Actions Name="CardSelected">
                    <Action Method="SetVar" Param1="SelectedCardNames:0" Param2="Card$index$" Condition="CardIndexIs0" />
                    <Action Method="SetVar" Param1="SelectedCardNames:1" Param2="Card$index$" Condition="CardIndexIs1" />
                    <Action Method="SetVar" Param1="SelectedCardNames:2" Param2="Card$index$" Condition="CardIndexIs2" />
                    <Action Method="AddVar" Param1="TotalCardsValue" Param2="CardValues:$index$" />
                    <Action Method="AddVar" Param1="CardIndex" Param2="1" />
                    <Action Method="SetTimeline" Param1="FlashANim" />
                    <Action Method="CallActions" Param1="CheckAnswer" Condition="CardIndexIs3" />
                </Actions>
            </Label>
        </Template>

        <!-- Create a grid that contains our cards -->
        <Variable Name="SelectedCard" Type="int" Value="-1" />
        <Grid Name="ItemsGrid" Size="-100, -90" Docking="bottom" Background="PanelBrush" BackgroundColour="150, 200, 255, 255" SelectedColour="150, 200, 255, 255" HitTest="false" ClipMargin="10, 10, 10, 10" MultiSelect="true" UseParentOpacity="false" ItemsData="CardValues" ItemsTemplate="CardTemplate" ItemsTargetType="text" Selection="SelectedCard">
            <RowDefinition Name="r0" AlignV="middle" Height="-33" />
            <RowDefinition Name="r1" AlignV="middle" Height="-33" />
            <RowDefinition Name="r2" AlignV="middle" Height="-33" />
            <ColumnDefinition Name="c0" AlignH="left" Width="-20" />
            <ColumnDefinition Name="c1" AlignH="left" Width="-20" />
            <ColumnDefinition Name="c2" AlignH="left" Width="-20" />
            <ColumnDefinition Name="c3" AlignH="left" Width="-20" />
            <ColumnDefinition Name="c5" AlignH="left" Width="-20" />
        </Grid>

        <!-- Create the menu at the top of the screen -->
        <StackPanel Size="-100, -10" HitTest="false" Docking="top" Orientation="horizontal">
            <Label Name="Restart" Background="Button1Brush" SelectedColour="80, 80, 80, 255" Size="-15, -100" Font="smallfont1" Text="Restart" Margin="0, -5, 0, 0" OnTapped="RestartRound" />
            <Label Background="PanelBrush" Size="-10, -100" Font="smallfont1" Text="Round" />
            <Label Name="RoundScore" Background="PanelBrush" Size="-10, -100" Font="smallfont1" Binding="[Text]CurrentRound" />
            <Label Background="PanelBrush" Size="-10, -100" Font="smallfont1" Text="Score" />
            <Label Name="Score" Background="PanelBrush" Size="-10, -100" Font="smallfont1" Binding="[Text]Score" />
            <Label Background="PanelBrush" Size="-10, -100" Font="smallfont1" Text="Round Score" />
            <Label Name="RoundScore" Background="PanelBrush" Size="-10, -100" Font="smallfont1" Binding="[Text]RoundScore" />
            <Label Name="Test3" Background="PanelBrush" Size="-10, -100" Font="smallfont1" Binding="[Text]TotalCardsValue" />
        </StackPanel>

        <!-- Create answer notification overlay -->
        <Animation Name="AnswerNotificationAnim" Duration="2" Type="float">
            <Frame Value="-2000" Time="0" Easing="quadin" />
            <Frame Value="0" Time="0.5" />
            <Frame Value="0" Time="1.5" Easing="quadout" />
            <Frame Value="2000" Time="2" />
        </Animation>
        <Timeline Name="AnswerNotificationAnim" AutoPlay="true">
            <Animation Anim="AnswerNotificationAnim" Repeat="1" Target="PositionX" />
        </Timeline>
        <Label Name="AnswerNotification" Position="-2000, 0" Size="-50, -15" Background="PanelBrush" BackgroundColour="80, 255, 80, 255" Font="font1" HitTest="false" />

    </Scene>

</xml>

4 thoughts on “The Worlds first 100% XML Mark-up Driven Puzzle Game made with XOML

  1. N David Brown says:

    “World’s first” is nonsense, this has been done many times before including by me in Lua using Corona SDK.

    That aside, it’s great you’ve provided so much for the community, however the lack of IwGX + shader support kills it for me.

    Look forward to monitoring future developments.

  2. drmop says:

    I think you may have not read the full title, the bit where it says “made with XOML”. The claim is merely a catchy title that does not put a claim over ALL mark-up languages, only XOML. I’m quite certain that many have for example created simple games in Microsoft XAML / Adobe MXML

    IwGame uses IwGx throughout, Iw2D rendering support was removed a while back. Shaders will be supported in the near future, but as IwGame is not currently in a monetised format, new features such as shaders will take time to arrive. (at the moment features appearing in iwGame are dependent on what our clients needs are). To eliminate this dependency our plans for monetising IwGame are in the works (hence our mark-up driven game and apps focus). We want to enable anyone with no technical coding knowledge to be able to put apps and games together using mark-up and assets only (future IwGame users wouldn’t even need a Marmalade license). This app is proof of concept stuff really, just to show (mainly to ourselves) that the idea works.

    Can you point me to a reference showing your mark-up engine using Corona? I’m very interested in taking a look at how you handle logic and animation etc..

  3. jorasso says:

    Mat, what you mean saying this:
    future IwGame users wouldn’t even need a Marmalade license

    Does it mean you consider something different to Marmalade as alternative? Can you disclose what you have in mind? I can strongly suggest you to take a look at Haxe NME then! It would be something really awesome!

    We could make small jam to make 100% game in XOML if there are others ready to participate;)

  4. drmop says:

    Never seen Haxe NME before, took a quick look and it looks pretty good, although as I understand it, a game or app requires compiling on each platform. Our system will initially use LUA (inclusion of other scripting languages later) mixed with XOML. We will provide more info soon when we have a working prototype of the final system ready.

Leave a Reply