La inyección de dependencia en Mini2DX nos ofrece una forma elegante y potente de organizar cómo se conectan las distintas clases en nuestro juego, especialmente cuando queremos manejar diferentes procesadores de entrada según la plataforma en la que estemos trabajando. Esto es especialmente útil para juegos multiplataforma donde el control en escritorio puede ser muy distinto al de Android.
Para entenderlo mejor, recordemos que en Mini2DX la clase BasicGame implementa la interfaz InputProcessor, que es la encargada de manejar los eventos de entrada, como pulsaciones de teclas o toques en pantalla. Cuando usamos el método GDXInput.setInputProcessor, le indicamos a LiveGDX que notifique a esa clase cada vez que ocurra un evento de entrada, ejecutando métodos como keyDown, keyUp o touchDown.
Pero no estamos limitados a usar solo esa clase para procesar la entrada. Podemos crear nuestros propios InputProcessor personalizados. Por ejemplo, podríamos hacer uno que imprima en consola la tecla que se ha pulsado, usando algo como:
public class InputHandlerH implements InputProcessor {
@Override
public boolean keyDown(int keycode) {
System.out.println("He pulsado la tecla " + Input.Keys.toString(keycode));
return true;
}
// Implementar otros métodos con retornos por defecto o vacíos
}
Luego, simplemente asignamos este procesador con:
InputHandlerH h = new InputHandlerH();
GDXInput.setInputProcessor(h);
Así, cada evento de entrada será manejado por nuestro InputHandlerH.
Ahora bien, imaginemos que queremos tener dos procesadores distintos: uno para escritorio y otro para Android. Por ejemplo, en escritorio manejamos eventos de teclado, y en Android eventos táctiles. Podríamos crear dos clases, DesktopInputHandler y AndroidInputHandler, cada una con su lógica específica:
public class DesktopInputHandler implements InputProcessor {
@Override
public boolean keyDown(int keycode) {
System.out.println("Tecla pulsada en escritorio: " + Input.Keys.toString(keycode));
return true;
}
// Otros métodos implementados según necesidad
}
public class AndroidInputHandler implements InputProcessor {
@Override
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
System.out.println("Pantalla tocada en Android");
return true;
}
// Otros métodos implementados según necesidad
}
El reto es cómo conectar automáticamente el procesador adecuado según la plataforma sin complicarnos con comprobaciones manuales o configuraciones engorrosas. Aquí es donde entra la inyección de dependencia de Mini2DX.
Para aprovecharla, primero movemos AndroidInputHandler al proyecto Android y DesktopInputHandler al proyecto de escritorio, manteniendo el código organizado y específico para cada plataforma. Esto hace que el proyecto principal quede agnóstico y no conozca directamente estas clases, lo que rompe la conexión tradicional.
Mini2DX resuelve esto usando anotaciones y reflexión para detectar y conectar las clases adecuadas en tiempo de ejecución. Para ello, marcamos ambas clases con la anotación @Prototype, indicando que son prototipos que pueden ser inyectados:
@Prototype
public class DesktopInputHandler implements InputProcessor {
// implementación
}
@Prototype
public class AndroidInputHandler implements InputProcessor {
// implementación
}
Luego, creamos una clase llamada InputRetriever que será la encargada de obtener el InputProcessor correcto. Esta clase tendrá un campo handler anotado con @Autowired, para que Mini2DX inyecte automáticamente la instancia adecuada según la plataforma:
@Singleton
public class InputRetriever {
@Autowired
private InputProcessor handler;
public InputProcessor getHandler() {
return handler;
}
}
Aquí, @Singleton indica que InputRetriever será una única instancia durante la ejecución, mientras que @Autowired conecta el campo handler con el InputProcessor correspondiente, ya sea el de escritorio o el de Android.
Para que Mini2DX reconozca estas anotaciones y realice la inyección, debemos llamar al método scan del sistema de inyección de dependencias, pasando el paquete raíz donde están nuestras clases:
try {
MDX.di.scan("com.miproyecto");
} catch (Exception e) {
e.printStackTrace();
}
Este método busca recursivamente todas las clases anotadas y prepara la inyección.
Finalmente, para obtener el InputProcessor inyectado, no creamos una instancia nueva de InputRetriever directamente, sino que la solicitamos al contenedor de dependencias de Mini2DX:
InputRetriever retriever = MDX.di.getBean(InputRetriever.class);
GDXInput.setInputProcessor(retriever.getHandler());
De esta forma, retriever.getHandler() nos devuelve el procesador adecuado según la plataforma en la que estemos ejecutando el juego, sin que tengamos que preocuparnos por hacer comprobaciones manuales.
Este sistema puede parecer un poco mágico al principio, pero una vez configurado funciona de manera muy limpia y eficiente, permitiéndonos mantener el código organizado y adaptado a cada plataforma sin complicaciones.
Así, Mini2DX nos facilita la gestión de entradas multiplataforma mediante la inyección de dependencia, usando anotaciones como @Prototype, @Singleton y @Autowired, y un sencillo método scan para activar el sistema. Esto nos permite centrarnos en la lógica de nuestro juego sin preocuparnos por la complejidad de conectar las clases adecuadas en cada entorno.