Sei sulla pagina 1di 38

004 – C# Nivel: aficionado parte 2

Contenido
5. Convirtiendo las ideas en código — Parte 2 ........................................................ 2

5.1. Input Manager ............................................................................................... 6

6. Instrucciones de salto ........................................................................................ 15

6.1. Return ......................................................................................................... 16

6.1.1. Un ejemplo básico ................................................................................ 17

6.2. Returning Objects ....................................................................................... 18

6.3. Una clase es un tipo.................................................................................... 20

6.4. Null NO ES Void.......................................................................................... 22

7. Operadores y Condiciones ................................................................................ 23

7.1. Operadores Condicionales && y || .............................................................. 23

7.1.1. Ejemplo básico ..................................................................................... 23

8. Arrays: Un primer vistazo .................................................................................. 28

8.1. Matrices de tamaño fijo ............................................................................... 29

8.1.1. Un ejemplo básico ................................................................................ 29

8.2. Foreach ....................................................................................................... 34

8.2.1. Un ejemplo básico ................................................................................ 34

8.3. Inicialización dinámica ................................................................................ 35

8.4. Usando el bucle while en matrices .............................................................. 36

8.4.1. Estableciendo valores para las matrices .............................................. 37

8.4.2. Obteniendo valores de una matriz ........................................................ 38


5. Convirtiendo las ideas en código — Parte 2
Hemos llegado bastante lejos. En este punto, estamos casi listos para poner todas
las lecciones que hemos aprendido en un juego sencillo.
Para empezar, añadimos unos objetos usando el menú GameObject: un plano, una
luz direccional y algunos cubos. Cada objeto se coloca usando el editor de modo
que la cámara principal tenga algo que ver.

Esto nos da una escena básica que podemos utilizar. Ahora para construir los
conceptos básicos de un controlador de cámara en primera personal, vamos a
añadir un scirpt llamado FPSAim.cs al objeto de la cámara principal.
Para mover la cámara, en primer lugar, vamos a querer comprobar los datos que la
clase Input nos dará. Comenzando con Input.mousePosition, donde podemos
ver que consta de un Vector3. Input tiene muchas variables y funciones
estáticas que podemos usar, pero vamos a ver las otras variables y funciones
disponibles más adelante.

Por lo tanto, una variable Vector3 mousePosition será útil aquí. Sin embargo, el
mouse es un dispositivo 2D, así que ¿como son los datos que arroja el mouse?

void Update ()
{
Vector3 mousePosition = Input.mousePosition;
Debug.Log(mousePosition);
}

Añadimos un Debug.Log() para mostrar el valor de mousePosition que hemos


asignado de Input.mousePosition. Con este Debug.Log() en marcha,
tenemos muchos números que salen en x y en y, pero z permanece en 0.0. Esto
era más o menos lo se esperaba ya que el mouse no tiene ninguna manera de
detectar a qué distancia de la alfombrilla está.
Por lo tanto, es el momento para aislar los datos en una variable más fácil de usar.

void Update ()
{
Vector3 mousePosition = Input.mousePosition;
float mouseX = mousePosition.x;
float mouseY = mousePosition.y;
}

Por lo tanto, ahora, hemos reducido mousePosition en mouseX y mouseY para


tratar con mayor facilidad a cada eje por su cuenta. Ahora, miremos la rotación local
de la cámara.

Debug.Log(transform.localRotation);

Esto nos da una salida interesante. En el inspector podemos modificar la cámara y


jugar con los diferentes valores en el componente Transform.
Cuando arrastramos el valor de Y en la rotación, veremos unos números raros en la
consola. Las rotaciones de un objeto se almacenan como un cuaternión. Los
cuaterniones son tipos especiales de vectores que tienen cuatro valores. Estos
valores se utilizan tanto para rotar y orientar un objeto. Pero sin entrar demasiado
profundo en las matemáticas de cuaterniones, vamos a dejar esta opción un lado y
usaremos rotaciones de Euler, algo mucho más fácil de tratar.

Debug.log (transform.eulerAngles);

Hay un valor transform.eulerAngles que podemos leer en lugar de


cuaterniones. De esta manera obtenemos los números reflejados en el componente
Transform en el Inspector. Ahora ¿cómo podemos efectuar la rotación de la cámara
utilizando eulerAngles?

