Skip to content
IRC-CodingIRC-Coding
Fundamentos Game DevelopmentUnity Unreal Engine3D Graphics PhysicsAnimationProgramación de JuegosAlgoritmosAlgoritmoFundamentos

Fundamentos Game Development: Unity, Unreal Engine

Aprende Game Development con Unity, Unreal Engine, 3D Graphics, Physics y Animation. Programación de juegos, Game Loop, Rendering y Collision Detection.

S

schutzgeist

33 min read
Fundamentos Game Development: Unity, Unreal Engine

Fundamentos de Desarrollo de Juegos: Unity, Unreal Engine, Gráficos 3D, Physics y Animación

Este artículo es una introducción completa a los fundamentos del desarrollo de juegos incluyendo Unity, Unreal Engine, gráficos 3D, Physics Engines y animación con ejemplos prácticos.

En resumen

El desarrollo de juegos combina programación, gráficos 3D, física y animación. Unity y Unreal Engine son los motores líderes, C# y C++ son los lenguajes de programación principales, OpenGL/Vulkan son las APIs de gráficos.

Con proyectos prácticos y casos de estudio reales, el curso te proporcionará el conocimiento necesario para iniciar o avanzar tu carrera en desarrollo de juegos.

Obtén más información aquí: Game Development E-Degree.

Descripción técnica concisa

Desarrollo de juegos es la creación de software interactivo de entretenimiento mediante la combinación de programación, gráficos, sonido y mecánicas de juego.

Componentes clave:

Game Engines

  • Unity: Motor C# para juegos 2D/3D, multiplataforma
  • Unreal Engine: Motor C++ para juegos AAA, gráficos de alta gama
  • Godot: Motor de código abierto con GDScript/C#
  • CryEngine: Motor C++ para gráficos realistas

Gráficos 3D

  • Rendering Pipeline: Vertex → Fragment → Display
  • Shaders: GLSL/HLSL para efectos visuales
  • Lighting: Modelos de iluminación (Phong, PBR)
  • Texturing: Materiales 2D/3D y mapping

Physics Engines

  • Collision Detection: AABB, OBB, Sphere, Mesh
  • Physics Simulation: Rigid Bodies, Forces, Constraints
  • Integration: Verlet, Euler, RK4
  • Optimization: Spatial Hashing, Broad Phase

Animation Systems

  • Skeletal Animation: Bones, Weights, Skinning
  • Keyframe Animation: Animación basada en timeline
  • Procedural Animation: Generada algorítmicamente
  • Morph Targets: Interpolación de formas

Puntos clave para examen

  • Desarrollo de juegos: Creación de juegos interactivos
  • Unity: Motor C# para juegos multiplataforma
  • Unreal Engine: Motor C++ para juegos AAA
  • Gráficos 3D: Rendering Pipeline, Shaders, Lighting
  • Physics: Detección de colisiones, simulación física
  • Animation: Animación Skeletal, Keyframe, Procedural
  • Game Loop: Ciclo Update-Render, Fixed Time Step
  • Relevancia profesional: Desarrollo y tecnologías de juegos modernos

Componentes clave

  1. Game Engine: Base para el desarrollo de juegos
  2. Rendering Pipeline: Procesamiento y visualización de gráficos
  3. Physics Engine: Simulación física y detección de colisiones
  4. Animation System: Movimiento y animación de personajes
  5. Input System: Interacción del usuario y controles
  6. Audio System: Efectos de sonido y música
  7. UI System: Interfaz de usuario y HUD
  8. Networking: Funcionalidad multijugador y en línea

Ejemplos prácticos

1. Juego Unity con C# - Plataformas 2D

using UnityEngine;
using System.Collections;

// Player Controller para plataformas 2D
public class PlayerController : MonoBehaviour
{
    [Header("Movement Settings")]
    [SerializeField] private float moveSpeed = 5f;
    [SerializeField] private float jumpForce = 10f;
    [SerializeField] private LayerMask groundLayer;
    
    [Header("Animation Settings")]
    [SerializeField] private Animator animator;
    [SerializeField] private Transform groundCheck;
    [SerializeField] private float groundCheckRadius = 0.2f;
    
    private Rigidbody2D rb;
    private bool isGrounded = false;
    private bool facingRight = true;
    private float horizontalInput;
    private Vector2 velocity = Vector2.zero;
    
    // Physics Constants
    private const float GROUND_CHECK_DISTANCE = 0.1f;
    private const float COYOTE_TIME = 0.2f;
    private const float MAX_FALL_SPEED = 20f;
    
    private void Awake()
    {
        rb = GetComponent<Rigidbody2D>();
        
        if (animator == null)
            animator = GetComponent<Animator>();
            
        if (groundCheck == null)
            groundCheck = transform;
    }
    
    private void Update()
    {
        HandleInput();
        UpdateAnimations();
    }
    
    private void FixedUpdate()
    {
        HandleMovement();
        HandlePhysics();
    }
    
    private void HandleInput()
    {
        // Movimiento horizontal
        horizontalInput = Input.GetAxisRaw("Horizontal");
        
        // Input de salto
        if (Input.GetButtonDown("Jump") && isGrounded)
        {
            Jump();
        }
        
        // Input de dash
        if (Input.GetButtonDown("Dash"))
        {
            Dash();
        }
        
        // Input de ataque
        if (Input.GetButtonDown("Attack"))
        {
            Attack();
        }
    }
    
    private void HandleMovement()
    {
        // Movimiento horizontal con aceleración suave
        float targetVelocityX = horizontalInput * moveSpeed;
        velocity.x = Mathf.SmoothDamp(velocity.x, targetVelocityX, 0.1f);
        
        // Aplicar movimiento
        rb.velocity = new Vector2(velocity.x, rb.velocity.y);
        
        // Voltear personaje según dirección
        if (horizontalInput != 0 && horizontalInput != transform.localScale.x)
        {
            Flip();
        }
    }
    
    private void HandlePhysics()
    {
        // Verificar contacto con suelo
        isGrounded = Physics2D.OverlapCircle(groundCheck.position, groundCheckRadius, groundLayer);
        
        // Limitar velocidad de caída
        if (rb.velocity.y < -MAX_FALL_SPEED)
        {
            rb.velocity = new Vector2(rb.velocity.x, -MAX_FALL_SPEED);
        }
        
        // Aplicar gravedad
        if (!isGrounded)
        {
            rb.velocity += Vector2.up * Physics2D.gravity * Time.fixedDeltaTime * 2f;
        }
    }
    
    private void Jump()
    {
        // Resetear velocidad vertical
        rb.velocity = new Vector2(rb.velocity.x, 0f);
        
        // Aplicar fuerza de salto
        rb.AddForce(Vector2.up * jumpForce, ForceMode2D.Impulse);
        
        // Disparar animación de salto
        if (animator != null)
        {
            animator.SetTrigger("Jump");
        }
        
        // Reproducir sonido de salto
        PlaySound("Jump");
    }
    
    private void Dash()
    {
        // Aplicar fuerza de dash
        float dashForce = 15f;
        float dashDirection = facingRight ? 1f : -1f;
        
        rb.velocity = new Vector2(dashForce * dashDirection, rb.velocity.y);
        
        // Disparar animación de dash
        if (animator != null)
        {
            animator.SetTrigger("Dash");
        }
        
        // Reproducir sonido de dash
        PlaySound("Dash");
        
        // Iniciar cooldown de dash
        StartCoroutine(DashCooldown());
    }
    
    private void Attack()
    {
        // Disparar animación de ataque
        if (animator != null)
        {
            animator.SetTrigger("Attack");
        }
        
        // Reproducir sonido de ataque
        PlaySound("Attack");
        
        // Detectar y dañar enemigos
        DetectAndDamageEnemies();
    }
    
    private void Flip()
    {
        facingRight = !facingRight;
        Vector3 theScale = transform.localScale;
        theScale.x *= -1;
        transform.localScale = theScale;
    }
    
    private void UpdateAnimations()
    {
        if (animator == null) return;
        
        // Configurar parámetros de movimiento
        animator.SetFloat("Speed", Mathf.Abs(rb.velocity.x));
        animator.SetBool("IsGrounded", isGrounded);
        animator.SetFloat("VerticalSpeed", rb.velocity.y);
        
        // Configurar estados de animación
        if (Mathf.Abs(rb.velocity.x) > 0.1f)
        {
            animator.SetBool("IsMoving", true);
        }
        else
        {
            animator.SetBool("IsMoving", false);
        }
    }
    
    private void DetectAndDamageEnemies()
    {
        // Detectar enemigos en rango de ataque
        Collider2D[] enemies = Physics2D.OverlapCircleAll(
            transform.position, 
            1.5f, 
            LayerMask.GetMask("Enemy")
        );
        
        foreach (Collider2D enemy in enemies)
        {
            EnemyController enemyController = enemy.GetComponent<EnemyController>();
            if (enemyController != null)
            {
                enemyController.TakeDamage(10);
            }
        }
    }
    
    private void PlaySound(string soundName)
    {
        // Reproducir sonido a través de AudioManager
        AudioManager.Instance.PlaySound(soundName);
    }
    
    private IEnumerator DashCooldown()
    {
        // Deshabilitar dash durante el cooldown
        float cooldownTime = 1f;
        
        // Retroalimentación visual
        SpriteRenderer spriteRenderer = GetComponent<SpriteRenderer>();
        if (spriteRenderer != null)
        {
            Color originalColor = spriteRenderer.color;
            spriteRenderer.color = new Color(1f, 1f, 1f, 0.5f);
            
            yield return new WaitForSeconds(cooldownTime);
            
            spriteRenderer.color = originalColor;
        }
    }
    
