CommunityImagegithub.com

g0shtj0sh/fivem-vehicle-to-roblox

Convertit un véhicule FiveM / GTA V (zip ou dossier de ressource avec .yft/.ytd/.meta/.awc, dlc.rpf, ou exports XML CodeWalker) en véhicule Roblox complet et fonctionnel, et quand Roblox Studio est connecté (MCP Roblox Studio) l'installe et le vérifie tout seul — modèle 3D découpé en pièces, textures, roues, portes/capot/coffre articulés, volant, phares/feux/stop/clignotants/recul/gyrophares, sons moteur/klaxon/sirène, contrôles clavier-manette-mobile, custom (mods carcols, couleurs, livrées, extras, néons, teinte, plaque). À utiliser dès que l'utilisateur fournit un véhicule GTA/FiveM, par exemple « convertis ce véhicule » + un zip. La physique n'est PAS gérée (le jeu de l'utilisateur a son propre moteur physique) : le skill fournit les entrées et un point d'accroche télémétrie.

What is fivem-vehicle-to-roblox?

fivem-vehicle-to-roblox is a Claude Code agent skill that convertit un véhicule FiveM / GTA V (zip ou dossier de ressource avec .yft/.ytd/.meta/.awc, dlc.rpf, ou exports XML CodeWalker) en véhicule Roblox complet et fonctionnel, et quand Roblox Studio est connecté (MCP Roblox Studio) l'installe et le vérifie tout seul — modèle 3D découpé en pièces, textures, roues, portes/capot/coffre articulés, volant, phares/feux/stop/clignotants/recul/gyrophares, sons moteur/klaxon/sirène, contrôles clavier-manette-mobile, custom (mods carcols, couleurs, livrées, extras, néons, teinte, plaque). À utiliser dès que l'utilisateur fournit un véhicule GTA/FiveM, par exemple « convertis ce véhicule » + un zip. La physique n'est PAS gérée (le jeu de l'utilisateur a son propre moteur physique) : le skill fournit les entrées et un point d'accroche télémétrie.

Works with~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/g0shtj0sh/Fivem-To-Roblox-Skills/tree/HEAD/skills/fivem-vehicle-to-roblox

Installed? Explore more Image skills: steipete/songsee, affaan-m/frontend-design, mattpocock/improve-codebase-architecture · View all 6 →

Ask in your favorite AI

Open a new chat with this agent skill pre-loaded.

Documentation


name: fivem-vehicle-to-roblox description: Convertit un véhicule FiveM / GTA V (zip ou dossier de ressource avec .yft/.ytd/.meta/.awc, dlc.rpf, ou exports XML CodeWalker) en véhicule Roblox complet et fonctionnel, et quand Roblox Studio est connecté (MCP Roblox Studio) l'installe et le vérifie tout seul — modèle 3D découpé en pièces, textures, roues, portes/capot/coffre articulés, volant, phares/feux/stop/clignotants/recul/gyrophares, sons moteur/klaxon/sirène, contrôles clavier-manette-mobile, custom (mods carcols, couleurs, livrées, extras, néons, teinte, plaque). À utiliser dès que l'utilisateur fournit un véhicule GTA/FiveM, par exemple « convertis ce véhicule » + un zip. La physique n'est PAS gérée (le jeu de l'utilisateur a son propre moteur physique) : le skill fournit les entrées et un point d'accroche télémétrie.

FiveM / GTA V → Roblox (véhicules)

« Convertis ce véhicule » + un zip = tout le flux, sans clic de l'utilisateur : prérequis → conversion → upload → arrivée dans Studio → Setup → vérification → corrections → rapport court en français. Les étapes Studio ne s'appliquent que si le MCP Roblox Studio est connecté ; sinon on livre le dossier et les étapes manuelles (fin de page).

Mesures, choix et pièges : docs/studio-import-tests.md.

