Прошлый месяц по ссылке
С игр 2489$ падение от месяца к месяцу. Вот что бывает, когда делаешь мало релизов.
Игра про остров принесла 261$ итого помесячный доход выглядит так
январь 3
февраль 9
март 35
апрель 162
май 169
июнь 200
июль 520
август 515
сентябрь 620
октябрь 630
ноябрь 317
декабрь 261
3441$
Результат получился средним. С одной стороны она продолжит приносить какие-то деньги и дальше, с другой на нее было потрачено примерно три месяца активного труда и в пересчете на помесячный доход она дала мало денег. Впрочем игра про рыцаря все равно выглядит еще более провальной. Потрачены деньги, куча времени а прибыль в декабре только 386$
Поэтому сосредоточусь на своем потенциальном хите, игре про хомячков.
Продуктивность
Рабочих 82 часа. Что для меня больше обычного. Все же освоение чего-то нового может хорошо увлечь. При этом я еще и очень много играл сразу в несколько игр. Основное время заняла oxygen not included, игра по жанру очень близкая к хомякам.
Unity
Продолжаю осваивать юнити. Допиливать свои классы и переделывать свою среду.
1) Нашел самый быстрый способ вывода графики. Благодаря шейдерам и полигональной нарезке он реально быстрый. ИМХО быстрее возможно только в кокосе получить.
2) Написал свою функцию сортировки объектов
public float recalculate_by_siblingindex(Transform tr) { float z_temp = 0; foreach (Transform child in tr) { var pos = child.localPosition; pos.z = -(z_temp); child.localPosition = pos; z_temp += recalculate_by_siblingindex(child); z_temp += 1f; } return z_temp; }
Он выстраивает иерархию через z индекс в зависимости от иерархии вложений.
Почему мне пришлось ее делать? Потому, что стандартный компонент SortingGroup не сортирует коллайдеры.
Зато теперь я могу использовать привычный 2D механизм очередности, как это было в флеше, и как это в общем-то и должно быть в 2D играх.
3) Написал (перенес свои же наработки с флеша) целый механизм следящий за перекрытием картинок и скрывающий те что перекрыты. И соответственно активирующий их когда перекрытие исчезает. Да код не идеальный. Перекрытие считается тупо по прямоугольнику и поэтому например если две картинки перекрывают другую вдвоем, но ни одна не перекрывает полностью, то нижняя картинка будет обсчитываться, как не перекрытая.
Но все это не критично, потому, что перекрытия нужны в первую очередь когда одно окно перекрывает другое. А перекрывающее окно само по себе как раз и представляет большой прямоугольник.
4) Много возился с масками. Понял, что для масок нужны другие шейдеры (не самые быстрые) не критично, хотя и не круто.
5) Вставил анимацию из спайна. При чем сделал это через код, что было не так-то просто.
ИСПРАВИЛ ОШИБКУ в официальном runtime коде для спайна, который не давал мне использовать самые быстрые шейдеры. (Самые быстрые шейдеры рисуются только с одной стороны. Сторона рисовки определяется порядком вершин треугольника, таким образом, чтобы они шли по часовой. В самом спайне есть возможность повернуть объект. После чего при сохранении того же порядка вершин треугольников, треугольник встает к нам обратной, невидимой, стороной.)
Так вот в файле https://github.com/EsotericSoftware/spine-runtimes/blob/3.6/spine-unity/Assets/spine-unity/Mesh%20Generation/SpineMesh.cs есть кусок
var tris = currentSubmeshBuffer.Items; int triangleIndex = 0; var skeleton = submeshInstruction.skeleton; var skeletonDrawOrderItems = skeleton.drawOrder.Items; for (int a = submeshInstruction.startSlot, endSlot = submeshInstruction.endSlot; a<endSlot; a++) { var attachment = skeletonDrawOrderItems[a].attachment; if (attachment is RegionAttachment) { tris[triangleIndex] = attachmentFirstVertex; tris[triangleIndex + 1] = attachmentFirstVertex + 2; tris[triangleIndex + 2] = attachmentFirstVertex + 1; tris[triangleIndex + 3] = attachmentFirstVertex + 2; tris[triangleIndex + 4] = attachmentFirstVertex + 3; tris[triangleIndex + 5] = attachmentFirstVertex + 1; triangleIndex += 6; attachmentFirstVertex += 4; continue; } var meshAttachment = attachment as MeshAttachment; if (meshAttachment != null) { int[] attachmentTriangles = meshAttachment.triangles; for (int ii = 0, nn = attachmentTriangles.Length; ii<nn; ii++, triangleIndex++) tris[triangleIndex] = attachmentFirstVertex + attachmentTriangles[ii]; attachmentFirstVertex += meshAttachment.worldVerticesLength >> 1; // length/2; }
Разработчики просто перебирают слоты, и рисуют два треугольника на базе прямоугольного куска текстуры. (Этим они кстати игнорируют всю фишку полигональной нарезки, которая меня наоборот так радует, потому, что позволяет ускорить графику. И возможно я даже заморочусь и перепишу и этот кусок)
Так, вот они просто игнорируют поворот и создают повернутый полигон.
Чтож, я просто проверяю слот на разворот и меняю порядок вершин треугольников!
var tris = currentSubmeshBuffer.Items; int triangleIndex = 0; var skeleton = submeshInstruction.skeleton; var skeletonDrawOrderItems = skeleton.drawOrder.Items; for (int a = submeshInstruction.startSlot, endSlot = submeshInstruction.endSlot; a < endSlot; a++) { var attachment = skeletonDrawOrderItems[a].attachment; if (attachment is RegionAttachment) { if (submeshInstruction.skeleton.slots.Items[a].bone.ascaleX < 0) //Моя правка, чтобы треугольники смотрели в камеру. { tris[triangleIndex] = attachmentFirstVertex+1; tris[triangleIndex + 1] = attachmentFirstVertex + 2; tris[triangleIndex + 2] = attachmentFirstVertex + 0; tris[triangleIndex + 3] = attachmentFirstVertex + 1; tris[triangleIndex + 4] = attachmentFirstVertex + 3; tris[triangleIndex + 5] = attachmentFirstVertex + 2; } else { tris[triangleIndex] = attachmentFirstVertex; tris[triangleIndex + 1] = attachmentFirstVertex + 2; tris[triangleIndex + 2] = attachmentFirstVertex + 1; tris[triangleIndex + 3] = attachmentFirstVertex + 2; tris[triangleIndex + 4] = attachmentFirstVertex + 3; tris[triangleIndex + 5] = attachmentFirstVertex + 1; } triangleIndex += 6; attachmentFirstVertex += 4; continue; } var meshAttachment = attachment as MeshAttachment; if (meshAttachment != null) { int[] attachmentTriangles = meshAttachment.triangles; for (int ii = 0, nn = attachmentTriangles.Length; ii < nn; ii++, triangleIndex++) tris[triangleIndex] = attachmentFirstVertex + attachmentTriangles[ii]; attachmentFirstVertex += meshAttachment.worldVerticesLength >> 1; // length/2; } }
Конечно тут учтена только ось X (сделать игрек вообще не проблема по аналогии) и это работа связанная с поворотом кости, а там есть еще поворот самой текстуры (думаю чуток поразбираться и можно сделать, место где задаются вершины теперь известно).
Но свою проблему я решил.
6) Еще в юнити есть удобная возможность добавить все графичесские атлассы в одну папку (папка при этом должна быть в папке Resources) и потом просто вызвать код
public static Sprite[] sprites; ... ... sprites = Resources.LoadAll<Sprite>("Atlases/");
И все у нас массив всех спрайтов, бери и юзай. При чем после первого вызова это будет доступно из любого места, не зря ж там static
Правда обращение по имени приходится делать через перебор всего массива и сравнение имен с искомым. Но в нормальном проекте обращения к спрайтам не так уж и часто делаются, так, что совершенно некритично.
На самом деле это одна из самых клевых вещей.
7) И в итоге я так и не понял до сих пор на кого рассчитан юнити на дурачков или на профессионалов. С одной стороны там есть много достаточно сложных вещей.
С другой я несколько недель парился с тем, что юнити от балды подставляет позиции и масштабирование, пока не узнал что, оказывается в функции
transform.SetParent(parent) есть второй параметр, который при вызове функции ломает напрочь все позиционирование и масштабирование таким образом, чтобы родитель не оказывал влияние. То есть если у родителя масштабирование 0.5 и мы добавялем объект, то объекту выставялется масштабирование 2, чтобы на выходе получилось 1, которое было до добавления. Так же и с координатами.
И по умолчанию он ВКЛЮЧЕН. Блин, ну если я назначаю родителя, то очевидно не просто так же, а чтобы через родителя в том числе влиять на потомков. А юнити зачем-то ломает эту базовую логику.
В итоге мой кодовый фреймворк-надстройка занимает уже около 100 кб кода. (Спайн и прочие сторонние асеты я конечно не считаю), несколько материалов, пачку разных папок. При всем при этом в редакторе у меня только один объект — камера. И к ней прикреплен только один скрипт, который уже тянет все остальное и в итоге, во время исполнения те же хомяки выглядят вот так в 3D режиме
(Для снимка сделал все объекты активными)
И да для полупрозрачного черного прямоугольника ввел дополнительные правила так, чтобы он отображался только один раз на самом верхнем экране, где он нужен. Иначе слишком дорого по производительности. Снимок еще до этого сделан.
Сам перенос хомяков закончен на 90%. Кодовая база в юнити уже 675кб.
Хотел закончить к новому году, но все время то там то там вылазят расхождения старого и нового кода и приходится или переписывать под новый, или создавать реализацию старых механизмов.
Так же решил повысить ставки, и сделать хомяков мультиплеерными.
В целом по юнити было еще много всякого. Все же 80 часов было потрачено.
Акции
Основной счет -1%.
Второстепенный счет -7.5%
Тиньков +1,2%
Крипта -3,7%
По итогам всего 2017 года
Чистое изменение на конец года от всех вложенных денег.
Основной счет -6,9% (Большую часть из которых я потерял вот так)
Второстепенный счет -21,5% (которые он получил еще когда был под ДУ)
Тиньков +3,4%
Тут чистое изменение по итогам года не получается посчитать, потому, что не знаю сколько было на счету в начале года. Поэтому просто перемножу проценты помесячно.
Крипта +67,4%
___
UPD
Правда в итоге я еще раз переписал код спайна, потому, что если представить две кости, которые вложены друг в друга, то если родительская кость повернута, то повернута будет и вложенная кость, но через код указанный выше этого не узнать. В итоге я ввел дополнительный параметр всем костям и считают их фактичесское состояние. В камеру они сморят, или от нее и тем кто смотрит от камеры перестраиваю треугольники.
Автор: Elsper.ru