Implementar PNG_SHAPE y sistema de física mejorado
Nuevas Características:
- PNG_SHAPE (tecla O): Logo JAILGAMES desde PNG 1-bit
- Extrusión 2D con detección de bordes/relleno configurable
- Rotación "legible": 90% frente, 10% volteretas aleatorias
- 15 capas de extrusión con relleno completo (22K+ puntos 3D)
- Fix: Z forzado a máximo cuando está de frente (brillante)
- Excluido de DEMO/DEMO_LITE (logo especial)
- Sistema de texturas dinámicas
- Carga automática desde data/balls/*.png
- normal.png siempre primero, resto alfabético
- Tecla N cicla entre todas las texturas encontradas
- Display dinámico del nombre (uppercase)
- Física mejorada para figuras 3D
- Constantes SHAPE separadas de ROTOBALL
- SHAPE_SPRING_K=800 (+167% rigidez vs ROTOBALL)
- SHAPE_DAMPING_NEAR=150 (+88% absorción)
- Pelotas mucho más "pegadas" durante rotaciones
- applyRotoBallForce() acepta parámetros personalizados
Archivos:
- NEW: source/shapes/png_shape.{h,cpp}
- NEW: data/shapes/jailgames.png
- NEW: data/balls/{normal,small,tiny}.png
- MOD: defines.h (constantes PNG + SHAPE physics)
- MOD: engine.cpp (carga dinámica texturas + física SHAPE)
- MOD: ball.{h,cpp} (parámetros física configurables)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -32,6 +32,7 @@
|
||||
#include "shapes/cylinder_shape.h" // for CylinderShape
|
||||
#include "shapes/icosahedron_shape.h" // for IcosahedronShape
|
||||
#include "shapes/atom_shape.h" // for AtomShape
|
||||
#include "shapes/png_shape.h" // for PNGShape
|
||||
|
||||
// Función auxiliar para obtener la ruta del directorio del ejecutable
|
||||
std::string getExecutableDirectory() {
|
||||
@@ -80,18 +81,54 @@ bool Engine::initialize() {
|
||||
|
||||
// Inicializar otros componentes si SDL se inicializó correctamente
|
||||
if (success) {
|
||||
// Cargar todas las texturas disponibles
|
||||
// Cargar todas las texturas disponibles desde data/balls/
|
||||
std::string exe_dir = getExecutableDirectory();
|
||||
std::string balls_dir = exe_dir + "/data/balls";
|
||||
|
||||
// Textura 0: ball.png (10x10)
|
||||
std::string texture_path_normal = exe_dir + "/data/ball.png";
|
||||
textures_.push_back(std::make_shared<Texture>(renderer_, texture_path_normal));
|
||||
// Buscar todas las texturas PNG en data/balls/
|
||||
namespace fs = std::filesystem;
|
||||
if (fs::exists(balls_dir) && fs::is_directory(balls_dir)) {
|
||||
std::vector<std::pair<std::string, std::string>> texture_files; // (nombre, path)
|
||||
|
||||
// Textura 1: ball_small.png (6x6)
|
||||
std::string texture_path_small = exe_dir + "/data/ball_small.png";
|
||||
textures_.push_back(std::make_shared<Texture>(renderer_, texture_path_small));
|
||||
for (const auto& entry : fs::directory_iterator(balls_dir)) {
|
||||
if (entry.is_regular_file() && entry.path().extension() == ".png") {
|
||||
std::string filename = entry.path().stem().string(); // Sin extensión
|
||||
std::string fullpath = entry.path().string();
|
||||
texture_files.push_back({filename, fullpath});
|
||||
}
|
||||
}
|
||||
|
||||
// Establecer textura inicial (índice 0)
|
||||
// Ordenar alfabéticamente (normal.png será primero)
|
||||
std::sort(texture_files.begin(), texture_files.end());
|
||||
|
||||
// Cargar texturas en orden (con normal.png primero si existe)
|
||||
int normal_index = -1;
|
||||
for (size_t i = 0; i < texture_files.size(); i++) {
|
||||
if (texture_files[i].first == "normal") {
|
||||
normal_index = static_cast<int>(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Poner normal.png primero
|
||||
if (normal_index > 0) {
|
||||
std::swap(texture_files[0], texture_files[normal_index]);
|
||||
}
|
||||
|
||||
// Cargar todas las texturas
|
||||
for (const auto& [name, path] : texture_files) {
|
||||
textures_.push_back(std::make_shared<Texture>(renderer_, path));
|
||||
texture_names_.push_back(name);
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback si no hay texturas (no debería pasar)
|
||||
if (textures_.empty()) {
|
||||
std::cerr << "ERROR: No se encontraron texturas en data/balls/" << std::endl;
|
||||
success = false;
|
||||
}
|
||||
|
||||
// Establecer textura inicial (índice 0 = normal.png)
|
||||
current_texture_index_ = 0;
|
||||
texture_ = textures_[current_texture_index_];
|
||||
current_ball_size_ = texture_->getWidth(); // Obtener tamaño dinámicamente
|
||||
@@ -315,6 +352,10 @@ void Engine::handleEvents() {
|
||||
activateShape(ShapeType::ATOM);
|
||||
break;
|
||||
|
||||
case SDLK_O:
|
||||
activateShape(ShapeType::PNG_SHAPE);
|
||||
break;
|
||||
|
||||
// Ciclar temas de color (movido de T a B)
|
||||
case SDLK_B:
|
||||
// Ciclar al siguiente tema con transición suave (LERP)
|
||||
@@ -1288,8 +1329,8 @@ void Engine::performDemoAction(bool is_lite) {
|
||||
if (random_value < accumulated_weight) {
|
||||
ShapeType shapes[] = {ShapeType::SPHERE, ShapeType::WAVE_GRID, ShapeType::HELIX,
|
||||
ShapeType::TORUS, ShapeType::CUBE, ShapeType::CYLINDER,
|
||||
ShapeType::ICOSAHEDRON, ShapeType::ATOM};
|
||||
int shape_index = rand() % 8;
|
||||
ShapeType::ICOSAHEDRON, ShapeType::ATOM, ShapeType::PNG_SHAPE};
|
||||
int shape_index = rand() % 9;
|
||||
activateShape(shapes[shape_index]);
|
||||
return;
|
||||
}
|
||||
@@ -1336,8 +1377,8 @@ void Engine::performDemoAction(bool is_lite) {
|
||||
if (random_value < accumulated_weight) {
|
||||
ShapeType shapes[] = {ShapeType::SPHERE, ShapeType::WAVE_GRID, ShapeType::HELIX,
|
||||
ShapeType::TORUS, ShapeType::CUBE, ShapeType::CYLINDER,
|
||||
ShapeType::ICOSAHEDRON, ShapeType::ATOM};
|
||||
int shape_index = rand() % 8;
|
||||
ShapeType::ICOSAHEDRON, ShapeType::ATOM, ShapeType::PNG_SHAPE};
|
||||
int shape_index = rand() % 9;
|
||||
activateShape(shapes[shape_index]);
|
||||
return;
|
||||
}
|
||||
@@ -1430,7 +1471,7 @@ void Engine::randomizeOnDemoStart(bool is_lite) {
|
||||
toggleShapeMode(false); // Salir a física sin forzar gravedad
|
||||
}
|
||||
} else {
|
||||
// Modo figura: elegir figura aleatoria
|
||||
// Modo figura: elegir figura aleatoria (excluir PNG_SHAPE - es logo especial)
|
||||
ShapeType shapes[] = {ShapeType::SPHERE, ShapeType::WAVE_GRID, ShapeType::HELIX,
|
||||
ShapeType::TORUS, ShapeType::CUBE, ShapeType::CYLINDER,
|
||||
ShapeType::ICOSAHEDRON, ShapeType::ATOM};
|
||||
@@ -1468,7 +1509,7 @@ void Engine::randomizeOnDemoStart(bool is_lite) {
|
||||
toggleShapeMode(false);
|
||||
}
|
||||
} else {
|
||||
// Modo figura: elegir figura aleatoria
|
||||
// Modo figura: elegir figura aleatoria (excluir PNG_SHAPE - es logo especial)
|
||||
ShapeType shapes[] = {ShapeType::SPHERE, ShapeType::WAVE_GRID, ShapeType::HELIX,
|
||||
ShapeType::TORUS, ShapeType::CUBE, ShapeType::CYLINDER,
|
||||
ShapeType::ICOSAHEDRON, ShapeType::ATOM};
|
||||
@@ -1576,7 +1617,10 @@ void Engine::switchTexture() {
|
||||
|
||||
// Mostrar texto informativo (solo si NO estamos en modo demo)
|
||||
if (!demo_mode_enabled_ && !demo_lite_enabled_) {
|
||||
std::string texture_name = (current_texture_index_ == 0) ? "NORMAL" : "SMALL";
|
||||
// Obtener nombre de textura (uppercase)
|
||||
std::string texture_name = texture_names_[current_texture_index_];
|
||||
std::transform(texture_name.begin(), texture_name.end(), texture_name.begin(), ::toupper);
|
||||
|
||||
text_ = "SPRITE: " + texture_name;
|
||||
text_pos_ = (current_screen_width_ - static_cast<int>(text_.length() * 8)) / 2;
|
||||
show_text_ = true;
|
||||
@@ -1653,6 +1697,9 @@ void Engine::activateShape(ShapeType type) {
|
||||
case ShapeType::ATOM:
|
||||
active_shape_ = std::make_unique<AtomShape>();
|
||||
break;
|
||||
case ShapeType::PNG_SHAPE:
|
||||
active_shape_ = std::make_unique<PNGShape>("data/shapes/jailgames.png");
|
||||
break;
|
||||
default:
|
||||
active_shape_ = std::make_unique<SphereShape>(); // Fallback
|
||||
break;
|
||||
@@ -1714,9 +1761,11 @@ void Engine::updateShape() {
|
||||
float target_y = center_y + y_3d;
|
||||
|
||||
// Aplicar fuerza de atracción física hacia el punto rotado
|
||||
// Pasar el tamaño de la figura para escalar fuerzas
|
||||
// Usar constantes SHAPE (mayor pegajosidad que ROTOBALL)
|
||||
float shape_size = scale_factor * 80.0f; // 80px = radio base
|
||||
balls_[i]->applyRotoBallForce(target_x, target_y, shape_size, delta_time_);
|
||||
balls_[i]->applyRotoBallForce(target_x, target_y, shape_size, delta_time_,
|
||||
SHAPE_SPRING_K, SHAPE_DAMPING_BASE, SHAPE_DAMPING_NEAR,
|
||||
SHAPE_NEAR_THRESHOLD, SHAPE_MAX_FORCE);
|
||||
|
||||
// Calcular brillo según profundidad Z para renderizado
|
||||
// Normalizar Z al rango de la figura (asumiendo simetría ±shape_size)
|
||||
|
||||
Reference in New Issue
Block a user