Что это за приложение?
iTrace — это мобильное приложение для того, чтобы научить детей писать буквы. Электронные прописи на iPad. Сейчас она используется в нескольких странах мира (в основном в США) для обучения детей письму. Придумал и организовал всю работу по проекту Миша Богорад, а мне довелось участвовать в проекте разработчиком всяких внутренностей, главным образом, отрисовкой букв и анализом качества их рисования.
Идея, сложности
Идея iTrace ничем не отличается от обычных прописей. Берём букву, просим ребёнка её нарисовать, подсказывая, если ему трудно. Сначала буква большая и ошибиться можно сильно, потом она уменьшается, и допуск тоже всё меньше и меньше. В конце-концов ребёнок, благодаря привычке, запоминает, как пишется буква.
В статье я расскажу про сложности, с которыми пришлось столкнуться и то, как их удалось решить. Если тема окажется интересной, спрашивайте в комментариях, про техническую часть я могу рассказать подробнее.
Помимо организационных трудностей (нарисовать несколько тысяч картинок для призов и анимаций, найти специалистов по обучению, которые помогли проработать методологию, понять, где найти музыку и озвучку к приложению, и так далее), встретились и технические сложности. Три главных — оптимизация для работы на старых устройствах, отрисовка букв разной толщины и контроль качества ввода букв ребёнком.
Оптимизация
Задача оптимизации появилась из аудитории приложения. Дети часто используют старые Айпады, и, в частности, первый. Это не самое быстрое устройство, с не самым большим количеством памяти. Пришлось придумать, как работать с анимацией, какого она должна быть размера, чтобы работать на старом железе, сколько можно сделать кадров в секунду и так далее.
Также пришлось оптимизировать работу с ресурсами в приложении. Казалось бы, просто положи их в бандл и всё, что там оптимизировать, но, увы. Во-первых, сами ресурсы большие. Потребовалось каждую картинку прогнать через оптимизатор, проанализировать, какой формат минимальный, выбрать лучший. Частично это делает система сборки проекта, но лучше контролировать самому. Во-вторых, установка тестовой сборки (и, как следствие, установка самого приложения на Айпад) занимала огромное количество времени. Оба этих процесса связаны с копированием и распаковкой файлов, и когда количество файлов — несколько тысяч, процесс может занять существенное время. Я не зря сначала вспомнил про первый Айпад. Установка тестовой версии на это устройство занимало порядка 10 минут.
В этот момент я вспомнил про пак-файлы. Это техника, которая давным-давно применяется в играх, где много-много текстур. Все файлы запаковываются в один файл, работу с которым можно организовать очень эффективно (смаппить, например). Можно было бы придумать свой формат, но мне удачно подумалось попробовать zip без сжатия и по тестам, скорость была почти такой же, как и доступ к файлам напрямую.
Единственная проблема, которая немного мешала — zip-файлы лишены случайного доступа к файлам, пришлось строить свою собственную таблицу соответствия именам местоположения файлов, которая в простейшем случае выглядит вот так:
{"Levels":{"":[3149,48427877],"iphone_cursive_word_levels.csv":[3153,48428251],"iphone_cursive_levels.csv":[3152,48428149],"iphone_regular_levels.csv":[3154,48428358],"regular_word_levels.csv":[3157,48428662],"save_before_rollback.zip":[3158,48428762] ...
После чего немного соптимизировать её по памяти (для большого количества файлов она занимает существенное место), по скорости загрузки (она кешируется при старте приложения), но после этого приложение стало устанавливаться за 20–40 секунд.
Код DPLPacker'а можно глянуть тут: https://github.com/bealex/DPLPacker А простейшая работа с паком выглядит так:
Создаем файл
_zipFile = [[DPLZipFile alloc] initWithZipFile:_zipFilePath];
Проверяем, что файл есть в архиве, получаем его (по пути и имени):
NSData *data = nil;
if ([_zipFile fileExistsForPath:filePath]) {
data = [_zipFile dataForPath:filePath];
}
return data;
Нужно не забыть аккуратно разобраться с @2x-картинками, если пакуем их. В отличие от штатных файлов, система не загрузит за нас нужную версию:
- (UIImage *)imageAtPath:(NSString *)filePath {
CGFloat scale = 1;
if (DPL_isRetina()) {
if (![filePath contains:@"@2x"]) {
NSString *filePath2x = [filePath stringByReplacingOccurrencesOfString:@".png"
withString:@"@2x.png"];
if ([self fileExistsAtPath:filePath2x]) {
filePath = filePath2x;
if (DPL_isIPad()) {
scale = 2;
} else {
scale = 1;
}
}
} else {
scale = 2;
}
} else {
if (![filePath contains:@"@2x"] && ![self fileExistsAtPath:filePath]) {
NSString *filePath2x = [filePath stringByReplacingOccurrencesOfString:@".png"
withString:@"@2x.png"];
if ([self fileExistsAtPath:filePath2x]) {
filePath = filePath2x;
scale = 2;
}
} else {
scale = 1;
}
}
NSData *data = [self dataWithContentsOfFile:filePath];
if (fabs(scale - 1) > 0.01) {
if (DPL_OSVersionMajor() >= 6) {
return [UIImage imageWithData:data scale:scale];
} else {
return [UIImage imageWithCGImage:[UIImage imageWithData:data].CGImage
scale:scale
orientation:UIImageOrientationUp];
}
}
return [UIImage imageWithData:data];
}
Насколько кривая эта кривая?
Во всех остальных приложениях, которые обучают детей письму букв, самого обучения не происходит. Ребенку просто показывают, как должна писаться буква, а дальше он может рисовать её, как хочет. Нет никакой обратной связи, никакой возможности проверить правильность, поправить при необходимости. Почему это важно? Потому что есть школа, которая учит определённым образом (в США три базовых варианта написания букв, плюс курсив, варианты для правой/левой руки и мелкие отличия между разными школами).
Если ребёнок научится в приложении писать «как попало», ему придётся переучиваться. Это будет сложно, больно и неприятно.
Поэтому основной фичей, которую хотелось реализовать, должна была стать проверка корректности написания. Если ребёнок повёл линию не туда, нужно сразу ему подсказать, что не туда. Если начал не из начала — подсказать, где оно, начало. Эту задачу и пришлось решать.
Сложность составилась из двух частей. Первая — как сделать обработку быстрой. Она не должна существенно задерживать рисование буквы даже на старом, первом, Айпаде. Вторая — как именно определить, что ребёнок сделал «не то». Вот примеры ошибок, которые iTrace отлавливает:
IWTaskErrorCodeErrorTooBig = 1, // накопилась слишком большая ошибка
IWTaskErrorCodeLineExitedCorridor = 2, // вышли за коридор
IWTaskErrorCodeCornerDrawingDistanceWrong = 3, // слишком срезали или "накрутили" угол
IWTaskErrorCodeStraightLineDrawingDistanceWrong = 4, // далеко отошли от середины
IWTaskErrorCodeCornerDoubleEnter = 5, // вернулись уже в нарисованный угол
IWTaskErrorCodeLineNotCovered = 6, // не около любой точки идеальной кривой нарисовали
IWTaskErrorCodeWrongStart = 7, // если начали не оттуда (надо это как-то проверять — грубо говоря, если первая точка вне первого угла, или если ошибка в первом углу)
IWTaskErrorCodeTooOverextended = 8 // если перевели за конец. Это, видимо, чуть сложнее, но это важно, поскольку это очень частая ошибка. Идея примерно такова — если мы дошли до последнего (финального) угла, а потом обломали пользователя по причине того, что слишком длинная кривая в углу или по причине выхода за границы коридора — сохраняем в истории вот эту ошибку.
Первая часть решилась сравнительно обычными приёмами. Сначала я реализовал все алгоритмы, используя высокоуровневые структуры (классы Objective-C, коллекции оттуда же), но, увидев в профайлере, что слишком много времени тратится на работу с ними (даже на боксинг/анбоксинг чисел из NSNumber), перешёл на обычные С-структуры. После чего ввёл несколько кешей, чтобы пересчитывать только конец нарисованной линии, а не её всю. Это позволило убрать тормоза при рисовании длинных линий, и добиться нужной производительности.
Главная задача определения «что ребёнок сделал не то» состояла в том, чтобы определить, что такое «не то». Какие бывают ошибки? Мы выделили несколько:
- начало рисования не из правильной точки,
- незавершение линии,
- слишком далеко отошли вбок от идеальной линии,
- пошли «не в ту сторону». Эта ошибка отличается от предыдущей, так как мы можем пойти обратно по идеальной же линии,
- срезали угол,
После чего попробовали придумать, как формализовать все эти ошибки. Понятно, что нужно сравнивать идеальную линию и нарисованную. Понятно, что нужно разбивать их на небольшие отрезки, сравнивать их и потом аккумулировать накапливаемую ошибку. Сложность оказалась в углах. Совсем выкидывать их нельзя, углы — важная часть буквы. Но ошибка в них накапливается очень просто и очень быстро.
После двух месяцев проб и ошибок получился примерно такой алгоритм:
- разбиваем кривую на «линейные отрезки»,
- между отрезками у нас появляются области, которые мы назовём «углы». Угол — это просто небольшой отрезок, где направление линии меняется резко. Это может быть либо настоящий угол, либо какая-нибудь петелька, либо начало/конец линии.
- на линейных отрезках мы считаем, как раньше считали. Смотрим схожесть направлений отрезков и расстояние между идеальной/нарисованной кривой. Накапливаем ошибку.
- в углах мы смотрим на разницу между длиной идеальной и нарисованной кривой. И всё. Удивительным образом оказалось, что если правильно подобрать допустимую разницу, то это простое правило хорошо проверяет рисование углов.
На изображении видны углы (большие окружности). Ищутся они автоматически, по кривой, заданной в SVG-формате.
Русский язык
Логично было предположить, что приложение потребуется локализовывать на разные языки. Но какие языки это будут и когда именно после старта это будем делать, не было понятно. И разработка велась без оглядки на какой-то другой язык.
Когда же появилось желание и возможность поддержать русский язык, выяснилось, что это не совсем просто.
Во-первых, нужно переводить интерфейс. Причём, если интерфейс должен быть привязан к языку системы, то язык обучения может быть другой. Нужно уметь их переключать. И нужно сделать так, чтобы длиннющий русский влезал везде в интерфейсе.
Во-вторых, красивый шрифт, который мы использовали, не содержал русские буквы. Пришлось заказать доработку шрифта.
В третьих, понадобилось больше картинок. В русском языке больше букв, больше упражнений. Кроме картинок необходима была также новая озвучка.
В четвёртых, потребовалось доработать алгоритм работы с буквами. Диакритика («й», «ё»), мелкие штрихи («ц» или «щ») — всё это усложняло алгоритм контроля качества рисования.
Очень кстати пришлась запаковка ресурсов в файл. Создав несколько таких пакетов и переключая эти файлы, оказалось очень удобно переключаться между языками.
Мелочи
Конечно же, было и много мелочей в разработке. Например, iTrace умеет печатать «настоящие» прописи, бумажные. Чтобы детям было интереснее, снизу каждой прописи рисуется лабиринт. Он генерируется каждый раз заново, занятно было подобрать параметры так, чтобы детям было и интересно, и не сильно просто/сложно.
Были проблемы и с апрувом. Например, когда мы попробовали в первый раз включить Touch ID для парент-гейта (спец-задачи, которую решают родители, но не дети, для входа в настройки приложения), нам отказали, сказав «нельзя». Пришлось пообщаться с представителями Apple, придумать более аккуратный алгоритм работы с Touch ID, после чего фичу приняли.
Также мы не сразу придумали, как правильно делать покупку внутри бесплатной версии. Сначала хотели сделать покупку каждой мелкой фичи, но после обсуждения решили сделать только одну покупку «на всё».
Интересно было и сами буквы рисовать. Они рисуются много где, и в прописях пунктиром, и разной толщиной на экране рисования, и в истории, где можно просмотреть попытки ребёнка рисовать буквы вместе с ошибками… везде свои требования, свои сложности.
В результате получилось отличное приложение. Посмотрите. Есть и бесплатная версия, с покупкой внутри, и платная. Более 350 тысяч человек уже посмотрели, и многим нравится. :-)
Комментарии (30)
neuotq
06.03.2016 15:12А насколько актуально в английском тренировать письменное начертания, я помню еще в школе с изучением англ.языка(начало 90ых) переписываясь с американцами заметил что почти все писали печатными буквами, и даже в нашей подгруппе изучения английского мы тоже писали печатными(а в параллельной писали письменными). Короче говоря я к тому, что в тех же США тренд на отказ от письменных уже давно, хотя конечно же есть и другие англоговорящие страны.
В целом наверное правильно отказываться от это каллиграфии, так как красивый почерк в итоге у единиц, зато расшифровывать "уникальные образцы" напряжно. С печатными легче.bealex
06.03.2016 15:15+3Приложение создавалось в том числе и для школ. Образование, что у нас, что в США, не всегда поспевает за последними веяниями моды, да и школы разные. В некоторых действительно обучают писменным буквам и для этого нам потребовалось это реализовать.
Насколько это актуально — вопрос, на который у меня нет ответа, я специалист немного в другом. Но судя по тому, что я слышал в процессе разработки, в реальной жизни это действительно не требуется. Зато развивает моторику рук, а некоторым просто нравится, как картинки рисовать :)neuotq
06.03.2016 15:17Спасибо за ответ. Я просто подумал есть ли какие-либо метрики в приложении, на основе которых можно было делать вывод о популярности написания письменных, да и вообще о многом. О любимой букве, а не любимое букве, а букве которую сложнее всего деткам написать :)
bealex
06.03.2016 15:47Метрики, конечно же, есть. Но их нельзя использовать для оценки «что популярно, что нет», потому что выборка по пользователям неправильная. Использование зависит от школы, а не от желания ребенка и его родителей.
iLexey
06.03.2016 21:54Идея хорошая, реализация интересная и находки отличные (здорово, что вы думаете про поддержку старых девайсов). Но! Как папа двоих детей могу сказать, что неодушевленный девайс и рисование пальцем по экрану (да даже если и стилусом) вообще ничем не помогает в освоении письма. Я пробовал предлагать своим детям огромное количество разных приложений. Вначале детям нравится тупо закрашивать буквы без какого либо порядку и правильности в написании, потом просто надоедает. Может быть ваш вариант проверки написания будет лучше и сразу ограничит некие рамки. Для себя я понял одно — даже самые интересные обучающие приложения с великолепной анимацией и озвучкой через какое-то время наскучивают. Возможно, что это вопрос централизованного и целенаправленного обучения с использованием современных мультимедийных возможностей. В наших школах правда до этого еще очень далеко.
К сожалению, на данный момент ничего лучше старого доброго карандаша/ручки и бумажной тетрадки не придумали. Нужно что-то осязаемое! Нужна возможность перелистнуть несколько страниц назад и посмотреть на прогресс (хотя у вас вроде бы это есть). Вот были буковки кривые, а вот ровные. На мой взгляд офигенная реализация подхода обучения — в тетрадках Кумон.
Сделайте дополнительно версию просто с прописями для печати (и наклонными линиями, как у нас в школе) — было бы здорово и полезно. А для школьников ничего лучше старых добрых прописей с палочками, крючочками и соединительными линиями не придумали, не знаю правда как там в Америке учат...Timursan
06.03.2016 22:06Я тоже купил игрушку — считаю, что всё супер — и идея, и реализация! А рисовать надо не пальцем, а стилусом. Моему 5-летнему сыну очень понравилась игра, все буквы прошли уже :)
bealex
06.03.2016 22:07+1Возможно, вы правы. Но нашим детям (уже троим) помогло. Также приложением пользуются сотни тысяч пользователей.
Тем не менее, я поддерживаю вас. И приложение поддерживает. Во-первых, мы рекомендуем пользоваться стилусом для рисования букв. А во-вторых, прямо из iTrace можно напечатать обычные, бумажные прописи для любой буквы, цифры или даже слов. После чего писать в них, как обычно, на бумаге.
egormerkushev
07.03.2016 01:00Расскажите, пожалуйста, кратко, как писали под iOS 5 и тестировали? Были проблемы совместимости с новым Xcode, например? Может быть какие-то приёмы хитрые были в работе? Спасибо.
bealex
07.03.2016 01:50Разве что кратко… :)
— самое важное — наличие устройства. Для iOS 5 — это первый Айпад.
— второе — аккуратное тестирование. Тестировать нужно и память, и всё, что исполняется, по всем веткам. Свалиться (по неизвестному селектору или как-то ещё) может где угодно, и тестирования требуется основательное количество.
— в помощь — AppCode. У него есть спец-фича подсвечивания кода, который не выполняется в Target SDK (Objective-C).
В остальном — ничего интересного. Что-то пишем, нужно проверять, что оно доступно во всех нужных iOS. Не доступно — обрамляем, отключаем...egormerkushev
07.03.2016 12:16Я что имел в виду. Например, такую ситуацию. Берем El Capitan и Xcode 7 — нельзя выбрать деплоймент таргет древнее iOS 6.
bealex
07.03.2016 12:27Когда публиковали последние версии, ещё можно было.
Сейчас — либо будем использовать более старый Xcode (если Apple позволит), либо просто вынужденно уберём поддержку iOS 5.
Бесконечно поддерживать iOS 5 всё-равно не получится.egormerkushev
07.03.2016 12:28Понятно, сейчас пробую собрать стандартный шаблон под старый свой айпад (который отдан дочери), аж интересно стало.
Большое спасибо, что уделили этой группе девайсов внимание!
UPD
Xcode 7 сказал что мой девайс более не поддерживается 8(bealex
07.03.2016 12:34Печально. Все Xcode можно скачать с developer.apple.com, но старые могут не поддерживать новые OS X. То есть, чтобы собрать что-то тестовое, может потребоваться поставить виртуальную машину со старой OS X, старым Xcode...
И всё это будет бесполезно, если Эпл вдруг перестанет такое принимать :) Точных правил я не знаю, это достаточно редкий кейс, чтобы он удержался у меня в голове.
Так что да, увы, поддержка старых устройств выпиливается Эплом с завидной настойчивостью.
Idot
07.03.2016 20:12А китайский (или японский) вариант с порядком и направлением написания черт в иероглифе ожидается? (не столько для детей, сколько для изучающих язык, чтобы правильно находить ключ иероглифов, зависящий от порядка написания черт)
bealex
07.03.2016 20:18Мы бы с удовольствием (система должна позволять), но для реализации этой идеи нужно:
— специалисты, которые бы рассказали, как оно правильно делается. Мы, увы, не знаем совсем. Нужно рассказать, какие буквы есть, в какой последовательности это все изучать, как правильно перевести все примеры для сценок…
— художник, который выдаст SVG'шки для каждой изучаемой буквы (с правильными направлениями).
— специалист, который наговорит все буквы и слова.
— придумать и дорисовать призы для всех иероглифов (по 3–5 штук для каждой)
Объём работы гигантский, и решиться на него ох как непросто. :(Idot
09.03.2016 18:10Речь не о буквах алфавита, речь об иероглифических ключах, коих 214. Ключ — это простой базовый иероглиф, а сложные иероглифы состоят из сочетания базовых иероглифов. Например, если три раза написать базовый иероглиф «дерево», то получится более сложный иероглиф «лес».
Поясню зачем это нужно, в китайском языке 84 тысячи иероглифов, которые относительно широко используются и в японском языке, и довольно ограниченно в Южной Корее (а вот Северная Корея и Вьетнам, от иероглифов отказались). И чтобы разобраться в тысячах иероглифах существуют словари иероглифов, в которых они сгруппированы по ключам, и по ключу в них можно найти любой незнакомы.
Чем будет КРАЙНЕ ПОЛЕЗНА ваша программа: чтобы правильно определить ключ в незнакомом иероглифе, очень важно уметь правильно писать иероглифы, то есть делать это в правильном порядке, твёрдо зная какая черта является первой, а какая второй, третьей, и т.п.
PS в КНР и Японии широко используют упрощённые иероглифы, но упростили их… по-разному. То есть упрощённый китайский иероглиф может оказаться заметно отличающимся от упрощённого японского. То есть получается три варианта записи — традиционный, упрощённый китайский и упрощённый японский (естественно, изучающим китайский или японский в первую очередь интересна соответствующая упрощённая запись).
PPS насчёт чтения иероглифов: как в японском, так и корейском языке, каждый иероглиф имеет два варианта чтения — национальный и китайский («китайский» — это не то как китайцы читают этот иероглиф сейчас, а то как его читали в Китае сотни лет назад).bealex
09.03.2016 19:21Очень интересно. Пойду обсужу это всё с Михаилом, который, собственно, придумал и развивает iTrace (я технической частью заведую).
kovpas
Отличная статья, спасибо! Расскажите, как придумывали картинки для букв? Понятно, что в России "А" это "Арбуз", но в Америке это будет что-то другое, а в Великобритании может быть вообще что-то третье. Если в одно приложение паковать все возможные варианты, то получится очень жирное приложение, не?
ivanych
А буква "Д" — это, судя по скриншотам, "Утка":)
kovpas
Duck. Либо я не вкурил шутку :)
bealex
Да, скриншоты из английской версии. С дебажными контролами :-)
Shultc
удалён
HaruAtari
Duck
PapaBubaDiop
Дикая она.
bodqhrohro
Слава китайских кубиков покоя не даёт :)
bealex
:)
С идеями картинок было тяжело, пришлось советоваться с учителями и знакомыми (или изобретать что-то на буквы Ы и Ъ). Александра Мазурина https://dribbble.com/uvat-2 также помогала идеями, после чего всё это рисовала (ну, почти всё, остальные перечислены в разделе «О программе»).
Впрочем, сейчас, с получившимся набором картинок, уже есть из чего выбрать. Со следующими языками будет проще.
Moskus
Вбив в поиске "alphabet blocks vintage", можно обнаружить фото старинных кубиков (в том числе — с картинками) и позаимствовать идеи оттуда.
bealex
О, круто, будем иметь в виду в дальнейшем.