Статьи

Обработка экранной архитектуры: безболезненный путь!

Вы когда-нибудь думали, что архитектура экрана была излишне болезненной и утомительной задачей? Тогда этот урок — твой друг.

Мы нашли этого замечательного автора благодаря FlashGameLicense.com , месту, где можно покупать и продавать флэш-игры!


Давайте посмотрим на конечный результат, к которому мы будем стремиться:


Если вы похожи на меня, я всегда ненавидел начало проекта, потому что мне пришлось бы настроить все экраны. После того, как я прекратил кодировать на временной шкале, я потерял способность просто говорить: gotoAndStop (x).

Как мы все знаем, временная кодировка просто неправильна; Это загрязняет окружающую среду и вызывает разрушение зубов. Однако переключать экраны было просто. Я потратил много времени в Интернете, пытаясь найти эффективный метод переключения экрана, но все, что я нашел, это методы, полные боли, наказывающие разработчика за сложные архитектуры экрана. И не меняя экраны с основного экрана, я вставлял в свою игру ужасный код, такой как:

1
parent.parent.parent.dispatchEvent(Event.CUSTOM_EVENT, screen1_to_screen2);

Видишь, о чем я? Индивидуальное событие после пользовательского события, я устал от этой нелепости. Там должен быть лучший способ.


Итак, я решил найти метод обработки сложных экранных архитектур без всей этой надоедливой психической травмы. Нет событий Я обратился к своему любимому способу обработки задач, на которые нужно ссылаться из любой точки игры: статические переменные и методы.

Использование статических переменных позволило бы мне ссылаться на объект из любого места, где я хотел, даже в всплывающем окне всплывающего окна. Я решил связать это с простотой и удобством использования списка отображения Flash.

Введите класс ScreenHandler.


Это, вероятно, имеет много экранов. Скорее всего, у вас есть заставка, главное меню, экран игры, кредиты, экран победы и многие другие. Нам нужно сначала настроить наши экраны. Мы пока не будем помещать в него какой-либо контент, игровой процесс зависит от вас.

Вот экраны, которые у меня есть:

Это экраны, которые я буду использовать в моей замечательной игре.

Как вы можете видеть, я исключил предварительный загрузчик. Правильная загрузка — это совсем другой урок. Вы можете узнать об этом здесь:

Active Tuts +: полное руководство по предварительной загрузке одного SWF-файла

Я объясню, как совместить это с этим уроком в конце. Теперь о той части, которую вы все ждали!


По сути, класс ScreenHandler — это экранный объект, который содержит все ваши экраны и переключает их по своему усмотрению. Код на удивление прост. Тем не менее, я не просто проложу стену кода для вас, чтобы столкнуться. Было бы напрасной тратой, если бы вы не полностью понимали код. Поэтому я разобью его на пару разделов.

Первое, что нам нужно сделать, это создать реальный класс. Вот код:

01
02
03
04
05
06
07
08
09
10
package {
    import flash.display.Sprite;
    public class ScreenHandler extends Sprite{
        //Variables go here
        public function ScreenHandler() {
            //Constructor goes here
        }
        //Functions go here
    }
}

Вау, это очень пусто.

Далее мы добавим на наши экраны переменные:

1
2
3
4
5
6
private var splashScreen:SplashScreen;
private var mainMenu:MainMenu;
private var levelSelect:LevelSelect;
private var game:Game;
private var credits:Credits;
private var victory:Victory;

Просто добавьте их под комментарием «Переменные идут сюда».

И вот так, мы 1/10 пути туда!


Это функция, которую вы будете вызывать для переключения экранов. Хорошей новостью является то, что это всего 4 строки кода. Плохая новость в том, что это только потому, что мне нравится разбивать мой код на управляемые куски. Это единственная открытая функция во всем классе, так как это все, что вам нужно для вызова класса. Инкапсуляция во всей красе!

1
2
3
4
public function switchTo(screenName:String):void{
    newScreenName = screenName;
    switchScreens();
}

Это идет под комментарием «Функции идут сюда». Просто нет? Теперь вам нужно создать переменную с именем newScreenName и функцию switchScreens.

