Меню

Сергей Драган

О программировании

DevGAMM Hamburg 2015 — впечатления

Devgamm

В первую очередь, большое спасибо всей команде Девгамма за эти два отличных и насыщенных дня. Конференция была прекрасно организована, я не заметил ни единой заминки или технической проблемы. Всё проходило гладко и по плану. Лерика порхала, бодрая и улыбчивая — даже под самый вечер, когда, казалось бы, самое время падать от усталости. Откуда в ней столько сил? Целый склад клонов, не иначе.

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

Было приятно увидеть и пообнимать знакомых, и наконец-то лично познакомиться с теми кого раньше знал только по переписке.

А вот доска вакансий неприятно удивила. Флеш-вакансий то ли не было вообще, то ли не более двух-трёх. Юнити и HTML5 захватили позиции флеша в предложениях работы. Похоже, оставаясь (наёмным) флешером, можно оказаться динозавром, владеющим устаревшей и неактуальной более технологией. Читающие меня коллеги, заинтересованные в наёмной работе, что вы думаете об этом? На какую технологию целитесь?

Доклады

Больше всего мне понравился коротенький «Make It Wiggle«. На серии простеньких демок Ditto взял стрёмный арт, словно нарисованный слепым ДЦПшником в чём-то навроде MS Paint (а то и, ещё хуже, в каком-нибудь Gimp), и, добавляя к нему очень простые эффекты, требующие очень простого программирования (и совсем никакого рисования), сделал из него вполне себе самобытную однородную игрушку. Это было чем-то похоже на старый доклад «Juice It Or Lose It» https://www.youtube.com/watch?v=Fy0aCDmgnxg — очень, к слову, рекомендую.

Из доклада Майка Роуза «How Well Do Games Sell in 2015» я вынес для себя: «Кругом полно народу, даже не суйтесь на мобильные, пробуйте незаезженные платформы навроде консолей».

«One Source To Rule Them All» — каким-то образом Андре Вайссфлог умудрился рассказывать настолько скучно, что после доклада я не был уверен, сколько прошло дней и какая сейчас пора года. Однако то, что он рассказывал, было увлекательно, информативно и вдохновляюще. Действительно, компилятор языка C — это первое, что появляется для любой платформы. На чистом C можно написать приложение под абсолютно любое устройство, любую ОС. Всё, учу С 🙂

«Indie Developers Survival Guide» — Флазм провёл приятный круглый стол, но что можно обсудить с четырьмя разными людьми, имея в запасе меньше часа? Потому вышло не очень, как по мне, информативно. «Как вы думаете, сложно ли быть инди?» — «Да, инди быть сложно». «Как вы считаете, делать хорошие игры важно?» — «Да, мы считаем, что важно делать хорошие игры». Подождём индивидуальных подкастов от Флазма с каждым.

«Creating Amazing Art in The Witcher 3:Wild Hunt» — посмотрев, лишь укрепился в мысли: «Нужно собрать мощный компьютер, попрощаться с женой на месяц и купить третьего Ведьмака».

Игры

В перерывах между докладами я урывками поиграл/посмотрел несколько игр:

Skara на церемонии награждения взяла Grand Prize. Улыбчивый парень по имени Цезарь, показывавший игру, сперва уточнил, играл ли я в Dark Souls, объяснив, что эта игра — про мультиплеерные схватки в стиле Dark Souls. «В маркетинге использовать название той игры нельзя, потому мы просто говорим, что у нашей есть душа — и все всё понимают».
Кстати, они раздают приглашения в бета-версию в обмен на фидбек в стиме: http://skarathebladeremains.com/

Skara screenshot

Verge или «Симулятор ярких жизненных перспектив в Бердянске». Я старательно искал, но так и не нашёл ни апельсинов, ни гвоздей, ни — на крайний случай — стреляющих регдоллами пушек.

Verge screenshot

Goliath — hack-and-slash со сбором ресурсов, крафтом деревянных роботов, которыми можно управлять, и с шейдерами «как в Borderlands». На презентации выглядело симпатично, но совсем пусто, мало контента — сомневаюсь, что игра будет, согласно их продюсеру, готова к концу зимы. Но, когда выйдет, скорее всего куплю.

Goliath screenshot

