Skip to content

4. Mecánicas de Elementos

Hektor edited this page Jan 16, 2024 · 30 revisions

Diseño de las Cajas

  1. Creación de la jerarquía de nodos y descripción de para qué usaremos cada parte

image

  1. Desactivamos la gravedad en el rigidbody

image

  1. Configuramos el Sprite, Carga Rápida -> Seleccionamos el Tileset, descentramos y recortamos región

image image

  1. Configuramos la máscara de colisión

image image image

  1. Incrementamos el z-index de visibilidad del personaje

image image

  1. Centramos el Raycast al centro

image

Desarrollo de las Cajas

Implementación de la lógica y la funcionalidad.

  1. Si el jugador se mueve sobre una caja recuperaremos su dirección y ejecutaremos una función de la caja para moverla en esa misma dirección. Eso solo ocurrirá si el raycast de la caja no detecta ninguna colisión. Así que crearemos un script Box.gd con la función
extends StaticBody2D

@onready var _raycast = $RayCast2D

# Función para mover la caja
func move(dir : Vector2) -> void:
	position += dir * Globals.TILE_SIZE

# Función que se llamará desde el jugador para empujar la caja
func push(dir: Vector2) -> bool:
	# Si hay una colisión en la dirección del empuje, devolvemos falso
	if check_collision(dir):
		return false
	# Si no hay colisión, movemos la caja en la dirección y retornamos true
	move(dir)
	return true
	
func check_collision(dir : Vector2) -> bool:
	_raycast.set_target_position(dir * Globals.TILE_SIZE)
	_raycast.force_raycast_update() # Forzamos la nueva dirección
	return _raycast.is_colliding()
  1. Para saber si chocamos contra una caja la añadiremos a un grupo de colisiones. Esto se puede hacer en el editor pero es mejor hacerlo en el código en la función _enter_tree de Box.gd. Esta función tiene prioridad sobre los demás eventos
""" Box.gd """
func _enter_tree() -> void: # se ejecuta antes que _ready
   add_to_group("boxes")
  1. Si el Player recibe una colisión, comprobaremos si es una caja y la empujaremos
""" Player.gd """
func _physics_process(_delta) -> void:
    for action in movements:
        if Input.is_action_just_pressed(action):
            var dir = movements[action]["dir"]
            # Si no ocurre una colisión movemos al personaje
            if not check_collision(dir):
                move(dir)
            else:
                # Si ocurre una colisión recuperamos el objeto de la colisión
                var collider = _raycast.get_collider()
                # Si es una caja y tiene el método "push" lo llamamos
                if collider.is_in_group("boxes") and collider.has_method("push"):
                    # Si el método push devuelve true, luego movemos al personaje
		    if collider.push(dir):
		        move(dir)

image

  1. Guardamos el nodo como una escena Box.tscn para luego trabajar con ella

image

Diseño de los Objetivos

Creación de la jerarquía de nodos y descripción de para qué usaremos cada parte.

  1. Creamos la jerarquía de un nuevo Nodo hijo llamado Target de tipo Area2D

image

  1. Configuramos el sprite y desactivamos el centrado

image image

  1. Configuramos la colisión, haremos un cuadro menor de 64x64, por ejemplo 5x5. Si hacemos la máscara 64x64, al tocar un extremo ya detectaría la colisión y no queremos eso, queremos detectarla cuando la caja esté completamente encima. No importa si el tamaño es 1x1 o 60x60, siempre que los extremos no tengan colisión funcionará.

image

  1. Desactivamos la colisión en la capa 1 pero conservamos la máscara. Esto permitirá que los demás no choquen contra el objetivo pero podamos detectar colisiones contra él igualmente.

image

  1. Si comprobamos las colisiones en el debug deberia verse algo así

image

  1. Cambiaremos la profundidad z-index del objetivo a 1 para que aparezca siempre por encima de la caja que es 0

image

Desarrollo de las Objetivos

Implementación de la lógica y la funcionalidad.

  1. Para detectar si una caja entra o sale del objetivo podemos crear unas señal body_entered y body_exited, ya que las cajas son de tipo RigidBody deberia detectarlas (si fueran staticbody no las detectaria). Primero añadimos un script Target.gd y dentro creamos la señal:

image image

image image

El código quedará asi

""" Target.gd """
extends Area2D

func _on_body_entered(body: Node2D) -> void:
    print("ha entrado un objeto")

func _on_body_exited(body: Node2D) -> void:
    print("ha salido un objeto")

image

  1. Para controlar si un objetivo está completo usaremos una variable booleana completed. Además como tendremos más de uno los tendremos contados en el grupo targets asi que los añadiremos ahí automáticamente al crearlos y de paso comprobaremos si el objeto que colisiona es una caja, aunque no es realmente necesario puede ser una buena práctica en caso de que extendiésemos el juego para permitir otro tipo de objetos interactivos
""" Target.gd """
extends Area2D

# Controlaremos si el objetivo se ha realizado o no 
var completed : bool = false

# Añadimos los targets a un grupo para llevar un recuento
func _enter_tree() -> void:
    add_to_group("targets")

func _on_body_entered(body: Node2D) -> void:
    if body.is_in_group("boxes"):
        completed = true

func _on_body_exited(body: Node2D) -> void:
    if body.is_in_group("boxes"):
        completed = false
  1. Guardamos el nodo como una nueva escena Target

image

  1. Podemos duplicar Control+D cajas y objetivos para hacer pruebas. Si todo funciona bien, como las cajas son cuerpos fisicas como las paredes, también deberian colisionar entre ellas

image

Clone this wiki locally