Unreal Engine: Creación de un Sistema de Carga/Guardado de Partidas con SaveGame y Blueprint
Este tutorial te guiará paso a paso en la creación de un sistema de guardado y carga de partidas en Unreal Engine 5, utilizando Blueprints y el objeto SaveGame. Aprenderás a persistir datos importantes del juego, como la posición del jugador, el inventario y el progreso de la partida, para ofrecer una experiencia de juego fluida y memorable.
Introducción al Guardado y Carga de Partidas en Unreal Engine 🎮
Crear un sistema de guardado y carga de partidas es fundamental para casi cualquier videojuego. Permite a los jugadores continuar su progreso donde lo dejaron, mejorando significativamente la experiencia de usuario. En Unreal Engine, esto se logra principalmente a través del objeto SaveGame, que actúa como un contenedor para los datos que queremos persistir.
En este tutorial, exploraremos cómo implementar un sistema robusto utilizando Blueprints. Cubriremos la creación de un objeto SaveGame, la gestión de diferentes ranuras de guardado y cómo cargar y aplicar esos datos al estado actual del juego.
¿Por qué es importante un sistema de guardado? 🤔
- Persistencia: Mantiene el progreso del jugador entre sesiones de juego.
- Comodidad: Permite a los jugadores tomarse un descanso y volver más tarde.
- Flexibilidad: Facilita la implementación de múltiples partidas guardadas o puntos de control.
- Experiencia de Usuario: Reduce la frustración de perder el progreso y mejora la retención de jugadores.
1. Conceptos Fundamentales de SaveGame en Unreal Engine ✨
Antes de sumergirnos en la implementación, es crucial entender los componentes clave involucrados en el sistema de guardado de Unreal Engine.
El Objeto SaveGame 📖
El SaveGame es la piedra angular de nuestro sistema. Es un objeto de Unreal Engine que se utiliza para almacenar los datos que deseamos guardar. Es como una 'caja' donde metemos toda la información relevante antes de 'cerrarla' (guardarla en disco) y 'abrirla' (cargarla desde disco).
Los datos que se guardan en el SaveGame son típicamente variables que representan el estado del juego: salud del jugador, posición, inventario, misiones completadas, etc. Estas variables deben ser declaradas dentro de la clase SaveGame.
GameInstance y GameState 📌
El GameInstance es un objeto que persiste durante toda la vida de la aplicación del juego, incluso a través de diferentes mapas. Es un lugar excelente para almacenar referencias al sistema de guardado o datos globales que necesitan persistir durante una sola sesión de juego (por ejemplo, qué ranura de guardado está activa actualmente).
El GameState (y PlayerState) son objetos que persisten durante la vida de un solo nivel (mapa) en el juego. Son útiles para almacenar el estado actual del juego o del jugador dentro de un nivel, pero no persisten a través de cargas de nivel. Para que sus datos persistan a través de niveles y sesiones, deberás copiarlos hacia/desde tu SaveGame.
SaveGame no se guarda automáticamente. Debes serializarlo explícitamente a disco y deserializarlo desde disco usando funciones como SaveGameToSlot y LoadGameFromSlot.Flujo Básico del Sistema de Guardado/Carga
2. Creando Nuestra Clase SaveGame en Blueprint 🛠️
El primer paso es crear nuestra propia clase SaveGame que contendrá todas las variables que queremos guardar. Para este tutorial, guardaremos la posición del jugador, su salud y un array simple de nombres de objetos en el inventario.
Paso 2.1: Crear el Blueprint BP_MySaveGame
- En el Content Browser, haz clic derecho y selecciona Blueprint Class.
- En la ventana Pick Parent Class, busca y selecciona
SaveGame. NombraloBP_MySaveGame. - Abre
BP_MySaveGame.
Paso 2.2: Añadir Variables al BP_MySaveGame
Dentro de BP_MySaveGame, añade las siguientes variables:
PlayerLocation: TipoVector. Representará la última posición guardada del jugador.PlayerHealth: TipoFloat. La salud actual del jugador.InventoryItems: TipoArraydeString. Una lista simple de ítems en el inventario.SaveGameSlotName: TipoString. El nombre de la ranura de guardado.SaveGameSlotIndex: TipoInteger. El índice de la ranura de guardado (útil para UIs).
3. Implementando la Lógica de Guardado en Blueprint 💾
Ahora que tenemos nuestro objeto SaveGame, necesitamos la lógica para llenarlo con datos y guardarlo en un slot.
Paso 3.1: Función para Guardar Partida (Save Game) en PlayerController o GameMode
Es una buena práctica encapsular la lógica de guardado en un lugar central, como tu PlayerController (si el guardado es por jugador) o GameMode (para datos de juego global). Para este ejemplo, lo haremos en PlayerController.
-
Abre tu
PlayerControllerBlueprint (o crea uno si no tienes). -
Crea una nueva función llamada
SaveGameData. -
Dentro de
SaveGameData, implementa la siguiente lógica:- Comprobar si existe
SaveGameen el Slot: UsaDoesSaveGameExistcon elSaveGameSlotName(por ejemplo, "Slot1"). - Crear o Cargar
SaveGame:- Si existe:
LoadGameFromSlot(claseBP_MySaveGame). - Si NO existe:
CreateSaveGameObject(claseBP_MySaveGame).
- Si existe:
- Asignar datos al
SaveGameObjeto:- Obtén la ubicación actual del jugador (
GetActorLocationdeSelfsi es elPlayerControllero delPawnposeído). - Obtén la salud del jugador (asumiendo que tienes una variable de salud en tu
PlayerControlleroPawn). - Obtén los ítems del inventario (asumiendo que tienes una lista de ítems).
- Setea estas variables en tu
BP_MySaveGame(por ejemplo,Set PlayerLocation).
- Obtén la ubicación actual del jugador (
- Guardar el
SaveGameen el Slot: UsaSaveGameToSlot, pasando el objetoSaveGamerecién llenado y elSaveGameSlotNameySaveGameSlotIndex. -
💡 Consejo: Puedes usar el `PlayerState` para obtener la salud, el inventario, etc., ya que este persiste a través de los respawns del jugador en un mismo nivel.
- Comprobar si existe
Paso 3.2: Disparar la Función de Guardado
Puedes llamar a SaveGameData en diferentes momentos:
- Al presionar una tecla (por ejemplo, P para "Guardar") en
PlayerController. - Al alcanzar un punto de control (Checkpoint).
- Al salir del juego (evento
OnEndPlayenGameModeoGameInstance). - Mediante un widget de menú de pausa.
// Ejemplo de Blueprint para llamar a SaveGameData al presionar 'P'
Event Keyboard P
Pressed
Call SaveGameData (Target: Self)
4. Implementando la Lógica de Carga en Blueprint ↩️
Cargar una partida es el proceso inverso: recuperamos el SaveGame del disco y usamos sus datos para restaurar el estado del juego.
Paso 4.1: Función para Cargar Partida (Load Game) en PlayerController o GameMode
-
En tu
PlayerControllerBlueprint, crea una nueva función llamadaLoadGameData. -
Implementa la siguiente lógica:
- Comprobar si existe
SaveGame: UsaDoesSaveGameExistcon elSaveGameSlotName. - Cargar
SaveGame: Si existe, usaLoadGameFromSlot(claseBP_MySaveGame). - Aplicar datos del
SaveGameal juego:- Obtén la referencia al
BP_MySaveGamecargado (recuerdaCast To BP_MySaveGame). - Obtén
PlayerLocationdelSaveGamey usaSetActorLocationen elPlayerPawn(GetControlledPawn). - Obtén
PlayerHealthy actualiza la salud delPlayerPawnoPlayerState. - Obtén
InventoryItemsy actualiza el inventario del jugador.
- Obtén la referencia al
-
⚠️ Advertencia: Al cargar la ubicación del jugador, asegúrate de que el `Pawn` ya esté en el mundo y que la colisión esté desactivada temporalmente si es necesario para evitar problemas de teletransporte.
- Comprobar si existe
Paso 4.2: Disparar la Función de Carga
La función LoadGameData generalmente se llama en los siguientes escenarios:
- Al iniciar una nueva partida desde el menú principal (después de seleccionar "Cargar Partida").
- Al morir el jugador y reiniciar desde el último punto de guardado.
- Al presionar una tecla (por ejemplo, L para "Cargar") en
PlayerController(solo para pruebas).
// Ejemplo de Blueprint para llamar a LoadGameData al presionar 'L'
Event Keyboard L
Pressed
Call LoadGameData (Target: Self)
5. Gestión de Múltiples Slots de Guardado y UI 📦
Los juegos modernos suelen permitir a los jugadores tener múltiples partidas guardadas. Esto se logra fácilmente con el sistema SaveGame usando diferentes SlotName y SlotIndex.
5.1: Almacenar Información de Slots Disponibles
Para mostrar una lista de partidas guardadas en un menú, necesitarás saber qué slots tienen un SaveGame y quizás alguna metainformación (fecha de guardado, nombre del nivel, etc.).
-
BP_SaveGameMeta(Opcional pero recomendado): Puedes crear otra claseSaveGamellamadaBP_SaveGameMetaque solo guarde metadatos para cada slot. EsteBP_SaveGameMetase guardaría en un slot fijo (ej: "MetaSlot") y contendría unMapoArrayde estructuras con información de cadaBP_MySaveGame(nombre del slot, fecha, captura de pantalla, etc.).- Cuando guardas
BP_MySaveGameen "Slot1", también actualizasBP_SaveGameMetapara añadir o actualizar la entrada de "Slot1" con sus metadatos. - Cuando muestras la UI de "Cargar Partida", cargas
BP_SaveGameMetapara poblar la lista de opciones.
- Cuando guardas
5.2: Interfaz de Usuario (UMG) para Guardar/Cargar
- Widget
WBP_SaveLoadMenu: Crea un widget UMG que contenga botones para "Guardar Juego" y "Cargar Juego", así como una lista de slots de guardado disponibles. - Lógica del Widget:
- Al inicializar el widget, puedes iterar a través de un rango de posibles
SlotIndex(por ejemplo, del 0 al 4) y usarDoesSaveGameExistpara determinar qué slots están ocupados. - Si un slot existe, cárgalo temporalmente (
LoadGameFromSlot) para obtener suSaveGameSlotNamey cualquier metadato (como la fecha de guardado si la incluyes enBP_MySaveGame) y muéstralos en la UI. - Cuando el jugador selecciona un slot, pasas el
SlotNameySlotIndexa tus funcionesSaveGameDataoLoadGameDataen elPlayerController.
- Al inicializar el widget, puedes iterar a través de un rango de posibles
WBP_SaveLoadMenu con una lista de botones/entradas.DoesSaveGameExist.LoadGameFromSlot (solo para leer metadatos, no para aplicar al juego).SaveGameData o LoadGameData en el PlayerController con el SlotName y SlotIndex seleccionados.6. Consideraciones Avanzadas y Mejores Prácticas 🚀
Guardado Automático (Autosave)
Puedes implementar un guardado automático periódico o en eventos específicos (por ejemplo, al entrar en una nueva área, al completar una misión). Asegúrate de no interrumpir la experiencia del jugador y de que el guardado automático no sobrescriba una partida guardada manual importante sin advertencia.
Seguridad y Anti-Trampas
Los archivos .sav de Unreal Engine se pueden editar fácilmente con un editor hexadecimal si no se cifran. Para juegos que requieren mayor seguridad:
- Cifrado: Utiliza la opción
Encrypten el nodoSaveGameToSloto implementa tu propio algoritmo de cifrado/descifrado antes de guardar/cargar. - Validación de Checksum: Calcula un checksum de tus datos antes de guardar y verifícalo al cargar para detectar modificaciones.
Qué No Guardar Directamente en SaveGame
- Referencias a Actores en el Nivel: Guarda identificadores únicos (como
FNameo unGuid) y luego busca o recrea esos actores al cargar. - Datos que pueden ser recalculados: Por ejemplo, si tienes una fórmula para la fuerza de un enemigo basada en el nivel, guarda el nivel y recalcula la fuerza al cargar.
- Datos de Gran Tamaño: Si el
SaveGamese vuelve muy grande, puede ralentizar el proceso de guardado/carga. Optimiza la información que guardas.
Manejo de Errores y Edge Cases
- Slots corruptos: Implementa un manejo de errores si
LoadGameFromSlotfalla o devuelve un objeto nulo. - Versiones de guardado: Si tu juego evoluciona y añades o quitas variables en
BP_MySaveGame, las partidas guardadas antiguas podrían no ser compatibles. Considera un sistema de versionado en tuSaveGamepara migrar datos o invalidar partidas antiguas.
Ejemplo de Sistema de Versionado Básico
En tu BP_MySaveGame, añade una variable SaveGameVersion de tipo Integer. Al guardar, siempre establece esta variable a la versión actual. Al cargar, compara la SaveGameVersion cargada con la versión actual del juego. Si son diferentes, puedes implementar lógica para adaptar los datos o notificar al usuario que el guardado es obsoleto.
Barra de Progreso de Guardado/Carga
Para partidas muy grandes, el proceso de guardado o carga puede tardar unos segundos. Es una buena práctica mostrar una barra de progreso o un icono de "guardando..." para que el jugador sepa que el juego no se ha congelado.
Conclusión ✅
Has llegado al final de este tutorial sobre cómo crear un sistema de guardado y carga de partidas en Unreal Engine usando Blueprints. Hemos cubierto desde los conceptos fundamentales del objeto SaveGame hasta la implementación práctica de funciones de guardado y carga, pasando por la gestión de múltiples slots y consideraciones avanzadas. Con este conocimiento, puedes construir un sistema de persistencia robusto para tus juegos, mejorando enormemente la experiencia del jugador.
Experimenta con diferentes tipos de datos y escenarios de guardado para adaptar el sistema a las necesidades específicas de tu proyecto. ¡Feliz desarrollo!
Tutoriales relacionados
- Dominando el Sistema de Animación en Unreal Engine 5: Desde Retargeting hasta Blend Spacesintermediate20 min
- Unreal Engine: Creando Cinemáticas Dinámicas con Sequencer y Blueprintintermediate20 min
- Unreal Engine: Creando un Sistema de Puzzles de Bloques Deslizantes con Blueprintintermediate20 min
- Unreal Engine: Creación de un Sistema de Daño y Puntos de Golpe (HP) con Blueprintintermediate20 min
- Unreal Engine: Implementando IA Básica con Behaviour Trees y EQS para Enemigosintermediate15 min
Comentarios (0)
Aún no hay comentarios. ¡Sé el primero!