Simulateur VR / Projet d'école

DriveForest

Conduire en VR avec un téléphone comme volant

UnityC#VRRustOSCBlender

DriveForest est un simulateur de conduite VR fait en 2026 pour un cours. On était deux : l’artiste a modélisé l’environnement, j’ai pris le code et les outils.

J’ai choisi la largeur : plusieurs briques branchées autour d’un gameplay qui tourne (jeu Unity, serveur Rust, outils d’asset), aucune vraiment finie. La boucle jouable fonctionne, le reste n’est pas finalisé. Dans le dépôt, les premiers commits datent des 24 et 25 mars 2026, puis une sauvegarde du 5 juillet avant ma migration sous Linux.

Unity : contrôleur véhicule

J’ai écrit le contrôleur de zéro en C#. Car_Drive.cs gère la vitesse, l’accélération et le braquage, et un ground-snap par raycast (longueur du rayon et vitesse de recalage réglables) plaque la voiture sur le terrain.

Les entrées passent par plusieurs scripts. Car_Input.cs lit le clavier et la manette, Car_JoinControler.cs fusionne les sources en une entrée commune, Car_Honk.cs déclenche le klaxon, et OSC_Receiver.cs reçoit les messages du serveur Rust. Le téléphone se branche donc comme une manette de plus, et le reste du gameplay ne bouge pas.

Unity : VR et recentrage

Le build est en OpenXR. Si le joueur met son casque sans être assis dans la direction du volant, tout est décalé. VR_SeatRecenter.cs règle ça : il attend que le casque soit tracké, puis recale le point de vue sur le siège virtuel de la scène. Une action de recentrage manuel, branchée sur l’Input System, sert quand le joueur se retrouve de travers en cours de partie.

Serveur Rust : quaternions et OSC

Le téléphone fait le volant. ZIG SIM tourne dessus et envoie quaternion et accéléromètre en OSC sur UDP. Le serveur Rust écoute sur le port 50000 (sur toutes les interfaces) et lit les messages /quaternion et /accel des bundles reçus.

La première version projetait l’avant du téléphone sur un plan pour en tirer un angle. Je l’ai remplacée par le quaternion relatif à une pose de référence : nalgebra 0.33 calcule référence⁻¹ × courant, j’en tire l’angle autour d’un axe (X, Y ou Z) avec 2 × atan2, je le divise par un angle maximal (π par défaut) et je borne le résultat entre -1 et 1. Le choix de l’axe et du signe est réglable, parce qu’ils changent avec la façon dont on tient le téléphone.

À chaque bundle reçu, le serveur renvoie en OSC à Unity sur 127.0.0.1:9000 :

  • /driveforest/steering : braquage normalisé
  • /driveforest/accel : accélération normalisée
  • /driveforest/honk : valeur 1.0, envoyée quand la variation de l’accéléromètre entre deux trames dépasse un seuil (8.0 par défaut, réglable de 2 à 20), au plus une fois toutes les 500 ms

Je mesure la variation entre deux trames pour ne pas avoir à retirer la gravité : une secousse brusque dans n’importe quelle direction suffit.

Interface de calibration

L’interface (eframe 0.30) liste les adresses IP locales avec le port, ce qui permet d’entrer la bonne dans ZIG SIM. Elle affiche braquage et accélération en barres et règle le seuil du klaxon.

La calibration se fait en quatre temps : tourner à droite, tourner à gauche, incliner vers l’avant, incliner vers l’arrière, en tenant chaque position quelques secondes. L’interface prend l’axe qui a le plus bougé et en déduit le signe, après un retour au neutre qui sert de pose de référence.

Pipeline d'assets PowerShell et addons DCC

Je voulais que l’artiste puisse créer et versionner ses fichiers Blender et Substance Painter sans passer par moi. Create-AssetTemplate.ps1 crée l’arborescence d’un nouvel asset (personnage, accessoire ou véhicule), Increment-AssetVersion.ps1 archive les fichiers de la version courante et ouvre la suivante, et tout se lance depuis un menu console, Asset.bat.

Pour Blender, le script crée le fichier .blend avec l’addon embarqué directement dedans : Blender est lancé en arrière-plan et y enregistre l’addon, qui suit donc l’asset. Substance Painter ne permet pas de lier l’addon au fichier de projet : il s’installe dans le dossier de plugins du logiciel, avec des scripts d’installation, de mise à jour et de retrait.

Les deux addons sont restés à l’état de base, un panneau avec un bouton d’essai. Je voulais y mettre la création et le versionnage pour éviter la console, mais ça s’est arrêté à la création des dossiers et des fichiers, à cause de cette limite côté Adobe.

Ce que ça change

