Skip to content
On this page

⌨Input System - Moteur Graphique R-Type


L'Input System gère toutes les entrées utilisateur (clavier, souris) et les expose via :

  • Actions sémantiques : Booléens persistants (actionPressed, actionJustPressed, actionJustReleased)
  • Axes : Valeurs continues (-1, 0, +1) pour les déplacements
  • Touches directes : Accès brut aux touches individuelles
  • Souris : Position et boutons
  • Rebinding dynamique : Modifier les contrôles à l'exécution
  • Singleton pattern : Accès global facile
  • Abstraction backend : Indépendant du renderer

Architecture

Flux d'Entrée

┌─────────────────────────────────┐
│   Événements SFML/Backend       │
│   (Clavier, Souris, Fenêtre)    │
└────────────┬────────────────────┘

┌────────────▼────────────────────┐
│  IInputBackend                  │
│  (Interface abstraction)        │
└────────────┬────────────────────�┘

┌────────────▼────────────────────┐
│  InputManager (Singleton)       │
│  • Mappings actions             │
│  • État des touches             │
│  • Détection just pressed/rel   │
└────────────┬────────────────────┘

┌────────────▼────────────────────┐
│  Votre Code de Jeu              │
│  if (inputMgr.isActionPressed) │
└─────────────────────────────────┘
┌─────────────────────────────────┐
│   Événements SFML/Backend       │
│   (Clavier, Souris, Fenêtre)    │
└────────────┬────────────────────┘

┌────────────▼────────────────────┐
│  IInputBackend                  │
│  (Interface abstraction)        │
└────────────┬────────────────────�┘

┌────────────▼────────────────────┐
│  InputManager (Singleton)       │
│  • Mappings actions             │
│  • État des touches             │
│  • Détection just pressed/rel   │
└────────────┬────────────────────┘

┌────────────▼────────────────────┐
│  Votre Code de Jeu              │
│  if (inputMgr.isActionPressed) │
└─────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

Composants

InputManager (Singleton)

cpp
class InputManager {
    static InputManager& getInstance();
    
    // Actions (booléens)
    void createAction(const std::string& name);
    void bindKey(const std::string& action, Key key);
    void bindMouseButton(const std::string& action, MouseButton btn);
    
    bool isActionPressed(const std::string& action) const;
    bool isActionJustPressed(const std::string& action) const;
    bool isActionJustReleased(const std::string& action) const;
    
    // Axes (valeurs -1, 0, +1)
    void createAxis(const std::string& name, Key negative, Key positive);
    float getAxis(const std::string& name) const;
    
    // Touches directes
    bool isKeyPressed(Key key) const;
    bool isKeyJustPressed(Key key) const;
    bool isKeyJustReleased(Key key) const;
    bool isMouseButtonPressed(MouseButton btn) const;
    
    // Souris
    void getMousePosition(int& x, int& y) const;
    
    // Cycle de mise à jour
    void update();
    
    // Backend
    void setBackend(IInputBackend* backend);
};
class InputManager {
    static InputManager& getInstance();
    
    // Actions (booléens)
    void createAction(const std::string& name);
    void bindKey(const std::string& action, Key key);
    void bindMouseButton(const std::string& action, MouseButton btn);
    
    bool isActionPressed(const std::string& action) const;
    bool isActionJustPressed(const std::string& action) const;
    bool isActionJustReleased(const std::string& action) const;
    
    // Axes (valeurs -1, 0, +1)
    void createAxis(const std::string& name, Key negative, Key positive);
    float getAxis(const std::string& name) const;
    
    // Touches directes
    bool isKeyPressed(Key key) const;
    bool isKeyJustPressed(Key key) const;
    bool isKeyJustReleased(Key key) const;
    bool isMouseButtonPressed(MouseButton btn) const;
    
    // Souris
    void getMousePosition(int& x, int& y) const;
    
    // Cycle de mise à jour
    void update();
    
