понедельник, 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.

вторник, 19 ноября 2013 г.

Unity Continuous Integration

And finally Unity Continuous Integration (CI) plugin is on Unity Asset Store! Check it out: https://www.assetstore.unity3d.com/#/content/12695.

I spent a day setting up Jenkins build machine on Road Smash project and spent one more day to unify builder script and writing documentation, so I can use it on any unity project with any CI framework and any CVS systems, including asset server, git, svn. It works out-of-box, the only thing you need is to call the right method(like BuildAndroid, BuildIOS) from command line(OSX, Windows).

For now Unity CI supports:

  • iOS build
  • Android builds (including different builds for different textures format, including ATC, ETC, DXT & PVRTC). You can call a method to build 5 different apks, which will support different textures from AndroidManifest.xml and may be uploaded to Google Play as multiple apks.

I didn't include changing version code to plugin, because every project may have its own policy and I may break it, but it is really easy to add there. I'll write another post about all android building stuff later.

I'm planning to include unit-testing plugin to CI and make it portable. I'll finish it up as soon as I'll have a free time for it.

P.S. Unity Technologies rejected my Flurry plugin(full iOS & Android, Windows Phone beta support) for its own internal business reasons. Is anybody interested in it?

среда, 13 ноября 2013 г.

Codility prefix problem

I got a problem from codility site and was asked to solve it. Here's a link on stackoverflow. In two words: assume we have a string S and we need to choose such a prefix P of S, that product of its length and occurrence in S will be maximum.

For example, S = "abababa" has the following prefixes:
"a", whose product equals 1 * 4 = 4,
"ab", whose product equals 2 * 3 = 6,
"aba", whose product equals 3 * 3 = 9,
"abab", whose product equals 4 * 2 = 8,
"ababa", whose product equals 5 * 2 = 10,
"ababab", whose product equals 6 * 1 = 6,
"abababa", whose product equals 7 * 1 = 7.
I like problems like that. It has obvious solution in O(N ** 3) time, elegant and easy to guess if you know z-algorithm O(N ** 2) solution and tricky O(N) solution.

Ok, first solution is naive brute-force. You choose length from 1 to N, get a prefix of its length and count occurences by brute-force searching. So, choosing length takes O(N) time and brute force takes O(N ** 2) time, totally O(N ** 3).

Ok, it doesn't really what we want to achieve. Let's think how we can reduce complexity. Anyone who ever learned strings knows that string occurence can be searched in O(N) time, so with a little code we can achieve O(N ** 2) time compexity solution. I used Z-algorithm. Here's the code:



vector<int> z_function(string &S); //z-function, z[0] = S.length()

int calculate(string &S) {
 vector<int> z = z_function(S);

 int ans = 0;
 for (int i = 1; i <= S.length(); ++i) { //iterate on length
  int k = 0; //counter of occurrences
  for (int j = 0; j < S.length(); ++j) 
   if (z[j] >= i) ++k;  

  //update answer
  ans = max(ans, i * k);
 }
        return ans;
}

But it is still not enough though. How we can get rid of nested loop? We can precalc prefix occurences with Z-algo and finding them in constant time. Initially Z-algo returns an array, where z[i] is the length of longest substring starting from S[i], which is also prefix of S. For "abacaba" it will return {7, 0, 1, 0, 3, 0, 1}. And we know that if prefix of S with length i has N occurrences, then all smaller prefixes of S will have at least N occurrences. So, we create an array of occurrences, iterate over z-array, updating new created array and finally iterate over it backwards, updating as explained above.


vector<int> z_function(string &S); //z-function, z[0] = S.length()

int calculate(string &S) {
 vector<int> z = z_function(S);

 int n = S.length(); 
 vector<int> cnt(n + 1);


 //cnt[i] - count of i-length prefix occurrences of S 
 for (int i = 0; i < n; ++i) 
  ++cnt[z[i]];

 //if cnt[i] is in S, cnt[i - 1] will be in S
 int previous = 0;
 for (int i = n; i > 0; --i) {
  cnt[i] += previous;
  previous = cnt[i];
 }

 int ans = 0;
 for (int i = 1; i <= S.length(); ++i) { //iterate on length
  int test = cnt[i] * i;
  //update answer
  ans = max(ans, test);
 }

 return ans;
}

We separately count occurrences in O(N) time and find the answer in O(N) time as wanted.

For me it is the most easiest way to solve this problem. If anyone knows better solution, please kindly share.

четверг, 31 октября 2013 г.

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


Unity! Я тебя обожаю! Ты из под-коробки поддерживаешь разные типы сжатия в билдах! Не надо руками конвертить, нет больше извратов с AndroidManifest. Поменял опцию и билди! It just automagically works!


Для любителей CI - поменять значение
EditorUserBuildSettings.androidBuildSubtarget
http://docs.unity3d.com/Documentation/ScriptReference/AndroidBuildSubtarget.html

