Bienvenue sur la documentation d’urbanotopus !

Documentation générée

La documentation doxygen générée du code C#, est disponible ici.

Téléchargement et démo

Les liens de téléchargement sont disponibles ici, vous pouvez aussi directement lancer le jeu depuis votre navigateur web ici.

Contenu

Le jeu

Urbanotopus est un jeu où le joueur se fond dans l’histoire d’un urbaniste avec une multitude de choix menant à une infinité de dénouements de l’histoire.

L’objectif de ce jeu est d’apprendre au joueur les implications de chaque choix et donc l’important qu’à et que les choix, parfois difficiles de l’urbaniste ont.

Le fonctionnement

Chaque jour, le joueur est présenté à de nouvelles situations ou des situations en continues où le joueur doit prendre une ou des actions qui modifient les variables de sa partie. Ces situations et décisions peuvent influencer en bien ou en mal la partie du joueur en fonction de ses décisions.

Le joueur peut très facilement juger de ses performances à partir des conséquences, mais aussi en lisant ce que les gens postent sur le flux Twitter. Mais encore en regardant les statistiques de la partie.

Le déroulement d’une partie

Après avoir été introduit au jeu, le joueur retourne dans son bureau d’urbaniste, où, il est présenté à de nouvelles tâches et décisions dans l’onglet Histoire, parmi lesquels il doit choisir sa prochaine destination.

L’interface

L’interface est très simple, les boutons principals sont au milieu du bas de l’écran :

  • Histoire, permet de sélectionner le chapitre suivant ou relire un précédent ;
  • Statistiques, permet de voir l’avancement et l’état de la ville ;
  • Twitter, permet de voir les réactions des citoyens face aux décisions et à l’état de la ville.

Puis, de boutons dans le coin du haut à droite, permettant de :

  • Sauvegarder une partie ;
  • Charger une partie.

Et enfin, un bouton dans le coin du haut à gauche, permettant de retourner au menu principal.

Capture d'écran de l'interface principale

Capture d’écran de l’interface principale

Le début de la partie

-

La pleine partie

-

La fin de la partie

-

Le paramétrage d’une partie

-

Conventions du projet

Vous retrouverez ci-dessous toutes les informations sur comment nous travaillons sur notre projet. Pour les conventions de codage, voyez Style de codage.

La répartition des tâches

Les dates limites

Une date limite (deadline) est définie pour chaque ensemble de tâches. Par exemple, dans le tableau de bord d’octobre à novembre, il est défini que cet ensemble doit être prêt pour le 5 novembre 2018.

À partir de la date d’échéance d’un ensemble de tâches, nous imposons que tous les sous-ensembles de tâches aient leur date limite qui soit estimée par rapport à la date limite de l’ensemble parent, en essayant de garder une marge de retard entre les deux dates en cas d’imprévues.

Les tâches

Ensuite, maintenant que les dates des ensembles sont définies, nous arrivons aux tâches des sous-ensembles.

Elles n’ont pas forcément une date limite associée tant quelles sont à faible priorité, donc que d’autres qu’il y a plus de cinq tâches prioritaires non dépendantes. Cela nous permet de donner la possibilité aux personnes de l’équipe de pouvoir s’assigner eux-même aux tâches n’ayant personne d’assigner, grâce à leurs variables :

  • Disponibilités (Université, santé, etc.) ;
  • Difficultés ;
  • Apprentissage sur le tas.

L’objectif principal de cette méthode de travail est d’empêcher toute charge écrasante envers l’équipe qui peut avoir des difficultés à sortir le même débit que d’autres membres, ce qui permet donc à chacun de prendre son temps pour apprendre correctement son travail et les technologies utilisées mais en échange, une plus grande qualité est attendue.

Si, avec prise en compte de la difficulté et donc du temps requis, les dates butoirs des tâches sont trop proches, dans ce cas, les tâches se font assigner par le chef de projet à un (ou des) membres qui répondent le mieux au travail attendu.

Guides de contribution

Merci d’utiliser le système de tickets (issues) et de pull requests pour rapporter des problèmes, suggérer et discuter de modifications et de nouvelles fonctionnalités.

EditorConfig

EditorConfig est un fichier standard de configuration qui a pour but d’assurer un style de codage constant à travers les environnements de développement.

Le projet possède un fichier .editorconfig à sa racine qui décrit nos exigences de formatage de notre code source.

La plupart des éditeurs et IDEs supportent ce fichier, que cela soit par défaut ou par le biais d’un add-on. Pour plus d’informations et d’instructions, consultez la liste des éditeurs et IDEs supportés.

Vous retrouverez notamment cet add-on pour Visual Studio. Dans le cas de l’IDE Rider, le module est déjà préinstallé.

Merci de vous assurer que votre environnement de développement respecte bien ce fichier et corrigera ou vous avertira de tout problème d’indentation, d’encodage, et de fin de ligne.

Style de codage

Python

Suivez toujours la convention PEP 8 dans sa totalité (ceci incluant la limite de 80 caractères sur une ligne).

Chaînes de caractères

Utilisez guillemets simples sauf si la chaîne est une docstring.

Les blocs de code

Merci de suivre le style de formatage “hanging grid”. Comme suit :

some_dict = {
    'one': 1,
    'two': 2,
    'three': 3}
some_list = [
    'foo', 'bar', 'baz']

Merci d’éviter de laisser pendre des parenthèses, crochets, ou virgules. Par exemple, ne faites pas cela :

this_is_wrong = {
   'one': 1,
   'two': 2,
   'three': 3,
}

Cassez les lignes de code directement après les parenthèses. Ne faites donc pas cela :

also_wrong('this is hard',
           'to maintain',
           'as it often needs to be realigned')
Linters

Utilisez :

  1. isort pour maintenir une consistance dans vos imports ;
  2. pylint pour détecter les erreurs dans votre code ;
  3. pycodestyle pour assurer que votre code respecte les conventions PEP 8 ;
  4. pydocstyle pour vérifier que vos docstrings sont correctement formatées ;
  5. eslint pour vérifier votre code Javascript ;
  6. tslint pour vérifier votre code Typescript.

