Возрождение Framework-а (часть 2)

от автора

Как я и обещал, продолжаю описание процесса разработки новой версии Marmalade Framework. Сегодня я расскажу об описаниях scopes и regions, позволяющих строить каркас приложения. Также я расскажу о том, как будут определяться и использоваться переменные и приберегу небольшой бонус напоследок.

В качестве примера рассмотрим следующее описание:

main.json

  resources:  { world_frame: { localized:        Y,                                file:             backgrounds/world_frame.png                              },                 b_1:         { file:             backgrounds/world_1.png                              },                 b_2:         { file:             backgrounds/world_2.png                              },                 w_1:         { localized:        Y,                                file:             labels/world_1.png                              },                 w_2:         { localized:        Y,                                file:             labels/world_2.png                              }               },   templates:  { worlds:      { regions:      [ { action:          w_1,                                                  name:            wr_1,                                                  x:               1,                                                  y:               1,                                                  width:          -1,                                                  height:          30,                                                  image:           w_1                                                },                                                { action:          w_2,                                                  name:            wr_2,                                                  lock:            stars>=10,                                                  x:               1,                                                  y:               31,                                                  width:          -1,                                                  height:          30,                                                  image:           w_2                                                }                                              ]                              }               },   scopes:     { start:       { default:          Y,                                vars:           { type:            number,                                                  stars:           0                                                },                                vars:           { background:      world_frame,                                                  back:            quit                                                },                                regions:      [ { zorder:         -1,                                                  x:               10,                                                  y:               10,                                                  width:          -10,                                                  height:         -10,                                                  template:        worlds                                                }                                              ]                              },                 w_N:         { load:             w_N.json,                                game:             arcanoid,                                vars:           { type:            number,                                                  life:            5,                                                  score:           0                                                },                                vars:           { background:      b_1,                                                  back:            back                                                },                                set:            { event:           impact,                                                  score:           score+10                                                }                              }               } 

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

Первоначально, в стек помещается состояние помеченное настройкой default. Переключение состояний выполняется при помощи действий (action). Если имя выполняемого действия совпадает с одним из scope, его выполнение приводит к загрузке этого состояния на вершину стека. Несколько предопределенных действий (back, exit, quit) снимают одно или несколько состояний с вершины стека. Как только стек становится пустым, приложение завершается.

Сами по себе scope не имеют какого-то экранного отображения, но они определяют списки регионов. Регион задает прямоугольную область на экране и может быть связан с одним или несколькими графическими ресурсами. Также, регион может содержать вложенные подрегионы.

Посмотрев на описание выше, можно понять, что мы определили меню выбора игрового мира:

                               regions:      [ { zorder:         -1,                                                  x:               10,                                                  y:               10,                                                  width:          -10,                                                  height:         -10,                                                  template:        worlds                                                }                                              ] 

Здесь мы определяем регион, не имеющий экранного отображения с отступом 10 условных единиц от каждой стороны экрана (вспоминаем, что я говорил про отрицательные координаты и размеры в прошлый раз), расположенный ниже фонового изображения (в котором будет сделано прозрачное окно). Он загружает две кнопки для выбора игрового мира:

                               regions:      [ { action:          w_1,                                                  name:            wr_1,                                                  x:               1,                                                  y:               1,                                                  width:          -1,                                                  height:          30,                                                  image:           w_1                                                },                                                { action:          w_2,                                                  name:            wr_2,                                                  lock:            stars>=10,                                                  x:               1,                                                  y:               31,                                                  width:          -1,                                                  height:          30,                                                  image:           w_2                                                }                                              ] 

Настройка action определяет действие, которое будет возбуждаться при нажатии на кнопку. Допускается перечисление нескольких действий разделенных символом ‘/’ (например: back/w_1). Настройка lock определяет, при каких условиях кнопка будет видимой.

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

И состояния и регионы могут определять переменные. Рассмотрим их подробнее:

                               vars:           { type:            number,                                                  stars:           0                                                }, 

Здесь мы определяем числовую переменную stars и присваиваем ей значение 0. Поскольку переменная определена в стартовом состоянии, она будет автоматически сохранена в state.json, при завершении приложения:

{ set:  { stars: 100         } } 

Секция set из state.json будет автоматически добавлена в стартовое состояние и изменит значение сохраненной переменной при его открытии. В качестве значений переменных, в описаниях set и vars могут использоваться произвольные арифметические выражения, с участием переменных определенных ранее.

