суббота, 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.

вторник, 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-шник приложения, у которого ты хочешь обновить статус проверки/ожидания проверки), то начинают проверять сначала не то приложение, которое ты упомянул в письме. Я думал, это одноразовый баг, но такое поведение повторялось у меня несколько раз! И до сих пор в ожидании ревью висит приложение, на которое я отправил запрос проверить в первую очередь, хотя остальные давно проверены.