El documento describe cómo agregar objetivos adicionales a un juego existente. Se agrega un arreglo de tomates como nuevos objetivos. Cada tomate se configura con una textura, posición y estado visible. Al golpear un tomate con el queso, este se hace invisible para eliminarlo del juego.
1. B L A D I M I R D I A Z C A M P O S
Adicionar otros objetivos
2. Agenda
Hasta ahora atenemos un sprite que golpea a otro.
Ahora le vamos a agregar nuevos objetivos, para que
el jugador dirija el queso.
Se adiciona un tomate a la pantalla.
Cuando el queso golpea el tomate, este desaparecerá
3. Adicionar nuevos objetivos
Queremos tener un gran número de objetivos.
Necesitamos además que sea fácil de cambiar el número de
objetivos.
La manera de manejar este gran número de objetivos
es mediante un arreglo.
El código declara la referencia del arreglo y establece
el número de tomates en 20.
Estas variables son parte del juego.
4. La nueva textura
Los objetivos que
adicionaremos son
tomates.
Se dibujan de la misma
manera que los sprite
anteriores.
La textura fue creada y
adicionada de la misma
manera que el pan y el
queso.
tomatoTexture = Content.Load<Texture2D>("Images/Tomato");
5. Arreglos de estructuras
Colocando la información del queso y pan en la
estructura, nos resulto más fácil.
No solo se hizo más fácil el manejo, si no que además
hace posible para nosotros almacenarlos de manera
más fácil dentro de un arreglo
Una vez creado nuestro propio tipo de datos,
podemos utilizarlo en arreglos
el nuevo tipo se vuelve otro bloque de código
6. Creando el arreglo
El código crea un arreglo de GameSpriteStruct, que
almacenan los tomates.
El tamaño del arreglo es dado por la variable
numberOfTomatoes.
Cada elemento del arreglo es creado vacio, con
número de campos a cero y la referencia de campos a
null
tomatoes = new GameSpriteStruct[numberOfTomatoes];
7. Espaciar los tomates
Esta sentencia crea una variable local que almacena
el espacio entre cada fila de tomates.
Este es el calculo del ancho dibujable de la pantalla
(maxDisplayX-minDisplayX) dividido por el número
de tomates que serán dibujados.
Esto significa que el juego puede manejar diferentes
números de tomates, para diferentes tamaños de
pantalla automáticamente.
float tomatoSpacing = (maxDisplayX - minDisplayX) /
numberOfTomatoes;
8. Configurar los sprites
Este pequeño lazo configura los sprites
Pro cada sprite, el código configura la textura, la
posición y tamaño llamando a setupSprite.
for (int i = 0; i < numberOfTomatoes; i++)
{
tomatoes[i].SpriteTexture = tomatoTexture;
setupSprite(
ref tomatoes[i],
0.05f, // 20 tomatoes across the screen
1000, // 1000 ticks to move across the screen
minDisplayX + (i * tomatoSpacing), // x position
minDisplayY); // y position
}
9. Configurar el lazo
Este lazo trabaja a través de cada uno de los tomatos
que se han establecido.
for (int i = 0; i < numberOfTomatoes; i++)
{
tomatoes[i].SpriteTexture = tomatoTexture;
setupSprite(
ref tomatoes[i],
0.05f, // 20 tomatoes across the screen
1000, // 1000 ticks to move across the screen
minDisplayX + (i * tomatoSpacing), // x position
minDisplayY); // y position
}
10. Configurar el lazo
Esto hace que la referencia de la textura de cada
tomate sea la misma.
Es decir todos los tomates, se verán igual
for (int i = 0; i < numberOfTomatoes; i++)
{
tomatoes[i].SpriteTexture = tomatoTexture;
setupSprite(
ref tomatoes[i],
0.05f, // 20 tomatoes across the screen
1000, // 1000 ticks to move across the screen
minDisplayX + (i * tomatoSpacing), // x position
minDisplayY); // y position
}
11. Llamando a la configuración del sprite
Esta es la llamada a la configuración
Es el mismo método utilizado para el pan y el queso.
for (int i = 0; i < numberOfTomatoes; i++)
{
tomatoes[i].SpriteTexture = tomatoTexture;
setupSprite(
ref tomatoes[i],
0.05f, // 20 tomatoes across the screen
1000, // 1000 ticks to move across the screen
minDisplayX + (i * tomatoSpacing), // x position
minDisplayY); // y position
}
12. Pasando una referencia
setupSprite necesita una referencia para el sprite que
va a trabajar.
En otro caso, el elemento será pasado por valor
for (int i = 0; i < numberOfTomatoes; i++)
{
tomatoes[i].SpriteTexture = tomatoTexture;
setupSprite(
ref tomatoes[i],
0.05f, // 20 tomatoes across the screen
1000, // 1000 ticks to move across the screen
minDisplayX + (i * tomatoSpacing), // x position
minDisplayY); // y position
}
13. Establecer el tamaño de cada sprite
El número de tomates en pantalla
for (int i = 0; i < numberOfTomatoes; i++)
{
tomatoes[i].SpriteTexture = tomatoTexture;
setupSprite(
ref tomatoes[i],
0.05f, // 20 tomatoes across the screen
1000, // 1000 ticks to move across the screen
minDisplayX + (i * tomatoSpacing), // x position
minDisplayY); // y position
}
14. Establecer los movimientos
Los tomates no se mueven en esta versión del juego.
Sin embargo, vamos a colocar una velocidad
for (int i = 0; i < numberOfTomatoes; i++)
{
tomatoes[i].SpriteTexture = tomatoTexture;
setupSprite(
ref tomatoes[i],
0.05f, // 20 tomatoes across the screen
1000, // 1000 ticks to move across the screen
minDisplayX + (i * tomatoSpacing), // x position
minDisplayY); // y position
}
15. Establecer la posición X
La posición X de cada tomate es calculada utilizando
el valor del espacio creado anteriormente.
El primero estará en el lado izquierdo
for (int i = 0; i < numberOfTomatoes; i++)
{
tomatoes[i].SpriteTexture = tomatoTexture;
setupSprite(
ref tomatoes[i],
0.05f, // 20 tomatoes across the screen
1000, // 1000 ticks to move across the screen
minDisplayX + (i * tomatoSpacing), // x position
minDisplayY); // y position
}
16. Establecer la posición Y
Todos los tomates estan presenten en la parte de
arriba de la pantalla.
Establecemos el valor de Y para lograr esto.
for (int i = 0; i < numberOfTomatoes; i++)
{
tomatoes[i].SpriteTexture = tomatoTexture;
setupSprite(
ref tomatoes[i],
0.05f, // 20 tomatoes across the screen
1000, // 1000 ticks to move across the screen
minDisplayX + (i * tomatoSpacing), // x position
minDisplayY); // y position
}
17. Actualizar el comportamiento
Durante el update, necesitamos copiar las posiciones
de X y Y de cada tomate dentro del Rectangle que
usamos para dibujarlo.
Hacemos esto, aunque los tomates no se mueven
aun.
// Update the tomatoes
for (int i = 0; i < numberOfTomatoes; i++)
{
tomatoes[i].SpriteRectangle.X = (int)tomatoes[i].X;
tomatoes[i].SpriteRectangle.Y = (int)tomatoes[i].Y;
}
18. Dibujar el comportamiento
El comportamiento del dibujo es muy simple.
El lazo trabaja para cada uno de los tomates.
La llamada del spriteBatch.Draw usa la textura y el
rectangulo de cada elemento de tomate dibujado en
pantalla
for (int i = 0; i < numberOfTomatoes; i++)
{
spriteBatch.Draw(tomatoes[i].SpriteTexture,
tomatoes[i].SpriteRectangle,
Color.White);
}
20. La colisión
Cuando el queso golpea el tomate, queremos que el
tomate desaparezca.
Esta es una manera que el jugador gane puntos.
Para detectamor la colisión entre un tomate y el
queso utilizamos el Intersects
Sin embargo, hay que desparecer el tomate.
21. Objetos y estado
Muchos objetos del programa tienen estados.
Una orden puede ser: “pendiente”, “siendo empaquetado” o
“despachado”
Una cuenta de banco puede estar: “abierta”, “cerrada” o
“suspendida”.
Necesitamos adicionar alguna información del sprite
en el juego, debemos rastrear si o no un tomate fue
alcanzado.
Debemos hacer esto para todos los valores de
GameSpriteStruct
22. Adicionar un estado de visible
Vamos a adicionar un estado a la estructura
GameSpriteStruct, que determina si es visible o no.
Un sprite que no es visible no se dibuja en la pantalla.
El sprite que no es visible no puede colisionar.
Cuando un tomate es alcanzado, su estado cambia y
no es mas visible.
23. Adicionar el estado
Aquí se ha adicionado el campo visible
Es del tipo bool
struct GameSpriteStruct
{
public Texture2D SpriteTexture;
public Rectangle SpriteRectangle;
public float X;
public float Y;
public float XSpeed;
public float YSpeed;
public float WidthFactor;
public float TicksToCrossScreen;
public bool Visible;
}
24. Establecer el estado de visible
El método setupSprite tiene un parámetro que puede
ser utilizado para configurar la visibilidad inicial del
sprite
void setupSprite(
ref GameSpriteStruct sprite,
float widthFactor,
float ticksToCrossScreen,
float initialX,
float initialY,
bool initialVisibility)
{
// original sprite setup code here
sprite.Visible = initialVisibility;
}
25. Configuración de cada sprite
Cuando se llama setupSprite, podemos colocar la
visibilidad como parámetro
Esto afecta la configuración del pan, del queso y
todos los tomates
setupSprite(
ref cheese, // reference to the sprite to set up
0.05f, // width factor (a 20th)
200.0f, // ticks to cross the screen
minDisplayX, // starting X position
minDisplayY, // starting Y position
true); // initially visible
26. Usar la visibilidad para controlar el dibujado
Necesitamos modificar el código para dibujar el
tomate.
Solo los tomates visibles deben ser dibujados.
for (int i = 0; i < numberOfTomatoes; i++)
{
if (tomatoes[i].Visible)
{
spriteBatch.Draw(tomatoes[i].SpriteTexture,
tomatoes[i].SpriteRectangle, Color.White);
}
}
27. Detectar la colisión
Código para colisión y destrucción del sprite tomato
for (int i = 0; i < numberOfTomatoes; i++)
{
if (tomatoes[i].Visible)
{
if (cheese.SpriteRectangle.Intersects(
tomatoes[i].SpriteRectangle))
{
tomatoes[i].Visible = false;
cheese.YSpeed = cheese.YSpeed * -1;
break;
}
}
}
28. Destruir un solo tomato
Utilizamos un break en el lazo por que necesitamos
destruir solo un tomato por llamada del update
for (int i = 0; i < numberOfTomatoes; i++)
{
if (tomatoes[i].Visible)
{
if (cheese.SpriteRectangle.Intersects(
tomatoes[i].SpriteRectangle))
{
tomatoes[i].Visible = false;
cheese.YSpeed = cheese.YSpeed * -1;
break;
}
}
}
30. Resumen
Es posible crear arreglos de cualquier tipo de datos
que se crean.
Una variable de arreglos es una referencia que refiere
una instancia del arreglo.
Varios objetos pueden compartir un solo objeto si
cada uno tiene una referencia a ese objeto.
Es muy frecuente que los objetos tengan un estado.
31. Verdadero o Falso
Una variable de arreglos es del tipo referencia.
Es posible crear arreglos de estructuras que han sido
creadas.
La sentencia break, detiene la ejecución del
programa
Un objeto en memoria puede tener solo una
referencia a el.
El estado de un objeto puede ser representado por un
campo dentro del objeto.
32. Verdadero o Falso
Una variable de arreglos es del tipo referencia.
Es posible crear arreglos de estructuras que han sido
creadas.
La sentencia break, detiene la ejecución del
programa
Un objeto en memoria puede tener solo una
referencia a el.
El estado de un objeto puede ser representado por un
campo dentro del objeto.
33. Verdadero o Falso
Una variable de arreglos es del tipo referencia.
Es posible crear arreglos de estructuras que han sido
creadas.
La sentencia break, detiene la ejecución del
programa
Un objeto en memoria puede tener solo una
referencia a el.
El estado de un objeto puede ser representado por un
campo dentro del objeto.
34. Verdadero o Falso
Una variable de arreglos es del tipo referencia.
Es posible crear arreglos de estructuras que han sido
creadas.
La sentencia break, detiene la ejecución del
programa
Un objeto en memoria puede tener solo una
referencia a el.
El estado de un objeto puede ser representado por un
campo dentro del objeto.
35. Verdadero o Falso
Una variable de arreglos es del tipo referencia.
Es posible crear arreglos de estructuras que han sido
creadas.
La sentencia break, detiene la ejecución del
programa
Un objeto en memoria puede tener solo una
referencia a él.
El estado de un objeto puede ser representado por un
campo dentro del objeto.
36. Verdadero o Falso
Una variable de arreglos es del tipo referencia.
Es posible crear arreglos de estructuras que han sido
creadas.
La sentencia break, detiene la ejecución del
programa
Un objeto en memoria puede tener solo una
referencia a él.
El estado de un objeto puede ser representado por un
campo dentro del objeto.
38. Agenda
Ahora ya tenemos lo fundamental del juego.
Los jugadores puede mover el queso con la ayuda
del pan para destruir los tomates
Embargo, hasta este momento el juego todavía
necesita algunos retoques.
En esta sección, haremos ajustes al juego,
adicionando comportamientos nuevos para hacerlo
más entretenido.
39. Los juegos
Hay varias cosas a tener en mente para que un juego
sea interesante hacia el jugador:
Recompensa: El jugador debe de ser recompensado por las
cosas que haga correctamente.
Sobrevivir: el jugador debe prevenir cosas que pasan y que
pueden causar “daño”
Progresión: el juego debe cambiar durante el tiempo para darle
nuevos desafíos al jugador.
Necesitamos adicionar estas características para que
el juego sea más divertido
40. Adicionar los premios
En este momento, el juego no premia el
comportamiento del jugador.
Destruir algo es divertido, pero no provee una
recompensa por hacerlo.
Si el jugador gana puntos por la acción esto puede
hacer al juego más interesante.
Otro mecanismo de premio es ejecutar un sonido
cuando el jugador hace algo de manera correcta
41. Adicionar el puntaje
La variable de puntos es una variable de tipo entera
que adicionamos al mundo del juego.
Al inicio del juego esta variable debe de estar
configurada a cero en el método setupSprite.
Durante el juego, actualizaremos esta variable Dr y el
método Draw mostrará su valor en pantalla
// Game World
int score;
42. Actualizar el puntaje
for (int i = 0; i < numberOfTomatoes; i++)
{
if (tomatoes[i].Visible)
{
if (cheese.SpriteRectangle.
Intersects(tomatoes[i].SpriteRectangle))
{
tomatoes[i].Visible = false;
cheese.YSpeed = cheese.YSpeed * -1;
score = score + 10;
break;
}
}
}
43. Dibujar el texto
El juego debe mostrar cuando se está jugando.
La mejor forma de hacer esto es crear un método que
muestre en pantalla el texto.
El método debe tener una cadena de textos a mostrar
y la posición.
El método Draw puede llamar al método que lo
muestra, para mostrar el estado del juego.
Podemos llamarle a este método drawText
44. La cabecera del método
El método drawText necesita un distrito para
despojarlo, el color del texto, y la posición.
Utilizaremos los miembros spriteBatch y font del
juego para mostrar el texto
void drawText(string text, Color textColor, float x, float y)
{
// draw the text with a nice shadow here
}
45. El cuerpo del método
El cuerpo del método drawText está basado en el
código anteriormente visto.
Utilizan lazos para dibujar de manera repetida
caracteres que nos de un efecto de 3D.
Si necesitamos un tipo diferente de texto podremos
cambiar el comportamiento del método drawText
46. Orden de dibujado
El orden en el cual los items se dibujan, afectan
como ellos aparecen en la pantalla.
Los items que se dibujan después, se muestran
encima de los que han sido previamente dibujados.
En nuestro caso se dibujara el puntaje por último
para que siempre sea visible
48. Adicionar supervivencia
Ahora tenemos un juego un poco más interesante,
pero aún no es propiamente un juego.
Se necesita tener algún tipo de sobrevivencia o
elementos de riesgos.
La forma más fácil, es forzar al jugador a mantener el
queso rebotando.
Si eso golpea la parte baja de la pantalla, se pierde
una vida.
Si se pierden tres vidas en juego termina
49. Adicionar el contador de vidas
La variable lives es parte del mundo del juego y
cuenta cuántas vidas le restan al jugador.
Cada vez que el queso toque la parte baja de la
pantalla, una vida se pierde.
Cuando el valor de esta variable alcanza cero, el
juego termina.
Inicialmente se configura a tres
// Game World
int score;
int lives;
50. Perdiendo una vida
Este es el código en el método update , que hace
rebotar el queso de este la parte baja de la pantalla.
Esto asegura que el queso esté siempre en
movimiento
Esto reduce el valor de la variable si es mayor a cero
if (cheese.Y + cheese.SpriteRectangle.Height >= maxDisplayY)
{
cheese.YSpeed = Math.Abs(cheese.YSpeed) * -1;
if (lives > 0)
{
lives--;
}
}
51. Mostrar las vidas
Esta llamada del método drawText también dibuja el
número adheridas que resta.
El método es llamado dentro de update.
Ahora se construye una cadena de caracteres más
larga que será mostrada
drawText(
"Score : " + score.ToString() +
" Lives : " + lives.ToString(),
Color.White,
minDisplayX,
maxDisplayY - 50);
52. Detener el juego
Necesitamos detener el juego cuando el jugador
utilice todas sus vidas.
Se puede utilizar la sentencia return para retornar al
método de donde fue llamado.
En este caso, regresaremos al método update, antes
de haber movido el queso pero después de mover el
pan y verificar los tomates
if (lives <= 0)
{
return;
}
54. Adicionar progresión
En este momento, el juego es efectivo hasta alcanzar
la destrucción del último tomate.
Necesitamos proveer una manera en la que el juego
pueda progresar mediante algún tipo de
comportamiento de nuevo nivel.
Cuando este último tomate haya sido destruido,
podemos llamarlos a todos de nuevo.
Cada vez que los dibujemos, deberán aparecer un
poquito más debajo en la pantalla, dándole al
jugador menos tiempo para reaccionar
55. Detectando cuando todos los sprites se han ido
Utilizamos una variable bool para detectar cuando
no existen más sprites de tomatoes; está
originalmente configurada true.
Si un tomate es encontrado, noTomatoes es
configurado false
bool noTomatoes = true;
for (int i = 0; i < numberOfTomatoes; i++)
{
if (tomatoes[i].Visible)
{
noTomatoes = false;
// Check to see if the cheese has hit this tomato
}
}
56. Restaurar los tomates
Si la bandera notomatoes es aún verdadera después
que todas los sprites han sido verificados con el juego
debe de resetear la pantalla con la nueva
información.
La fila de sprites debe ser movida un poco hacia
abajo y mostrar todos los tomates nuevamente.
Se ha creado un método para hacer esto.
if (noTomatoes)
{
resetTomatoDisplay();
}
57. Moviendo la fila hacia abajo
Éstas variables del mundo manejan la altura de los
tomates.
tomatoesHeight: La posición Y del redibujado de tomates.
tomatoesStepFactor: la fracción de altura a mover los tomates
cada vez.
tomatoesHeightLimit: ultima posición en la pantalla de los
tomates
float tomatoHeight;
float tomatoStepFactor = 0.1f;
float tomatoHeightLimit;
58. Reseteando los tomates
void resetTomatoDisplay()
{
tomatoHeight = tomatoHeight +
(displayHeight * tomatoStepFactor);
if (tomatoHeight > tomatoHeightLimit)
{
tomatoHeight = minDisplayY;
}
for (int i = 0; i < numberOfTomatoes; i++)
{
tomatoes[i].Visible = true;
tomatoes[i].Y = tomatoHeight;
}
}
60. Resumen
Un juego esta basado alrededor de los premios para
el jugador, cuando hace las cosas correctas y dando
algún tipo de riesgo que enfrentar.
La recompensa debe ser visible; un puntaje es una
buena manera de hacer esto.
El juego debe tener además una progresión, para que
el jugador tenga diferentes desafíos cuando el juego
continua.
61. Verdadero o falso
La variables booleanas pueden ser utilizadas como
banderas.
Un método debe ejecutarse siempre hasta que se
completa.
Lo que se dibuja recientemente, lo hace arriba de
cualquier otro.
No es posible configurar la posición Y de un
rectángulo, sin configurar la posición X.
62. Verdadero o falso
La variables booleanas pueden ser utilizadas como
banderas.
Un método debe ejecutarse siempre hasta que se
completa.
Lo que se dibuja recientemente, lo hace arriba de
cualquier otro.
No es posible configurar la posición Y de un
rectángulo, sin configurar la posición X.
63. Verdadero o falso
La variables booleanas pueden ser utilizadas como
banderas.
Un método debe ejecutarse siempre hasta que se
completa.
Lo que se dibuja recientemente, lo hace arriba de
cualquier otro.
No es posible configurar la posición Y de un
rectángulo, sin configurar la posición X.
64. Verdadero o falso
La variables booleanas pueden ser utilizadas como
banderas.
Un método debe ejecutarse siempre hasta que se
completa.
Lo que se dibuja recientemente, lo hace arriba de
cualquier otro.
No es posible configurar la posición Y de un
rectángulo, sin configurar la posición X.
65. Verdadero o falso
La variables booleanas pueden ser utilizadas como
banderas.
Un método debe ejecutarse siempre hasta que se
completa.
Lo que se dibuja recientemente, lo hace arriba de
cualquier otro.
No es posible configurar la posición Y de un
rectángulo, sin configurar la posición X.