
Esta é a primeira parte da história (misturada com a história dos meus erros e suas soluções) sobre como eu (cerca de dois anos no meu tempo livre) desenvolvi um aplicativo móvel para iOS e Android que motivaria minha filha a resolver exemplos de matemática para que ela automatismo alcançado no básico da aritmética (armazém número 10 ou placa de multiplicação). Como resultado, descobriu-se um aplicativo que permite que uma criança ganhe dinheiro com sua mente.
Usei o mecanismo Unity e a linguagem C #, além de um conjunto adicional de software, como o Photohsop ou o Audacity (para criar sons).
O esboço da história (parte um)
- Antecedentes
- Sobre monetização
- Por que unidade
- Sobre objetos graváveis
- Sobre o plugin Anima2D
- Sobre a localização enxuta
- Sobre o iTween
- Sobre a Unity Analitics
- Sobre o Visual Studio
- Referências
Besouros de fundo e Colorado
Minha experiência anterior foi há vários anos em 3D e, mais recentemente, no desenvolvimento de vários jogos independentes, juntamente com um programador, onde atuava principalmente como designer e artista (muito raramente escrevendo alguns scripts elementares em C #). Embora eu esteja familiarizado com programação em primeira mão (costumava bater no Basic na escola e me envolvia em C / C ++ na universidade).
Todo esse épico começou com um problema. Vi que, ao resolver os problemas da escola, minha filha “pára” e se engana, não em algo complicado, mas com base. Decidi que seria divertido se eu escrevesse uma aplicação divertida para ela, onde ela resolveria exemplos em forma de jogo (ganhando experiência dessa maneira e obtendo automatismo). E, para motivá-la ainda mais, certifiquei-me de que ela recebesse dinheiro pela decisão correta dos exemplos (a quantia em dinheiro foi calculada pelo aplicativo com base no número de respostas corretas e depois deduzi a quantia necessária, pagando à minha filha em dinheiro).
Embora ... essa história tenha começado ainda mais cedo. Primeiro, fiz para minha filha um aplicativo que pagava dinheiro para aprender palavras em inglês. Mas esse aplicativo acabou sendo muito mais difícil de implementar, tornando-se conveniente não apenas para mim (o desenvolvedor) e minha filha, mas também para outros pais. Portanto, o aplicativo em inglês ainda é um desenvolvimento interno.
Eu escolhi o dinheiro como motivador, porque era o mais fácil de implementar. E também por causa das lembranças pessoais da infância: eu adorava fazer tudo por dinheiro. Mesmo que fosse um trabalho muito tedioso, como colecionar besouros de batata do Colorado. Lembro-me de pegar o pote de meio litro (meus pais me pagaram 1 copeque por cada inseto). Então pensei que desde a minha filha (não verifiquei, mas tenho certeza), ela deveria gostar. Bem, no final, eu não perdi.
Minha esposa é contra incentivar os filhos a fazer algo por dinheiro. Mas meu argumento a convenceu um pouco: no caso desse pedido, a criança recebe dinheiro não pelo que deve fazer (como lição de casa), mas pelo fato de praticar adicionalmente a matemática em seu tempo livre.
A segunda razão pela qual comecei a desenvolver foi porque eu queria praticar programação. “Tornar-me programador” tem sido meu sonho acalentado desde as primeiras séries da escola (logo após o sonho de “tornar-se cientista”, mas antes do sonho de “fazer desenhos animados”).
Monetização e prazer
Inicialmente, fiz esse aplicativo (o nome de trabalho era Math4Ami) exclusivamente para o iPod touch 5 da minha filha. Eu nem pensei em disponibilizar o aplicativo em todos os dispositivos iOS ou publicá-lo para todos e, especialmente, não pensei em liberá-lo para a plataforma Android (ouvi muitas histórias assustadoras dos desenvolvedores do iOS, além disso, não tenho nada para testar a versão do Android).
Há algum tempo, fiquei tentado a publicá-lo na AppStore (fiquei muito atraído pelo pensamento de ter meu próprio aplicativo na loja da Apple e todos pudessem vê-lo).
Eu pensei que em um mês eu vou conseguir. Afinal, toda a funcionalidade estava pronta, a única coisa que restava era fazê-la funcionar em todas as proporções da tela e compreensível não apenas para mim, mas também para outros pais. E agora, seis meses depois, publiquei na AppStore e no Google Play.
Inicialmente, decidi tornar o Math4Ami totalmente gratuito, sem uma pitada de monetização. Existem várias razões para esta decisão.
Primeiro. Como você já entendeu, inicialmente eu a disponibilizei gratuitamente para minha filha e não queria estragar nada no "final" do desenvolvimento.
O segundo Decidi que seria um desenvolvimento para meu próprio prazer. Eu já tenho uma experiência semelhante - faço um blog por prazer (que inicialmente consumia apenas dinheiro: dinheiro para hospedagem e nome de domínio, tempo para escrever artigos e sua promoção). Minha lógica era a seguinte: se eu pago dinheiro para visitar o parque aquático, compro um livro ou sorvete para me divertir, por que não posso me dar ao luxo de pagar pela hospedagem, um nome de domínio ou associação ao Apple Developer Program, se isso também me traz prazer.
A terceira - em prol de um público mais amplo, que seria significativamente reduzido, tornando o aplicativo pago (como fazem muitos desenvolvedores de aplicativos infantis). Demiti outros tipos de monetização pelos motivos descritos abaixo.
Não suporto a publicidade no jogo - não gosto quando o design do aplicativo é desfigurado por mensagens publicitárias (exceto publicidade sob a forma de exibições de vídeo à vontade , e não quando o vídeo aparece na esquina). Além disso, para participar dos programas "Made for kids" na Apple e "Designed for Families" no Google, você precisa filtrar estritamente os anúncios exibidos para crianças.
Compras no jogo, eu, como pai, bloqueio em todos os dispositivos e filhos, quando eles fazem o download de aplicativos por conta própria, simplesmente não podem comprar nada dentro do aplicativo. Outra coisa é quando o próprio pai inicialmente compra o aplicativo para a criança (mas eu já disse sobre isso acima).
Por que unidade e como
Eu escolhi o Unity porque trabalhei nele antes e gostei. Eu também tinha um bom amigo como programador de C # e esperava que ele me ajudasse com a programação, se isso. O Unity também possui uma comunidade maravilhosa e é muito fácil encontrar respostas no Google para quase todas as perguntas relacionadas à implementação de algo no C # + Unity.
Também trabalhei com a Unreal (como artista 3D), mas não entendi a funcionalidade C ++ ou 2D.
Inicialmente, o Math4Ami estava "nublado", embora seja dito em voz alta. Todos os dados foram armazenados no meu eVPS (Elastic Virtual Private Server) e usei o FTP para transferir arquivos TXT com configurações de dados e aplicativos (minhas mãos não chegaram ao banco de dados, embora eu tenha escrito as primeiras etapas ao escrever meu servidor no node.js empreendeu). Para trabalhar com ftp, prendi ao Unity a Classe FTP C # Simples e fácil de usar.
Então, quando decidi tornar o aplicativo público, abandonei o servidor.
Por um lado, seria muito confuso: fazer autenticação (os usuários não gostam disso) ou salvar o identificador da sessão no iCloud usando NSUbiquitousKeyValueStore (isso identificaria automaticamente o usuário entre desinstalar o aplicativo e reinstalá-lo), mas ainda não o descobri. com isso (talvez o artigo em que escrevi o plug-in do Unity corretamente me ajudasse . Parte 1: iOS , mas ainda não era).
Por outro lado, os dados neste aplicativo não são tão importantes que precisam ser armazenados no servidor.
Por outro lado, não havia necessidade de sincronização do servidor. Aqui para o meu pedido de ensino de inglês - sim, era necessário sincronizar. Como o pai adiciona novas palavras no aplicativo pai e a criança as ensina no aplicativo infantil (embora talvez eu seja um amador para complicar as coisas).
Como resultado, verifiquei se tudo estava armazenado localmente (no dispositivo), mas não no txt, mas no formato JSON.
ScriptableObject e respostas corretas
O formato JSON em conjunto com o ScriptableObject acabou sendo uma descoberta maravilhosa. Usei os métodos nativos do UnityEngine para serializar objetos em json - JsonUtility (e salvei os arquivos de texto json localmente no dispositivo na pasta Application.persistentDataPath ).
ScriptableObject (SO) é um tópico separado da conversa, mas continuarei a abordá-lo. Eu nem consigo imaginar como eu vivia sem SO.
Tudo o que eu uso no meu trabalho, recebi desses dois vídeos mega úteis sobre os princípios de trabalhar com o SO (e o código que acompanha o GitHub e o Bitbucket):
Pessoalmente, usei o SO para tais propósitos:
- Para armazenar dados (para que você não precise entrar no código todas as vezes para adicionar novas funcionalidades ou dados):
- variedade de exemplos
- tipo de moeda
- estilo de botão (eu tenho os mesmos botões em muitos lugares e apenas crio skins para eles com base no SO),
- valor da recompensa, etc.
- Como variáveis globais (que são visíveis em todas as cenas):
- número de respostas corretas
- quantidade de dinheiro ganho
- configurações atualmente ativas
- tipo atual de exemplo
- temporizador, recordes, etc.
- Para armazenar lógica (por exemplo, assinando o evento de recebimento da resposta correta).
O único ponto negativo do SO para trabalhar com dados é que você não pode armazenar dados entre as sessões do aplicativo: os ativos do SO (após uma inicialização a frio do aplicativo) sempre conterão os dados que você escreveu lá no editor. Portanto, a lógica do meu trabalho é a seguinte:
- Depois de iniciar o aplicativo, leio os arquivos json do disco e carrego os dados deles nos ativos SO (método FromJsonOverwrite).
- Enquanto o aplicativo está em execução e preciso de desempenho máximo - só trabalho com ativos de Objeto de Script. Esses ativos armazenam dados o tempo todo enquanto o aplicativo está em execução ou em segundo plano.
- Quando você precisa salvar dados (por exemplo, no encerramento do aplicativo ou enquanto trabalha), serializo SO para json (método ToJson) e os salvo em disco.
Existe uma desvantagem (óbvia) dessa abordagem - você não pode salvar apenas um parâmetro alterado no disco (se houver vários deles no SO), é necessário salvar o arquivo de texto json inteiro o tempo todo.
Mas muitos dados não precisam ser salvos no disco (por exemplo, o número atual de respostas corretas) e, em seguida, o SO é uma ferramenta poderosa que me permite simplificar bastante o trabalho.
No vídeo abaixo, mostro um exemplo da minha implementação da contabilidade de respostas corretas e incorretas usando o UnityEvent (evento - o número de respostas corretas mudou) + Ouvinte (os ouvintes fazem algum trabalho se ouvem que a resposta correta é recebida, e a lógica dos ouvintes que assinam o evento também é implementado em SO) + SO (controla o número de respostas corretas):
Assim, não posso apenas inserir as respostas corretas e erradas com as mãos, mas simplesmente movendo o controle deslizante, gerar novos exemplos e testar a lógica do aplicativo.
Anima2D, personagens e sorriso contorcido
O vídeo acima mostra que, quando um novo centavo cai, os outros centavos começam a sorrir amplamente e, quando um cocô cai, os centavos ficam horrorizados.
Durante muito tempo, não pude evitar a falha, quando, ao mudar de um tipo de sorriso para outro, a mudança não ocorreu instantaneamente, mas piscou (de um estado para outro) por um tempo. Além disso, vou lhe contar como percebi isso e como derrotei essa falha.
A mudança de expressões faciais é implementada usando o script Sprite Mesh Animation, que faz parte do poderoso plugin Anima2D (que o Unity recentemente comprou e liberou ). Esse script basicamente alterna os sprites para a boca (sorriso, sorriso aberto, boca assustada) usando o controle deslizante Quadro :

Toda a emboscada é que o valor do controle deslizante Quadro não pode ser alterado diretamente dos scripts, mas apenas através de um sistema de animação. Portanto, criei uma nova camada de animação OpenSmile (seta 1 na figura abaixo) no modo de mistura Aditiva com um peso = 1 e adicionei animação de horror ( Coin_scared ) e um sorriso largo ( SmillingWide ).
A propósito, você percebeu o mau exemplo que estou dando, com nomes de animação? Eu ainda estou no processo de trazer nomes para um único estilo. Seria correto alterar Coin_scared para A_CoinScared (por que basta ler isso na seção “Do que me arrependo”).
Criei uma nova camada e não usei a antiga, porque não queria substituir a animação da boca. Tudo o que eu precisava fazer era mudar o espírito da boca (de sorriso para sorriso largo ou de sorriso para horror) e para que a animação da boca permanecesse da camada de base. Por isso, escolhi o modo de mesclagem aditivo - adicionando uma nova animação a uma existente (sem substituí-la).
Em seu núcleo, as animações SmillingWide e Coin_scared são apenas animações do controle deslizante Quadro nas posições 1 e 2, respectivamente.

Todo o problema era que a transição de qualquer estado para o estado de horror (quando você clica em uma transição (seta 2 na figura acima), o inspetor abre as propriedades dessa transição (seta 3 na figura acima)) não aconteceu instantaneamente, mas suavemente durante a duração da transição (seta 4 na figura acima), que não era nula por padrão. Portanto, o valor do controle deslizante Quadro não pôde ser alterado corretamente, porque havia apenas números inteiros, o que significa que não há valor intermediário entre 0 e 1. Portanto, para se livrar da falha intermitente, era necessário redefinir o valor da Duração da transição .
Bem, o gatilho isScared (seta 5 na figura acima) serve como condição para entrar em um estado de horror. Ativo esse gatilho no código usando a seguinte chamada para o objeto no qual o componente Animator trava (com o controlador, cujas camadas foram mostradas acima):
...GetComponent<Animator>().SetTrigger("isScared");
Como traduzi o aplicativo para diferentes idiomas
Em algum lugar aqui, em Habré, li que você precisava pensar em localização no início da criação do aplicativo e segui esse conselho ... imediatamente ... após um ano e meio de desenvolvimento (assim que decidi que o Math4Ami seria público).
Por que escolhi a Localização Lean (exceto pelo fato de o plug-in ser gratuito) não me lembro, mas lembro que escolhi por um longo tempo e bastante.
Usá-lo acabou por ser muito simples. Você pode definir manualmente o idioma ou usar a detecção automática de idioma. Eu decidi pela detecção automática de idioma (seguindo o exemplo de aplicativos de outras crianças).
O plugin traduz tudo (de texto a sons e imagens).
Mas ainda cometi um erro na localização (embora tenha cometido isso intencionalmente, porque queria experimentar abordagens diferentes). O erro é que eu não coloquei todas as frases em um arquivo de texto (à esquerda na figura abaixo). Algumas frases permaneceram dentro do componente Localização Lean (à direita na figura abaixo). Então agora, quando eu entrego esse arquivo para um tradutor japonês, tenho que trabalhar manualmente (para transferir TUDO para um arquivo de texto).

Embora algumas coisas não possam ser traduzidas com um arquivo de texto (como o espaço "", que eu usei como separador entre milhares) - você ainda precisa usar o componente.
Juicy iTween
Era uma vez, assisti a um vídeo lindo de Juice ou perdi-o sobre como todos os tipos de pequenos movimentos e nuances de animação ajudam a fazer uma ação de tirar o fôlego em um jogo chato. E mesmo antes disso, outro vídeo afundou em minha alma - A arte do screenhake , que na verdade não é apenas e nem tanto sobre trepidação da tela.
Durante todo o tempo em que estava criando o Math4Ami, lembrei-me dos conceitos dos vídeos acima, bem como da ideia de que toda essa animação adicional deveria ser a mais curta possível e agir mais no subconsciente do que no consciente. Às vezes, gastei mais tempo adicionando “suculência” do que adicionando funcionalidades úteis.
Apenas um lugar me incomoda muito - a contagem final do dinheiro ganho (você pode ver esse momento no final da minha demonstração em vídeo acima). Abreviei-o assim que pude, mas ainda leva um pouco mais de 4 segundos (o teclado desaparece, a vitória da inscrição aparece, copecks estão sendo contados, a tabela de registros é encerrada, a placa de identificação "New Recorder" é exibida, o botão "More" é exibido).
A melhor "fonte de suco" para mim é o complemento gratuito iTween . Não consigo nem imaginar como, sem o Unity, você pode fazer qualquer coisa. Eu o uso sempre que pelo menos algum tipo de animação é necessário (seja uma animação de um botão ou a aparência de um item de menu ou animação de contar centavos).
Tentei implementar algo semelhante por conta própria, com base em corotinas e Mathf.Lerp ou Mathf.MoveTowards, mas não era flexível nem universal (e às vezes funcionava de maneira diferente no editor e no dispositivo). Então agora não estou tentando reinventar a roda, apenas gosto do iTween.
Existem armadilhas neste sistema de animação, com as quais eu lutei inicialmente incorretamente:
- Se durante a operação do iTween para ocultar um objeto (via SetActive (false) , por exemplo) e depois mostrá-lo novamente, o iTween continuará sendo executado a partir do local interrompido.
- Se durante a operação de um iTween você iniciar outro (que afeta os mesmos valores), no final da execução de ambos, o objeto poderá não retornar à sua posição original.
- Você precisa acompanhar qual GameObject inicia o iTween e em qual animação ele funciona.
Por exemplo (no último ponto), o objeto A inicia o iTween para que ele funcione no objeto B. Para interromper a animação do iTween, não é possível iniciar o iTween.Stop () no objeto A. Você precisa iniciar o iTween.Stop (objeto B).
A força do iTween é sua capacidade de usar diferentes tipos de flexibilização. Ising é um parâmetro que suaviza o movimento (para que ele não comece instintivamente e não termine estupidamente).
Achado impressionante para mim são os tipos de ising:
- primavera
- easeOutBounce
- easeInBack
- easOutElastic
Para encontrar a ising certa, uso a demonstração visual Easing (preciso de um flash). E aqui eu pego a documentação para todos os tipos de animações do iTween .
As estatísticas da Apple e do Google são boas, mas o Unity Analytics é melhor
Mesmo com a experiência de jogos anteriores, eu sabia que ter suas próprias estatísticas é muito legal. No começo, eu queria criar algum tipo de sistema de registro, mas depois lembrei da Unity Analitics . E qual foi a minha surpresa quando descobriu que a versão gratuita da funcionalidade para o meu caso não é limitada por nada. Teria pior se tivesse algum tipo de monetização. As ferramentas de análise estão disponíveis apenas para assinantes do Pro.
Apenas incorporando o Analytics.CustomEvent no lugar certo no código, posso rastrear quais exemplos são mais populares, quantas crianças resolvem exemplos nos primeiros dias ou depois de um tempo etc.
Posso comparar dados de diferentes plataformas (iOS e Android) em um só lugar.
E quantas coisas são interessantes lá, o que eu gostaria de tentar, mas nem todas as mãos alcançam. Digite Configurações remotas (alterando o conteúdo do aplicativo sem atualizar) ou Teste A / B ou Gerenciador de tutoriais .
Visual Studio como Sublime
No passado, quando eu precisava editar algum código (seja python, html ou node.js), usei o Notepad ++ (totalmente gratuito, mas apenas no Windows) e o Sublime Text (pago por todo o sistema operacional, mas você pode experimentá-lo gratuitamente) )
No Unity, eu estava sentado no MonoDevelop, mas ele estava tão cansado de mim com suas falhas (como a incapacidade de alternar entre layouts ou colar algo copiado fora do Mono) que decidi que era hora de deixar o navio afundar e subir na Comunidade do Visual Studio 2017 (bom, é gratuito para desenvolvedores individuais como eu).
Para os desenvolvedores no Unity 2018, isso não é relevante no momento, pois o código do Visual Studio multiplataforma está incluído na versão 2018. Mas eu queria que meu aplicativo funcionasse no iOS 7 (já que o iPhone da minha filha está com esse iOS), então tive que usar qualquer versão do Unity anterior a 2018.
Isso me ajudou na transição para o vídeo VS Como configurar o Visual Studio com Unity .
Desde o início, o VS não possui todas as coisas legais com as quais estou acostumado em outros editores, então simplifiquei minha vida:
- ativado o minimapa em vez de um simples deslocamento vertical:

- adicionou a extensão SemanticColorizer , que permite uma personalização mais flexível das cores do código. Especificamente, eu precisava distinguir variáveis globais de variáveis locais por cor.
- instalou a extensão Match Margin , que seleciona a palavra sob o carro e todas as suas cópias de acordo com o texto do código, e também faz isso no minimapa. Isso é muito conveniente para a navegação rápida por código, para encontrar todos os locais onde algum método ou variável é usado:

- Eu uso o Strip'em para corrigir automaticamente as terminações de linha.
Meus scripts para este aplicativo estão no GitHub. Existem apenas meus scripts , e não o projeto inteiro do Unity - desculpe, se isso os torna impossíveis de entender. Até o último momento, não planejei fornecer um link para as fontes, porque não considero meu código aquele que valha a pena focar. Mas então ele mudou de idéia por causa da chance de que desenvolvedores mais experientes pudessem apontar meus erros.
Este é o fim da primeira parte. Continue a ler na segunda parte , onde direi:
- Sobre como escrever código
- Sobre o controle de versão
- Sobre dublagem
- Sobre o ícone
- Sobre o Android Build
- Sobre a compilação para iOS
- Sobre o título e a promoção
- Estatísticas
- Do que me arrependo
- O que entendeu
Referências
Lista de links do corpo do artigo na ordem em que são mencionados:
+
Classe FTP C # simples.+
ID da sessão para iOS.+
Escrevemos o plugin para o Unity corretamente. Parte 1: iOS .
Métodos para serializar objetos em JSON (ajuda oficial).
+
ScriptableObject (ajuda oficial).
+ Vídeo tutorial
Arquitetura do jogo com objetos programáveis (
código ).
+ Workshop
Derrubando a tirania do MonoBehavior em uma gloriosa revolução de objetos que podem ser gravados (
código ).
+
Demonstração em vídeo da minha aplicação no editor .
+
Plugin Anima2D gratuito
para animação esquelética de caracteres 2D .
+ Biblioteca gratuita para localização de aplicativos -
Localização Lean.+ Vídeo sobre truques que melhoram a percepção do jogo.
+ Vídeo sobre técnicas subliminares de atuação da animação
A arte do screenshake .
+ Grátis, mas poderoso
sistema de animação
iTween .
+
Demonstração visual
dos tipos ising (precisa de um flash).
+
iTween (ajuda oficial).
+
Unidade Analítica .
+ Editores de texto
Notepad ++ e
Sublime Text .
+
Comunidade do Visual Studio 2017 e
Código do Visual Studio .
+ Tutorial em vídeo
Como configurar o Visual Studio com Unity .
+ Plugin
SemanticColorizer (para configurações de cores de código).
+ Plugin
Match Margin (seleciona a palavra sob o carro e todas as suas cópias).
+ Plugin
Strip'em (correção automática de terminações de linha).