Gdx2d est une librairie graphique 2D qui permet de créer facilement des jeux pour différentes plateformes, comme Windows, Linux ou Android. Cette librairie Java propose un grand nombre de fonctionnalités qui facilitent le développement de jeux. Les différents composants disponibles dans cette librairie sont présentés brièvement dans ce document.
Voici quelques exemples d’applications développées à l’aide de Gdx2d. Différentes applications de démonstration (ainsi que le code correspondant) sont disponibles dans l’application DemoSelector.
Les applications développées pour Gdx2d doivent hériter de la classe PortableApplication
. Le code ci-dessous présente une application de démonstration basique :
package hevs.gdx2d.demos.simple;
import com.badlogic.gdx.Gdx;
import hevs.gdx2d.lib.GdxGraphics;
import hevs.gdx2d.desktop.PortableApplication;
import com.badlogic.gdx.graphics.Color;
/**
* A very simple demonstration on how to display something animated with the
* library
*
* @author Pierre-André Mudry (mui)
* @version 1.0
*/
public class DemoSimpleAnimation extends PortableApplication {
int radius = 5, speed = 1;
int screenHeight, screenWidth;
public DemoSimpleAnimation(boolean onAndroid) {
super(onAndroid);
}
@Override
public void onInit() {
// Sets the window title
setTitle("Simple demo, mui 2013");
screenHeight = Gdx.graphics.getHeight();
screenWidth = Gdx.graphics.getWidth();
}
@Override
public void onGraphicRender(GdxGraphics g) {
// Clears the screen
g.clear();
g.drawAntiAliasedCircle(screenWidth / 2, screenHeight / 2, radius, Color.BLUE);
// If reaching max or min size, invert the growing direction
if (radius >= 100 || radius <= 3) {
speed *= -1;
}
// Modify the radius
radius += speed;
g.drawSchoolLogo();
}
public static void main(String[] args) {
/**
* Note that the constructor parameter is used to determine if running
* on Android or not. As we are in main there, it means we are on
* desktop computer.
*/
new DemoSimpleAnimation(false);
}
}
import com.badlogic.gdx.Gdx
import com.badlogic.gdx.graphics.Color
import ch.hevs.gdx2d.lib.GdxGraphics
import ch.hevs.gdx2d.desktop.PortableApplication
/**
* A very simple demonstration on how to display something animated with the library
*
* @author Pierre-André Mudry (mui)
* @author Steve Devènes (dst)
* @version 1.0
*/
class DemoSimpleAnimation() extends PortableApplication {
var radius = 5
var speed = 1
var screenHeight, screenWidth = 0
override def onInit(): Unit = {
// Sets the window title
setTitle("Simple demo, mui 2013")
screenHeight = Gdx.graphics.getHeight
screenWidth = Gdx.graphics.getWidth
}
override def onGraphicRender(g: GdxGraphics): Unit = {
// Clears the screen
g.clear()
g.drawAntiAliasedCircle(screenWidth / 2, screenHeight / 2, radius, Color.BLUE)
// If reaching max or min size, invert the growing direction
if (radius >= 100 || radius <= 3) {
speed *= -1
}
// Modify the radius
radius += speed
g.drawSchoolLogo()
}
}
object DemoSimpleAnimation extends App{
new DemoSimpleAnimation()
}
Le code ci-dessus correspond à l’application suivante :
Les applications Gdx2d (classe PortableApplication
) ne disposent pas uniquement d’une fonction main
comme les programmes Java standards. Des méthodes spécifiques sont appelées lorsque l’application est initialisée ou lorsqu’elle doit être mise à jour. Elles sont résumées à l’aide du diagramme suivant et détaillées dans le tableau ci-dessous.
Le cycle de vie d’une application Gdx2d diffère légèrement entre une exécution sur Pc et sur Android. Les méthodes de l’interface GameInterface
sont détaillées ci-dessous :
Méthode | Description |
---|---|
onInit |
Méthode appelée lors de l’initialisation d’application (appelée une seule fois au démarrage). Elle permet d’initialiser les objets de l’application ainsi que charger les ressources (images, sons, etc.). Les ressources doivent être chargées une seule fois au démarrage et non pas périodiquement dans la fonction onGraphicRender . |
onGraphicRender |
Cette méthode est appelée périodiquement, lorsque le rendu graphique de l’application doit être mis à jour (60 fois par seconde). L’objet GdxGraphics passé en paramètre de cette méthode permet de réaliser toutes les opérations graphiques (dessin de formes, d’images, de textes, etc.). |
onGameLogicUpdate |
Appelée lorsque la physique/logique du jeu est mise à jour. |
onPause |
Sur PC, cette méthode est appelée immédiatement avant onDispose , lorsque l’on quitte l’application. Sur Android, cette méthode est appelée lorsque une application prioritaire prend la main (exemple : un appel téléphonique qui met en pause l’application) ou lorsque l’on appuie sur le bouton “Home”. |
onResume |
Méthode appelée uniquement sur Android lorsque l’application revient au premier plan (après une interruption). |
onDispose |
Méthode appelée lorsque l’application est détruite. |
Afin d’interagir avec les applications Gdx2d, différentes interfaces sont à disposition. Elles permettent par exemple de détecter les clics de souris, les pressions de touches du clavier ou encore d’utiliser l’écran tactile sur Android. Toutes les applications Gdx2d disposent des méthodes suivantes, qui peuvent être surchargées selon les besoins de chaque application.
La méthode onGraphicRender est appelée périodiquement (60 fois par seconde) afin de mettre à jour le rendu (le dessin) de l’application. La scène doit d’abord être effacée, puis les objets qui composent l’application doivent être dessinés les uns après les autres, en sachant que les objets dessinés en dernier peuvent recouvrir les objets précédemment dessinés.
@Override
public void onGraphicRender(GdxGraphics g) {
g.clear(Color.BLACK);
g.setColor(Color.RED);
// TODO: add all drawing stuff here...
g.drawSchoolLogo();
g.drawFPS();
}
override def onGraphicRender(g: GdxGraphics): Unit = {
g.clear(Color.BLACK)
g.setColor(Color.RED)
// TODO: add all drawing stuff here...
g.drawSchoolLogo()
g.drawFPS()
}
La classe GdxGraphics
met à disposition un nombre varié de méthodes qui peuvent être utilisées pour afficher des images, dessiner des formes, afficher du texte, etc. La Javadoc de la classe GdxGraphics
contient une description détaillée des méthodes disponibles.
Les classes BitmapImage
, FileHandle
, MusicPlayer
et Spritesheet
gèrent les ressources de la même manière. Pour être utilisées dans votre application, elles doivent être placées dans le dossier “data/”. Vous pouvez les classer selon vos besoin dans ce dossier.
BitmapImage imgBitmap;
@Override
public void onInit() {
imgBitmap = new BitmapImage("data/images/compass_150.png");
}
@Override
public void onGraphicRender(GdxGraphics g) {
g.clear();
g.drawPicture(getWindowWidth()/2, getWindowHeight()/2, imgBitmap);
}
var imgBitmap: BitmapImage = _
override def onInit(): Unit = {
imgBitmap = new BitmapImage("data/images/hei-pi.png")
}
override def onGraphicRender(g: GdxGraphics): Unit = {
g.clear()
g.drawPicture(getWindowWidth() / 2, getWindowHeight() / 2, imgBitmap)
}
BitmapFont optimus40;
@Override
public void onInit() {
FileHandle optimusF = Gdx.files.internal("data/font/OptimusPrinceps.ttf");
FreeTypeFontGenerator generator = new FreeTypeFontGenerator(optimusF);
optimus40 = generator.generateFont(40);
optimus40.setColor(Color.BLUE);
optimus60 = generator.generateFont(60);
generator.dispose();
}
@Override
public void onGraphicRender(GdxGraphics g) {
g.clear();
g.drawStringCentered(50, "Optimus size 40", optimus40);
}
@Override
public void onDispose() {
optimus40.dispose();
}
var optimus40: BitmapFont = _
var optimus60: BitmapFont = _
override def onInit(): Unit = {
val optimusF: FileHandle = Gdx.files.internal("data/font/OptimusPrinceps.ttf")
val generator: FreeTypeFontGenerator = new FreeTypeFontGenerator(optimusF)
val parameter: FreeTypeFontParameter = new FreeTypeFontParameter()
parameter.size = 40
optimus40 = generator.generateFont(parameter)
optimus40.setColor(Color.BLUE)
parameter.size = 60
optimus60 = generator.generateFont(parameter)
generator.dispose()
}
override def onGraphicRender(g: GdxGraphics): Unit = {
g.clear()
g.drawStringCentered(50, "Optimus size 40", optimus40)
g.drawStringCentered(200, "Optimus size 60", optimus60)
}
override def onDispose(): Unit = {
optimus40.dispose()
optimus60.dispose()
}
MusicPlayer player;
@Override
public void onInit() {
player = new MusicPlayer("data/music/Blues-Loop.mp3");
}
@Override
public void onClick(int x, int y, int button) {
if (player.isPlaying())
player.stop();
else
player.loop();
}
var player: MusicPlayer = _
override def onInit(): Unit = {
player = new MusicPlayer("data/music/Blues-Loop.mp3")
}
override def onClick(x: Int, y: Int, button: Int): Unit = {
if (player.isPlaying)
player.stop()
else
player.loop()
}
Spritesheet sprites;
@Override
public void onInit() {
sprites = new Spritesheet("data/images/lumberjack_sheet.png", 64, 64);
}
@Override
public void onGraphicRender(GdxGraphics g) {
g.clear();
g.spriteBatch.draw(sprites.sprites[1][1], 0, 0); // Draw a region
}
var sprites: Spritesheet = _
override def onInit(): Unit = {
sprites = new Spritesheet("data/images/lumberjack_sheet.png", 64, 64)
}
override def onGraphicRender(g: GdxGraphics): Unit = {
g.clear()
g.draw(sprites.sprites(1)(1), 0, 0)
g.draw(sprites.sprites(2)(1), 64, 0)
}
Voici un exemple de texture qui peut être utilisé comme “spritesheet”. La même image contient différentes régions de textures qui peuvent être sélectionnées lors du dessin.
L’exemple ci-dessous permet de capturer certains événements du clavier (KeyboardInterface
) et de la souris (TouchInterface
) :
Vector2 mouse = new Vector2();
boolean isMousPressed = false ;
@Override
public void onClick(int x, int y, int button) {
super.onClick(x, y, button);
if (button == Input.Buttons.RIGHT) {
Logger.log("Right button clicked");
isMousPressed = true;
} else
Logger.log("Left button clicked");
mouse.x = x;
mouse.y = y;
}
@Override
public void onDrag(int x, int y) {
super.onDrag(x, y);
mouse.x = x;
mouse.y = y;
}
@Override
public void onRelease(int x, int y, int button) {
super.onRelease(x, y, button);
mouse.x = x;
mouse.y = y;
if (button == Input.Buttons.RIGHT) {
isMousPressed = false;
Logger.log("Right button released");
}
}
@Override
public void onKeyDown(int keycode) {
super.onKeyDown(keycode);
switch (keycode) {
case Input.Keys.DOWN:
Logger.log("Down key pressed");
break;
case Input.Keys.UP:
Logger.log("Up key pressed");
break;
default:
Logger.log(String.format("Key '%d' pressed", keycode));
break;
}
}
@Override
public void onKeyUp(int keycode) {
Logger.log(String.format("Key '%d' released", keycode));
}
import com.badlogic.gdx.math.Vector2
import com.badlogic.gdx.Input
import ch.hevs.gdx2d.lib.utils.Logger
...
var mouse: Vector2 = new Vector2()
var isMousePressed: Boolean = false
override def onInit(): Unit = {}
override def onGraphicRender(g: GdxGraphics): Unit = {
g.clear()
}
override def onClick(x: Int, y: Int, button: Int): Unit = {
super.onClick(x, y, button)
if (button == Input.Buttons.RIGHT) {
Logger.log("Right button clicked")
isMousePressed = true
}
else
Logger.log("Left button clicked")
mouse.set(x, y)
}
override def onDrag(x: Int, y: Int): Unit = {
super.onDrag(x, y)
mouse.set(x, y)
}
override def onRelease(x: Int, y: Int, button: Int): Unit = {
super.onRelease(x, y, button)
mouse.set(x, y)
if (button == Input.Buttons.RIGHT) {
isMousePressed = false
Logger.log("Right button released")
}
}
override def onKeyDown(keycode: Int): Unit = {
super.onKeyDown(keycode)
keycode match {
case Input.Keys.DOWN => Logger.log("Down key pressed")
case Input.Keys.UP => Logger.log("Up key pressed")
case _ => Logger.log(s"Key '$keycode' pressed")
}
}
override def onKeyUp(keycode: Int): Unit = {
Logger.log(s"Key '$keycode' released")
}