Yann Pellegrini

Blog

[fr] Ludum Dare 28

Published at 08/01/2014 à 17:37:53 by Yann


[English version] Salut,

j'ai pris un week-end en Décembre pour participer à la 28ème édition de Ludum Dare. ld

Un mot sur le concours Ludum Dare

Ludum Dare est une compétition de développement de jeux vidéo avec limite de temps et thème imposé qui se déroule trois ou quatre fois par an. Il y a deux catégories :

  • "Compo" qui impose de travailler seul, en 48h et en rendant public le code source,
  • "Jam", qui autorise de travailler à plusieurs, en 72h et en gardant le code source privé.

Cette compétition prend une ampleur impressionnante, avec plus de 2000 entrées cette édition.

Mon entrée

Le thème (voté par les membres du site) était "You only get one".
img
Mon entrée s'intitule "Handle with care", un jeu de plateforme dans lequel un personnage évolue dans un souterrain. Toutes les portes des niveaux s'ouvrent avec une clé de cristal qui casse si on ne la transporte pas avec précaution, et vous n'en avez qu'une...

Vous pouvez jouer à la version originale, voir la fiche LD ou encore l'éditeur de niveaux jouables fait après le concours si vous voulez vous amuser.

Bilan du week end

Problèmes du jeu

  • Maniabilité : mal pensés + bugs. J'ai voulu faire un mécanisme de double saut avec la touche espace. Manque de bol, la combinaison de touches haut/gauche/espace très utile pour faire un boost ne marche pas sur 9/10ème des claviers et je ne le savais pas. Le jeu reste jouable mais demande un timing impeccable de la part du joueur, qui perd rapidement patience. C'est la dernière fois que j'utilise la touche espace dans les contrôles d'un de mes jeux...
  • Difficulté : bien trop difficile ! Les 3/4 des retours que j'ai eu sur le jeu dans les commentaires pointaient le manque de checkpoints et les sauts difficile, ainsi que le fait que les ennemis tirent aléatoirement (10 flèches en une seconde est aussi probable qu'aucune :D)
  • Audio : je n'ai pas eu le temps de chercher de musique d'ambiance ni de BGS. Et le son de la clé qui se casse est trop fort.

Ce qui a bien fonctionné

  • Le moteur de jeu : codé en CoffeeScript, résultat HTML5 très compatible, le moteur de jeu fonctionne très bien et j'ai fait la base assez rapidement (une matinée). Peu de bugs mais la clé passe des fois à travers le sol. Implémenter le système de la clé, les ennemis, etc, s'est fait sans trop de problèmes (mais c'est long)
  • Les ennemis : je me suis donné le temps de faire au moins deux types d'ennemis et je suis content du résultat car ils ont chacun un rôle précis : faire perdre la clé des mains du joueur pour les archers, et les bombes s'occupent du reste ;) img
  • Le pixel art : je n'ai pas beaucoup d'expérience, c'est du très gros pixel art (3:1) un peu fait à l'arrache mais les retours sont positifs, donc je n'ai pas à me plaindre sur ce point

img
Globalement, je suis content pour cette première participation ! Je ne pensais pas participer au départ, mais j'ai réussi à finir un jeu et ce n'est pas souvent que ça m'arrive.

Résultats

img

Mon jeu est arrivé 77ème au classement général de ma catégorie "Compo" et 19ème sur le critère "Thème" :)
Je m'attendais à nettement moins bien mais les joueurs semblent avoir pardonné la maniabilité horrible.

Plein de jeux cool

Beaucoup de super jeux ont été produits comme à chaque Ludum Dare, en voici quelques uns :
  • Titan souls par Clawhammer Mark (Jam) : vous n'avez qu'une seule flèche pour battre quatre titans.
  • PUSH par Patacorow : un puzzle bien pensé et agréable à jouer.
  • Jameson the pilot par rezoner : beau jeu HTML5 *-*
et encore des tonnes... Vous en voulez d'autres ? Les listes de gagnants par catégorie et top 100

Bref, la prochaine session du concours est en Avril 2014. Je vous recommande vivement de participer ! Tous les langages sont autorisés ainsi que les logiciels. Seul inconvénient : le Lundi matin est difficile après :p