transform.eulerAngles = new Vector3 (30,0,0);

Para probar la declaración anterior, vamos a copiarla en la función Start.


El resultado nos dice que sí podemos apuntar la cámara hacia arriba y hacia abajo
usando este eje. Podríamos entonces en la función Update modificar el valor de
eulerAngles.x con el valor de mouseY y ver que pasa.

transform.eulerAngles = new Vector3 (mouseY, 0,0);

Sin embargo, esto no tiene el efecto deseado. Estamos limitados por el tamaño de
nuestro monitor. Cuando el ratón llega a la parte superior del monitor, estamos
atascados y no podemos mirar hacia arriba o hacia abajo más allá de lo que nuestro
monitor nos permita. Tal vez tenemos los datos equivocados!

5.1. Input Manager


¿Dónde está llegando entrada desde todos modos?
Vamos al Input manager siguiendo la ruta: Edit → Project Settings → Input y
aparecerá en el inspector.
El manager tiene un apartado para Mouse X, así como otros tipos de entrada. Tal
vez esto nos sirva, por lo tanto, antes de avanzar, debe quedar claro que la mayor
parte de esto se puede encontrar en la web. Encontramos en el manual de Unity un
ejemplo que nos muestra cómo utilizar Input.GetAxis ("Mouse X");, por lo que
debemos empezar por ahí.

void Update ()
{
float mouseX = Input.GetAxis("Mouse X");
Debug.Log(mouseX);
transform.eulerAngles = new Vector3(0, mouseX, 0);
}

Si tratamos de usar el código así, vamos a ver la cámara con un movimiento


tembloroso. La consola nos ofrece un interesante conjunto de números que salen de
la variable mouseX.
Vemos una fluctuación entre 0 y -0,1 o más cuando se mueve en una dirección y
valores positivos cuando se mueve en la otra dirección. Tal vez esto significa que
debemos estar agregando el número a la rotación de la cámara en lugar de tratar de
usarlo como un valor establecido. Para realizar este cambio, vamos a mantener el
valor mouseX fuera del ámbito de la función Update.

float mouseX;

void Update ()
{
mouseX += Input.GetAxis("Mouse X");
Debug.Log(mouseX);
transform.eulerAngles = new Vector3(0, mouseX,0);
}

Esto significa que tendremos una variable de clase que no se restablece cada vez
que la función Update se llama. Luego cambiamos el mouseX = input... por
mouseX += Input.GetAxis("MouseX");.
Probamos esto otra vez y nos da un comportamiento más agradable. Ahora que
tenemos esto, debemos hacer la misma configuración para mouseY.

float mouseX;
float mouseY;

void Update ()
{
mouseX + = Input.GetAxis("Mouse X");
mouseY + = Input.GetAxis("Mouse Y");
transform.eulerAngles = new Vector3(mouseY, mouseX,
0);
}
Hasta ahora, todo bien. Sin embargo, la mira invertida. Si esto sigue el diseño,
entonces no hay problema, pero es mejor añadir una opción para que el jugador
escoja si usar el mouse invertido o no.

float mouseX;
float mouseY;
public bool InvertedMouse;

void Update ()
{
mouseX + = Input.GetAxis("Mouse X");
if (InvertedMouse)
{
mouseY + = Input.GetAxis("Mouse Y");
} else
{
mouseY - = Input.GetAxis("Mouse Y");
}
Debug.Log(mouseX);
transform.eulerAngles = new Vector3(mouseY, mouseX,
0);
}

Todavía estamos en necesidad de algún tipo de movimiento controlado con el


teclado. Sin embargo, hay que acostumbrarse a la idea de separar nuestro código
en diferentes clases, así que vamos a crear una nueva clase sólo para tomar la
entrada desde el teclado.
Añadimos una nueva clase llamada FPSMove.cs a la cámara principal. Dentro
FPSMove.cs, podemos usar Input.GetKey () y comprobar si hay alguna
entrada del teclado.

void Update ()
{
if(Input.GetKey(KeyCode.W))
{
transform.position + = transform.forward;
}
}

Usaremos esto para añadir al transform.position un transform.forward,


