mirror of
https://github.com/BeamMP/Docs.git
synced 2026-08-27 04:37:30 +00:00
Fix: renommage dossier modding (suppression espace finale)
This commit is contained in:
@@ -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>
|
||||

|
||||
</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)
|
||||
|
||||
@@ -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.
|
||||
````
|
||||
|
||||
Reference in New Issue
Block a user