El personaje y el controlador

1

Introducción: De la idea al desarrollo

2

Estructura de carpetas en Unity

3

Assets, Game Objects y Sprite Atlas

4

Animaciones desde un sprite map

5

Animation Controller y Transiciones

6

Plataformas y gravedad

7

Física en Unity

8

El script de control

9

Programando funciones en C# y Unity: Jump

10

Detectar el suelo con Raycast

11

Herramientas de debugging visual

12

Cambiar las animaciones según los estados

13

Reto: Terminando nuestras animaciones

14

Solución del reto

15

Hacer que el personaje camine

El manager del videojuego

16

Cómo funciona el Game Manager

17

El Singleton

18

El modo de juego

19

Input manager y juegos multiplataforma

20

Corrección del Bug del salto

21

La zona de muerte

22

Reiniciar la partida

23

Corrigiendo los bugs al reiniciar nuestro juego

Diseño procedural de niveles 2D

24

El diseño de niveles a mano

25

Configurando nuestros assets para el diseño procedural

26

Generación procedural de niveles

27

Creando la zona que eliminará bloques del nivel excedentes

28

Añadir un nuevo bloque de forma aleatoria

29

La cámara que sigue al jugador

30

Destrucción de bloques antiguos

31

Terminando de programar la destrucción de bloques antiguos

32

Solucionando el salto de la cámara al reiniciar el juego

HUD, menús y gameplay

33

El canvas en Unity

34

Uso de botones para crear un menú

35

La lógica de los menús

36

Ejercicio: Preparando el menú del juego

37

Programando el menú del juego

38

Los coleccionables del juego

39

Actualizar UI de coleccionables

40

Iniciando con pociones y maná

41

Pociones de vida y maná

42

Programando las barras de vida y maná

43

Calculando los puntajes con la distancia y el tiempo de juego

44

La lógica del maná

Enemigos y dificultad

45

Plataformas móviles

46

Iniciar movimiento de la plataforma con trigger

47

Enemigos móviles

48

Enemigos móviles: preparando nuestro enemigo para girar

49

Enemigos móviles: programando que gire al chocar

50

Arreglando el collider de nuestra roca

51

Programando la condición de muerte del personaje

52

Añadiendo música de fondo a nuestro videojuego

53

Añadiendo efectos de sonido y cierre del curso

No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

Convierte tus certificados en títulos universitarios en USA

Antes: $249

Currency
$209

Paga en 4 cuotas sin intereses

Paga en 4 cuotas sin intereses
Suscríbete

Termina en:

17 Días
17 Hrs
49 Min
35 Seg
Curso de C# para Videojuegos

Curso de C# para Videojuegos

Juan Gabriel Gomila

Juan Gabriel Gomila

Input manager y juegos multiplataforma

19/53
Recursos

El Input Manager es una herramienta que nos proporciona Unity para expandir la entrada de controles y soportar y reaccionar correctamente a todas las posibles acciones de los jugadores en las plataformas disponibles. Unity nos enviará códigos especiales cuando el jugador oprima ciertos botones y nosotros devolveremos la acción que queremos realizar.

En esta clase vamos a trabajar con nuestros scripts controladores para cambiar entre los estados del juego por medio de esta nueva entrada de controles en vez de reaccionar por una tecla en particular.

El desafío de esta clase es evitar que nuestro personaje pueda saltar a menos que estemos en estado de ““In Game””, así como hemos evitado que nuestro personaje pueda avanzar en el resto de estados.

Aportes 29

Preguntas 3

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

Yo logre detener el movimiento del personaje llamado a un método de su RigidBody2D

rigid.Sleep ();

Mi código. Yo le tengo configurado para que no camine automáticamente, sino cuando yo presiono las flechas.

