В статье речь пойдёт о создании preloader-а для flash игры.
В данном контексте имеется ввиду прелоадер, который отображает процесс загрузки самого flash-приложения (т.к. есть ещё одно понятие прелоадера — при загрузке конкретного внутри игрового контента).
Рассмотрим 2 вида прелоадеров и реализуем их во FlashDevelop.
Что такое preloader и как он работает?
Изначально логика загрузки flash приложения (флешки) сделана следующим образом: сначала грузится первый кадр, а затем грузится всё остальное и при этом первый кадр может получать информацию об остальной загрузке флешки.
Таким образом мы можем отображать игроку процесс загрузки. И логично можно догадаться, что чем первый кадр меньше(легче), тем лучше, т.к. если первый кадр будет размером почти со всю флешку(это возможно, если в прелоадере использовать весь контент, который используется во всей игре), то игрок будет видеть чёрный экран пока вся флешка не загрузится.
Когда мы создаём во FlashDevelop (далее FD) новый проект as3 с прелоадером, то конструктор создаёт 2 класса Main и Preloader. При этом точка входа в программу — это класс Main, при этом отсюда уже начинается наша игра и казалосьбы флешка сразу тянет за собой весь контент и ни о каком маленьком первом кадре речи быть не может и вообще как ко всему этому причастен класс Preloader… Но перед классом Main мы замечаем строчку [Frame(factoryClass=»Preloader»)].
Это метатег, который говорит компилятору, что класс Preloader должен быть загружен первым, а т.к. он наследован от MovieClip, а MC состоит из кадров, то вот мы и нашли тот самый первый кадр, который загрузится впереди всего. При этом весь код, который мы напишем в нём, будет функционировать и мы сможем узнавать информацию о процессе загрузки всего приложения.
Теперь запилим парочку разных прелоадеров для закрепления материала.
Прелоадер состоящий из кадров
Этот вид прелоадера представляет из себя заранее заготовленный MovieClip. Где каждый шаг прогресса прелоадера — это определённый кадр заготовленного клипа. Например, делаем клип с 11-ю кадрами, где каждый кадр отображает каждые 10% загрузки: 0%, 10% и т.д. до 100%. В таком прелоадере при 5% реальной загрузки надо будет показывать 0%, при 10% — 10%, при 29% — 20% и т.д.
Создаём новый основной проект «AS3 Project with preloader» в FD. Создаём новый проект во Flash CS#, сохраняем его в папку lib основного проекта под именем Preloader.fla, настраиваем публикацию для компиляции swc библиотеки и подключаем её в FD (об этом я писал в предыдущей статье).
Во Flash создаём MovieClip с 11 кадрами, отображающий прогресс загрузки и кнопку «Play» на первом кадре, которую в коде мы будет выдёргивать из этого клипа и ложить в список отображения класса прелоадера.
Затем пишем код. Я создал специальный класс для прелоадера StepPreloader и использую его в Preloader.as.
Код класса StepPreloader:
package
{
import flash.display.MovieClip;
import flash.display.SimpleButton;
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.text.TextField;
/**
* @author Monax
*/
public class StepPreloader extends Sprite
{
private var _mc: MovieClip; // основной клип с кадрами отображения процесса
private var _btnPlay: SimpleButton; // кнопка play
private var _bytesTotal: Number = 0; // всего байт
private var _bytesLoaded: Number = 0; // байт загружено
private var _loadPercent: Number; // процент загрузки
private var _tfLoadPercent: TextField; // тексты отображающие процесс загрузки
private var _tfBytes: TextField;
// обработчик события нажатия кнопки Play
public var onBtnPlayClick: Function;
public function StepPreloader(aMC: MovieClip, aBtnPlay: SimpleButton)
{
super();
_mc = aMC;
_mc.gotoAndStop(1);
_btnPlay = aBtnPlay;
_btnPlay.addEventListener(MouseEvent.CLICK, onBtnPlayClickHandler);
addChild(_mc);
addChild(_btnPlay);
}
// Обновление прелоадера
private function refresh():void
{
// высчитываем процент загрузки
_loadPercent = 100 * _bytesLoaded / _bytesTotal;
if (_loadPercent < 0) _loadPercent = 0;
if (_loadPercent > 100) _loadPercent = 100;
// переходим на кадр соотвествующий текущему проценту загрузки
_mc.gotoAndStop(Math.floor(_mc.totalFrames / 100 * _loadPercent) + 1);
// отображаем текст процента, если поле доступно
if (_tfLoadPercent) _tfLoadPercent.text = "Loading: " + String(int(_loadPercent)) + "%";
// отображаем байты
if (_tfBytes) _tfBytes.text = "(" + String(int(_bytesLoaded / 1000)) + "/" + String(int(_bytesTotal / 1000)) + " KBytes)";
}
private function onBtnPlayClickHandler(e:MouseEvent):void
{
if (onBtnPlayClick != null) onBtnPlayClick.apply();
}
public function get btnPlayVisible():Boolean { return _btnPlay.visible; }
public function set btnPlayVisible(value:Boolean):void { _btnPlay.visible = value; }
public function get bytesTotal():Number { return _bytesTotal; }
public function set bytesTotal(value:Number):void {
_bytesTotal = value;
refresh();
}
public function get bytesLoaded():Number { return _bytesLoaded; }
public function set bytesLoaded(value:Number):void {
_bytesLoaded = value;
refresh();
}
public function setTFLoadPercent(aTF: TextField):void
{
_tfLoadPercent = aTF;
}
public function setTFBytes(aTF: TextField):void
{
_tfBytes = aTF;
}
public function free():void
{
onBtnPlayClick = null;
removeChild(_mc);
removeChild(_btnPlay);
_mc = null;
_btnPlay = null;
}
}
}
Код класса Preloader:
package
{
import flash.display.DisplayObject;
import flash.display.MovieClip;
import flash.display.SimpleButton;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.events.MouseEvent;
import flash.events.ProgressEvent;
import flash.text.TextField;
import flash.utils.getDefinitionByName;
/**
* @author Monax
*/
public class Preloader extends MovieClip
{
private var _preloader: StepPreloader;
public function Preloader()
{
if (stage) {
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.align = StageAlign.TOP_LEFT;
}
addEventListener(Event.ENTER_FRAME, checkFrame);
loaderInfo.addEventListener(ProgressEvent.PROGRESS, progress);
loaderInfo.addEventListener(IOErrorEvent.IO_ERROR, ioError);
// создаём основной клип
var mcPreloader: Preloader_mc = new Preloader_mc();
// берём с основного клипа кнопку по имени
var btnPlay: SimpleButton = mcPreloader.getChildByName("btnPlay") as SimpleButton;
// создаём класс пошагового прелоадера
_preloader = new StepPreloader(mcPreloader, btnPlay);
_preloader.setTFLoadPercent(mcPreloader.getChildByName("tfPerc") as TextField);
_preloader.setTFBytes(mcPreloader.getChildByName("tfBytes") as TextField);
_preloader.x = 320;
_preloader.y = 240;
_preloader.btnPlayVisible = false;
// прописываем обработчик нажатия кнопки play
_preloader.onBtnPlayClick = playClick;
//
addChild(_preloader);
}
private function ioError(e:IOErrorEvent):void
{
trace(e.text);
}
private function progress(e:ProgressEvent):void
{
// TODO update loader
if (_preloader.bytesTotal == 0) _preloader.bytesTotal = e.bytesTotal;
_preloader.bytesLoaded = e.bytesLoaded;
}
private function checkFrame(e:Event):void
{
if (currentFrame == totalFrames) {
stop();
loadingFinished();
}
}
private function loadingFinished():void
{
removeEventListener(Event.ENTER_FRAME, checkFrame);
loaderInfo.removeEventListener(ProgressEvent.PROGRESS, progress);
loaderInfo.removeEventListener(IOErrorEvent.IO_ERROR, ioError);
// показываем кнопку
_preloader.btnPlayVisible = true;
}
private function playClick():void {
_preloader.free();
removeChild(_preloader);
_preloader = null;
startup();
}
private function startup():void
{
var mainClass:Class = getDefinitionByName("Main") as Class;
addChild(new mainClass() as DisplayObject);
}
}
}
А в коде файла Main.as я реализовал имитацию данного прелоадера (для наглядности) с повторением после нажатия кнопки Play.
Важное замечание! Все остальные ресурсы для игры должны содержаться в отдельной swc библиотеки и не использоваться в прелоадере, иначе прелоадер потянет за собой всю флешку и смысла от него не будет.
В результате получился вот такой прелоадер:
Скачать исходники можно прям отсюда: MyPreloader.rar.
Плавный прелоадер с маской
Данный прелоадер отображает процесс загрузки каким-либо плавным действием-анимацией. В моём случае плавная полоска загрузки.
Вот так это выглядит (нажимайте кнопку Play для повтора):
Способ реализации
Для такого прелоадера нам необходимо сделать полоску прогресса отдельным клипом.
Затем создать фон, текст и кнопку (всё кроме полоски) в новом клипе и так же разместить в нём клип полоски.
По списку библиотеки так же можно видеть, что я встроил шрифт и кнопка лежит отдельным символом в библиотеке.
Как встраивать шрифты в библиотеку? Когда выбрали для текстового поля какой-нибудь шрифт из системы, нажимаем кнопку embed рядом с выбором, как показано на рисунке:

Затем, в открывшемся окне, вводим имя которое будет отображаться в библиотеке и отмечаем галочкой символы, которые нам потребуются, например All (все символы):
Вот и всё, после нажатия Ok шрифт появится в библиотеке.
Итак продолжаем. Далее назначаем имена для всех элементов (в клипе mcPreloader), которые нам понадобиться распознать программно: клип полоски, текст, кнопка.
Для отображения всего прелоадера я создал отдельный класс в котором создаётся экземпляр основного клипа прелоадера и распознаются на нём все нужные элементы. Самая основная часть — маска полоски прогресса, создаётся программно. В комментариях кода всё должно быть понятно.
Код класса SmoothPreloader:
package
{
import flash.display.Graphics;
import flash.display.MovieClip;
import flash.display.SimpleButton;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.text.TextField;
/**
* @author Monax
*/
public class SmoothPreloader extends Sprite
{
private var _preFon: Preloader_mc; // основной клип
private var _preBar: MovieClip; // клип полоски прогресса
private var _barMask: Sprite; // маска для полоски
private var _btnPlay: SimpleButton;
private var _tfPercent: TextField;
//
private var _bytesTotal: int = 0;
private var _bytesLoaded: int = 0;
// событие нажатия кнопки Play
public var onBtnPlayClick: Function;
public function SmoothPreloader()
{
super();
// создаём основной клип прелоадера
_preFon = new Preloader_mc();
// находим на полоску прогресса
_preBar = _preFon.getChildByName("bar") as MovieClip;
// создаём маску для полоски
_barMask = new Sprite();
_preFon.addChild(_barMask);
// размещаем маску в тех же координатах что и полоска
_barMask.x = _preBar.x;
_barMask.y = _preBar.y;
// рисуем на маске прямоугольник высотой и шириной как у полоски прогресса
var gr: Graphics = _barMask.graphics;
gr.beginFill(0);
gr.drawRect(0, 0, _preBar.width, _preBar.height);
gr.endFill();
// и выставляем масштаб по ширине в 0, в прогрессе мы будем увеличивать масштаб до 1(100% загрузки - масштаб = 1)
_barMask.scaleX = 0;
// присваиваем маску полоске
_preBar.mask = _barMask;
// распознаём кнопку плей и создаём ей обработчик клика
_btnPlay = _preFon.getChildByName("btnPlay") as SimpleButton;
_btnPlay.addEventListener(MouseEvent.CLICK, function(e: MouseEvent):void {
if (onBtnPlayClick != null) onBtnPlayClick.apply();
});
_btnPlay.visible = false;
// распознаём текст отображения процентов
_tfPercent = _preFon.getChildByName("tfLoading") as TextField;
bytesTotal = 0;
bytesLoaded = 0;
addChild(_preFon);
}
public function get bytesTotal():int { return _bytesTotal; }
public function set bytesTotal(value:int):void { _bytesTotal = value; }
public function get bytesLoaded():int { return _bytesLoaded; }
public function set bytesLoaded(value:int):void {
_bytesLoaded = value;
if (_bytesTotal != 0) {
_barMask.scaleX = _bytesLoaded / _bytesTotal;
_tfPercent.text = "Loading: " + int(100 * _bytesLoaded / _bytesTotal) + "%";
}
else {
_barMask.scaleX = 0;
_tfPercent.text = "Loading: 0%";
}
}
public function get btnPlayVisible():Boolean { return _btnPlay.visible; }
public function set btnPlayVisible(aVal: Boolean):void { _btnPlay.visible = aVal; }
public function free():void
{
removeChild(_preFon);
_preFon = null;
}
}
}
Сразу видно удобство данного варианта, для меня например таковым является, что вся заготовка распологается на одном кадре клипа.
Если вы обратили внимание, что код вышел покомпактнее чем в первом примере, то это лишь от того, что я немножко пооптимальнее написал.
Класс Preloader использует этот класс по назначению, а в классе Main я так же реализовал имитацию процесса загрузки для наглядности.
Таким образом, чтобы использовать прелоадер по назначению, нужно просто убрать имитацию из класса Main.
Архив с исходниками плавного прелоадера: SmoothPreloader.
Пишите ваши замечания и предложения в комментариях, а так же про свой опыт создания красивых прелоадеров!
До новых встреч!