pero ... la cámara se está moviendo muy rápido! Debemos solucionar este problema
con algún tipo de multiplicador de velocidad.

public float speed;


void Update ()
{
if(Input.GetKey(KeyCode.W))
{
transform.position + = transform.forward * speed;
}
}

Al hacer pública la variable speed, podemos jugar con el valor en el editor.

Este código nos permitirá desacelerar el movimiento a una tasa más controlable. Al
final el código completo de la clase FPSMove.cs Luciría así:

public float speed = 0.1f;

void Update ()
{
if (Input.GetKey(KeyCode.W))
{
transform.position + = transform.forward * speed;
}
if (Input.GetKey(KeyCode.S))
{
transform.position - = transform.forward * speed;
}
if (Input.GetKey(KeyCode.A))
{
transform.position - = transform.right * speed;
}
if (Input.GetKey(KeyCode.D))
{
transform.position + = transform.right * speed;
}
}

Hemos tomado transform.forward y utilizamos -= para moverla hacia atrás y


+= para adelante. De la misma manera, ya que no hay transform.left, tenemos
que utilizar transform.right. Usamos -= para mover a la izquierda y += para
mover hacia la derecha.
Si damos click derecho sobre forward, podemos ir a la declaración.

Nos daremos cuenta que también hay un up. Muchos otros valores estáticos se
encuentran aquí. Es importante aprender que Unity ha organizado los datos en cada
clase de tal manera que podamos hacer uso de ellos.
Bueno, ahora tenemos un par de scripts bastante útiles y sencillos, que podemos
adjuntar a cualquier objeto en una escena. Sin embargo, aún debemos evitar que la
cámara traspase el suelo. Si añadimos un Rigidbody y un Capsule Collider a
la cámara principal, el objeto chocará con la tierra y todo lo que posea un collider.

Los colisionadores de física son estructuras de datos complejas que solidifican los
objetos en un juego. Podemos utilizar estas estructuras para hacer que los objetos
se sientan y actúen pesados. Es posible que la cámara resbale un poco como si
estuviéramos caminando sobre el hielo. Esto se debe a que el Rigidbody tiene un
Drag (Fricción) de 0 por defecto. Cambiarlo a 1 evitará esto.

6. Instrucciones de salto
Supongamos que vamos a buscar un crayón de un color en específico en escritorio
de 10 cajones. Después de revisar en diferentes gavetas, encontramos el crayón
que queremos y dejamos de mirar a través del resto de los cajones. Hemos utilizado
una instrucción de salto y retomamos nuestro trabajo con el crayón en las manos.
Las palabras clave break , return y continue nos permiten dar saltos en el
código y así conrtrolar la ejecución del código. Miremos return por ahora.

6.1. Return
Esta palabra clave convierte una función en datos. Hay un par de condiciones que
deben cumplirse antes de que esta funcione. Hasta ahora, hemos estado utilizando
la palabra clave void para declarar el tipo de retorno de una función.

void MyFunction()
{
//código ...
}

En este caso, el uso del return será bastante simple.

void MyFunction()
{
//código ...
return;
}

Esta función no devuelve nada, pero eso tiene un significado más profundo. La
palabra clave void al inicio de la declaración de la función significa que esta función
no tiene un tipo de retorno. Si cambiamos la declaración, tenemos que asegurarnos
de que hay un valor de retorno que corresponda la declaración. Esto puede ser tan
simple como el fragmento de código siguiente.

int MyFunction()
{
//código ...
return 1;// 1 es un int
}
Esta función devuelve un int 1. Declarar una función con un valor de retorno
requiere que el tipo de retorno y el de la declaración concuerden.

6.1.1. Un ejemplo básico


Aquí está una clase que tiene una función declarada como int MyNumber que es
llamada desde la función Start.

using UnityEngine;
using System.Collections;
public class Example : MonoBehaviour
{
int MyNumber()
{
return 7;
}

void Start ()
{
int a = MyNumber();
print (a);
}
}

Cuando este bloque de código se une a la cámara principal en la escena, el número


7 es impreso en el panel de la consola. MyNumber devuelve un 7 cuando es
llamado. Cuando añadimos algunos parámetros a la función, podemos hacer la
declaración de retorno mucho más útil.

int MyAdd(int a, int b)


{
return a + b;
}
void Start ()
{
int a = MyAdd(6, 7);
print (a);
}

En este fragmento, tenemos int MyAdd (int a, int b), que luego le asignamos a
int a en la función Start. Al final se Imprime 13 en la consola. Podemos saltar un
paso para hacer que el código quede un poco más corto.

void Start ()
{
print (MyAdd(6, 7));
}

Este código produce el mismo 13 que se imprimen en el panel de la consola y nos


muestra que la función puede ser fácilmente utilizada como un valor.

6.2. Returning Objects


Podemos conseguir algo más interesante de una función una vez que esta retorna
algo más sustancial que un simple número. Por lo tanto, en el caso de la obtención
de un zombie de una función, nos gustaría definir Zombie primero.
Como un simple ejemplo, seleccionamos GameObject → Create Other →

Capsule para dejar caer una cápsula sencilla en la escena.


En el Inspector, seleccionamos Add Component → New Script, luego

lo nombramos Zombiey seleccionamos CSharp como el tipo de script. Este


representará lo que finalmente se convertirá en el comportamiento zombie. Por
supuesto, el trabajo de escribir la inteligencia artificial adecuada para los zombies es
algo que tendrá que esperar, por ahora tenemos una nueva clase Zombie.cs, que se
puede adjuntar como un componente a la cápsula en la escena.
En la cámara principal, adjuntaremos un nuevo script llamado ReturnZombie, que
servirá de ejemplo para probar una función que nos dará el zombie en la escena. En
el script ReturnZombie, vamos a añadir una función que devuelve un Zombie.

using UnityEngine;
using System.Collections;
public class ReturnZombie : MonoBehaviour
{
//retorna un Zombie
Zombie GetZombie()
{
return (Zombie) GameObject.FindObjectOfType
(typeof(Zombie));
}
}

6.3. Una clase es un tipo


Nuestra función que devuelve un Zombie, GetZombie (), utiliza una función simple
proporcionada en la clase GameObject llamada FindObjectOfType que requiere
un tipo como argumento. Las diferentes clases que creamos se convierten en un
nuevo tipo. Así como un int es un tipo, un Zombie es ahora un tipo basado en el
hecho de que hemos creado una nueva clase llamada Zombie.
Para informar a la función FindObjectOfType del tipo, usamos la función
typeof(Zombie), que le dice a la función que estamos buscando a un tipo
Zombie. Hay una sutil diferencia entre decirle que nos gustaría algo tipo Zombie a
un Zombie en específico.
Después de asegurarnos de que estamos obteniendo un Zombie de la función,
utilizamos return en la función Zombie GetZombie para cumplir con el tipo de
retorno. No estamos utilizando void ya que realmente si hay algo para devolver. El
uso de esto se hace más interesante si le sumamos en la función Update, el
siguiente código para dibujar una línea de la cámara al Zombie.

void Update ()
{
Debug.DrawLine(transform.position,
GetZombie().transform.position);
}

Debug.DrawLine comienza en una posición y va a otra posición. Si nos fijamos en


la Escena, verás una línea blanca entre la cámara y la cápsula con el componente
Zombie.
Esto significa que el GetZombie().transform.position está devolviendo el
valor como si la función fuera el mismísimo Zombie. Tener clases, o más bien
objetos, que se comunican entre si es una característica particularmente importante
de cualquier lenguaje de programación. El acceso a los miembros de otra clase a
través de una función es sólo una forma en que nuestros objetos pueden
comunicarse.
Esta función arrojaría un error si no hay zombies en la escena. Si desactivamos la
cápsula limpiando la casilla superior en el Inspector, efectivamente la cápsula deja
de “existir”.

Desmarcando aquí mientras el juego está en marcha traerá inmediatamente un


error.

NullReferenceException: Object reference not set to an


instance of an object
ReturnZombie.Update () (at Assets/ReturnZombie.cs:12)
Un NullReference es causada cuando el tipo de retorno de la función no tiene
nada que devolver. Los errores no son buenos para el rendimiento y debemos
manejar el problema con la mayor eficacia. Tenemos que hacer que nuestro función
sea más robusta.

void Update ()
{
Zombie target = GetZombie();
if (target != null)
{
Debug.DrawLine(transform.position,
target.transform.position);
}
}

Si añadimos una comprobación para ver si Zombie es nulo, vamos a ser capaces de
evitar dar a la función Debug.DrawLine datos inexistentes. Por lo tanto, creamos
una variable local a la función. Añadimos Zombie target = GetZombie(); y
ahora tenemos una variable que puede ser nulo o un Zombie.

6.4. Null NO ES Void


Hay UNA diferencia conceptual entre void, que es nada y null, que es más como
no hay algo. La palabra void significa que no hay intención de devolver algo en una
función, mientras null implica que puede haber algo. Por ejemplo al añadir
if(target != null) Existe la probabilidad de que target esté vacío o que
tenga un Zombie. Cuando target no es un Zombie, target es igual a null;
cuando no, es igual a la instancia Zombie.
Podemos añadir if(target != null) — que en español sería "si target no es
nulo", ejecutar el siguiente bloque de código. Este código alivia el error anterior.
7. Operadores y Condiciones
Los operadores condicionales permiten preguntar si más de un caso es cierto en un
if. Si vamos a nadar en un día frío, solo porque el cielo es claro, entonces
podríamos terminar congelados. Pero si condicionamos la ida a piscina
dependiendo de la temperatura y el estado del cielo.

7.1. Operadores Condicionales && y ||


La sentencia if en pseudocódigo podría ser algo como lo siguiente: Si (la
temperatura es caliente Y el cielo está claro) { ir a nadar }. Para traducir esto a C#,
tendríamos lo siguiente.

void Start ()
{
float temp = 90f;
bool sunny = true;
if (temp > 60 && sunny)
{
print("Hora de nadar!");
}
}

Si la temperatura está por encima de 60 y hace sol, entonces es el momento de ir a


nadar. El operador && se utiliza para unir varias condiciones. Este operador
pertenece a una familia de símbolos llamados operadores condicionales y este
operador en particular es el operador Y.

7.1.1. Ejemplo básico


Para tener una mejor comprensión de cómo funciona este operador, vamos a
romper las cosas en un ejemplo simplificado. Podríamos comenzar con dos
declaraciones if.

void Start ()
{
if (true)
{
if (true)
{
print("Esto podría ser más simple.");
}
}
}

El uso de más de una sentencia if debe parecer un poco torpe. La razón de utilizar
operadores condicionales es simplificar el número de sentencias if utilizadas.

void Start ()
{
if (true && true)
{
print("Ambos lados del && son verdaderos");
}
}

En el interior de la sentencia if hay dos casos booleanos diferentes. Sólo si ambos


lados del operador condicional && son verdaderos se ejecutará el bloque de código.
Si cualquiera de los lados es falso, entonces se omitirá el bloque.

void Start ()
{
if (false && true)
{
print("Esto no se escribirá jamas");
}
}
La declaración anterior tiene un valor falso y no será evaluado. Todas las
condiciones de los argumentos del caso de los estados tienen que ser verdaderos
para que las instrucciones encapsuladas se ejecuten. Esta lógica se puede ampliar
aún más con más de un operador condicional.

void Start ()
{
if (true && true && true)
{
print("Esto si se imprime!");
}
if (true && true && true && true && true && true &&
false)
{
print("pero esto no");//un false al final lo daña todo
}
}

No hay límites a la cantidad de argumentos que son aceptados en una sola


sentencia if. Esta ausencia de límite permite la toma de decisiones más complejas,
pero podemos hacer que las cosas sean aún más interesantes añadiendo en el otro
operador condicional O.

void Start ()
{
if (true || false)
{
print("Esto se imprime");
}
}

La || o doble barra, se utiliza para indicar el condicional O, que se utiliza para


evaluar en una sentencia if si cualquiera de las opciones es verdadera. El único
caso en el que O no permitirá que la sentencia if continue es cuando ambas partes
son falsas.

void Start ()
{
if (false || false)
{
print("No se imprime");
}
if (false || false || true)
{
print("Se imprime");
}
if (false || false || false || false || false || false ||
true)
{
print("Y también imprime!");
// solo necesita de un true para funcionar!
}
}

Cuando ambos lados del operador O son falsos, no se ejecuta el contenido de la