Merci d'avoir lu et à la prochaine !

Projet : Tower Defense

Published at 17/10/2013 à 16:30:55 by Yann


Hello, 6 mois se sont écoulés après le dernier article sur mon blog, il est grand temps de rajouter un peu de contenu :D Je me suis lancé dans la création d'un "Tower Defense" un peu modifié et j'ai pensé que ce serait cool d'écrire ici où ça en est.

Détails
  • Code : CoffeeScript ; rendu DOM (et non Canvas)
  • C'est un jeu solo. Pas de multijoueur prévu pour l'instant.
  • J'essaierai de le rendre jouable sur smartphone
  • Graphismes de RPG Maker pour changer (Enterbrain)

Avancement

5 - Les habitations arrivent !

(euuuuuh)

4 - Animations éphémères

3 - Pathfinding et course-poursuite entre entités

2 - Système de vagues

1 - Des entités mobiles plein l'écran !

Algos pour puissance 4 avec intelligence artificielle (partie 1)

Published at 01/04/2013 à 22:23:21 by Yann


Bonjour à tous ! À la suite d'un projet ISN consistant à réaliser un morpion en Python, j'ai dû imaginer plusieurs algorithmes qui pourraient vous être utiles pour créer n'importe quel jeu du type Puissance 4, 5 croix ou Morpion si vous en avez envie :) J'ai également fait ce programme en JavaScript (vous pouvez l'essayer ici : http://yann-p.fr/puissance4). Je partage donc les techniques que j'ai utilisées pour y arriver. Ce ne sont sûrement pas les meilleures, mais ça marche. Enjoy !

I - Bases

On tient l'état du jeu avec les variables suivantes :

var grille = [ // Générée avec une petite boucle
	[0, 0, 0, 0, 0],
	[0, 0, 0, 0, 0],
	[0, 0, 0, 0, 0],
	[0, 0, 0, 0, 0],
	[0, 0, 0, 0, 0]
]
var tour = 1 // 1 : au tour du joueur, 2: au tour de l'IA

II - Détecter si un coup clôture la partie avec un gagnant

Ce qu'on veut : connaitre l'alignement maximal qu'a réalisé un joueur, connaissant son dernier coup et l'état du jeu (grille). Je me base donc sur le dernier coup. Ensuite, connaissant ses coordonnées, on cherche dans toutes les directions d'autres pions du même joueur (ici, des croix.) Pour chaque direction, on initialise le compteur d'alignement à 1 (le coup sur lequel on se base pour la recherche). Puis on part dans les deux sens que représente cette direction, à partir du dernier coup. Prenons ici la diagonale verte. Pour les deux sens, on cumule le nombre de coups du joueur et on s'arrête lorsqu'on rencontre soit une case vide, soit un coup de l'adversaire, soit le bord de la grille. On recommence pour chaque direction à chercher dans les deux sens et finalement, on prend le compteur correspondant à la direction ayant effectué le plus haut score. Voici ce que ça donne niveau algorithmique :

var alignementMaximal = function(dernierX, dernierY, tour) {
	directions = [
        [[1, 0], [-1, 0]], // Ligne horizontale : [ vers la droite : [x+, y], vers la gauche : [x-, y]]
        [[0, 1], [0, -1]], // Ligne verticale : [ vers la bas : [x, y+], vers le haut : [x, y-]]
        [[-1, 1], [1, -1]],// Diagonale \
        [[-1, -1], [1, 1]] // Diagonale /
    ]

    for (i = 0; i < directions.length; i++) { // Pour toutes les directions
  		direction = directions[i];
  		compteur = 1;
  		for (j = 0; j < direction.length; j++) { // Pour les deux sens de cette direction
    		sens = direction[j];
    		x = dernierX + sens[0]; // On initialise les variables de la position du coup initial
    		y = dernierY + sens[1]; // sens[0] est le changement de x, sens[1] celui de y
    		while (!horsGrille(x, y) && !caseOccupeeParAdversaire(x, y) && !caseVide(x, y)) { // Tant que rien ne bloque la progression
      			compteur++;   // Le compteur augmente
      			x += sens[0]; // On avance
      			y += sens[1];
    	}
    }
}
Je m'arrête là pour aujourd'hui et dans mon prochain tuto, je vous explique comment fonctionne l'intelligence artificielle :) L'IA se servira de cette fonction car elle étudiera toutes les issues des coups réalisables sur la grille, et aura donc besoin de savoir si tel ou tel coup clôture une partie. Merci d'avoir lu et bonne journée ! :D

Un jeu du pendu en Python 2.7

Published at 14/03/2013 à 04:12:16 by Yann


Salut à tous, aujourd'hui je vous explique rapidement comment créer le classique jeu du pendu en Python, suite à un petit devoir d'ISN. :) Commençons par définir une variable contenant les mots qui seront choisis aléatoirement au lancement de la partie, et l'alphabet qui permettra de vérifier les entrées du joueur.
alphabet = ['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']
mots = ['informatique', 'isn', 'nyancat']
Ensuite, il faut choisir un de ces mots aléatoirement. Pour se servir de l'aléatoire, vous devez placer la ligne suivante en haut de votre fichier :
import random
Puis, générer un index aléatoire entre 0 et 2, puisqu'ici mots[0] = 'informatique', mots[1] = 'isn' et mots[2] = 'nyancat'. On s'aide de la fonction len() pour connaître la limite, mais il faut la décrémenter de 1 car la longueur de mots est bien de 3 entrées, mais mots[3] n'existe pas.
index = random.randint(0, len(mots) - 1)
mot = mots[index]
mot = mot.upper()                 # On met le mot en majuscules, on travaillera uniquement en majuscules
On créé ainsi la variable mot (sans s) qui contient le mot à deviner. Nous avons besoin de connaitre la progression du joueur. Je vais la stocker dans un tableau, qui contiendra False pour les lettres a deviner et les lettres déjà trouvées. Donc au lancement on veut un tableau rempli de False de la longueur du mot. On initialise aussi les "vies" et le tableau des lettres déjà proposées.
progression = [False]*len(mot)
chances = 10
lettresProposees = []
On créé une fonction proposition() qui sera exécutée à chaque fois qu'on demandera une lettre au joueur et qui traitera sa demande.
def proposition():
    lettre = raw_input("Proposez une lettre")
À ce stade, on veut savoir si la lettre que le joueur a proposée est dans le mot, et si oui, à quels emplacements. On créé donc une fonction verifier(lettre). Ici, on initialise une variable resultat qui, sous la forme d'un tableau, contiendra tous les index auxquels la lettre proposée est présente dans le mot. On parcourt donc tout le tableau en quête de la lettre.
def verifier(lettre):
    resultat = []
    for i in range(len(mot)):
        if(mot[i] == lettre):
            resultat.append(i)
    return resultat
D'ici on peut revenir sur la fonction proposition().
def proposition():
    lettre = raw_input("Proposez une lettre")
    lettre = lettre.upper()                         #On la met en majuscule
    if not(lettre in alphabet):                     #La lettre est-elle dans l'alphabet ?
        print "Ceci n'est pas une lettre"
        proposition()                               #On redemande la lettre
        return                                      #On sort de la fonction
    if lettre in lettresProposees:                  #Lettre déjà proposée ?
        print "Vous avez déjà proposé cette lettre"
        proposition()
        return

    lettresProposees.append(lettre)                 #On ajoute la lettre aux lettres proposees

    resultat = verifier(lettre)                     
    ...etc...
Ici, j'ai ajouté toutes les vérifications préalables nécessaires avant de vérifier que la lettre est dans le mot. Le morceau de code "resultat = verifier(lettre)" va récupérer un tableau retourné par la fonction verifier(). Si la lettre n'est pas présente dans le mot, on aura resultat = []. Si, admettons, elle est présente à la première position, on aura resultat = [0] Si elle est présente à la première et a la troisième, resultat = [0, 2]. Et ensuite ? On regarde si résultat est vide. Si il l'est, on lui fera perdre une vie avec la très simple fonction qui suit. (j'ai mis global parce qu'il prenait la variable chances pour une variable locale, n'y faites pas attention mais ajoutez le bien, plus d'infos ici)
def perdreVie():
    global chances
    chances -= 1
    if(chances == 0):
        print "Vous avez perdu ! Le mot était " + mot                    # Dead-end, on ne demande plus de proposition après !
    else:
        proposition()
Sinon, nous allons parcourir resultat avec une boucle et remplacer dans progression les lettres trouvées. Si le mot était ISN, au fur et à mesure le tableau pourrait évoluer en [False, False, False] puis [False, 'S', 'N'] jusque ['I', 'S', 'N'].
def proposition():
    print progression
    lettre = raw_input("Proposez une lettre (" + str(chances) + " chances restantes)")
    lettre = lettre.upper()                         #On la met en majuscules
    if not(lettre in alphabet):                     #Lettre valide ?
        print "Ceci n'est pas une lettre"
        proposition()
        return
    if lettre in lettresProposees:                  #Lettre déjà proposée ?
        print "Vous avez déjà proposé cette lettre"
        proposition()
        return

    lettresProposees.append(lettre)                 #On ajoute la lettre aux lettres proposees
    resultat = verifier(lettre)
    if len(resultat) == 0:                          #Lettre n'est pas dans le mot
        print "La lettre " + lettre + " n'est pas dans le mot"
        perdreVie()
        return
    else:
        for i in range(len(resultat)):
            index = resultat[i]                     #Ou est cette lettre dans le mot ?
            progression[index] = lettre             #On met à jour la progression
        proposition()                               #C'est bien, on continue !
Ici, j'ai ajouté les chances restantes à la demande de la lettre avec str() car chances est un entier et on ne concatène pas les entiers avec les chaines de caractères en Python. Ensuite j'ajoute le traitement de la variable resultat. Au début de la fonction, j'affiche la progression. Il reste deux petites étapes. 1. Vérifier si le joueur a gagné
if not(False in progression):
            print "Le mot était bien " + mot + ", bravo"
            return
2. Afficher sous forme lisible la variable progression (pour cela on la transforme en chaine de caractères et on remplace les False par des underscore pour avoir un rendu genre "INFO_MA_IQUE")
def afficherProgression(progression):
    resultat = ""
    for i in range(len(progression)):
        if(progression[i] == False):
            resultat += "_"
        else:
            resultat += progression[i]
    return resultat
Et voici le code final ! On oublie pas de lancer le jeu en appelant proposition() en bas !
import random

alphabet = ['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']
mots = ['informatique', 'isn', 'nyancat']

index = random.randint(0, len(mots) - 1)
mot = mots[index]
mot = mot.upper()                                   #On ne travaillera qu'avec des majuscules

progression = [False]*len(mot)
chances = 10
lettresProposees = []

def proposition():
    print afficherProgression(progression)
    lettre = raw_input("Proposez une lettre (" + str(chances) + " chances restantes)")
    lettre = lettre.upper()                         #On la met en majuscules
    if not(lettre in alphabet):                     #Lettre valide ?
        print "Ceci n'est pas une lettre"
        proposition()
        return
    if lettre in lettresProposees:                  #Lettre déjà proposée ?
        print "Vous avez déjà proposé cette lettre"
        proposition()
        return

    lettresProposees.append(lettre)                 #On ajoute la lettre aux lettres proposees
    resultat = verifier(lettre)
    if len(resultat) == 0:                          #Lettre n'est pas dans le mot
        print "La lettre " + lettre + " n'est pas dans le mot"
        perdreVie()
        return
    else:
        for i in range(len(resultat)):
            index = resultat[i]                     #Ou est cette lettre dans le mot ?
            progression[index] = lettre             #On met à jour la progression
        if not(False in progression):
            print "Le mot était bien " + mot + ", bravo"
            return
        proposition()                               #C'est bien, on continue !

def afficherProgression(progression):
    resultat = ""
    for i in range(len(progression)):
        if(progression[i] == False):
            resultat += "_"
        else:
            resultat += progression[i]
    return resultat

def perdreVie():
    global chances
    chances -= 1
    if chances == 0:
        print "Vous avez perdu ! Le mot était " + mot
    else:
        proposition()

def verifier(lettre):
    resultat = []
    for i in range(len(mot)):
        if(mot[i] == lettre):
            resultat.append(i)
    return resultat

proposition()
Merci d'avoir lu, et laissez un message si vous avez un problème avec le code :)