Baixe o app para aproveitar ainda mais
Prévia do material em texto
Programação para jogos 2D Unidade 2 - Projeto de jogo: Breakout Autor: Vanessa Pereira do Nascimento Revisor técnico: Renato Medeiros Iniciar Introdução A programação de jogos 2D de maneira orientada a objetos foi introduzida na unidade anterior. Até este momento você entrou em contato com os seguintes principais conceitos de Orientação a Objetos: classes, objetos, atributos, métodos, construtores, hierarquia, encapsulamento e polimor�smo. Vimos também os principais elementos da interface da game engine Unity , além da programação de um de seus scripts na IDE (Ambiente de desenvolvimento integrado) Visual Studio . A pequena inserção de código que �zemos, os conceitos que foram abordados e a apresentação da engine servirão de base para o conteúdo desta e das próximas unidades. Nesta segunda unidade, conforme adiantado pelo seu título, você irá programar seu primeiro jogo 2D na Unity . Tendo como ponto de partida um jogo clássico, o Breakout (1976) da empresa Atari, iniciaremos um exercício de abstração e aplicaremos os conceitos estudados até aqui no desenvolvimento desse projeto. Na primeira parte desta unidade, você poderá aplicar os conceitos de OO enquanto cria as relações necessárias para o desenvolvimento de uma versão do game Breakout . Serão abordadas as diversas maneiras de se construir uma classe, incluindo termos como agregação, composição, classes estáticas e abstratas, dentre outros. Durante a segunda parte, já trabalhando na Unity , criaremos a interface grá�ca do jogo, ou seja, todos os elementos visuais necessários para seu funcionamento. Essa etapa abordará estes elementos básicos da engine : GameObjects , Componentes, Tipos primitivos, Sprites , Câmera, Luzes, Skybox e Prefabs . Na terceira parte desta unidade, iniciaremos a programação de scripts em C#, utilizando simultaneamente a Unity e o Visual Studio . Serão desenvolvidos os códigos para a movimentação dos objetos e sua interação. Na quarta e última parte você aprenderá a centralizar o controle do game. Prepare-se para pôr em prática todos os conceitos de programação de jogos 2D aprendidos até aqui. Bons estudos! 1. Construção das classes As classes representam um dos conceitos base da POO, já que é a partir da de�nição de uma classe que torna- se possível a criação de objetos, também chamada de instanciação. Uma instância é uma das cópias de uma classe, é o objeto que apresenta os atributos e métodos de�nidos pela sua classe. Na Orientação a Objetos precisamos, portanto, de�nir as classes que criarão os objetos para interagirem entre si. Conforme foi abordado na unidade anterior, é através do exercício de abstração que é possível classi�car objetos em conjuntos, de�nindo suas características e comportamentos. A abstração nos permite separar um elemento de sua realidade, tornando possível transpor objetos do mundo físico para o universo computacional. Ao projetar um jogo, ainda que este apresente um universo fantástico, representamos ali elementos conhecidos do mundo físico, com características e comportamentos que podem ser agrupadas em diferentes conjuntos. Sendo cada um desses conjuntos uma classe, é importante conhecermos as possibilidades de construção de classes em OO. 1.1 Relacionamento entre classes Em programação é muito comum a utilização de diagramas, permitindo a representação grá�ca dos sistemas. Através de um diagrama, por exemplo, é possível visualizar as conexões entre comandos de uma Programação Estruturada. Em POO, um diagrama permite visualizarmos a comunicação entre os objetos. Devido a OO não ser focada nas ações de um sistema, sua visualização deve ser feita de maneira que possibilite a representação de seus conceitos, do seu foco na relação entre seus objetos. Para essa tarefa é utilizada a UML ( Uni�ed Modeling Language) , isto é, a Linguagem de Modelagem Uni�cada. A ULM não se trata de um diagrama, pois é uma linguagem que possui códigos próprios para construir e documentar um sistema. Como é possível utilizar diferentes tipos de diagramas de ULM na representação de um sistema, durante esta disciplina será utilizado apenas um dos tipos mais utilizados: o diagrama de classes. Em um diagrama de classes são descritos os tipos de objetos e suas relações. Em seu desenho são inseridas caixas (retângulos) para representar as classes e linhas e/ou setas que indicam suas relações. Cada caixa indica o nome da classe, junto a seus atributos e métodos. Por enquanto, utilizaremos essa representação sem todos os detalhes, pois a notação especí�ca do diagrama de classes representa cada tipo de atributos e métodos de uma determinada maneira. Veja um modelo de representação de classes: Figura 1: Diagrama de classes simpli�cado. Fonte: Proprio autor, 2020 Dentre os principais tipos de relacionamento entre classes estão: generalização, realização, dependência, associação, agregação e composição. O que foi representado na imagem acima é a relação do tipo associação. Seguem as de�nições dos tipos de relacionamento de classes e como são representadas no diagrama: ● Generalização Uma classe que abrange características mais gerais, sendo possível especi�car tais características em subclasses. Na generalização vemos aplicado o conceito de herança e a possibilidade de aplicar o polimor�smo. Esse tipo é representado por uma seta que parte do topo da classe �lha e aponta para a base da classe mãe; ● Realização Esse tipo descreve a relação entre uma interface, espécie de contrato implementado em uma classe, e essa classe, já que esta última não herda mas sim implementa a anterior. Essa relação é representada por uma seta tracejada que parte do topo da classe e aponta para a base da interface implementada; ● Dependência Relação criada quando uma classe utiliza em seus métodos outra classe, consequentemente, ao modi�car o método na segunda, a primeira também é modi�cada. É representada por uma seta tracejada que parte lateralmente da primeira classe, a classe dependente, e aponta para a segunda. ● Associação Quando os objetos de classes diferentes podem se comunicar. É representada por uma linha que liga as classes lateralmente; ● Agregação Um tipo especí�co do relacionamento de associação. Na agregação, duas classes existem de maneira independente à outra. Objetos podem ser criados em uma classe tendo como base a outra, contendo assim a outra classe dentro dela. É representada por uma espécie de seta, que conecta as classes lateralmente, e apresenta um losango vazado em sua ponta. Ela parte da classe que é contida e aponta para a classe que a contém. ● Composição Outro tipo de relacionamento por associação. Na composição, uma classe é composta por outra, isto é, a existência de uma classe só é possível devido a existência da outra. Caso uma classe que compõem outra seja removida, essa também será. Sua representação no diagrama é semelhante a do tipo agregação, mas o losango é preenchido, apontando para a classe que é composta por aquela que estiver ligada a ela. Além de de�nir o tipo de relacionamento entre uma classe, temos que de�nir se essa será concreta, abstrata ou estática. Pode haver ainda a necessidade de se criar uma interface. Durante a construção de classes, portanto, é importante ainda o conhecimento das seguintes variações: A partir do próximo tópico você começará a pôr esses conceitos em prática. Até o �nal desta disciplina, você será capaz de construir classes para a programação de seus jogos, de�nindo seu tipo e suas relações. 1.2 O game Breakout Durante a primeira unidade desta disciplina, você exercitou a construção de classes ao propor uma versão do game Pong (1972). Agora que você tem mais informações sobre esse processo, vamos fazer um exercício de abstração para organizar os elementos do jogo Breakout (1976), um dos games derivados do Pong . ● Classe concreta Permite a criação de instâncias e todos os seus métodos podem ser implementados. Utilizada para a criação de objetos; ● Classe estática Não pode serinstanciada nem herdada, pois somente possui atributos e métodos auxiliares estáticos, isto é, que se referem a classe como um todo e não a objetos. Utilizada para não permitir sua instanciação; ● Classe abstrata Uma classe base que não pode ser instanciada, pois contém ao menos um método abstrato, isto é, um método que não pode ser implementado, apenas uma de�nição genérica de algo que será especi�cado em cada uma de suas classes derivadas. Utilizada para centralizar atributos e métodos que serão herdadas por outras classes, suas �lhas, que serão classes concretas; ● Interface Não é uma classe, mas sim uma espécie de contrato que as classes com que se relaciona devem assinar. Em uma interface todos os seus métodos são abstratos e as classes que a implementam são obrigadas a implementá-los. Utilizada para aplicar o conceito de herança múltipla, no qual uma mesma classe implementa duas ou mais interfaces. Assim com o Pong , o Breakout foi lançado em arcade, mais conhecido no Brasil como �iperama. Essa máquina integra o sistema do jogo com todo o hardware necessário para jogá-lo. Por ter sido criado nos anos iniciais do desenvolvimento de jogos eletrônicos, a estrutura do Breakout não é muito complexa, mas já apresenta novos elementos em relação ao Pong . Os elementos adicionais no Breakout são alguns blocos localizados no topo da tela. Ao serem atingidos pela bola, tais blocos desaparecem e o jogador acumula pontos. O jogador controla uma espécie de raquete que �ca na base da tela e apenas se movimenta para os lados. Com a tarefa de não deixar a bola cair, o jogador deve destruir a maior quantidade possível de blocos no menor número de tentativas. Figura 2: Uma das versões do game Breakout. Fonte: wikimedia, 2020 Contendo essas poucas regras, o jogo permite que sejam criadas diversas variações, que podem ser facilmente encontradas na internet em uma simples pesquisa. Durante esta unidade, criaremos uma versão de Breakout próxima àquela lançada em 1976, mas dentro do que já foi aprendido nesta disciplina. Partindo do que foi abordado nesta aula, vamos de�nir como esse jogo pode ser representado em classes. Por ter sua mecânica muito simpli�cada em relação a jogos atuais, o Breakout não necessita de um exercício muito complexo de abstração. Além disso, programaremos uma versão do jogo já existente, eliminando assim a etapa de abstrair características e comportamentos do nosso mundo físico. Seguindo a �gura 2, que apresenta um exemplo de versão do jogo, podemos identi�car 5 elementos diferentes: o jogador, a bola, os blocos, a pontuação ( score ) e as vidas ( lives ). Para esse primeiro exercício, podemos considerar cada um desses elementos como diferentes classes. Observe que a partir da classe blocos serão criadas diversas instâncias, cada uma contendo, ao menos o atributo cor, com alguma variedade. Durante o desenvolvimento do game continuaremos a criar relações entre os conceitos estudados e sua aplicação prática. Você quer ler O livro A condição eletrolúdica: Cultura visual nos jogos eletrônicos, do autor Guilherme Xavier, traz um histórico da origem dos games, incluindo a era dos arcades . 2. Elementos de construção do jogo Para iniciar o desenvolvimento de uma versão do game Breakout , abra o Unity Hub e crie um novo projeto a partir de um template 2D. Lembre-se de nomear o arquivo e indicar um diretório do computador para encontrá- lo facilmente. A primeira vez que a Unity abrir o projeto levará mais tempo para carregar seus elementos, o que não acontecerá nas próximas vezes Primeiramente, vamos inserir os sprites que serão necessários. Para esse projeto apenas utilizaremos os tipos primitivos do software , isto é, as formas disponíveis em seu menu de opções, sem incluirmos as possibilidades de importação de grá�cos. Utilizando o menu Assets > Create > Sprites , adicione dois quadrados ( square ) e um círculo ( circle ). Renomeie respectivamente para Jogador, Blocos e Bola. Arraste os três objetos para a cena, a Scene . Foram criados um GameObject para cada uma de suas inserções, já que esse elemento na Unity corresponde a um objeto. Observe que a câmera também é um objeto dentro da engine , e precisa existir para conseguirmos fechar o jogo e para visualizá-lo no painel Game . Salve seu projeto em File > Save . Antes de adicionar diversos blocos na cena vamos transformar esse GameObject em um Prefab , isto é, um modelo que servirá de base para diversas cópias. Como uma espécie de template de um GameObject , um Prefab permite a criação de instâncias na cena. Dessa maneira, assim que as propriedades de um forem modi�cadas é possível aplicar a todos. O Prefab é indicado para a criação dos blocos pois permite essa alteração em massa, já que um asset não tem componentes, somente quando é inserido como objeto na cena. Para transformar nosso GameObject Blocos em um Prefab é necessário arrastá-lo para uma pasta do painel Project . Clique no menu Assets > Create > Folder e renomeei a pasta criada, por exemplo, chame-a de Prefabs . Arraste o GameObject Blocos, que está localizado na janela Hierarchy , para dentro da pasta Prefabs criada no painel Project . Automaticamente a Unity exibe o ícone do GameObject Blocos em azul, indicando se tratar de um Prefab . Você pode até mesmo deletar o GameObject da janela hierarquia que ele continuará existindo na pasta Prefabs , pronto para ser adicionado novamente. Posicione alguns blocos na cena arrastando-os da pasta Prefabs , ao invés do asset Blocos que você criou através da adição do sprite . Figura 3: Elementos do projeto Breakout. Fonte: Proprio autor, 2020 Ao clicar em um dos objetos da cena, você poderá ver seus componentes de�nidos por padrão na janela Inspector. É possível modi�car seu posicionamento alterando os eixos x e y na opção Position , assim como suas dimensões na opção Scale . Aproveite para melhorar a aparência dos objetos da cena, já que por padrão a bola �ca muito grande e o jogador quadrado di�culta a jogabilidade. Clique no jogador e utilize as opções de escala ( Scale ) no painel Inspector para modi�car seu formato. No exemplo da �gura 3 suas dimensões são 1.5 em x e 0.3 em y. A bola foi con�gurada com 0.3 em ambos os eixos. Como nossos blocos agora são Prefabs , faremos essa adição em apenas um deles e aplicaremos em todos os demais automaticamente. Selecione um dos blocos na cena e modi�que suas dimensões. O exemplo da �gura 3 apresenta as seguintes medidas: 1 em x e 0.4 em y. Ainda no painel Inspector clique em Overrides > Apply All , para aplicar a modi�cação em todos os blocos. Não esqueça de salvar seu projeto. Para mais customização, selecione a câmera na janela Scene e no painel Inspector você poderá alterar a cor de fundo na opção Background , o plano de fundo. Clique na cor azul padrão e selecione uma cor na roda de cores. Lembre-se de manter um contraste entre �gura e fundo que seja agradável para jogar, por esse motivo, dê preferência a cores mais contrastantes. Se preferir, você poderá também modi�car a cor dos outros elementos, clicando no objeto correspondente e na opção Color do painel Inspector . Nesse mesmo painel poderemos adicionar os novos componentes que serão necessários. Para que os scripts de controle do jogo consigam identi�car quando os objetos se colidirem com a bola, teremos que adicionar um componente do tipo Collider . Clique no jogador, no painel Inspector clique na opção Add Component . Pesquise nesta caixa de busca pela palavra collider . Escolha a seguinte opção: Box Collider 2D . Para a bola selecione o collider correspondente a seu formato: o Circle Collider 2D . Para adicionar simulação de física na bola, como a possibilidade dela rebater e ser lançada automaticamente, adicione mais um componente: o RigidBody 2D. Clique em um dos blocos e adicione também o componente Box Collider 2D. Lembre-se de aplicar a mudança a todos os blocos utilizando a opção Overrides > Apply All . Nesse momento, todos os elementosvisuais do jogo foram inseridos, com exceção do placar de pontuação e a contagem de tentativas. Para que os scripts , que serão programados na próxima aula, consigam calcular essas informações, precisamos adicionar elementos que identi�quem quando o jogador deixar a bola cair. Para que a bola não saia da tela e seja rebatida também pelas paredes do jogo, precisamos adicionar um elementos que impeça essa saída. Vamos adicionar um GameObject vazio, isto é, sem nenhum asset associado. Clique no menu GameObject > Create Empty . Ele será adicionado na janela de hierarquia e pode ser renomeado, basta clicar com o botão direito do mouse sobre seu nome e escolher a opção Renomear. Dê o nome de Topo. Adicione mais três GameObjects vazios renomeando para: Base, Direita e Esquerda. Esses elementos servirão para limitar o espaço da bola na tela, rebatendo ela de volta, ou, no caso da Base, indicando que o jogador perdeu uma tentativa. Para isso adicione o componente Box Collider 2D em cada um desses GameObjects vazios. Para que esses elementos cumpram seu papel de limitar a bola, devemos redimensioná-los a �m de cobrir todo o perímetro da tela. Arraste cada um deles para sua respectiva posição, fora dos limites da tela, e clique na opção Edit Collider , dentro do painel Inspector para redimensioná-los. Observe o exemplo na �gura a seguir: Figura 4: Posicionamento do GameObject Base. Fonte: proprio autor, 2020 Após posicionar e redimensionar corretamente cada um dos elementos, clique novamente no menu GameObject , mas dessa vez escolha a opção UI > Text . Repita esse processo mais uma vez, criando assim dois textos. Mais adiante, durante os estudos desta disciplina, abordaremos os conceitos relacionados a UI, a interface do usuário, do inglês: User Interface . Por enquanto, adicionaremos apenas dois GameObjects do tipo texto para exibirmos o placar e a contagem de tentativas do jogador. Renomeie um dos objetos de texto para Placar e o outro para Tentativas. Você deve ter percebido que eles pertencem a outro GameObject , o Canvas, que será responsável por posicionar esses elementos sobre nosso jogo. Clique em cada um dos textos e altere suas propriedades no painel Inspector. Você também poderá posicioná- los no melhor lugar da tela para visualização durante a partida. Nesse momento considere como tela do jogo a área maior delimitada pelo Canvas. É possível visualizar a tela do jogo com os elementos de UI na janela Game . Conforme exempli�cado na �gura a seguir, foram alterados além do posicionamento: o texto, a cor e o estilo das letras. Figura 5: Interface do game Breakout. Fonte: elaborado pela autora. 2020 Agora que todos os elementos necessários para a programação do jogo foram inseridos, iniciaremos a programação dos scripts na próxima aula. Não esqueça de salvar o seu projeto. Você quer ver O site itch.io é uma plataforma para a comercialização de jogos de desenvolvedores independentes. Nesse site é possível encontrar diversas versões do game Breakout para jogar gratuitamente, além de milhares de outros jogos. Para acessar as versões disponíveis de Breakout clique no link: https://itch.io/search? q=breakout 3. Programação das interações Antes de iniciarmos a criação dos scripts é necessário pensar em quais interações deverão ser programadas. Para esse primeiro projeto, não se preocupe ainda em representar as classes de acordo com um diagrama de ULM. Essa etapa será abordada na próxima unidade, quando criaremos um jogo de maior complexidade. Em nossa versão de Breakout teremos as seguintes interações de quem irá jogar: movimentar o jogador e reiniciar a partida. Podemos destacar as seguintes interações entre objetos: Barreiras - Topo, Esquerda e Direita: limitam os movimentos da Bola; Barreira - Base: soma às Tentativas cada vez que for atingida pela Bola; Blocos : somam pontos no Placar ao serem atingidos pela Bola; Observe que todas essas interações partem de comportamentos do objeto Bola. Ainda que seja possível programar todo o código em praticamente um script , apenas se referindo aos outros objetos quando necessário, em OO devemos sempre optar por separar as ações de nossos objetos, facilitando a manutenção do código. Dessa maneira, a colisão da Bola com a Base será programada em um script dedicado somente a essa tarefa, e será associado à Base. Caso seja necessário corrigir erros ou modi�car alguma parte do código, saberemos exatamente onde encontrá-lo. Faremos então estes cinco scripts : Script Bola : controla sua movimentação; Script Jogador : controla sua movimentação dentro dos limites da tela; Script Blocos : destrói sua cópia que foi atingida pela Bola e aciona o comando que soma pontos ao Placar; Script Base: identi�ca a colisão com a Bola e então aciona o comando que soma mais um às Tentativas, enviando a Bola novamente para o centro da tela; Script GameManager: controla os comandos de UI e de gerenciamento de cena, além de calcular a soma do Placar e das Tentativas. Veremos mais sobre o objeto e o script GameManager na próxima aula, quando serão programados os controles gerais do jogo. Por enquanto, faremos a programação dos scripts que controlarão as movimentações dos nossos objetos. Note que não precisamos de um script para limitar os movimentos da Bola, pois isso já está https://itch.io/ https://itch.io/search?q=breakout sendo feito com a interação entre dois componentes da Unity : o Circle Collider 2D e o RigidBody 2D . Esse último, por ser um componente de simulação física, identi�ca que colidiu e responde automaticamente a essa colisão, rebatendo a bola nesse caso. 3.1 Movimentação do jogador Para iniciar a programação do objeto Jogador crie um novo script com esse nome. Você também pode organizar todos os scripts em uma pasta dentro da janela Project . Observe a �gura a seguir contendo todo o código necessário para esse script : Figura 6: Script Jogador. Fonte: Próprio autor, 2020 Conforme foi projetado o script Jogador apenas cumpre uma ação: permite a movimentação do objeto Jogador, limitando seus movimentos para que ele permaneça visível na tela. Além da estrutura apresentada na unidade anterior temos novos elementos nesse script . Um novo método chamado MovimentaJogador (entre as linhas 23 e 37) foi criado para realizar a ação do objeto. Ele apresenta a mesma estrutura dos métodos Start e Update , mas para ser executado precisa ser chamado em algum momento dentro de um deles. Na linha 20 podemos ver a notação para invocá-lo dentro do método Update , ou seja, a cada frame . Dentro do método MovimentaJogador, temos decisões sendo feitas baseadas em condições. O objeto deverá se movimentar para a direita e para a esquerda de acordo com a seta do teclado que for pressionada. Na linha 28 temos a expressão If, que é uma forma de veri�carmos se uma condição é atendida. Nesse caso, ela veri�ca se a tecla pressionada (no código o trecho: Input.GetKey ) é a seta para a direita (no código o trecho: KeyCode.RightArrow ). Caso essa condição seja atendida, o programa executará o que foi determinado entre as chaves que vem a seguir. Na linha 30, temos a expressão responsável pela movimentação do Jogador. A partir do componente transform, embutido no GameObject , modi�camos seu posicionamento utilizando seu método Translate . Esse método movimenta o objeto pelos eixos x e y da tela, indicados pela expressão Vector2 . A palavra right (direita) indica a direção que o objeto deverá seguir. Para que ele se movimente em uma velocidade acima de 1, devemos multiplicá-lo pelo valor desejado. Para que o Jogador se movimente com a mesma velocidade em todas as execuções do jogo, devemos multiplicar ainda pela expressão Time.deltaTime , que converte a movimentação do Jogador para pontos por segundo, ao invés de pontos por frame . Isso deve ser feito, pois, ao rodar o jogo, o computador não mantém o mesmo intervalo entre a execução de cada frame , o que resulta em diferentes velocidades durante a partida. Observeque a expressão Vector2.right não está multiplicando seu valor padrão (1) por um outro número, mas sim pela palavra velocidade. Essa palavra se refere a variável declarada na linha 7 do script . Cada variável é uma espécie de caixa, onde é possível guardar uma informação. Seu valor é atribuído de acordo com o seu tipo, e pode ser modi�cado. A palavra public , no início da linha 7, indica o tipo de encapsulamento desse atributo, ou seja, seu modi�cador de acesso é do tipo público. Isso possibilita que o valor de velocidade pode ser atribuído também na interface da Unity . A expressão indica ainda o tipo �oat , que se refere a um tipo de variável que aceita guardar números com casas decimais. Esse tipo é utilizado para o atributo velocidade para assim, permitir mais variações de valores, não somente números inteiros. A movimentação do Jogador será, portanto, o resultado da expressão da linha 30, e esse cálculo será feito de acordo com o valor que for atribuído à variável velocidade. Entre as linhas 33 e 36 do script está a segunda condição, aquela que caso seja atendida, decide a movimentação do Jogador para a esquerda. Somente com as expressões que foram explicadas até aqui já é possível movimentar o Jogador. Para isso, é preciso associar o script Jogador ao GameObject de mesmo nome, arrastando o primeiro a partir do painel Project para o segundo no painel Hierarchy . No painel Game , ao dar play no jogo você já poderá movimentar seu jogador. Nesse momento ele ainda poderá sair da tela ao atingir suas extremidades, por esse motivo que a linha 26 do código foi adicionada. Novamente vamos modi�car o componente transform a partir do script . A expressão transform.position modi�ca o posicionamento do objeto. O símbolo = indica que o que vem depois é o que está sendo atribuído ao que veio antes. Nessa linha, a posição do objeto será igual ao posicionamento de seus eixos x e y, que estão sendo movimentados pelas linhas 30 e 35. Para inserir algum limite na movimentação do Jogador, devem ser de�nidos os pontos mínimo e máximo de sua movimentação na tela. Observe que dentro dos parentes de Vector2 , na linha 26, temos a expressão transform.position .y, indicando que seu posicionamento vertical será aquele mesmo do objeto. Onde �caria a expressão transform.position .x, para o posicionamento horizontal, temos a seguinte expressão: Mathf.Clamp(transform.position.x, xMin, xMax) Esse método permite que o posicionamento do objeto no eixo indicado, nesse caso o x, se modi�que de acordo com os valores mínimo e máximo indicados. Para modi�carmos esses valores com maior facilidade, sem ter que procurar por essa linha no código, criamos uma variável para cada valor: a xMin e a xMax. Veja que o nome das variáveis são inventados, bem como os nomes dos métodos que criamos. Apenas certi�que-se de nomeá- los de maneira que sua escrita e leitura sejam facilitados. As variáveis utilizadas pelo método Clamp foram declaradas nas linhas 8 e 9 do código. Elas também são do tipo �oat , já que esse cálculo precisa ser feito com números decimais. Observe que elas não possuem a indicação public , o que as torna privadas por padrão. Como seus valores já são atribuídos dentro do código (-8 e 8), não há necessidade de �carem acessíveis na interface da Unity . A linha 26, portanto, garante que o posicionamento do objeto Jogador no eixo x não ultrapasse os limites indicados, ou seja, ele somente poderá se movimentar entre o ponto -8 e o ponto 8 do eixo x. Esse números indicam exatamente os limites da tela do jogo, e podem ser veri�cados no painel Inspector, ao posicionar seu objeto nesses limites. Agora que seu jogador se movimenta e não sai da tela, vamos para o próximo script . Lembre-se de salvar seu projeto a cada modi�cação signi�cativa. 3.2 Movimentação da bola Ao clicar em play na janela Game , você verá que já é possível movimentar o Jogador. A Bola também se movimenta, caindo de sua posição inicial e parando ao atingir o jogador. Isso se dá porque o componente RigidBody2D foi adicionado, ainda que não haja nenhum script associado, ela já é afetada pela gravidade. Para que a Bola seja rebatida é necessário indicar ao componente RigidBody 2D qual seu tipo de material. Clique no menu Assets > Create > Physics Material 2D para adicionar o tipo de material correspondente à simulação física 2D. Renomeie o material criado para Bola. Agora selecione o GameObject Bola na janela de hierarquia e arraste o material Bola, que acabou de ser criado, para o campo Material do componente RigidBody 2D, no painel Inspector. Esse processo deve ser feito para associar o asset material Bola com o objeto Bola inserido na cena. Selecione o asset material Bola, no painel Project , e modi�que suas propriedades no painel Inspector. O campo Friction permite con�gurar um valor para o atrito do material, e o Bounciness sua elasticidade. Insira 0 e 1 para cada campo respectivamente, fazendo com que a bola não sofra nenhum atrito ao colidir, mas continue sendo rebatida. Crie um novo script para controlar a Bola e renomeie-o também como Bola, assim �ca mais fácil identi�car a relação entre os objetos e seus componentes. Figura 7: Script Bola. Fonte: proprio autor, 2020 Na �gura 7, temos o script Bola já com o código necessário para seu funcionamento. Logo após a de�nição da classe, feita na linha 5, temos a declaração da variável corpoBola (linha 7). Seu encapsulamento é do tipo público e esse atributo pertence à classe RigidBody2D . Lembre-se que um componente com esse mesmo nome foi adicionado ao GameObject Bola, associando assim ambas as classes. Dentro do método Start foi inserida o código para a movimentação da Bola. Dessa maneira, essa ação será iniciada assim que o objeto Bola for criado no jogo. Na linha 12, portanto, temos o comando de movimentação da Bola. Novamente vemos a criação de um objeto Vector2 , mas dessa vez com seus valores padrão: um ponto por vez. Nesse caso, sua direção inicial será para cima e para direita, sendo Vector2 (x,y). Aqui também temos a multiplicação pelo Time.deltaTime , a �m de padronizar a execução do jogo. Essa movimentação é atribuída ao atributo velocity , do objeto corpoBola. Nossa variável corpoBola, portanto, é um atributo da classe Bola, mas ela também é um objeto da classe RigidBody2D . Essa última classe tem como um de seus atributos a velocity (velocidade). Como as classes Bola e RigidBody2D estão associadas, podemos criar um novo objeto do tipo RigidBody2D (chamado corpoBola) dentro do método Start da classe Bola. Isso deve ser feito para que possamos utilizar as opções de simulação física, da classe RigidBody2D , em nosso objeto Bola. Associe o script Bola com o GameObject Bola. No painel Inspector, arraste o componente RigidBody2D para o atributo Corpo Bola dentro do componente Script . Ao �nal dessa etapa, o painel de seu objeto Bola deverá estar conforme a �gura a seguir: Figura 8: Material Bola aplicado ao componente RigidBody2D e este último aplicado ao atributo Corpo Bola. Fonte: proprio autor, 2020. 3.3 Comportamento dos blocos Para que os Blocos identi�quem a colisão com a Bola e possam desaparecer, podemos identi�car a Bola com uma etiqueta, uma tag . Dessa maneira, utilizaremos um método no script dos Blocos que veri�cará se eles foram atingidos pela Bola, executando assim a ação de auto destruição. Clique no GameObject Bola e na janela Inspector clique em Tag > Untagged > Add Tag > + . Para nomear essa nova tag escreva Bola e clique em Save . Clique no objeto Bola novamente e agora na janela Inspector clique em: Tag > Bola. Você acaba de associar uma tag para facilitar a identi�cação de seu objeto pelos demais objetos da cena. Crie um novo script chamado Blocos. Lembre-se de associá-lo a pelo menos um dos blocos e, como são Prefabs , no painel Inspector clique em Overrides > Apply All . Figura 9: Script Blocos. Fonte: proprio autor, 2020 Na �gura 8, você pode observar o código necessário para os Blocos desapareceremao colidirem com a Bola. Entre as linhas 19 e 25, vemos o método OnCollisionEnter2D sendo utilizado. Ele é um método da classe Collider , a classe base de todas as classes de colliders . Lembre-se que ela está associada a nossa classe Blocos, pois adicionamos como um componente através do painel Inspector. Esse método veri�ca a colisão entre colliders e objetos do tipo RigidBody2D . A variável collision dentro dos parênteses, indica ser do tipo Collision2D . Essa variável é utilizada na linha 21, referindo-se a seguinte condição: collision.gameObject.CompareTag (“Bola”). Essa expressão indica que o objeto da classe Blocos colidiu com um GameObject com a tag Bola. Caso essa condição seja atendida o método executará ação da linha 23, ou seja, destruirá este ( this ) GameObject . Salve seu projeto para continuarmos a programação de scripts . Na próxima aula serão programados os códigos para o controle geral do jogo. 4. Game manager Para controlar os elementos de interface do usuário do jogo e o gerenciamento da cena, podemos trabalhar com um Game Manager . Esse é o nome padrão do gerenciador do jogo dentro da Unity , permitindo que o script que controla o game seja identi�cado facilmente pelo desenvolvedor. Crie um novo script chamado GameManager . Observe que quando esse nome é dado a seu script ele apresenta um ícone diferente dos demais, uma engrenagem. Para que o código inserido nele seja rodado junto com o jogo você deverá associá-lo a um objeto. Crie um novo GameObject vazio e adicione a ele esse novo script . Figura 10: Script GameManager.Fonte: proprio autor, 2020 Para que possamos controlar os elementos da interface do usuário, utilizando os métodos já existentes na Unity é necessário adicionar a linha 4, indicada na �gura anterior, em seu código. A linha 5 deve ser adicionada para que possamos controlar as cenas do projeto. Nas linhas 9 e 10 foram criadas duas variáveis do tipo int , que indicam somente aceitar números inteiros. A primeira servirá para guardar os pontos do jogador e a segunda, para guardar as vidas já utilizadas. Nas linhas 11 e 12 foram criadas variáveis da classe Text , que serão utilizadas para exibir os valores das variáveis pontos e vidas em nossos objetos de texto da cena. Entre as linhas 23 e 26 temos uma condicional. Caso a tecla espaço seja apertada a cena será carregada novamente. Para isso foi utilizado o método LoadScene da classe SceneManager . O LoadScene carrega a cena que for indicada entre aspas dentro de seus parênteses. Veri�que o nome de sua cena no painel Project , caso não a tenha modi�cado em nenhum momento está com seu nome padrão: SampleScene . Esse trecho de código então deverá ser: SceneManager.LoadScene("SampleScene "); Entre as linhas 28 e 32 foi criado o método VezesCaiu, ele soma um à variável vidas e modi�ca o texto da variável Tentativas para: Tentativas + o valor da variável vidas. O mesmo acontece com o método Pontua, mas ele soma um à variável pontos e modi�ca o texto da variável Placar. Observe que esses métodos não estão sendo chamados dentro de seu próprio script . A ação de somar pontos, somar tentativas e modi�car o texto da UI será acionada pelo objeto Base. Para que o GameManager consiga acessar os objetos Placar e Tentativas arrastes esses GameObjects para os campos correspondentes no painel Inspector com o GameObject GameManager selecionado. 4.1 Programação da base Crie um novo script com o nome Base. Ele servirá para invocar o método VezesCaiu cada vez que a Bola atingir o objeto Base. Veja o código completo na �gura a seguir: Figura 11: Script Base. Fonte: proprio autor, 2020 O método OnTriggerEnter2D é utilizado para identi�car a colisão do objeto da classe RigidBody2D , sem interferir em suas propriedades físicas. Dessa maneira, a Base servirá apenas para identi�car a colisão da Bola, não mais para rebatê-la. Para que esse método possa ser utilizado clique no objeto Base e acione a opção Is Trigger , no painel Inspector . Assim como o método que adicionamos na Bola, esse também executa uma ação com base na comparação de qual tag colidiu com seu objeto. A linha 21 veri�ca se o objeto other é a Bola. Na linha 23 o script procura o objeto GameManager e invoca seu método VezesCaiu. A linha 25 retorna o objeto Bola (identi�cado como other ) para a posição central da tela, o Vector2 .zero. Não esqueça de associar o script Base com seu respectivo GameObject . Antes de salvar seu projeto adicione mais uma linha no script dos Blocos. Nesse último passo nós invocaremos o método Pontua, que criamos no GameManager , dentro do método OnCollisionEnter2D dos Blocos. Adicione uma linha abaixo da linha 23 desse script , insira a seguinte expressão: FindObjectOfType<gamemanager> ().Pontua()</gamemanager> ; Agora o método criado dentro do script dos Blocos deverá ter �cado com o seguinte código: Muito bem! Você acaba de concluir uma programação de uma versão de Breakout . Caso você tenha testado seu jogo verá que todos os controles funcionam, porém, a bolinha apresenta um comportamento errático. Não se preocupe, na próxima unidade iniciaremos o processo de testes de jogabilidade e correção de bugs, uma etapa essencial para a programação de jogos. Você sabia A habilidade de programar jogos é muito útil no mercado de trabalho. Há muito anos a indústria de games é a maior indústria de entretenimento do mundo, com rendimentos superiores aos das indústrias cinematográ�ca e fonográ�ca. Informações detalhadas sobre quais foram os jogos e categorias mais rentáveis em 2019 podem ser acessadas em: www.superdataresearch.com/reports/2019-year-in-review Síntese Programar um jogo com OO facilita o entendimento do porquê desse paradigma de programação ser tão utilizado em game engines . Nossa programação de Breakout focou nos objetos utilizados no jogo e nas suas interações. Assim, durante o desenvolvimento de um jogo da era dos arcades foi possível aplicar os conceitos da Programação Orientada a Objetos. O projeto de uma versão do Breakout também nos ajudou a conhecer melhor o funcionamento da game engine Unity e a programar utilizando a linguagem C#. Vimos que em programação há diversas maneiras de se realizar uma tarefa, o que fazemos é utilizar as boas práticas para escrever um código de acordo com as indicações do paradigma de OO, a organização de dois projetos podem ser diferentes, desde que sigam as orientações para melhor escrita e leitura do código, bem como aproveitar as facilidades oferecidas pela Unity , como os Prefabs e os componentes de simulação física. Durante esta unidade você entrou em contato com: ● Criação de um projeto de jogo simpli�cado; ● Construção de classes e diagrama de ULM; ● Elementos básicos na Unity; http://www.superdataresearch.com/reports/2019-year-in-review ● Programação de movimentos e de interação entre objetos; ● Controle geral do jogo. Download do PDF da unidade Bibliografia FOWLER, Martin. UML Distilled : A Brief Guide to the Standard Object Modeling Language. Addison-Wesley Professional, 2003. XAVIER, Gley Fabiano Cardoso. Lógica de Programação . São Paulo: Editora Senac São Paulo, 2011. XAVIER, Guilherme. A condição eletrolúdica : Cultura visual nos jogos eletrônicos. Teresópolis, RJ: Novas Ideias, 2010. (Documentação da Unity). Disponível em: https://docs.unity3d.com . Acesso em 28/03/2020. Referências Imagéticas: Figura 1 e 3 a 11: Cópias de tela produzidas pela própria redatora. Figura 2: Uma das versões do game Breakout. Disponível em: < https://commons.wikimedia.org/wiki/File:Yakanoid2.png > Acesso em 03/2020. https://docs.unity3d.com/ https://commons.wikimedia.org/wiki/File:Yakanoid2.png
Compartilhar