quinta-feira, 26 de setembro de 2013

Unity - Traduzir o jogo para várias línguas

E ai pessoal, tudo bem?

Bom, infelizmente não tenho condições de escrever aqui todos os dias =( é trabalho d+ pra tempo d-, e também trivializa um pouco demais cada post já que eles viriam a falar de coisas muito pontuais...

Mas, hoje e ontem, por acaso, nós trabalhamos em uma coisa que pode ser interessante pra algumas pessoas: traduzir seu jogo para várias línguas!

"Ta me zuando que vou ter que traduzir pra grego 2x?"
Bom, não tem muito mais o que se dizer sobre isso! Usando Unity, existem porções de plugins para manejar fácil as línguas e não sei o que... nós aqui na Fire Horse não usamos nenhum deles (mas talvez devêssemos =P), nós criamos uma solução bem bobinha, mas que tem funcionado muito bem.

Até o momento todos os nossos jogos tiveram de ser no mínimo bilíngues, Commander Flag e Knights Trial tiveram de ser traduzidos para Inglês, Português e Espanhol devido á competição da Square Enix, até Ready, Eat, Fight! teve que ser traduzido para o Português pra colocarmos no ClickJogos. O único que esta sendo perdoado até o momento é o Like a Boss, mas já está sendo montado de forma que essa tradução seja fácil.

No entanto, o caso em que estamos trabalhando agora, e certamente o mais intenso deles... é o Level Up: Heroes of Neverfail. O nosso querido Cartoon Network, não feliz com Inglês, Português e Espanhol, as línguas presentes inicialmente no jogo... nos pediram pra traduzir o jogo também para Frances, Italiano, Russo, Turco e Árabe. Sim, Árabe -.-

Grafico (pouco racista) da globalização chegando no Reino Unido. Quanto mais línguas, melhor!
E desde o Commander Flag... nós usamos exatamente a mesma solução... uma classezinha que eu chamo de LanguageDictionary. Ela é tão idiota que é mais fácil colocar o código dela aqui que explicar o que ela faz.

Você pode adicionar quantos valores quiser no dicionário, 10, 100, 1000, conforme for necessário.
Viu que ridículo? Basicamente eu tenho um dicionario com chaves string e valores string... e onde eu precisar de um texto, eu simplesmente busco no meu dicionario pela chave, e mostro o valor. Não tem como ser mais simples que isso. Toda vez que o jogador troca de linguagem, eu zero o dicionário e coloco os novos valores. Então basicamente, em qualquer string do jogo, invés de escrever "Espada Legal" na mão, eu escrevo "cool-sword". Quando a cena for carregada, o script vai procurar la na minha lista global de variáveis pelo índice do dicionario com cool-sword, e se for em inglês, vai mostrar "Cool Sword", se for em português "Espada Legal", e o meu preferido, se for em árabe vai mostrar "السيف بارد"!

Olha que difícil que é usar o código acima... Se você jogar esse script num GameObject com um TextMesh, ele vai traduzir o TextMesh pra linguagem que estiver ativa com base a chave que você passar.
Claro, isso só serve pra tradução de palavras, mas e imagens? Bom, ai depende. Antes do jogo do Level Up, nós não usávamos nenhum plugin do tipo NGUI, EZGUI e não sei o que... era tudo na mão (na mão, mas não com a GUI do Unity, obviamente (PS: até sair o novo GUI do Unity, NÃO USE O GUI DO UNITY)), o que dificultava um pouco as coisas. Nós criávamos planos, com materiais e texturas. Logo, pra traduzir essas imagens, precisamos criar um dicionário também de string/string, só que ao invés de simplesmente passar o valor do dicionário pra uma caixa de texto, nós usávamos um Resources.Load(LanguageDictionary.stringList["chave"]). Dessa forma, no loading da cena, todas as imagens que precisavam ser trocadas para a língua desejada eram carregadas e jogadas na textura do plano.

Paralaxe de 4 profundidades... estrelinhas... levels abertos, travados... tudo com plano, que coisa bonita... (Commander Flag)
Hoje em dia nós ainda utilizamos o approach precário de criar planos na mão, mas em conjunto com o NGUI. Não usamos NGUI puro porquê por mais milagroso que seja o plugin (e sim, é MUITO bom), ele pode causar (e certamente causará) SÉRIOS problemas de desempenho no seu projeto. Não vou entrar em detalhes sobre isso, mas o negócio é que no NGUI as texturas das imagens não são referenciadas pela textura propriamente dito, mas sim pelo NOME da textura, o que facilita tudo 10 mil vezes. Então ao invés de dar Resources.Load pra trocar a textura, simplesmente colocamos nguiSprite.spriteName = LanguageDictionary.stringList["chave"];, e ao receber o nome correto, o sprite do NGUI troca pra imagem certa.

(PS2: Se você usa NGUI, esse método tem um seríssimo problema. Se alguém quiser, posso explicar em outro post qual é o problema e como resolver esse problema de maneira fácil)

Usando NGUI no Like a Boss. Visualmente parece 10x + putaria que a imagem do Commander Flag... a diferença é que aqui tem uns 4 draw calls com muito mais elementos, enquanto la tinha uns 20... mas nem tudo ai é NGUI.
Esses métodos podem ser utilizados pra traduzir qualquer texto e imagem no seu jogo, sejam menus, legendas, falas, enfim, qualquer coisa. Pra ser bem sincero, eu suspeito que esses métodos, no entanto, tenham um problema meio chato. O primeiro deles é que procurar coisas em um dicionário... e pior ainda, procurar comparando string com string (quando você diz stringList["chave"], internamente o C# tem que procurar na lista toda do que é que você ta falando), é um processo que gera um certo overhead. Sinceramente eu evito ao máximo qualquer comparação de string, e dicionários as vezes não da pra evitar, mas quando possível, uso listas ou arrays. O segundo problema é que se você tem 100, 200, 500 strings... beleza (Level Up tem quase 500), mas conforme esse número vai crescendo, tanto a velocidade de procura quanto o uso de memória da sua stringList vai crescer... bom, digamos assim, se eu tivesse fazendo Mass Effect, não ia dar pra fazer desse jeito vagabundo =P.

Eu acredito que o jeito mais correto e profissional de fazer a coisa seja usando XML. Você cria um arquivo XML com todas as strings, e procura nesse arquivo o que você quer, e mostra o resultado da sua busca dentro do jogo. Esse método eu também vejo 2 problemas: o primeiro é que eu realmente não sei se fazer a busca num XML é la muito mais rápido/melhor que fazer a busca em um dicionário... afinal de contas é string com string do mesmo jeito... enfim, eu não manjo de XML (ou JSON que seja)... e o segundo e ainda maior problema... é que eu não sei fazer desse jeito =P, infelizmente não tive tempo pra ir procurar uma solução pra um problema que eu nunca tive, mas prometo que qualquer hora eu vou e posto um update aqui pra vocês, ou se alguma alma caridosa souber como ou um jeito ainda melhor de fazer essas traduções, deixa um comentário, por favor!

Pra quem ficou curioso, sim, essa é a arvore de skills do Like a Boss. Ainda estamos trabalhando em cima, na verdade essa imagem já é antiga, mas da pra dar uma ideia...
Por fim, resta o problema de traduzir sons... e bem, esse problema vai continuar restando =P. Sinceramente, se eu tivesse que fazer hoje um sistema de tradução de sons ia ser na base do Resources.Load e carregar o som certo na língua certa, mas não sei se essa é a melhor solução. Por outro lado eu suponho que o número de indies (ou índios) brasileiros que tem um jogo pra traduzir em várias línguas e vão traduzir o áudio também não seja o maior do mundo... imagino que o número de jogos indies com NARRAÇÃO de qualquer tipo já não seja muito grande graças ao preço infeliz de fazer uma narração boa pro jogo... (se alguém conhecer um estúdio bom e barato, favor mandar o contato pra gente! contact@firehorse.com.br (PS3: não estou falando que acho narração uma coisa ruim... Bastion só ta nos meus top 5 jogos por causa da narração, e Mass Effect, Dragon Age também tão nessa lista e com certeza a narração ajudou bastante... só digo que é caro a ponto de ser inviável produzir mesmo (pelo menos os orçamentos que já fizemos nos levam a pensar assim (pelo menos com nossa verba limitadíssima))))

Bom, acho que no mais é isso! Se ficar com alguma dúvida, tiver um método melhor ou simplesmente quiser saber mais sobre qualquer assunto aqui do blog, ou até sugerir algum assunto, deixa um comentário ou entra em contato direto com a gente!

Abraços,
Allan

Um comentário:

  1. Passado quase 3 anos e já na versão do Unity 5, vocês continuam usando a mesma solução de Array ? E quanto aos Assets ? São tantas opções como I2 Location, G2U, NGUI que ficamos sem saber qual seria a melhor (Custo Benefício e Performance) para um projeto. No meu caso pretendo ter muito texto para traduzir :-(

    ResponderExcluir