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…
Basic Elements (playing field, pieces, movement, turns, players, ‘rules’, graphics, etc.)
Organize Interactions (between players and game, between game elements, rules enforcement, etc.)
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)
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
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" )'
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…
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).
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.