    // Backend
    void setBackend(IInputBackend* backend);
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

Énumérations des Touches

cpp
enum class Key {
    A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z,
    Num0, Num1, Num2, Num3, Num4, Num5, Num6, Num7, Num8, Num9,
    Escape, LControl, LShift, LAlt, LSystem, RControl, RShift, RAlt, RSystem,
    Menu, LBracket, RBracket, Semicolon, Comma, Period, Quote, Slash, Backslash,
    Tilde, Equal, Hyphen, Space, Return, Backspace, Tab, PageUp, PageDown,
    End, Home, Insert, Delete, Add, Subtract, Multiply, Divide,
    Left, Right, Up, Down, Numpad0, Numpad1, Numpad2, Numpad3, Numpad4,
    Numpad5, Numpad6, Numpad7, Numpad8, Numpad9,
    F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, F13, F14, F15,
    Pause, ...
};

enum class MouseButton {
    Left,
    Right,
    Middle,
    XButton1,
    XButton2
};
enum class Key {
    A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z,
    Num0, Num1, Num2, Num3, Num4, Num5, Num6, Num7, Num8, Num9,
    Escape, LControl, LShift, LAlt, LSystem, RControl, RShift, RAlt, RSystem,
    Menu, LBracket, RBracket, Semicolon, Comma, Period, Quote, Slash, Backslash,
    Tilde, Equal, Hyphen, Space, Return, Backspace, Tab, PageUp, PageDown,
    End, Home, Insert, Delete, Add, Subtract, Multiply, Divide,
    Left, Right, Up, Down, Numpad0, Numpad1, Numpad2, Numpad3, Numpad4,
    Numpad5, Numpad6, Numpad7, Numpad8, Numpad9,
    F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, F13, F14, F15,
    Pause, ...
};

enum class MouseButton {
    Left,
    Right,
    Middle,
    XButton1,
    XButton2
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

Initialisation

1. Charger le Backend d'Entrée

cpp
#include "GameEngine/Graphic/Systems/Input_system.hpp"
#include "GameEngine/Graphic/Systems/InputManager.hpp"

// Charger backend (après avoir créé le backend graphique)
void* backendHandle = dlopen("./LibEngine/Backends/sfml_backend.so", RTLD_NOW);

auto get_input_backend = (input::IInputBackend* (*)(graphics::IGraphicsBackend*))
    dlsym(backendHandle, "get_input_backend");

input::IInputBackend* inputBackend = get_input_backend(graphicsBackend);

// Configurer le manager
auto& inputMgr = input::InputManager::getInstance();
inputMgr.setBackend(inputBackend);
#include "GameEngine/Graphic/Systems/Input_system.hpp"
#include "GameEngine/Graphic/Systems/InputManager.hpp"

// Charger backend (après avoir créé le backend graphique)
void* backendHandle = dlopen("./LibEngine/Backends/sfml_backend.so", RTLD_NOW);

auto get_input_backend = (input::IInputBackend* (*)(graphics::IGraphicsBackend*))
    dlsym(backendHandle, "get_input_backend");

input::IInputBackend* inputBackend = get_input_backend(graphicsBackend);

// Configurer le manager
auto& inputMgr = input::InputManager::getInstance();
inputMgr.setBackend(inputBackend);
1
2
3
4
5
6
7
8
9
10
11
12
13
14

2. Enregistrer le Système ECS

cpp
void* inputHandle = dlopen("./LibEngine/Systems/input_system.so", RTLD_NOW);

auto register_input = (void(*)(registry&))
    dlsym(inputHandle, "register_systems");

register_input(reg);
void* inputHandle = dlopen("./LibEngine/Systems/input_system.so", RTLD_NOW);

auto register_input = (void(*)(registry&))
    dlsym(inputHandle, "register_systems");

register_input(reg);
1
2
3
4
5
6

3. Configurer les Contrôles

cpp
auto& inputMgr = input::InputManager::getInstance();

// ===== ACTIONS =====
// Mouvements
inputMgr.createAction("MoveUp");
inputMgr.bindKey("MoveUp", input::Key::Z);
inputMgr.bindKey("MoveUp", input::Key::Up);

inputMgr.createAction("MoveDown");
inputMgr.bindKey("MoveDown", input::Key::S);
inputMgr.bindKey("MoveDown", input::Key::Down);

inputMgr.createAction("MoveLeft");
inputMgr.bindKey("MoveLeft", input::Key::Q);
inputMgr.bindKey("MoveLeft", input::Key::Left);

inputMgr.createAction("MoveRight");
inputMgr.bindKey("MoveRight", input::Key::D);
inputMgr.bindKey("MoveRight", input::Key::Right);

// Actions
inputMgr.createAction("Shoot");
inputMgr.bindKey("Shoot", input::Key::Space);
inputMgr.bindMouseButton("Shoot", input::MouseButton::Left);

inputMgr.createAction("Pause");
inputMgr.bindKey("Pause", input::Key::Escape);

inputMgr.createAction("Sprint");
inputMgr.bindKey("Sprint", input::Key::LShift);

// ===== AXES =====
// Déplacement horizontal
inputMgr.createAxis("Horizontal", input::Key::Q, input::Key::D);

// Déplacement vertical
inputMgr.createAxis("Vertical", input::Key::S, input::Key::Z);

std::cout << "✓ Contrôles configurés" << std::endl;
auto& inputMgr = input::InputManager::getInstance();

// ===== ACTIONS =====
// Mouvements
inputMgr.createAction("MoveUp");
inputMgr.bindKey("MoveUp", input::Key::Z);
inputMgr.bindKey("MoveUp", input::Key::Up);

inputMgr.createAction("MoveDown");
inputMgr.bindKey("MoveDown", input::Key::S);
inputMgr.bindKey("MoveDown", input::Key::Down);

inputMgr.createAction("MoveLeft");
inputMgr.bindKey("MoveLeft", input::Key::Q);
inputMgr.bindKey("MoveLeft", input::Key::Left);

inputMgr.createAction("MoveRight");
inputMgr.bindKey("MoveRight", input::Key::D);
inputMgr.bindKey("MoveRight", input::Key::Right);

// Actions
inputMgr.createAction("Shoot");
inputMgr.bindKey("Shoot", input::Key::Space);
inputMgr.bindMouseButton("Shoot", input::MouseButton::Left);

inputMgr.createAction("Pause");
inputMgr.bindKey("Pause", input::Key::Escape);

inputMgr.createAction("Sprint");
inputMgr.bindKey("Sprint", input::Key::LShift);

// ===== AXES =====
// Déplacement horizontal
inputMgr.createAxis("Horizontal", input::Key::Q, input::Key::D);

// Déplacement vertical
inputMgr.createAxis("Vertical", input::Key::S, input::Key::Z);

std::cout << "✓ Contrôles configurés" << std::endl;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

Utilisation Courante

Actions (Booléens)

Vérifier si une Action est Pressée

cpp
auto& inputMgr = input::InputManager::getInstance();

if (inputMgr.isActionPressed("Shoot")) {
    // Tir en continu tant que la touche est maintenue
    shootTimer += deltaTime;
    
    if (shootTimer > fireRate) {
        spawnBullet();
        shootTimer = 0.f;
    }
}
auto& inputMgr = input::InputManager::getInstance();

if (inputMgr.isActionPressed("Shoot")) {
    // Tir en continu tant que la touche est maintenue
    shootTimer += deltaTime;
    
    if (shootTimer > fireRate) {
        spawnBullet();
        shootTimer = 0.f;
    }
}
1
2
3
4
5
6
7
8
9
10
11

Vérifier si une Action Vient d'être Pressée

cpp
if (inputMgr.isActionJustPressed("Jump")) {
    // Déclenché UNE SEULE FOIS au moment du pressage
    player.jump();
}

if (inputMgr.isActionJustPressed("Pause")) {
    pauseGame();
}
if (inputMgr.isActionJustPressed("Jump")) {
    // Déclenché UNE SEULE FOIS au moment du pressage
    player.jump();
}

if (inputMgr.isActionJustPressed("Pause")) {
    pauseGame();
}
1
2
3
4
5
6
7
8

Vérifier si une Action Vient d'être Relâchée

cpp
if (inputMgr.isActionJustReleased("Charge")) {
    // Le joueur a relâché le bouton après une charge
    releaseChargedAttack();
}
if (inputMgr.isActionJustReleased("Charge")) {
    // Le joueur a relâché le bouton après une charge
    releaseChargedAttack();
}
1
2
3
4

Axes (Valeurs Continues)

Axe Simple (Déplacement)

cpp
// Retourne -1 (négatif appuyé), 0 (rien), ou +1 (positif appuyé)
float horizontalAxis = inputMgr.getAxis("Horizontal");
float verticalAxis = inputMgr.getAxis("Vertical");

// Appliquer le mouvement
auto& transforms = reg.get_components<component::transform>();
auto* playerT = transforms.get_ptr(playerEntity);

if (playerT) {
    playerT->_x += horizontalAxis * moveSpeed * deltaTime;
    playerT->_y += verticalAxis * moveSpeed * deltaTime;
}
// Retourne -1 (négatif appuyé), 0 (rien), ou +1 (positif appuyé)
float horizontalAxis = inputMgr.getAxis("Horizontal");
float verticalAxis = inputMgr.getAxis("Vertical");

// Appliquer le mouvement
auto& transforms = reg.get_components<component::transform>();
auto* playerT = transforms.get_ptr(playerEntity);

if (playerT) {
    playerT->_x += horizontalAxis * moveSpeed * deltaTime;
    playerT->_y += verticalAxis * moveSpeed * deltaTime;
}
1
2
3
4
5
6
7
8
9
10
11
12

Normaliser la Diagonale

cpp
float h = inputMgr.getAxis("Horizontal");
float v = inputMgr.getAxis("Vertical");

// Éviter le boost diagonal
if (h != 0.f && v != 0.f) {
    float magnitude = std::sqrt(h*h + v*v);
    h /= magnitude;
    v /= magnitude;
}

velocity.x = h * moveSpeed;
velocity.y = v * moveSpeed;
float h = inputMgr.getAxis("Horizontal");
float v = inputMgr.getAxis("Vertical");

// Éviter le boost diagonal
if (h != 0.f && v != 0.f) {
    float magnitude = std::sqrt(h*h + v*v);
    h /= magnitude;
    v /= magnitude;
}

velocity.x = h * moveSpeed;
velocity.y = v * moveSpeed;
1
2
3
4
5
6
7
8
9
10
11
12

Touches Directes (Sans Actions)

cpp
// Vérifier une touche individuelle
if (inputMgr.isKeyPressed(input::Key::F1)) {
    toggleDebugMode();
}

if (inputMgr.isKeyJustPressed(input::Key::Escape)) {
    openPauseMenu();
}

if (inputMgr.isKeyJustReleased(input::Key::Return)) {
    submitMenuOption();
}
// Vérifier une touche individuelle
if (inputMgr.isKeyPressed(input::Key::F1)) {
    toggleDebugMode();
}

if (inputMgr.isKeyJustPressed(input::Key::Escape)) {
    openPauseMenu();
}

if (inputMgr.isKeyJustReleased(input::Key::Return)) {
    submitMenuOption();
}
1
2
3
4
5
6
7
8
9
10
11
12

Souris

cpp
// Position souris
int mouseX, mouseY;
inputMgr.getMousePosition(mouseX, mouseY);

// Boutons souris
if (inputMgr.isMouseButtonPressed(input::MouseButton::Left)) {
    fireAtMousePosition(mouseX, mouseY);
}

if (inputMgr.isMouseButtonJustPressed(input::MouseButton::Right)) {
    openContextMenu(mouseX, mouseY);
}
// Position souris
int mouseX, mouseY;
inputMgr.getMousePosition(mouseX, mouseY);

// Boutons souris
if (inputMgr.isMouseButtonPressed(input::MouseButton::Left)) {
    fireAtMousePosition(mouseX, mouseY);
}

if (inputMgr.isMouseButtonJustPressed(input::MouseButton::Right)) {
    openContextMenu(mouseX, mouseY);
}
1
2
3
4
5
6
7
8
9
10
11
12

Cycle de Mise à Jour

Où Appeler update() ?

L'InputSystem appelle automatiquement inputMgr.update() à chaque frame via le registre ECS :

cpp
while (backend->isOpen()) {
    // Votre logique de jeu
    
    // Le système ECS s'exécute (Input System inclus)
    reg.run_systems();  // ← update() appelé ici
}
while (backend->isOpen()) {
    // Votre logique de jeu
    
    // Le système ECS s'exécute (Input System inclus)
    reg.run_systems();  // ← update() appelé ici
}
1
2
3
4
5
6

Ou manuellement si vous n'utilisez pas run_systems() :

cpp
while (backend->isOpen()) {
    // Événements
    backend->pollEvents();
    
    // Mise à jour input
    inputMgr.update();  // ← Important pour just pressed/released
    
    // Logique
    updateGame();
    
    // Rendu
    render();
}
while (backend->isOpen()) {
    // Événements
    backend->pollEvents();
    
    // Mise à jour input
    inputMgr.update();  // ← Important pour just pressed/released
    
    // Logique
    updateGame();
    
    // Rendu
    render();
}
1
2
3
4
5
6
7
8
9
10
11
12
13

Configuration Avancée

Rebinding Dynamique

cpp
// Permettre au joueur de reconfigurer ses contrôles

void rebindKey(const std::string& action, input::Key newKey) {
    auto& inputMgr = input::InputManager::getInstance();
    
    // Enlever tous les anciens bindings
    // (Note: API complète dépend de l'implémentation)
    
    // Ajouter le nouveau
    inputMgr.bindKey(action, newKey);
}

// Utilisation
if (userSelectsRebindMenu) {
    rebindKey("Shoot", input::Key::W);
}
// Permettre au joueur de reconfigurer ses contrôles

void rebindKey(const std::string& action, input::Key newKey) {
    auto& inputMgr = input::InputManager::getInstance();
    
    // Enlever tous les anciens bindings
    // (Note: API complète dépend de l'implémentation)
    
    // Ajouter le nouveau
    inputMgr.bindKey(action, newKey);
}

// Utilisation
if (userSelectsRebindMenu) {
    rebindKey("Shoot", input::Key::W);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

Gamepads (Future Extension)

Actuellement supporté :

  • Clavier
  • Souris

Extensible pour :

  • Gamepads/Manettes
  • Joysticks
  • Manettes Xbox/PlayStation
cpp
// Futur
inputMgr.createAxis("StickHorizontal", GamepadAxis::LeftX);
inputMgr.createAction("ButtonA");
inputMgr.bindGamepadButton("ButtonA", GamepadButton::A);
// Futur
inputMgr.createAxis("StickHorizontal", GamepadAxis::LeftX);
inputMgr.createAction("ButtonA");
inputMgr.bindGamepadButton("ButtonA", GamepadButton::A);
1
2
3
4

Combos (Entrées Multiples)

cpp
struct InputCombo {
    std::string name;
    std::vector<std::string> sequence;
    float timeWindow;
    float timeSinceLastInput;
};

// Système de combo personnalisé
if (inputMgr.isActionJustPressed("Punch")) {
    // Ajouter à la séquence
    comboSequence.push_back("Punch");
    comboTimer = timeWindow;
}

if (inputMgr.isActionJustPressed("Kick")) {
    comboSequence.push_back("Kick");
    comboTimer = timeWindow;
}

// Vérifier pattern
if (comboSequence.size() >= 3 &&
    comboSequence[0] == "Punch" &&
    comboSequence[1] == "Punch" &&
    comboSequence[2] == "Kick") {
    executeSpecialMove();
    comboSequence.clear();
}
struct InputCombo {
    std::string name;
    std::vector<std::string> sequence;
    float timeWindow;
    float timeSinceLastInput;
};

// Système de combo personnalisé
if (inputMgr.isActionJustPressed("Punch")) {
    // Ajouter à la séquence
    comboSequence.push_back("Punch");
    comboTimer = timeWindow;
}

if (inputMgr.isActionJustPressed("Kick")) {
    comboSequence.push_back("Kick");
    comboTimer = timeWindow;
}

// Vérifier pattern
if (comboSequence.size() >= 3 &&
    comboSequence[0] == "Punch" &&
    comboSequence[1] == "Punch" &&
    comboSequence[2] == "Kick") {
    executeSpecialMove();
    comboSequence.clear();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

Exemples Pratiques

Jeu de Type Shoot'em Up (R-Type)

cpp
auto& inputMgr = input::InputManager::getInstance();

// Configuration
inputMgr.createAction("MoveUp");
inputMgr.createAction("MoveDown");
inputMgr.createAction("MoveLeft");
inputMgr.createAction("MoveRight");
inputMgr.createAction("Shoot");

// Bindage
inputMgr.bindKey("MoveUp", input::Key::Z);
inputMgr.bindKey("MoveDown", input::Key::S);
inputMgr.bindKey("MoveLeft", input::Key::Q);
inputMgr.bindKey("MoveRight", input::Key::D);
inputMgr.bindKey("Shoot", input::Key::Space);

// Dans la boucle de jeu
while (backend->isOpen()) {
    auto& transforms = reg.get_components<component::transform>();
    auto* playerT = transforms.get_ptr(playerEntity);
    
    if (playerT) {
        // Déplacement
        const float speed = 300.f;
        
        if (inputMgr.isActionPressed("MoveUp")) {
            playerT->_y -= speed * deltaTime;
        }
        if (inputMgr.isActionPressed("MoveDown")) {
            playerT->_y += speed * deltaTime;
        }
        if (inputMgr.isActionPressed("MoveLeft")) {
            playerT->_x -= speed * deltaTime;
        }
        if (inputMgr.isActionPressed("MoveRight")) {
            playerT->_x += speed * deltaTime;
        }
        
        // Tir automatique
        if (inputMgr.isActionPressed("Shoot")) {
            shootTimer += deltaTime;
            if (shootTimer >= shootInterval) {
                spawnBullet(reg, playerT->_x, playerT->_y);
                shootTimer = 0.f;
            }
        }
    }
    
    reg.run_systems();
}
auto& inputMgr = input::InputManager::getInstance();

// Configuration
inputMgr.createAction("MoveUp");
inputMgr.createAction("MoveDown");
inputMgr.createAction("MoveLeft");
inputMgr.createAction("MoveRight");
inputMgr.createAction("Shoot");

// Bindage
inputMgr.bindKey("MoveUp", input::Key::Z);
inputMgr.bindKey("MoveDown", input::Key::S);
inputMgr.bindKey("MoveLeft", input::Key::Q);
inputMgr.bindKey("MoveRight", input::Key::D);
inputMgr.bindKey("Shoot", input::Key::Space);

// Dans la boucle de jeu
while (backend->isOpen()) {
    auto& transforms = reg.get_components<component::transform>();
    auto* playerT = transforms.get_ptr(playerEntity);
    
    if (playerT) {
        // Déplacement
        const float speed = 300.f;
        
        if (inputMgr.isActionPressed("MoveUp")) {
            playerT->_y -= speed * deltaTime;
        }
        if (inputMgr.isActionPressed("MoveDown")) {
            playerT->_y += speed * deltaTime;
        }
        if (inputMgr.isActionPressed("MoveLeft")) {
            playerT->_x -= speed * deltaTime;
        }
        if (inputMgr.isActionPressed("MoveRight")) {
            playerT->_x += speed * deltaTime;
        }
        
        // Tir automatique
        if (inputMgr.isActionPressed("Shoot")) {
            shootTimer += deltaTime;
            if (shootTimer >= shootInterval) {
                spawnBullet(reg, playerT->_x, playerT->_y);
                shootTimer = 0.f;
            }
        }
    }
    
    reg.run_systems();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

Jeu Puzzle avec Axes

cpp
auto& inputMgr = input::InputManager::getInstance();

inputMgr.createAxis("Horizontal", input::Key::Left, input::Key::Right);
inputMgr.createAxis("Vertical", input::Key::Down, input::Key::Up);
inputMgr.createAction("Interact");
inputMgr.bindKey("Interact", input::Key::Return);

while (backend->isOpen()) {
    float h = inputMgr.getAxis("Horizontal");
    float v = inputMgr.getAxis("Vertical");
    
    // Déplacer le curseur/sélection
    if (h != 0.f || v != 0.f) {
        gridX += (int)h;
        gridY += (int)v;
        gridX = std::clamp(gridX, 0, 9);
        gridY = std::clamp(gridY, 0, 9);
    }
    
    // Interagir
    if (inputMgr.isActionJustPressed("Interact")) {
        selectTile(gridX, gridY);
    }
    
    reg.run_systems();
}
auto& inputMgr = input::InputManager::getInstance();

inputMgr.createAxis("Horizontal", input::Key::Left, input::Key::Right);
inputMgr.createAxis("Vertical", input::Key::Down, input::Key::Up);
inputMgr.createAction("Interact");
inputMgr.bindKey("Interact", input::Key::Return);

while (backend->isOpen()) {
    float h = inputMgr.getAxis("Horizontal");
    float v = inputMgr.getAxis("Vertical");
    
    // Déplacer le curseur/sélection
    if (h != 0.f || v != 0.f) {
        gridX += (int)h;
        gridY += (int)v;
        gridX = std::clamp(gridX, 0, 9);
        gridY = std::clamp(gridY, 0, 9);
    }
    
    // Interagir
    if (inputMgr.isActionJustPressed("Interact")) {
        selectTile(gridX, gridY);
    }
    
    reg.run_systems();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
cpp
enum MenuOption { PLAY, SETTINGS, QUIT };
MenuOption currentOption = PLAY;

while (backend->isOpen()) {
    if (inputMgr.isActionJustPressed("MoveUp")) {
        currentOption = (MenuOption)((currentOption - 1 + 3) % 3);
    }
    if (inputMgr.isActionJustPressed("MoveDown")) {
        currentOption = (MenuOption)((currentOption + 1) % 3);
    }
    
    if (inputMgr.isActionJustPressed("Shoot")) {  // Select
        switch (currentOption) {
            case PLAY:
                startGame();
                break;
            case SETTINGS:
                openSettings();
                break;
            case QUIT:
                backend->close();
                break;
        }
    }
    
    renderMenu(currentOption);
    reg.run_systems();
}
enum MenuOption { PLAY, SETTINGS, QUIT };
MenuOption currentOption = PLAY;

while (backend->isOpen()) {
    if (inputMgr.isActionJustPressed("MoveUp")) {
        currentOption = (MenuOption)((currentOption - 1 + 3) % 3);
    }
    if (inputMgr.isActionJustPressed("MoveDown")) {
        currentOption = (MenuOption)((currentOption + 1) % 3);
    }
    
    if (inputMgr.isActionJustPressed("Shoot")) {  // Select
        switch (currentOption) {
            case PLAY:
                startGame();
                break;
            case SETTINGS:
                openSettings();
                break;
            case QUIT:
                backend->close();
                break;
        }
    }
    
    renderMenu(currentOption);
    reg.run_systems();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

État des Touches

État Complet d'une Touche

Avant : [Relâché]
        |

[Espace] Pressé → [isKeyJustPressed = true]
        |

Pendant : [Appuyé] → [isKeyPressed = true]
          (frames suivantes)
        |

[Espace] Relâché → [isKeyJustReleased = true]
        |

Après : [Relâché] → [isKeyPressed = false]
Avant : [Relâché]
        |

[Espace] Pressé → [isKeyJustPressed = true]
        |

Pendant : [Appuyé] → [isKeyPressed = true]
          (frames suivantes)
        |

[Espace] Relâché → [isKeyJustReleased = true]
        |

Après : [Relâché] → [isKeyPressed = false]
1
2
3
4
5
6
7
8
9
10
11
12
13
14

Exemple Temporel

Frame 1 : Utilisateur appuie
  - isKeyJustPressed() → TRUE
  - isKeyPressed() → TRUE

Frame 2 : Utilisateur maintient
  - isKeyJustPressed() → FALSE
  - isKeyPressed() → TRUE

Frame 3 : Utilisateur maintient
  - isKeyJustPressed() → FALSE
  - isKeyPressed() → TRUE

Frame 4 : Utilisateur relâche
  - isKeyJustReleased() → TRUE
  - isKeyPressed() → FALSE

Frame 5 : Touche relâchée
  - isKeyJustReleased() → FALSE
  - isKeyPressed() → FALSE
Frame 1 : Utilisateur appuie
  - isKeyJustPressed() → TRUE
  - isKeyPressed() → TRUE

Frame 2 : Utilisateur maintient
  - isKeyJustPressed() → FALSE
  - isKeyPressed() → TRUE

Frame 3 : Utilisateur maintient
  - isKeyJustPressed() → FALSE
  - isKeyPressed() → TRUE

Frame 4 : Utilisateur relâche
  - isKeyJustReleased() → TRUE
  - isKeyPressed() → FALSE

Frame 5 : Touche relâchée
  - isKeyJustReleased() → FALSE
  - isKeyPressed() → FALSE
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

🔧 Debugging

Afficher l'État des Entrées

cpp
// En développement
if (inputMgr.isKeyPressed(input::Key::F1)) {
    std::cout << "=== INPUT DEBUG ===" << std::endl;
    std::cout << "MoveUp: " << inputMgr.isActionPressed("MoveUp") << std::endl;
    std::cout << "Shoot: " << inputMgr.isActionPressed("Shoot") << std::endl;
    std::cout << "Horizontal: " << inputMgr.getAxis("Horizontal") << std::endl;
    
    int mx, my;
    inputMgr.getMousePosition(mx, my);
    std::cout << "Mouse: (" << mx << ", " << my << ")" << std::endl;
}
// En développement
if (inputMgr.isKeyPressed(input::Key::F1)) {
    std::cout << "=== INPUT DEBUG ===" << std::endl;
    std::cout << "MoveUp: " << inputMgr.isActionPressed("MoveUp") << std::endl;
    std::cout << "Shoot: " << inputMgr.isActionPressed("Shoot") << std::endl;
    std::cout << "Horizontal: " << inputMgr.getAxis("Horizontal") << std::endl;
    
    int mx, my;
    inputMgr.getMousePosition(mx, my);
    std::cout << "Mouse: (" << mx << ", " << my << ")" << std::endl;
}
1
2
3
4
5
6
7
8
9
10
11

Logger les Événements

cpp
void logInputEvent(const std::string& eventType, const std::string& key) {
    std::cout << "[INPUT] " << eventType << ": " << key << std::endl;
}

if (inputMgr.isActionJustPressed("Shoot")) {
    logInputEvent("PRESSED", "Shoot");
}

if (inputMgr.isActionJustReleased("Charge")) {
    logInputEvent("RELEASED", "Charge");
}
void logInputEvent(const std::string& eventType, const std::string& key) {
    std::cout << "[INPUT] " << eventType << ": " << key << std::endl;
}

if (inputMgr.isActionJustPressed("Shoot")) {
    logInputEvent("PRESSED", "Shoot");
}

if (inputMgr.isActionJustReleased("Charge")) {
    logInputEvent("RELEASED", "Charge");
}
1
2
3
4
5
6
7
8
9
10
11

Référence Complète

Méthodes Actions

MéthodeRetourDescription
createAction(name)voidCréer une nouvelle action
bindKey(action, key)voidLier une touche à une action
bindMouseButton(action, btn)voidLier un bouton souris
isActionPressed(name)boolAction maintenue ?
isActionJustPressed(name)boolAction vient d'être pressée ?
isActionJustReleased(name)boolAction vient d'être relâchée ?

Méthodes Axes

MéthodeRetourDescription
createAxis(name, neg, pos)voidCréer un axe
getAxis(name)floatObtenir valeur (-1, 0, +1)

Touches Directes

MéthodeRetourDescription
isKeyPressed(key)boolTouche maintenue ?
isKeyJustPressed(key)boolTouche vient d'être pressée ?
isKeyJustReleased(key)boolTouche vient d'être relâchée ?
isMouseButtonPressed(btn)boolBouton souris appuyé ?

Souris

MéthodeRetourDescription
getMousePosition(x, y)voidObtenir position

Bonnes Pratiques

  1. Créer des actions sémantiques : Utiliser des noms logiques ("Shoot" plutôt que "Space")
  2. Multi-binding : Permettre plusieurs touches pour une action (ZQSD + Flèches)
  3. Appeler update() : Ne pas oublier à chaque frame
  4. Just vs Pressed : Utiliser JustPressed pour les événements, Pressed pour l'effet continu
  5. Normaliser les diagonales : Pour les axes en 2D
  6. Rebinding : Offrir la possibilité aux joueurs de modifier les contrôles
  7. Feedback : Vibrations/son pour confirmer les entrées