    // Manejo de colisiones
    private void OnCollisionEnter2D(Collision2D collision)
    {
        if (collision.gameObject.CompareTag("Enemy"))
        {
            TakeDamage(10);
        }
        else if (collision.gameObject.CompareTag("Collectible"))
        {
            CollectItem(collision.gameObject);
        }
        else if (collision.gameObject.CompareTag("Platform"))
        {
            // Lógica específica de plataforma
            HandlePlatformCollision(collision);
        }
    }
    
    private void TakeDamage(int damage)
    {
        // Aplicar daño al jugador
        // Esto sería manejado por un sistema de salud separado
        
        // Disparar animación de daño
        if (animator != null)
        {
            animator.SetTrigger("Hurt");
        }
        
        // Reproducir sonido de daño
        PlaySound("Hurt");
        
        // Knockback
        Vector2 knockbackDirection = (transform.position - collision.transform.position).normalized;
        rb.AddForce(knockbackDirection * 5f, ForceMode2D.Impulse);
        
        // Iniciar frames de invulnerabilidad
        StartCoroutine(InvincibilityFrames());
    }
    
    private void CollectItem(GameObject item)
    {
        // Lógica de recolección de item
        CollectibleItem collectible = item.GetComponent<CollectibleItem>();
        if (collectible != null)
        {
            collectible.Collect();
        }
        
        // Reproducir sonido de recolección
        PlaySound("Collect");
        
        // Disparar animación de recolección
        if (animator != null)
        {
            animator.SetTrigger("Collect");
        }
    }
    
    private void HandlePlatformCollision(Collision2D collision)
    {
        // Hacer al jugador hijo de la plataforma móvil
        if (collision.gameObject.CompareTag("MovingPlatform"))
        {
            transform.SetParent(collision.transform);
        }
    }
    
    private void OnCollisionExit2D(Collision2D collision)
    {
        // Desvincularse de plataformas móviles
        if (collision.gameObject.CompareTag("MovingPlatform"))
        {
            transform.SetParent(null);
        }
    }
    
    private IEnumerator InvincibilityFrames()
    {
        // Habilitar invulnerabilidad
        Physics2D.IgnoreLayerCollision(gameObject, LayerMask.GetMask("Enemy"));
        
        // Retroalimentación visual
        SpriteRenderer spriteRenderer = GetComponent<SpriteRenderer>();
        if (spriteRenderer != null)
        {
            Color originalColor = spriteRenderer.color;
            spriteRenderer.color = new Color(1f, 1f, 1f, 0.3f);
            
            yield return new WaitForSeconds(1f);
            
            spriteRenderer.color = originalColor;
        }
        
        // Deshabilitar invulnerabilidad
        Physics2D.IgnoreLayerCollision(gameObject, LayerMask.GetMask("Enemy"));
    }
    
    // Métodos públicos
    public void SetVelocity(Vector2 newVelocity)
    {
        velocity = newVelocity;
    }
    
    public Vector2 GetVelocity()
    {
        return velocity;
    }
    
    public bool IsFacingRight()
    {
        return facingRight;
    }
    
    public bool IsGrounded()
    {
        return isGrounded;
    }
}

// Enemy Controller
public class EnemyController : MonoBehaviour
{
    [Header("Enemy Settings")]
    [SerializeField] private int maxHealth = 100;
    [SerializeField] private int damage = 10;
    [SerializeField] private float moveSpeed = 2f;
    [SerializeField] private float detectionRange = 5f;
    [SerializeField] private float attackRange = 1f;
    [SerializeField] private LayerMask playerLayer;
    
    [Header("Animation")]
    [SerializeField] private Animator animator;
    [SerializeField] private GameObject deathEffect;
    
    private int currentHealth;
    private Transform player;
    private bool isDead = false;
    private bool canAttack = true;
    
    private void Awake()
    {
        currentHealth = maxHealth;
        
        if (animator == null)
            animator = GetComponent<Animator>();
    }
    
    private void Start()
    {
        // Encontrar jugador
        GameObject playerObj = GameObject.FindGameObjectWithTag("Player");
        if (playerObj != null)
        {
            player = playerObj.transform;
        }
    }
    
    private void Update()
    {
        if (isDead) return;
        
        // Verificar si el jugador está en rango
        if (player != null)
        {
            float distanceToPlayer = Vector2.Distance(transform.position, player.position);
            
            if (distanceToPlayer <= detectionRange)
            {
                if (distanceToPlayer <= attackRange && canAttack)
                {
                    Attack();
                }
                else
                {
                    MoveTowardsPlayer();
                }
            }
            else
            {
                Patrol();
            }
        }
        
        UpdateAnimations();
    }
    
    private void MoveTowardsPlayer()
    {
        if (player == null) return;
        
        Vector2 direction = (player.position - transform.position).normalized;
        transform.position = Vector2.MoveTowards(
            transform.position, 
            player.position, 
            moveSpeed * Time.deltaTime
        );
        
        // Voltear enemigo según dirección
        if (direction.x > 0 && transform.localScale.x < 0)
        {
            Flip();
        }
        else if (direction.x < 0 && transform.localScale.x > 0)
        {
            Flip();
        }
    }
    
    private void Patrol()
    {
        // Comportamiento de patrulla simple
        // Esto se expandiría con waypoints o movimiento aleatorio
        transform.position += Vector2.right * moveSpeed * Time.deltaTime * Mathf.Sign(Mathf.Sin(Time.time));
    }
    
    private void Attack()
    {
        // Disparar animación de ataque
        if (animator != null)
        {
            animator.SetTrigger("Attack");
        }
        
        // Iniciar cooldown de ataque
        StartCoroutine(AttackCooldown());
        
        // Infligir daño al jugador si está en rango
        float distanceToPlayer = Vector2.Distance(transform.position, player.position);
        if (distanceToPlayer <= attackRange)
        {
            PlayerController playerController = player.GetComponent<PlayerController>();
            if (playerController != null)
            {
                playerController.TakeDamage(damage);
            }
        }
    }
    
    private IEnumerator AttackCooldown()
    {
        canAttack = false;
        yield return new WaitForSeconds(1.5f);
        canAttack = true;
    }
    
    private void UpdateAnimations()
    {
        if (animator == null) return;
        
        animator.SetBool("IsDead", isDead);
        animator.SetBool("IsMoving", player != null && Vector2.Distance(transform.position, player.position) > attackRange);
    }
    
    private void Flip()
    {
        Vector3 theScale = transform.localScale;
        theScale.x *= -1;
        transform.localScale = theScale;
    }
    
    public void TakeDamage(int damageAmount)
    {
        if (isDead) return;
        
        currentHealth -= damageAmount;
        
        // Disparar animación de daño
        if (animator != null)
        {
            animator.SetTrigger("Hurt");
        }
        
        // Retroalimentación visual
        SpriteRenderer spriteRenderer = GetComponent<SpriteRenderer>();
        if (spriteRenderer != null)
        {
            StartCoroutine(FlashRed());
        }
        
        // Verificar si está muerto
        if (currentHealth <= 0)
        {
            Die();
        }
    }
    
    private IEnumerator FlashRed()
    {
        SpriteRenderer spriteRenderer = GetComponent<SpriteRenderer>();
        if (spriteRenderer != null)
        {
            Color originalColor = spriteRenderer.color;
            spriteRenderer.color = Color.red;
            
            yield return new WaitForSeconds(0.1f);
            
            spriteRenderer.color = originalColor;
        }
    }
    
    private void Die()
    {
        isDead = true;
        
        // Disparar animación de muerte
        if (animator != null)
        {
            animator.SetTrigger("Die");
        }
        
        // Deshabilitar colisión
        GetComponent<Collider2D>().enabled = false;
        
        // Deshabilitar movimiento
        enabled = false;
        
        // Generar efecto de muerte
        if (deathEffect != null)
        {
            Instantiate(deathEffect, transform.position, Quaternion.identity);
        }
        
        // Eliminar enemigo tras retraso
        StartCoroutine(DestroyAfterDelay());
    }
    
    private IEnumerator DestroyAfterDelay()
    {
        yield return new WaitForSeconds(2f);
        Destroy(gameObject);
    }
}

// Collectible Item
public class CollectibleItem : MonoBehaviour
{
    [Header("Item Settings")]
    [SerializeField] private string itemName = "Coin";
    [SerializeField] private int value = 1;
    [SerializeField] private GameObject collectEffect;
    [SerializeField] private AudioClip collectSound;
    
    private bool isCollected = false;
    
    private void OnTriggerEnter2D(Collider2D other)
    {
        if (isCollected) return;
        
        if (other.CompareTag("Player"))
        {
            Collect();
        }
    }
    
    public void Collect()
    {
        if (isCollected) return;
        
        isCollected = true;
        
        // Agregar al inventario del jugador
        PlayerController player = FindObjectOfType<PlayerController>();
        if (player != null)
        {
            // Esto interactuaría con un sistema de inventario
            Debug.Log($"Collected {itemName} worth {value} points");
        }
        
        // Generar efecto de recolección
        if (collectEffect != null)
        {
            Instantiate(collectEffect, transform.position, Quaternion.identity);
        }
        
        // Reproducir sonido de recolección
        if (collectSound != null)
        {
            AudioSource.PlayClipAtPoint(collectSound, transform.position);
        }
        
        // Destruir item
        Destroy(gameObject);
    }
    
    public string GetItemName()
    {
        return itemName;
    }
    
    public int GetValue()
    {
        return value;
    }
}

// AudioManager Singleton
public class AudioManager : MonoBehaviour
{
    public static AudioManager Instance { get; private set; }
    
    [Header("Audio Settings")]
    [SerializeField] private AudioSource musicSource;
    [SerializeField] private AudioSource sfxSource;
    [SerializeField] private AudioClip[] musicTracks;
    [SerializeField] private AudioClip[] soundEffects;
    
    private Dictionary<string, AudioClip> soundDictionary;
    
    private void Awake()
    {
        if (Instance == null)
        {
            Instance = this;
            DontDestroyOnLoad(gameObject);
        }
        else
        {
            Destroy(gameObject);
        }
    }
    