После определения, переменная становится доступна в том состоянии или регионе, в котором она была определена, а также во всех состояниях находящихся выше по стеку и всех регионах и подрегионах, загруженных текущим состоянием. Используя описание vars, мы можем переопределить значение переменной на любом участке этой иерархии, либо создать синоним, связав имя с ранее определенной переменной (в случае, если в качестве значения указывается имя переменной). Выполнение описания set приводит к поиску переменной вниз по иерархии и изменению ее значения.

Имена действий, а также значение служебной переменной background (определяющий ресурс фонового рисунка) также могут переопределяться конструкцией vars:

                               vars:           { background:      world_frame,                                                  back:            quit                                                }, 

Здесь мы определяем ресурс фонового рисунка, а также переопределяем действие back (возбуждаемое при нажатии на Android-устройстве кнопки «назад») действием quit (снимающим со стека все состояния и завершающим работу приложения). В состояниях, загруженных позднее, эти определения могут быть изменены:

                               vars:           { background:      b_N,                                                  back:            back                                                }, 

Здесь действию back возвращается его первоначальное значение.

Теперь поговорим о магии. Она у нас будет:

                w_N:         { load:             w_N.json,                                game:             arcanoid,                                vars:           { type:            number,                                                  life:            5,                                                  score:           0                                                },                                vars:           { background:      b_N,                                                  back:            back                                                },                                set:            { event:           impact,                                                  score:           score+10                                                }                              } 

Этот синтаксический сахар, также как и шаблоны, позволит бороться с копипастом. Если в имени состояния присутствуют заглавные буквы, при загрузке состояния оно сравнивается с действительным именем (например w_1) и, по результату сравнения, в состояние автоматически добавляются переменные следующего вида:

{ vars:  { N: 1          } } 

При обработке значения (например w_N.json) мы выполняем поиск соответствующих переменных и замену заглавных букв найденными значениями. В результате, при переходе в состояние w_1 будет загружено локальное описание w_1.json (а также все описания загруженные им при помощи опции load, рассмотренной ранее). Поскольку цифр от 0 до 9 нам может не хватить, последующие значения 10, 11,… будут соответствовать строчным буквам латинского алфавита от ‘a’ до ‘z’.

Рассмотренное выше состояние w_N будет служить оберткой к игровому интерфейсу arcanoid. Задача этого состояния — инициализировать игровой интерфейс, передать ему значения определенных в этом состоянии переменных и запустить на выполнение. Обратная связь осуществляется при помощи конструкций следующего вида:

                               set:            { event:           impact,                                                  score:           score+10                                                } 

Это присвоение срабатывает при получении от игрового интерфейса действия impact и изменяет значение переменной score. Изменение переменной приводит к автоматической передаче ее нового значения в игровой интерфейс. Разумеется, таким же образом, игровой интерфейс может формировать такие действия как back или quit.

Напоследок, обещанный бонус. Рассмотрим внимательно следующую конструкцию:

main.json

  templates:  { worlds:      { regions:      [ { action:          w_1,                                                  name:            wr_1,                                                  x:               1,                                                  y:               1,                                                  width:          -1,                                                  height:          30,                                                  image:           w_1                                                },                                                { action:          w_2,                                                  name:            wr_2,                                                  lock:            stars>=10,                                                  x:               1,                                                  y:               31,                                                  width:          -1,                                                  height:          30,                                                  image:           w_2                                                }                                              ]                              }               } 

На мой взгляд, в ней слишком много повторов, ведущих к увеличению размера описания и появлению возможности рассогласования данных (здесь всего два элемента в списке, а что делать если их будет 10?). Ранее я обещал рассказать, зачем могут понадобиться локальные шаблоны. Вот как эта конструкция будет выглядеть с их применением:

main.json

  templates: { wt_N:   { action:      w_N,                          name:        wr_N,                          template:    lock,                          x:           1,                          template:    y,                          width:      -1,                          height:      30,                          image:       w_N                        },                worlds: { regions: [ { templates: { y:    { y:    1 } },                                       template:            wt_1                                     },                                     { templates: { y:    { y:    31 } },                                       templates: { lock: { lock: stars>=10 } },                                       template:            wt_1                                     }                                   ]                        }              }, 

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

На этом все. В следующей статье мы приступим к программированию.

ссылка на оригинал статьи http://habrahabr.ru/post/170255/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *