Fix: renommage dossier modding (suppression espace finale)

This commit is contained in:
3vfi-dev
2026-08-12 23:42:03 +02:00
committed by Starystars67
parent 71bd4350f2
commit 9d4f95e3c0
2 changed files with 0 additions and 590 deletions
@@ -1,104 +0,0 @@
!!! warning "Ce site est en cours de construction !"
```
Ce site est actuellement en cours de développement.
Vous pensez pouvoir contribuer ? Cliquez simplement sur l'icône en forme de crayon située à droite de la page !
Vous pouvez également contribuer à n'importe quelle autre page.
```
# Créer une fenêtre ImGui
Cette page explique comment créer une fenêtre ImGui basique.
## Configuration
Avant d'utiliser ImGui, une petite configuration est nécessaire :
```lua
local im = ui_imgui -- raccourci pour éviter de rechercher ui_imgui à chaque utilisation. Cela peut aider à optimiser les performances.
local imguiExampleWindowOpen = im.BoolPtr(true)
```
`imguiExampleWindowOpen` sera utilisé pour déterminer si la fenêtre d'exemple doit être affichée.
## Affichage de la fenêtre
Les fenêtres ImGui ainsi que leur contenu doivent être recréés à chaque image où elles doivent être affichées. Il est donc nécessaire d'utiliser une fonction `onUpdate` ou un mécanisme similaire.
```lua
local function onUpdate()
if worldReadyState == 2 then
if imguiExampleWindowOpen[0] == true then
imguiExample()
end
end
end
M.onUpdate = onUpdate
```
Cette fonction exécutera `imguiExample()` pour créer la fenêtre d'exemple, à condition que le niveau soit complètement chargé et que la fenêtre soit configurée pour être visible.
## Contenu de la fenêtre
Si vous débutez avec ImGui, vous pouvez le considérer comme un lointain cousin du HTML :
* `im.SetNextWindowSize(im.ImVec2(x, y), im.Cond_FirstUseEver)` définit la taille de la fenêtre si celle-ci n'a pas encore été configurée.
* `im.Begin()` et `im.End()` correspondent approximativement à `<body>` et `</body>`.
* `im.Text()` correspond approximativement à `<p></p>`.
```lua
local buttonPresses = 0
local function imguiExample()
im.SetNextWindowSize(im.ImVec2(366, 100), im.Cond_FirstUseEver) -- prépare notre fenêtre
im.Begin("Bonjour, je suis une fenêtre") -- crée une fenêtre avec ce titre
im.Indent() -- ajoute un retrait
im.Text("Bonjour, je suis du texte.") -- ajoute une ligne de texte, similaire à un élément <p>
im.SameLine() -- permet de placer l'élément suivant sur la même ligne
if im.Button("Le bouton Bonjour") then -- similaire à <button>. Exécute du code Lua lorsqu'il est pressé.
buttonPresses = buttonPresses + 1
end
if buttonPresses > 0 then
im.Text("Le bouton Bonjour a été utilisé " .. buttonPresses .. " fois !")
else
im.Text("Le bouton Bonjour n'a pas encore été utilisé.")
end
im.Unindent() -- termine le retrait
im.End() -- termine la fenêtre afin qu'elle puisse être affichée
end
```
Vous pouvez également ajouter la fonction suivante afin de pouvoir facilement afficher ou masquer la fenêtre :
```lua
local function toggleExampleImgui()
imguiExampleWindowOpen[0] = not imguiExampleWindowOpen[0]
end
```
## Résultat
<figure class="image image_resized" style="width:100%" markdown>
![Exemple de fenêtre ImGui affichée dans le jeu](../../../../assets/content/imguiExample.png)
</figure>
Lorsque vous appuyez sur le bouton **Le bouton Bonjour**, le compteur situé en dessous est mis à jour afin d'afficher le nombre de fois où le bouton a été utilisé.
## Téléchargement
Ce tutoriel est presque entièrement basé sur le mod d'exemple ImGui de [StanleyDudek](https://github.com/StanleyDudek).
Vous pouvez télécharger le mod d'exemple ici :
[**Télécharger l'exemple ImGui**](../../../../assets/content/imguiExample.zip)
-486
View File
@@ -1,486 +0,0 @@
!!! warning "Ce site est en cours de construction !"
```
Ce site est actuellement en cours de développement.
Vous pensez pouvoir contribuer ? Cliquez simplement sur l'icône en forme de crayon située à droite de la page !
Vous pouvez également contribuer à n'importe quelle autre page.
```
# Création d'une UI-App
Pour créer une UI-App, vous devez avoir quelques connaissances du framework **AngularJS**. La documentation principale est disponible ici : [documentation AngularJS](https://docs.angularjs.org/guide).
## Structure des fichiers
Une UI-App fonctionnelle nécessite quatre fichiers principaux :
* `app.js` — Contient le code JavaScript principal de l'UI-App. [Documentation JavaScript](https://developer.mozilla.org/fr/docs/Web/JavaScript)
* `app.html` — Contient le code HTML qui affiche l'application. [Documentation HTML](https://developer.mozilla.org/fr/docs/Web/HTML)
* `app.json` — Contient les informations et la configuration de l'UI-App.
* `app.png` — Image affichée dans le sélecteur d'UI-Apps.
### Style de l'UI-App
Il est recommandé d'utiliser une balise `<style>` directement dans le fichier HTML pour styliser votre application. Un fichier `.css` fonctionne également, mais les modifications ne seront pas visibles en temps réel.
---
## Exemple
Cet exemple provient de **DanielW**. Merci à lui !
### `ui\modules\apps\ExampleApp\app.html`
```html
<div style="width: 100%; height: 100%;" class="bngApp">
<link type="text/css" rel="stylesheet" href="/ui/modules/apps/ExampleApp/app.css" />
<div id="exampleAppContainer">
<span>Rapport : <span>{{ gearName }}</span></span>
<div layout="row" layout-align="center center">
<md-input-container flex>
<label>Message</label>
<input ng-model="message" ng-keydown="sendMessage($event)">
</md-input-container>
<md-button md-no-ink class="md-warn"
ng-disabled="!message"
ng-click="sendMessage()">Envoyer</md-button>
</div>
<span style="display: block">Messages :</span>
<!-- Zone de défilement -->
<ul bng-nav-scroll style="margin: 0; padding: 0; overflow-y: auto; width: 100%; height: 100%; background-color: #37373740;">
<!-- Parcourt les messages et les affiche -->
<li ng-repeat="message in messages track by $index"
style="display: flex; align-items: center; height: 35px;">
<span style="padding: 0 0.2em; width: 100%;">{{ message }}</span>
<!-- Bouton permettant de supprimer le message.
Appelle la fonction deleteMessage() dans app.js -->
<md-button md-no-ink class="md-icon-button md-warn"
ng-click="deleteMessage($index)">
<md-icon class="material-icons">delete</md-icon>
</md-button>
</li>
</ul>
</div>
</div>
```
Ici, vous pouvez voir :
* une balise `<span>` affichant le rapport du véhicule ;
* un champ de saisie permettant d'envoyer un message à la fonction `sendMessage()` du JavaScript ;
* une balise `<li>` répétée grâce à `ng-repeat`, utilisant la variable `messages` définie dans le JavaScript.
---
### `ui\modules\apps\ExampleApp\app.js`
```js
angular.module('beamng.apps')
.directive('exampleApp', [function() {
return {
templateUrl: '/ui/modules/apps/ExampleApp/app.html',
replace: true,
restrict: 'EA',
scope: true,
controller: ['$scope', function($scope) {
$scope.gearName = '0'
$scope.message = ''
$scope.messages = []
// Configure les flux de données dont nous avons besoin.
// Ici, nous utilisons uniquement les informations du moteur.
let streamList = ['engineInfo']
StreamsManager.add(streamList)
$scope.$on('destroy', function() {
StreamsManager.remove(streamList)
})
$scope.$on('streamsUpdate', function(event, streams) {
if (!streams.engineInfo)
return
// lua/vehicle/controller/vehicleController.lua:538
// (ou utilisez console.log pour effectuer vos propres tests)
let gear = streams.engineInfo[5]
// Met à jour le rapport affiché dans le HTML si nécessaire
if ($scope.gearName !== gear)
$scope.gearName = gear
})
$scope.sendMessage = function(event) {
if (event && event.key !== 'Enter')
return
if ($scope.message == '')
return
// Envoie le message à l'extension Lua pour le modifier
bngApi.engineLua(
'extensions.exampleMod.modifyMessage("' +
$scope.message +
'")'
)
$scope.message = ''
}
$scope.deleteMessage = function(idx) {
$scope.messages.splice(idx, 1)
}
// La fonction modifyMessage() déclenchera cet événement
// avec le message modifié.
$scope.$on('MessageReady', function(_, modifiedMessage) {
$scope.messages.push(modifiedMessage)
})
}]
}
}])
```
### Le fonctionnement de `$scope`
L'utilisation de **`$scope`** est particulièrement importante.
Vous devez définir vos variables et vos fonctions dans `$scope` afin qu'elles puissent être utilisées depuis le HTML via les directives `ng-*`.
Par exemple :
```js
$scope.message = ''
$scope.messages = []
$scope.sendMessage = function() {
// ...
}
```
Le HTML peut alors accéder directement à ces éléments :
```html
<input ng-model="message">
```
ou :
```html
<md-button ng-click="sendMessage()">
Envoyer
</md-button>
```
Dans cet exemple, lorsque `sendMessage()` est exécutée depuis le HTML, elle envoie le message vers une extension Lua située dans le dossier `extensions` du mod afin d'exécuter la fonction `modifyMessage()`.
---
## Communication entre JavaScript et Lua
Voici à quoi peut ressembler la partie Lua :
```lua
local function modifyMessage(message)
message = message .. " [Modifié !]"
guihooks.trigger('MessageReady', message)
end
```
Il s'agit ici d'une version simplifiée permettant de comprendre le fonctionnement.
L'élément important est :
```lua
guihooks.trigger('MessageReady', message)
```
`guihooks.trigger()` déclenche un événement AngularJS qui peut être récupéré avec :
```js
$scope.$on('MessageReady', function(_, modifiedMessage) {
$scope.messages.push(modifiedMessage)
})
```
Le fonctionnement est donc :
```text
HTML
sendMessage()
bngApi.engineLua()
Lua : modifyMessage()
guihooks.trigger()
JavaScript : $scope.$on()
$scope.messages
ng-repeat
Affichage dans l'UI-App
```
---
# Extension Lua complète
### `lua\ge\extensions\exampleMod.lua`
```lua
local M = {}
--[[
Point d'entrée de notre extension.
C'est ce que le jeu charge depuis notre modScript.lua.
Dans ce fichier, nous pouvons communiquer avec :
1. Notre extension du véhicule.
2. Les entrées utilisateur.
]]
-- Hooks de l'extension
--------------------------------------------
local function onExtensionLoaded()
log('D', "onExtensionLoaded", "Appelée")
end
local function onExtensionUnloaded()
log('D', "onExtensionUnloaded", "Appelée")
end
-- Fonctions personnalisées
--------------------------------------------
local function onActionKeyDown()
log('D', "onActionKeyDown", "Touche pressée !")
end
local function onVehicleExtensionLoaded(vehID)
log('D', "onVehicleExtensionLoaded", "Envoi de données au véhicule")
local veh = be:getObjectByID(vehID)
-- Si vous n'avez pas l'ID, vous pouvez également utiliser :
-- be:getPlayerVehicle(0)
if not veh then
return
end
local data = {
["name"] = "Daniel W"
}
veh:queueLuaCommand(
"extensions.exampleVehicleExtension.onDataReceived('" ..
jsonEncode(data) ..
"')"
)
end
local function modifyMessage(message)
message = message .. " [Modifié !]"
guihooks.trigger('MessageReady', message)
end
-- Interface publique
--------------------------------------------
M.onExtensionLoaded = onExtensionLoaded
M.onExtensionUnloaded = onExtensionUnloaded
M.onActionKeyDown = onActionKeyDown
M.onVehicleExtensionLoaded = onVehicleExtensionLoaded
M.modifyMessage = modifyMessage
--[[
D'autres fonctions peuvent notamment être :
- onPreRender(dtReal, dtSim, dtRaw)
- onUpdate(dtReal, dtSim, dtRaw)
- onClientPreStartMission(levelPath)
- onClientPostStartMission(levelPath)
Pour trouver les différents hooks disponibles,
recherchez "extensions.hook(" dans :
BeamNG.Drive/lua
]]
return M
```
Il est **très important de retourner la variable `M`** contenant les fonctions nécessaires.
Par exemple, sans :
```lua
M.modifyMessage = modifyMessage
```
l'appel JavaScript :
```js
bngApi.engineLua(
'extensions.exampleMod.modifyMessage("' + $scope.message + '")'
)
```
ne pourra pas trouver la fonction `modifyMessage()`.
---
# CSS de l'UI-App
### `ui\modules\apps\ExampleApp\app.css`
```css
#exampleAppContainer {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
align-content: center;
}
#exampleAppContainer > * {
margin: 0;
padding: 0;
}
```
---
# Configuration de l'UI-App
### `ui\modules\apps\ExampleApp\app.json`
```json
{
"domElement": "<example-app></example-app>",
"name": "Example App",
"types": [
"ui.apps.categories.debug"
],
"description": "example-app",
"css": {
"left": "0px",
"height": "auto",
"width": "270px",
"min-width": "200px",
"min-height": "90px",
"top": "0px"
},
"author": "Daniel W",
"version": "0.1",
"directive": "exampleApp"
}
```
La propriété `directive` doit correspondre exactement à la directive définie dans le fichier JavaScript :
```js
.directive('exampleApp', [function() {
```
Dans cet exemple :
```text
app.json
"directive": "exampleApp"
app.js
.directive('exampleApp', ...)
```
---
# Fonctions JavaScript fournies par BeamNG
Pour exécuter une fonction Lua depuis une UI-App, utilisez :
```js
bngApi.engineLua("chemin.lua.fonction()")
```
Cette fonction est particulièrement utile pour exécuter une fonction Lua avec ou sans arguments.
Par exemple :
```js
bngApi.engineLua(
'extensions.exampleMod.modifyMessage("Bonjour !")'
)
```
---
# Fonctions Lua fournies par BeamNG pour les UI-Apps
Pour envoyer un événement vers l'interface JavaScript :
```lua
guihooks.trigger("EventName", Payload)
```
Le `Payload` peut être de différents types. Il est cependant recommandé d'utiliser un **tableau**, un **objet** ou une **chaîne de caractères** afin d'éviter les problèmes de transmission des données.
Exemple :
```lua
guihooks.trigger("MessageReady", "Bonjour !")
```
Puis, côté JavaScript :
```js
$scope.$on('MessageReady', function(_, message) {
console.log(message)
})
```
!!! warning "Attention aux noms d'événements"
````
Il est possible que le nom d'événement que vous utilisez soit déjà utilisé en interne par BeamNG.drive.
Cela peut provoquer des conflits avec d'autres systèmes du jeu.
Par exemple, si votre application s'appelle `Nickel`, il est préférable de préfixer vos événements :
```text
NKEventName
NKMessageReady
NKUpdate
```
plutôt que d'utiliser des noms génériques comme :
```text
EventName
MessageReady
Update
```
Utiliser un préfixe propre à votre application permet de réduire fortement les risques de conflits.
````