Стенд с Party Hard и Speedrunners был постоянно оккупирован игроками. На второй день конференции в соседнем зале был какой-то банкет, и нарядные немецкие детишки, которых привели с собой родители, постоянно прибегали к стенду TinyBuild и увлеченно сидели с геймпадами. Придя домой, сразу купил Party Hard, и мешаю соседям по ночам спать саундтреком из игры.

Party Hard screenshot

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

Message Quest screenshot

Какая-то похожая на XCom игра, название которой я не запомнил. Выглядела здорово, а схожесть интерфейса с икскомовским сразу привлекла внимание (а вот разработчик зачем-то стал сразу из-за этого оправдываться — хотя, по-моему, это ведь здорово: сразу чувствуешь себя «как дома» и знаешь, на что нужно жать).

Heliborne! Ооо, к этой игре я питаю особую симпатию. Люблю вертолёты (и пылесосы, но игр о пылесосах вроде бы нет), и потому «танкам про вертолёты» очень радуюсь. Игра выжимает из «убогого юнити» красивую мыльную картинку, рисует симпатичную водичку, солнечные блики, стрельба, взрывы, пыщ-пыщ, солдатики бегают, всё такое. Онлайн пока совсем маленький, но, надеюсь, после релиза будет с кем поиграть.

Heliborne screenshot

А какие у вас остались впечатления? Давайте делиться ими. Что запомнилось? Что понравилось, а что — нет?

Советы приезжающим на DevGamm 2015

В этот раз DevGamm проводится в Гамбурге, и, основываясь на своём (не слишком богатом, впрочем) опыте жизни в этом городе, хочу поделиться несколькими заметками и советами.

Как прилететь

Не скажу относительно России, а вот для Украины самый недорогой и удобный способ добраться сюда — лоукосты Wizzair из Киева. Правда, рейсы у них в сентябре будут неудобные: в понедельник и пятницу. Выходит, нужно прилететь в понедельник 7 сентября, а обратно — либо в следующий понедельник, либо пропустить второй день конференции и улететь в пятницу, 11 сентября.

А вообще: http://skyscanner.com — здесь удобно искать лоукосты.

Прилетите вы либо в аэропорт города Любек (Lübeck), либо в Hamburg International Airport. От первого ещё нужно добраться до Гамбурга (~70 км); второй находится в самом Гамбурге.

В первом случае — не берите такси до Гамбурга! Эти 70 км дороги обойдутся в добрых 110 евро. Садитесь на автобус, который почти всегда уже стоит у здания аэропорта — билет обойдётся в 12 евро, а автобус будет ехать без остановок прямиком до Hamburg ZOB — центрального автовокзала в трех минутах ходьбы от Hauptbahnhof Sud (см.ниже), где можно сесть на абсолютно любой поезд метро. В крайнем случае, от автовокзала можно взять такси, и по городу это обойдётся до 15-20 евро. (далее…)

Как я HTML5-игру с OpenFL делал (и наплодил больше багов, чем там было самой игры)

В этом посте я собрал список проблем, с которыми столкнулся при разработке простой HTML5-игры на связке Haxe-OpenFL-Box2D. И самому себе на будущее, и кому-нибудь, может, тоже пригодится.

В первую очередь дисклеймер: мои руки растут из жопы, я плохо разбираюсь в OpenFL и Haxe в целом. Я понимаю, что это опенсорс и «если что-то не устраивает — возьми и почини сам». Также я очень благодарен всему русскому сообществу Haxe за неоднократные консультации и помощь!

На киевском DevGamm 2013 я хотел штурмовать Speed Game Dating, и для этого на пару с художником мы сделали за две недели Cake Break — Box2D-физпаззл на флеше. Времени было немного, поучаствовать хотелось, а игры такого плана делаются как раз быстро.

Спонсоры, глядя на игру, с равнодушным лицом отвечали: «Meh», добавляя, что вот если бы она была на модном HTML5 — то было бы, конечно, совсем другое дело, и что как только её портирую — сразу идти к ним.

Не вопрос! Откопал Haxe, сделал «haxelib install box2d», восхитился: «Как же легко портировать с флешика на хакс! Вот буквально только int на Int заменить и void на Void!», а дальше маленько обосрался, наивно полагая, что раз работает на десктопе и айпаде, то и везде будет работать. Как же я был наивен.

