Whilst the days of selecting your game from the shelf, placing the cassette in the deck, pressing SHIFT + RUN/STOP and following the on screen instruction to PRESS PLAY ON TAPE before sitting back to marvel at some classic 8-bit artwork accompanied my another Rob Hubbard masterpiece (or going downstairs to make a nice cup of tea) are long gone, the pre-loader very much lives on with Flash games (all be it with a heavy advert bias).
I wanted to ceate a preloader that captured the fit inducing brilliance of those early loading screens with their multicoloured fast loaders forcing data through video memory. You can see it on the front page of the new site as the preloader for the retro TV. It’s also resized and used between each video that the retro TV shows.
Our splash screen will be represented by a single class called, imaginitvely, Splash. Not only will the height, width and border size be configurable but also the foreground & progress bar colours, logo and link URL will be assigned from parameters so that it can easily be branded. I use FlashDevelop to work with ActionScript 3 and the Flex SDK compiler. You can download the full source code here as a zipped FlashDevelop project. The splash screen classes will be placed in the lib/com/buzzardgames directory for use by many applications.
The splash screen itself is made up of two elements: the foreground screen area and the border of flashing lines. These will be represented by the two classes: Foreground and LoaderLines.
The Foreground class has two elements: a clickable logo and a progress bar. These will be represented by two further classes: Logo and ProgressBar.
The Logo class simply displays the supplied bitmap and adds a click event to open the supplied URL in a browser:
package com.buzzardgames.splash
{
import flash.display.Bitmap;
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.net.navigateToURL;
import flash.net.URLRequest;
/**
* A clickable logo that opens a URL in a browser window
*/
internal class Logo extends Sprite
{
private var _url:String; // The URL to open when the logo is clicked
/**
* Constructor for a logo link
*
* @param a_logo The bitmap for use as a logo
* @param a_url The url for the logo
*/
public function Logo(a_logo:Bitmap, a_url:String)
{
_url = a_url;
buttonMode = true;
useHandCursor = true;
addChild(a_logo);
addEventListener(MouseEvent.CLICK, logoClicked);
}
/**
* Remove event handlers
*/
public function dispose():void
{
removeEventListener(MouseEvent.CLICK, logoClicked);
}
/**
* Open a browser window when the logo is clicked
*
* @param a_event Mouse click event
*/
private function logoClicked(a_event:MouseEvent):void
{
var urlRequest:URLRequest = new URLRequest(_url);
navigateToURL(urlRequest, "_blank");
}
}
}
The Logo class also has a dispose method to remove the event handler called once we’re done with the class.
The ProgressBar class displays a rectangle in the designated colours that fills as calls are made to it’s update method. The update method accepts a fractional value representing the proportion of data loaded/time passed since the progress bar began:
package com.buzzardgames.splash
{
import flash.display.Sprite;
/**
* A progress bar
*/
internal class ProgressBar extends Sprite
{
private const _BORDER_SIZE:uint = 1; // The size of the border
private var _colorBackground:uint; // The background colour
private var _colorProgressBar:uint; // The colour of the border and progress bar
private var _height:uint; // The height of the progress bar
private var _width:uint; // The width of the progress bar
/**
* Constructor for a progress bar
*
* @param a_height The height of the splash screen foreground area
* @param a_width The width of the splash screen foreground area
* @param a_colorBackground The color of the foreground area
* @param a_colorProgressBar The color of the progress bar
*/
public function ProgressBar(a_height:uint, a_width:uint, a_colorBackground:uint, a_colorProgressBar:uint)
{
_height = a_height;
_width = a_width;
_colorBackground = a_colorBackground;
_colorProgressBar = a_colorProgressBar;
}
/**
* Called to update the progress bar with the fraction of data loaded
*
* @param a_fraction The fraction of loading complete (0.00 - 1.00)
*/
public function update(a_fraction:Number):void
{
// Set the border style
graphics.lineStyle(_BORDER_SIZE, _colorProgressBar);
// Draw the border and background
graphics.beginFill(_colorBackground);
graphics.drawRect(0, 0,_width, _height);
graphics.endFill();
// Draw the inside bar proportional to the fraction of loading complete
graphics.beginFill(_colorProgressBar);
graphics.drawRect(0, 0, Math.round(a_fraction * _width), _height);
graphics.endFill();
}
}
}
These two classes are displayed by the Foreground class on a rectangle in the designated colour. The Foreground class also defines an update method that calls the ProgressBar update method and the Logo dispose method:
package com.buzzardgames.splash
{
import flash.display.Bitmap;
import flash.display.Sprite;
/**
* A screen area containing a clickable logo and a progress bar
*/
internal class Foreground extends Sprite
{
private var _logo:Logo; // The logo
private var _progressBar:ProgressBar; // The progress bar
/**
* Constructor for a foreground area
*
* @param a_height The height of the foreground area
* @param a_width The width of the foreground area
* @param a_color The color of the foreground area
* @param a_colorProgressBar The color of the progress bar
* @param a_logo The bitmap for use as a logo
* @param a_url The url for the logo
*/
public function Foreground(a_height:uint, a_width:uint, a_color:uint, a_colorProgressBar:uint, a_logo:Bitmap, a_url:String)
{
graphics.beginFill(a_color);
graphics.drawRect(0, 0, a_width, a_height);
graphics.endFill();
addLogo(a_height, a_width, a_logo, a_url);
addProgressBar(a_width, a_color, a_colorProgressBar);
}
/**
* Add a logo link to the foreground area
*
* @param a_height The height of the foreground area
* @param a_width The width of the foreground area
* @param a_logo The bitmap for use as a logo
* @param a_url The url for the logo
*/
private function addLogo(a_height:uint, a_width:uint, a_logo:Bitmap, a_url:String):void
{
_logo = new Logo(a_logo, a_url);
_logo.x = (a_width - _logo.width) / 2;
_logo.y = (a_height - _logo.height) / 2;
addChild(_logo);
}
/**
* Add the progress bar to the foreground area
*
* @param a_width The width of the foreground area
* @param a_colorBackground The color of the foreground area
* @param a_colorProgressBar The color of the progress bar
*/
private function addProgressBar(a_width:uint, a_colorBackground:uint, a_colorProgressBar:uint):void
{
_progressBar = new ProgressBar(10, Math.round(a_width / 3), a_colorBackground, a_colorProgressBar);
_progressBar.x = a_width / 3;
_progressBar.y = _logo.y + _logo.height + 10;
addChild(_progressBar);
}
/**
* Remove event handlers
*/
public function dispose():void
{
_logo.dispose();
}
/**
* Called to update the progress bar with the fraction of data loaded
*
* @param a_fraction The fraction of loading complete (0.00 - 1.00)
*/
public function update(a_fraction:Number):void
{
_progressBar.update(a_fraction);
}
}
}
The final element of our splash screen is the LoaderLines class. In it’s constructor the size of the lines to display is set to be proportionate to the height of the full display area. The while loop in the update method fills the height of the screen with rectangles of random height. The colours of these rectangles are randomly selected from the _colors array. The array of colours is made up of constants from a palette I’ve created for approximating the Commodore 64! These can be replaced with any valid uint. Repeated calls to the update method create the spangly effect we’re after!
package com.buzzardgames.splash
{
import com.buzzardgames.palette.Commodore64;
import flash.display.Sprite;
/**
* A screen area displaying random colour lines
*
* @author Gareth Taft
*/
internal class LoaderLines extends Sprite
{
// The selection of colours used to draw lines
private static var _colors:Array = new Array(
Commodore64.BLACK,
Commodore64.BLUE,
Commodore64.BROWN,
Commodore64.CYAN,
Commodore64.DARK_GREY,
Commodore64.GREEN,
Commodore64.GREY,
Commodore64.LIGHT_BLUE,
Commodore64.LIGHT_GREEN,
Commodore64.LIGHT_GREY,
Commodore64.LIGHT_RED,
Commodore64.ORANGE,
Commodore64.PURPLE,
Commodore64.RED,
Commodore64.WHITE,
Commodore64.YELLOW
);
private var _height:uint; // The height of the display area
private var _lineHeightMax:uint; // The maximum height allowed for a line
private var _lineHeightMin:uint; // The minimum height allowed for a line
private var _width:uint; // The width of the display area
/**
* Constructor for an area displaying loader lines
*
* @param a_height The height of the area to display loader lines
* @param a_width The width of the area to display loader lines
*/
public function LoaderLines(a_height:uint, a_width:uint)
{
_height = a_height;
_width = a_width;
_lineHeightMax = Math.ceil(_height / 10);
_lineHeightMin = Math.ceil(_lineHeightMax / 4);
}
/**
* Called to alter the displayed loader lines
*/
public function update():void
{
var color:uint; // The colour of the line we're currently drawing
var lineHeight:Number; // The height of the line we're currently drawing
var lineY:Number = 0; // The y position of the line we're currently drawing
// Continue drawing lines until we reach the height of the display area
while (lineY < _height)
{
// Generate the height of the new line
lineHeight = _height - lineY < _lineHeightMax ? _height - lineY : _lineHeightMin + Math.round(Math.random() * _lineHeightMax);
// Select a random color
color = _colors[Math.round(Math.random() * (_colors.length - 1))];
// Draw the new line
graphics.beginFill(color);
graphics.drawRect(0, lineY, _width, lineHeight);
graphics.endFill();
// Set the position of the next line below the line we've just drawn
lineY += lineHeight;
}
}
}
}
You’ll notice that all our classes so far have been designated internal. This is because we do not (currently) wish to allow them to be instantiated outside of our splash package. Our Splash class though is public and simply displays our Foreground class on top of our LoaderLines class and calls the update methods of both within another public update method.
package com.buzzardgames.splash
{
import flash.display.Bitmap;
import flash.display.Sprite;
/**
* A splash screen displaying a clickable logo, a progress bar and a border of random coloured lines
*/
public class Splash extends Sprite
{
private var _foreground:Foreground; // The central screen area
private var _loaderLines:LoaderLines; // The border surrounding the central screen area
/**
* Constructor for a splash screen
*
* @param a_height The height of the splash screen
* @param a_width The width of the splash screen
* @param a_borderSize The size of border in which the loader lines are displayed
* @param a_colorForeground The color of the foreground area
* @param a_colorProgressBar The color of the progress bar
* @param a_logo The bitmap to use as a logo link
* @param a_url the URL to open when the logo is clicked
*/
public function Splash(a_height:uint, a_width:uint, a_borderSize:uint, a_colorForeground:uint, a_colorProgressBar:uint, a_logo:Bitmap, a_url:String)
{
addLines(a_height, a_width);
addForeground(a_height, a_width, a_borderSize, a_colorForeground, a_colorProgressBar, a_logo, a_url);
}
/**
* Add the foreground area to the splash screen
*
* @param a_height The height of the splash screen
* @param a_width The width of the splash screen
* @param a_borderSize The size of border in which the loader lines are displayed
* @param a_color The color of the foreground area
* @param a_colorProgressBar The color of the progress bar
* @param a_logo The bitmap to use as a logo link
* @param a_url the URL to open when the logo is clicked
*/
private function addForeground(a_height:uint, a_width:uint, a_borderSize:uint, a_color:uint, a_colorProgressBar:uint, a_logo:Bitmap, a_url:String):void
{
_foreground = new Foreground(a_height - (2 * a_borderSize), a_width - (2 * a_borderSize), a_color, a_colorProgressBar, a_logo, a_url);
_foreground.x = a_borderSize;
_foreground.y = a_borderSize;
addChild(_foreground);
}
/**
* Add the loader lines to the splash screen
*
* @param a_height The height of the splash screen
* @param a_width The width of the splash screen
*/
private function addLines(a_height:uint, a_width:uint):void
{
_loaderLines = new LoaderLines(a_height, a_width);
addChild(_loaderLines);
}
/**
* Remove event handlers
*/
public function dispose():void
{
_foreground.dispose();
}
/**
* Called to update the progress bar and loader lines
*
* @param a_fraction The fraction of loading complete (0.00 - 1.00)
*/
public function update(a_fraction:Number):void
{
// Update the progress bar
_foreground.update(a_fraction);
// Update the loader lines
_loaderLines.update();
}
}
}
Now to make use of our Splash class! We’re going to use our splash screen in a preloader and then show off how it can be resized once the application has loaded.
First, because we’re going to use our new splash screen in a couple of places, we’ll create a BrandedSplash class in our src directory to subclass our generic Splash class. This implements a number of constants to keep the colours, logo and URL consistent whilst exposing the height, width and border size parameters to allow for resizing:
package
{
import com.buzzardgames.palette.Commodore64;
import com.buzzardgames.splash.Splash;
/**
* A resizable splash screen branded with set colours and a logo
*/
public class BrandedSplash extends com.buzzardgames.splash.Splash
{
// The colour of the central screen area
private static const _COLOR_FOREGROUND:uint = Commodore64.WHITE_WITHOUT_ALPHA_CHANNEL;
// The colour of the progress bar
private static const _COLOR_PROGRESS_BAR:uint = Commodore64.BLUE_WITHOUT_ALPHA_CHANNEL;
// The URL to open when the logo is clicked
private static const _URL:String = "http://www.buzzardgames.com/";
// The bitmap to use as a logo
[Embed(source = "assets/img/logo.jpg")]
private static const _LOGO:Class;
/**
* Constructor for a resizable splash screen
*
* @param a_height The height of the splash screen
* @param a_width The width of the splash screen
* @param a_borderSize The size of the border in which to display loader lines
*/
public function BrandedSplash(a_height:uint, a_width:uint, a_borderSize:uint)
{
super(a_height, a_width, a_borderSize, _COLOR_FOREGROUND , _COLOR_PROGRESS_BAR, new _LOGO(), _URL);
}
}
}
For preloaders I use a slightly modified version of the Urbansquall preloader described in this Game Poetry blog article. I’ve added a minimum display time to their code to ensure that, even if the SWF loads quickly, the Buzzard Games logo and link are displayed for a sufficient amount of time. This also means I can use the preloader as a splash screen that displays after a Mochi Ads preloader.
Now we add a MainPreloader class to the src directory. As described in the Game Poetry article this class should be set to “Always Compile” and in Project -> Properties -> Compiler Options -> Advanced you should set Additional Compiler Options to -frame start Main.
The MainPreloader class creates a new BrandedSplash class and then calls it’s update method from the overriden updateLoading method. Once loading is complete it tidies up the splash screen in the endLoading method:
package
{
import com.urbansquall.preloader.Preloader;
/**
* An application pre-loader
*
* Set this to "Always Compile"
* Set Project->Properties->Compiler Options->Advanced->Additional Compiler Options to:
*
* -frame start Main
*/
public class MainPreloader extends Preloader
{
private var _splash:BrandedSplash; // The splash screen
/**
* Constructor for the pre-loader
*/
public function MainPreloader()
{
// Call the base constructor to set up the preloader
super(2000);
// Display the splash screen
_splash = new BrandedSplash(400, 600, 50);
addChild(_splash);
}
/**
* Update the progress bar
*
* @param a_percent The fraction of loading complete (0.00 - 1.00)
*/
override protected function updateLoading(a_percent:Number):void
{
_splash.update(a_percent);
}
/**
* Clean up the splash screen
*/
override protected function endLoading():void
{
_splash.dispose();
removeChild(_splash);
_splash = null;
}
}
}
Now let’s use our Main class to test how resizable our splash screen is. When Main is loaded we draw an instruction label and add a click event.
When the screen is clicked a new BrandedSplash is created with random dimensions. The mouse click event handler is removed (so that we only display one splash screen at a time) and an enter frame event is added to call the updateSplash method. We also store the time at which the splash screen is first displayed.
The updateSplash method calculates the elapsed time that the splash screen has been displayed. If the splash screen been displayed for less time than the _SPLASH_SHOW_TIME then the progress bar is updated accordingly. Otherwise the splash screen is removed and the original mouse click event is re-instated:
package
{
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.text.TextField;
import flash.text.TextFormat;
import flash.text.TextFormatAlign;
import flash.text.TextFieldAutoSize;
import flash.utils.getTimer;
/**
* An application to test a resizeable splash screen
*
* @author Gareth Taft
*/
public class Main extends Sprite
{
// The number of milliseconds to display the splash screen
private static const _SPLASH_SHOW_TIME:uint = 3000;
private var _splash:BrandedSplash; // The splash screen
private var _splashStartTime:uint; // The time at which display of the splash screen began
/**
* Entry point for the application
*/
public function Main():void
{
if (stage) addedToStage();
else addEventListener(Event.ADDED_TO_STAGE, addedToStage);
}
/**
* Called once the application has been added to the stage
*
* @param a_event Added to stage event
*/
private function addedToStage(a_event:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, addedToStage);
// Draw an instruction label
drawLabel();
// Add a mouse click event handler to show the splash screen
stage.addEventListener(MouseEvent.CLICK, showSplash);
}
/**
* Called to hide the splash screen
*/
private function hideSplash():void
{
// Remove the event handler to update the splash screen
removeEventListener(Event.ENTER_FRAME, updateSplash);
// Destroy the splash screen
_splash.dispose();
removeChild(_splash);
_splash = null;
// Re-instate the click event handler
stage.addEventListener(MouseEvent.CLICK, showSplash);
}
/**
* Called to display the splash screen
*
* @param a_event Mouse click event
*/
private function showSplash(a_event:MouseEvent):void
{
// Remove the mouse click event handler to stop multiple splash screens being displayed
stage.removeEventListener(MouseEvent.CLICK, showSplash);
// Store the start time of the splash screen being displayed
_splashStartTime = getTimer();
// Calculate some random dimensions for the splash screen
var height:uint = 190 + Math.round(Math.random() * 210);
var width:uint = 302 + Math.round(Math.random() * 298);
var borderSize:uint = 10 + Math.round(Math.random() * 40);
// Display the splash screen
_splash = new BrandedSplash(height, width, borderSize);
_splash.x = (600 - width) / 2;
_splash.y = (400 - height) / 2;
addChild(_splash);
// Add an event handler to update the splash screen progress bar
addEventListener(Event.ENTER_FRAME, updateSplash);
}
/**
* Called each frame to update the splash screen
*
* @param a_event Enter frame event
*/
private function updateSplash(a_event:Event):void
{
// Calculate the elapsed time since the splash screen was shown
var time:uint = getTimer() - _splashStartTime;
// Has the splash screen been displayed long enough
if (time < _SPLASH_SHOW_TIME)
{
// No, update the progress bar
_splash.update(time / _SPLASH_SHOW_TIME);
}
else
{
// Yes, remove the splash screen
hideSplash();
}
}
/**
* Draw an instruction label to the screen
*/
private function drawLabel():void
{
// Set the text format
var titleFormat:TextFormat = new TextFormat();
titleFormat.font = "Arial";
titleFormat.color = 0x000000;
titleFormat.size = 24;
titleFormat.align = TextFormatAlign.CENTER;
// Draw and position the label
var label:TextField = new TextField();
label.defaultTextFormat = titleFormat;
label.text = "Click me";
label.width = 600;
label.selectable = false;
label.x = 0;
label.y = 150;
addChild(label);
}
}
}
And here’s the result:
You can download the full source code here as a zipped FlashDevelop project.



3 Comments
Cool, I’ve been looking on and off for the past 20 years on how this was done.
Any plans for the other variants which were around e.g. rolling colours 2 tone, speedlock loadering borders, which were on the amstrad/spectrum?
The one thing I was considering doing was the effect of a bitmap gradually loading due to the slow rendering in hi-res mode on the C64.
Aside from that I’ve got no plans to make more at the moment but I guess they wouldn’t be too hard to simulate. Why not give it a go and let me know the results!
yep the gradual image loading would be good.
If i get the chance I will have a look at the others, but my coding skills are rusty now.
I’m still now sure how they actually achieved the effect, as it was based on the sound of the tape loading.