void Update () {

		if(GameManager.sharedInstance.currentGameState == GameState.inGame){
			if(Input.GetButtonDown("Jump")){
				Jump();
			}
		}
	
		animator.SetBool(STATE_ON_THE_GROUND, isTouchingTheGround());

		Debug.DrawRay(this.transform.position, Vector2.down*1.4f, Color.red); // for debug purposes
	}

	void FixedUpdate() {
		// if(rigidBody.velocity.x < runningSpeed){
		// 	rigidBody.velocity = new Vector2(runningSpeed, rigidBody.velocity.y);
		// }
		if(GameManager.sharedInstance.currentGameState == GameState.inGame){
			playerMovement();
		}
	}

	void playerMovement(){
		
		float isWalking = Input.GetAxisRaw("Horizontal");
	
		if(isWalking != 0){ //is true

			animator.SetBool(STATE_IS_QUIET, false);
			animator.SetBool(STATE_IS_WALKING, true);
			rigidBody.velocity = new Vector2(isWalking * runningSpeed,rigidBody.velocity.y);
			spriteRenderer.flipX = (isWalking == -1) ? true : false; 
			//if the value returns -1 that means is walking to the left and we need to make a flip in X
		}else{
			animator.SetBool(STATE_IS_QUIET, true);
			animator.SetBool(STATE_IS_WALKING, false);
		}
		
	}

Podemos encontrar la herramienta de Input Manager en Edit > Project Settings > Input.

Solo hay que hacer eso para declarar que el personaje solo salte en modo juego, sin embargo al probarlo les recomiendo cambiar un momento la tecla de salto o intenten saltar con el mouse, ya que como tenemos designado que se cambie el estado del juego a inGame con la tecla Enter, unity me reconoce el spacebar tambien como enter, por ende al orpimir la barra espaciadora el personaje salta y me cambia el estado del juego automaticamente.

<if 

(GameManager.sharedInstance.currentGameState == GameState.inGame) // solo puede saltar si esta en modo juego
        {
            if (Input.GetButtonDown("Jump")) //condicional para que se ejecute metodo jump al oprimir espacio o click derecho
            {
                Jump();
            }
        }>

Para los que lo están viendo en Diciembre 2021 la ruta es
Edit > Project Settings > Input Manager

Saludos. Activé el brinco solo si el estado del Juego está en “inGame”:

void Update()
    {
        if (GameManager.sharedInstance.currentGameState == GameState.inGame) {
            if (Input.GetButtonDown("Jump")) { // Input manager action
                Jump();
            }
        }
        
        animator.SetBool(STATE_ON_THE_GROUND, IsTouchingTheGround());
    }

por que me pasa que cuando presiono space mi game state cambia a ingame??

En el caso de que se quiera usar movimiento tanto horizontal como vertical controlado por el usuario. se debe usar la palabra “Horizontal” y “vertical” y para saber si es movimiento positivo o negativo, se debe usar el metodo GetAxisRaw.

// movimiento a la derecha
if(Input.GetButton("Horizontal") && Input.GetAxisRaw("Horizontal") > 0)

// movimiento a la izquierda
if(Input.GetButton("Horizontal") && Input.GetAxisRaw("Horizontal") < 0)

// movimiento hacia arriba
if(Input.GetButton("Vertical") && Input.GetAxisRaw("Vertical") > 0)

// movimiento hacia abajo
if(Input.GetButton("Vertical") && Input.GetAxisRaw("Vertical") < 0)

Yo lo solucione de esta forma

//saltara solo si se presiona la tecla designada por unity para el salto y si estamos en modo juego
        if (Input.GetButtonDown("Jump") && Gamemanager.ShareInstance.currentGameState == GameState.inGame)
        {
            Jump();
        }```

arregle el bug apagando el rigidbody en vez de modificando su velocidad, de esta manera no se ve afectado por las fisicas

En mi caso al teclear la barra espaciadora se iba a inGame porque en el input manager en el segundo submit lo tiene configurado con esa tecla

```js public class GameManager : MonoBehaviour { public GameState currentGameState = GameState.menu; //"Singleton" is a unique script that will be used for a specific set of functionalities or a particular group, and it cannot conflict with others of the same type, such as the current state of the game. public static GameManager sharedInstance; //se llama shared instance porque hace mas enfacis al singleton void Awake(){ if (sharedInstance == null){ sharedInstance = this; } } // Start is called before the first frame update void Start() { currentGameState = GameState.menu; } // Update is called once per frame void Update() { if (Input.GetButtonDown("Submit")){ if (currentGameState == GameState.menu){ StartGame(); }else{ BackToMenu(); } } } public void StartGame(){ SetGameState(GameState.inGame); } public void GameOver(){ SetGameState(GameState.gameOver); } public void BackToMenu(){ SetGameState(GameState.menu); } void SetGameState(GameState newGameState) { currentGameState = newGameState; switch (newGameState) { case GameState.menu: // TODO: Implementar la lógica para mostrar el menú Time.timeScale = 0; break; case GameState.inGame: // TODO: Implementar la lógica para iniciar el juego Time.timeScale = 1; break; case GameState.gameOver: // TODO: Implementar la lógica para mostrar la pantalla de Game Over y reiniciar el juego Time.timeScale = 0; break; default: break; } } ```yo preferi hacerlo directamente en el game manager, ya que es mucho mas sencillo encontrarlo, ademas modificando el timescale te ahorras codigo en el playercontroller.
yo creo que esto lo deberia de programar en el game manager, a pesar de que el profe lo haga en el player controller, ya que es mucho mas sencillo ademas encontrar estas cosas aqui. por sierto utilice otro metodo que es modificando la escala de tiempo, es decir, si estas en in game, la escala es 1 y el tiempo corre normal, sin embargo, estando en menu, o game over deberia ser 0 y el tiempo se ve parado. ```js public class GameManager : MonoBehaviour { public GameState currentGameState = GameState.menu; //"Singleton" is a unique script that will be used for a specific set of functionalities or a particular group, and it cannot conflict with others of the same type, such as the current state of the game. public static GameManager sharedInstance; //se llama shared instance porque hace mas enfacis al singleton void Awake(){ if (sharedInstance == null){ sharedInstance = this; } } // Start is called before the first frame update void Start() { currentGameState = GameState.menu; } // Update is called once per frame void Update() { if (Input.GetButtonDown("Submit")){ if (currentGameState == GameState.menu){ StartGame(); }else{ BackToMenu(); } } } public void StartGame(){ SetGameState(GameState.inGame); } public void GameOver(){ SetGameState(GameState.gameOver); } public void BackToMenu(){ SetGameState(GameState.menu); } void SetGameState(GameState newGameState) { currentGameState = newGameState; switch (newGameState) { case GameState.menu: // TODO: Implementar la lógica para mostrar el menú Time.timeScale = 0; break; case GameState.inGame: // TODO: Implementar la lógica para iniciar el juego Time.timeScale = 1; break; case GameState.gameOver: // TODO: Implementar la lógica para mostrar la pantalla de Game Over y reiniciar el juego Time.timeScale = 0; break; default: break; } } ```

PlayerController:

void Update()
    {
	// Se gestionan las animaciones arriba para evitar bugs visuales in-game
        anim.SetBool(STATE_GROUNDED, IsGrounded());
        anim.SetBool(STATE_WALKING, rigidBody.velocity.x != 0);

	// Se invierte este if para mejorar la legibilidad y reducir la complejidad cognitiva
        if (GameManager.sharedInstanceGM.currentGameState != GameState.inGame) return;

        if (Input.GetButtonDown("Jump"))
        {
            Jump();
        }

        Debug.DrawRay(this.transform.position - new Vector3(0, playerCollider.size.y / 2, 0), Vector2.down * gndRaycastLenght, Color.red);
    }
void FixedUpdate()
    {
	// Se invierte este if para mejorar la legibilidad y reducir la complejidad cognitiva
        if (GameManager.sharedInstanceGM.currentGameState != GameState.inGame) return;

        float xAxis = Input.GetAxis("Horizontal");

        if (xAxis != 0)
        {
            Walk(xAxis);
        }
    }

Finalmente se edita el Input Submit para que no se ejecute al presionar la barra espaciadora.

A mi me sale un error al tratar de cambiar el GameState.
Me sale este error:
NullReferenceException: Object reference not set to an instance of an object
Player_controler.FixedUpdate () (at Assets/Scripts/Player_controler.cs:46)
¿Qué debo de hacer?
copie lo mismo del profesor

 if (GameManager.sharedInstance.currentGameState == GameState.inGame)
        {
            if (Input.GetButtonDown("Jump"))
            {
                Jump();
            }
        }
        

De hecho, al ver el juego en la primera clase ya imaginé cómo hacer los botones de juego

Solo hay que añadirle este if:

if (GameManager.sharedInstance.currentGameState == GameState.inGame)

A la función de getButtonDown(“jump”)

esat es mi solucion para JUMP

void Jump()
    {
        if(GameManager.sharedInstance.currentGameState == GameState.inGame)
        {
            if (IsTouchingTheGround()) //dependiendo del valor regresado del metodo, si es true pasara al salto
            {
                playerRB.AddForce(Vector2.up * jumpForce, ForceMode2D.Impulse);

            }
        }
        
    }```

El salto no debería estar en el FixedUpdate?

Para congelar el personaje en el aire al poner pausa usé rigidBody.Sleep() dentro del PlayerController

private void FixedUpdate()
    {
        if(GameManager.sharedInstance.currentGameState == GameState.inGame)
        {
            if (rigidBody.velocity.x < runningSpeed)
            {
                rigidBody.velocity = new Vector2(runningSpeed, rigidBody.velocity.y);
            }
        } else
        {
            rigidBody.Sleep();
        }
    }
 if (Input.GetButtonDown("Jump") && GameManager.SharedInstance.currentGameState == GameState.inGame) 
        {
            Jump();
  
        }

Lo mejor es de momento usar otra tecla para saltar y no el Space, porque esa tecla aparece como secundaria para el argumento “Jump”, así que al presionarla siempre va a saltar e inmediatamente va a cambiar a estado inGame. Ya luego de cambiar la tecla de salto se puede poner la condición if(GameManager.sharedInstance.currentGameState == GameState.inGame)

Ya sea en la propia función Jump() o en el Update.

Si alguien quiere implementar un menú de pausa este vídeo esta muy bueno.
https://www.youtube.com/watch?v=JivuXdrIHK0

Activar el auto completado del MonoDevelop es muy util

Este es el codigo de este video:

void FixedUpdate()
{
if (GameManager.sharedInstance.currentGameState == GameState.inGame)
{
if (rigidBody.velocity.x < runningSpeed)
{
rigidBody.velocity = new Vector2 (runningSpeed, rigidBody.velocity.y);
}
} else {
rigidBody.velocity = new Vector2(0, rigidBody.velocity.y);
}
}
![](

Genial

Mi solución fue, primero como el profesor indica, los cambios a las fisicas los dejo en FixedUpdate, las entradas de datos como movimiento y jump los inclui en Update, asi que dejando fuera los estados de animación inserte una condicional para limitar el movimiento y el brinco a que solo sea en inGame, las animaciones iniciales las dejo fuera ya que al iniciar el juego en GameState.menu el player caía sin animación y con eso lo solucione

 void Update()
    {
        animator.SetBool(STATE_ON_THE_GROUND, IsTouchingTheGround());
        animator.SetBool(STATE_FALLING, IsFalling());

        if (GameManager.sharedInstance.currentGameState == GameState.inGame) 
        {
            if(Input.GetButtonDown("Jump"))
            {
                jumpKey = true;
            }
            move = Input.GetAxis("Horizontal");
            if (move > 0)
            {
                spriteRenderer.flipX = false;
                animator.SetBool(STATE_RUNNING, true);
            }
            else if (move < 0)
            {
                spriteRenderer.flipX = true;
                animator.SetBool(STATE_RUNNING, true);
            }
            else
            {
                animator.SetBool(STATE_RUNNING, false);
            }
        }
        else
        {
            jumpKey = false;
            move = 0f;
        }

Aqui mi código en FixedUpdate

    void FixedUpdate()
    {
        if (jumpKey)
        {
            Jump();
            jumpKey = false;
        }
        rigidBody.velocity = new Vector2(move * runningSpeed, //x
                                        rigidBody.velocity.y); //y
    }

Yo le deshabilite el animator también por que no me gustaba que terminará la animación

            rigidBody.Sleep();
            animator.enabled = false;