[{"data":1,"prerenderedAt":658},["ShallowReactive",2],{"projects-virusmania-fr":3},{"id":4,"title":5,"body":6,"category":641,"date":642,"description":643,"extension":644,"image":645,"meta":646,"navigation":647,"path":648,"repo":649,"role":650,"seo":651,"slug":652,"stem":653,"tags":654,"__hash__":657},"projects\u002Fprojects\u002Fvirusmania.fr.md","VirusMania",{"type":7,"value":8,"toc":632},"minimark",[9,14,32,36,39,44,47,51,54,58,70,74,77,88,93,97,102,106,109,112,120,123,128,131,136,139,143,147,152,200,205,209,218,221,236,241,245,248,254,259,268,272,277,281,286,290,293,309,314,318,322,325,328,333,337,340,344,368,372,395,399,428,432,445,449,461,466,470,473,480,485,488,490,492,494,496,498,500,502,504,506,508,510,512,514,516,518,520,522,524,526,528,530,532,534,536,538,540,542,544,546,548,550,552,554,556,558,560,562,564,566,568,570,572,574,576,578,580,584,590,595,601,606,612,617,623,628],[10,11,13],"h1",{"id":12},"introduction","Introduction",[15,16,17,18,25,26,31],"p",{},"Le but de ce projet, au départ, était de donner une suite à un jeu que nous avions réalisé avec un ami (hello Théo si tu passes par là). Nous avons donc rejoint une Game Jam sur itch.io ayant pour thème la transformation, un peu naïvement : ce n'est qu'à la fin de la Jam que nous avons compris exactement son sous-texte. Nous étions néanmoins lancés et c'est ainsi qu'est née cette idée un peu étrange d'un jeu cartoon dans lequel le personnage principal est un scientifique enfermé par l'État pour créer des virus capables de contrôler la population. Le jeu s'inspire grandement de titres comme ",[19,20,24],"a",{"href":21,"rel":22},"https:\u002F\u002Fstore.steampowered.com\u002Fapp\u002F1599600\u002FPlateUp\u002F?l=french",[23],"nofollow","PlateUp!"," ou même ",[19,27,30],{"href":28,"rel":29},"https:\u002F\u002Fstore.steampowered.com\u002Fapp\u002F448510\u002FOvercooked\u002F",[23],"OverCooked",". Pour la répartition des rôles, Théo voulait se concentrer sur la modélisation 3D tandis que, comme d'habitude, je me chargeais de la partie technique.",[10,33,35],{"id":34},"les-débuts","Les débuts",[15,37,38],{},"Pour commencer, nous sommes partis sur Unity car, même si nous avions un mois pour réaliser ce projet, nos rythmes scolaire et professionnel ne nous permettaient pas d'explorer sereinement une nouvelle technologie tout en livrant quelque chose de propre. Comme nous avions déjà travaillé ensemble sur un jeu Unity, nous avons pu reprendre nos marques très vite. Après avoir défini le gameplay, nous avons produit un prototype au plus vite : Théo s'est occupé du personnage pendant que je construisais le reste de la logique.",[40,41],"terminal-video-player",{"src":42,"title":43},"\u002Fvirusmania_media\u002Fles_debuts.webm","virusmania-prototype.mp4",[15,45,46],{},"À partir de ce premier prototype, une chose était déjà presque sûre : nous étions partis un peu trop loin dans nos ambitions. Nous avons donc décidé d'agrandir nos rangs et de travailler avec de nouvelles personnes.",[10,48,50],{"id":49},"léquipe-sagrandit","L'équipe s'agrandit",[15,52,53],{},"C'est ainsi que Médéric et Danielle ont rejoint l'aventure pour donner vie à l'idée un peu folle de deux développeurs qui s'ennuyaient un jeudi matin. Le rythme de production global du jeu s'est alors nettement accéléré.",[40,55],{"src":56,"title":57},"\u002Fvirusmania_media\u002Fanimation_movement.webm","animation-movement.mp4",[59,60,61],"blockquote",{},[15,62,63,64,69],{},"Pour mieux nous organiser, nous avons migré d'un GitHub Project à ",[19,65,68],{"href":66,"rel":67},"https:\u002F\u002Flinear.app",[23],"Linear",",\nun outil de gestion de projet simple et sobre : pas le plus puissant, mais exactement ce qu'il nous fallait.",[10,71,73],{"id":72},"début-des-problèmes-1-les-metaballs","Début des problèmes 1 - Les metaballs",[15,75,76],{},"Pour faire des virus qui se mélangent correctement, on a réfléchi... pas suffisamment longtemps. Nous avons décidé de partir sur un système de metaballs où chaque virus fusionne proprement.\nMais les metaballs, c'est quoi ? Pour faire simple, voici un gif qui les illustre parfaitement :",[78,79,84],"div",{"className":80},[81,82,83],"w-full","flex","justify-center",[85,86],"img",{"src":87},"\u002Fvirusmania_media\u002Fgifmetaball.gif",[59,89,90],{},[15,91,92],{},"Efficace, non ? Eh bien c'était tellement galère à faire que nous avons décidé de mettre ça de côté pour la première version.",[40,94],{"src":95,"title":96},"\u002Fvirusmania_media\u002Fmetaball.webm","metaball.mp4",[59,98,99],{},[15,100,101],{},"L'un de nos rares essais, qui consommait beaucoup trop de performances pour un rendu visuel finalement assez limité.",[10,103,105],{"id":104},"début-des-problèmes-2-le-style-cartoon","Début des problèmes 2 - Le style cartoon",[15,107,108],{},"Le style cartoon, c'est mignon... mais concrètement, comment est-ce qu'on en fabrique un qui soit potable ?",[15,110,111],{},"Prenons l'exemple d'un jeu à peine connu : Fortnite.",[15,113,114,115,119],{},"On y constate deux éléments clés : un contour (outline) et, côté ombrage, un rendu par paliers (foncé, moins foncé, pas foncé, ...). En soi ce n'est pas très compliqué à faire, mais c'est compliqué à ",[116,117,118],"strong",{},"bien"," faire.",[15,121,122],{},"Direction donc une petite veille pour voir s'il existe des shaders intéressants pour ça !",[124,125,127],"h2",{"id":126},"le-toonshading","Le ToonShading",[15,129,130],{},"Concernant le ToonShading (l'effet d'ombrage par étapes évoqué plus haut), on trouve déjà des ressources, mais aucune ne nous satisfaisait vraiment. Nous sommes donc partis créer notre propre ToonShader sur Unity !",[59,132,133],{},[15,134,135],{},"Disclaimer : je ne connaissais pas du tout le langage de programmation de shaders utilisé par Unity ni toutes les subtilités associées, et j'étais encore moins à l'aise à l'idée de faire un Shader Graph.",[15,137,138],{},"Pour ce shader, la consigne était simple : obtenir un rendu de lumière étape par étape sur un objet. Sur le papier, c'est assez straightforward.",[40,140],{"src":141,"title":142},"\u002Fvirusmania_media\u002Ftoon_on_metaball.webm","toon1.mp4",[40,144],{"src":145,"title":146},"\u002Fvirusmania_media\u002Ftoon_on_guy.webm","toon2.mp4",[59,148,149],{},[15,150,151],{},"Et hop, on tenait notre shader de Toon ! Avec certes quelques défauts\n(actuellement il ne prend pas en compte l'ombre si un objet se trouve\ndevant), mais c'était suffisant et nous n'avions pas besoin de mieux !",[153,154,159],"pre",{"className":155,"code":156,"language":157,"meta":158,"style":158},"language-js shiki shiki-themes github-dark","\u002F\u002F On prend la direction de la lumière light\nvar mainLightDirection = GetMainLight().direction;\n\u002F\u002F Et ici on obtient de \"combien la surface regarde vers la lumière\"\nvar lookAtLight = saturate(dot(N, -mainDir));\n\u002F\u002F On découpe pour avoir un dégradé avec des steps plutôt que quelque chose de lisse\nvar render = floor(lookAtLight * steps) \u002F (steps - 1.0);\n","js","",[160,161,162,170,176,182,188,194],"code",{"__ignoreMap":158},[163,164,167],"span",{"class":165,"line":166},"line",1,[163,168,169],{},"\u002F\u002F On prend la direction de la lumière light\n",[163,171,173],{"class":165,"line":172},2,[163,174,175],{},"var mainLightDirection = GetMainLight().direction;\n",[163,177,179],{"class":165,"line":178},3,[163,180,181],{},"\u002F\u002F Et ici on obtient de \"combien la surface regarde vers la lumière\"\n",[163,183,185],{"class":165,"line":184},4,[163,186,187],{},"var lookAtLight = saturate(dot(N, -mainDir));\n",[163,189,191],{"class":165,"line":190},5,[163,192,193],{},"\u002F\u002F On découpe pour avoir un dégradé avec des steps plutôt que quelque chose de lisse\n",[163,195,197],{"class":165,"line":196},6,[163,198,199],{},"var render = floor(lookAtLight * steps) \u002F (steps - 1.0);\n",[59,201,202],{},[15,203,204],{},"Une version extrêmement simplifiée écrite en pseudo-code JS.\nL'idée est de vérifier si la texture fait face à la lumière et, au lieu du dégradé habituel, d'arrondir la valeur avec notre système. On n'obtient ainsi que des zones de couleurs plates.",[124,206,208],{"id":207},"loutline","L'outline",[15,210,211,212,217],{},"Pour l'outline, ",[19,213,216],{"href":214,"rel":215},"https:\u002F\u002Fgithub.com\u002Fchrisnolet\u002Fquickoutline",[23],"QuickOutline"," existe déjà. On pensait donc s'embêter un peu moins. (Spoiler : non.)",[15,219,220],{},"Pour faire simple, QuickOutline fusionne les lignes des différents objets. Donc si tu as deux objets complètement différents côte à côte avec QuickOutline, leurs outlines finissent par se rejoindre...",[15,222,223,224,229,230,235],{},"Heureusement, ",[19,225,228],{"href":226,"rel":227},"https:\u002F\u002Fgithub.com\u002Fzacccharv",[23],"zacccharv",", un contributeur, a proposé une version modifiée de la librairie qui règle ce souci !\n(",[19,231,234],{"href":232,"rel":233},"https:\u002F\u002Fgithub.com\u002Fchrisnolet\u002FQuickOutline\u002Fpull\u002F45",[23],"La pull request",")",[59,237,238],{},[15,239,240],{},"Bon, ça n'a pas tout réglé, nous avons dû faire quelques ajustements par-dessus, mais c'est grâce à cette impulsion que le problème s'est débloqué, donc immense merci à lui.",[10,242,244],{"id":243},"user-interface","User Interface",[15,246,247],{},"Pour l'UI, Médéric a eu une première idée : des conteneurs fluides, animés.",[78,249,251],{"className":250},[81,82,83],[85,252],{"src":253},"\u002Fvirusmania_media\u002Ffirst_ui.png",[59,255,256],{},[15,257,258],{},"Sauf que là, on a un souci : on l'importe comment ? Vidéo ? Une vidéo pour chaque conteneur ? Un SVG ? Et on fait comment pour que ce soit un minimum responsive ?",[15,260,261,262,267],{},"Nous avons donc pris la décision de recoder un shader pour l'UI afin d'obtenir cet effet !\nOn s'épargne les détails techniques (le shader est dispo ",[19,263,266],{"href":264,"rel":265},"https:\u002F\u002Fgithub.com\u002Fmethil-mods\u002Fvirusmania\u002Fblob\u002Fb2a17c13c57f273e7c823add9de65a9161a79670\u002Funity\u002FAssets\u002FResources\u002FShaders\u002FMethilUiWavyBlob.shader",[23],"ici",").",[40,269],{"src":270,"title":271},"\u002Fvirusmania_media\u002Fessai_rate_ui.webm","essai_ui_bad.mp4",[59,273,274],{},[15,275,276],{},"Ça, c'était raté par exemple.",[40,278],{"src":279,"title":280},"\u002Fvirusmania_media\u002Fessai_reussi.webm","essai_ui_good.mp4",[59,282,283],{},[15,284,285],{},"Ça, c'était vraiment mieux — et paramétrable !",[10,287,289],{"id":288},"gameplay","Gameplay",[15,291,292],{},"Il a ensuite fallu mettre tout cela en forme pour obtenir un gameplay presque « final », histoire d'avoir un vrai jeu plutôt qu'un assemblage de morceaux éparpillés. Nous sommes donc partis sur une boucle de gameplay simple :",[294,295,296,300,303,306],"ol",{},[297,298,299],"li",{},"Récupérer un brief de création de virus",[297,301,302],{},"Acheter ce qu'il faut pour fabriquer le virus",[297,304,305],{},"Combiner les éléments pour créer le virus",[297,307,308],{},"Envoyer le virus",[59,310,311],{},[15,312,313],{},"Tout simple !",[40,315],{"src":316,"title":317},"\u002Fvirusmania_media\u002Fvirusmania_game.webm","onboarding.mp4",[10,319,321],{"id":320},"lonboarding","L'onboarding",[15,323,324],{},"Et oui, ce n'est pas fini : nous devions encore nous attaquer à l'onboarding, l'une des parties les plus intéressantes, car elle oblige à comprendre comment l'utilisateur va apprendre à interagir avec nos mécaniques de jeu. Parfois, elle révèle même qu'une mécanique est mal pensée.",[40,326],{"src":327,"title":317},"\u002Fvirusmania_media\u002Fonboarding.webm",[59,329,330],{},[15,331,332],{},"Une petite vidéo de la première version de l'onboarding avant qu'elle ne soit démolie par tous les retours !",[10,334,336],{"id":335},"remerciements","Remerciements",[15,338,339],{},"C'était long, mais voilà : un mois intensif à travailler sur le projet. Je n'ai pas mentionné tout le monde parce qu'au fil du temps, de nouvelles personnes se sont greffées à l'équipe, donc voici les remerciements :",[124,341,343],{"id":342},"development","Development",[345,346,347,358],"ul",{},[297,348,349,352,353],{},[116,350,351],{},"Ethan Carollo"," — Lead Developer & Project Manager — ",[19,354,357],{"href":355,"rel":356},"https:\u002F\u002Fgithub.com\u002FEthanCarollo",[23],"GitHub",[297,359,360,363,364],{},[116,361,362],{},"Théo Lessage"," — Developer — ",[19,365,357],{"href":366,"rel":367},"https:\u002F\u002Fgithub.com\u002Fnak0x",[23],[124,369,371],{"id":370},"art-direction-design","Art Direction & Design",[345,373,374,385],{},[297,375,376,379,380],{},[116,377,378],{},"Médéric Chapiseau"," — Visual Art Direction, main 3D models, game UI — ",[19,381,384],{"href":382,"rel":383},"https:\u002F\u002Fmedericchapiseau.com",[23],"Portfolio",[297,386,387,390,391],{},[116,388,389],{},"Danielle Levinzon"," — Main menu design & itch.io page — ",[19,392,384],{"href":393,"rel":394},"https:\u002F\u002Fdaniellelevinzon.github.io\u002FPortfolio",[23],[124,396,398],{"id":397},"_3d-modeling","3D Modeling",[345,400,401,409,418],{},[297,402,403,405,406],{},[116,404,362],{}," — 3D Modeling — ",[19,407,357],{"href":366,"rel":408},[23],[297,410,411,405,414],{},[116,412,413],{},"Mathieu",[19,415,357],{"href":416,"rel":417},"https:\u002F\u002Fgithub.com\u002Fmath-pixel",[23],[297,419,420,423,424],{},[116,421,422],{},"Ugo Mollier-Billet"," - 3D Modeling — ",[19,425,384],{"href":426,"rel":427},"https:\u002F\u002Fwww.ugoportfolio.fr",[23],[124,429,431],{"id":430},"sound-design","Sound Design",[345,433,434],{},[297,435,436,439,440],{},[116,437,438],{},"P-A"," — Sound production & full sound design — ",[19,441,444],{"href":442,"rel":443},"https:\u002F\u002Fsoundcloud.com\u002Fpaaaa_als",[23],"SoundCloud",[124,446,448],{"id":447},"testing-ideas","Testing & Ideas",[345,450,451],{},[297,452,453,456,457],{},[116,454,455],{},"Emmanuel Moulin"," — Testing, idea proposals & README writing 😎 — ",[19,458,357],{"href":459,"rel":460},"https:\u002F\u002Fgithub.com\u002FKibishi47",[23],[59,462,463],{},[15,464,465],{},"C'était la première fois que je travaillais sur un projet perso avec autant de monde, mais cela nous a permis de créer un groupe avec sa propre identité, et j'en suis très content.",[10,467,469],{"id":468},"jouer","Jouer !",[15,471,472],{},"La Jam est terminée, le jeu est envoyé !",[15,474,475,476],{},"Voici le lien itch.io où le jeu est disponible. N'hésitez pas à laisser\nun commentaire si le jeu vous plaît :\n",[19,477,478],{"href":478,"rel":479},"https:\u002F\u002Fmethil-group.itch.io\u002Fvirusmania",[23],[59,481,482],{},[15,483,484],{},"Merci d'avoir lu ^^'",[486,487],"br",{},[486,489],{},[486,491],{},[486,493],{},[486,495],{},[486,497],{},[486,499],{},[486,501],{},[486,503],{},[486,505],{},[486,507],{},[486,509],{},[486,511],{},[486,513],{},[486,515],{},[486,517],{},[486,519],{},[486,521],{},[486,523],{},[486,525],{},[486,527],{},[486,529],{},[486,531],{},[486,533],{},[486,535],{},[486,537],{},[486,539],{},[486,541],{},[486,543],{},[486,545],{},[486,547],{},[486,549],{},[486,551],{},[486,553],{},[486,555],{},[486,557],{},[486,559],{},[486,561],{},[486,563],{},[486,565],{},[486,567],{},[486,569],{},[486,571],{},[486,573],{},[486,575],{},[486,577],{},[486,579],{},[10,581,583],{"id":582},"bonus","Bonus",[78,585,587],{"className":586},[81,82,83],[85,588],{"src":589},"\u002Fvirusmania_media\u002Fvirusglow.png",[59,591,592],{},[15,593,594],{},"Un virus qui brille",[78,596,598],{"className":597},[81,82,83],[85,599],{"src":600},"\u002Fvirusmania_media\u002Faccueil_page.png",[59,602,603],{},[15,604,605],{},"La page d'accueil (Danielle au dessin !)",[78,607,609],{"className":608},[81,82,83],[85,610],{"src":611},"\u002Fvirusmania_media\u002Fcolor_virusmania.png",[59,613,614],{},[15,615,616],{},"Une façon de communiquer les couleurs, hmmm... singulière",[78,618,620],{"className":619},[81,82,83],[85,621],{"src":622},"\u002Fvirusmania_media\u002Ffourmimania.png",[59,624,625],{},[15,626,627],{},"Le vrai logo de Fourmimania",[629,630,631],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":158,"searchDepth":172,"depth":172,"links":633},[634,635,636,637,638,639,640],{"id":126,"depth":172,"text":127},{"id":207,"depth":172,"text":208},{"id":342,"depth":172,"text":343},{"id":370,"depth":172,"text":371},{"id":397,"depth":172,"text":398},{"id":430,"depth":172,"text":431},{"id":447,"depth":172,"text":448},"Game Dev","2025-12-01","Le but de ce projet, au départ, était de donner une suite à un jeu que nous avions réalisé avec un ami (hello Théo si tu passes par là). Nous avons donc rejoint une Game Jam sur itch.io ayant pour thème la transformation, un peu naïvement : ce n'est qu'à la fin de la Jam que nous avons compris exactement son sous-texte. Nous étions néanmoins lancés et c'est ainsi qu'est née cette idée un peu étrange d'un jeu cartoon dans lequel le personnage principal est un scientifique enfermé par l'État pour créer des virus capables de contrôler la population. Le jeu s'inspire grandement de titres comme PlateUp! ou même OverCooked. Pour la répartition des rôles, Théo voulait se concentrer sur la modélisation 3D tandis que, comme d'habitude, je me chargeais de la partie technique.","md","\u002Fvirusmania_media\u002Fvirusmania_banner.png",{},true,"\u002Fprojects\u002Fvirusmania.fr","https:\u002F\u002Fgithub.com\u002Fmethil-mods\u002Fvirusmania","Lead Developer",{"title":5,"description":643},"virusmania","projects\u002Fvirusmania.fr",[655,656],"Unity","C#","fJbjixtq0E2xjjMl_0Ls8R2O2XyEgVYK6Cw-raoMJYw",1787159127990]