Мультиязычные игры. Это просто!

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

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

Реализация мультиязычности

Я предлагаю рассмотреть универсальную реализацию мультиязычности, которая может поддерживать бесконечное количество языков, быть гибкой к любым модификациям и, конечно, же должна уметь переключать язык в любой момент. Реализуем подгрузку текстов из XML файлов, что может быть удобно если вам необходимо отдать тексты для правки носителю языка.

? так, за язык у нас будет отвечать класс Language.as — он не будет иметь конструктора, так как все его константы, переменные и методы будут статическими, что позволит получить к нему доступ из любой точки вашей игры.

Первым делом мы объявляем константы — это будут уникальные ключи для доступа к определенным текстам:

public static const NEW_GAME:String = "btn_newGame";
public static const CONTINUE:String = "btn_continue";
public static const HIGHSCORES:String = "btn_highscores";
public static const CREDITS:String = "btn_credits";
        
public static const TITLE:String = "tf_title";
public static const AWESOME:String = "tf_awesome";
// и т.д.

Количество таких констант напрямую зависит от количества текстов (текстовых меток и кнопок) в вашей игре.

Следом мы объявим публичную статическую переменную, которая будет определять какой язык используется в данный момент времени:

public static var current:String = "none";

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

private static var _labels:Object = {};

Конструктора у данного класса нет, поэтому мы сразу переходим к рассмотрению необходимых нам методов. ? первый из них — это метод, устанавливающий английские тексты:

public static function setEnglish():void
{
  _labels[NEW_GAME] = "New Game";
  _labels[CONTINUE] = "Continue";
  _labels[HIGHSCORES] = "Highscores";
  _labels[CREDITS] = "Credits";

  _labels[TITLE] = "My Great Game";
  _labels[AWESOME] = "Awesome!";
            
  current = "eng";
}

Обратите внимание, здесь мы используем объект _labels, как контейнер для хранения текстов. Объявленные ранее нами константы определяют ячейки, в которых хранятся соотвествующие им тексты. Получается что-то вроде ассоциированного массива, где "btn_newGame" => "New Game". Для других языков мы делаем такие же методы, но используем другое наполнение ячеек, например, метод загрузки русского языка будет выглядеть так:

public static function setRussian():void
{
  _labels[NEW_GAME] = "Новая игра";
  _labels[CONTINUE] = "Продолжить";
  _labels[HIGHSCORES] = "Очки";
  _labels[CREDITS] = "Авторы";
            
  _labels[TITLE] = "Моя крутая игра";
  _labels[AWESOME] = "Афигительна!";
            
  current = "rus";
}

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

public static function getText(id:String):String
{
  return (_labels[id] == null) ? "undefined" : _labels[id];
}

В качестве параметра мы задаем ключ для текста, который нам нужно получить, и получим в результате строку, соответсвующую данному ключу в зависимости от того, какой язык был загружен в _labels. Если вдруг запрашиваемого ключа нет в нашем контейнере текстов, то вернется строка "undefined". Но такое может быть только по невнимательности разработчика — кнопку добавили, а текст для нее забыли.

?спользование

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

Language.setEnglish();

Язык выбран, тексты загружены, теперь можно использовать:

btnNewGame.text = Language.getText(Language.NEW_GAME);

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

Если вы любите создавать игровые меню непосредственно во Flash IDE визуально, то для вас я могу предложить очень простой способ. Расставляете клипы ваших кнопок и задаете им Instance Name соотвествующие ключам в Language.as. Например, размещаем клип кнопки "New Game" и в Instance Name пишем: "btn_newGame", что будет идентично Language.NEW_GAME, и так делаем для всех кнопок и даже для обычных текстовых меток.

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

private function updateMenu(menuClip:MovieClip):void
{
  var obj:MovieClip;
    // Перебираем все клипы вложенные в меню.
  for (var i:int = 0; i < menuClip.numChildren; i++)
  {
    // Обращаемся к каждому клипу, как к MovieClip
    obj = menuClip.getChildAt(i) as MovieClip;
    // Если это клип и он содержит текстовую метку с instance name "label", то
        if (obj != null && obj["label"] != null)
        {
      // Применяем к этой метке соответствующий текст
      (obj["label"] as TextField).text = Language.getText(obj.name));
    }
  }
}

Принцип работы данного метода очень прост: мы перебираем все вложенные клипы нашего меню, и если эти клипы содержат текстовое поле (TextField) с Instance Name равное "label", значит это кнопка, либо просто текстовая метка, и мы неприменно должны установить соотвествующий имени клипа текст. Так же данный метод нам теперь может позволить создать динамический переключатель языка, например такой:

private function switchLang():void
{
  // Если текущий язык английский, то...
  if (Language.current == "eng")
  {
    // Переключаем на русский.
    Language.setRussian();
  }
  else
  {
    // ?наче русский, значит переключаемся на английский.
    Language.setEnglish();
  }
            
  updateMenu(myMenu);
}

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

 

Кликните на кнопку "Rus" чтобы переключить язык.

Если вы не используете Flash IDE для создания игровых меню и создаете кнопки и текстовые кнопки непосредственно в коде, то вам можно так же использовать аналогичный метод извлечения нужного текста из Language, просто нужно добавить кнопкам и меткам какую-нибудь публичную переменную в которую следует записывать идентификатор строки которую она должна отображать, то есть сделать свое поле для хранения id и использовать его так же как Instance Name.

Ссылка на исходники — CS4, *.zip, 16кб

Загрузка языка из XML

Хранить языки в XML удобно. XML позволит переводить игру носителю языка без правки вашего кода. Вам же только необходимо будет вставить содержимое переведенного XML в ваш код. Давайте посмотрим как мы сможем изменить наш код, чтобы поместить все языки в XML. В классе Language.as создадим новый метод:

public static function setEnglishXML():void
{
  var xmlLang:XML = <langData>
    <lang>eng</lang>
    <text id="btn_newGame">New Game</text>
    <text id="btn_continue">Continue</text>
    <text id="btn_highscores">Highscores</text>
    <text id="btn_credits">Credits</text>

    <text id="tf_title">My Great Game</text>
    <text id="tf_awesome">Awesome!</text>
  </langData>;
            
  readXML(xmlLang);
}

Здесь содержится наш XML, который мы вставили прямо в код, потому что мы не можем поставлять дополнительные файлы с нашей игрой (!) или подгружать их динамически со своего сервера. После XML кода идет вызов метода readXML() — мы предполагаем, что все наши языковые файлы будут иметь единый формат для всех языков, поэтому метод чтения XML так же будет единый для всех. Рассмотрим теперь метод чтения XML:

private static function readXML(xmlData:XML):void
{
  var n:int = xmlData.text.length();
  var item:XML;
  for (var i:int = 0; i < n; i++)
  {
    item = xmlData.text[i] as XML;
    _labels[item.@id] = item;
  }
            
  current = xmlData.lang[0];
}

Мы просто перебираем все содержимое нашего XML и записываем его в хранилище текстов, а в последней строке не забываем и про идентификатор текущего языка. Вот собственно и все.

Заключение

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

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

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

 

Антон, еще раз спасибо за статью!

Grizzly
1 Октября 2012
— 22:54
#

Согласен, спасибо за статью.

Роман
1 Октября 2012
— 23:40
#

Вставлю свои 5коп.

Когда то имел дело с мультиязычностью, но с иностранными языками у меня проблемы, да и заказчик хотел в дальнейшем расшырять набор языков самостоятельно.
Принцип работы был похож на Ваш, но в коде не было жестко прописано какие языки будет использовать приложение. ? вместо набора методов setEnglish(), setRussian() использовалс метод translate(langID:String). А набор языков определялся в XML-файле:
<data>
<!-- Описание доступных язиков -->
<langs>
<!-- Описание языка, id указывает на название тега с переводом -->
<item id="en", icon="en.png" />
<item id="ru", icon="ru.png" default="true" />
...
</langs>

<!-- Коллекция строк на всех язиках -->
<strings>
<!-- Описание строки разными языками -->
<item id="btn_continue">
<!-- строка на конкретном языке -->
<en>Continue</en>
<ru>Далее</ru>
...
</item>
...
</strings>
</data>


В начале работы нужно изучить все дочерние узлы data.langs, чтобы составить массив доступных языков, а потм уже делать выборку из набора строк используя id текущего языка, как название тега.

Способ не весьма удебен, но гибок в настройке/модификации.

elder_Nosferatu
2 Октября 2012
— 01:33
#

В свое время пришел к такому же коду. ?спользовал его пару раз в своих проектах.
Единственное, что хотелось бы добавить: я использовал не статический класс, а синглтон. Тогда имеется возможность в методах, где изменяется язык диспатчить событие Change, а в классах с текстовыми полями подписаться на это событие и уже там менять все надписи. Данное уточнение имеет смысл только, если у вас более 1 экрана в приложении.

Olexandr Fedorow
2 Октября 2012
— 02:12
#

@elder_Nosferatu,

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

Даже не смотря на то, что вы избавляетесь от методов загрузки языка, вам все равно нужно своему "переводчику" указывать какой язык используется в данный момент времени, что в принципе одинаково.

В общем, по моим ощущениям, такой подход только все запутывает и усложняет.

Но в любом случае, спасибо, что поделились!

Ant.Karlov
2 Октября 2012
— 03:20
#

@Olexandr Fedorow,

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

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

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

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

Ant.Karlov
2 Октября 2012
— 03:30
#

Вот это Language.getText(Language.NEW_GAME);
можно сократить до Language.NEW_GAME

hitsuke
2 Октября 2012
— 07:24
#

Спвсибо большое! Очень круто!

flahhi
2 Октября 2012
— 09:14
#

@Ant.Karlov

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

1) Структура секции <strings> не обязана быть такой, как в примере. Мне просто легче было, когда все переводы строки стоят вместе. Но соглашусь, что отдельным людям править свой язык в таком файле неудобно. Тогда можна изменить конструкцию на:
<data>
...
<strings>
<ru>
<item id="btn_continue">Далее</item>
...
</ru>
<en>
<item id="btn_continue">Continue</item>
...
</en>
</strings>
</data>
В такой файл одним Ctrl+C / Ctrl+V можна вставить подготовлениый набор строк на новом языке. Ну а выкинуть языки можна просто убрав их описание из data.langs. Приложение игнорирует все языки, кроме заявленых в этой секции.

2) На счет перевода - список доступных языков читается из data.langs. Вместо того, чтобы прописывать перевод на каждый язык я использовал поиск нужной строки (data.strings.item) по ее id. В результате получал XMLList, каждый елемент которого соответствовал переводу на свой язык. Подставив в метод XMLList#child(tagName:String) id нужного языка (который соответствует имени тега) получаем перевод строки. В результате ни один язык в коде не упоминается, все решает XML-файл!

3) На счет уверености в правильности файла мне тужыть не приходилось. Он грузился извне и что в нем было - проблема заказчика. Я всего лишь доказал ему, что все отлично работает на двух языках и показал, как вставлять новые на примере третего языка.

ПС: критикуйте, материте, игнорируйте или даже баньте, но очень прошу не отвечайте в стиле "МНОГА БУКАВ". Я и сам вижу... :/

elder_Nosferatu
2 Октября 2012
— 10:42
#

@Ant.Karlov

Это все неплохо, но можно вместо XML ипользовать более легкие по весу JSON или кастомный файлики *.txt или *.cfg с парами ключ - значение:
btn_newGame=New Game

muravey
2 Октября 2012
— 12:29
#

А давайте усложним задачу.
ведь иногда нужно не просто подменить фразу, а генерировать её на лету. Например

"Бонус: 1 очко" - "Bonus: 1 point"
"Бонус: 2 очка" - "Bonus: 2 points"
"Бонус: 5 очков" - "Bonus: 5 points"

Первое это нужна возможность подставить цифру.
Второе это то что окончание зависит от числа, причём множественное число в разных языках образуется по своему.

Вот тут начинается веселуха :)


p.s.
обратите внимание на флексовский ресурс менеджер, его можно подключать и к as3 проектам mx.resources.ResourceManager

Good
2 Октября 2012
— 13:58
#

@hitsuke,

> Вот это Language.getText(Language.NEW_GAME);
можно сократить до Language.NEW_GAME


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

Ant.Karlov
2 Октября 2012
— 14:25
#

@elder_Nosferatu, спасибо за уточнение! Обновленная структура выглядит по лучше. Еще бы разделить на файлы и вообще хорошо бы было. Но если я правильно понимаю, то в случае деления XML с текстами на отдельные файлы, XMLList уже не сможет "переводить" текст так как вы это делали :)

В целом вариант интересный, возможно кому-то подойдет такой подход.

Ant.Karlov
2 Октября 2012
— 14:30
#

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

Ant.Karlov
2 Октября 2012
— 14:32
#

@Good, не нужно усложнять себе задачи и вместе с ними свою жизнь :) Нужно упрощать!

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

Ant.Karlov
2 Октября 2012
— 14:35
#

Безусловно, я за упрощение двумя руками, но тем не менее, подобная ситуация может возникнуть, это просто пример, может не самый удачный, но в реальности иногда есть ситуации когда невозможно обойтись без словоформ, и подстановок.
Вот пример посложнее, игра в которой вы можете выбрать пол персонажа. Каждый NPC который обращается к персонажу должен видеть его пол и составлять фразу соответствующим образом.
"Приветствую тебя о великий(ая) %username%!, ты доблестно сражался(ась) и я хочу дать тебе за это %ххх% золотых монет(-ы,-у)"

Good
2 Октября 2012
— 15:10
#

@Ant.Karlov
Я не имел ввиду, что каждую кнопку подписывать на смену языка. Я говорил о подписке каждого экрана. Соответственно нет необходимости обновлять тексты при переходе к этому экрану.
Все же, как по мне, это два равносильных подхода. Мне удобнее пользоваться событиями, чем явно вызывать какую-то функцию.

Olexandr Fedorow
2 Октября 2012
— 15:37
#

2 Good ну тогда придется делать окончания :)
Это вообщем-то не так сложно, я в свое время кодил для русского текстового MUD, там имена всех объектов были в 6-и падежах, за окончания числительных вроде не парились, хотя при желании это тоже можно сделать.

Если я правильно помню, то на каждую фразу была заготовка из 3-х вариантов (мужской, женский и средний род)

Скажем на русском текст будет выглядеть так:
для мужского
"Приветствую тебя о великий %username.r%!, ты доблестно сражал и я хочу дать тебе за это %gold% золотых монет"

для женского
"Приветствую тебя о великая %username.r%!, ты доблестно сражалась и я хочу дать тебе за это %gold% золотых монет"

где %username.r% - имя игрока в родительном падеже
%gold% - кол-во передаваемых предметов

Но по сравнению с вариантом Антона геморроя тут конечно довольно много :)
? для какого-нибудь китайского все видимо будет по другому.

p.s. оказывается исходники мада открыты, кому интересно может посмотреть реализацию русский окончаний http://www.mud.ru/?license

orbit
2 Октября 2012
— 17:10
#

@orbit Я не спрашиваю как это реализовывать, просто сам с такими задачами сталкивался неоднократно и на собственном опыте знаю какие еще функции требуются от класса отвечающего за локализацию.

Вариант с разными ресурсами для каждого пола мне абсолютно не нравится

Я это реализовываю через специальные тэги:

npcMessage = Приветствую тебя о великая [[username]], ты доблестно сражал[[gender|isMale|ся|ась]] и я хочу дать тебе за это [[gold]] [[plural|gold|золотую монету|золотые монеты|золотых монет]]

а в коде это выглядит так:
getTemplate(`npcMessage`, { isMale:true, gold:17 });

Good
2 Октября 2012
— 18:00
#

npcMessage = Приветствую тебя о велик[[gender|isMale|ий|ая]] [[username]], ты доблестно сражал[[gender|isMale|ся|ась]] и я хочу дать тебе за это [[gold]] [[plural|gold|золотую монету|золотые монеты|золотых монет]]

Good
2 Октября 2012
— 18:01
#

2Good адская жесть :D
Таки мне кажется, что с текстами обычно работают не программисты, и им проще 2-3 варианта заполнить, чем все эти пачки окончаний перечислять...да и мне честно говоря эта каша не кажется сильно читаемой. Ну т.е. если короткая фраза и 1-2 окончания, то еще ничего, но а если длинное предложение?

Но это конечно все вопрос вкуса.
Кстати падежи-то у тебя есть? На каждый объект ведь их приходится заполнять?

Это мы сейчас обсудили перевод на русский. Ну с немецким примерно также где-то, английский совсем простой. А какой-нибудь китайский? Там наверное все совсем по другому :)

orbit
3 Октября 2012
— 12:55
#

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

Good
3 Октября 2012
— 14:09
#

@orbit

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

Также хочется отметить правоту Антона, который не перегружает свои творения ненужным функционалом. Нет в его играх динамических строк значит и не нужно методов для их генерации.

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

elder_Nosferatu
3 Октября 2012
— 14:18
#

2elder_Nosferatu
>? грошь цена программе (а также программисту
>ее создавшему) если пользователь берет на себя
>гору механической рутинной работы.
ЛОЛ

Сразу виден большой опыт работы с пользователями :D Я лет 15 назад тоже думал, что задача программиста - сделать наиболее универсальное решение.

Только вот незадача, научить конечного пользователя (не программиста) пользоваться этим универсальным решением очень и очень сложно (у меня не получилось, хотя казалось бы, что сложного в упрощенном формировании отчетов а-ля 1с?)

>Поиск универсального алгоритма для решения
>однотипных задач
Падежей нет, нифига он не универсальный. Мне кажется куда проще отредактировать 2 варианта текста для разных родов, чем ковряться в этой жуткой мешанине из спецсимволов. Уж всяко это не "юзер-френдли" :D

orbit
3 Октября 2012
— 20:51
#

2elder_Nosferatu
>Поиск оптимального решения конкретной задачи
>(без пятого колеса и кондома с блю-тусом)
признак опытного программиста

>поиск универсального алгоритма для решения >однотипных задач
почти всегда признак неопытного программиста

orbit
3 Октября 2012
— 20:52
#

@orbit

Вас наверное уже задолбало программирование. Зачерствели вы к нему и оно превратилось в обычную рутинную работу. Но ведь не у всех так. Ктото до сих пор радуется созданию красивых изящных решений. А для некоторых это самоцель.

Если вы столько времени программируете, то наверное знакомы с таким примером программного творчества как .kkrieger (http://ru.wikipedia.org/wiki/.kkrieger). ? не говорите, что это не шедевр.

На счет универсальных решений и "юзер-френдли": вот вам пример. Языки программирования и их трнасляторы - продукт. Программисты - пользователи этих продуктов. Вспомним Регулярные Виражения. Тот, кто разобрался с магией "крякозябр" начинает творить чудеса в несколько строк. А те, кому лень занимаются многострочными перетасовками, поисками-перепоисками и прочими танцобубнами со строками.

Ну и на конец, вы правы на счет моей неопытности. Будь я опытным программистом, я б завел себе личный блог, где и делился бы с народом своими знаниями. А не совал бы, как выскочка, свои 5коп. здесь (и не только здесь)...

elder_Nosferatu
4 Октября 2012
— 01:24
#

Серьезную вы тут тему по работе с текстами и переводами подняли. Я же в свою очередь демонстрировал самое простое решение которое подойдет в 95% Flash игр. Впрочем и с более сложными такой вариант достаточно жизне способен. Просто необходимо максимально оптимизировать сами тексты.

Оптимизация может выглядеть следующим образом, например, словосочетание "золотых монет" заменять иконкой монетки аля свой знак валюты, так же стараться избегать прямых обращений к игроку. В крайнем случае проще будет разделить тексты для мужского и женского пола игрока полностью (немного разные диалоги) — это не только упростит работу программисту, но и, возможно позволит сделать игру более интересной. Например тексты для женского пола могут немного отличатся от мужского пола и Н?Пы могут даже как бы заигрывать или флиртовать с игроком женского пола ;) Ну и фразы ответов для игрока можно предлагать в таком случае с учетом женской логики.

Ant.Karlov
4 Октября 2012
— 03:32
#

Столкнулся с проблемой жму Новая ?гра, но игра не запускается. Что я сделал не так? шутка:)

Новичок
4 Октября 2012
— 19:00
#

Спасибо, статья интересная, но как начинающего больше интересует вопрос - когда же вы, Антон, продолжите серию замечательных уроков Tower Defence?

Energy_beam
5 Октября 2012
— 14:23
#

2elder_Nosferatu
>Программисты - пользователи этих продуктов
%) это вырожденный случай. В 99% случаев пользователь в жизни не разберется с регулярными выражениями. Рекомендую книгу "Психбольница в руках пациентов" о том, почему нельзя давать программистам проектировать интерфейсы.

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

>Но ведь не у всех так
Боюсь, что у всех кто программированием зарабатывает себе на жизнь. Это очень долго было моим увлечением, потом стало работой и очень быстро меня утомило. Буквально на пару лет хватило, потом сменил род деятельности.
Кроме того, надо помнить, что сейчас программисты это обычная рабочая специальность, и больше определенного (весьма небольшого) предела программированием никак не заработать.

>А для некоторых это самоцель
Эта цель имеет смысл только если собираешься стать крутым программистом, чтобы всю жизнь потом корячиться за копейки ;)

Тут вроде собрались те, у кого цель - сделать игру. Программирование в создании игры - 1/3 от общего объема работ. Попытки программиста решать задачи наиболее униварсальным образом приводят к неудачам, трате времени, потере интереса -> неудаче в создании игры.

Надо придерживаться приоритетной цели (создание игры), а не подменять эту цель всякой фигней вроде "создания красивого решения работы с текстом "(ну если вы конечно не скучаете на высокооплачиваемой должности :) )

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

