12.2 Boîtes de dialogues
Il est maintenant temps d'afficher le texte dans le dialogue. Afin de pouvoir réutiliser le dialogue, nous aurons recours à la classe MessageDialog
.
Étapes à suivre
- modifiez la classe
Dialog
afin de supporter le texte pour les messages.- Creez la classe
MessageDialog
MessageDialog
permet au joueur de terminer le dialogue lorsque le texte à terminé d'être affiche.- Utilisez la méthode
nextText
afin d'afficher le prochain texte du message.- incorporez l'affichage de la fenêtre de dialogue dans la classe
HUD
/* Dialog.java */
package com.tutorialquest.ui.dialogs;
// import ..
public class Dialog {
// ...
public static final float ICON_SIZE = 16;
private static final int FONT_SCALE = 1;
// ...
// AJOUT:
// Ajout des variables pour la gestion du texte
protected String visibleText = "";
protected int currentTextIndex = 0;
protected String currentText;
protected String[] texts;
protected Texture arrowTexture;
protected BitmapFont font;
protected final static float SPEED = 10;
protected final static float FAST_FORWARD = 2.5f;
protected float currentTextProgress = 0;
protected int currentTextLength = 0;
protected boolean finished = false;
protected boolean enabled = false;
public void reset(String text)
{
finished = false;
currentTextProgress = 0;
visibleText = "";
currentText = new String(text);
currentTextLength = currentText.length();
}
public Dialog()
{
// ...
// AJOUT:
// Chargement de la texture pour l'icône
arrowTexture = new Texture("ui/arrow_down.png");
// AJOUT: Chargement de la police de caractères
font = new BitmapFont(Gdx.files.internal("fonts/player2_small.fnt"));
font.setColor(Color.DARK_GRAY);
font.getData().setScale(FONT_SCALE);
}
public void update(float deltaTime)
{
if(!enabled) return;
if(
currentText == null ||
currentText.equals(""))
return;
// AJOUT:
// Obtenir le texte à afficher de manière progressive
currentTextProgress +=
deltaTime * (Game.hud.input.isInteractPressed() ?
SPEED * FAST_FORWARD :
SPEED);
// Borne pour ne pas dépasser la taille du texte
currentTextProgress =
currentTextProgress < currentTextLength ?
currentTextProgress :
currentTextLength;
visibleText = currentText.substring(
0,
(int) currentTextProgress);
}
public void render(
SpriteBatch spriteBatch,
Vector2 position)
{
if(!enabled) return;
// ...
// AJOUT:
// Affichage du text visible
spriteBatch.begin();
font.draw(
spriteBatch,
visibleText,
position.x + (MARGIN) * 1,
position.y + (HEIGHT - MARGIN) * 1,
(WIDTH - MARGIN * 2) * 1,
Align.left,
true);
spriteBatch.flush();
spriteBatch.end();
}
}
/* MessageDialog.java */
package com.tutorialquest.ui.dialogs;
// import ..
public class MessageDialog extends Dialog {
public MessageDialog() {
super();
}
public void open(String[] texts)
{
if(texts.length == 0) return;
open();
this.texts = texts;
currentTextIndex = 0;
reset(texts[0]);
}
public void nextText()
{
currentTextIndex++;
if(currentTextIndex > texts.length - 1)
{
currentTextIndex = 0;
close();
return;
}
reset(texts[currentTextIndex]);
}
@Override
public void update(float deltaTime)
{
if(!enabled) return;
super.update(deltaTime);
if(currentTextProgress >= currentTextLength - 1)
{
finished = true;
currentTextProgress = currentTextLength -1;
if(Game.hud.input.isInteractJustPressed()) nextText();
return;
}
}
@Override
public void render(
SpriteBatch spriteBatch,
Vector2 position)
{
if(!enabled) return;
super.render(spriteBatch, position);
// Draw arrow icône
if(finished) {
spriteBatch.begin();
spriteBatch.draw(
arrowTexture,
position.x + (WIDTH - MARGIN - ICON_SIZE),
position.y + MARGIN,
ICON_SIZE,
ICON_SIZE);
spriteBatch.end();
}
}
}
/* HUD.java */
package com.tutorialquest.ui;
// import ..
public class HUD {
// AJOUT:
public MessageDialog messageDialog = new MessageDialog();
public void update(float deltaTime) {
// ...
// AJOUT:
messageDialog.update(deltaTime);
}
public void render() {
// ...
// AJOUT:
position = new Vector2(
camera.position.x + -Dialog.WIDTH/2,
camera.position.y + -Dialog.HEIGHT * 1.5f );
messageDialog.render(
spriteBatch,
position);
}
}
Maintenant que nous avons le mécanisme de fenêtre prêt-à être utilisé, nous pouvons ajouter les points d'interactions à notre monde dans lesquels nous voulons ajoutez du texte. L'exemple canonique d'éléments interactif dans les jeux vidéos de rôles est les personnages non jouables souvent appelés NPC (Non-player character).
Étapes à suivre
- ajoutez l'interface
IInteractible.java
- Cette interface définit un point d'interaction dans le monde
- ajoutez la classe
NPC
qui implémenteIInteractible
- Chaque
NPC
que l'on définit contient du texte qui lui est propre et permet d'ouvrir une fenêtre de dialogue lorsqu'on interagit avec lui.
/* NPC.java */
package com.tutorialquest.entities;
// import ..
public class NPC extends Character implements IInteractible{
private int WIDTH = 32;
private int HEIGHT = 32;
private String[] texts;
private Sprite sprite;
public NPC(Vector2 position, int type, String[] texts)
{
super(position, -1, -1, -1, -1, -1);
this.sprite = new Sprite("objects/npc_spritesheet.png", WIDTH, HEIGHT, type, 0);
this.sprite.origin = new Vector2(WIDTH/2, 0);
this.collider = new Collider(
Collider.DEFAULT_SIZE,
Collider.FLAG_INTERACTIBLE | Collider.FLAG_COLLIDABLE);
this.collider.origin.set(Collider.DEFAULT_SIZE.x/2, 0);
this.texts = texts;
}
@Override
public void render(SpriteBatch spriteBatch) {
super.render(spriteBatch);
sprite.render(spriteBatch, position);
}
@Override
public void update(float deltaTime) {
super.update(deltaTime);
sprite.update(deltaTime);
collider.update(position);
}
@Override
public void interact(Avatar avatar) {
Game.hud.messageDialog.open(texts);
}
}
À l'intérieur de levels/overworld.tmx
, chaque NPC
définit des champs de texte dans ses propriétés. Nous utiliserons le préfix Text
à l'avant de tout les propriétés qui serviront de dialogue pour le personnage.
Étapes à suivre
- ajoutez le code nécessaire à la création de NPC dans le monde.
- considérez chaque propriété avec le préfixe
Text
comme texte pour le dialogue du personnage.- ajoutez la méthode
interact
à l'avatar lui permettant d'interagir avec certains objets de son environnement.
/* Level.java */
package com.tutorialquest;
// import ..
public class Level {
// AJOUT:
public static final String OBJECT_PROP_TEXT = "Text";
// AJOUT:
public static final String OBJECT_NPC = "NPC";
public static final String OBJECT_PROP_NPC_TYPE = "NPCType";
// MODIF:
public load(int transitionID, Avatar avatar) {
// ...
for (MapLayer layer : tiledMap.getLayers()) {
switch (layer.getName()) {
// ...
case LAYER_OBJECT:
for (int i = 0; i < layer.getObjects().getCount(); i++) {
// ...
switch (object.getProperties().get(OBJECT_PROP_TYPE, String.class)) {
// AJOUT:
case OBJECT_NPC: {
// rassemble les valeurs tout les propriétés avec le préfixe 'Text'
LinkedList<String> texts = new LinkedList<>();
object.getProperties()
.getKeys()
.forEachRemaining(
x -> texts.add(
x.startsWith(OBJECT_PROP_TEXT) ?
object.getProperties().get(x, String.class) :
null));
texts.removeIf(x -> x == null);
// Création du NPC
add(new NPC(new Vector2(
object.getProperties().get(OBJECT_PROP_X, float.class),
object.getProperties().get(OBJECT_PROP_Y, float.class)),
object.getProperties().get(OBJECT_PROP_NPC_TYPE, int.class),
texts.toArray(new String[texts.size()])));
}
break;
}
}
break;
}
}
}
}
/* Avatar.java */
package com.tutorialquest.entities;
// import ..
public class Avatar extends Character {
// ...
// AJOUT:
private static final float INTERACTION_RANGE = 8f;
@Override
public void update(float deltaTime) {
// ...
// AJOUT:
interact();
// ...
}
// AJOUT:
public void interact() {
// Lorsque le joueur appui sur le bouton d'interaction
// nous vérifions à l'avant du personnage s'il y à un objet
// avec lequel interagir.
if (
input.isInteractJustPressed()) {
Vector2 interactionOffset =
new Vector2(direction)
.scl(INTERACTION_RANGE);
List<PhysicalObject> results = new LinkedList<>();
collider.getObjectCollisions(
this,
interactionOffset.x,
interactionOffset.y,
Collider.FLAG_INTERACTIBLE,
results);
// Interaction avec chaque résultat de la collision
for (PhysicalObject interactible : results) {
((IInteractible) interactible).interact(this);
}
}
}
}