bad-computer

(далее…)

Год в Германии — впечатления

Чуть меньше года назад сбылась моя розовая мечта, и я переехал в Германию. Началось с того, что со мной через LinkedIn связался рекрутер из гамбуржской игродельной конторы, письмо которого я сперва проигнорировал, потому как «ну кого может заинтересовать какой-то зачуханный быдлокодер-говноляп аж из Украины?». Пробы ради ответил и — опа! — прошёл собеседование.

Переезд

Оформление документов заняло довольно много времени, из-за чего работодателю пришлось дважды переносить на месяц дату начала моего контракта — с 1 апреля на 1 июня.

Наибольшие задержки были, во-первых, из-за того, что контракт по бумажной почте всё никак не доходил, и, во-вторых, из-за диплома: посольство не хотело выдавать визу. В Германии есть специальная БД зарубежных ВУЗов и специальностей, выпускники которых могут получить Blue Card (визу для высококвалифицированных работников, навроде врачей, программистов и инженеров) — http://anabin.kmk.org/no_cache/filter/institutionen.html (там вкладка «Suchen»). Хоть в ней и есть мой ВУЗ, моей специальности там не было, потому работодателю пришлось подтверждать мой диплом через немецкое бюро трудоустройства (чтобы последние посмотрели на мой диплом и выдали официальную справку, что, мол, «выпускники этой специальности — норм ребята, в компах шарят, к высококвалифицированным работникам относятся»), и потом мне отправили соответствующее письмо в Украину, с которым я уже в посольство и шёл.

Кстати, без диплома Blue Card не выдают, и переезд был бы невозможен. С благодарностью вспоминал родительское: «Иди учись и не выдумывай глупостей! Ишь ты, университет бросит он, в жизни оно ему не пригодится!».

Диплом котируется

В конце-концов все необходимые бумаги были собраны (к слову, не так их и много нужно для национальной визы — даже меньше, чем для туристической), и спустя две-три недели мне позвонили из посольства, пригласив зайти с загранпаспортом и получить визу. Так что в мае 2014 я собрал дорожную сумку, закинул на плечи рюкзак, и, тихо пища от восторга — настолько круто, оказывается, летать в самолёте — полетел из Киева в Гамбург. (далее…)

OpenFL: Float32Array is not defined

Я знаком с OpenFL крайне поверхностно, потому каждый раз, когда он ведёт себя «как-то не так, как вёл бы Flash» — искренне пугаюсь и теряюсь.

Всё было хорошо, пока не обнаружилось, что HTML5-игра не запускалась в IE9, говоря: «Float32Array is not defined».

Наверняка, есть изящные и удобные способы решить эту проблему, но мне помогло открыть сгенерированный .js, найти в нём эту строку

this.__array = new Float32Array([a,b,c,d,tx,ty,0,0,1]);

и заменить на эту:

this.__array = [a,b,c,d,tx,ty,0,0,1];

Уверен, что это не лучшее решение, и скорее всего при этом от игры что-то втихаря отваливается — но у меня всё вроде бы заработало.

cat_smiling

Уроки, вынесенные из не слишком удачной социалки

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

Команда подобралась настолько компактной, насколько возможно: серверщик, клиентщик, дизайнер, художник-аниматор. Раздувать штат инвестор не хотел, потому как сам не был уверен в целесообразности данного предприятия. При этом каждый (кроме, пожалуй, меня) хорошо знал своё дело, и был в нём действительно хорош. Казалось бы, всё должно получиться!

Ошеломляющего успеха не было, retention и платежные показатели оказались крайне скромными (правда, и деньги в раскрутку не вкладывались — оценки делали по первым 200к игроков, пришедших в игру в первые недели просто из каталога Вконтакте, пока игра висела в разделе «новые»). В вопросах монетизации и геймдизайна я много опыта всё равно не набрал, однако несколько выводов для себя сделал, и хотел бы ими поделиться — может, кому пригодится. Тем более, сейчас, работая в большой игровой компании, я особенно чётко вижу, как умные и опытные люди избегают моих «детских» ошибок.

