Bases de programmation de jeux vidéo

Dimitri Robert

Qu'est-ce qu'un jeu vidéo ?

  • Application multimédia → son, image (généralement animée)
  • Interactive → gestion des événements clavier, souris, joystick, etc.
  • Une machine trop faible sera plus pénalisante pour un jeu que pour une autre appli
  • D'un autre côté, un jeu doit être programmé de manière à exploiter au mieux le peu de puissance dont il dispose

Squelette d'un jeu

Boucle d'exécution

Un cycle succède à un autre
→ l'ordre des actions n'a pas d'importance.

L'avancement du jeu peut être neutralisé en mode pause.

Exemple en C

int main(int argc, char** argv)
{
    init_game(argc, argv);
    while (!end_game)
    {
        manage_events();
        manage_time();
        if (!pause)
            manage_game();
        display();
    }
    free_game_data();
}

Avancement du jeu

Et les threads ?

Objets en mouvement

Déplacer à chaque frame

Même si cela diffère en apparence, la manière est identique.

Maîtrise du déplacement

Les déplacements sont sujets aux ralentissements divers et non maîtrisables.

Processeurs : différence entre un processeur affichant à 64 FPS et à 16 FPS → rapport de 4. Pendant que le premier passe 4 cycle, le second n'en passe qu'un. Ralentir les processeurs plus rapide n'est pas une solution.

Ponctuellement, un cycle peut être plus long : plus d'objets à traiter, calcul coûteux, etc.

Besoin d'une horloge

Ainsi un même objet se déplace à la même vitesse quelque soit le processeur et les circonstances.

Pour les éléments du jeu, on préfère leur donner une vitesse plutôt qu'un déplacement.

Animation

Affichage

Le jeu doit pouvoir fonctionner sans l'affichage.

Exemple d'affichage

void display()
{
    terrain_display();
    objects_display(); // bâtiments, végétaux, etc.
    characters_display(); // personnages éventuels
    hud_display(); // indicateurs et commandes du jeu
    if (pause)
        pause_display();
}

Attention : l'ordre d'affichage a son importance !
On affiche généralement du plus loin au plus près.

Le poids du test

Un code optimisable

switch (paysan[i]->etat)
{
    case ASK:   character_choice(paysan[i]);
                break;
    case MOVE:  character_move(paysan[i]);
                break;
    case SLEEP: character_sleep(paysan[i]);
                break;
    case EAT:   character_eat(paysan[i]);
                break;
}

Une solution

On a déjà la variable d'état. On en étend son usage.

Exemple : init

enum etat { ASK, MOVE, EAT, SLEEP, NB_ETAT };

void (**action)(character*);
action = malloc (NB_ETAT * sizeof(void*));

Initialisation des fonctions

action[ASK] = character_choice;
action[MOVE] = character_move;
action[EAT] = character_eat;
action[SLEEP] = character_sleep;

On a un tableau de paysan, objet de type character. Il peut accomplir trois actions : se déplacer, manger, dormir. Lorsqu'il achève une action, il repasse dans l'état ASK pour en choisir une autre.

Le champs etat de la structure character est de type enum etat.

Exemple : prototypes

void character_choice(character* person);
void character_move(character* person);
void character_eat(character* person);
void character_sleep(character* person);

Dans character_choice() on fait des tests pour déterminer le nouvel état.

Ensuite, plus aucun test !
Le personnage vit sa vie.

On utilise ainsi dans la fonction de gestion des personnages :

action [paysan[i]->etat] (paysan[i]);

Sauvegarde rapide

Influence du matériel

Crédits

E-mail : d POINT robert AT point-libre.org

Blog : http://www.point-libre.org/~dimitri/blog/

Illustration de Mathieu Leyssenne : http://www.aniii.com

Présentation réalisée avec S5: A Simple Standards-Based Slide Show System : http://www.meyerweb.com/eric/tools/s5/