Avant, créer un asset, monter ses dossiers, ses fichiers Blender et Substance Painter, puis le versionner se faisait à la main, dans l’explorateur. Maintenant, un menu console crée le dossier avec sa structure et les fichiers de travail, installe le plugin Painter si besoin, et archive la version précédente avant d’en ouvrir une nouvelle.

Lancer le jeu

Le build se lance en deux processus côte à côte : Unity_DriveSim.exe (standalone Windows) et le serveur Rust, compilé et copié dans dist/ par build.ps1. Une fois le téléphone appairé via ZIG SIM et le casque sur la tête, c’est jouable.

Équipe

VR Simulator / School project

DriveForest

Driving in VR with a phone as the steering wheel

UnityC#VRRustOSCBlender

DriveForest is a VR driving simulator made in 2026 for a course. There were two of us: the artist modeled the environment, I took the code and the tools.

I went for breadth: several pieces wired around a gameplay loop that runs (Unity game, Rust server, asset tools), none of them really finished. The playable loop works, the rest is not finalized. In the repository, the first commits date from 24 and 25 March 2026, then a backup from 5 July before I moved to Linux.

Unity: vehicle controller

I wrote the controller from scratch in C#. Car_Drive.cs handles speed, acceleration and steering, and a raycast ground snap (adjustable ray length and snap speed) sticks the car to the terrain.

Inputs go through several scripts. Car_Input.cs reads keyboard and gamepad, Car_JoinControler.cs merges the sources into one shared input, Car_Honk.cs fires the horn, and OSC_Receiver.cs receives the Rust server’s messages. The phone therefore plugs in like one more gamepad, and the rest of the gameplay doesn’t change.

Unity: VR and recentering

The build uses OpenXR. If the player puts the headset on without sitting square to the wheel, everything is offset. VR_SeatRecenter.cs fixes that: it waits until the headset is tracked, then moves the viewpoint onto the scene’s virtual seat. A manual recenter action, wired through the Input System, covers the case where the player ends up crooked mid-game.

Rust server: quaternions and OSC

The phone is the wheel. ZIG SIM runs on it and sends quaternion and accelerometer data as OSC over UDP. The Rust server listens on port 50000 (all interfaces) and reads the /quaternion and /accel messages of the bundles it receives.

The first version projected the phone’s forward axis onto a plane to get an angle. I replaced it with the quaternion relative to a reference pose: nalgebra 0.33 computes reference⁻¹ × current, I take the angle around one axis (X, Y or Z) with 2 × atan2, divide it by a maximum angle (π by default) and clamp the result between -1 and 1. The axis and sign are adjustable, because they change with how the phone is held.

For every bundle received, the server sends OSC to Unity on 127.0.0.1:9000:

  • /driveforest/steering: normalized steering
  • /driveforest/accel: normalized acceleration
  • /driveforest/honk: value 1.0, sent when the accelerometer change between two frames exceeds a threshold (8.0 by default, adjustable from 2 to 20), at most once every 500 ms

I measure the change between two frames so I don’t have to subtract gravity: one sharp shake in any direction is enough.

Calibration interface

The interface (eframe 0.30) lists the local IP addresses with the port, so you can enter the right one in ZIG SIM. It shows steering and acceleration as bars and sets the horn threshold.

Calibration has four steps: turn right, turn left, tilt forward, tilt backward, holding each position for a few seconds. The interface takes the axis that moved the most and derives the sign, after a return to neutral that serves as the reference pose.

PowerShell asset pipeline and DCC addons

I wanted the artist to create and version their Blender and Substance Painter files without going through me. Create-AssetTemplate.ps1 creates the folder tree of a new asset (character, prop or vehicle), Increment-AssetVersion.ps1 archives the current version’s files and opens the next one, and everything runs from a console menu, Asset.bat.

For Blender, the script creates the .blend file with the addon embedded directly in it: Blender is launched in the background and registers the addon, so it follows the asset. Substance Painter does not let you tie the addon to the project file: it is installed in the app’s plugins folder, with install, update and uninstall scripts.

The two addons stayed at a basic state, a panel with a test button. I wanted to put creation and versioning in them to avoid the console, but it stopped at creating the folders and files, because of that Adobe limit.

What it changes

Before, creating an asset, building its folders, its Blender and Substance Painter files, then versioning it was done by hand in the file explorer. Now a console menu creates the folder with its structure and the working files, installs the Painter plugin if needed, and archives the previous version before opening a new one.

Running the game

The build runs as two processes side by side: Unity_DriveSim.exe (standalone Windows) and the Rust server, compiled and copied to dist/ by build.ps1. Once the phone is paired through ZIG SIM and the headset is on, it is playable.

Team