
Paso a paso videojuego cámara 0
Introducción
Este videojuego serio en 2D, desarrollado con Unity, está diseñado como una herramienta educativa para enseñar de manera práctica y entretenida los conceptos básicos sobre el funcionamiento de la cámara. A través de una experiencia interactiva, el jugador aprenderá desde las partes fundamentales de una cámara hasta principios clave como la exposición, el enfoque, la apertura del diafragma y la velocidad de obturación.
Con un estilo visual accesible y dinámicas diseñadas específicamente para facilitar la comprensión, el juego combina elementos pedagógicos con mecánicas simples para fomentar el aprendizaje autónomo. Su diseño en 2D y desarrollo en Unity permiten una ejecución fluida y multiplataforma, adecuada tanto para entornos educativos como para uso personal.
A continuación, se presenta el proceso detallado de creación de este videojuego.
Pantalla principal
Primeramente se va a configurar una pantalla para poder iniciar el juego, para esto se crea una nueva escena.
En este caso se llama menú inicial ya que será la pantalla con los botones para iniciar el juego
Se abre dicha escena para que se pueda ver en la jerarquía (Hierachy) en la parte izquierda del editor
Para crear el canvas inicial, dentro de la jerarquía se presiona clic derecho, UI y en la opción donde dice canvas
Ahora dentro del canvas creado anteriormente se va a crear un panel que será la imagen de fondo que se va a tener para la pantalla inicial
En este caso se le dio el nombre de inicio
El plano creado se debe ver de la siguiente manera
Ahora en la parte derecha, es decir en el inspector se busca el apartado donde dice background
Para la imagen que se va a poner se arrastra la imagen deseada en la parte de abajo en los assets
Posteriormente se selecciona la imagen y se arrastra al apartado background mostrado anteriormente
Después de esto el plano quedará automáticamente con la imagen asignada
Botones
En la jerarquía se vuelve a presionar clic derecho para crear un botón en las siguientes opciones
Se creará un botón y al desplegar saldrá un apartado llamado text TMP
Se selecciona para poder editar el texto que llevará el botón desde el inspector
Después de esto volviendo a seleccionar el botón se podrá cambiar el color nuevamente en el inspector
Ahora se repite el mismo procedimiento para crear el botón de salir
Una vez realizado esto se tendrán los dos botones que irán en la pantalla principal
Ya que se tienen acomodados los botones se les debe agregar la funcionalidad mediante scripts usando en este caso Visual Studio Code, para esto nos dirigimos al canvas
Y en la parte derecha, en el inspector seleccionamos la opción Add component para añadir un nuevo script
Se le da un nombre y se selecciona new script de la siguiente manera
Se abre el script en Visual y por defecto se va a ver así:
using UnityEngine;
public class MenuInicial : MonoBehaviour
{
}
Para poder cambiar entre escenas en la parte superior del código se debe importar la siguiente librería
using UnityEngine.SceneManagment;
Se crea la función jugar y dentro de este se escribe scene manger.load scene para cargar una escena y dentro del paréntesis se le escribe get active scene para abrir la escena en la que nos encontramos y se le suma 1 al final para que cargue la siguiente
public void Jugar(string Selección)
{
SceneManager.LoadScene(Selección);
}
Ahora se crea la función para salir de la aplicación
Adentro se escribe Application.Quit(); que será lo que va a cerrar el juego cuando este creado
public void salir()
{
Debug.Log("Salir...");
Application.Quit();
}
}
Para tener el orden de las escenas posteriores y que se pueda hacer el cambio de escenas correctamente se deben ordenar, para esto nos dirigimos a el apartado file y luego en build profiles en la parte superior
Aca se deben ordenar correctamente las escenas que se van a tener, en caso de agregar una una nueva se presiona Add open scenes y para ordenarlas se selecciona la escena y se arrastra al orden que se quiera, en este caso ya se ordenaron correctamente
Por último se le agrega la funcionalidad que se establece en los canvas a los botones creados, para esto abrimos el botón jugar primero y en el inspector en la parte derecha nos vamos al componente llamado button
En la parte de abajo de este componente donde dice On click se presiona el simbolo + de la parte inferior derecha
Cuando se le da al + saldra lo siguente dentro de esa interfaz
Ahora se debe arrastrar el canvas en donde dice None(Object)
Al lado donde dice no function se despliega ese menú y se le da en donde dice menú Inicial que corresponde al script creado anteriormente
Y dentro de menú inicial se busca la funcion jugar
Se repite la misma operación para el botón de salir a diferencia de que en el último paso en la función se selecciona salir
Para probar la funcionalidad de los botones se le da play en la parte superior del editor
Al presionar salir nos aparecerá el mensaje en la parte inferior ya que no se tiene el juego terminado por ahora solo se mostrará dicho mensaje
Y al presionar jugar nos lleva a la siguiente escena.
Historia
Antes de empezar con el juego, se da una breve introducción donde se le da al jugador su misión además de empezar con el aprendizaje que se quiere impartir
Se crea una nueva escena llamada introducción
Se repite el mismo procedimiento que se hizo para agregar una imagen en la portada para poder darle un fondo a esta nueva escena, posteriormente se añade un texto
Por último se le agrega un botón con el mismo script usado en la escena anterior para cambiar a la siguiente
Cinemática inicial
Para la cinemática inicial realizada con CapCut y se eañade a los assets de unity el video en formato mp4

Se creo una nueva escena llamada cinemática y siguiendo la misma lógica de poner los botones para la siguiente escena se creó un canvas donde se añadió dicho botón y se crea un nuevo objeto llamado videoPlayer


Seleccionando el video player nos dirigimos al inspector y en la opción llamada videoplayer donde dice videoClip se arrastra el video anterior

Al reproducir la escena se reproduce el video con el botón previamente establecido

Video de la cinemática
Ventana de instrucciones
Se sigue el mismo procedimiento anterior con la imagen requerida para darle las instrucciones al jugador
Para añadir las escenas se debe tener en cuenta el orden de las mismas, esto se hace yendo nuevamente a la sección filoe en la parte superior izquierda y en build profiles, más adelante si se siguen incluyendo escenas se debe tener en cuenta la realización de este paso
Construcción nivel 1
Para esto se importa un mapa previamente buscado en los assets de unity y se arrastra a la escena

Para poder poner los fondos se crea un nuevo objeto llamado Quad

Este objeto se verá como un rectángulo en la escena y se le pueden arrastrar imágenes previamente cargadas para generar el efecto del fondo

Y para llenar el mapa se pueden simplemente copiar y pegar para posteriormente distribuirlos a lo largo del mapa

Después de completar el primer nivel va a aparecer una pantalla con la información de cada una de las partes encontradas y la cámara armada
Para esto se hace una nueva escena y se le pone un plano siguiendo los mismos pasos anteriores

Construcción nivel 2
Animación de enemigos
Es necesario colocar el sprint en complementos del muñeco y coloque el avatar para que el enemigo siga al jugador
Script Enemy controller
using UnityEngine;
using System.Collections;
[RequireComponent(typeof(Animator))]
[RequireComponent(typeof(Rigidbody2D))]
[RequireComponent(typeof(Collider2D))] // Añadido para asegurar colisionador
public class EnemyControler : MonoBehaviour
{
[Header("Configuración de Objetivo")]
[SerializeField] private Transform playerTarget;
[SerializeField] private float detectionRange = 5f;
[SerializeField] private float stoppingDistance = 1f;
[Header("Configuración de Movimiento")]
[SerializeField] private float moveSpeed = 3f;
[SerializeField] private float acceleration = 5f;
[SerializeField] private float deceleration = 7f;
[Header("Configuración de Ataque")]
[SerializeField] private float attackRange = 0.8f;
[SerializeField] private int attackDamage = 10;
[SerializeField] private float attackCooldown = 2f;
[SerializeField] private float attackDuration = 1f;
[SerializeField] private LayerMask playerLayer;
public int damageOnCollision = 1;
public float knockbackForce = 10f;
public float stompForce = 15f;
[Header("Componentes")]
private Animator animator;
private Rigidbody2D rb;
private Collider2D enemyCollider; // Añadido referencia al collider
private float lastAttackTime;
private bool isAttacking;
private Vector2 currentVelocity;
private Coroutine attackCoroutine;
private void Awake()
{
animator = GetComponent<Animator>();
rb = GetComponent<Rigidbody2D>();
enemyCollider = GetComponent<Collider2D>(); // Inicializar collider
// Configuración física mejorada
rb.constraints = RigidbodyConstraints2D.FreezeRotation;
rb.gravityScale = 3f;
rb.collisionDetectionMode = CollisionDetectionMode2D.Continuous; // Mejor detección de colisiones
// Asegurar que el tag es correcto
gameObject.tag = "Enemy";
}
private void Start()
{
// Buscar al jugador si no está asignado
if (playerTarget == null)
{
GameObject player = GameObject.FindGameObjectWithTag("Player");
if (player != null) playerTarget = player.transform;
}
}
private void Update()
{
if (playerTarget == null) return;
float distanceToPlayer = Vector2.Distance(transform.position, playerTarget.position);
bool playerInRange = distanceToPlayer <= detectionRange;
bool shouldChase = playerInRange && distanceToPlayer > stoppingDistance && !isAttacking;
bool shouldAttack = distanceToPlayer <= attackRange && !isAttacking && Time.time >= lastAttackTime + attackCooldown;
HandleMovement(shouldChase);
HandleOrientation();
animator.SetBool("IsAttacking", isAttacking);
if (shouldAttack)
{
attackCoroutine = StartCoroutine(PerformAttack());
}
if (isAttacking && distanceToPlayer > attackRange * 1.5f)
{
InterruptAttack();
}
}
private void HandleMovement(bool shouldChase)
{
if (isAttacking)
{
currentVelocity = Vector2.zero;
rb.linearVelocity = new Vector2(0, rb.linearVelocity.y); // Mantener velocidad Y
return;
}
if (shouldChase)
{
Vector2 direction = (playerTarget.position - transform.position).normalized;
direction.y = 0;
currentVelocity = Vector2.MoveTowards(
currentVelocity,
direction * moveSpeed,
acceleration * Time.deltaTime);
}
else
{
currentVelocity = Vector2.MoveTowards(
currentVelocity,
Vector2.zero,
deceleration * Time.deltaTime);
}
rb.linearVelocity = new Vector2(currentVelocity.x, rb.linearVelocity.y);
animator.SetBool("running", shouldChase);
}
private void HandleOrientation()
{
if (playerTarget == null) return;
float directionToPlayer = playerTarget.position.x - transform.position.x;
if (Mathf.Abs(directionToPlayer) > 0.1f)
{
transform.localScale = new Vector3(
Mathf.Sign(directionToPlayer) * Mathf.Abs(transform.localScale.x),
transform.localScale.y,
transform.localScale.z);
}
}
private IEnumerator PerformAttack()
{
isAttacking = true;
lastAttackTime = Time.time;
// Detener movimiento pero mantener gravedad
currentVelocity = Vector2.zero;
rb.linearVelocity = new Vector2(0, rb.linearVelocity.y);
animator.SetTrigger("Attack");
animator.SetBool("IsAttacking", true);
yield return new WaitForSeconds(0.3f);
if (Vector2.Distance(transform.position, playerTarget.position) <= attackRange * 1.2f)
{
ApplyDamage();
}
yield return new WaitForSeconds(attackDuration - 0.3f);
isAttacking = false;
animator.SetBool("IsAttacking", false);
attackCoroutine = null;
}
private void InterruptAttack()
{
if (attackCoroutine != null)
{
StopCoroutine(attackCoroutine);
attackCoroutine = null;
}
isAttacking = false;
animator.SetBool("IsAttacking", false);
animator.SetTrigger("CancelAttack");
}
private void ApplyDamage()
{
Collider2D[] hits = Physics2D.OverlapCircleAll(transform.position, attackRange, playerLayer);
foreach (var hit in hits)
{
if (hit.CompareTag("Player"))
{
WarriorMovimiento warrior = hit.GetComponent<WarriorMovimiento>();
if(warrior != null)
{
Vector2 direction = (hit.transform.position - transform.position).normalized;
if(Mathf.Abs(direction.x) < 0.3f) direction.x = Mathf.Sign(direction.x) * 0.3f;
direction.y = Mathf.Clamp(direction.y, 0.3f, 0.7f);
warrior.RecibirDanio(direction, attackDamage);
}
break;
}
}
}
private void OnCollisionEnter2D(Collision2D collision)
{
if(collision.gameObject.CompareTag("Player"))
{
WarriorMovimiento warrior = collision.gameObject.GetComponent<WarriorMovimiento>();
if(warrior != null)
{
if(IsPlayerStomping(collision))
{
warrior.RecibirDanio(Vector2.up, damageOnCollision);
warrior.GetComponent<Rigidbody2D>().AddForce(Vector2.up * stompForce, ForceMode2D.Impulse);
// Aplicar fuerza hacia abajo al enemigo
rb.AddForce(Vector2.down * stompForce * 0.5f, ForceMode2D.Impulse);
}
else
{
Vector2 direction = (collision.transform.position - transform.position).normalized;
if(Mathf.Abs(direction.x) < 0.3f) direction.x = Mathf.Sign(direction.x) * 0.3f;
direction.y = Mathf.Clamp(direction.y, 0.3f, 0.7f);
warrior.RecibirDanio(direction, damageOnCollision);
}
}
}
}
private bool IsPlayerStomping(Collision2D collision)
{
foreach(ContactPoint2D contact in collision.contacts)
{
if(contact.normal.y < -0.5f && collision.rigidbody.linearVelocity.y < 0)
{
return true;
}
}
return false;
}
// Para visualizar rangos en el editor
private void OnDrawGizmosSelected()
{
// Rango de detección (amarillo)
Gizmos.color = Color.yellow;
Gizmos.DrawWireSphere(transform.position, detectionRange);
// Rango de ataque (rojo)
Gizmos.color = Color.red;
Gizmos.DrawWireSphere(transform.position, attackRange);
// Rango de parada (verde)
Gizmos.color = Color.green;
Gizmos.DrawWireSphere(transform.position, stoppingDistance);
}
}