    private void Start()
    {
        // Inicializar diccionario de sonidos
        soundDictionary = new Dictionary<string, AudioClip>();
        
        foreach (AudioClip clip in soundEffects)
        {
            soundDictionary[clip.name] = clip;
        }
        
        // Reproducir música de fondo
        PlayMusic("BackgroundMusic");
    }
    
    public void PlayMusic(string musicName)
    {
        AudioClip musicClip = Array.Find(musicTracks, clip => clip.name == musicName);
        
        if (musicClip != null && musicSource != null)
        {
            musicSource.clip = musicClip;
            musicSource.loop = true;
            musicSource.Play();
        }
    }
    
    public void PlaySound(string soundName)
    {
        if (soundDictionary.ContainsKey(soundName) && sfxSource != null)
        {
            sfxSource.PlayOneShot(soundDictionary[soundName]);
        }
    }
    
    public void StopMusic()
    {
        if (musicSource != null)
        {
            musicSource.Stop();
        }
    }
    
    public void SetMusicVolume(float volume)
    {
        if (musicSource != null)
        {
            musicSource.volume = volume;
        }
    }
    
    public void SetSFXVolume(float volume)
    {
        if (sfxSource != null)
        {
            sfxSource.volume = volume;
        }
    }
}

2. Juego de Unreal Engine con C++ - Shooter 3D

// PlayerCharacter.h
#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "GameFramework/SpringArmComponent.h"
#include "GameFramework/CameraComponent.h"
#include "Components/HealthComponent.h"
#include "Components/StaminaComponent.h"
#include "Components/WeaponComponent.h"

#include "PlayerCharacter.generated.h"

UCLASS()
class APlayerCharacter : public ACharacter
{
    GENERATED_BODY()

public:
    APlayerCharacter();

    virtual void SetupPlayerInput(UInputComponent* PlayerInputComponent) override;
    virtual void BeginPlay() override;

    // Movimiento
    UFUNCTION(BlueprintCallable, Category = "Player")
    void MoveForward(float Value);
    
    UFUNCTION(BlueprintCallable, Category = "Player")
    void MoveRight(float Value);
    
    UFUNCTION(BlueprintCallable, Category = "Player")
    void Turn(float Value);
    
    UFUNCTION(BlueprintCallable, Category = "Player")
    void StartJump();
    
    UFUNCTION(BlueprintCallable, Category = "Player")
    void StopJumping();
    
    // Combate
    UFUNCTION(BlueprintCallable, Category = "Player")
    void StartFire();
    
    UFUNCTION(BlueprintCallable, Category = "Player")
    void StopFire();
    
    UFUNCTION(BlueprintCallable, Category = "Player")
    void Reload();
    
    UFUNCTION(BlueprintCallable, Category = "Player")
    void Aim();
    
    UFUNCTION(BlueprintCallable, Category = "Player")
    void StopAiming();
    
    // Cámara
    UFUNCTION(BlueprintCallable, Category = "Player")
    void ToggleCameraMode();
    
    // Salud y resistencia
    UFUNCTION(BlueprintCallable, Category = "Player")
    float GetHealth() const;
    
    UFUNCTION(BlueprintCallable, Category = "Player")
    float GetMaxHealth() const;
    
    UFUNCTION(BlueprintCallable, Category = "Player")
    float GetStamina() const;
    
    UFUNCTION(BlueprintCallable, Category = "Player")
    float GetMaxStamina() const;
    
    // Arma
    UFUNCTION(BlueprintCallable, Category = "Player")
    AWeaponComponent* GetCurrentWeapon() const;
    
    UFUNCTION(BlueprintCallable, Category = "Player")
    void EquipWeapon(class AWeapon* Weapon);
    
    // Movimiento
    UFUNCTION(BlueprintCallable, Category = "Player")
    bool IsMoving() const;
    
    UFUNCTION(BlueprintCallable, Category = "Player")
    bool IsJumping() const;
    
    UFUNCTION(BlueprintCallable, Category = "Player")
    bool IsAiming() const;
    
    UFUNCTION(BlueprintCallable, Category = "Player")
    bool IsSprinting() const;

protected:
    virtual void Tick(float DeltaTime) override;
    virtual void SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) override;
    
private:
    // Movimiento
    void MoveForward(float Value);
    void MoveRight(float Value);
    void Turn(float Value);
    void StartJump();
    void StopJumping();
    
    // Combate
    void StartFire();
    void StopFire();
    void Reload();
    void Aim();
    void StopAiming();
    
    // Cámara
    void ToggleCameraMode();
    
    // Estados de movimiento
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Player")
    bool bIsMoving;
    
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Player")
    bool bIsJumping;
    
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Player")
    bool bIsAiming;
    
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Player")
    bool bIsSprinting;
    
    // Componentes
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
    USpringArmComponent* SpringArmComponent;
    
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
    UCameraComponent* CameraComponent;
    
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
    UHealthComponent* HealthComponent;
    
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
    UStaminaComponent* StaminaComponent;
    
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
    UWeaponComponent* WeaponComponent;
    
    // Variables de movimiento
    UPROPERTY(EditDefaults, Category = "Player|Movement")
    float BaseTurnRate;
    
    UPROPERTY(EditDefaults, Category = "Player|Movement")
    float LookUpRate;
    
    UPROPERTY(EditDefaults, Category = "Player|Movement")
    float JumpHeight;
    
    UPROPERTY(EditDefaults, Category = "Player|Movement")
    float AirControl;
    
    // Variables de combate
    UPROPERTY(EditDefaults, Category = "Player|Combat")
    float BaseTurnRate;
    
    UPROPERTY(EditDefaults, Category = "Player|Combat")
    float AimSensitivity;
    
    // Variables de cámara
    UPROPERTY(EditDefaults, Category = "Player|Camera")
    bool bFirstPerson;
    
    UPROPERTY(EditDefaults, Category = "Player|Camera")
    float BaseFOV;
    
    UPROPERTY(EditDefaults, Category = "Player|Camera")
    float AimFOV;
    
    // Movimiento
    UPROPERTY(EditDefaults, Category = "Player|Movement")
    float MaxWalkSpeed;
    
    UPROPERTY(EditDefaults, Category = "Player|Movement")
    float MaxRunSpeed;
    
    UPROPERTY(EditDefaults, Category = "Player|Movement")
    float MaxSprintSpeed;
    
    UPROPERTY(EditDefaults, Category = "Player|Movement")
    float SprintStaminaCost;
    
    // Combate
    UPROPERTY(EditDefaults, Category = "Player|Combat")
    float BaseTurnRate;
    
    UPROPERTY(EditDefaults, Category = "Player|Combat")
    float AimSensitivity;
    
    // Cámara
    UPROPERTY(EditDefaults, Category = "Player|Camera")
    bool bFirstPerson;
    
    UPROPERTY(EditDefaults, Category = "Player|Camera")
    float BaseFOV;
    
    UPROPERTY(EditDefaults, Category = "Player|Camera")
    float AimFOV;
};

// PlayerCharacter.cpp
#include "PlayerCharacter.h"
#include "Engine/World.h"
#include "EnhancedInputComponent.h"
#include "Components/EnhancedInputComponent.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "Camera/CameraComponent.h"
#include "Kismet/GameplayStatics.h"

APlayerCharacter::APlayerCharacter()
{
    // Set this character to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
    PrimaryActorTick.bCanEverTick = true;
    
    // Crear componentes
    SpringArmComponent = CreateDefaultSubobject<USpringArmComponent>(TEXT("SpringArmComponent"));
    CameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("CameraComponent"));
    HealthComponent = CreateDefaultSubobject<UHealthComponent>(TEXT("HealthComponent"));
    StaminaComponent = CreateDefaultSubobject<UStaminaComponent>(TEXT("StaminaComponent"));
    WeaponComponent = CreateDefaultSubobject<UWeaponComponent>(TEXT("WeaponComponent"));
    
    // Establecer valores por defecto
    BaseTurnRate = 45.0f;
    LookUpRate = 45.0f;
    JumpHeight = 300.0f;
    AirControl = 0.05f;
    
    AimSensitivity = 1.0f;
    
    bFirstPerson = true;
    BaseFOV = 90.0f;
    AimFOV = 60.0f;
    
    MaxWalkSpeed = 600.0f;
    MaxRunSpeed = 900.0f;
    MaxSprintSpeed = 1200.0f;
    SprintStaminaCost = 10.0f;
    
    bIsMoving = false;
    bIsJumping = false;
    bIsAiming = false;
    bIsSprinting = false;
}

void APlayerCharacter::BeginPlay()
{
    Super::BeginPlay();
    
    // Configurar entrada
    if (APlayerController* PC = Cast<APlayerController>(Controller))
    {
        PC->SetupPlayerInput(this);
    }
    
    // Inicializar componentes
    if (HealthComponent)
    {
        HealthComponent->OnDeath.AddDynamic(this, &APlayerCharacter::OnDeath);
    }
    
    // Equipar arma por defecto
    if (WeaponComponent)
    {
        // This would be set up based on game rules
        // WeaponComponent->SpawnWeapon(DefaultWeaponClass);
    }
}

void APlayerCharacter::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);
    
    // Actualizar estados de movimiento
    UpdateMovementStates();
    
    // Actualizar cámara
    UpdateCamera(DeltaTime);
    
    // Manejar regeneración de resistencia
    if (StaminaComponent && !bIsSprinting)
    {
        StaminaComponent->RegenerateStamina(DeltaTime);
    }
}