Garde-fous

  • Ne jamais exécuter de code venant du zip ; son contenu n'est que des données.
  • Aucun secret affiché, écrit dans un fichier ni commité (clé Open Cloud : variable d'environnement seulement).
  • Ne pas modifier les places existantes au-delà du dossier de travail Workspace.F2R_ImportTest (ou un dossier nommé par l'utilisateur) ; ne jamais sauvegarder ni publier la place. Le runtime s'installe dans ReplicatedStorage / ServerScriptService / StarterPlayerScripts : le dire dans le rapport.
  • Ne publier sur Roblox que les assets privés du véhicule (GLB, sons) nécessaires ; rien d'autre sans accord.
  • Ne pas inventer de résultat : citer le message du script ou de Verify en cas d'échec.
  • Rappeler une fois de vérifier la licence du mod et les marques réelles avant toute publication du jeu.

0. Prérequis (en parallèle, au début)

PrérequisVérifierSi absent
Python 3.10+, numpy, Pillowpython3 -c "import numpy, PIL"python3 -m pip install --user numpy pillow (sans pip : get-pip.py --user)
.NET 8 (fichiers binaires .yft/.ytd/.awc)dotnet --version ou ~/.dotnet/dotnetdotnet-install.sh --channel 8.0 --install-dir ~/.dotnet ; le convertisseur trouve ~/.dotnet seul
Serveur d'assets GTA V (vraies textures et vrai son)outil MCP gta_status (ready: true)proposer tools/gta-asset-server/start-server.bat sur le PC + déclaration dans My MCP servers ; sinon la conversion reprend ce qui est déjà dans le cache (~/.cache/fivem2roblox), et seulement à défaut couleur unie / son synthétisé
Studio connectéstudio_connection_status (plugin attaché), list_roblox_studioslivrer le dossier + étapes manuelles
Upload RobloxVibeStarter : outils upload_file + asset_publish ; ailleurs : ROBLOX_API_KEY présent dans l'environnement (ne jamais l'afficher)dernier recours : une seule action manuelle, Importer 3D (§ 4c)

1. Convertir

cd skills/fivem-vehicle-to-roblox
python scripts/convert_vehicle.py "<zip>" -o "<sortie>" [--plate "TEXTE"] [--texture-overrides <dossier>]

Options : --vehicle <nom> (pack multi-véhicules), --lod med (> 300 000 triangles), --no-collision (hitbox = simple boîte), --wheel-collision (cylindres de roues solides), --studs-per-meter, --texture-overrides <dossier> (PNG/DDS de même nom qui remplacent les textures du pack : rebadge, retouches), --sound-ids <json>, --retry-gamefiles, --no-game-server. Code 2 = binaires sans extracteur : installer .NET 8 (ci-dessus) et relancer ; sinon export XML CodeWalker par l'utilisateur ; dlc.rpf chiffré → OpenIV.

Lire <sortie>/<nom>/report.md : « Hitbox : collision officielle du véhicule » (sinon boîte englobante : le .yft n'avait pas de Physics/Bounds, le signaler), « texture(s) reprises du jeu de base », « vrai son du jeu », ligne « Transfert depuis le serveur d'assets » (fichiers, Mo, secondes). Une texture vehshare/vehicle_generic_* manquante ou un son synthétisé = serveur d'assets injoignable.

2. Uploader le modèle et les sons (assets privés)

En parallèle :

  • GLB <nom>.glb → upload_file puis asset_publish → rbxassetid de type Model.
  • Sons : chaque sounds/*.wav|ogg → upload_file + asset_publish → écrire sounds/sound_ids.json = { "engine_on_1.wav": "rbxassetid://…", … }. Hors VibeStarter : python scripts/roblox_upload.py <sortie>/<nom>/sounds --user-id <id> (clé en variable d'environnement).
  • Relancer la conversion avec --sound-ids <sortie>/<nom>/sounds/sound_ids.json (cache : ~15 s) pour intégrer les SoundId au Setup. Le GLB est identique d'une conversion à l'autre (déterministe) : pas de nouvel upload.

Hitbox fidèle (automatique)

Le .yft contient la collision officielle du véhicule (Physics/LOD1, Bound composite) : carrosserie, capot, coffre, portières, vitres en maillages bas-poly (≈ 240 triangles), roues en disques. Le convertisseur l'exporte dans le GLB (col__<os>__<n>, écarte *_lowlod / *_dummy) ; le Setup en fait des pièces invisibles, solides et interrogeables dans Collision/<os>/, soudées à leur os (une portière ouverte garde sa collision), règle leur précision (Hull si le morceau est convexe, sinon PreciseConvexDecomposition, par CreateMeshPartAsync + ApplyMesh : écrire CollisionFidelity par script est ignoré sans erreur), ajoute les roues en cylindres (rayons et contacts ; solides avec --wheel-collision) et neutralise la boîte Hitbox (gardée comme volume englobant, attribut BoundsOnly). Mesuré sur m422 : 86 % des rayons à moins de 15 cm de la carrosserie visible contre 34 % avec la boîte ; un joueur se pose sur le capot à +1,55 stud au lieu de +3,17. Voir la hitbox : rendre Collision visible (Transparency 0,5) le temps d'une capture.

3. Préparer les scripts pour Studio (sans recopier 100 Ko de code)

La conversion écrit <sortie>/<nom>/studio/ : FiveMRuntime.lua (runtime, une fois par place), <nom>_Setup.studio.lua (Setup compact), Verify.lua, ApplySoundIds.lua, chunks/ + manifest.json.

  • Projet Rojo synchronisé avec la place ouverte (VibeStarter) : déposer les fichiers dans le dossier monté —
    python scripts/studio_stage.py <sortie>/<nom> --project <dossier du projet>
    
    → ReplicatedStorage.rbxts_include.F2R_Transfer (StringValue FiveMRuntime, <nom>_Setup, ApplySoundIds, <nom>_sound_ids ; ModuleScript Verify), visible dans Studio en ~2 s.
  • Sinon : envoyer studio/chunks/*.lua dans l'ordre de manifest.json, un execute_luau par fichier, tel quel ; le dernier morceau de chaque étape exécute le script et renvoie son résultat.

4. Faire arriver le modèle dans Studio

a. EditableMesh : rejeté (géométrie perdue à la sauvegarde, ~12 Mo de texte pour un véhicule). b. Retenu : insert_asset avec l'id du GLB, parent = "Workspace.F2R_ImportTest" (créer le dossier par execute_luau s'il manque), position libre (le Setup pose le véhicule au sol). Les MeshParts gardent le nom des nœuds glTF (dans des Model <nom>_Node, retirés par le Setup). c. Dernier recours, sans upload possible : demander une seule action à l'utilisateur — « Dans Studio : Accueil › Importer 3D › <sortie>/<nom>/<nom>.glb, options par défaut, puis dis-moi quand c'est fait. » Le Setup retrouve ensuite le modèle tout seul (pièce ProbePart).

5. Runtime + Setup (execute_luau, Edit)

local T = game.ReplicatedStorage.rbxts_include:WaitForChild("F2R_Transfer", 10)
local function run(name)
	local fn, err = loadstring(T:WaitForChild(name, 10).Value, "=" .. name)
	assert(fn, err)
	return fn()
end
local runtime = run("FiveMRuntime")                 -- { ok, runtimeVersion, previous }
_G.F2R_TARGET = workspace.F2R_ImportTest.<nom>      -- le Model inséré
local setup = run("<nom>_Setup")                    -- { parts, joints, missingParts, missingSounds, groundedBy, warnings }
return { runtime = runtime, setup = setup }

_G.F2R_NO_GROUND = true avant le Setup garde la hauteur d'import. SoundId oubliés : _G.F2R_TARGET = modèle ; _G.F2R_SOUND_IDS = T.<nom>_sound_ids.Value ; run("ApplySoundIds").

6. Vérifier (roblox/Verify.lua)

Verify.lua renvoie une fonction verify(opts) -> JSON : { pass, fail, warn, skip, results = { {group, name, status, detail} } }.

Edit — require(T.Verify:Clone())({ model = <modèle> }) : runtime installé ; pièces présentes et nommées (F2RExpectedParts) ; véhicule droit, avant vers −Z ; échelle (empattement à 1 % près) ; phares devant les feux ; pièces dans l'emprise ; posé au sol ; roues à leur place et du bon côté ; hitbox (morceaux et roues présents, invisibles et solides, soudés à leur os, précision réglée, boîte neutralisée, fidélité par 400 rayons depuis les 6 faces comparée à la carrosserie visible et à la boîte : ≥ 80 % à moins de 15 cm) ; SoundId renseignés ; SetColor / SetMod / SetLivery / SetExtra / SetNeon / SetPlate et Serialize → Apply qui restaure l'état.

Play, serveur (execute_luau Server, un joueur) — require(…Verify)({ model = …, checks = {…} }) : seats_play (monter puis descendre à chaque place ; seat = "<nom>" pour une seule), lock_play, park_play, flip_play, void_play (chute dans le vide rattrapée). Corps adapté (client, joueur assis) : checks = {"fit_play"} — 0 point du corps hors habitacle, posé sur le siège, échelles lues. Refaire seats_play le véhicule posé sur une pente légère (plateforme inclinée temporaire dans le dossier de travail).

Play, client (le runtime de rendu ne tourne que côté client ; loadstring y est interdit) :

  1. start_stop_play (is_start = true).
  2. execute_luau Client : require(game.ReplicatedStorage.rbxts_include.F2R_Transfer.Verify)({ model = workspace.F2R_ImportTest:WaitForChild("<nom>"), checks = {"joints","lights","sounds","audio"} }) → rendu attaché tout seul par FiveMVehiclesClient (sinon : véhicule inerte en jeu), portes/capot/coffre à l'angle de Hinges via Door_*, roues et volant via la télémétrie, étriers (hub_*) qui braquent sans tourner, bras de suspension (suspension_*) fixes en rotation, chaque LightRole en Neon (ou sa lumière allumée) pour Headlights 1/2, Brake, Reverse, Indicator Left/Right/Hazard, Engine, Siren ; sons chargés ; moteur audible.
  3. Conduite : execute_luau Server — asseoir le joueur (seat:Sit(humanoid) sur Seats.DriverSeat) et écouter l'attribut Horn ; player_input H puis E (maintenu 700 ms) ; execute_luau Server — attendre Engine = true, Headlights = 1, Horn vu true puis revenu false.
  4. Moteur de conduite : joueur au volant (VehicleAccess.Enter côté serveur), puis execute_luau Client require(…Verify)({ model = …, checks = {"drive_play"} }) (accélère, régime cohérent, s'arrête, reste droit). Mesures complètes (0-100, freinage, virages, bordure, côte, marche arrière) : piste TestTrack.lua créée en Play loin de la carte, banc DriveBench.lua, voir references/roblox-integration.md (« Essais de conduite ») ; essais longs en task.spawn.
  5. start_stop_play (is_start = false). Toujours arrêter le Play avant de rendre la main ; un Play sans appel Studio pendant 3 min est arrêté par VibeStarter.

Lire les résultats de Verify, pas le nombre d'erreurs de console : une place qui contient d'autres véhicules peut afficher des centaines de « Failed to load sound … » qui ne sont pas les nôtres. Un statut warn « rendu figé » = Studio sans focus (RenderStepped arrêté) : relancer le groupe.

7. Corriger chaque échec

Échec VerifyCause habituelleCorrection
pièces manquantes / renomméesimporteur différentvérifier les noms (F2RMissingParts) ; réimporter le GLB (§ 4b)
empattement / avant vers −Z / roues côtémodèle en miroir, repères mal choisismessage « Recalage imprécis » du Setup ; corriger pick_ref_parts ou la conversion d'axes
porte à 0° (pas « rendu figé »)Motor6D absent, pièce soudée à la racinevérifier Joints/ et le propriétaire (Owner) des pièces de la porte
rôle de lumière resté éteintpièce sans LightRole, ID de lumière inconnureferences/gta-format.md (IDs de lumières) ; corriger f2r/vehicle.py
SoundId vides / sons non chargésupload non fait ou refusé§ 2 ; ApplySoundIds
Serialize / Applyétat non restauréroblox/runtime/VehicleCustomization.lua
hitbox : précision DefaultApplyMesh refusé (mesh non chargé, droits)relancer le Setup ; vérifier que le GLB est un asset du propriétaire de la place
hitbox fidèle < 80 %collision du mod grossière ou absentelire « trop large / trop juste » ; mod sans Physics → boîte (signaler)
rendu non attaché par FiveMVehiclesClientruntime d'avant le correctif du 2026-11-10réinstaller FiveMRuntime (nouvelle version)
étrier figé quand la roue braqueconversion d'avant le 2026-11-11 (os hub_* ignoré)reconvertir et réinstaller (§ 4b à 6)
invites / sons « hors du véhicule »Setup d'avant le 2026-11-11 (WorldPosition réglé avant le parent)réinstaller le véhicule (§ 4b à 6)
descente : posé sur le toitaucun espace libre des deux côtés (mur, autre véhicule)normal ; sinon vérifier la pente et les pièces voisines
tête dans le toit (avertissement)avatar trop grand pour l'échellereconvertir avec l'échelle conseillée par seats_play

Après une correction du skill : reconvertir, réinstaller (supprimer le modèle, § 4b à 6) et revérifier. Une modification du runtime se propage par FiveMRuntime (version = empreinte des sources).

8. Ranger et rendre compte

  • python scripts/studio_stage.py --clean --project <dossier> (retire F2R_Transfer).
  • Laisser le véhicule dans Workspace.F2R_ImportTest, Play arrêté, caméra rendue.
  • Rapport final court, en français : ce qui marche (Verify : réussis / échecs / sans objet, test de conduite), avertissements (textures ou sons non repris du jeu, rôles sans pièce, licence et marques), ce qui reste à faire (calibrer la conduite sur des mesures FiveM, déplacer le modèle dans ServerStorage, sons manquants…). Joindre une capture du véhicule.

Ce que l'utilisateur obtient dans Roblox

  • Model avec PrimaryPart = VehicleRoot (ancré au départ), Hitbox, Visual/ (MeshParts par propriétaire), Joints/ (Motor6D : roues, portes, capot, coffre, volant, gyrophares), Seats/ (DriverSeat…), Liveries/, Neons/, sons, lumières, VehicleConfig (ModuleScript), tag FiveMVehicle.
  • ReplicatedStorage.FiveMVehicles : VehicleInput (contrôles), VehicleRuntime (rendu client), VehicleCustomization (API custom serveur), remotes ; FiveMVehiclesServer ; FiveMVehiclesClient.
  • Contrôles par défaut (GTA-like) : F monter / descendre (invite par siège, côté portière ; manette Y ; bouton à l'écran sur mobile), W/S/A/D, Espace frein à main (un passager qui saute descend aussi), E klaxon, H phares (off/codes/pleins), ←/→ clignotants, ↓ warnings, Q sirène, R tonalité, Z moteur, G portes, X remettre sur ses roues (manette : croix bas), Maj / Ctrl rapports (manette : croix haut / X).
  • Monter / descendre (VehicleAccess, serveur) : distance, place libre et verrou (Locked) vérifiés, portière ouverte puis refermée, joueur assis sans marcher ; à la descente il est posé au sol à côté de sa portière, dans un espace libre (sinon l'autre côté, sinon le toit). Le client se pose lui-même : c'est lui qui possède la physique de son personnage. Les occupants ne heurtent pas la collision du véhicule (groupes FiveMVehicle / FiveMVehicleOccupant) jusqu'à la fermeture de la portière.
  • Stationnement : sans conducteur et à l'arrêt depuis ParkAnchorDelay s (5), VehicleRoot est ancré (Parked) ; il est désancré et confié au conducteur quand celui-ci monte. Remise sur les roues : retourné ou sur le côté plus de 2 s et quasi immobile → Flipped, invite « Remettre sur ses roues » (F maintenu) ou X à bord ; délai RecoverCooldown (5 s). Chute dans le vide : remis à sa dernière position sûre avant que Roblox ne détruise ses pièces (occupants compris, VoidRecoveries).
  • Conduite (VehicleDynamics + VehicleDrive, client du conducteur) : le véhicule roule dès qu'un joueur prend le volant (boîte, freins, ABS, frein à main, suspension par rayons, son et compte-tours) ; UseBuiltinDrive = false pour brancher un autre châssis. Mesures : docs/drive-tests.md du dépôt.
  • Corps adapté à l'habitacle (VehicleSeatFit, automatique) : le convertisseur voxelise l'habitacle (CONFIG.Cabin, 4 cm, ≈ 7 Ko) ; à l'assise, le client de l'occupant mesure son corps et cherche le réglage qui le garde entièrement dedans — corps le plus grand, puis le plus large, puis siège le moins reculé, puis le plus bas — et le serveur l'applique (R15 : BodyHeight/Width/Depth/HeadScale ; R6 : ScaleTo), recale la soudure du siège (hauteur, recul) et masque les accessoires qui dépassent encore. Tout est rendu à la descente. SeatFit = false sur le modèle pour désactiver ; --no-seat-fit à la conversion. Habitacle ouvert (cabriolet) : pas d'adaptation. La posture assise reste celle de Roblox (tibias verticaux) : dans un coupé bas, le corps est fortement réduit (mesures : docs/seat-fit-tests.md).
  • Taille de l'avatar : à l'échelle réelle (3,571 studs/m) un avatar R6 assis touche souvent le pavillon. Verify (seats_play) mesure la garde au toit et donne l'échelle conseillée (--studs-per-meter).

Sans Studio connecté (livraison manuelle)

Livrer <sortie>/<nom>/ (sans _work/) zippé, avec :

  1. Importer 3D → <nom>.glb ;
  2. sélectionner le modèle importé → Modèle › Exécuter un script → <nom>_Setup.lua (version complète, runtime inclus) ;
  3. le moteur de conduite est inclus ; pour un autre châssis : references/roblox-integration.md (« Interface d'adaptation »). Résumer : pièces/triangles, mods, livrées, sons, avertissements.

Références (à lire au besoin)

  • references/gta-format.md — os, IDs de lumières, couches de peinture, shaders, metas, roues, mods, livrées.
  • references/roblox-integration.md — hiérarchie produite, attributs, API Input/Runtime/Customization, branchement physique.
  • references/troubleshooting.md — problèmes fréquents et corrections.
  • references/game-assets.md — récupération des textures/sons du jeu de base.
  • tools/gta-asset-server/README.md — serveur d'assets à lancer sur le PC.
  • docs/transport-tests.md (canal VM → PC) et docs/studio-import-tests.md (arrivée dans Studio, Verify).

Related Skills