Why Fragmenti?
- I admit that I had not heard of Microsoft Silverlight until quite recently
- which I understand is not uncommon for those outside of the 'MIX' crowd. Even
today, close computer geeks and freaks look blank when I mention Silverlight! However,
when I realised that there was a way to move web pages away from the restrictions
of classic HTML, I was excited to explore Silverlight's potential.
- To understand Silverlight's scope, instead of just building a page that was something
like a blue square in a yellow circle, with an orbiting red triangle, I felt the
need to build something real world, something useful. I'd already seen the
odd drag-and-drop demo, but nothing with the infrastructure of a full feature web
application.
- Enter Fragmenti.
- So after downloading a few megabytes of Shawn Wildermuth and Jesse Liberty
how-to videos, plus doing just a tiny bit of coding, you're finally reading
this. Thanks for stopping by :-)
Why Silverlight 1.0?
- I've already raised a few eyebrows by using the outmoded Silverlight One,
but back in the day, that's March 2008, Silverlight Beta 2 was called 1.1, and was
in a state of 'evolution'. Also, I initially intended to build Fragmenti so it would
not require a windows server to run it on. As it turns out, my friendly web host
can do all of that Beta 2 stuff, sans agro. Oh well, there's always next time!
What no source code?
- To be honest, Fragmenti was at the toe of my Silverlight learning curve experience,
and some of my coding at both client and server sides reflects this! So it would be
unfair to inflict my bad habits on you good people :-)
Schematics 4 U
- The pretty picture below outlines Fragmenti's functional environment - suit
speak for, how it kind of works.
- As is typical these days, the application has a server side and client
side. And between each side occurs an asynchronous interaction; once
the Fragmenti application has successfully loaded onto the client's browser.
- The core server side element is a Custom API, which provides database table
interaction and server error handling. It is interrogated by a number of custom
request handlers written in ASP.NET
- The core client side elements are the Fragmenti Script Engine, which is a
Javascript program and, the Silverlight Plug-in, which refines raw XAML (zam-ill)
into nice blobs of coloured stuff.
- Furthermore, although not illustrated, the Fragmenti Script Engine provides its
own error handling and soft landing scheme: if a script error occurs, a client
is presented with the option of reporting the error back to the server.
- Puzzle information and player scores are held on a MySQL Database. Menus
and scores are requested by the client using the Download Manager (see code
notes below) which, calls the server side Menu Handler or, the Score Handler.
On reflection these two coulda woulda shoulda been a dedicated http handler - or
even a web service!
- Information is returned by the server as a JSON stream - Javascript Object Notion
is a very efficient method of passing serialised data to a web client where the
client is running Javascript. After the script engine has eval-uated the
Json stream, it generates XAML (createFromXaml), which is rendered as either as
the puzzle menu or, the scoreboard.
- Fragmented images for game play are downloaded to the client on-demand
and, are packaged up into a ZIP file. Compressed file handling is one of
Silverlight's cooler features and is ideal for delivering a multiplicity
of image fragments. Before a game starts, the image fragments are unpacked
from the ZIP file and then dynamically rendered onto the puzzle board.
- Another cool Silverlight feature is the ability to download custom fonts
without having to ask the client to agree to their installation. You'll note how
Fragmenti's status display features a dot matrix-like text. This font is called
Dotimatrix5 and was created by tepidmonkey.com, which is free for
distribution with not-for-profit applications.
- Of course, player interaction is initiated with mouse events, such as mouse down
(drag), and mouse up (drop). Setting the user alias name uses the keyboard input
event. Although the lack of a 'text box' like construct in Silverlight 1.0 is a
deficiency. And so too is the lack of a scrollbar. I had to go back to basics and
create my own!
- Finally, the Fragmenti Script Engine hires and fires a number of animation sequences
which make things gracefully appear and disappear, and, makes the game play buttons
slide in and out of view.

Code notes
- So how are the scores calculated? Simply, no points awarded if either the time to
complete is exceeded or, the numbers of moves exceeds the total number of squares
plus, a weighting for the rating of the puzzle. Where points are awarded, this is
calculated from time taken as a percentage multiplied by moves taken. So, although the
speed at which a puzzle is solved matters, the fewer the moves the better. I tried a
number of methods, some simpler, some far more complex, but this one seems to work.
As for the puzzle rating, that's just some value I assign from easy to extreme, based on
the number of squares and the visual complexity of the image.
Some will have noticed that by repeatedly clicking the jumble button, the squares
will arrange themselves in almost the correct order. The chance of randomly
landing on a correct solution is 1 in 81 for a 9 square puzzle, 1 in 256 for a 16
square puzzle, etc.
- To handle the numerous imports of images packages and Json streams, I coded a single
'download manager' which can be called from around the Script Engline. It builds
a download object which then sends out an XMLHttpRequest to the specified
Uri. On a download response, it routes to the specified function for success or
failure.
// Download manager...Globals...
var LoadManagerTimer = new Object()
var LoadManagerCompletedToken = 0
var LoadManagerFailedToken = 0
var LoadManager = new Object()
var LoadManagerMsg = ''
var ImportCallbackOkay = null
var ImportCallbackFail = null
// fn : LoadManagerDownload : Start async download to client...
//
// targetUri - for example: 'http://silverlight.net/throws.aspx?id=bigexception
// onOkay - the name of the function to call when the download is complete
// onFail - the name of the function to call if the download fails
// [ timeoutSec ] - a watchdog timeout period in seconds
function LoadManagerDownload(targetUri,onOkay,onFail,timeoutSec){ try {
LoadManagerMsg = ''
LoadManagerCallbackOkay = onOkay
LoadManagerCallbackFail = onFail
LoadManager = Plugin.createObject('downloader')
LoadManagerCompletedToken = LoadManager.addEventListener('Completed',LoadManagerCompleted)
LoadManagerFailedToken = LoadManager.addEventListener('DownloadFailed',LoadManagerFaulted)
LoadManager.open('GET',targetUri)
LoadManager.send()
// Go Ajax baby, go.
if ( typeof(timeoutSec) == 'number'){ LoadManagerTimer = setTimeout('LoadManagerTimeout',
(1000 * timeoutSec )) }
} catch(e) {}
// fn : LoadManagerFaulted : Handle failed download callback...
function LoadManagerFaulted(sender){ try {
clearTimeout(LoadManagerTimer)
LoadManager.removeEventListener('completed',LoadManagerCompletedToken)
LoadManagerMsg = 'Could not complete download. Network reported ' + LoadManager.StatusText
LoadManager.abort()
LoadManagerCallbackFail(null)
} catch(e) {}
// fn : LoadManagerTimeout : Handle download timeout event...
function LoadManagerTimeout(){ try {
LoadManager.removeEventListener('Completed',LoadManagerCompletedToken)
LoadManager.removeEventListener('DownloadFailed',LoadManagerFailedToken)
LoadManager.abort()
LoadManagerMsg = 'Download time exceeded'
LoadManagerCallbackFail(null)
} catch(e) {}
// fn : LoadManagerCompleted : Handle download callback...
function LoadManagerCompleted(sender){ try {
if ( sender.Status != '200' ) {
LoadManagerMsg = 'No content received'
LoadManagerCallbackFail(null)
} else { LoadManagerCallbackOkay(sender) } // Action the next function
} catch(e) {}
// Example usage: Here, the package for a puzzle is to be loaded. On success LoadPuzzleCallback is called and
// the puzzle created. LoadPuzzleFailed provides a soft landing if the package is 404 or, download times out.
LoadManagerDownload(ContentPackUri, LoadPuzzleCallback, LoadPuzzleFailed, ContentTimeout)
- I found the x:name reference sender.findName('xyz').setValue('abc','123')
could be shorthanded with a string prototype.
// With global variable Root pointing to the plugin,
String.prototype.setX =
function(i,j,k){Root.findName((typeof(k)=='string')?k+this.toString():this.toString()).setValue(i,j)}
// means code can be shorthanded to, for example,
'pnlPlayGhostImage'.setX('Visibility','Visible')
// And likewise this prototype returns a value from an x:name,
String.prototype.getX =
function(i,k){return Root.findName((typeof(k)=='string')?k+this.toString():this.toString()).getValue(i)}
z = 'pnlPlayGhostImage'.getX('Visibility')
What next?
- A Fragmenti version 2 written in Silverlight 2, with managed code, skins, data-binding,
web-services... probably :-)
But there's also Expression Blend and PopFly to play with first!
Any questions?
Andrew J. Brown - June 2008