четверг, 22 мая 2014 г.

Мысли об игре

Ок, игра закончилась.

Она не является новой, это вольная интерпретация игры бм (которая в свою очередь является интерпретацией тренингов Кови), в которой, если выражаться игровой терминологией, были исправлены некоторые косяки баланса


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

воскресенье, 20 апреля 2014 г.

Большие и маленькие компании


Ты хочешь всю оставшуюся жизнь продавать сладкую газировку? Или хочешь пойти со мной и изменить мир? 
(с)Джобс

Небольшой совет молодым и талантливым разработчикам, которые ищут работу, — никогда не идите работать в большие компании. Никогда, ни за какие деньги, ни при каких обстоятельствах. Даже если Вы проработаете всего год, оттуда Вы уйдете уже другими людьми, лишитесь лучшего, что у Вас сейчас есть. Поработав «по графику» с унылым пузатым менеджерьем, Вы станете беспомощным отработанным материалом с рабско-потребительской ментальностью. Ваш опыт работы в Google, Яндексе или Mail.ru — мощная антирекомендация для любого здравого руководителя маленькой команды.
Бездельничайте, учитесь, играйте, рисуйте, создавайте музыку, занимайтесь фрилансом, открывайте стартапы, делайте никому не нужные проекты, голодайте — но никогда не идите работать в корпорации. Помните: всякий раз, когда молодой и талантливый разработчик идет работать в большую компанию, умирает котенок. 
(с)Дуров

Все совпадения случайны и являются выдумками читающего индивидуума


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

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

пятница, 14 марта 2014 г.

Сжатие текстур андроида в Unity

В айфонах почти все хорошо. Сделал приложение, потестил на паре девайсов (iphone 4s, ipad2) и можешь быть почти уверен, что с остальными не будет головной боли.

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

Поскольку очень много оптимизации связывается с отрисовкой, нужно уделить большое внимание отрисовываемым текстурам, а именно сжатию. Unity поддерживает несколько видов сжатия для разных видеокарт: ETC1, ATC, DXT, PVRTC, ETC2. Более того, можно не только руками для каждой текстуры выставлять желаемое сжатие, а задать AndroidBuildSubtarget - при билде все запакуется в лучшем виде.
Внимание, вопрос: в какой формат нужно запаковывать? Ответ: зависит от того, сколько APK вы хотите вылить в google-play.
Дело в том, что магазин приложений гугл поддерживает выливку нескольких билдов для разных девайсов. Это сделано специально для того, чтобы приложения поддерживало как можно больше устройств. Там можно задать разные apk для разных видеокарт, разных пропорций экранов, разных разрешений и т.д. Но есть одно большое НО: рекомендуется использовать эту возможность только тогда, когда есть полное понимание, что одним билдом не обойтись.  Потому что разработку нескольких билдов тяжело поддерживать, а также должна быть правильная систематизация версий, ведь в том же мануале сказано, что гугл отдает поддерживаемый билд с наивысшей версией. То есть, если видеокарта поддерживает etc билд и для нее предпочтительнее atc, но версия etc > версии atc, гугл отдаст etc билд.

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

0411079, или более наглядно 04 1 1079, где первые две цифры - min android sdk(04 - это что-то типа android froyo или еще древнее), второе число - это номер сжатия(об этом позже), а последние цифры - это номер версии билда, без точек(1.07.9).

Как правильно выбрать номер сжатия, чтобы приоритетный билд попал в нужное устройство. Надо исходить из логики: что реже используется, то и надо выше ставить приоритет. А статистика такова: etc > atc > dxt > pvrtc > etc2. Именно в таком порядке их можно и занумеровать.

А чтобы не мучиться каждый раз с генерацией билда и билд-номера, стоит завести один маленький скрипт:

[MenuItem("Custom/Build apks for all Texture Compressions")]
    private static void BuildApksForAllCompressions()
    {
        BuildAndroid(AndroidBuildSubtarget.DXT);
        BuildAndroid(AndroidBuildSubtarget.ATC);
        BuildAndroid(AndroidBuildSubtarget.PVRTC);
        BuildAndroid(AndroidBuildSubtarget.ETC);

        Debug.Log("Finished building");
    }

    //Version code looks like this: 04 1 1079
    //first two numbers = API LEVEL (04)
    //second two numbers = Android Subtarget (1)
    //Last numbers = version number, revision (1079, got from "1.07.9")
    private static int GenerateVersionCode(AndroidBuildSubtarget target)
    {
        string number = string.Format("{0}{1}{2}", ((int) PlayerSettings.Android.minSdkVersion).ToString("D2"),
            GetTargetVersion(target), PlayerSettings.bundleVersion);
        number = number.Replace(".", "");
        int result = int.Parse(number);
        Debug.Log("Android " + target + " version: " + number + ", " + result);
        return result;
    }

  //all devices support etc, so it has lowest value
    private static int GetTargetVersion(AndroidBuildSubtarget target)
    {
        switch (target)
        {
            case AndroidBuildSubtarget.ETC:
                return 4;
            case AndroidBuildSubtarget.ATC:
                return 5;
            case AndroidBuildSubtarget.DXT:
                return 6;
            case AndroidBuildSubtarget.PVRTC:
                return 7;
            default:
                return 0;
        }
    }

    private static void BuildAndroid(AndroidBuildSubtarget target)
    {
        if (EditorUserBuildSettings.activeBuildTarget != BuildTarget.Android)
            EditorUserBuildSettings.SwitchActiveBuildTarget(BuildTarget.Android);
        EditorUserBuildSettings.androidBuildSubtarget = target;

        //to include more build options use bitwise OR, for instance: BuildOptions options = BuildOptions.Development | BuildOptions.ShowBuiltPlayer;
        var bo = BuildOptions.None;
        PlayerSettings.Android.keystoreName = "keystore_location";
        PlayerSettings.Android.keyaliasName = "alias";
        PlayerSettings.Android.keystorePass = "password";
        PlayerSettings.Android.keyaliasPass = "password";

        PlayerSettings.bundleIdentifier = "yourbundle";
        PlayerSettings.Android.bundleVersionCode = GenerateVersionCode(target);

        try
        {
            string apkName = "your_apk_name"
            BuildPipeline.BuildPlayer(
                (from scene in EditorBuildSettings.scenes where scene.enabled select scene.path).ToArray(), apkName,
                BuildTarget.Android, bo);
        }
        catch (Exception e)
        {
            Debug.Log("Error: " + e.Message);
        }
    }
Как-то так. Да, при выливании в сторы, нужно заливать билды в том же порядке. Если перепутать, то стор просто отвергнет билд с меньшим номером.

четверг, 13 марта 2014 г.

Успешная игра - каждому по приоритету

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


У каждого вида деятельности при разработке игр есть свои приоритеты. Например, для предпринимателей - количество принесенных денег, маркетологов - количество пользователей и ARPPU. У разработчиков игр свои приоритеты, свои идеалы: количество и качество фичей, а так же реакция пользователей на них. Вполне очевидно, что проггер становится счастливым, когда он добавляет вроде как маленькую фичу, а юзеры оценивают ее, начинают играть, радуются. Таким же образом программист бесится, когда чувствует, что фича не приносит ожидаемого отклика, ведь по факту, он делает ее зря. Всегда лучше ничего не делать, чем делать ничего.

Совсем недавно нам удалось реализовать игру, которая подходит под мое определение успешности, как программиста. Нет, это не road smash, хотя, судя по статистике, она очень понравилась пользователям и наверняка приносит хороший доход.

