feat: Bordes como obstáculos + Variables BOIDS ajustables + Fix tecla G
**1. Bordes como obstáculos (no más wrapping):** - Implementada fuerza de repulsión cuando boids se acercan a bordes - Nueva regla: Boundary Avoidance (evitar bordes) - Fuerza proporcional a cercanía (0% en margen, 100% en colisión) - Constantes: BOID_BOUNDARY_MARGIN (50px), BOID_BOUNDARY_WEIGHT (7200 px/s²) **2. Variables ajustables en runtime:** - Añadidas 11 variables miembro en BoidManager (inicializadas con defines.h) - Permite modificar comportamiento sin recompilar - Variables: radios (separation/alignment/cohesion), weights, speeds, boundary - Base para futuras herramientas de debug/tweaking visual **3. Fix tecla G (BOIDS → PHYSICS):** - Corregido: toggleBoidsMode() ahora acepta parámetro force_gravity_on - handleGravityToggle() pasa explícitamente false para preservar inercia - Transición BOIDS→PHYSICS ahora mantiene gravedad OFF correctamente **Implementación:** - defines.h: +2 constantes (BOUNDARY_MARGIN, BOUNDARY_WEIGHT) - boid_manager.h: +11 variables miembro ajustables - boid_manager.cpp: - Constructor inicializa variables - Todas las funciones usan variables en lugar de constantes - applyBoundaries() completamente reescrito (repulsión vs wrapping) - engine.h/cpp: toggleBoidsMode() con parámetro opcional 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -17,7 +17,18 @@ BoidManager::BoidManager()
|
||||
, screen_width_(0)
|
||||
, screen_height_(0)
|
||||
, boids_active_(false)
|
||||
, spatial_grid_(800, 600, BOID_GRID_CELL_SIZE) { // Tamaño por defecto, se actualiza en initialize()
|
||||
, spatial_grid_(800, 600, BOID_GRID_CELL_SIZE) // Tamaño por defecto, se actualiza en initialize()
|
||||
, separation_radius_(BOID_SEPARATION_RADIUS)
|
||||
, alignment_radius_(BOID_ALIGNMENT_RADIUS)
|
||||
, cohesion_radius_(BOID_COHESION_RADIUS)
|
||||
, separation_weight_(BOID_SEPARATION_WEIGHT)
|
||||
, alignment_weight_(BOID_ALIGNMENT_WEIGHT)
|
||||
, cohesion_weight_(BOID_COHESION_WEIGHT)
|
||||
, max_speed_(BOID_MAX_SPEED)
|
||||
, min_speed_(BOID_MIN_SPEED)
|
||||
, max_force_(BOID_MAX_FORCE)
|
||||
, boundary_margin_(BOID_BOUNDARY_MARGIN)
|
||||
, boundary_weight_(BOID_BOUNDARY_WEIGHT) {
|
||||
}
|
||||
|
||||
BoidManager::~BoidManager() {
|
||||
@@ -146,7 +157,7 @@ void BoidManager::applySeparation(Ball* boid, float delta_time) {
|
||||
float center_y = pos.y + pos.h / 2.0f;
|
||||
|
||||
// FASE 2: Usar spatial grid para buscar solo vecinos cercanos (O(1) en lugar de O(n))
|
||||
auto neighbors = spatial_grid_.queryRadius(center_x, center_y, BOID_SEPARATION_RADIUS);
|
||||
auto neighbors = spatial_grid_.queryRadius(center_x, center_y, separation_radius_);
|
||||
|
||||
for (Ball* other : neighbors) {
|
||||
if (other == boid) continue; // Ignorar a sí mismo
|
||||
@@ -159,10 +170,10 @@ void BoidManager::applySeparation(Ball* boid, float delta_time) {
|
||||
float dy = center_y - other_y;
|
||||
float distance = std::sqrt(dx * dx + dy * dy);
|
||||
|
||||
if (distance > 0.0f && distance < BOID_SEPARATION_RADIUS) {
|
||||
if (distance > 0.0f && distance < separation_radius_) {
|
||||
// FASE 1.3: Separación más fuerte cuando más cerca (inversamente proporcional a distancia)
|
||||
// Fuerza proporcional a cercanía: 0% en radio máximo, 100% en colisión
|
||||
float separation_strength = (BOID_SEPARATION_RADIUS - distance) / BOID_SEPARATION_RADIUS;
|
||||
float separation_strength = (separation_radius_ - distance) / separation_radius_;
|
||||
steer_x += (dx / distance) * separation_strength;
|
||||
steer_y += (dy / distance) * separation_strength;
|
||||
count++;
|
||||
@@ -177,8 +188,8 @@ void BoidManager::applySeparation(Ball* boid, float delta_time) {
|
||||
// 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;
|
||||
vx += steer_x * separation_weight_ * delta_time;
|
||||
vy += steer_y * separation_weight_ * delta_time;
|
||||
boid->setVelocity(vx, vy);
|
||||
}
|
||||
}
|
||||
@@ -194,7 +205,7 @@ void BoidManager::applyAlignment(Ball* boid, float delta_time) {
|
||||
float center_y = pos.y + pos.h / 2.0f;
|
||||
|
||||
// FASE 2: Usar spatial grid para buscar solo vecinos cercanos (O(1) en lugar de O(n))
|
||||
auto neighbors = spatial_grid_.queryRadius(center_x, center_y, BOID_ALIGNMENT_RADIUS);
|
||||
auto neighbors = spatial_grid_.queryRadius(center_x, center_y, alignment_radius_);
|
||||
|
||||
for (Ball* other : neighbors) {
|
||||
if (other == boid) continue;
|
||||
@@ -207,7 +218,7 @@ void BoidManager::applyAlignment(Ball* boid, float delta_time) {
|
||||
float dy = center_y - other_y;
|
||||
float distance = std::sqrt(dx * dx + dy * dy);
|
||||
|
||||
if (distance < BOID_ALIGNMENT_RADIUS) {
|
||||
if (distance < alignment_radius_) {
|
||||
float other_vx, other_vy;
|
||||
other->getVelocity(other_vx, other_vy);
|
||||
avg_vx += other_vx;
|
||||
@@ -224,14 +235,14 @@ void BoidManager::applyAlignment(Ball* boid, float delta_time) {
|
||||
// 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;
|
||||
float steer_x = (avg_vx - vx) * alignment_weight_ * delta_time;
|
||||
float steer_y = (avg_vy - vy) * 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;
|
||||
if (steer_mag > max_force_) {
|
||||
steer_x = (steer_x / steer_mag) * max_force_;
|
||||
steer_y = (steer_y / steer_mag) * max_force_;
|
||||
}
|
||||
|
||||
vx += steer_x;
|
||||
@@ -251,7 +262,7 @@ void BoidManager::applyCohesion(Ball* boid, float delta_time) {
|
||||
float center_y = pos.y + pos.h / 2.0f;
|
||||
|
||||
// FASE 2: Usar spatial grid para buscar solo vecinos cercanos (O(1) en lugar de O(n))
|
||||
auto neighbors = spatial_grid_.queryRadius(center_x, center_y, BOID_COHESION_RADIUS);
|
||||
auto neighbors = spatial_grid_.queryRadius(center_x, center_y, cohesion_radius_);
|
||||
|
||||
for (Ball* other : neighbors) {
|
||||
if (other == boid) continue;
|
||||
@@ -264,7 +275,7 @@ void BoidManager::applyCohesion(Ball* boid, float delta_time) {
|
||||
float dy = center_y - other_y;
|
||||
float distance = std::sqrt(dx * dx + dy * dy);
|
||||
|
||||
if (distance < BOID_COHESION_RADIUS) {
|
||||
if (distance < cohesion_radius_) {
|
||||
center_of_mass_x += other_x;
|
||||
center_of_mass_y += other_y;
|
||||
count++;
|
||||
@@ -284,14 +295,14 @@ void BoidManager::applyCohesion(Ball* boid, float delta_time) {
|
||||
// Solo aplicar si hay distancia al centro (evitar división por cero)
|
||||
if (distance_to_center > 0.1f) {
|
||||
// Normalizar vector dirección (fuerza independiente de distancia)
|
||||
float steer_x = (dx_to_center / distance_to_center) * BOID_COHESION_WEIGHT * delta_time;
|
||||
float steer_y = (dy_to_center / distance_to_center) * BOID_COHESION_WEIGHT * delta_time;
|
||||
float steer_x = (dx_to_center / distance_to_center) * cohesion_weight_ * delta_time;
|
||||
float steer_y = (dy_to_center / distance_to_center) * 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;
|
||||
if (steer_mag > max_force_) {
|
||||
steer_x = (steer_x / steer_mag) * max_force_;
|
||||
steer_y = (steer_y / steer_mag) * max_force_;
|
||||
}
|
||||
|
||||
float vx, vy;
|
||||
@@ -304,32 +315,69 @@ void BoidManager::applyCohesion(Ball* boid, float delta_time) {
|
||||
}
|
||||
|
||||
void BoidManager::applyBoundaries(Ball* boid) {
|
||||
// Mantener boids dentro de los límites de la pantalla
|
||||
// Comportamiento "wrapping" (teletransporte al otro lado)
|
||||
// NUEVA IMPLEMENTACIÓN: Bordes como obstáculos (repulsión en lugar de wrapping)
|
||||
// Cuando un boid se acerca a un borde, se aplica una fuerza alejándolo
|
||||
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;
|
||||
float steer_x = 0.0f;
|
||||
float steer_y = 0.0f;
|
||||
|
||||
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;
|
||||
// Borde izquierdo (x < boundary_margin_)
|
||||
if (center_x < boundary_margin_) {
|
||||
float distance = center_x; // Distancia al borde (0 = colisión)
|
||||
if (distance < boundary_margin_) {
|
||||
// Fuerza proporcional a cercanía: 0% en margen, 100% en colisión
|
||||
float repulsion_strength = (boundary_margin_ - distance) / boundary_margin_;
|
||||
steer_x += repulsion_strength; // Empujar hacia la derecha
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
// Borde derecho (x > screen_width_ - boundary_margin_)
|
||||
if (center_x > screen_width_ - boundary_margin_) {
|
||||
float distance = screen_width_ - center_x;
|
||||
if (distance < boundary_margin_) {
|
||||
float repulsion_strength = (boundary_margin_ - distance) / boundary_margin_;
|
||||
steer_x -= repulsion_strength; // Empujar hacia la izquierda
|
||||
}
|
||||
}
|
||||
|
||||
if (wrapped) {
|
||||
boid->setPosition(pos.x, pos.y);
|
||||
// Borde superior (y < boundary_margin_)
|
||||
if (center_y < boundary_margin_) {
|
||||
float distance = center_y;
|
||||
if (distance < boundary_margin_) {
|
||||
float repulsion_strength = (boundary_margin_ - distance) / boundary_margin_;
|
||||
steer_y += repulsion_strength; // Empujar hacia abajo
|
||||
}
|
||||
}
|
||||
|
||||
// Borde inferior (y > screen_height_ - boundary_margin_)
|
||||
if (center_y > screen_height_ - boundary_margin_) {
|
||||
float distance = screen_height_ - center_y;
|
||||
if (distance < boundary_margin_) {
|
||||
float repulsion_strength = (boundary_margin_ - distance) / boundary_margin_;
|
||||
steer_y -= repulsion_strength; // Empujar hacia arriba
|
||||
}
|
||||
}
|
||||
|
||||
// Aplicar fuerza de repulsión si hay alguna
|
||||
if (steer_x != 0.0f || steer_y != 0.0f) {
|
||||
float vx, vy;
|
||||
boid->getVelocity(vx, vy);
|
||||
|
||||
// Normalizar fuerza de repulsión (para que todas las direcciones tengan la misma intensidad)
|
||||
float steer_mag = std::sqrt(steer_x * steer_x + steer_y * steer_y);
|
||||
if (steer_mag > 0.0f) {
|
||||
steer_x /= steer_mag;
|
||||
steer_y /= steer_mag;
|
||||
}
|
||||
|
||||
// Aplicar aceleración de repulsión (time-based)
|
||||
// boundary_weight_ es más fuerte que separation para garantizar que no escapen
|
||||
vx += steer_x * boundary_weight_ * (1.0f / 60.0f); // Simular delta_time fijo para independencia
|
||||
vy += steer_y * boundary_weight_ * (1.0f / 60.0f);
|
||||
boid->setVelocity(vx, vy);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -341,16 +389,16 @@ void BoidManager::limitSpeed(Ball* boid) {
|
||||
float speed = std::sqrt(vx * vx + vy * vy);
|
||||
|
||||
// Limitar velocidad máxima
|
||||
if (speed > BOID_MAX_SPEED) {
|
||||
vx = (vx / speed) * BOID_MAX_SPEED;
|
||||
vy = (vy / speed) * BOID_MAX_SPEED;
|
||||
if (speed > max_speed_) {
|
||||
vx = (vx / speed) * max_speed_;
|
||||
vy = (vy / speed) * max_speed_;
|
||||
boid->setVelocity(vx, vy);
|
||||
}
|
||||
|
||||
// FASE 1.2: Aplicar velocidad mínima (evitar boids estáticos)
|
||||
if (speed > 0.0f && speed < BOID_MIN_SPEED) {
|
||||
vx = (vx / speed) * BOID_MIN_SPEED;
|
||||
vy = (vy / speed) * BOID_MIN_SPEED;
|
||||
if (speed > 0.0f && speed < min_speed_) {
|
||||
vx = (vx / speed) * min_speed_;
|
||||
vy = (vy / speed) * min_speed_;
|
||||
boid->setVelocity(vx, vy);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user