orbit
5 Октября 2012
— 16:09
#

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

В остальном чистая правда, согласен по всем пунктам.

BuxomBerry
5 Октября 2012
— 20:42
#

Есть у меня знакомый который зарабатывает на жизнь тем что вправляет мозги программистам.
Его зовут тогда когда проект зависает и инвестору надо принять решение зарубить все к бениной маме или как то заставить эту массу именующую себя "программистами" собраться и добить таки проект до релиза.
Меня всегда поражала его способность слету воспринимать общую архитектуру проекта и конкретную логику чужого кода но самое смешное что до этого практически никогда не доходит.
После его дружеских визитов в коммандах в лучшем случае остается 2/3 первоначального состава. Но остаются те кто реально тянет проект. ? вот уже с ними буквально за пару дней идет работа открывающая чакры =)

Все по Рингельману - если один человек поднимает 100 кило то двое - уже с трудом поднимают 80.
=)

Так к чему вся эта лирика.
Боюсь испугать вас его гонорарами.
Недельный визит по профилактике мозговой активности обходится заказчику примерно в 80 000 $
? самое смешное - у человека очередь, чайку попить некогда.
А вы говорите - пределы =)

и.о. Капитан Очевидность
6 Октября 2012
— 19:24
#

2и.о. Капитан Очевидность
А я получаю 999$ в секунду.
Так что лох твой знаковый, за копейки продался капиталистам. А чтоб столько получать надо-то всего *вырезано цензурой*

2 BuxomBerry
Я хотел сказать, что з/п программиста с определенного времени упирается в "стеклянный потолок", и чтобы получать больше, приходится переходить на руководящие должности. А там решает уже не скилл программирования.

orbit
6 Октября 2012
— 22:18
#

Привет Антон. Хотел задать вопрос, он не по теме, но по теме кодинга. Что считаешь красивее и удобнее в плане реализации: когда объекты добавляют сами себя в движок или же, когда движок добавляет объекты в себя?

Виталий
6 Октября 2012
— 22:22
#

инфа кэпа про 80 тыс. баксов требует изрядной дозы непиздина

Anton
6 Октября 2012
— 23:07
#

@Energy_beam, сейчас готовлю большой интересный материал к публикации в блоге. После него сразу вернемся к урокам и закончим их "одним махом" ;)

Ant.Karlov
7 Октября 2012
— 13:15
#

@Ant.Karlov, - "одним махом" - это было бы замечательно!) Жду с нетерпением!) Спасибо, вам за труды:)

Energy_beam
7 Октября 2012
— 13:35
#

@Ant.Karlov, было бы здорово если бы вы в продолжили серии уроков и внедрили свой
Anthill , получилось отличные примеры работы с Anthill =).

PHILIPS=)
8 Октября 2012
— 12:01
#

Полезный материал, Антон. Хотел лишь добавить, что ХМЛ-ку можно вынести в отдельный файл, например вот это запихнуть в LangEnglish.xml:

var xmlLang:XML = <langData>
<lang>eng</lang>
<text id="btn_newGame">New Game</text>
<text id="btn_continue">Continue</text>
<text id="btn_highscores">Highscores</text>
<text id="btn_credits">Credits</text>

<text id="tf_title">My Great Game</text>
<text id="tf_awesome">Awesome!</text>
</langData>;

а в коде написать вместо этого написать:

include "LangEnglish.xml"

получится что-то вроде отдельного файла. Не совсем xml-шного правда, но тем не менее )

Oleg Antipov
14 Октября 2012
— 16:04
#

Уважаемый Антон, спасибо вам большое за то что делитесь опытом (вчера впервые столкнулся с созданием кнопок для игры и класс в этом примере мне помог понять в чём были проблемы). Желаю крепкого здоровья, семейного благополучия, неиссякаемого вдохновения и успехов в коммерческих проектах.

Андрей
11 Ноября 2012
— 14:03
#

Спасибо Антон, наткнулся на вашу статью, но все же задумка не супер. Удобнее грузить json обджект и переменными continue="Continue", new_game="new game"......
из файла lang/en.json
или lang/ru.json
и т.д...

Азиз
18 Мая 2016
— 16:02
#

@Азиз, формат не важен, главное принцип и подход! :) На момент написания записи xml мне был понятен и удобен, поэтому он и был взят за основу. Спасибо!

Ant.Karlov
19 Мая 2016
— 00:17
#