1
private var newScreenName:String = «»;

Вы уже знаете, куда это идет. А вот функция switchScreens:

1
2
3
4
private function switchScreens():void{
    removeOldScreen();
    makeNewScreen();
}

Я предупредил вас: управляемые куски.


Прежде чем злиться на меня, пойми, что я делаю это для твоего же блага. Нет, правда. Такое разбиение на управляемые куски упрощает поиск и изменение кода, если вам нужны пользовательские функции. Я сам всегда нахожу необходимость изменить код позже в игре, поэтому я просто перенял эту практику кодирования. Кроме того, если вы пишете код и что-то сломано, то легче найти источник проблемы. Хорошо, хватит моего отвода. Вот функции, которые делают это (на этот раз по-настоящему).


Функция removeOldScreen берет свои чудесные функции из списка отображения AS3. Это было, вероятно, лучшее улучшение от AS2. Родительско-дочерние отношения, которые имеет отображаемый список, чрезвычайно полезны практически при любых визуальных манипуляциях, и цикл по дочерним элементам в экранном объекте выполняется быстрее, чем цикл по мувиклипам в массиве. Это действительно здорово. Прежде чем мы напишем функции removeOldScreen и makeNewScreen, нам нужен родитель для хранения экранов. Вот еще одна переменная:

1
private var screenLayer:Sprite = new Sprite();

и добавьте эту строку кода в ваш конструктор:

1
this.addChild(screenLayer);

Хорошо, теперь у нас есть родительский фундамент, который позволяет легко модифицировать и отлаживать. Осталось только написать функцию removeOldScreen. Вот код:

1
2
3
4
5
private function removeOldScreen():void{
    var oldScreen:MovieClip;
    oldScreen = screenLayer.getChildAt(0) as MovieClip;
    screenLayer.removeChild(oldScreen);
}

То, что мы делаем, — это создание переменной-заполнителя, которая станет нашим текущим экраном. Затем мы берем дочерний элемент с индексом ‘0’ (который является первым дочерним элементом родительского объекта) и устанавливаем наш заполнитель равным ему. Этот удобный метод позволяет нам делать то, что нам нравится, на любом экране без необходимости называть экраны конкретной переменной. Затем мы используем метод removeChild, чтобы избавиться от экрана навсегда. Просто прекрасно.

Ну, теперь мы можем сделать пустой экран. Было бы неплохо иметь возможность положить что-то там, верно? Ну, я собираюсь рассказать вам, как это сделать.


Это самый подробный раздел кода, но его очень легко создать, понять и настроить. Этот раздел кода представляет собой гигантский оператор switch, содержащий все ваши экраны. Аргумент, который мы передаем в функцию switch, — это переменная newScreenName, которую мы установили в функции switchTo.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
private function makeNewScreen():void{
    switch(newScreenName){
        case «SplashScreen»:
            splashScreen = new SplashScreen();
            screenLayer.addChild(splashScreen);
        break;
        case «MainMenu»:
            mainMenu = new MainMenu();
            screenLayer.addChild(mainMenu);
        break;
        case «LevelSelect»:
            levelSelect = new LevelSelect();
            screenLayer.addChild(levelSelect);
        break;
        case «Game»:
            game = new Game();
            screenLayer.addChild(game);
        break;
        case «Credits»:
            credits = new Credits();
            screenLayer.addChild(credits);
        break;
        case «Victory»:
            victory = new Victory();
            screenLayer.addChild(victory);
        break;
        default:
            mainMenu = new MainMenu();
            screenLayer.addChild(mainMenu);
        break;
    }
    newScreenName = «»;
}

Код довольно понятен, но я все равно объясню.

1
2
3
4
case «Screen»:
    screen = new Screen();
    screenLayer.addChild(screen);
break;

Вы связываете строку с экраном. Эта строка является аргументом, который вы передадите в функцию switchTo. Затем он проходит через оператор switch и выбирает правильный экран для добавления. Затем он создает экземпляр переменной и добавляет его в screenLayer. Вы не обязаны устанавливать значение по умолчанию, но полезно иметь значение по умолчанию для любого оператора switch, который вы используете для целей отладки. Он активируется, если ни один из других случаев не соответствует аргументу.