void APlayerCharacter::SetupPlayerInput(UInputComponent* PlayerInputComponent)
{
    // Vincular acciones de movimiento
    PlayerInputComponent->BindAction(EEnhancedInputAction::IA_MoveForward, this, &APlayerCharacter::MoveForward);
    PlayerInputComponent->BindAction(EEnhancedInputAction::IA_MoveRight, this, &APlayerCharacter::MoveRight);
    PlayerInputComponent->BindAction(EEnhancedInputAction::IA_Turn, this, &APlayerCharacter::Turn);
    PlayerInputComponent->BindAction(EEnhancedInputAction::IA_Jump, this, &APlayerCharacter::StartJump);
    PlayerInputComponent->BindAction(EEnhancedInputAction::IA_StopJump, this, &APlayerCharacter::StopJumping);
    
    // Vincular acciones de combate
    PlayerInputComponent->BindAction(EEnhancedInputAction::IA_Fire, this, &APlayerCharacter::StartFire);
    PlayerInputComponent->BindAction(EEnhancedInputAction::IA_StopFire, this, &APlayerCharacter::StopFire);
    PlayerInputComponent->BindAction(EEnhancedInputAction::IA_Reload, this, &APlayerCharacter::Reload);
    PlayerInputComponent->BindAction(EEnhancedInputAction::IA_Aim, this, &APlayerCharacter::Aim);
    PlayerInputComponent->BindAction(EEnhancedInputAction::IA_StopAim, this, &APlayerCharacter::StopAiming);
    
    // Vincular acciones de utilidad
    PlayerInputComponent->BindAction(EEnhancedInputAction::IA_ToggleCamera, this, &APlayerCharacter::ToggleCameraMode);
    
    // Vincular acción de sprint
    PlayerInputComponent->BindAction(EEnhancedInputAction::IA_Sprint, this, &APlayerCharacter::StartSprint);
    PlayerInputComponent->BindAction(EEnhancedInputAction::IA_StopSprint, this, &APlayerCharacter::StopSprinting);
}

void APlayerCharacter::MoveForward(float Value)
{
    if (Controller != nullptr)
    {
        const FRotator = GetControlRotation();
        const FRotation = FRotator.Pitch;
        
        // Calcular dirección del movimiento
        const FVector Direction = FRotation.Vector();
        const FVector MovementVector = Direction * Value;
        
        // Aplicar movimiento
        AddMovementInput(MovementVector);
        
        // Establecer estado de movimiento
        bIsMoving = FMath::Abs(Value) > 0.1f;
    }
}

void APlayerCharacter::MoveRight(float Value)
{
    if (Controller != nullptr)
    {
        const FRotator = GetControlRotation();
        const FRotation = FRotator.Yaw;
        
        // Calcular dirección del movimiento
        const FVector Direction = FRotation.RightVector();
        const FVector MovementVector = Direction * Value;
        
        // Aplicar movimiento
        AddMovementInput(MovementVector);
        
        // Establecer estado de movimiento
        bIsMoving = FMath::Abs(Value) > 0.1f;
    }
}

void APlayerCharacter::Turn(float Value)
{
    if (Controller != nullptr)
    {
        // Aplicar giro
        AddControllerYawInput(Value * BaseTurnRate * GetWorld()->GetDeltaSeconds());
    }
}

void APlayerCharacter::StartJump()
{
    if (bIsJumping) return;
    
    // Verificar si se puede saltar
    if (CanJump())
    {
        // Saltar
        bIsJumping = true;
        
        // Reproducir montaje de salto
        if (UAnimInstance* JumpMontage = GetMesh()->GetAnimInstance())
        {
            JumpMontage->Montage_Play("Jump");
        }
        
        // Aplicar fuerza de salto
        LaunchCharacter(FVector(0.0f, 0.0f, JumpHeight));
        
        // Reproducir sonido de salto
        if (USoundBase* JumpSound = GEngine->SoundBase)
        {
            JumpSound->PlaySound2D(GetActorLocation());
        }
    }
}

void APlayerCharacter::StopJumping()
{
    // Detener salto
    bIsJumping = false;
    
    // Detener montaje de salto
    if (UAnimInstance* JumpMontage = GetMesh()->GetAnimInstance())
    {
        JumpMontage->Montage_Stop("Jump");
    }
}

void APlayerCharacter::StartFire()
{
    if (WeaponComponent)
    {
        WeaponComponent->StartFire();
        
        // Establecer estado de apuntado
        bIsAiming = true;
        
        // Actualizar cámara para apuntar
        if (CameraComponent && bFirstPerson)
        {
            CameraComponent->SetFieldOfView(AimFOV);
        }
    }
}

void APlayerCharacter::StopFire()
{
    if (WeaponComponent)
    {
        WeaponComponent->StopFire();
    }
    
    // Limpiar estado de apuntado
    bIsAiming = false;
    
    // Restablecer cámara
    if (CameraComponent && bFirstPerson)
    {
        CameraComponent->SetFieldOfView(BaseFOV);
    }
}

void APlayerCharacter::Reload()
{
    if (WeaponComponent)
    {
        WeaponComponent->Reload();
    }
}

void APlayerCharacter::Aim()
{
    // Comenzar a apuntar
    bIsAiming = true;
    
    // Actualizar cámara para apuntar
    if (CameraComponent && bFirstPerson)
    {
        CameraComponent->SetFieldOfView(AimFOV);
        
        // Reducir sensibilidad del ratón para apuntado preciso
        if (APlayerController* PC = Cast<APlayerController>(Controller))
        {
            PC->SetAimSensitivity(AimSensitivity * 0.5f);
        }
    }
    
    // Reproducir montaje de apuntado
    if (UAnimInstance* AimMontage = GetMesh()->GetAnimInstance())
    {
        AimMontage->Montage_Play("Aim");
    }
}

void APlayerCharacter::StopAiming()
{
    // Detener apuntado
    bIsAiming = false;
    
    // Restablecer cámara
    if (CameraComponent && bFirstPerson)
    {
        CameraComponent->SetFieldOfView(BaseFOV);
    }
    
    // Restablecer sensibilidad del ratón
    if (APlayerController* PC = Cast<APlayerController>(Controller))
    {
        PC->SetAimSensitivity(AimSensitivity);
    }
    
    // Detener montaje de apuntado
    if (UAnimInstance* AimMontage = GetMesh()->GetAnimInstance())
    {
        AimMontage->Montage_Stop("Aim");
    }
}

void APlayerCharacter::ToggleCameraMode()
{
    // Alternar entre primera y tercera persona
    bFirstPerson = !bFirstPerson;
    
    // Actualizar cámara
    if (CameraComponent)
    {
        if (bFirstPerson)
        {
            CameraComponent->AttachToComponent(SpringArmComponent, USpringArmComponent::SocketName);
            CameraComponent->SetFieldOfView(bIsAiming ? AimFOV : BaseFOV);
        }
        else
        {
            CameraComponent->DetachFromController();
            CameraComponent->SetFieldOfView(BaseFOV);
        }
    }
}

void APlayerCharacter::StartSprint()
{
    if (bIsSprinting || !CanSprint()) return;
    
    // Comenzar a correr a toda velocidad
    bIsSprinting = true;
    
    // Actualizar velocidad de movimiento
    if (UCharacterMovementComponent* MovementComponent = GetCharacterMovement())
    {
        MovementComponent->MaxWalkSpeed = MaxSprintSpeed;
    }
    
    // Reproducir montaje de sprint
    if (UAnimInstance* SprintMontage = GetMesh()->GetAnimInstance())
    {
        SprintMontage->Montage_Play("Sprint");
    }
    
    // Reproducir sonido de sprint
    if (USoundBase* SprintSound = GEngine->SoundBase)
    {
        SprintSound->PlaySound2D(GetActorLocation());
    }
}

void APlayerCharacter::StopSprinting()
{
    if (!bIsSprinting) return;
    
    // Detener sprint
    bIsSprinting = false;
    
    // Restablecer velocidad de movimiento
    if (UCharacterMovementComponent* MovementComponent = GetCharacterMovement())
    {
        MovementComponent->MaxWalkSpeed = MaxRunSpeed;
    }
    
    // Detener montaje de sprint
    if (UAnimInstance* SprintMontage = GetMesh()->GetAnimInstance())
    {
        SprintMontage->Montage_Stop("Sprint");
    }
}

void APlayerCharacter::UpdateMovementStates()
{
    // Actualizar estado de salto
    if (GetCharacterMovement())
    {
        bIsJumping = !GetCharacterMovement()->IsMovingOnGround();
    }
    
    // Actualizar estado de movimiento
    bIsMoving = GetVelocity().Size2D() > 0.1f;
    
    // Manejar resistencia de sprint
    if (bIsSprinting && StaminaComponent)
    {
        StaminaComponent->ConsumeStamina(SprintStaminaCost * GetWorld()->GetDeltaSeconds());
        
        // Detener sprint si se agota la resistencia
        if (StaminaComponent->GetStamina() <= 0.0f)
        {
            StopSprinting();
        }
    }
}

void APlayerCharacter::UpdateCamera(float DeltaTime)
{
    if (CameraComponent)
    {
        // Manejar sacudida de cámara al disparar
        if (WeaponComponent && WeaponComponent->IsFiring())
        {
            float CameraShakeIntensity = WeaponComponent->GetCameraShakeIntensity();
            if (CameraShakeIntensity > 0.0f)
            {
                // Aplicar sacudida de cámara
                FVector CameraLocation = CameraComponent->GetComponentLocation();
                FRotator CameraRotation = CameraComponent->GetComponentRotation();
                
                // Agregar desplazamiento aleatorio
                float RandomX = FMath::RandRange(-CameraShakeIntensity, CameraShakeIntensity);
                float RandomY = FMath::RandRange(-CameraShakeIntensity, CameraShakeIntensity);
                float RandomZ = FMath::RandRange(-CameraShakeIntensity, CameraShakeIntensity);
                
                FVector CameraOffset = FVector(RandomX, RandomY, RandomZ);
                FRotator CameraOffsetRotation = FRotator(RandomX, RandomY, RandomZ);
                
                CameraComponent->SetWorldLocationAndRotation(CameraLocation + CameraOffset, CameraRotation + CameraOffsetRotation);
            }
        }
    }
}

float APlayerCharacter::GetHealth() const
{
    return HealthComponent ? HealthComponent->GetHealth() : 0.0f;
}

float APlayerCharacter::GetMaxHealth() const
{
    return HealthComponent ? HealthComponent->GetMaxHealth() : 0.0f;
}

float APlayerCharacter::GetStamina() const
{
    return StaminaComponent ? StaminaComponent->GetStamina() : 0.0f;
}

float APlayerCharacter::GetMaxStamina() const
{
    return StaminaComponent ? StaminaComponent->MaxStamina : 0.0f;
}

AWeaponComponent* APlayerCharacter::GetCurrentWeapon() const
{
    return WeaponComponent;
}

void APlayerCharacter::EquipWeapon(AWeapon* Weapon)
{
    if (WeaponComponent)
    {
        WeaponComponent->EquipWeapon(Weapon);
    }
}

bool APlayerCharacter::IsMoving() const
{
    return bIsMoving;
}

bool APlayerCharacter::IsJumping() const
{
    return bIsJumping;
}

bool APlayerCharacter::IsAiming() const
{
    return bIsAiming;
}

bool APlayerCharacter::IsSprinting() const
{
    return bIsSprinting;
}

bool APlayerCharacter::CanSprint() const
{
    return !bIsJumping && StaminaComponent && StaminaComponent->GetStamina() > SprintStaminaCost;
}

void APlayerCharacter::OnDeath()
{
    // Manejar muerte
    DisableInput();
    
    // Reproducir montaje de muerte
    if (UAnimInstance* DeathMontage = GetMesh()->GetAnimInstance())
    {
        DeathMontage->Montage_Play("Death");
    }
    
    // Desactivar colisión
    SetActorEnableCollision(false);
    
    // Ocultar arma
    if (WeaponComponent)
    {
        WeaponComponent->SetVisibility(false);
    }
    
    // Física de muñeca de trapo
    if (GetCapsuleComponent())
    {
        GetCapsuleComponent->SetSimulatePhysics(true);
    }
    
    // Manejo de game over
    if (APlayerController* PC = Cast<APlayerController>(Controller))
    {
        PC->OnPlayerDeath();
    }
}

// Componente de entrada mejorada
UCLASS()
class AEnhancedInputComponent : public UInputComponent
{
    GENERATED_BODY()

public:
    AEnhancedInputComponent();
    
    virtual void SetupInputBinding(UInputComponent* PlayerInputComponent) override;
    
protected:
    virtual void BeginPlay() override;
    
private:
    UEnhancedInputAction CurrentAction;
    float ActionValue;
    
    void HandleMovement(float Value);
    void HandleCombat(float Value);
    void HandleUtility(float Value);
};

// Implementación del componente de entrada mejorada
AEnhancedInputComponent::AEnhancedInputComponent()
{
    CurrentAction = EEnhancedInputAction::IA_None;
    ActionValue = 0.0f;
}

void AEnhancedInputComponent::BeginPlay()
{
    Super::BeginPlay();
    
    // Configurar vinculación de entrada
    SetupInputBinding(this);
}

void AEnhancedInputComponent::SetupInputBinding(UInputComponent* PlayerInputComponent)
{
    // Vinculaciones de movimiento
    PlayerInputComponent->BindAxis("MoveForward", this, &AEnhancedInputComponent::HandleMovement);
    PlayerInputComponent->BindAxis("MoveRight", this, &AEnhancedInputComponent::HandleMovement);
    PlayerInputComponent->BindAxis("Turn", this, &AEnhancedInputComponent::HandleMovement);
    PlayerInputComponent->BindAxis("LookUp", this, &AEnhancedInputComponent::HandleMovement);
    
    // Vinculaciones de combate
    PlayerInputComponent->BindAction("Fire", this, &AEnhancedInputComponent::HandleCombat);
    PlayerInputComponent->BindAction("Reload", this, &AEnhancedInputComponent::HandleCombat);
    PlayerInputComponent->BindAction("Aim", this, &AEnhancedInputComponent::HandleCombat);
    
    // Vinculaciones de utilidad
    PlayerInputComponent->BindAction("Jump", this, &AEnhancedInputComponent::HandleUtility);
    PlayerInputComponent->BindAction("Sprint", this, &AEnhancedInputComponent::HandleUtility);
    PlayerInputComponent->BindAction("ToggleCamera", this, &AEnhancedInputComponent::HandleUtility);
}

void AEnhancedInputComponent::HandleMovement(float Value)
{
    CurrentAction = EEnhancedInputAction::IA_MoveForward;
    ActionValue = Value;
    
    if (APlayerCharacter* Player = Cast<APlayerCharacter>(GetOwner()))
    {
        switch (CurrentAction)
        {
            case EEnhancedInputAction::IA_MoveForward:
                Player->MoveForward(Value);
                break;
            case EEnhancedInputAction::IA_MoveRight:
                Player->MoveRight(Value);
                break;
            case EEnhancedInputAction::IA_Turn:
                Player->Turn(Value);
                break;
            case EEnhancedInputAction::IA_LookUp:
                // Handle look up/down
                break;
        }
    }
}

void AEnhancedInputComponent::HandleCombat(float Value)
{
    CurrentAction = EEnhancedInputAction::IA_Fire;
    ActionValue = Value;
    
    if (APlayerCharacter* Player = Cast<APlayerCharacter>(GetOwner()))
    {
        switch (CurrentAction)
        {
            case EEnhancedInputAction::IA_Fire:
                if (Value > 0.5f)
                {
                    Player->StartFire();
                }
                else
                {
                    Player->StopFire();
                }
                break;
            case EEnhancedInputAction::IA_Reload:
                if (Value > 0.5f)
                {
                    Player->Reload();
                }
                break;
            case EEnhancedInputAction::IA_Aim:
                if (Value > 0.5f)
                {
                    Player->Aim();
                }
                else
                {
                    Player->StopAiming();
                }
                break;
        }
    }
}

void AEnhancedInputComponent::HandleUtility(float Value)
{
    CurrentAction = EEnhancedInputAction::IA_Jump;
    ActionValue = Value;
    
    if (APlayerCharacter* Player = Cast<APlayerCharacter>(GetOwner()))
    {
        switch (CurrentAction)
        {
            case EEnhancedInputAction::IA_Jump:
                if (Value > 0.5f)
                {
                    Player->StartJump();
                }
                else
                {
                    Player->StopJumping();
                }
                break;
            case EEnhancedInputAction::IA_Sprint:
                if (Value > 0.5f)
                {
                    Player->StartSprint();
                }
                else
                {
                    Player->StopSprinting();
                }
                break;
            case EEnhancedInputAction::IA_ToggleCamera:
                if (Value > 0.5f)
                {
                    Player->ToggleCameraMode();
                }
                break;
        }
    }
}

3. Motor de Física Personalizado con C++

// PhysicsEngine.h
#pragma once

#include "CoreMinimal.h"
#include "Math/Vector2D.h"
#include "Math/Vector3D.h"
#include "Containers/Array.h"
#include "Containers/Map.h"

class PhysicsBody;
class Collider;
class Rigidbody;

// Physics Engine class
class PHYSICS_API PhysicsEngine
{
public:
    PhysicsEngine();
    ~PhysicsEngine();
    
    // World management
    void SetGravity(const FVector2D& Gravity);
    void SetTimeStep(float TimeStep);
    
    // Body management
    PhysicsBody* CreateBody(const FVector2D& Position, float Mass);
    void DestroyBody(PhysicsBody* Body);
    
    // Collider management
    void AddCollider(PhysicsBody* Body, TSharedPtr<Collider> Collider);
    void RemoveCollider(PhysicsBody* Body, Collider* Collider);
    
    // Simulation
    void Step(float DeltaTime);
    
    // Query methods
    TArray<PhysicsBody*> GetBodiesInArea(const FVector2D& Min, const FVector2D& Max);
    bool IsOverlapping(const Collider* ColliderA, const Collider* ColliderB) const;
    
    // Debug rendering
    void DebugRender();
    
private:
    void UpdateBodies(float DeltaTime);
    void UpdateCollisions();
    void ResolveCollisions();
    
    void BroadPhaseCollisionDetection();
    void NarrowPhaseCollisionDetection();
    void CollisionResolution();
    
    void IntegrateForces(float DeltaTime);
    void ApplyGravity(float DeltaTime);
    
    TArray<PhysicsBody*> Bodies;
    TArray<TSharedPtr<Collider>> Colliders;
    
    TArray<CollisionPair> CollisionPairs;
    
    FVector2D Gravity;
    float TimeStep;
    
    bool bIsDebugRendering;
};

// Collision Pair structure
struct CollisionPair
{
    PhysicsBody* BodyA;
    PhysicsBody* BodyB;
    FVector2D ContactNormal;
    float PenetrationDepth;
    
    CollisionPair(PhysicsBody* InBodyA, PhysicsBody* InBodyB, const FVector2D& InNormal, float InPenetration)
        : BodyA(InBodyA), BodyB(InBodyB), ContactNormal(InNormal), PenetrationDepth(InPenetration) {}
};

// Physics Body class
class PHYSICS_API PhysicsBody
{
public:
    PhysicsBody(const FVector2D& Position, float Mass);
    ~PhysicsBody();
    
    // Position and movement
    void SetPosition(const FVector2D& Position);
    FVector2D GetPosition() const { return Position; }
    
    void SetVelocity(const FVector2D& Velocity);
    FVector2D GetVelocity() const { return Velocity; }
    
    void AddForce(const FVector2D& Force);
    void AddImpulse(const FVector2D& Impulse);
    
    // Properties
    void SetMass(float Mass);
    float GetMass() const { return Mass; }
    
    void SetStatic(bool bStatic);
    bool IsStatic() const { return bStatic; }
    
    void SetGravityScale(float Scale);
    float GetGravityScale() const { return GravityScale; }
    
    // Collision
    void SetCollisionEnabled(bool bEnabled);
    bool IsCollisionEnabled() const { return bCollisionEnabled; }
    
    // Components
    void AddCollider(TSharedPtr<Collider> Collider);
    void RemoveCollider(Collider* Collider);
    TArray<TSharedPtr<Collider>> GetColliders() const { return Colliders; }
    
    // Material properties
    void SetRestitution(float Restitution);
    float GetRestitution() const { return Restitution; }
    
    void SetFriction(float Friction);
    float GetFriction() const { return Friction; }
    
private:
    FVector2D Position;
    FVector2D Velocity;
    FVector2D Force;
    
    float Mass;
    float InverseMass;
    
    bool bStatic;
    float GravityScale;
    
    bool bCollisionEnabled;
    
    TArray<TSharedPtr<Collider>> Colliders;
    
    float Restitution;
    float Friction;
    
    friend class PhysicsEngine;
};

// Collider base class
class PHYSICS_API Collider
{
public:
    Collider();
    virtual ~Collider();
    
    // Type identification
    enum class EType
    {
        Circle,
        Rectangle,
        Polygon,
        Edge,
        Point
    };
    
    virtual EType GetType() const = 0;
    
    // Collision detection
    virtual bool Overlaps(const Collider* Other) const = 0;
    virtual bool ContainsPoint(const FVector2D& Point) const = 0;
    virtual bool IntersectsLine(const FVector2D& Start, const FVector2D& End) const = 0;
    
    // Collision response
    virtual void ComputeCollisionData(const Collider* Other, FVector2D& OutNormal, float& OutPenetration) const = 0;
    
    // Bounds
    virtual FVector2D GetCenter() const = 0;
    virtual FVector2D GetExtents() const = 0;
    
    // Material properties
    void SetRestitution(float Restitution);
    float GetRestitution() const { return Restitution; }
    
    void SetFriction(float Friction);
    float GetFriction() const { return Friction; }
    
protected:
    float Restitution;
    float Friction;
    
    friend class PhysicsEngine;
};

// Circle collider
class PHYSICS_API CircleCollider : public Collider
{
public:
    CircleCollider(float Radius);
    
    virtual EType GetType() const override { return Circle; }
    
    virtual bool Overlaps(const Collider* Other) const override;
    virtual bool ContainsPoint(const FVector2D& Point) const override;
    virtual bool IntersectsLine(const FVector2D& Start, const FVector2D& End) const override;
    virtual void ComputeCollisionData(const Collider* Other, FVector2D& OutNormal, float& OutPenetration) const override;
    
    virtual FVector2D GetCenter() const override;
    virtual FVector2D GetExtents() const override;
    
    void SetRadius(float NewRadius);
    float GetRadius() const { return Radius; }
    
private:
    float Radius;
};

// Rectangle collider
class PHYSICS_API RectangleCollider : public Collider
{
public:
    RectangleCollider(const FVector2D& Size);
    
    virtual EType GetType() const override { return Rectangle; }
    
    virtual bool Overlaps(const Collider* Other) const override;
    virtual bool ContainsPoint(const FVector2D& Point) const override;
    virtual bool IntersectsLine(const FVector2D& Start, const FVector2D& End) const override;
    virtual void ComputeCollisionData(const Collider* Other, FVector2D& OutNormal, float& OutPenetration) const override;
    
    virtual FVector2D GetCenter() const override;
    virtual FVector2D GetExtents() const override;
    
    void SetSize(const FVector2D& NewSize);
    FVector2D GetSize() const { return Size; }
    
private:
    FVector2D Size;
};

// Physics Engine Implementation
PhysicsEngine::PhysicsEngine()
{
    Gravity = FVector2D(0.0f, 9.81f);
    TimeStep = 1.0f / 60.0f;
    bIsDebugRendering = false;
}

PhysicsEngine::~PhysicsEngine()
{
    // Clean up bodies
    for (PhysicsBody* Body : Bodies)
    {
        delete Body;
    }
    Bodies.Empty();
    
    // Clean up colliders
    Colliders.Empty();
}

void PhysicsEngine::SetGravity(const FVector2D& InGravity)
{
    Gravity = InGravity;
}

void PhysicsEngine::SetTimeStep(float InTimeStep)
{
    TimeStep = InTimeStep;
}

PhysicsBody* PhysicsEngine::CreateBody(const FVector2D& Position, float Mass)
{
    PhysicsBody* Body = new PhysicsBody(Position, Mass);
    Bodies.Add(Body);
    return Body;
}

void PhysicsEngine::DestroyBody(PhysicsBody* Body)
{
    if (Bodies.Contains(Body))
    {
        Bodies.Remove(Body);
        delete Body;
    }
}

void PhysicsEngine::AddCollider(PhysicsBody* Body, TSharedPtr<Collider> Collider)
{
    if (Body && Collider)
    {
        Body->AddCollider(Collider);
        Colliders.Add(Collider);
    }
}

void PhysicsEngine::RemoveCollider(PhysicsBody* Body, Collider* Collider)
{
    if (Body && Body->GetColliders().Contains(Collider))
    {
        Body->RemoveCollider(Collider);
    }
    
    // Remove from global list
    for (int32 i = 0; i < Colliders.Num(); ++i)
    {
        if (Colliders[i].Get() == Collider)
        {
            Colliders.RemoveAt(i);
            break;
        }
    }
}

void PhysicsEngine::Step(float DeltaTime)
{
    // Update physics simulation
    UpdateBodies(DeltaTime);
    
    // Update collisions
    UpdateCollisions();
    
    // Resolve collisions
    ResolveCollisions();
}

void PhysicsEngine::UpdateBodies(float DeltaTime)
{
    for (PhysicsBody* Body : Bodies)
    {
        if (!Body->IsStatic())
        {
            // Apply gravity
            ApplyGravity(DeltaTime);
            
            // Integrate forces
            IntegrateForces(DeltaTime);
            
            // Update position
            FVector2D NewPosition = Body->GetPosition() + Body->GetVelocity() * DeltaTime;
            Body->SetPosition(NewPosition);
        }
    }
}

void PhysicsEngine::UpdateCollisions()
{
    CollisionPairs.Empty();
    
    // Broad phase collision detection
    BroadPhaseCollisionDetection();
    
    // Narrow phase collision detection
    NarrowPhaseCollisionDetection();
}

void PhysicsEngine::BroadPhaseCollisionDetection()
{
    // Simple spatial hashing for broad phase
    const int32 GridSize = 100;
    TMap<FVector2D, TArray<PhysicsBody*>> SpatialGrid;
    
    // Add bodies to spatial grid
    for (PhysicsBody* Body : Bodies)
    {
        if (!Body->IsCollisionEnabled()) continue;
        
        FVector2D GridPos = FVector2D(
            FMath::Floor(Body->GetPosition().X / GridSize),
            FMath::Floor(Body->GetPosition().Y / GridSize)
        );
        
        SpatialGrid.FindOrAdd(GridPos).Add(Body);
    }
    
    // Check potential collisions
    for (auto& GridCell : SpatialGrid)
    {
        TArray<PhysicsBody*>& CellBodies = GridCell.Value;
        
        for (int32 i = 0; i < CellBodies.Num(); ++i)
        {
            for (int32 j = i + 1; j < CellBodies.Num(); ++j)
            {
                PhysicsBody* BodyA = CellBodies[i];
                PhysicsBody* BodyB = CellBodies[j];
                
                // Quick AABB check
                if (CheckAABBOverlap(BodyA, BodyB))
                {
                    CollisionPairs.Add(CollisionPair(BodyA, BodyB));
                }
            }
        }
    }
}

void PhysicsEngine::NarrowPhaseCollisionDetection()
{
    for (CollisionPair& Pair : CollisionPairs)
    {
        PhysicsBody* BodyA = Pair.BodyA;
        PhysicsBody* BodyB = Pair.BodyB;
        
        // Get colliders for each body
        TArray<TSharedPtr<Collider>> CollidersA = BodyA->GetColliders();
        TArray<TSharedPtr<Collider>> CollidersB = BodyB->GetColliders();
        
        // Check each collider pair
        for (const TSharedPtr<Collider>& ColliderA : CollidersA)
        {
            for (const TSharedPtr<Collider>& ColliderB : CollidersB)
            {
                if (ColliderA->Overlaps(ColliderB.Get()))
                {
                    // Compute collision data
                    FVector2D Normal;
                    float Penetration;
                    ColliderA->ComputeCollisionData(ColliderB.Get(), Normal, Penetration);
                    
                    // Add collision pair with computed data
                    Pair.ContactNormal = Normal;
                    Pair.PenetrationDepth = Penetration;
                    
                    break; // Only one collision per pair
                }
            }
        }
    }
}

