TCC com numeração
Transcrição
TCC com numeração
UNIVERSIDADE FEDERAL DE SANTA CATARINA Metodologia para Ensino de Programação Orientada a Objetos com Jogos 2D Kaléu Caminha Florianópolis, 2º semestre de 2012 1 / 227 UNIVERSIDADE FEDERAL DE SANTA CATARINA DEPARTAMENTO DE INFORMÁTICA E ESTATÍSTICA CURSO DE SISTEMAS DE INFORMAÇÃO Metodologia para Ensino de Programação Orientada a Objetos com Jogos 2D Kaléu Caminha Trabalho de Conclusão de Curso apresentado como requisito parcial para a obtenção de grau de Bacharel em Sistemas de Informação. Florianópolis, 2º semestre de 2012 2 / 227 Kaléu Caminha Metodologia para Ensino de Programação Orientada a Objetos com Jogos 2D Trabalho de Conclusão de Curso apresentado como requisito parcial para a obtenção de grau de Bacharel em Sistemas de Informação. Orientador Professor Doutor Ricardo Pereira e Silva Banca Examinadora Leandro José Komosinski Patrícia Vilain 3 / 227 Sumário Lista de Reduções............................................................................................................................. 13 Resumo.............................................................................................................................................. 14 1 Introdução...................................................................................................................................... 15 1.1 Justificativa.............................................................................................................................. 16 1.2 Objetivo Geral..........................................................................................................................18 1.3 Objetivos Específicos ............................................................................................................ 18 1.4 Metodologia de Pesquisa......................................................................................................... 19 2 Fundamentação Teórica................................................................................................................ 20 2.1 Programação Orientada a Objetos........................................................................................... 20 2.1.1 Conceitos POO ...............................................................................................................21 2.1.2 Estruturas Básicas de Programação em Java................................................................... 21 2.1.3 Conceitos da Linguagem Java..........................................................................................22 2.2 Jogos 2D.................................................................................................................................. 22 2.2.1 Desenho de imagens........................................................................................................23 2.2.2 Animações........................................................................................................................ 24 2.2.3 Tiles e Composição de Cenários...................................................................................... 24 2.2.4 Pontos / Plano Cartesiano................................................................................................ 24 2.2.5 Sprites...............................................................................................................................25 2.2.6 Dispositivos de Entrada .................................................................................................. 25 2.2.7 Execução de sons............................................................................................................. 25 2.2.8 Taxa de Quadros e atualização do jogo............................................................................25 2.2.9 Acumuladores de Tempo..................................................................................................26 2.2.10 Gravidade....................................................................................................................... 26 2.2.11 Detecção e Tratamento de Colisões............................................................................... 26 2.2.12 O Game Loop.................................................................................................................27 2.2.13 Movimentação e Gravidade nos cenários...................................................................... 27 2.3 Frameworks Orientado a Objetos............................................................................................ 27 2.3.1 Frameworks OO............................................................................................................... 27 2.3.2 Metodologias de Desenvolvimento de Frameworks OO................................................. 28 2.3.3 Uso de Frameworks OO...................................................................................................29 2.4 Padrões Pedagógicos................................................................................................................29 2.4.1 O Mais Importante Primeiro (Early Bird)........................................................................30 2.4.2 Espiral (Spiral)................................................................................................................. 30 2.4.3 Metáfora Consistente (Consistent Mataphor).................................................................. 30 2.4.4 Caixa de Brinquedos (Toy Box).......................................................................................31 4 / 227 2.4.5 Caixa de Ferramentas (Tool Box).................................................................................... 31 2.4.6 Disposição do território (Lay of the Land)...................................................................... 31 2.4.7 Corrigir para cima (Fixer Upper)..................................................................................... 32 2.4.8 Maior que a Vida (Larger than Life)................................................................................ 32 2.4.9 Sprint de Projeto dos Estudantes (Student Design Sprint)...............................................33 2.4.10 Engano (Mistake)........................................................................................................... 33 2.4.11 Canal de Teste (Test Tube)............................................................................................. 33 2.4.12 Preencha as Lacunas (Fill in the Blanks)....................................................................... 34 2.4.13 Estrela de Ouro (Gold Star)........................................................................................... 34 2.4.14 Avalie isto novamente Sam (Grade it again Sam)..........................................................34 3 Experiência prévia com ensino de POO e Jogos.........................................................................35 3.1 Conteúdos trabalhados............................................................................................................. 35 3.2 Metodologia............................................................................................................................. 37 3.2.1 Exemplo 1 – Movimentando uma bola............................................................................ 38 3.2.2 Exemplo 2 – Construindo o primeiro jogo.......................................................................39 3.2.3 Exemplo 3 – Ítens e fases.................................................................................................39 3.2.4 Exemplo 4 – Guerra Especial...........................................................................................40 3.2.5 – Personagens e Cenários.................................................................................................41 3.2.6 – Jogo de Luta.................................................................................................................. 42 3.2.7 – Revisão..........................................................................................................................42 3.3 Avaliação e Resultados............................................................................................................ 43 3.3.1 Avaliação Teórica............................................................................................................. 43 3.3.2 Avaliação da Motivação................................................................................................... 44 3.3.3 Avaliação Prática.............................................................................................................. 44 3.3.3.1 Grupo 1 – PowerMusic.............................................................................................45 3.3.3.2 Grupo 2 – Champions Fighter.................................................................................. 45 3.3.3.3 Grupo 3 – Ghomaine................................................................................................ 45 3.3.3.4 Grupo 4 – Ice Crown Citadel................................................................................... 45 3.3.3.5 Grupo 5 – Moluscation.............................................................................................45 3.3.3.6 Grupo 6 – Terrorist Strategy.....................................................................................45 3.3.3.7 Grupo 7 – God of Worms......................................................................................... 45 3.3.3.8 Grupo 8 – Sami Game.............................................................................................. 45 3.3.4 Resultado avaliação prática..............................................................................................46 3.3.4 Conclusão ao final do módulo......................................................................................... 46 4 Ensino de Programação Orientada a Objetos............................................................................ 48 4.1 Metodologias para Ensino OO.................................................................................................48 4.1.1 Isaias Camilo Boratti........................................................................................................49 4.1.1.1 Análise Geral do Método..........................................................................................51 4.1.1.2 Pontos Fortes ........................................................................................................... 52 4.1.1.3 Pontos Fracos........................................................................................................... 56 4.1.1.4 Conclusão................................................................................................................. 58 4.1.2 David J. Barnes e Michael Kölling.................................................................................. 59 5 / 227 4.1.2.1 Diretrizes ................................................................................................................. 61 4.1.2.2 Análise geral do método........................................................................................... 63 4.1.2.3 Pontos fracos............................................................................................................ 64 4.1.2.4 Conclusão................................................................................................................. 65 4.1.3 Bruno Feijó et al...............................................................................................................66 4.1.3.1 Análise geral do método........................................................................................... 68 4.1.4 Stelios Xinogalos............................................................................................................. 68 4.1.4.1 O Uso de ambientes de programação....................................................................... 69 4.1.4.2 Diretrizes para o Ensino das lições propostas.......................................................... 70 4.1.4.3 Análise Geral do método.......................................................................................... 77 4.1.5 Michael Caspersen........................................................................................................... 77 4.1.5.1 As etapas da metodologias....................................................................................... 79 4.1.5.2 O Framework conceitual.......................................................................................... 79 4.1.5.3 O início..................................................................................................................... 79 4.1.5.4 Padrões de Código....................................................................................................80 4.1.5.5 Outros elementos da metodologia............................................................................ 80 4.1.5.6 Análise Geral da Metodologia.................................................................................. 81 4.2 Diretrizes para o Ensino de OO ............................................................................................. 81 4.2.1 Princípios baseados na prática com o BlueJ.................................................................... 81 4.3.2 Representações visuais para todas as estruturas e conceitos trabalhados........................81 4.3.3 Operações de abstração e framework conceitual............................................................. 82 4.3.4 Ambiente educacional + Ambiente profissional.............................................................. 82 4.3.5 Processo sistemático de desenvolvimento....................................................................... 82 4.3.6 A partir do zero.................................................................................................................82 4.3.7 Recursos computacionais devem ter acesso fácil.............................................................82 4.3.8 Estilo de código-fonte...................................................................................................... 83 4.3.9 Modelagens genéricas...................................................................................................... 83 4.3.10 Códigos e documentação na língua materna..................................................................83 5 Framework Java para Jogos 2D.................................................................................................. 85 5.1 Aquisição do Conhecimento de Domínio................................................................................ 85 5.2 Modelagem do Framework:.....................................................................................................85 5.2.1 Generalização...................................................................................................................86 5.2.2 Flexibilização................................................................................................................... 86 5.2.3 Modelagem das Classes................................................................................................... 87 5.2.3.1 Pacote aljava.............................................................................................................87 5.2.3.2 Pacote aljava.entrada................................................................................................ 87 5.2.3.3 Pacote aljava.saida....................................................................................................88 5.2.3.4 Pacote aljava.midia...................................................................................................88 5.2.3.5 Pacote aljava.jogo.....................................................................................................88 5.2.3.6 Pacote aljava.util...................................................................................................... 90 5.3 Validação..................................................................................................................................90 5.4 Documentação......................................................................................................................... 91 5.4.1 Livro de Receitas............................................................................................................. 91 5.4.2 JavaDoc............................................................................................................................ 93 6 / 227 5.5 Código Fonte............................................................................................................................95 6 Metodologia de Ensino.................................................................................................................. 96 6.1 Introdução................................................................................................................................ 97 6.2 Módulo 1 – Animações computadorizadas............................................................................ 100 6.2.1 Modelagem – Classe Carro............................................................................................ 101 6.2.2 Tipos Básicos................................................................................................................. 101 6.2.3 Live Coding – Classe Carro........................................................................................... 103 6.2.3 Live Coding – Desenhando............................................................................................107 6.4.4 Live Coding – Animações.............................................................................................. 112 6.2.5 Live Coding – Parâmetros e estruturas condicionais..................................................... 114 6.4.6 Resultados...................................................................................................................... 120 6.4.6.1 Classe Sapo.............................................................................................................121 6.4.6.2 Classe BatmanLogo................................................................................................122 6.4.6.3 Classe Onibus ........................................................................................................ 123 6.4.6.4 Classe Carro........................................................................................................... 124 6.3 Módulo 2 – Mini Game......................................................................................................... 124 6.5.1 Resultados...................................................................................................................... 135 6.4 Módulo 3 – Um Jogo, Muitos Objetos.................................................................................. 136 6.4.1 Modelagem Básica......................................................................................................... 136 6.4.2 Lógica do Jogo............................................................................................................... 141 6.4.3 Herança.......................................................................................................................... 145 6.4.4 Listas.............................................................................................................................. 149 6.4.5 Tiros e Métodos com Retorno........................................................................................ 151 6.4.6 Colisões entre listas e finalização do jogo..................................................................... 154 6.4.7 Classe ContadorTempo.................................................................................................. 157 6.4.8 Pacote de Mídia..............................................................................................................157 6.4.9 Resultados...................................................................................................................... 157 6.4.9.1 A Ponte................................................................................................................... 158 6.4.9.2 Pacman................................................................................................................... 158 6.4.9.3 Ken vs Akuma........................................................................................................ 159 6.4.9.4 Mundo de Molland................................................................................................. 160 6.5 Módulo 4 – Pequeno mundo virtual...................................................................................... 160 6.5.1 Modelando um jogo escalável........................................................................................161 6.5.2 Ambiente de Desenvolvimento Profissional.................................................................. 164 6.5.3 Compartilhamento de Objetos e o método Main........................................................... 165 6.5.4 Variáveis e Métodos Estáticos........................................................................................168 6.5.5 Resultados...................................................................................................................... 170 6.6 Módulo 5 – Frameworks........................................................................................................171 6.6.1 Frameworks....................................................................................................................172 6.6.2 Classe Motor e Ciclo de Vida.........................................................................................172 6.6.2 Classes Interfaces........................................................................................................... 174 7 / 227 6.6.3 Troca de fases e o método “main”................................................................................. 180 6.6.4 Classes Abstratas............................................................................................................183 6.6.5 Polimorfismo..................................................................................................................189 6.6.6 Resultados...................................................................................................................... 193 6.7 Limitações da Metodologia Apresentada ..............................................................................194 7 Conclusão..................................................................................................................................... 195 7.1 Revisão dos Objetivos propostos........................................................................................... 197 7.2 As 10 diretrizes...................................................................................................................... 199 7.2.1 Princípios baseados na prática com o BlueJ.................................................................. 199 4.3.2 Representações visuais para todas as estruturas e conceitos trabalhados......................199 4.3.3 Operações de abstração e “framework” conceitual........................................................200 4.3.4 Ambiente educacional + Ambiente profissional............................................................ 200 4.3.5 Processo sistemático de desenvolvimento..................................................................... 200 4.3.6 A partir do zero...............................................................................................................200 4.3.7 Recursos computacionais devem ter acesso fácil...........................................................201 4.3.8 Estilo de código-fonte.................................................................................................... 201 4.3.9 Modelagens genéricas.................................................................................................... 201 4.3.10 Códigos e documentação na língua materna................................................................201 7.3 Outros Aprendizados..............................................................................................................201 7.3.1 Aplicações conectadas com a realidade do educando.................................................... 201 7.3.2 Frameworks e bibliotecas como apoio ao desenvolvimento mesmo em etapas iniciais .................................................................................................................................................202 7.3.3 Modelagem é essencial.................................................................................................. 202 7.4 Trabalhos Futuros.................................................................................................................. 202 7.4.1 Compreensão do Pensamento Computacional e Abstrato..............................................203 7.4.2 Conexão com a pedagogia e produção de REA `s– Recursos Educacionais Abertos....203 7.4.3 Uso abrangente da UML................................................................................................ 203 7.4.4 Técnicas de documentação de software para estudantes novatos..................................204 7.4.5 Ensino de POO Avançada com Android;....................................................................... 204 7.5 Considerações Finais ............................................................................................................ 205 8 Bibliografia...................................................................................................................................206 8.1 Livros..................................................................................................................................... 206 8.2 Artigos e Documentos acadêmicos........................................................................................ 206 8.3 Bibliografia para código-fonte e materiais didáticos desenvolvidos pelo autor....................207 8.4 Bibliografia Consultada......................................................................................................... 208 9. Anexos.......................................................................................................................................... 210 9.1 Anexo I - Classe Carro........................................................................................................... 211 9.2 Anexo II – Personagens do jogo “A Aventura do Quadrado” ...............................................212 8 / 227 9.2.1 Classe SenhorQ.............................................................................................................. 212 9.2.2 Classe InimigoQuadradao.............................................................................................. 215 9.2.3 Classe InimigoTic.......................................................................................................... 215 9.2.4 Classe SuperQuadrado................................................................................................... 216 9.3 Anexo III – Classe “TelaSelecao”..........................................................................................217 9.4 Anexo IV – Fases do jogo “A Aventura do Quadrado”.........................................................220 9.4.1 Classe TelaInicial........................................................................................................... 220 9.4.2 Classe Fase1................................................................................................................... 222 9.4.3 Classe Fase2................................................................................................................... 225 9.4.4 Classe FaseChefao......................................................................................................... 228 9 / 227 Índice de ilustrações Ilustração 1: Cenário baseado em Tiles.............................................................................................. 24 Ilustração 2: Exemplo de uma Sprite..................................................................................................25 Ilustração 3: Movimentando uma bola na tela................................................................................... 38 Ilustração 4: Exemplo mais complexo utilizando a bola criada anteriormente..................................38 Ilustração 5: Tela final do jogo desenvolvido com o auxílio do roteiro............................................. 39 Ilustração 6: Terceira fase do exemplo com diversos itens na tela.....................................................40 Ilustração 7: Versão final do jogo Guerra Espacial............................................................................ 41 Ilustração 8: Personagem saltando sobre um inimigo representado por uma bola vermelha.............42 Ilustração 9: Exemplo jogo de luta..................................................................................................... 42 Ilustração 10: A Construção do modelo. Fonte: Boratti..................................................................... 53 Ilustração 11: Representação Visual de Objetos. Fonte: Boratti. Adaptado por Kaléu Caminha.......54 Ilustração 12: Implementação de Objetos. Fonte: Boratti. Adaptado por Kaléu Caminha................55 Ilustração 13: Representação Visual de um Array. Fonte: Boratti. adaptado por Kaléu Caminha.....55 Ilustração 14: Instanciação de objeto e chamada de método em código............................................ 64 Ilustração 15: Objetos na bancada de objetos (em vermelho) e chamada de método de um objeto. .65 Ilustração 16: Instanciação de um objeto no objectKarel com a ajuda da interface gráfica...............71 Ilustração 17: Criação de uma tarefa a partir dos métodos de um robô.............................................72 Ilustração 18: Execução de um programa. à direita a visualização do robô, à esquerda a lista de tarefas e abaixo a descrição do que ocorre no interior do método em execução................................72 Ilustração 19: Criação sistemática de um programa orientado a objetos...........................................78 Ilustração 20: Texto de introdução ao Aljava..................................................................................... 91 Ilustração 21: Submenu da seção sobre os comandos da classe Alj...................................................92 Ilustração 22: Submenu com alguns exemplos integrando diferentes comandos da classe Alj.........92 Ilustração 23: Outros submenus da documentação............................................................................ 92 Ilustração 24: Documentação de como desenhar um Retângulo........................................................92 Ilustração 25: Exemplo de uso dos recursos de acesso ao Mouse......................................................93 Ilustração 26: Exemplo de detalhe da tradução do gerador de JavaDoc............................................93 Ilustração 27: Visualização de diversas classes do mesmo pacote..................................................... 94 Ilustração 28: Métodos da classe som................................................................................................ 94 Ilustração 29: Detalhamento do método "configTile"........................................................................ 94 Ilustração 30: Tela do Jogo Space Invaders........................................................................................97 Ilustração 31: Representação de uma classe do jogo SpaceInvaders.................................................99 Ilustração 32: Representação visual de dois objetos criados a partir da classe Inimigo.................... 99 Ilustração 33: Exemplo de Classes "Carro" com diferentes objetivos.............................................101 Ilustração 34: Exemplo de uma classe motor com as características e os respectivos tipos............103 Ilustração 35: Exemplo de Classe Carro.......................................................................................... 107 Ilustração 36: Exemplos de objetos da classe Carro ao lado após execução de alguns dos métodos. .......................................................................................................................................................... 107 Ilustração 37: Exemplo de criação de objetos usando parâmetros pelo construtor..........................118 Ilustração 38: Exemplo Classe Sapo desenvolvida por um aluno.................................................... 121 Ilustração 39: Variáveis na classe Sapo............................................................................................ 122 Ilustração 40: Classe BatmanLogo................................................................................................... 122 Ilustração 41: Classe Onibus............................................................................................................ 123 Ilustração 42: Exemplo de má formatação de código na classe Onibus...........................................124 Ilustração 43: Exemplo Classe Carro............................................................................................... 124 Ilustração 44: Representação visual do uso de matriz......................................................................128 Ilustração 45: Métodos classe JogoDaVelha.................................................................................... 131 Ilustração 46: Modelagem básica do jogo Guerra Espacial............................................................. 138 Ilustração 47: Sintaxe de Construção de objetos.............................................................................. 143 10 / 227 Ilustração 48: Telas da implementação parcial do jogo GuerraEspacial.......................................... 145 Ilustração 49: Representação Visual da Lista................................................................................... 150 Ilustração 50: Representação visual para o "loop foreach" ............................................................. 151 Ilustração 51: Exemplo de tela do jogo "A Ponte"........................................................................... 158 Ilustração 52: Tela do jogo Pacman..................................................................................................159 Ilustração 53: Tela do jogo Ryu vs Akuma.......................................................................................159 Ilustração 54: Tela do jogo Mundo de Molland................................................................................160 Ilustração 55: Representação Visual dos padrões.............................................................................162 Ilustração 56: Organização de Pacotes do Jogo "A Aventura do Quadrado"....................................164 Ilustração 57: Classe Fase1 com os métodos minimizados..............................................................165 Ilustração 58: Exemplo de visualização de "referência".................................................................. 167 Ilustração 59: Exemplo de classe Fase com os três métodos vazios................................................ 175 Ilustração 60: Exemplo de sobrescrita do método inicia da classe Fase.......................................... 175 Ilustração 61: NetBeans sugerindo implementar os métodos abstratos...........................................176 Ilustração 62: Métodos criados pelo NetBeans após clicar na sugestão de implementar os métodos abstratos............................................................................................................................................ 177 Ilustração 63: Exemplo de uso de uma interface ObjetoJogo.......................................................... 184 Ilustração 64: Implementação da Interface "ObjetoJogo" por um dos objetos do jogo...................184 Ilustração 65: Exemplo da classe abstrata "ObjetoJogo"................................................................. 187 Ilustração 66: Classe abstrata "ObjetoJogo" estendendo a classe "RetanguloGravidade" ..............187 Ilustração 67: Métodos que encapsulam os métodos "processa" e "desenha" da superclasse..........189 Ilustração 68: Exemplo de Hierarquia de Herança na classe "SenhorQ"......................................... 190 Ilustração 69: Classe que representa um item de cura......................................................................191 Ilustração 70: Classe que representa um item que diminui a forca do Pulo.....................................191 Ilustração 71: Exemplo da fase final contendo os itens implementados.......................................... 193 11 / 227 Índice de tabelas Tabela 1: Conceitos POO................................................................................................................... 21 Tabela 2: Documentação de “frameworks”........................................................................................ 29 Tabela 3: Tabela demonstrando os conteúdos trabalhados na experiência prévia com ensino de orientação a objetos baseado em jogos...............................................................................................36 Tabela 4: Índice de acerto em cada um dos critérios de avaliação do jogo 2D.................................. 46 Tabela 5: Conteúdos e Exemplos da metodologia de Boratti............................................................. 51 Tabela 6: Conteúdos e exemplos da metodologia de Barnes..............................................................61 Tabela 7: Conteúdos e exemplos da metodologia de Feijó................................................................ 67 Tabela 8: Conteúdos e ambiente de programação na metodologia de Xinogalos..............................69 Tabela 9: "Live Coding" para a classe Carro....................................................................................106 Tabela 10: "Live Coding"- Desenhando um carro............................................................................110 Tabela 11: Demonstração comparativa para uso de parâmetros....................................................... 118 Tabela 12: "Live Coding" para desenho das casas do Jogo da Velha............................................... 131 Tabela 13: "Live Coding" para finalização do jogo da velha........................................................... 135 Tabela 14: Comparação com e sem uso da herança......................................................................... 147 12 / 227 Lista de Reduções POO Programação Orientada a Objetos OO Orientação a Objetos 2D Duas dimensões 13 / 227 Resumo O presente trabalho apresenta uma metodologia de ensino de Programação Orientada a Objetos tendo como campo de prática os jogos em duas dimensões. A Metodologia foi desenvolvida a partir do estudo de seis autores da área: 1) Barnes; 2) Kölling; 3) Boratti; 4) Feijó; 5) Xinogalos; 6) Caspersen. O método é apresentado detalhadamente em cinco módulos, está baseado em dez princípios e acompanha um framework Java que permite aos estudantes o uso fácil de recursos essenciais para o desenvolvimento de jogos. 14 / 227 1 Introdução Por volta de abril de 2011 o autor deste trabalho iniciou a experiência de ensinar Programação Orientação a Objetos para alunos entre 15 e 17 anos no curso técnico de programação no SENAI de São José, em Santa Catarina. Dentre muitas dificuldades, as que mais se destacaram eram: 1) a desmotivação dos alunos com a programação; 2) a dificuldade com a sintaxe e recursos específicos do Java e 3) a ausência de método para transformação de um problema em um conjunto de classes que representariam sua respectiva solução. A partir desta constatação, iniciou-se na turma do segundo semestre de 2011 a adoção da metodologia de ensino criada por David J. Barnes e Michael Kölling utilizando o BlueJ como ambiente de programação. No semestre seguinte, no início de 2012 e com a mesma turma, iniciou-se um trabalho de desenvolvimento de jogos em Java a partir dos materiais desenvolvidos por Bruno Feijó e Esteban Clua. Ficou evidente após a aplicação destas práticas que a motivação dos estudantes aumentou e os problemas com certas sintaxes e recursos do Java também diminuíram. Entretanto, na análise dos trabalhos finais de desenvolvimento de jogos, ficou evidente que para metade da turma, ainda havia muita dificuldade em resolver problemas aplicando os conceitos de Programação Orientada a Objetos, sem auxílio do professor. Acredita-se que a inexperiência e a falta de método do autor acarretou nas dificuldades percebidas nos alunos ao final do processo de ensino aprendizagem. E é com este ponto de partida que se inicia este trabalho com a busca por métodos, práticas, conceitos e ferramentas que auxiliem alunos – principalmente os que possuem maiores dificuldades com programação – a modelarem e desenvolverem softwares que solucionem problemas com os quais eles próprios venham a se deparar no seu dia a dia, de modo, que se aprenda não apenas a desenvolver códigos orientado a objetos, mas principalmente, conseguir “enxergar os objetos”, como bem colocado pelo professor Ricardo Pereira e Silva na primeira reunião de orientação. 15 / 227 1.1 Justificativa Na Orientação a Objetos, as metodologias e ferramentas de ensino não são tão maduras quanto nos paradigmas mais antigos como a programação estruturada e funcional (Xinogalos), de forma que muitos professores de ciência da computação (Xinogalos, Proulx, Caspersen, Bennedsen, Rosenberg, Kölling, Barnes, Boratti) que se depararam diante da tarefa de ensinar orientação a objetos, iniciaram uma série de pesquisas em busca de diretrizes, ferramentas e metodologias que norteasse essa importante tarefa. O autor considera, pela experiência com ensino, que a grande maioria dos exemplos didáticos em Java, mesmo em algumas metodologias recentes, trabalha com softwares muito simples para serem interessantes ou ainda fora da área de interesse e conhecimento da maioria dos jovens estudantes de programação. Um exemplo é o primeiro projeto apresentado no livro texto do ambiente BlueJ, uma máquina de vender bilhetes de trem, algo completamente fora da realidade brasileira, porém, mesmo que adaptássemos o projeto para, por exemplo, uma máquina de vender café, ainda teríamos a distância do aluno entre simular uma máquina de vender café e programar uma máquina de vender café da vida real. Esta é a vantagem da área de jogos, é possível que o estudante crie algo com o qual ele efetivamente poderá se divertir e além disso, a maioria dos jovens com os quais o autor pode trabalhar, aos 15 e 16 anos, já possuem um grande número de horas de experiência com jogos, permitindo que eles tragam um conhecimento prévio para a atividade de programação, facilitando a fixação dos conceitos com situações problema com as quais eles já estão familiarizados. Além desta perspectiva, o uso de jogos para o ensino de programação é um recurso já utilizado por muitos instrutores na comunidade de educadores de ciência da computação, não só pela sua capacidade de estimular os alunos, mas também por ser um tipo de aplicação para o qual convergem os maiores desafios da computação (Feijó) independente do paradigma de programação utilizado. Porém, livros com conteúdos nesta área geralmente são complexos, utilizando recursos das linguagens de programação que envolvem um grande número de conceitos e regras de sintaxe, o que dificulta sua aplicação didática em sala de aula. Por exemplo, para que seja possível desenhar uma imagem na tela em Java (recurso essencial no desenvolvimento de jogos) é preciso conhecer conceitos complexos como herança e 16 / 227 sobrecarga de métodos. A partir disto, percebe-se que o suporte ao desenvolvimento de jogos em fases iniciais do ensino de programação exige o auxilio de ferramentas que tornem mais fácil o desenvolvimento de recursos como uso de sons, imagens e cenários. Duas soluções atuais foram encontradas, a primeira, um ambiente de programação denominado Greenfoot (Kölling, 2010) desenvolvido para permitir o ensino de programação com interação e visualização, permitindo uma inclusão rápida e fácil de imagens, sons e posicionamento de elementos em um cenário. A segunda, é o conjunto de bibliotecas denominado JavaLib (Proulx, 2010), cujo objetivo é permitir que o estudante mantenha o foco no projeto do jogo, no seu comportamento e na representação de cada cena através de formas geométricas simples. Mais recentemente a biblioteca foi ampliada para permitir o ensino de OO através da arte de escrever e tocar músicas com abstrações de notas e arranjos musicais. Para este projeto, foi escolhida uma abordagem próxima a de Proulx pois como relata Xinogalos - que utilizou e ainda utiliza o ambiente BlueJ parcialmente em sua metodologia - é importante preparar os estudantes para serem profissionais usando ferramentas profissionais de desenvolvimento, manter o aluno por muito tempo em ambientes especificamente voltados para o ensino pode torná-lo por demais acostumado aos recursos didáticos e limitadores destes ambientes, não desenvolvendo plenamente as habilidades de programação e abstração, necessárias a um profissional. Outro fator favorável ao uso de bibliotecas e frameworks no ensino deriva do fato de ser muito comum no desenvolvimento de software o aprendizado de novas bibliotecas e frameworks. Sendo necessário ao profissional de programação o domínio das habilidades de compreensão da documentação e utilização de tais bibliotecas e frameworks. Observa-se também que alguns conceitos como Interfaces e classes abstratas tem sua aplicação observada mais frequentemente em frameworks, sendo a extensão de um framework uma forma de estudo de tais conceitos. Deste modo, considera-se essencial para este trabalho o desenvolvimento de um framework cuja responsabilidade é abstrair as funções básicas necessárias para jogos 2D como construção de cenários, execução de sons, animações, desenho de imagens e demais necessidades dos jogos 2D de modo que o aluno possa se concentrar, no projeto 17 / 227 e construção das classes e de suas interações. A proposta da metodologia deve conter em detalhes o processo de ensino de modo a ser facilmente replicado. É essencial também para o planejamento de outros professores, a estimativa da carga horária utilizada e de exemplos de projetos e tarefas a serem solicitados aos alunos. Por fim, justifica-se a escolha da linguagem Java em uníssono com Feijó que caracteriza cinco vantagens para esta linguagem: 1) Permite aplicações em um grande número de plataformas; 2) Roda em mais de um Sistema Operacional e conta com um bom ambiente de programação gratuito (NetBeans); 3) O Java é uma linguagem Orientada a Objetos; 4) Java é um ponto de partida para a programação para internet; e 5) A próxima geração de linguagens orientadas a objetos será uma evolução natural do Java. 1.2 Objetivo Geral Criar uma metodologia de ensino de Orientação a Objetos através do desenvolvimento de jogos 2D. 1.3 Objetivos Específicos • Apresentar e analisar algumas das principais metodologias de ensino de programação orientada a objetos ressaltando seus pontos fortes e fracos segundo a experiência do autor; • Definir diretrizes para o desenvolvimento da metodologia a partir das metodologias estudadas e da experiência anterior do autor; • Elencar elementos pedagógicos que auxiliem a construção da metodologia; • Construir um “framework” Orientado a Objetos que atenda as principais características de jogos 2D e esteja de acordo com as diretrizes definidas; • Documentar uma metodologia que atenda a um curso introdutório de programação orientada a objetos de 72 horas aula de modo a ser facilmente replicada por qualquer professor de orientação a objetos; 18 / 227 1.4 Metodologia de Pesquisa Inicia-se o trabalho com a fundamentação teórica de alguns elementos considerados essenciais ao trabalho como programação orientada a objetos, jogos em duas dimensões, “frameworks” orientados a objeto e elementos pedagógicos para a ciência da computação. Após, considera-se essencial a documentação da metodologia utilizada para o ensino de orientação a objetos baseados em jogos 2D na experiência que deu origem ao tema do presente trabalho, de modo que as dificuldades e pontos fortes do ensino anterior fortaleçam as observações sobre a nova metodologia a ser desenvolvida. Após, será realizada uma pesquisa nas metodologias para ensino de orientação a objetos, sendo escolhidas para detalhamento as consideradas pelo autor como mais relevantes ao desenvolvimento deste trabalho. A partir deste detalhamento serão definidas diretrizes para o ensino de OO que deverão ser contempladas nesta metodologia. Com as diretrizes definidas, bem como os elementos de jogos em duas dimensões, será proposto um framework que atenda a estes elementos. Em seguida, é proposta a metodologia de ensino utilizando os elementos apresentados nos capítulos anteriores. 19 / 227 2 Fundamentação Teórica Este capítulo apresenta alguns elementos básicos ao trabalho sem, entretanto, apresentar um estrutura lógica sequencial entre estes elementos, de modo que servem como base para os demais capítulos e discussões. 2.1 Programação Orientada a Objetos Biologia e matemática. Esta era a formação base de Alan Kay quando ele criou a Smalltalk, reconhecida como a primeira linguagem totalmente orientada a objetos, no famoso laboratório da Xerox PARC em Palo Alto, no final dos anos 60. Observamos a influência desta formação em dois dos princípios iniciais que nortearam sua pesquisa, o primeiro era que ele inicialmente pensava em objetos ”como células biológicas e/ou computadores individuais em uma rede, apenas habilitados a se comunicarem através de mensagens.”. O segundo, que pode ter sido uma das origens às modernas classes, é derivado de sua raiz matemática, que lhe fez perceber que “cada objeto poderia ter certas álgebras relacionadas a ele, e que poderiam haver famílias destas álgebras, o que seria muito útil.” Além disso, foi fortemente influenciado pela Sketchpad, Simula, Lisp, o projeto da ARPAnet e a (segundo ele), quase inacreditável arquitetura dos discos rígidos B500 da Burroughs. Estes princípios são compatíveis com Boratti que coloca sobre o paradigma de Programação Orientada a Objetos: “Esse paradigma procura abordar a resolução de um problema através de uma construção que represente, da melhor forma possível, como as coisas acontecem no mundo real. O mundo real é constituído por entidades que interagem entre si. Uma entidade pode ser definida como alguma coisa que tenha sua própria existência, características e que apresente alguma função no mundo real...uma entidade pode ser denominada objeto e, consequentemente a identificação do paradigma de programação orientada a objetos”. 20 / 227 Portanto Programação Orientada a Objetos corresponde um modo de programar no qual são construídos objetos (entidades, células), pertencentes à classes, que interagem entre si através de mensagens e possuem características próprias. 2.1.1 Conceitos POO Os conceitos relacionados à POO que serão abordados pela metodologia foram selecionados principalmente a partir da metodologia de Barnes. Para uma melhor organização, estes foram categorizados em dois níveis de dificuldade segundo a experiência do autor: Nível Básico Nível Avançado • Classe; • Métodos de acesso; • Interfaces; • Métodos modificadores; • Classes abstratas; • Variáveis e Tipos; • • Variável de objeto / Variável de Instância / atributo; Polimorfismo: Variáveis polimórficas / Polimorfismo de método; • Sobrescrita de método; • Objetos / Instâncias; • • Parâmetros; Modificador static: contantes de classe / métodos estáticos; • Construtores; • Coesão; • Abstração; • Encapsulamento / Modificadores de acesso; • Associação / Composição; • Herança / Especialização / Generalização; Tabela 1: Conceitos POO Alguns conceitos importantes como “Tratamento de Exceção” e “Acoplamento” não foram abordados por limitação do escopo do trabalho. 2.1.2 Estruturas Básicas de Programação em Java De modo a permitir o uso desta metodologia em cursos introdutórios de programação se faz fundamental o ensino de conceitos de determinadas estruturas básicas de controle e de repetição. as estruturas e conceitos básicos que serão abordadas na metodologia serão: 21 / 227 • Estruturas condicionais (if, else e else if); • Estruturas de repetição (while, for e foreach); • Arrays / Matrizes unidimensionais e bidimensionais; • Instrução de atribuição (=, +=, -=, etc); • Variáveis Locais; • Comentários; • Tipos primitivos; 2.1.3 Conceitos da Linguagem Java A linguagem utilizada como base para o trabalho é o Java, versão 6, de modo que algumas de suas especificidades deverão ser levadas aos alunos sempre que necessário. Estas especificidades podem incluir certas construções sem nenhuma relação com orientação a objetos, processo de compilação, tipos de arquivos (bytecode, .class, etc) e certas classes específicas que utilizaremos ao longo do projeto como “ArrayList”. 2.2 Jogos 2D Esta seção apresenta os principais conceitos utilizados na arquitetura de jogos em duas dimensões a partir do material desenvolvido por Perucia e que acompanha um framework desenvolvido na linguagem de programação C++ para o desenvolvimento de jogos. Um jogo desenvolvido com o framework apresentado por Perucia, pode apresentar, dentre outros recursos: • Menu principal e de opções; • Personagem animado controlável por teclado; • Cenários com profundidade; • Itens especiais de jogo; • Interação do personagem com cenário e itens; • Inimigos e interação do personagem com estes inimigos; 22 / 227 • Controle de pontos de vida do personagem; • Execução de sons; A arquitetura de um jogo pode envolver diversos subsistemas, um jogo 2D sempre apresentará pelo menos alguns dos subsistemas abaixo: • Interface gráfica (menus, botões, controle, etc); • Tratador de eventos de teclado e/ou joystick; • Gerenciador de dados (fases, gráficos, etc); • Gerenciador de física (velocidade, movimento, colisão, etc); • Engine gráfica (exibição de imagens, rotação, efeitos especiais, etc); • Engine sonora; • Engine lógica (o coração do jogo); • Camadas de abstração de hardware; • Sistema de configuração do jogo (opções, nível de dificuldade, salvamento, etc); • Sistema de menus; • Sistema de ajuda; • Sistema de música; A partir de Perucia et al, dos exemplos tratados por Feijó e da experiência pessoal do autor foram selecionados os principais conceitos envolvendo a arquitetura de jogos e que serão indispensavelmente cobertos pelo framework que oferecerá suporte a metodologia 2.2.1 Desenho de imagens O desenho de imagens na tela a partir de determinados arquivos-fonte é uma operação frequente e pode ser complementada com recursos desejáveis como transparência, rotação, aumento ou redução do tamanho original e espelhamento que corresponde ao desenho da mesma imagem só que em posição invertida ou espelhada. 23 / 227 2.2.2 Animações Animações são sequências de imagens que se alternam em função de um determinado tempo. Em um jogo, é importante que seja possível aumentar e diminuir a velocidade de uma animação (em casos por exemplo, da movimentação de um personagem mais rápido ou devagar) e que assim como nas imagens, seja possível aplicar efeitos de transparência, rotação, aumento ou redução do tamanho e espelhamento. 2.2.3 Tiles e Composição de Cenários “Tiles são imagens adicionadas em um cenário e divididas em pedaços iguais de tamanho fixo” (Perucia et al). Tiles podem ser utilizados em conjunto com arquivos de configuração para criar cenários complexos. Segue abaixo uma demonstração de construção de um cenário baseado em Tiles. Ilustração 1: Cenário baseado em Tiles Na experiência de ensino de POO baseado em jogos, os alunos requisitaram em vários momentos recursos extras associados ao uso de cenários com Tiles, como por exemplo: que os tiles possam ser escondidos e reexibidos (para casos por exemplo onde existam passagens secretas), que possuam tratamento de colisão com o personagem e demais objetos do jogo e que possam também serem movimentados 2.2.4 Pontos / Plano Cartesiano Um jogo em duas dimensões ocorre em um plano cartesiano. É importante que existam abstrações para pontos neste plano e operações entre estes pontos como calculo da distância e detecção de ponto mais próximo dentre um conjunto de pontos por exemplo. 24 / 227 2.2.5 Sprites “Sprites são estruturas com imagens próprias que permitem a criação de animações e o livre posicionamento na tela” (Perucia). Uma sprite em geral é representada por um único arquivo dividido em quadros de tamanho fixo, contendo cada quadro uma imagem. A mesma sprite pode ser posicionada em um determinado ponto da tela exibindo apenas um dos seus quadros internos por vez. A mudança da exibição destes quadros ao longo do tempo resulta em uma animação. Ilustração 2: Exemplo de uma Sprite 2.2.6 Dispositivos de Entrada A interação com o jogo irá ocorrer sempre através de um dispositivo. Os dois mais comuns são teclado e mouse. No caso do teclado é preciso que o desenvolver tenha disponível a informação de se uma (ou um conjunto) determinada tecla está ou não pressionada em um determinado momento no tempo. No caso do mouse, as principais necessidades são a posição do ponteiro do mouse e se o botão esquerdo foi pressionado. Também é importante que exista uma interface para um joystick, ou seja, um controle tradicional de video-game com 4 botões de ação e quatro botões direcionais. 2.2.7 Execução de sons Os recursos de som disponíveis para o desenvolvedor são apenas: 1) carregamento dos arquivos de som; 2) tocar um som (com ou sem loop) e 3) parar um som. O arquivo de áudio pode ser no formato wave, comumente usado em jogos. 2.2.8 Taxa de Quadros e atualização do jogo Representa o número de quadros de um jogo que uma aplicação consegue executar em um segundo. O jogo irá sempre executar uma chamada de atualização do estado do jogo em loop. A taxa de quadros representa o número de repetições deste quadro em um segundo. 25 / 227 2.2.9 Acumuladores de Tempo É comum em jogos eletrônicos determinadas ações somente ocorrerem após um determinado tempo. Para realizar este cálculo de tempo uma estrutura conhecida como acumulador de tempo pode ser fornecida. Esta estrutura inicializa com o tempo que irá controlar e, a cada atualização do jogo, deve também ser atualizada. A estrutura fornece uma interface para que o desenvolver identifique se o fim do tempo já chegou e a possibilidade de reiniciar o acumulador. 2.2.10 Gravidade A gravidade pode ser representada em um jogo por um número em pixels por segundo que representa a aceleração da gravidade. Na vida real, a força da gravidade pode ser contrabalanceada pelo atrito com o ar. Em um jogo, ao invés de utilizar o atrito com o ar, cada objeto recebe um número que representa a aceleração da gravidade sobre ele. Isso permite por exemplo, que um objeto passe a ter um salto maior, simplesmente se a força da gravidade sobre ele for diminuída. No mundo real, existe um ponto em que o atrito com o ar se iguala a força gravitacional de modo que a velocidade de queda permanece constante. Isto é reproduzido em um jogo, através de uma velocidade máxima que um objeto pode receber quando está em queda. 2.2.11 Detecção e Tratamento de Colisões A detecção de colisões ocorre verificando se duas formas geométricas que representam objetos do jogo, se tocam. Estas formas geométricas são comumente retângulos e/ou círculos, e a colisão será verificada através de operações matemáticas simples que verificam a intersecção destas formas. Após a detecção ter ocorrido é necessário tratar a colisão. Ou seja, se o personagem colidir com uma parede à direita, ele não poderá mais se mover para a direita e deverá ser reposicionado para o exato momento antes da colisão. Uma das formas de dividir este tratamento é tratando primeiro a colisão com o eixo vertical e somente após, com o eixo horizontal. Também é importante que o desenvolvedor tenha condições de saber para qual direção ocorreu a colisão. 26 / 227 2.2.12 O Game Loop Todo jogo de computador ocorre dentro de um laço de repetição. Este laço realiza três operações básicas: • Entrada: onde são verificados os comandos emitidos pelos dispositivos de entrada, mensagens via rede, etc; • Processamento: onde é feito o cálculo das posições dos objetos, colisão, etc; • Desenho: onde os objetos são apresentados na tela do dispositivo. 2.2.13 Movimentação e Gravidade nos cenários Os cenários são um dos grandes núcleos do jogo. A grande maioria dos jogos possuem cenários grandes que podem ser percorridos por um jogador. Além disso, o efeito da gravidade deve parar de ocorrer quando um jogador encosta no chão, geralmente definido em função do cenário. 2.3 Frameworks Orientado a Objetos Este capítulo disserta sobre o desenvolvimento e uso de frameworks orientados a objetos a partir do trabalho de Silva, esclarecendo as metodologias de desenvolvimento existentes bem como as maneiras de documentá-la de modo a proporcionar o uso adequado dos frameworks de acordo com as diferentes necessidades dos seus usuários. 2.3.1 Frameworks OO Frameworks Orientados a Objetos correspondem a um conjunto de classes que se adaptam ao desenvolvimento de aplicações de um domínio específico, oferecendo estruturas flexíveis para os pontos em que aplicações de um mesmo domínio diferem entre si. Duas das principais características de um framework são alterabilidade e extensibilidade: • Alterabilidade: Permite que os conceitos específicos das aplicações sejam suportados a partir de estruturas flexíveis do framework; • Extensibilidade: Garante que o framework possa ser estendido no seu conjunto de funcionalidades, ou seja, tenha sua manutenibilidade garantida; 27 / 227 Um framework sempre depende de outros artefatos de software para existir, basicamente, as aplicações específicas de um domínio que o originaram e as aplicações geradas a partir dele. 2.3.2 Metodologias de Desenvolvimento de Frameworks OO Muitos elementos do processo de desenvolvimento de um framework são comuns à diversas metodologias, podemos elencar estes elementos da seguinte forma: • Aquisição de conhecimento de domínio: A primeira etapa é o estudo de aplicações desenvolvidas para o mesmo domínio do framework de modo a identificar generalidades e especificidades dentro das aplicações. • Construção da estrutura de classes: Após a análise, é realizada a modelagem de classes. Esta modelagem pode ser dividida em cinco etapas iterativas: ◦ Generalização: Criação de classes para as estruturas idênticas das aplicações; ◦ Flexibilização: Localização das especificidades dos frameworks; ◦ Aplicação de meta padrões: Criação de métodos template que podem ser flexibilizados através de métodos hooks para os pontos de flexibilização encontrados; ◦ Aplicação de Padrões de Projeto: Incluir classes de um padrão (design pattern) já catalogado e conhecido para uma determinada estrutura do framework; ◦ Aplicação de Princípios de OO: Aplicação de princípios específicos de Orientação a Objetos como uso de herança e composição. • Implementação: Criação do framework em uma linguagem de programação; • Avaliação: O framework deve ser avaliado a partir do desenvolvimento de aplicações do domínio para o qual foi criado; • Refinamento: A partir do desenvolvimento de novas aplicações, o conhecimento sobre o domínio aumentará, possibilitando a melhoria do framework. 28 / 227 2.3.3 Uso de Frameworks OO Um framework deve facilitar o desenvolvimento de aplicações de um domínio. Se entender o framework for muito complexo, pode ser mais fácil desenvolver a aplicação sem o framework. Uma das formas de garantir uma documentação que atenda às necessidades dos usuários do framework é adaptando a documentação à estas necessidades. Segue abaixo uma proposta de documentação específica para três tipos de usuários comuns: Tipo de Usuário Tipo de Documentação Precisa decidir qual framework utilizar para o desenvolvimento de uma aplicação Descrição do que pode e do que não pode ser feito com o framework. Precisa descobrir como desenvolver uma aplicação Receitas específicas de como utilizar um determinado recurso e/ou desenvolver uma aplicação (Cookbook). Precisam conhecer em detalhes o projeto do framework para o desenvolvimento de aplicações complexas Podem ser utilizados diagramas, descrições mais detalhadas da estrutura das classes, código fonte e outras ferramentas. As questões chave para este entendimento são: • Quais classes usar: Quais classes concretas do framework podem ser utilizadas e quais devem ser criadas pelo desenvolvedor; • Quais métodos: devem ser redefinidos pelo desenvolvedor (métodos abstratos) e quais devem ser definidos pelo framework. Entra aqui também a definição dos métodos template; • O que os métodos fazem: qual o cmportamento dos métodos definidos pelo framework. Tabela 2: Documentação de “frameworks” 2.4 Padrões Pedagógicos Como apoio às metodologias de ensino de programação orientada a objetos os autores frequentemente recorrem à padrões pedagógicos que são projetados para capturar as melhores práticas e a excelência da arte de aprender e ensinar (THE PEDAGOGICAL PATTERNS PROJECT) . Para o ensino de ciências da computação, Joseph Bergin documentou catorze padrões pedagógicos. Os padrões possuem vários níveis de aplicação, desde a organização geral de uma disciplina até mesmo práticas em sala de aula. Os padrões são apresentados em ordem de escala, iniciando pelos que envolvem o planejamento de cursos semestrais até atividades diárias em salas de aula. 29 / 227 2.4.1 O Mais Importante Primeiro (Early Bird) O curso é organizado de modo que o mais importante deve ser apresentado primeiro (e frequentemente), e caso isto seja impossível, o quanto antes. Por exemplo, se funções for mais importante que estruturas if, ensine funções primeiro, se objetos são mais importantes que funções, ensine objetos primeiro. Esta prática é importante porque os estudantes geralmente se lembram melhor do que viram primeiro e é importante que eles percebam desde o início a ideia principal do curso. Alguns exemplos são: • Ensinar objetos primeiro; • Ensinar projeto primeiro; • Ensinar concorrência primeiro em sistemas operacionais; • Ensinar requisitos de usuário primeiro em Banco de Dados; • Ensinar recursão antes de loops; 2.4.2 Espiral (Spiral) Quando um conteúdo é trabalhado em sua ordem lógica, o instrutor precisa entrar em muitos detalhes, tornando o ensino desgastante para os estudantes. Uma solução é trabalhar os conteúdos em fragmentos que permitam que o estudante resolva um determinado problema, posteriormente, mais fragmentos são trabalhados de modo a resolver outros problemas, e assim, sucessivamente, trabalhando o mesmo conteúdo mas em nível de detalhe crescente. Deste modo, desde cedo o estudante é empoderado a construir aplicações. 2.4.3 Metáfora Consistente (Consistent Mataphor) Ao ensinar tópicos complexos, é importante utilizar metáforas cuja base seja conhecida pelos estudantes, de modo que ele tenham clareza sobre como o sistema deve se comportar e possam fazer conexões dos novos conceitos com conceitos que já são bem conhecidos por eles. 30 / 227 2.4.4 Caixa de Brinquedos (Toy Box) O objetivo deste padrão é permitir que desde cedo os estudantes possam entrar em contato com o conhecimento da área do curso através de ferramentas pedagógicas ilustrativas, os brinquedos. Estes brinquedos podem ser classes que abstraem parte da complexidade envolvida no desenvolvimento de um software de maior escala, ferramentas que tornam a programação mais acessível, ou qualquer outro tipo de artefato que permita que os estudantes desenvolvam projetos maiores e mais interessantes. Um dos requisitos deste padrão é que os instrutores precisam preparar o material e ter tempo disponível para que os estudantes explorem os recursos disponibilizados. 2.4.5 Caixa de Ferramentas (Tool Box) Desde o início do curso os estudantes desenvolvem ferramentas que lhes serão úteis em fases avançadas do curso, funcionando também como ensino de reuso de software. Os exercícios do curso devem ter diversos níveis e nos níveis iniciais os estudantes são estimulados a discutir e desenvolver seus componentes. É importante também que o projeto de software para reuso seja discutido e apresentado o quanto antes pelo instrutor. Estes componentes desenvolvidos pelos alunos podem ser complementados por bibliotecas fornecidas pelo instrutor. Um bom exemplo da aplicação deste padrão é o uso de elementos construídos em disciplinas de estruturas de dados. 2.4.6 Disposição do território (Lay of the Land) Os estudantes devem examinar um artefato do domínio que estão estudando de maior complexidade, de modo a ver a imagem real de um produto na sua área de atuação. Isto permite que eles façam conexões das diversas partes do sistema com os conteúdos que estão aprendendo, situando cada um deles no contexto de uma aplicação real. Exemplos podem ser: • Um software com várias classes interagindo em Orientação a Objetos; 31 / 227 • Um modelo entidade relacionamento com diversos relacionamentos em um curso de bases de dados; • O projeto completo de um software em um curso de projeto orientado a objetos. É importante que o artefato seja algo semelhante ao que o instrutor deseja que o estudante desenvolva ao final do curso. É essencial também, que o artefato tenha um design e utilize os conceitos do curso de forma excelente. Assim, as diversas partes do artefato a suas interações podem ser examinadas pelos estudantes e posteriormente utilizadas como exemplo de um determinado conceito do curso. 2.4.7 Corrigir para cima (Fixer Upper) Neste padrão, um artefato de grande porte é oferecido aos alunos com falhas cuidadosamente preparadas. Este padrão permite o ensino de novos conceitos e da análise e correção de erros. As falhas devem ser óbvias para a maioria dos leitores, contendo uma ou duas falhas de maior complexidade. A origem do nome do padrão vem da expressão “Fixxer Upper” que representa uma casa vendida em condições ruins, de modo que o comprador precisará repará-la após a compra. 2.4.8 Maior que a Vida (Larger than Life) Em cursos de literatura, sociologia e história por exemplo, é comum que os estudantes trabalhem com textos que eles não teriam condições de criar. E como ler é mais fácil do que escrever, este padrão fala sobre usar artefatos de software de maior complexidade e bem escritos como exemplos a serem copiados. Esta é uma forma de estimular o conhecimento sobre o desenvolvimento de softwares reais que os estudantes ainda não teriam condições de projetar e desenvolver. Um exemplo são bibliotecas padrão das linguagens de programação atuais. 32 / 227 2.4.9 Sprint de Projeto dos Estudantes (Student Design Sprint) Ensinar a projetar softwares é hoje reconhecido como necessidade por muitos instrutores. Este padrão sugere o uso de atividades que permitam que os estudantes projetem soluções para problemas, recebam feedback, projetem novamente, recebam novo feedback, e assim sucessivamente. Um exemplo de atividade é separar os alunos em grupos de dois ou três e solicitar o projeto para um determinado problema. Ao final, são levantadas questões que não podem ser respondidas com um design inadequado e os estudantes são organizados em grupos de 4 ou 5 de modo que cada grupo tem pelo menos duas propostas de projeto. Eles são desafiados a criarem uma nova proposta, ao final, mais questões podem ser levantadas e alguns projetos discutidos. O processo pode continuar enquanto o professor desejar. 2.4.10 Engano (Mistake) Os estudantes são estimulados a criarem artefatos com erros propositais, de modo a ensiná-los como reconhecer erros e suas consequências. Todos cometemos erros, profissionais experientes geralmente conseguem reconhecer um erro quando visualizam as consequências de um, estudantes não sabem reconhecer estes erros. 2.4.11 Canal de Teste (Test Tube) Também conhecido como “teste e veja”, este padrão força que os estudantes testem suas próprias perguntas para questões do tipo “O que aconteceria se eu..”. Desde que exista acesso a um computador, eles podem testar suas hipóteses. É importante que o instrutor forneça listas de exercícios levando em conta as principais dúvidas apresentadas colocando em diversos pontos questões do tipo “O que aconteceria se você...”. Isto pode ser usado para testar as regras de sintaxe de uma linguagem por exemplo e/ou o uso de operadores. Um bom exemplo é o ensino de loops, onde a simples mudança de algumas variáveis podem gerar impressões na tela diferentes, de 1 até 10 ou mesmo um loop infinito. 33 / 227 2.4.12 Preencha as Lacunas (Fill in the Blanks) É muito difícil e o tempo gasto seria muito alto para estudantes iniciantes desenvolverem softwares complexos, porém, o que eles podem fazer com as habilidades iniciais geralmente é muito chato. A solução proposta é disponibilizar um software complexo que os estudantes possam analisar e deixar pequenas lacunas no software a serem implementadas pelos alunos com os conhecimentos que possuem. Um exemplo simples é uma definição de classe em Java onde o corpo de um método foi deixado em branco. 2.4.13 Estrela de Ouro (Gold Star) Os estudantes devem ser recompensados pelo que eles fazem bem e jamais os deprecia por ainda não terem atingido as competências almejadas. 2.4.14 Avalie isto novamente Sam (Grade it again Sam) Permita que trabalhos e atividades sejam enviados posteriormente para reavaliação. Isso estimula os estudantes a criarem erros e os motiva a melhorarem seus trabalhos para melhorar suas notas. 34 / 227 3 Experiência prévia com ensino de POO e Jogos A documentação detalhada da primeira experiência do autor com o desenvolvimento de jogos e ensino de OO se fez necessária para demonstração das principais dificuldades encontradas, de modo a justificar certas escolhas para a metodologia a ser construída, bem como, os resultados na motivação dos estudantes. No primeiro semestre de 2012, no curso Técnico em Programação articulado com o Ensino Médio do SENAI São José em Santa Catarina, na disciplina de Programação Orientada a Objetos II, foi trabalhado um módulo de desenvolvimento de jogos com Java. A turma possuia 33 alunos entre 16 e 18 anos. Esta disciplina está presente no terceiro semestre do curso. Os estudantes já chegam a esta disciplina com uma base sobre alguns conceitos básicos de OO como construção de classes, métodos, atributos e parâmetros, bem como conceitos básicos de estruturas de programação como if, while, switch, dentre outros. Entretanto, por ser um curso paralelo ao ensino médio e pelo perfil do público, é comum que eles cheguem na terceira fase após o período de férias sem se lembrar com exatidão dos conteúdos trabalhados nos semestres anteriores, sendo necessário relembrá-los. Este módulo teve duração de 76 horas/aula. Destas, 40 horas/aula foram utilizadas para o ensino dos conceitos de Programação e Jogos 2D. As demais 28 horas foram utilizadas pelos alunos em sala com orientação do professor para o desenvolvimento do jogo. 3.1 Conteúdos trabalhados O conteúdo foi dividido em duas categorias, Programação Orientada a Objetos (foco da disciplina) e Jogos 2D. Além destes conteúdos, questões como uso de controle de versão e uma IDE de programação também foram abordados, porém, por não fazerem parte deste estudo, não serão citados. Segue abaixo tabela com os conteúdos trabalhados: 35 / 227 Conteúdos POO C1) Composição; C2) Encapsulamento; C3) Herança; C4) Polimorfismo; C5) sobrecarga e sobreescrita de métodos e construtores; C6) Classes abstratas, C7) Interfaces; C8) Enum; C9) Métodos e atributos estáticos C10) Tratamento de Exceção C11) Padrões de desenvolvimento (frameworks); Conteúdos Jogos 2D C20) Sprites; C21) TileMaps; C22) Animação; C23) Desenho 2D; C23) Captura Eventos Teclado; C24) Captura Eventos Mouse. Tabela 3: Tabela demonstrando os conteúdos trabalhados na experiência prévia com ensino de orientação a objetos baseado em jogos. Para melhor entendimento, segue abaixo uma descrição breve de como cada um dos conteúdos foi trabalhado: • C1) Composição: Para formar objetos de um jogo, como personagens de luta, era preciso ter uma classe composta de outras que representassem imagens, 'magias' dos personagens, etc; • C2) Encapsulamento: Alguns recursos como colisão de personagens com o cenário foi encapsulado pelo professor e fornecido através de classes, como forma de passar o conceito, depois, no desenvolvimento do jogo e dos exemplos em aula eles foram sembre lembrados de encapsular a lógica de alguns elementos do jogo dentro de sua respectiva classe; • C3) Herança: Todos os objetos do jogo precisavam estender uma classe chamada GameObject que já possuía características básicas de um objeto, como a posição inicial, métodos de acesso e métodos modificadores a estes atributos; • C4) Polimorfismo: É comum termos itens em jogos 2D, estes itens podem aumentar a vida do personagem, deixá-lo mais rápido ou mais lento, dentre outras características. Uma das formas de se trabalhar o polimorfismo foi com classes que representavam itens, estas classes estendiam uma classe Item, e todos os itens, representados com subclasses abstratas eram referenciados no projeto como objetos da classe Item, para permitir extensão do software. Também foi visto polimorfismo de método com métodos de colisão que podiam receber diferentes parâmetros. • C5) Sobrescrita de métodos e construtores: A maioria dos métodos implementados pelos estudantes eram da própria classe ou abstratos. A sobrescrita foi trabalhada apenas quando os estudantes estendiam uma classe que representava um objeto com gravidade. A lógica responsável por parar o objeto em caso de queda se ele colidisse com um chão era um comportamento padrão que os estudantes precisavam sobrescrever quando haviam especificidades nos seus objetos, sempre lembrando de chamar o método sobrescrito da classe superior para não perder o efeito da gravidade; • C6) Classes abstratas: A classe GameObject, da qual todos os objetos do jogo devem herdar, é abstrata e exige a implementação de dois métodos, um para execução da lógica do objeto e outro para desenhá-lo. • C7) Classes Interface: Cada fase do jogo era representada por uma classe que implementava uma interface. Esta interface apresentava métodos para quando uma fase iniciava, era carregada, desenhada ou estava em execução; 36 / 227 • C8) Enum: Quando foram trabalhados jogos de luta, um enum foi utilizado para controlar o estado do personagem, ou seja, se ele estava pulando, apanhando ou em estado normal; • C9) Métodos e atributos estáticos: O framework utilizado apresentava diversos métodos estáticos, principalmente para recuperar a referência aos objetos que capturam ações dos periféricos e realizam operações de desenho. Os atributos estáticos foram trabalhados para representar as teclas do teclado; • C10) Tratamento de Exceção: Sempre que uma imagem era carregada para o jogo, corria-se o risco dela não existir. Neste caso, uma exceção era gerada internamente pelo software e o estudante deveria tratá-la. Infelizmente neste ponto, apenas era exibida uma mensagem de erro ao usuário e o tratamento de exceção não foi visto em sua plenitude, quando o programador usa exceções para manter o software funcionando mesmo em caso de falhas. • C11) Padrões de desenvolvimento (frameworks): Foi utilizado durante toda a disciplina o framework javaPlay (Feijó et al), de modo que o conceito de framework foi trabalhado constantemente; • C20) Sprites: Em um jogo onde o jogador precisava mover uma nave pelo cenário, foi utilizada uma sprite com a imagem da nave em todas as direções. Como suporte, foi fornecida uma classe que desenhava os quadros de uma sprite, adaptada do framework javaPlay; • C21) TileMaps: Cada cenário era representado por um conjunto de Tiles e um arquivo de configuração que dizia como posicionar os tiles no cenário. Uma classe que desenhava o cenário a partir do arquivo de configuração foi fornecida; • C22) Animação: Foi usado o efeito de bombas explodindo, onde a cada fatia de tempo era preciso mudar a imagem da bomba desenhada na tela; • C23) Desenho 2D: Nos exemplos iniciais foram utilizados somente figuras geométricas simples como círculos e linhas. Todas desenhadas pela API Graphics do próprio Java; • C24) Captura de Eventos Teclado: Através da classe Keyboard fornecida pelo framework javaPlay com métodos para saber se uma determinada tecla estava pressionada; • C25) Captura de Eventos Mouse: Através da classe Mouse fornecida pelo framework javaPlay com métodos para saber se o botão esquerdo ou direito do mouse estava pressionado e qual a posição do cursor na tela; 3.2 Metodologia O principal elemento da metodologia foi o uso de exemplos, no qual era aplicado um determinado conjunto de conceitos na construção de um jogo e, a seguir, atividades práticas de melhoria do exemplo apresentado de modo a permitir que o estudante tivesse um contato com o conceito e a sua implementação. Apenas em um único momento os estudantes iniciaram o desenvolvimento com uma tela vazia, neste caso, tinham em mãos um script que os guiaria pelos conceitos e recursos básicos do javaPlay (Feijo), framework utilizado como base para o projeto. Antes do início deste módulo com jogos houve uma revisão breve dos conceitos de classe, métodos, atributos, e outros conceitos básicos de OO. 37 / 227 Todos os exemplos foram desenvolvidos tendo como base o framework javaPlay (Feijó). E, após um período de aulas teóricas, os alunos puderam desenvolver seu próprio jogo, com tema livre. Seguem abaixo o relato dos exemplos utilizados: 3.2.1 Exemplo 1 – Movimentando uma bola No primeiro momento, foi apresentado um exemplo de uma bola se movimentando horizontalmente na tela a partir do movimento do teclado (Caminha, Exemplo 1). Esta lição demonstrou o uso básico do framework, como acessar o teclado e como criar um objeto de jogo, no caso, a bola. Ilustração 3: Movimentando uma bola na tela Após a apresentação, os estudantes receberam a tarefa de permitir o movimento vertical da bola no eixo vertical sem perder o movimento horizontal existente. Ao final, um exemplo de um jogo mais complexo (Caminha, Exemplo 2) utilizando o objeto da bola foi mostrado para exemplificar o potencial do que eles estavam aprendendo. Também foi disponibilizado um tempo para que eles fizessem alterações neste exemplo mais complexo, agora porém, de forma livre. Ilustração 4: Exemplo mais complexo utilizando a bola criada anteriormente 38 / 227 3.2.2 Exemplo 2 – Construindo o primeiro jogo No segundo momento, era importante que os estudantes iniciassem o desenvolvimento de um jogo simples, porém, passando por todas as etapas do processo de desenvolvimento, já que na etapa anterior, haviam apenas realizado uma modificação. Para auxiliá-los foi fornecido um roteiro de 10 passos (Caminha, javaPlay em 10 passos) que guiava a construção de um jogo (Caminha, Exemplo 3) no qual o personagem controla um jogador em formato de bola (verde) que deve fugir de alguns inimigos (bolas amarelas e vermelhas) até um elemento de chegada, uma estrela. Ilustração 5: Tela final do jogo desenvolvido com o auxílio do roteiro 3.2.3 Exemplo 3 – Ítens e fases Passamos agora a melhorar o jogo desenvolvido com o auxílio do roteiro. A primeira atividade foi o uso do framework para poder realizar a passagem de fases. Então, foi construído um jogo com três fases. 1. A primeira fase corresponde ao jogo já criado na etapa anterior; 2. Na segunda fase o personagem também pode capturar itens. Um dos itens é representado por um quadrado verde e aumenta a velocidade do jogador. O segundo é um quadrado vermelho e inverte a movimentação. Para a construção dos itens foi criada uma classe abstrata que obriga o programador a implementar um método de ação quando o personagem tem colisão com alguns dos itens. Antes de avançar para a terceira fase, os estudantes são orientados a criar um novo tipo de item; 3. Na terceira fase, uma única lista é utilizada para armazenar os diversos itens do 39 / 227 jogo. Quando existe colisão com algum deles o método definido na classe abstrata é executado. Ilustração 6: Terceira fase do exemplo com diversos itens na tela 3.2.4 Exemplo 4 – Guerra Especial A partir deste ponto, iniciou-se o trabalhando com o uso de imagens e a criação de novos objetos a partir de objetos já existentes. Decidi utilizar uma metáfora similar aos projetos anteriores e escolhi um jogo no qual o jogador controla uma nave que se move pelo cenário e precisa destruir as naves inimigas (Caminha, Guerra Espacial). Como este projeto era complexo, ele foi divido em 4 etapas: • Etapa 1: apenas foi desenhada uma nave que se movimentava livremente pelo cenário, de modo a trabalhar o uso das classes para desenho de imagens; • Etapa 2: a nave foi capaz de atirar. Os tiros eram criados pela nave, de modo a trabalhar que um objeto era capaz de criar outros objetos ao longo da execução do software. Estes tiros são armazenados em uma lista, trabalhando a capacidade dinâmica de armazenamento das listas; • Etapa 3: uma nave inimiga é criada e o objetivo é, com os tiros, eliminar a nave inimiga. Este módulo trabalha principalmente a interação entre três objetos diferentes em tempo de execução. Também é adicionada uma classe que gerencia a explosão que ocorre quando o tiro acerta a nave. • Etapa 4: um segundo jogador é adicionado e explosões especiais são adicionadas quando uma nave inimiga atinge um dos jogadores. Várias naves inimigas são 40 / 227 criadas e os tiros de ambos os jogadores podem acertar as naves. Isto cria um ambiente complexo que só pode ser gerenciado através do uso de listas. Cada jogador é construído com uma classe de modo a estimular os estudantes na identificação de código duplicado e possibilidade de herança. Ilustração 7: Versão final do jogo Guerra Espacial 3.2.5 – Personagens e Cenários Nesta etapa, era importante demonstrar os mesmos conceitos abordados em outro contexto. Foi escolhido o contexto de jogos com cenários e gravidade. Nestes jogos um personagem se movimenta por um cenário que possui uma estrutura considerada como chão e pode saltar sobre obstáculos, plataformas e outros. Como este projeto envolvia grande complexidade, foi dividido em 2 etapas: • Etapa 1: Apenas um personagem (chamado Ryu) se movimentando por um cenário construído a partir de um arquivo de configuração. Toda a colisão com o cenário é feita por uma classe que encapsula a lógica. Os alunos não são instruídos sobre a lógica utilizada pela classe devido a sua complexidade. Os estudantes são instruídos sobre como controlar os pulos do personagem (Caminha, Exemplo Ryu); • Etapa 2: Após, são adicionados inimigos na fase representados por bolas vermelhas que rolam em direção ao jogador, semelhante as naves inimigas no jogo Guerra Espacial (Caminha, Exemplo Ryu 2). 41 / 227 Ilustração 8: Personagem saltando sobre um inimigo representado por uma bola vermelha 3.2.6 – Jogo de Luta Como exemplo final foi trabalhado um jogo de luta utilizando o mesmo personagem anterior contra outro personagem (chamado Vegeta). Neste jogo os personagens possuem golpes e um deles pode atingir o outro. Este material serviu principalmente como estímulo ao desenvolvimento dos jogos dos alunos no projeto final e demonstrou comportamentos mais complexos de interação entre os objetos (Caminha, Ryu vs Vegeta). Ilustração 9: Exemplo jogo de luta 3.2.7 – Revisão Ao final foi preparado um material de revisão de todos os conceitos relembrando os tópicos abordados e todos os conceitos apresentados (Caminha, Revisão jogos 2D). 42 / 227 3.3 Avaliação e Resultados A avaliação da disciplina foi feita através de conhecimentos e habilidades. Por conhecimento entende-se as capacidades teóricas, avaliadas com três provas de curta duração e uma de revisão sobre todo o conteúdo. Por habilidades entende-se a capacidade prática de desenvolver um jogo utilizando os conceitos aprendidos. Para avaliar o nível de motivação com a disciplina, principalmente ao trabalharmos o tema de jogos, foram incluídas questões sem peso avaliativo e de resposta voluntária nas provas teóricas. 3.3.1 Avaliação Teórica As três primeiras provas avaliaram diferentes aspectos ao longo das 40 horas/aula trabalhadas e ao final do conteúdo, houve uma prova com todo o conteúdo abordado durante o ensino. As três primeiras provas foram divididas em duas partes, a primeira parte era sempre aplicada no início das atividades e a segunda ao final. Segue a descrição de cada uma e a média de pontuação obtida. • Primeira prova: foi aplicada logo após a construção do primeiro jogo e abordou conceitos complexos como classes abstratas, interface e herança. A média geral da prova em uma escala de 0 a 100 foi de 66 pontos e meio (Caminha, Prova 2 a e Prova 2b). • Segunda prova: Após este resultado baixo os mesmos conceitos foram retrabalhados nos exemplos posteriores e a segunda prova foi aplicada após o exemplo com uso de itens e diversas fases, de modo que polimorfismo foi incluído com os demais conteúdos na prova. A média geral da turma foi de 90 pontos em uma escala de 0 a 105 (Caminha, Prova 3a e Prova 3b). • Terceira Prova: A terceira prova teve como foco o reuso de código com composição e herança, acrescido de alguns poucos tópicos sobre jogos 2D. A média geral foi de 73 em uma escala de 0 a 100 (Caminha, Prova 4a e 4b). • Prova de Revisão: A prova revisão foi aplicada a todos os alunos após a revisão com o objetivo de verificar de forma ampla o entendimento teórico da disciplina após todo o conteúdo trabalhado. Esta prova teve pelo menos um tópico sobre cada conteúdo trabalhado e a média geral da turma foi de 72 pontos em uma escala de 0 a 100 (Caminha, Prova de Revisão). 43 / 227 Após a análise da prova percebi que as questões envolvendo os conceitos de herança, composição, polimorfismo, classes abstratas e classes interface foram devidamente absorvidos. Porém, tive índice de erro de quase 50% em uma questão que envolvia a identificação e classificação de algumas estruturas básicas como atributos, métodos e construtores (questão 3 da prova de revisão). Porém, no momento de correção da prova praticamente todos percebiam e compreendiam o erro após este ter sido identificado. A hipótese levantada é que esta dificuldade ocorreu poque foi trabalhado muito com estruturas básicas já prontas que eles apenas copiavam, colavam e modificavam para a sua necessidade, até mesmo quando era criada uma aplicação do zero, eles tinham um roteiro com códigos fonte de apoio. Como trabalhamos neste modelo por dois meses e a disciplina anterior de Orientação a Objetos havia sido ministrada no ano anterior, acredito que eles acabaram esquecendo o nome de algumas estruturas simples. 3.3.2 Avaliação da Motivação Na primeira prova, logo após a construção do primeiro jogo os estudantes foram perguntados se estava sendo interessante aprender Orientação a Objetos com Jogos. 82% disseram que sim, 9% não responderam e 9% disseram que não fazia diferença. Havia uma opção para o caso de não estar gostando, nenhum estudante marcou esta opção. Ficou claro também na relação de convivência que o interesse havia aumentado muito em relação à disciplina de Programação Orientada a Objetos I onde havíamos trabalhado com softwares Desktop e com exemplos proposto por Barnes. Ao final, pelo menos 5 alunos demonstraram grande interesse em seguir a carreira de programador de jogos. 3.3.3 Avaliação Prática Ao final das 40 horas aula teóricas, os estudantes tiveram 28 horas aula para desenvolver o próprio jogo. Os 33 alunos se organizaram em 8 grupos e desenvolveram os seguintes trabalhos: 44 / 227 3.3.3.1 Grupo 1 – PowerMusic Jogo similar ao popular GuitarHero onde o jogador simula que está tocando uma guitarra no acompanhamento da música e deve pressionar os botões correspondendo a nota da guitarra no momento exato indicado por uma animação na tela. 3.3.3.2 Grupo 2 – Champions Fighter Jogo de luta como o apresentado no exemplo para a turma com personagens clássicos de diferentes desenhos animados e jogos de vídeo-game. Cada personagem pode dar socos, chutes e alguns contém um especial. 3.3.3.3 Grupo 3 – Ghomaine Jogo de luta como o apresentado no exemplo para a turma com personagens clássicos de diferentes desenhos animados e jogos de vídeo-game com o diferencial de cada personagem contém de 2 à 3 ataques especiais específicos. 3.3.3.4 Grupo 4 – Ice Crown Citadel Jogo com muitas fases onde três jogadores unem habilidades distintas para derrotar diversos inimigos. Cada inimigo representa uma fase no jogo. 3.3.3.5 Grupo 5 – Moluscation Um personagem criado pelos estudantes deve percorrer um cenário pulando por obstáculos e bombas. A cada fase é possível ganhar alguns poderes especiais. 3.3.3.6 Grupo 6 – Terrorist Strategy Jogo no estilo do clássico Bomberman onde dois personagens disputam entre si lançando bombas em um cenário com obstáculos. 3.3.3.7 Grupo 7 – God of Worms Disputa entre duas minhocas realizado em turnos. Cada minhoca, no seu turno, pode lançar um tiro sobre a outra escolhendo um ângulo e uma força. 3.3.3.8 Grupo 8 – Sami Game Jogo similar a um futebol de botão no qual cada personagem é um círculo que precisa acertar uma bola e jogá-la para dentro do gol adversário. 45 / 227 3.3.4 Resultado avaliação prática A avaliação técnica do jogo contou com diversos critérios para verificar se os conceitos de Orientação a Objetos foram aprendidos corretamente, segue abaixo os critérios, o respectivo índice de acerto médio dos grupos e o número de grupos que atenderam perfeitamente ao critério: Critério Porcentagem de Acerto Número de Grupos com acerto máximo Uso de Composição 90% 6 Uso de Herança 81% 4 Uso de Polimorfismo 38% 3 Classes Enum 38% 3 Classes Abstratas 38% 3 Uso de Listas (ArrayList) 88% 5 Tabela 4: Índice de acerto em cada um dos critérios de avaliação do jogo 2D A avaliação mostrou de forma clara que os estudantes não conseguiram identificar em seus projetos oportunidades para uso de recursos mais avançados de programação como polimorfismo e classes abstratas. Foi identificado na maioria dos projetos oportunidades do uso destes recursos, de modo que as restrições não foram decorrentes dos jogos projetados. Outros conceitos mais básicos como herança e composição foram absorvidos com tranquilidade e implementados com qualidade pela maioria dos grupos. No caso de herança, apesar de todos terem utilizado pelo menos alguma classe com herança, existiam muitos lugares no código onde a herança seria adequada e o grupo não percebeu. Por isso que apenas metade dos grupos atingiram aproveitamento perfeito neste quesito, tendo, todos os outros, aproveitamento parcial. 3.3.4 Conclusão ao final do módulo Ficou evidente o grande aumento da motivação dos alunos, principalmente pela 46 / 227 qualidade de muitos dos jogos apresentados ao final da disciplina. Porém, a ênfase em um grande conjunto de códigos previamente elaborados que eles apenas modificavam acabou por gerar dificuldade em reconhecer quando e como aplicar um determinado conceito aprendido para um problema novo. Por exemplo, eles tinham facilidade em entender quando usar herança para os casos semelhantes aos apresentados em sala, mas não em casos diferentes. Acredito que a metodologia aqui adotada carece de maturidade na elaboração de atividades e exemplos que permita aos estudantes adquirirem maturidade no processo de desenvolvimento e identificarem adequadamente onde cada conceito pode ser aplicado. 47 / 227 4 Ensino de Programação Orientada a Objetos O ensino de programação em Java implica em grandes desafios para o ensino de programação orientada a objetos. segundo Barnes, um programa mínimo em java possui numerosos obstáculos de sintaxe tas como: escrever uma classe; escrever um método main; a criação de um objeto utilizando a palavra-chave new; uma atribuição a uma variável, a declaração de uma variável, incluindo o tipo; uma chamada de método; utilizando a notação de ponto; possivelmente, uma lista de parâmetros. Podemos somar a isso o excesso de conhecimento necessário para realizar operações básicas de input e output, o grande número de termos como instância, herança, método com os quais os estudantes não estão familiarizados e a dificuldade adicional para os estudantes cuja língua nativa não é o inglês (Clark). Por fim, alguns autores (Barnes, Feijó) identificaram a necessidade de ensinar através de projetos, para que os conceitos possam ser vistos em aplicações reais. Esta seção visa apresentar algumas metodologias para o ensino de Orientação a Objetos com Java e extrair ao final, um conjunto de diretrizes guia para o presente trabalho. 4.1 Metodologias para Ensino OO Para a elaboração deste trabalho foram escolhidas cinco metodologias de ensino de Orientação a Objetos. Os critérios para a escolha foram a sua relevância entre os instrutores de programação e a experiência prática do autor deste trabalho com cada metodologia. A primeira metodologia é a desenvolvida pelo professor do Departamento de Informática e Estatística da Universidade Federal de Santa Catarina, Isais Camilo Boratti. Metodologia responsável pelo primeiro contato do autor com Orientação a Objetos. A segunda é a de maior abrangência internacional, principalmente por ter sido a 48 / 227 primeira metodologia a vir acompanhada de um livro texto e de uma ferramenta que oferecesse suporte total a ela. É a metodologia construída por David J. Barnes e Michael Kolling utilizando o BlueJ como ambiente de programação. Esta metodologia foi utilizada parcialmente pelo autor para um curso de Programação Orientada a Objetos I de 90 horas aula. A terceira escolhida foi a metodologia de introdução à ciência da computação com jogos que deu origem ao curso apresentado no capítulo três, baseada no trabalho de Bruno Feijó do VisionLab da PUC do Rio de Janeiro. A quarta, é a metodologia desenvolvida por Stelios Xinogalos ao longo de quatro anos de ensino de programação orientada a objetos. Xinogalos também trabalhou com a metodologia de Barnes e Kolling, porém com o tempo, passou a adotá-la apenas parcialmente no projeto do seu curso, utilizando um conjunto mais abrangente de ferramentas. Por fim, para atender uma necessidade observada pelo autor deste trabalho, foi adotada a metodologia de Caspersen, que trabalha com uma abordagem baseada na modelagem do problema a ser resolvido antes de trabalhar como produzir o código para um determinado problema. Para cada metodologia é apresentada a sequencia de seções da metodologia com os respectivos conteúdos e exemplos trabalhados. Após, é feita uma descrição geral do método, uma análise dos pontos fortes e fracos de cada metodologia e uma revisão final. 4.1.1 Isaias Camilo Boratti Esta metodologia foi vivenciada pelo autor como aluno no primeiro semestre de 2007, no curso de Sistemas de Informação da Universidade Federal de Santa Catarina, na disciplina de Programação Orientada a Objetos I. A disciplina foi ministrada pelo próprio professor Isaias Camilo Boratti que, no mesmo ano, em agosto, publicou o livro que documenta a metodologia intitulado “Programação Orientada a Objetos em Java”. O livro está organizado em oito capítulos conforme tabela abaixo: 49 / 227 Capítulo Exemplos de Projetos Desenvolvidos Principais Conceitos abordados Capítulo 1 Nenhum projeto desenvolvido • Relação POO com o mundo real; • Processo de Abstração; • Operações de Abstração. Capítulo 2 Nenhum projeto desenvolvido • Objetos e Classes; • Representação visual de objetos e classes; • Linguagem Java; Capítulo 3 • Imprimir na tela uma linha de texto; • Calcular a soma de dois números; • Escrever um programa que determine a área de um círculo; • Método main; • Identificadores; • Tipagem de valores; • Variáveis; • Criação de objetos; • String / sequência de caracteres; • Declaração de atributos; • Declaração de métodos; • Construtores; • Parâmetros e Argumentos; • Compilação Java; • Processo de modelagem de um software a partir de um problema; Capítulo 4 • Escrever uma aplicação que determine a distância entre dois pontos; • Implementação de métodos; • Métodos de acesso e métodos modificadores; • Implementação de construtores; • Escopo; • Comentários; • Parâmetros; • Implementação de objetos / referência para um espaço de memória; • Tipagem de valores; • Atribuição; • Conversão explícita de tipo; • Expressões aritméticas; • Expressões Lógicas; Capítulo 5 • Escrever um programa que determine a • Processos de repetição / loops; idade média de um grupo de pessoas; • Processos de tomada de decisão. • Escrever um programa que determine a Ex: if, switch; média de idade das pessoas do sexo masculino e • Encapsulamento; também a média de idade das pessoas do sexo • Modificadores de acesso; feminino de um grupo de pessoas; • Palavra chave this; • Classe Aluno; • Classe Triângulo; 50 / 227 • Verificar se um número é primo; Capítulo 6 • Desenvolver uma aplicação que faça uma análise do desempenho de alunos em determinada disciplina. A aplicação deve determinar, para cada um dos alunos, a sua média final, juntamente com uma informação dizendo se o aluno foi aprovado ou não. Considerar que o aluno tenha realizado três avaliações, todas com o mesmo peso; • Em um determinado jogo de dados participam dois jogadores. Cada jogador deve lançar o dado e computar para si os pontos obtidos. Será considerado vencedor o jogador que atingir um total de 100 pontos ou mais. Sempre que no lançamento do dado, o jogador obter o valor 1 ou 6, este terá direito a um novo lançamento; • Modelagem das classes Funcionario, Chefe e Apoio, sendo as duas últimas subclasses de Funcionario; • Modelagem da classe abstrata Contribuinte e das subclasses Fisico e Juridico. • • • • • • • • Capítulo 7 • “Desenvolver uma aplicação que avalie o desempenho de um grupo de estudantes em determinada disciplina...que prevê a realização de vários testes e uma prova final...a média dos testes terá peso de 60% e a prova final peso de 40%.... ” • Arranjos / Arrays; • Arrays Multidimensionais; • Operações com arrays. Capítulo 8 • • • • • Herança; A palavra-chave super; Reutilização de código; Sobreposição de métodos; Polimorfismo; Polimorfismo de método; Sobrecarga de métodos; Classes abstratas; Atributos de classe; Métodos de classe; Métodos da classe Math; Manipulação de Strings; Classe Character; Tabela 5: Conteúdos e Exemplos da metodologia de Boratti 4.1.1.1 Análise Geral do Método A metodologia inicia demonstrando que o mundo real “é constituído por entidades que interagem entre si”, e que tais entidades possuem características e funções, podendo também serem chamadas de objetos. Dessa forma, a construção de um software orientado a objetos passa pelo processo de identificação dos objetos envolvidos e de como estes objetos irão se relacionar entre si para a resolução de um determinado problema. 51 / 227 A partir disto, Boratti conduz o estudante pelo conceito de abstração, que corresponde: “ao processo utilizado na análise de determinada situação, através da qual observa-se uma realidade, tendo-se por objetivo a determinação dos aspectos e fenômenos considerados essenciais, excluindo-se todos os aspectos considerados irrelevantes.” E a partir de problemas simples como “determinar o raio de um círculo” demonstra como o problema pode ser modelado em termos de objetos com características e funções interagindo entre si, modela as classes que representam estes objetos e apresenta sua respectiva implementação em Java. Os conceitos chave são demonstrados na medida em que são úteis para resolver problemas mais complexos e sub seções complementares se encarregam de determinados aspectos da linguagem Java como operadores matemáticos e detalhes de sintaxe, sem necessariamente, serem parte da solução dos problemas apresentados. Ao final de cada seção, é apresentado um resumo dos conceitos e conhecimentos trabalhados, uma lista de exercícios, geralmente extensa e abrangente, nos casos que apresentem conteúdo prático, diversos exercícios resolvidos também são fornecidos. Por fim, ressalta-se que todos os códigos construídos por Boratti são em língua portuguesa e sempre que apresentam recursos novos, são comentados com descrições detalhadas do seu comportamento. 4.1.1.2 Pontos Fortes 4.2.1.2.1 Operações de Abstração No capítulo introdutório da metodologia, Boratti traz o conceito de operações de abstração que “mostram como o ser humano mentaliza, organiza e modela o mundo ao seu redor”. Estas operações são úteis na medida em que definem uma taxionomia para as interações entre as entidades no mundo real e podem ser representadas diretamente através de código orientado a objetos. Logo, o processo de analisar uma interação entre entidades no mundo real e o código que deve ser construído para resolver problemas envolvendo esta interação pode ser mais adequadamente identificado. As Operações de abstração são demonstradas utilizando representações parecidas com o utilizado pelo diagrama de classes da UML para as classes identificadas. Segue abaixo as quatro operações de abstração: 52 / 227 • Classificação / Instanciação: Classificação corresponde a determinar um conjunto de características de um determinado grupo de objetos de modo a classificá-los em uma categoria ou classe. Instanciação é o processo inverso, no qual exemplos (instâncias) de determinadas categorias são ressaltados. Por exemplo a Classe Carro e Instância carro vermelho do josé; • Generalização e Especialização: Generalização ocorre quando determinamos características comuns a conjunto de classes e assim, criamos uma classe mais genérica. Especialização é o inverso, quando a partir de uma classe genérica, determinamos classes especialistas. Por exemplo, a classe genérica Médico poderia ser especializada em Obstetra, Pediatra e Ornitolaringologista; • Agregação / Decomposição: A Agregação é caracterizada pela relação de “é composto por” entre duas classes. A decomposição é quando dizemos que um objeto “é parte de” outro objeto. Por exemplo, a classe Carro é composta por objetos da classe Motor. Já um objeto da classe Motor é parte de um objeto da classe Carro; • Associação: Ocorre quando dois objetos possuem algum tipo de ligação entre si mas podem existir independentemente do outro, Por exemplo, na relação que ocorre entre objetos das classes Professor e Aluno. 4.2.1.2.2 Processo de Modelagem Boratti coloca que o modelo de resolução de um problema, ou seja, o software, é o resultado de um processo de abstração que em orientação a objetos corresponde à identificação dos objetos com suas características e funções e a respectiva modelagem das suas classes. Ilustração 10: A Construção do modelo. Fonte: Boratti 53 / 227 4.2.1.2.3 Representação Visual de Objetos Após a identificação dos objetos é necessário representá-los visualmente pelo projetista de software. A visualização proposta por Boratti utiliza um círculo para representar um objeto com um texto dentro representando o identificador deste objeto. Quando um objeto é composto por outros objetos estes são representados como círculos dentro do círculo maior. No caso de atributos simples de um objeto como números e textos, estes objetos são representados com um retângulo. Ilustração 11: Representação Visual de Objetos. Fonte: Boratti. Adaptado por Kaléu Caminha 4.2.1.2.4 Implementação de Objetos Recordo, como estuante do professor Isaías Boratti, que fortaleceu muito o meu aprendizado no momento que compreendi que cada objeto ocupava um espaço na memória e que uma variável, na verdade, era um apontador para este espaço na memória. Este aspecto da implementação de objetos é tratada na seção 4.8 onde Boratti coloca que “a identificação de um objeto constitui-se em uma posição na memória (portanto, uma variável) que armazena o endereço do espaço de memória ocupado por aquele objeto”. Para exemplificar este conceito o seguinte código é apresentado: Ponto umPonto, outroPonto; // Um ponto contém um atributo inteiro x e um y umPonto = new Ponto(); 54 / 227 outroPonto = new Ponto(); outroPonto.redefina_se(2.0, 3.0); // este método altera os valores de x e y E a partir do código acima, é utilizada uma adaptação da visualização de objetos demonstrando que as variáveis são apenas apontadores para os objetos: Ilustração 12: Implementação de Objetos. Fonte: Boratti. Adaptado por Kaléu Caminha 4.2.1.2.5 Representação Visual de um Array Arranjos, como são chamados os tradicionais Arrays por Boratti são “elementos que armazenam vários valores”. Para facilitar o ensino foi criado um modelo visual para o uso de arrays. Para exemplificar o modelo usaremos o seguinte código: int[] numeros = new int[8]; int cont = 2; numeros[1] = 34; numeros[cont] = 15; numeros[cont+1] = numeros[cont] – 10; O código acima apresentaria a seguinte representação visual: Ilustração 13: Representação Visual de um Array. Fonte: Boratti. adaptado por Kaléu Caminha 55 / 227 4.1.1.3 Pontos Fracos 4.2.1.3.1 Início pelo método main com Hello World Após os dois primeiros capítulos de introdução, com uma explicação detalhada e abrangente sobre orientação a objetos e o processo de modelagem, o primeiro programa mostrado tem como problema “Imprimir uma linha na tela” e o seguinte código-fonte: public class Programa1 { //Declaração do método main public static void main(String[] parametro) { System.out.println(“Primeiro Programa!”); } } O programa que resolve este problema não envolve a criação de nenhum objeto, e o único método chamado é um método acessado através de um atributo estático da classe System, usando notação de ponto. E isto é feito dentro do método main, um método estático que exige como parâmetro um array de strings, conteúdos que somente serão trabalhado nos capítulos sete e oito. É evidente que esta abordagem se torna necessária na medida em que Boratti, por não adotar nenhuma ferramenta que suporte a execução de código sem a criação do método main, não tem saída a não ser iniciar por ele. Esta abordagem porém, não é interessante pois a primeira porção de código que o estudante enxerga é algo absolutamente incompreensível para ele, devido a grande quantidade de conceitos envolvidos (Kolling e Rosemberg). 4.2.1.3.2 Primeiro o código, depois o conceito Em diversos exemplos, certas regras de sintaxe são apresentadas, cujos conceitos envolvidos somente são trabalhados em seções ou capítulos posteriores. Um exemplo é a primeira classe com a qual os alunos tem contato. A classe Círculo, apresentada como o terceiro trecho de código com o qual os estudantes tem contato. public class Circulo { //Declaração do atributo 56 / 227 protected double raio; //Declaração dos métodos public Circulo() { raio = 0.0; } public double forneceArea() { double area; area = 3.1415*raio*raio; return area; } public void recebaValorRaio(double vRaio) { raio = vRaio; } } Esta classe apresenta mais de 10 conceitos: comentários, declaração de atributos, tipagem de variáveis, declaração de método com retorno, declaração de método sem retorno, declaração de um construtor, assinatura de classe, parâmetros, atribuições, operações matemáticas e uso de modificadores de acesso. A maioria destes conceitos só serão trabalhados posteriormente, por exemplo, atribuições serão detalhadas no capítulo 4 enquanto modificadores de acesso somente serão vistos no capítulo 5. Percebe-se que esta abordagem tem como intenção demonstrar a modelagem completa de uma classe adequadamente escrita para resolver um problema. Isto fica claro pelo uso de modificadores de acesso desde o início, com a intenção de evitar que estudantes declarem atributos como públicos. Isto causa, porém, uma sobrecarga de conceitos apresentados sem a devida possibilidade do estudante experimentar cada um deles e suas consequências para o programa, sem saber qual a função de cada palavra-chave. 4.2.1.3.3 Métodos estáticos desde o início Para complementar o exemplo do capítulo 3 no qual é preciso determinar a área de um círculo a partir do seu raio, é apresentada uma classe chamada Interface (que não tem nenhuma relação com o conceito de interfaces em Java) responsável por solicitar ao usuário um número que irá representar o raio do círculo. O principal método desta classe chamado “pegaValorRaio” contém 4 linhas, conforme apresentado abaixo: 57 / 227 … String valorLido; valorLido = JoptionPane.showImputDialog(“Digite o raio: ”); double valor = Double.parseDouble(valorLido); return valor; … O problema deste método é que as principais instruções são métodos estáticos de classes específicos da biblioteca do Java. Isso nada ensina sobre orientação a objetos, sendo necessários apenas para que o programa seja minimamente interessante. Construções como esta são utilizadas em outros pontos como no capítulo 4 com o uso do método sqrt da classe Math, responsável por recuperar a raiz quadrada de um número. A grande crítica a esta abordagem é que métodos e atributos estáticos são ensinados apenas no capítulo 8, de modo que o estudante precisa simplesmente ignorar o fato da sintaxe ser diferente de outros objetos com os quais ele trabalha. O autor já presenciou, como professor, estudantes que foram ensinados sob metodologias parecidas chamando métodos diretamente das classes, sem diferenciar se um método era ou não estático. 4.1.1.4 Conclusão A metodologia se mostra muito interessante ao tornar claro para o estudante o processo de construção de um software, a sua respectiva modelagem de objetos e classes e as diversas representações visuais do programa. Também se mostra interessante a forma de abordar os conceitos sempre que possível a partir de problemas do mundo real. Observa-se porém que a complexidade dos problemas tratados é muito pequena como por exemplo, calcular a média de um grupo de alunos ou determinar a área de um círculo. Um dos motivos para o uso de exemplos triviais é a dificuldade em utilizar recursos avançados de programação em função da complexidade destes recursos conforme apresentado pela linguagem Java. De forma mais clara isto é visto na modelagem da classe Interface que para recuperar um valor digitado pelo usuário precisa de métodos estáticos logo no início da metodologia. Este ponto ressalta que alguns dos pontos fracos observados na metodologia poderiam ser melhor trabalhados com o apoio de um ferramental adequado como um 58 / 227 editor que não exigisse a criação de um método main para executar código ou uma ferramenta que permitisse executar métodos diretamente dos objetos. 4.1.2 David J. Barnes e Michael Kölling “Partindo de Objetos em Java”. Esta é a primeira frase do livro-texto para a metodologia. A metodologia utiliza o ambiente de programação BlueJ e possui como algumas de suas linhas guia: • Uma abordagem iterativa: A partir do uso do padrão “Early Bird”, documentado na seção 2.4 os principais conceitos são apresentados primeiro de uma forma simplificada para que um determinado problema seja resolvido. Posteriormente no curso, os mesmos conceitos são retomados segundo outro ponto de vista e o conhecimento é aprofundado; • Sem completa cobertura da linguagem: em função do foco do livro ser em POO, detalhes específicos da linguagem java não são abordados a menos que sejam necessários para a resolução de um determinado problema; • Abordagem baseada em projeto: ao invés de introduzir uma nova construção e então fornecer exercícios para aplicar a construção aprendida, primeiro é fornecido um objetivo e um problema. A análise do problema determina o que será necessário e então, os recursos da linguagem e os conceitos são apresentados; • Sequencia dos conceitos em vez de construções de linguagem: é preferível, sempre que existe conflito entre trabalhar construções de linguagem ou os conceitos, trabalhar os conceitos e o mínimo possível das construções de linguagem que auxiliem a implementar o conceito. A metodologia está organizada em duas partes: 1) Fundamentos da Orientação a Objetos e 2) Estruturas de aplicação. Na primeira parte são abordados os conceitos básicos como classes, objetos, métodos, etc. Na segunda, conceitos importantes para o desenvolvimento de aplicações e reuso como herança, polimorfismo e interfaces são abordados. O livro-texto da metodologia possui 13 capítulos, sendo os 7 primeiros reservados para a primeira parte e os seis últimos para a segunda. O último capítulo é reservado para um estudo de caso de uma aplicação de maior porte. Segue abaixo descrição dos exemplos e conteúdos trabalhados em cada capítulo: 59 / 227 Capítulo Exemplos de Projetos Desenvolvidos Capítulo 1 Objetos e Classes • Desenhos simples de algumas formas geométricas; • Exemplo utilizando um objetos do projeto anterior para desenhar uma figura; • Exemplo simples com classes de alunos que participam de aulas de laboratório. Capítulo 2 Entendendo as definições de classe • Simulação uma máquina de vender bilhetes de trem; • Armazenamento de informações sobre um livro. Capítulo 3 Interação entre objetos • Implementação de um mostrador de um relógio digital com horas e minutos; • Uma simulação simples de um correio eletrônico. • • • • • • Abstração; Modularização; Criação de objeto; Diagramas de objeto; chamadas de método; depuradores. Capítulo 4 Agrupando objetos • Implementação simples de um bloco de anotações eletrônico; • Um sistema de leilão; • Um analisador de registros de acesso a uma página web. • • • • Iteradores; Coleções; Arrays; Loops. Capítulo 5 • Implementação de um programa de diálogo Comportamento semelhante ao Eliza utilizado para fornecer um mais sofisticado “suporte técnico” para clientes; • Animação gráfica com bolas que quicam. Capítulo 6 Objetos bemcomportados • Estágios iniciais de um diário que armazena compromissos; • Implementação de uma calculadora de mesa; Capítulo 7 Design de Classes • Um jogo iterativo de aventura baseado em texto; Principais Conceitos abordados • • • • Objetos; Classes; Métodos; Parâmetros. • Campos; • Construtores; • Parâmetros; • Métodos de acesso e modificadores; • Atribuição e instrução condicional. • Utilizando classes de biblioteca; • Lendo a documentação; • Escrevendo a documentação. • Testes; • Depuração; • Automação de testes. • Design baseado na responsabilidade; • Acoplamento; • Coesão; • Refatoração. Capítulo 8 Herança • Um banco de dados de CDs e vídeos; • • • • Capítulo 9 • Continuação do jogo iterativo do capítulo 7; • Polimorfismo de método; Herança; Subtipagem; Substituição; Variáveis polimórficas. 60 / 227 Mais sobre herança • Continuação do banco de dados de CDs e vídeos do capítulo 8; Capítulo 10 Técnicas adicionais de abstração Uma simulação de caçador-caça conhecida como “raposa-coelho”. Capítulo 11 Tratamento de erros • Implementação de um catálogo de endereços; Capítulo 12 Design de aplicações • Sistema de reserva de lugares em um cinema; Capítulo 13 Estudo de caso • Combinação de um sistema de reserva de táxis com um sistema de gerenciamento e de simulação. • tipo estático e dinâmico; • Sobrescrição; • Escolha dinâmica de método. • Classes abstratas; • Interfaces. • Programação defensiva; • Lançamento e tratamento de exceção; • Informe de erro; • Processamento de arquivos simples. • • • • Descobrindo classes; Cartões CRC; Padrões; Design de interfaces. • Desenvolvimento integral de aplicação. Tabela 6: Conteúdos e exemplos da metodologia de Barnes 4.1.2.1 Diretrizes De forma complementar, para que obtenha uma boa compreensão da metodologia, segue-se o trabalho de KÖLLING e ROSENBERG, que, a partir da experiência com o desenvolvimento de diversos cursos utilizando o ambiente BlueJ elencaram oito diretrizes adequadas para o ensino de programação orientada a objetos: 4.1.2.1.1 Objetos primeiro É consenso entre muitos professores de Programação Orientada a Objetos que é melhor ensinar a criar e manipular objetos antes de qualquer outra coisa. Esta atividade, porém, é difícil nos ambientes tradicionais, pois para executar o método de um objeto é preciso: 1. Uma classe contendo um método main estático com um array de Strings como parâmetro; 2. Uso de uma variável tipada; 3. Uma atribuição de um objeto à uma variável; 61 / 227 4. A instanciação de um objeto; 5. A sintaxe de chamada de um método; 6. A visualização do resultado da chamada do método. Com BlueJ a primeira atividade é abrir um projeto existente e criar objetos a partir das classes, em seguida, os estudantes podem chamar métodos destes objetos e visualizar seu estado. Tudo isso sem escrever nenhuma linha de código. 4.1.2.1.2 Não inicie com uma tela em branco. Um dos maiores erros em programação OO é iniciar com uma tela vazia. Escrever projetos OO envolvem a definição de quais classes o programa terá e quais métodos cada classe deverá ter, e este, é um exercício extremamente difícil para estudantes novatos. Ao invés disso, pode-se iniciar com pequenas modificações e adições em código existente. 4.1.2.1.3 Leia código Muitos estudantes são forçados a começar a programar sem terem lido praticamente nenhum código. Isso não acontece em nenhuma outra disciplina. Estudantes aprendem mais quando tem acesso a códigos bem escritos que eles possam utilizar como base para os seus projetos. 4.1.2.1.4 Use projetos grandes Programas curtos dificilmente deixam claro as vantagens em se utilizar Orientação a Objetos. A sugestão é que sejam utilizados programas bem escritos com um número maior de classes e métodos que permitam aos estudantes compreenderem as vantagens do uso de OO, assimilarem a importância de um código limpo e comentado, pratiquem a leitura de código e estudem a interação entre as classes. 4.1.2.1.5 Não inicie com o método main O método main exige muitos conceitos para ser compreendido. O ideal é que os 62 / 227 estudantes só sejam apresentados a ele após terem se envolvido com os conceitos principais como a criação de objetos e chamada de métodos. 4.1.2.1.6 Não use “Hello world” Para se fazer um “Hello World” em Java é preciso iniciar do método main, que por sua vez significa: 1) construir uma classe da qual nenhum objeto é criado. 2) construir um método (o main) que não age sobre um objeto. Isso não é Programação OO e fere outras diretrizes já apresentadas. 4.1.2.1.7 Mostre a estrutura do programa Classes e seus relacionamentos são muito importantes. É essencial que os estudantes possam visualizar a estrutura dos programas nos quais trabalham. O BlueJ exibe a estrutura do programa em um diagrama semelhante ao diagrama de classes da UML. Quando o BlueJ não estiver sendo utilizado é importante que o professor encontre meios de mostrar claramente esta estrutura. 4.1.2.1.8 Tenha cuidado com a interface do usuário É importante ensinar uma forma de realizar a interface com o usuário para fornecimento de parâmetros e para a impressão de resultados do software. Em Java, podemos fazer isso com Applets, com a API de entrada e saída ou com frameworks GUI como o Swing. Porém, nenhuma destas alternativas é simples o suficiente para ser utilizada em cursos introdutórios. São sugeridos então, dois caminhos para lidar com este problema: 1. Específico no BlueJ, são as janelas pop-up que solicitam ao usuário os valores para os parâmetros dos métodos e as janelas que exibem os retornos dos métodos; 2. Um projeto maior com classes que encapsulem a interface com o usuário e que os estudantes apenas precisem aprender a utilizar seus métodos. 4.1.2.2 Análise geral do método Não é sem mérito que esta metodologia está sendo adotada por um grande número de instrutores de programação orientada a objetos em todo o mundo. Sem dúvida que a 63 / 227 metodologia trouxe grandes contribuições e dentre os pontos fortes podemos destacar: 1) a abordagem baseada em projetos que permite ao estudante aprender a resolver problemas; 2) a priorização do conceito antes das estruturas de construção da linguagem, dando sentido ao que o aluno está aprendendo; 3) O rico acervo de projetos trabalhados, trazendo diversas dimensões importantes da computação; 4) O uso do Bluej que permite que se inicie a tarefa de ensinar diretamente através de objetos e escrita de seus métodos. Além destes, na prática com a metodologia foi possível perceber que os estudantes que em geral possuem mais dificuldade com programação se sentiam mais seguros e confiantes ao verem os objetos na tela e poder manipulá-los. É visível que muitos alunos não se sentem a vontade quando se deparam diariamente nas aulas de programação com telas repletas de código-fonte, sem outras representações visuais do trabalho que ele está realizando. 4.1.2.3 Pontos fracos Apesar do inúmero sucesso, é importante ressaltar algumas dificuldades geradas pela metodologia. Estas dificuldades foram percebidas em prática do autor com a mesma e através da pesquisa de Xinogalos. 4.1.2.3.1 Ênfase nas técnicas de visualização e manipulação direta do BlueJ Em função do BlueJ disponibilizar interface gráfica para instanciação de objetos e chamada de métodos, alguns alunos ficam por demais “acomodados” a esta maneira de trabalhar, o que gera dificuldades com instanciação de objetos e chamadas de métodos direto do código-fonte. Ilustração 14: Instanciação de objeto e chamada de método em código Isso porque no BlueJ, a diferença entre a forma de manter objetos em variáveis e chamar métodos destas variáveis (Ilustração 14) é muito diferente da forma como a mesma ação é feita no BlueJ (Ilustração 15). 64 / 227 Ilustração 15: Objetos na bancada de objetos (em vermelho) e chamada de método de um objeto Outra crítica é a que métodos que possuem retorno são chamados da mesma forma que métodos sem retorno. Esta questão causa confusão ao tentar passar para os estudantes que métodos com retorno devem ser sempre atribuídos a uma variável. Métodos com retorno, no BlueJ, imprimem o valor retornado na tela. Quando, posteriormente o autor trabalhou métodos com retorno sem o uso do BlueJ, uma das dúvidas apresentas foi questionando se, quando se retornava um valor, este valor não deveria ser impresso na tela? 4.1.2.3.2 Ênfase em projetos existentes Em função da utilização de projetos com uma boa quantidade de código pronto, a dificuldade em iniciar projetos do zero se mostrou muito maior. Além disso, métodos get e set, bem como construtores, são geralmente fornecidos pelos projetos e os alunos raramente os implementam. 4.1.2.3.3 Ensino posterior do método main: O ensino apenas no final do curso do método main exige que os alunos utilizem apenas o BlueJ para testar código, potencializando as dificuldades comentadas no item 1. 4.1.2.4 Conclusão Percebe-se que o BlueJ apesar de suas inúmeras qualidades, não pode ser visto como a única solução, pois, em concordância com Xinogalos, “mais de uma ferramenta é necessário para suportar as diferentes necessidades cognitivas no ensino de 65 / 227 programação”, de modo que esta metodologia apesar de sua qualidade, pode ser complementada com atividades e práticas em ambientes profissionais de desenvolvimento. 4.1.3 Bruno Feijó et al A metodologia apresentada por Feijó visa segundo ele próprio “apresentar os conceitos e os bons hábitos da arte de programar”. Seu material aborda não apenas a programação orientada a objetos mas também a programação estruturada e brevemente a programação guiada a eventos. Apesar do foco não ser orientação a objetos, sua metodologia e o framework que o acompanha serviram de base e inspiração para a metodologia descrita no capítulo três e consequentemente, base e inspiração para o presente trabalho. O livro-texto da metodologia apresenta 9 capítulos que segundo sugestão da metodologia poderia ser trabalhado em uma disciplina de 64 h/aula em 16 aulas de 4 h/aula cada, conforme modelo abaixo: • Semana 1: apresentação e capítulo 1; • Semana 2: Capítulo 2; • Semana 3: Capítulo 3; • Semana 4 a 6: Capítulo 4; • Semana 7: Capítulo 5; • Semana 8: Capítulo 6; • Semanas 9 a 11: Capítulo 7; • Semanas 12 a 14: Capítulo 8; • Semanas 15 e 16: Capítulo 9; Segue abaixo organização dos capítulos, exemplos e conteúdos apresentados. 66 / 227 Capítulo Exemplos de Projetos Desenvolvidos Capítulo 1 Computadores Nenhum exemplo trabalhado; Capítulo 2 • Pequenos exemplos didáticos demonstrando Variáveis, Tipos os conceitos; de Dados e Expressões Capítulo 3 Controles de Fluxo por comandos de seleção • Pequenos exemplos didáticos demonstrando os conceitos; Capítulo 4 Métodos e Soluções • Pequenos exemplos didáticos demonstrando os conceitos; Capítulo 5 • Pequenos exemplos didáticos demonstrando Dados os conceitos; compostos como vetores Capítulo 6 Usando mais objetos e classes simples • Jogo similar ao tradicional Pong; Principais Conceitos abordados • • • • • Comentários; Variáveis; Entrada de Dados; Saída de Dados; Expressões; • estruturas de tomada de decisão if-else e switch. • • • • Métodos; Métodos Estáticos; Sobrecarga de métodos; Loops. • Vetores unidimensionais; • Vetores bidimensionais; • Classes; • Strings; • Leitura e gravação de arquivos; • Classes abstratas e interfaces. Capítulo 7 • Continuação do jogo similar ao tradicional Classes, objetos, Pong; herança • • • • • Capítulo 8 Programação Gráfica • Conceitos de jogos 2D; Capítulo 9 Programação guiada a eventos • Jogo específico desenvolvido para o livro e continuação do jogo similar ao tradicional pong; • Jogo específico desenvolvido para o livro. Classes e objetos; Modificador static; Encapsulamento; Herança; Classes abstratas. • Captura de dados do teclado e do mouse. Tabela 7: Conteúdos e exemplos da metodologia de Feijó 67 / 227 4.1.3.1 Análise geral do método A proposta é muito abrangente, segundo Feijó, pretende-se do capítulo 1 ao 6 trabalhar com programação estruturada, no 7 e no 8 orientação a objetos e no capítulo 9, programação guiada a eventos. Em função até mesmo deste grande conjunto de paradigmas, o livro não oferece um bom suporte para os conceitos de orientação a objetos, os exemplos são poucos, muito limitados e as explicações dos conceitos não acompanham boas referências visuais. Em contrapartida, oferece um rico material sobre como unir conceitos de jogos em duas dimensões com técnicas de programação, tanto estruturada quanto orientada a objetos. Um grande mérito está no uso do framework para trabalhar classes abstratas e interfaces, pois, para que o aluno possa criar uma fase, deve implementar uma interface e para criar um objeto no jogo, deve estender uma classe abstrata que já fornece os atributos de posicionamento de um objeto na tela. 4.1.4 Stelios Xinogalos Ao fim de 4 anos de avaliações e modificações, Xinogalos chegou a uma metodologia que utiliza três ambientes de programação: objectKarel (Satratzemi ), BlueJ e JCreator (um ambiente profissional de programação simplificado). O material didático utilizado é baseado no material que acompanha o objectKarel e na metodologia de Barnes com o BlueJ, descrita na seção 2.4.2. O curso estruturado por Xinogalos possui 12 lições de 4 horas aula semanais, duas teóricas e duas em laboratório. As aulas são divididas na seguinte estrutura: 68 / 227 Aula Temas trabalhados Ambiente 1 Objetos, classes, herança: objeto, construção e inicialização de um objeto, mensagens/métodos, atributos e comportamentos de um objeto, herança, superclasse, subclasse, hierarquia de herança, diagrama de classe UML objectKarel 2 Herança em vários níveis, polimorfismo e sobrescrita. objectKarel 3 Definição de Classe: campos, construtores, métodos de acesso, métodos modificadores, estrutura de retorno, parâmetros, escopo de variáveis, estruturas condicionais. BlueJ 4 Interação com objeto: abstração, modularização, objetos criando objetos, múltiplos construtores, diagrama de classe, diagrama de objetos, tipos primitivos, tipos de objetos, chamadas de métodos internos e externos. BlueJ 5 Métodos estáticos: métodos de instância vs métodos de classe, o método main, execução sem o BlueJ, byte code, Java Virtual Machine. BlueJ, JCreator 6, 7 Agrupando objetos em coleções: Coleções de tamanho flexível (ArrayList), coleções de tamanho fixo (arrays), classes genéricas, iteradores, loops (while, for, for-each). BlueJ, JCreator 8 Usando bibliotecas de classe: Classes padrão do Java, leitura de BlueJ, JCreator documentação, interface vs implementação de uma classe, explorando e usando classes (HashMap, Random, HashSet), modificadores de acesso (public, private), informações escondidas, variáveis de classe (estáticas). 9 Projeto de Classes: acoplamento, coesão, encapsulamento, duplicação BlueJ, JCreator de código, projeto dirigido à responsabilidade, reuso de código. 10 Melhorando a estrutura com herança: hierarquia de classes, superclasse, subclasse, construtor da superclasse, “subtyping”, substituição, “autoboxing”. BlueJ, JCreator 11 Polimorfismo, sobrescrita: tipos dinâmicos e estáticos, busca dinâmica de método, super, acesso protegido. BlueJ, JCreator 12 Classes abstratas e Interface. BlueJ, JCreator Tabela 8: Conteúdos e ambiente de programação na metodologia de Xinogalos 4.1.4.1 O Uso de ambientes de programação A escolha do ambiente de programação que será utilizado é difícil e importante. Ambientes profissionais são inadequados para trabalhar aspectos introdutórios, ambientes de micro-mundos como o objectKarel são interessantes para trabalhar conceitos mas não uma linguagem específica, já ambientes educacionais são interessantes para transferir os conceitos para uma determinada linguagem, porém, sem a transição para uma IDE profissional os estudantes ficam condicionados às características específicas das ferramentas educacionais. 69 / 227 A dinâmica de 4 anos no curso apresentado mostrou que mais de uma ferramenta é necessária para que os diversos aspectos cognitivos possam ser trabalhados. Esta dinâmica gerou as seguintes diretrizes sobre o uso de ambientes de programação: • Use um ambiente de micro-mundo para uma introdução curta dos principais aspectos de programação orientada a objetos: o ambiente escolhido deve suportar o ensino dos principais conceitos de OO sem preocupações com a sintaxe. O conceito de objeto deixa de ser abstrato pois agora os objetos podem ser manipulados, os estudantes também ganham confiança na sua capacidade de programação; • Use um ambiente educacional para apresentar os conceitos vistos no micromundo para a sua implementação em Java: a transição para um ambiente educacional é mais suave do que para um ambiente profissional pois ambientes específicos para o ensino eliminam a complexidade permitindo que os estuantes possam se dedicar a sintaxe dos conceitos fundamentais. É essencial que estes ambientes tenham ferramentas de visualização e manipulação direta de classes e objetos e permitam a invocação dinâmica de métodos. O tempo com o um ambiente educacional não deve se estender muito para evitar falsas percepções, principalmente sobre os aspectos dinâmicos da Orientação a Objetos. • Use um ambiente profissional para prepará-los para o futuro: É inevitável que esta transição aconteça, a maior preocupação deve ser quando realizá-la. Neste curso foi decidido realizar esta transição na quinta lição dando ênfase para as características destes ambientes que poderiam ajudar os estudantes como 1) Visão das classes em árvore; 2) Navegação rápida dentro de uma classe; 3) autocompletar de código; 4) o destaque de sintaxe. Ao realizar esta transição os estudantes se sentem mais confiantes com o seu conhecimento. 4.1.4.2 Diretrizes para o Ensino das lições propostas As lições são baseadas como já mencionado no livro-texto que acompanha o BlueJ., seguindo suas principais diretrizes. Concomitantemente, é utilizada a abordagem “modelo primeiro” de Bennedsen e Caspersen (2004) conforme documentada na seção 4.1.5. Abaixo, seguem as diretrizes relacionadas a cada lição trabalhada. As lições utilizam projetos do livro-texto que acompanha o BlueJ. 70 / 227 4.1.4.2.1 Lição 1: Objetos, classes e herança no objectKarel Na primeira lição, são apresentados os conceitos básicos de classe, objeto e método. Os estudantes iniciam criando um novo objeto da classe “Robo” com a ajuda da interface Gráfica (Ilustração 16), depois, devem utilizar os métodos do robô (como “mover”, “virar para a esquerda”, etc) para realizar tarefas específicas como completar um determinado percurso. Ilustração 16: Instanciação de um objeto no objectKarel com a ajuda da interface gráfica O programa vai sendo construído a partir de botões onde o estudante seleciona os métodos que irá executar, criando uma sequencia que no objectKarel é chamada de tarefa (Ilustração 17). 71 / 227 Ilustração 17: Criação de uma tarefa a partir dos métodos de um robô Após, podem executar o programa e observar qual o comportamento do robô para cada método e o que isso significa na linguagem dos robôs (por exemplo, virar para a esquerda significa mudar a frente do robô em 90 graus negativos.), estas informações são fornecidas na forma de texto no rodapé do ambiente (Ilustração 18). Ilustração 18: Execução de um programa. à direita a visualização do robô, à esquerda a lista de tarefas e abaixo a descrição do que ocorre no interior do método em execução. Em uma segunda atividade, problemas mais complexos são apresentados de modo que é necessário utilizar herança para a criação de uma nova classe com métodos que irão facilitar a realização da tarefa. Xinogalos ressalta que é essencial durante o processo explicar que cada método muda o estado interno do objeto (mesmo que os alunos não possam ver as variáveis de 72 / 227 instância). Também é importante utilizar exemplos que utilizem vários objetos da mesma classe para evitar que os estudantes considerem os dois conceitos como a mesma coisa. 4.1.4.2.2 Lição 2: Herança em vários níveis, polimorfismo e sobrescrita. A didática se mantém a mesma e agora os problemas apresentados exigem a construção de uma hierarquia de classes. Também são utilizados exemplos que trabalhem a necessidade da sobrescrita de métodos como por exemplo, robôs mais velozes que no método “move”, andam mais rápido que os robôs utilizados nos exercícios anteriores. O polimorfismo, também é trabalhado com cuidado por se uma das principais dificuldades observadas nos estudantes. 4.1.4.2.3 Lição 3: Definição de classe A definição de uma classe é apresentada aos alunos utilizando o exemplo de uma máquina de vender bilhetes (TicketMachine no livro texto que acompanha o BlueJ). Os estudantes criam instâncias desta classe, chamam seus métodos, observam a implementação em Java da classe e por fim, tem a oportunidade de realizar pequenas modificações nela. No primeiro ano do curso foram identificadas as dificuldades mais comuns referentes ao ensino do paradigma de POO, é importante que o instrutor esteja atento a elas desde o início do ensino para reforçar o entendimento destes tópicos. Segue abaixo as principais dificuldades encontradas: • A compreensão e uso de múltiplos construtores; • O uso do this é de difícil compreensão e em geral os estudantes o evitam; • A inicialização dos campos no construtor; • Costumam declarar o tipo de retorno de métodos “set”; • Tentam acessar valores de campos privados diretamente, sem o uso de métodos get; Outras dificuldades são atribuídas mais especificamente ao uso dos recursos do BlueJ, são elas: • Não declaração do tipo das variáveis que armazenam os objetos. Isso pode ser trabalhado dando ênfase na janela de instanciação de um objeto à variável 73 / 227 onde o objeto será armazenado e o tipo desta variável. O BlueJ já apresenta estas informações, porém, comumente passam despercebidas. • Chamada de métodos. Frequentemente métodos com retorno são chamados da mesma maneira que métodos void. No BlueJ, é possível chamar ambos os tipos de métodos da mesma forma, de modo qque o resultado de métodos com retorno frequentemente é interpretado como “impressão do valor na tela”; Por fim, é essencial verificar se os conceitos aprendidos no objectKarel foram efetivamente transportados para o Java. Isso pode ser feito solicitando que os estudantes representem em Java os modelos dos robôs criados nas últimas duas lições. 4.1.4.2.4 Lição 4: Interação entre objetos Estudantes são apresentados aos conceitos de modularização, abstração e interação entre objetos utilizando o exemplo de um relógio digital com duas classes, uma representando um relógio digital e outra que representa um mostrador de dois números. O Relógio é composto por dois mostradores, um para as horas e outro para os minutos. As dificuldades observadas nesta etapa foram: • Os estudantes não tem certeza onde devem inicializar os campos; • Não sabem como manipular este novo tipo; • Tem dificuldades com a notação de ponto. Para estas dificuldades é recomendado o uso de diagramas de objetos e atividades onde os estudantes identifiquem chamadas de método internas e externas; 4.1.4.2.5 Liçao 5: O método main nesta aula é apresentado como executar código Java sem o BlueJ. Uma introdução sobre uso de métodos estáticos versus métodos de instância é feita utilizando o BlueJ. Após, os estudantes criam seu próprio método main no Jcreator para um projeto que eles já tenham conhecimento como o TicketMachine. Esta lição também é aproveitada para falar sobre compilação, “byte code” e máquina virtual. As principais dificuldades encontradas foram: 74 / 227 • Uso de parâmetros formais da chamado do construtor (Ex: new Classe(Type param); ); • O tipo das variáveis dos objetos são esquecidos; • Esquecem argumentos em chamadas de métodos; • Esquecem os parênteses em chamadas de métodos sem argumentos; Para lidar com estas questões é sugerido: • Atividades que são de alguma forma “silenciadas” pelo BlueJ como o tipo das variáveis que armazenam os objetos são demonstrados utilizando as caixas de diálogo do BlueJ e seu paralelo em sintaxe Java; • Exemplos e tarefas são realizados em cima de projetos já conhecidos; • Um programa contendo erros comuns é apresentado e os estudantes são ensinados a reconhecer os erros e tratá-los. 4.1.4.2.6 Lição 6: Agrupando objetos em um ArrayList Uso de coleções em programação é comum e necessário mesmo em programas de baixa complexidade. Alguns autores já colocam o uso de ArrayList como prioridade sobre Arrays por ser uma estrutura mais adequada para representar listas continuas. Durante os quatro anos de curso os autores identificaram diversas dificuldades envolvendo o uso de ArrayList`s e chegaram às seguintes conclusões: 1. O uso dos diagramas de objetos são fundamentais desde o início do projeto. Os estudantes são estimulados a criar um diagrama de objetos para um dado programa e simular operações de remoção e inclusão no diagrama de objetos. 2. São oferecidos exercícios para com código incompleto para que os estudantes preencham os espaços vazios com determinados comandos. 3. Após, são construídos programas do zero. 4.1.4.2.7 Lição 7: Agrupando objetos em coleções (ArrayList vs Array) O uso de Arrays é apresentado e o mesmo projeto no qual foi trabalhado o uso de ArrayList é agora modificado para funcionar com Arrays. 75 / 227 4.1.4.2.8 Lição 8: Utilizando bibliotecas de classe Nesta aula os estudantes são apresentados à documentação das classes Java, com ênfase nas classes String e HashMap. A aula também apresenta a estrutura de pacotes. 4.1.4.2.9 Lição 9: Projeto de Classes O projeto de classes é algo difícil de ser realizado e a simples exposição dos principais conceitos teóricos para uma solução bem projetada não causa um grande impacto no aprendizado. É importante que os estudantes entendam através de uma aplicação os problemas de um mau projeto de classes. A maior dificuldade encontrada foi a limitação do tempo que impossibilita o desenvolvimento de uma solução completa, na qual, o entendimento dos principais conceitos de um bom projeto de classes seria trabalhado. 4.1.4.2.10 Lição 10: Melhorando a estrutura com Herança O projeto desta lição é uma biblioteca multimídia de CD's e DVD`s. A primeira versão do projeto não usa herança, de modo que muito código acaba sendo duplicado. Quando os alunos são perguntados sobre como resolver o problema, eles se recordam da apresentação feita com o objectKarel e facilmente compreendem o uso de herança. A maior dificuldade percebida é a chamada do construtor da superclasse através da subclasse. Uma sugestão é utilizar o debugger para ver a execução do código passo a passo e os campos sendo preenchidos. 4.1.4.2.11 Lição 11: Polimorfismo e Sobrescrita Através de uma continuação do exemplo visto anteriormente, é realizado um exemplo onde cada subclasse sobrescreve o comportamento de um método na classe pai. 4.1.4.2.12 Lição 12: Classes abstratas e Interface Nesta aula o uso de classes abstratas e interface são utilizados para expandir a simulação de um ambiente com raposas e coelhos para conter diversos tipos de animais. Percebeu-se que não é difícil para os estudantes compreenderem o conceito e o 76 / 227 implementarem, porém, demonstram grande dificuldade em perceber quando uma classe deve ser declarada como concreta, abstrata ou interface. 4.1.4.3 Análise Geral do método As duas principais conclusões de Xinogalos são 1) a necessidade de avaliação continua dos cursos de modo a validar as pesquisas e dar continuidade aos processos de melhoria e que 2) mais de uma ferramenta é necessário para suportar as diferentes necessidades cognitivas no ensino de programação. O artigo também clarifica como integrar alguns dos diferentes tipos de ambientes de ensino de programação desenvolvidos até o momento de modo a garantir um ensino eficaz ao mesmo tempo em que prepara os estudantes para o mercado. É muito rica a descrição detalhada de todas as aulas realizadas e das principais dificuldades em cada tema, de modo que o instrutor, sabendo de antemão as dificuldades mais comuns, pode atacá-las logo no início das atividades. A partir do material apresentado não foi possível identificar pontos fracos na metodologia proposta por Xinogalos, na medida em que copia os pontos fortes da metodologia de Barnes e adiciona ela o aspecto de modelagem e uso de ferramentas profissionais de programação. 4.1.5 Michael Caspersen Caspersen propõe um método baseado muito no fornecimento aos estudantes do modelo conceitual, antes de iniciar a codificação, bem como em um processo sistemático de transformação de um modelo para código-fonte. Segundo Caspersem, modelagem conceitual é a definição de características de orientação a objetos e provê uma perspectiva unificada e uma aproximação pedagógica com foco sobre o aspecto de modelagem da orientação a objetos. Esta visão contempla um dos papéis da linguagens de programação segundo Knudsen & Madsen (1988, citado por Caspersen) que é justamente o uso da linguagem de programação para expressar conceitos e fenômenos. Para que seja possível a sistematização do processo de desenvolvimento, foram identificadas técnicas para a criação sistemática de programas orientado a objetos em 4 diferentes níveis de abstração: 77 / 227 • Domínio do Problema → modelagem conceitual: Criar um diagrama de classes UML do domínio do problema, focando nas classes e nas relações entre as classes; • Domínio do problema → modelagem dinâmica: Criar um diagrama de estados UML que captura a dinâmica do comportamento; • Modelagem conceitual e modelagem dinâmica → modelagem da especificação: Propriedades específicas e distribuição das responsabilidades entre as classes; • Modelagem da especificação → implementação: ◦ Implementação das estruturas entre as classes: cria um esqueleto para o programa utilizando padrões de código para as diferentes relações entre as classes; ◦ Implementação da estrutura interna das classes: Cria estruturas de classe descrevendo os elementos internos que tem de ser estabelecidos antes e depois de cada chamada de método; ◦ Implementação dos métodos: Uso padrões de algoritmo para os problemas tradicionais de algoritmo por exemplo varredura e busca. Ilustração 19: Criação sistemática de um programa orientado a objetos A modelagem do software pode ser vista em diferentes níveis de detalhes caracterizados por diferentes degraus de formalidade: • um um modelo conceitual informal descrevendo os conceitos chave do domínio do problema e suas relações; 78 / 227 • um modelo de classes mais detalhado dando uma visão mais detalhada da solução; • e a implementação em uma linguagem orientada a objetos. Este foco na perspectiva da modelagem conceitual, enfatiza que orientação a objetos não é meramente um conjunto de soluções e tecnologias, mas uma forma de entender, descrever e comunicar sobre o domínio de um problema e uma implementação concreta deste domínio. 4.1.5.1 As etapas da metodologias Na primeira metade do curso, de forma grosseira, o foco é paralelamente sobre o entendimento e uso de um modelo conceitual como um “blue print” para programação real. Na segunda metade do curso o foco primário é na qualidade interna do software. Codificar e entender o modelo o conceitual são feitos de mãos dadas, de forma que o modelo sempre vem primeiro, trazendo o código-fonte. A Introdução às diferentes construções da linguagem são subordinadas às necessidades da implementação de um dado conceito do modelo conceitual. 4.1.5.2 O Framework conceitual São trabalhados com ênfase três modelos conceituais básicos: 1) associação, 2) composição e 3) especialização. O ponto de partida é uma classe, propriedades desta classe e o relacionamento entre a classe e os objetos criados a partir dela. Uma das propriedades de uma classe pode ser uma associação com outra classe; consequentemente o próximo tópico é associação. Isto é correlacionado com o fato que uma associação (referência) é o tipo mais comum de estrutura entre classes (objetos). Composição é um caso especial de associação, sendo trabalhado logo em seguida A última estrutura a ser coberta é especialização. Especialização faz a ponte com a segunda metade do curso onde o foco é a qualidade do projeto onde especialização é frequentemente utilizada como um modo de obter projetos mais flexíveis. 4.1.5.3 O início Os estudantes iniciam por aprenderem os conceitos básicos de classes e objetos bem como noções básicas de modelagem através de um diagrama de classes semelhante ao diagrama da UML. Depois de terem usado classes e objetos, nós nos 79 / 227 voltamos para uma visão interna e começamos a escrever classes; nós fazemos isso introduzindo o primeiro padrão de código: Implementação de uma classe. 4.1.5.4 Padrões de Código Um padrão de código é uma descrição geral da implementação de um elemento do framework conceitual. Através de um número progressivo de exemplos nós mostramos que uma associação é uma propriedade de uma classe, uma classe tem mais que uma associação e é uma relação dinâmica. O ensino inicia com associações recursivas de cardinalidade 1. Para poder implementar estas associações, os estudantes precisam aprender sobre referências e o valor null, bem como a chamada de métodos entre diferentes objetos. A partir de diversos exemplos (1) e dos conceitos trabalhados é construída uma abstração (2) que corresponde ao modelo conceitual trabalhado. Para esta abstração, é apresentado um padrão de codificação (3) que permite transformá-la em código fonte funcional. Este mesmo processo é utilizado para composição e especialização, dando confiança e instrumentos para que os estudantes passem a identificar e implementar os mesmos conceitos em seus projetos 4.1.5.5 Outros elementos da metodologia Nós usamos BlueJ como ferramenta de programação. O foco é em um modo sistemático de programar. Isto implica em três coisas 2) alguns exemplos são exibidos para os estudantes; 2) uso explícito da UML e 3) um foco 80 / 227 no processo de programação. Uma vez que nós percebemos a importância do foco no processo de programação e não apenas no produto final, nós passamos a usar muita codificação ao vivo. O propósito disto é mostrar aos estudantes como um profissional de programação ataca os problemas. 4.1.5.6 Análise Geral da Metodologia As duas grades contribuições de Caspersen são o processo sistemático de desenvolvimento de software e o framework conceitual e suas respectivas abstrações. Em contrapartida, não é possível avaliar outros aspectos da metodologia apenas com o material consultado. 4.2 Diretrizes para o Ensino de OO A partir dos trabalhos acima, foram identificadas e definidas 10 diretrizes base para guiar o presente trabalho, são elas: 4.2.1 Princípios baseados na prática com o BlueJ A metodologia deve seguir todos os princípios e diretrizes que servem como base para a metodologia de Barnes que são: • Abordagem iterativa; • Abordagem baseada em projetos; • Sequencia dos conceitos em vez de construções de linguagem; • Sem cobertura completa da linguagem. Além destes quatro princípios, são norteadoras também as 8 diretrizes apresentadas por KÖLLING e ROSENBERG, documentadas na seção 4.1.2.1 4.3.2 Representações visuais para todas as estruturas e conceitos trabalhados Todos os exemplos desenvolvidos e apresentados aos estudantes devem vir acompanhados de suas respectivas visualizações. Estas visualizações podem utilizar a UML como base ou alguma das visualizações propostas nas metodologias previamente 81 / 227 estudadas, por terem estas metodologias se mostrado eficientes no trabalho dos respectivos autores. 4.3.3 Operações de abstração e framework conceitual Sempre que um novo conceito de relação entre dois ou mais objetos for apresentado, deve ser acompanhado de sua respectiva operação de abstração segundo Boratti e do modelo conceitual conforme Caspersen. Estas representações devem ser utilizadas com frequência de modo que os estudantes aprendam a enxergá-las nos problemas com facilidade. 4.3.4 Ambiente educacional + Ambiente profissional Por volta da metade do curso, os estudantes devem ser guiados do ambiente educacional BlueJ para um ambiente de desenvolvimento profissional de preferência do instrutor. É necessário entretanto, que algum tempo seja dedicado exclusivamente ao uso do novo ambiente, com exercícios e práticas que demonstrem as vantagens e recursos do novo ambiente. 4.3.5 Processo sistemático de desenvolvimento Desde as primeiras aulas os estudantes devem ser guiados sobre as etapas do processo de desenvolvimento de software, através de métodos sistemáticos de desenvolvimento e também “live coding”, que corresponde ao professor resolver problemas e tarefas de codificação ao vivo diante dos estudantes (Caspersen). 4.3.6 A partir do zero. Tão logo os estudantes adquiram confiança com um determinado conjunto de recursos e conceitos, estes devem ser colocados em situações de resolução de problemas a partir de uma tela vazia ou de um projeto em branco. Inclusive o processo de buscar classes de bibliotecas utilizadas em outros projetos é importante para o desenvolvimento profissional. 4.3.7 Recursos computacionais devem ter acesso fácil Frequentemente a maior dificuldade para que se desenvolvam jogos ou outros softwares mais complexos ainda em fases iniciais é devido a dificuldade com operações computacionais básicas como exibir algo na tela ou obter uma entrada de dados do usuário. 82 / 227 Deve ser fácil para os estudantes acessarem estes recursos utilizando o mínimo de conhecimento acerca dos conceitos de orientação a objetos. É interessante que ao final do curso seja demonstrado aos alunos como realizar as mesmas funções fornecidas pelo framework de forma “crua”, diretamente na linguagem de programação apresentada. 4.3.8 Estilo de código-fonte Estudantes geralmente não se preocupam com nomes de classes e variáveis, bem como edentação. É importante que desde o início o estilo do código seja padronizado pois conforme observações do autor, os próprios estudantes acabam por se perder em códigos mal formatados, com variáveis e classes sem diferenciação de maiúsculas e minúsculas e com nomes de variáveis que não façam sentido com o contexto trabalhado. 4.3.9 Modelagens genéricas Cada módulo do curso deve permitir a criação de um novo artefato de software, mais complexo que o módulo anterior. É extremamente importante para o desenvolvimento das faculdades de modelagem que o próprio aluno possa escolher o tema do seu interesse e construir por si próprio a modelagem e o respectivo código do seu projeto. Para que isso seja possível, devem ser trabalhados exemplos de modelagem e o respectivo código, seguindo modelo proposto por Carpensen e documentado na seção 4.1.5.4, de situações geralmente genéricas para muitos jogos como efeito da gravidade, movimentação de personagens, lançamento de artefatos como tiros ou poderes especiais e interação entre objetos para verificações de colisão. Estas modelagens genéricas permitem fácil adaptação pelo estudante para as suas necessidades, permitindo que este aprenda com bons exemplos e tenha um maior número de material para poder desenvolver sua própria modelagem. 4.3.10 Códigos e documentação na língua materna O autor observou que a grande maioria dos estudantes não tinha familiaridade com inglês e tinham muita dificuldade com nomes de classes e métodos nesta linguagem. Também ficou claro logo no início dos trabalhos que não seria viável usar fontes de consulta em língua estrangeira. Deste modo, todos os códigos deste TCC, bem como o framework proposto devem 83 / 227 estar na língua materna do estudante. Como é de conhecimento geral a necessidade do inglês para o desenvolvimento de software é importante que os alunos sejam informados sobre isto e recebam gradualmente pequenos desafios como decifrar classes nativas do Java (naturalmente em inglês) com sua respectiva documentação. O autor observou também que alguns alunos naturalmente começam a escrever seus códigos em inglês. Os que assim desejarem devem ser estimulados a continuar. 84 / 227 5 Framework Java para Jogos 2D Esta seção apresenta o “framework Aljava”, desenvolvido especialmente para este trabalho com a missão de permitir que iniciantes em programação desenvolvam jogos de duas dimensões. Na maior parte da metodologia apenas algumas classes e recursos do “framework” serão utilizados, sendo que o conceito de “framework” efetivamente, como o código que determina o ciclo de vida do software só será visto ao final da metodologia. Até lá, portanto, o “framework” será usado pelos estudantes e apresentado como uma biblioteca de código. O nome “Aljava” corresponde a cesta que os arqueiros utilizam para armazenar suas flechas. A ideia é que o “Aljava” contenha diversos recursos, cada qual como uma flecha na mão do estudante. É também possível usar o nome fazendo uma correlação com “All Java”, indicando um uso maior da linguagem Java, digamos, “ao todo”. 5.1 Aquisição do Conhecimento de Domínio Para realizar o estudo do domínio, serão utilizados os oito jogos produzidos na primeira versão da disciplina conforme apresentado anteriormente. Os temas dos jogos foram escolhidos livremente e classes foram desenvolvidas pelo professor para auxiliar o desenvolvimento. A primeira versão da disciplina já utilizava um “framework” base, oriundo da metodologia de Feijó, de modo que muito se aprendeu sobre o domínio e as necessidades de jogos 2D a partir do desenvolvimento dos exemplos e das necessidades dos estudantes. 5.2 Modelagem do Framework: Para a definição do “framework” foi utilizado parcialmente o processo conforme demonstrado por Silva (2000) e brevemente resumido na seção 2.3.2. 85 / 227 5.2.1 Generalização As principais generalidades dos projetos pesquisados foram identificadas e agrupadas nos seguintes pacotes lógicos: • Pacote Entrada: ◦ Uso de informações de teclas pressionadas do teclado; ◦ Uso de informações do mouse; ◦ Captação de informações do usuário como nome, números, etc. • Pacote Saída: ◦ Desenho de formas geométricas e imagens na tela; ◦ Criação de animações a partir de uma sequencia de imagens; ◦ Execução e pausa de sons; • Pacote Jogo: ◦ Criação de cenários; ◦ Colisão com o cenário; ◦ Colisão com outros objetos; ◦ Troca de fases; 5.2.2 Flexibilização De forma geral, existem os seguintes pontos de flexibilização nos jogos pesquisados. • Fases: Os jogos apresentaram telas onde jogadores realizam uma luta, saltam sobre objetos, atiram uns nos outros baseado em turnos e outros. O conceito de fase também vale para mapas de navegação em um cenário e menus de opções. • Objetos do Jogo: Objetos de um jogo são todos os elementos que exercem influência direta sobre o jogo, como inimigos, o próprio jogador, obstáculos na tela, 86 / 227 itens para recuperação de vida, força ou energia, pontos de chegada, tiros, golpes especiais, etc. • Cenários: Muitos cenários apresentavam necessidades especificas como exibir ou esconder determinados blocos como por exemplo para liberar uma passagem secreta, plataformas móveis e outros. Estes cenários devem possuir sistema de colisão e gerenciamento de diversos objetos 5.2.3 Modelagem das Classes A versão final do “framework” Aljava contém 25 classes organizadas em 6 pacotes. Destas 25, 15 classes podem vir a serem manipuladas pelos estudantes no decorrer da metodologia. Segue abaixo a descrição de cada pacote e de cada uma das classes desenvolvidas. 5.2.3.1 Pacote aljava Contém classes gerais que fazem a maior parte da interface com o usuário do “Aljava”. • Aljava: É a principal classe do “framework” contendo referência para os objetos de entrada e saída e realizando operações de desenho na tela. É fortemente acoplada aos pacotes aljava.entrada, aljava.saida e aljava.util. Esta classe não é manipulada diretamente pelos estudantes na metodologia apresentada, porém, não haveria problema caso o professor decidisse utilizá-la. • Alj: Classe contendo atalhos sob a forma de métodos estáticos para todos os métodos públicos da classe Aljava. O objetivo desta classe é funcionar da mesma forma que o tradicional comando “System.out.println(String s)”. Os métodos da classe “Alj” são organizados em classes internas com métodos estáticos. Um exemplo clássico é o método responsável por exibir a tela “Alj.tela.exibe()”. 5.2.3.2 Pacote aljava.entrada Contém classes que capturam eventos de entrada como mouse e teclado. Estas classes não são manipuladas diretamente pelo usuário da biblioteca. Quem faz a interface com estas classes é a classe “Aljava”. • MouseObserver: Classe que implementa as interfaces necessárias para poder observar os eventos de mouse de uma janela; 87 / 227 • Mouse: Faz a interface entre a classe Aljava e a classe MouseObserver; • TecladoObserver: Classe que implementa as interfaces necessárias para poder observar os eventos de teclado de uma janela; • Teclado: Faz a interface entre a classe Aljava e a classe TecladoObserver. 5.2.3.3 Pacote aljava.saida Contém classes relacionadas a saída de dados e visualização de informações. No futuro do “framework” este pacote poderia ter classes para fazer saída para arquivos ou outros dispositivos. • Tela: Classe que estende a classe Java “JFrame” do java e representa a janela do jogo. Esta classe não são manipuladas diretamente pelo usuário da biblioteca. Quem faz a interface com ela é a classe “Aljava”. 5.2.3.4 Pacote aljava.midia Contém classes para trabalhar com arquivos de mídia como imagens e sons. Todas as classes deste pacote podem (e frequentemente são altamente requisitas) ser utilizadas pelos estudantes. Futuramente, podem pertencer a este pacote classes para trabalhar com vídeos, “sprites” e outros formatos de áudio. • Imagem: Representa uma imagem do disco rígido. Pode ser usada com imagens “png”, “jpeg” e “gif”. Contém métodos para desenhar, mudar as dimensões da imagem e invertê-la; • Animação: Representa um conjunto de imagens que são exibidas como quadros de uma animação em função de um tempo informado pelo usuário. Tem funções semelhantes ao Design Pattern “Composite” em relação às imagens; • Gif: Extensão da classe Imagem para que um gif deixe de ser desenhado após um certo tempo. Recurso geralmente necessário para animações com explosões. • Som: permite executar, pausar e reiniciar sons com o formato “Wav”. 5.2.3.5 Pacote aljava.jogo Contém classes essenciais para a construção de jogos 2D envolvendo colisão e cenários bem como as classes base utilizadas como parte da estrutura do framework: 88 / 227 • Retangulo: Principal classe do pacote, é a base de todos os objetos do jogo, contendo quatro variáveis (x, y, largura e altura), métodos modificadores e de acesso para todas, métodos que verificam colisão com um ponto ou com outros retângulos e métodos auxiliares para recuperar pontos específicos do Retângulo, como as laterais. É a classe base para verificação de colisão com o cenário; • RetanguloGravidade: extensão do retângulo, com aplicação de gravidade e método que permite “pulo”; • Cena: Desenha um cenário utilizando a técnica “TileMap” e contém métodos que permitem a movimentação do cenário em relação a tela, isso significa que o cenário pode ser maior que a área visível da janela e é relativamente simples controlar a movimentação do cenário em relação a janela. Esta classe atende ao ponto de flexibilização de cenários junto com a classe “CenaComColisao”, explicada abaixo; • CenaComColisao: Estende a classe Cena e implementa a colisão de objetos da classe “Retangulo” com os Tiles. É a classe mais complexa do jogo e ainda apresenta diversas possibilidades de melhoria no seu algoritmo; • Tile: Classe que representa cada “tile” de uma cena. Um “tile” corresponde a um dos pequenos retângulos que formam o cenário completo; • TipoTile: Classe usada para cada tipo de bloco dentro de uma Cena, por exemplo, “tiles” que representam o chão possuem como tipo um objeto “TipoTile” que é sólido e usa uma imagem semelhante a um chão; • Camera: Realiza a movimentação do cenário a partir de um objeto da classe Retangulo e um objeto da classe Cena; • Motor: Classe que controla a execução do loop principal do jogo utilizando as interfaces “Fase” para o gerenciamento de fases; • MotorSingleton: Classe que implementa o padrão “Singleton” para acesso a um único objeto da classe Motor. Importante para o ensino de “Design Pattern”. • Fase: Interface representando uma fase de um jogo, atendendo ao ponto de flexibilização de fases; • ObjetoJogo: Classe abstrata representando objetos do jogo, atendendo ao ponto 89 / 227 de flexibilização de objetos do jogo. É uma subclasse do “RetanguloGravidade”. 5.2.3.6 Pacote aljava.util Contém classes auxiliares diversas. • ContadorTempo: Permite contar o tempo passado em milissegundos. É uma das classes com maior potencial de uso do framework; • CarregadorMidia: Classe interna do framework, essencial para o carregamento de imagens; • Direcao: Enum utilizado no pacote “jogo” para controle de colisões. Pode também ser usada na demonstração de ENUM`s, porém, este conceito não foi incluído na metodologia por questões de escopo. 5.3 Validação Como o “framework” foi sendo desenvolvido durante a aplicação dos módulos junto aos estudantes, conforme os exemplos dos módulos eram criados e os alunos desenvolviam seus próprios recursos, o “framework” foi sendo validado. Pôde ser verificado que a única classe que não atendeu completamente as necessidades dos estudantes foi a classe “CenarioComColisao” que apresenta eventuais “bugs” conforme o tamanho dos objetos e tiles utilizados. As dificuldades decorrem da complexidade do algoritmo usado para a verificação de colisões de um Retângulo com diversos outros Retângulos usados para compor o cenário. Uma opção seria trabalhar com bibliotecas que utilizam sistema de colisão baseado em medidas métricas como a biblioteca JBox2D. Porém, realizar a abstração desta biblioteca sem ter que ensinar novos conceitos aos estudantes é bastante complexo e não pode ser feito para o presente trabalho. Apesar das limitações, a classe “CenaComColisao” funciona bem em jogos onde o tamanho dos objetos é semelhante ao tamanho dos “Tiles” que compõe o cenário e os movimentos dos objetos são realizados com intervalos moderados de até 5 “pixels” por rodada. Dessa forma, a grande maioria dos jogos propostos pelos estudantes consegue rodar com a classe conforme modelada atualmente. Outro problema observado é que no BlueJ, eventualmente ocorre um erro ao tentar exibir uma janela em função de erros de acesso ao contexto gráfico. Para evitá-los, basta 90 / 227 reiniciar a máquina virtual sempre antes de executar um novo código. 5.4 Documentação Conforme visto na seção 2.3.3 e documentado na tabela 2, existem 3 tipos básicos de documentação para “frameworks”: 1) uma voltada para quem decide se vai ou não utilizar o “framework” e quais recursos ele oferecer; 2) uma segunda descrevendo como utilizar o “framework” (conhecida como Livro de Receitas) e 3) uma terceira detalhando os métodos e classes disponíveis . Este trabalho, para atender aos três tipos de documentação, propõe dois formatos: 1) o primeiro corresponde a um livro de receitas contendo na introdução uma descrição dos recursos disponíveis e 2) O “JavaDoc” do “framework”. Este “JavaDoc” é voltado para os estudantes e contém apenas a documentação para as classes utilizadas pelos estudantes. 5.4.1 Livro de Receitas O Livro de receitas foi escrito na linguagem de marcação “Markdown”, hospedado no serviço GitHub e disponibilizado online em formato de leitura agradável pelo “site” “DocumentUp”. Em função da extensão (1000 linhas e 35.653 caracteres) o arquivo não foi anexado ao documento, porém, encontra-se disponível na URL http://documentup.com/kaleucaminha/aljava e acompanha, em formato HTML, o CD, entregue em conjunto com o presente trabalho. Segue abaixo trecho inicial da documentação com uma breve descrição do que é o Aljava. Ilustração 20: Texto de introdução ao Aljava O Livro de receitas está organizado em 8 seções: 1) Uma introdução sobre instalação, download, etc 2) Seção descrevendo todos os comandos da classe Alj; 3) Seção com exemplos completos dos comandos da classe Alj. 4) Classes de Mídia; 5) Classes representando elementos do jogo; 6) Classes do framework; 7) Classes Extras e 91 / 227 8) Conclusão . Veja abaixo alguns exemplos dos submenus de navegação na documentação. Ilustração 21: Submenu da seção sobre os comandos da classe Alj Ilustração 22: Submenu com alguns exemplos integrando diferentes comandos da classe Alj Ilustração 23: Outros submenus da documentação Todas as classes públicas foram comentadas com descrições e exemplos de uso. Veja abaixo o exemplo da documentação do comando usado para desenhar um Retângulo. Ilustração 24: Documentação de como desenhar um Retângulo Sempre que necessário exemplos mais completos foram fornecidos como no caso dos recursos do mouse para saber se um determinado botão foi pressionado. 92 / 227 Ilustração 25: Exemplo de uso dos recursos de acesso ao Mouse. 5.4.2 JavaDoc O “JavaDoc” é o formato mais tradicional das documentações de bibliotecas e “frameworks” em Java. Com um JavaDoc é possível ver cada um dos métodos e variáveis de uma determinada classe bem como todas as classes de um pacote. Tudo organizado em arquivos HTML. Para este projeto foi necessária realizar alterações no gerador de “JavaDoc” do Java para que as principais palavras-chave estivessem em português, conforme a diretriz 10 informada na seção “4.3” do presente documento. Este processo está documentado na URL https://github.com/kaleucaminha/javadoc-ptbr com os respectivos códigos. Ilustração 26: Exemplo de detalhe da tradução do gerador de JavaDoc. Seguem abaixo algumas telas do JavaDoc gerado demonstrando os diferentes níveis de detalhe desta forma de documentação. 93 / 227 Ilustração 27: Visualização de diversas classes do mesmo pacote Abaixo, visualização dos métodos da classe Som. Ilustração 28: Métodos da classe som E por fim, a visualização detalhada de um determinado método, no caso, o método “configTile” da classe “Cena”. Ilustração 29: Detalhamento do método "configTile" 94 / 227 Em função da extensão dos arquivos, não foi possível adicioná-los como anexo nem colocá-los por completo no corpo deste trabalho. Para acessá-los, basta fazer download do arquivo compactado com todo o JavaDoc na URL http://goo.gl/Xz8HT. 5.5 Código Fonte Assim como com o “JavaDoc” gerado, em função da grande quantidade de arquivos o código fonte do framework está disponível para download através da URL http://goo.gl/cQ9Z9. 95 / 227 6 Metodologia de Ensino A Metodologia proposta é baseada em projetos. Os estudantes são guiados para níveis cada vez mais complexos de conceitos OO em módulos distintos, seguindo o padrão pedagógico “Espiral”, produzindo ao final de cada módulo um produto adequado ao nível de complexidade trabalhado. Toda a metodologia deve seguir os 10 princípios relatados na seção 4.2 e utilizar os recursos do framework Aljava, conforme proposto na seção 5. Cada módulo apresenta uma carga horária recomendada seguindo experiência de aplicação parcial da metodologia durante o primeiro e segundo semestre de 2012 na instituição SENAI São José. Além da carga horária, cada módulo apresenta as seguintes informações: • Conceitos de orientação a objetos (COO); • Conceitos programação (CP); • Conceitos do Framework Aljava (CFA); • Ambiente de desenvolvimento sugerido; • Um exemplo didático de um projeto, acompanhado do seu respectivo processo de desenvolvimento, incluindo definição do problema, modelagem conceitual, modelagem das classes e processo de “live coding” comentado; • Desafios que podem ser fornecidos aos estudantes; • Uma seção de resultados da metodologia contendo exemplos de trabalhos desenvolvidos por alunos durante a aplicação da metodologia; 96 / 227 6.1 Introdução A primeira etapa da metodologia deve oferecer para os estudantes uma visão geral da estrutura do curso com a apresentação de cada módulo e uma introdução ao que significa programar orientado a objetos. Nesta introdução deve ser dada ênfase em: • Um programa orientado a objetos corresponde a um conjunto de classes interligadas; • Uma classe representa um modelo a partir do qual podem ser gerados diversos objetos; • Um objeto é uma entidade que possui características variáveis e pode realizar ações. A aula pode ser ministrada tendo como base o capítulo 1 do livro “Programação Orientada a Objetos com Java” (Barnes). Este capítulo oferece uma visão clara da criação de objetos a partir de classes e da execução de métodos a partir de objetos. O fato de um programa orientado a objetos corresponder a um conjunto de classes conectadas pode ser demonstrado através de qualquer software de conhecimento dos alunos. Por exemplo, pode ser usado o jogo Space Invaders (disponível em http://www.freespaceinvaders.org/) para demonstrar os três tópicos citados: Ilustração 30: Tela do Jogo Space Invaders 97 / 227 Neste exemplo um professor poderia identificar algumas classes nesta tela como por exemplo: • Inimigo: Classe representando os alienígenas que aparecem em branco; • Tiro: Classe representando os tiros que percorrem a tela; • Personagem: Classe representando o jogador que se movimenta e atira para salvar a terra dos alienígenas; • Barreira: Classe para os blocos em verde que protege o personagem dos tiros; • SpaceInvaders: Classe que representa o jogo completo e interliga as demais. Demonstrar o número de objetos que existem de cada uma das classes: • Inimigo: 55 objetos; • Tiro: No momento em que a imagem foi capturada, apenas um. Porém, durante um jogo podem surgir muitos outros objetos desta classe; • Personagem: 1 objeto; • Barreira: 3 objetos; • SpaceInvaders: 1 objeto. Por fim, para demonstrar que cada objeto tem características e ações, é preciso dizer que a classe é responsável por projetar quais são essas características, e então, que cada objeto poderá ter um valor diferente para estas características. Por exemplo, a classe Inimigo poderia ter as características de posição, as imagens do inimigo e um indicador dizendo se ele está morto ou não. 98 / 227 Ilustração 31: Representação de uma classe do jogo SpaceInvaders A representação visual das classes pode ser feita com um diagrama de classes simplificado (sem tipagem e sem preocupação com modificadores de acesso) conforme a ilustração 21 e dos objetos como a representação sugerida por Boratti. Ilustração 32: Representação visual de dois objetos criados a partir da classe Inimigo Este processo pode ser repetido quantas vezes for necessário para que a turma compreenda o conceito de classes e objetos. Para verificar o aprendizado pode ser solicitado aos estudantes modelarem as classes de algum outro jogo amplamente conhecido como “Mario” ou “Asteroides” e modelarem objetos criados a partir destas classes. 99 / 227 6.2 Módulo 1 – Animações computadorizadas O principal objetivo do primeiro módulo é a construção de classes, tanto conceitual quanto na sintaxe da linguagem Java. São essenciais os conceitos de método e variável de objeto, bem como a capacidade de identificar as características e métodos de uma classe. É neste módulo também que o conceito de coesão é brevemente apresentado para a compreensão de que os métodos de uma classe agem sobre as variáveis de objeto. A escolha por criar animações foi feita pois elas permitem pensar em um grande número de problemas onde existem características com valores variáveis e a possibilidade de criar diversos métodos que criam animações trabalhando sobre estas variáveis. Um ótimo exemplo criado por um dos alunos foi uma classe “Sapo” que tinha características como tamanho e posição de cada olho, cor do sapo, etc. E tinha métodos como “olhar para esquerda”, “olhar para a direita”, “ficar vesgo”, etc. Para permitir o desenho dos objetos será iniciado o uso do framework Aljava, mas somente dos métodos necessários para o desenho de formas geométricas e manipulação básica da janela. Carga horária teórica 10 horas/aula Carga horária prática 6 horas/aula Produto Final Uma classe representando um objeto com métodos que executam animações através da alteração dos valores de certas características destes objetos. COO Classe; Métodos modificadores; Variáveis e Tipos; Variável de objeto; Objetos / Instâncias; Parâmetros; Construtores; Abstração; CP Estruturas condicionais (if, else e else if); Estrutura de repetição “while”; Instrução de atribuição (=, +=, -=, etc); CFA Desenho de formas geométricas; Exibição e limpeza da tela de desenho; CLJ Arquivo Jar; Tipo String; Ambiente de Programação BlueJ 100 / 227 6.2.1 Modelagem – Classe Carro O exemplo base deste módulo consiste na modelagem de um carro que será animado para andar pra frente. O início deste módulo é focado em trabalhar o conceito de abstração como uma forma de extrair dos objetos do mundo real as características e ações adequados ao objetivo do software. Portanto, ao modelar uma classe é necessário conhecer qual o objetivo desta classe. Por exemplo, um carro para o site de uma concessionária é diferente da classe de um carro para um jogo de corrida. Isto pode ser demonstrado criando junto dos estudantes modelos no quadro de classes “Carro” com diferentes objetivos. Ilustração 33: Exemplo de Classes "Carro" com diferentes objetivos Após a compreensão do processo de abstração, deve-se passar para os métodos que essa classe pode ter, levando em conta, assim como nas características, qual o objetivo do software para o qual a classe está sendo modelada. Assim como na introdução, pode-se modelar alguns objetos de cada uma das diversas classes “Carro” identificando o processo de abstração “Classificação / Instanciação” conforme descrito por Boratti e apresentado aqui na seção 4.2.1.2.1. Com a modelagem pronta, podem ser passados exercícios para os alunos modelarem outras classes e objetos a partir delas. 6.2.2 Tipos Básicos Para ser possível modelar uma classe em Java é preciso demonstrar que cada variável de objeto (característica) deve possuir um tipo. Neste momento são abordados 101 / 227 quatro tipos primitivos básicos (int, double, boolean e char) e o tipo String. Para tornar mais claro para os alunos esta questão é importante oferecer uma breve visão de como o computador entende as informações e de como os tipos mais comuns foram sendo criados. Isto pode ser feito com a seguinte sequencia de conceitos: 1. Qualquer variável em um programa de computador deve ter um valor que pode ser compreendido computacionalmente. Por exemplo, como que o motor de um carro poderia ser compreendido por um computador? 2. Os computadores funcionam com base em eletrônica, portanto, o tipo básico de qualquer valor em um computador pode ser representado como ligado/desligado, um/zero, verdadeiro/falso; 3. A partir disso, criou-se o tipo boolean que só pode armazenar dois valores, ou verdadeiro (ligado, 1) ou falso (desligado, 0). Este tipo pode ser usado em variáveis como o mecanismo de “pausar” um jogo, um indicador se o inimigo está vivo ou morto, se o carro tem ar condicionado ou não, etc. 4. A partir dos valores binários foi criado um sistema numérico capaz de representar qualquer número apenas utilizando valores 0 e 1 lado a lado. Por exemplo, o valor 0 corresponde ao próprio 0, o 1 corresponde ao valor 1, 00 corresponde ao valor 2, 01 ao 3, 11 ao 4, 000 ao 5 e assim sucessivamente. 5. Com este sistema foi possível aos computadores compreenderem números, no Java, um tipo corresponde a um número inteiro é o int. Podem ser variáveis deste tipo a velocidade de um carro, a posição na tela em pixels, o número de competidores em uma pista de corrida, etc. 6. A partir da codificação de binário para inteiro, também foi possível representar números reais (com casa decimal). Em Java este tipo corresponde ao double. São exemplos de valores deste tipo o preço de um ítem em um comércio eletrônico, o valor da gravidade ou o resultado de uma fórmula de alta precisão numérica. 7. Com os números mapeados foram criadas formas de mapear cada letra de um alfabeto para um número inteiro correspondente, assim, os computadores passaram também a compreender caracteres. Um caracter em Java corresponde ao tipo char. Pode-se usar este tipo para armazenar identificadores em forma de letras, como por exemplo, o sexo de uma pessoa (m ou f) ou a alternativa de 102 / 227 resposta a um questionário (a, b, c, d, e, etc). 8. A partir do char foi possível criar sequencias de caracteres, que podem ser usadas para representar palavras e frases. Este tipo em Java é o String. 9. A partir do conjunto de diversas variáveis diferentes é possível modelar Classes que representam objetos mais complexos do mundo real. 10. Em orientação a objetos são estes os meios que podemos utilizar para que o computador compreenda nossas abstrações. Para completar, um exemplo de algo complexo como o motor citado no ítem 1, seria: Ilustração 34: Exemplo de uma classe motor com as características e os respectivos tipos 6.2.3 Live Coding – Classe Carro Para este processo deve ser utilizado o BlueJ. A ideia é que, com o modelo anterior e o conhecimento sobre tipos, os alunos possam ver como construir na linguagem Java cada um dos conceitos apresentados. Deste modo, eles já sabem parcialmente o que precisa ser feito, só lhes falta aprender como. Segue abaixo o processo de criação de uma classe Carro para a construção das animações com a respectiva explicação dada aos alunos. 103 / 227 class Carro { } O primeiro passo é dizer para o Java que estamos criando uma classe. fazemos isso com a palavra-chave “class” seguida do nome da classe. O principal é perceber que tudo que diz respeito a classe deve estar dentro das chaves. O que estiver fora delas não será reconhecido. class Carro { int posicaoX; int posicaoY; double quantGasolina; boolean estaLigado; } Iniciamos colocando 4 variáveis de objeto, as duas primeiras para poder posicionar nossos carros na tela. Uma para saber como está o combustível e outra para informar se o carro está ligado ou não. Repare que ao final de cada variável foi colocado “;”. Em Java, é assim que dizemos que uma instrução terminal. class Carro { int posicaoX; int posicaoY; double quantGasolina; boolean estaLigado; char tipoVeiculo; String cor; } Agora foram adicionadas mais duas variáveis, uma representando o tipo do veículo. O objetivo é mostrar aos alunos que um char pode ser usado para diversos fins desde que seja previamente definido. Por exemplo, o char tipoVeiculo irá armazenar o valor “e” se o carro for esportivo, “c” se for clássico ou “m” se for moderno. A variável modelo deve armazenar a montadora do veículo. class Carro { ... char tipoVeiculo; String cor; Agora iniciamos a construção do esqueleto dos métodos da nossa classe carro. O significado da palavra void antes do nome void andaFrente(){ de cada método só poderá ser explicado } posteriormente, sendo neste momento dita 104 / 227 void andaTras(){ como necessária para a construção de um } método em Java. } Além disso os alunos devem ser orientados a olhar com atenção os parênteses, cuja utilidade será explicada em breve e as chaves. Todo o código correspondente ao método deverá ficar dentro das chaves. ... void andaFrente(){ posicaoX = posicaoX + 1; } void andaTras(){ posicaoX = posicaoX - 1; } void desliga(){ estaLigado = false; } ... O corpo dos métodos irá conter a lógica necessária para a ação correspondente. Iremos ao longo do curso ver diversas formas de implementar métodos. Uma das características mais presentes em métodos é que eles alteram o valor das variáveis do objeto. Por exemplo, andar para frente em um jogo pode ser o mesmo que movimentar a posição do carro ao longo do eixo x da tela. Neste momento o instrutor deve aproveitar para explicar o operador de atribuição e o uso de operações aritméticas como soma e subtração. Os alunos devem ser direcionados também para reparar no uso do “;” ao final de cada instrução. ... char tipoVeiculo; String marca; Carro(){ posicaoX = 20; posicaoY = 20; quantGasolina = 40.00; estaLigado = false; tipoVeiculo = 'e'; cor = “vermelho”; } Além das variáveis de objeto e dos métodos existe uma estrutura essencial para as classe chamada Construtor. Essa estrutura tem a função de inicializar os valores das variáveis de objeto de modo que quando um objeto for criado, ele já terá valores 105 / 227 void andaFrente(){ válidos para as suas diversas variáveis. posicaoX = posicaoX + 1; } Cada tipo de variável tem uma forma de ser ... inicializada. Neste momento o professor deve explicar a sintaxe de cada uma delas Tabela 9: "Live Coding" para a classe Carro Com a classe base construída o BlueJ pode ser usado para construir objetos desta classe e ver o valor das variáveis após a execução de cada método. Neste processo o professor pode ir criando diversos métodos com os estudantes para alterar os valores das variáveis de cada objeto, inclusive aumentando e mudando as variáveis apresentadas nesta classe carro. 106 / 227 Ilustração 35: Exemplo de Classe Carro Ilustração 36: Exemplos de objetos da classe Carro ao lado após execução de alguns dos métodos. 6.2.3 Live Coding – Desenhando Neste momento, os estudantes geralmente compreendem a ideia de orientação a objetos mas tem baixa motivação pois tudo o que veem são valores sendo alterados e nada que se pareça realmente com um software. Aqui entra a primeira função do Aljava que é permitir que os estudantes desenhem os objetos da classe Carro conforme os valores informados. 107 / 227 Para esta tarefa o Aljava fornece métodos para desenho de formas geométricas e uso de cores. Os métodos devem ser apresentados somente no contexto de construção do desenho. Somente após os primeiros processos é que eles devem ter acesso a documentação como apresentada na seção 5. Como estaremos usando código externo ao Java puro os alunos devem ser informados que: • Para realizar desenhos em Java puro seriam necessários muitos conceitos que só serão vistos posteriormente no curso; • Portanto, foram desenvolvidos diversos códigos em Java que permitem a realização de tarefas como desenho de forma fácil para iniciantes; • Esse conjunto de códigos corresponde a uma biblioteca chamada Aljava; • Em Java, bibliotecas de código são armazenadas em um arquivo com a extensão “.jar”; • Sempre que se iniciar um novo projeto que use esta biblioteca o arquivo “.jar” deverá ser adicionado; • Por fim, vale ressaltar que em qualquer software profissional o uso de bibliotecas externas é absolutamente comum, de modo que faz parte do processo de aprendizado usar bibliotecas externas. Segue abaixo processo de “Live coding” do desenho dos carros. 108 / 227 class Carro { ... void desenha() { } ... Uma das ações de um carro é poder desenhar a si mesmo. O desenho é uma ação e portanto, deve ser escrito como um método. } Para os desenhos usaremos comandos específicos do Aljava. Tudo no Aljava está ligado a uma tela de desenho. Portanto, existem métodos para desenhar nessa tela. Um exemplo desse método serve para o desenho de um retângulo. Todos os métodos de desenho são acessados com “Alj.desenha.comando()”. O termo “comando” deve ser usado para os métodos estáticos acessados da classe Alj para que os alunos não os confundam com os métodos que estamos trabalhando (apesar de terem a mesma lógica) e não sejam ainda introduzidos ao conceito de contexto estático. void desenha() { Alj.desenha.retangulo( 50, 50, 80, 40); } Alj.desenha.retangulo( posicaoX, posicaoY, larguraDoRetangulo, alturaDORetangulo ); Grande parte dos comandos Aljava podem receber parâmetros. Parâmetros são valores que informamos para que o comando possa ser executado. Por exemplo, para desenhar um retângulo é essencial saber a largura e altura do retângulo, bem como qual a posição em que ele será exibido na tela. Aqui também deve ser explicado aos estudantes o modelo cartesiano de desenho utilizando coordenadas x e y. A ordem dos parâmetros é sempre definida por quem escreveu o código do comando e os parâmetros devem semrpe ficar dentro dos parênteses e serem separados com vírgula. No Aljava também existem comandos para mudança de cores, sempre que uma cor é 109 / 227 alterada, todas as formas geométricas após ela são desenhadas com esta cor. No exemplo abaixo, é desenhando um retângulo para o chassi do carro, na cor laranja e as rodas na cor preta. Ao final de um desenho, para que ele efetivamente apareça, deve ser usado o comando específico “Alj.tela.exibe();” void desenha(){ Alj.cor.nome("laranja"); Alj.desenha.retangulo(50, 50, 80, 40); Alj.cor.nome("preto"); Alj.desenha.oval(60, 40, 20, 20); Alj.desenha.oval(60, 80, 20, 20); Alj.desenha.oval(100, 40, 20, 20); Alj.desenha.oval(100, 80, 20, 20); Alj.tela.exibe(); } import aljava.*; class Carro { Para que o código funcione é preciso realizar a importação do código do aljava para esta classe específica. ... Isso pode ser feito usando o comando } “import “acima da declaração da classe. Tabela 10: "Live Coding"- Desenhando um carro Geralmente o carro desenhado não chama muita atenção e os alunos podem ser desafiados a fazerem algo melhor. Aqui deve ser apresentada a documentação de todos os métodos de desenho, de mudança de cores e de uso de transparência. Bem, mas como as variáveis de objeto se relacionam com o desenho do carro? O próximo processo de “live coding” tem como foco demonstrar o uso das variáveis e como o uso dos demais métodos definidos pelos estudantes afetam o desenho. Agora nosso objetivo é fazer com que o desenho apareça mais a frente toda fez que executarmos o método “andaFrente()”. Deve-se notar que a principal ação do método “andarFrente()” é aumentar o valor da variável “posicaoX”. 110 / 227 Para conseguirmos o efeito do carro andando para frente é essencial que ao desenharmos os retângulos, círculos de mais formas, o valor da variável “posicaoX” seja levado em conta. Fazemos isso inicialmente substituindo o valor “50” colocado manualmente no desenho do retângulo pela variável. Alj.desenha.retangulo( posicaoX , 50, 80, 40); Ao fazer a mudança nota-se que as rodas não acompanharam o retângulo. O processo deve aproveitar a oportunidade para explicar que as todas devem sempre ser desenhadas relativamente ao carro. Por exemplo, a roda esquerda traseira sempre será desenhada próxima ao valor X inicial, já a roda esquerda da frente terá um valor da posição X maior do que o X original, porém, este valor é sempre fixo. Ao realizar a mudança, será percebido que um desenho sobrescreve o outro, por isso, sempre, antes de desenhar, a Tela deverá ser limpa com o comando “Alj.tela.limpa()”. O resultado do método neste momento será: void desenha(){ Alj.tela.limpa(); Alj.cor.nome("laranja"); Alj.desenha.retangulo(posicaoX, 50, 80, 40); Alj.cor.nome("preto"); Alj.desenha.oval(posicaoX + 10, 40, 20, 20); Alj.desenha.oval(posicaoX + 10, 80, 20, 20); Alj.desenha.oval(posicaoX + 50, 40, 20, 20); Alj.desenha.oval(posicaoX + 50, 80, 20, 20); Alj.tela.exibe(); } Deve ser pedido aos alunos executarem agora o método “andaFrente()” e em seguida o método “desenha()” diversas vezes seguidas. Neste momento eles devem compreender que: • Os métodos podem mudar os valores das variáveis; • Outros métodos podem usar os valores destas variáveis para fazer coisas como desenhar. 111 / 227 • A execução consecutiva destes métodos realizar uma impressão de movimento. Alguns desafios que podem ser passados aos alunos agora são: 1) criar um método “andaTras()”; 2) criar um método “andaDireita()” que muda o valor de uma nova variável “posicaoY” ; 3) Usar a variável “cor” para a cor do carro; 4) Sugerir que os estudantes criem suas próprias variáveis e formas de utilizá-la. Também é interessante após os alunos realizarem o desafio usar uma variável ângulo usada para rotacionar o carrinho antes de desenhá-lo. Um exemplo da classe Carro completa após diversas interações e desafios com os alunos pode ser demonstrado no Anexo I. Como pode ser visto neste exemplo, é possível chegar rapidamente a uma classe relativamente complexa. Os alunos nesse momento demonstram compreender os conceitos mas ainda tem dificuldades com a sintaxe. Aqui pode-se pedir que cada estudante desenvolva sua própria classe seguindo a mesma ideia do carro. Pode ser um animal, um outro veículo, enfim, qualquer objeto que eles julguem interessante para animar em um exercício posterior. 6.4.4 Live Coding – Animações Chegamos ao objetivo do módulo. Ao terem feito o exercício com os métodos “andaFrente()” e “desenha()” é possível demonstrar que uma animação de um carro em movimento para frente seria algo como: andaFrente(); desenha(); andaFrente(); desenha(); andaFrente(); desenha(); Fazemos isso criando um novo método “animacaoCarroAndando()” contendo o código acima. Ao executar o código o resultado da animação não é visto pois o processamento do computador é muito rápido. Deve então, ser usado o método “Alj.util.espera(200);” para realizar uma pausa de alguns milissegundos no processamento, e assim, o desenho pode ser observado. Com isso, o código final fica: 112 / 227 void animacaoCarroAndando(){ desenha(); Alj.util.espera(200); andaFrente(); desenha(); Alj.util.espera(200); andaFrente(); desenha(); Alj.util.espera(200); andaFrente(); desenha(); Alj.util.espera(200); } Para fazer melhor, introduzimos o uso da estrutura de repetição “while”: void animacaoCarroAndando(){ while( posicaoX < 200) { andaFrente(); desenha(); Alj.util.espera(200); } } Aqui deve ser explicado que todo o código dentro das chaves será executado repetidas vezes, até que a “expressão booleana”, entre parênteses, seja falsa. O conceito de “expressão booleana” e os quatro operadores básicos (maior, menor, igual e diferente) devem ser explicados aqui. A partir deste ponto diversos desafios podem ser propostos, indicando para os alunos, como sugerido por Caspersen, um modelo genérico para a programação de animações: 113 / 227 void metodoDaAnimacao(){ while( expressaoBooleana ) { metodoQueAlteraValorDeVariavel() ; metodoQueDesenha() ; Alj.util.espera( tempoPausa ); } } Para aumentar o potencial das animações deve-se iniciar o uso de variáveis locais, com um contador para contar os quadros da animação. void animacaoCarroAndando(){ int contadorQuadros = 0; while( contadorQuadros < 10) { andaFrente(); desenha(); Alj.util.espera(200); contadorQuadros = contadorQuadros + 1; } } Com isso, encerra-se o conteúdo principal para execução de animações. Na experiência do autor não são necessários mais exemplos. Os estudantes em geral tem muita criatividade e conseguem encontrar diversos exemplos de animações utilizando o mesmo estilo do apresentado. A maior dificuldade fica por conta da sintaxe e do uso de operadores que tenham sido apenas indicados mas que não constam no exemplo. Isso pode ser facilmente resolvido com acompanhamento durante as atividades de desafio. 6.2.5 Live Coding – Parâmetros e estruturas condicionais O objetivo desta seção é oferecer um número maior de recursos para os projetos dos estudantes. Este “live coding” mostra primeiro como usar estruturas condicionais para 114 / 227 realizar coisas diferentes no sistema e posteriormente como passar informações de fora para dentro da classe através dos parâmetros. Bem no início da modelagem da classe Carro foi modelado um atributo do tipo “char” que identificava o tipo do veículo. Este atributo poderia ter três valores diferente. A ideia é que cada tipo de carro escolhido tenha um desenho específico: • 'e' representa um carro esportivo, deve ser desenhado com um formato diferente, que lembre um carro de corrida; • 'c' representa um carro clássico, deve ser desenhado com retângulo; • 'm' representa um carro moderno, deve ser desenhado com um formado arredondado. Aqui deve-se explicar para os estudantes que dependendo do valor de uma certa variável o programa deve fazer ações diferentes. A principal estrutura condicional em programação é conhecida como “if / else” e pode ser exemplificada com o código de desenho abaixo: if(tipoVeiculo == 'c') { Alj.desenha.retangulo(posicaoX, posicaoY, 80, 40); } else if (tipoVeiculo == 'm') { Alj.desenha.oval(posicaoX, posicaoY, 80, 40); } else { Alj.desenha.retangulo(posicaoX + 15, posicaoY, 10, 40); Alj.desenha.retangulo(posicaoX + 20, posicaoY+12, 65, 16); Alj.desenha.retangulo(posicaoX + 55, posicaoY, 10, 40); } E para completar a demonstração clara do uso da estrutura: 115 / 227 if( expressaoBooleana ) { codigo(); } else { codigo(); } //ou, quando existirem mais possibilidades if( expressaoBooleana ) { codigo(); } else if( e xpressaoBooleana ) { codigo(); } eles { codigo(); } O exemplo funciona, mas para testar carros de diferentes tipos só mudando a inicialização da variável no construtor. Deve ficar claro para a turma que o problema disso é que obrigatoriamente todos os objetos da classe carro terão o mesmo valor para a variável “tipoVeiculo”. Deve ser mostrado agora que o valor do tipo do veiculo deve ser informado na hora que um novo objeto da classe Carro for construído e que este valor deverá vir de fora, de quem criar o objeto e para que valores de fora possam ser transmitidos para dentro de uma classe são utilizados os parâmetros. Qualquer método, inclusive o construtor, que é um método especial, podem receber parâmetros. E é para isso que são utilizados os parênteses ao lado do nome dos métodos. Vamos iniciar a demonstração de como usar parâmetros pelo construtor. O objetivo é que no momento em que formos criar um objeto da classe Carro seja obrigatório informar qual o tipo do veículo. Para iniciar, ressalta-se que todo parâmetro é uma variável é que toda variável em java precisa obrigatoriamente ter um tipo. Portanto, a variável que usaremos para receber um valor informado por quem criar a classe será do tipo “char” (o mesmo da variável tipoVeiculo) e terá o nome “parametroTipoVeiculo”. Veja abaixo como é a sintaxe para declarar um parâmetro obrigatório no método construtor. 116 / 227 Carro(char parametroTipoVeiculo){ //Código do construtor } A variável que estamos usando para armazenar o parâmetro enviado é uma variável que só existirá durante a execução do construtor. Diferente das variáveis de objeto que existem durante todo o tempo de vida de um objeto e podem ser acessadas de qualquer método executado pelo objeto. Precisamos agora usar esta variável, passando o seu valor, que conterá o valor informado na criação do objeto, para a variável de objeto, que o manterá enquanto o objeto existir. int posicaoY; int angulo; String cor; double quantGasolina; boolean estaLigado; char tipoVeiculo; Carro(char parametroTipoVeiculo){ posicaoX = 20; posicaoY = 50; angulo = 0; quantGasolina = 40.00; estaLigado = false; tipoVeiculo = parametroTipoVeiculo; cor = "vermelho"; } O BlueJ, sempre que executa um método que precisa de parâmetros exibe uma janela solicitando que o estudante preencha o valor dos parâmetros solicitados. Esta janela deve ser mostrada com cuidado pelo instrutor para que os estudantes leiam o que ela apresenta para que posteriormente associem o que vem nesta janela com a execução do método via código. Foi observado que sempre que os estudantes são apresentados a janelas que solicitam dados ou realizam ações, muitas vezes eles tentam utilizar sem ler as instruções ou informações apresentadas. 117 / 227 Ilustração 37: Exemplo de criação de objetos usando parâmetros pelo construtor Outro exemplo é o método “andaFrente()”. Da forma como foi construído ele só anda 10 pixels por execução, não sendo possível informar quanto o carro deve andar. Uma forma de resolver este problema é usando um parâmetro no método “andaFrente()”, do tipo inteiro, que informa quanto o carro deve andar. Isso pode ser feito facilmente da seguinte forma: void andaFrente(){ void andaFrente(int distancia){ posicaoX = posicaoX + 10; posicaoX = posicaoX + distancia; angulo = 0; angulo = 0; //se andou, perde gasolina //se andou, perde gasolina quantGasolina -= 0.1; quantGasolina -= 0.1; } } Método sem parâmetro. Mesmo método com parâmetro. Tabela 11: Demonstração comparativa para uso de parâmetros Neste momento porém, ocorrerá um erro no método “animacaoCarroAndando()”, pois dentro do método existe uma chamada para o método “andaFrente()”. Só que agora, colocamos um parâmetro no método e todo parâmetro deve obrigatoriamente ser informado quando o método é executado. A forma que temos para resolver isso é informar manualmente o valor que queremos para que o carro ande. Para este exemplo, usarei o valor 1, de modo que a animação ficará mais realista. 118 / 227 void animacaoCarroAndando(){ int contadorQuadros = 0; while( contadorQuadros < 10) { andaFrente(1); desenha(); Alj.util.espera(200); contadorQuadros = contadorQuadros + 1; } } E aproveitando que estamos trabalhando no método “andaFrente()” podemos ver mais um exemplo de uso de estruturas condicionais. Por exemplo, se o carro estiver sem combustível, ele não pode andar para frente. Modificaremos este método para que se o método for executado e não houver combustível, uma mensagem será exibida ao usuário. void andaFrente(int distancia){ if(quantGasolina <= 0){ Alj.tela.exibeMensagem("Carro sem combustível. Abasteça"); return; } posicaoX = posicaoX + distancia; angulo = 0; ... Repare que dentro do bloco de código do “if”, após exibir a mensagem, foi usada a instrução “return;”. Esta instrução finaliza a execução do método retornando a execução para quem o executou. Nosso único problema agora é que não conseguimos testar esta alteração porque o valor inicial do combustível está em 40 litros. Podemos fazer com que o usuário informe, na hora que está criando a classe, qual a quantidade de combustível do carro. Com isso, podemos informar um valor baixo e testar o método “andaFrente()” até acabar o combustível do veículo. 119 / 227 Carro(char parametroTipoVeiculo, double parametroGasolina){ posicaoX = 20; posicaoY = 50; angulo = 0; quantGasolina = parametroGasolina; estaLigado = false; tipoVeiculo = parametroTipoVeiculo; cor = "vermelho"; } Agora que os estudantes já conhecem parâmetros e a estrutura condicional “if”, eles podem ser desafiados a criar um método abastece que adiciona uma certa quantidade de gasolina no tanque e adicionar verificações para que o carro não saia para fora da tela em hipótese nenhuma. 6.4.6 Resultados Este módulo foi trabalhado de forma similar a apresentada aqui na metodologia no segundo semestre de 2012 para uma turma de Introdução a Orientação a Objetos. Nesta primeira aplicação do método haviam as seguintes diferenças: • Comandos Aljava: Os comandos de desenho do Aljava eram fornecidos através de um objeto da classe Aljava. Deste modo, os estudantes precisavam dentro do código ter uma variável de instância para um objeto da classe Aljava, ter a instanciação do objeto usando a palavra-chave “new” e execução de métodos desse objeto. Foi verificado que esta estrutura era extremamente complexa para o início e tão logo foi possível foi alterado para o uso de comandos (métodos estáticos); • Parâmetros e Condicionais: O uso de parâmetros e estruturas condicionais eram feitos antes de ensinar como fazer uma animação propriamente dita. Porém, o conteúdo “parâmetros” se mostrou o mais complicado e muitos estudantes não conseguiram fazer as animações usando parâmetros. Passando este conteúdo para o final, o objetivo é poder ter uma tarefa adicional que é efetivamente adicionar parâmetros para as animações depois de já as terem feito. Conforme observado as principais dificuldades foram: • Parâmetros no construtor: Compreender o uso de parâmetros no construtor e 120 / 227 necessidade de declarar uma variável para receber o valor e então passar o valor para a variável de objeto. Ficou evidente a necessidade de se frisar o escopo das variáveis e que os parâmetros também são variáveis; • Rotaciona do Aljava: O método para rotacionar objetos do Aljava é de difícil compreensão e somente alguns alunos conseguem de fato usá-lo; • Sintaxe Java: Mesmo com os diversos exemplos, a sintaxe de construção de métodos, principalmente o uso dos parênteses e das chaves causa bastante confusão. Foi percebido também que muitos estudantes eventualmente colocavam algum código fora de um método e não conseguiam perceber o erro; • Menor cuidado com o nome das variáveis: Em diversos momentos durante as explicações, perdeu-se o cuidado com o nome de certas variáveis. Isso se refletiu no resultado dos alunos conforme será observado na análise dos resultados. O trabalho foi feito em dupla em uma sala de 24 alunos de modo que resultou em 12 classes. Das 12, 8 tiveram um bom desempenho. Segue abaixo mais detalhes sobre 4 destas classes: 6.4.6.1 Classe Sapo A Classe Sapo se destacou pela criatividade, pelo bom número de métodos e pelo uso correto dos parâmetros. Ilustração 38: Exemplo Classe Sapo desenvolvida por um aluno 121 / 227 Apesar do bom desempenho visual é importante ressaltar alguns pontos no código da classe que poderiam ter sido melhor trabalhados. O primeiro é o nome dado às variáveis de objeto, ilegíveis até que se compreenda todo o código. Ilustração 39: Variáveis na classe Sapo O método que desenha o sapo ficou com 130 linhas. A classe poderia ter ficado muito mais clara se tivesse sido passada a ideia de dividir um método grande em outros menores, facilitando o entendimento e modificação do código. 6.4.6.2 Classe BatmanLogo Esta classe demonstra um talento fenomenal do estudante na criação do desenho usando apenas o sistema de rotação e as formas geométricas básicas. Ilustração 40: Classe BatmanLogo 122 / 227 Apesar da grande qualidade e do bom uso de parâmetros e dos demais conceitos, este estudante se dedicou muito mais ao desenho do que para a Orientação a Objetos. Este sem dúvida é um risco que se corre ao adotar a metodologia. Foi percebido porém, que desde que não seja frequente, o ganho motivacional do estudante quando é permitido que ele gaste tempo em funções como o desenho é muito alto. E é esta dedicação que trará desafios de programação tanto para o professor quanto para os estudantes. Um exemplo sobre esta questão é que assim que havia finalizado os métodos para exibir e apagar a logo do Batman, o problema levantado pelo estudante foi sobre como usar o mouse para ligar e desligar a luz. Isso foi passado ao estudante que já pode implementar um recurso avançado antes mesmo deste ter sido explicado para a turma, demonstrando que a facilidade de uso do Aljava permite também compreender diferentes velocidades de aprendizado. 6.4.6.3 Classe Onibus Esta classe demonstrou um uso perfeitamente adequado dos padrões de codificação apresentados. Tendo aplicado as mesmas ideias em diferentes métodos, com bastante precisão. Ilustração 41: Classe Onibus O código desta classe demonstra também um outro problema muito comum. a edentação. Em muitos pontos o código fica desorganizado e foi frequente os estudantes se confundindo com os seus códigos por causa de má formatação de código. 123 / 227 Ilustração 42: Exemplo de má formatação de código na classe Onibus 6.4.6.4 Classe Carro Esta classe apesar de possuir o mesmo nome da classe trabalhada no exemplo foi desenvolvida de modo totalmente diferente pelo estudante. O principal destaque é a demonstração clara de como o método de animação ensina corretamente o uso das variáveis de objeto. Nos 6 métodos que possuem estrutura de repetição da classe foram usadas corretamente variáveis de objeto para movimentar partes específicas do desenho e fazer degradê de cores. Ilustração 43: Exemplo Classe Carro 6.3 Módulo 2 – Mini Game O objetivo deste módulo é demonstrar como utilizar os conteúdos aprendidos no módulo 1 para efetivamente construir um jogo. Além dos recursos já vistos anteriormente, serão trabalhados o conceito de matrizes com a respectiva construção em “arrays”, métodos de acesso (com retorno) e interação com o mouse. Carga horária teórica 10 horas/aula Carga horária prática 6 horas/aula Produto Final Uma classe representando um pequeno jogo com interação com o mouse. 124 / 227 COO Fortalecimento dos recursos anteriores. Uso de métodos com retorno do framework Aljava. CP Estrutura de repetição “for”; expressão booleana com mais de uma condição. Matrizes. CFA Acesso ao mouse. CLJ Arrays. Ambiente de Programação BlueJ. O jogo escolhido para demonstração é o Jogo da Velha. E, seguindo o princípio de sempre modelar antes de codificar, deve ser lançada a seguinte pergunta aos estudantes “como modelar um jogo da velha de modo que o computador o compreenda?”. Esta etapa é particularmente difícil e pode ser iniciada por dados simples como o nome dos jogadores e um contador de vitórias. Caso nenhum aluno se manifeste sobre o tabuleiro do jogo da velha, o professor deve iniciar desenhando o tabuleiro no quadro é questionando sobre o que é variável no tabuleiro. Normalmente se percebe que existem 9 casas e que cada uma destas casas pode apresentar três situações distintas: 1) estar livre; 2) estar ocupada pelo jogador 1 ou estar ocupada pelo jogador 2. A próxima pergunta é “como representar cada casa usando variáveis de objeto?”. Na experiência do autor o tipo preferido pelos estudantes é o “char” neste caso. Sem dúvida é uma escolha interessante, porém, vale incentivá-los a usar inteiros para que se inicie a percepção de que o programador pode dar significado também aos números, usando-os para além de operações matemáticas. Deste modo, pode-se construir uma modelagem conforme demonstrada abaixo, com nove casas do tipo inteiro. class JogoDaVelha { String nomeJogador1; String nomeJogador2; int casa1; int casa2; int casa3; int casa4; ... } 125 / 227 Em breve iremos transformar estas nove casinhas em um “array”, só que antes de demonstrar isso aos estudantes é importante que eles comecem a ver qual a vantagem de usar esta estrutura ao invés de variáveis separadas. Para isso, iremos usar a seguinte estratégia: o tabuleiro a ser desenhado deve usar a cor preta para casas livres, azul para casas ocupadas pelo jogador 1 e vermelho para as casas ocupadas pelo jogador 2. Para conseguir desenhar as nove casas, os estudantes são obrigados a repetir a mesma estrutura condicional nove vezes. Para demonstrar será criado um construtor que inicializa os valores das nove casas com dados aleatórios e os nomes dos dois jogadores. O método de desenho do cenário já desenha também os nomes dos jogadores. JogoDaVelha(){ Alj.inicializa(302, 352); nomeJogador1 = "Kaléu"; nomeJogador2 = "Olavo"; casa1 = 0; casa2 = 1; casa3 = 1; casa4 = 0; casa5 = 0; casa6 = 2; casa7 = 0; casa8 = 0; casa9 = 2; } void desenha(){ Alj.desenha.texto(20, 25, "Jogador 1: "+nomeJogador1); Alj.desenha.texto(170, 25, "Jogador 2: "+nomeJogador2); Alj.desenha.linha(0, 48, 302, 48); desenhaCasinhas(); Alj.tela.exibe(); } void desenhaCasinhas(){ } Repare que somente na preparação para o jogo da velha, devem ser apresentados dois novos conceitos: 1) o primeiro é a concatenação de Strings no 126 / 227 desenho do nome do jogador e 2) o segundo é o uso de um método interno “desenhaCasinhas” ao invés de colocar todo o código dentro de um único método. Isto deve ser orientado como uma boa prática para manter o código organizado e de fácil compreensão. Agora, segue exemplo de código para desenho das casinhas, repetido somente para as duas primeiras, mas que deve, pelo professor e pelos alunos, ser repetido até a nona casa. //Desenha casinha 1 if(casa1 == 2){ Alj.cor.nome("vermelho"); } else if(casa1 == 1) { Alj.cor.nome("azul"); } else { Alj.cor.nome("preto"); } Alj.desenha.retangulo(0, 50, 100, 100); //Desenha casinha 2 if(casa2 == 2){ Alj.cor.nome("vermelho"); } else if(casa2 == 1) { Alj.cor.nome("azul"); } else { Alj.cor.nome("preto"); } Alj.desenha.retangulo(101, 50, 100, 100); O código completo é de quase 100 linhas e além disso, os estudantes devem ser levados a ver que, caso eles decidam mudar o desenho das casinhas para acrescentar uma determinada imagem, terão que fazer a alteração em todas as nove casas. Neste momento deve-se apresentar a matriz como uma estrutura de armazenamento de dados que pode ser percorrida computacionalmente. Ou seja, ao invés de nove casas em variáveis separadas, colocaremos as nove casas em uma matriz que será percorrida para realizar o desenho das nove casas. A estrutura de matriz pode ser representada da forma como sugerida por Boratti. Auxilia muito a compreensão dos 127 / 227 estudantes se eles puderem aqui ser apresentados ao fato de que quando declaramos uma variável, um espaço no memória do Java é reservado. Assim, quando declararmos nossa matriz, iremos reservar um certo número de espaços na memória, todos ligados a uma só variável. A representação visual disso, em contraste com o modelo anterior seria: Ilustração 44: Representação visual do uso de matriz A seguir, transformamos as variáveis em uma matriz e inicializamos os valores da matriz demonstrando: 1) a sintaxe de declaração de “arrays”; 2) a sintaxe de inicialização de arrays; 3) o acesso a cada posição do array. Ajuda deixar claro que uma matriz nada mais é do que um conjunto de variáveis organizadas de modo que cada uma corresponda a uma determinada posição. int casas[]; JogoDaVelha(){ ... casas = new int[9]; casas[0] = 0 casas[1] = 1; casas[2] = 1; ... casas[7] = 0; casas[8] = 2; } 128 / 227 A compreensão dos estudantes fica mais clara quando o professor demonstra a execução do código descrito aqui construindo a representação visual da matriz a correspondente a cada linha, durante esse processo deve ser explicado que em computação, a primeira posição de um “array” é o 0 ao invés do 1, dando destaque claro para o fato de o número “9” na inicialização da matriz corresponder ao número total de casas na matriz, mas que a última casa sempre estará na posição igual ao número de casas menos um, nesse caso, oito. Até aqui ainda não tivemos nenhum ganho real com o uso de “arrays”. Fazemos isso agora no método “desenhaCasinhas” através da estrutura de repetição “for”. Sobre esta estrutura deve ficar claro que: • Esta estrutura é adequada para repetições que envolvam números, e que devam ser executadas um número fixo de vezes, diferente da estrutura “while” que deve ser usada quando a condição de saída da estrutura não é previamente conhecida. • Na declaração do “for”, existem três partes: 1) a declaração de uma variável de controle; 2) a condição para que o “loop” seja executado e 3) Uma instrução que altera o valor da variável de controle para que o “loop” avançe. É útil que em paralelo ao uso do for para o desenho das casinhas, o professor apresente também exemplos como “executar um código com um número de 1 até 100” e “Somente os números pares do 1000 ao 100”. Para o desenho das casinhas, iniciamos simplesmente colocando um “loop” for que irá executar um certo código nove vezes, com a variável de controle indo de 0 até oito, ou seja, uma vez para cada posição da matriz. Segue processo que pode ser usado com os estudantes: 129 / 227 void desenhaCasinhas(){ for(int i = 0; i <= 8; i = i + 1 ){ if(casa1 == 2){ Alj.cor.nome("vermelho"); } else if(casa1 == 1) { Alj.cor.nome("azul"); } else { Na primeira etapa o foco é explicar as três partes do “for” e como elas são necessárias para que o código entre as chaves seja executado o número exato de vezes. Alj.cor.nome("preto"); } Alj.desenha.retangulo(0, 50, 100, 100); } } for(int i = 0; i <= 8; i = i + 1 ){ if(casas[i] == 2){ Alj.cor.nome("vermelho"); } else if(casas[i] == 1) { Alj.cor.nome("azul"); } else { Alj.cor.nome("preto"); } Neste passo existe uma certa demora na compreensão de que é possível usar uma variável no lugar onde antes havia um número. Para melhor percepção disto vale que o professor se aproprie Alj.desenha.retangulo(0, 50, 100, 100); } do tradicional “teste de mesa” e demonstre ele mesmo o que está acontecendo na execução do código (em geral na quarta rodada do “loop” eles já compreenderam). int linha = i / 3; Apesar de ter sido diminuído o int coluna = i % 3; número de linhas do código, int posX = coluna * 101; será necessário adicionar a int posY = (linha * 101) + 50; complexidade de saber em qual Alj.desenha.retangulo(posX, posY, 100, 100); posição X e Y o retângulo deverá ser desenhado, tendo como variável apenas a posição da casa no array. Nesta tarefa é necessário 130 / 227 apresentar o operador módulo (resto da divisão) e demonstrar como o conhecimento básico de matemática é importante na resolução de problemas computacionais. Também vale ressaltar neste exemplo o uso das variáveis locais para auxiliar o desenvolvimento de uma tarefa. Tabela 12: "Live Coding" para desenho das casas do Jogo da Velha Aqui temos um jogo da velha, que, apesar de desenhado, não funciona. Para avançar, primeiro fazemos a modelagem do jogo junto aos estudantes tendando identificar quais métodos podem ser úteis ao jogo da velha. Nesta seção, apresenta-se que todo jogo possui um “loop” principal que tem como função: 1) Pegar informações dos dispositivos de entrada; 2) Processar e 3) Desenhar. Com este conhecimento podemos modelar nossa classe JogoDaVelha com os seguintes métodos: Ilustração 45: Métodos classe JogoDaVelha Segue abaixo processo de “live coding” usado para finalizar o jogo. 131 / 227 void loop(){ while(true){ Alj.tela.limpa(); processaMouse(); desenha(); Iniciamos pelo método “loop”. Todo jogo tem um “loop” central que só termina quando o jogo termina. O “loop” métodos deve que chamar compõe jogo na sequencia verificaEmpate(); método verifica vitória deve estar antes } por o demais verificaVitoria(); } correta, os exemplo, o do verifica empate para que alguém possa ter chance de ganhar na última rodada. void processaMouse(){ if( Alj.mouse.clickE() ){ int xMouse = Alj.mouse.x(); int yMouse = Alj.mouse.y(); int coluna = xMouse / 101; O método que faz o processamento do mouse possui 4 partes, A primeira, correspondente às três primeiras linhas simplesmente verifica se o mouse está sendo pressionado e em seguida int linha = (yMouse - 50) / 101; armazena em variáveis locais o valor da posição X e Y do mouse. Aqui é o int pos = (linha * 3) + coluna; if(casas[pos] != 0){ primeiro caso em que um método está presente onde deveria booleana. Isto existir deve uma //A casa já está ocupada. expressão ser return; ressaltado e informado aos alunos que os três métodos usados para pegar a } posição if(vezJogador1){ casas[pos] = 1; vezJogador1 = false; } else { casas[pos] = 2; vezJogador1 = true; } do mouse tem uma característica em comum. Todos tem retorno. O “clickE()” retorna verdadeiro ou falso e os métodos “x()” e “y()” retornam um valor inteiro. Na segunda parte os valores X e Y são transformados na posição da matriz } } correspondente à casa. A operação é a inversa da que foi feita no “loop for” que desenhou as casas. Na terceira parte, verifica-se se a casa já 132 / 227 está ocupada e em caso positivo, para a execução do método. Na última parte, a casinha é marcada para o jogador correspondente à sua vez. Para controlar de qual jogador é a vez, foi usada uma variável booleana como variável de objeto. void verificaVitoria(){ //verifica linha horizontal 1... if(casas[0] == 1 && casas[1] == 1 && casas[2] == 1){ //jogador 1 venceu } if(casas[0] == 2 && casas[1] == 2 && casas[2] == 2){ //jogador 2 venceu } } O método verifica vitória precisa verificar 8 combinações de três casas preenchidas para cada jogador. Ou seja, são dezesseis verificações de vencedor. Construir todas essas verificações manualmente, além de ser cansativo, aumenta a probabilidade de erro pois é difícil verificar se todas as combinações foram informadas corretamente em função da sobrecarga de elementos de sintaxe na instrução condicional. Uma das formas de lidar com este problema é construindo um método que verifica a vitória dos dois jogadores para três determinadas casas que podem ser passadas por parâmetro. Se tivermos esse método, o código do “verificaVitoria” ficaria mais simples. void verificaVitoria(){ //Horizontais verificaVitoria3Casas(0, 1, 2); verificaVitoria3Casas(3, 4, 5); verificaVitoria3Casas(6, 7, 8); É fácil perceber que o código aqui ficou muito mais limpo. Agora o novo método que foi criado só precisa de duas estruturas condicionais, uma para cada Atenção especial deve ser dada para: 1) //Verticais uso do operador “&&” na expressão verificaVitoria3Casas(0, 3, 6); booleana e 2) uso das variáveis para verificaVitoria3Casas(1, 4, 7); acessar um valor no “array” e a 133 / 227 verificaVitoria3Casas(2, 5, 8); //Diagonais consecutiva comparação desse valor com “1” ou “2”. verificaVitoria3Casas(0, 4, 8); verificaVitoria3Casas(2, 4, 6); } void verificaVitoria3Casas(int c1, int c2, int c3){ if(casas[c1] == 1 && casas[c2] == 1 && casas[c3] == 1){ Alj.tela.exibeMensagem("Jogador "+nomeJogador1+" venceu."); reinicia(); } if(casas[c1] == 2 && casas[c2] == 2 && casas[c3] == 2){ Alj.tela.exibeMensagem("Jogador "+nomeJogador2+" venceu."); reinicia(); } } void reinicia(){ for(int i = 0; i <= 8; i++){ casas[i] = 0; } } O método “reinicia” altera o valor das casas para “livre”, permitindo que o jogo continue. Pode-se aproveitar para explicar o uso de “i++” no lugar de “i = i + 1;”; void verificaEmpate(){ for(int i = 0; i <= 8; i++){ if( casas[i] == 0){ return; } } Por fim, para verificar se ocorreu empate da maneira mais correta teríamos que ver se não existe mais nenhuma possibilidade de vitória. Isto é complexo e para este jogo será adotado um mecanismo mais simples. Se todas as 9 Alj.tela.exibeMensagem("Empate."); casas reinicia(); } jogador estiverem 1 ou considerado “verificaVitoria” ocupadas pelo empate. foi ou jogador pelo 2, Como é o executado anteriormente, não existe risco de um cenário em que todas as casas estão ocupadas e existe um vencedor. Nestas condições, sempre que as nove casas chegarem neste método como ocupadas, 134 / 227 haverá empate. Ao invés de usar um “if” muito extenso, abordou-se a técnica de detectar se alguma casa está livre, em caso positivo, não há empate. Se nenhuma livre for encontrada, então, empatou e o jogo deve ser reiniciado. Tabela 13: "Live Coding" para finalização do jogo da velha E assim finaliza-se o primeiro jogo desenvolvido com a metodologia. O foco aqui foi ganhar maior autonomia e demonstrar como resolver problemas complexos utilizando muitos recursos semelhantes aos vistos no primeiro capítulo. Como desafio poderia ser passado aos estudantes: 1. Adicionar um contador de vitória para cada jogador; 2. Adicionar uma animação na casinha quando fosse preenchida; 3. Desenvolver um novo jogo utilizando recursos do mouse e matrizes. Um exemplo em potencial é a criação de um jogo da memória usando matrizes com duas dimensões. 6.5.1 Resultados O material apresentado foi oferecido aos estudantes a título de exemplo e nenhuma atividade extra foi cobrada. Observa-se que seria interessante solicitar que em pequenos grupos os estudantes pensassem e desenvolvessem pequenos jogos usando apenas o mouse e o desenho de formas geométricas. São exemplos de projetos que poderiam ser desenvolvidos: 1. Jogo de perguntas para crianças do tipo: “Clique não azul” e a tela apresenta blocos de diferentes cores. 2. Jogo em que são desenhados na tela diversos números e uma conta matemática é apresentada, e assim, a criança precisa clicar na resposta correta. Uma variação seria os valores ficarem mudando de posição em intervalos de tempo regulares. 3. Jogo para destreza com o mouse onde retângulos coloridos vão sendo exibidos de 135 / 227 tempos em tempos e cada retângulo acertado rende uma certa pontuação. Para estes jogos ou outros podem ser apresentados os recursos do Aljava para sortear números aleatórios e os recursos do próprio Java para controle de tempo. 6.4 Módulo 3 – Um Jogo, Muitos Objetos Neste momento os estudantes apenas desenvolveram classes individuais, o módulo três tem como desafio demonstrar como objetos de classes diferentes podem interagir através dos seus métodos. Toda a interação entre os objetos ocorre via métodos (mensagens) de modo que de forma alguma são usadas variáveis públicas. Outro ponto deste módulo é o uso de herança da classe Retangulo do Alava, usada essencialmente para facilitar o trabalho com colisões e movimentação dos objetos. E para ampliar consideravelmente as possibilidades do jogo é demonstrado o uso da classe ArrayList do Java. Carga horária teórica 12 horas / aula Carga horária prática 12 horas / aula Produto Final Um pequeno jogo com 4 classes, colisão entre objetos e uso de listas. COO Métodos com retorno; Herança; Associação / Composição; CP Estrutura de repetição “foreach”; CFA Acesso ao teclado; uso de imagens e sons; Classe Retangulo. CLJ sintaxe dos métodos com retorno. Ambiente de Programação BlueJ 6.4.1 Modelagem Básica Inicia-se apresentando para os estudantes o desafio de construir um jogo simulando uma guerra espacial, o jogo deve ser pequeno neste momento para que possase estudar os conceitos básicos, portanto, o jogo terá as seguintes funcionalidades: • O jogador controlará uma nave que poderá se mover em todas as direções; 136 / 227 • A nave poderá lançar tiros que se moverão de baixo para cima; • Asteroides descerão da tela; • Se um asteroide acertar a nave, o jogador perde o jogo; • Se um tiro da nave acertar um asteroide, destrói o asteroide; • Quando todos os asteroides estiverem destruídos, o jogador 1 venceu o jogo. A modelagem deve iniciar pelas três classes base: “Nave”, “Tiro” e “Asteroide”, pois já é familiar a eles a modelagem de classes como estas. Com as três classes prontas demonstra-se a modelagem de uma classe que irá representar todo o jogo e que ficará responsável pelas ações de interação entre objetos diferentes. Neste ponto é importante ficar claro: • Cada objeto conhece somente as suas variáveis de objeto. Deste modo, um objeto da classe Nave não pode conhecer o valor de uma variável da classe Asteróide; • A classe “GuerraEspacial” que representa o jogo contém como variáveis, um objeto da classe “Nave”, alguns objetos da classe “Asteroide” e diversos objetos da classe “Tiro”; • Somente a “GuerraEspacial” pode realizar operações onde mais de um objeto estão envolvidos porque somente ela conhece estes objetos. Por exemplo, uma colisão entre asteroide e nave só poderá ser feita nesta classe. Segue abaixo um exemplo da modelagem deste jogo apropriada para os conteúdos deste módulo. Lembrando que inicialmente a Herança não é apresentada, de modo que todos os objetos das classes base possuem “x”, “y”, “largura” e “altura”. 137 / 227 Ilustração 46: Modelagem básica do jogo Guerra Espacial A seguir, as três classes base devem ser modeladas. Todas são bastante simples, segue abaixo o código de cada uma delas e os respectivos comentários do que pode ser dado destaque para os estudantes. import aljava.*; Na declaração das variáveis, nenhuma class Nave novidade. Apenas é sugerido voltar a { int x; destacar que aqui as variáveis são int y; apenas declaradas, de modo que o int largura; “Java” pode reservar um espaço na int altura; memória para os valores destas variáveis, porém, os valores ainda não int velocidade; String nomeJogador; Nave(){ x = 140; y = 320; largura = 40; altura = 60; foram inicializados. No construtor é onde inicializamos os valores. Destaque para o método “solicitaTexto” do Aljava que permite que os estudantes peguem um valor informado pelo usuário. velocidade = 5; nomeJogador = Alj.tela.solicitaTexto("Nome:"); void movimenta(){ if(Alj.tecla.press("esquerda")){ x -= velocidade; O movimento do teclado ocorre com o uso do método “Alj.tecla.press”, como 138 / 227 } parâmetro este método pode receber if(Alj.tecla.press("direita")){ x += velocidade; } if(Alj.tecla.press("cima")){ y -= velocidade; qualquer letra do teclado e algumas teclas especiais como “enter” e “espaco”. Na atribuição da velocidade para o valor de “x” ou de “y” é extremamente importante demosntrar que poderíamos } ter colocado manualmente o valor “5” if(Alj.tecla.press("baixo")){ y += velocidade; } por exemplo. Mas que dessa forma, se quiséssemos alterar este valor caso a nave pegasse um ítem especial, isso } não seria possível. Como está em uma variável, durante o jogo, bastaria um método que altera a velocidade da nave. void desenha() { Desenha a nave como um triângulo. Alj.cor.nome("vermelho"); Alj.desenha.texto( x, y+altura+10, nomeJogador ); Alj.desenha.triangulo( x, y, largura, altura ); } void atira(){ //Precisaremos de novos recursos para este método... O método atira por hora serve como um lembrete de que a nave poderá atirar, sua implementação porém, será } completamente diferente como será visto posteriormente. } Fim da classe Segue abaixo agora a modelagem da classe Asteroide. import aljava.*; Declaração de variáveis da classe class Asteroide { Asteróide. int x; 139 / 227 int y; int largura; int altura; int velocidade; Asteroide(){ Inicialização dos valores das variáveis x = Alj.util.sorteia(0, 360); y = Alj.util.sorteia(-400, -50); largura = Alj.util.sorteia(30, 50); altura = Alj.util.sorteia(30, 50); usando um método que sorteia números aleatórios entre um mínimo e um máximo informado pelo usuário. velocidade = Alj.util.sorteia(2, 8); } void movimenta(){ O movimento do asteroide corresponde y += velocidade; a ele cair (aumentar o y) até que chegue ao limite inferior da tela. Neste if(y > 400){ x = Alj.util.sorteia(0, 360); momento uma nova posição para ele é y = Alj.util.sorteia(-400, -50); sorteada e ele reaparece no topo. } } void desenha() { Desenha o asteroide como um círculo. Alj.cor.rgb(120, 120, 120); Alj.desenha.oval( x, y, largura, altura ); } } Fim da classe Por fim, segue abaixo a classe Tiro. import aljava.*; Declaração de variáveis da classe Tiro. Deve- class Tiro { se dar destaque que são os mesmos atributos int x; int y; do Asteroide e quase os mesmos da classe int largura; Nave. Isso mostra que em programação é int altura; comum encontrarmos padrões que se repetem, e com a prática e conhecimento destes int velocidade; padrões o desenvolvimento passar a ser muito 140 / 227 mais rápido. Tiro(int _x, int _y){ x = _x; y = _y; largura = 2; altura = 4; Inicialização dos valores das variáveis. A única diferença para os demais é que tanto o “x” como o “y” deverão vir de fora, pois um tiro será lançado a partir de um determinado ponto do cenário, que só será conhecido no momento velocidade = 10; da execução. } void movimenta(){ y -= velocidade; } O movimento do tiro é sempre de baixo para cima. Mais tarde pode ser demonstrado pelo professor como lidar com estes tiros que já saíram do cenário para que eles não ocupem muito espaço na memória do computador. void desenha() { Alj.cor.rgb(255, 255, 255); Alj.desenha.retangulo( Desenha o asteroide como um pequeno retângulo. x-1, y-2, largura, altura); } } Fim da classe 6.4.2 Lógica do Jogo Apesar das três classes terem sido trabalhadas, ainda não existe um local onde os objetos destas classes podem interagir, este local é a classe “GuerraEspacial”. É neste ponto que demonstramos que uma variável pode conter um objeto de qualquer classe, e não apenas os cinco tipos básicos demonstrados até o momento. Os estudantes também já sabem que todo jogo possui um “loop” que realiza as funções de processamento e desenho em um jogo. Iniciamos por demonstrar o esqueleto da classe, apenas com a declaração das variáveis de objeto; 141 / 227 import aljava.* public class GuerraEspacial { Nave jogador; Asteroide a1; Asteroide a2; Asteroide a3; //Lista<Tiro> tiros -> próximas seções GuerraEspacial(){ } void loop() { } void desenha() { } } Até este momento a única forma usada para criar objetos foi com o BlueJ. Portanto, iremos relacionar a janela de criação de um objeto do BlueJ com o código usado quando precisamos criar um objeto diretamente no código. Sobre a criação de objetos e a respectiva relação com o BlueJ o professor pode ressaltar que: • Em Java ou qualquer outra linguagem de programação, a criação de objetos é sempre feita via código. O recurso usado do BlueJ é puramente didático e ajudou a compreender o conceito; • Sempre que um novo objeto é criado, fica reservado um espaço na memória do computador para este objeto. As variáveis apenas apontam para este espaço na memória; • Durante um jogo ou outro software diversos objetos são criados e destruídos a medida que são necessários. Um editor de texto por exemplo, pode usar um objeto de uma classe Letra para cada letra digitada, um jogo, um objeto para cada tiro lançado, enfim, softwares de grande porte podem trabalhar com dezenas de milhares de objetos usando diversas classes diferentes. Vale ressaltar que projetos podem chegar na casa das centenas de classes com certa frequencia. E no construtor da GuerraEspacial, inicializam-se os objetos: 142 / 227 GuerraEspacial(){ jogador = new Nave(); a1 = new Asteroide(); a2 = new Asteroide(); a3 = new Asteroide(); } Para tornar a explicação mais clara pode-se associar a criação de objetos com a chamada do construtor da classe ao mesmo em que o professor vai preenchendo a visualização do objeto com os valores do construtor. Se este processo for repetido para a criação de algo em torno de três objetos, fica claro para os alunos que 1) ao criar um objeto, ele ganha um espaço na memória com as variáveis projetadas na classe; 2) o construtor é executado. A imagem abaixo pode ser usada para ajudar os estudantes a relembrar a sintaxe de construção de objetos, pois conforme experiência do autor, para certas regras de sintaxe, é interessante este tipo de elucidação do nome de cada elemento. Ilustração 47: Sintaxe de Construção de objetos A próxima novidade é que, assim como os objetos são criados via código, os métodos também são chamados utilizando a linguagem de programação. O recurso do BlueJ é meramente para fins didáticos, para que fosse possível aprender a escrever métodos antes de aprender a chamá-los. Para chamar métodos utiliza-se uma sintaxe conhecida como notação de ponto, 143 / 227 onde usa-se o ponto como indicador de que será acessado um elemento pertencente a um objeto. A execução dos métodos é facilmente demonstrada com a chamada dos métodos de processamento e de desenho de cada um dos objetos criados. void loop() { void desenha() { while(true){ jogador.desenha(); Alj.tela.limpa(); a1.desenha(); a2.desenha(); jogador.movimenta(); a1.movimenta(); a3.desenha(); } a2.movimenta(); a3.movimenta(); desenha(); Alj.tela.exibe(); Alj.util.espera(50); } } Pode ser ressaltado do código acima o uso do ponto para informar que um método será chamado de um objeto, o uso do método espera do Aljava para que o jogo fique em uma velocidade agradável ao olho humano e que a partir de agora, o desenvolvimento consiste em 1) pensar quais métodos são adequados para as tarefas que desejamos realizar; 2) construir estes métodos e 3) executar estes métodos. O momento de execução agora é muito interessante para os estudantes porque demonstra bastante movimento, e com frequência, somente aqui é que eles percebem como poderão construir um jogo conforme os seus interesses. Antes de avançar é sugerido a demonstração de execução do programa utilizando as representações visuais de objetos já demonstradas aqui e como a cada execução do “loop” os valores vão sendo alterados dentro dos diferentes objetos. 144 / 227 Ilustração 48: Telas da implementação parcial do jogo GuerraEspacial 6.4.3 Herança Para este exemplo, fica evidente a necessidade de fazer colisões funcionarem entre retângulos para podermos criar mais interações. O autor optou por um caminho em que os estudantes não devem ser exigidos em questões matemáticas mais complexas. Essa escolhe ocorreu por dois motivos: • Porque o foco é orientação a objetos e não matemática; • Porque este é um fator que desmotiva muitos dos estudantes antes mesmo de terem tido a oportunidade de sentir satisfação em programar. Portanto, a colisão precisa ser fornecida pelo professor. Este problema ofereceu uma excelente oportunidade para o uso de herança. Com as três classes que representam objetos possuem os mesmos quatro parâmetros (x, y, largura e altura) e todas precisarão de métodos de colisão, foi modelada uma classe “Retangulo”, que servirá de base para qualquer objeto que possa ser apresentado visualmente e precisa de colisão. Inicialmente é importante que eles conheçam a classe “Retangulo” e para tal, a classe deve ser apresentada para os estudantes conforme apresentado na seção de documentação, inclusive com exemplos e se possível, com exercícios práticos. Deve ficar muito claro antes de avançarmos para herança que a classe “Retangulo”: • Tem quatro variáveis inteiras, x, y, largura e altura; • Tem dois construtores, um vazio que inicializa tudo como zero e um que recebe 145 / 227 quatro parâmetros inteiros (x, y, largura e altura); • Possui os métodos: ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ moveX(int distancia); moveY(int distancia); alteraX(int novoX); alteraY(int novoY); alteraLargura(int novaLargura); alteraAltura(int novaAltura); toca(Retangulo outro); desenha(); Uma das formas de demonstrar o uso da classe é usar objetos da classe Retangulo para representar outros asteroides. import aljava.*; import aljava.jogo.Retangulo; public class GuerraEspacial { ... Retangulo r1; GuerraEspacial(){ ... r1 = new Retangulo(130, 200, 40, 40); } void loop() { ... r1.moveY(4); if(r1.pegaY() > 400){ r1.alteraY( Alj.util.sorteia(-400, 0) ); } ... } void desenha() { ... r1.desenha(); } } O uso da Herança pode ser apresentado agora como recurso para aumentar o 146 / 227 reuso de código. Pode-se apresentar em conjunto a operação de abstração “generalização e especialização” e o uso do termo “é um”, demonstrando que existe uma boa oportunidade de herança quando podermos dizer que um objeto de uma classe é também um objeto de outra classe, por exemplo, “um objeto da classe Asteroide também é um Retangulo”. Segue abaixo comparação da classe Asteroide com e sem herança. import aljava.*; import aljava.*; class Asteroide import aljava.jogo.Retangulo; { class Asteroide extends Retangulo int x; { int y; int largura; int velocidade; ... int altura; int velocidade; ... Tabela 14: Comparação com e sem uso da herança Neste momento, mostra-se que como a classe “Retangulo” já declara as variáveis x, y, largura e altura, estas podem ser removidas da classe Asteroide, mas mesmo assim, podem ser utilizadas nos demais métodos. Além disso, agora qualquer objeto da classe Asteroide tem disponível, além dos métodos dele, todos os métodos da classe “Retangulo”. Entre eles, o método “toca(Retangulo outro)” que será usado agora para fazer colisão dos asteroides com as naves. O método “toca(Retangulo outro)” tem dois conceitos de destaque, ele é um método com retorno e recebe como parâmetro um objeto da classe “Retangulo”. Isso pode ser demonstrado aos alunos através da própria assinatura do método. boolean toca(Retangulo outro) Agora, no lugar do termo “void” existe boolean, mostrando que sempre que um método tiver um retorno, o tipo do retorno estará no lugar de “void”, que significa apenas que o método não retorna nenhum valor. Como o método retorna um valor booleano ele pode ser usado dentro de uma estrutura condicional como “if/else”. Os métodos que verificam se algum botão do mouse ou tecla do teclado estão pressionados também são métodos que retornam valores booleanos. Já os métodos que retornam o valor do x e do y do mouse são métodos que 147 / 227 retornam valores inteiros. O segundo ponto é o uso de um objeto como parâmetro. Isto pode fortalecer o entendimento de que parâmetros também são variáveis, assim como as variáveis de objeto ou as variáveis locais, e que, toda variável tem um tipo e este tipo pode ser um dos 5 primitivos apresentados ou um objeto de alguma classe. Segue exemplo de uso do método que verifica se um asteroide colide com uma nave. Este código é adicionado dentro do “loop”, antes do método “desenha”. if( a1.toca(jogador) ){ Alj.tela.exibeMensagem("Você perdeu"); Alj.tela.finaliza(); } Seguindo o padrão pedagógico “Mistake” documentado na seção 2.4.10, solicita-se que os alunos compilem este código. Assim, eles serão apresentados ao erro: “cannot find symbol - method toca(Nave); maybe you meant: toca(Retangulo)”. Em geral, mesmo os alunos que conhecem inglês não conseguem compreender nada da mensagem, de modo que sugere-se uma explicação parte a parte: • “cannot find symbol” - Significa que foi utilizada alguma variável ou método que o Java não reconheceu como válido. Isso acontece quando erramos a digitação, o escopo ou o tipo dos parâmetros (no caso de métodos); • “method toca(Nave)” - O símbolo não encontrado foi o método “toca(Nave)”. Ele está informando que não existe nenhum método toca que recebe uma nave como parâmetro. • “maybe you meant: toca(Retangulo)” - O BlueJ já ajuda com a dica e pergunta se não estava-se querendo dizer um método toca que recebe um retangulo como parâmetro. Assim, basta perguntar se da forma como o nosso código está programado, objetos da classe nave são ou não retângulos. Após a discussão os estudantes devem corrigir o erro e colocar herança também na classe Tiro para podemos usá-la no próximo capítulo. 148 / 227 Para fortalecer o conteúdo pode ser solicitado que os estudantes criem uma classe “Bonus” e criassem objetos desta classe para que quando a nave tocasse em um deles, adquirisse maior velocidade. Devem ser adicionados pelo menos dois objetos desta classe ao Jogo para fortalecer o conteúdo. 6.4.4 Listas Os asteroides foram colocados em variáveis separadas, para se verificar a colisão de cada asteroide com a nave é necessário três “if`s” no “loop” principal. Este é o mesmo problema do desenho das “casinhas” no jogo da velha e provavelmente os estudantes sugerirão o uso de matrizes para resolver esta questão. De modo a demonstrar que matrizes são ótimas para coleções de tamanho fixo, mas não são adequadas para listas variáveis pode ser usado o exemplo de que o jogo, de tempos em tempos poderá aumentar o número de asteroides, aumentando a dificuldade. Podemos armazenar quantos objetos forem necessários em uma lista. Em Java, já existem algumas classes que implementam o conceito de listas, dentre elas “ArrayList” e “LinkedList”. Durante a aplicação da metodologia, verificou-se que o conceito de uma classe que armazena diversos objetos ficou abstrato demais e os estudantes não conseguiam compreendê-lo. Uma sugestão seria demonstrar, usando as representações visuais já citadas, como funcionam as classes “ArrayList” e “LinkedList”. Para o nosso curso usaremos a classe “ArrayList” por ser a mais comum. O uso da classe “ArrayList” segue o mesmo princípio das demais classes, podem ser criados objetos e cada objeto tem acesso a certos métodos como “add(Object o)” ou “remove(Object o)”. A única grande diferença é a sintaxe usada pelo Java para informarmos para a lista, qual o tipo dos objetos que a lista conterá. Conceitualmente é fácil explicar isso aos alunos questionando: “Uma lista de que?”. No nosso caso será uma lista de asteroides, portanto, a declaração da variável que conterá o objeto da lista e arespectiva inicialização desta variável podem ser feitas da seguinte forma: ArrayList<Asteroide> asteroides; Costrutor(){ asteroides = new ArrayList<Asteroide>(); } 149 / 227 Agora que já temos uma lista, ela esta vazia. Demonstramos a inclusão de novos asteroides dentro dela com o seguinte código: asteroides.add( new Asteroide() ); asteroides.add( new Asteroide() ); asteroides.add( new Asteroide() ); Este trecho de código precisa ser acompanhado da respectiva construção visual dada a dificuldade de abstração de termos objetos dentro de outro objeto. A representação visual usada pode ser a seguinte: Ilustração 49: Representação Visual da Lista Esta mesma representação pode ser utilizada como auxiliar para a demonstração do funcionamento da estrutura de repetição “foreach”, a forma mais fácil de se percorrer uma lista. Segue código para processar a colisão de todos os asteroides com a nave: for(Asteroide a : asteroides) { a.movimenta(); if( a.toca(jogador) ){ Alj.tela.exibeMensagem("Você perdeu"); Alj.tela.finaliza(); } } Em geral os alunos tem muita dificuldade com a variável temporária usada no “loop foreach”, é de difícil percepção o fato de que a cada rodada do “loop” aquela variável estará apontando para um objeto diferente da lista. Isso pode ser demonstrado com o 150 / 227 auxílio da representação abaixo e com a execução do tradicional “teste de mesa”. for( int i = 0; i < asteroides.size(); i++) { Asteroide a = asteroides.get( i ); a.desenha(); } Se for percebido que os estudantes continuam sem compreender pode ser usado o mesmo método utilizado com os “Arrays”, ou seja, com o for tradicional. Uma terceira opção é usar também o “loop” “while” e assim, fortalecer ao máximo que existem diversas formas de se percorrer uma coleção, sendo importante sabermos escolher qual delas iremos querer usar. Ilustração 50: Representação visual para o "loop foreach" 6.4.5 Tiros e Métodos com Retorno Agora faremos os tiros da nave. O primeiro passo é criar a lista de tiros da mesma forma que fizemos com os asteroides, sendo que a principal diferença está no fato desta lista iniciar vazia. Nota-se aqui que a lista, mesmo iniciando vazia, deve ser percorrida para que os tiros sejam processados e desenhados para que, no momento que a lista receber o primeiro objeto, ele já seja automaticamente desenhado. Havia sido modelado na classe “Nave” um método “atira”, pois atirar é uma função da nave. Porém, a ação de tirar consiste em adicionar um objeto da classe “Tiro” dentro 151 / 227 da lista, logo, esta ação precisa ser feito dentro da classe “GuerraEspacial” que é onde está a variável que contém a lista de tiros. Mesmo assim, o objeto “jogador” da classe “Nave” ainda terá um papel importante pois o tiro deve sair posicionado inicialmente em relação a posição da nave. Para a inclusão de tiros no cenário, pode ser modelado com os alunos o seguinte algoritmo: 1. Verifica quando o jogador pressionou espaço. 2. Pega o x e o y iniciais do Tiro. Inicialmente, iremos posicionar o tiro inicialmente no mesmo x e no mesmo y da nave, mais tarde ajustaremos estes valores; 3. Cria um objeto da classe Tiro; 4. Adiciona o objeto na lista. //Passo 1 if( Alj.tecla.press("espaco") ){ //Passo 2 int xTiro = jogador.pegaX(); int yTiro = jogador.pegaY(); //Passo 3 Tiro umTiro = new Tiro(xTiro, yTiro); //Passo 4 tiros.add( umTiro ); } Cada passo do algoritmo deve ser cuidadosamente explicado. Os métodos “pegaX()” e “pegaY()” devem ser apresentados como métodos que retornam valores inteiros e possuem a seguinte implementação na classe “Retangulo”: 152 / 227 int pegaX() { return x; } int pegaY() { return y; } A instrução “return” é apresentada como responsável por fazer com que um valor seja enviado de dentro do método para o local onde ele foi chamado, neste caso, para dentro das variáveis “xTiro” e “yTiro”. Na inicialização do objeto da classe “Tiro” pode ser relembrada a assinatura e o código do respectivo construtor, para melhor assimilação do uso de parâmetros. Agora, nota-se que os tiros são lançados do canto superior esquerdo da nave, obviamente. Para corrigir, é preciso fazer com que o “x” do tiro inicie no equivalente à ponta do triangulo. Para esta tarefa é sugerida a criação de um método na classe Nave, que, ao invés de simplesmente retornar o valor de “x”, retorna o valor exato de onde o tiro deve ser lançado. Este método é o primeiro método com retorno que os estudantes escrevem de modo que todas as mudanças em relação aos métodos de acesso, escritos até então, sejam clarificadas. int pegaXTiro(){ int xDoTiro = x + (largura/2); return xDoTiro; } Com isso os estudantes podem fazer com que o tiro seja lançado de qualquer posição do triângulo. E ainda mais, se desejarem, podem fazer com que a cada vez que o espaço for pressionado, dois tiros sejam incluídos no cenário, um da direita da nave e um da esquerda. Para ampliar o nível dos desafios, a nave poderia lançar tiros para lados diferentes ou ainda usar teclas do teclado distintas para lançar tiros de posições diferentes da nave. Com estes desafios os estudantes tem a oportunidade de treinar o uso de métodos com retorno e a modelagem de classes com a possibilidade dos tiros em diferentes direções. 153 / 227 6.4.6 Colisões entre listas e finalização do jogo Com os tiros no cenário, podemos trabalhar na colisão deles com os asteroides. Podem ser feitas diversas ações diferentes quando um tiro acertar um asteroide. A opção escolhida para este exemplo foi diminuir o tamanho dos asteroides até que atinjam uma largura menor do que 20, a partir de onde, não seriam mais desenhados. É criado então, um método “foiAtingido()” na classe asteroide que diminui em um, os valores da largura e da altura do asteroide. Além disso, no método desenha, se a largura for menor do que 20, o asteroide não é mais desenhado. A colisão entre asteroides e tiros envolve duas listas, cada tiro precisa verificar a colisão com cada um dos asteroides. Fazemos isso percorrendo cada tiro com um “foreach” e para cada um dos tiros, um outro “loop foreach” para percorrer cada um dos tiros. Como já existe um “loop” que processa o movimento de cada tiro, o código de colisão com os asteroides foi feito dentro deste “loop”. for(Tiro t : tiros) { t.movimenta(); for(Asteroide a : asteroides){ if(t.toca(a)){ a.foiAtingido(); } } } Mais uma vez, o teste de mesa demonstrando as variáveis “t” e “a” apontando para cada objeto durante a execução dos dois “loops” é de grande auxílio. Neste momento do código, o método “loop” da classe “GuerraEspacial” já está tomando proporções grandes e ficará cada vez mais difícil realizar alterações nele. Para melhorar o código, os alunos podem ser ensinados sobre a importância de dividir códigos grandes em unidades menores. Para este exemplo, cada bloco do método “loop” foi dividido para um método dentro da própria classe “GuerraEspacial” de modo que após esta mudança o código ficou muito mais limpo: 154 / 227 void loop() { while(true){ Alj.tela.limpa(); jogador.movimenta(); processaAsteroides(); processaTiros(); lancaTiros(); desenha(); Alj.tela.exibe(); Alj.util.espera(50); } } Ao executar o jogo, percebe-se um grande problema, os asteroides considerados “mortos”, continuam colidindo com o jogador. Pode aqui ser solicitado que os alunos tendem descobrir o que aconteceu de errado. Após descoberto o problema existem duas soluções possíveis: 1. Realizar um procedimento que a cada rodada, remove da lista de asteroides os que estão com largura menor do que 20. 2. Antes de verificar a colisão com o personagem verificar se o asteroide tem largura adequada, em caso negativo, não verifica a colisão com o jogador. Pode-se aproveitar esta escolha para mostrar o problema de performance que seria ter um grande número de asteroides que não estão sendo nem desenhados e nem verificados na colisão dentro das listas. Por exemplo, se houvessem 20 asteroides na lista, mas somente três visíveis e 100 tiros, haveria o processamento de 2000 colisões entre asteroides e tiros para rodada do jogo. Isso é muito e poderia ser facilmente diminuída para 300, se os asteroides já destruídos não estivessem na lista. Para remover os asteroides não mais utilizados o melhor algoritmo encontrado foi o que percorre a lista de traz pra frente e vai removendo os que já estiverem destruídos. Usar o “foreach” não é possível pois toda operação de inclusão ou remoção de itens em uma lista que está sendo percorrida lançarão uma exceção. Este algoritmo é complexo e não é sugerido cobrar que os alunos consigam implementá-los em uma prova por 155 / 227 exemplo. Porém, como são necessários para a performance do jogo, é importante que sejam apresentados. void removeObjetosNaoUtilizados(){ int max = asteroides.size() - 1; for(int i = max; i >= 0; i--){ Asteroide a = asteroides.get(i); if(a.estaDestruido()){ asteroides.remove(a); } } int maxTiros = tiros.size() - 1; for(int i = maxTiros; i >= 0; i--){ Tiro t = tiros.get(i); if(t.estaForaDaTela()){ tiros.remove(t); } } } Para que o algoritmo acima pudesse ser construído foram necessários mais dois métodos com retorno, um que informa se o asteroide foi destruído e outro se o tiro está fora da tela. Estes métodos tem retorno booleano e são mais uma ótima forma de exercitar o uso de métodos com retorno. A finalização do jogo depende agora de só mais uma regra, quando a lista de asteroides ficar fazia, o jogo pode ser encerrado com a vitória do jogador. if(asteroides.size() == 0){ Alj.tela.exibeMensagem("Parabéns, voce venceu"); Alj.tela.finaliza(); } Aqui fechamos o jogo conforme o exemplo, existe ainda muito potencial de funcionalidades como pontuação, contagem de tempo, mais inimigos, bônus, etc. O professor neste momento deve decidir para quais desafios direcionar a turma. 156 / 227 6.4.7 Classe ContadorTempo Sempre surgem necessidades de uso de tempo durante os jogos, como por exemplo, após a nave lançar um tiro, aguardar uma certa quantidade de milissegundos para poder lançar outro. Esse tipo de ação pode ser feito com o uso da classe “ContadorTempo”, de modo que o professor deve apresentar a documentação desta classe. 6.4.8 Pacote de Mídia Para tornar ainda maior o interesse dos estudantes, é possível usar as classes do pacote mídia para criar animações, usar imagens e executar sons. E sugerido que isso só seja explicado após os jogos erem sido iniciados usando formas geométricas básicas, para que eles percebam que por trás de cada objeto, sempre existirá uma forma geométrica (no nosso caso, um Retângulo). Este material também pode ser demonstrado via documentação. 6.4.9 Resultados Assim como o módulo um, este módulo foi aplicado a uma turma de 25 estudantes com as seguintes diferenças: • Classe java.awt.Rectangle: Na primeira aplicação, herança ainda não era vista (era assunto do módulo 4), então, para as colisões, foi usada a classe “Rectangle” da própria plataforma Java. O problema era que para verificar a colisão entre objetos, cada classe tinha que ter um método que retornava um retângulo. Assim, a colisão se torna muito complexa e foi verificado que os estudantes só conseguiam utilizá-la através de “cópia”, sem compreensão clara do seu significado; • Herança: Foi percebido durante a aplicação do módulo quatro (posterior a este) que a compreensão de herança foi bastante fácil, dessa forma, optou-se por trazêla para o módulo anterior; • Sem aprofundamento no foreach: O autor considerou o “loop foreach” tão óbvio que não se teve cuidado em demonstrar com clareza o que acontecia por trás dele, bem como não foram utilizadas outras estruturas de repetiçao para percorrer uma lista. Mais tarde, surgiam diversas duvidas dos alunos quanto a esta estrutura. Foi proposto como tarefa final do módulo a construção parcial de um jogo, que deveriam ter pelo menos uma lista e realizar interações diversas entre os objetos. 157 / 227 Ao todo foram desenvolvidos 12 produtos que chamaram atenção pela criatividade e diversidade das propostas. dos 12 trabalhos, apenas 2 não demonstraram não ter compreendido o conteúdo apresentado. Seguem alguns exemplos de trabalhos desenvolvidos. 6.4.9.1 A Ponte Este jogo apresenta dois personagens que podem ser movimentados pelo teclado lançando flechas em “zumbis”. Se os zumbis atingem os personagens, eles perdem pontos de vida. Ilustração 51: Exemplo de tela do jogo "A Ponte" Nota-se que o jogo utiliza exatamente a mesma lógica que o Guerra Espacial, como se os personagens fossem objetos da classe “Nave”, os “zumbis”, personagens da classe “Asteroide” e as flechas personagens da classe “Tiro”. Este jogo, pois, não apresenta nenhum recurso computacional novo, mas aplica com fidelidade os recursos apresentados. 6.4.9.2 Pacman Este jogo ficou apenas parcialmente desenvolvido e apresenta o personagem “Pacman” comendo as tradicionais bolinhas. 158 / 227 Ilustração 52: Tela do jogo Pacman O destaque foi o interesse do aluno em construir o cenário do Pacman. como já havia sido trabalhado o conteúdo de matrizes ele foi apresentado à técnica de uso de matrizes bidimensionais para a construção de cenários. Ao final também conseguimos adicionar parcialmente colisão do Pacman com o cenário. 6.4.9.3 Ken vs Akuma Mais um “remake” dos jogos tradicionais, este jogo simula uma batalha entre dois conhecidos jogos de StreetFighter. Ilustração 53: Tela do jogo Ryu vs Akuma Um dos destaques do jogo foi uma classe usada para verificar se o jogador pressionou uma certa sequencia de botões. Em caso positivo, o personagem lança golpes especiais. 159 / 227 6.4.9.4 Mundo de Molland Este jogo apresenta um personagem que precisa pular e fugir das “cabeçadas” de ovelhas. Ilustração 54: Tela do jogo Mundo de Molland Assim como o primeiro jogo demonstrado, este utiliza praticamente a mesma lógica da “GuerraEspacial”. Seu destaque está no fato de que todas as imagens usadas no jogo foram produzidas pelos próprios estudantes, demonstrando o potencial do método usado mesmo com alunos cujo maior foco e interesse é o artístico. 6.5 Módulo 4 – Pequeno mundo virtual O objetivo do módulo 4 é demonstrar que usando padrões de codificação simples é possível construir uma estrutura escalável para o software, ou seja, que permita o crescimento do mesmo. Esta seção deve oferecer uma visão inicial sobre arquitetura de um software. Como projeto deste módulo é produzido um pequeno mundo, chamado de “Mundo dos Quadrados” que é percorrido por um personagem em uma tela de seleção, duas fases e um “chefão”. Neste módulo também, passamos a usar um ambiente de desenvolvimento profissional como o NetBeans ou Eclipse. Para este projeto foi escolhido o ambiente NetBeans. Carga horária teórica 8 horas/aula Carga horária prática 12 horas/aula 160 / 227 Produto Final Um jogo com pelo menos 8 classes distribuídas em fases, objetos do jogo e classes auxiliares. COO Sobrescrita de método; Modificador static: contantes de classe / métodos estáticos; CP nenhum novo. CFA Classes Para manipulação do Cenário CLJ nenhum novo. Ambiente de Programação NetBeans 6.5.1 Modelando um jogo escalável Jogos tradicionalmente possuem muitos objetos e fases. Em software é comum a identificação de certos padrões. Estes padrões, quando percebidos e identificados agilizam muito o desenvolvimento de software. Já percebemos por exemplo que todo objeto que aparece na tela do jogo pode ser representado através de uma forma geométrica ou mais especificamente, de um Retângulo. Percebemos também que frequentemente estes objetos possuem um método para processar a lógica interna deles e outro para o desenho do mesmo. Dessa forma, a equipe de desenvolvimento pode acordar que todos os objetos do jogo serão uma especialização da classe “Retangulo” e terão dois métodos, um chamado “processa()” para a lógica interna e um chamado “desenha()” para, obviamente, o desenho. Da mesma forma, uma fase, conforme vimos até aqui, possui um método “loop()” para o loop central do jogo e um método “desenha()” para desenhar todos os elementos daquela fase. Além destes dois, poderíamos ter um terceiro método que direciona o jogador para alguma outra fase, chamaremos este terceiro método de “”proximaFase(int numFase)”. Apesar de ser claro que estes dois casos são evidentemente casos dignos de uso de classes abstratas (no caso dos objetos do jogo) e interfaces (no caso das fases), estes dois conceitos serão abordados no módulo 5. Primeiro, é importante perceber o conceito de um padrão, e somente depois, de como formalizá-lo com recursos de programação 161 / 227 orientada a objetos. Com estes dois padrões definidos, podemos ter uma visão da arquitetura do jogo como no modelo abaixo: Ilustração 55: Representação Visual dos padrões Com esta arquitetura em mente, pode-se partir para a construção dos objetos e das fases do jogo. É importante que os estudantes percebam que inicialmente, o importante é ter o padrão porque ele facilita tanto o entendimento dos códigos quanto aumenta a velocidade de escrita de novos códigos, fica mais claro por onde começar. Para demonstrar o uso dos padrões considera-se o jogo “A Aventura do Quadrado” a ser desenvolvido neste módulo. O jogo possui quatro telas, sendo a primeira a tela de seleção de um personagem, duas fases e uma fase final contendo um personagem “Chefão”. O jogo também conta com 4 personagens (objetos de jogo), 1) o “SenhorQ”, um quadrado que pode saltar e se mover pela tela e que precisa atravessar as fases; 2) “InimigoQuadradao”, um grande quadrado que fica parado na tela e sempre que o “SenhorQ” encosta nele, perde parte da vida; 3) “InimigoTic”, um quadrado que fica pulando sem parar, no qual o “SenhorQ” não deve tocar e 4) “SuperQuadradao”, o inimigo final do jogo que é um grande quadrado que lança tiros. O código das classes dos objetos são bastante simples e seguem os padrões já vistos nos demais módulos, o código completo de cada um dos quatro personagens pode ser visto no anexo 2. Os únicos destaques que devem ser considerados são 1) uso de sobrescrita de método nos métodos “desenha” e “processa” em alguns dos personagens 2) a chamada do construtor “Retangulo” com quatro parâmetros para informar os valores 162 / 227 “x”, “y”, ”largura” e “altura” de todos os personagens. public class SenhorQ extends RetanguloGravidade { ... int velocidade; public SenhorQ(){ super(40, 200, 32, 32); ... velocidade = 4; } ... @Override public void processa(){ processaMovimento(); super.processa(); } ... @Override public void desenha(){ Alj.cor.hexadecimal("#4512AE"); int novoY = y + (altura - vida); Alj.desenha.retangulo(x, novoY, largura, vida); super.desenha(); } } Texto 1: Novo recurso de sobrescrita de métodos e chamada de métodos da superclasse As fases seguem praticamente os mesmos conceitos trabalhados na classe “GuerraEspacial” com três exceções: • O método “proximaFase” utiliza métodos estáticos e conversão explícita para realizar a troca de fases, esta parte será explicada na seção 6.5.6; • A classe “TelaInicial” utiliza uma classe “TelaSelecao” para auxiliar a escolha de um personagem do usuário. A classe “TelaSelecao” foi produzida junto dos estudantes e está documentada no anexo 3; 163 / 227 • As classes usam objetos da classe “CenaComColisao”. Esta classe é mais complexa que as demais e deve ser explicada aos alunos em detalhes com o auxílio da documentação da seção 5 deste documento. Todos os códigos das fases estão disponíveis no anexo 4. Outro ponto agora é que se tomarmos como exemplo jogos como o tradicional “Super Mario” o número de fases e de personagens é muito alto. Para organizar um grande número de classes em projetos de grande porte, pode-se usar o recurso de “pacotes” para organizar as classes. Este recurso é importante também para que, caso sejam incluídas bibliotecas externas ao projeto, os nomes das classes não entrarão em conflito pois estão em pacotes diferentes. Ilustração 56: Organização de Pacotes do Jogo "A Aventura do Quadrado" O uso de pacotes traz a necessidade de que os métodos que devem ser públicos sejam declarados como tal. Este momento pode ser utilizado para apresentar o conceito de encapsulamento e os modificadores de acesso “public”, “protected” e “private”. 6.5.2 Ambiente de Desenvolvimento Profissional Todo o processo de demonstração das classes e da aplicação deve ser feito já dentro do NetBeans com as respectivas explicações sobre cada uma das janelas do 164 / 227 software e dos principais recursos do editor de texto como por exemplo: • Minimizar e maximizar métodos; • Autocompletar de código; • Refatoração de variáveis e nomes de classes; • Busca pelo uso de métodos e variáveis; Um exemplo deste processo é o uso da maximização e minimização de código para demonstrar e navegar em uma classe: Ilustração 57: Classe Fase1 com os métodos minimizados A medida que o código cresce, sem exceção, os alunos passam a se confundir com mais frequência e demorar muito mais tempo para localizar um código. A demonstração clara dos recursos do ambiente de desenvolvimento é essencial. Os alunos geralmente não se dedicam a explorar a ferramenta para descobrir os recursos que ela apresenta, porém, geralmente estes recursos são muito úteis. 6.5.3 Compartilhamento de Objetos e o método Main Até agora os projetos foram executados através da criação direta de objetos pelo BlueJ e a respectiva execução dos seus métodos. Os alunos devem ser infromados que 165 / 227 este recurso do BlueJ é puramente didático e que para iniciar um software orientado a objetos, é preciso construir o código que cria os objetos iniciais e executa os métodos necessários para a execução do programa. Em Java, existe um método especial para iniciar a execução do software, conhecido como “método main”. Este método pode estar dentro de qualquer classe do programa, mas para fins de organização, comumente usa-se uma classe a parte somente para este método. geralmente chamada de “Main” ou “Principal”. Neste ponto, os estudantes já tem conhecimento necessário para compreender quase que integralmente, de modo que cada palavra na sintaxe do método pode ter seu significado explicado: • public: significa que o método é público e pode ser executado de fora da classe; • static: significa que o método pertence à classe e não a um objeto dessa classe. Isso será visto brevemente no próximo capítulo. • void: significa que o método não retorna nenhum valor; • main: é o nome do método padronizado pela linguagem Java; • String[] args: O método pode receber uma matriz de Strings como parâmetro. Isto é útil apenas para programas que são executados via linha de comando. Em programas, quando se executa um programa na linha de comando, podem ser informados diversos textos como parâmetros. O jogo consiste basicamente em criar um objeto da classe TelaInicial e executar o respectivo método “loop”. Deste modo, um exemplo de método “main” inicial seria: public class Principal { public static void main(String[] args) { TelaInicial t = new TelaInicial( ); t.loop(); } } 166 / 227 Precisa-se agora, organizar a arquitetura de tal forma que todas as fases do jogo apontem para o mesmo objeto da classe “SenhorQ”. Isso é importante para que o estado do jogador se mantenha entre as fases. Se por exemplo, durante a fase 1 o jogador perdeu 20 pontos de vida, na fase 2 ele não pode iniciar com vida total. Isso aconteceria se criássemos um novo objeto “SenhorQ” dentro de cada fase. Uma abordagem que pode ser utilizada é fazer com que o “SenhorQ” seja criado antes de cada uma das fases e enviado para elas via parâmetro. SenhorQ senhorq = new SenhorQ(); Fase1 f = new Fase1( senhorq ); TelaInicial t = new TelaInicial( senhorq ); Fase2 f2 = new Fase2( senhorq ); FaseChefao c = new FaseChefao( senhorq ); Assim, cada fase tem uma variável que faz uma referência para o objeto do “SenhorQ”. É muito importante a percepção de que as variáveis de cada fase apenas “apontam” para um objeto na memória do java. Isso pode ser representado visualmente com o seguinte diagrama: Ilustração 58: Exemplo de visualização de "referência" 167 / 227 Agora, ainda temos um problema que é fazer com que uma fase possa executar o método “loop” de outra sem precisar criar um novo objeto da fase. Uma forma d e fazer isso é usando a mesma solução do caso do “SenhorQ”. O problema desta abordagem está 1) no uso de grande número de parâmetros caso uma fase tenha muitas saídas e 2) na inclusão e remoção de novas fases, pois afetariam o código do construtor de todas as fases que podem levar a ela. Uma solução alternativa é vista no próximo capítulo, tendo como novo conceito o modificador static. 6.5.4 Variáveis e Métodos Estáticos As fases precisam ser acessadas a partir de qualquer outra fase. Este problema pode ser facilmente resolvido com uso de variáveis estáticas para armazenamento das fases e seu posterior acesso. De modo a construir algo que pudesse atender ao crescimento do software e suportasse a arquitetura de fases e objetos propostas foi construída a classe “GerenciadorFases” que contém uma única variável estática do tipo “HashMap” para armazenar os objetos e dois métodos, um para inclusão e outro para recuperação de fases do “HashMap”. package aaventuradoquadrado; import java.util.HashMap; public class GerenciadorFases { public static HashMap<String, Object> fases = new HashMap<String, Object>(); public static void adicionaFase(String chave, Object fase){ fases.put(chave, fase); } public static Object pegaFase(String chave){ return fases.get(chave); } } O ensino de métodos e variáveis estáticos é, segundo opinião do autor, muito difícil, 168 / 227 pois não possui nenhuma relação com Orientação a Objetos. Não há objetos envolvidos. Funciona muito mais como uma forma de construir variáveis e métodos “globais”, semelhante ao que é feito na programação estruturada. Por isto, é dada muito pouca ênfase a este recurso, de modo que os estudantes precisam conhecê-lo para sua posterior experiência profissional, mas não precisam em nenhum momento da metodologia, escrever métodos ou variáveis estáticas. Outro ponto importante deste exemplo é o uso da classe “HashMap”, muito comum em softwares Java. Na experiência do autor, os alunos precisam de uma atenção especial para esta classe e com o conceito de “chave => valor” usado pela estrutura para armazenar objetos. Antes de avançarmos para o uso da classe deve estar claro para os estudantes que: • Métodos e variáveis estáticos são identificados com o modificador “static” imediatamente antes do tipo de retorno; • Métodos e variáveis estáticas, pertencem à classe, isso significa que a criação de objetos não afeta estes métodos e que um objeto não pode executar um método estático, somente a referência para a própria classe, é que pode executar estes métodos; • Métodos e variáveis estáticas podem estar presentes em qualquer classe; • “HashMap” é uma estrutura que pode armazenar coleções de objetos da mesma forma que a “ArrayList”. A grande diferença é que a classe “HashMap” permite que seja informado um objeto de qualquer classe para servir como “chave” de um objeto. No nosso caso usaremos como chave objetos da classe String para armazenar qualquer tipo de objeto. O primeiro passo é colocar cada objeto que representa uma fase dentro do “HashMap” da classe “GerenciadorFases” através do método “adiciona()”. Isso pode ser feito no método “main”, após a inicaalização de cada fase. 169 / 227 GerenciadorFases.adicionaFase("telaInicial", t); GerenciadorFases.adicionaFase("fase1", f); GerenciadorFases.adicionaFase("fase2", f2); GerenciadorFases.adicionaFase("chefao", c); Agora, podemos observar o método “vaiParaFase1()” dentro da classe “TelaInicial” como exemplo de como recuperar um dos objetos que representam uma fase, convertê-lo para o seu tipo (classe) correspondente e executar o método “loop()” da fase. private void vaiParaFase1() { Fase1 fase = (Fase1)GerenciadorFases.pegaFase("fase1"); fase.loop(); } Ressalta-se apenas que o método “pegaFase()” pode retornar qualquer objeto de qualquer classe. Desse modo, precisamos informar ao Java através da conversão explícita que o objeto retornado pertence à classe Fase1. Após, basta executar o método “loop()” da fase. Desse modo, encerra-se o módulo 4 demonstrando que é possível manipular as fases de muitas formas, como por exemplo, fazendo com que um usuário precise ir até uma fase específica para liberar um recurso de outra fase. O uso da arquitetura proposta permite um número infinito de fases, tendo cada fase seus próprios desafios. Como desafio do módulo, pode ser pedido que os etsudantes criem uma nova fase e um novo personagem. Com isso, terão acesso aos dois padrões usados e maior percepção de como fazer com que o jogo “cresça” usando a arquitetura definida. 6.5.5 Resultados As atividades finais deste módulo estão sendo desenvolvidas no momento da escrita deste trabalho e só estarão disponíveis a partir do dia 12 de Dezembro. Entretanto, seguem alguns dos resultados e dificuldades da metodologia observados até o momento: 170 / 227 • Passagem para o NetBeans: Pelo menos 5 estudantes de uma turma de 24 comentaram que estavam perdidos com o NetBeans e achavam mais fácil o BlueJ. A percepção do autor é que a facilidade do BlueJ está na organização espacial das classes ao invés de uma lista no canto esquerdo da tela. Outro comentário de dois alunos foi que sentiram falta dos “blocos coloridos” que o BlueJ usa para separar cada nível do código. Por exemplo, todo o bloco de um método tem uma cor, se dentro existe um bloco “if” ou “while”, este bloco contém uma cor específica. • Dificuldade com a classe “CenaComColisao”: Por ser um pouco mais complexa e existir pouca documentação disponível, vários alunos precisaram de orientação para usar a classe “CenaComColisao”, mais complexa que as demais; • Colisão com animação: Os alunos frequentemente queria saber se era possível ver se uma animação possuía colisão com outra, sem perceber que para colisões, precisariam usar uma animação como variável de uma classe “Retangulo”. A estratégia de uso de animações com Retângulos por trás para simular as colisões precisou ser explicada; • Bugs Aljava: A classe “CenaComColisao” contém a maior complexidade do Aljava pois realiza a colisão de um grande número de objetos como cenário. Esta classe apresentou alguns problemas conforme a complexidade dos trabalhos dos estudantes estão aumentando. Ficou claro que os estudantes não se importam muito se existirem limitações no uso das classes, mas é muito importante que as limitações estejam claras, como por exemplo, qual o tamanho máximo de um “Retângulo” para que seja verificada colisão deste com o cenário. O resultado conforme o trabalho dos estudantes só estará disponível a partir de 12 de dezembro, quando este documento será ampliado. 6.6 Módulo 5 – Frameworks Todas as experiências profissionais do autor deste trabalho envolveram o uso de “frameworks”, fosse para internet ou “desktop”. O desenvolvimento nos dias atuais é fortemente baseado no uso de “frameworks” e de padrões formalizados através de classes abstratas. Esta seção apresenta o conceito de “frameworks” e o respectivo uso de classes abstratas e interfaces para que o estudante se familiarize com esta realidade. Carga horária teórica 8 horas/aula 171 / 227 Carga horária prática 12 horas/aula Produto Final Um jogo com pelo menos 12 classes distribuídas em fases, objetos do jogo e classes auxiliares utilizando como base o framework oferecido. COO Interfaces; Classes abstratas; Polimorfismo: Variáveis polimórficas / Polimorfismo de método; CP Fortalecimento anteriores; CFA Classes base para o framework. CLJ Fortalecimento anteriores; Ambiente de Programação NetBeans 6.6.1 Frameworks “Frameworks” constituem uma abordagem de desenvolvimento que visa maximizar o reuso de software (Silva), portanto, uma empresa que desenvolve jogos para a internet pode ter um “framework” específico para o desenvolvimento de jogos para a internet, e o mesmo ocorre para “sites”, sistemas web, dispositivos Android, etc. Em Orientação a Objetos um “framework” consiste em em conjunto de classes que incorpora um projeto abstrato que soluciona uma família de problemas relacionados. Até agora, usamos as classes do Aljava como se fossem classes de biblioteca. Para que um conjunto de classes seja considerado um “framework” efetivamente, deve incorporar um “projeto abstrato”, ou seja, indica parcialmente como deve estar organizada a arquitetura base de um código. Foi construído no módulo quatro um padrão para o desenvolvimento de jogos com várias fases e objetos. Neste módulo iremos formalizar o padrão anterior com recursos específicos de Orientação a Objetos e usar uma classe “Motor” que se encarregará das trocas de fases e da execução do “loop” do jogo. 6.6.2 Classe Motor e Ciclo de Vida Quando trabalhamos com “frameworks”, colocamos o controle do ciclo de execução geral da aplicação aos cuidados deste “framework”. No módulo quatro, todo o código que 172 / 227 controlava o fluxo da aplicação foi construído. Isso pode ser visto nos métodos que implementam o “loop” do jogo. Cada fase tem a sua própria estrutura de repetição e o respectivo código necessário para interromper o “loop” e poder chamar uma próxima fase. Para facilitar este trabalho, foi construída uma classe “Motor” que tem como responsabilidade controlar o fluxo principal de um jogo. Isto envolve 1) Guardar as fases disponíveis (antigo papel da classe GerenciadorFases do módulo 4); 2) executar o “loop” principal do jogo e 3) Controlar o número de quadros por segundo de exibição do jogo. O fluxo de controle da classe “Motor” está contido no método “executa()” e possui a seguinte estrutura: 1. Verifica se existe uma fase atual definida 1. Em caso negativo, exibe uma mensagem de erro; 2. Executa o método “inicia()” da fase atual; 2. Inicia o “loop” do jogo; 3. Verifica se passou tempo suficiente para a execução do jogo e em caso negativo, passa para a próxima iteração do “loop” (passo 2); 4. Executa em sequencia: 1) o método “processa()” da fase atual; 2) o método “desenha()” da fase atual e 3) exibe o desenho com o método “Alj.tela.exibe()”; 5. Verifica se existe uma próxima fase: 1. Em caso positivo, executa o método “inicia()” da próxima fase, passa a próxima fase como referência para a a variável que armazena a fase atual e por fim, passa a variável que aponta para a próxima fase para “null”, de modo a prosseguir na execução do ciclo de vida do jogo; 2. Em caso negativo, apenas segue novamente para o passo dois e segue a execução do “loop” do jogo. Este ciclo de vida pode ser mostrado aos estudantes utilizando o próprio código da classe “Motor” que foi preparado justamente para que os alunos tenham algum entendimento sobre o que significa e como age uma classe que detém o controle da aplicação. 173 / 227 Esta demonstração é importante também como preparação para a explicação de classes interfaces já que o tipo das variáveis “faseAtual” e “proximaFase” são do tipo “Fase”, uma interface desenvolvida especificamente para dar suporte ao motor do jogo. public void executa() { //Passo 1 if(faseAtual == null) { Alj.tela.exibeMensagem("Fase atual não definida no Motor."); Alj.tela.finaliza(); } faseAtual.inicia(); //Passo 2 motorEmExecucao = true; while(motorEmExecucao) { //Passo 3 if(!podeExecutarProximoFrame()){ continue; } //Passo 4 faseAtual.processa(); faseAtual.desenha(); Alj.tela.exibe(); //Passo 5 if(proximaFase != null) { proximaFase.inicia(); faseAtual = proximaFase; proximaFase = null; } } } Em cursos onde os estudantes tenham também conhecimento sobre outros focos como “software desktop” ou “softwares web” pode ser dada uma breve explicação sobre como funcionam “frameworks” dessa área. 6.6.2 Classes Interfaces Conforme visto no capítulo anterior, o ciclo de vida do “framework” exige que cada 174 / 227 fase tenha três métodos distintos: 1. processa(): Que executa a lógica de processamento de um quadro da fase; 2. desenha(): Que desenha todos os objetos de um quadro da fase; 3. inicia(): Que é executado sempre antes do primeiro quadro da fase ser executado. Isso representa um problema, é preciso garantir que todas as fases tenham estes três métodos e estes três métodos devem ser reconhecidos por um tipo, afinal, as variáveis que representam fases dentro da classe “Motor” precisam de um tipo. Não é possível dizer que as variáveis “faseAtual” e “proximaFase” serão do tipo “Object” pois a classe “Object” não possui nenhum dos três métodos informados. Nem podemos dizer que são do tipo “Fase1” pois isso não garantiria que objetos da classe “Fase2” teriam os mesmos métodos. Uma das formas de resolver este problema é criando uma classe Fase com estes três métodos vazios e usando-a como superclasse para cada fase do jogo como no exemplo das ilustrações abaixo : Ilustração 60: Exemplo de sobrescrita do método inicia da classe Fase Ilustração 59: Exemplo de classe Fase com os três métodos vazios Esta técnica funciona para o problema apresentado, porém, pode levar o programador a erros, como por exemplo: 1) criar objetos da casse Fase e 2) esquecer de implementar algum dos métodos definidos na interface. Nenhum dos dois problemas apresentados resultariam em erros no software, 175 / 227 porém, existe uma estrutura mais apropriada para estes casos chamada “Interface”. Em Java, uma interface é uma especificação de um tipo na forma de um nome e um conjunto de métodos que não define nenhuma implementação para os métodos (Barnes). Isso significa que o uso de Interfaces é excelente quando é preciso definir um tipo para usar em nossos projetos, sem precisar implementar qualquer um dos métodos. Quando usamos interface deixamos claro para quem for criar objetos deste tipo que ele deverá implementar um determinado conjunto de métodos. Criar Interfaces é simples, basta usar a palavra-chave “interface” no lugar de “class” e omitir o corpo dos métodos, apenas finalizando-os com um “;”. public interface Fase { public void processa(); public void desenha(); public void inicia(); } Agora, quando uma classe quiser usar uma interface, dizemos que esta classe “implementa” uma determinada interface e não mais “estende”. Outra das vantagens de uma interface é que qualquer ferramenta computacional pode inferir que uma classe que implementa uma certa interface deve obrigatoriamente implementar os métodos definidos nesta interface, com isso, ambientes de desenvolvimento como o NetBeans auxiliam o trabalho de implementação destes métodos exibindo mensagens de erro e operações de atalho, conforme pode ser observado nas ilustrações abaixo. Ilustração 61: NetBeans sugerindo implementar os métodos abstratos Métodos sem implementação são chamados de métodos “abstratos”. Métodos com implementação são chamados de métodos “concretos”. 176 / 227 Ilustração 62: Métodos criados pelo NetBeans após clicar na sugestão de implementar os métodos abstratos Agora que já conhecemos a interface “Fase” podemos implementá-la nas fases do jogo “A Aventura do Quadrado”. É essencial que os estudantes agora percebam que a arquitetura mudou e que a responsabilidade agora pelo “loop” principal do jogo pertence ao objeto da classe “Motor”. As principais mudanças que ocorrem com a implementação da interface fase são: • método desenha passa para público ao invés de privado; • as operações executadas antes de iniciar a estrutura de repetição “while” dentro do método “loop” migram para o método “inicia()”; • o método “loop” pode ser renomeado para “processa” e a estrutura de repetição “while” pode ser removida bem como o código posterior que guia para uma próxima fase; • O método “proximaFase” pode continuar existindo normalmente e pode passar a ser chamado de qualquer lugar do método processa(). Segue abaixo um exemplo destes três métodos implementados para a fase 1 (o método desenha não foi exibido pois não sofre nenhuma alteração). 177 / 227 public void inicia(){ //Garante que a tela, nesta fase, tenha sempre este tamanho Alj.inicializa(288, 288); //Garante que o personagem //começará no início da fase q.alteraX(40); q.alteraY(40); } public void processa(){ q.processaMovimento(); q.processa(); saltitador.processa(); fase1.processa(); camera.processa(); processaColisoes(); //processaVitoria if(q.pegaX() >= 288){ Alj.tela.exibeMensagem("Você venceu. Boa sorte no próximo estágio."); proximaFase( 0 ); } //processaDerrota if(q.estaMorto()){ Alj.tela.exibeMensagem("Você perdeu. Tente de novo."); proximaFase( 1 ); } } O processo de transformação das classes do modo anterior para com a interface implementada pode ser feito em paralelo com eles realizando o mesmo processo. O professor pode neste momento ressaltar como o código novo ficou muito mais simples e falar da importância de planejar uma boa arquitetura antes de iniciar o desenvolvimento. E que justamente este é um dos motivos para o uso de “frameworks”, pois eles definem uma arquitetura geralmente testada e utilizada amplamente. 178 / 227 Antes de avançar, ressalta-se que para fortalecer a explicação, pode ser demonstrado o funcionamento da classe “MouseObserver” que implementa diversas interfaces para capturar os movimentos do mouse. Se for um objetivo do curso, neste ponto os estudantes já tem condições de serem apresentados ao padrão “observer” e ao uso de “Jframe” com classes que implementam interfaces para mouse e teclado. Com o conceito de interface explicado e as classes reorganizadas, podemos demonstrar agora com mais clareza como ficará o método “main” utilizando o “framework” e o quanto as mudanças feitas afetarão o método de troca de fases. 6.6.3 Troca de fases e o método “main” Aproveitando que neste momento estamos trabalhando nas classes das fases, vamos começar pela troca de fases. Antes fazíamos esta tarefa executando o método “loop()” de uma fase. Agora, esta responsabilidade é do “framework”, sendo necessário apenas informar qual a próxima fase. No nosso “framework”, o responsável pela lógica central do jogo é um objeto da classe “Motor” e assim como colocado no problema de compartilhamento de objetos no módulo 4, deve existir apenas um único objeto desta classe em todo o software. Uma das formas de fazermos isso seria, assim como feito com o objeto da classe “SenhorQ”, passá-lo como parâmetro para cada uma das fases. Como é somente um objeto esta seria uma solução possível. Porém, aproveitaremos este problema para demosntrar uma forma de resolvê-lo amplamente utilizada na computação e conhecido como “Design Pattern Singleton”. Um “design pattern”, ou simplesmente “padrão de projeto” é uma forma de resolver um problema de projeto, no nosso caso, projeto de software. Neste caso o problema é “ter acesso a um único objeto de uma classe em qualquer ponto da aplicação”. Este padrão usa uma variável e um método estático. A variável estática armazena o objeto e o método estático retorna este objeto sempre que executado. A implemantação deste padrão pode ser observada na classe “MotorSingleton” do aljava. 179 / 227 public class MotorSingleton { private static Motor instancia = null; public static Motor pegaInstancia() { if(instancia == null) { instancia = new Motor(); } return instancia; } } Agora, sempre que for necessário recuperar o objeto motor basta executar “MotorSingleton.pegaInstancia()”. Podemos ver isso no método “main” onde recuperamos o objeto, adicionamos todas as quatro fases, informamos a fase inicial e executamos o motor. public static void main(String[] args) { ... //Passo 1 – Recupera objeto motor Motor motorJogo = MotorSingleton.pegaInstancia(); //Passo 2 - Adiciona as fases existentes, //todas implementando a interface Fase motorJogo.adicionaFase("telaInicial", t); motorJogo.adicionaFase("fase1", f); motorJogo.adicionaFase("fase2", f2); motorJogo.adicionaFase("faseChefao", c); //Passo 3 - Define fase inicial através da chave motorJogo.defineFaseInicial("telaInicial"); //Passo 4 - Executa o motor motorJogo.executa(); } 180 / 227 Para mudar de fase, basta chamar o método “defineProximaFase(String chave)” do objeto motor informando a chave da fase que deve ser executada em seguida. Como já foi visto como recuperar um objeto da classe motor, pode ser demonstrado o código que realiza a troca de fase: Motor motorJogo = MotorSingleton.pegaInstancia(); motorJogo.defineProximaFase("fase2"); Esta é uma boa situação para demonstrar também o “uso da notação” para chamada de métodos de forma encadeada. MotorSingleton.pegaInstancia().defineProximaFase("telaInicial"); Esta última demonstração em geral causa surpresa aos estudantes e portanto, é importante demonstrar que ao executar o método “pegaInstancia()” ele retornou um a referência para um objeto da classe Fase, e que podemos tanto guardar essa referência em uma variável quanto executar imediatamente algum código do objeto para o qual a referência aponta. Com isto fechamos a transformação do “A Aventura do Quadrado” para o uso da nova arquitetura. 6.6.4 Classes Abstratas Outro padrão que foi documento foram os objetos de jogo, que continham dois métodos, um para desenhar e outro para processar a lógica interna. Este padrão também poderia ser formalizado com uma “interface”. Uma das vantagens desta formalização é que poderíamos ter em cada fase uma lista de objetos de jogo, e, para desenhar os objetos, apenas executaríamos o método desenha de cada objeto da lista. A vantagem desta abordagem é que ao adicionar novos objetos ao jogo, poderíamos somente adicioná-los na lista. Veja nas ilustrações abaixo este exemplo aplicado para a fase 2. 181 / 227 Ilustração 64: Implementação da Interface "ObjetoJogo" por um dos objetos do jogo Ilustração 63: Exemplo de uso de uma interface ObjetoJogo Repare no exemplo que uma classe pode estender uma classe e implementar uma outra interface, aliás, uma classe pode implementar quantas interfaces desejar. Esta abordagem é correta, porém, em muitos casos, como este, temos um padrão que contém métodos abstratos (que devem ser implementados pelas classes filha) mas que também poderia conter outros métodos que serviriam para todos que estendessem a classe. Com este próprio exemplo, a classe “ObjetoJogo” poderia ter métodos que informassem se o objeto está “vivo”, ou seja, se ele se mantém aparecendo na tela. Dessa forma, precisaríamos que a classe “ObjetoJogo” contivesse métodos tanto abstratos (desenha e processa) quanto concretos (estaMorto, morre). Em Orientação a Objetos isto é possível e pode ser implementado com o uso de classes abstratas. Uma classe Abstrata é uma classe que não foi concebida para criar instâncias. Seu propósito é servir como superclasse para outras classes. As classes abstratas podem conter método abstratos (Barnes) e concretos. Criar uma classe abstrata é simples e exige apenas o uso da palavra “abstract” logo antes da palavra-chave “class”. Ao definir uma classe como abstrata o efeito imediato é que nenhuma instância poderá ser criada e ela só poderá ser usada via herança. Repare na ilustração 55 que uma classe abstrata pode conter qualquer elemento de classes concretas e também métodos abstratos. Para que outra classe possa utilizar esta classe ela deverá fazer herança. O problema com isto é nossos objetos já estendem a classe “Retangulo” ou “RetanguloGravidade” e em Java, não é possível a realização de herança múltipla, ou seja, de mais de uma classe. 182 / 227 Ilustração 65: Exemplo da classe abstrata "ObjetoJogo" Esta questão pode ser resolvida dizendo que a classe “ObjetoJogo” estende a classe “Retangulo”, isto faz sentido pois como já vimos no módulo quatro, um objeto de jogo sempre pode ser representado na tela por um retângulo. Esta técnica traria o problema de que as classes que estendem “RetanguloGravidade” não poderiam ser objetos da classe “ObjetoJogo”, como “RetanguloGravidade” também é um “Retangulo”, a classe “ObjetoJogo” pode estender a classe “RetanguloJogo”. Ilustração 66: Classe abstrata "ObjetoJogo" estendendo a classe "RetanguloGravidade" Esta abordagem tem a vantagem de nos permitir adicionar diversos 183 / 227 comportamentos aos objetos do jogo, mas garantindo que a classe só será usada como superclasse. Da forma como fizemos até agora, tanto o método “processa()” da classe “RetanguloGravidade()” como o método “desenha()” da classe “Retangulo()” são perdidos. Uma forma de mantê-los é criar outros métodos que os encapsulam e os disponibilizam sobre outros nomes, como demonstrado na ilustração abaixo: Ilustração 67: Métodos que encapsulam os métodos "processa" e "desenha" da superclasse Agora, basta fazer com esta classe seja estendida pelos objetos do jogo e realizar as modificações necessárias, que consistem basicamente em chamar os dois métodos apresentados na ilustração acima quando necessário. Esta seção apresentou o uso de classes abstratas com um exemplo bastante complexo que utiliza estas classes em conjunto com herança. De modo a fortalecê-lo, veremos este conceito novamente na seção abaixo, junto com o conceito de polimorfismo. 6.6.5 Polimorfismo Polimorfismo é a capacidade de um objeto poder ser referenciado de várias formas. Isso significa que um objeto pode em um determinado momento ser referenciado como pertencente ao tipo “Retangulo” ou “ObjetoJogo”. Isso é possível porque um objeto pode, em função da hierarquia de herança e das interfaces que implementa, ser um representante válido de diversos tipos. Consideremos um objeto da classe “SenhorQ”, poderíamos armazenar objetos desta classe em variáveis do tipo: “SenhorQ”, “ObjetoJogo”, “RetanguloGravidade” e “Retangulo”. Porém, se armazenássemos um objeto desta classe em uma variável do tipo “Retangulo” poderíamos executar apenas os métodos da classe “Retangulo”, e assim com todas as demais. 184 / 227 Ilustração 68: Exemplo de Hierarquia de Herança na classe "SenhorQ" Porém, se tivermos diversas classes que fazem extensão do mesmo tipo, podemos usar este tipo para fazer uma lista de objetos desta classe. Para verificar este exemplo, usaremos a ideia de ter itens especiais no Jogo que, quando pegos pelo “SenhorQ” realizam alguma ação como aumentar o seu tamanho, recuperar a vida ou a forca do pulo. Primeiro, temos um padrão para o ítem, existe um método abstrato que deverá ser executado toda vez que um ítem colidir com o “SenhorQ”. Segue abaixo o código da classe abstrata representando ítens. 185 / 227 public abstract class Item extends ObjetoJogo { public Item(){ x = Alj.util.sorteia(100, 500); y = Alj.util.sorteia(100, 300); largura = 32; altura = 32; } public abstract void executaAcao(SenhorQ q); public void processa() { if(!estaVivo()){ ressucita(); //Sorteia uma nova posição para o objeto alteraX( Alj.util.sorteia(100, 500) ); alteraY( Alj.util.sorteia(100, 300) ); } } } E para a demonstração, criaremos dois itens, um com efeito de recuperar a vida do jogador e outro que deixa o pulo mais fraco. segue código de ambas as classes. Ilustração 69: Classe que representa um item de cura Ilustração 70: Classe que representa um item que diminui a forca do Pulo Estes itens serão incluídos na fase final. Se não pudéssemos utilizar polimorfismo teríamos que fazer uma lista para os itens de cura e uma lista separada para os itens que fazem com que o personagem perca força do pulo. Usando polimorfismo, podemos colocar ambos os tipos de itens em uma lista de 186 / 227 objetos do tipo Item e percorrer esta lista para executar os métodos de processamento, desenho que realizam uma ação quando entram em colisão com o “SenhorQ”. Observem que o método “toca” da classe “Retangulo” só aceita outros retângulos como parâmetro, portanto, nós já utilizamos polimorfismo desde o módulo três. public class FaseChefao implements Fase { ... ArrayList<Item> itens; public FaseChefao(SenhorQ senhorq){ ... itens = new ArrayList<Item>(); itens.add( new Remedio() ); itens.add( new Remedio() ); itens.add( new PerdePulo() ); itens.add( new PerdePulo() ); for(Item i : itens){ cenario.moveComCenario(i); } } ... public void processa(){ ... for(Item i : itens){ i.processa(); if(i.toca(q)){ i.executaAcao(q); } } ... } ... Conforme pode ser visto na execução da fase, os itens demonstrados aqui não são muito interessantes, de modo que este módulo pode ser encerrado com o desafio da criação de diversos itens diferentes e inclusão destes itens nas diferentes fases. 187 / 227 Ilustração 71: Exemplo da fase final contendo os itens implementados Outro potencial para o uso de polimorfismo e classes abstratas é fazer uma classe abstrata Inimigo da qual todos os inimigos estendem, e, assim como com os itens, criar listas de Inimigos nas fases. Tanto os itens quanto os inimigos abrem a possibilidade para que se crie uma classe fase que já processa e desenha todos os inimigos, diminuindo ainda mais a complexidade das fases das classes filha e demonstrando como a arquitetura do software pode ir se transformando com ele e permitindo a criação ainda mais rápida de fases e objetos mais complexos. Vale deixar claro que este é um dos maiores desafios da programação orientada a objetos, planejar arquiteturas de software escaláveis e de fácil compreensão pelos programadores, a fim de facilitar a posterior manutenção de código. O código completo de todas as classes do jogo “A Aventura do Quadrado” é muito extenso para ser disponibilizado no corpo deste documento ou mesmo nos anexos. Deste modo, foi disponibilizado para download através da URL http://goo.gl/2rlpc. 6.6.6 Resultados Este módulo foi baseado na versão do curso aplicada no primeiro semestre de 2012 e documentada no capítulo 3 deste documento. De forma geral observou-se que os estudantes compreendiam os conceitos de classes abstratas e interfaces, conseguindo bons resultados em avaliações teóricas e conseguindo utilizar com facilidade as classes do “framework”. Entretanto, tiveram muita dificuldade em identificar nos seus projetos possibilidades para a criação de novas classes abstratas e interfaces. 188 / 227 Apesar do código do projeto final deste módulo ser relativamente complexo, percebeu-se que grande parte dos estudantes conseguiram desenvolver bons projetos e replicar as técnicas utilizadas com sucesso. 6.7 Limitações da Metodologia Apresentada Alguns tópicos não foram tratados nos módulos apresentados em função da restrição de prazo para este trabalho, mas que poderiam estar perfeitamente incluídos a partir de mais exercícios dentro dos módulos existentes ou mesmo de novos módulos. As duas principais restrições foram as estruturas Enum e o Tratamento de Exceção. Ambos porém, foram trabalhados na primeira versão do curso de Orientação a Objetos baseada em jogos conforme documentada na seção 3 deste documento. A linguagem Java também teve baixa cobertura por não ser este o foco do presente trabalho. 189 / 227 7 Conclusão Nada é tão fácil a ponto de ser óbvio nem tão difícil a ponto de ser impossível. Programar usando o paradigma orientado a objetos é sem sombra de dúvida altamente complexo. Foi surpreendente perceber que questões aparentemente óbvias para o autor deste trabalho levavam dias para serem compreendidas por alguns estudantes. A capacidade de pensamento abstrato e de construção de modelos mentais, tão necessárias para a programação neste paradigma não são naturais do ser humano, ou pelo menos não são habilidades naturalmente desenvolvidas. É certo que alguns estudantes simplesmente se saem muito melhor do que outros, mas mesmo estes, precisam de orientação total quanto aos conceitos e respectivas implementações. Em nenhum momento encontrei estudantes que simplesmente conseguiam inferir o uso dos recursos. Os que se saiam melhor, em geral, praticavam mais programação fora do horário de aula. Os alunos excepcionais, tinham no mínimo, uma ou duas horas de programação por dia, por vontade própria, em casa. O apelo de que programação orientada a objetos é mais semelhante a forma como pensamos, parece ao autor deste trabalho, um equívoco. Sendo o tipo de pensamento usado nesta arte uma habilidade que precisa ser adquirida, assim como qualquer técnica. Por não ser natural é essencial que o professor instrutor dessa área não caia na tentação de querer que os estudantes aprendam simplesmente “observando” como um código funciona. É preciso ir além, construir conexões na mente do estudante dos conceitos vistos com elementos já bem estabelecidos no seu aprendizado de vida(metáfora consistente), usar representações visuais sempre que houver a explicação de questões abstratas e apresentar ao menos parcialmente como que o computador funciona para que alguns recursos pareçam minimamente mais concretos, como por exemplo, o armazenamento de objetos na memória do Java. A partir destas observações destaca-se que para um aprendizado de alta qualidade é essencial a preocupação do instrutor com 1) Motivação dos estudantes; 2) Método de 190 / 227 Ensino e 3) Elementos pedagógicos de apoio. A motivação é essencial pois a fixação das regras de sintaxe e dos conceitos de Orientação a Objetos exigem repetição. Com frequência os alunos esqueciam conceitos menos de duas semanas após os termos utilizado. Parte disso é a causa da alta exigência de carga horária prática em sala de aula com o uso desta metodologia. O estudante precisa programar o máximo possível, e para isso, ele realmente precisa estar motivado. O método se torna cada vez mais necessário na medida que o número de conceitos que são trabalhados é absolutamente alto. Foi percebido com a aplicação da metodologia que ao mesmo tempo são transmitidos aos estudantes conceitos de modelagem, de orientação a objetos, de lógica de programação e da linguagem Java. Sem um método claramente definido e documentado, é altamente provável que o professor se perca, não conseguindo realizar as conexões necessárias entre os conteúdos e muito menos a elaboração de materiais didáticos de apoio e reforço. Estes elementos pedagógicos se mostraram altamente essenciais. Por elementos pedagógicos entende-se exercícios de fixação, avaliações, tabelas de resumo dos conteúdos, atividades lúdicas para associação dos conceitos, etc. Estes elementos só podem ser preparados com qualidade se existe um método bem definido no qual eles estejam apoiados. Poderiam também, serem pensados elementos de apoio pedagógico específicos para o ensino de computação e do “pensamento computacional”. Verificou-se que o presente trabalho conseguiu atingir plenamente a questão da motivação, trazendo para o mundo da programação alunos com baixo interesse na disciplina. Quanto o método, ainda seriam necessários diversos ajustes para incluir alguns conceitos e melhorar alguns dos exemplos propostos. Porém, quanto aos elementos pedagógicos, este trabalho só os atendeu muito brevemente, fornecendo a documentação do aljava. O autor considera que seria essencial o uso de mais elementos avaliativos, assim como documentado na seção 3 e mais elementos de apoio aos estudantes como resumos e atividades extras de fixação. Para melhor detalhamento das conclusões deste trabalho são revisados os objetivos propostos inicialmente, a influência de cada uma das dez diretrizes na metodologia final e finalmente, descritos os aprendizados gerais proporcionados pela excepcional experiência de construir softwares com adolescentes. 191 / 227 7.1 Revisão dos Objetivos propostos Verifica-se que claramente o objetivo principal foi atingido, a metodologia proposta está altamente detalhada, trabalha essencialmente com conceitos de Orientação a Objetos e utiliza como elemento para os exemplos, o desenvolvimento de jogos. Agora, segue análise de cada um dos objetivos específicos. • Objetivo específico 1: Apresentar e analisar algumas das principais metodologias de ensino de programação orientada a objetos ressaltando seus pontos fortes e fracos segundo a experiência do autor; Sem dúvida a análise das cinco metodologias descritas na seção 4 atendem a este objetivo. Vale ressaltar que cada autor contribui com alguma questão fundamental do método usado, de forma resumida. Boratti é excelente no uso de representações visuais conectadas com a memória do computador para auxiliar o entendimento dos conceitos, com simplicidade, mas sem ser simplista, suas representações vão ficando marcadas na memória e ajudam a difícil meta de “enxergar os objetos”. Lembra-se que o autor do seu TCC teve o primeiro contato com esta disciplina através das aulas de Boratti e recorda até hoje diversos aprendizados obtidos. Como ponto negativo havia o início pelo método “main” que foi solucionando com o uso do BlueJ. Barnes e Köling são impecáveis ao transformar as aulas de Orientação a Objetos em verdadeiras aulas de desenvolvimento de software. A apresentação de conceitos a medida que os problemas surgem foi um dos principais guias deste trabalho, assim como o ganho de poder começar a ensinar falando de objetos, através do software BlueJ. Como ponto negativo, estava o uso excessivo do BlueJ, modificado neste método por uma passagem para o NetBeans a partir do módulo 3. Feijó é o responsável por fazer crer ser possível iniciar um curso utilizando um “framework” de apoio. Após a aplicação de sua metodologia, ficou claro que o uso de bibliotecas e “frameworks” são fatores essenciais para aumentar a motivação e quando usados com a devida atenção e orientação, não causam mais dificuldades aos programadores novatos. O ponto negativo do que trazia era apenas a falta deum método mais detalhado, também resolvido nesta proposta. Xinogalos ensinou a arte de detalhar, de revisar um método até encontrar um 192 / 227 modelo perfeito. Além disso, trouxe uma excelente organização dos ambientes de desenvolvimento e a sugestão de transição para um ambiente profissional o quanto antes. Em função de quatro anos de revisão e reformulação do seu curso, não foram encontrados pontos fracos no seu método. Por fim, Caspersen, com a metodologia mais recente de todas, fala de aprender e programar orientado ao modelo do projeto. Pensar antes, projetar antes de codificar, e a partir dos projetos, perceber os padrões que se repetem. Por fim, contribui também de forma excelente com a ideia de trazer aos estudantes métodos sistemáticos de programação. Com a documentação oferecida, também não foi possível encontrar pontos fracos. • Objetivo específico 2: Definir diretrizes para o desenvolvimento da metodologia a partir das metodologias estudadas e da experiência anterior do autor; Este objetivo também foi atendido conforme documentado na seção 4.2. Na seção posterior cada diretriz é avaliada individualmente. • Objetivo específico 3: Elencar elementos pedagógicos que auxiliem a construção da metodologia; Aqui o método sem sombra de dúvida deixou a desejar. Apesar dos “Padrões pedagógicos” terem sido amplamente utilizados, eles não são suficientes para grantir um aprendizado adequado. É essencial o uso de mais recursos, principalmente materiais de consulta, resumo e exercícios de fixação. • Objetivo Específico 4: Construir um “framework” Orientado a Objetos que atenda as principais características de jogos 2D e esteja de acordo com as diretrizes definidas; Uma das grandes questões é como construir códigos que executam algoritmos complexos e troná-los simples de usar por estudantes novatos. Acredita-se que o trabalho também atendeu a esta expectativa somente em função da experiência do autor com técnicas de “Código Limpo”, na qual o código é escrito para ser o mais legível possível por humanos. Esta técnicas não foram apresentadas no presente trabalho por limitação de escopo. • Objetivo específico 5: Documentar uma metodologia que atenda a um curso introdutório de programação orientada a objetos de 72 horas aula de modo a ser 193 / 227 facilmente replicada por qualquer professor de orientação a objetos; Até o módulo 4, são 74 horas/aula sugeridas. Muito próximo ao valor desejado inicialmente. Incluindo o módulo 5 alcança-se um total de 94 horas/aula. É importante observar que a medida que os módulos ficam mais complexos é reservado um tempo maior para carga horária prática da disciplina. Os maiores aprendizados ocorriam, durante a aplicação da metodologia, não quando os conceitos eram apresentados, mas quando os estudantes, no desejo de criarem seus próprios jogos, conseguiam colocá-los em prática. E isto, para que o estudante não desista facilmente, exige a presença constante do professor ou de colegas que já tenham compreendido os mesmos conceitos. A carga horária pode ser ajustada segundo diversos critérios. Em turmas de graduação por exemplo, o tempo de atividades práticas pode ser reduzido pois os estudantes tendem a ter maior iniciativa pessoal. Já em cursos técnicos de nível médio, sugere-se aplicar menos módulos, mas usar muito tempo de programação com orientaçào do professor. 7.2 As 10 diretrizes Na seção 4.2 são sugeridas 10 diretrizes base usadas para a metodologia. Este capítulo avalia o uso e a influência de cada uma delas. 7.2.1 Princípios baseados na prática com o BlueJ Qualquer um que conheça o método de ensino usando o BlueJ verificará imediatamente a mesma estrutura nos métodos do presente trabalho. O método guiado a problemas e realizado em espiral apresenta os conceitos somente quando eles são necessários e fazem sentido, e isto é essencial no ensino. Quando o estudante não vê sentido em algo, não há aprendizado. Ressalta-se apenas que exercícios repetitivos tradicionais, orientados à sintaxe, poderiam contribuir muito para o aprendizado pois auxiliam a fixação de certas regras de sintaxe, como o uso do “loop for”. 4.3.2 Representações visuais para todas as estruturas e conceitos trabalhados Pensar orientado a objetos não é natural, os estudantes precisam sim de 194 / 227 representações visuais para todos os novos conceitos. Quando mais consistentes e diversas as representações visuais, maiores as chances do conceito ser assimilado e gravado pelo estudante. 4.3.3 Operações de abstração e “framework” conceitual Em muitos casos onde existiam possibilidades de uso destas duas questões, elas foram muito pouco aproveitadas. Acredita-se que esta diretriz pode ser muito melhor usada, principalmente nos módulos 4 e 5 quando a modelagem dos problemas ganha complexidade. 4.3.4 Ambiente educacional + Ambiente profissional Sem dúvida esta passagem é fundamental, mas precisa ser melhor estudada e guiada. Também foi surpreendente notar a dificuldade da maioria dos estudantes com o “NetBeans” após terem usado o “BlueJ”. A representação das classes como uma simples lista os deixava bastante perdidos inicialmente. Além disso, eles não conseguem perceber as vantagens de um ambiente profissional imediatamente, precisam ser apresentados a eles. A apresentação posterior do método mais também se mostrou relativamente tranquila e ais fácil do que o processo oposto que é passar para classes após ter trabalhado inicialmente no “main”. 4.3.5 Processo sistemático de desenvolvimento O processo didático mais adotado em todo o método foi o “live coding”. Não só por permitir bastante flexibilidade, este método vai, lentamente, dando confiança aos estudantes por já terem visto, diversas vezes, como se começa a atacar um problema. O uso do processo de modelagem antes de cada atividade também foi fundamental. 4.3.6 A partir do zero. Apesar de quase sempre ao final de um métodos os estudantes serem desafiados a construir seu próprio jogo. Praticamente todos iniciavam modificando os códigos passados por exemplo. Essa diretriz não conseguiu ser plenamente atendida. Iniciar com um projeto completamente em branco se mostrou complexo para todos os estudantes. 195 / 227 4.3.7 Recursos computacionais devem ter acesso fácil O uso do framework “Aljava” atendeu este requisito plenamente. Alguns alunos se interessavam em saber como que as mesmas coisas poderiam ser feitas em Java puro. Recomenda-se que, existindo tempo para isso, seja demonstrado como realizar algumas das operações do “framework” em Java puro. 4.3.8 Estilo de código-fonte Do início ao fim da aplicação do método os estudantes ainda apresentavam códigos com má identação apesar dos diversos avisos do professor e de todos os códigos apresentados estarem bem formatados. Não foi possível identificar a causa disto. 4.3.9 Modelagens genéricas Julgando pela diversidade dos projetos apresentados, sim, esta diretriz conseguiu ser plenamente atendida. 4.3.10 Códigos e documentação na língua materna Conforme elucidado, é realmente distante o acesso ao inglês para alguns estudantes de 15 e 16 anos. Eventualmente, alguns começavam, por vontade própria a construir seus códigos em inglês. Isso sempre foi motivado e incentivado, mas a cobrança era por códigos em português. Seria necessário domínio fluente em inglês para o processo de modelagem não ficasse prejudicado. 7.3 Outros Aprendizados Além do observado nas seções anteriores, seguem alguns tópicos específicos de aprendizados obtidos durante os três semestres de desenvolvimento deste trabalho. 7.3.1 Aplicações conectadas com a realidade do educando Apesar do uso de jogos ter funcionado perfeitamente bem como elemento de motivação. Muitos estudantes perguntavam como fazer softwares “de verdade”, como sistemas para biblioteca. Outro pedido frequente era o desenvolvimento para dispositivos móveis. Isso levou a percepção de que o mais importante não eram os jogos, mas sim, que os estudantes estavam trabalhando em algo que eles conheciam, que fazia parte da sua 196 / 227 realidade. Além disso, a turma demonstrava grande satisfação em poder gerar o executável do jogo. Alguns chegaram a distribuir as suas criações para os amigos. Com isso, ressaltam-se dois fatores para a escolha do tema usado no ensino de Orientação a Objetos: • O tema deve fazer parte do cotidiano dos estudantes; • Deve ser possível desenvolver produtos completos, que efetivamente possam ser utilizados. 7.3.2 Frameworks e bibliotecas como apoio ao desenvolvimento mesmo em etapas iniciais Linguagens de programação profissionais geralmente exigem um grande número de conceitos para realizar operações simples como desenhar uma imagem na tela. Verificou-se com este trabalho que o uso de bibliotecas de métodos e classes não prejudicam em nada o aprendizado de orientação a objetos. E “frameworks” apesar de não recomendados antes que se conheçam os conceitos de lasse e herança, são excelentes em fases posteriores para aumentar a percepção da necessidade de padrões, introduzir à arquitetura de software de demais recursos avançados. 7.3.3 Modelagem é essencial Conseguir transformar ideias em classes com seus respectivos métodos e variáveis de objeto é uma tarefa de altíssima complexidade que precisa ser praticada constantemente. Iniciar qualquer desenvolvimento sem uma breve atividade de análise é, na experiência do autor, um grande equívoco que trará sem dúvida, prejuízos posteriores ao software. Ainda é preciso, porém, aumentar a qualidade do ensino de modelagem pois a maioria dos estudantes não conseguia ao final da metodologia ir além dos exemplos apresentados, e acabavam por desenvolver jogos com modelos semelhantes aos exemplos. 7.4 Trabalhos Futuros Após a identificação e elucidação de diversos pontos de metodologia, seguem algumas possibilidades de trabalho futuro, ordenadas, segundo visão do autor, das mais 197 / 227 relevantes para as menos relevantes. 7.4.1 Compreensão do Pensamento Computacional e Abstrato Foi perceptível que alguns estudantes simplesmente aprendiam muito mais rápido do que os outros e foi incrível que, para alguns estudantes, parecia existir um momento específico em que algo acontecia no seu entendimento e que a partir daquele dia, era como se o estudante houvesse compreendido algo essencial que o fazia ser melhor em todas as atividades. Não foi possível sequer perceber que fatores eram estes que pareciam despertar no estudante um entendimento superior e que abriam portas para diversos novos conceitos. Uma proposta de trabalho futuro é estudar em detalhes o processo de pensamento abstrato e quais os fatores cognitivos que influenciam tão positivamente no processo de aprendizado de programação. 7.4.2 Conexão com a pedagogia e produção de REA `s– Recursos Educacionais Abertos A partir de um método claramente definido e considerado pela sociedade de qualidade, pode-se iniciar um processo de ampliação do método para incluir outros elementos do processo de ensino-aprendizagem, e, a respectiva transformação destes recursos em recursos educacionais abertos. Outra possibilidade é que considerando o avanço dos cursos realizados a distância, que a metodologia pudesse ser transformada para um formato de replicação em formato de curso digital, inclusive nas formas de avaliação para as quais poderiam ser desenvolvidos pequenos aplicativos ou mesmo plugins para o ambiente moodle, específicos para o ensino de programação. 7.4.3 Uso abrangente da UML Uma das ideias que surgiram durante o projeto foi o uso abrangente da UML para as representações visuais das classes, objetos e suas relações. Após a finalização do trabalho foi percebido pelo autor que os diagramas UML poderiam ser utilizados em muitos contextos como forma de explicar certos conceitos aos estudantes. Isso é interessante pois não haveria necessidade por exemplo, de uma aula sobre o que é um “Diagrama de Atividades”, ele estaria presente na demonstração do ciclo de vida da 198 / 227 classe Motor, de modo que seu aprendizado seria absolutamente natural dentro do contexto de um projeto de software. Outros exemplos poderiam ser: • Diagrama de Classes: Uso mais amplo de recursos como composição e Agregação bem como o uso da sintaxe já correspondente ao definido na UML; • Diagrama de Sequência: Demonstração da troca de mensagens entre objetos no “loop” de uma determinada fase; • Diagrama de Máquina de Estados: Para explicação do uso dos métodos para modificar o estado dos objetos e programar as regras de negócio para realizar mudanças de estado consistentes. 7.4.4 Técnicas de documentação de software para estudantes novatos Existem hoje, disponíveis em formato de código aberto, um grande número de “frameworks” e bibliotecas que poderiam aumentar o potencial dos softwares desenvolvidos em fases iniciais. Estes recursos frequentemente ficam inacessíveis para jovens programadores em função da precariedade da sua documentação; Um possível trabalho seria o estudo de técnicas para documentação de bibliotecas e “frameworks” que os tornassem compreensíveis para novatos em programação. 7.4.5 Ensino de POO Avançada com Android; Muitos estudantes tinham alto interesse em desenvolver softwares para aplicativos mobile. O autor considera que este tópico seria o auge da motivação para os estudantes. Após um breve estudo do SDK Android, foi percebido que ele é inacessível em fases iniciais, mas tem grande potencial para o ensino de recursos avançados como arquitetura de software, classes abstratas e interfaces. 199 / 227 7.5 Considerações Finais O presente trabalho trouxe diversos desafios. Mas o maior de todos e também o mais recompensador é jamais desistir de ensinar. Muitos estudantes apresentavam grande dificuldade e após diversos meses conseguiam bons resultados. Muitos foram os momentos em que foi preciso repetir individualmente os conteúdos apresentados. Percebe-se que todo o esforço no sentido de clarear um conceito, de produzir uma representação visual adequada, uma metáfora consistente ou um material de resumo, é válido e auxilia muito o grupo de alunos que a princípio, não tem a habilidade natural para a programação. Neste sentido, em que todo o esforço é válido na tarefa de ensinar, acreditando sempre no potencial de cada aluno, este trabalho teve amplo sucesso. Foram pelo menos 20 jogos produzidos nos dois semestres de desenvolvimento do método e mais dezenas de classes, cada qual, com o jeito e um pouco do estilo de quem a criou. Ver ideias ganhando vida, ver que com esforço, a inteligência e o talento de cada estudante eram incorporados aos produtos desenvolvidos fez com que os três semestres de desenvolvimento do presente trabalho, valessem toda a dedicação. 200 / 227 8 Bibliografia 8.1 Livros Feijó, Bruno e Clua, Esteban. Introdução à ciência da computação com jogos: aprendendo a programar com entretenimento. Rio de Janeiro. Elsevier, 2010. BARNES, David J.; KOLLING, Michael. Programação Orientada a Objetos com Java: Uma introdução prática usando o BlueJ. São Paulo: Pearson Prentice Hall, 2004. 368 pg. KÖLLING, Michael. Introduction to Programming with Greenfoot: Object-Oriented Programming in Java With Games and Simulations. Upper Saddle River, New Jersey: Prentice Hall, 2010. 190 p. BORATTI, Isaias Camilo. Programação Orientada a Objetos com Java. Florianópolis: Visual Books, 2007. 308 p. 8.2 Artigos e Documentos acadêmicos KAY, Alan. Dr. Alan Kay on the Meaning of “Object-Oriented Programming”. Email de 23 de julho de 2003. Disponível em: <http://www.purl.org/stefan_ram/pub/doc_kay_oop_en>. Acesso em: 24 jun. 2012. PROULX, Viera K.(2010). Music in Introductory Object Oriented Programming: Music and sound library for a novice Java programmer.. Constructionism 2010. Disponível em: <http://www.ccs.neu.edu/home/vkp/Papers/Music-Constructionism2010.pdf>. Acesso em: 20 jun. 2012. PROULX, Viera K. (2009).The Pedagogy of Program Design: Design Recipe throughout BOOTSTRAP, TeachScheme!, ReachJava.. DIDINFO 2009. Disponível em: <http://www.ccs.neu.edu/home/vkp/Papers/PPD-didinfo2009.pdf>. Acesso em: 20 jun. 2012. PROULX, Viera K.(2003). Objects From the Beginning - With GUIs: Interactive GUI-based labs for introducing objects first.. ITiCSE 2003. Disponível em: <http://www.ccs.neu.edu/home/vkp/Papers/GUIsiticse2002.pdf>. Acesso em: 20 jun. 2012. CASPERSEN, M.E. e Christensen, H.B. (2008): CS1: Getting Started: Reflections on the Teaching of Programming, LNCS 4821, Springer-Verlag, 2008, pp. 130-141. BENNEDSEN, J.B. e Caspersen, M.E. (2008): Model-Driven Programming: Reflections on the Teaching of Programming, LNCS 4821, Springer-Verlag, 2008, pp. 116-129. 201 / 227 KÖLLING, Michael e ROSENBERG, John. 2001. Guidelines for teaching object orientation with Java. In Proceedings of the 6th annual conference on Innovation and technology in computer science education (ITiCSE '01). ACM, New York, NY, USA, 33-36. DOI=10.1145/377435.377461 http://doi.acm.org/10.1145/377435.377461 XINOGALOS, Stelios (2009). Guidelines for Designing and Teaching an Effective Object-Oriented Design and Programming Course, Advanced Learning, Raquel Hijn-Neira (Ed.), ISBN: 978-953-307-010-0, InTech, Disponível em: http://www.intechopen.com/books/advanced-learning/guidelines-for-designing-andteaching-an-effective-object-oriented-design-and-programming-course. Acesso em 20 de junho de 2012. M. Satratzemi, and S. Xinogalos, and V. Dagdidelis. An environment for teaching objectoriented programming: ObjectKarel. Proceedings of The 3rd IEEE International conference on Advanced Learning Technologies (ICALT 03), 342--343. THE PEDAGOGICAL PATTERNS PROJECT (Org.).Pedagogical Patterns. Disponível em: <http://www.pedagogicalpatterns.org/>. Acesso em: 24 jun. 2012. Clark, David; MacNish, Cara and Royle, Gordon F.. Java as a teaching language - opportunities, pitfalls and solutions, in Proceedings of the Third Autralasian Conference on Computer Science Education, ACM, Brisbane, Australia, July 1998. 8.3 Bibliografia para código-fonte e materiais didáticos desenvolvidos pelo autor CAMINHA, Kaléu. Exemplo 1: movimentando uma bola. Disponível em: <https://github.com/kaleucaminha/javaPlay2Examples/tree/master/src/exemplo1>. Acesso em: 24 jun. 2012. CAMINHA, Kaléu. Exemplo 2: um primeiro jogo com o framework javaPlay. Disponível em: <https://github.com/kaleucaminha/javaPlay2Examples/tree/master/src/exemplo2>. Acesso em: 24 jun. 2012. CAMINHA, Kaléu. Exemplo 3: jogo que acompanha o roteiro de 10 passos sobre o javaPlay. Disponível em: <https://github.com/kaleucaminha/javaPlay2Examples/tree/master/src/exemplo3>. Acesso em: 24 jun. 2012. CAMINHA, Kaléu. Guerra Espacial. Disponível em: <https://github.com/kaleucaminha/Guerra-Espacial>. Acesso em: 24 jun. 2012. CAMINHA, Kaléu. Exemplo Ryu: Etapa 1. Disponível em: <https://github.com/kaleucaminha/Ryu-VersusVegeta/tree/master/src/ryuversusvegeta_2>. Acesso em: 24 jun. 2012. CAMINHA, Kaléu. Exemplo Ryu 2: Etapa 2. Disponível em: <https://github.com/kaleucaminha/Ryu-VersusVegeta/tree/master/src/ryuversusvegeta_3>. Acesso em: 24 jun. 2012. CAMINHA, Kaléu. Exemplo Ryu vs Vegeta. Disponível em: <https://github.com/kaleucaminha/Ryu-VersusVegeta/tree/master/src/ryuversusvegeta_final>. Acesso em: 24 jun. 2012. Material didático 202 / 227 CAMINHA, Kaléu. JavaPlay em 10 passos: Tutorial para desenvolvimento de um jogo simples utilizando o framework javaPlay. Disponível em: <http://www.slideshare.net/kaleu/tutorial-1-javaplay-em-10-passos>. Acesso em: 24 jun. 2012. CAMINHA, Kaléu. Revisão Jogos 2D: Orientação a Objetos com Jogos 2D. Disponível em: <http://www.slideshare.net/kaleu/conceitos-de-oo-aplicados-ao-desenvolvimento-de-jogos-2d>. Acesso em: 24 jun. 2012. Provas CAMINHA, Kaléu. Prova 2a. Disponível em: <http://www.slideshare.net/kaleu/prova-2a>. Acesso em: 24 jun. 2012. CAMINHA, Kaléu. Prova 2b. Disponível em: <http://www.slideshare.net/kaleu/prova-2b-2012>. Acesso em: 24 jun. 2012. CAMINHA, Kaléu. Prova 3b. Disponível em: <http://www.slideshare.net/kaleu/prova-3a-2012>. Acesso em: 24 jun. 2012. CAMINHA, Kaléu. Prova 3b. Disponível em: <http://www.slideshare.net/kaleu/prova-3b>. Acesso em: 24 jun. 2012. CAMINHA, Kaléu. Prova 4a. Disponível em: <http://www.slideshare.net/kaleu/prova-4>. Acesso em: 24 jun. 2012. CAMINHA, Kaléu. Prova 4b. Disponível em: <http://www.slideshare.net/kaleu/prova-4b>. Acesso em: 24 jun. 2012. CAMINHA, Kaléu. Prova de Revisão. Disponível em: <http://www.slideshare.net/kaleu/prova-de-reviso>. Acesso em: 24 jun. 2012. 8.4 Bibliografia Consultada Esta seção corresponde ao conjunto de materiais que não compõe o núcleo do trabalho, mas que contribuíram, de forma menor, para a realização deste trabalho. Java ALVES, William Pereira. Java 2: Programação Multiplataforma. São Paulo: Érica, 2006. 286 p. MECENAS, Ivan. Java 2 - Fundamentos, Swing e JDBC: Programação Multiplataforma. 2. ed. Rio de Janeiro: Érica, 2006. 306 p. DEITEL, Harvey; DEITEL, Paul. Java Como Programar. 6. ed. São Paulo: Pearson Prentice Hall, 2005. 1100 p. 203 / 227 HORSTMANN, Cay S.; L., Gary Cornell. Core Java: Volume I – Fundamentos. 7. ed. Rio de Janeiro: Alta Books, 2005. 568 p. HORSTMANN, Cay S.; L., Gary Cornell. Core Java: Volume II - Recursos Avançados. São Paulo: Pearson Prentice Hall, 2003. 823 p. Qualidade de Software MARTIN, Robert C.. Código Limpo: Habilidades Práticas do Agile Software. São Paulo: Alta Books, 2009. 440 p. KOSCIANSKI, André; SOARES, Michel Dos Santos. Qualidade de Software: Aprenda as metodologias e técnicas mais modernas para o desenvolvimento de software. 2. ed. São Paulo: Novatec, 2007. 394 p. 204 / 227 9. Anexos 205 / 227 9.1 Anexo I - Classe Carro import aljava.*; class Carro { int posicaoX; int posicaoY; int angulo; String cor; double quantGasolina; boolean estaLigado; char tipoVeiculo; Carro(){ posicaoX = 20; posicaoY = 50; angulo = 0; quantGasolina = 40.00; estaLigado = false; tipoVeiculo = 'e'; cor = "vermelho"; } void andaFrente(){ posicaoX = posicaoX + 10; angulo = 0; //se andou, perde gasolina quantGasolina -= 0.1; } void andaDireita(){ posicaoY = posicaoY + 10; angulo = 90; //se andou, perde gasolina quantGasolina -= 0.1; } 206 / 227 void liga(){ estaLigado = true; } void pintaVerde(){ cor = "verde"; } void desenha(){ Alj.tela.limpa(); Alj.transforma.rotaciona(angulo, posicaoX+40, posicaoY+20); Alj.cor.nome( cor ); Alj.desenha.retangulo(posicaoX, posicaoY, 80, 40); Alj.cor.nome("preto"); Alj.desenha.oval(posicaoX + 10, posicaoY - 10, 20, 20); Alj.desenha.oval(posicaoX + 10, posicaoY + 30, 20, 20); Alj.desenha.oval(posicaoX + 50, posicaoY - 10, 20, 20); Alj.desenha.oval(posicaoX + 50, posicaoY + 30, 20, 20); Alj.transforma.limpaRotacao(); Alj.tela.exibe(); } } 9.2 Anexo II – Personagens do jogo “A Aventura do Quadrado” Este anexo apresenta as quatro classes que representam os quatro personagens do jogo “O Mundo dos Quadrados”. 9.2.1 Classe SenhorQ package aaventuradoquadrado.personagens; import aljava.Alj; import aljava.jogo.RetanguloGravidade; /** * O SenhorQ é o nosso herói. * Na verdade ele era um ser humano normal, mas que de tanto ficar na frente do computador, 207 / 227 * foi absorvido por um aplicativo do Facebook que o aprisionou em um mundo de duas dimensões. * * Para ser capaz de voltar ele precisará * manter sua identidade sem tocar em nenhum outro quadrado. * * Ao final, ainda precisará eliminar o super quadradão.. * para só então, poder voltar para a sua vida normal. */ public class SenhorQ extends RetanguloGravidade { int vida; int forcaPulo; int velocidade; public SenhorQ(){ super(40, 200, 32, 32); vida = 32; forcaPulo = 20; velocidade = 4; } public void alteraVida(int novaVida){ vida = novaVida; } public void alteraForcaPulo(int novaForca){ forcaPulo = novaForca; } public void alteraVelocidade(int novaVelocidade){ velocidade = novaVelocidade; } @Override public void processa(){ processaMovimento(); super.processa(); } public void processaMovimento(){ if(Alj.tecla.press("direita")){ 208 / 227 moveX(+velocidade); } if(Alj.tecla.press("esquerda")){ moveX(-velocidade); } if(Alj.tecla.press("espaco")){ pula(forcaPulo); } } public void perdeVida(){ vida -= 3; } public boolean estaMorto(){ return vida <= 0; } @Override public void desenha(){ Alj.cor.hexadecimal("#4512AE"); //O y também muda porque o efeito deve ser do retangulo sendo consumido int novoY = y + (altura - vida); Alj.desenha.retangulo(x, novoY, largura, vida); super.desenha(); } } 209 / 227 9.2.2 Classe InimigoQuadradao package aaventuradoquadrado.personagens; import aljava.jogo.Retangulo; /** * Inimigo besta. * É só um bloco parado. * O único porém é que ele é grande, o pulo precisa ser preciso para se manter vivo. */ public class InimigoQuadradao extends Retangulo { public InimigoQuadradao(int x, int y){ super(x, y, 60, 60); } } 9.2.3 Classe InimigoTic package aaventuradoquadrado.personagens; import aljava.jogo.RetanguloGravidade; /** * Inimigo maluco que pula sem parar */ public class InimigoTic extends RetanguloGravidade { public InimigoTic(int x, int y){ super(x, y, 32, 32); } @Override public void processa(){ super.processa(); pula(16); } } 210 / 227 9.2.4 Classe SuperQuadrado package aaventuradoquadrado.personagens; import aljava.Alj; import aljava.jogo.RetanguloGravidade; public class SuperQuadrado extends RetanguloGravidade { //-1 = esquerda e 1 = direita int direcaoMovimento = 1; int velocidade = 3; int vida = 100; int passos; public SuperQuadrado(){ super(400, 200, 100, 100); passos = 0; } /** * Este método contém o comportamento do chefão. * Muitas coisas podem ser feitas aqui. * Por hora, ele caminhará de um lado ao outro do cenário. */ public void inteligenciaArtificial(){ moveX(velocidade * direcaoMovimento); passos++; if(passos > 140){ direcaoMovimento *= -1; passos = 0; } } public int pegaDirecao(){ return direcaoMovimento; } public void perdeVida() { 211 / 227 vida -= 2; } public boolean estaMorto(){ return vida <= 0; } @Override public void desenha(){ Alj.cor.hexadecimal("#dd4444"); //O y também muda porque o efeito deve ser do retangulo sendo consumido int novoY = y + (altura - vida); Alj.desenha.retangulo(x, novoY, largura, vida); super.desenha(); } } 9.3 Anexo III – Classe “TelaSelecao” A Classe “TelaSelecao” foi desenvolvida em sala com os estudantes de modo a estimulá-los a pensar e construir classes que possam ser reusadas em projetos diferentes. Deste modo, a intenção da classe é ter baixo acoplamento com o projeto. Segue abaixo o código da classe: package aaventuradoquadrado; import aljava.*; import aljava.midia.Imagem; import aljava.util.ContadorTempo; public class TelaSelecao { Imagem matriz[]; int selecionado; ContadorTempo contador; int x; int y; int numItensPorLinha; 212 / 227 int espacamento; int larguraImg; int alturaImg; public TelaSelecao(int _x, int _y, Imagem _matriz[]){ numItensPorLinha = 4; x = _x; y = _y; matriz = _matriz; espacamento = 20; larguraImg = matriz[0].pegaLargura(); alturaImg = matriz[0].pegaAltura(); contador = new ContadorTempo(200); selecionado = 2; } public int pegaSelecionado(){ return selecionado; } public void mudaItensPorLinha(int numItens){ numItensPorLinha = numItens; } public void processa(){ if(!contador.terminou()){ return; } if(Alj.tecla.press("direita")){ selecionado++; contador.reinicia(); } if(Alj.tecla.press("baixo")){ selecionado += numItensPorLinha; contador.reinicia(); } 213 / 227 if(Alj.tecla.press("cima")){ selecionado -= numItensPorLinha; contador.reinicia(); } if(Alj.tecla.press("esquerda")){ selecionado--; contador.reinicia(); } if(selecionado > matriz.length-1) { selecionado = 0; } if(selecionado < 0) { selecionado = matriz.length-1; } } public void desenha(){ for(int i =0; i < matriz.length; i++){ int linha = ( i / numItensPorLinha ); int coluna = (i % numItensPorLinha); int xCalculado = (coluna * (larguraImg+espacamento) ); xCalculado += x; int yCalculado = (linha * (alturaImg+espacamento) ); yCalculado += y; if(selecionado == i){ Alj.cor.nome("verde"); Alj.desenha.retangulo(xCalculado-5, yCalculado-5, larguraImg+10, alturaImg+10); } matriz[i].desenha( xCalculado , yCalculado); } } } 214 / 227 9.4 Anexo IV – Fases do jogo “A Aventura do Quadrado” Este anexo apresenta as classes utilizadas como fases no jogo desenvolvido durante o módulo 4. 9.4.1 Classe TelaInicial package aaventuradoquadrado.fases; import aaventuradoquadrado.GerenciadorFases; import aaventuradoquadrado.TelaSelecao; import aaventuradoquadrado.personagens.SenhorQ; import aljava.Alj; import aljava.midia.Imagem; public class TelaInicial { SenhorQ q; TelaSelecao telaSelecao; public TelaInicial(SenhorQ _q){ q = _q; Imagem[] imagens = new Imagem[2]; imagens[0] = new Imagem("recursos/img/q7.png"); imagens[1] = new Imagem("recursos/img/q8.png"); telaSelecao = new TelaSelecao(30, 100, imagens); } public void loop() { Alj.inicializa(288, 288); int proximoPasso = 0; boolean loop = true; while(loop){ telaSelecao.processa(); if(Alj.tecla.press("enter")){ selecionaPersonagem( telaSelecao.pegaSelecionado() ); 215 / 227 proximoPasso = 1; loop = false; } desenha(); } proximaFase(proximoPasso); } public void proximaFase(int f){ if(f == 0){ Alj.tela.finaliza(); } else if(f == 1){ vaiParaFase1(); } } public void selecionaPersonagem(int num){ if(num == 0){ q.alteraForcaPulo(17); q.alteraVelocidade(7); q.alteraVida(27); q.alteraAltura(27); q.alteraLargura(27); } if(num == 1){ q.alteraForcaPulo(20); q.alteraVelocidade(4); q.alteraVida(32); q.alteraAltura(32); q.alteraLargura(32); } } public void desenha(){ Alj.cor.nome("preto"); Alj.desenha.retangulo(0, 0, 288, 288); Alj.cor.nome("branco"); 216 / 227 Alj.desenha.texto(30, 50, "Escolha o personagem", 22); telaSelecao.desenha(); Alj.tela.exibe(); } private void vaiParaFase1() { Fase1 fase = (Fase1)GerenciadorFases.pegaFase("fase1"); fase.loop(); } } 9.4.2 Classe Fase1 package aaventuradoquadrado.fases; import aaventuradoquadrado.GerenciadorFases; import aaventuradoquadrado.personagens.InimigoQuadradao; import aaventuradoquadrado.personagens.InimigoTic; import aaventuradoquadrado.personagens.SenhorQ; import aljava.Alj; import aljava.jogo.Camera; import aljava.jogo.CenaComColisao; public class Fase1 { Camera camera; SenhorQ q; CenaComColisao fase1; InimigoQuadradao umQuadradao; InimigoTic saltitador; public Fase1(SenhorQ senhorq){ q = senhorq; fase1 = new CenaComColisao("recursos/cena/fase1.txt"); //Aqui configura cada bloco do cenário fase1.configTile(1, "recursos/img/terra.png"); //O terceiro parâmetro informa se o bloco é sólido. 217 / 227 fase1.configTile(2, "recursos/img/fundo.jpg", true); fase1.tamanhoTiles(32, 32); camera = new Camera(fase1, q); //Inicializa inimigos umQuadradao = new InimigoQuadradao(350, 200); saltitador = new InimigoTic(200, 200); //~étodo para garantir colis"ao com o cenário fase1.adicionaObjeto(q); fase1.adicionaObjeto(umQuadradao); fase1.adicionaObjeto(saltitador); fase1.moveComCenario(umQuadradao); fase1.moveComCenario(saltitador); } public void loop(){ Alj.inicializa(288, 288); q.alteraX(40); q.alteraY(40); int proximoPasso = 0; boolean loop = true; while(loop){ q.processaMovimento(); q.processa(); saltitador.processa(); fase1.processa(); camera.processa(); processaColisoes(); //processaVitoria if(q.pegaX() >= 288){ loop = false; Alj.tela.exibeMensagem("Você venceu. Boa sorte no próximo estágio."); proximoPasso = 2; } 218 / 227 //processaDerrota if(q.estaMorto()){ Alj.tela.exibeMensagem("Você perdeu. Tente de novo."); loop = false; proximoPasso = 1; } desenha(); Alj.util.espera(20); } proximaFase( proximoPasso ); } private void desenha(){ fase1.desenha(); umQuadradao.desenha(); saltitador.desenha(); q.desenha(); Alj.tela.exibe(); } private void proximaFase(int f){ if(f == 0){ Alj.tela.finaliza(); } else if(f == 1){ TelaInicial fase = (TelaInicial)GerenciadorFases.pegaFase("telaInicial"); fase.loop(); } else if(f == 2){ Fase2 fase = (Fase2)GerenciadorFases.pegaFase("fase2"); fase.loop(); } } private void processaColisoes() { if(q.toca(umQuadradao)){ q.perdeVida(); 219 / 227 fase1.moveHorizontal(1000); fase1.processa(); q.alteraX(40); } if(q.toca(saltitador)){ q.perdeVida(); fase1.moveHorizontal(1000); fase1.processa(); q.alteraX(40); } } } 9.4.3 Classe Fase2 package aaventuradoquadrado.fases; import aaventuradoquadrado.GerenciadorFases; import aaventuradoquadrado.personagens.InimigoQuadradao; import aaventuradoquadrado.personagens.InimigoTic; import aaventuradoquadrado.personagens.SenhorQ; import aljava.Alj; import aljava.jogo.Camera; import aljava.jogo.CenaComColisao; public class Fase2 { Camera camera; SenhorQ q; CenaComColisao fase2; InimigoTic saltitador; InimigoTic saltitador2; public Fase2(SenhorQ senhorq){ q = senhorq; fase2 = new CenaComColisao("recursos/cena/fase2.txt"); //Aqui configura cada bloco do cenário fase2.configTile(1, "recursos/img/terra.png"); 220 / 227 //O terceiro parâmetro informa se o bloco é sólido. fase2.configTile(2, "recursos/img/fundo.jpg", true); //opa, parede invisível fase2.configTile(3, "recursos/img/fundo.jpg", false); fase2.tamanhoTiles(32, 32); camera = new Camera(fase2, q); //Inicializa inimigos saltitador = new InimigoTic(200, 200); saltitador2 = new InimigoTic(32*7, 32*4); fase2.adicionaObjeto(q); fase2.adicionaObjeto(saltitador); fase2.adicionaObjeto(saltitador2); fase2.moveComCenario(saltitador); fase2.moveComCenario(saltitador2); } public void loop(){ Alj.inicializa(288, 288); q.alteraX(40); q.alteraY(40); int proximoPasso = 0; boolean loop = true; while(loop){ q.processaMovimento(); q.processa(); saltitador.processa(); saltitador2.processa(); fase2.processa(); camera.processa(); processaColisoes(); //processaVitoria if(q.pegaX() >= 288){ loop = false; proximoPasso = 2; } 221 / 227 //processaDerrota if(q.pegaY() >= 288){ Alj.tela.exibeMensagem("Caiu no buraco. Até mais."); loop = false; proximoPasso = 1; } if(q.estaMorto()){ Alj.tela.exibeMensagem("Você perdeu. Tente de novo."); loop = false; proximoPasso = 1; } desenha(); Alj.util.espera(20); } proximaFase(proximoPasso); } private void desenha(){ fase2.desenha(); saltitador.desenha(); saltitador2.desenha(); q.desenha(); Alj.tela.exibe(); } private void proximaFase(int f){ if(f == 0){ Alj.tela.finaliza(); } else if(f == 1){ TelaInicial fase = (TelaInicial)GerenciadorFases.pegaFase("telaInicial"); fase.loop(); } else if(f == 2){ Alj.tela.exibeMensagem("Parabéns, você venceu denovo. Agora é o grande desafio."); 222 / 227 FaseChefao fase = (FaseChefao)GerenciadorFases.pegaFase("chefao"); fase.loop(); } } private void processaColisoes() { if(q.toca(saltitador)){ q.perdeVida(); fase2.moveHorizontal(1000); fase2.processa(); q.alteraX(40); } if(q.toca(saltitador2)){ q.perdeVida(); fase2.moveHorizontal(1000); fase2.processa(); q.alteraX(40); } } } 9.4.4 Classe FaseChefao package aaventuradoquadrado.fases; import aaventuradoquadrado.GerenciadorFases; import aaventuradoquadrado.ataques.Tiro; import aaventuradoquadrado.personagens.SenhorQ; import aaventuradoquadrado.personagens.SuperQuadrado; import aljava.Alj; import aljava.jogo.Camera; import aljava.jogo.CenaComColisao; import aljava.util.ContadorTempo; import java.util.ArrayList; public class FaseChefao { SuperQuadrado sq; SenhorQ q; CenaComColisao cenario; 223 / 227 Camera camera; ArrayList<Tiro> tiros; ContadorTempo contador; public FaseChefao(SenhorQ senhorq){ q = senhorq; sq = new SuperQuadrado(); cenario = new CenaComColisao("recursos/cena/chefao.txt"); cenario.configTile(1, "recursos/img/terra.png"); //O terceiro parâmetro informa se o bloco é sólido. cenario.configTile(2, "recursos/img/fundo.jpg", true); cenario.tamanhoTiles(32, 32); cenario.adicionaObjeto(sq); cenario.adicionaObjeto(q); cenario.moveComCenario(sq); camera = new Camera(cenario, q); tiros = new ArrayList<Tiro>(); //Lançará um tiro por segundo. contador = new ContadorTempo(1200); } public void loop(){ Alj.inicializa(600, 288); q.alteraX(40); q.alteraY(40); int proximoPasso = 0; boolean loop = true; while(loop){ q.processaMovimento(); q.processa(); sq.processa(); for(Tiro t : tiros){ 224 / 227 t.processa(); } sq.inteligenciaArtificial(); cenario.processa(); camera.processa(); desenha(); processaColisao(); lancaTiros(); if(q.estaMorto()){ proximoPasso = 0; loop = false; } if(sq.estaMorto()){ proximoPasso = 1; loop = false; } Alj.util.espera(20); } proximaFase(proximoPasso); } private void desenha(){ cenario.desenha(); q.desenha(); sq.desenha(); for(Tiro t : tiros){ t.desenha(); } Alj.tela.exibe(); } private void proximaFase(int f){ 225 / 227 if(f == 0){ Alj.tela.exibeMensagem("Você perdeu, tente novamente com outro personagem."); TelaInicial t = (TelaInicial)GerenciadorFases.pegaFase("telaInicial"); t.loop(); } else if(f == 1){ Alj.tela.exibeMensagem("Você venceu. Agora poderá voltar para casa."); Alj.tela.exibeMensagem("Ops...a requisição foi redirecionada pelo sistema..."); Alj.tela.exibeMensagem("Ah não...você está sendo redirecionado para uma nova missão antes de ser liberado..."); Alj.tela.exibeMensagem("Para poder ir embora você precisará passar ainda por mais um mundo.."); Alj.tela.exibeMensagem("O MUNDO DOS CÍRCULOS..."); Alj.tela.exibeMensagem("Em breve, uma nova aventura para o SenhorQ. Aguarde"); Alj.tela.finaliza(); } } private void processaColisao() { if(q.toca(sq)){ q.perdeVida(); int direcao = sq.pegaDirecao(); if(direcao == -1){ cenario.moveHorizontal(-1000); q.alteraX(550); } else { cenario.moveHorizontal(1000); q.alteraX(60); } } for(Tiro t : tiros){ if(t.toca(q)){ q.perdeVida(); tiros.remove(t); break; } } 226 / 227 for(Tiro t : tiros){ if(t.toca(sq)){ sq.perdeVida(); tiros.remove(t); break; } } for(Tiro t : tiros){ if(cenario.temColisaoComTile(t, 2)){ t.inverteDirecao(); } } } private void lancaTiros() { if(contador.terminou()){ contador.reinicia(); int yTiro = Alj.util.sorteia(sq.pegaY(), sq.pegaY() +sq.pegaAltura()); int xTiro; int velocidadeTiro; //Lança os tiros na direção do personagem if(q.pegaX() < sq.pegaX()){ xTiro = sq.pegaX(); velocidadeTiro = -9; } else { xTiro = sq.pegaX() + sq.pegaLargura(); velocidadeTiro = 9; } Tiro t = new Tiro( xTiro, yTiro, velocidadeTiro); cenario.moveComCenario(t); tiros.add(t); } } } 227 / 227