Skip to content

theligero/TowerDefense

Repository files navigation

Tower Defense Multiplataforma

Proyecto de práctica de Videojuegos para Dispositivos Móviles: un Tower Defense desarrollado en Java con arquitectura separada por módulos para poder ejecutar el mismo GameLogic tanto en Desktop/PC como en Android.

Este README está pensado como guía rápida de repaso: dónde está cada parte, qué mejoras se han añadido y qué archivos tocar si en un examen práctico se pide ampliar el juego.


Estructura del proyecto

TowerDefense/
├── Engine/            # Interfaces comunes del motor
├── DesktopEngine/     # Implementación del motor para PC
├── AndroidEngine/     # Implementación del motor para Android
├── GameLogic/         # Lógica común del juego
├── DesktopGame/       # Main de PC
├── AndroidGame/       # MainActivity de Android
└── data/              # Fuentes, sonidos y sprites

La idea principal es:

DesktopGame / AndroidGame
        ↓
DesktopEngine / AndroidEngine
        ↓
Engine interfaces comunes
        ↓
GameLogic común

GameLogic no debería depender directamente de Android ni de AWT/Swing. Para gráficos, sonido, input o almacenamiento usa las interfaces del módulo Engine.


Flujo general

MainActivity Desktop / MainActivity Android
        ↓
DesktopEngine / AndroidEngine
        ↓
SceneManager
        ↓
IntroScene → Menu → Game → EndScene
              ↓
          Achievements
              ↓
          OptionsMenu

Cambio de escena típico:

((SceneManager) this.engine.getScene()).changeScene(new OtraScene());

Con fade:

startFadeOut(0.5f, () -> {
    ((SceneManager) this.engine.getScene()).changeScene(new OtraScene());
});

Mejoras implementadas

Pantalla de logros

Archivo:

GameLogic/src/main/java/com/example/gamelogic/Scenes/Achievements.java

Muestra:

  • Enemigos eliminados.
  • Oleadas superadas totales.
  • Torres construidas.
  • Récord de modo infinito.

Se accede desde:

GameLogic/src/main/java/com/example/gamelogic/Scenes/IntroScene.java

Estadísticas persistentes

Clase principal:

GameLogic/src/main/java/com/example/gamelogic/Resources/GameStats.java

Archivos persistidos:

enemiesKilled.txt
totalWaves.txt
towersBuilt.txt
bestInfinite.txt
isMuted.txt
bestTime.txt
partida_a_continuar.txt

La lógica usa engine.getStorage(), así que sirve tanto para PC como para Android.


Persistencia multiplataforma

Interfaz común:

Engine/src/main/java/com/vdm_g13/engine/Storage.java

Implementaciones:

DesktopEngine/src/main/java/com/example/desktopengine/DesktopStorage.java
AndroidEngine/src/main/java/com/example/androidengine/AndroidStorage.java

Interfaz:

public interface Storage {
    void save(String filename, String content);
    String load(String filename);
    boolean exists(String filename);
    void delete(String filename);
}

Regla importante:

GameLogic nunca debería usar File, Context, FileOutputStream, etc.
GameLogic debe usar engine.getStorage().

Menú de opciones y mute

Escena:

GameLogic/src/main/java/com/example/gamelogic/Scenes/OptionsMenu.java

Interfaz común:

Engine/src/main/java/com/vdm_g13/engine/Audio.java

Métodos añadidos:

void setMuted(boolean muted);
boolean isMuted();

Implementaciones:

DesktopEngine/src/main/java/com/example/desktopengine/DesktopAudio.java
AndroidEngine/src/main/java/com/example/androidengine/AndroidAudio.java

Idea:

GameLogic pide reproducir sonido.
Audio decide si lo reproduce o lo ignora según el mute.

Continuar partida

Archivo principal:

GameLogic/src/main/java/com/example/gamelogic/Scenes/Menu.java

Comprueba:

this.engine.getStorage().exists("partida_a_continuar.txt");

Formato guardado:

vidas;dinero;oleada;

La partida se reconstruye con:

new Game(8, waves, lives, money);

Modo reto

Botón localizado en:

GameLogic/src/main/java/com/example/gamelogic/Scenes/Menu.java

Lanza:

new Game(5, 120);

Es decir:

  • 5 oleadas.
  • 120 segundos.

Lógica en:

GameLogic/src/main/java/com/example/gamelogic/Scenes/Game.java

Variables:

private boolean hasTime;
private float amountOfTimeLeft;
private TextLogic timeLeftText;

Casillas especiales

Archivo:

GameLogic/src/main/java/com/example/gamelogic/Maps/Grid.java

Se separa:

private int[][] grid;
private int[][] specialGrid;

Constantes:

public static final int NO_SPECIAL = 0;
public static final int SPECIAL_RED = 1;
public static final int SPECIAL_GREEN = 2;

Métodos importantes:

createRandomSpecialCells()
createFixedSpecialCells()
isNextToPath(...)
getSpecialCell(...)
drawSpecialCells(...)

La aplicación del bonus está en:

GameLogic/src/main/java/com/example/gamelogic/Scenes/Game.java

Método:

private void applySpecialCellBonus(TowerBase tower, int specialCell)

