HTML5 Game Development–Princípios da programação JavaScript com Prototype

Game Development & HTML5 & JavaScript

Olá pessoal. Saindo um pouco do habitual hoje vou falar sobre desenvolvimento de games com JavaScript e HTML5. Minha ideia é abordar alguns assuntos que  acho pertinentes sobre o desenvolvimento de games com HTML5 e JavaScript. Estou levando em conta que você já conhece pelo menos o básico de HTML5 e JavaScript portanto vou logo para o lado mais denso da brincadeira.

Entre os vários assuntos associados a este tema vou iniciar esta mini-série falando sobre este recurso poderoso que é o Prototype. Um dos motivos que me levaram a isso se dá ao fato deste ser o meu primeiro objeto de estudo quando decidi me aprofundar no JavaScript. Parte disso se da pelo fato da grande utilização deste padrão. Já quando falamos em desenvolver games com JavaScript, geralmente vamos ter um grande número de objetos, métodos e arquivos scripts. Manter esta estrutura de forma organizada e coesa é na maioria dos casos um desafio.

Sendo assim é importante entender como otimizar a criação dos objetos de certa forma que seja possível diminuir a quantidade de código e risco nas alterações futuras.

Para facilitar, vou disponibilizar o código fonte dos exemplos que pode ser baixado aqui!!!.

Este é um assunto que causa certa estranheza para os iniciantes em JavaScript, principalmente se já desenvolvem em alguma linguagem Orientada a Objetos. O primeiro passo é lembrar que o JavaScript não é uma linguagem baseada em classes e sim orientada a protótipos. Isso significa que (de maneira resumida) o JavaScript não usa classes para definir objetos e sim objetos para criar outros objetos.

Ficou um pouco confuso? Não tem problema, vou tentar esclarecer isso no decorrer deste post. ## Iniciando a conversa As linguagens como C#, C++ e Java são baseadas em classes. Como resultado desta abordagem tradicional, para criarmos um objeto é necessário primeiro criar uma classe que defina as propriedades e métodos deste objeto.  Neste contexto a classe é utilizada como um modelo para ser definir e conhecer como criar o referido objeto. Uma vez que a classe foi criada é só a utilizar para a se obter novas instâncias. Criar uma instância é uma maneira elegante de se dizer que você criou um objeto da classe utilizando o famoso NEW. No JavaScript não temos classes, apenas objetos e os tipos primitivos de dados. Isso justamente por que o JavaScript utiliza o modelo Prototype-based programming. Para simplificar vamos dizer que tudo em JavaScript é um objeto… tudo, até mesmo funções!!!

Então como criar meus objetos?

Em JavaScript temos “basicamente” duas maneiras de se criar objetos: Usando um object literal ou constructor function.

Já estou preparando um post específico sobre este assunto então vou apenas ilustrar estas duas maneiras de criar objetos como no código abaixo:

003

Existe uma série de assuntos a se considerar sobre estas abordagens. No entanto vamos abordar a criação de objetos com função de construtor, que é o mais utilizado por ser ”essencialmente” uma classe.

Observe o código abaixo:

04

Note que primeiro criamos nossa “função construtora de objeto”. Com ela definida podemos utilizar o bom e velho NEW para obter um novo objeto que é uma cópia do objeto “pai”, trazendo suas propriedades e métodos.

Porém reparem que estou utilizando uma função toString() que não está definido no objeto inicial. Isso porque qualquer objeto em JavaScript pode utilizar esta função para retornar uma string de si mesmo. Agora como isso é possível se JavaScript não é uma linguagem orientada a classe? A resposta é: Através do PROTOTYPE!!!

O que é esse tal de PROTOTYPE?

Todo objeto em JavaScript possui uma propriedade implícita chamada [Prototype]. Esta propriedade nada mais é que um ponteiro que guarda a referência ao objeto que vai ser executado via delegation caso a propriedade ou método não seja encontrada no objeto corrente.

Sendo assim quando você pede a um objeto determinado “recurso”, o JavaScript vai primeiro procurar no objeto corrente. Caso não encontre o próximo passo é olhar para a propriedade [Prototype] e procurar o “recurso” no objeto referenciado. Se não encontrar no objeto referenciado ele repete o mesmo ciclo até que a propriedade [Prototype] seja nulo.

Isso é chamado de prototype chain (cadeia de protótipos). Um prototype pode ser acessado utilizando o Object.isPrototypeOf(). Outra forma muito utilizada é acessar a propriedade [Prototype] com __proto__.

Vamos voltar ao exemplo do objeto Pessoa utilizando a função toString(). Neste caso o JavaScript procura a função toString() no objeto Pessoa e não acha. Vai para a propriedade Prototype que aponta para outro objeto. Neste objeto ele realiza o mesmo processo procurando a função e não acha, então, vai novamente para propriedade Prototype que aponta para outro objeto realiza a busca, acha a função e a executa.

005

É simples validar isso apenas utilizando a função hasOwnProperty() que retorna um true ou false indicando se o objeto tem ou não a propriedade indicada.

006

 

E a aplicação prática?

Já vimos que de maneira simples podemos pensar no Prototype como uma maneira de criar heranças frouxas onde é possível adicionar novas características evitando uma declaração global.

A ideia neste exemplo é criar uma função para calcular o XP de um usuário do game. Baseado no nível e na pontuação temos os pontos de experiência. Pois bem, o mais comum é trabalhar essa estrutura de maneira simples como segue o código abaixo:

007

Sem pensar muito é possível encontrar algumas falhas nessa abordagem

  • Fica complexo manter a medida que a função cresce
  • Fica mais difícil debugar
  • Torna os testes mais difíceis
  • Torna as alterações mais complexas e com mais tendência a falha

Não quero me deter nisso já que pretendo retomar este assunto em outro post. Agora vamos pensar neste mesmo código sobre a perspectiva do Prototype. Abaixo temos a criação do nosso objeto sem nenhuma implementação. Logo abaixo temos a referência ao nosso Script1.

009

O Script1 é responsável pela implementação inicial. Neste caso estou fazendo a mesma lógica do código inicial.

008

Agora voltamos ao que falei no início. Dificilmente você vai construir sua aplicação ou game utilizando um só script. Então vamos continuar nossa implementação… agora surgiu a necessidade de em dado momento alterar o calculo do XP.

De maneira muito simples apenas chamamos nosso objeto.prototype e a propriedade a ser alterada (neste caso o método calcularExperience), podemos realizar a alteração desejada, refletindo apenas no momento da necessidade. O resto ficou inalterado e pode ser utilizado normalmente. Vamos ao nosso Script2:

010

Como é possível ver no output continuamos recebendo o mesmo valor para os cálculos anteriores e um novo resultado derivado da alteração que realizamos por último.

 

Várias maneiras de implementação

Outra coisa bacana é como podemos trabalhar o protótipos dentro do JavaScript. Vamos observar o exemplo abaixo:

011

Primeiro criamos um objeto Monstro com apenas uma propriedade chamada vidas. Logo depois crio um objeto monstro1 usando Monstro como protótipo. Agora monstro1 também tem a propriedade vidas.

Já em monstro2 estamos utilizando o Object.create(). Note que de maneira simples estamos setando o número de vidas e criando uma nova propriedade chamada força.

Em monstro3 utilizamos o próprio Object.create() para a criação de propriedade extras. Neste caso criando a propriedade poder.

Podemos criar a mesma estrutura sem utilizar o Object.create().

012

Neste caso estamos criando nosso protótipo (personagemPrototype) e logo após uma função (personagem)  para forçar a “inicialização” para as novas chamadas ao protótipo. Não é a maneira mais simples mas traz o mesmo resultado.

O que é possível perceber nestes exemplos é a facilidade na criação e orientação de novos objetos utilizando prototipagem sem o compromisso de um único “jeito” para a implementação. Vale anotar que dependendo da sua abordagem o entendimento o entendimento futuro pode se tornar bastante complexo.  

 

O que pode ter passado desapercebido, é que através do prototype podemos criar novas características para as "classes" já definidas pelo próprio JavaScript. Como exemplo, vamos colocar um novo método na "classe" String do JavaScript.

Apenas para fins didáticos vamos incluir a função parseInt() a fim de converter uma String em Inteiro de uma maneira simplificada utilizando prototype:

001

Agora podemos chamar o parseInt() direto de uma String em vez de chamar a função e passar uma string a ser convertida [parseInt("950");]

 

Conclusão

Como vimos o  prototype nos permite adicionar características e comportamentos para as "classes", após sua definição, sendo assim podemos reutilizar toda a definição de uma “classe” em outra.

 

Bons estudos e até a próxima pessoal  ;)


Author's profile picture

Vitor is a computer scientist who is passionate about creating software that will positively change the world we live in.

MVP Azure - Cloud Architect - Data science enthusiast


8 minutes to read