...

суббота, 6 февраля 2016 г.

[Перевод] Обзор физики в играх Sonic. Часть 1: твердые тайлы

image

От переводчика: этот пост — перевод одной из частей масштабного обзора физики (Sonic Physics Guide) в играх серии Sonic the Hedgehog для Sega Genesis/Mega Drive и Sonic CD. В следующих частях рассматриваются такие темы: бег, прыжки, вращение, потеря колец, поведение под водой, суперскорость, специальные возможности, камера, анимации и некоторые другие. Так как частей много (14 штук), в конце поста я добавил опрос. Стоит ли продолжать — решать вам.

Примечание

В статье описываются столкновения и взаимодействия Соника с твердыми тайлами. Твердые объекты, такие как мониторы, подвижные платформы и блоки, имеют собственные процедуры обработки столкновений с Соником, которые не обязательно совпадают с поведением твердых тайлов.

Введение

Что такое твердые тайлы? В зонах (уровнях), из которых состоят игры про Соника, очень много твердых объектов, поэтому зона потребовала бы слишком много памяти, если окружение было бы целиком составлено из твердых объектов, каждый из которых занимает 64 байта RAM. Был сделан умный ход — зона создается из тайлов, поэтому все, что требуется — знать, является ли тайл твердым (непроницаемым), или нет.

Возможно, вы знаете, что зоны разбиты на блоки размером 128x128 пикселей (или 256x256 пикселей в Sonic 1 и Sonic CD), которые, в свою очередь, разбиты на тайлы в 16x16 пикселей; они также разбиты на более мелкие тайлы размером 8x8 пикселей. Вся магия обработки твердых элементов происходит на уровне тайлов 16x16, поэтому в этом обзоре нас будут интересовать только они.

Взаимодействие и столкновения Соника с этими твердыми тайлами и образуют базовый движок игры. Они определяют способ работы с полами, стенами, потолками, скатами и петлями. Поскольку это объемная и сложная тема, мой обзор будет отличаться от других руководств по физике игр Sonic, но я постараюсь ограничить рассуждения до минимума.

Стояние

image

Если принять положение земли по оси Y равным $02E0, то Соник стоит над ней в координате $02CC. Это на 20 пикселей выше уровня земли.

Толкание

Соник должен останавливаться при ударе об стену. Этого можно достичь, проверяя коллизию с линией. Линия коллайдера (от переводчика: здесь и далее в статье используется термин sensor, я выбрал, как мне кажется, адекватный аналог) должна находиться не на его позиции на оси Y, а немного ниже, иначе она «не заметит» короткие шаги. Она также не может быть слишком низко, иначе она «заметит» склоны и кривые, от которых Соник не должен отталкиваться. Достаточным будет ее расположение в координате Y+4 от расположения Соника, потому что обычно не бывает лестниц ниже 16 пикселей в высоту (когда такие лестницы есть, то они состоят из твердых объектов, а не тайлов).

Насколько широкой должна быть линия коллайдера?

image

Если принять координату X левой стороны стены равной $02C0, то Соник не должен оказаться ближе $02B5. Если принять координату X правой стороны стены равной $033F, то Соник не должен оказаться ближе $034A. Это составляет разницу в 11 пикселей в обоих направлениях.

Поэтому линия коллайдера должна иметь ширину 20 пикселей, и быть растянутой от X-10 до X+10 Соника. При каждом обнаружении твердого тайла Соник должен «выталкиваться» установкой координаты тайла минус (или плюс) 11, при этом скорость перемещения по земле должна обнуляться. (Его нельзя вытолкнуть только на 10 пикселей, в противном случае точка с координатами X+10 будет по-прежнему находиться внутри граничного пикселя тайла. При этом будет зарегистрирована постоянная коллизия, и он прилипнет к стене.)

Так как граница тайла минус положение Соника должно равняться 11, между центром Соника и границей тайла есть всего 10 свободных пикселей. Одиннадцатый пиксель от Соника будет уже самой границей тайла. Поэтому Соник в действительности имеет ширину 20 пикселей.

Падение

Соник должен иметь возможность сбегать с площадок. Он не может вести себя как койот Wile E. Coyote из мультфильмов, не замечающий, что под ним ничего нет.

Это означает, что Соник также должен проверять наличие под ним твердых тайлов. Этого можно достичь, добавив еще две линии коллайдеров, направленных вниз. Один (A) должен быть с левой стороны Соника, в координате X-9. Другой (B) — с правой стороны, в X+9. Они должны начинаться с его положения по оси Y и опускаться не менее чем на 16 пикселей ниже от его ног на уровне земли, которая расположена в Y+20 (но не слишком низко, или он будет сваливаться при спуске с низких ступенек или лестниц, чего бы нам не хотелось).

Если коллайдеры A и B не обнаруживают твердых тайлов, Соник «падает» — устанавливается флаг, сообщающий движку, что он находится в воздухе.

Мы помним, что при столкновении со стенами Соник имеет ширину 20 пикселей. Однако коллайдеры обнаружения земли находятся всего в 18 пикселях от центра. Получается, что Соник «худее» на 2 пикселя при сбегании с площадок, чем при ударах о стены.

Балансирование на краю

Хорошая деталь — Соник переходит в анимацию балансирования, находясь близко к краю площадки. Это случается, только когда он остановился (его скорость движения по земле равна 0).

Откуда об этом узнает движок? Все просто — в момент, когда активен только один из коллайдеров, Соник находится на краю. Если A активен, а B — нет, то площадка находится справа от него. В противном случае площадка слева.

Однако если Соник начнет балансировать, сразу же, как только один из коллайдеров ничего не обнаружит, он начнет балансировать «рано», и это будет выглядеть глупо. Поэтому это случается, когда активен только один коллайдер, и его положение по оси X больше, чем граница твердого тайла, обнаруженного активным коллайдером.

Если принять координату правого края площадки равной $0A5F, Соник начнет балансировать только в $0A60. Он упадет в точке $0A69, когда оба коллайдера ничего не обнаружат.

В Sonic 2 and Sonic CD если площадка находится в противоположном от взгляда Соника направлении, то включается вторая анимация балансирования.

В Sonic 2, Sonic 3 и Sonic & Knuckles Соник имеет третью анимацию балансирования, когда он еще ближе к краю площадки. С учетом установленных выше значений, она включается, когда он в координате $0A66.

Примечание: при балансировании некоторые возможности недоступны (прижимание к земле, смотрение вверх, вращение и т.д.). В Sonic 3 & Knuckles игрок может прижиматься к земле и вращаться (но не смотреть вверх) при балансировании на земле, но не при балансировании на объекте.

Скаты и кривые

Sonic the Hedgehog стала одной из первых игр, использующих кривые поверхности и петли с оборотом на 360 градусов. В большинстве игр той эры окружение полностью создавалось из блоков (и иногда из склонов).

Возможность работы с плавно изменяющими форму объектами — это один из фундаментальных аспектов новизны и привлекательности игр про Соника. К сожалению, это, наверно, самый сложный для воссоздания в фанатских играх аспект.

Как же он работает?

Маски высот

При каждом обнаружении коллайдерами A или B твердого тайла они возвращают высоту этого тайла.

Как находится высота тайла?

Каждый тайл имеет ассоциированное с ним значение, привязанное к маске, хранящейся в памяти. Маска — это простой массив из 16 значений высоты в диапазоне от $00 до $10 и значения угла.

Например, эта маска высот имеет массив высот $00 00 01 02 02 03 04 05 05 06 06 07 08 09 09 09 и угол $E8.

Какое используется значение массива высот? Вычитаем положение X тайла из положения X коллайдера. Результат будет используемым индексом массива высот.

Если найденное значение высоты равно $10, то коллайдер должен проверить еще один тайл выше первого найденного, и определить его значение высоты.

Какой бы коллайдер ни обнаружил самую большую высоту, координата Y Соника устанавливается равной этой высоте минус 20 пикселей. Его угол также устанавливается равным углу твердого тайла, вернувшего самую большую высоту.

Если коллайдер не обнаружил твердый тайл, по умолчанию возвращается уровень ног (Y+20).

Ошибки при использовании этого метода

К сожалению, из-за использования этого метода в оригинальном движке есть пара раздражающих багов.

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

Соник поднимается вместе с коллайдером B при перемещении вправо. Когда B «падает» с площадки, Соник по умолчанию перемещается на уровень коллайдера A. Затем он поднимается вместе с коллайдером A при дальнейшем движении вправо. Поэтому он сначала будет подниматься, падать и снова подниматься при сбегании с площадки.

Есть всего несколько областей, в которых это заметно, но такой баг присутствует во всех частях игры для Genesis/Mega Drive, и это выглядит довольно неаккуратно.

Второй баг возникает в случае наличия двух тайлов противоположных склонов одного над другим, таких как низкие холмы на уровнях Green Hill Zone и Marble Zone.

Коллайдер B начинает спускаться со склона вправо, но Соник все еще по умолчанию ориентируется на уровень предыдущего склона, обнаруженного коллайдером A. Поскольку такие склоны довольно невысокие, это приводит к опусканию Соника в середине примерно на 1 пиксель.

Но это еще не все. Соник получает данные об угле от самого высоко расположенного коллайдера, поэтому даже если похоже, что он должен быть под углом склона справа (потому что он ближе к нему), он по-прежнему будет иметь угол склона слева. При прыжке он будет прыгать под этим углом, двигаясь назад, а не вперед, как ожидается.

Перемещение под углами

Все это очень хорошо и замечательно — Соник плавно движется по поверхности с различными высотами. Однако движку нужно сделать еще кое-что. Для реалистичности скорость Соника должна снижаться на поверхностях под углом.

Существует два способа, которыми углы могут влиять на скорость Соника. Первый гарантирует, что он не пробегает по холму за то же время, что и по плоской земле той же ширины. Второй замедляет его при движении вверх по холму и ускоряет его при движении вниз. Давайте рассмотрим каждый из них по очереди.

Три переменных скорости

Если бы Соник был обычным платформером, использующим только блоки, для него понадобились бы только две переменные скорости: движение по оси X (Xsp) и по оси Y (Ysp), горизонтальная и вертикальная составляющие скорости Соника. Ускорение (acc), замедление (dec) и трение (frc) добавляются к Xsp; скорость прыжка (jmp) и гравитация (grv) добавляются к Ysp (когда Соник находится в воздухе).

Однако при использовании скатов, когда Соник движется по скату, он одновременно перемещается горизонтально и вертикально. Это означает, что и Xsp, и Ysp имеют ненулевые значения. Простое добавление acc, dec или frc к Xsp больше не работает; представьте, что Соник пытается бежать вверх по стене — прибавление к горизонтальной скорости будет бесполезным, ведь он пытается двигаться вверх.

Хитрость заключается в задействовании третьей переменной скорости (что и делает оригинальный движок), назовем ее скоростью перемещения по земле (Gsp). Это скорость Соника вдоль земли вне зависимости от угла. Значения acc, dec и frc прибавляются к Gsp, а не к Xsp или Ysp.

При нахождении на земле Xsp и Ysp извлекаются из Gsp на каждом шаге перед перемещением Соника. Думаю, здесь будет уместен пример на псевдокоде:

Xsp = Gsp*cos(angle);
Ysp = Gsp*-sin(angle);
 
X += Xsp;
Y += Ysp;


Вне зависимости от изменения угла скорость Gsp сохраняется, поэтому движок всегда знает, с какой скоростью Соник «по-настоящему» движется.

Коэффициент ската

Теперь Соник может обрабатывать взаимодействие с любыми холмами, сохраняя точную скорость. Однако ему по-прежнему необходимо замедляться при подъеме и ускоряться при спуске.

К счастью, этого легко добиться, воспользовавшись понятием коэффициента ската (slp). Просто будем прибавлять slp*sin(angle) к Gsp в начале каждого шага. Значение slp при беге всегда равно 0.125 ($0020), но отличается при вращении. Когда Соник вращается, поднимаясь вверх по холму (знак Gsp не равен знаку sin(angle)), slp равен 0.078125 ($001E). При вращении вниз по холму (знак Gsp равен знаку sin(angle)) slp равен 0.3125 ($0050).

Примечание: похоже, что в Sonic 1 величина slp не прибавляется, если Соник останавливается и находится в цикле стояния/ожидания. Однако в Sonic 3 & Knuckles величина slp прибавляется даже в этом случае, поэтому Соник не может стоять на крутых скатах — ему приходится спуститься по ним назад.

Прыжки под углами

Угол, под которым стоит Соник в момент прыжка, также влияет на прыжок. Он не может просто присвоить Ysp значение jmp, ему необходимо отпрыгнуть от угла, под которым он стоит. Поэтому значение jmp нужно присвоить и Xsp, и Ysp, воспользовавшись cos() и sin() для получения правильных значений.

Еще немного псевдокода:

Xsp -= jmp*sin(angle);
Ysp -= jmp*cos(angle);


Переключение режимов

Итак, Соник может бегать по холмам, склонам и площадкам, и это уже неплохо. Но этого недостаточно. Он не может проделать путь с земли к стенам и потолку без дополнительного кода.

Почему? Коллайдеры земли A и B проверяют препятствия непосредственно под ними, находя высоту земли. Они никак не могут обработать переход к стенам, потому что вся схема рассчитана на перемещение ровно вверх и вниз по оси Y.

Как решить эту проблему? Использованием четырех различных режимов движения. Это требует небольшого объяснения.

Четыре режима

Довольно логично предположить, что если Соник может перемещаться на 360 градусов, движок примерно одинаковым образом обрабатывает все 360 градусов. На самом же деле движок разделяет углы на четыре квадранта, что значительно упрощает работу.

Для лучшего понимания того, о чем я говорю, представим более простой платформер без полных петель, только с низкими холмами и склонами. Все, что нужно сделать персонажу после горизонтального перемещения — переместиться вверх или вниз, пока он не достигнет уровня пола. Затем нужно замерить угол пола. Угол используется для снижения Gsp, и ни для чего более. Персонаж по-прежнему всегда будет двигаться горизонтально и перемещаться строго вверх и вниз для достижения уровня пола.

В основном также происходит и в играх про Соника. Только если угол становится слишком крутым, Соник меняет «квадрант», переключаясь из режима пола в режим правой стены (затем в режим потолка, левой стены, и обратно в режим пола, и так далее). В любой момент времени и в любом отдельном режиме Соник ведет себя как в обычном платформере. Магия возникает при комбинировании этих четырех режимов с умно сделанным плавным переключением между ними.

Но как и когда Соник переключается между режимами?

Если при нахождении в режиме пола обнаружен угол больше $E0, движок переключается в режим правой стены. В основном все остается прежним, только коллайдеры проверяют правую сторону вместо низа, и Соник перемещается на уровень «пола» горизонтально, а не вертикально.

Если при нахождении в режиме правой стены обнаруживается угол меньше $E0, движок переключается обратно в режим пола.

Другие переходы работают аналогичным образом.

Может возникнуть справедливый вопрос — где находятся коллайдеры земли, когда мы находимся в режиме правой стены? Они расположены там же, но повернуты на 90 градусов. Коллайдер A находится относительно центра Соника в Y+9 вместо X-9. Коллайдер B находится в Y-9 вместо X+9. Линии коллайдеров становятся не вертикальными, а горизонтальными, растянувшись на 16 пикселей за пределы уровня его ног (который теперь на 20 пикселей «ниже» Соника, в координате X+20).

Да, поскольку коллайдеры перемещаются, Соник может быть «вытолкнут» на новое положение в шаге, на котором переключается режим. Однако перемещение составляет всего несколько пикселей, и при обычной игре совсем незаметно.

Еще один аспект: как я уже сказал, твердые тайлы состоят из массивов высот. Ключевое слово здесь «высота». Как они ведут себя в режиме правой стены? Довольно удивительным образом — оказывается, в оригинальном движке каждый твердый тайл имеет два дополняющих друг друга массива высот; один используется для горизонтального, а другой — для вертикального перемещения.

А как насчет режимов левой стены и потолка? Разве не нужны четыре массива высот? Нет, потому что тайлы такой формы используют обычные массивы высот, только инвертированные. В режиме потолка Соник знает, что найденное значение высоты должно использоваться для перемещения его вниз, а не вверх.

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

Падение со стен и потолков

Находясь в режиме правой стены, левой стены или потолка, Соник падает, когда абсолютное значение Gsp становится ниже 2.5 ($0280) (Gsp в этот момент приравнивается к 0, однако Xsp и Ysp не изменяются, поэтому Соник может продолжить двигаться по своей траектории в воздухе). Это происходит, даже когда под ним есть земля.

Соскальзывание назад

Когда Соник падает указанным выше способом, таймеру блокировки горизонтально управления присваивается значение $1E (таймер не начнет обратный отсчет, пока Соник не прикоснется к земле). Пока значение таймера не равно нулю, а Соник находится на земле, он не дает игроку изменять скорость Соника кнопками «вправо» и «влево». Значение таймера уменьшается на 1 с каждым шагом, поэтому блокировка длится примерно полсекунды. В течение этого времени на движение влияют только slp и скорость, с которой Соник упал на землю, поэтому Соник будет соскальзывать вниз по скату.

Состояние нахождения в воздухе

Когда Соник находится в воздухе, не нужно волноваться об углах, Gsp, slp и прочем. Все, что необходимо — перемещаться с использованием Xsp и Ysp до обнаружения контакта с землей, после чего перейти в режим нахождения на земле.

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

Коллайдеры нахождения в воздухе

Горизонтальный коллайдер

Соник должен ударяться о стены так же, как он делает это, находясь на земле. Поэтому имеется линия горизонтального коллайдера, исходящая из его положения Y и растянувшаяся от X-10 до X+10. При столкновении с твердым тайлом Соник «выталкивается» к границе тайла плюс/минус 11, как и находясь на земле.

Разница в том, что эта линия коллайдера шире, чем вертикальные коллайдеры A и B, обнаруживающие наличие земли (мы вернемся к ним чуть ниже). Это означает, что линия горизонтального коллайдера может обнаружить блок, мимо которого пролетает Соник, даже если у него совсем нет Xsp. Поэтому он проскальзывает с твердых предметов при ударах об их крайние грани.

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

Вертикальные коллайдеры

Коллайдеры A и B Соника действуют в воздухе примерно так же, как и на земле. Разница в том, что при обнаружении твердого тайла высота Соника не устанавливается сразу же равной высоте, обнаруженной для тайла, минус 20 пикселей. Вместо этого такая высота устанавливается только когда он уже ниже этой высоты. Иначе он бы прилип к полу при приближении к нему.

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

Поскольку Соник в воздухе может перемещаться как вверх, так и вниз, должно быть еще два коллайдера, проверяющих сверху (C и D), чтобы он мог ударяться о потолок и кривые, расположенные выше него. (C и D являются точными зеркальными отражениями A и B — они имеют то же положение X и длину, только направлены не вниз, а вверх.) Соник обнаруживает потолки и отталкивается от них, двигаясь как вверх, так и вниз, в отличие от полов, которые обнаруживаются только при движении вниз. Можно удариться о «потолок» (который на самом деле является нижней частью блока) при движении вниз, прижавшись к стене, в которой есть зазор, или прыгнув к стороне верхней кривой.

Прыжки «через» пол

Есть площадки, которые Соник может пролетать «насквозь» при прыжках вверх. Они часто встречаются в холмистых зеленых зонах, таких как Green Hill Zone, Emerald Hill Zone, Palmtree Panic Zone и так далее. Твердые тайлы, составляющие такие площадки, помечаются движком как имеющие особый тип, который может обнаруживаться только коллайдерами A и B. Они игнорируются коллайдерами C и D, а также линией горизонтального коллайдера.

Повторное обнаружение земли

Когда Соник находится на земле, Xsp и Ysp получаются из Gsp. Когда он падает или иным образом отрывается от земли, Xsp и Ysp уже содержат нужные значения для продолжения траектории в воздухе. Но когда Соник приземляется, Gsp должна быть рассчитана из Xsp и Ysp, имевшихся на момент приземления. Можно подумать, что для получения точного значения используются cos() и sin(), но это не так. В действительности происходит нечто гораздо более простое, при этом алгоритмы при ударе об искривленный потолок и при приземлении на искривленную землю различаются, поэтому я рассмотрю их отдельно.

При движении вниз

Если угол обнаруженной земли находится в диапазоне $F0~$FF (и их зеркальных отражений $00~$0F), Gsp присваивается значение Xsp. Если угол находится в диапазоне $E0~$EF ($10~$1F), Gsp присваивается значение Xsp, но только если абсолютное значение Xsp больше, чем Ysp. В противном случае Gsp равна Ysp*0.5*-sign(cos(angle)). Если угол находится в диапазоне $C0~$DF ($20~$3F), Gsp равна Xsp, если абсолютное значение Xsp больше Ysp. Иначе Gsp присваивается значение Ysp*-sign(cos(angle)).

При движении вверх

Если угол обнаруженного потолка находится в диапазоне $A0~BF ($40~$5F), Соник прикрепляется к потолку, а Gsp принимает значение Ysp*-sign(cos(angle)). Если угол находится в диапазоне $60~9F, Соник ударяется головой, как об обычный потолок, и не прикрепляется к нему. Ysp обнуляется, а Xsp не изменяется.

Справка: преобразование углов

В играх на Genesis/Mega Drive используются значения углов в шестнадцатеричном формате, от $00 до $FF, поэтому круг разделен не на 360 частей, как мы привыкли, а на 256. Что еще хуже, в отличие от углов в других языках, например, GML, они считаются против часовой стрелки, поэтому $20 равен не 45°, как должен, а 315°.

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

return (256-hex_angle)*1.40625;


Примечания
  • Соник может тормозить («скрипя» ногами) только в режиме пола.
  • Соник не может прыгать, если над ним есть низкий потолок. Если обнаружена коллизия в Y-25 с линией коллайдера, расположенной от X-9 до X+9, то Соник не начнет прыгать.
  • В разные моменты Соник имеет разную высоту. Когда он стоит, бежит, падает или взлетает на пружинной платформе, он имеет высоту 40 пикселей. Его положение по оси Y всегда является его центром, поэтому он всегда стоит в 20 пикселях над землей (и в 20 пикселях под потолками, когда стукается об них). Однако при прыжках и вращении он имеет высоту всего 30 пикселей, и поднят на 15 пикселей над землей (а также опущен на 15 пикселей ниже потолка, и т.д.). В шаге, в котором Соник вращается или прыгает, движок добавляет 5 к его положению Y, поэтому несмотря на то, что он становится короче и его центр меняет положение, положение нижней точки не изменяется. При завершении вращения или приземлении после прыжка 5 пикселей вычитается из Y. Система камеры также должна учитывать это смещение, иначе при изменении высоты Соника вид будет также изменяться.
  • Коллайдеры A, B, C и D, описанные в этом обзоре, расположены в координатах X-9 и X+9. Это справедливо только при ходьбе, падении, отталкивании от пружинной платформы и так далее — всегда, когда он не свернут в клубок. Если Соник вращается или прыгает, они находятся в координатах X-7 и X+7. Однако его линия горизонтального коллайдера всегда остается одинаковой.

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.

Встреча с Евгением Бурмако: макросы в Scala

сегодня в 14:10

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

В грядущий вторник, 9 февраля, у нас в гостях будет Евгений Бурмако, PhD студент EPFL (École polytechnique fédérale de Lausanne), работающий в команде, ответственной за поддержку и эволюцию Scala. Во время своего пребывания в EPFL Евгений придумал макросы для Scala и реализовал их в официальном компиляторе. Последнее время Евгений работает над новым проектом, который значительно упрощает метапрограммирование в Scala и предоставляет новые возможности авторам средств анализа кода и интегрированной разработки.

Тема доклада: Какие можно сделать выводы из эксперимента с макросами в Scala?

Вот описание встречи от Евгения: «Доклад резюмирует мое кандидатское исследование на тему метапрограммирования в Scala. Мы начнем с рассмотрения макросов — встроенной в язык функциональности для поддержки метапрограммирования, после чего обсудим их популярность и влияние на стиль программирования на Scala. Также мы увидим, как эксперимент с макросами привел нас к разработке более совершенной системы рефлексии».

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

Время и место: Начало в 15-00. Доклад пройдет на 7м этаже главного офиса разработок JetBrains в БЦ «Universe».

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

Автор: @beenom

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

This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.

AI, BigData & HPC Digest #4

Привет, Хабрапользователь. Кажется, ты используешь AdBlock. Хабрахабр
развивается и существует за счет доходов от рекламы. Добавь нас в исключения.

открытое письмо как отключить

This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.

Что новенького предлагает Git 2.7

Всего через два месяца после версии 2.6 вышел Git 2.7 с новыми возможностями, исправлениями и улучшениями производительности. Что интересного он нам приготовил? Я расскажу о нескольких новинках, которые показались интересными команде Bitbucket.

Полноценный набор команд git worktree


Команда git worktree появилась в Git 2.5, она позволяет выгружать и одновременно работать со многими ветками репозитория в отдельных папках. Например, если нужно сделать срочную правку, но при этом не хочется трогать текущую рабочую копию, можно просто выгрузить нужную ветку в новую папку с помощью команды:
$ git worktree add -b hotfix/BB-1234 ../hotfix/BB-1234
Preparing ../hotfix/BB-1234 (identifier BB-1234)
HEAD is now at 886e0ba Merged in bedwards/BB-13430-api-merge-pr (pull request #7822)


Git 2.7 добавляет команду git worktree list, которая выводит список рабочих копий репозитория и веток, ассоциированных с ними:
$ git worktree list
/Users/kannonboy/src/bitbucket/bitbucket            37732bd [master]
/Users/kannonboy/src/bitbucket/staging              d5924bc [staging]
/Users/kannonboy/src/bitbucket/hotfix/BB-1234       37732bd [hotfix/BB-1234]


Улучшена поддержка многих рабочих копий командой git bisect. Ссылки, которые использовались bisect для «хороших» и «плохих» коммитов, переехали из .git/refs/bisect в .git/refs/worktrees/$worktree_name/refs/bisect, поэтому теперь стала возможной одновременная работа bisect в разных рабочих копиях репозитория.

Кроме того, начиная с Git 2.7 можно использовать git clone, указав в качестве аргумента такую отдельную рабочую копию, — при этом будет создан независимый git-репозиторий, а не ещё одна рабочая копия существующего.

Примечательно, что отдельные рабочие копии могут быть созданы не только для веток. Как и множество других команд, git worktree add можно вызвать с указателем на коммит, будь то его хеш или тег:

$ git worktree add ../git-2.4.7 ca00f80
Preparing ../git-2.4.7 (identifier git-2.4.7)
HEAD is now at ca00f80 Git 2.4.7

$ git worktree add ../git-v2.6.0 v2.6.0
Preparing ../git-v2.6.0 (identifier git-v2.6.0)
HEAD is now at be08dee Git 2.6

$ git worktree add ../git-v2.7.0 v2.7.0
Preparing ../git-v2.7.0 (identifier git-v2.7.0)
HEAD is now at 7548842 Git 2.7

$ git worktree list
/Users/kannonboy/src/git         7548842 [master]
/Users/kannonboy/src/git-2.4.7   ca00f80 (detached HEAD)
/Users/kannonboy/src/git-v2.6.0  be08dee (detached HEAD)
/Users/kannonboy/src/git-v2.7.0  7548842 (detached HEAD)

Несколько улучшений git stash


Если вы фанат git rebase, то, скорее всего, знакомы с опцией --autostash. Она автоматически сохраняет все локальные изменения во временное хранилище (stash) до выполнения rebase, а после его завершения применяет их снова.
$ git rebase master --autostash
Created autostash: 54f212a
HEAD is now at 8303dca It's a kludge, but put the tuple from the database in the cache.
First, rewinding head to replay your work on top of it...
Applied autostash.


Это удобно, поскольку можно делать rebase на «грязной» рабочей копии. Для ещё большего удобства существует параметр rebase.autostash, который делает описанное поведением по умолчанию. Применить его глобально можно с помощью команды:
$ git config --global rebase.autostash true


Этот параметр существует ещё с Git 1.8.4, но в Git 2.7 добавлена возможность отменить его с помощью опции --no-autostash. Скорее всего, эта опция добавлена для полноты, поскольку единственное, что она даёт при попытке выполнить rebase на «грязной» рабочей копии, — это соответствующее предупреждение:
$ git rebase master --no-autostash
Cannot rebase: You have unstaged changes.
Please commit or stash them.


Говоря о конфигурации, стоит также упомянуть о параметре stash.showPatch, который также появился в Git 2.7. При стандартных настройках команда git stash show выводит только краткую информацию о файлах во временном хранилище:
$ git stash show
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)


Если же дополнительно указать опцию -p, вывод будет дополнен расширенным описанием изменений файлов:
diff --git a/package.json b/package.json
index c876b26..e21eeb3 100644
--- a/package.json
+++ b/package.json
@@ -48,7 +48,7 @@
     "mkdirp": "^0.5.0",
     "byline": "^4.2.1",
     "express": "~3.3.4",
-    "git-guilt": "^0.1.0",
+    "git-guilt": "^0.1.1",
     "jsonfile": "^2.0.0",
     "jugglingdb-sqlite3": "0.0.5",
     "jugglingdb-postgres": "~0.1.0",


Параметр stash.showPatch делает это поведением по умолчанию. Применить его глобально можно с помощью аналогичной команды:
$ git config --global stash.showPatch true


Как и в предыдущем случае, включённый параметр можно отменить и тем самым вернуться к старому краткому выводу, — с помощью опции --stat:
$ git stash show --stat
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)


Будьте аккуратны: опция --no-patch не приводит к ошибке, однако она не отменяет stash.showPatch, как можно было бы ожидать.

Ускорение git filter-branch и индикатор прогресса


git filter-branch — это универсальный инструмент для изменения истории репозитория. Поскольку каждый коммит имеет ссылки на родительские коммиты, а значит, транзитивно ссылается на все коммиты, являющиеся его предками, изменение одного коммита неизбежно влечет за собой изменение всех его потомков. Это, в свою очередь, означает, что операция даже элементарного изменения истории может занять какое-то время, если изменить нужно достаточно старый коммит.

В Git 2.7 появился изящный индикатор прогресса, который отображает предполагаемое время до окончания выполнения filter-branch:

Помимо этого, в случае если filter-branch не изменяет объекты в индексе или деревья, то индекс вообще не читается при выполнении команды, что значительно увеличивает её производительность. Опция --commit-filter на анимации выше изменяет только автора каждого коммита и не затрагивает ассоциированные с ними объекты деревьев. Изменение первых 1000 коммитов Bitbucket Server заняло всего 38 секунд при использовании Git 2.7.0, в то время как аналогичная операция с Git 2.6.0 потребовала 64 секунды, то есть прирост скорости составляет целых 40%. Тесты производительности, появившиеся в Git 2.7 вместе с этими улучшениями, показывают ещё более впечатляющее ускорение — до 60%.

Улучшенное отрицание в .gitignore


Файлы .gitignore позволяют исключить из репозитория некоторые файлы, находящиеся в рабочей копии, т.е. они не будут добавляться в индекс. Шаблоны поддерживают флаг отрицания с помощью префикса !, чтобы было возможно отменить игнорирование определённого файла. Например, при таких шаблонах git будет игнорировать все файлы с расширением .json, кроме cat.json:
# .gitignore
*.json
!cat.json


Однако в Git 2.6 нельзя было применить отрицание к файлу, находящемуся в уже игнорируемой папке.
# .gitignore
/animals
!/animals/cat.json # <-- этот файл игнорируется в Git 2.6 и более ранних версиях


Начиная с Git 2.7, второй пример работает ровно так, как этого стоило ожидать: с помощью ! теперь можно отменить игнорирование файлов в папках, которые иначе были бы игнорированы.

Но и это ещё не всё!


Это только небольшая часть плюшек, которые появились в Git 2.7. Полный список изменений можно найти в заметках к релизу, а также в комментариях к коммитам в репозитории самого Git:
$ git log v2.6.0..v2.7.0




Автор оригинальной статьи — Тим Петтерсен, участвовал в разработке JIRA, FishEye/Crucible и Stash. С начала 2013 года он рассказывает о процессах разработки, git, непрерывной интеграции и поставке (continuous integration/deployment) и инструментах Atlassian для разработчиков, особенно о Bitbucket. Тим регулярно публикует заметки об этих и других вещах в Twitter под псевдонимом @kannonboy.

This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.

Хакерская ёлка, или Как провести Детский День в недетской компании

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

На самом деле, в основе мероприятия лежала вполне серьёзная идея: рассказать и показать детям сотрудников Positive Technologies, где и как работают их папы и мамы. Провести эдакую Позитивную Профориентацию.

Для чего? Есть одна проблема, с которой сталкиваются многие родители, работающие в таких абстрактных сферах, как наша. Взрослые каждый день уходят на загадочную работу, которую дети не видят и не понимают. Зато детей заставляют заниматься своими абстракциями в школе — и родители, в свою очередь, не знают, что происходит за школьным порогом. Эта ситуация не улучшает взаимопонимание в семье.

Когда моему старшему сыну было четыре года, он сказал в детском саду, что его папа работает дворником. В выходные мы с ним расчищали снег дворницкой лопатой, и неудивительно, что такое наглядное, весёлое и полезное дело запомнилось ему как отцовская «профессия». Подобные наблюдения привели к тому, что в моём «Руководстве по дзену для родителей» появилась глава «Скрытые миры». Один из её выводов — показывать детям свою работу надо, даже если вам кажется, что для них это сложно. Потому что дети понимают гораздо больше, чем кажется.

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

Как вы уже поняли по самой первой фотографии, Дед Мороз должен быть бородатым даже на хакерской ёлке. А вот кровавый халат в нашей профессии необязателен. Зато можно сделать симпатичные слайды для выступления.

К слову сказать, подготовка презентации для детей — хороший способ научиться делать презентации для взрослых. Коллеги-технари очень любят напихать в каждый слайд множество мелких схем и длинных текстов, а количество самих слайдов довести как минимум до 50. Потом они приходят к маркетологам и просят всё это улучшить на основе секретных принципов, которые должны быть известны маркетологам. А принципов этих секретных — как собак нерезаных. Число Миллера, цветовое кодирование, использование узнаваемых образов и человеческих лиц, побудительные сигналы, и так далее.

Но всё можно сделать гораздо проще. Представьте, что вы делаете презентацию для семилетних детей. Такие же слайды можно с успехом показывать и взрослым.

То же самое касается и формата выступления. Первая часть нашей детской программы официально называлось «мини-лекция о безопасности». Однако нормальным детям (как и нормальным взрослым) очень скучно слушать длинные монологи без возможности задать вопросы или высказать собственные мнения. Нет, лучшая учёба — это диалог.

В нашем случае дети рассказали нам даже больше забавных историй, чем мы им. «Вы знаете, зачем нужны пароли?» — спрашиваю я. «Да! У моей мамы пароль 1985!» — тут же отвечает девчонка лет шести в первом ряду. Все смеются. «Нельзя делать пароль из даты рождения!» — строго замечает другая егоза того же возраста.

Для ведущего в такой игре главное — следить за общим движением мысли, чтобы не уйти далеко в сторону. Это не всегда легко. Во время разговора про вирусы один карапуз с серьёзным лицом вдруг спрашивает: «А когда мы будем говорить о музыке?!»

Вот так поворот! Рассказать ему про earworms? Посоветовать читать на ночь "Музыкофилию" Оливера Сакса? Нет, пожалуй, прибережём эту тему для старшей группы. Перед мероприятием мы провели опрос сотрудников, подсчитали детей разных возрастов и решили устроить две Профориентации: первая для младших (6-10), вторая для старших (10-15). Этот репортаж – про младшую группу.

Впрочем, малый возраст для них совсем не помеха: по всем вопросам безопасности у них нашлись собственные соображения. На классической ёлке такая игра называлась бы «расскажи стишок Деду Морозу». Но у нас было интересней: по сути, дети обучали друг друга на реальных историях. Горизонтальное обучение часто работает лучше вертикального.

Правда, тут есть своя хитрость управления процессом. Со времён учёбы на матмехе я уверен, что любую сложную концепцию можно объяснить «на пальцах» — только нужно найти подходящее представление. Если вы спросите ребёнка, что он думает об «открытых протоколах связи», он едва ли поддержит разговор. Другое дело, если предложить аналогию: ты хочешь передать записку другу-однокласснику, но не хочешь, чтобы её прочитали или подменили другие. Как поступить? Вот с таким представлением задачи они тут же изобретают и шифрование, и «чёрные/белые списки», и прочие технологии защиты информации. И потихоньку работа родителей становится понятней.

Но хватит лекций, хочется уже подвигаться! Остальные новогодние традиции тоже переводим в практическое русло. Вместо хоровода — круговая экскурсия по всем отделениям компании, от взломанных банкоматов до кабинета генерального директора. Роль фейерверков играют большие экраны оперативного центра безопасности (SOC) – здесь отслеживают кибер-атаки, пыщ-пыщ! Некоторые сотрудники SOCа не знали, что придут дети. Им кажется, что это атака.

Но вообще-то экскурсия не означает «хаотичное передвижение». Компания большая, при желании тут можно надолго потеряться. Поэтому мы заранее, до мероприятия, сели и выбрали на карте «десять остановок» — наиболее интересных мест, которые можно показать детям. И предупредили всех рассылкой по компании, что может случиться некоторая беготня.

Что ещё бывает на ёлках? Состязания? Вот как раз проходим мимо спортзала. Всем внезапно очень хочется подтянуться.

После физкультуры главным хитом становится автомат с газированной водой, работающий на советских трёхкопеечных монетах. Почти как игра «Ёлочка, зажгись!», только даёт газировку.

Теперь бы ещё перекусить, но…. Ёлочные традиции на этом не кончаются. Рассказываем голодным детям классическую историю о похищении подарков и угощений. Только в роли похитителей — злобные хакеры. Однако они очень торопились, и убегая, потеряли парочку ноутбуков. Это наша единственная зацепка.

Так стартует настоящий хакерский квест на две детские команды. Сначала они брутфорсят запароленные ноуты… и очень быстро проходят этот этап. Мы конечно не жестили, но было удивительно слышать от первоклашек возгласы «попробуй admin!» или «давайте такой же пароль, как имя пользователя!», не говоря уже про банальные 1111 или 12345. В общем, за десять минут ноутбуки взломаны.

Открывается карта лабиринта. На самом деле, это карта нашего офиса – и сделали её гораздо раньше, для решения более серьёзных рабочих задач (см. подробности в посте «Создание интерактивной карты офиса»). Оказалось, что это же инструмент очень удобен для детских квестов. Так бывает: делаешь серьёзное, а в итоге случайно получается полезное.

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

В найденной секретной комнате проходит третий этап квеста – надо расшифровать закодированный телефонный номер. Здесь голодные дети тоже проявляют неожиданную смекалку: начинают расшифровку буквенной последовательности с двух сторон. В итоге проходят и этот этап раньше запланированного времени. Дозваниваются до хакера-похитителя. Он сдаётся и говорит, где спрятаны угощения.

Пир на весь мир и подарки – лучший способ закрепить полученные знания («Пап, теперь я точно хочу быть белой шляпой!») Параллельно проходит конкурс рисования на стенах. Первым показал пример наш гендиректор в своём кабинете – но в офисе есть ещё много стен для рисования, и одна как раз напротив кухни. Дети рисуют хакеров, вирусы и себя, пишут пожелания.

Напоследок можно ещё самостоятельно изучить рабочие места родителей («Пап, а зачем у тебя тут моя кукла?»)

Сейчас мы готовимся провести вторую Профориентацию, для старшей группы. Уже ясно, что квест для них надо делать сложнее. Что-то в духе нашей «Конкурентной разведки» на PHDays.

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

Впрочем, такой веселухи и на взрослых мероприятиях хватает. Пару недель назад я с опозданием зашёл на один доклад, автор которого отлично рекламировал DDoS-атаки. «100 MBit/s всего за 79 долларов!», говорил он. Потом оказалось, что это был представитель правоохранительных органов, который рассказывал о борьбе с DDoS. Будь я школьником, я бы сразу спросил у него, где взять такой забористый ботнет. Чисто для образовательных целей, конечно. А то ведь уже ЕГЭ на носу

Автор: Лёха Андреев, технический маркетинг Positive Technologies

This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.

Навыки гейм-дизайнера

Hola chicos!

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

Обрел же я его, проработав 5 лет в московском филиале компании Glu Mobile (4.5 лет из них — дизайнером) и 7 месяцев в компании Social Point, где и продолжаю работать на данный момент. Также я общался со многими людьми, которые работали в таких компаниях, как Game Insight, Crazy Panda, Game Garden и другие.

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

Одно из самых главных умений для начинающего дизайнера — фильтровать информацию, которую человек получает из различных источников: будь то коллеги, статьи в интернете или книги. Даже если материал является достойным, относиться к нему стоит с долей скептицизма, пытаться анализировать и спрашивать “почему?” каждую минуту.


Первым и главным источником знаний должны стать игры. Если вы хотите стать гейм-дизайнером, они должны стать неотъемлемой частью жизни. При этом, если вы планируете создавать мобильные игры, то играть нужно именно в мобильные игры. В этом состоит одна из интересных особенностей разработки — люди, работающие над ААА проектами совсем ничего не смыслят в free 2 play, то же самое действительно и наоборот.

Для начала необходимо обзавестись iOS девайсом не старше двух лет. Как найти игры, в которые стоит поиграть? Top Grossing USA AppStore — это то, что вам нужно (за исключением одинаковых казино и паззл игр). Также проверяйте, какие игры Apple выбирает лучшими в USA каждую неделю (это происходит ночью четверга на пятницу). Не факт что все игры будут хорошими, а скорее только некоторые из них. Но если выходит хит, то он определенно будет там.

Вторым источником будет чтение статей и профильных сайтов. Сложно найти действительно качественные материалы, лучшими источниками будут deconstructoroffun и gamasutra. Также в целом про мобильные игры можно почитать на toucharcade, pocketgamer и slidetoplay. С точки зрения бизнеса, разработку игр обозревают на gamesindustry.

Другой вариант — читать книжки про дизайн. Это достаточно интересное занятие, если вы обладаете нужным уровнем английского и заинтересованы в объекте повествования. Однако, применить полученные знания на практике будет очень сложно и вряд ли вы когда-нибудь сможете использовать довод из книги в качестве аргумента во время спора. Т.е. если вам интересна тематика и вы располагаете достаточным количеством времени, то вот здесь есть достаточно качественная подборка.

Последним способом получения знаний являются курсы по гейм-дизайну, которые стоят огромных денег и, по-моему, не подтверждают своей эффективности. К сожалению, в России возможности для начинающих дизайнеров куда скромнее, нежели зарубежом. В Европе и Америке в каждой крупной компании предоставляются интернатуры для дизайнеров, а также существует множество эвентов, где люди собираются и делают игры друг с другом, так называемые game jam. Это интересная практика взаимодействия с людьми, однако и тут практические навыки для free 2 play разработки обрести будет достаточно сложно.


Представим, что есть 2 человека, которые проработали 5 лет гейм-дизайнерами. Это огромный срок и, правильно подойдя к вопросу, можно научиться всему необходимому. Один из них все это время работал в Gameloft NY, вырос с интерна до ведущего гейм-дизайнера, а впоследствии устроился к нам в компанию и не смотря на свой молодой возраст является одним из лучших дизайнеров в студии.

Второй из них работал в Ubisoft, Pretty Simple Games, Black Sheep Studio — в последней он был единственным дизайнером в компании и именно там он проработал 3 года из 5. После длительного опыта работы даже в плохом месте у человека сформировываются определенные умения: балансирования, составления документации и другие. Умения тех, кто учился без какой-либо помощи будут несравнимо ниже, т.к. им не с кого было брать пример. Самый высокий уровень, что они видели — это была их собственная работа… Но главная проблема даже не в этом. Они совсем не понимают как работает рынок, как работает free 2 play и что такое бизнес.

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

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

Даже посмотрев на планируемые изменения в Hearthstone, можно сказать, что данный апдейт делается не только ради упрощения баланса для разработчиков и опыта новых игроков, но также для улучшения монетизации. Разработчики наоборот запрещают использовать старые карты, мотивируя игроков покупать новый контент. В противном случае, Blizzard пришлось бы выпускать все более и более сильные карты в новых аддонах, соразмерно дестабилизируя баланс игры.

Игра вторая — Heroes Charge. В данном случае основной источник получения новых героев — это гача, т.е. коробка, где вы можете выиграть определенных героев с неким шансом. Дизайнер предлагает параллельно продавать этих героев в магазине. Своей идеей он сразу же убивает потенциал монетизации, благодаря объявлению игроку точной стоимости каждого предмета, а также напрямую сокращает стоимость в глазах игрока, ибо контент можно приобрести в любой момент, заплатив эту сумму.

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

Непонимание того, какой должна быть игра, чтобы оказаться успешной и порождает такие нелепые примеры, как Sandstorm: Pirate Wars и Pocket Mortys, если брать из недавно вышедших игр.

Другие факторы, которые могут сформировать неверное восприятие:

  • Непрофессиональные компании, коих в России, к сожалению, очень много
  • Непрофессиональные работники, имеющие влияние
  • Атмосфера “Мы и так знаем как надо делать игры” вокруг вас
  • Неверный анализ рынка, восприятие количества загрузок игры как признак успеха
  • Выбор игр по неверным критериям, на которые вы будете ровнять свой дизайн

Последний фактор зачастую уводит людей на кривую дорожку. Многие дизайнеры считают хорошими играми те, в которые им нравится играть. Это могут быть хардкорные японские РПГ, которые никогда (или по крайней мере в ближайшие годы) не смогут стабильно позитивно выступать на западном рынке. Отсюда вытекает желание дизайнить фичи или даже целую игру для себя, что погубило не один проект.

Помните, что разработка игр — это в первую очередь бизнес. Нельзя оценивать игру, как “интересная” или “не интересная”, есть определенные факторы, определяющие успешность.


В первую очередь нужно понимать что нужно вашей игре. Главными метриками каждой игры являются:
  • Acquisition — то, насколько легко вы можете привлечь новых игроков
  • Retention — то, насколько долго игроки остаются в вашей игре
  • Monetization — то, насколько много и часто игроки совершают покупки

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

С моей точки зрения 50% успешности Retention в игре определяет то, насколько в нее интересно играть. Весело ли в ней и хочется ли возвращаться каждый день? Следующие 25% определяются жанром, т.к. чем сложнее игра, тем сильнее она отпугивает игроков. И последние 25% определяются разнообразными механиками, вокруг которых строится геймплей, мотивирующий игрока возвращаться. Самым банальным примером будут “жизни” в Candy Crush Saga, которые восстанавливаются через определенное время.

Но если игра обладает хорошим Acquisition и Retention, это совсем не гарантирует ей коммерческого успеха. Следующим шагом будет монетизировать ваших пользователей. Как это сделать — в одном абзаце не уместится, существуют десятки различных систем, которые мотивируют или напрямую заставляют игрока заплатить. Примеры, приведенные выше о неудачном дизайнере показывают, как не стоит пытаться изменить вашу игру — это убивает монетизацию.

Внутри каждого крупного элемента есть кучи различных метрик — такие как ARPU (Average Revenue Per User — средняя прибыль за каждого игрока), ARPPU (Average Revenue Per Paying User — средняя прибыль за каждого платящего игрока), Conversion (доля заплативших людей от общего количества людей) и другие.

Каждый раз, смотря на игру — оценивайте ее как продукт: что сделано хорошо, что сделано плохо. Что стоит запомнить и использовать при разработке, а какие ошибки наоборот никогда не повторять? Критический взгляд на вещи, взгляд гейм-дизайнера, а не восторженного игрока или прожженного циника поможет получить всю необходимую информацию.


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

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

Чтобы было, что структурировать, нужно уметь придумывать. Многие говорят, что нужно родиться креативным, но я считаю, что данный навык можно и нужно развивать. Большинство мыслей, которые сформировываются в голове так или иначе базируются на информации, которой вы владеете. Т.е. чем больше вы знаете игр, чем больше вы знаете механик, тем проще будет их комбинировать и придумывать то, чего еще не было. Также поможет и просто практика. Найдите оптимальный способ для пробуждения креативности — кому-то нужно уйти в комнату и подумать в тишине, кому-то поможет брейншторм сессия с другими дизайнерами, кто-то визуализирует свои мысли, набрасывая варианты в тетради.

После того, как вы продумаете и подробно опишете все детали, наступает время балансировки. Главное, с чего начинается каждый баланс — это найти все элементы системы, которые влияют на поведение фичи. Когда элементы становятся ясны, следует подумать над исходными предположениями для системы. Например, какое поведение вы ожидаете от игрока? Сколько раз в день он будет заходить в игру и сколько действий совершит? Относительно этих предположений и следует начинать менять элементы, пока не будет достигнут ожидаемый результат. Всегда должны быть некие “якорные” элементы, которые не понадобится менять в будущем. Например, балансируя требования опыта для игрока — можно закрепить количество получаемого опыта из различных источников и, исходя из этого, балансить требования для следующего уровня. А можно наоборот зафиксировать требования и, исходя из них, балансить источники.

И наконец, последним шагом следует проанализировать фичу. На этапе дизайна, стоит подумать — какие результаты будут приемлемыми. К примеру, дизайн ПвП. Сколько % игроков должны играть каждый день? Сколько игр каждый из них должен играть? Если присутствуют соревновательные элементы, то повлияет ли это на монетизацию? Когда фича станет доступной для пользователей, стоит проверить соответствует ли ожиданиям реальные результаты. И если нет, то в первую очередь стоит задуматься о балансе. Слишком сложно? Слишком легко? Числа должны вам помочь в большинстве случаев. Или по крайней мере привести к определенному решению, которое также может оказаться неверным.

Задавайте свои вопросы и предлагайте темы для следующих материалов!

This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.

Про использование React с элементом canvas

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

Но я бы хотел рассказать про опыт использования React, который позволяет работать с мутабельной абстракцией над "иммутабельным" canvas элементом. Звучит странно, но работает отлично.

Мотивация


Я использую элемент <canvas> очень часто. Я сделал несколько достаточно сложных веб-приложений, в которых canvas — это основной элемент представления данных. Использовать canvas без всяких фреймворков и библиотек может быть действительно сложно в крупных приложениях. Поэтому я начал часто использовать фреймворки. Сейчас я поддерживаю фреймворк Konva (есть обзорная статья http://ift.tt/1KvJnXj).

Konva помогает очень сильно, но хочется большего. Так же я начал использовать React в своих приложениях, и он мне действительно нравится. И я подумал, как же я могу использовать React для рисования графики на canvas?

React + canvas

React + canvas без фреймворков


Получить доступ к контексту canvas из React компонента и что-нибудь нарисовать очень просто:
class CanvasComponent extends React.Component {
    componentDidMount() {
        this.updateCanvas();
    }
    updateCanvas() {
        const ctx = this.refs.canvas.getContext('2d');
        ctx.fillRect(0,0, 100, 100);
    }
    render() {
        return (
            <canvas ref="canvas" width={300} height={300}/>
        );
    }
}
ReactDOM.render(<CanvasComponent/>, document.getElementById('container'));

Демо: http://ift.tt/1PGl0Xf

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

// компонент, который будет использовать многократно
function rect(props) {
    const {ctx, x, y, width, height} = props;
    ctx.fillRect(x, y, width, height);
}
class CanvasComponent extends React.Component {
    componentDidMount() {
        this.updateCanvas();
    }
    componentDidUpdate() {
        this.updateCanvas();
    }
    updateCanvas() {
        const ctx = this.refs.canvas.getContext('2d');
        ctx.clearRect(0,0, 300, 300);
        // отобразить "дочерние" компоненты
        rect({ctx, x: 10, y: 10, width: 50, height: 50});
        rect({ctx, x: 110, y: 110, width: 50, height: 50});
    }
    render() {
         return (
             <canvas ref="canvas" width={300} height={300}/>
         );
    }
}
ReactDOM.render(<CanvasComponent/>, document.getElementById('container'));

А что на счет достаточно удобных реактовских методов (такие как "shouldComponentUpdate", "componentDidMount" и т.д.)?
А как же подход "полное описание представления в функции render"?

Реализация


Мне действительно нравится реактовский подход к построению приложения. Поэтому я сделал плагин react-konva, который рисует на canvas через фреймворк Konva.
npm install react konva react-konva --save

Далее
import React from 'react';
import ReactDOM from 'react-dom';
import {Layer, Rect, Stage, Group} from ‘react-konva’;

class MyRect extends React.Component {
    constructor(...args) {
      super(...args);
      this.state = {
        color: 'green'
      };
      this.handleClick = this.handleClick.bind(this);
    }
    handleClick() {
      this.setState({
        color: Konva.Util.getRandomColor()
      });
    }
    render() {
        return (
            <Rect
                x={10} y={10} width={50} height={50}
                fill={this.state.color}
                shadowBlur={10}
                onClick={this.handleClick}
            />
        );
    }
}
function App() {
    return (
      <Stage width={700} height={700}>
        <Layer>
            <MyRect/>
        </Layer>
      </Stage>
    );
}
ReactDOM.render(<App/>, document.getElementById('container'));

Демо: http://ift.tt/1PGl3lW

Сравнения


react-konva vs react-canvas


react-canvas это совершенно другой плагин. Он позволяет рисовать "стандартные DOM объекты" (картинки, текст) с очень большой производительностью. Но этот плагин НЕ позволяет рисовать графику. В то время как react-konva сделан именно для рисования сложной графики на canvas с помощью React.

react-konva vs react-art


react-art так же позволяет рисовать графику и имеет поддержку SVG, но он не имеет поддержки событий для графических фигур.

react-konva vs vanilla canvas


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

Но я сделал этот плагин НЕ для решения проблем с производительностью. Прямое использование canvas должно быть наиболее производительным путём, так, используя react-konva, мы имеем Konva, которая работает поверх canvas, и React, который работает поверх Konva.

Но я сделал плагин для борьбы со сложностью приложения. Konva помогает очень сильно (особенно когда нужны события для объектов на холсте, например "click", без фреймворка это сделать очень тяжело). Но React помогает еще сильнее, так как он предоставляет очень хорошую структуру кода и движения данных.

Особенности


konva logo

react-konva имеет поддержку большого количества основных фигур: Circle, Rect, Ellipse, Line, Sprite, Image, Text, TextPath, Star, Ring, Arc, Label, SVG Path, RegularPolygon, Arrow и вы можете создать свои собственные фигуры. Так же есть встроенная поддержка drag&drop, анимации, фильтры, система кэширования, десктоп и мобильные события (mouseenter, click, dblclick, dragstart, dragmove, dragend, tap, dbltap, и так далее).

Пример нестандартной фигуры

function MyShape() {
  return (
     <Shape fill=”#00D2FF” draggable
         sceneFunc={function (ctx) {
             ctx.beginPath();
             ctx.moveTo(20, 50);
             ctx.lineTo(220, 80);
             ctx.quadraticCurveTo(150, 100, 260, 170);
             ctx.closePath();
             // Konva specific method
             ctx.fillStrokeShape(this);
         }}
     />
  );
}

Демо: http://ift.tt/1PGl3lY

Пример работы с событиями

class MyCircle extends React.Component {
    constructor(…args) {
        super(…args);
        this.state = { isMouseInside: false};
        this.handleMouseEnter = this.handleMouseEnter.bind(this);
        this.handleMouseLeave = this.handleMouseLeave.bind(this);
    }
    handleMouseEnter() {
        this.setState({ isMouseInside: true});
    }
    handleMouseLeave() {
        this.setState({ isMouseInside: false});
    }
    render() {
        return (
            <Circle
                x={100} y={60} radius={50}
                fill=”yellow” stroke=”black”
                strokeWidth={this.state.isMouseInside ? 5 : 1}
                onMouseEnter={this.handleMouseEnter}
                onMouseLeave={this.handleMouseLeave}
            />
        );
    }
}

Демо: http://ift.tt/20NjYfJ

Ссылки


Github: http://ift.tt/1PzVmDv
Konva framework: http://ift.tt/16w0xlk

This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.

пятница, 5 февраля 2016 г.

STM32: SPI: LCD — Вы всё делаете не так [восклицательный знак]

Надеюсь сообщество простит меня за такой заголовок, просто в последнее время все чаще и чаще сталкиваюсь с программами в которых к микроконтроллерам STM32 подключают различные дисплеи с интерфейсом SPI и очень часто передачу данных при этом делают не правильно.
Как следствие — либо код не работает совсем и тогда в него внедряют различные задержки, или пишут код таким образом что он гарантированно будет работать медленно (по сравнению с возможной скоростью). А кто то, не разобравшись просто копирует чужой «с костылями» код, и потом такие «произведения» ходят по интернету из примера в пример…

Откуда растут такие проблемы и каким образом они решаются под катом.
Для начала я расскажу как работает интерфейс SPI при передаче данных в режиме MASTER.

В Reference manual на стр. 868 есть наглядная схема устройства интерфейса:

если вы уже пробовали передавать данные при помощи SPI, то эту диаграмму вы уже знаете наизусть, ведь так?

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

ну что тут может быть проще?
при отправке данных мы записываем их в "Буфер передатчика", из которых они попадают в сдвиговый регистр и по очереди «выдаются» на линию MOSI (на схеме выделено красным цветом). При передаче генерируются флаги BSY и TXE по которым можно узнать состояние передачи.

Как у AVR ?
НЕТ !!

на схеме нарисовано абсолютно точно: при отправке используются два регистра:
— регистр "Буфера передатчика" (в программе SPI_DR),
регистр сдвига (shift register)

и это два регистра, КАЖДЫЙ из которых может иметь свое значение!!!
Для КАЖДОГО из этих регистров предусмотрен свой флаг — который показывает их заполненность.

  • Для регистра SPI_DR — это флаг TXE
  • Для регистра сдвига — это флаг BSY

Для того чтобы понять как вся эта связка двух регистров и двух флагов работает (в Reference manual я не нашел прямого ответа на этот вопрос, хотя если внимательно читать — то там это описывается в логике работы) — разберемся как происходит передача:
  1. для отправки значения (8-ми либо 16-ти битного) мы записываем его в SPI_DR, одновременно происходит установка флага TXE = 0 — что показывает что SPI_DR содержит значение для отправки
  2. поскольку это первое отправляемое значение (до операции флаг BSY = 0), то значение записывается одновременно и в SPI_DR и в регистр сдвига (Shift_Reg), с которого первый бит (в зависимости от настроек MSB/LSB) выставляется по линии MOSI
  3. В следующем SCK такте, после записи значения в SPI_DR и в сдвиговый регистр (Shift_Reg), устанавливается флаг TXE = 1 — что означает что в регистр SPI_DR можно записать следующее значение для отправки. Обращаю внимание, прежнее значение содержится в Shift_Reg и еще выгружается на линию MOSI! (см. схему ниже! в момент установки флага TXE = 1 происходит отправка лишь второго бита первоначального значения (из 8 или 16 бит значения)
  4. поскольку в SPI_DR больше данные не записываем — то с флагом TXE =1 ничего и не происходит, интерфейс ждет загрузки следующего байта..
  5. данные из Shift_Reg выгружаются по такту SCK на линию MOSI. При передаче последнего бита данных, проверяется есть ли новое значение в SPI_DR для отправки (в этом случае флаг TXE = 0), если нет (это флаг TXE = 1), то устанавливается флаг BSY = 0 и передача прекращается.

Схематично передача одного байта по SPI будет выглядеть так:

Согласитесь это не сложно!!!

Зачем так сделано? — для того чтобы обеспечить непрерывность передаваемых данных!

В тех же AVR, при помощи SPI, невозможно передать несколько байт данных без перерыва, всегда между передаваемыми значениями будет пауза в 1 такт SPI. А вот в STM32 возможна по настоящему непрерывная передача, которая будет выглядеть вот так:

Как видно из схемы — для обеспечения непрерывности передачи достаточно всего лишь ожидать установления флага TXE = 1 и записывать в SPI_DR следующее значение для передачи.

Теперь о подключении дисплеев к STM32 по SPI.

1. 90% приведенных в интернете решений (не правильных решений) предлагают делать отправку данных на дисплей одним из следующих вариантов кода:

  • Первый вариант, проверка флага TXE после загрузки данных в SPI_DR:
    void SPISend(uint16_t data) {
            SPI_I2S_SendData(SPI1, data);  // отправили данные
            while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET); // ждём, пока данные не отправятся
    }
    
    
  • Второй, на первый взгляд более правильный, вариант, проверка флага BSY после отправки данных в SPI_DR:
    void SPISend(uint16_t data) {
            SPI_I2S_SendData(SPI1, data); //Передаем байт data через SPI1
            while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY) == SET) //Передатчик занят?
    }
    
    

Оба варианта не правильны ! Кстати на просторах интернета еще предлагают третий вариант — проверять флаг RXE после отправки данных в SPI_DR, который обычно используется при получении каждого байта (слова) данных — это оставляю без комментариев…

Посмотрите внимательно на схемы которые я приводил выше! Флаг TXE нужно проверять перед отправкой данных в SPI_DR… Дальше будет работать конвейер самого SPIMOSI уйдет первый байт из Shift_Reg, потом Shift_Reg прогрузится значением из SPI_DR, и опять произойдет отправка в MOSI)
То есть код отправки данных на дисплей должен выглядеть следующим образом (пример для 16-ти битных посылок):

void SPISend(uint16_t data) {
        while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET); // ждём, освобождения SPI_DR
        SPI_I2S_SendData(SPI1, data);  // отправим байт данных в очередь на отправку
}


Только в этом случае можно получить самую большую скорость обмена по SPI!

Почему же самый очевидный способ не используют?

Дело в том что многие дисплеи имеют наряду со стандартными SPI выводами (SCK, MOSI, MISO, CS) и такой вывод как DC (D/C, A0, CMD и так далее)
Вывод DC показывает что же передается в дисплей, обычно при DC=0 дисплей воспринимает переданное как команду, а при DC=1 — как данные.
Соответственно код отправки команды и данных после нее обычно пишут таким образом

// процедура отправки, правильный вариант который все равно не будет работать
void SPISend(uint16_t data) {
        while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET); // ждём, освобождения SPI_DR
        SPI_I2S_SendData(SPI1, data);  // отправим байт данных в очередь на отправку
}

// сама процедура отправки команды и потом данных
void SPISendCommand(uint16_t data, data1, data2) {
        SetCS0; // выбор дисплея для операции

[1]        SetDC0;  // установили режим передачи команд

[2]     SPISend(commandCode);  //  отправим команду

[3]        SetDC1; // установили режим передачи данных
        SPISend(data);  //  отправим команду
        SPISend(data1);  //  отправим команду
        SPISend(data2);  //  отправим команду

[4]        SetCS1; // отмена выбора дисплея
}


Примечание к коду:
SetCSx — выбирает/отменяет выбор дисплея (здесь не привожу)
SetDCx — установка режима передачи команд/данных для дисплея

И этот код не работает !!!

Почему?

В точке [1] мы указываем дисплею что собираемся передавать команды, затем передаем код команды [2], но согласно нашей процедуры отправки и схем работы SPI которые я приводил выше — мы вернемся из подпрограммы отправки байта данных к шагу [3] к моменту отправки всего 2-3 бита команды (!!) — причем чем медленнее интерфейс SPI (ниже частота SCK) — тем меньше бит мы успеем передать!
И в этот момент мы указываем дисплею, что дальше идут данные [3] — у ЛЮБОГО дисплея смена состояния пина DC во время передачи команды/данных вызывает сбой!!!
Потом отправляем три байта данных (хотя команда уже не прошла), и в итоге отменяя выбор дисплея [4] мы окончательно «сносим голову» дисплею!!! ведь у нас согласно схемы работы SPI при отправке нескольких байт — скорее всего при выполнении команды SetCS1 будет передаваться только data1 (он будет в регистре сдвига), а data2 будет еще ждать своей очереди в SPI_DR

Как большинство выходит из этой ситуации? — используют процедуру отправки с проверкой флага BSY после записи в SPI_DR (второй вариант решений который я приводил выше)… или вообще используют искусственные задержки!!! (например, командами delay !)

Вот реальный пример
void LCD_set_XY(unsigned char X, unsigned char Y) {
   unsigned char x;
   x = 6 * X;

   DCOff();
   SPISend(0x80 | x);
   SPISend(0x40 | Y);
   Delay(1); // Задержка, чтобы успела примениться последняя команда
   DCOn(); // Принуждает выполнить последнюю команду
}


Другая крайность, это использование флага BSY везде! код работать будет, но вот о максимальной скорости передачи данных придется забыть, потери составят около 10-20% практически независимо от частоты SCK (!!), так как код будет постоянно ожидать установку BSY=0 и только потом будет готовиться к следующей передаче (готовить следующий байт), и если это приемлемо и правильно при отправке команды (как правило один байт), то при отправке например буфера экрана, например для PCD8544 (Nokia 5110) — будет работать заметно медленнее!!!

// отправка данных\команд на дисплей
void lcd8544_senddata(unsigned char data) {
        SPI_I2S_SendData(SPI2, data);
        while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY) == SET) //Передатчик занят?
}

// очистка дисплея
void lcd8544_refresh(void) {

        LCD_DC0;                         // режим передачи команд

        lcd8544_senddata(0x40); // установка курсора в позицию Y=0; X=0
        lcd8544_senddata(0x80);

        LCD_DC1;                         // режим передачи данных

        unsigned char y, x;

        for (y=0;y<6;y++) for (x=0;x<84;x++) lcd8544_senddata(lcd8544_buff[y*84+x]); // отправка буфера
}

хотя, как вы уже наверное догадались — выход лежит на поверхности — при необходимости смены вида передаваемых данных (по линии DC), или отмены выбора дисплея (линией CS) предварительно нужно проверять флаг BSY для того чтобы убедиться что физическая передача данных/команды завершилась. В остальных случаях нужно использовать проверку флага TXE ПЕРЕД загрузкой значения в SPI_DR:

// передача данных на дисплей
void SPI2_SendByte(uint8_t sendData)
{
  while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET);
  SPI_I2S_SendData(SPI2, sendData);
}

// определение области для вывода
void ili9341c_SetWindow(uint16_t ystart, uint16_t xstart, uint16_t yend, uint16_t xend)
{
. . .
  GPIO_ResetBits(GPIOB, GPIO_Pin_12);   // DC=0;

  SPI2_SendByte(LCD_PAGE_ADDR);  

  while (SPI_I2S_GetFlagStatus(SPI2,SPI_FLAG_BSY)!=RESET);  //ждем пока команда уйдет на дисплей (а не перейдет в shift reg)

  GPIO_SetBits(GPIOB, GPIO_Pin_12);     // DC=1;

  SPI2_SendByte(xstart>>8);      // данные 
  SPI2_SendByte(xstart&0xFF);  //      передаются
  SPI2_SendByte(xend>>8);       //                  в непрерывном 
  SPI2_SendByte(xend&0xFF);  //                             режиме без пауз !

  while (SPI_I2S_GetFlagStatus(SPI2,SPI_FLAG_BSY)!=RESET); // ждем пока данные передадутся до конца 

. . .
}

этот код будет работать и максимально быстро и самое главное правильно!!!

некоторые статьи, где реализованы неправильные алгоритмы отправки, либо тема «правильной» отправки так и не раскрыта.

p.s. Некоторое время назад я сам разбирался с этим интерфейсом, и был, в отличие от многих, удивлен его продуманностью и функциональностью, надеюсь теперь и для Вас интерфейс SPI у STM32 это не черный ящик с непонятно когда используемыми флагами, а четкий, понятный и продуманный автомат для максимально быстрой отправки/получения данных!

если что не так — пишите в комментарии к статье, в личку, или на емайл gorbuкov @ тот_кто_знает_все. ru

This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.

[Перевод] Знакомство с Apache Spark

Здравствуйте, уважаемые читатели!

Мы наконец-то приступаем к переводу серьезной книги о фреймворке Spark:

Сегодня мы предлагаем вашему вниманию перевод обзорной статьи о возможностях Spark, которую, полагаем, можно с полным правом назвать слегка потрясающей.

Я впервые услышал о Spark в конце 2013 года, когда заинтересовался Scala – именно на этом языке написан Spark. Несколько позже я принялся ради интереса разрабатывать проект из области Data Science, посвященный прогнозированию выживаемости пассажиров «Титаника». Оказалось, это отличный способ познакомиться с программированием на Spark и его концепциями. Настоятельно рекомендую познакомиться с ним всем начинающим Spark-разработчикам.

Сегодня Spark применяется во многих крупнейших компаниях, таких, как Amazon, eBay и Yahoo! Многие организации эксплуатируют Spark в кластерах, включающих тысячи узлов. Согласно FAQ по Spark, в крупнейшем из таких кластеров насчитывается более 8000 узлов. Действительно, Spark – такая технология, которую стоит взять на заметку и изучить.

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

Что такое Apache Spark? Введение

Spark – это проект Apache, который позиционируется как инструмент для «молниеносных кластерных вычислений». Проект разрабатывается процветающим свободным сообществом, в настоящий момент является наиболее активным из проектов Apache.

Spark предоставляет быструю и универсальную платформу для обработки данных. По сравнению с Hadoop Spark ускоряет работу программ в памяти более чем в 100 раз, а на диске – более чем в 10 раз.