Примечание. Точка регистрации экранов должна находиться в верхнем левом углу, чтобы экраны отображались правильно.

Теперь у нас есть функциональность за классом ScreenHandler. Теперь пришло время применить его к нашей программе! Перед тем, как применить его к нашей программе, нам нужно добавить дочерний элемент в screenLayer, в противном случае нам нечего будет удалить при первом вызове removeOldScreen. Это даст нам ошибку, а ошибки плохие. Mkay?

1
2
splashScreen = new SplashScreen();
screenLayer.addChild(splashScreen);

Добавьте это под остальной частью конструктора. Теперь перейдите к началу класса и импортируйте flash.display.MovieClip, если вы еще этого не сделали, и мы можем двигаться дальше.


Если вы не смотрели учебник, на который я ссылался ранее, сейчас самое время сделать это.

Active Tuts +: полное руководство по предварительной загрузке одного SWF-файла

Назад? Отлично.

Обработчик экрана будет добавлен в класс Application. Сам фактический спрайт будет публичной статической переменной, так что вы можете ссылаться на нее из любого места в вашем коде, и он переключит экран. Легко, правда?

1
public static var screens:ScreenHandler = new ScreenHandler();

затем добавьте это в конструктор класса Application:

1
this.addChild(screens);

Если вам когда-нибудь понадобится переключать экраны из любого места в вашем коде, вот как вы это делаете:

1
Application.screens.switchTo(«SelectedScreen»);

Ну, мы закончили с обработчиком экрана как таковым. После того, как я закодирую все кнопки, чтобы переключиться на любой экран, он работает.

Вы можете сказать: «Томас, это переключение экрана ужасно! Я хочу переходы экрана!»

Хорошо, что коды легко настраиваются. Просто спроси в следующий раз.


Первым шагом в добавлении переходов экрана является определение того, какой переход экрана вы хотите. Для этого примера я просто собираюсь сделать простое затухание. Легко, верно?

  • Начните с создания нового символа.
  • Назовите его Transition и экспортируйте его для actioncript.
  • Нарисуйте прямоугольник размером с ваш экран.
  • Создайте новый ключевой кадр в кадре 10.
  • Сделайте новый ключевой кадр в кадре 20.
  • Вернитесь в первый кадр и преобразуйте альфа в 0;
  • Перейти к кадру 20 и преобразовать альфа в 0;
  • Щелкните правой кнопкой мыши пространство между ключевыми кадрами и выберите «Создать анимацию формы».

Ваш законченный переход экрана должен выглядеть следующим образом:

Это скриншот перехода экрана. Не много движения здесь.

Теперь, когда мы это настроили, давайте закодируем наш класс Transition!


Это простой класс, который нужно настроить для наших целей, хотя вы всегда можете настроить его под свои нужды. Он должен расширять MovieClip, и единственное, что мы добавляем к нему, это переменная.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
package {
    import flash.display.MovieClip;
    import flash.events.Event;
    public class Transition extends MovieClip{
        public static var exitFrames:Number = 11;
        private var timer:Number = 0;
        public function ScreenTransition() {
            this.addEventListener(Event.ENTER_FRAME, remove);
            this.addEventListener(Event.REMOVED_FROM_STAGE, removeListeners);
        }
        private function remove(e:Event):void{
            timer++;
            if(timer >= 20){
                parent.removeChild(this);
            }
        }
        private function removeListeners(e:Event):void{
            this.removeEventListener(Event.ENTER_FRAME, remove);
            this.removeEventListener(Event.REMOVED_FROM_STAGE, removeListeners);
        }
    }
}

Переменная, которую мы добавили, была exitFrames. Мы установили его на 11. Почему? Потому что в этом кадре переход достигает 100% альфа, и именно в этом кадре мы собираемся включить экраны. Другие функции обрабатывают удаление самого клипа и обрабатывают удаление прослушивателей событий после его удаления. Меньше сборщика мусора, а?


Помните, как я сказал, что мы не будем использовать события? Ну, я солгал. Переход экрана требует нескольких событий, поэтому переключение экрана должным образом задерживается, и переход удаляется после того, как он завершил свою работу.

