Aller au contenu principal

Initialisation du projet et début des prototypes

· 5 minutes de lecture
Valentin Beauget
Valentin Beauget
Audric Fullhardt
Audric Fullhardt
Gabriel Maillard
Gabriel Maillard
Marius Nogueron
Marius Nogueron

Valentin

J'ai mis le site web en production sur mon VPS.

Nous voulions directement pouvoir déployer le site avec un push sur le repository Github.

Nous utilisons Docusaurus, l'équivalent d'un Gitbook.

La problématique avec ce dernier est qu'il a quelques exigences concernant son déploiement.

Ce dernier doit être npm install et build npm run build.

cicd

Pour tout le processus de déploiement, j'utilise n8n

ci.yml
name: CI/CD call n8n auto deploy

on:
push:
branches: - main
pull_request:

jobs:
build-and-test:
runs-on: ubuntu-latest

steps:
- name: Trigger n8n webhook
run: curl -fsSL "https://n8n.beauget.fr/webhook/d81232cf-0467-4dd0-9d23-2d52acc5cabf"

J'ai également réalisé un premier prototype : 'SlotMachine' où l'utilisateur observe une roulette avec 3 slots faisant défiler des fruits et doit appuyer 3 fois d'affilée à un moment précis lorsqu'un 7 apparaît pour réussir le mini-jeu.

7

Gabriel

Je me suis occupé de créer une bonne base que ce soit dans la hiérarchisation des éléments du jeu ou dans les méthodes communes réutilisables par tous les autres dans la création des mini-jeux (GameManager).

GameManager.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
using System;

public class GameManager : MonoBehaviour
{
[Header("Config Initiale")]
[SerializeField] public int startingLives = 2;
[SerializeField] public int difficulty = 1;
[SerializeField] private int lives;
public int Lives { get; private set; }

[Header("Timer Partagé")]
[SerializeField] private bool timerRunning;
public bool TimerRunning { get; private set; }
[SerializeField] private float remainingTime;
public float RemainingTime { get; private set; }
[SerializeField] private float duration;
public float Duration { get; private set; }
private Coroutine _timerCo;
public event Action OnTimerEnded;
public event Action<float> OnTimerTick;

[Header("Leaderboard")]
[SerializeField] private int roundsPlayed;
public int RoundsPlayed { get; private set; }

[Header("UI")]
[SerializeField] public TimerUI timerUI;
[SerializeField] private LivesUI livesUI;

//ACTION À EFFECTUER À LA FIN D'UN MINI-JEU
public event Action OnMinigameWon;
public event Action OnMinigameFailed;

void Awake()
{
Lives = startingLives;
livesUI?.SetLives(Lives);
RoundsPlayed = 0;
}

public void AddRound()
{
RoundsPlayed++;
//ON POURRA RAJOUTER D'AUTRES ACTIONS AU CHANGEMENT DE ROUND ICI
}

//GESTION DES VIES
public void LoseLife()
{
Lives--;
livesUI?.SetLives(Lives);
}

public void ResetLives()
{
Lives = startingLives;
livesUI?.SetLives(Lives);
}

//GESTION DU TIMER
public void StartTimer(float seconds)
{
StopTimer(); //pour être sur qu'on en a pas deux qui tournent
Duration = Mathf.Max(0f, seconds);
RemainingTime = Duration;
TimerRunning = true;
timerUI?.Show(Duration);
_timerCo = StartCoroutine(CoTimer());
}

public void StopTimer()
{
if (_timerCo != null) StopCoroutine(_timerCo);
_timerCo = null;
TimerRunning = false;
timerUI?.Hide();
}

IEnumerator CoTimer()
{
while (RemainingTime > 0f)
{
RemainingTime -= Time.deltaTime;
if (RemainingTime < 0f) RemainingTime = 0f;
timerUI?.UpdateTime(RemainingTime, Duration);
OnTimerTick?.Invoke(RemainingTime);
yield return null;
}
TimerRunning = false;
OnTimerEnded?.Invoke();
}

//ACTIONS QUI SE LANCENT QUAND ON GAGNE OU PERD UN MINI-JEU
public void NotifyWin()
{
StopTimer();
OnMinigameWon?.Invoke();
}

public void NotifyFail()
{
StopTimer();
OnMinigameFailed?.Invoke();
}

//TOUT CE QUI EST EN DESSOUS : NE PAS UTILISER - A REFACTORER SUR MON MINI-JEU
public TMP_Text livesText;

void Update()
{
livesText.text = "Vies: " + lives;
}
}

J'ai également réalisé un prototype 'TriPommePoire'. Un fruit aléatoire entre ces deux apparaît à l'écran et la personne doit choisir rapidement entre le panier de gauche contenant les pommes ou celui de droite avec les poires.

tripommepoire

Audric

J'ai créé l'API en Node.js pour implémenter une communication directe entre la borne et le site. En l'occurrence, le site va afficher les leaderboard.

const express = require("express");
const cors = require("cors");
const data = require("./data.json");
const fs = require("fs");
require("dotenv").config();

const app = express();
const PORT = 3001;

app.use(cors());
app.use(express.json());

app.get("/", (req, res) => {
res.send("API !");
});

let leaderboard = data;

function checkApiKey(req, res, next) {
const apiKey = req.headers["x-api-key"];
if (apiKey && apiKey === process.env.API_KEY) {
next();
} else {
res.status(403).json({ error: "Clé API manquante ou invalide" });
}
}

app.get("/leaderboard", (req, res) => {
leaderboard.sort((a, b) => b.score - a.score);
res.json(leaderboard);
});

app.post("/leaderboard", checkApiKey, (req, res) => {
let { name, score } = req.body;
if (!name || typeof score !== "number") {
return res.status(400).json({ error: "Nom et score requis" });
}
if (name.length > 3) {
name = name.substring(0, 3);
}
name = name.toUpperCase();
const existingEntry = leaderboard.find((entry) => entry.name === name);

if (existingEntry) {
if (score > existingEntry.score) {
existingEntry.score = score;
} else {
return res.json({
success: false,
message: "Score non mis à jour (inférieur ou égal au score existant).",
leaderboard,
});
}
} else {
leaderboard.push({ name, score });
}
leaderboard.sort((a, b) => b.score - a.score);
fs.writeFileSync("./data.json", JSON.stringify(leaderboard, null, 2));
res.json({ success: true, leaderboard });
});

app.listen(PORT);

api leaderboard

J'ai fait un prototype en 3D d'un jeu type 'Dropper' où l'on doit ajouter les différentes balles dans les cylindres avec un bon timing.

dropper

Marius

J'ai créé le site web. On utilise docusaurus, un outil similaire à GitBook qui supporte nativement la gestion d'un blog.

J'ai commencé à créer le prototype d'un jeu 'MentalMath' qui est un jeu de calcul mental similaire à "l'Entraînement Cérébral du Dr Kawashima". Un calcul assez simple, 3 propositions, si le choix est bon, apparition d'un visage joyeux et inversement.

Entraînement Cérébral du Dr Kawashima

J'ai réutilisé les méthodes créées par Gabriel pour mon `CalculManager'

CalculManager.cd
using UnityEngine;

public class CalculManager : MonoBehaviour
{
[SerializeField] private GameManager gameManager;
[SerializeField] private CalculLogic calculLogic;
[SerializeField] private CalculUIManager calculUIManager;

void Start()
{
gameManager.StartTimer(10f);
GenerateNewCalculation();
}

public void GenerateNewCalculation()
{
var calcul = calculLogic.GenerateCalculation();
calculUIManager.DisplayCalculation(calcul);
}

public bool OnAnswerSelected(int index)
{
bool correct = calculLogic.CheckAnswer(index);

if (correct)
{
gameManager.NotifyWin();
}
else
{
gameManager.LoseLife();
gameManager.NotifyFail();
}

return correct;
}
}

Commencement du projet

· Une minute de lecture
Marius Nogueron
Marius Nogueron

Première réflexion sur le jeu : Réflexions

Après réflexion, nous proposons un Wario Ware like, une multitude de mini-jeux très rapide que l'on enchaine avec un certain nombre de vie (souvent quelques secondes par jeux avec une difficultée croissante). Il en va donc de soit que ces mini-jeux doivent être compréhensible au premier coup d'oeil.

Nouveaux prototypes et Création d'un Scène Loader

· 3 minutes de lecture
Valentin Beauget
Valentin Beauget
Audric Fullhardt
Audric Fullhardt
Gabriel Maillard
Gabriel Maillard
Marius Nogueron
Marius Nogueron

Valentin

Optimisation de la CI/CD pour un déploiement en production plus rapide du site web.

optimisation CI/CD

Cette dernière était hébergée sur mon site mon VPS, j'ai fait en sorte que le déploiement et le host soient sur mon serveur. Le VPS était trop lent et n'avait pas assez de place pour supporter la charge

Implémentation de la logique pour le retour menu :

  • Appui du bouton échap (correspondant au bouton blanc sur la borne)
  • Système d'anti afk, nous ramenant au menu au bout de 200 secondes.

optimisation CI/CD

Bug des listeners audio multiples réglé. Ces derniers se créaient en masse dans une scène lors du lancement de manière dynamique

J'ai donc créé des fonctions qui s'assurent qu'il n'y en ait juste un.

Audric

Avec Valentin : Création du Loader de minijeu (code ci-dessous) :

ScenesLoader.cd
using UnityEngine;
using UnityEngine.SceneManagement;
using System.Collections;

public class ScenesLoader : MonoBehaviour
{
// [SerializeField] private GameObject sceneContainer;
public GameObject sceneContainer; // Ton GameObject vide
public Vector3 targetScale = new Vector3(0.5f, 0.5f, 1f);

private bool SceneLoaded = false;

public void LoadMiniGame(string sceneName)
{
StartCoroutine(LoadMiniGameCoroutine(sceneName));
}

IEnumerator LoadMiniGameCoroutine(string sceneName)
{
// Charge la scène additive
AsyncOperation asyncLoad = SceneManager.LoadSceneAsync(sceneName, LoadSceneMode.Additive);
while (!asyncLoad.isDone)
yield return null;

// Récupère la scène
Scene miniScene = SceneManager.GetSceneByName(sceneName);

// Parent tous les objets racines au container
foreach (GameObject go in miniScene.GetRootGameObjects())
{
if (go.name == "Main Camera")
{
Destroy(go);
continue;
}
; // Ignore la caméra principale
go.transform.SetParent(sceneContainer.transform, false);
}
sceneContainer.transform.localScale = new Vector3(0.5f, 0.5f, 1f);
}

public void UnloadMiniGame(string sceneName)
{
SceneManager.UnloadSceneAsync(sceneName);
foreach (Transform child in sceneContainer.transform)
{
Destroy(child.gameObject);
}
}
}

Intégration dans le GameManager + Récupération et affichage random des jeux (code ci-dessous) :

GameManager.cd
private string GetRandomGame()
{
string[] scenesList = Directory.GetFiles("Assets/Scenes/MiniGames", "\*.unity");
for (int i = 0; i < scenesList.Length; i++)
{
scenesList[i] = Path.GetFileNameWithoutExtension(scenesList[i]);
}

System.Random rand = new System.Random();
int index = rand.Next(scenesList.Length);

if (currentGame == scenesList[index])
{
index = (index + 1) % scenesList.Length;
}

return scenesList[index];

}

if (Input.GetButtonDown("P1_B6"))
{
string nextGame = GetRandomGame(); //ou alors le jeu que vous voulez tester comme ça : nextGame = "SlotMachine";
scenesLoader.LoadMiniGame(nextGame);
currentGame = nextGame;
}
if (Input.GetButtonDown("P1_B3"))
{
if (currentGame != "")
{
scenesLoader.UnloadMiniGame(currentGame);
}
}

Gabriel

Faire en sorte que mes mini-jeux aient le bon système de timer.

Réglage d'un bug qui lançait le timer et certaines logiques de MentalMath à l'allumage du jeu et non pas au load de la scène de MentalMath.

Je commence la réalisation d'assets graphiques pour un mini jeu pour Valentin (celui de la bouteille de champagne)

Marius

Mise à jour du site Blog et ésthétique de ce dernier, ajout d'un logo + Création en cours d'un nouveau jeu Taiko

Ce dernier, en ajoutant une musique dans un fichier grâce à un algo, créera deux types de boules de couleurs qui passeront de gauche à droite et qui devront être cliqué à un instant t.

setup unity

En fonction de la précision de la pression, retour visuel.

retour visuel sur la précision d&#39;un click

Il y a aussi un facteur de difficulté qui sera en accord avec un facteur de difficulté défini au début de la partie.