sentencia if. Si hay más de dos declaraciones, si una de las declaraciones es
verdadera, se ejecutarán las instrucciones contenidas. El operador Y y el operador
O pueden trabajar juntos, pero la lógica puede ser engañosa.

void Start ()
{
if (false || true && true)
{
print("Esto se imprime");
}
}
Es difícil adivinar de inmediato lo que podría suceder. Para hacer las cosas más
claras, podemos usar paréntesis como lo hicimos con los números.

void Start ()
{
if ((false || true) && true)
{
print("Vuelve e imprime");
}
}

La evaluación de esta sentencia if funciona de una manera similar a cómo


funcionan las matemáticas. Se evalúa la condición (false || true) que resulta
en true y se pasa a evaluar la condición [true] && true y cómo ambos lados de la
&& son verdaderos, la sentencia if se evalúa y se ejecuta el código entre las dos
llaves.
Los operadores condicionales se suelen utilizar al comparar números. Echemos un
escenario muy artificial, donde tenemos dos variables numéricas enemyHealth y
myHealth..

void Start ()
{
int enemyHealth = 10;
int myHealth = 1;
bool ImStronger = MyHealth > EnemyHealth;

if (ImStronger)
{
print("Yo le puedo ganar!");
}
}
Con el código anterior, el operador relacional nos dice que estamos claramente en
desventaja. Sin embargo, podemos añadir un poco de información adicional para
tomar una decisión mejor informada.

void Start ()
{
int enemyHealth = 10;
int myHealth = 1;
bool imStronger = myHealth > enemyHealth;
int enemyBullets = 0;
int myBullets = 11;
bool imArmed = myBullets > enemyBullets;
if (imStronger || imArmed)
{
print("Puedo ganar!");
}
}

Si estamos mejor armados que nuestro enemigo, entonces debemos tener una
mejor oportunidad de ganar. Por lo tanto, en este caso, si imStronger o imArmed
son verdaderas entonces es posible que "pueda ganar!".

8. Arrays: Un primer vistazo


Los arrays o matrices son listas de datos organizados. Pensemos en una lista
numerada que comienza en cero y se extiende cada vez que se añada algo a la
lista. Las matrices son útiles para cualquier número de situaciones porque son un
solo objeto de datos.
Por ejemplo, si deseamos almacenar un montón de puntuaciones altas, querríamos
hacer eso con una matriz. Al principio, es posible que deseemos tener una lista de
10 elementos. Se podría, en teoría, utilizar el siguiente código para almacenar cada
puntaje.
int score1;
int score2;
int score3;
int score4;
int score5;
int score6;
int score7;
int score8;
int score9;
int score10;

Para empeorar las cosas, si necesitamos procesar cada valor, habría que ocuparse
de cada variable por su nombre. Para comprobar si score2 es superior score1,
habría que escribir una función específicamente para comprobar esas dos variables.
Benditas sean las matrices.

8.1. Matrices de tamaño fijo


Un array se puede inicializar como una matriz de tamaño fijo, o un array puede ser
creado para ser de tamaño variable. Una matriz de tamaño fijo es más fácil de crear,
así que vamos a cubrir esa primero.
Las matrices son una parte fundamental de la programación informática, en general.
Sin matrices, las computadoras no serían capaces de hacer lo que mejor hacen:
repetir una tarea sencilla de manera rápida y consistente.

8.1.1. Un ejemplo básico


El concepto es simple en teoría, pero es mucho más fácil ver lo que está pasando, si
se utiliza el siguiente código y miramos lo que está pasando en el Inspector.

using UnityEngine;
using System.Collections;

public class ArraysAFirstLook : MonoBehaviour


{
public int[] scores = new int[10];
}

La inclusión de los corchetes indica al compilador que se está creando una matriz
de enteros y se llama scores. Debido a que estamos trabajando con un tipo int,
cada valor almacenado en la matriz se inicializa en 0. Un nuevo array int[]se
debe crear antes de que ser asignada a la matriz scores.