С самого начала моей целью было сделать этот класс максимально универсальным и простым в использовании. Я не хотел головной боли, когда настраивал архитектуру экрана. В соответствии с этими рекомендациями, я сделаю добавление переходов экрана одним из вариантов, поскольку иногда переход экрана не требуется.


Чтобы добавить переходы на экране, нам даже не нужно трогать код removeOldScreen или makeNewScreen, потому что я их предварительно разделил. Как будто я знал, что это должно было случиться …

Нам понадобится множество новых переменных:

1
2
3
4
private var transitionLayer:Sprite = new Sprite();
private var transition:Transition;
private var transTimer:Number = 0;
private var makeTransition:Boolean;

Переходный слой собирается разместить наш переходной клип. Таким образом, это не влияет на количество детей нашего screenLayer. Таймер перехода будет использоваться для синхронизации наших действий в том случае, если это нужно. Переменная make transition будет контролировать, будет ли использоваться переход экрана, это ваше дело!

Далее нам нужно изменить вещи и в конструкторе. Вот как должен выглядеть ваш новый конструктор:

1
2
3
4
this.addChild(screenLayer);
this.addChild(transitionLayer);
splashScreen = new SplashScreen();
screenLayer.addChild(splashScreen);

И, наконец, что не менее важно, перейдите к области импорта и импортируйте flash.events.Event. После этого мы можем освободиться.


Я все еще хочу сохранить эту функцию короткой и приятной, чтобы не усложнять конечный результат для пользователя. Инкапсуляция отличная, нет?

1
2
3
4
5
public function switchTo(screenName:String, trans:Boolean = true):void{
    newScreenName = screenName;
    makeTransition = trans;
    this.addEventListener(Event.ENTER_FRAME, switchScreens);
}

Здесь много нового. В разделе аргументов мы добавили trans, который по умолчанию имеет значение true. Это означает, что, если вы не скажете иначе, автоматически устанавливается переход на экран. Это избавляет вас от необходимости печатать «true» при каждом переключении экранов. Наша переменная makeTransition тогда устанавливается равной trans. Функция switchScreens теперь будет принимать аргумент события, который приведет нас к следующему разделу.


Давайте сосредоточимся на коде, чтобы сначала сработал переход экрана. Это будет отличаться от нашего ранее простого кода.

01
02
03
04
05
06
07
08
09
10
11
12
13
private function switchScreens(e:Event):void{
    transTimer++;
    if(transTimer == 1 && transitionLayer.numChildren < 1){
        transition = new Transition();
        transitionLayer.addChild(transition);
    }
    if(transTimer >= transition.exitFrames){
        removeOldScreen();
        makeNewScreen();
       transTimer = 0;
       this.removeEventListener(Event.ENTER_FRAME, switchScreens);
    }
}

Позвольте мне сломать это:

1
2
3
4
5
6
private function switchScreens(e:Event):void{
    transTimer++;
    if(transTimer == 1 && transitionLayer.numChildren < 1){
        transition = new Transition();
        transitionLayer.addChild(transition);
    }

Сначала мы добавляем аргумент Event в функцию. Мы устанавливаем значение transTimer на единицу каждый кадр. Если transTimer равен единице, а у transitionLayer нет дочерних элементов, мы добавляем переход.

1
2
3
4
5
6
7
   if(transTimer == transition.exitFrames){
    removeOldScreen();
    makeNewScreen();
    transTimer = 0;
    this.removeEventListener(Event.ENTER_FRAME, switchScreens);
 
}

Как только transTimer достигает выходных фреймов, которые мы установили ранее, мы осуществляем изменение экрана. Потому что это все, верно? Затем он сбрасывает transTimer, затем удаляет прослушиватель событий. Теперь он переключает экраны с плавным переходом экрана!


Теперь мы учтем возможность того, что вы не хотите, чтобы происходил переход экрана. Мы собираемся обернуть весь наш текущий код switchScreens в оператор if:

1
2
3
if(makeTransition){
    //All of your current switchScreens code goes here
}

Разве это не было легко? Теперь мы создаем раздел else для случая, когда makeTransition не соответствует действительности:

1
2
3
4
5
6
7
if(makeTransition){
    //All of your current switchScreens code goes here
} else {
    removeOldScreen();
    makeNewScreen();
    this.removeEventListener(Event.ENTER_FRAME, switchScreens);
}

И вот он, полностью функциональный класс обработки экрана с возможностью управления добавлением переходов экрана! Отличный материал.


Вот как будет выглядеть готовый код:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
package {
    import flash.display.Sprite;
    import flash.display.MovieClip;
    import flash.events.Event;
 
    public class ScreenHandler extends Sprite{
        private var splashScreen:SplashScreen;
        private var mainMenu:MainMenu;
        private var levelSelect:LevelSelect;
        private var game:Game;
        private var credits:Credits;
        private var victory:Victory;
         
        private var newScreenName:String = «»;
         
        private var screenLayer:Sprite = new Sprite();
        private var transitionLayer:Sprite = new Sprite();
        private var transition:Transition;
        private var transTimer:Number = 0;
        private var makeTransition:Boolean;
         
        public function ScreenHandler() {
            this.addChild(screenLayer);
            this.addChild(transitionLayer);
            splashScreen = new SplashScreen();
            screenLayer.addChild(splashScreen);
        }
         
        public function switchTo(screenName:String, trans:Boolean = true):void{
            newScreenName = screenName;
            makeTransition = trans;
            this.addEventListener(Event.ENTER_FRAME, switchScreens);
        }
         
        private function switchScreens(e:Event):void{
            if(makeTransition){
                transTimer++;
                if(transTimer == 1 && transitionLayer.numChildren < 1){
                    transition = new Transition();
                    transitionLayer.addChild(transition);
                }
                if(transTimer == transition.exitFrames){
                    removeOldScreen();
                    makeNewScreen();
                    transTimer = 0;
                    this.removeEventListener(Event.ENTER_FRAME, switchScreens);
                }
            } else {
                removeOldScreen();
                makeNewScreen();
                this.removeEventListener(Event.ENTER_FRAME, switchScreens);
            }
        }
         
        private function removeOldScreen():void{
            var oldScreen:MovieClip;
            oldScreen = screenLayer.getChildAt(0) as MovieClip;
            screenLayer.removeChild(oldScreen);
        }
         
        private function makeNewScreen():void{
            switch(newScreenName){
                case «SplashScreen»:
                    splashScreen = new SplashScreen();
                    screenLayer.addChild(splashScreen);
                break;
                case «MainMenu»:
                    mainMenu = new MainMenu();
                    screenLayer.addChild(mainMenu);
                break;
                case «LevelSelect»:
                    levelSelect = new LevelSelect();
                    screenLayer.addChild(levelSelect);
                break;
                case «Game»:
                    game = new Game();
                    screenLayer.addChild(game);
                break;
                case «Credits»:
                    credits = new Credits();
                    screenLayer.addChild(credits);
                break;
                case «Victory»:
                    victory = new Victory();
                    screenLayer.addChild(victory);
                break;
                default:
                    mainMenu = new MainMenu();
                    screenLayer.addChild(mainMenu);
                break;
            }
            newScreenName = «»;
        }
    }
}

Вот как вы реализуете это в классе Application:

1
public static var screens:ScreenHandler = new ScreenHandler();

в конструкторе приложений добавьте

1
this.addChild(screens);

и используйте эту функцию в любом месте вашего кода для переключения экранов:

1
Application.screens.switchTo(«SelectedScreen»);

Если вы не хотите переход экрана:

1
Application.screens.switchTo(«SelectedScreen», false);


Я верю, что выполнил то, что намеревался сделать. Класс прост в использовании и даже более универсален в добавлении переходов экрана, чем временная шкала good ole. Я надеюсь, что вы получите некоторую пользу от этого класса, и даже улучшите его и сделаете его еще более универсальным. Небо — это предел с переходами экрана, и, возможно (возможно), вы можете придумать улучшенные методы управления архитектурой экрана: безболезненный путь!


Надеюсь, вам понравился этот урок, спасибо за чтение!