Refactor fase 10: Implementar BoidManager completo
Cambios realizados: - Creado BoidManager (source/boids_mgr/) con algoritmo de Reynolds (1987) * Separación: Evitar colisiones con vecinos cercanos * Alineación: Seguir dirección promedio del grupo * Cohesión: Moverse hacia centro de masa del grupo * Wrapping boundaries (teletransporte en bordes) * Velocidad y fuerza limitadas (steering behavior) - Añadido BOIDS a enum SimulationMode (defines.h) - Añadidas constantes de configuración boids (defines.h) - Integrado BoidManager en Engine (inicialización, update, toggle) - Añadido binding de tecla J para toggleBoidsMode() (input_handler.cpp) - Añadidos helpers en Ball: getVelocity(), setVelocity(), setPosition() - Actualizado CMakeLists.txt para incluir source/boids_mgr/*.cpp Arquitectura: - BoidManager sigue el patrón establecido (similar a ShapeManager) - Gestión independiente del comportamiento de enjambre - Tres reglas de Reynolds implementadas correctamente - Compatible con sistema de resolución dinámica Estado: Compilación exitosa, BoidManager funcional Próximo paso: Testing y ajuste de parámetros boids 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -25,7 +25,7 @@ if (NOT SDL3_ttf_FOUND)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Archivos fuente (excluir main_old.cpp)
|
# Archivos fuente (excluir main_old.cpp)
|
||||||
file(GLOB SOURCE_FILES source/*.cpp source/external/*.cpp source/input/*.cpp source/scene/*.cpp source/shapes/*.cpp source/shapes_mgr/*.cpp source/state/*.cpp source/themes/*.cpp source/text/*.cpp source/ui/*.cpp)
|
file(GLOB SOURCE_FILES source/*.cpp source/external/*.cpp source/boids_mgr/*.cpp source/input/*.cpp source/scene/*.cpp source/shapes/*.cpp source/shapes_mgr/*.cpp source/state/*.cpp source/themes/*.cpp source/text/*.cpp source/ui/*.cpp)
|
||||||
list(REMOVE_ITEM SOURCE_FILES "${CMAKE_SOURCE_DIR}/source/main_old.cpp")
|
list(REMOVE_ITEM SOURCE_FILES "${CMAKE_SOURCE_DIR}/source/main_old.cpp")
|
||||||
|
|
||||||
# Comprobar si se encontraron archivos fuente
|
# Comprobar si se encontraron archivos fuente
|
||||||
|
|||||||
@@ -71,6 +71,13 @@ class Ball {
|
|||||||
GravityDirection getGravityDirection() const { return gravity_direction_; }
|
GravityDirection getGravityDirection() const { return gravity_direction_; }
|
||||||
bool isOnSurface() const { return on_surface_; }
|
bool isOnSurface() const { return on_surface_; }
|
||||||
|
|
||||||
|
// Getters/Setters para velocidad (usado por BoidManager)
|
||||||
|
void getVelocity(float& vx, float& vy) const { vx = vx_; vy = vy_; }
|
||||||
|
void setVelocity(float vx, float vy) { vx_ = vx; vy_ = vy; }
|
||||||
|
|
||||||
|
// Setter para posición simple (usado por BoidManager)
|
||||||
|
void setPosition(float x, float y) { pos_.x = x; pos_.y = y; }
|
||||||
|
|
||||||
// Getters/Setters para batch rendering
|
// Getters/Setters para batch rendering
|
||||||
SDL_FRect getPosition() const { return pos_; }
|
SDL_FRect getPosition() const { return pos_; }
|
||||||
Color getColor() const { return color_; }
|
Color getColor() const { return color_; }
|
||||||
|
|||||||
314
source/boids_mgr/boid_manager.cpp
Normal file
314
source/boids_mgr/boid_manager.cpp
Normal file
@@ -0,0 +1,314 @@
|
|||||||
|
#include "boid_manager.h"
|
||||||
|
|
||||||
|
#include <algorithm> // for std::min, std::max
|
||||||
|
#include <cmath> // for sqrt, atan2
|
||||||
|
|
||||||
|
#include "../ball.h" // for Ball
|
||||||
|
#include "../engine.h" // for Engine (si se necesita)
|
||||||
|
#include "../scene/scene_manager.h" // for SceneManager
|
||||||
|
#include "../state/state_manager.h" // for StateManager
|
||||||
|
#include "../ui/ui_manager.h" // for UIManager
|
||||||
|
|
||||||
|
BoidManager::BoidManager()
|
||||||
|
: engine_(nullptr)
|
||||||
|
, scene_mgr_(nullptr)
|
||||||
|
, ui_mgr_(nullptr)
|
||||||
|
, state_mgr_(nullptr)
|
||||||
|
, screen_width_(0)
|
||||||
|
, screen_height_(0)
|
||||||
|
, boids_active_(false) {
|
||||||
|
}
|
||||||
|
|
||||||
|
BoidManager::~BoidManager() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void BoidManager::initialize(Engine* engine, SceneManager* scene_mgr, UIManager* ui_mgr,
|
||||||
|
StateManager* state_mgr, int screen_width, int screen_height) {
|
||||||
|
engine_ = engine;
|
||||||
|
scene_mgr_ = scene_mgr;
|
||||||
|
ui_mgr_ = ui_mgr;
|
||||||
|
state_mgr_ = state_mgr;
|
||||||
|
screen_width_ = screen_width;
|
||||||
|
screen_height_ = screen_height;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BoidManager::updateScreenSize(int width, int height) {
|
||||||
|
screen_width_ = width;
|
||||||
|
screen_height_ = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BoidManager::activateBoids() {
|
||||||
|
boids_active_ = true;
|
||||||
|
|
||||||
|
// Desactivar gravedad al entrar en modo boids
|
||||||
|
scene_mgr_->forceBallsGravityOff();
|
||||||
|
|
||||||
|
// Inicializar velocidades aleatorias para los boids
|
||||||
|
auto& balls = scene_mgr_->getBallsMutable();
|
||||||
|
for (auto& ball : balls) {
|
||||||
|
// Dar velocidad inicial aleatoria si está quieto
|
||||||
|
float vx, vy;
|
||||||
|
ball->getVelocity(vx, vy);
|
||||||
|
if (vx == 0.0f && vy == 0.0f) {
|
||||||
|
// Velocidad aleatoria entre -1 y 1
|
||||||
|
vx = (rand() % 200 - 100) / 100.0f;
|
||||||
|
vy = (rand() % 200 - 100) / 100.0f;
|
||||||
|
ball->setVelocity(vx, vy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mostrar notificación (solo si NO estamos en modo demo o logo)
|
||||||
|
if (state_mgr_ && ui_mgr_ && state_mgr_->getCurrentMode() == AppMode::SANDBOX) {
|
||||||
|
ui_mgr_->showNotification("Modo Boids");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BoidManager::deactivateBoids(bool force_gravity_on) {
|
||||||
|
if (!boids_active_) return;
|
||||||
|
|
||||||
|
boids_active_ = false;
|
||||||
|
|
||||||
|
// Activar gravedad al salir (si se especifica)
|
||||||
|
if (force_gravity_on) {
|
||||||
|
scene_mgr_->forceBallsGravityOn();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mostrar notificación (solo si NO estamos en modo demo o logo)
|
||||||
|
if (state_mgr_ && ui_mgr_ && state_mgr_->getCurrentMode() == AppMode::SANDBOX) {
|
||||||
|
ui_mgr_->showNotification("Modo Física");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BoidManager::toggleBoidsMode(bool force_gravity_on) {
|
||||||
|
if (boids_active_) {
|
||||||
|
deactivateBoids(force_gravity_on);
|
||||||
|
} else {
|
||||||
|
activateBoids();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BoidManager::update(float delta_time) {
|
||||||
|
if (!boids_active_) return;
|
||||||
|
|
||||||
|
auto& balls = scene_mgr_->getBallsMutable();
|
||||||
|
|
||||||
|
// Aplicar las tres reglas de Reynolds a cada boid
|
||||||
|
for (auto& ball : balls) {
|
||||||
|
applySeparation(ball.get(), delta_time);
|
||||||
|
applyAlignment(ball.get(), delta_time);
|
||||||
|
applyCohesion(ball.get(), delta_time);
|
||||||
|
applyBoundaries(ball.get());
|
||||||
|
limitSpeed(ball.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Actualizar posiciones con velocidades resultantes
|
||||||
|
for (auto& ball : balls) {
|
||||||
|
float vx, vy;
|
||||||
|
ball->getVelocity(vx, vy);
|
||||||
|
|
||||||
|
SDL_FRect pos = ball->getPosition();
|
||||||
|
pos.x += vx;
|
||||||
|
pos.y += vy;
|
||||||
|
|
||||||
|
ball->setPosition(pos.x, pos.y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// REGLAS DE REYNOLDS (1987)
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
void BoidManager::applySeparation(Ball* boid, float delta_time) {
|
||||||
|
// Regla 1: Separación - Evitar colisiones con vecinos cercanos
|
||||||
|
float steer_x = 0.0f;
|
||||||
|
float steer_y = 0.0f;
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
SDL_FRect pos = boid->getPosition();
|
||||||
|
float center_x = pos.x + pos.w / 2.0f;
|
||||||
|
float center_y = pos.y + pos.h / 2.0f;
|
||||||
|
|
||||||
|
const auto& balls = scene_mgr_->getBalls();
|
||||||
|
for (const auto& other : balls) {
|
||||||
|
if (other.get() == boid) continue; // Ignorar a sí mismo
|
||||||
|
|
||||||
|
SDL_FRect other_pos = other->getPosition();
|
||||||
|
float other_x = other_pos.x + other_pos.w / 2.0f;
|
||||||
|
float other_y = other_pos.y + other_pos.h / 2.0f;
|
||||||
|
|
||||||
|
float dx = center_x - other_x;
|
||||||
|
float dy = center_y - other_y;
|
||||||
|
float distance = std::sqrt(dx * dx + dy * dy);
|
||||||
|
|
||||||
|
if (distance > 0.0f && distance < BOID_SEPARATION_RADIUS) {
|
||||||
|
// Vector normalizado apuntando lejos del vecino, ponderado por cercanía
|
||||||
|
steer_x += (dx / distance) / distance;
|
||||||
|
steer_y += (dy / distance) / distance;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count > 0) {
|
||||||
|
// Promedio
|
||||||
|
steer_x /= count;
|
||||||
|
steer_y /= count;
|
||||||
|
|
||||||
|
// Aplicar fuerza de separación
|
||||||
|
float vx, vy;
|
||||||
|
boid->getVelocity(vx, vy);
|
||||||
|
vx += steer_x * BOID_SEPARATION_WEIGHT * delta_time;
|
||||||
|
vy += steer_y * BOID_SEPARATION_WEIGHT * delta_time;
|
||||||
|
boid->setVelocity(vx, vy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BoidManager::applyAlignment(Ball* boid, float delta_time) {
|
||||||
|
// Regla 2: Alineación - Seguir dirección promedio del grupo
|
||||||
|
float avg_vx = 0.0f;
|
||||||
|
float avg_vy = 0.0f;
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
SDL_FRect pos = boid->getPosition();
|
||||||
|
float center_x = pos.x + pos.w / 2.0f;
|
||||||
|
float center_y = pos.y + pos.h / 2.0f;
|
||||||
|
|
||||||
|
const auto& balls = scene_mgr_->getBalls();
|
||||||
|
for (const auto& other : balls) {
|
||||||
|
if (other.get() == boid) continue;
|
||||||
|
|
||||||
|
SDL_FRect other_pos = other->getPosition();
|
||||||
|
float other_x = other_pos.x + other_pos.w / 2.0f;
|
||||||
|
float other_y = other_pos.y + other_pos.h / 2.0f;
|
||||||
|
|
||||||
|
float dx = center_x - other_x;
|
||||||
|
float dy = center_y - other_y;
|
||||||
|
float distance = std::sqrt(dx * dx + dy * dy);
|
||||||
|
|
||||||
|
if (distance < BOID_ALIGNMENT_RADIUS) {
|
||||||
|
float other_vx, other_vy;
|
||||||
|
other->getVelocity(other_vx, other_vy);
|
||||||
|
avg_vx += other_vx;
|
||||||
|
avg_vy += other_vy;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count > 0) {
|
||||||
|
// Velocidad promedio del grupo
|
||||||
|
avg_vx /= count;
|
||||||
|
avg_vy /= count;
|
||||||
|
|
||||||
|
// Steering hacia la velocidad promedio
|
||||||
|
float vx, vy;
|
||||||
|
boid->getVelocity(vx, vy);
|
||||||
|
float steer_x = (avg_vx - vx) * BOID_ALIGNMENT_WEIGHT * delta_time;
|
||||||
|
float steer_y = (avg_vy - vy) * BOID_ALIGNMENT_WEIGHT * delta_time;
|
||||||
|
|
||||||
|
// Limitar fuerza máxima de steering
|
||||||
|
float steer_mag = std::sqrt(steer_x * steer_x + steer_y * steer_y);
|
||||||
|
if (steer_mag > BOID_MAX_FORCE) {
|
||||||
|
steer_x = (steer_x / steer_mag) * BOID_MAX_FORCE;
|
||||||
|
steer_y = (steer_y / steer_mag) * BOID_MAX_FORCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
vx += steer_x;
|
||||||
|
vy += steer_y;
|
||||||
|
boid->setVelocity(vx, vy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BoidManager::applyCohesion(Ball* boid, float delta_time) {
|
||||||
|
// Regla 3: Cohesión - Moverse hacia el centro de masa del grupo
|
||||||
|
float center_of_mass_x = 0.0f;
|
||||||
|
float center_of_mass_y = 0.0f;
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
SDL_FRect pos = boid->getPosition();
|
||||||
|
float center_x = pos.x + pos.w / 2.0f;
|
||||||
|
float center_y = pos.y + pos.h / 2.0f;
|
||||||
|
|
||||||
|
const auto& balls = scene_mgr_->getBalls();
|
||||||
|
for (const auto& other : balls) {
|
||||||
|
if (other.get() == boid) continue;
|
||||||
|
|
||||||
|
SDL_FRect other_pos = other->getPosition();
|
||||||
|
float other_x = other_pos.x + other_pos.w / 2.0f;
|
||||||
|
float other_y = other_pos.y + other_pos.h / 2.0f;
|
||||||
|
|
||||||
|
float dx = center_x - other_x;
|
||||||
|
float dy = center_y - other_y;
|
||||||
|
float distance = std::sqrt(dx * dx + dy * dy);
|
||||||
|
|
||||||
|
if (distance < BOID_COHESION_RADIUS) {
|
||||||
|
center_of_mass_x += other_x;
|
||||||
|
center_of_mass_y += other_y;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count > 0) {
|
||||||
|
// Centro de masa del grupo
|
||||||
|
center_of_mass_x /= count;
|
||||||
|
center_of_mass_y /= count;
|
||||||
|
|
||||||
|
// Dirección hacia el centro
|
||||||
|
float steer_x = (center_of_mass_x - center_x) * BOID_COHESION_WEIGHT * delta_time;
|
||||||
|
float steer_y = (center_of_mass_y - center_y) * BOID_COHESION_WEIGHT * delta_time;
|
||||||
|
|
||||||
|
// Limitar fuerza máxima de steering
|
||||||
|
float steer_mag = std::sqrt(steer_x * steer_x + steer_y * steer_y);
|
||||||
|
if (steer_mag > BOID_MAX_FORCE) {
|
||||||
|
steer_x = (steer_x / steer_mag) * BOID_MAX_FORCE;
|
||||||
|
steer_y = (steer_y / steer_mag) * BOID_MAX_FORCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
float vx, vy;
|
||||||
|
boid->getVelocity(vx, vy);
|
||||||
|
vx += steer_x;
|
||||||
|
vy += steer_y;
|
||||||
|
boid->setVelocity(vx, vy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BoidManager::applyBoundaries(Ball* boid) {
|
||||||
|
// Mantener boids dentro de los límites de la pantalla
|
||||||
|
// Comportamiento "wrapping" (teletransporte al otro lado)
|
||||||
|
SDL_FRect pos = boid->getPosition();
|
||||||
|
float center_x = pos.x + pos.w / 2.0f;
|
||||||
|
float center_y = pos.y + pos.h / 2.0f;
|
||||||
|
|
||||||
|
bool wrapped = false;
|
||||||
|
|
||||||
|
if (center_x < 0) {
|
||||||
|
pos.x = screen_width_ - pos.w / 2.0f;
|
||||||
|
wrapped = true;
|
||||||
|
} else if (center_x > screen_width_) {
|
||||||
|
pos.x = -pos.w / 2.0f;
|
||||||
|
wrapped = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (center_y < 0) {
|
||||||
|
pos.y = screen_height_ - pos.h / 2.0f;
|
||||||
|
wrapped = true;
|
||||||
|
} else if (center_y > screen_height_) {
|
||||||
|
pos.y = -pos.h / 2.0f;
|
||||||
|
wrapped = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wrapped) {
|
||||||
|
boid->setPosition(pos.x, pos.y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BoidManager::limitSpeed(Ball* boid) {
|
||||||
|
// Limitar velocidad máxima del boid
|
||||||
|
float vx, vy;
|
||||||
|
boid->getVelocity(vx, vy);
|
||||||
|
|
||||||
|
float speed = std::sqrt(vx * vx + vy * vy);
|
||||||
|
if (speed > BOID_MAX_SPEED) {
|
||||||
|
vx = (vx / speed) * BOID_MAX_SPEED;
|
||||||
|
vy = (vy / speed) * BOID_MAX_SPEED;
|
||||||
|
boid->setVelocity(vx, vy);
|
||||||
|
}
|
||||||
|
}
|
||||||
107
source/boids_mgr/boid_manager.h
Normal file
107
source/boids_mgr/boid_manager.h
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef> // for size_t
|
||||||
|
|
||||||
|
#include "../defines.h" // for SimulationMode, AppMode
|
||||||
|
|
||||||
|
// Forward declarations
|
||||||
|
class Engine;
|
||||||
|
class SceneManager;
|
||||||
|
class UIManager;
|
||||||
|
class StateManager;
|
||||||
|
class Ball;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class BoidManager
|
||||||
|
* @brief Gestiona el comportamiento de enjambre (boids)
|
||||||
|
*
|
||||||
|
* Responsabilidad única: Implementación de algoritmo de boids (Reynolds 1987)
|
||||||
|
*
|
||||||
|
* Características:
|
||||||
|
* - Separación: Evitar colisiones con vecinos cercanos
|
||||||
|
* - Alineación: Seguir dirección promedio del grupo
|
||||||
|
* - Cohesión: Moverse hacia el centro de masa del grupo
|
||||||
|
* - Comportamiento emergente sin control centralizado
|
||||||
|
* - Física de steering behavior (velocidad limitada)
|
||||||
|
*/
|
||||||
|
class BoidManager {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Constructor
|
||||||
|
*/
|
||||||
|
BoidManager();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Destructor
|
||||||
|
*/
|
||||||
|
~BoidManager();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Inicializa el BoidManager con referencias a componentes del Engine
|
||||||
|
* @param engine Puntero al Engine (para acceso a recursos)
|
||||||
|
* @param scene_mgr Puntero a SceneManager (acceso a bolas)
|
||||||
|
* @param ui_mgr Puntero a UIManager (notificaciones)
|
||||||
|
* @param state_mgr Puntero a StateManager (estados de aplicación)
|
||||||
|
* @param screen_width Ancho de pantalla actual
|
||||||
|
* @param screen_height Alto de pantalla actual
|
||||||
|
*/
|
||||||
|
void initialize(Engine* engine, SceneManager* scene_mgr, UIManager* ui_mgr,
|
||||||
|
StateManager* state_mgr, int screen_width, int screen_height);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Actualiza el tamaño de pantalla (llamado en resize/fullscreen)
|
||||||
|
* @param width Nuevo ancho de pantalla
|
||||||
|
* @param height Nuevo alto de pantalla
|
||||||
|
*/
|
||||||
|
void updateScreenSize(int width, int height);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Activa el modo boids
|
||||||
|
*/
|
||||||
|
void activateBoids();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Desactiva el modo boids (vuelve a física normal)
|
||||||
|
* @param force_gravity_on Si debe forzar gravedad ON al salir
|
||||||
|
*/
|
||||||
|
void deactivateBoids(bool force_gravity_on = true);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Toggle entre modo boids y modo física
|
||||||
|
* @param force_gravity_on Si debe forzar gravedad ON al salir de boids
|
||||||
|
*/
|
||||||
|
void toggleBoidsMode(bool force_gravity_on = true);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Actualiza el comportamiento de todas las bolas como boids
|
||||||
|
* @param delta_time Delta time para física
|
||||||
|
*/
|
||||||
|
void update(float delta_time);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Verifica si el modo boids está activo
|
||||||
|
* @return true si modo boids está activo
|
||||||
|
*/
|
||||||
|
bool isBoidsActive() const { return boids_active_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Referencias a componentes del Engine
|
||||||
|
Engine* engine_;
|
||||||
|
SceneManager* scene_mgr_;
|
||||||
|
UIManager* ui_mgr_;
|
||||||
|
StateManager* state_mgr_;
|
||||||
|
|
||||||
|
// Tamaño de pantalla
|
||||||
|
int screen_width_;
|
||||||
|
int screen_height_;
|
||||||
|
|
||||||
|
// Estado del modo boids
|
||||||
|
bool boids_active_;
|
||||||
|
|
||||||
|
// Métodos privados para las reglas de Reynolds
|
||||||
|
void applySeparation(Ball* boid, float delta_time);
|
||||||
|
void applyAlignment(Ball* boid, float delta_time);
|
||||||
|
void applyCohesion(Ball* boid, float delta_time);
|
||||||
|
void applyBoundaries(Ball* boid); // Mantener boids dentro de pantalla
|
||||||
|
void limitSpeed(Ball* boid); // Limitar velocidad máxima
|
||||||
|
};
|
||||||
@@ -133,7 +133,8 @@ enum class ShapeType {
|
|||||||
// Enum para modo de simulación
|
// Enum para modo de simulación
|
||||||
enum class SimulationMode {
|
enum class SimulationMode {
|
||||||
PHYSICS, // Modo física normal con gravedad
|
PHYSICS, // Modo física normal con gravedad
|
||||||
SHAPE // Modo figura 3D (Shape polimórfico)
|
SHAPE, // Modo figura 3D (Shape polimórfico)
|
||||||
|
BOIDS // Modo enjambre (Boids - comportamiento emergente)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Enum para modo de aplicación (mutuamente excluyentes)
|
// Enum para modo de aplicación (mutuamente excluyentes)
|
||||||
@@ -287,6 +288,16 @@ constexpr float LOGO_FLIP_TRIGGER_MIN = 0.20f; // 20% mínimo de progres
|
|||||||
constexpr float LOGO_FLIP_TRIGGER_MAX = 0.80f; // 80% máximo de progreso de flip para trigger
|
constexpr float LOGO_FLIP_TRIGGER_MAX = 0.80f; // 80% máximo de progreso de flip para trigger
|
||||||
constexpr int LOGO_FLIP_WAIT_PROBABILITY = 50; // 50% probabilidad de elegir el camino "esperar flip"
|
constexpr int LOGO_FLIP_WAIT_PROBABILITY = 50; // 50% probabilidad de elegir el camino "esperar flip"
|
||||||
|
|
||||||
|
// Configuración de Modo BOIDS (comportamiento de enjambre)
|
||||||
|
constexpr float BOID_SEPARATION_RADIUS = 30.0f; // Radio para evitar colisiones (píxeles)
|
||||||
|
constexpr float BOID_ALIGNMENT_RADIUS = 50.0f; // Radio para alinear velocidad con vecinos
|
||||||
|
constexpr float BOID_COHESION_RADIUS = 80.0f; // Radio para moverse hacia centro del grupo
|
||||||
|
constexpr float BOID_SEPARATION_WEIGHT = 1.5f; // Peso de separación (evitar colisiones)
|
||||||
|
constexpr float BOID_ALIGNMENT_WEIGHT = 1.0f; // Peso de alineación (seguir dirección del grupo)
|
||||||
|
constexpr float BOID_COHESION_WEIGHT = 0.8f; // Peso de cohesión (moverse al centro)
|
||||||
|
constexpr float BOID_MAX_SPEED = 3.0f; // Velocidad máxima (píxeles/frame)
|
||||||
|
constexpr float BOID_MAX_FORCE = 0.1f; // Fuerza máxima de steering
|
||||||
|
|
||||||
constexpr float PI = 3.14159265358979323846f; // Constante PI
|
constexpr float PI = 3.14159265358979323846f; // Constante PI
|
||||||
|
|
||||||
// Función auxiliar para obtener la ruta del directorio del ejecutable
|
// Función auxiliar para obtener la ruta del directorio del ejecutable
|
||||||
|
|||||||
@@ -249,6 +249,11 @@ bool Engine::initialize(int width, int height, int zoom, bool fullscreen) {
|
|||||||
// Actualizar ShapeManager con StateManager (dependencia circular - StateManager debe existir primero)
|
// Actualizar ShapeManager con StateManager (dependencia circular - StateManager debe existir primero)
|
||||||
shape_manager_->initialize(this, scene_manager_.get(), ui_manager_.get(), state_manager_.get(),
|
shape_manager_->initialize(this, scene_manager_.get(), ui_manager_.get(), state_manager_.get(),
|
||||||
current_screen_width_, current_screen_height_);
|
current_screen_width_, current_screen_height_);
|
||||||
|
|
||||||
|
// Inicializar BoidManager (gestión de comportamiento de enjambre)
|
||||||
|
boid_manager_ = std::make_unique<BoidManager>();
|
||||||
|
boid_manager_->initialize(this, scene_manager_.get(), ui_manager_.get(), state_manager_.get(),
|
||||||
|
current_screen_width_, current_screen_height_);
|
||||||
}
|
}
|
||||||
|
|
||||||
return success;
|
return success;
|
||||||
@@ -319,6 +324,9 @@ void Engine::update() {
|
|||||||
} else if (current_mode_ == SimulationMode::SHAPE) {
|
} else if (current_mode_ == SimulationMode::SHAPE) {
|
||||||
// Modo Figura 3D: actualizar figura polimórfica
|
// Modo Figura 3D: actualizar figura polimórfica
|
||||||
updateShape();
|
updateShape();
|
||||||
|
} else if (current_mode_ == SimulationMode::BOIDS) {
|
||||||
|
// Modo Boids: actualizar comportamiento de enjambre (delegado a BoidManager)
|
||||||
|
boid_manager_->update(delta_time_);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actualizar Modo DEMO/LOGO (delegado a StateManager)
|
// Actualizar Modo DEMO/LOGO (delegado a StateManager)
|
||||||
@@ -406,6 +414,32 @@ void Engine::toggleDepthZoom() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Boids (comportamiento de enjambre)
|
||||||
|
void Engine::toggleBoidsMode() {
|
||||||
|
if (current_mode_ == SimulationMode::BOIDS) {
|
||||||
|
// Salir del modo boids
|
||||||
|
current_mode_ = SimulationMode::PHYSICS;
|
||||||
|
boid_manager_->deactivateBoids();
|
||||||
|
} else {
|
||||||
|
// Entrar al modo boids (desde PHYSICS o SHAPE)
|
||||||
|
if (current_mode_ == SimulationMode::SHAPE) {
|
||||||
|
// Si estamos en modo shape, salir primero sin forzar gravedad
|
||||||
|
current_mode_ = SimulationMode::PHYSICS;
|
||||||
|
|
||||||
|
// Desactivar atracción de figuras
|
||||||
|
auto& balls = scene_manager_->getBallsMutable();
|
||||||
|
for (auto& ball : balls) {
|
||||||
|
ball->enableShapeAttraction(false);
|
||||||
|
ball->setDepthScale(1.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Activar modo boids
|
||||||
|
current_mode_ = SimulationMode::BOIDS;
|
||||||
|
boid_manager_->activateBoids();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Temas de colores
|
// Temas de colores
|
||||||
void Engine::cycleTheme(bool forward) {
|
void Engine::cycleTheme(bool forward) {
|
||||||
if (forward) {
|
if (forward) {
|
||||||
|
|||||||
@@ -10,7 +10,8 @@
|
|||||||
#include <string> // for string
|
#include <string> // for string
|
||||||
#include <vector> // for vector
|
#include <vector> // for vector
|
||||||
|
|
||||||
#include "ball.h" // for Ball
|
#include "ball.h" // for Ball
|
||||||
|
#include "boids_mgr/boid_manager.h" // for BoidManager
|
||||||
#include "defines.h" // for GravityDirection, ColorTheme, ShapeType
|
#include "defines.h" // for GravityDirection, ColorTheme, ShapeType
|
||||||
#include "external/texture.h" // for Texture
|
#include "external/texture.h" // for Texture
|
||||||
#include "input/input_handler.h" // for InputHandler
|
#include "input/input_handler.h" // for InputHandler
|
||||||
@@ -46,6 +47,9 @@ class Engine {
|
|||||||
void resetShapeScale();
|
void resetShapeScale();
|
||||||
void toggleDepthZoom();
|
void toggleDepthZoom();
|
||||||
|
|
||||||
|
// Boids (comportamiento de enjambre)
|
||||||
|
void toggleBoidsMode();
|
||||||
|
|
||||||
// Temas de colores
|
// Temas de colores
|
||||||
void cycleTheme(bool forward);
|
void cycleTheme(bool forward);
|
||||||
void switchThemeByNumpad(int numpad_key);
|
void switchThemeByNumpad(int numpad_key);
|
||||||
@@ -87,6 +91,7 @@ class Engine {
|
|||||||
std::unique_ptr<InputHandler> input_handler_; // Manejo de entradas SDL
|
std::unique_ptr<InputHandler> input_handler_; // Manejo de entradas SDL
|
||||||
std::unique_ptr<SceneManager> scene_manager_; // Gestión de bolas y física
|
std::unique_ptr<SceneManager> scene_manager_; // Gestión de bolas y física
|
||||||
std::unique_ptr<ShapeManager> shape_manager_; // Gestión de figuras 3D
|
std::unique_ptr<ShapeManager> shape_manager_; // Gestión de figuras 3D
|
||||||
|
std::unique_ptr<BoidManager> boid_manager_; // Gestión de comportamiento boids
|
||||||
std::unique_ptr<StateManager> state_manager_; // Gestión de estados (DEMO/LOGO)
|
std::unique_ptr<StateManager> state_manager_; // Gestión de estados (DEMO/LOGO)
|
||||||
std::unique_ptr<UIManager> ui_manager_; // Gestión de UI (HUD, FPS, notificaciones)
|
std::unique_ptr<UIManager> ui_manager_; // Gestión de UI (HUD, FPS, notificaciones)
|
||||||
|
|
||||||
|
|||||||
@@ -98,6 +98,11 @@ bool InputHandler::processEvents(Engine& engine) {
|
|||||||
engine.activateShape(ShapeType::PNG_SHAPE, "Forma PNG");
|
engine.activateShape(ShapeType::PNG_SHAPE, "Forma PNG");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
// Toggle Modo Boids (comportamiento de enjambre)
|
||||||
|
case SDLK_J:
|
||||||
|
engine.toggleBoidsMode();
|
||||||
|
break;
|
||||||
|
|
||||||
// Ciclar temas de color (movido de T a B)
|
// Ciclar temas de color (movido de T a B)
|
||||||
case SDLK_B:
|
case SDLK_B:
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user