В статье речь пойдёт о создании 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.
Пишите ваши замечания и предложения в комментариях, а так же про свой опыт создания красивых прелоадеров!
До новых встреч!