Game Maker - создание игр | HellRoom Games
Февраль 25, 2017, 05:23:20 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
Новости: Jelly Killer - релиз!
 
   Начало   Game Maker Помощь Правила форума Поиск Календарь Войти Регистрация  
Страниц: [1]   Вниз
  Печать  
Автор Тема: Сравнение вариантов локализаций, мультиязычность, выбор языка в игре  (Прочитано 1406 раз)
0 Пользователей и 1 Гость смотрят эту тему.
Fantom
I am... All of me
Гл. Администратор
*

Репутация: 1311
Offline Offline

Пол: Мужской
Russian Federation Russian Federation

Награды:
5000 сообщений!За постоянность! [500 дней на форуме]За лояльность! [+1000 репутации]За отличные статьи по Game Maker!Тру Админ :DЗнаток Game Maker...
API: GameMaker Studio Master
Деятельность: Experienced Programmer
Сообщений: 5035



« : Апрель 14, 2016, 12:55:21 »

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

   Под локализацией подразумевают наличие нескольких языков в игре, возможность переключения между ними и перспективу добавления новых языков в будущем.
Некоторые указанные коды, обозначенные словом "Инициализация", и содержащие большое количество текста на одном языке, то есть весь текст без какой-либо обработки, можно размещать в пользовательских событиях (Добавить событие - Другие - Пользовательское событие; User defined) или в скрипте. Вместо того, чтобы объявлять несколько одинаковых переменных для разных языков (text1_rus, text1_eng, text1_deu), можно хранить в одном наборе переменных (text1, text2, text3) любой язык, выполняя соответствующий языку event_user при старте игры и в событии переключения языка. Так, в text1 и другие переменные запишется нужный текст из выбранного языка.


Вариант 1: Переменные, глобальные или локальные.
   Пример:
Код:
// - - инициализация, отдельный event_user для каждого языка - -
global.text_start_game='Начать игру';
global.text_end_game='Выход';
. . .
. . .

// - - рисование - -
draw_text(100,100,global.text_start_game);
// или в случае с локальными переменными
draw_text(100,100,Controller.text_start_game);
  Плюсы:
- есть возможность называть переменную таким образом, чтобы видеть соответствие конкретной переменной определённому тексту.
   Минусы:
- ровная противоположность плюсу - широкое многообразие имён переменных, из-за которого рано или поздно возникнут различные неудобства в поддержке кода. Можно забыть название переменных или долго придумывать название для новой, в обоих случаях приходится смотреть на список переменных.
- не совсем удобно передавать тексты переводчикам для перевода на другие языки. Нужно копировать тексты из кода в текстовый файл, а затем переведённый копируется обратно.
   Удобство:
- среднее, сокращается при увеличении объёма текста.


Вариант 2: Массив.
   Пример:
Код:
// - - инициализация, отдельный event_user для каждого языка - -
text_array[1]='Game start';  // старт игры
text_array[2]='...';   // другой текст
. . .   // ...
. . .

// - - рисование - -
draw_text(100,100,text_array[1]);  // это "старт игры", чтобы не забыть
  Плюсы:
- все тексты хранятся под одним именем, нет такого многообразия имён, как в варианте с переменными.
   Минусы:
- необходимость запоминать соответствие номеров элементов конкретным текстам или постоянно переключаться с кода рисования текста на код объявления массива, чтобы находить это соответствие. Из этого следует сложность в поиске нужного draw_text, где рисуется текст, координаты которого, например, требуют исправления.
- неудобство в передаче текста переводчикам для перевода. Так же необходимо копировать тексты из кода в файл, а при потере правой части выражения (после знака равно) этот текст вовсе восстановлению не подлежит без повторного запроса исходного текста.
   Удобство:
- ниже среднего.


Вариант 3: ini-файлы.
   Пример:
Код:
// - - инициализация - -
ini_open(global.lang_file);  // где global.lang_file содержит имя ini-файла
text_new_game=ini_read_string(global.lang,'Game_start','***error***');  // где global.lang - выбранный язык в виде строки
ini_close();

// - - рисование - -
draw_text(100,100,text_new_game);
  Плюсы:
- удобное обозначение текстов в файле, выглядит в виде пар "Game_start=Новая игра".
- удобно передавать файлы с текстом переводчикам.
- удобное обращение к текстам в игре, видно по ключу.
- все языки находятся в одном файле. Каждый язык будет расположен под соответствующей секцией [rus], [eng] и так далее, нужно иметь в виду эту структуру при самостоятельном формировании ini-файла. Хотя при желании можно разделить языки по разным файлам, тогда названия секций будут объединять тексты в логические группы.
   Минусы:
- частое открывание и закрывание файла при сложной архитектуре кода, например, если невозможно переместить считывание текста из события рисования в создание объекта.
   Удобство:
- высокое. Файлы уже готовы к передаче переводчикам, а в коде видно по ключу, какой именно текст рисуется в определённом месте.
   Примечания:
- файл с текстами подключается к проекту с помощью Included files, global.lang_file содержит только имя файла.
- как правило, совмещается с одним из двух предыдущих вариантов - тексты считываются либо в переменные, либо в массив.


Вариант 4: ds_map.
   Пример:
Код:
// - - инициализация без парсера - -
global.text_map=ds_map_create();

// отдельный event_user для каждого языка
ds_map_replace(global.text_map,'Game_start','New game');
ds_map_replace(global.text_map,'Game_end','Exit');
. . .
. . .

// - - инициализация с парсером, допускается размещение в скрипте с аргументом lang - -
global.text_map=ds_map_create();
. . .
switch (lang)   // argument0
{case 'eng': ds_map_read(global.text_map,'*******строка*из*парсера******'); break;
 case 'rus': ds_map_read(global.text_map,'******************строка*из*парсера*********************'); break;
 }

// - - рисование - -
draw_text(100,100,ds_map_find_value(global.text_map,'Game_start'));
// или с использованием аксессоров
draw_text(100,100,global.text_map[? 'Game_start']);
  Плюсы:
- удобное обозначение текстов в файле, структура как в ini-файле.
- удобно передавать файлы с текстом переводчикам.
- удобное обращение к текстам в игре, видно по названию ключа, какой выводится текст.
- инициализация одного языка занимает всего одну строку кода в случае с парсером.
   Минусы:
- для упрощения переработки текстовых файлов в ds_map и сокращения кода в игре требуется парсер, перерабатывающий текст в ds_map_write, который потом копируется в игру одной строкой. В принципе, минусом это является до тех пор, пока кто-нибудь не поделится уже готовым кодом парсера.
- кого-то может смущать длина строки ds_map_read, растущая приблизительно на 100 символов при добавлении одной новой переменной текста.
   Удобство:
- высокое, и отличное после создания парсера. Единственное, что нужно делать после изменений в файлах с текстами, это прогнать их через парсер и вставить строку в скрипт.
   Примечания:
- структура ds_map_write различается между GM8.1- и GMS, поэтому парсер должен быть создан на той же версии Game Maker, на которой пишется основной проект.


Вариант 5: Макросы (только для GMS).
   Пример:
Код:
// инициализация происходит в диалоговом окне Resources - Define macros...
// - - образец экспортированного файла с макросами, перед импортом необходимо очистить окно макросов - -
  // имена макросов с текстами умышленно не начинаются с TEXT, чтобы не мешали во время выбора из автозаполнения
TEXT_GAME_START=scr_text_selector('TEXT_GAME_START')
TEXT_GAME_END=scr_text_selector('TEXT_GAME_END')
T_GAME_START_ENG='New game'
T_GAME_END_ENG='Exit'
T_GAME_START_RUS='Новая игра'
T_GAME_END_RUS='Выход'
=

// - - скрипт scr_text_selector('TEXT_*') - -
switch (global.lang)
{
 case 'rus':
  switch (argument0)
  {
   case 'TEXT_GAME_START': return T_GAME_START_RUS; break;
   case 'TEXT_GAME_END': return T_GAME_END_RUS; break;
   . . .
  }
 break;
 
 case 'eng':
  switch (argument0)
  {
   case 'TEXT_GAME_START': return T_GAME_START_ENG; break;
   case 'TEXT_GAME_END': return T_GAME_END_ENG; break;
   . . .
  }
 break;
}

// - - рисование - -
draw_text(100,100,TEXT_GAME_START);
  Плюсы:
- всплывающая подсказка автозаполнения при наборе первых букв макроса не требует запоминания её названия и помогает избежать ошибок в написании.
- в связи со сходством данного варианта с вариантом, использующим переменные, плюс в возможности давать осмысленные названия макросам.
- макросы добавляются в специальном диалоговом окне, имеющем кнопки сохранения и загрузки из текстового файла, что означает удобные выгрузку из проекта, передачу файла переводчикам, а также добавление текста обратно в проект нажатием одной кнопки.
- при добавлении макросов с одинаковыми именами, о конфликте будет сказано во время компиляции в окне ошибок, "одинаковые значения внутри switch".
   Минусы:
- широкое многообразие имён, как с вариантом, использующим переменные. В связи с наличием всплывающей подсказки автозаполнения, не такой уж и большой минус.
- возможность допустить ошибку при добавлении нового текста или копировании и исправлении старого с целью добавления нового. Это добавление макроса, вызывающего скрипт, добавление макросов с текстами на всех языках, и добавление в скрипт новой позиции case в каждый язык.
- относительно сложная структура экспортированного файла с макросами требует заблаговременного продумывания имён макросов, чтобы после их сортировки (кнопка в окне Define macros) другие макросы, не относящиеся к тексту, не мешали переводчикам.
   Удобство:
- высокое. Против всплывающего автозаполнения возникает не вполне удобная процедура добавления нового текста в проект и сложная структура файла.
   Примечания:
- кто-то может назвать этот вариант замаскированным использованием скриптов, мол, можно было бы draw_text(100,100,scr_text_selector('TEXT_GAME_START')), однако, в таком случае нет помощи автозаполнения и, следовательно, нужно снова запоминать имена переменных.


Вариант 6: switch на месте.
   Пример:
Код:
// - - рисование - -
switch (global.lang)
{
 case 'rus': draw_text(100,100,'Новая игра'); break;
 case 'eng': draw_text(100,100,'New game'); break;
}
  Плюсы:
- видно сразу, какой текст отображается на данном участке кода.
- быстро внедряется в проекты с малым количеством текста.
   Минусы:
- очень неудобное редактирование текстов. Если вдруг находится ошибка в тексте или нужно слегка его изменить, необходимо искать текст по всему проекту. Другие варианты локализаций инициализируют все тексты в одном-двух событиях - в начале игры и в коде переключения языка, или вовсе во внешнем файле.
- невозможная передача текста переводчикам для перевода. Даже если тексты хранятся отдельно в файле, добавление их в код займёт очень много времени, с вероятностью допущения ошибок.
- ужасное добавление нового языка в проект с большим количеством текста. Чтобы не пропустить какой-либо текст, нужно просматривать все объекты, или использовать поиск по скриптам - draw_text.
   Удобство:
- очень низкое, обратно пропорциональное объёму текста. Самый быстрый и понятный вариант, самый неудобный для исправлений и перевода.


Вариант 7: Копирование игры.
   Плюсы:
- отсутствуют?
- разве что оправдано при наличии объёмных файлов озвучки текста на разных языках, занимающие весомую часть от общего размера игры. Сомнительный фактор, который к тому же можно решить загрузкой файлов выбранной озвучки перед началом игры. Результатом этого решения станет одна копия игры, только с разными внешними файлами.
   Минусы:
- уйма. Следующие минусы раздавят все остальные преимущества, которые возникают при использовании любого варианта хранения текстов в каждой копии игры (переменные, массивы, константы).
- отсутствует возможность переключения языка в игре. Чтобы был другой язык, игроку необходимо скачать эту же игру, но с другим языком.
- при нахождении ошибок в проекте, не только в тексте, необходимо исправлять ошибку во всех копиях игры, собирать и загружать их заново на форумы или серверы (Google Play, AppStore, подобные).
- в конце концов, параллельная разработка одного и того же проекта, делать одно и то же несколько раз по количеству языков.
   Удобство: -
- стремится к нулю. Интересно, кому вообще может быть удобна параллельная разработка одного и того же проекта, но с разными языками.
   Примечания:
- такой вариант тоже может возникнуть у кого-либо, поэтому и его нужно обсудить, однако, скорее всего, о таком может подумать только теоретик, не понимающий всех последующих за таким решением неудобств в сопровождении проекта.




  Вывод: наиболее удобными, популярными и рекомендуемыми оказались варианты локализаций, использующие ini-файлы, ds_map и макросы.
Оставшиеся варианты - с переменными, массивом и switch на месте - приемлемы в небольших проектах с малым объёмом текста, а также легче понимаются новичками.
Вариант копирования игры не рекомендуется.



Прилагаю код парсера текстового файла в ds_map_write - вариант локализации "ds_map".
Структура файла должна быть следующей:
Код: (Образец файла)
//список текстов в игре
Game start=Новая#игра
Game end=Выход

//бонусы
Bonus speed=Ускорение
Очень похоже на ini-файл, но отсутствуют секции в привычном виде. Допускаются пропуски строк и вообще любые строки, не содержащие знак равно, такие считываться не будут. Решётка является принудительным переносом строки. Для кириллицы убедиться, что файл сохранён в кодировке UTF-8.

Код открывает файл с текстами через диалог, переводит текст в ds_map_write и записывает результат в буфер обмена.
Код: (ds_map_write_parser)
var name,f,map,pos,key;
name=get_open_filename('Text|*.txt','');  // you can select file from any location if on Windows
if name='' {exit}

map=ds_map_create();
f=file_text_open_read(name);

