Kotlin и стоимость разработки игры (+ немного оффтопика)

в 17:03, , рубрики: kotlin, rxjava, разработка игр, Разработка под android, я пиарюсь

Добрый день. Сегодня я хочу рассказать о разработке игры с использованием языка программирования Kotlin. Также приведу небольшой пример работы с RxJava в конце статьи.

Первый commit для этой игры случился 4 июня сего года, то есть до открытой беты я дошёл примерно за 3 с половиной 4 недели. Нельзя сказать что это первая игра или программа под Android которую я разрабатываю, но и опытным разработчикам под Андроид я также не являюсь. Мой предыдущий опыт в основном составляла Enterprise разработка.

Я хочу обозначить несколько тем в этой статье и пробежаться по ним коротенечко. Тему Kotlin’a постараюсь раскрыть подробно, по остальным возможны дополнительные статьи если будет такой запрос от вас (ну и плюс уточняющие вопросы в комментариях помогут улучшить подачу материала). Итак к списку: это сравнение стоимости разработка на Kotlin vs Java, где брать графику для вашей игры. Немного про деньги (пока про затраты, т.к. статистики по доходам пока нет). Также я считаю очень важно коснуться мотивировочной части. Начнем пожалуй с конца.

Мотивация

Почему я начинаю эту статью с такой казалось бы мелочи как мотивация? Ведь если я хочу создать приложение, значит я уже мотивирован. И всё хорошо, и меня ждет успех. Проблема в том что часто приступая к работе мы не осознаем свою истинную мотивацию: ждем ли мы денег, есть ли личная потребность, хотим ли мы создать пример для своего портфолио. Вариантов может быть много. Казалось бы, какая разница если я все равно мотивирован? Но взять к примеру портфолио — вроде бы хорошая инвестиция. Ты сможешь показывать своим потенциальным работодателям готовый продукт и они конечно же впечатлятся и тут же назначат огромную зарплату. Так вот, по мере разработки, по мере того как вы вкладываете свои силы и время, ценность портфолио начинает отходить на второй план (особенно если визуальной привлекательности добиться не удается). Уже хочется получить каких-то денег и если изначально планов монетизации приложения не было, то каждый следующий день требует все большей силы воли. Так в чём же проблема? Казалось бы — начинал работать с одной мотивацией, а в середине процесса разработки добавил монетизацию, выложил в Google Play и заработал 100500 руб. К сожалению так не работает. Пользователи не хотят тратить время на глючное, сырое или неинтересное приложение. То есть вы не можете выложить проект в середине разработки. Если вы так сделаете, скорее всего это просто приведет к падению рейтинга и отсутствию скачивания. То есть вся ваша работа по сути уйдёт в мусорную корзину.

Если вы изначально рассматриваете монетизацию как один из своих интересов, еще до самого старта работ вы должны понимать на чём вы будете зарабатывать деньги. Самое простое и самое неэффективное это рекламные баннеры. Чуть сложнее или примерно так же просто — показ рекламы на весь экран. Далее идет магазин. Но для того чтобы магазин заработал, вам необходимо зацепить пользователя чем-то. Чем раньше вы получите просмотр рекламы, тем больше это будет подпитывать вашу мотивацию продолжать работать дальше.

Графика

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

Существует значительное количество стоков ресурсов где графика продается, существуют ресурсы с бесплатными картинками. И в принципе да, действительно, так оно и есть. Есть только одна проблема — эти ресурсы выполнены в разных художественном стиле и комбинация из них даёт крайне странные результаты. Поэтому если вы разрабатываете свою первую игру, мой вам настоятельный совет — не ищите графику под свои задумки. Ищите готовые наборы графики под игру, и исходя из графики решайте что вы можете реализовать. Такие наборы есть, их не так чтобы много, но по крайней мере для первого второго раза вам хватит.

В моем же случае получилось что я потратил 2 недели на разработку, купил какие-то ресурсы в маркетах, но естественно их не хватило для того чтобы реализовать мою задумку. Скажу честно — купленная графика составляла менее чем 30% от необходимого. Но ведь нет нерешаемых вопросов, можно найти исполнителей которые нарисуют то, что вам нужно. И здесь мы с удивлением узнаём, что это не так просто. Особенно если нас интересует качественный результат. Что делал я — я просматривал стоки, находил интересные в плане графики экземпляры, переходил на описание автора и пробовал связаться.

Уже на этом этапе у вас должен быть перечень элементов которые вы хотите заказать и уже на этом этапе скорее всего ваши розовые слоники приопустят хоботы. Но пока это только предчувствие. Вы отправляете письмо с вопросом “можно ли обратиться для заказа графики” и в зависимости от профессионализма исполнителя (а те люди с которыми я обращался были профессионалами за исключением одного) вы получите сразу ответ что “Да можно, часовая ставка 25$/час”, ваш запрос будет выполнен за 30-40 часов не включая доработки по замечаниям (свободное время для заказа будет через полтора месяца). Или “Могу сделать за месяц-полтора, будет стоить 50 тысяч рублей”. Либо “Нет, у меня я слишком много работы и я дополнительные заказ не беру”. Это всё профессиональный варианты ответа. А самое неудачное что со мной случилось это следующее:

— А сколько вы готовы заплатить? (То есть человек не оценивает ресурсоемкость или сложность поставленной задачи, а пытается адаптироваться под платежеспособность заказчика)
— от 10000 до 20000 руб (Это был первый художник с которым я связался и в тот момент я считал что данная сумма Вполне себе подходит для небольшой, как я считал, работы)
Человек большого интереса не проявил, на вроде как согласился. Сам назначил срок когда даст описание что он за эту сумму может мне предоставить… и пропал. Это даже не звоночек это колокол что дальнейшая работа с подобным человеком для вас закончится неудачей. Тем не менее через 3 дня после озвученного им же срока, он связался со мной и сказал что сделает всё что мне надо за 15000 рублей. Я очень настороженно отношусь к обобщениям, поэтому попросил перечислить что же будет входить в это “всё” по пунктам. После чего человек пропадает с концами и я считаю что это счастливый конец истории.

Kotlin

Разработка личного проекта существенно отличается от рабочего. Различий несколько, но я остановлюсь на самом (для меня) существенном. Это отношение к стоимости разработки. В подавляющем большинстве случаев работодатель определяет требования, часто инструмент. Он же несет финансовую ответственность. Есть тестирование или нет, какие библиотеки и какой стек технологий. В личном проекте все права и все обязанности — на вас лично. Хотите использовать новый фреймворк — ради бога. Но время которое вы потратите на его освоение вы будете “оплачивать” из своего кармана. И да, не стоит думать что вы работаете бесплатно. Выкиньте из головы эту мысль, напишите на стикере вашу ставку в час и крайне желательно чтобы она соответствовала хотя бы средней стоимости специалиста вашей квалификации по городу. Не надо занижать себе цену.

Таким образом вопрос эффективности вашей работы встает на первый план. А сделал ли я что-то полезное за эти 8 часов, за что в ином случае я получил бы (к примеру) 4000 рублей? То есть вопрос Kotlin vs Java я предлагаю решать исключительно с финансовой точки зрения. Оба языка тьюринг-полные и значит любую программу написанную на Kotlin можно реализовать на Java и наоборот. За счет чего же мы можем получить разницу в стоимости разработки/стоимости владения продуктом?

Стоимость разработки — это количество денег для реализации функциональности. Стоимость владения = стоимость разработки + стоимость поддержки. В некоторых случая стоимость поддержки несоизмеримо выше стоимости разработки. Это особенно характерно для write-only языков (пример RegExp. Гораздо проще написать новое выражение чем понять где ошибка в существующем).

В идеале язык должен быть дешевым и в разработке, и в поддержке. Сокращение boilerplate code однозначно удешевляет разработку. Синтаксический сахар удешевляет разработку, но может (подчеркну может, но не обязан) приводить к увеличению стоимости владения. Синтаксическая соль удорожает разработку но удешевляет стоимость владения. Ниже я приведу примеры кода на Kotlin и Java и опишу какой вариант на мой взгляд дешевле и почему. Часть примеров будет из моего проекта, часть нет.

class Car(val id: String) {
    var speed: Double = 0.0
}

public class Car() {

    public final String id;
    public Double speed;

    public Car(String id) {
        this.id = id;
        this.speed = 0.0;
    }
}

* Для DTO классов нет необходимости в геттерах/сеттерах
** Геттеры/сеттеры имеют смысл только и только в том случае, если они меняют поведение при работе с полями

В данном сравнении мы видим что код на Kotlin более читаемый и самое главное он защищен от “ошибки на миллиард” — NPE. И id, и speed в Kotlin не могут быть null.

var car: Car = Car(null) // compile error
car.speed = null // compile error

Второй, не менее важный момент в приведенном выше примере — это мутабельность.

fun toRoman(value: Int): String {
   val singles = arrayOf("", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX")
   val tens = arrayOf("", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC")
   val hundreds = arrayOf("", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM")
   val thousands = arrayOf("", "M", "MM", "MMM")

   val roman = thousands[value / 1000] + hundreds[value % 1000 / 100] + tens[value % 100 / 10] + singles[value % 10]

   return roman
}

public String toRoman(int value) {
   final String[] singles = new String[] { "", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX" };
   final String[] tens = new String[] { "", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC" };
   final String[] hundreds = new String[] { "", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM" };
   final String[] thousands = new String[] { "", "M", "MM", "MMM" };

   final String roman = thousands[value / 1000] + hundreds[value % 1000 / 100] + tens[value % 100 / 10] + singles[value % 10];
   return roman;
}

Не так много разницы, не правда ли? Тем не менее модификаторы final смотрятся крайне неуместно и визуально засоряют программу. Это приводит к тому, что final используется в Java гораздо реже чем следовало бы. А ведь иммьютбл гораздо дешевле и в разработке и во владении.

А вот пример трансформации данных:

val data:List<Int> = ArrayList()
val sum = data.filter { it > 0 }.sum()

List<Integer> data = new ArrayList<>();
final Integer[] sum = { 0 }; //Variable used in lambda expression should be final or effectively final
data.stream().filter(value -> value > 0).forEach(value -> sum[0] += value);

Тут два момента. Kotlin выполняется на jvm 6, Java требует jvm 8 для стримов. А для андроида, это на минуточку 24 API. На 5 июня это всего 9,5% устройств. И второй момент — final or effectively final переменные в лямбдах Java.


Инициализация объекта
private val buttonGroup = ButtonGroup<Button>().apply {
   setMinCheckCount(0)
}

private ButtonGroup<Button> buttonGroup = new ButtonGroup<>();
…
public constructor(...) {
    …
    buttonGroup.setMinCheckCount(0);
    …
}

Инициализация объекта в Kotlin’e возможна в одном месте что сокращает контекст для программиста и снижает стоимость вложения

В принципе, из того что я попробовал в Kotlin это самые серьезные вещи, влияющие на стоимость владения продуктом. Я сейчас не хочу говорить про вкусовщину типа data classes, перегрузку операторов, string template, lazy properties и т.п. Все это вещи интересные но они могут как сокращать, так и увеличивать стоимость владения.

В заключение небольшой пример Kotlin + RxJava + Jackson. Я хочу иметь dto класс, который позволит не просто хранить данные, но и уведомлять об их изменениях. Пример упрощенный для более наглядной демонстрации.

interface Property<T> {
   var value: T
   val rx: Observable<T>
}

open class BaseProperty<T>(defaultValue: T) : Property<T> {

   override var value: T = defaultValue
       set(value) {
           field = value
           _rx.onNext(field)
       }

   private val _rx = PublishSubject.create<T>()
   override val rx: Observable<T> = _rx
       get() {
           return field.startWith(value)
       }
}

Здесь хочу обратить внимание на перегрузку val rx. При подписке на Observable сразу же приходит текущее значение. Это важно т.к. десериализация из json'a случается раньше чем верстка экрана и подвязывание графических элементов к свойству. А при startWith мы сразу инициализируем графический элемент текущим значением и меняем по ходу пьесы.

class Car {
   private val _speed = BaseProperty(0.0)

   var speed: Double
       get() = _speed.value
       set(value) {
           _speed.value = value
       }
  
   @JsonIgnore
   val rxSpeed: Observable<Double> = _speed.rx
}

class Police {
   val cars: List<Car> = listOf(Car("1st"), Car("2nd"), Car("3rd"))

   init {
       cars.forEach {
           car -> car.rxSpeed
                   .map { speed -> speed > 60 } // преобразует double скорость в boolean скоростьПревышена
                   .distinctUntilChanged()
                   .filter { aboveLimit -> aboveLimit == true }
                   .subscribe { writeTicket(car) }
       }
   }

   private fun writeTicket(car: Car) {
       // do some stuff
   }
}

Класс Car прекрасно сериализуется/десериализуется Jackson'ом, может быть использован как классический dto класс, но в то же время позволяет обрабатывать изменения свойств в реактив стиле.

Ниже пример подвязки Label к свойству объекта:

Label("", assets.skin, "progress-bar-time-indicator").apply {
                        setAlignment(Align.center)
                        craft.rx(DURATION).subscribe {
                            setText(TimeFormat.format(it))
                        }
                    })

Заключение:

К сожалению я не могу представить объективных цифр насколько стоимость владения продуктом на Kotlin'e дешевле Java. Да и само это утверждения я уверен будет не раз оспорено в комментариях. Могу сказать только по своему субъективному суждению, цифра в 1.5 — 2 раза для меня реальна. Причем сокращение стоимости владения в полтора раза характерно для начала перехода с Java на Kotlin, примерно через неделю-две я думаю на двойную эффективность вышел. В основном за счет NPE-proof, immutable, lambda & функции высшего порядка.

Автор: TerraV

Источник

Поделиться

* - обязательные к заполнению поля