Tech Articles


Elevate 2017 session: Frogpond


Frogpond – A study in game design

By: Matt Balent

Presented at the Elevate 2017 conference.

 

This project began as an exercise with two goals: improve my knowledge of the datawindow ‘modify’ method and provide a simple computer ‘game’ for my four year old to use to improve her mouse skills.  When I was thrust into the role of PowerBuilder consultant in 2007 I knew I had to improve my overall skill level with PB in order to be successful.  An area I had not done much prior work was using the ‘describe’ and ‘modify’ methods to alter a datawindow when the application was running.

Over the years I have demonstrated this application at a variety of user group meetings and conference sessions, often as  a way to pass time.  One near universal reaction is ‘I never knew you could do that in PowerBuilder!’  The source code is provided for this (PB2017 format) application.

In many respects game design mirrors the processes used to create any type of software, especially if you use an ‘agile’ like methodology.  In that regard I encourage you to use this as an example of ‘outside the box’ thinking.

 

Game Design Primer

What is a Game?

Funny that there is no clear-cut definition of the term ‘game’, especially when you consider all of the various forms that games can take.  In Frogpond the goal is to move the frog to catch flys.

Goals and Purpose

These are some of the many questions which, at least in a cursory way, need to be asked.  Again, these are very similar to those asked during any application development (or should be).

How many players will there be?  (in Frogpond there is one)

How long should the game be? (Frogpond has no time limit or ‘turns’)

What choices will the player make, how and when will they make them? (in Frogpond the player can move the frog in any direction to catch the flies on the game board)

How will the players interact with each other? (not applicable in Frogpond)

How does the game progress? Is it strictly turn-based, or is it in rounds with phases? (not applicable in Frogpond)

What actions will the player be able to take? (player clicks on adjacent space to move frog or can ‘drag and drop’ it to an adjacent space)

How will the outcome of an action be determined? (if fly is eaten the frog sound is played and the ‘Flies Eaten counter advances)

What is the player's goal? (allow the frog to eat flies)

How can the player win? (not really a goal but you could say by eating all the flies)

Now the Hard Part

The following is a broad overview of the processes involved in creating the game.  As with any software this is an iterative cycle which goes on and on…

Content Development

                Basic Elements (playing field, pieces, movement, turns, players, ‘rules’, graphics, etc.)

Simplify

Organize Interactions (between players and game, between game elements, rules enforcement, etc.)

Build Prototype

Playtest (QA)

Feedback

 

Dissecting Frogpond

As someone who played a lot of wargames as a youth (Avalon Hill, SPI, among other publishers) I immediately thought of using a hex grid for movement in the game.  This gives a ‘more realistic’ looking movement for the pieces in a game.  This also involved some opportunity to modify the datawindow at runtime.

The other concept taken from the games I played is that each ‘piece’ in the game contained information about it, ie., movement abilities, strength, and current state.  This is a classic case for the Object Orientation principle of inheritance.

Main objects in application

Main window – w_frogpond

Datawindow (game board) – dw_1

Data object – d_hex3

Gamepiece NVO – n_gamepiece (ancestor)

N_Frog

N_Fly

N_Lillypad

N_Leaf

Datawindow object – d_hex3

Taking a look at the datawindow object you can see it has an external datasource.

For the initial version of the game I set up the dwo to have a set limit of twenty columns.  They are all the same size and evenly spaced.  Notice the space between each and between the top and bottom of the detail band.  This space is where the lines making up the hexagons will be drawn.

In order for the images to appear on the board each column object has the display as picture property checked.

In the ue_create_board event of the main window (w_frogpond) the first thing done is to modify the ‘X’ position of the column objects.  This is needed to make the hexagonal movement possible.  The code is:

ll_xpos = long(dw_1.describe(ls_control[ll_i] + '.X'))

ls_syntax = ls_control[ll_i] + '.X = "0~tif( mod( getRow(), 2 ) = 0, ' + string(ll_xpos - 160) + ', ' + string(ll_xpos) + ')"'    

// modify each contol by itself to keep things simple

dw_1.modify(ls_syntax)

 

The resulting dwo looks similar to the following partial example (in green to make the column boundaries show better):

Now the lines making up the hexagons are created.  One nice thing about this process is that you only need to draw three lines for each column – the other sides of the hex will be drawn when adjacent objects are created.  The only exception is the very last column on the right side of the board.  If you change the pen_color for each line within the ‘create line’ statements you can see the effect more clearly.