CSharp (C#)

Les blocs de code

Veuillez ne pas retourner à la ligne après l’ouverture d’un crochet puis retourner à la ligne après une fermeture.

Faites :

namespace models {
    public class Employee {
        public Employee(int id) {
            if (id == 0) {
                try {
                    id = id / 0;
                }
                catch(System.DivideByZeroException exc) {
                    // We did not stick `catch(...) {` to `}`.
                }
            }
            else {
                // We did not stick `else {` to `}`.
            }
        }
    }
}

L’accès à l’instance courante

Veuillez utiliser le mot this pour accèder à l’instance courante.

Faites :

public class Employee {
    private string _nameAlias;
    private string _name;

    public Employee(string name, string nameAlias) {
        // Use this to qualify the members of the class
        // instead of the constructor parameters.
        this._alias = name;
        this._nameAlias = nameAlias;
        System.Console.WriteLine(this._name);
    }
}

Et non pas :

public class Employee {
    private string _nameAlias;
    private string _name;

    public Employee(string name, string nameAlias) {
        // Use this to qualify the members of the class
        // instead of the constructor parameters.
        _name = name;
        _nameAlias = nameAlias;
        System.Console.WriteLine(_name);
    }
}

Convention de nommage

Python

  1. Les variables, nom de fonctions et méthodes doivent être écrites en minuscles et les mots séparés par des underscores ;
  2. Les constantes doivent être écrites en majuscules et les mots séparés par des underscores ;
  3. Les noms de classes et d’objets doivent commencer par une majuscule et suivre le format CamelCase ;
  4. Les méthodes et variables privées doivent commencer par un underscore.
CONSTANT = 'abc'

class HelloWorld(object):
    _private_prop = 'hello'

    def __init__(self):
        pass

    def _private_method(self):
        pass

    def say_hi(self):
        pass


def main():
    var = 123

CSharp (C#) et Javascript/ Typescript

  1. Les variables, nom de fonctions et méthodes doivent commencer par une minuscles et suivre le format camelCase ;
  2. Les constantes doivent être écrites en majuscules et les mots séparés par des underscores ;
  3. Les noms de classes, d’objets et de propriétés doivent commencer par une majuscule et suivre le format CamelCase ;
  4. Les méthodes et variables privées doivent commencer par un underscore.
const int CONSTANT = 123;

private class HelloWorld {
    public int SomethingPublic = 123;

    private const _PRIVATE_CONSTANT = 123;
    private int _privateProp;

    HelloWorld() {
        // do something...
    }

    private int _privateMethod() {
        int variableWorld = 123;
        return variableWorld;
    }

    public void say_hi() {
        // do something...
    }
}

Contribuer au code

Ajouter et modifier du contenu dans nos projets

Si vous souhaitez ajouter ou modifier du code ou n’importe quel autre contenu dans nos projets, vous devez tout d’abord commencer par créer une issue avant d’ouvrir une pull request (sauf si le changement est vraiment très mineur, comme une ou des typos, dans ce cas une simple pull request est suffisante).

L’objectif de l’ouverture d’une issue avant l’ouverture d’une pull request est de pouvoir discuter du problème que vous souhaitez résoudre ou de la solution que vous proposez. Cela permet donc d’ouvrir une discussion avant d’ouvrir une pull request qui risque fortement d’être refusée car non convenable et donc demandera une ou des modifications, menant à du travail et temps supplémentaire, et cela salira l’historique git du projet.

De plus, si votre changement apporte des changements ou des ajouts graphiques, veillez à ajouter une ou des captures d’écran ou une vidéo de démonstration.

Warning

Veillez à avoir un message de commit propre et ne pas créer une masse de commits pour des changements mineurs. Quitte à réécrire ou modifier les commits afin de nettoyer ce que vous avez fait. Plus d’informations.

Note

Évitez de pull la branche master dans le but de mettre à jour votre branche ou afin de résoudre des conflits. Préférez plutôt un git rebase -i ce qui permettra de préserver un historique propre.

Comment contrôler (review) les pull requests

Notre méthode standard afin de review une pull request est :

Première étape

Lire le code source et changements à la recherche d’erreurs (bugs ou possibles bugs et typos), de mauvaises pratiques, de violation de convention, de complexités et de code non optimal.

https://i.imgur.com/KScJSW6.png
Deuxième étape

Suggérer des changements si certaines choses semblent mauvaises, pourraient être améliorées ou affinées. Le tout avec des commentaires constructifs.

https://i.imgur.com/AGnYokS.png
Troisième étape

Récupèrer les changements pour les tester et les vérifier localement. Pour cela, assurez-vous que votre clone local du dépôt git a pour upstream remote, la source de base du projet, celui pour lequel la pull request est associée. Pour vérifier, faites git remote -v, vous devriez avoir en upstream fetch le dépôt de source, comme suit :

https://i.imgur.com/zyFhyEz.png

Si vous avez un remote mais pas le bon, faites : git remote remove upstream ;

Si vous n’avez le remote upstream ou vous avez supprimé l’upstream (avec la commande ci-dessus), ajoutez via git remote add URL_DÉPÔT_SOURCE>. Si le projet est Urbanotopus, faites git remote add git@github.com:Urbanotopus/urbanotopus.git.

Vérifiez avec git remote -v.

Ensuite, récupérez le contenu de la pull request en faisant un git fetch upstream pull/ID_DE_LA_PULL_REQUEST/head:NOM_DE_BRANCH puis git checkout NOM_DE_BRANCH.

Par exemple, si la pull request est #2, faites git fetch upstream pull/2/head:test-database puis git checkout test-database.

Testez les changements qui ont été récupérés.

Quatrième étape

Maintenant que vous avez testé les changements, vous pouvez valider ou non les changements. Si vous trouvez un bug ou souci à une ligne donnée ajouter un review à la ligne concernée :

https://i.imgur.com/AGnYokS.png

Si vous avez des commentaires globaux ou vous voulez refuser ou accepter la pull request, donnez votre avis via le pop-up :

https://i.imgur.com/WkGDy2x.png

Documenter le code source

Pour chaque méthode et classe et veillez à ce que les documentations en commentaires restent valides et à jour si vous modifiez la signature et/ ou le corps d’une méthode ou d’une classe.

Documenter le code C# (XML)

Veuillez inclure les commentaires XML standards de C#. Chaque méthode doit au moins inclure un <summary> et tous les paramètres (<param name="something">) ainsi qu’un <returns> si la méthode n’est void.

Formatage des tags XML
  1. Les tags avec une seule ligne doivent commencer et se terminer sur la même ligne.
  2. Les tags sur multiples lignes doivent avoir une ligne vide après l’ouverture et avant la fermeture. Et ne doivent comporter une indentation.
  3. Les tags imbriqués doivent avoir une indentation de 4 espaces.

Exemple :

/// <summary>This has only one line</summary>
/// <remarks>
/// This has multiple
/// lines
/// </remarks>
/// <param name="n">
///     <ul>
///         <li>This is nested</li>
///     </ul>
/// </param>
Exemple complet
/// <summary>Class level summary documentation goes here.</summary>
/// <remarks>
/// Longer comments can be associated with a type or member through
/// the remarks tag.
/// </remarks>
public class TestClass : TestInterface {
    /// <summary>Store for the name property.</summary>
    private string _name = null;

    /// <summary>The class constructor.</summary>
    public TestClass() {
        // TODO: Add Constructor Logic here.
    }

    /// <summary>Name property.</summary>
    /// <value>A value tag is used to describe the property value.</value>
    public string Name {
        get {
            if (_name == null) {
                throw new System.Exception("Name is null");
            }
            return _name;
        }
    }

    /// <summary>Description for SomeMethod.</summary>
    /// <param name="s">Parameter description for s goes here.</param>
    /// <seealso cref="System.String">
    /// You can use the cref attribute on any tag to reference a type or member
    /// and the compiler will check that the reference exists.
    /// </seealso>
    public void SomeMethod(string s) {
    }

    /// <summary>Some other method.</summary>
    /// <returns>Return results are described through the returns tag.</returns>
    /// <seealso cref="SomeMethod(string)">
    /// Notice the use of the cref attribute to reference a specific method.
    /// </seealso>
    public int SomeOtherMethod() {
        return 0;
    }

    /// <summary>The entry point for the application.</summary>
    /// <param name="args">A list of command line arguments.</param>
    static int Main(System.String[] args) {
        // TODO: Add code to start application here.
        return 0;
    }
}

/// <summary>Documentation that describes the interface goes here.</summary>
/// <remarks>Details about the interface go here.</remarks>
interface TestInterface {
    /// <summary>Documentation that describes the method goes here.</summary>
    /// <param name="n">Parameter n requires an integer argument.</param>
    /// <returns>The method returns an integer.</returns>
    int InterfaceMethod(int n);
}

Documenter le code Python avec des docstring Sphinx

Veuillez inclure des docstring du format Sphinx sur Python. Chaque méthode doit au moins inclure une courte description sur ce qu’elle fait et tous les paramètres (:param something: it's something) ainsi qu’un :returns: Blablabla si la méthode retourne quelque chose (plus d’informations).

Formatage de la docstring

Veuillez noter que la description doit être sur la même ligne que sur l’ouverture de la docstring. Par exemple :

"""This is a good way to do.
I'm happy.
"""

Et ne pas faire :

"""
This is a good way to do.
I'm happy.
"""
Exemple complet
"""
.. module:: useful_1
   :platform: Unix, Windows
   :synopsis: A useful module indeed.

.. moduleauthor:: Andrew Carter <andrew@invalid.com>


"""

def public_fn_with_googley_docstring(name, state=None):
    """This function does something.

    Args:
       name (str):  The name to use.

    Kwargs:
       state (bool): Current state to be in.

    Returns:
       int.  The return code::

          0 -- Success!
          1 -- No good.
          2 -- Try again.

    Raises:
       AttributeError, KeyError

    A really great idea.  A way you might use me is

    >>> print public_fn_with_googley_docstring(name='foo', state=None)
    0

    BTW, this always returns 0.  **NEVER** use with :class:`MyPublicClass`.

    """
    return 0

def public_fn_with_sphinxy_docstring(name, state=None):
    """This function does something.

    :param name: The name to use.
    :type name: str.
    :param state: Current state to be in.
    :type state: bool.
    :returns:  int -- the return code.
    :raises: AttributeError, KeyError

    """
    return 0

def public_fn_without_docstring():
    return True

def _private_fn_with_docstring(foo, bar='baz', foobarbas=None):
    """I have a docstring, but won't be imported if you just use ``:members:``.
    """
    return None


class MyPublicClass(object):
    """We use this as a public class example class.

    You never call this class before calling :func:`public_fn_with_sphinxy_docstring`.

    .. note::

       An example of intersphinx is this: you **cannot** use :mod:`pickle` on this class.

    """

    def __init__(self, foo, bar='baz'):
        """A really simple class.

        Args:
           foo (str): We all know what foo does.

        Kwargs:
           bar (str): Really, same as foo.

        """
        self._foo = foo
        self._bar = bar

    def get_foobar(self, foo, bar=True):
        """This gets the foobar

        This really should have a full function definition, but I am too lazy.

        >>> print get_foobar(10, 20)
        30
        >>> print get_foobar('a', 'b')
        ab

        Isn't that what you want?

        """
        return foo + bar

    def _get_baz(self, baz=None):
        """A private function to get baz.

        This really should have a full function definition, but I am too lazy.

        """
        return baz

Documenter le projet et fonctionnalités

Le projet est documenté en utilisant Sphinx. Vous pouvez retrouver les syntaxes sur la documentation du projet sphinx.

Veillez à ce que :

  • Chaque fonctionnalité ajoutée au projet soit documentée au sein de la section des guides.
  • L’architecture doit être documentée, et donc si un changement d’architecture a lieu, veuillez mettre à jour la section Architecture.
  • Si la ou les méthodes de déploiement changent, veillez à mettre à jour la section Déploiement.

Guides de développement

Cette section regroupe nos différents guides pour vous guider au travers des différents mécanismes mis en place par le projet et vous assister dans le développement de nouvelles fonctionnalités.

Scènes de Visual Novel

Ajouter un script

Dans le dossier Assets/YarnScripts, créez un nouveau script Yarn ayant pour extension de fichier .yarn.txt (important).

Modifier un script (Yarn)

Capture d'écran du menu Unity

Capture d’écran du menu Unity

Les scripts sont écrits dans le langage Yarn (documentation).

Afin d’éditer les nodes et scripts, utilisez l’éditeur Merino qui est inclut avec le projet dans le menu Window : Window > Merino (Yarn Editor).

Les dialogues

Un dialogue se compose ainsi :

Eve: Hey, I'm a character speaking in a dialogue!
Bob: Wow! Me too!
I'm a narrative guy I guess...
Les nœux (nodes)
  • Le node de base : Start

    Eve: Hi!
    [[ NodeInTheWoods ]]
    Eve: I'm back.
    

    Ce node instruit Eve a dire bonjour et à aller dans les bois, donc dans le node NodeInTheWoods, Eve ne reviendra jamais des bois, car c’est un GOTO.

  • Le node dans les bois : NodeInTheWoods

    Eve: the woods are so scary...
    
Les choix de nœux

Si nous souhaitons laisser le choix à l’utilisateur entre aller dans le nœu de la maison hantée ou dans le nœu de la plage on fera :

Eve: where should I go...
[[ The haunted house | HauntedHouse ]]
[[ The beach | Beach ]]
Les choix simples

Si vous souhaitez faire en sorte que l’utilisateur puisse choisir pour executer un dialogue puis retourner à l’exécution normale, faites :

Mae: What did you say to her?
-> Nothing.
    Mae: Oh, man. Maybe you should have.
-> That she was an idiot.
    Mae: Hah! I bet that pissed her off.
Mae: Anyway, I'd better get going.
Les choix conditionnels

Si vous souhaitez afficher des actions seulement dans certains cas, vous avez la possibilité de faire cela :

<< set $money = 2 >>
Bob: What would you like?
-> A burger. << if $money >= 5 >>
    Bob: Nice. Enjoy!
-> A soda. << if $money >= 2 >>
    Bob: Yum!
-> Nothing.
Bob: Thanks for coming!
Les déclarations de variables et de conditions

Vous pouvez déclarer des variables avec des expressions et des conditions avec les mots if, elseif et else.

<< set $number_of_hostages = 12 >>
<< set $hostages_saved = 1 >>
<< set $hostages_saved = $number_of_hostages / 2 >>

<< if $hostages_saved == $number_of_hostages and $time_remaining > 0 >>
    You win the game!
<< elseif $hostages_saved < $number_of_hostages and $time_remaining > 0 >>
    You need to rescue more hostages!
<< elseif $bomb_has_exploded == 0 >>
    You failed to rescue the hostages before time ran out!
<< else >>
    You failed everything.
<< endif >>

Gestion des décisions

Répertoriage des choix possibles

Afin de permettre une consistance simple et efficace des choix possibles entre les scripts Yarn et les bases de données côté serveur, nous utilisons un mécanisme de répertoriage de ces derniers.

Méta-données d’un chapitre

Dans un fichier, Assets/ChapterData/<chapter_id>/questions.json, vous retrouverez et respecterez la structure suivante :

Assets/ChapterData/<chapter_id>/questions.json
1
2
3
4
5
6
7
{
    "<question_id>": {
        "<answer_id>": {
            "description": "something"
        }
    }
}

Sauvegarde et propagation d’une décision

Lorsque le joueur prend une décision, elle doit être annoncée en accordance avec les méta-données d’un chapitre par le biais de l’instruction Yarn suivante :

<< register_choice Globals <chapter_id> <question_id> <answer_id> >>

Le jeu se chargera ensuite de la serialisation et de la communication avec le serveur distant (en tâche asynchrone).

Note

Veillez à ce que le prefab Globals soit disponible dans votre scène.

Charger des scènes

Pour charger des scènes, vous devez utiliser Managers.InternalScenesManager.LoadScene(name: string) afin de correctement prendre en charge les longues étapes de chargement, et non pas geler l’écran de l’utilisateur. Voici la définition :

class Managers.InternalScenesManager
MainMenu = "MainMenuScene";

Le nom de la scène du menu principal.

Office = "OfficeScene";

Le nom de la scène du bureau de l’urbaniste.

VisualNovel = "VisualNovelScene";

Le nom de la scène du bureau de visual novel.

LoadScene(name: string)
Parameters:name – Le nom de la scène à charger.

Déploiement

Ressources

Cette page contient les liens vers les différentes ressources du projet disponibles sur Google Drive.

  1. Le cahier des charges fonctionnel ;
  2. Tableau de bord des tâches du projet.