La mejor parte es que la matriz scores se maneja como un solo objeto, vamos a
ver lo que eso significa en un momento. En lugar de escribir una función que
procesa un valor numérico a la vez, podemos escribir una función que utilice la
matriz y todos sus valores a la vez.
Copiar int [10] crea una matriz con 10 elementos. Como se pueden imaginar, se
pueden hacer matrices grandes o pequeñas, cambiando el número entre corchetes.
No hay limitaciones en el tamaño que se puede asignar a una matriz. Algo de
sentido común debería aplicarse, ya que una matriz con muchos miles de millones
de valores podría no ser tan útil.
Cada fragmento de datos de la matriz se encuentra en lo que se llama un índice, un
número entero. Las matrices se pueden crear a partir de algo que no sea int
también.

public string[] strings = new string[10];


public float[] floats = new float[10];
Ambas declaraciones crean tipos válidos de matrices. También se le permite crear
nuevos tipos de datos y hacer arreglos de esos también. Una matriz se pueden
crear para cualquier tipo.

Los float, int y otros números en general tendrá 0 como valor predeterminado en
la matriz cuando se crean. Los string, por otro lado, se inicializan con null en la
matriz, o mejor dicho, no tienen ningún valor asignado. Hay que recordar que una
matriz puede contener un solo tipo en todas sus entradas. Por lo tanto, una matriz
de string sólo puede contener string, una matriz de int sólo puede contener
int y así sucesivamente.

public string[] TopScoreList = new string[10];

La convención de utilizar un plural hace entender la variable con más facilidad, pero
de ninguna manera es necesario para la identificación de una matriz.

public class MyClass


{
}
public MyClass[] myClasses = new MyClass[10];
Este script crea una nueva clase llamada MyClass, que para este ejemplo es una
clase vacía. se crea una matriz del tipo MyClass. Desafortunadamente, debido a
que Unity no sabe qué hacer con MyClass, la matriz no se muestra en el Inspector.
Más tarde, vamos a averiguar algunas maneras de hacer que este tipo de
información aparezca en el inspector.
Lo que hace de una matriz en Unity más interesante es cuando no asignamos un
número para establecer el tamaño de la matriz.

public GameObject[] myGameObjects;

Si simplemente dejamos una matriz sin asignar, somos libres de establecer el


tamaño después. Seleccionemos la cámara principal y damos clic en el icono de
candado en la parte superior derecha del panel Inspector. Esto evitará que el
Inspector cambie cuando se selecciona algo más en la escena. A continuación,
seleccionamos los demás objetos de la escena y los arrastramos hasta la variable
myGameObjects en el Inspector.
Esta acción establece el tamaño de la matriz una vez que los objetos han sido
arrastrados a la matriz. Sin embargo, esto no significa que el código es consciente
del tamaño de la matriz. El tamaño de una matriz no puede ser modificada una vez
que se ha creado. Por lo tanto, ¿cómo sabemos cuántos objetos hay en un array?
Esto se puede acceder directamente desde la propiedad .length de la matriz.

En la función de Start, podemos utilizar la siguiente declaración:

void Start ()
{
Debug.Log(myGameObjects.Length);
}

Le damos play y observamos impreso en la consola, el número de objetos que


habían sido arrastrados a la matriz.

6
UnityEngine.Debug:Log(Object)
ArraysAFirstLook:Start () (at Assets/ArraysAFirstLook.cs:16)

Hasta aquí todo bien, pero ¿cómo podemos usar esto? Ahora que tenemos una
gran variedad de objetos en la escena, que seremos capaces de manipular en un
bucle for.

void Start ()
{
Debug.Log(myGameObjects.Length);
for (int i = 0; i < myGameObjects.Length; i++)
{
myGameObjects [i].name = i.ToString();
}
}

Este código cambia los nombres de los objetos en la matriz uno por uno. La
propiedad de la matriz .length devuelve un valor entero que podemos utilizar de
varias maneras, el uso más práctico es para establecer el número de iteraciones de
un bucle for.

8.2. Foreach
Este bucle también refleja muy bien cómo se organizan los objetos de la matriz.
Como habíamos experimentado antes con bucles, podemos usar la matriz en una
multitud de formas. Una vez que hemos cambiado los nombres de cada objeto,
podemos mostrar su nombre.

8.2.1. Un ejemplo básico