Игра называется Clumsy Fino (тыц для ios), это очередной клон Flappy Bird с респавном и дракончиком. Вроде ничего примечательного, но игра успешна. Объясню почему:

  1. У нас отличная команда - множество непересекающихся скиллов. Альберт Александровский взял на себя роль издателя, он занимался продвижением, поиском подходящих запросов, делал завораживающие картинки. Игорь Фомичев делал игровую часть, делал баланс. Я прикручивал плагины, занимался неигровой частью. Каждый занимался тем, что он лучше всего умеет. В условиях "экстремальной разработки" эта тактика сыграла как нельзя лучше.
  2. С самого начала мы решили делать максимально быстро. Первая версия игры была сделана за два дня по вечерам, содержала в себе все мастхэвы и вылита в сторы. Это была самая продуктивная итерация на моей памяти.
  3. Вторая итерация заняла чуть больше времени(пять дней "грязного времени", где-то три человекодня на всех), за это время мы полностью обновили GUI, добавили важную фичу - респавн птицы при смерти. После апдейта количество скачиваний удвоилось (спасибо, Альберт!), а игровая сессия стала больше в среднем на три минуты (спасибо, Игорь).
  4. В разработке не было факапов. Вообще. Причины две: 
    1. мы делали только те фичи, которые казались нам нужными. Те, которые не очень привлекательны игрокам или трудозатратны - мы не делали. Наконец-то смогли применить принцип Парето.
    2. Никто не лез в чужую область, каждый делал то, что умел лучше всего. Минимум ресерча, просто берешь и получаешь результат. А через четыре часа люди радуются этому результату.
  5. Не знаю, как остальные ребята, лично я получил огромное удовольствие. Это было подобно очень короткому кэмпу, где ты просто делаешь то, что тебе нравится. Результат не заставил себя ждать. 
  6. Эта игра уже окупила наши трудозатраты. Доход до сих пор растет :)
