Как я и обещал, продолжаю описание процесса разработки новой версии Marmalade Framework. Сегодня я расскажу об описаниях scopes и regions, позволяющих строить каркас приложения. Также я расскажу о том, как будут определяться и использоваться переменные и приберегу небольшой бонус напоследок.
В качестве примера рассмотрим следующее описание:
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.
Напоследок, обещанный бонус. Рассмотрим внимательно следующую конструкцию:
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?). Ранее я обещал рассказать, зачем могут понадобиться локальные шаблоны. Вот как эта конструкция будет выглядеть с их применением:
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