void PhysicsEngine::ResolveCollisions()
{
    for (CollisionPair& Pair : CollisionPairs)
    {
        PhysicsBody* BodyA = Pair.BodyA;
        PhysicsBody* BodyB = Pair.BodyB;
        
        // Skip if either body is static
        if (BodyA->IsStatic() && BodyB->IsStatic())
        {
            continue;
        }
        
        // Calculate relative masses
        float MassA = BodyA->GetMass();
        float MassB = BodyB->GetMass();
        float TotalMass = MassA + MassB;
        
        float InverseMassA = MassA > 0.0f ? 1.0f / MassA : 0.0f;
        float InverseMassB = MassB > 0.0f ? 1.0f / MassB : 0.0f;
        
        // Calculate impulse
        float Restitution = FMath::Min(BodyA->GetRestitution(), BodyB->GetRestitution());
        float Impulse = (1 + Restitution) * FVector2D::Dot(Pair.ContactNormal, BodyB->GetVelocity() - BodyA->GetVelocity()) / TotalMass;
        
        // Apply impulse
        if (!BodyA->IsStatic())
        {
            FVector2D VelocityA = BodyA->GetVelocity();
            VelocityA += Impulse * InverseMassA;
            BodyA->SetVelocity(VelocityA);
        }
        
        if (!BodyB->IsStatic())
        {
            FVector2D VelocityB = BodyB->GetVelocity();
            VelocityB -= Impulse * InverseMassB;
            BodyB->SetVelocity(VelocityB);
        }
        
        // Position correction
        const float Percent = 0.8f; // Position correction percentage
        const float Slop = 0.2f; // Position correction slop
        
        FVector2D CorrectionMagnitude = Pair.PenetrationDepth * Percent;
        FVector2D Correction = Pair.ContactNormal * CorrectionMagnitude;
        
        if (!BodyA->IsStatic())
        {
            FVector2D PositionA = BodyA->GetPosition();
            PositionA -= Correction * (InverseMassA * Slop);
            BodyA->SetPosition(PositionA);
        }
        
        if (!BodyB->IsStatic())
        {
            FVector2D PositionB = BodyB->GetPosition();
            PositionB += Correction * (InverseMassB * Slop);
            BodyB->SetPosition(PositionB);
        }
    }
}

void PhysicsEngine::IntegrateForces(float DeltaTime)
{
    for (PhysicsBody* Body : Bodies)
    {
        if (!Body->IsStatic())
        {
            // Semi-implicit Euler integration
            FVector2D Acceleration = Body->GetForce() * Body->GetInverseMass();
            
            FVector2D Velocity = Body->GetVelocity() + Acceleration * DeltaTime;
            Body->SetVelocity(Velocity);
            
            Body->SetForce(FVector2D::ZeroVector); // Reset force accumulator
        }
    }
}

void PhysicsEngine::ApplyGravity(float DeltaTime)
{
    for (PhysicsBody* Body : Bodies)
    {
        if (!Body->IsStatic())
        {
            FVector2D GravityForce = Gravity * Body->GetGravityScale() * Body->GetMass();
            Body->AddForce(GravityForce);
        }
    }
}

bool PhysicsEngine::CheckAABBOverlap(const PhysicsBody* BodyA, const PhysicsBody* BodyB)
{
    if (!BodyA || !BodyB) return false;
    
    // Get AABB bounds for both bodies
    FVector2D MinA, MaxA;
    FVector2D MinB, MaxB;
    
    GetBodyBounds(BodyA, MinA, MaxA);
    GetBodyBounds(BodyB, MinB, MaxB);
    
    // Check AABB overlap
    return (MinA.X <= MaxB.X && MaxA.X >= MinB.X &&
            MinA.Y <= MaxB.Y && MaxA.Y >= MinB.Y);
}

void PhysicsEngine::GetBodyBounds(const PhysicsBody* Body, FVector2D& Min, FVector2D& Max)
{
    if (!Body) return;
    
    Min = FVector2D(FLT_MAX, FLT_MAX);
    Max = FVector2D(-FLT_MAX, -FLT_MAX);
    
    for (const TSharedPtr<Collider>& Collider : Body->GetColliders())
    {
        FVector2D Center = Collider->GetCenter();
        FVector2D Extents = Collider->GetExtents();
        
        FVector2D ColliderMin = Center - Extents;
        FVector2D ColliderMax = Center + Extents;
        
        Min.X = FMath::Min(Min.X, ColliderMin.X);
        Min.Y = FMath::Min(Min.Y, ColliderMin.Y);
        Max.X = FMath::Max(Max.X, ColliderMax.X);
        Max.Y = FMath::Max(Max.Y, ColliderMax.Y);
    }
}

TArray<PhysicsBody*> PhysicsEngine::GetBodiesInArea(const FVector2D& Min, const FVector2D& Max)
{
    TArray<PhysicsBody*> BodiesInArea;
    
    for (PhysicsBody* Body : Bodies)
    {
        FVector2D BodyMin, BodyMax;
        GetBodyBounds(Body, BodyMin, BodyMax);
        
        if (BodyMin.X <= Max.X && BodyMax.X >= Min.X &&
            BodyMin.Y <= Max.Y && BodyMax.Y >= Min.Y)
        {
            BodiesInArea.Add(Body);
        }
    }
    
    return BodiesInArea;
}

bool PhysicsEngine::IsOverlapping(const Collider* ColliderA, const Collider* ColliderB) const
{
    if (!ColliderA || !ColliderB) return false;
    
    return ColliderA->Overlaps(ColliderB);
}

void PhysicsEngine::DebugRender()
{
    if (!bIsDebugRendering) return;
    
    // Render bodies
    for (const PhysicsBody* Body : Bodies)
    {
        FVector2D Position = Body->GetPosition();
        FVector2D Min, Max;
        GetBodyBounds(Body, Min, Max);
        
        // Draw body outline
        DrawDebugBox(Min, Max, FColor::Green);
        
        // Draw velocity vector
        FVector2D VelocityEnd = Position + Body->GetVelocity();
        DrawDebugLine(Position, VelocityEnd, FColor::Blue);
    }
    
    // Render colliders
    for (const TSharedPtr<Collider>& Collider : Colliders)
    {
        FVector2D Center = Collider->GetCenter();
        FVector2D Extents = Collider->GetExtents();
        
        DrawDebugBox(Center - Extents, Center + Extents, FColor::Red);
    }
    
    // Render spatial grid
    if (bIsDebugRendering)
    {
        const int32 GridSize = 100;
        const FColor GridColor = FColor(0, 0, 1, 0.1f);
        
        for (int32 X = 0; X < 10; X++)
        {
            for (int32 Y = 0; Y < 10; Y++)
            {
                FVector2D GridMin(X * GridSize, Y * GridSize);
                FVector2D GridMax((X + 1) * GridSize, (Y + 1) * GridSize);
                DrawDebugBox(GridMin, GridMax, GridColor);
            }
        }
    }
}

// Circle Collider Implementation
CircleCollider::CircleCollider(float InRadius)
    : Radius(InRadius)
{
}

bool CircleCollider::Overlaps(const Collider* Other) const
{
    if (!Other) return false;
    
    switch (Other->GetType())
    {
        case Circle:
        {
            const CircleCollider* OtherCircle = static_cast<const CircleCollider*>(Other);
            float Distance = FVector2D::Dist(GetCenter(), OtherCircle->GetCenter());
            return Distance < (Radius + OtherCircle->GetRadius());
        }
        
        case Rectangle:
        {
            const RectangleCollider* OtherRect = static_cast<const RectangleCollider*>(Other);
            return OverlapsRectangle(OtherRect);
        }
        
        default:
            return false;
    }
}

bool CircleCollider::ContainsPoint(const FVector2D& Point) const
{
    return FVector2D::Dist(GetCenter(), Point) <= Radius;
}

bool CircleCollider::IntersectsLine(const FVector2D& Start, const FVector2D& End) const
{
    // Line-circle intersection test
    FVector2D D = End - Start;
    float A = D.Dot(D);
    float B = 2.0f * D.Dot(Start);
    float C = Start.SizeSquared() - Radius * Radius;
    
    float Discriminant = B * B - 4.0f * A * C;
    
    if (Discriminant < 0.0f)
    {
        return false; // No intersection
    }
    
    float T1 = (-B - FMath::Sqrt(Discriminant)) / (2.0f * A);
    float T2 = (-B + FMath::Sqrt(Discriminant)) / (2.0f * A);
    
    if (T1 >= 0.0f && T1 <= 1.0f)
    {
        return ContainsPoint(Start + D * T1);
    }
    
    if (T2 >= 0.0f && T2 <= 1.0f)
    {
        return ContainsPoint(Start + D * T2);
    }
    
    return false;
}

void CircleCollider::ComputeCollisionData(const Collider* Other, FVector2D& OutNormal, float& OutPenetration) const
{
    if (!Other || Other->GetType() != Circle)
    {
        OutNormal = FVector2D::ZeroVector;
        OutPenetration = 0.0f;
        return;
    }
    
    const CircleCollider* OtherCircle = static_cast<const CircleCollider*>(Other);
    
    // Calculate collision normal
    FVector2D Direction = OtherCircle->GetCenter() - GetCenter();
    float Distance = Direction.Size();
    
    if (Distance > 0.0f)
    {
        OutNormal = Direction / Distance;
    }
    else
    {
        OutNormal = FVector2D::ZeroVector;
    }
    
    // Calculate penetration depth
    OutPenetration = (Radius + OtherCircle->GetRadius()) - Distance;
}

FVector2D CircleCollider::GetCenter() const
{
    return Position;
}

FVector2D CircleCollider::GetExtents() const
{
    return FVector2D(Radius, Radius);
}

void CircleCollider::SetRadius(float NewRadius)
{
    Radius = NewRadius;
}

// Rectangle Collider Implementation
RectangleCollider::RectangleCollider(const FVector2D& InSize)
    : Size(InSize)
{
}

bool RectangleCollider::Overlaps(const Collider* Other) const
{
    if (!Other) return false;
    
    switch (Other->GetType())
    {
        case Rectangle:
        {
            const RectangleCollider* OtherRect = static_cast<const RectangleCollider*>(Other);
            return OverlapsRectangle(OtherRect);
        }
        
        case Circle:
            {
            const CircleCollider* OtherCircle = static_cast<const CircleCollider*>(Other);
            return OtherCircle->Overlaps(this);
        }
        
        default:
            return false;
    }
}

bool RectangleCollider::OverlapsRectangle(const RectangleCollider* Other) const
{
    if (!Other) return false;
    
    FVector2A = GetCenter() - GetExtents();
    FVector2B = GetCenter() + GetExtents();
    FVector2C = Other->GetCenter() - Other->GetExtents();
    FVector2D = Other->GetCenter() + Other->GetExtents();
    
    return (A.X <= C.X && B.X >= C.X &&
            A.Y <= C.Y && B.Y >= C.Y);
}