На эту же тему, к слову, я ещё могу порекомендовать хорошие статьи «как умудриться совершить 14 ошибок, разработав одну социальную игру» и «Целенаправленный сбор и анализ граблей в разработке игр для соцсетей«.

Нижеизложенное, повторюсь — это сугубо мои персональные выводы (во многом капитанские), основанные на собственных наблюдениях и (часто) ошибках. Итак!..

Я понятия не имею как проектировать

Не переоценивать себя

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

Возможный выход: попросить подробную консультацию тех людей, которые уже таким занимались и съели собаку. Даже если за это придётся заплатить денег — оно себя окупит. (далее…)

Итоги 2014

Год закончился, почему бы и не подвести итоги?

Побывав на киевском девгамме я тоже воспылал энтузиазмом, и, наслушавшись, что флеш как обычно мертв, решил, что я теперь у мамы html5-разработчик. Прикинул, что куплю на заработанные за год миллионы, потёр в предвкушении ладошки, взял Haxe+OpenFL+Bitfive и стал делать всякие поделки.

ВНЕЗАПНО спойлер: миллионов нет, за год почти ничего не сделано 🙂
(далее…)

Метод утенка

Недавно прочел о дебаге «методом утёнка» (http://en.wikipedia.org/wiki/Rubber_duck_debugging).

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

Стив Макконнелл в главе «Совместное конструирование» книги «Совершенный код» тоже описывает это:

Вероятно, вам знакома одна довольно распространенная ситуация. Вы подходите к столу другого программиста и говорите: «Не мог бы ты взглянуть на этот код? Он не работает». Вы начинаете объяснять: «Причиной не может быть вот это, потому что я сделал то-то и то-то. Причиной также не может быть это, потому что я сделал вот это. Кроме того, причиной не может быть… подожди… Это может быть причиной. Спасибо!» Вы решили проблему, хотя ваш «помощник» не произнес ни слова.

Дергать других я не люблю, потому объяснил проблему первому, что нашлось под рукой — пластиковой бутылке. И что я скажу: это работает! Бутылка не только внимательно и не перебивая выслушала меня, но и быстро помогла найти причину — буквально за двадцать минут после двух часов безуспешного чесания затылка и тяжких вздохов.

Одним словом — ещё одна замечательная вещь из разряда: «Это же так просто, почему я не делал это раньше?!».

quack_quack_mtfckr

Как остановить setTimeout()

Вообще, использование setTimeout() не слишком желательно, потому как странно ведёт себя с памятью, но в некритичных для производительности местах — вполне можно.

А для остановки setTimeout() (т.е. когда после его вызова необходимо остановить таймер, чтобы указанный метод не выполнился по таймауту) используется метод clearTimeout() — вот так:

var timeoutId: uint = setTimeout(myMethod, 1000);
clearTimeout(timeoutId);

А чтобы пост был интереснее, вот вам фото котейки в шапочке.

Cat in hat

Observer — паттерн, который изменит вашу жизнь

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

Кстати, слышал любопытное мнение: «Если у тебя есть класс, в названии которого есть слово Manager — значит, у тебя, скорее всего, проблемы с архитектурой приложения». Как думаете? Согласны или нет?

Вот как можно подступиться к этой проблеме:

  • сделать его синглтоном, и обращаться к нему из нужных мест в программе;
  • передавать ссылку на него каждому заинтересованному классу;
  • наоборот, передавать ему ссылку на каждый класс, в котором AchievementsManager заинтересован;
  • использовать Dependency Injection;
  • использовать паттерн Observer.

Вариант с синглтоном не очень ужасен, но что, когда нам понадобиться сделать ещё несколько подобных по принципу действия механизмов? Плодить ещё синглтоны? Так себе решение. Передавать ссылки друг на друга между объектами — выглядит довольно криво, и быстро превратит код в гору мусора. Dependency Injection — красиво и изящно, но несколько сложно в реализации.

И вот здесь нам приходит на помощь паттерн Observer («Наблюдатель»). Я расскажу о несколько упрощённой его вариации, немного похожей на то, как это устроено во фреймворке PureMVC и использующей стандартный EventDispatcher флэша.

(о том, как делать «по-правильному», я рекомендую прочесть в книге William Sanders, Chandima Cumaranatunge — ActionScript 3.0 Design Patterns или — если знаете украинский язык — в замечательной «Дизайн-патерни — просто, як двері» за авторством Андрея Будая)

Итак, в первую очередь создаём некий синглтон (да, увы; но только один), который будет у нас центральным эвентдиспетчером.

import flash.events.EventDispatcher;
 
	public class CentralEventDispatcher extends EventDispatcher
	{
		private static var _instance: Observable;
 
		public function CentralEventDispatcher() 
		{
 
		}
 
		public function broadcastMessage(name: String, content: Object = null):void
		{
			dispatchEvent(new GameEvent(name, content));
		}
 
		static public function get instance():CentralEventDispatcher 
		{
			if (!_instance) _instance = new CentralEventDispatcher();
			return _instance;
		}
	}

Затем делаем NotificationEvent. Это — обычный Event, в котором есть два поля: String name и Object body. Первое используем для того, чтобы получатели событий понимали, от кого и о чём оно; во втором (опционально) находится полезная нагрузка.

import flash.events.Event;
 
	public class NotificationEvent extends Event 
	{
		public var name: String;
		public var body: Object;
 
		public function NotificationEvent(name:String, body: Object = null, bubbles:Boolean=false, cancelable:Boolean=false) 
		{ 
			super(name, bubbles, cancelable);
 
			this.name = name;
			this.body = body;
		} 
 
		public override function clone():Event 
		{ 
			return new NotificationEvent(name, body, bubbles, cancelable);
		} 
 
		public override function toString():String 
		{ 
			return formatToString("NotificationEvent", "name", "body", "bubbles", "cancelable", "eventPhase"); 
		}
 
	}

Эти два класса — всё, что необходимо для того, чтобы организовать Observer. Теперь разберёмся, как этим добром пользоваться.

Когда какому-то классу нужно поставить другие о чём-то в известность, мы делаем так:

public class Game 
	{
		public static const NAME: String = "Game";
		public static const SOMETHING_INTERESTING_HAPPENED: String = NAME + "SomethingInterestingHappened"
		public static const ANOTHER_THING_HAPPENED: String = NAME + "AnotherThingHappened"

		public function Game() 
		{
			CentralEventDispatcher.instance.broadcastMessage(SOMETHING_INTERESTING_HAPPENED);
			CentralEventDispatcher.instance.broadcastMessage(ANOTHER_THING_HAPPENED, {payload:String("Some random stuff")});
		}

	}

А в других классах, которые должны узнавать об этих событиях, просто подписываемся на них при помощи addEventListener. Только нюанс в том, что отсылает их не экземпляр класса Game, в котором событие происходит, а CentralEventDispatcher, так что именно на него нам и нужно подписаться.

public class AchievementsManager 
	{
 
		public function AchievementsManager() 
		{
			CentralEventDispatcher.instance.addEventListener(Game.SOMETHING_INTERESTING_HAPPENED, onNotification);
			CentralEventDispatcher.instance.addEventListener(Game.ANOTHER_THING_HAPPENED, onNotification);
		}
 
		private function onNotification(e:NotificationEvent):void 
		{
			switch (e.name) 
			{
				case Game.SOMETHING_INTERESTING_HAPPENED:
					trace("I am AchievementsManager and I received SOMETHING_INTERESTING_HAPPENED notification");
				break;
 
				case Game.ANOTHER_THING_HAPPENED:
					trace("I am AchievementsManager and I received ANOTHER_THING_HAPPENED notification", {payload:String("Some random stuff")});
					trace("And here is its body contents: " + e.body['payload']);
				break;
			}
		}
 
	}

Вот так. Таким образом, мы можем добавить сколько угодно классов, которые могут слушать любые событие, происходящие в любых концах приложения, при этом ничего не зная об объектах, эти события порождающих — они должны знать только о CentralEventDispatcher.

Очевидный недостаток этого подхода, помимо необходимости держать синглтон — это постоянная генерация Event’ов, что не обрадует GC.

А константа-префикс NAME перед именем события является признаком хорошего тона и добавлена для того, чтобы разные классы могли иметь одинаковые имена уведомлений, и не было недоразумений, когда вместо уведомления от одного класса будут выхватываться все уведомления с таким именем, независимо от отправителя.