1

Índice:
Introducción ………………………………………………………………………………………………………………………………………………………….Pág.3 Capitulo 1.1-1.2………………………………………………………………………………………………………………………………………………………Pág.4 Capitulo 1.3 ……………………………………………………………………………………………………………………………………………………………Pág.9 Capitulo 1.4 ……………………………………………………………………………………………………………………………………………………………Pág.12 Capitulo 1.5 ……………………………………………………………………………………………………………………………………………………………Pág.15 Capitulo 1.6 ……………………………………………………………………………………………………………………………………………………………Pág.19 Capitulo 1.7 ……………………………………………………………………………………………………………………………………………………………Pág.24 Capitulo 1.8 ……………………………………………………………………………………………………………………………………………………………Pág.27 Capitulo 1.9 ……………………………………………………………………………………………………………………………………………………………Pág.32 Capitulo 1.10 …………………………………………………………………………………………………………………………………………………………Pág.35 Capitulo 1.11 …………………………………………………………………………………………………………………………………………………………Pág.38 Capitulo 1.12 …………………………………………………………………………………………………………………………………………………………Pág.42 Capitulo 1.13 …………………………………………………………………………………………………………………………………………………………Pág.47 Capitulo 1.14 …………………………………………………………………………………………………………………………………………………………Pág.52 Capitulo 1.15 …………………………………………………………………………………………………………………………………………………………Pág.54 Capitulo 1.16 …………………………………………………………………………………………………………………………………………………………Pág.56 Capitulo 1.17…………………………………………………………………………………………………………………………………………………………Pág.58 Capitulo 1.18 …………………………………………………………………………………………………………………………………………………………Pág.62 Capitulo 1.19 …………………………………………………………………………………………………………………………………………………………Pág.66 Capitulo 1.20 …………………………………………………………………………………………………………………………………………………………Pág.69

2

Introducción
Bienvenidos, me presento. Soy cotolonco, usuario de Taringa. Un día (mediados de Julio/2012) estuve buscando tutoriales Fps de Unity, ya que los que había eran ejemplos ya hechos o no explicaban casi nada, decidí buscar tutoriales en Inglés, ya que en Español no hay mucho, y me encontré con ETeeskiTutorial, que enseña lo básico de Unity, ya sean las transform, Quaternion, la interfaz, javascript básico, entre otras, y entre eso hay tutoriales de Fps, y explica sencillamente bien, incluso no hay que saber inglés para darse cuenta de las cosas. Empece a ver los tutoriales, y decidi hacerlo pdf primero, para que no se me olvidara, y luego se me ocurrio compartirlo en una comunidad de Taringa en lla que estoy, donde hay bastante gente. Les dejo el canal de Eteeski: http://www.youtube.com/user/ETeeskiTutorials Este documento, en esta primera parte, estarán los videos tutorials FPS1.1 hasta el FPS1.20 Aquí les dejo un video de todo lo que conseguiremos con estos 20 primeros tutoriales: http://www.youtube.com/watch?v=Y4O3BRbEpG4 (Destaco que es una pistola de disparo rápido, solo para ejemplificar y disculpen lo lento del video, iba fluido cuando lo grabé). Cada video tutorial tendrá su capítulo, el cual estará compuesto por el video original en inglés de ETeeski, para verlo online. Una pequeña intro de qué se verá en el capítulo, los pasos a seguir, los script por parte o completos dependiendo el caso, y una explicación de este si lo requiere. Resumen por mí en español, con lo más importante. Requisitos Antes de Continuar: Conocer la interfaz de Unity. Saber como crear objetos vaciós, cubos, crear terrenos, lo que es un GameObject, entre otras. Diferenciar de un Padre, un hijo, en qué afecta, etc. Variables básicas acerca del transform, position, rotation, quaternion, definir variables, etc. Saber qué es un Tag, un Input, entre otras y como definirlo o modificarlo. Conocimientos básicos de JavaScript, (Mayor importancia a sentencia if-else) y su estructura.

3

Cualquier duda antes de continuar, pueden contactarme a: cotolonco@outlook.com coto.kun@gmail.com O también a ETeeski, enviándole un MP por youtube: http://www.youtube.com/user/ETeeskiTutorials Nota: Hay líneas de código que no se alcanzan a escribir en una línea del documento.Fíjense en eso. Les Recomiendo lean con cuidado, y pregunten.

4

Capítulo 1.1 Visión General
Ver en Inglés: http://www.youtube.com/watch?v=MPN5kYkdYUY&feature=plcp Empezamos con estos capítulos en español. Bueno básicamente aquí se habla de lo que se tratará en el Tema, del FPS. Les recomiendo que vean el video. Bueno, ETeeski en cada video dedica su primera parte del video a explicar qúe se hará y como se hará sin usar Unity aún. Y la segunda parte del video muestra el proceso en Unity.

Capítulo 1.2 Mouse Look Script (Mirar moviendo el ratón)
Ver en Inglés: http://www.youtube.com/watch?v=3JsuldsGuNw&feature=plcp Esto es lo más primordial en un FPS, destaco anteriormente que si alguna cosa no sale como uno quiso, pueden ser varias razones, pero yo les recuerdo que pueden ser, el valor que toma una variable o alguna línea o código que posiblemente estuvo mal redactada o el posicionamiento/rotación de algún GameObject. Vamos a ver 2 cosas aquí, primero como manejar la cámara a través del mouse y lo segundo como suavizar su movimiento. Empecemos: Antes de comenzar creen un pequeño terreno (A partir de un Plano, Cubo o del Terrain Editor). Agreguen a la Escena Unos 5 cubos, ya sea en el suelo o en el aire y además agreguen una luz direccional. Renombren los Game Objects como les acomode. Debe quedar más o menos como se muestra en la imagen:

Seguido de esto, creamos un objeto Vacío (GameObject -> Create Empty). Lo Renombramos como “Escenario” o “Level Stuff(Como se menciona en el video)”. Luego arrastren todos los cubos que colocaron, incluyendo su terreno dentro de su GameObject Vacío. Esto significará que el GameObject “Escenario” (En mi caso) Será Padre de Todos los GameObjects que se hayan arrastrado a este. ¿Por qué se hace esto? Principalmente para ordenarse y además para decir “Si nosotros pisamos al Padre (El Escenario), estamos pisando algún hijo de este. Entonces Ahora podremos saltar” (Caso hipotético, no se usa para estos tutoriales). Para saber que algo es padre de GameObject/s basta con que esté así:

5

Ahora Vamos a Project, y creamos una Carpeta llamada “Scripts” (O como quieran). Pueden Crear dentro de esta carpeta otra carpeta que se llame “Player Scripts” (En mi caso lo hice así). Crean un Nuevo JavaScript y lo renombran como “MouseLook”. Antes de proceder a armar el Script, Arrastren el JavaScript al GameObject “Cámara” o “Main Camera” por defecto. Creando Script: Para Comenzar crearemos 8 variables: ------------------------------------------------------------------------------------------var lookSensitivity : float=5; var yRotation : float; var xRotation : float; var currentYRotation : float; var currentXRotation : float; var yRotationV:float; var xRotationV:float; var lookSmoothDamp:float=0.1; -------------------------------------------------------------------------------------------