Кроме того, код на Spark пишется быстрее, поскольку здесь в вашем распоряжении будет более 80 высокоуровневых операторов. Чтобы оценить это, давайте рассмотрим аналог “Hello World!” из мира BigData: пример с подсчетом слов (Word Count). Программа, написанная на Java для MapReduce, содержала бы около 50 строк кода, а на Spark (Scala) нам потребуется всего лишь:

sparkContext.textFile("hdfs://...")
            .flatMap(line => line.split(" "))
            .map(word => (word, 1)).reduceByKey(_ + _)
            .saveAsTextFile("hdfs://...")

При изучении Apache Spark стоит отметить еще один немаловажный аспект: здесь предоставляется готовая интерактивная оболочка (REPL). При помощи REPL можно протестировать результат выполнения каждой строки кода без необходимости сначала программировать и выполнять все задание целиком. Поэтому написать готовый код удается гораздо быстрее, кроме того, обеспечивается ситуативный анализ данных.

Кроме того, Spark имеет следующие ключевые черты:

  • В настоящее время предоставляет API для Scala, Java и Python, также готовится поддержка других языков (например, R)
  • Хорошо интегрируется с экосистемой Hadoop и источниками данных (HDFS, Amazon S3, Hive, HBase, Cassandra, etc.)
  • Может работать на кластерах под управлением Hadoop YARN или Apache Mesos, а также работать в автономном режиме

Ядро Spark дополняется набором мощных высокоуровневых библиотек, которые бесшовно стыкуются с ним в рамках того же приложения. В настоящее время к таким библиотекам относятся SparkSQL, Spark Streaming, MLlib (для машинного обучения) и GraphX – все они будут подробно рассмотрены в этой статье. Сейчас также разрабатываются другие библиотеки и расширения Spark.

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

  • управление памятью и восстановление после отказов
  • планирование, распределение и отслеживание заданий кластере
  • взаимодействие с системами хранения данных

В Spark вводится концепция RDD (устойчивый распределенный набор данных) – неизменяемая отказоустойчивая распределенная коллекция объектов, которые можно обрабатывать параллельно. В RDD могут содержаться объекты любых типов; RDD создается путем загрузки внешнего набора данных или распределения коллекции из основной программы (driver program). В RDD поддерживаются операции двух типов:

  • Трансформации – это операции (например, отображение, фильтрация, объединение и т.д.), совершаемые над RDD; результатом трансформации становится новый RDD, содержащий ее результат.
  • Действия – это операции (например, редукция, подсчет и т.д.), возвращающие значение, получаемое в результате некоторых вычислений в RDD.

Трансформации в Spark осуществляются в «ленивом» режиме — то есть, результат не вычисляется сразу после трансформации. Вместо этого они просто «запоминают» операцию, которую следует произвести, и набор данных (напр., файл), над которым нужно совершить операцию. Вычисление трансформаций происходит только тогда, когда вызывается действие, и его результат возвращается основной программе. Благодаря такому дизайну повышается эффективность Spark. Например, если большой файл был преобразован различными способами и передан первому действию, то Spark обработает и вернет результат лишь для первой строки, а не станет прорабатывать таким образом весь файл.

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

SparkSQL

SparkSQL – это компонент Spark, поддерживающий запрашивание данных либо при помощи SQL, либо посредством Hive Query Language. Библиотека возникла как порт Apache Hive для работы поверх Spark (вместо MapReduce), а сейчас уже интегрирована со стеком Spark. Она не только обеспечивает поддержку различных источников данных, но и позволяет переплетать SQL-запросы с трансформациями кода; получается очень мощный инструмент. Ниже приведен пример Hive-совместимого запроса:


// sc – это существующий SparkContext.
val sqlContext = new org.apache.spark.sql.hive.HiveContext(sc)

sqlContext.sql("CREATE TABLE IF NOT EXISTS src (key INT, value STRING)")
sqlContext.sql("LOAD DATA LOCAL INPATH 'examples/src/main/resources/kv1.txt' INTO TABLE src")

// Запросы формулируются на HiveQL
sqlContext.sql("FROM src SELECT key, value").collect().foreach(println)

Spark Streaming

Spark Streaming поддерживает обработку потоковых данных в реальном времени; такими данными могут быть файлы логов рабочего веб-сервера (напр. Apache Flume и HDFS/S3), информация из соцсетей, например, Twitter, а также различные очереди сообщений вроде Kafka. «Под капотом» Spark Streaming получает входные потоки данных и разбивает данные на пакеты. Далее они обрабатываются движком Spark, после чего генерируется конечный поток данных (также в пакетной форме) как показано ниже.

API Spark Streaming точно соответствует API Spark Core, поэтому программисты без труда могут одновременно работать и с пакетными, и с потоковыми данными.

MLlib

MLlib – это библиотека для машинного обучения, предоставляющая различные алгоритмы, разработанные для горизонтального масштабирования на кластере в целях классификации, регрессии, кластеризации, совместной фильтрации и т.д. Некоторые из этих алгоритмов работают и с потоковыми данными — например, линейная регрессия с использованием обычного метода наименьших квадратов или кластеризация по методу k-средних (список вскоре расширится). Apache Mahout (библиотека машинного обучения для Hadoop) уже ушла от MapReduce, теперь ее разработка ведется совместно с Spark MLlib.

GraphX

GraphX – это библиотека для манипуляций над графами и выполнения с ними параллельных операций. Библиотека предоставляет универсальный инструмент для ETL, исследовательского анализа и итерационных вычислений на основе графов. Кроме встроенных операций для манипуляций над графами здесь также предоставляется библиотека обычных алгоритмов для работы с графами, например, PageRank.

Как использовать Apache Spark: пример с обнаружением событий

Теперь, когда мы разобрались, что такое Apache Spark, давайте подумаем, какие задачи и проблемы будут решаться с его помощью наиболее эффективно.

Недавно мне попалась статья об эксперименте по регистрации землетрясений путем анализа потока Twitter. Кстати, в статье было продемонстрировано, что этот метод позволяет узнать о землетрясении более оперативно, чем по сводкам Японского Метеорологического Агентства. Хотя технология, описанная в статье, и не похожа на Spark, этот пример кажется мне интересным именно в контексте Spark: он показывает, как можно работать с упрощенными фрагментами кода и без кода-клея.

Во-первых, потребуется отфильтровать те твиты, которые кажутся нам релевантными – например, с упоминанием «землетрясения» или «толчков». Это можно легко сделать при помощи Spark Streaming, вот так:


TwitterUtils.createStream(...)
            .filter(_.getText.contains("earthquake") || _.getText.contains("shaking"))

Затем нам потребуется произвести определенный семантический анализ твитов, чтобы определить, актуальны ли те толчки, о которых в них говорится. Вероятно, такие твиты, как «Землетрясение!» или «Сейчас трясет» будут считаться положительными результатами, а «Я на сейсмологической конференции» или «Вчера ужасно трясло» — отрицательными. Авторы статьи использовали для этой цели метод опорных векторов (SVM). Мы поступим также, только реализуем еще и потоковую версию. Полученный в результате образец кода из MLlib выглядел бы примерно так:

// Готовим данные о твитах, касающихся землетрясения, и загружаем их в формате LIBSVM
val data = MLUtils.loadLibSVMFile(sc, "sample_earthquate_tweets.txt")

// Разбиваем данные на тренировочные (60%) и тестовые (40%).
val splits = data.randomSplit(Array(0.6, 0.4), seed = 11L)
val training = splits(0).cache()
val test = splits(1)

// Запускаем тренировочный алгоритм, чтобы построить модель
val numIterations = 100
val model = SVMWithSGD.train(training, numIterations)

// Очищаем пороговое значение, заданное по умолчанию
model.clearThreshold()

// Вычисляем приблизительные показатели по тестовому множеству 
val scoreAndLabels = test.map { point =>
  val score = model.predict(point.features)
  (score, point.label)
}

// Получаем параметры вычислений
val metrics = new BinaryClassificationMetrics(scoreAndLabels)
val auROC = metrics.areaUnderROC()

println("Area under ROC = " + auROC)

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

// sc – это имеющийся SparkContext.
val sqlContext = new org.apache.spark.sql.hive.HiveContext(sc)
// sendEmail – это пользовательскаяфункция
sqlContext.sql("FROM earthquake_warning_users SELECT firstName, lastName, city, email")
          .collect().foreach(sendEmail)

Другие варианты использования Apache Spark

Потенциально сфера применения Spark, разумеется, далеко не ограничивается сейсмологией.
Вот ориентировочная (то есть, ни в коем случае не исчерпывающая) подборка других практических ситуаций, где требуется скоростная, разноплановая и объемная обработка больших данных, для которой столь хорошо подходит Spark:

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

В электронной коммерции информация о транзакциях, поступающая в реальном времени, может передаваться в потоковый алгоритм кластеризации, например, по k-средним или подвергаться совместной фильтрации, как в случае ALS. Затем результаты даже можно комбинировать с информацией из других неструктутрированных источников данных — например, с отзывами покупателей или рецензиями. Постепенно эту информацию можно применять для совершенствования рекомендаций с учетом новых тенденций.

В финансовой сфере или при обеспечении безопасности стек Spark может применяться для обнаружения мошенничества или вторжений, либо для аутентификации с учетом анализа рисков. Таким образом можно получать первоклассные результаты, собирая огромные объемы архивированных логов, комбинируя их с внешними источниками данных, например, с информацией об утечках данных или о взломанных аккаунтах (см., например, http://ift.tt/1l33Xi1), а также использовать информацию о соединениях/запросах, ориентируясь, например, на геолокацию по IP или на данные о времени

Заключение

Итак, Spark помогает упростить нетривиальные задачи, связанные с большой вычислительной нагрузкой, обработкой больших объемов данных (как в реальном времени, так и архивированных), как структурированных, так и неструктурированных. Spark обеспечивает бесшовную интеграцию сложных возможностей – например, машинного обучения и алгоритмов для работы с графами. Spark несет обработку Big Data в массы. Попробуйте – не пожалеете!

This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.