Click here to Skip to main content
16,019,109 members
Articles / Operating Systems / Windows / Win8

The Mothernode

Rate me:
Please Sign up or sign in to vote.
4.93/5 (4 votes)
17 Nov 2013CPOL10 min read 14.1K   1   5
Four player sci-fi maze game with traps and looting and that

This article is an entry in our AppInnovation Contest. Articles in this sub-section are not required to be full articles so care should be taken when voting.

Image 1Introduction 

The Mothernode is a new game where up to four players compete against each other, racing through a maze of random storage boxes to reach the safety node in the center of the room.  As they make their way towards the center the players receive bonuses that they can use to help themselves and to hinder the progress of others.  

The game is designed to be used on the Levono Horizon All-In-One PC and is a Round 2 Finalist within the "Games" category for the Intel® App Innovation Contest 2013. 

Update 

 [17th Nov 2013] Round 2 entry submitted, wish me luck. 

PROMOTIONAL VIDEO for ROUND 2  

[This is a promotional video showing the various features of the game]   

 

Update 

 [18th Oct 2013] New demo available to download and test. 

DOWNLOAD CURRENT DEMO [r02]  

[This download will also work on a non-touch-screen PC, but only in single player mode]

VIDEO OF THE DEMO IN ACTION  

[This video shows three rooms being played on EASY mode]  

Here are some screen shots from the latest demo with some explanations about what has changed. 

 Image 2

A back image has been added to make the title screen more interesting.  Having now tested on the AIO, there is a LOT of space to fill for the intro screen.  A back button has been added, together with a settings menu to change the difficulty.  The tutorial button has been added, but the actual tutorial functionality is still being worked on.

 Image 3

The hidden boxes sometimes take there colour from tiny 30x16 pixel images.  The one shown above shows my face.  This does absolutely nothing, but I felt it made the screen more interesting.  The invincible red block are also now hidden until they are in the line of sight of a player so make the screen more appealing. 

 Image 4

The end of level trigger has been made more exciting with the use of pyrotechnics!  The image above shows the yellow raider triggering all the box traps by reaching the Safety Node.  All the other players will be damaged as the wave of death reaches them.

 Image 5

The sequence been rooms have been changed to look prettier. It now shows the unlocked bonus for each room more clearly, it shows the winning colour for that room and it plays a little animation of the raiders walking from one room to the next.

Summary 

The game has been designed to take full advantage of the Lenovo Horizon All-In-One's magnificent 27" display, its high quality audio and its multi-tough features. 

Image 6  

The game is naturally optimised for four players to battle together, but the game also works with the AIO in desktop mode for single player experiences using the mouse. 

Image 7 

A short video of one round of the game can be found in the follwoing link.  The video shows the game running on a Windows8 Ultrabook:

VIDEO LINK  

 

The image below provides a quick overview of how every square inch of the 27" screen is used to produce a simple, competitive and thoroughly engaging experience.  

Image 8

The Gameplay 

The Mothenode gameplay consists of a number of simple tasks, the basics of which are explained below.

The Objective/Goal 

Each round starts with your raider in one of the four corners of the screen and your aim is to guide your raider to the safety zone in the center of the screen before any of the other raiders get there.  

Blocking your way is a large grid of storage boxes.  The only boxes you can see at the start are the red invincible boxes and the two boxes adjacent to your raider.  

 Image 9

Movement 

The game is played in real-time so you will need to guide your raider immediately.  To move your raider towards the safety zone your raider needs to break open the boxes in their path.  Boxes are targeted to be broken by touching them on the screen.  

Image 10

As boxes are broken open, some of them grant the player a bonus.  All the bonuses collected by the player sit along side the raider's start zone. 

 Image 11

Bonuses 

There are two types of bonuses the raider can collect and use: One-Shots and Decayers.

One-Shot bonuses are simply that, bonuses you can use once such as: 

  • Medipac (replenishes health),
  • Destroy 3-in-a-row (instantly destroys 3 containers in a line),
  • Block 3-in-a-row (sets 3 containers to invinciple, to nicely prevent your enemes from taking the easy route),
  • Set-Trap (makes the intended container a trap)  

Image 12Image 13Image 14Image 15


Decayer bonuses last for 30 seconds.  Sample Decayer bonuses are: 

  • Strength Boost (increases the damage inflicted on the containers)
  • Trap Sense (shades trapped containes red, the colour fades with use)
  • Beyond Sight (shows the the type of containers beyond the current line of sight  helpful for route planning - the distance seen reduces with use) 
  • Gas Mask (reduces the damage taken due to gas) 

Image 16Image 17Image 18Image 19

 

The bonuses are used in different ways.  Decayer bonuses are activated by tapping the bonus and the activated bonus then appears in the activated bonus area.  The image below shows the "Trap Sense" decayer bonus active and it also shows how trapped boxes are identified when this bonus is active. 

Image 20 

One-Shot bonuses are used by dragging them to specific boxes in the grid.  The image below shows the "3 in-a-row Destroy" bonus being dragged from the collected bonus area to the target box. 

Image 21 

Once armed, the 3 in-a-row bonus is dragged to a second target and released to activate.  

Image 22

Boobytraps 

As well as bonuses, some boxes also contain traps.  Explosion traps cause immediate injury to the raider and can also destroy adjacent boxes.  Poison gas traps are permanent traps that slowly poison the rider if they stand in the poisoned zone. 

Image 23 

 

End of Round Feedback 

Eventually a raider will reach the safety zone and trigger the end of the round.  When this happens, play stops and the players are given some feedback on their performance.  

Image 24

Game Rounds 

The game types are briefly described below, but they all consist of 2 phases:

Phase 1: Get to the Safety Node   

As explained above, players direct their raider by breaking boxes in their raider's path to the Safety none. 

Raiders can use their bonuses during the level by tapping and/or dragging them from the collected bonuses area to their target. 

Multi-touch is essential for this game as there will be many players taping and dragging from all four corners of the screen simultaneously.  

This phase ends when a raider reaches the Safety node. 

Phase 2: Trade your bonuses for a head-start 

After the room is cleared, the players get 10 seconds in which to trade any unused bonuses for a 5.0 second head-start over the other players, trade 2 bonuses and get a 10.0 second head-start and so forth. The unwanted bonuses are flicked into the center of the screen.  

This feature could be ideally combined with the AIO Striker, in a manner similar to that of dragging the winning chips in roulette or grabbing the balls in Hungry Hippos.   

This phase ends when the timer runs out. and the game restarts at phase 1.  

Game Types 

There are also five variations of the game planned for the final version, although only "Last Man Standing" will be available in the demo: 

  • Last Man Standing - The players make it through warehouse after warehouse until only one raider remains alive.
  • All For One - Unlike Last Man Standing players can only progress if everyone makes it to the Safenode. 
  • Clear Out - A variation of Last Man Standing, but The players must clear a set % of the containers in order to progress. 
  • Time Of Your Life - Another variation of Last Man Standing, but player must reach the Safenode before the time runs out. 
  • Of Sentinels - The warehouse also contains some simple sentinels that attack the riaders if they are spotted.  Like Last Man Standing again, but with added threat. 

Image 25  

Game Story   

Legend has it that the Champanova, the flagship of the once mighty Galactic Trading Corp (GTC), was filled with half the world’s wealth when it vanished from known space 50 years ago.  So when the ship mysteriously drifted back into the Solar System, apparently abandoned, there was a mass panic by the Earth's population to capture the craft and claim the treasure as their own.

You play as one of the many raiding individuals trying to reach the Mothernode – the control centre of the Champanova that will grant to access to the unimagineable wealth the ship contains. The location of the Mothernode is not known, lost in history as the databanks of the GTC were wiped as it went into financial meltdown and the few that knew the details were tortured to death . 

Raiders start at the docking bay of the Champanova and have to navigate their way through a series of connected warehouses. Access to the adjoining warehouses is controlled by a safety node in the middle of the storage area and has completely surrounded by storage containers. The safety node grants the user access to one of the connecting storage areas, but also activates the internal security systems which destroys all the remaining storage containers. 

To get to the safety node, raiders break open the storage containers in their path. Some containers are harder to break than others (wooden crates are easier than metal cases) and some are impossible to break. Containers can contain good and bad surprises. Some containers grant the raider bonuses (extra speed, extra health, cash, etc.) some causes problems (explosions, release toxic gas, etc.) which the raider either needs to flee from, or use protection against.  

The game is aimed at young adults and teenagers. Although there is no gore and there are no offensive weaponry, the tone of the game and its style is aimed at a more mature audience.   

Development  

The game is being developed using the App Game Kit (AGK) v108 from The Game Creators (TGC).  I chose this environment as I have previously developed a number of games using AGK and I find the language very easy to use and it provides some great functions to access the features of the AIO.

The example function below shows how the touch interaction is handled.

<span style="font-size: 12px; white-space: pre;">function DetermineTouchInput()
</span><span style="font-size: 12px; white-space: pre;">//determines what action each touch/release is related to</span><span style="font-size: 12px; white-space: pre;">
</span><span style="font-size: 12px; white-space: pre;">    // actions are:
</span><span style="font-size: 12px; white-space: pre;">    // * set target
</span><span style="font-size: 12px; white-space: pre;">    // * select/drag bonus
</span><span style="font-size: 12px; white-space: pre;">    // * arm a container
</span><span style="font-size: 12px; white-space: pre;">    // * trigger bonus</span><span style="font-size: 12px; white-space: pre;">
</span><span style="font-size: 12px; white-space: pre;">    beingtouched = getrawfirsttouchevent(1)</span><span style="font-size: 12px; white-space: pre;">
</span><span style="font-size: 12px; white-space: pre;">    while beingtouched>0</span><span style="font-size: 12px; white-space: pre;">
</span><span style="font-size: 12px; white-space: pre;">        if DebugON>0
</span><span style="font-size: 12px; white-space: pre;">            px# = screentoworldx(getrawtouchlastx(beingtouched))
</span><span style="font-size: 12px; white-space: pre;">            py# = screentoworldy(getrawtouchlasty(beingtouched))
</span><span style="font-size: 12px; white-space: pre;">            setspritepositionbyoffset(TouchCrossSpt,px#,py#)
</span><span style="font-size: 12px; white-space: pre;">            setspritevisible(TouchCrossSpt,1)
</span><span style="font-size: 12px; white-space: pre;">        else
</span><span style="font-size: 12px; white-space: pre;">            setspritevisible(TouchCrossSpt,0)
</span><span style="font-size: 12px; white-space: pre;">        endif</span><span style="font-size: 12px; white-space: pre;">
</span><span style="font-size: 12px; white-space: pre;">        if getrawtouchvalue(beingtouched)>0
</span><span style="font-size: 12px; white-space: pre;">            //dragging bonus
</span><span style="font-size: 12px; white-space: pre;">            //arming container
</span><span style="font-size: 12px; white-space: pre;">            //using bonus</span><span style="font-size: 12px; white-space: pre;">
</span><span style="font-size: 12px; white-space: pre;">            if getrawtouchreleased(beingtouched)=1
</span><span style="font-size: 12px; white-space: pre;">                //dropping bonus</span><span style="font-size: 12px; white-space: pre;">
</span><span style="font-size: 12px; white-space: pre;">                //find bonus and player
</span><span style="font-size: 12px; white-space: pre;">                usprite = getrawtouchvalue(beingtouched)
</span><span style="font-size: 12px; white-space: pre;">                for p=0 to 3
</span><span style="font-size: 12px; white-space: pre;">                    for b=1 to TotalBonusTypes
</span><span style="font-size: 12px; white-space: pre;">                        if PlayerBonus[p,b].count > 0
</span><span style="font-size: 12px; white-space: pre;">                            if PlayerBonus[p,b].UsingSprite = usprite
</span><span style="font-size: 12px; white-space: pre;">                                pid = p
</span><span style="font-size: 12px; white-space: pre;">                                bid = b</span><span style="font-size: 12px; white-space: pre;">
</span><span style="font-size: 12px; white-space: pre;">                                p = 3
</span><span style="font-size: 12px; white-space: pre;">                                b = TotalBonusTypes
</span><span style="font-size: 12px; white-space: pre;">                            endif
</span><span style="font-size: 12px; white-space: pre;">                        endif
</span><span style="font-size: 12px; white-space: pre;">                    next b
</span><span style="font-size: 12px; white-space: pre;">                next p</span><span style="font-size: 12px; white-space: pre;">
</span><span style="font-size: 12px; white-space: pre;">                //arming or using
</span><span style="font-size: 12px; white-space: pre;">                if bonus[bid].NeedsArm = 1
</span><span style="font-size: 12px; white-space: pre;">                    //check for container
</span><span style="font-size: 12px; white-space: pre;">                    px# = screentoworldx(getrawtouchlastx(beingtouched))
</span><span style="font-size: 12px; white-space: pre;">                    py# = screentoworldy(getrawtouchlasty(beingtouched))
</span><span style="font-size: 12px; white-space: pre;">                    //-32.0</span><span style="font-size: 12px; white-space: pre;">
</span><span style="font-size: 12px; white-space: pre;">                    xtile = trunc((px#+32)/64)
</span><span style="font-size: 12px; white-space: pre;">                    ytile = trunc((py#+32)/64)</span><span style="font-size: 12px; white-space: pre;">
</span><span style="font-size: 12px; white-space: pre;">                    ArmBonus(pid, bid, Xtile, Ytile)</span><span style="font-size: 12px; white-space: pre;">
</span><span style="font-size: 12px; white-space: pre;">                else
</span><span style="font-size: 12px; white-space: pre;">                    //trigger bonus
</span><span style="font-size: 12px; white-space: pre;">                    TriggerBonus(pid, bid, 0, 0, 0, 0)
</span><span style="font-size: 12px; white-space: pre;">                endif</span><span style="font-size: 12px; white-space: pre;">
</span><span style="font-size: 12px; white-space: pre;">                //clear dragged sprite
</span><span style="font-size: 12px; white-space: pre;">                usprite = getrawtouchvalue(beingtouched)
</span><span style="font-size: 12px; white-space: pre;">                if getspriteexists(usprite)=1 then deletesprite(usprite)
</span><span style="font-size: 12px; white-space: pre;">                setrawtouchvalue(beingtouched,0)</span><span style="font-size: 12px; white-space: pre;">
</span><span style="font-size: 12px; white-space: pre;">            else
</span><span style="font-size: 12px; white-space: pre;">                //just dragging</span><span style="font-size: 12px; white-space: pre;">
</span><span style="font-size: 12px; white-space: pre;">                usprite = getrawtouchvalue(beingtouched)
</span><span style="font-size: 12px; white-space: pre;">                px# = screentoworldx(getrawtouchcurrentx(beingtouched))
</span><span style="font-size: 12px; white-space: pre;">                py# = screentoworldy(getrawtouchcurrenty(beingtouched))
</span><span style="font-size: 12px; white-space: pre;">                //-32.0</span><span style="font-size: 12px; white-space: pre;">
</span><span style="font-size: 12px; white-space: pre;">                setspritepositionbyoffset(usprite,px#,py#)</span><span style="font-size: 12px; white-space: pre;">
</span><span style="font-size: 12px; white-space: pre;">            endif</span><span style="font-size: 12px; white-space: pre;">
</span><span style="font-size: 12px; white-space: pre;">        else
</span><span style="font-size: 12px; white-space: pre;">            //selecting target container
</span><span style="font-size: 12px; white-space: pre;">            //selecting bonus
</span><span style="font-size: 12px; white-space: pre;">            //draging armed container</span><span style="font-size: 12px; white-space: pre;">
</span><span style="font-size: 12px; white-space: pre;">            if getrawtouchreleased(beingtouched)=1
</span><span style="font-size: 12px; white-space: pre;">                //activating?
</span><span style="font-size: 12px; white-space: pre;">                //targetting?</span><span style="font-size: 12px; white-space: pre;">
</span><span style="font-size: 12px; white-space: pre;">                px# = screentoworldx(getrawtouchstartx(beingtouched))
</span><span style="font-size: 12px; white-space: pre;">                py# = screentoworldy(getrawtouchstarty(beingtouched))
</span><span style="font-size: 12px; white-space: pre;">                //-32.0</span><span style="font-size: 12px; white-space: pre;">
</span><span style="font-size: 12px; white-space: pre;">                //translate to tile coordinates
</span><span style="font-size: 12px; white-space: pre;">                xtile = trunc((px#+32)/64)
</span><span style="font-size: 12px; white-space: pre;">                ytile = trunc((py#+32)/64)</span><span style="font-size: 12px; white-space: pre;">
</span><span style="font-size: 12px; white-space: pre;">                if StorageArea[xtile,ytile].armbonusid>0
</span><span style="font-size: 12px; white-space: pre;">                    //tile is armed
</span><span style="font-size: 12px; white-space: pre;">                    xend# = screentoworldx(getrawtouchlastx(beingtouched))
</span><span style="font-size: 12px; white-space: pre;">                    yend# = screentoworldy(getrawtouchlasty(beingtouched))
</span><span style="font-size: 12px; white-space: pre;">                    //-32.0
</span><span style="font-size: 12px; white-space: pre;">                    xetile = trunc((xend#+32)/64)
</span><span style="font-size: 12px; white-space: pre;">                    yetile = trunc((yend#+32)/64)</span><span style="font-size: 12px; white-space: pre;">
</span><span style="font-size: 12px; white-space: pre;">                    dext = trunc(xetile - xtile)
</span><span style="font-size: 12px; white-space: pre;">                    deyt = trunc(yetile - ytile)</span><span style="font-size: 12px; white-space: pre;">
</span><span style="font-size: 12px; white-space: pre;">                    if StorageArea[xtile,ytile].armbonusid=1 or StorageArea[xtile,ytile].armbonusid=2
</span><span style="font-size: 12px; white-space: pre;">                        //3 in a row</span><span style="font-size: 12px; white-space: pre;">
</span><span style="font-size: 12px; white-space: pre;">                        if abs(dext)>2.0 or abs(deyt)>2.0
</span><span style="font-size: 12px; white-space: pre;">                            p = StorageArea[xtile,ytile].armplayerid
</span><span style="font-size: 12px; white-space: pre;">                            b = StorageArea[xtile,ytile].armbonusid</span><span style="font-size: 12px; white-space: pre;">
</span><span style="font-size: 12px; white-space: pre;">                            if abs(dext)>abs(deyt)
</span><span style="font-size: 12px; white-space: pre;">                                if dext>0
</span><span style="font-size: 12px; white-space: pre;">                                    dext = xtile+2
</span><span style="font-size: 12px; white-space: pre;">                                else
</span><span style="font-size: 12px; white-space: pre;">                                    dext = xtile
</span><span style="font-size: 12px; white-space: pre;">                                    xtile = xtile-2
</span><span style="font-size: 12px; white-space: pre;">                                endif
</span><span style="font-size: 12px; white-space: pre;">                                deyt = ytile
</span><span style="font-size: 12px; white-space: pre;">                            else
</span><span style="font-size: 12px; white-space: pre;">                                dext = xtile
</span><span style="font-size: 12px; white-space: pre;">                                if deyt>0
</span><span style="font-size: 12px; white-space: pre;">                                    deyt = ytile+2
</span><span style="font-size: 12px; white-space: pre;">                                else
</span><span style="font-size: 12px; white-space: pre;">                                    deyt = ytile
</span><span style="font-size: 12px; white-space: pre;">                                    ytile = ytile-2
</span><span style="font-size: 12px; white-space: pre;">                                endif</span><span style="font-size: 12px; white-space: pre;">
</span><span style="font-size: 12px; white-space: pre;">                            endif</span><span style="font-size: 12px; white-space: pre;">
</span><span style="font-size: 12px; white-space: pre;">                            TriggerBonus(p, b, xtile, ytile, dext, deyt)
</span><span style="font-size: 12px; white-space: pre;">                        endif
</span><span style="font-size: 12px; white-space: pre;">                    endif</span><span style="font-size: 12px; white-space: pre;">
</span><span style="font-size: 12px; white-space: pre;">                    if StorageArea[xtile,ytile].armbonusid=5 or StorageArea[xtile,ytile].armbonusid=12
</span><span style="font-size: 12px; white-space: pre;">                        //2 x 2
</span><span style="font-size: 12px; white-space: pre;">                        if abs(dext)>1.0 and abs(deyt)>1.0
</span><span style="font-size: 12px; white-space: pre;">                            p = StorageArea[xtile,ytile].armplayerid
</span><span style="font-size: 12px; white-space: pre;">                            b = StorageArea[xtile,ytile].armbonusid</span><span style="font-size: 12px; white-space: pre;">
</span><span style="font-size: 12px; white-space: pre;">                            if dext>0.0
</span><span style="font-size: 12px; white-space: pre;">                                dext = xtile+1
</span><span style="font-size: 12px; white-space: pre;">                            else
</span><span style="font-size: 12px; white-space: pre;">                                dext = xtile
</span><span style="font-size: 12px; white-space: pre;">                                xtile = xtile-1
</span><span style="font-size: 12px; white-space: pre;">                            endif</span><span style="font-size: 12px; white-space: pre;">
</span><span style="font-size: 12px; white-space: pre;">
</span><span style="font-size: 12px; white-space: pre;">                            if deyt>0.0
</span><span style="font-size: 12px; white-space: pre;">                                deyt = ytile+1
</span><span style="font-size: 12px; white-space: pre;">                            else
</span><span style="font-size: 12px; white-space: pre;">                                deyt = ytile
</span><span style="font-size: 12px; white-space: pre;">                                ytile = ytile-1
</span><span style="font-size: 12px; white-space: pre;">                            endif</span><span style="font-size: 12px; white-space: pre;">
</span><span style="font-size: 12px; white-space: pre;">                            TriggerBonus(p, b, xtile, ytile, dext, deyt)
</span><span style="font-size: 12px; white-space: pre;">                        endif
</span><span style="font-size: 12px; white-space: pre;">                    endif</span><span style="font-size: 12px; white-space: pre;">
</span><span style="font-size: 12px; white-space: pre;">                    if StorageArea[xtile,ytile].armbonusid=6 or StorageArea[xtile,ytile].armbonusid=11
</span><span style="font-size: 12px; white-space: pre;">                        //single square
</span><span style="font-size: 12px; white-space: pre;">                        p = StorageArea[xtile,ytile].armplayerid
</span><span style="font-size: 12px; white-space: pre;">                        b = StorageArea[xtile,ytile].armbonusid</span><span style="font-size: 12px; white-space: pre;">
</span><span style="font-size: 12px; white-space: pre;">                        TriggerBonus(p, b, xtile, ytile, xtile, ytile)
</span><span style="font-size: 12px; white-space: pre;">                    endif</span><span style="font-size: 12px; white-space: pre;">
</span><span style="font-size: 12px; white-space: pre;">                    StorageArea[xtile,ytile].armbonusid = 0</span><span style="font-size: 12px; white-space: pre;">
</span><span style="font-size: 12px; white-space: pre;">                else
</span><span style="font-size: 12px; white-space: pre;">                    //not armed so target?</span><span style="font-size: 12px; white-space: pre;">
</span><span style="font-size: 12px; white-space: pre;">                    tsprite = StorageArea[xtile,ytile].ShroudSpriteID
</span><span style="font-size: 12px; white-space: pre;">                    class = StorageArea[xtile,ytile].class
</span><span style="font-size: 12px; white-space: pre;">                    if getspriteexists(tsprite)=0 and class>0 and xtile>1 and xtile<areamaxx and ytile>1 and ytile<areamaxy
</span><span style="font-size: 12px; white-space: pre;">                        //target
</span><span style="font-size: 12px; white-space: pre;">                        for p=0 to 3
</span><span style="font-size: 12px; white-space: pre;">                            if player[p].health>0
</span><span style="font-size: 12px; white-space: pre;">                                psx# = getspritexbyoffset(Player[p].spriteid)
</span><span style="font-size: 12px; white-space: pre;">                                psy# = getspriteybyoffset(Player[p].spriteid)</span><span style="font-size: 12px; white-space: pre;">
</span><span style="font-size: 12px; white-space: pre;">                                tdist# = sqrt(((psx#-px#)^2)+((psy#-py#)^2))</span><span style="font-size: 12px; white-space: pre;">
</span><span style="font-size: 12px; white-space: pre;">                                if dist#=0.0
</span><span style="font-size: 12px; white-space: pre;">                                    dist# = tdist#
</span><span style="font-size: 12px; white-space: pre;">                                    selectedplayer = p
</span><span style="font-size: 12px; white-space: pre;">                                else
</span><span style="font-size: 12px; white-space: pre;">                                    if tdist#<dist#
</span><span style="font-size: 12px; white-space: pre;">                                        dist# = tdist#
</span><span style="font-size: 12px; white-space: pre;">                                        selectedplayer = p
</span><span style="font-size: 12px; white-space: pre;">                                    endif
</span><span style="font-size: 12px; white-space: pre;">                                endif
</span><span style="font-size: 12px; white-space: pre;">                            else
</span><span style="font-size: 12px; white-space: pre;">                                spriteid = Player[p].TargetSpriteID
</span><span style="font-size: 12px; white-space: pre;">                                setspritevisible(spriteid,0)
</span><span style="font-size: 12px; white-space: pre;">                            endif
</span><span style="font-size: 12px; white-space: pre;">                        next p</span><span style="font-size: 12px; white-space: pre;">
</span><span style="font-size: 12px; white-space: pre;">                        //set target box
</span><span style="font-size: 12px; white-space: pre;">                        spriteid = Player[selectedplayer].TargetSpriteID
</span><span style="font-size: 12px; white-space: pre;">                        setspritevisible(spriteid,1)
</span><span style="font-size: 12px; white-space: pre;">                        setspritepositionbyoffset(spriteid,xtile*64,ytile*64)
</span><span style="font-size: 12px; white-space: pre;">                        Player[selectedplayer].TargetX = getspritexbyoffset(spriteid)
</span><span style="font-size: 12px; white-space: pre;">                        Player[selectedplayer].TargetY = getspriteybyoffset(spriteid)</span><span style="font-size: 12px; white-space: pre;">
</span><span style="font-size: 12px; white-space: pre;">                    endif</span><span style="font-size: 12px; white-space: pre;">
</span><span style="font-size: 12px; white-space: pre;">                endif</span><span style="font-size: 12px; white-space: pre;">
</span><span style="font-size: 12px; white-space: pre;">            else
</span><span style="font-size: 12px; white-space: pre;">                //selecting bonus?
</span><span style="font-size: 12px; white-space: pre;">                px# = screentoworldx(getrawtouchstartx(beingtouched))
</span><span style="font-size: 12px; white-space: pre;">                py# = screentoworldy(getrawtouchstarty(beingtouched))
</span><span style="font-size: 12px; white-space: pre;">                //-32.0</span><span style="font-size: 12px; white-space: pre;">
</span><span style="font-size: 12px; white-space: pre;">                //translate to tile coordinates
</span><span style="font-size: 12px; white-space: pre;">                xtile = trunc((px#+32)/64)
</span><span style="font-size: 12px; white-space: pre;">                ytile = trunc((py#+32)/64)</span><span style="font-size: 12px; white-space: pre;">
</span><span style="font-size: 12px; white-space: pre;">                if StorageArea[xtile,ytile].armbonusid>0
</span><span style="font-size: 12px; white-space: pre;">                    //armed
</span><span style="font-size: 12px; white-space: pre;">                    lx# = (64.0*xtile)-32.0
</span><span style="font-size: 12px; white-space: pre;">                    ly# = (64.0*ytile)-32.0</span><span style="font-size: 12px; white-space: pre;">
</span><span style="font-size: 12px; white-space: pre;">                    ex# = screentoworldx(getrawtouchcurrentx(beingtouched))-32.0
</span><span style="font-size: 12px; white-space: pre;">                    ey# = screentoworldy(getrawtouchcurrenty(beingtouched))-32.0</span><span style="font-size: 12px; white-space: pre;">
</span><span style="font-size: 12px; white-space: pre;">                    DrawLine( lx#, ly#, ex#, ey#, 255, 0, 0 )
</span><span style="font-size: 12px; white-space: pre;">                else
</span><span style="font-size: 12px; white-space: pre;">                    //select bonus
</span><span style="font-size: 12px; white-space: pre;">                    //if py#<96 or py#>((areamaxy-1)*64)
</span><span style="font-size: 12px; white-space: pre;">                        for p=0 to 3
</span><span style="font-size: 12px; white-space: pre;">                            for b=1 to TotalBonusTypes
</span><span style="font-size: 12px; white-space: pre;">                                if PlayerBonus[p,b].count > 0
</span><span style="font-size: 12px; white-space: pre;">                                    bspriteid = PlayerBonus[p,b].spriteid
</span><span style="font-size: 12px; white-space: pre;">                                    if getspritehittest(bspriteid,px#,py#)=1
</span><span style="font-size: 12px; white-space: pre;">                                        //clone sprite
</span><span style="font-size: 12px; white-space: pre;">                                        usprite = clonesprite(bspriteid)
</span><span style="font-size: 12px; white-space: pre;">                                        setspritedepth(usprite,1)
</span><span style="font-size: 12px; white-space: pre;">                                        //PlayerBonus[p,b].UsingOffsetX = px# - getspritexbyoffset(bspriteid)
</span><span style="font-size: 12px; white-space: pre;">                                        //PlayerBonus[p,b].UsingOffsetY = py# - getspriteybyoffset(bspriteid)
</span><span style="font-size: 12px; white-space: pre;">                                        PlayerBonus[p,b].UsingSprite = usprite
</span><span style="font-size: 12px; white-space: pre;">                                        setrawtouchvalue(beingtouched,usprite)
</span><span style="font-size: 12px; white-space: pre;">                                        p = 3
</span><span style="font-size: 12px; white-space: pre;">                                        b = TotalBonusTypes
</span><span style="font-size: 12px; white-space: pre;">                                    endif
</span><span style="font-size: 12px; white-space: pre;">                                endif
</span><span style="font-size: 12px; white-space: pre;">                            next b
</span><span style="font-size: 12px; white-space: pre;">                        next p
</span><span style="font-size: 12px; white-space: pre;">                    //endif
</span><span style="font-size: 12px; white-space: pre;">                endif
</span><span style="font-size: 12px; white-space: pre;">            endif</span><span style="font-size: 12px; white-space: pre;">
</span><span style="font-size: 12px; white-space: pre;">        endif
</span><span style="font-size: 12px; white-space: pre;">        beingtouched = getrawnexttouchevent()</span><span style="font-size: 12px; white-space: pre;">
</span><span style="font-size: 12px; white-space: pre;">    endwhile</span><span style="font-size: 12px; white-space: pre;">
</span><span style="font-size: 12px; white-space: pre;">endfunction</span> <span style="color: black; font-family: Consolas, 'Courier New', Courier, mono; font-size: 9pt; white-space: pre;"</span>

The code above is reasonably complicated to follow, but it captures ALL of the touch commands within the game.  These commands include:

  • Setting the raider's target,
  • Activating a decayer bonus,
  • Dragging a one-shot bonus to arm a box, 
  • Dragging a one-shot bonus from an armed box,
What makes these commands more complicated is the fact that any one of four players could be making them, at the same time.  Luckily the AGK language makes this possible with commands such as:

beingtouched = getrawfirsttouchevent(1

The environment automatically detects the touch events and stores them in a list.  The touch events are also catagorized into "short", "long", "drag" and "unknown" types. The command getrawfirsttouchevent(1) extracts the first touch event from this list.  The parameter "1" allows all touch events, including the "unknown" types to be return.

The following parameters can be extracted, once the touch event ID has been returned:

  • Start co-ordinates,
  • Current co-ordinates,
  • Last co-ordinates (kept for touch events that have been released)
  • Released status,
  • Duration of touch. 

These features of the BASIC language enable me to develop the game quickly.  

The entire loop to control each round is shown below: 

repeat
    FrameTime = (getframetime()*1000)
    SecondCounter = SecondCounter-FrameTime

    CheckTraps()
    DetermineTouchInput()

    if HumanPlayers<4
        for p=0 to 3
            if player[p].Human < 1 and player[p].health>0
                MoveAI(p)
            endif
        next p
    endif

    MovePlayers()
    AnimatePlayers()
    MoveNewBonuses()
    ManageActiveBonuses()

    if SecondCounter<=0
        for p=0 to 3
            PlayerLevelStats[p].TimeTaken = PlayerLevelStats[p].TimeTaken +1
        next p
        SecondCounter = SecondCounter+1000
    endif

    if getrawkeyreleased(13)=1 then FakeExit = 1

    sync()

until InNodeZone>3000 or FakeExit = 1

The AGK IDE compiles the source code into byte code which an optimised "player" runs.  The player utilises Open GL and has been specifically developed to maintain a high frame rate when handing a large number of sprites. 

The game started development in early August and has already reached a playable mature alpha-state, as the video link shows and which can be downloaded here.  To ensure compatibility of the game with the AIO PC, the game is being developed and tested on a Windows8 Ultrabook.  An Ultrabook is being used primarily since this device also has a touch-screen interface and has the same operating system as the AIO.  Although the screen size and overall power of the Ultrabook is inferior to the AIO, these restrictions ensure a high level of performance will be possible on the AIO and development on a second machine ensures the development environment does not influence the installation of the game. 

Points of Interest 

The Lenovo All-In-One PC is ideal for running multi-player borard-games such as the Mothernode.  The Mothernode game takes full advantage of the AIO's large 27" screen, its 10-point multi-touch feature and it's Striker accessories to give the players a real-time multi-player board game experience like no other. 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Systems Engineer None
United Kingdom United Kingdom
By day I am a Spacecraft engineer working on interplanetary spacecraft.

By night I write games.

I have been designing games since I was a teenager, starting with board games before branching out into the digital world.

I started video game creation using SEUCK on the Atari ST, making a number of small/ simple shoot-em-ups. I then moved onto using Visual Basic and VBA becoming an active member of the XL gaming forum and creating a number of games such as Cim Sity, Nations and a multiplayer version of Champ Manager (A Football/Soccer management game) within Microsoft Excel. I still play Champy today with my friends and continue to develop it some 5 years after I started writing it.

More recently I have been developing games using Dark Basic Professional (DBPro) and the App Game Kit (AGK). With the former I have created a number of FPS's and released a number of free demos. With the later I have created a number of 2D games for the Blackberry PlayBook and for Windows 8 Ultrabooks.

Comments and Discussions

 
QuestionHow's app development going? Will you be submitting on time? Pin
Kevin Priddle24-Oct-13 5:11
professionalKevin Priddle24-Oct-13 5:11 
AnswerRe: How's app development going? Will you be submitting on time? Pin
Ched198025-Oct-13 5:44
professionalChed198025-Oct-13 5:44 
GeneralRe: How's app development going? Will you be submitting on time? Pin
Kevin Priddle25-Oct-13 10:50
professionalKevin Priddle25-Oct-13 10:50 
GeneralMy vote of 5 Pin
Steve Vink10-Sep-13 21:24
professionalSteve Vink10-Sep-13 21:24 
A great and unique looking "board game" that can only be executed digitally. This is definitely one of the new breed of games that is only possible now with the new hardware. Good Luck!
AdminThanks! Pin
Kevin Priddle19-Aug-13 4:39
professionalKevin Priddle19-Aug-13 4:39 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.