Не знаю, как другие программисты, но я очень хочу, чтобы моими продуктами пользовались. Очень сладостно ощущение того, что ты делаешь не зря. И совершенно пофиг, сколько денег от этого ты зарабатываешь.




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

    четверг, 13 февраля 2014 г.

    Делайте быстрее!


    Вася и Петя одновременно начали писать один и тот же продукт.

    Вася был «ориентирован на результат» и начал сразу писать говнокод не продумав толком архитектуру.
    А Петя месяц разрабатывал архитектуру, месяц делал удобный интуитивный интерфейс, которому позавидывал бы Джони Айв, потом месяц писал тесты, потом два месяца писал сам код и получил идеальное стабильное приложение.
    Но Вася выпустил уже через месяц первую версию программы, пусть и не идеальную, пусть с багами, но рабочую, и начал её продавать. Ещё через месяц выпустил вторую версию исправляющие баги первой и добавляющие новые баги. Ещё через месяц на доходы от продаж нанял двух толковых программеров, которые за два месяца перелопатили весь код, согласно пожеланиям пользователей допилили интерфейс и выпустили третью версию программы.
    Итого, через пять месяцев у Васи было два работника, куча клиентов и сносно работающее приложение отвечающее желаниям клиентов.
    У Пети было вылизанное никому не известное приложение, минус на банковском счёте и ни одного клиента.

    В завершение этого выдуманного примера можно сказать, что через полгода Вася купил все наработки Пети, Петю взял в штат тестировщиком, а сам по пьяни разбился на своём новеньком Туареге

    История №1

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

    Итог: игра на гугл-плее, утонувшая в тысячах ей подобных, получен бесценный опыт.
    Совершенные ошибки: распыление на фичи, что повлекло за собой долгий срок разработки, разработка, никому не показывая - не было объективного фидбека.
    Вывод: не бояться показывать продукты, делать мало, но быстро.


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


    История №2
    Игра road smash делается с февраля 2013, скоро ожидается полноценный релиз, я присоединился в конце сентября. Ребята делали игру, завели блог разработчика, к первой публичной бете (которая была, честно говоря, пре-альфой) были фанаты, игра за месяц набрала миллион загрузок без рекламы и продвижения. Мы пережили падение сервера, кучу клиенстких багов, вопли детей "Разраб, верни галду!", факапы длиной в месяц, разработка фич, которые никому не нужны. Большинство факапов связано с привычкой все усложнять, болезнью абстрактно ориентированного программиста
    Итог: игра делается в сумме год, публичная бета(которая пре-альфа) неожиданно взлетела.
    Совершенные ошибки: over-engineering, построение сложных систем, вместо того, чтобы сделать по простому. Разработка того, что не принесет пользы.
    Вывод: yagni и почаще думать головой. Головой думать всегда полезно, не только в разработке.
    Простота - ключ к надежности
    (с)Дейкстра

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

    P.S. Потратил полтора часа на написание статьи, а у самого лежит бизнес-план, пишущийся с января и сделанный на 80%. Ну не идиот ли я? 



    суббота, 25 января 2014 г.

    UIViewController for dummies

    UIViewControllers became harder to understand with Storyboards - there are lots of auto-layouts you might not handle.

    Just a liitle hint to help debugging:

    UIViewController's method invoke sequence is as below
    • awakeFromNib
    • viewDidLoad
    • viewWillAppear
    • viewWillLayoutSubviews
    • viewDidLayoutSubviews
    • viewDidAppear
    • viewWillLayoutSubviews
    • viewDidLayoutSubviews
    So, if you have views building by code, make sure that storyboard layout will not break it into mess.

    понедельник, 9 декабря 2013 г.

    5 things you need to implement in any mobile game

    русская версия - здесь

    I have three posts in drafts(called "You don't need server" and "3 reasons to switch from Asset Server", "Execute impossible to pardon: how to deal with hackers"), but decided to write this one just because it seems more timely than others.

    We had discussion with Yuri about what makes any game perfect and what features must be implemented and come up with some common and must-have features:

    • in-app-purchases. No comments here. If you want to make money from your game, you'd better have well-tested, stable in-apps;
    • analytics. The thing is you can not predict user behaviour unless you have any clue what your user is doing in the game. And it's better to have as much information as possible - you know where is your problem in your game mechanics and can easily fix it up;
    • local and remote push notifications. We didn't implement them in the first releases of Road Smash and we feel regret about it. What is the easiest way to let the users play updated game? Obviously, push notifications.I know that users may hate pushes and remove your app just because they are got annoyed by your app, but in 70% it works.
    • Anticheat protection. You know, cheaters always exist, especially on android and you need to prevent your game from being hacked. Somebody may say that you need to protect everything, somebody have an opinion that you need to give up on hackers and let them play. I think that hackers wouldn't pay. They would keep hacking the game until they find something or even delete it from their devices, but they would never pay. So I came up with the decision here: protect only that things that cost money or may affect other users. In business terms there are two most important things for developer: users that bring money. When I say money, I mean in-apps, when I say affect other users, I mean scoreboard, social, competition parts of your game. And you need to keep tiny balance between letting gamers play comfortably and prevent players from stealing your money.
    • Ads. I underestimated role of ads, thought it's annoying and may push your user away. But I was wrong. Firstly, you can integrate ads, so they will become part of your gameplay. Users will want to see some ads just to get some goods instead. And of course, it is a great chance to  get money from users that will never pay :)
    The main purpose of any game is to have a lot of users in game and keep them as long as long as possible, letting them pay for any reason. In that meaning these five features described must be implemented, well-tested as soon as possible.

    And to make this post more concrete, I have some links to assets to share with you to get that features implemented in the easiest way.
    • in-apps: use unibill. It supports app store, google play, windows store out-of-box, including receipts, subscriptions, etc. Must-have!
    • analytics: there's a wonderful and free plugin, called GameAnalytics. It supports events, heat maps, gameplay stats. If you are fan of old-school analytics, you may check out for my wrapper to Flurry plugin in my github repository, I'll push all the code as soon as I find free time for it. 
    • push notification: there are too many good plugins in assets, so I don't know what's the best option here. I'd recommend pushwoosh service, because it provides a lot of features out-of-box, but let me leave this topic open.
    • anticheat: well... there's no common solution as it really depends on what kind of game you are working on. Maybe some kind of playerprefs encryption, in-apps encryption? Did I miss something?
    • ads: there is a bunch of different options: admob, tapjoy, adcolony, applifier. Some of them provide banner and image ads, some of them kind of video or partnership ads. Choose one you like the most.