← SEARCHABLE ARCHIVE
C / C++ · 2026-07-12

C++ de los fundamentos al dominio — Lección 12

C++ organizado en la estructura del juego.

INTERACTIVE READING MODE →

Los artículos escritos en este blog fueron creados con el objetivo de enseñar. Además, la información del artículo puede ser incorrecta, así que tenga cuidado.

Conectemos la gramática que hemos aprendido hasta ahora con la estructura real del juego. Decidir qué clases y estructuras de datos tienen qué responsabilidades es más difícil que memorizar la gramática.

compartiendo responsabilidad

Los personajes pueden ser responsables de la salud y el ataque, y los monstruos pueden ser responsables del estado de los monstruos. DungeonGame administra tu grupo, los monstruos activos, la cola de generación y la base de datos de elementos.

class DungeonGame
{
public:
    void AddCharacter(std::unique_ptr<Character> character)
    {
        if (character)
        {
            party.push_back(std::move(character));
        }
    }

    void QueueMonster(std::shared_ptr<Monster> monster)
    {
        if (monster)
        {
            spawnQueue.push(std::move(monster));
        }
    }

private:
    std::vector<std::unique_ptr<Character>> party;
    std::vector<std::shared_ptr<Monster>> activeMonsters;
    std::queue<std::shared_ptr<Monster>> spawnQueue;
    std::stack<int> killHistory;
    std::unordered_map<int, Item> itemDatabase;
    std::weak_ptr<Monster> currentTarget;
};

Los personajes del grupo son propiedad exclusiva de DungeonGame, por lo que se utilizó Uniqueptr. Los monstruos se pueden compartir entre la cola y la lista activa, por eso usamos shareptr. El objetivo actual es débil_ptr porque no es de su propiedad.

Con solo mirar el tipo, se puede saber hasta cierto punto quién mantiene vivo el objeto.

Procesamiento de valores límite

Si asume que solo se recibe información normal, el código del juego se descompondrá rápidamente.

bool AttackMonster(std::size_t characterIndex, int monsterId)
{
    if (characterIndex >= party.size())
    {
        return false;
    }

    auto iter = std::find_if(
        activeMonsters.begin(),
        activeMonsters.end(),
        [monsterId](const auto& monster)
        {
            return monster && monster->GetId() == monsterId;
        }
    );

    if (iter == activeMonsters.end())
    {
        return false;
    }

    (*iter)->TakeDamage(party[characterIndex]->Attack());
    return true;
}

Primero busca índices no válidos, ID inexistentes y posibilidades de nullptr y regresa temprano. No debes llamar a front en una cola vacía o top en una pila vacía.

La salud no debe bajar de 0 y los objetos que ya están muertos no deben procesarse nuevamente. Las salvaguardas no son una característica llamativa, pero reducen significativamente el tiempo de depuración.

Conceptos correspondientes a Unreal

Las estructuras de datos en general de tipo C++ y Unreal suelen tener funciones similares.

  • std::vectoryTArray
  • std::unordered_mapclaseTMap
  • std::unique_ptrclaseTUniquePtr
  • std::shared_ptrclaseTSharedPtr
  • std::weak_ptrclaseTWeakPtr

Sin embargo, std::shared_ptr no debe usarse para UObject solo porque el nombre es similar. Mientras que los objetos C++ normales tienen su vida útil administrada por RAII y punteros inteligentes, los UObjects son administrados por GC de Unreal.

AActor no se elimina directamente como un objeto normal.Destroy(), y el proceso de terminación esEndPlay()Procesado en Cabe señalar que el tipo F general y el tipo UObject tienen diferentes métodos de propiedad.

Además, si se produce una reasignación tanto de TArray como de std::vector, el puntero que apunta al elemento puede dejar de ser válido.

Al final, la sintaxis de C++ no es una función separada. Las clases vinculan responsabilidades, los contenedores almacenan el estado, los punteros inteligentes representan vidas y los algoritmos realizan operaciones.

Una vez que comience a ver esta estructura, podrá comprender por qué el código se ve así en lugar de simplemente memorizar la gramática.