Example of one line object:

// line 1 'left side'

ll_x1odd = 1

ll_x2odd = 1

ll_x1even = 160

ll_x2even = 160

 

ls_syntax = &

'create line(band=detail x1="0~tif( mod( getRow(), 2 ) = 0, ' + string(ll_x1even + ((ll_i - 1) * ll_width)) + ', ' + string(ll_x1odd + ((ll_i - 1) * ll_width)) + &

')" y1="64" x2="0~tif( mod( getRow(), 2 ) = 0, ' + string(ll_x2even + ((ll_i - 1) * ll_width)) + ', ' + string(ll_x2odd + ((ll_i - 1) * ll_width)) + ')" y2="260"  name=l_1_' + string(ll_i) + ' visible="1" pen.style="0" pen.width="18" pen.color="33554432"  background.mode="1" background.color="553648127" )'

dw_1.modify(ls_syntax)        

 

Now there are other ways to make these lines; I did it the ‘hard way’ (trial and error) but I didn’t want to have to do the needed math at the time.

 

Remember you are only doing the modify and create line statements for each column in the dwo (in our case twenty).  When the rows are inserted at runtime the magical hamsters which run the datawindow engine do all the rest…

 

W_frogpond window

 

When the window opens the player options datastore is created and populated.  This is a way to allow for keeping some player information between games if desired.  The constructor event of dw_1 posts the ue_create_board event which begins the ‘meat’ of the board creation (the modify and create line statements) and the initial placement of the game pieces.

 

The majority of the code is on the window and the datawindow contained within it.  A stripped down version of the PFC is included in the application, mostly to allow for resizing of the application and for the file and string services.

 

The ‘gameplay’ code is primarily in the clicked and various drag/drop events.  This controls the positioning of the frog and the reaction to the cursor placement.

 

N_gamepiece – ancestor class

 

This contains all of the standard methods for all pieces in the game.  These involve setters/getters for the row and column the piece is on as well as the image it will display.  Of particular interest is the of_move function which determines how the piece will move based on the neighboring pieces adjacent to it.

 

N_leaf – no special code except of_dwclearimage

N_fly – implements of_capture which is called when the fly is eaten

N_lillypad – no special code

N_frog – has the most descendent code.  Most of the ‘tongue’ related methods are not fully tested or implemented (the idea was to have the frog appear to capture the adjacent flies with its tongue).

Finally

All of the code and graphics can be found at this Dropbox link.  The code is set up for PowerBuilder 2017.  There is also a recording of the session (mp4 format) and the slide deck.

Comments (0)
There are no comments posted here yet

Find Articles by Tag

Menu GhostScript JSONGenerator SqlExecutor Script Open Source 64-bit DataWindow Model Debug Repository C# Array Expression License UI Themes SVN SDK PowerServer Web WebBrowser RichTextEdit Control DataWindow JSON DLL PFC OLE Filter Installation Messagging Design External Functions .NET DataStore PowerScript (PS) CoderObject .NET Std Framework IDE API COM Configuration WinAPI Charts Performance REST Migration Import JSON Windows 10 Icons SOAP SqlModelMapper Export JSON PostgreSQL ODBC driver PDFlib .NET Assembly Encryption SnapDevelop SnapObjects Bug 32-bit DataType HTTPClient XML Platform MessageBox Excel Database Profile Database Import File Database Table Web Service Proxy Database Object Debugger PowerServer Mobile CI/CD Android Linux OS Database Connection Icon Event Handler OrcaScript Sort RibbonBar JSONParser Error Automated Testing PDF iOS TFS OAuth InfoMaker Stored Procedure Graph RibbonBar Builder SQL Server Mobile Authorization PBDOM NativePDF Web API CrypterObject Database Table Schema Text Class Database Painter Oracle SQL Source Code Authentication Windows OS Source Control Jenkins Debugging Variable Service BLOB Interface Export Testing RESTClient OAuth 2.0 Elevate Conference UI Modernization Syntax PowerBuilder Compiler ActiveX Validation PowerBuilder Branch & Merge Event Handling DragDrop DevOps Window TLS/SSL PostgreSQL JSON Transaction TortoiseGit PowerBuilder (Appeon) Application Deployment Event ODBC Trial Encoding Git Resize Azure UI Outlook TreeView Data Database Table Data