Для ленивых: разные типы сжатия и девайсы, поддерживающие их:
DXTS3 texture compression, nonspecific to DXT variant. Supported on devices running Nvidia Tegra2 platform, including Motorala Xoom, Motorola Atrix, Droid Bionic, and others.
PVRTCPowerVR texture compression. Available in devices running PowerVR SGX530/540 GPU, such as Motorola DROID series; Samsung Galaxy S, Nexus S, and Galaxy Tab; and others.
ATCATI texture compression. Available on devices running Adreno GPU, including HTC Nexus One,
Droid Incredible, EVO, and others.
ETCETC1 texture compression (or RGBA16 for textures with alpha), supported by all devices.

среда, 23 октября 2013 г.

Баг шейдера при операции mul

Натолкнулись на баг в шейдере unity:

  • шейдер ломается частично, а именно: чем ярче пиксель, тем больше он стробит (если оттенок черного - все норм, ярче - становится заметней). То есть шейдер работает, fallback-a не происходит!
  • происходит только при операции умножения матрицы 4x4 на вектор4.
  • воспроизводится только на адрено устройствах. 
И это при условии, что мы потом отбрасываем последнюю часть и приводим полученный ответ к vector3.
Называется, поймали эксепшн, который никак нельзя додуматься поймать.



В общем, у кого стробление пикселей в шейдере, напишите собственный mul (благо, это просто несколько операций dot).

вторник, 8 октября 2013 г.

Интересное об app store review

Кто хоть раз заливал приложение в app store, знает, что нужно пройти  этапы ожидания ревью, самого ревью и ready for sale. Ну так вот, несколько интересных моментов об ожидании в app store и ускорении ожидания (только ios и под каждым словом надо ставить имхо, ибо только мой опыт):
  • Среднее время review - 5 рабочих дней
  • Походу действительно весь штат apple сидит в США, поэтому надо еще рассчитывать их часовой пояс(где-то -7 UTC), их локальное рабочее время. Это важно в случае написания писем.
  • Expedited app review(это запрос на review без ожидания в случае, если там оказался критический баг или вот-вот начнется пиар-кампания) работает магическим образом. Более того, мне кажется, это сильно зависит от настроения отвечающего на запрос. Однажды я нашел некритичный баг после двух часов релиза и получил добро на expedited review. В другом случае я за пять дней до пиар-выставки отправил приложение и получил отказ в досрочном ревью. В общем, сделал следующий вывод: на expedited app review нельзя полагаться.
  • Форма Contact us работает оперативно. Даже если они не отвечают, в течение часа что-то происходит.
  • Статья хабра не сработала. Или я не перескочил минимальный порог приложений для ожидания, или звезды ушли в параллельную вселенную, но это нисколько не повлияло на время ожидания.
  • Если прошло более пяти рабочих дней, то стоит черкнуть письмецо через Contact us. Реально, они начинают проверять! 
  • А теперь самое интересное: если в ожидании ревью висит несколько приложений и написать письмо(а там в форме надо написать id-шник приложения, у которого ты хочешь обновить статус проверки/ожидания проверки), то начинают проверять сначала не то приложение, которое ты упомянул в письме. Я думал, это одноразовый баг, но такое поведение повторялось у меня несколько раз! И до сих пор в ожидании ревью висит приложение, на которое я отправил запрос проверить в первую очередь, хотя остальные давно проверены.

понедельник, 16 сентября 2013 г.

Optimize performance in Unity3d: practical notes

(Russian post goes here)
I wrote an article in russian about unity performance optimization recently. In this topic I want to share some small, but efficient practical tips with you. Here we go:


  • if you're running out of memory, use proper texture compression. PVRTC is acceptable for iOS, DXT for android. And don't forget about mip-maps. I know, it gonna increase binary size by 30%, but it's worth it. Don't be so modest to limit max size to textures. 
  • If you don't need shadows in renderers (for example, in UI or something), just uncheck "Receive shadows" & "Cast shadows" options. They are always on by default.
  • Don't forget about batching. And yes, static batching allows much more than dynamic, so you don't move object, mark it as static. If you want to combine meshes runtime, use StaticBatchingUtility.Combine.




  • Take into account renderer settings, physics settings of project (Edit->Project settings)
  • Anti-aliasing, texture quality, shadows and vsync have many options. Just experiment with them to gain best performance/quality.
  • Uncheck layer collision checks you don't need to calculate.
  • If you don't really need physics, change fixed timestep.
  • Don't move static objects. 
  • Design your level, so your camera will not take a lot of objects, then camera culling will save a lot of frames for you.
  • Mesh collider checks are very expensive! It's better to use double-check technique: firstly make a capsule or box collider and check with it's collision, then check with mesh collider.
  • Lights. Know the difference between point & directional, use them properly. 
  • Cache  transform, collider, rigidbody. We use CachedMonoBehaviour in our projects and happy with that (P.S. From 4.1 Unity did a lot of work with GetComponent optimization, but CachedMonoBehaviour is still faster).