Antes de seguir primero explicaré para que se utilizara cada una. lookSensitivity será la sensibilidad del mouse. En defecto es 5, a mayor sea más rápido rotará la cámara. xRotation e yRotation son las rotaciones en los ejes Y y X respectivamente. Pero, se preguntarán por qué xRotation representa la rotación en el eje Y y yRotation la rotación en el eje X. Es Simple, si ustedes van a los GameObjects y seleccionan la cámara, Verán que en la sección Transform está su componente la Rotación, y Si ustedes cambian el valor de X (Representando xRotation), entonces la cámara mirará verticalmente a otro lado. Y con el valor de Y (Representando a yRotation), la camará comenzara a mirar horizontalmente. Las 5 Variables Restantes las explicaremos después. Ahora vamos a Crear El script: ------------------------------------------------------------------------------------------var lookSensitivity : float=5; var yRotation : float; var xRotation : float; var currentYRotation : float; var currentXRotation : float; var yRotationV:float; var xRotationV:float; var lookSmoothDamp:float=0.1; function Update () { //Mirar con el Mouse: yRotation+=Input.GetAxis("Mouse X") * lookSensitivity; //Movimiento horizontal de la cámara xRotation-=Input.GetAxis("Mouse Y") * lookSensitivity;//Movimiento vertical de la cámara //Rotación final de la cámara transform.rotation=Quaternion.Euler(xRotation, yRotation, 0); } ------------------------------------------------------------------------------------------Antes de seguir explicaré lo que es Quaternion.Euler Quaternion es un constructor que representa las rotaciones mucho más suavizadas y rápidas ya que se involucra con números complejos. Euler es una Función Clase o Class Function. Esta Función pide 3 parámetros o valores
(x: float,y : float,z: float). Devuelve una rotación que rota z grados alrededor del eje z, x grados

6

alrededor del eje x, e y grados alrededor del eje y.(En ese orden) Pueden ver el Script Reference de aquí: http://docs.unity3d.com/Documentation/ScriptReference/Quaternion.Euler.html Lo otro que pueden notar es que xRotation se le va restando y no sumando como a yRotation. Bueno simplemente si modifican y lo dejan de “-=” a “+=” cuando muevan el cursor hacia arriba la cámara verá hacia abajo y viceversa.

Pueden probar el juego y verán que funciona, pero notarán que los movimientos son un poco bruscos, por lo que las demás variables ayudarán a suavizar el movimiento. Nota: Si agregan arriba de la function Update() este código: ------------------------------------------------------------------------------------------function Awake() { Screen.showCursor=false; } ------------------------------------------------------------------------------------------Podrán esconder el Cursor mientras juegan. Vamos a la segunda parte del script: ------------------------------------------------------------------------------------------var lookSensitivity : float=5; var yRotation : float; var xRotation : float; var currentYRotation : float; var currentXRotation : float; var yRotationV:float; var xRotationV:float; var lookSmoothDamp:float=0.1; function Awake() { Screen.showCursor=false; } function Update () { //Mirar con el Mouse: yRotation+=Input.GetAxis("Mouse X") * lookSensitivity; //Movimiento horizontal de la cámara xRotation-=Input.GetAxis("Mouse Y") * lookSensitivity;//Movimiento vertical de la cámara //Mirar con el Mouse más suavizado: currentXRotation=Mathf.SmoothDamp(currentXRotation,xRotation,xRotationV,lookSmoothDamp); currentYRotation=Mathf.SmoothDamp(currentYRotation,yRotation,yRotationV,lookSmoothDamp); //Rotación final de la cámara

7

transform.rotation=Quaternion.Euler(currentXRotation, currentYRotation, 0); } ------------------------------------------------------------------------------------------Veamos qué es eso de Mathf.SmoothDamp Es una Class Function (así como colocar Mathf.Cos, que sería el coseno de un número) que pide máximo 6 parámetros. En este caso agregamos los 4 primeros. (current : float, target : float, ref currentVelocity : float, smoothTime: float,maxSpeed: float = Mathf.Infinity, deltaTime : float = Time.deltaTime) El Primer parámetro es la posición actual. El segundo parámetro es la posición a la que queremos buscar, el Tercer parámetro es la velocidad actual. Y el Cuarto parámetro es el tiempo que tardará en llegar del primer parámetro al tercer parámetro. Pueden ver el Script Reference aquí: http://docs.unity3d.com/Documentation/ScriptReference/Mathf.SmoothDamp.html Por lo tanto, queremos llegar de la rotación xRotation, yRotation a la rotación currentXRotation, currentYRotation, en un tiempo determinado por lookSmoothDamp. Ahora surge un último problema que si lo pueden notar si mueven el cursor del mouse hacia arriba a cada momento, la cámara comenzará a rotar sobre sí mismo, como si fuera una rueda. Por si no se entiende pueden apreciarlo en este video de ETeeski en el minuto 25:00. http://www.youtube.com/watch?v=3JsuldsGuNw&feature=plcp Para Arreglar esto, terminaremos el Script quedando finalmente así: ------------------------------------------------------------------------------------------var lookSensitivity : float=5; var yRotation : float; var xRotation : float; var currentYRotation : float; var currentXRotation : float; var yRotationV:float; var xRotationV:float; var lookSmoothDamp:float=0.1; function Awake() { Screen.showCursor=false; } function Update ()

8

{ //Mirar con el Mouse: yRotation+=Input.GetAxis("Mouse X") * lookSensitivity; //Movimiento horizontal de la cámara xRotation-=Input.GetAxis("Mouse Y") * lookSensitivity;//Movimiento vertical de la cámara xRotation=Mathf.Clamp(xRotation,-90,90); //Mirar con el Mouse más suavizado: currentXRotation=Mathf.SmoothDamp(currentXRotation,xRotation,xRotationV,lookSmoothDamp); currentYRotation=Mathf.SmoothDamp(currentYRotation,yRotation,yRotationV,lookSmoothDamp); //Rotación final de la cámara transform.rotation=Quaternion.Euler(currentXRotation, currentYRotation, 0); } ------------------------------------------------------------------------------------------Para finalizar, explicaré que es Mathf.Clamp Es una Class Function que pide 3 parámetros: el valor, el mínimo, el máximo. La función devuelve un valor que esté entre el mínimo y el máximo (Incluyéndolos). Pueden ver el Script Reference Aquí: http://docs.unity3d.com/Documentation/ScriptReference/Mathf.Clamp.html Ahora prueben y verá que funciona perfectamente.

9

Capítulo 1.3 Caminar/Correr básicamente
Ver en Inglés: http://www.youtube.com/watch?v=5pkeRlpjFzQ&feature=plcp Quiero aclarar antes de iniciar el capítulo que no es correr presionando shift, o caminar lentamente presionando shift, si no que tu movimiento natural es una aceleración, por lo que si más rato mantienes mantenido WASD o las flechas del teclado, cada vez aumentarás más la velocidad. Sabiendo esto comencemos: Antes de comenzar con los Script. Agreguen a la Escena una cápsula y llámenla PlayerCapsule y arrastren la Cámara, de tal manera que sea hijo de la cápsula. Cuando hayan arrastrado la cámara. Ubiquen su posición (x,y,z) en (0,0.7,0). Como se muestra en la imagen:

10

Ahora seleccionen al PlayerCapsule, vayan a Component -> Physics -> Rigidbody. Para añadirle un rigidbody al PlayerCapsule. Antes de seguir, como ahora a nuestro jugador le afectan las físicas. No queremos que se caiga como un Pin de los juegos de Bolos. Por lo tanto agregamos esto:

Así estará siempre “de pie”. Ahora si podremos continuar. Creen un nuevo JavaScript y llámenlo PlayerMovement (Arrástrenlo al PlayerCapsule). Dentro del Script crearemos 2 variables. ------------------------------------------------------------------------------------------var walkAcceleration: float=5; var cameraObject:GameObject; function Update () { transform.rotation=Quaternion.Euler(0,cameraObject.GetComponent(MouseLook).currentYRotation,0); } ------------------------------------------------------------------------------------------Ya sabemos que es Quaternion.Euler, Primero definiremos cual es el GameObject. Cuando guarden el Script, arrastren el GameObject cámara al cameraObject del script del PlayerCapsule, para que pueda funcionar, ya que la variable cameraObject es de tipo GameObject. De tal forma que quede así:

11

Ahora explicaré qué significa ese parámetro que le dimos cameraObject.GetComponent(MouseLook).currentYRotation. Primero ingresamos al cameraObject qué es un GameObject (En este caso la cámara que arrastramos), Ingresamos al Script MouseLook y obtenemos el valor de currentYRotation. Así el Player mirará hacia donde mira la cámara. Así no tendremos algún problema al caminar. (Si prueban el juego ahora no notarán mucha diferencia). Ahora vamos al Script final: ------------------------------------------------------------------------------------------var walkAcceleration: float=5; var cameraObject:GameObject; function Update () { //Rotar Hacia donde ve la cámara transform.rotation=Quaternion.Euler(0,cameraObject.GetComponent(MouseLook).currentYRotation,0); //Añade una Fuerza Relativa rigidbody.AddRelativeForce(Input.GetAxis("Horizontal")*walkAcceleration,0,Input.GetAxis("Vertical")*walkAccelerati on); } ------------------------------------------------------------------------------------------Expliquemos qué es rigidbody.AddRelativeForce. Para empezar solo puede ser usado por un GameObject que posea un rigidbody. Además es una función que recibió 3 parámetros. El primero, le añade una fuerza relativa a través del eje x. El segundo, a través del eje y. Y finalmente el tercero a través del eje z. Aquí el Script Reference: http://docs.unity3d.com/Documentation/ScriptReference/Rigidbody.AddRelativeForce.html Si probamos el juego, y presionamos W o algún otro botón, notaremos que irá lento al principio y comenzará a aumentar su velocidad. Si cambian walkAcceleration a 10 irá más rápido pero si sueltan el botón el Playercapsule se deslizará. En el próximo capítulo veremos como arreglarlo.

12

Capítulo 1.4 Estableciendo una velocidad máxima
Ver en Inglés: http://www.youtube.com/watch?v=GddQmaFLZ00&feature=plcp En este tutorial, importaremos una textura para colocarla en nuestro terreno y aprenderemos a limitar la velocidad de nuestro jugador. Comencemos:

Agranden su terreno de suelo (En mi caso el Scale de mi suelo es 200, 1,200). Creen una carpeta que se llame Materiales (Opcional). Busquen una textura, para el suelo. Yo usaré una de piedra como ETeeski, esta de aquí:

13

Dentro de la carpeta creada Materiales, creen un Material (Renómbrenlo como TexturaPiedra). Luego Arrastren la imagen a Unity, y colóquenla la imagen en el Material (TexturaPiedra). Y éste Material Arrástrenlo a su GameObject Suelo. Para que no sea una imagen super larga de la textura Pierda, cambien el Tiling del Material.

Obviamente luego de esto, tendrían que ver la textura en el suelo repetidamente. O donde lo hayan colocado, en la Escena y en el Juego. Abrimos nuestro script anterior PlayerMovement. Y lo Editamos por esto: ------------------------------------------------------------------------------------------var walkAcceleration: float=5; var cameraObject:GameObject; var maxWalkSpeed: float=20; @HideInInspector var horizontalMovement:Vector2; function Update () { horizontalMovement=Vector2(rigidbody.velocity.x,rigidbody.velocity.z); if (horizontalMovement.magnitude>maxWalkSpeed) { horizontalMovement=horizontalMovement.normalized; horizontalMovement*=maxWalkSpeed; } rigidbody.velocity.x=horizontalMovement.x; rigidbody.velocity.z=horizontalMovement.y; //Rotar Hacia donde ve la cámara transform.rotation=Quaternion.Euler(0,cameraObject.GetComponent(MouseLook).currentYRotation,0); //Añade una Fuerza Relativa rigidbody.AddRelativeForce(Input.GetAxis("Horizontal")*walkAcceleration,0,Input.GetAxis("Vertical")*walkAcceleration); } ------------------------------------------------------------------------------------------Vamos explicando, primero agregamos una variable llamada maxWalkSpeed, que será la velocidad máxima del PlayerCapsule. Además agregamos una variable llamada horizontalMovement, que es un Vector2. Por lo tanto podrá guardar 2 variables. Y podremos descomponerlo en horizontalMovement.x y horizontalMovement.y. @HideInInspector, como dice su nombre simplemente esconde la variable de abajo en el inspector. Sigamos analizando el Script: A horizontalMovement le entregaremos 2 variables. La rigidbody.velocity.x y rigidbody.velocity.z que son la velocidad en x y en z del rigidbody (la velocidad en y sería saltando). Recuerden que la velocidad es un vector. Por lo tanto, luego nos preguntamos si la magnitud del horizontalMovement (La magnitud es la rapidez resultante) es mayor a la maxWalkSpeed. Si eso es cierto ocurren 2 cosas: Primero con horizontalMovement.normalized cambiamos la magnitud del vector por 1, manteniendo su dirección.

14

Script Reference aquí: http://docs.unity3d.com/Documentation/ScriptReference/Vector2-normalized.html Segundo Le multiplicamos por maxWalkSpeed para entregarle el límite de la velocidad. Pero con eso no se soluciona el problema. Así que debemos entregarle un nuevo valor a las velocidades en x y z del rigidbody. rigidbody.velocity.x=horizontalMovement.x; rigidbody.velocity.z=horizontalMovement.y; Si prueban el juego podrán notar que en un momento no aumenta la velocidad. Pueden cambiar el valor de maxWalkSpeed a 10 y notarán más el resultado.

15

Capítulo 1.5 Saltando de Manera Correcta
Ver en Inglés: http://www.youtube.com/watch?v=eSnzuRgrN_8&feature=plcp Tal como dice el título, veremos como saltar de manera correcta. Además de que podrá saltar cuando el vector normal que se genera entre el jugador y lo que pisas forma algún ángulo que sea menor al máximo (tampoco podrás saltar si chocaste con la cabeza un objeto, también sirve). Reabrimos nuestro Script PlayerMovement y lo editamos de tal forma que quede así: ------------------------------------------------------------------------------------------//Variables Movimiento: var walkAcceleration: float=10; var cameraObject:GameObject; var maxWalkSpeed: float=20; @HideInInspector var horizontalMovement:Vector2; //Variables Salto: var jumpVelocity: float=20; @HideInInspector var grounded:boolean=false; var maxSlope:float=60; function Update () { //Movimiento: horizontalMovement=Vector2(rigidbody.velocity.x,rigidbody.velocity.z);

if (horizontalMovement.magnitude>maxWalkSpeed) { horizontalMovement=horizontalMovement.normalized; horizontalMovement*=maxWalkSpeed; } rigidbody.velocity.x=horizontalMovement.x; rigidbody.velocity.z=horizontalMovement.y; transform.rotation=Quaternion.Euler(0,cameraObject.GetComponent(MouseLook).currentYRotation,0); rigidbody.AddRelativeForce(Input.GetAxis("Horizontal")*walkAcceleration,0,Input.GetAxis("Vertical")*walkAccel eration); //Salto: if (Input.GetButtonDown("Jump")) rigidbody.AddForce(0,jumpVelocity,0); } ------------------------------------------------------------------------------------------Primero notemos las nuevas 3 variables jumpVelocity, grounded, maxSlope. jumpVelocity, representa la velocidad con la que salta (Velocidad del eje y), grounded indicará si está habilitado para saltar. Y finalmente maxSlope, indica el ángulo en grados para limitarnos el tipo de inclinación podremos subir. Luego se agregó 2 líneas. La primera Input.GetButtonDown regresa verdadero si el botón fue presionado (solo con una vez basta, no mantenido). Y “Jump” es el botón space por defecto definido en el apartado (Edit->ProjectSetting->Input). Y si presionamos space entonces, le aplicamos una fuerza hacia arriba. Si presionan space, en el juego tal vez vean que no salta mucho. Eso es porque, jumpvelocity es muy bajo, auméntenlo a 300 o 400 para fijarse mejor. Claro que hasta ahora podremos saltar incluso en el aire. Antes de seguir creen un cubo y transfórmenlo en rampa, y arrástrelo al GameObject Escenario (Opcional). Pueden copiar los datos de esta imagen:

16

Si se fijan la rotación en z es 315, es decir que se forma un ángulo de 45°. Ahora modificamos el Script, para que finalmente quede así: ------------------------------------------------------------------------------------------//Variables Movimiento: var walkAcceleration: float=10; var cameraObject:GameObject; var maxWalkSpeed: float=20; @HideInInspector var horizontalMovement:Vector2; //Variables Salto: var jumpVelocity: float=20; @HideInInspector var grounded:boolean=false; var maxSlope:float=60; function Update () { //Movimiento: horizontalMovement=Vector2(rigidbody.velocity.x,rigidbody.velocity.z); if (horizontalMovement.magnitude>maxWalkSpeed) { horizontalMovement=horizontalMovement.normalized; horizontalMovement*=maxWalkSpeed; } rigidbody.velocity.x=horizontalMovement.x; rigidbody.velocity.z=horizontalMovement.y; transform.rotation=Quaternion.Euler(0,cameraObject.GetComponent(MouseLook).currentYRotation,0); rigidbody.AddRelativeForce(Input.GetAxis("Horizontal")*walkAcceleration,0,Input.GetAxis("Vertical")*walkAccel eration); //Salto: if (Input.GetButtonDown("Jump") && grounded) rigidbody.AddForce(0,jumpVelocity,0); } function OnCollisionStay(collision:Collision) { for (var contact:ContactPoint in collision.contacts)

17

{ if (Vector3.Angle(contact.normal, Vector3.up)<maxSlope) grounded=true; } } function OnCollisionExit() { grounded=false; } ------------------------------------------------------------------------------------------Ahora veamos, primero modificamos esta parte: if (Input.GetButtonDown("Jump") && grounded), porque ahora nos preguntamos si está grounded o no. Que significa si podrá saltar. Luego de esto, agregamos 2 nuevas funciones OnCollisionStay y OnCollisionExit. OnCollisionStay se ejecuta cada frame cada vez que nosotros estemos en colisión con un collider o rigidbody. Y OnCollisionExit se ejecuta en el instante que dejemos de colisionar con el collider o rigidbody. El parámetro collision que se le da a OnCollisionStay, recibe información del collider/rigidbody con el cual está colisionando. Luego llamamos esta extraña parte: for (var contact : ContactPoint in collision.contacts) Que simplemente significa: para los puntos de contacto (var contact:ContactPoint) con el collider/rigidbody (collision.contacts), el cual almacena el punto de contacto, una normal, y los collider que colisionan. Script Reference: http://docs.unity3d.com/Documentation/ScriptReference/Collision-contacts.html La línea siguiente: if (Vector3.Angle(contact.normal, Vector3.up)<maxSlope) Nos preguntamos si el Ángulo (Vector3.Angle) formado desde el vector normal que se forma entre ambos collider y el Vector (0,1,0) es menor al maxSlope. Así sabremos si realmente podremos saltar dependiendo de la inclinación de la rampa en grados. Por lo tanto si es cierto, grounded=true. Luego OnCollisionExit nos indicará que ya dejó de colisionar y por lo tanto ya no podremos saltar, haciendo grounded=false. Ahora si intentan saltar, lo podrán hacer sin problemas. Pero si colocan una pared y saltan al lado de esta, se “estancarán”. Esto se resolverá en el Capítulo 1.7.

18

19

Capítulo 1.6 Añadiendo Fricción al Movimiento
Ver en Inglés: http://www.youtube.com/watch?v=SUKddKIgCUQ&feature=plcp Si ustedes habrán notado. Nuestro jugador al soltar las teclas de movimiento sigue deslizándose hasta detenerse y toma bastante tiempo. Por lo tanto lo que haremos en este capítulo será suavizar el movimiento hasta detener al jugador. Para hacer eso abrimos nuestro script PlayerMovement y lo modificamos: ------------------------------------------------------------------------------------------//Variables Movimiento: var walkAcceleration: float=10; var walkDeacceleration: float=5; var cameraObject:GameObject; var maxWalkSpeed: float=20; @HideInInspector var horizontalMovement:Vector2; //Variables Salto: var jumpVelocity: float=20; @HideInInspector var grounded:boolean=false; var maxSlope:float=60; function Update () { //Movimiento: horizontalMovement=Vector2(rigidbody.velocity.x,rigidbody.velocity.z); if (horizontalMovement.magnitude>maxWalkSpeed) { horizontalMovement=horizontalMovement.normalized; horizontalMovement*=maxWalkSpeed; } rigidbody.velocity.x=horizontalMovement.x; rigidbody.velocity.z=horizontalMovement.y; if (Input.GetAxis("Horizontal")==0 && Input.GetAxis("Vertical")==0 && grounded) { rigidbody.velocity.x /=walkDeacceleration; rigidbody.velocity.z /=walkDeacceleration; }

transform.rotation=Quaternion.Euler(0,cameraObject.GetComponent(MouseLook).currentYRotation,0); rigidbody.AddRelativeForce(Input.GetAxis("Horizontal")*walkAcceleration,0,Input.GetAxis("Vertical")*walkAccel eration); //Salto: if (Input.GetButtonDown("Jump") && grounded) rigidbody.AddForce(0,jumpVelocity,0); } function OnCollisionStay(collision:Collision) { for (var contact:ContactPoint in collision.contacts) { if (Vector3.Angle(contact.normal, Vector3.up)<maxSlope) grounded=true; } } function OnCollisionExit() { grounded=false; } ------------------------------------------------------------------------------------------Ya ha quedado un poco largo el Script, pero si se fijan bien. Agregamos una nueva variable llamada walkDeacceleration (Por defecto 5) y se agregó 2 nuevas líneas: if (Input.GetAxis("Horizontal")==0 && Input.GetAxis("Vertical")==0 && grounded) { rigidbody.velocity.x /=walkDeacceleration; rigidbody.velocity.z /=walkDeacceleration; } ¿Qué significa que el Input.GetAxis(“Horizontal”)==0? Eso es equivalente a preguntarse si no se está presionando el botón. Es decir si no se está apretando ni WASD, ni las flechas (Que vienen por defecto en el Axis Horizontal y Vertical). Entonces las velocidades x e y se iran reduciendo, en este caso dividiendo hasta llegar a 0, si es que además está tocando suelo. Si ustedes prueban se darán cuenta que el jugador frena casi de golpe. Para suavizarlo un poco bajen el valor de walkDeacceleration a 1.05 o 1.1.

20

21 ¿Qué tal si suavizamos más el movimiento? Antes de saber cómo, así queda el Script. ------------------------------------------------------------------------------------------//Variables Movimiento: var walkAcceleration: float=10; var walkDeacceleration: float=5; @HideInInspector var walkDeaccelerationVolx:float; @HideInInspector var walkDeaccelerationVolz:float; var cameraObject:GameObject; var maxWalkSpeed: float=20; @HideInInspector var horizontalMovement:Vector2; //Variables Salto: var jumpVelocity: float=20; @HideInInspector var grounded:boolean=false; var maxSlope:float=60; function Update () { //Movimiento: horizontalMovement=Vector2(rigidbody.velocity.x,rigidbody.velocity.z); if (horizontalMovement.magnitude>maxWalkSpeed) { horizontalMovement=horizontalMovement.normalized; horizontalMovement*=maxWalkSpeed; } rigidbody.velocity.x=horizontalMovement.x; rigidbody.velocity.z=horizontalMovement.y; if (Input.GetAxis("Horizontal")==0 && Input.GetAxis("Vertical")==0 && grounded) { rigidbody.velocity.x = Mathf.SmoothDamp(rigidbody.velocity.x,0,walkDeaccelerationVolx, walkDeacceleration); rigidbody.velocity.z =Mathf.SmoothDamp(rigidbody.velocity.z,0,walkDeaccelerationVolz, walkDeacceleration); }

transform.rotation=Quaternion.Euler(0,cameraObject.GetComponent(MouseLook).currentYRotation,0); rigidbody.AddRelativeForce(Input.GetAxis("Horizontal")*walkAcceleration,0,Input.GetAxis("Vertical")*walkAccel eration); //Salto: if (Input.GetButtonDown("Jump") && grounded) rigidbody.AddForce(0,jumpVelocity,0); } function OnCollisionStay(collision:Collision) { for (var contact:ContactPoint in collision.contacts) { if (Vector3.Angle(contact.normal, Vector3.up)<maxSlope) grounded=true; } } function OnCollisionExit() { grounded=false; } ------------------------------------------------------------------------------------------¿Recuerdan esta función Mathf.SmoothDamp? Si no recuerdan, fue usada en el Script MouseLook, para suavizar el movimiento de la rotación de la cámara. Por lo tanto se hizo lo mismo para suavizar la fricción del jugador al detenerse. Se agregaron de la misma forma walkDeaccelerationVolx y walkDeaccelerationVolz. Si no recuerdan la función. Hará esto: En el caso de rigidbody.velocity.x, modificar la velocidad de la rigidbody.velocity.x actual hasta la velocidad 0, con una velocidad actual de 0 (walkDeaccelerationVolx, Preferible no modificar) en un tiempo determinado por walkDeacceleration. Así que ahora podemos modificar el valor de walkDeacceleration a 0.5 y veremos que queda bien suavizado. Script Reference: http://docs.unity3d.com/Documentation/ScriptReference/Mathf.SmoothDamp.html

22

Nota: Pueden modificar esta parte del Script. rigidbody.AddRelativeForce(Input.GetAxis("Horizontal")*walkAcceleration,0,Input.GetAxis("Vertical")*walkAccelerati on); Por esta: rigidbody.AddRelativeForce(Input.GetAxis("Horizontal")*walkAcceleration *Time.deltaTime,0,Input.GetAxis("Vertical")*walkAcceleration*Time.deltaTime); Esto hará más suavizado el caminar. Pero tendrán que aumentar walkAcceleration a 500 o 1000, o algo parecido.

23

24

Capítulo 1.7 Control del Aire y Pegarse a las Paredes Limitado
Ver en Inglés: http://www.youtube.com/watch?v=1Ptl4V4Zwy0&feature=plcp Control del Aire significa que al saltar, podremos movernos más lento que en el suelo, y al chocar con las paredes, nos iremos “deslizando” hasta llegar al suelo, dependiendo de su inclinación. Antes de comenzar vamos a cambiar nuestra Escena y le agregaremos una especie de escalera hecha con cubos, de esta forma:

Prueba subir con una walkAcceleration de 1000 o 1500. Verás que costará subir. Ya que vas chocando con cada cubo. Por lo tanto debes hacer este pequeño truco: Haz click en cada escalón colocado, excepto el último (El grande), y donde dice Is Trigger, marca la casilla. De tal modo que puedas traspasar los cubos. Acto seguido crea otro cubo y llámalo RampaEscalera o como quieras. Y ubícalo de tal forma que te quede como si subieras entre el primer escalón y el último, como se muestra en esta imagen:

25

Ahora intenta subir la escalera, y verás que es sin problemas. Claro que si bajas el valor del walkAcceleration. No podrás poder subirla totalmente. Por lo tanto si sucede, debes aumentar el valor. Volviendo al Script, para saber cómo controlarte en el aire y no pegarte a las paredes. Abre tu Script PlayerMovement. Y edítalo por esto: ------------------------------------------------------------------------------------------//Variables Movimiento: var walkAcceleration: float=10; var walkAccelAirRatio:float=0.1; var walkDeacceleration: float=5; @HideInInspector var walkDeaccelerationVolx:float; @HideInInspector var walkDeaccelerationVolz:float; var cameraObject:GameObject; var maxWalkSpeed: float=20; @HideInInspector var horizontalMovement:Vector2; //Variables Salto: var jumpVelocity: float=20; @HideInInspector var grounded:boolean=false; var maxSlope:float=60;

function Update () { //Movimiento: horizontalMovement=Vector2(rigidbody.velocity.x,rigidbody.velocity.z); if (horizontalMovement.magnitude>maxWalkSpeed) { horizontalMovement=horizontalMovement.normalized; horizontalMovement*=maxWalkSpeed; } rigidbody.velocity.x=horizontalMovement.x; rigidbody.velocity.z=horizontalMovement.y; if (grounded) { rigidbody.velocity.x = Mathf.SmoothDamp(rigidbody.velocity.x,0,walkDeaccelerationVolx, walkDeacceleration); rigidbody.velocity.z =Mathf.SmoothDamp(rigidbody.velocity.z,0,walkDeaccelerationVolz, walkDeacceleration); } transform.rotation=Quaternion.Euler(0,cameraObject.GetComponent(MouseLook).currentYRotation,0); if (grounded) rigidbody.AddRelativeForce(Input.GetAxis("Horizontal")*walkAcceleration *Time.deltaTime,0,Input.GetAxis("Vertical")*walkAcceleration*Time.deltaTime); else rigidbody.AddRelativeForce(Input.GetAxis("Horizontal")*walkAcceleration*walkAccelAirRatio *Time.deltaTime,0,Input.GetAxis("Vertical")*walkAcceleration*walkAccelAirRatio*Time.deltaTime); //Salto: if (Input.GetButtonDown("Jump") && grounded) rigidbody.AddForce(0,jumpVelocity,0); } function OnCollisionStay(collision:Collision) { for (var contact:ContactPoint in collision.contacts) { if (Vector3.Angle(contact.normal, Vector3.up)<maxSlope) grounded=true; } }

26

function OnCollisionExit() { grounded=false; } ------------------------------------------------------------------------------------------Se hizo 3 pasos en este Script. El primer paso es declarar una variable llamada walkAccelAirRatio. Lo que hará esta variable será controlar el movimiento del aire del jugador, así no dará grandes saltos largos. Si no de menor tamaño. En el segundo paso se editó esta parte: if (Input.GetAxis("Horizontal")==0 && Input.GetAxis("Vertical")==0 && grounded) Por esta: If (grounded) Ya que solo nos queremos preguntar si está en el suelo o no. El tercer paso, es editar la parte de movimiento. Si está en el suelo se moverá normal, de caso contrario (sentencia ifelse), se moverá más lento, determinado por un valor pequeño de walkAccelAirRatio.

27

Capítulo 1.8 Arma básica con Peso
Ver en Inglés: http://www.youtube.com/watch?v=ay2SMb9-nEE&feature=plcp En este capítulo veremos como añadir un arma, y rotarla dependiendo de donde esté la cámara. Antes de empezar, pueden descargar este modelo de brazo y pistola que dejo en el link: http://www.mediafire.com/?ecj1616z0qazhh5 pass: cotolonco Pueden usar su propio modelo pero yo usaré el que hice a la rápida. (Si se fijan las texturas no están ordenadamente diseñadas). Primero, descomprimimos el .rar, y obtendremos 3 archivos. Habrá un archivo .fbx que trae el modelo. Y 2 texturas: de la pistola y del brazo.

Ahora nos vamos a Unity, y creamos una carpeta llamada Modelos. Luego arrastran estos 3 archivos a la carpeta. Se les tendría que ver así:

28

Vamos a arrastrar la textura Arma1 y Brazo1 a la carpeta Materials. Ahora coloquen la textura Arma1.png al material Arma1, (si es que no la posee) y coloquen la textura Brazo1.png en el material ArmaBrazo1-Material. Obtendrán esto como resultado:

Ahora para terminar vamos y hacemos un click en ArmaBrazo1 y cambiamos su Scale Factor por 0.5. O si no se verá muy pequeño.

29

Y ahora podemos comenzar. Hagan click en el GameObject cámara, y luego vayan a GameObject-> Create Empty y nómbrenlo como Gun (Esto se hace para que Gun esté exactamente donde esta el GameObject camara). Arrastren el Armabrazo1 al GameObject Gun. Borren Lamp y Camera del GameObject ArmaBrazo1. Y coloquen estos datos al GameObject, para ubicarlo bien en la cámara.

Lo ideal es que se vea como en Game. Si no funcionan esos valores intenten modificarlos. Ahora si comenzamos con el script. Creen un nuevo JavaScript y renómbrenlo como GunScript. Abran el JavaScript y empezaremos definiendo una variable de tipo GameObject ------------------------------------------------------------------------------------------var cameraObject : GameObject; ------------------------------------------------------------------------------------------Arrastramos el script al GameObject Gun. Y donde diga Camera Object None(Game Object). Arrastraremos la cámara.

30

Ahora crearemos 7 variables más. De las cuales ya más o menos sabrán, para que serán. ------------------------------------------------------------------------------------------var cameraObject : GameObject; @HideInInspector var targetXRotation : float; @HideInInspector var targetYRotation : float; @HideInInspector var targetXRotationV : float; @HideInInspector var targetYRotationV : float; var rotateSpeed : float = 0.3; var holdHeight : float = -0.5;

var holdSide : float = 0.5; ------------------------------------------------------------------------------------------Como ya dijimos @HideInInspector esconde la variable de abajo, para que no aparezca en el Inspector. Las variables targetXRotation, targetYRotation, targetXRotationV, targetYRotationV y rotateSpeed, si pudieron notarlo estas variables serán usadas con la función Mathf.SmoothDamp, para suavizar el movimiento. Las otras 2 variables, es para ubicar el arma más a la derecha o izquierda (holdSide), o hacia arriba o abajo (holdHeight). Para la posición original, que en este caso ya definimos nosotros la dejaremos en 0. Ahora terminemos de armar el Script: ------------------------------------------------------------------------------------------var cameraObject : GameObject; @HideInInspector var targetXRotation : float; @HideInInspector var targetYRotation : float; @HideInInspector var targetXRotationV : float; @HideInInspector var targetYRotationV : float; var rotateSpeed : float = 0.3; var holdHeight : float = -0.5; var holdSide : float = 0.5; function Update () { //Mantener la posicion de la camara transform.position=cameraObject.transform.position + Quaternion.Euler(0,targetYRotation,0)*Vector3(holdSide,holdHeight,0); targetXRotation=Mathf.SmoothDamp(targetXRotation,cameraObject.GetComponent(MouseLook).xRotation,targetX RotationV,rotateSpeed); targetYRotation=Mathf.SmoothDamp(targetYRotation,cameraObject.GetComponent(MouseLook).yRotation,targetY RotationV,rotateSpeed); transform.rotation=Quaternion.Euler(targetXRotation, targetYRotation,0); } -------------------------------------------------------------------------------------------

31

La primera línea estamos estableciendo que a cada frame del juego la posición del arma transform.position, esté en el 32 centro de la cámara, más como ya dijimos un poco a la derecha/izquierda o arriba/abajo establecido por lo que sigue del signo positivo. Las siguientes 2 líneas donde se define targetXRotation y targetYRotation, si lo notaron se usa un movimiento suavizado desde su ultima rotación targetXRotation/ targetYRotation hasta la rotación obviamente de la cámara xRotation/yRotation, con una velocidad targetXRotationV/targetYRotationV en un tiempo determinado por rotateSpeed. Si pruebas el juego ahora, y le cambias las variables a holdHeight y holdSide, notarás que el arma se desplazará. Además si rotas la vista, te darás cuenta que el arma con el brazo giran junto con la cámara. Pero en ocasiones la el arma con el brazo desaparecen de la cámara, porque tardan en llegar. Para arreglar esto, cambiaremos el valor de rotateSpeed por 0.1 y a probar! Ahora está quedando bien.

Capítulo 1.9 Apuntando (Aiming)
Ver en Inglés: http://www.youtube.com/watch?v=_YhahZPE9Mg&feature=plcp En este capítulo realizaremos un cambio de posición del arma, de manera suavizada y rápida. En unas líneas más adelante explico un pequeño error, eso es porque cuando importen el arma y situen, debe estar como en el segundo cuadro de la imagen y no como el primero (como nosotros lo hicimos): Lo que queremos conseguir aquí es que al tener click derecho presionado pase esto:

A nuestro script anterior GunScript, hay que añadirle unas 3 variables. Volveremos a usar Mathf.SmoothDamp. Pero Antes de continuar, vamos a modificar ciertas cosas del arma. Ya que hay un pequeño error que cometimos. (Al explicar el script entenderán por qué). Vamos a modificar esto del GameObject ArmaBrazo1 (hijo del GameObject Gun).

33

Si logran notarlo el arma quedó en el medio del GameObject camara, como se muestra en el Segundo cuadro de la imagen anterior a esta. Ahora a holdHeight y holdSide ya no serán 0, ya que de ser así el script siguiente no tendrá efecto.

Ahora vamos al script y me dispondré a explicar: ------------------------------------------------------------------------------------------var cameraObject : GameObject; @HideInInspector var targetXRotation : float; @HideInInspector var targetYRotation : float; @HideInInspector var targetXRotationV : float; @HideInInspector

var targetYRotationV : float; var rotateSpeed : float = 0.3; var holdHeight : float = -0.5; var holdSide : float = 0.5; var racioHipHold : float=1; var hiptoAimSpeed : float=0.1; @HideInInspector var racioHipHoldV : float; function Update () { if (Input.GetButton("Fire2")) racioHipHold=Mathf.SmoothDamp(racioHipHold,0,racioHipHoldV,hiptoAimSpeed); if (Input.GetButton("Fire2")==false) racioHipHold=Mathf.SmoothDamp(racioHipHold,1,racioHipHoldV,hiptoAimSpeed);

34

//Mantener la posicion de la camara transform.position=cameraObject.transform.position + Quaternion.Euler(0,targetYRotation,0)*Vector3(holdSide*racioHipHold,holdHeight*racioHipHold,0);

targetXRotation=Mathf.SmoothDamp(targetXRotation,cameraObject.GetComponent(MouseLook).xRotation,targetXRot ationV,rotateSpeed); targetYRotation=Mathf.SmoothDamp(targetYRotation,cameraObject.GetComponent(MouseLook).yRotation,targetYRota tionV,rotateSpeed); transform.rotation=Quaternion.Euler(targetXRotation, targetYRotation,0); } ------------------------------------------------------------------------------------------Como dijimos agregamos 3 variables racioHipHold, hiptoAimSpeed y racioHipHoldV. La última solo será usada para la función Mathf.SmoothDamp, así que no hay que explicarla. Ahora, hay 2 if en cuanto al Input.GetButton(“Fire2”), recuerden que Fire2 es el click derecho del ratón. El primer if es cuando la sentencia si se cumple, es decir cuando estamos presionando click derecho, en ese caso racioHipHold tomará un valor de 0 en un tiempo definido por hiptoAimSpeed. De caso contrario, raciohipHold tomará un valor de 1, en el tiempo definido por hiptoAimSpeed. Vamos a la última línea modificada:

transform.position=cameraObject.transform.position + Quaternion.Euler(0,targetYRotation,0)*Vector3(holdSide*racioHipHold,holdHeight*racioHipHold,0); Vamos a tomar en cuenta solo una parte de la línea, esta: Vector3(holdSide*racioHipHold,holdHeight*racioHipHold,0) Como ya dijimos racioHipHold solo toma valores entre 0 y 1. Cuando presionamos click derecho del ratón, este valor es 0, qué implica esto. Que el Vector3 tendrá coordenadas (0, 0, 0), de este modo el arma estará posicionado en el centro (con los valores que definimos en la primera imagen de este capítulo). De caso contrario cuando se suelta, racioHipHold, tomara un valor de 1, en este caso el Vector3 tendrá un valor de (holdSide, holdHeight, 0), esto significa que cuando no haces click, el arma estará desplazada a la derecha (por los valores que definimos). Prueben el juego y mantengan presionado el botón derecho del ratón y notarán que está quedando muy bien. Si no es así, es que hay algún problema, con variables o alguna línea mal escrita.

35

Capítulo 1.10 Sensibilidad del arma al apuntar (Gun Aim Sensitivity)
Ver en Inglés: http://www.youtube.com/watch?v=T8G3uXSAZ3g&feature=plcp En este capítulo se verá como colocar cierta sensibilidad cuando nosotros estemos apuntando (manteniendo presionado click derecho del ratón), es decir, cuando apuntemos irá más lenta lla cámara que cuando no. Si pudieron deducirlo, se habrán dado cuenta que hay que modificar 2 scripts, MouseLook y GunScript. Vamos a modificar el script MouseLook, ábranlo y coloquen lo siguiente: ------------------------------------------------------------------------------------------var lookSensitivity: float=5; @HideInInspector var yRotation: float; @HideInInspector var xRotation: float; @HideInInspector var currentYRotation:float; @HideInInspector var currentXRotation:float; @HideInInspector

var yRotationV:float; @HideInInspector var xRotationV:float; var lookSmoothDamp:float=0.1; var currentAimRacio : float=1; function Awake() { Screen.showCursor=false;// Esconder cursor } function Update () { //Mirar Con el Mouse: yRotation+=Input.GetAxis("Mouse X") * lookSensitivity * currentAimRacio; xRotation-=Input.GetAxis("Mouse Y") * lookSensitivity * currentAimRacio; xRotation=Mathf.Clamp(xRotation,-90,90); //Mirar Con el Mouse Más suavizado: currentXRotation=Mathf.SmoothDamp(currentXRotation,xRotation,xRotationV,lookSmoothDamp); currentYRotation=Mathf.SmoothDamp(currentYRotation,yRotation,yRotationV,lookSmoothDamp); //hacia donde mira: transform.rotation=Quaternion.Euler(currentXRotation,currentYRotation,0); } ------------------------------------------------------------------------------------------Solamente se agregó una variable, currentAimRacio. Esta variable si analizamos las líneas donde están yRotation y xRotation nos daremos cuenta que mientras más grande el número, más rápido rotará la cámara. Ya sabiendo esto, ahora abrimos nuestro script GunScript y lo modificamos. ------------------------------------------------------------------------------------------var cameraObject : GameObject; @HideInInspector var targetXRotation : float; @HideInInspector var targetYRotation : float; @HideInInspector var targetXRotationV : float; @HideInInspector

36

var targetYRotationV : float; var rotateSpeed : float = 0.3; var holdHeight : float = -0.5; var holdSide : float = 0.5; var racioHipHold : float=1; var hiptoAimSpeed : float=0.1; @HideInInspector var racioHipHoldV : float; var aimRacio : float = 0.4; function Update () { if (Input.GetButton("Fire2")) { cameraObject.GetComponent(MouseLook).currentAimRacio=aimRacio; racioHipHold=Mathf.SmoothDamp(racioHipHold,0,racioHipHoldV,hiptoAimSpeed); } if (Input.GetButton("Fire2")==false) { cameraObject.GetComponent(MouseLook).currentAimRacio=1; racioHipHold=Mathf.SmoothDamp(racioHipHold,1,racioHipHoldV,hiptoAimSpeed); } //Mantener la posicion de la camara transform.position=cameraObject.transform.position + Quaternion.Euler(0,targetYRotation,0)*Vector3(holdSide*racioHipHold,holdHeight*racioHipHold,0);

37

targetXRotation=Mathf.SmoothDamp(targetXRotation,cameraObject.GetComponent(MouseLook).xRotation,tar getXRotationV,rotateSpeed); targetYRotation=Mathf.SmoothDamp(targetYRotation,cameraObject.GetComponent(MouseLook).yRotation,tar getYRotationV,rotateSpeed); transform.rotation=Quaternion.Euler(targetXRotation, targetYRotation,0); } -------------------------------------------------------------------------------------------

Ahora notaremos otra variable aimRacio, la cual nos dirá que al mantener presionado click derecho, la variable currentAimRacio del Script MouseLook será igual a la variable aimRacio. En el caso que no se haga un click, volverá a la normalidad, en donde currentAimRacio será 1. Vamos a probar y verán que si mueven el mouse la cámara rotará como siempre, pero al mantener presionado el bóton derecho del ratón (apuntando), y al mover el mouse se moverá más lentamente la cámara.

38

Capítulo 1.11 Zoom de la Cámara
Ver en Inglés: http://www.youtube.com/watch?v=0DWC-1Qb_2Y&feature=plcp Tal como dice el título de este Capítulo, veremos cómo hacer un zoom de la cámara, cuando nuestra arma esté apuntando (cuando mantenemos presionado click derecho). Tal como muestra esta imagen:

Como es de esperarse nuevamente tendremos que modificar el Script MouseLook y el Script GunScript. Empezamos abriendo el script MouseLook para editarlo: ------------------------------------------------------------------------------------------var defaultCameraAngle : float=60; var currentTargetCameraAngle : float=60; var racioZoom : float=1; var racioZoomV : float; var racioZoomSpeed : float=0.2; var lookSensitivity: float=5; @HideInInspector var yRotation: float; @HideInInspector var xRotation: float; @HideInInspector

var currentYRotation:float; @HideInInspector var currentXRotation:float; @HideInInspector var yRotationV:float; @HideInInspector var xRotationV:float; var lookSmoothDamp:float=0.1; var currentAimRacio : float=1; function Awake() { Screen.showCursor=false;// Esconder cursor } function Update () { if (currentAimRacio == 1) racioZoom=Mathf.SmoothDamp(racioZoom, 1, racioZoomV, racioZoomSpeed); else racioZoom=Mathf.SmoothDamp(racioZoom, 0, racioZoomV, racioZoomSpeed); camera.fieldOfView = Mathf.Lerp(currentTargetCameraAngle, defaultCameraAngle, racioZoom); //Mirar Con el Mouse: yRotation+=Input.GetAxis("Mouse X") * lookSensitivity * currentAimRacio; xRotation-=Input.GetAxis("Mouse Y") * lookSensitivity * currentAimRacio; xRotation=Mathf.Clamp(xRotation,-90,90); //Mirar Con el Mouse Más suavizado: currentXRotation=Mathf.SmoothDamp(currentXRotation,xRotation,xRotationV,lookSmoothDamp); currentYRotation=Mathf.SmoothDamp(currentYRotation,yRotation,yRotationV,lookSmoothDamp); //hacia donde mira: transform.rotation=Quaternion.Euler(currentXRotation,currentYRotation,0); } -------------------------------------------------------------------------------------------

39

Creamos 5 nuevas variables. Las 3 últimas, racioZoom, racioZoomV y racioZoomSpeed serán usadas, para la función 40 Mathf.SmoothDamp. Las 2 primeras variables, currentTargetCameraAngle y defaultCameraAngle definirán el ángulo de la cámara apuntando, y el ángulo por default de la cámara. La primera sentencia nos pregunta si currentAimRacio es 1, es decir, cuando NO estamos presionando click derecho. Entonces racioZoom será 1 en un tiempo determinado por racioZoomSpeed. De caso contrario será 0. Antes de ver la función, analicemos la variable camera.fieldOfView. Esta variable es el campo de visión de la cámara en grados. A menor grado, más zoom obtendrás y viceversa. Script Reference: http://docs.unity3d.com/Documentation/ScriptReference/Camera-fieldOfView.html Vamos a la nueva función Mathf.Lerp, para entender mejor la situación: Esta es una función, que pide 3 valores (from:float, to:float, t:float). Al entregárselas, Mathf.Lerp Interpola entre el valor from y el valor to (Recuerden que interpolar, básicamente es generar otros puntos, a partir de estos 2 puntos). Para terminar, el valor t, no es de tiempo. Es un valor que está entre 0 y 1, Por lo tanto si t es 0 corresponde al valor from y si t es 1 corresponderá al valor to. Script Reference: http://docs.unity3d.com/Documentation/ScriptReference/Mathf.Lerp.html Volviendo a nuestro caso, entonces cuando NO presionamos click derecho, currentAimRacio será igual a 1, por lo tanto el valor de racioZoom, luego de ser “suavizado” por la función Mathf.SmoothDamp será 1, esto implica que en la función Mathf.Lerp elegirá a la variable defaultCameraAngle dejando a camera.fieldOfView igual a 60, es decir con su ángulo original. De caso contrario si presionamos click derecho, como racioZoom ahora será 0, camera.fieldOfView será igual a currentTargetCameraAngle. Pero si se fijan este valor también es 60, entonces ¿Cómo le decimos que sea 30 por ejemplo? La respuesta es simple, nosotros le diremos el ángulo a través del Script GunScript. Abramos este Script y lo editaremos: ------------------------------------------------------------------------------------------var cameraObject : GameObject; @HideInInspector var targetXRotation : float; @HideInInspector var targetYRotation : float; @HideInInspector var targetXRotationV : float;

@HideInInspector var targetYRotationV : float; var rotateSpeed : float = 0.3; var holdHeight : float = -0.5; var holdSide : float = 0.5; var racioHipHold : float=1; var hiptoAimSpeed : float=0.1; @HideInInspector var racioHipHoldV : float; var aimRacio : float = 0.4; var zoomAngle:float=30; function Update () { cameraObject.GetComponent(MouseLook).currentTargetCameraAngle=zoomAngle; if (Input.GetButton("Fire2")) { cameraObject.GetComponent(MouseLook).currentAimRacio=aimRacio; racioHipHold=Mathf.SmoothDamp(racioHipHold,0,racioHipHoldV,hiptoAimSpeed); } if (Input.GetButton("Fire2")==false) { cameraObject.GetComponent(MouseLook).currentAimRacio=1; racioHipHold=Mathf.SmoothDamp(racioHipHold,1,racioHipHoldV,hiptoAimSpeed); } //Mantener la posicion de la camara transform.position=cameraObject.transform.position + Quaternion.Euler(0,targetYRotation,0)*Vector3(holdSide*racioHipHold,holdHeight*racioHipHold,0); targetXRotation=Mathf.SmoothDamp(targetXRotation,cameraObject.GetComponent(MouseLook).xRotation,tar getXRotationV,rotateSpeed); targetYRotation=Mathf.SmoothDamp(targetYRotation,cameraObject.GetComponent(MouseLook).yRotation,tar getYRotationV,rotateSpeed); transform.rotation=Quaternion.Euler(targetXRotation, targetYRotation,0); } -------------------------------------------------------------------------------------------

41

Agregamos una nueva variable zoomAngle que nos dará el nuevo ángulo de la cámara. Y la siguiente línea le decimos que el currentTargetCameraAngle sea igual al zoomAngle. Cabe notar que no es necesario, meter esta línea dentro de un if, ya que Mathf.Lerp, se encarga de elegir si el ángulo es 30, 60, o algún valor entre esos 2. Probemos el juego y sí! Efectivamente tiene un zoom.

42

Capítulo 1.12 Estableciendo la Frecuencia de Disparo (Fire Rate)
Ver en Ingles: http://www.youtube.com/watch?v=VkNUDn7rdPY&feature=plcp Empezaré definiendo Fire Rate. Fire Rate es la frecuencia en la cual el arma puede disparar. Esta frecuencia está determinada por Disparos/segundos, Disparos/Minutos, etc. Por ejemplo: Encuentro una pistola en el suelo y disparo, y cada 2 segundos puedo disparar, entonces la frecuencia sería 30 Disparos/Minuto. Antes de comenzar a crear el Script. Tenemos que crear una bala. Así que será una bala de prueba. Por lo tanto, creen un cubo, renómbrenlo como Test Bullet, y modifiquen estos datos determinados por la línea roja:

Acto Seguido. Creen una carpeta llamada Prefabs. Ahora dentro de esta carpeta creen un prefab y renómbrenlo como bullet. Ahora arrastren el GameObject Test Bullet a este prefab (Para saber que si lo arrastraron, hagan click en el prefab y verán todos los datos del cubo).

43

Ahora borren el GameObject Test Bullet que tenemos en la Escena, ya que tenemos el prefab de este. Creen un Objeto Vacío, y renómbrenlo como bullet spawn. Arrástrenlo al GameObject Pistola (que es hijo de ArmaBrazo1). Luego coloquen su coordenada de Position en (0, 0, 0). Y ahora arrastren el GameObject bullet spawn, hasta que quede en la salida de disparo del arma.

Para asegurarse, prueben el juego, y vean la vista Escena y noten que efectivamente rota con el arma y que el vector azul, este siempre alineado con el arma y la salida del arma. Armemos el Script. Abramos nuestro Script GunScript. Y vamos a crear 4 variables más (la bala, donde apárece la bala, el tiempo para volver a disparar y la rapidez con que el tiempo para volver a disparar disminuye). Antes de mostrar el Script, solamente voy a mostrar la sección de variables, para que no anden tan confusos. ------------------------------------------------------------------------------------------var cameraObject : GameObject; @HideInInspector var targetXRotation : float; @HideInInspector var targetYRotation : float; @HideInInspector var targetXRotationV : float; @HideInInspector var targetYRotationV : float; var rotateSpeed : float = 0.3; var holdHeight : float = -0.5; var holdSide : float = 0.5; var racioHipHold : float=1; var hiptoAimSpeed : float=0.1; @HideInInspector var racioHipHoldV : float; var aimRacio : float = 0.4; var zoomAngle:float=30; var fireSpeed:float=15; @HideInInspector var waitTilNextFire:float=0; var bullet:GameObject; var bulletSpawn:GameObject; ------------------------------------------------------------------------------------------Como podrán haberlo deducido, la variable fireSpeed, es la velocidad que afectará a waitTilNextFire, waitTilNextFire es el tiempo que debemos esperar para nuestro próximo disparo. La variable bullet es el GameObject de la bala que dispararemos y bulletSpawn es de donde aparecerá la bala (del GameObject bullet spawn como establecimos). Antes de continuar, arrastren el prefab bullet y el GameObject bullet spawn donde corresponda. Como se muestra en la imagen:

44

45

Terminemos este Script: ------------------------------------------------------------------------------------------var cameraObject : GameObject; @HideInInspector var targetXRotation : float; @HideInInspector var targetYRotation : float; @HideInInspector var targetXRotationV : float; @HideInInspector var targetYRotationV : float; var rotateSpeed : float = 0.3; var holdHeight : float = -0.5; var holdSide : float = 0.5; var racioHipHold : float=1; var hiptoAimSpeed : float=0.1; @HideInInspector var racioHipHoldV : float; var aimRacio : float = 0.4; var zoomAngle:float=30; var fireSpeed:float=15;

@HideInInspector var waitTilNextFire:float=0; var bullet:GameObject; var bulletSpawn:GameObject; function Update () { if (Input.GetButton("Fire1")) { if (waitTilNextFire<=0 && bullet) { Instantiate(bullet,bulletSpawn.transform.position,bulletSpawn.transform.rotation); waitTilNextFire=1; } } waitTilNextFire-=Time.deltaTime*fireSpeed; cameraObject.GetComponent(MouseLook).currentTargetCameraAngle=zoomAngle; if (Input.GetButton("Fire2")) { cameraObject.GetComponent(MouseLook).currentAimRacio=aimRacio; racioHipHold=Mathf.SmoothDamp(racioHipHold,0,racioHipHoldV,hiptoAimSpeed); } if (Input.GetButton("Fire2")==false) { cameraObject.GetComponent(MouseLook).currentAimRacio=1; racioHipHold=Mathf.SmoothDamp(racioHipHold,1,racioHipHoldV,hiptoAimSpeed); } //Mantener la posicion de la camara transform.position=cameraObject.transform.position + Quaternion.Euler(0,targetYRotation,0)*Vector3(holdSide*racioHipHold,holdHeight*racioHipHold,0); targetXRotation=Mathf.SmoothDamp(targetXRotation,cameraObject.GetComponent(MouseLook).xRotation,targetXRot ationV,rotateSpeed); targetYRotation=Mathf.SmoothDamp(targetYRotation,cameraObject.GetComponent(MouseLook).yRotation,targetYRota tionV,rotateSpeed); transform.rotation=Quaternion.Euler(targetXRotation, targetYRotation,0); } -------------------------------------------------------------------------------------------

46

Esto es más fácil de entender. Primero nos preguntamos si estamos presionando el botón izquierdo del ratón. Si es así, entonces nos preguntamos si el tiempo de espera para el próximo disparo ya acabó waitTilNextFire y si existe un GameObject bullet (si no arrastraron el prefab, será false, caso contrario true). Luego se llama a la función Instantiate. La función Instantiate, recibe 3 parámetros. El objeto a clonar, la posición en la que se creará y la rotación que tendrá. Si ustedes se ponen a disparar verán que se crean bullet (Clone), ya que son clonaciones de la bala original. Después le cambiamos el valor de waitTilNextFire a 1. Y sencillamente después cada frame se le va descontando el valor a waitTilNextFire.

47

Capítulo 1.13 Raycast y agujeros de Bala
Ver en Inglés: http://www.youtube.com/watch?v=cZsG4dfCUec&feature=plcp Antes de comenzar, básicamente Raycast, es un rayo que se crea, en donde nos devuelve información del objeto golpeado (collider) e incluso cambiar algunos aspectos de este. Vayan a su GameObject Rampa y cambien su Transform.Rotation a (0, 0, 0). Si han seguido estos capítulos antes debería haber estado en (0, 0, 315). Esto es para probar lo de los agujeros de bala.

Lo que haremos ahora, será crear el Agujero de Bala, y un cubo de color azul, para saber que cuando se cree el Bullet Hole, de fondo aparezca de color azul. Vamos y crearemos un GameObject Vacío y dentro de este un Plano.

48

Acto Seguido, crearemos un cubo. Busquen en internet la imagen de un Bullet Hole. Yo usaré esta:

Ahora dentro de la carpeta Materiales, crearemos 2 materiales. Llamen a uno texAzul, y al otro texBulletHole. Al material texAzul coloquen un Main Color azul.

Con la imagen que descargaron, arrástrenla a Unity, luego colóquenla en texBulletHole y finalmente establezcan el Shader en Particles -> Multiply.

49

Arrastren el material texAzul al último cubo creado y el material texBulletHole al Plano creado. Se me olvido mencionar, al prefab bullet marquen la casilla Is Trigger, y desmarquen la casilla Mesh Renderer o si no tendrán algunos errores (ya que la bala no se dispara, la bala crea el rayo que genera el “disparo”). Dentro de la carpeta Prefabs creen un nuevo prefab, renómbrenlo como bulletHole. Arrastren el GameObject Vacío (con el Plane) (El plano con el material del bullet hole) a este prefab y luego borren el GameObject Vacío.

Para terminar, vamos a Edit -> Project Settings -> Tags. Cambiamos el tamaño del Tag a 2, y en Element 0 colocamos Level Parts.

50

Y ahora a cada GameObject hijo del GameObject Escenario y al cubo azul creado, colóquenle el Tag Level Parts.

Ahora si podremos comenzar con el Script. Creen un nuevo Script y renómbrenlo como bulletScript y arrástrenlo al Prefab bullet. ------------------------------------------------------------------------------------------var maxDist : float = 1000000000; var decalHitWall : GameObject; var floatInFronOfWall:float=0.001; function Update () { var hit : RaycastHit; if (Physics.Raycast(transform.position,transform.forward,hit,maxDist)) { if (decalHitWall && (hit.transform.tag=="Level Parts")) { Instantiate(decalHitWall,hit.point+(hit.normal*floatInFronOfWall), Quaternion.LookRotation(hit.normal)); } } Destroy(gameObject); } -------------------------------------------------------------------------------------------

Primero vamos explicando: creamos 3 variables, maxDist que será el tamaño máximo del rayo (un número grande), 51 decalHitWall, que es el bulletHole que se crea en la pared y floatInFrontOfWall, que es para que el bullet hole cuando se cree aparezca en frente de la pared y no la traspase o esté por detrás. Recuerden arrastrar el Prefab bullethole donde lo pida.

Aquí hay algo nuevo, estamos definiendo una variable de tipo RaycastHit. Es una estructura que devuelve información de vuelta a través de un raycast. Script Reference: http://docs.unity3d.com/Documentation/ScriptReference/RaycastHit.html Ahora veremos la nueva función Physics.Raycast. Hay distintos tipos de está función, pero la que usamos nosotros pide 4 parámetros. El origen que es el transform.position de la bala, la dirección establecida por transform.forward (El eje z positivo), el hit, que es la información que obtendremos de vuelta de lo que golpeamos. Y el tamaño máximo maxDist del rayo. Script Reference: http://docs.unity3d.com/Documentation/ScriptReference/Physics.Raycast.html?from=RaycastHit

Es decir si del rayo que se crea choca con algún collider, te devuelve un valor true. Luego nos preguntamos si colocamos algún GameObject para decalHitWall (Si es que arrastraron el bulletHole será true) y además si el tag del collider al cual golpea es Level Parts, entonces creará un clón del bullet hole, en el punto de colisión y un poco más al frente, con una rotación determinado por la normal que se forma entre los 2. Al final de esto hay un Destroy, esta función destruye al GameObject bala que se creó. Si no lo colocan les irá lento el juego.

Prueben el juego y se darán cuenta que los bullet holes están mal rotados. Para esto vamos al prefab bulletHole y cambiamos esto a su hijo Plane:

52

Ahora prueben, por ahora hay algunos errores que se crean varios bulletHoles encima de otros. Pero eso lo veremos en el próximo capítulo.

Capítulo 1.14 Tiempo de Destrucción Agujero Bala
Ver en Inglés: http://www.youtube.com/watch?v=ei635F6xeP8&feature=plcp Como notaron en el capítulo anterior el bullet hole creado está ahí por siempre. Y al haber muchos te alenta el juego, por lo tanto veremos la forma de destruirlos. Primero vayan al prefab bulletHole-> Plane y Remuevan el componente del Mesh Collider, ya que o sino provoca colisiones con nuestro Jugador. Ahora creen un Script y renómbrenlo DestroyAfterTimeScript y arrástrenlo al prefab bulletHole. Abramos el Script y a trabajar:

------------------------------------------------------------------------------------------var destroyAfterTime : float= 30; var destroyAfterTimeRandomization :float=0; @HideInInspector var countToTime : float; function Awake() { destroyAfterTime+= Random.value * destroyAfterTimeRandomization; } function Update () { countToTime+=Time.deltaTime; if (countToTime>=destroyAfterTime) Destroy(gameObject); } ------------------------------------------------------------------------------------------Bueno, primero definimos una variable destroyAfterTime, que es el tiempo que tiene que transcurrir para que se destruya el bullethole, destroyAfterTimeRandomization será para generar un valor al azar para destroyAfterTime. Y finalmente countToTime, va a ser como un cronómetro. Apenas se crea el GameObject en la Escena, a destroyAfterTime se le sumára un valor al azar entre 0 y 1 multiplicado por destroyAfterTimeRandomization. Script Reference: http://docs.unity3d.com/Documentation/ScriptReference/Random-value.html Luego countToTime se va sumando el tiempo, que transcurre y si llega al tiempo requerido por destroyAterTime entonces destruirá al gameObject. Luego de esto, vayan al inspector Y cambien las 2 variables a 5,o a 3, dependiendo del tiempo. Destaco que el tiempo está en segundos. Prueben el juego, disparen al cubo azul, a la rampa, al suelo, etc.

53

Capítulo 1.15 Propagación de Balas
Ver en Inglés: http://www.youtube.com/watch?v=wx8PcZipBUo&feature=plcp En este capítulo veremos como crear un efecto de rotación en el arma cuando las balas se propagan, para generar disparos con cierta inexactitud (algo así como un recoil, en el próximo capítulo veremos una mejor recoil). Bueno esto será muy corto. Abrimos nuestro Script GunScript y lo editamos: ------------------------------------------------------------------------------------------var cameraObject : GameObject; @HideInInspector var targetXRotation : float; @HideInInspector var targetYRotation : float; @HideInInspector var targetXRotationV : float; @HideInInspector var targetYRotationV : float; var rotateSpeed : float = 0.3; var holdHeight : float = -0.5; var holdSide : float = 0.5; var racioHipHold : float=1; var hiptoAimSpeed : float=0.1; @HideInInspector var racioHipHoldV : float; var aimRacio : float = 0.4; var zoomAngle:float=30; var fireSpeed:float=15; @HideInInspector var waitTilNextFire:float=0; var bullet:GameObject; var bulletSpawn:GameObject; var shootAngleRandomizationAiming : float=5; var shootAngleRandomizationNotAiming : float=15; function Update ()

54

{ if (Input.GetButton("Fire1")) { if (waitTilNextFire<=0 && bullet) { Instantiate(bullet,bulletSpawn.transform.position,bulletSpawn.transform.rotation); targetXRotation+=(Random.value0.5)*Mathf.Lerp(shootAngleRandomizationAiming,shootAngleRandomizationNotAiming,racioHipHold); targetYRotation+=(Random.value0.5)*Mathf.Lerp(shootAngleRandomizationAiming,shootAngleRandomizationNotAiming,racioHipHold); waitTilNextFire=1; } } waitTilNextFire-=Time.deltaTime*fireSpeed; cameraObject.GetComponent(MouseLook).currentTargetCameraAngle=zoomAngle; if (Input.GetButton("Fire2")) { cameraObject.GetComponent(MouseLook).currentAimRacio=aimRacio; racioHipHold=Mathf.SmoothDamp(racioHipHold,0,racioHipHoldV,hiptoAimSpeed); } if (Input.GetButton("Fire2")==false) { cameraObject.GetComponent(MouseLook).currentAimRacio=1; racioHipHold=Mathf.SmoothDamp(racioHipHold,1,racioHipHoldV,hiptoAimSpeed); } //Mantener la posicion de la camara transform.position=cameraObject.transform.position + Quaternion.Euler(0,targetYRotation,0)*Vector3(holdSide*racioHipHold,holdHeight*racioHipHold,0);

55

targetXRotation=Mathf.SmoothDamp(targetXRotation,cameraObject.GetComponent(MouseLook).xRotation,targetXRot ationV,rotateSpeed); targetYRotation=Mathf.SmoothDamp(targetYRotation,cameraObject.GetComponent(MouseLook).yRotation,targetYRota tionV,rotateSpeed); transform.rotation=Quaternion.Euler(targetXRotation, targetYRotation,0); } -------------------------------------------------------------------------------------------

56 Primero declaramos 2 variables shootAngleRandomizationAiming y shootAngleRandomizationNotAiming, estás ayudarán en un valor al azar cuando estas manteniendo presionado click derecho y cuando no. Luego a la rotación en X e Y del arma le sumamos un valor al azar entre 0 y 1, le restamos 0.5 y lo multiplicamos por un valor que será o shootAngleRandomizationAiming o shootAngleRandomizationNotAiming en este caso. En el inspector pueden cambiarlo a 3 y 5, prueben el juego y verán que si resulta. Nota: Esta es una pistola de disparo rápido, pueden bajar la frecuencia, para que sea de disparo lento.

Capítulo 1.16 Recoil del Arma
Ver en Inglés: http://www.youtube.com/watch?v=GwgYt3EzRV0&feature=plcp Como ya mencionamos, vamos a generar una recoil al arma. Abramos el Script GunScript y editemos: ------------------------------------------------------------------------------------------var cameraObject : GameObject; @HideInInspector var targetXRotation : float; @HideInInspector var targetYRotation : float; @HideInInspector var targetXRotationV : float; @HideInInspector var targetYRotationV : float; var rotateSpeed : float = 0.3; var holdHeight : float = -0.5; var holdSide : float = 0.5; var racioHipHold : float=1; var hiptoAimSpeed : float=0.1; @HideInInspector var racioHipHoldV : float; var aimRacio : float = 0.4; var zoomAngle:float=30;

var fireSpeed:float=15; @HideInInspector var waitTilNextFire:float=0; var bullet:GameObject; var bulletSpawn:GameObject; var shootAngleRandomizationAiming : float=5; var shootAngleRandomizationNotAiming : float=15; var recoilAmount : float=0.5; var recoilRecoverTime : float=0.2; @HideInInspector var currentRecoilZPos:float; @HideInInspector var currentRecoilZPosV:float; function Update () { if (Input.GetButton("Fire1")) { if (waitTilNextFire<=0 && bullet) { Instantiate(bullet,bulletSpawn.transform.position,bulletSpawn.transform.rotation); targetXRotation+=(Random.value0.5)*Mathf.Lerp(shootAngleRandomizationAiming,shootAngleRandomizationNotAiming,racioHipHold); targetYRotation+=(Random.value0.5)*Mathf.Lerp(shootAngleRandomizationAiming,shootAngleRandomizationNotAiming,racioHipHold); currentRecoilZPos-=recoilAmount; waitTilNextFire=1; } } waitTilNextFire-=Time.deltaTime*fireSpeed; currentRecoilZPos=Mathf.SmoothDamp(currentRecoilZPos,0,currentRecoilZPosV,recoilRecoverTime); cameraObject.GetComponent(MouseLook).currentTargetCameraAngle=zoomAngle; if (Input.GetButton("Fire2")) { cameraObject.GetComponent(MouseLook).currentAimRacio=aimRacio; racioHipHold=Mathf.SmoothDamp(racioHipHold,0,racioHipHoldV,hiptoAimSpeed); } if (Input.GetButton("Fire2")==false)

57

{ cameraObject.GetComponent(MouseLook).currentAimRacio=1; racioHipHold=Mathf.SmoothDamp(racioHipHold,1,racioHipHoldV,hiptoAimSpeed); } //Mantener la posicion de la camara transform.position=cameraObject.transform.position + Quaternion.Euler(0,targetYRotation,0)*Vector3(holdSide*racioHipHold,holdHeight*racioHipHold,0)+Quaternion.Euler(ta rgetXRotation,targetYRotation,0)*Vector3(0,0,currentRecoilZPos);

58

targetXRotation=Mathf.SmoothDamp(targetXRotation,cameraObject.GetComponent(MouseLook).xRotation,targetXRot ationV,rotateSpeed); targetYRotation=Mathf.SmoothDamp(targetYRotation,cameraObject.GetComponent(MouseLook).yRotation,targetYRota tionV,rotateSpeed); transform.rotation=Quaternion.Euler(targetXRotation, targetYRotation,0); } ------------------------------------------------------------------------------------------Ahora si está quedando largo el Script, les recomiendo que lo lean y analicen, para ayudarlos las partes en Negrita son las nuevas líneas o modificaciones que se realizaron. Primero definimos 4 variables, recoilAmount será la cantidad de recoil que te genera cada disparo, por eso se le resta a currentRecoilZPos, esta variable indica el recoil que te genera en la posición z, recoilRecoverTime es para ir reduciendo tu recoil, hasta no tener, finalmente currentRecoilZPosV será usada para Mathf.SmoothDamp (Para volver a no tener recoil). Y a la posición que tendrá el arma dentro de la cámara será determinada por los nuevos valores agregados: Quaternion.Euler(targetXRotation,targetYRotation,0)*Vector3(0,0,currentRecoilZPos);

Capítulo 1.17 Sonido de Disparo
Ver en Inglés: http://www.youtube.com/watch?v=_Et8Y-pX6XA&feature=plcp Vamos a ver en este capítulo como añadir un sonido a nuestro disparoo. Antes que nada descargaremos un efecto de sonido de un disparo. Yo usaré este: http://www.mediafire.com/?zzpnnwfz14jb57j Creen una Carpeta que se llame Sonidos e importen el sonido descargado a esta carpeta.

Creen un objeto vacío y renómbrenlo gunsound, arrastren el sonido importado a este GameObject. Además arrastren el Script DestroyAfterTime y reduzcan la variable destroyAfterTime a 2. Ahora creen un prefab dentro de la Carpeta Prefab, nómbrenlo Bullet Sound, y arrastren el GameObject gunsound al prefab. Después borren el GameObject de la Hierarchy. Ahora abrimos el Script GunScript y lo editamos: ------------------------------------------------------------------------------------------var cameraObject : GameObject; @HideInInspector var targetXRotation : float; @HideInInspector var targetYRotation : float; @HideInInspector var targetXRotationV : float; @HideInInspector var targetYRotationV : float; var rotateSpeed : float = 0.3; var holdHeight : float = -0.5; var holdSide : float = 0.5; var racioHipHold : float=1; var hiptoAimSpeed : float=0.1; @HideInInspector var racioHipHoldV : float; var aimRacio : float = 0.4; var zoomAngle:float=30; var fireSpeed:float=15; @HideInInspector var waitTilNextFire:float=0; var bullet:GameObject; var bulletSpawn:GameObject; var shootAngleRandomizationAiming : float=5; var shootAngleRandomizationNotAiming : float=15; var recoilAmount : float=0.5; var recoilRecoverTime : float=0.2;

59

@HideInInspector var currentRecoilZPos:float; @HideInInspector var currentRecoilZPosV:float; var bulletSound:GameObject; function Update () { var holdSound:GameObject; if (Input.GetButton("Fire1")) { if (waitTilNextFire<=0 && bullet) { Instantiate(bullet,bulletSpawn.transform.position,bulletSpawn.transform.rotation); if (bulletSound) holdSound=Instantiate(bulletSound,bulletSpawn.transform.position,bulletSpawn.transform.rotation); targetXRotation+=(Random.value0.5)*Mathf.Lerp(shootAngleRandomizationAiming,shootAngleRandomizationNotAiming,racioHipHold); targetYRotation+=(Random.value0.5)*Mathf.Lerp(shootAngleRandomizationAiming,shootAngleRandomizationNotAiming,racioHipHold); currentRecoilZPos-=recoilAmount; waitTilNextFire=1; } } waitTilNextFire-=Time.deltaTime*fireSpeed; if (holdSound) holdSound.transform.parent=transform; currentRecoilZPos=Mathf.SmoothDamp(currentRecoilZPos,0,currentRecoilZPosV,recoilRecoverTime); cameraObject.GetComponent(MouseLook).currentTargetCameraAngle=zoomAngle; if (Input.GetButton("Fire2")) { cameraObject.GetComponent(MouseLook).currentAimRacio=aimRacio; racioHipHold=Mathf.SmoothDamp(racioHipHold,0,racioHipHoldV,hiptoAimSpeed); } if (Input.GetButton("Fire2")==false) { cameraObject.GetComponent(MouseLook).currentAimRacio=1; racioHipHold=Mathf.SmoothDamp(racioHipHold,1,racioHipHoldV,hiptoAimSpeed); }

60

//Mantener la posicion de la camara transform.position=cameraObject.transform.position + Quaternion.Euler(0,targetYRotation,0)*Vector3(holdSide*racioHipHold,holdHeight*racioHipHold,0)+Quaternion.Euler(ta rgetXRotation,targetYRotation,0)*Vector3(0,0,currentRecoilZPos);

61

targetXRotation=Mathf.SmoothDamp(targetXRotation,cameraObject.GetComponent(MouseLook).xRotation,targetXRot ationV,rotateSpeed); targetYRotation=Mathf.SmoothDamp(targetYRotation,cameraObject.GetComponent(MouseLook).yRotation,targetYRota tionV,rotateSpeed); transform.rotation=Quaternion.Euler(targetXRotation, targetYRotation,0) } ------------------------------------------------------------------------------------------Bueno obviamente creamos una variable bulletSound, para el prefab que acabamos de crear. Arrastramos nuestro prefab Bullet Sound, donde pida el GameObject el Script (de nuestro GameObject ArmaBrazo1). La función Instantiate creará el sonido de la bala, al igual donde se crea la bala, y esta bala clonada es guardada en la variable de tipo GameObject holdSound. La siguiente línea: if (holdSound) holdSound.transform.parent=transform; Es para decirle que el objeto holdSound creado se mantenga en la posición del bulletspawn. En el caso que no estuvieran estas 2 líneas y prueban el juego, disparen y roten rápidamente la cámara, y notaran en la vista Scene que el objeto sonido está detrás de uno y no en la salida del arma. Estas 2 líneas serán muy útiles para el próximo capítulo. Prueben el juego y diviértanse disparando, ahora está quedando mucho mejor. Antes de seguir les recuerdo que los Script se van alargando, así que les reitero que deben leer bien e intenten entender cada parte del Script. Vayamos al próximo capítulo:

Capítulo 1.18 Muzzle Flash
Ver en Inglés: http://www.youtube.com/watch?v=aKtow07MX40&feature=plcp En este capítulo veremos 2 procesos, el primero crear el muzzleflash, a partir de partículas, y el segundo modificar el Script para generarlo. Como dato usaremos el mismo proceso que con el sonido, así no tendrán dificultades. Vamos a bajar una imagen de algún muzzle flash. Yo usaré esta:

62

Arrástrenla a la carpeta Materiales. Creen un nuevo Material, llámenlo Muzzle Flash y coloquen la textura del MuzzleFlash y de Shader: Particle/Additive.

Ahora creen un objeto vacío, llámenlo muzzleFlash y agreguen una componente Particle -> Ellipsoid, Particle Animator y Particle Renderer. Primero arrastren el material del Muzzle Flash a este GameObject. Ahora editen los valores de las componentes según esta imagen:

63

Ahora creen un prefab, renómbrenlo como muzzle_flash y arrastren el GameObject muzzleflash a este prefab. Luego borren el GameObject. Abrimos GunScript, y editamos el Script (Recuerden que será el mismo proceso que con el capítulo anterior): ------------------------------------------------------------------------------------------var cameraObject : GameObject; @HideInInspector var targetXRotation : float; @HideInInspector var targetYRotation : float; @HideInInspector var targetXRotationV : float; @HideInInspector var targetYRotationV : float; var rotateSpeed : float = 0.3;

var holdHeight : float = -0.5; var holdSide : float = 0.5; var racioHipHold : float=1; var hiptoAimSpeed : float=0.1; @HideInInspector var racioHipHoldV : float; var aimRacio : float = 0.4; var zoomAngle:float=30; var fireSpeed:float=15; @HideInInspector var waitTilNextFire:float=0; var bullet:GameObject; var bulletSpawn:GameObject; var shootAngleRandomizationAiming : float=5; var shootAngleRandomizationNotAiming : float=15; var recoilAmount : float=0.5; var recoilRecoverTime : float=0.2; @HideInInspector var currentRecoilZPos:float; @HideInInspector var currentRecoilZPosV:float; var bulletSound:GameObject; var muzzleFlash:GameObject; function Update () { var holdMuzzleFlash:GameObject; var holdSound:GameObject; if (Input.GetButton("Fire1")) { if (waitTilNextFire<=0 && bullet) { Instantiate(bullet,bulletSpawn.transform.position,bulletSpawn.transform.rotation); if (bulletSound) holdSound=Instantiate(bulletSound,bulletSpawn.transform.position,bulletSpawn.transform.rotation);

64

if (muzzleFlash) holdMuzzleFlash=Instantiate(muzzleFlash,bulletSpawn.transform.position,bulletSpawn.transform.rotation); targetXRotation+=(Random.value0.5)*Mathf.Lerp(shootAngleRandomizationAiming,shootAngleRandomizationNotAiming,racioHipHold); targetYRotation+=(Random.value0.5)*Mathf.Lerp(shootAngleRandomizationAiming,shootAngleRandomizationNotAiming,racioHipHold); currentRecoilZPos-=recoilAmount; waitTilNextFire=1; } } waitTilNextFire-=Time.deltaTime*fireSpeed; if (holdSound) holdSound.transform.parent=transform; if (holdMuzzleFlash) holdMuzzleFlash.transform.parent=transform; currentRecoilZPos=Mathf.SmoothDamp(currentRecoilZPos,0,currentRecoilZPosV,recoilRecoverTime); cameraObject.GetComponent(MouseLook).currentTargetCameraAngle=zoomAngle; if (Input.GetButton("Fire2")) { cameraObject.GetComponent(MouseLook).currentAimRacio=aimRacio; racioHipHold=Mathf.SmoothDamp(racioHipHold,0,racioHipHoldV,hiptoAimSpeed); } if (Input.GetButton("Fire2")==false) { cameraObject.GetComponent(MouseLook).currentAimRacio=1; racioHipHold=Mathf.SmoothDamp(racioHipHold,1,racioHipHoldV,hiptoAimSpeed); } //Mantener la posicion de la camara transform.position=cameraObject.transform.position + Quaternion.Euler(0,targetYRotation,0)*Vector3(holdSide*racioHipHold,holdHeight*racioHipHold,0)+Quaternion.Euler(ta rgetXRotation,targetYRotation,0)*Vector3(0,0,currentRecoilZPos);

65

targetXRotation=Mathf.SmoothDamp(targetXRotation,cameraObject.GetComponent(MouseLook).xRotation,targetXRot ationV,rotateSpeed); targetYRotation=Mathf.SmoothDamp(targetYRotation,cameraObject.GetComponent(MouseLook).yRotation,targetYRota tionV,rotateSpeed); transform.rotation=Quaternion.Euler(targetXRotation, targetYRotation,0) } -------------------------------------------------------------------------------------------

Bueno no hay mucho que explicar aquí, es igual al capítulo Anterior, aún así lo explicaré de nuevo por las dudas. Primero 66 creamos una variable llamada muzzleFlash, que guardará información del prefab muzzle_flash (deben arrastrarlo donde lo pida). La segunda variable holdMuzzleFlash será usada, para mantener el muzzleFlash creado, en la salida del arma. Cabe destacar que si prueban el juego y disparan, el muzzle flash tendrá el mismo ángulo (no rotará para hacerlo “variado”). Para cambiar el Ángulo marquen la casilla Rnd Rotation del Particle Animator.

Pongan Play, para probar el juego y podrán ver que el muzzleflash creado si tiene rotaciones aleatorias. Nota: Dentro del prefab muzzle_flash, pueden agregar un objeto point light, de baja intensidad, para generar un efecto de iluminación en cada disparo.

Capítulo 1.19 HeadBobMovement
Ver en Inglés: http://www.youtube.com/watch?v=q4d-_CCNo6g&feature=plcp Bueno no se si han jugado algún video juego en el que si avanzas hacia al frente, la cámara como que va moviéndose izquierda-derecha, sin que muevas el mouse (La cámara como que va cabeceando). Además podrán apreciarlo en el video de la introducción que coloqué. Antes de crear el Script, le daré ciertos datos a usar: Debemos crear variables que contengan, la velocidad del Headbob, las cantidades en el eje X/Y que afecte al Headbob, y la ultima posición guardada, para originar movimientos suavizados del Headbob. Ahora vamos con el Script MouseLook:

------------------------------------------------------------------------------------------var defaultCameraAngle : float=60; var currentTargetCameraAngle : float=60; var racioZoom : float=1; var racioZoomV : float; var racioZoomSpeed : float=0.2; var lookSensitivity: float=5; @HideInInspector var yRotation: float; @HideInInspector var xRotation: float; @HideInInspector var currentYRotation:float; @HideInInspector var currentXRotation:float; @HideInInspector var yRotationV:float; @HideInInspector var xRotationV:float; var lookSmoothDamp:float=0.1; var currentAimRacio : float=1; var headbobSpeed:float=1; var headbobStepCounter:float; var headbobAmountX:float=1; var headbobAmountY:float=1; var parentlastPos:Vector3; var eyeHeightRacio:float=0.9; function Awake() { Screen.showCursor=false;// Esconder cursor parentlastPos=transform.parent.position; } function Update () { if (transform.parent.GetComponent(PlayerMovement).grounded)

67

headbobStepCounter+=Vector3.Distance(parentlastPos, transform.parent.position)*headbobSpeed; transform.localPosition.x=Mathf.Sin(headbobStepCounter)*headbobAmountX*currentAimRacio; transform.localPosition.y=(Mathf.Cos(headbobStepCounter*2)*headbobAmountY*1*currentAimRacio)+(transform.parent.localScale.y*eyeHeightRacio)-(transform.parent.localScale.y/2); parentlastPos=transform.parent.position; if (currentAimRacio == 1) racioZoom=Mathf.SmoothDamp(racioZoom, 1, racioZoomV, racioZoomSpeed); else racioZoom=Mathf.SmoothDamp(racioZoom, 0, racioZoomV, racioZoomSpeed); camera.fieldOfView = Mathf.Lerp(currentTargetCameraAngle, defaultCameraAngle, racioZoom); //Mirar Con el Mouse: yRotation+=Input.GetAxis("Mouse X") * lookSensitivity * currentAimRacio; xRotation-=Input.GetAxis("Mouse Y") * lookSensitivity * currentAimRacio; xRotation=Mathf.Clamp(xRotation,-90,90); //Mirar Con el Mouse Más suavizado: currentXRotation=Mathf.SmoothDamp(currentXRotation,xRotation,xRotationV,lookSmoothDamp); currentYRotation=Mathf.SmoothDamp(currentYRotation,yRotation,yRotationV,lookSmoothDamp); //hacia donde mira: transform.rotation=Quaternion.Euler(currentXRotation,currentYRotation,0); } ------------------------------------------------------------------------------------------Vamos a explicar: primero definimos 6 variables, headbobSpeed es la velocidad del “cabeceo”, parentLastPos guarda la última posición del padre, para luego suavizar el movimiento a la nueva posición del cabeceo, headbobStepCounter guarda la distancia entre parentLastPost y la nueva posición del padre (PlayerCapsule). En base a eso se genera 2 funciones Seno y Coseno, para establecer el cabeceo (No se maten pensando por qué es esa fórmula, si no entiéndanla). Ahora, prueben el juego y tal vez noten que el jugador baila mucho el mambo con la Pistola. El baile de la pistola es originado porque la función Update del Script GunScript interfiere mucho con la función Update, del script MouseLook. Por lo tanto lo que haremos es simplemente hacer que la función Update del Script GunScript se ejecute al termino de cada función Update. A el Script GunScript y cambien function Update por function LateUpdate Esto se ejecuta al termino de cada frame.

68

69

Ahora cambiaremos ciertas variables del inspector como muestra la imagen:

Estas variables cambiadas, harán que el jugador no tiemble mucho al caminar, y haga cabeceos cortos. Acomoden las variables como les acomode.

Capítulo 1.20 Gun-Bob Movement
Ver en Inglés: http://www.youtube.com/watch?v=SEk52vLPmWQ&feature=plcp Ya sabemos que es el cabeceo, ahora realizaremos el mismo cabeceo ahora para el arma Esta vez realizaremos el “cabeceo”, pero para el arma. Empezaremos abriendo el Script GunScript y lo editaremos rápidamente: ------------------------------------------------------------------------------------------var cameraObject : GameObject; @HideInInspector var targetXRotation : float; @HideInInspector var targetYRotation : float;

@HideInInspector var targetXRotationV : float; @HideInInspector var targetYRotationV : float; var rotateSpeed : float = 0.3; var holdHeight : float = -0.5; var holdSide : float = 0.5; var racioHipHold : float=1; var hiptoAimSpeed : float=0.1; @HideInInspector var racioHipHoldV : float; var aimRacio : float = 0.4; var zoomAngle:float=30; var fireSpeed:float=15; @HideInInspector var waitTilNextFire:float=0; var bullet:GameObject; var bulletSpawn:GameObject; var shootAngleRandomizationAiming : float=5; var shootAngleRandomizationNotAiming : float=15; var recoilAmount : float=0.5; var recoilRecoverTime : float=0.2; @HideInInspector var currentRecoilZPos:float; @HideInInspector var currentRecoilZPosV:float; var bulletSound:GameObject; var muzzleFlash:GameObject; var gunbobAmountX:float=0.5; var gunbobAmountY:float=0.5; var currentGunbobX:float; var currentGunbobY:float;

70

function LateUpdate () { currentGunbobX=Mathf.Sin(cameraObject.GetComponent(MouseLook).headbobStepCounter)*gunbobAmou ntX*racioHipHold; currentGunbobY=Mathf.Cos(cameraObject.GetComponent(MouseLook).headbobStepCounter*2)*gunbobAm ountY*1*racioHipHold; var holdMuzzleFlash:GameObject; var holdSound:GameObject; if (Input.GetButton("Fire1")) { if (waitTilNextFire<=0 && bullet) { Instantiate(bullet,bulletSpawn.transform.position,bulletSpawn.transform.rotation); if (bulletSound) holdSound=Instantiate(bulletSound,bulletSpawn.transform.position,bulletSpawn.transform.rotation); if (muzzleFlash) holdMuzzleFlash=Instantiate(muzzleFlash,bulletSpawn.transform.position,bulletSpawn.transform.rotation); targetXRotation+=(Random.value0.5)*Mathf.Lerp(shootAngleRandomizationAiming,shootAngleRandomizationNotAiming,racioHipHold); targetYRotation+=(Random.value0.5)*Mathf.Lerp(shootAngleRandomizationAiming,shootAngleRandomizationNotAiming,racioHipHold); currentRecoilZPos-=recoilAmount; waitTilNextFire=1; } } waitTilNextFire-=Time.deltaTime*fireSpeed; if (holdSound) holdSound.transform.parent=transform; if (holdMuzzleFlash) holdMuzzleFlash.transform.parent=transform; currentRecoilZPos=Mathf.SmoothDamp(currentRecoilZPos,0,currentRecoilZPosV,recoilRecoverTime); cameraObject.GetComponent(MouseLook).currentTargetCameraAngle=zoomAngle; if (Input.GetButton("Fire2")) { cameraObject.GetComponent(MouseLook).currentAimRacio=aimRacio; racioHipHold=Mathf.SmoothDamp(racioHipHold,0,racioHipHoldV,hiptoAimSpeed);

71

} if (Input.GetButton("Fire2")==false) { cameraObject.GetComponent(MouseLook).currentAimRacio=1; racioHipHold=Mathf.SmoothDamp(racioHipHold,1,racioHipHoldV,hiptoAimSpeed); } //Mantener la posicion de la camara transform.position=cameraObject.transform.position + Quaternion.Euler(0,targetYRotation,0)*Vector3(holdSide*racioHipHold+currentGunbobX,holdHeight*racioHipHold+curr entGunbobY,0)+Quaternion.Euler(targetXRotation,targetYRotation,0)*Vector3(0,0,currentRecoilZPos);

72

targetXRotation=Mathf.SmoothDamp(targetXRotation,cameraObject.GetComponent(MouseLook).xRotation,targetXRot ationV,rotateSpeed); targetYRotation=Mathf.SmoothDamp(targetYRotation,cameraObject.GetComponent(MouseLook).yRotation,targetYRot ationV,rotateSpeed); transform.rotation=Quaternion.Euler(targetXRotation, targetYRotation,0); } ------------------------------------------------------------------------------------------Bueno es el mismo proceso que en el capítulo anterior, les pido que analicen las variables y las líneas, ahora mostraré las variables que dejé yo en el Inspector:

Esta primera parte del tutorial les ayudará a entender mejor ciertas funciones que son muy importantes al proceso de crear el Script, estos tutoriales de ETeeski y traducidos por mi, espero que los ayuden a meterse más en el ámbito de la programación, y que requiere demasiada creatividad, para realizar cada Script. Le agradezco a ETeeski por darme permiso de poder publicar esta versión resumida de los tutoriales en una versión al Español, aún así yo les recomiendo que vean sus tutoriales, inclusive de la parte básica antes de empezar con los tutoriales FPS. Les recuerdo que para contactarse, pueden enviar un MP en inglés a ETeeski: http://www.youtube.com/user/ETeeskiTutorials O pueden enviarme un mail a cotolonco@outlook.com O a este correo: coto.kun@gmail.com También pueden mandarme un MP a mi perfil de Taringa http://www.taringa.net/cotolonco Les deseo suerte con sus proyectos, nos vemos en la segunda parte de estos Tutoriales.

73

Sign up to vote on this title
UsefulNot useful