Construcción nivel 3 Trivia
Se crea una nueva escena y se le añade un panel, en este caso se llama Nivel_1
Después se le añade una imagen a este panel
Posteriormente se añade un texto siguiendo el mismo paso anterior
Se añaden cuatro botones abajo que serán las opciones de respuesta
Se escriben los textos correspondientes a cada respuesta en los textos de los botones
Se arrastra una imagen en la en el apartado donde dice image en el inspector de la imagen que se creó al principio
Para realizar más preguntas copiamos el canvas creado para nivel 1 y se deben cambiar acorde a las preguntas que se vayan a realizar
Ya con las preguntas terminadas, se crea un gameobject en la jerarquía
En este caso se nombra GameManager
Y creamos un nuevo script llamado Manager

Volvemos al inspector del GameManager
Y dónde niveles se escribe la cantidad de preguntas realizadas, en este caso 4, al agregar esto se despliegan los elementos, en cada uno se deben arrastrar las preguntas
Ahora para las respuestas correctas nos vamos al inspector en cada una de las preguntas correctas y le damos la siguiente acción en el apartado onClick
Conclusión
Así fue como se creó Cámara 0, un videojuego 2D que mezcla lo educativo con lo narrativo para enseñar los fundamentos de la fotografía desde una experiencia interactiva y entretenida. A lo largo del desarrollo, se buscó no solo diseñar mecánicas que facilitaran el aprendizaje, sino también construir un universo visual y sonoro que envolviera al jugador en una historia cargada de nostalgia, exploración y descubrimiento.
Este documento detalla paso a paso cómo se construyó el juego en Unity: desde el diseño de los personajes y escenarios en pixel art, la planificación del guion visual, hasta la programación de mecánicas que permiten recolectar piezas y avanzar en la historia. Cada parte fue pensada no solo como una herramienta lúdica, sino como una forma de reforzar conceptos clave sobre el arte de la fotografía, su lenguaje, y su valor expresivo.
El objetivo principal siempre fue claro: demostrar que aprender también puede ser una aventura. Y Cámara 0 se convirtió en eso —una puerta a un mundo olvidado donde ver con intención es el primer paso para crear. Este proyecto no solo propone una forma alternativa de enseñar, sino que también rinde homenaje a la imagen como forma de memoria, arte y lenguaje universal.
Créditos
Autor: Catalina Rojas, Alexander Forero, Alejandro Rodriguez
Editor: Magister ingeniero Carlos Iván Pinzón Romero
Código: UCVS-9
Universidad: Universidad Central
Fuentes
KanarianDev. (2024, noviembre 3). ¡ANIMA a TU ENEMIGO en UNITY! [Video]. YouTube. https://www.youtube.com/watch?v=yWvUwpjUXkg&list=PL9v36qYVbM9hXN4qovHah718G_gfAkMJl&index=13
KanarianDev. (2024, mayo 3). ¡ANIMA a TU PERSONAJE en UNITY! [Video]. YouTube. https://www.youtube.com/watch?v=kM-jcFYVY3s
KanarianDev. (2024, agosto). ¡CÓMO hacer un ENEMIGO en UNITY! [Video]. YouTube. https://www.youtube.com/watch?v=ZvIB-u-jgEA
KanarianDev. (2024, agosto 21). ¡CÓMO hacer un ENEMIGO en UNITY! [Video]. YouTube. https://www.youtube.com/watch?v=ZvIB-u-jgEA&list=PL9v36qYVbM9hXN4qovHah718G_gfAkMJl&index=11
KanarianDev. (2024, mayo 15). ¡COMO usar el ANIMATOR en UNITY! [Video]. YouTube. https://www.youtube.com/watch?v=lu-w7A8weVI
KanarianDev. (2024, diciembre 19). Domina el Tilemap en Unity y crea juegos increíbles [Video]. YouTube. https://www.youtube.com/watch?v=ENOtmXIbA14