You may achieve better results if you know instruments you use. These tips are really easy to use and sometimes they give more performance than it's expected. Use it and know, premature optimization is root of all evil.


P.S. We are applying all this tips in game named technoball, so please free to try it and tell me what you think about it.
P.P.S We made a lot of work and prepared an awesome update, stay in touch!

воскресенье, 8 сентября 2013 г.

Мысля про скриншоты

Оказывается, "бухгалтероподобным" людям тяжело сделать скриншот и поделиться с друзьями. И вправду, я не умею стандартными средствами делать скриншот быстрее, чем следующими операциями:

  1. нажать prtscr
  2. открыть paint(быстрее, чем нажать пуск, набрать mspaint я не умею)
  3. вставить рисунок
  4. сохранить
  5. открыть файлопомойку, залить файл
  6. получить ссылку, поделиться


Как можно убыстрить этот процесс:

  • Воспользоваться связкой макинтош+дропбокс(кхе-кхе). В маке я делаю в три операции: делаю скриншот сразу в десктоп (cmd+shift+3), кидаю в паблик дропбокс, получаю ссылку из дропбокса
  • Воспользоваться сервисом snag.gy. Смысл таков: нажимаете prtscr, открываете сайт, нажимаете вставить(если нужно, корректируете встроенным редактором), сохраняете, делитесь. Примерно так же быстро, как в маке.
  • Наверняка есть уже готовые сервисы-демоны, которые висят в системе, при нажатии магического шортката делают скриншот, аплодят в файловую помойку и показывают ссылку. Если кто знает о таких, пожалуйста, поведайте! Если нет, скажите, если бы была такая прога, скажите, пользовались бы вы им? 

четверг, 22 августа 2013 г.

Facebook left-side menu with storyboards

Well, facebook-like left-side menus became really trendy UI pattern, so everyone tries to insert it to their projects.
Functionally it's primitive: you have left-view in background, and there's drag gesture recognizer, which "catches" foreground view and moves it on xz-direction.