Bonuses:

Casilla verde:
  rango x1.5

Casilla roja:
  fuego → daño x1.25
  hielo → ralentización x1.25
  rayo → dos enemigos

Nueva torre de veneno

Archivos:

GameLogic/src/main/java/com/example/gamelogic/Towers/PoisonTower.java
GameLogic/src/main/java/com/example/gamelogic/Towers/Towers.java
GameLogic/src/main/java/com/example/gamelogic/Scenes/Game.java

Para añadir otra torre:

1. Añadir enum en Towers.java.
2. Crear botón en Game.createTowerButtons().
3. Instanciarla en Game.createTower(...).
4. Crear clase hija de TowerBase.

Nuevos enemigos

Archivos:

GameLogic/src/main/java/com/example/gamelogic/Enemies/FastEnemy.java
GameLogic/src/main/java/com/example/gamelogic/Enemies/TankEnemy.java
GameLogic/src/main/java/com/example/gamelogic/Enemies/Wave.java

El spawn se decide en:

private void spawnEnemy()

Mapa de archivos importantes

Qué quiero cambiar Dónde mirar primero
Cambiar de pantalla Scenes/SceneManager.java
Añadir pantalla nueva Scenes/ + heredar de SceneBase
Añadir botón en menú inicial Scenes/IntroScene.java
Añadir modo de juego Scenes/Menu.java + constructor de Game
Gameplay principal Scenes/Game.java
Oleadas Enemies/WaveManager.java
Spawn de enemigos Enemies/Wave.java
Enemigo base Enemies/EnemyBase.java
Nueva torre Towers/Towers.java, Game.java, nueva clase
Bonus de torres Towers/TowerBase.java y torres concretas
Mapa/casillas Maps/Grid.java
Camino de enemigos Maps/Path.java
Estadísticas Resources/GameStats.java
Persistencia Engine/Storage.java, DesktopStorage, AndroidStorage
Audio/mute Engine/Audio.java, DesktopAudio, AndroidAudio

Guía rápida para examen

Pantalla nueva

public class NuevaPantalla extends SceneBase {
    private Button volver;

    @Override
    public void init(Engine engine, int width, int height) {
        super.init(engine, width, height);
        // crear textos, botones, imágenes...
    }

    @Override
    public void update(double deltaTime) {
        super.update(deltaTime);

        if (!fadingIn && !fadingOut && volver.isPressed()) {
            startFadeOut(0.5f, () -> {
                ((SceneManager) engine.getScene()).changeScene(new IntroScene());
            });
        }
    }
}

Botón nuevo

Button boton = new Button(engine.getAudio(), x, y, w, h);
boton.drawCentered();
boton.setText("Texto", font);
entities.add(boton);

En update(...):

if (boton.isPressed()) {
    // acción
}

Guardar/cargar

engine.getStorage().save("archivo.txt", contenido);
String contenido = engine.getStorage().load("archivo.txt");

Cambio de mapa/casilla

Patrón recomendado:

Grid guarda la información de la celda
Game consulta la celda cuando el jugador interactúa
Tower/Enemy recibe el efecto concreto

Nueva torre

Towers.java
Game.createTowerButtons()
Game.createTower(...)
NuevaTorre extends TowerBase

Nuevo enemigo

EnemyBase.java
NuevoEnemigo.java
Wave.java

Funcionalidad a nivel de motor

Tocar:

Engine/
DesktopEngine/
AndroidEngine/

No resolverlo solo dentro de GameLogic.


Nota importante sobre Desktop

Evitar medir texto con Graphics dentro de init() de una escena.

En Desktop, Graphics2D puede no estar activo en mitad de un cambio de escena durante update(). Es más seguro usar posiciones proporcionales o constantes:

new TextLogic("Defense", this.getWidth() / 2, this.getHeight() / 5 + 110, color);

Checklist antes de entregar

[ ] Funciona en Desktop.
[ ] Funciona en Android.
[ ] Entrar y salir de cada escena varias veces no rompe.
[ ] El botón nuevo funciona más de una vez.
[ ] La persistencia carga aunque el archivo no exista.
[ ] La persistencia no machaca valores por error.
[ ] Se han creado los archivos alumno y examen en raíz.
[ ] examen explica qué se ha tocado y dónde.
[ ] Se han borrado carpetas build/.
[ ] Se ha borrado .gradle/.
[ ] El ZIP no pesa innecesariamente.

Chuleta mental

Pantalla nueva        → Scenes/
Botón nuevo           → escena concreta + Button
Cambio de escena      → SceneManager.changeScene(...)
Guardar/cargar        → engine.getStorage()
Stats globales        → GameStats
Mute/audio            → Audio + DesktopAudio + AndroidAudio
Casillas/mapa         → Grid + Game.applySpecialCellBonus(...)
Nueva torre           → Towers enum + Game + TowerBase/hija
Nuevo enemigo         → EnemyBase/hija + Wave
Modo nuevo            → Menu + constructor Game + condición de victoria/derrota

About

Repositorio con modificaciones de la práctica 1 de Videojuegos en Dispositivos Móviles como preparación para el examen de la asignatura.

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages