J'ai pensé à créer une plate-forme multijoueur RPG web depuis mes jours en tant que développeur Flash.Il y a beaucoup d'options TTRPG en ligne là-bas (beaucoup de bons!) mais aucun d'entre eux ne se sentait tout à fait bon pour la façon dont je voudrais jouer. Vous avez vu où Deborah Ann Woll montre à Jon Bernthal comment jouer à D&D? (il a 2M vues!) La vidéo https://www.youtube.com/watch?v=JpVJZrabMQE&embedable=true Et si je pouvais faire quelque chose qui ressemble à ça ? L’idée Une plate-forme de RPG multijoueur en ligne basée sur le récit, légère sur les règles mais haute sur le récit partagé, avec des résultats déterminés par le rouleau d'un D20. J'ai toujours aimé Les rencontres détaillées et l'histoire épique planifient pour les joueurs à découvrir comme leurs personnages font leur chemin à travers une finale climatique. Modules D&D Et si je pouvais créer un moyen pour les gens de créer leurs propres modules d'aventure, pour n'importe quel genre de RPG, puis laisser les joueurs courir leurs personnages à travers ces aventures? Qui exécuterait les jeux cependant? Les maîtres de jeux sont difficiles à trouver. Et si je pouvais former une IA pour être un maître de jeu? Pour exécuter en fait un plan d'aventure bien structuré (conçu par un humain!) qui serait amusant pour les joueurs et pas seulement un tas de ? Le slop pour commencer Je me suis amusé à construire beaucoup de choses avec AI (voir Je vais utiliser ma pile préférée pour faire , une application Web Next.js avec Tailwind pour l'interface utilisateur (doit-on même le dire?) alimentée par le L’utilisation de Gemini et d’une Déployé à Vercel. Mon projet démarre D20Adventures.com Avec SDK Base de données convexe Et j'ai décidé de le construire en ouvert, en publiant le code . sur Github Le prototype Pour un prototype, je commence littéralement à partir du scénario exposé sur le podcast, un ranger qui se promène dans les bois la nuit entend une fissure à distance, ce qui se révèle être un ours. Mon objectif est de construire une courte aventure à un seul coup et de voir si je peux entraîner un DM d'IA pour exécuter en fait une petite série de tours et de rencontres pour cette aventure. Les Sommons de minuit Un mystérieux appel d'un vieil ami druide attire un ranger réclus dans la forêt de Valkarr. Les Sommons de minuit Un mystérieux appel d'un vieil ami druide attire un ranger réclus dans la forêt de Valkarr. Après beaucoup d'essai et d'erreur, j'ai finalement pu faire un jeu complet et le publier sur YouTube: https://youtu.be/9wzNVmhVMlg?si=qaNEJ8wcF4bzZJK9&embedable=true Comment ça fonctionne Landing Page La page de destination est une image de grand héros (générée sur Midjourney avec une prompt de « La puissance du D20 »). j'ai ajouté quelques fades simples dans l'animation en utilisant la nouvelle règle de style CSS @starting: .fade-in { @apply opacity-100 transition-opacity duration-1000 ease-in-out; @starting-style { opacity: 0; } } Authentication Pour jouer au démarrage rapide, j'ai besoin d'un compte utilisateur.Cela est pour éviter d'être facturé beaucoup d'argent en raison de personnes anonymes ou de robots utilisant mes API. Il est très simple d’ajouter la gestion des utilisateurs, et je les utilise sur tous mes projets. Le clergé En outre, j'ai un suivi de l'utilisation des jetons où je limiterai l'utilisation avec un système de jetons, où vous commencez avec suffisamment de jetons pour faire un jeu de la démo, puis pouvez acheter plus à mesure que vous allez. The First Turn Lorsque l'utilisateur atterrit sur la page d'aventure pour le démarrage rapide, la première chose qui se passe est que nous chargons les données pour l'aventure de démonstration, qui est juste un fichier JSON simple ( Un module d’aventure ou un plan dans mon système est constitué d’une série de rencontres, qui sont liées les unes aux autres par des instructions pour le LLM : comme ça "encounters": [ { "id": "broken-silence", "title": "Broken Silence", "intro": "Thalbern, a solitary ranger of the Valkarr woods, has always trusted the silence of the wilds more than the promises of men. Orphaned by border raiders and raised by the elves of the Valkrarr Forest, he has spent years living on the edge of Kordavos, guiding travelers, hunting for his own survival, and keeping his distance from the tangled politics of the city.\n\nYet on this night, a message delivered by a red squirrel bearing the unmistakable script of Wollandora, a trusted elven friend and druid, has drawn him from his hidden home. The note was simple and urgent: Meet me at the Old Standing Stones at midnight. The balance of the forest could depend on it.\n\nNow, as midnight approaches, Thalbern moves quietly through the dense undergrowth, guided by memory and instinct. It is dark with almost no moonlight coming through the forest canopy.\n\nSuddenly, the hush of the night is broken by a sharp crack. Something large has just stepped on a branch somewhere off in the distance.", "instructions": "A perception check is appropriate if Thalbern investigates (low difficulty with a plus 3 modifier). If successful, he will determine it is a large creature that is approaching quickly. With a high roll (18+), he will determine it is an Owlbear. If combat ensues and Thalbern is below 25% health, Wollandora will intervene. If Thalbern avoids or defeats the Owlbear, or if Wollandora saves him, he proceeds to the Old Standing Stones.", "image": "images/settings/realm-of-myr/the-midnight-summons/broken-silence-2.png", "transitions": [ { "condition": "If Thalbern successfully uses stealth to evade and proceeds cautiously towards the Standing Stones, go to meeting-at-stones.", "encounter": "meeting-at-stones" }, { "condition": "If Thalbern fails a perception check, advance to owlbear-confrontation.", "encounter": "owlbear-confrontation" }, { "condition": "If Thalbern fails any dice roll (including stealth, perception, or any other check), advance to owlbear-confrontation.", "encounter": "owlbear-confrontation" }, { "condition": "If Thalbern does NOT successfully use stealth to evade, go to owlbear-confrontation.", "encounter": "owlbear-confrontation" }, { "condition": "If Thalbern does nothing or takes no action, go to owlbear-confrontation.", "encounter": "owlbear-confrontation" }, { "condition": "If Thalbern has a healthPercent of less than 50%, go to wollandora-intervention.", "encounter": "wollandora-intervention" } ] }, { "id": "owlbear-confrontation", "title": "Owlbear Confrontation", "intro": "From the direction of the sound, a little bit of eye shine glints in the shadows of the tree line. A hulking fifteen foot tall monster with the body of a giant bear and the head of an owl. As it crashes out from the undergrowth, it lets out a guttural squawk, clearly agitated and territorial.", "instructions": "The Owlbear will attack. If Thalbern attempts an animal handling check (high difficulty) and succeeds, he can move past the Owlbear. If Thalbern wins initiative and attempts to hide, he can move past the Owlbear if he passes a medium difficulty stealth check. If Thalbern's health drops to a critical level, Wollandora appears and drives off the Owlbear, transitioning to 'wollandora-intervention'. If Thalbern defeats the Owlbear, describe his victory and transition to 'meeting-at-stones'.", "image": "images/settings/realm-of-myr/the-midnight-summons/owlbear-confrontation.png", "npc": [ { "id": "owlbear", "behavior": "Aggressively attacks any perceived threat. Will fight until heavily wounded or driven off.", "initialInitiative": 1 } ], "transitions": [ { "condition": "Thalbern defeats the Owlbear, manages to evade it, successfully uses Animal Handling to pacify and move past it, or successfully rolls any other way to move past it.", "encounter": "meeting-at-stones" }, { "condition": "Thalbern is reduced to critical health by the Owlbear.", "encounter": "timely-rescue" } ] }, ... ] The First Reply Puisque nous sommes en mode démo, il n'y a pas d'aventure réelle créée encore sur le backend. Cela se produit lorsque le joueur fait la première réponse. fonction qui utilise l'IA pour évaluer la réponse pour s'assurer qu'elle est grammaticalement correcte, à la troisième personne et ajoute un dialogue ou une autre prose pour qu'elle fonctionne bien dans un style narratif littéraire. formatNarrativeAction Après que la mise en forme est appliquée, la réponse est envoyée à une action de serveur. Parce que c'est une démo, nous créons l'aventure dans la base de données lorsque cette première réponse est reçue. Processing Player Responses Le La fonction charge le tour actuel de Convex, les données d'aventure de S3, et identifie la rencontre spécifique et le personnage effectuant l'action. processTurnReply Dans ce contexte, il utilise ensuite l'IA pour déterminer si l'action est plausible (mon fils lors des tests de jeu a eu le ranger lancer un nuke sur l'ours de l'ours) et, si oui, si un rouleau de dés est mécaniquement nécessaire (par exemple, un "Roll d'attaque" ou un "Stealth Check"), y compris le type de rouleau et sa difficulté. Nous pouvons le faire avec un appel de fonction, où nous pouvons spécifier à l'IA que nous voulons des données structurées retournées, dans ce cas un : rollRequirementSchema import { z } from "zod"; export const rollRequirementSchema = z.union([ z.object({ rollType: z.string().describe("The type of roll required, e.g. 'Stealth Check'"), difficulty: z.number().describe("The difficulty class (DC) for the roll"), modifier: z.number().optional().describe("Bonus or penalty to the roll, e.g. +2 or -1"), }), z.null() ]); export type RollRequirement = z.infer<typeof rollRequirementSchema>; Ensuite, nous avons une fonction qui envoie un prompt détaillé et le schéma à : generateObject export async function getRollRequirementForAction(action: string) { const prompt = ` Given the following player or NPC action, determine if a dice roll is required for the character to attempt the action. If a roll is required, return a JSON object with "rollType" (choose the most appropriate from the list below) and "difficulty" (a number between 5 and 25). If no roll is required, return the JSON value null (not a string). Possible roll types: - Perception Check - Investigation Check - Insight Check - Stealth Check ... Examples: Action: "Try to sneak past the guards." Result: { "rollType": "Stealth Check", "difficulty": 15 } Action: "Attack the goblin." Result: { "rollType": "Attack Roll", "difficulty": 12 } Action: "Try to determine what the sound is." Result: { "rollType": "Perception Check", "difficulty": 10 } Action: "Say hello." Result: null Now, given the following action, determine the roll requirement. Action: "${action}" `; try { const result = await generateObject({ schema: rollRequirementSchema, prompt, }); if ( result.object && typeof result.object === "object" && "rollType" in result.object && (result.object.rollType === "null" || result.object.rollType === "none" || result.object.rollType === "") ) { return null; } return result.object ?? null; } catch (error) { throw error; } } Après avoir appliqué la réponse du joueur à l'histoire du tour, si un rouleau de dés est requis, nous mettons à jour l'état du personnage dans les données du tour convexe pour refléter qu'ils ont répondu mais que leur tour n'est pas complet. Une fois que l'utilisateur roule, nous avons un action serveur qui met à jour le récit avec une affichage visuel du résultat et écrit de la prose décrivant le résultat.Nous avons également une autre fonction d'AI appel pour mettre à jour la santé et l'état de tous les personnages à son tour. resolvePlayerRollResult NPC Actions Lorsqu’il s’agit d’un NPC qui répond, nous suivons un modèle similaire en tant que joueur, sauf dans ce cas où l’IA écrira la réponse.Chaque rencontre inclut des informations sur la motivation des NPC et lorsqu’elle est combinée au contexte de la narration d’aventure jusqu’à présent, espérons que l’IA puisse générer une bonne réponse, puis générer sa propre mise à jour et la mise à jour des résultats. Training AI to Run RPGs Si vous avez déjà essayé de faire une session de jeu avec l'IA dans un chat, vous savez à quelle vitesse cela peut sortir des rails. Nous espérons que, en fournissant le contexte approprié, des instructions spécifiques et des appels de fonctions de données structurées, nous pouvons obtenir une expérience agréable. Il y a certainement beaucoup d'essai et d'erreur, de journalisation de la console et d'ajustement prompt. Par exemple, lorsque vous demandez pourquoi l'IA n'a pas suivi les instructions de rencontre dans l'un de mes tests, voici ce que le chat Cursor a dû me dire: En résumé, le LLM n'a pas suivi ses instructions explicites pour la transition lorsqu'un personnage échoue à un contrôle de perception, en dépit d'être fourni avec des preuves claires d'un tel échec et une règle de transition pour ce scénario spécifique. En résumé, le LLM n'a pas suivi ses instructions explicites pour la transition lorsqu'un personnage échoue à un contrôle de perception, en dépit d'être fourni avec des preuves claires d'un tel échec et une règle de transition pour ce scénario spécifique. Je suppose que le hasard de l'IA ne fait pas exactement ce qui est attendu pourrait faire partie du plaisir. Voici un exemple de . Une des séances de jeu Vous pouvez voir le code source complet de ce projet sur github.com/johnpolacek/d20adventures.com