void Start ()
{
Debug.Log(myGameObjects.Length);
for (int i = 0; i < myGameObjects.Length; i++)
{
myGameObjects [i].name = i.ToString();
}
foreach (GameObject go in myGameObjects)
{
Debug.Log(go.name);
}
}

La declaración foreach depende del tipo de datos que se encuentran en el interior


de la matriz. Por lo tanto, utilizamos GameObject go para a almacenar cada
miembro de la matriz myGameObjects en cada iteración del bucle foreach. Si
quisiéramos iterar sobre una matriz de un tipo diferente, tendríamos que cambiar los
parámetros del bucle foreach. Por lo tanto, para repetir una matriz de enteros que
fue declarada int[] myInts;, vamos a necesitar usar foreach(int i in
myInts) para iterar sobre cada miembro de la matriz.

8.3. Inicialización dinámica

void Start ()
{
float[] dynamicFloats = new float[10];
}

También podemos inicializar un nuevo array en una función. Esto significa que la
matriz sólo existe en el ámbito de la función y no se puede acceder desde fuera de
esa función. Es importante saber que el tamaño de una matriz se determina antes
de su uso. Cuando la matriz se declara en esta forma, el número de objetos que la
matriz puede contener debe estar definido.
La inicialización se divide en dos declaraciones. La primera línea le dice a C# que
estamos creando una variable float[] identificada como dynamicFloats.
Despues, tenemos que llenar esta variable con una nueva matriz. A esta se le
asigna una nueva matriz tipo float con 10 índices.
No podemos asignarle a la variable una matriz de diferente tipo.

Float[] dynamicFloats;
dynamicFloats = new int[10]; // ERROR
Por otro lado, podemos rellenar una matriz con información predeterminada.

public int[] primos = new int[]{1, 3, 5, 7, 11, 13, 17};

La declaración anterior establece una matriz que tiene siete miembros y asigna a
cada indice el valor de un número primo.

8.4. Usando el bucle while en matrices


La iteración a través de nuestra matriz es bastante simple. Hemos cubierto algunos
bucles básicos que manejan matrices bastante bien. Si usamos un bucle while,
podemos procesar cada valor almacenado en una matriz fija.

void Start ()
{
int[] scores = new int[10];
int i = 0;
while(i < 10)
{
print(scores[i]);
i++;
}
}

En este punto, todos los valores son efectivamente cero, por lo que vamos a obtener
10 ceros impresos en la consola. Sin embargo, hay algunas cosas interesantes que
señalar aquí. En primer lugar, int i se inicializa a 0 por delante del bucle while,
por lo que las matrices comienzan en cero. La siguiente cosa interesante es cómo
se accede a los números almacenados en scores[].

8.4.1. Estableciendo valores para las matrices

scores[0] = 10;

Podemos establecer el valor de cada índice de las puntuaciones en un valor


específico. Cuando la matriz scores[] se inicializa como int [10],
scores[i]pasa a tener 10 espacios. Para acceder a cada espacio, se utiliza un
número del 0 al 9 entre corchetes para obtener y definir cada valor.

void Start ()
{
int[] scores = new int[10];
int i = 0;
while(i < 10)
{
scores[i] = Random.Range(0, 100);
print (scores[i]);
i++;
}
}

Con este código, estamos usando una clase llamada Random y el uso de su función
miembro Range, que retorna un valor aleatorio entre 0 y 100, valor asignado al
index equivalente a i. Al inicio i se establece en 0. El bucle comienza con
scores[0] se establece en un número aleatorio entre 0 y 100.
Al final del bloque, la i se incrementa en 1 y el bucle while comienza de nuevo. La
próxima vez, sin embargo, estamos estableciendo a scores[1] un número
aleatorio entre 0 y 100.
8.4.2. Obteniendo valores de una matriz
Al igual que cuando lo establecimos, podemos acceder al valor de cada espacio en
la matriz

void Start ()
{
int[] scores = new int[10];
int i = 0;
while(i < 10)
{
scores[i] = Random.Range(0, 100);

//obtenemos el valor de la matriz


int score = scores[i];
print (score);
i++;
}
}

En la línea int score = scores [i];, estamos creando una variable entera a la
que se le asignará el valor en el índice actual.

Este documento es una traducción y modificación del libro “Learning C# with Unity
3D” de Alex Okita.

Potrebbero piacerti anche