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

в 7:53, , рубрики: game development, json, marmalade, Разработка под android, разработка под iOS, метки: ,

Как я и обещал, продолжаю описание процесса разработки новой версии 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
                                    }
                                  ]
                       }
             },

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

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

Автор: GlukKazan

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js