• Nenhum resultado encontrado

Tecnologias de Jogos de Vídeo

N/A
N/A
Protected

Academic year: 2021

Share "Tecnologias de Jogos de Vídeo"

Copied!
13
0
0

Texto

(1)

Tecnologias de Jogos de

Vídeo

Abel J. P. Gomes & Gonçalo Amador

LAB. 5

Departamento de Informática

Universidade da Beira Interior

Portugal

2012

(2)

LAB. 5

GESTÃO DE CENAS

1. Objectivos

2. Conceitos

(3)

Lab. 5

GESTÃO DE CENAS 3D

Nesta lição prático-laboratorial aprender-se-á como manipular e gerir o grafo de cenas 3D do sub-motor

gráfico (graphics engine) do motor de jogos GameProject.

1.

Objectivos específicos de aprendizagem

Terminada esta ficha de trabalho, o aluno deve saber e ser capaz de:

1. Adicionar ou remover objectos do grafo de cena, i.e., jogadores, obstáculos, etc.

2. Alterar objectos que fazem parte do grafo de cena, i.e., o chão e a caixa delimitadora do

mundo.

3. Adicionar objectos distintos com texturas distintas.

2.

Grafo de Cena

O grafo de cena do motor gráfico do GameProject é uma árvore de dois níveis, como se ilustra na Fig.1:

(4)

Na raiz da árvore da Fig. 1 encontra-se a cena que corresponde à classe scene.java. A cena contém um

nó de transformação (TransformNode) referente a ela própria. Os jogadores, obstáculos e as balas

presentes na cena estão armazenados numa tabela de hash e em duas listas de nós de transformação,

respectivamente. Cada nó de transformação da cena, com exceção do nó que representa a própria cena,

tem associada uma malha de triângulos ou, mais geralmente, uma malha de polígonos. A cena tem ainda

associados dois nós de transformação individuais, um para a caixa que representa os limites da cena

(SkyBox) e um nó que representa o chão da cena (Floor).

Diga-se desde já que cada jogador, cada obstáculo e cada bala presente na cena têm associada uma

“bounding box”. No entanto, o conceito de “bounding box” será abordado mais tarde com a profundidade

devida numa outra ficha, mais concretamente aquela referente à temática de deteção de colisões.

Na Fig. 2, como se assinala com uma elipse vermelha, estão indicadas as classes que dizem respeito ao

grafo de cena, que aparece incluído no módulo referente à geometria.

3.

Texturas e COLLADA

Como se mostra na Fig. 3, todas as texturas do mundo ou dos modelos geométricos encontram-se na

diretoria data.

Mais propriamente, os modelos geométricos (no formato Collada 1.4.1) estão guardados na sub-diretoria

models. Tenha-se em atenção que estes modelos foram previamente submetidos a um processo de

desindexação, através de uma ferramenta disponível em:

https://collada.org/mediawiki/index.php/COLLADA_Refinery

Todos os modelos desindexados têm um nome de ficheiro que termina em “_out.dae”.

Note-se que os modelos texturizados que não passaram por este processo de desindexação não serão

desenhados corretamente, ou, para ser mais preciso, as suas texturas não serão desenhadas

corretamente.

Note-se ainda que todas as texturas que tenham a palavra “world” nos seus nomes são tidas como tendo

nomes fixos, pelo que se os nomes forem alterados no código fonte do motor, então os nomes dos

ficheiros respectivos terão também de ser alterados.

(5)
(6)

Figure 3: Modelos e texturas do Tie Beam’Em.

4.

Exercícios de programação

Exercício 1

:

Na versão disponibilizada do projeto não é possível carregar dois modelos, e.g. um Tie Fighter e um

X-Wing com texturas distintas. A razão para tal comportamento prende-se com o facto de que o código

apenas permite que uma textura seja definida para qualquer objeto do tipo jogador/avatar. Esta textura é

definida como variável de sistema no construtor da classe principal do jogo, GameRenderer.java, após

definida o construtor da cena vai utilizar o valor que tiver atribuído para apenas permitir uma textura para

todos os modelos carregados. De notar que as texturas dos limites da cena são carregadas à parte, na

classe Scene.java. Vamos alterar o código para que seja possível.

(7)

0. try {

1. if ("true".equals(System.getProperty("gridgameproject.tie"))) {

2. texture = TextureIO.newTexture(Scene.class.getResourceAsStream("/data/ 3. textures/tie/tieskin.jpg"), true, TextureIO.JPG);

4. }

5.

6. if ("true".equals(System.getProperty("gridgameproject.xwing"))) {

7. texture = TextureIO.newTexture(Scene.class.getResourceAsStream("/data/ 8. textures/xwing/xwingskin.jpg"), true, TextureIO.JPG); 9. }

10.

11. if ("true".equals(System.getProperty("gridgameproject.cow"))) {

12. texture = TextureIO.newTexture(Scene.class.getResourceAsStream("/data/ 13. textures/cow/Cow.png"), true, TextureIO.PNG); 14. }

15.

16. if ("true".equals(System.getProperty("gridgameproject.duck"))) {

17. texture = TextureIO.newTexture(Scene.class.getResourceAsStream("/data/ 18. textures/duck/duckCM.jpg"), true, TextureIO.JPG); 19. }

20. }

21. catch (IOException e) {

22. throw new RuntimeException(e);

23. //e.printStackTrace();

24. }

De seguida, altere-se o seguinte método, designado por

loadModel

, entre as linhas 28 e 55:

25. @SuppressWarnings({"unchecked"})

26. public static TransformGroup loadModel(String modelName, String id) {

27. . . . (código intermédio)

28. for (CommonNewparamType commonNewparamType : commonNewparamTypes) { 29. if (commonNewparamType.getSurface() != null) { 30. geometryMesh.setTexture(texture); 31. } 32. } 33. } 34. 35. scale = 1; 36. 37. //if (modelName.contains("cow")) { 38. if ("true".equals(System.getProperty("gridgameproject.cow"))) { 39. scale = 1.1f; 40. } 41. 42. //if (modelName.contains("duck")) { 43. if ("true".equals(System.getProperty("gridgameproject.duck"))) { 44. scale = 250; 45. } 46. 47. //if (modelName.contains("tie")) { 48. if ("true".equals(System.getProperty("gridgameproject.tie"))) { 49. scale = 70; 50. } 51.

(8)

52. //if (modelName.contains("xwing")) { 53. if ("true".equals(System.getProperty("gridgameproject.xwing"))) { 54. scale = 3300; 55. } 56. . . . (código intermédio) 57. }

substituindo-o pelo seguinte código (sem alterar o restante código do método):

@SuppressWarnings({"unchecked"})

public static TransformGroup loadModel(String modelName, String id) {

. . . (código intermédio)

for (CommonNewparamType commonNewparamType : commonNewparamTypes) { if (commonNewparamType.getSurface() != null) {

try {

if (modelName.contains("cow")) {

texture = TextureIO.newTexture(Scene.class.getResourceAsStream("/data/ textures/cow/Cow.png"), true, TextureIO.PNG);

}

if (modelName.contains("duck")) {

texture = TextureIO.newTexture(Scene.class.getResourceAsStream("/data/ textures/duck/duckCM.jpg"), true, TextureIO.JPG);

}

if (modelName.contains("tie")) {

texture = TextureIO.newTexture(Scene.class.getResourceAsStream("/data/ textures/tie/tieskin.jpg"), true, TextureIO.JPG);

}

if (modelName.contains("xwing")) {

texture = TextureIO.newTexture(Scene.class.getResourceAsStream("/data/ textures/xwing/xwingskin.jpg"), true, TextureIO.JPG); }

}

catch (IOException e) {

throw new RuntimeException(e);

//e.printStackTrace(); } geometryMesh.setTexture(texture); } } } scale = 1; if (modelName.contains("cow")) { scale = 1.1f; } if (modelName.contains("duck")) { scale = 250; } if (modelName.contains("tie")) { scale = 70;

(9)

} if (modelName.contains("xwing")) { scale = 3300; } . . . (código intermédio) }

Por fim, falta realizar algumas alterações na classe principal do jogo: GameRenderer.java.

No método main, comente-se a seguinte porção de código:

58. if (spaceGame) { 59. //System.setProperty("gridgameproject.xwing","true"); 60. System.setProperty("gameproject.tie","true"); 61. } 62. 63. if (stupidGame) { 64. //System.setProperty("gridgameproject.duck","true"); 65. System.setProperty(gameproject.cow","true"); 66. }

Se não pretender visualizar as “bounding boxes”, comente também:

67. System.setProperty("gameproject.drawbb","true");

De seguida, vamos alterar o método initGame e substituir o seguinte código:

68. TransformGroup playerRotate = null; 69. if (spaceGame) {

70. if ("true".equals(System.getProperty("gameProject.xwing"))) {

71. //playerRotate = Scene.loadModel("/data/models/xwing.dae", playerId);

72. playerRotate = Scene.loadModel("/data/models/xwing_out.dae",playerId); 73. playerRotate.setRotationY(new Point3f( 0.0f, 180.0f, 0.0f)); 74. } 75. if ("true".equals(System.getProperty("gameProject.tie"))) { 76. //playerRotate = Scene.loadModel("/data/models/tiefighter.dae", 77. // playerId); 78. playerRotate = Scene.loadModel("/data/models/tiefighter_out.dae", 79. playerId); 80. } 81. } 82. else 83. if (stupidGame) { 84. if ("true".equals(System.getProperty("gameProject.duck"))) { 85. //playerRotate = Scene.loadModel("/data/models/duck_triangulate.dae", 86. // playerId); 87. playerRotate = Scene.loadModel("/data/models/ 88. duck_triangulate_out.dae", playerId); 89. playerRotate.setRotationY(new Point3f( 0.0f, -90.0f, 0.0f)); 90. } 91. if ("true".equals(System.getProperty("gameProject.cow"))) {

92. //playerRotate = Scene.loadModel("/data/models/cow.dae", playerId);

93. playerRotate = Scene.loadModel("/data/models/cow_out.dae", 94. playerId);

95. playerRotate.setRotationZ(new Point3f( 0.0f, 0.0f, 90.0f)); 96. playerRotate.setRotationX(new Point3f( -90.0f, 0.0f, 0.0f)); 97. }

(10)

98. } 99. else { 100. //playerRotate = Scene.loadModel("/data/models/Duke_posed.dae", 101. // playerId); 102. playerRotate = Scene.loadModel("/data/models/Duke_posed_out.dae", 103. playerId); 104. playerRotate.setRotationY(new Point3f(0, 180, 0)); 105. } 106. 107. . . . (código intermédio)

108. TransformGroup player2Rotate = null; 109. if (spaceGame) { 110. if ("true".equals(System.getProperty("gameProject.xwing"))) { 111. player2Rotate = Scene.loadModel("/data/models/xwing_out.dae", 112. "player2"); 113. player2Rotate.setRotationY(new Point3f( 0.0f, 180.0f, 0.0f)); 114. } 115. if ("true".equals(System.getProperty("gameProject.tie"))) { 116. player2Rotate = Scene.loadModel("/data/models/tiefighter_out.dae", 117. "player2"); 118. player2Rotate.setRotationY(new Point3f(0, 180, 0)); 119. } 120. } 121. else 122. if (stupidGame) { 123. if ("true".equals(System.getProperty("gameProject.duck"))) { 124. player2Rotate = Scene.loadModel("/data/models/ 125. duck_triangulate_out.dae","player2"); 126. player2Rotate.setRotationY(new Point3f( 0.0f, -90.0f, 0.0f)); 127. } 128. if ("true".equals(System.getProperty("gameProject.cow"))) { 129. player2Rotate = Scene.loadModel("/data/models/cow_out.dae", 130. "player2"); 131. player2Rotate.setRotationZ(new Point3f( 0.0f, 0.0f, 90.0f)); 132. player2Rotate.setRotationX(new Point3f( -90.0f, 0.0f, 0.0f)); 133. } 134. } 135. else { 136. player2Rotate = Scene.loadModel("/data/models/Duke_posed_out.dae", 137. "player2"); 138. player2Rotate.setRotationY(new Point3f(0, 180, 0)); 139. }

pelo seguinte código para o 1º jogador:

TransformGroup playerRotate = null; if (spaceGame) {

playerRotate = Scene.loadModel("/data/models/tiefighter_out.dae", playerId); }

else

if (stupidGame) {

playerRotate = Scene.loadModel("/data/models/cow_out.dae", playerId); playerRotate.setRotationZ(new Point3f( 0.0f, 0.0f, 90.0f));

playerRotate.setRotationX(new Point3f( -90.0f, 0.0f, 0.0f)); }

else {

playerRotate = Scene.loadModel("/data/models/Duke_posed_out.dae", playerId); playerRotate.setRotationY(new Point3f(0, 180, 0));

}

(11)

TransformGroup player2Rotate = null; if (spaceGame) {

player2Rotate = Scene.loadModel("/data/models/xwing_out.dae", "player2"); player2Rotate.setRotationY(new Point3f( 0.0f, 180.0f, 0.0f)); //player2Rotate = Scene.loadModel("/data/models/tiefighter_out.dae", // "player2"); } else if (stupidGame) { player2Rotate = Scene.loadModel("/data/models/duck_triangulate_out.dae", "player2"); player2Rotate.setRotationY(new Point3f( 0.0f, -90.0f, 0.0f));

//player2Rotate = Scene.loadModel("/data/models/cow_out.dae", "player2"); //player2Rotate.setRotationZ(new Point3f( 0.0f, 0.0f, 90.0f)); //player2Rotate.setRotationX(new Point3f( -90.0f, 0.0f, 0.0f)); } else { player2Rotate = Scene.loadModel("/data/models/Duke_posed_out.dae", "player2"); player2Rotate.setRotationY(new Point3f(0, 180, 0)); }

Neste momento o código, ao ser executado, carregará um modelo de um Tie fighter que pode ser

controlado pelo rato e/ou teclado, bem como um X-Wing que não faz nada.

Exercício 2

.

Quando os modelos são carregados não ficam necessariamente na posição ou com a rotação pretendida.

Vamos então agora adicionar um terceiro jogador (player3Player) com o vosso primeiro nome, e.g.,

Miguel, na posição inicial (30, 0, 40). Ter em atenção que o modelo que é controlado pelo teclado e pelo

rato aparece numa posição aleatória, o que é feito no ciclo do-while aquando na adição do primeiro

jogador à cena. O modelo utilizado pode ser qualquer um dos disponíveis, i.e., pato, vaca, x-xing, etc, ter

em atenção os nomes no código anteriormente alterado.

Para realizar este exercício tome atenção ao código que vem a seguir a:

140. TransformGroup playerRotate = null;

e

141. TransformGroup player2Rotate = null;

até à inclusão dum modelo na cena. Ao carregar o modelo, tenha particular atenção que após a seguinte

chamada ao método loadModel:

142. player2Rotate = Scene.loadModel("/data/models/xwing_out.dae", "player2");

existe normalmente um ou mais métodos de rotação para reposicionar o objecto, como por exemplo:

143. player2Rotate.setRotationY(new Point3f( 0.0f, 180.0f, 0.0f));

Experimente comentar uma ou mais das rotações referidas para qualquer um dos modelos (em alguns dos

modelos são feitas várias), e.g., o modelo da vaca.

(12)

Tente indicar o conjunto de passos necessários para adicionar um nó de transformação do tipo jogador à

cena.

Exercício 4

.

Vamos agora alterar ou remover os limites da cena. Para tal basta comentar na secção de código no

método initGame da classe principal do motor/jogo GameRenderer.java:

144. scene.setFloor(floor);

145. …

146. scene.setSkyBox(skyBox);

Seguidamente vamos correr o jogo/motor. O código neste mesmo método que precede estas chamadas

inicializa o chão ou os limites do mundo, i.e., associa a informação da geometria a elementos das texturas,

carrega texturas, etc. Estas chamadas apenas incluem o skybox e o chão na cena.

Agora descomente o código comentado previamente, e substitua a chamada (assumindo que no início da

classe GameRenderer, stupidGame e dukeBeanEm estão a false e spaceGame está a true):

147. skyBox = Scene.buildSkyBox(size/5, "/data/textures/World_5/SkyBox2");

Por uma das seguintes opções, cujas texturas estão na diretoria data:

148. skyBox = Scene.buildSkyBox(size/5, "/data/textures/World_4/SkyBox2"); 149. skyBox = Scene.buildSkyBox(size/5, "/data/textures/World_3/SkyBox2"); 150. skyBox = Scene.buildSkyBox(size/5, "/data/textures/World_2/SkyBox2"); 151. skyBox = Scene.buildSkyBox(size/5, "/data/textures/World_1/SkyBox2");

Exercício 5

.

Vamos por fim adicionar um obstáculo à cena. Em primeiro lugar, no seguinte

snippet de código do

método initGame:

152. if(!spaceGame) { 153. int boxCount = 6;

154. Texture obstacleTexture = null; 155. try {

156. TextureData textureData = TextureIO.newTextureData(GLProfile. 157. getDefault(),Scene.class.getResourceAsStream("/data/textures/ 158. brick1.jpg"), true, TextureIO.JPG);

159. obstacleTexture = TextureIO.newTexture(textureData); 160. }

161. catch (IOException e) {

162. throw new RuntimeException(e); 163. //e.printStackTrace();

164. } 165.

166. for (int i = 0; i < boxCount; i++) { 167. float rotation = i * (360 / boxCount);

168. TransformGroup obstacle = Scene.buildObstacle("obstacle" + (i + 1), 169. "brick1", obstacleTexture);

170. Point3f obstaclePostion = new Point3f();

171. obstaclePostion.x = (float) Math.sin(Math.toRadians(rotation)) * 15; 172. obstaclePostion.y = 0;

173. obstaclePostion.z = (float) Math.cos(Math.toRadians(rotation)) * 15; 174. obstacle.setTranslation(obstaclePostion);

175. scene.addObstacle(obstacle);

176. System.out.println("Obstacle " + (i + 1) + " added"); 177. }

(13)

178. }

comente:

if(!spaceGame) {

e

}

Corra o código agora que ele irá carregar-lhe uma conjunto de obstáculos em posições pré-definidas e

adicioná-los-á à cena através do método scene.addObstacle(obstacle);.

Modifique depois o código de forma a apenas adicionar um obstáculo na posição (10, 0, 110). Depois de

o fazer, procure o obstáculo. Se não o encontrar, experimente dar valores menores do que 110 à

coordenada Z.

De seguida, verifique o valor da variável size na classe GameRenderer.java. A que corresponde

metade do valor desta?

Referências

Documentos relacionados

É com base nesta nova perspectiva que professores de Língua Portuguesa atuam no Programa Ensino Médio com Intermediação Tecnológica (EMITec). As aulas são transmitidas

Importante, nesse contexto, ressaltar que a PNAB é uma Portaria que foi publicada no ano de 2017, cujo objetivo é estabelecer a revisão de diretrizes para a organização da

8- Bruno não percebeu (verbo perceber, no Pretérito Perfeito do Indicativo) o que ela queria (verbo querer, no Pretérito Imperfeito do Indicativo) dizer e, por isso, fez

Apesar de a Educação Ambiental ter se expandido, ela ainda é pouco abrangente e se caracteriza por ações pontuais e incipientes como prática a ser potencializada

Controlador de alto nível (por ex.: PLC) Cabo da válvula Tubo de alimentação do ar de 4 mm Pr essão do fluido Regulador de pressão de precisão Regulador de pressão de

Após 90 dias da semeadura (primeiro subcultivo), protocormos com um par de folíolos foram selecionadas visualmente, mantendo um padrão de altura de cerca de dois milímetros

Este módulo explica como criar serviços e aplicativos escalonáveis e dimensioná-los automaticamente usando balanceadores de carga de Web Apps, Gateway de Aplicativo do Azure

sempre benecifiar e impactar positivamente as pessoas que vivem na localidade, portanto a sociedade local (desenvolvimento, nesse sentido, não é induzido para que