Después de definir las clases involucradas en el diseño del juego, ahora definimos varios estados de estas clases:
SpaceWar representando las necesidades
del juego para conocerla centralStar, las
ships, los torpedoes disparados y su
color.
CentralStar tiene un estado masa. Que
necesitamos para calcular la fuerza de la gravedad aplicada a cada
nave.
SpaceShip que conozca su nombre, las
coordenadas de su posición (position), su ángulo de
orientación (heading), su vector de velocidad
(velocity), su estimación de combustible (fuel)
, su cantidad de torpedos disponibles (torpedoes), su masa
(mass) y su empuje de aceleración del motor
(acceleration).
Torpedo tiene los estados de posición (position),
velocidad (velocity) y tiempo de vida (lifeSpan).
Necesitamos explicar la naturaleza matemática de estos estados y, a continuación, analizar su representación objetiva en las variables de instancia de nuestras clases.
En las siguientes secciones, para facilitar la lectura, escribiremos «la variable
myVares unaString» en lugar de la correcta pero más engorrosa «la variable de instanciamyVares una referencia a una instancia deString».
SpaceWar ¶Este objeto es la entrada al juego. Queremos un nombre de clase significativo. Sus variables de instancia son los protagonistas involucrados en el juego:
centralStar es la única CentralStar del
juego. Necesitamos conocer sobre ella y poder pedirle su masa.
ships es una colectión de dos naves para los dos
jugadores. Es una instancia de Array, su tamaño fijo es de
dos elementos.
torpedoes es una colección de los torpedos
disparados en el juego. Como su cantidad es variables, tiene sentido
una OrderedCollection dinámica.
CentralStar ¶Es una variable de instancia única, mass es un número, lo
más probable un entero (Integer).
SpaceShip ¶La nave es el objeto más complejo, algunas aclaraciones al echar un vistazo a sus variables.
name (nombre) es una cadena String.
position es una coordenada de pantalla 2D, una
localización. Smalltalk utiliza la clase Point para
representar este tipo de objetos. Comprende muchas operaciones
matemáticas y de vectores; muy utilizados en cálculos mecánicos.
Un punto se instancia muy fácilmente con un mensaje binario #@
enviando a un número otro número como argumento: 100 @ 200
devuelve una instancia de Point representando las coordenadas
(x;y) = (100;200).
La posición (position) de la nave se recalcula
periódicamente de acuerdo a la ley de marco de referencia
galileano. El cálculo depende de la velocidad de la nave, su empuje de
motor actual y la gravedad que tira desde la estrella central.
heading es un ángulo en radianes, la dirección en la
que apunta la nariz de la nave. Por lo tanto es un número
Float. Cuando comienza el juego, las naves se orientan hacia
arriba. El valor de su heading es -pi/2 radianes; el eje
oy está orientado de arriba hacia abajo de la pantalla.
velocity es un vector representando la velocidad
instantánea de la nave. Es una instancia de Point.
fuel‘fuel‘ es un indicador, siempre que no sea cero,
de que el jugador puede encender los motores de cohete de la nave
proporcionándole aceleración para moverse alrededor del tirón de
gravedad de la estrella central Es un número entero.
torpedoes es la cantidad de torpedos
disponibles para ser disparados por el jugador. Es un número entero
(Integer).
mass es un Integer representando la masa de
la nave.
acceleration es la norma de aceleración intrínseca
que se proporciona a la nave cuando se encienden los motores. Por lo
tanto es un número Integer.
Unas pocas palabras sobre las coordenadas euclidianas: el origen de nuestro marco ortonormal es la estrella central, su primer vector está orientado hacia la derecha de la pantalla y el segundo hacia la parte superior de la pantalla. Esta elección facilita el cálculo de la aceleración, la velocidad y la posición de la nave. Más adelante daremos información al respecto.
Torpedo ¶Un torpedo se lanza o «dispara» desde una nave con la velocidad iniciar relacionada a la velocidad de la nave. Una vez que el tiempo de vida del torpedo llega a cero, se autodestruye.
position es una coordenada 2D en la pantalla, una
instancia de Point. A diferencia de la nave, no acelera por
gravedad de la estrella central. En efecto, un torpedo carece de un
estado de masa. Para nuestro propósito es esencialmente cero. Su
posición en el tiempo depende sólamente de la velocidad y su
aceleración inicial.
heading es un ángulo en radianes, la dirección en la
que la nariz del torpedo apunta. Su valor coincide con el de la nave
cuando se dispara. Por tanto también es un número Float.
velocity es un vector que representa la velocidad
instantánea del torpedo. Es constante durante el tiempo de vida del
torpedo. De nuevo la velocidad es una instancia de Point,
lifeSpan es un contador entero, cuando alcanza cero
el torpedo se autodestruye.
En el capítulo anterior, explicamos cómo definir las cuatro clases
SpaceWar, CentralStar, SpaceShip y
Torpedo. En esta sección añadiremos a esas definiciones las
variables de instancia –estados– que describimos arriba.
Para añadir las variables de la clase Torpedo, desde el
Browser, selecciona esa clase. A continuación, añade los nombres de
variables a la clave instanceVariableNames:, separadas por
espacio. Por último, guarda la definición de la clase actualizada con el
atajo Ctrl-s:
Object subclass: #Torpedo instanceVariableNames: 'position heading velocity lifeSpan' classVariableNames: '' poolDictionaries: '' category: 'Spacewar!'
Ejemplo 3.14: La clase Torpedo con sus variables de instancia
Añade las variables de instancias descritas arriba a las clases
SpaceWar,CentralStarySpaceShip.
Ejercicio 3.9: Variables de instancia de los protagonistas de Spacewar!
Necesitamos acceder a algunos de estos estados desde otras entidades:
ship name: 'The needle'.
star mass * ship mass.
Para escribir estos comportamientos en el Browser primero selecciona la
clase, luego la categoría de método que quieres, o si no quieres ninguna
-- all --.
En el panel de código aparece la plantilla de método:
messageSelectorAndArgumentNames "comment stating purpose of message" | temporary variable names | statements
Ejemplo 3.15: Plantilla de método
Se describe a sí misma:
El getter mass de SpaceShip está escrito como:
SpaceShip>>mass ^ mass
La parte SpaceShip>> no es código válido y no se debe
escribir en el Browser. Es una convención de texto para indicarle al
lector que es un método de la clase SpaceShip.
Escribe unos mensajes getter de
SpaceShippara sus atributos de posición, velocidad y masa.
Ejercicio 3.10: Mensajes getter de SpaceShip
Algunas variables de instancia se deben inicializar desde otra entidad, para esto es necesario un mensaje setter. Para establecer el nombre de una nave espacial podemos añadir el siguiente método:
SpaceShip>>name: aString name := aString
El caracter := es una asignación, significa que la variable de
instancia name contiene un objeto aString. Para
teclear este símbolo, teclea _ y luego espacio, Cuis-Smalltalk lo
convertirá en un símbolo de flecha izquierda. Alternativamente puedes
escribir name := aString. Se podría pronunciar :=
como «gets».
Dado que name es una variable de instancia, cada método de instancia
sabe que debe utilizar el espacio para el nombre. Lo que significa que
estamos colocando el valor del argumento aString en el espacio de la
instancia llamado name.
Dado que cada elemento de la variable de instancia puede contener un
objeto de cualquier clase, nos gusta nombrar el argumento para indicar
que pretendemos que la variable name contenga una cadena, una
instancia de la clase String.
La posición (
position) y velocidad (velocity) de la nave, tanto como el rumbo (heading) del torpedo se deben establecer al inicio del juego o cuando la nave salta en el hiperespacio. Escribe los apropiados setters.
Ejercicio 3.11: Mensajes setter de SpaceShip
Fíjate que no tenemos un mensaje setter para el atributo
mass (masa) de la nave espacial. De hecho, no tiene sentido
cambiar la masa de una nave desde otro objeto. De hecho, si consideramos
que ambas naves de los jugadores tienen la misma masa, deberíamos
eliminar la variable mass y editar el método #mass para
que devuelva un número literal:
SpaceShip>>mass ^ 1
Ejemplo 3.16: Un método devolviendo una constante
Por otro lado, también podríamos considerar que la masa depende del combustible y los torpedos consumidos. Después de todo, el 93 % de la masa del cohete Saturno V estaba compuesta por su combustible. Hablaremos más sobre esto más adelante, en el capítulo sobre refactorización.
Una nave espacial controlada por el jugador comprende mensajes para ajustar su dirección y aceleración17:
Dirección. El rumbo de la nave se controla con los mensajes
#left y #right. El primero reduce el rumbo en 0.1 y el
segundo lo incrementa en 0.1.
Escribe dos métodos llamados
leftyrightpara modificar el rumbo de la nave en 0.1 de acuerdo con las indicaciones anteriores.
Ejercicio 3.12: Métodos para controlar el rumbo de la nave
Aceleración. Cuando se envía el mensaje #push a la nave,
se encienden los motores y una aceleración interna de 10 unidades de
aceleración se aplican a la nave. Cuando se envía el mensaje
#unpush, la aceleración se detiene.
Escribe dos métodos llamados
pushyunpushpara ajustar la aceleración interna de acuerdo con las indicaciones anteriores.
Ejercicio 3.13: Métodos para controlar la aceleración de la nave
Cuando se crea una instancia, por ejemplo, SpaceShip new, se
inicializa automáticamente: se envía el mensaje #initialize al
objeto recién creado y se llama al método de instancia
initialize correspondiente.
El proceso de inicialización es útil para establecer los valores predeterminados de las variables de instancia. Cuando creamos un nuevo objeto nave espacial, queremos establecer su posición, velocidad y aceleración predeterminadas:
SpaceShip>>initialize super initialize. velocity := 0 @ 0. position := 100 @ 100. acceleration := 0
Ejemplo 3.17: Inicializar la nave espacial
En el método Ejemplo 3.17, observa la
primera línea super initialize. Cuando se envía un mensaje a super,
se refiere a la superclase de la clase del método. Hasta ahora, la clase
padre de SpaceShip es Object, por lo que, para la
inicialización, primero se llama al método Object>>initialize.
Cuando se crea una nave espacial se coloca en la parte superior y derecha de la estrella central. No tiene velocidad ni aceleración interna, solo la fuerza gravitatoria de la estrella central. Su proa apunta hacia la parte superior de la pantalla del juego.
Probablemente hayas notado que no hay código para inicializar el
atributo heading, algo como heading := Float
halfPi negated para orientar la nave en la dirección de la parte
superior de la pantalla. La verdad es que no necesitamos el
heading, ya que esta información la proporcionará la clase
Morph, que se utilizará más adelante como clase padre de
SpaceShip y Torpedo. En este momento, se eliminará la
variable heading y definiremos el comportamiento de la
dirección con los métodos heading y heading:
adecuados.
Escribe el método para inicializar la estrella central con 8000 unidades de masa.
Ejercicio 3.14: Inicializar la estrella central
La velocidad es una consecuencia de las aceleraciones aplicadas a la nave espacial.