while (!file_text_eof(f))           // read full file
{
 str1=file_text_read_string(f);     // read full line
 file_text_readln(f);
 pos=string_pos('=',str1);          // find equal sign
 if pos=0 {continue}                // empty string here, look next line
 key=string_copy(str1,1,pos-1);     // copy text before "="
 if string(ds_map_find_value(map,key))!='0' {show_message('Double key! '+key); continue}
   // found available key again, say to user and don't add the same key again
 ds_map_add(map,key,string_copy(str1,pos+1,string_length(str1)-pos)); // add new key to ds_map
}

file_text_close(f);
clipboard_set_text(ds_map_write(map));  // copy ds_map to clipboard, you can paste it into ds_map_read with Control+V
ds_map_destroy(map);



  Автор: Fantom.
Запрещается копирование данной статьи без ведома и согласия автора.
« Последнее редактирование: Апрель 27, 2016, 14:20:13 от Fantom » Записан

Christopher
GM Pro user
*

Репутация: 66
Offline Offline

Пол: Мужской
Russian Federation Russian Federation

Награды:
500 сообщений!За постоянность! [10 дней на форуме]
API: GameMaker Studio Pro
Деятельность: Инди-разработчик
Сообщений: 597


DragonGameStudios


WWW
« Ответ #1 : Апрель 14, 2016, 13:02:40 »

Ого, это определенно пойдет в копилку  Уважуха
Кстати, я использовал переменные, сохраняя текст в скрипте, вызывая его при надобности, менял локализацию через глобальную переменную(подводный камень таков, что каждый обьект должен был вызывать скрипт\или же обьект контроллер, что вызывало проблемы, где хранить строки)
Записан

life Jumb
GM Pro user
*

Репутация: 235
Offline Offline

Пол: Мужской
Russian Federation Russian Federation

Награды:
1000 сообщений!За постоянность! [100 дней на форуме]За лояльность! [+150 репутации]За добавление полезных программ!Третье место на HellRoom Jam #6 [Игра на конкурс]2 место в конкурсе Адекватные игры #3 [Антиутопия]
API: GameMaker Studio Pro
Деятельность: Программист
Сообщений: 1399



WWW
« Ответ #2 : Апрель 14, 2016, 16:48:29 »

Статья хорошая, самый нормальный вариант, это ds_map

дс_мапов столько же, сколько языков будет в игре (скорее всего 2)
С дублированным содержанием
global.txt_ru=ds_map_create();
global.txt_en=ds_map_create();

Далее либо руками прямо на месте, либо сканируем файл(ы) и заполняем содержимое обоих дс_мапов

Для упрощения поиска значения скрипт t_g("Game start")

Код: (draw_gui)
draw_text(x,y, t_g("Game end"))
В принципе, я бы сделал так.  
Записан

 
Fantom
I am... All of me
Гл. Администратор
*

Репутация: 1311
Offline Offline

Пол: Мужской
Russian Federation Russian Federation

Награды:
5000 сообщений!За постоянность! [500 дней на форуме]За лояльность! [+1000 репутации]За отличные статьи по Game Maker!Тру Админ :DЗнаток Game Maker...
API: GameMaker Studio Master
Деятельность: Experienced Programmer
Сообщений: 5035



« Ответ #3 : Апрель 14, 2016, 17:02:14 »

В моём варианте достаточно одного ds_map, который перезаписывается в начале игры и в момент переключения языка. Перезапись происходит одной командой ds_map_read, если есть парсер, или вызовом event_user/скрипта, который содержит ряд текстов на отдельном языке.
Для меня, такой скрипт в рисовании, и постоянное хранение нескольких ds_map в памяти, немного излишни.
Сам использую ds_map, даже в относительно небольших проектах, привык уже
Записан

life Jumb
GM Pro user
*

Репутация: 235
Offline Offline

Пол: Мужской
Russian Federation Russian Federation

Награды:
1000 сообщений!За постоянность! [100 дней на форуме]За лояльность! [+150 репутации]За добавление полезных программ!Третье место на HellRoom Jam #6 [Игра на конкурс]2 место в конкурсе Адекватные игры #3 [Антиутопия]
API: GameMaker Studio Pro
Деятельность: Программист
Сообщений: 1399



WWW
« Ответ #4 : Апрель 14, 2016, 17:10:27 »

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

 
igorm13
Новичок
*

Репутация: -2
Offline Offline

Russian Federation Russian Federation

API: GameMaker 8.1 Standard
Деятельность: шутерами
Сообщений: 5



« Ответ #5 : Май 23, 2016, 21:46:57 »

хорошо,буду ипользовать!!!!! 
Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  

Vendet | HellRoom Games © 2006-2017 All Rights Reserved
Powered by SMF 1.1.21 | SMF © 2013, Simple Machines