There are lots of open source implementations for ios:
  • ViewDeck
  • SASlideMenu
  • JWSlideMenu
  • DDMenuController
  • PKRevealController
  • ECSlidingViewController
  • MWFSlideNavigationViewController
  • MFSideMenu
  • HHTabListController
  • MTSlideViewController
  • SlideViewController
  • MTStackViewController
  • MMDrawerController

  • I'd recommend to use ViewDeck and SASlideMenu, because they are the most functional and easiest to integrate (For android developers it's worth to mention this stackoverflow answer).

    But there was a problem related to storyboards: there's no out-of-box solution to integrate left-side menu with storyboards power! Recently I found really easy to use framework: ViewDeckStoryBoards. Hooray, there's no need to revert back to xibs nightmare! It's based on viewDeck, there's a little code needed(just to overwrite left side & main windows). It supports arc, storyboards. 



    суббота, 29 июня 2013 г.

    Принцип самурая или почему надо радоваться преждевременным ошибкам

    Делаем игру с командой и часто инспектируем код друг друга.
    Натолкнулся вот на это:(ссылка)


    1.   void Awake ()
    2.   {
    3.     saveDirPath = Application.dataPath + "/Levels/";
    4.     
    5.     if (buttonSave != null) {
    6.       UIEventListener.Get (buttonSave.gameObject).onClick += OnButtonSave_Click;
    7.     }
    8.     if (buttonLoad != null) {
    9.       UIEventListener.Get (buttonLoad.gameObject).onClick += OnButtonLoad_Click;
    10.     }
    11.     if (buttonInputDialogOk != null) {
    12.       UIEventListener.Get (buttonInputDialogOk.gameObject).onClick += OnButtonInputDialogOk_Click;
    13.     }
    14.     if (buttonInputDialogCancel != null) {
    15.       UIEventListener.Get (buttonInputDialogCancel.gameObject).onClick += OnButtonInputDialogCancel_Click;
    16.     }
    17.     if (buttonselectLevelDialogClose != null) {
    18.       UIEventListener.Get (buttonselectLevelDialogClose.gameObject).onClick += OnButtonselectLevelDialogClose_Click;
    19.     }
    20.   }
    Коротко: есть кнопки, и при запуске подписываются обработчики. Все хорошо, код абсолютно безопасен. Но он плох. Почему?

    воскресенье, 23 июня 2013 г.

    Почему Unity?

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

    Рынок мобильных приложений, в частности, мобильного гейм-дева, такой, как сейчас, появился совсем недавно - в 2008-2009 году, почти сразу после появления революционного по тем меркам гаджета под названием iphone. А поскольку игры на мобильных начали пользоваться бешенным спросом, то программисты начали делать все, чтобы этот спрос удовлетворить. Совсем скоро начали появляться движки для создания мобильных игр, такие как shiva3d, cocos2d, airplay sdk(сейчас marmalade), corona sdk, unreal engine для мобилок(ограниченный udk), unity3d и так далее. У меня был совсем маленький опыт разработки под marmalade, немножко поигрался с udk, но больше всего мне понравился unity3d.

    Чем же привлекателен лично для меня unity3d:

    пятница, 21 июня 2013 г.

    среда, 12 июня 2013 г.

    Lugat sozluk

    Do you remember our osman-turkish ios dictionary? I've been asked for android version many times.
    To those people: there's android version with more than 50000 words! It's really cool app!

    On these holidays I reverse-engineered it and now I have their words database. I just need to convert it to my own format, solve licence issues. I think to finish this by the end of June.

    пятница, 24 мая 2013 г.

    Так, ни о чем

    один файл, а внутри: два энама, три класса, две руками написанные стейт-машины, 400 строк кода. Здравствуй, быдлокод:


    вторник, 14 мая 2013 г.

    Some materials from knowledge sharing seminar



    Two months ago we started our unity knowledge sharing seminars. I forgot to share materials I promised to, so let me do it here.


    2D UI - NGUI (Presentation on russian)


    2D in Unity3D from Рустам Ганеев

    Useful links:


    Unity shaders (Presentation on russian)


    Useful links:

    UPD: Dustin Lee, the Unity evangelist kindly shared awesome slides about shaders, just check it out:

    среда, 17 апреля 2013 г.

    Dynamic texts in ngui

    The only problem I have to deal with 2D UI in NGUI plugin is text fonts. In details, there's a bunch of problems:

    • Dynamic font support (UPD: from NGUI 2.5 dynamic fonts are supported out-of-box)
    • Localization(arabic, hebrew languages, chinese etc).
    • Input texts
    As I'm working on localization in deep, I found some useful things to solve problems described above:
    • for dynamic font, use https://github.com/imkira/unity-sysfont. It seems to be out-of-date (it hasn't been updated for 9 months), but it works and you need just to tweak it for youself. Maybe I'll finish it in holidays.
    • for chinese, japanese dynamic fonts works ok, use plugin above. If you intent to support arabic languages,  use http://u3d.as/content/abdullah-konash/unity-arabic-support/2B8. This asset allows you to write in rtl mode. Now I'm looking for hebrew plugin, or if I find someone who can read hebrew, I'll write it for myself. 
    • Situation about input text is troublesome. Changing uiinput component will cause problems with supporting and I don't like to do it. But there's no other way to cope with it.

    суббота, 6 апреля 2013 г.

    Кто виноват и что делать?

    Не знаю почему, но когда появляется проблема, мы задаем два исконно русских вопроса именно в таком порядке:

    • кто виноват?
    • что делать?
    А теперь маленький лайфхак - инвертируем порядок. То есть сначала думаем, что делать, как решить вопрос, а потом уже ищем виноватых, если в этом осталась какая-либо нужда.

    P.S. Мысль вроде как и капитанская, но, если применять в жизни, получается очень эффективно, намного эффективней первого способа. 

    пятница, 5 апреля 2013 г.

    //TODO, //HACK, FIXME

    В мире спортивного программирования всегда все просто: у тебя всегда валидные данные, обо всем позаботились организаторы. В промышленных масштабах это далеко не так: по закону Мерфи если что-то может пойти не так, так обязательно и будет. Поэтому ты тупо пишешь кучу проверок на случай ядерной войны. Или не пишешь. Оставляешь //TODO: check c надеждой на то, что когда будет скучно и не захочется ничего делать, будешь писать эти глупые проверки.

    Сегодня запустили игру, она падает на уровне. Смотрим, где падает: null-reference exception. Смотрим код:
    public void DoAction(Transform t) {
     //todo check before setting
     t.localPosition = foo;
     t.localRotation = bar;
     t.localScale = foobar;
    }
    

    Падало как раз при присвоении, потому что t - null. Как говорится, без комментариев. Хотя есть один: "//TODO:" существует не для того, что его оставляли и забывали, а для того, чтобы это фиксили и делали это как можно быстрее. И для //HACK, //FIXME инженерный закон "работает - не трогай" не катит - это просто бомба замедленного действия, который когда-нибудь рванет в совсем неожиданное время и неожиданном месте(ну, к примеру, во время презентации или, не дай Бог, релиза).

    пятница, 29 марта 2013 г.

    Оптимизация Unity на мобильниках(3D графика)

    update: (practical "hacks" -  http://rustam-kot.blogspot.ru/2013/09/optimize-performance-in-unity3d.html)

    В последний месяц только и занимаюсь тем, что провожу предельную оптимизацию и глобальный рефакторинг. Поэтому начинаю описывать типичные техники, которые использую каждый день, в стиле маленьких советов. Все нижесказанное опробовано на собственном опыте и могут быть в корне неверны, поэтому если кто хочет обсудить что-либо, оспорить - you're welcome! А кто после прочтения поста не понял, почему его FPS шутер превратился в пошаговую стратегию - тот артист :)

    Итак, у нас есть готовая рабочая версия игры, но она безбожно тормозит. Какие телодвижения в каком порядке произвести, чтобы выиграть процессорное и графическое время:

    1. В ПЕРВУЮ ОЧЕРЕДЬ юзаем профайлер. Даже если знаешь, где можно сделать быстрее, к примеру, уменьшить асимптотику у какой-нибудь структуры, все равно смотреть профайлер. Часто думаешь, что тормозит по одной причине, а на самом деле вылезает совершенно левый оверхед.
    2. Лидеры тормозов:
      • отрисовка - 70%
      • расчет физики - 20%
      • скрипты - 10%.
      И это совершенно понятно: графические карты на мобильниках слабы и весь боттлнек складывается именно в отрисовке. Поэтому дальнейшие оптимизации будут касаться в основном графики.
    3. Первый признак того, что все плохо: много draw-call-ов(далее dc). Немного теории: рисованием чего-либо занимается компонент Renderer(и различные его вариации: MeshRenderer, SkinnedMeshRenderer, ParticleRenderer, LineRenderer, etc), который принимает на вход материал с заданными параметрами(шейдер, текстура и т.д.) и отрисовывает. Видеокарта для каждого материала делает отдельный проход рисования, что и является dc. Ну так вот, чем больше материалов на сцене, тем больше страдает видеокарточка девайса-> тем больше страдает пользователь -> тем больше единичек в app store.
    4. А еще есть такая вещь, как batching, смысл его заключается в том, что несколько объектов, использующих один и тот же материал отрисуются за один проход. То есть, к примеру,  выгодней делать двадцать однотипных домов, как в Набережных Челнах, чем 4 дома как в центре Санкт-Петербурга, потому что двадцать однотипных домов отрисуется за один проход. Поэтому везде, где возможно, используем один и тот же материал.
    5. У батчинга есть проблемы:
      • если у двух объектов разный scale - батчинг не работает. Возможные пути решения:
        •  менять размер mesh-a, а не объекта, если это meshrenderer(к примеру, так делается в NGUI ).
        • строго следить за однотипными объектами и бить по рукам любого, кто не любит стандартные числа.
      • если объекты принимают тень в реалтайме(читай: не лайтмапятся) - батчинг не работает. 
      • если меш очень сложный(больше 900 вершин для тупо вершин, 300 вершин для uv, 180 вершин для uv1, uv2), то батчинг не работает. 
      • если объекты используют один и тот же материал, но у одного материала какое-нибудь свойство отличается - unity создает новый инстанс материала - тут разные материалы - не может быть и речи о батчинге
      • если шейдер использует более одного прохода - батчинг не работает. 
    6. На тему материалов: используем мобильные шейдера вместо обычных. Обычные шейдера используем только тогда, когда реально приспичит. Сегодня как раз столкнулись с веселой ситуацией: есть 50 совершенно одинаковых объектов(различаются только позицией и поворотом), они все помечены батчиться статически, при этом в игре с ними получается +50 dc. Начали искать, где проблема. Случайно решили вместо diffuse шейдера поставить mobile/diffuse - объекты забатчились. Вообще, юнитеки божатся, что мобильные шейдеры в десятки-сотни, а в некоторых случаях и тысячи раз быстрее десктопных.
    7. Потолок dc(буду судить только по ios, в android-е там совсем зоопарк):
      • iphone 3gs, ipod 4: 70 dc
      • iphone 4: 90 dc 
      • iphone 4s: 200 dc
      Тут еще несколько замечаний: 3gs парится больше относительно количества полигонов, для 4 айфона больше важно количество dc. 
    8. Оптимизировать количество материалов можно благодаря использованию текстурных атласов. Смысл очень прост: много картинок засовываем в одну большую, рендерерам говорим использовать определенные участки карты и видеокарта может отрисовывать это все в один проход. Я просто оставлю это здесь: http://www.digitalopus.ca/site/mesh-baker/, http://www.codeandweb.com/texturepacker.
    9. С атласами не все может быть так круто: четыре атласа 2048x2048, но загружаемые и разгружаемые в памяти лучше, чем один атлас 4096x4096. Пока хотя бы один материал использует  текстуру, видеокарта держит текстуру в памяти.
    10. Теперь придем к отрисовке текстур: используем сжатие на 146%. PVRTC для ios, ETC/DXT для android, mip-map-ы обязательно и тогда есть вероятность, что  видеокарточка не повесится, а скажет вам спасибо.
    11. 2D - это отдельная тема.
    12. Instantiate/Destroy объектов дорого не только для CPU, видеокарта тоже обязана загружать/освобождать ресурсы. Не заставляйте ее делать слишком часто, используйте  паттерн object pool.
    13. Ну и самая противная кнопка, подставленная на твой стул - это отрисовка где-нибудь в Update/FixedUpdate. На примере: вот так чуваки обновляют визуальный компонент подсчета очков:
      private void Update(){
        pointsMesh.text = Model.points.ToString();
      }
      
      Для далеких из мира Unity3d: Update вызывается каждый кадр! Вместо того, чтобы просто обновить один раз вьюшку, когда модель обновится, мы устраиваем маленький ddos мобильника. И вообще, нужно минимизировать количество Update/FixedUpdate и переходить к event-логике, а еще лучше к конечным автоматам.
    14. Да, совсем забыл про свет: везде, где возможно, заранее запекаем свет, предпосчитываем тени. Если тени не принципиальны(например, 2D графика), то убираем по возможности везде, где только возможно. Производительность увеличивается прямо на глазах!
    Про графику вроде все. Вспомню, напишу еще. На следующей неделе напишу некоторые советы по оптимизации скриптовой части.

    Вот что можно почитать у юнитеков на эту тему:

    понедельник, 25 марта 2013 г.

    Особенности национальной верстки или привет зоопарк устройств

    Часто приходится писать UI на NGUI с резиновой версткой, для того, чтобы приложение везде выглядело одинаково, еще чаще приходится обсуждать, как это сделать так, чтобы было меньше головной боли. И уже не в первый раз спрашивают требования к нарезке, а также описание процесса как это должно происходить, выкладываю в общий доступ стандарт, чтобы потом можно было просто на него ссылаться.

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

    • (дизайнерам) Забудьте о pixel-perfect верстке. Ее не будет. Но именно благодаря этому на всех экранах все будет выглядеть одинаково.
    • (программистам) Свыкнитесь, что будут грязные хаки. Это вначале все будет идти гладко да хорошо, а потом вдруг запустишь аpk на 320x240 и удивляешься, что все друг на друга наплыло.
    А теперь описание процесса, как мы достигаем вселенского счастья:
    • Есть UI (кнопки, слайдеры, менюшки), а есть бэкграунд(бэк). Ну так вот, бэк - это просто картинка(чаще всего однородная) на заднем фоне. Она нужна для того, чтобы по бокам не было черных фонов.
    • Мы делаем картинку бэка максимально большим и широким, а потом на каждом девайсе скейлим вниз пропорционально по высоте. К примеру, у нас бэк размером 2764 x1536.  На айфоне 5 отрисуется центральные 2726x640 пикселей. Таким образом, получается, что на самом деле наш бэк будет выходить за границы отрисовки. Скейл вверх нежелателен, но если есть ограничение по памяти, то тут никуда не денешься - никто не даст тебе рисовать картинки 4096x4096.
    • Бэк центрируется. Ваш кэп.
    • В нарезке UI сюрпризов нет, нарезаем по обычному.
    • При верстке UI абсолютно все располагаем относительно UIAnchor-ов. Каждый элемент экрана должен принадлежать к какой-то отдельной части. Да, на каждый чих индивидуального пространства создаем отдельный якорь. Типично используемых якорей хотя не так уж и много: центр, левый верхний угол, правый верхний угол, нижние углы по краям, верх и низ. Остальные используются редко.
    Да, еще одно ограничение: надо уложиться в 50МБ выходного файла. Это было интро, а теперь требования и настоятельные рекомендации:
    • (дизайнерам) Размеры бэкграундов: 1460x768(HD), 1090x460(SD).
    • (дизайнерам) Верстка под экраны: 1024x768(HD), 2048x1536(если совсем не жалко памяти). 
    • (дизайнерам) Формат картинок: png
    • (дизайнерам) По максимуму избегать альфы (особенность unity - чем больше площадь наложения альф, тем сильнее просаживается fps)
    • (дизайнерам) По максимуму избегать градиента (из-за проблем со скейлом)
    • (дизайнерам) По возможности, используем nine-patch везде, где только можно.
    • (дизайнерам) Шрифты в растровом формате. Более подробно: на ютубе
    • (программистам) UI элементы пакуем в одну текстуру с помощью NGUI, бэки оставляем как отдельные картинки(преимущество от запаковки бэков теряется при огромном размере атласа).
    • (программистам) Сжатие текстур: PVRTC для ios, ETC/DXT(1/5) для android.
    • (программистам) Атласы имеют размеры 2^N
    • (программистам) Все используемые картинки - квадратные.
    Может быть, что-то забыл. Что забыл, напишу позже.

    воскресенье, 24 марта 2013 г.

    Hack day?

    У меня нет никаких идей, чем можно позаниматься в следующие выходные. И я давно ничего не писал в гаражном стиле - сесть и быстренько накатать. Корочеееее, идея: на следующих выходных устроить себе нечто наподобие hack day. То есть к пятнице нужно придумать идею, а к вечеру воскресения уже реализовать прототип. Чего-нибудь простого, интересного, но уже работающего. Если будет запор идей и ничего в голову не придет, то буду разбирать старые завалы, начну переписывать халял гайд.

    Если вдруг у кого-нибудь есть идеи (в приоритете мобайл-дев, ios/android), напишите мне пожалуйста. Даже самая глупая идея имеет смысл. А вдруг кто тоже хочет поучаствовать - то тем более :)


    суббота, 23 марта 2013 г.

    Эффект молнии в Unity3d

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

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

    Вот примерно это у меня в конце и получилось: 


    Как решить этот вопрос?

    Нулевая мысль: надо загуглить. 

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

    Устал искать, решил взять заготовку и написать сам.

    Первая мысль: использовать партиклы. 

    Как? Непонятно. У кого есть идеи, велком.

    Вторая мысль: шейдером.

    Сделать в любом редакторе 3d прямую, а потом создавать ее, применив фрагментный шейдер, который будет:
    • отрисовывать ее не сразу, а по какому-то времени. Таким образом, мы добьемся эффекта, что молния "бьет", можно управлять скоростью.
    • рисовала не прямую, а ломаную в заданной амплитуде. По сути, это просто: амплитуда - это просто абсолютное значение координаты относительно Y, нужно делать кривое зеркало в 1D пространстве.
    • ну и цвет - простое умножение на цвет. 
    К слову сказать, первое место из той ссылки как раз и юзает такой шейдер, но меня он отпугнул своей неуклюжестью и тяжелой отрисовкой. 

    Уже решение: взять и тупо рисовать.

    И на мой взгляд, самое простое и гениальное решение: можно провести какой-нибудь "безбашенный" шатающийся отрезок из точки А в точку B, а в позициях, где он побывал, нарисовать пиксели.
    Более того, в Unity уже есть так называемый trail renderer, в нем есть и толщина линии, и материал с нужным мне цветом. По названию понятно, что это штука, которая рисует на позиции, где побывал гейм-объект. Юнитеки будто знали :) То есть вся задача заключалась в том, чтобы создать пустой трансформ в точке А и довести ее до точки В, в update иногда сбивая ей маршрут.

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




    понедельник, 18 марта 2013 г.

    Разговаривать на общем языке

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

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

    Теперь у меня новая проблема: мне надо объяснить моим коллегам, что когда нужна какая-либо функциональность, не нужно страдать синдромом not invented here, нужно взять готовую либу и, если нужно, добавить недостающую функциональность. И, блин, не унаследовав от базового класа, а используя композицию!

    Когда я вижу простыню из 900 строк, у меня возникает желание сделать вот так:


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

     и обязательно ищу в нем баг, чтобы получилось вот так:


    Вы не поверите, в 90% я его нахожу :)



    Честно, не надо придумывать заново колесо, не надо проявлять фантазию в именах переменных, пользуемся готовыми паттернами, реюзаем код. Проще говоря, для себя вывел следующие правила:
    • Используем единый стиль форматирования, желательно тот же самый, что используется внутри общества, в котором вы живете. Потому что если вы пишете иначе, то вам сложно понять код других программеров, а другие будут плеваться на ваш код. Это все равно как русскому туристу в Китае на рынке договориться о скидке.
    • Самое сложное - придумывать названия (переменным, классам). Это правда. Вот не ленимся переименовать кнопку c ButtonX на MessageButtonBehaviour, anotherTemp на что-либо более подходящее. Я уж молчу о переменных типа ttt, secondVar, var5 (я не шучу - сегодня видел в боевом проекте).
    •  Юзаем паттерны, а не изобретаем их! И не надо делегат называть адаптером(привет андроид!), не надо хвалиться, что придумал новое супер решение, не прочитав шестую страницу банды четырех. 
    • Не забываем, что паттерны паттернами, но принципы KISS и YAGNI игнорировать нельзя. Вообще, сначала применяем здравый смысл, а потом бегите за стереотипным решением.
    •  Изучаем алгоритмы и структуры данных не для того, чтоб победить на топкодере и получить новую футболку, а чтоб знать внутренности структуры данных и алгоритмов, уметь применять на практике и если нужно, написать самому. 
    • И да, если знаем алгоритм или структуру, то применяем готовое решение, а не пишем собственный мегабыстрый класс! Сегодня рыдал от строчки типа такого: public List<string> itemsStack = new List<string>();. Угадайте, что это за структура данных? Угадайте, как автор этого шедевра делал push, pop, top? И это было в файле объемом 900 строк!!!!!1111 Я рискнул рефакторить, но позже понял, что это было плохой идеей, потому что этот гребанный стек применялся еще где-то и передавался ref-ом в какой-то левый класс, который зачем-то использовал его по-своему как список(linkedList)!
    • И да, не боимся пользоваться плагинами, не ходим грудью колесом аля "я все умею!", все самое лучшее уже давно написано за нас. Мы жмотимся на плагин стоимостью 100 баксов, но сами в течение пяти дней готовы допиливать собственный говнокод, почти выполняющий функции того плагина.
    • Не забываем развиваться, изучать новые фичи своего инструмента. Поверьте, вопреки распространенному мнению, обновление - это фикс багов и добавление новых фичей, а не наоборот! И если в языке X появилась штука, которая позволяет в одну строчку написать то, что вы писали в сорок - то даже дурачок Боб, не сумеющий написать свое имя наоборот(с), поймет, что надо юзать эту штуку!
    Я очень рад, что в лкш ввели ручную проверку кода. Прикиньте, сколько бы еще можно было родить алгоритмических монстров, способных написать соптимизированную регекспу, но не способных разобраться в лапше из кучи классов. Надеюсь, они вряд ли столкнутся с теми проблемами, c которыми сталкиваюсь я.

    P.S. На проекте у нас два программиста: коллега и я. Сегодня произошел священный спор по поводу египетских скобок. Я сторонник египетских скобок, аргументировал так:

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








    пятница, 15 марта 2013 г.

    to-do or not to-do

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

    Встретились с руководителем, обсудили, договорились о перспективах и согласились уточнить детали позже. Приблизительное время разработки 2-3 месяца, первая итерация: 3 недели. В течение первых нескольки итераций(это примерно полтора месяца) я буду вплотную работать с дизайнером (а может быть и дизайнерами). Во время обсуждения произошел следующий диалог:
     Я:- Как у вас там с дизайнерами? Они у вас насколько гордые?
     Руководитель: - Не, не гордые. Ты с ними легко найдешь общий язык.

    Вот тут-то мы и встали. Дизайнер оказался очень гордым. Самым гордым из всех дизайнеров, с кем мне приходилось сталкиваться. Даже мем "упоротый" покажется уменьшительно-ласкательным. С другими дизайнерами, с которыми мне приходилось иметь дело, мы всегда находили общий язык. ВСЕГДА. И это происходило, в первую очередь, потому что мы оба понимали, что делаем ОБЩУЮ работу и стремимся к ОДНОЙ цели, находимся на ОДНОЙ стороне баррикад и не пытаемся вставить палки друг друг другу в колеса.

    А тут почему-то не получилось. Мы даже не начали делать проект, не написали ни одной строчки кода, не нарезали ни один макет, на этапе обсуждения стандарта формата нарезки мы успели послать друг друга далеко и надолго. Он вдоволь пообливал меня грязью, ясно дав мне понять, что я гавно, и со мной он работать не будет. На какой-то момент мне показалось, что это не я живу в Москве, а он, а он явно не представитель культурной столицы России. Ну в общем дизайнер отказался со мной держать связь, а я отказался от работы, потому что работа с дизайнером через посредника - это все равно, что водителю пытаться водить машину с закрытыми глазами и подсказками жены, сидящей на заднем сиденье.

    Договор подписать не успели. Если бы успели, это расценивалось как  мой отказ выполнять договор и я должен был вернуть средства, которые бы мне обязаны были заплатить за мою работу. Сейчас он лежит у меня на столе. Никакого пункта с намеком о том, что работа не может быть закончена по вине заказчика (ведь именно он должен отдавать арт и другие информационные средства) нет.

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

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






    среда, 13 марта 2013 г.

    Всем PM-ам посвящается


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

    воскресенье, 10 марта 2013 г.

    Пользователь! Ты скотина!

    Не, я серьезно! Пользователь, пользующийся АБСОЛЮТНО БЕСПЛАТНЫМ приложением и возникающий по поводу того, что что-то работает не так, как он хочет - ты последняя пользовательская СКОТИНА!

    Как пример: Вот поищите в app store  приложение Ted - ну просто отличнейшее приложение для просмотра огромного количества видеолекций на самые разные темы, смотри -не хочу. Лучше приложения я вообще не видел. И маркетологи решили выпустить его на российском рынке.

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

    А теперь к тебе, пользователь! Запомни несколько моментов:

    • Разработчики тебе ничем не обязаны, тем более если ты пользуешься полностью бесплатным приложением! И вообще нет разницы, это айфон пятый или андроид за 3000 рублей. Не устраивает - проходи мимо. 
    • Да, если у тебя слабый телефон - не жалуйся, что приложение тормозит или вообще не поддерживается у тебя; ты сам выбрал категорию hard life, когда покупал девайс. 
    • Если у тебя есть конструктивная критика - обратись к разработчикам (по мылу, сайту) и предложи, как можно сделать лучше. Поверь, разработчики ценят это и прислушиваются  к новым идеям! Даже врачу жалуются не просто так - он может предложить решение вашей болезни. 
    • Если ты рассказал свои идеи - не надейся, что завтра же твои хотелки будут реализованы - во-первых, программист тебе не обязан, а во-вторых, возможно, это трудно реализуемо или вообще отложено на другой срок. 
    Вообще считаю, что пользователя, который поставил одну звезду приложению, нужно кастрировать. Вот сидишь в свободное время, делаешь не игру какую-нибудь, а реально полезные тулзы, чтобы жизнь стала хоть немного лучше, а тебе приходит фидбек, что ты все делаешь зря, мне синенький цвет не нравится и вообще ничего не работает(при этом совершенно не понятно, что же там за проблема). В такие моменты руки опускаются - хотел только помочь, а получилось наоборот. Да ну нафиг, сделаю лучше под заказ еще один angry birds - и приятнее, и деньги платят.