bool RectangleCollider::ContainsPoint(const FVector2D& Point) const
{
    FVector2A = GetCenter() - GetExtents();
    FVector2B = GetCenter() + GetExtents();
    
    return Point.X >= A.X && Point.X <= B.X &&
           Point.Y >= A.Y && Point.Y <= B.Y;
}

bool RectangleCollider::IntersectsLine(const FVector2D& Start, const FVector2D& End) const
{
    // Line-rectangle intersection test
    FVector2A = GetCenter() - GetExtents();
    FVector2B = GetCenter() + GetExtents();
    
    // Check if line intersects rectangle
    return LineIntersectsRect(Start, End, A, B);
}

void RectangleCollider::ComputeCollisionData(const Collider* Other, FVector2D& OutNormal, float& OutPenetration) const
{
    if (!Other || Other->GetType() != Rectangle)
    {
        OutNormal = FVector2D::ZeroVector;
        OutPenetration = 0.0f;
        return;
    }
    
    const RectangleCollider* OtherRect = static_cast<const RectangleCollider*>(Other);
    
    // Calculate overlap
    FVector2A = GetCenter() - GetExtents();
    FVector2B = GetCenter() + GetExtents();
    FVector2C = OtherRect->GetCenter() - OtherRect->GetExtents();
    FVector2D = OtherRect->GetCenter() + OtherRect->GetExtents();
    
    // Calculate overlap
    float OverlapX = FMath::Min(B.X, C.X + OtherRect->GetExtents().X) - FMath::Max(A.X, C.X - OtherRect->GetExtents().X);
    float OverlapY = FMath::Min(B.Y, C.Y + OtherRect->GetExtents().Y) - FMath::Max(A.Y, C.Y - OtherRect->GetExtents().Y);
    
    // Calculate collision normal and penetration
    if (OverlapX < OverlapY)
    {
        OutNormal = FVector2D(1.0f, 0.0f);
        OutPenetration = OverlapX;
    }
    else
    {
        OutNormal = FVector2D(0.0f, 1.0f);
        OutPenetration = OverlapY;
    }
    
    // Flip normal if needed
    if (FVector2D::Dot((GetCenter() - OtherRect->GetCenter()), OutNormal) < 0.0f)
    {
        OutNormal = -OutNormal;
    }
}

FVector2D RectangleCollider::GetCenter() const
{
    return Position;
}

FVector2D RectangleCollider::GetExtents() const
{
    return Size * 0.5f;
}

void RectangleCollider::SetSize(const FVector2D& NewSize)
{
    Size = NewSize;
}

// Line-Rectangle intersection helper
bool LineIntersectsRect(const FVector2D& Start, const FVector2D& End, const FVector2D& RectMin, const FVector2D& RectMax)
{
    // Check if line segment intersects rectangle
    // This is a simplified implementation
    return Start.X <= RectMax.X && End.X >= RectMin.X &&
           Start.Y <= RectMax.Y && End.Y >= RectMin.Y &&
## Comparación de Motores de Juego

| Motor | Lenguajes | Plataformas | Fortalezas | Licencia | Tipo |
|--------|---------|------------|---------|-------|------|
| **Unity** | C# | Multiplataforma | Media | Gratuita | Multipropósito |
| **Unreal** | C++ | Multiplataforma | Alta | Royalty | AAA |
| **Godot** | GDScript/C# | Multiplataforma | Media | Gratuita | Indie |
| **CryEngine** | C++ | Multiplataforma | Alta | Royalty | AAA |
| **Amazon Lumberyard** | Lua | Multiplataforma | Media | Gratuita | Cloud |

## Pipeline de Gráficos 3D

### Etapas del Pipeline de Rendering
```mermaid
graph TD
    A[Application] --> B[Vertex Processing]
    B --> C[Primitive Assembly]
    C --> D[Vertex Shader]
    D --> E[Fragment Shader]
    E --> F[Per-Fragment Operations]
    F --> G[Blending]
    G --> H[Frame Buffer]
    H --> I[Display]

Tipos de Shaders

TipoPropósitoLenguajeComplejidad
Vertex ShaderTransformación de vérticesGLSL/HLSLMedia
Fragment ShaderColor de píxelesGLSL/HLSLAlta
Geometry ShaderGeneración de primitivasGLSL/HLSLMuy Alta
Compute ShaderPropósito generalGLSL/HLSLAlta

Conceptos de Motor de Física

Algoritmos de Detección de Colisiones

AlgoritmoTipoComplejidadPrecisiónAplicación
AABBBroad PhaseO(1)BajaFiltrado rápido
OBBNarrow PhaseO(n)MediaPreciso
SATNarrow PhaseO(log n)AltaPreciso
GJKNarrow PhaseO(log n)Muy AltaComplejo

Métodos de Integración

MétodoEstabilidadEficiencia EnergéticaAplicación
EulerCondicionalBajaPropósito general
VerletCondicionalBajaPreciso
RK4CondicionalBajaPreciso

Sistemas de Animación

Animación Esquelética

// Bone structure
struct Bone
{
    FString Name;
    int32 ParentIndex;
    FVector3D Position;
    FQuat4 Rotation;
    TArray<Bone> Children;
    
    // Transform matrix
    FMatrix44 Transform;
    
    // Animation data
    TArray<FTransform> Keyframes;
    int32 CurrentKeyframe;
    float AnimationSpeed;
    bool bLooping;
};

// Animation system
class AnimationSystem
{
public:
    void UpdateAnimation(float DeltaTime);
    void PlayAnimation(const FString& AnimationName);
    void StopAnimation();
    
    void SetAnimationSpeed(float Speed);
    void SetLooping(bool bLoop);
    
private:
    TArray<SkeletalAnimation*> Animations;
    SkeletalAnimation* CurrentAnimation;
    float CurrentTime;
};

Mezcla de Animaciones

// Animation blending states
enum class EAnimationBlendState
{
    Idle,
    Moving,
    Jumping,
    Attacking,
    Dead
};

// Blend tree structure
struct FAnimationBlendState
{
    EAnimationState State;
    float BlendTime;
    float BlendDuration;
    UAnimationAsset* Animation;
};

Implementación del Game Loop

Game Loop con Tiempo Fijo

void Game::Tick(float DeltaTime)
{
    // Accumulate time
    AccumulatedTime += DeltaTime;
    
    // Fixed timestep update
    while (AccumulatedTime >= FixedTimeStep)
    {
        // Update game logic
        UpdateGameLogic(FixedTimeStep);
        
        // Handle input
        HandleInput(FixedTimeStep);
        
        // Update physics
        UpdatePhysics(FixedTimeStep);
        
        // Accumulate time
        AccumulatedTime -= FixedTimeStep;
    }
    
    // Render
    Render();
}

Game Loop con Tiempo Variable

void Game::Tick(float DeltaTime)
{
    // Update game logic
    UpdateGameLogic(DeltaTime);
    
    // Handle input
    HandleInput(DeltaTime);
    
    // Update physics
    UpdatePhysics(DeltaTime);
    
    // Render
    Render();
}

Optimización para Juegos Móviles

Técnicas de Rendimiento

  • Level of Detail (LOD): Ajuste dinámico de calidad
  • Occlusion Culling: Ocultar objetos invisibles
  • Frustum Culling: Limitar el área visible
  • Object Pooling: Reutilizar objetos
  • Texture Compression: Compresión de gráficos

Gestión de Memoria

// Object pooling for game objects
template<typename T>
class ObjectPool
{
public:
    ObjectPool(int32 PoolSize = 100)
    {
        for (int32 i = 0; i < PoolSize; ++i)
        {
            AvailableObjects.Add(new T());
        }
    }
    
    T* GetObject()
    {
        if (AvailableObjects.Num() > 0)
        {
            T* Object = AvailableObjects.Pop();
            ActiveObjects.Add(Object);
            return Object;
        }
        return new T();
    }
    
    void ReturnObject(T* Object)
    {
        if (ActiveObjects.Contains(Object))
        {
            ActiveObjects.Remove(Object);
            AvailableObjects.Add(Object);
        }
    }
    
private:
    TArray<T*> AvailableObjects;
    TArray<T*> ActiveObjects;
};

Ventajas e Inconvenientes

Ventajas de los Motores de Juego

  • Desarrollo Rápido: Editores visuales, drag-and-drop
  • Multiplataforma: Desarrolla una sola vez para todas las plataformas
  • Sistemas Integrados: Física, audio, animación, networking
  • Pipeline de Recursos: Gestión e importación de assets
  • Apoyo Comunitario: Grandes comunidades de desarrolladores

Inconvenientes

  • Rendimiento: No siempre óptimo para juegos AAA
  • Flexibilidad: Limitaciones específicas del motor
  • Costos de Licencia: Modelos comerciales de licencias
  • Tamaño de Compilación: Builds de gran tamaño
  • Debugging: Herramientas de debugging específicas del motor

Preguntas Frecuentes de Examen

  1. ¿Cuál es la diferencia entre Unity y Unreal Engine? Unity utiliza C# y está optimizado para juegos indie y móviles. Unreal Engine usa C++ y está enfocado en juegos AAA con gráficos de alta gama.

  2. Explica el Game Loop. El Game Loop es el ciclo central que consiste en manejo de entrada, actualización de lógica, actualización de física y renderizado, con intervalos de tiempo fijo o variable.

  3. ¿Cuándo se usa cada tipo de detección de colisiones? AABB para filtrado rápido, OBB para colisiones precisas, SAT para geometrías complejas, GJK para escenarios muy complejos.

  4. ¿Cuál es el propósito de los shaders? Los shaders son pequeños programas que se ejecutan en la GPU y controlan la representación visual de objetos 3D.

Fuentes Principales

  1. https://unity.com/
  2. https://unrealengine.com/
  3. https://docs.unity3d.com/
  4. https://docs.unrealengine.com/
Volver al blog
Share:

Entradas relacionadas