Aller au contenu

Ce site est en cours de construction !

Ce site est actuellement en cours de développement.

Vous pensez pouvoir contribuer ? N'hésitez pas à le faire en cliquant sur l'icône en forme de crayon située à droite de la page !

Vous pouvez également proposer des modifications sur n'importe quelle page.

Extraits de code Lua pour BeamNG.drive

Ce site est en cours de construction !

Ce site est actuellement en cours de développement.

Vous pensez pouvoir aider ? N'hésitez pas à le faire en cliquant sur l'icône représentant un crayon à droite de la page !

Vous pouvez le faire sur n'importe quelle page.

Monde

Dessiner un marqueur et détecter un véhicule

Dessiner des marqueurs sur la carte peut être un excellent moyen d'indiquer au joueur qu'une interaction est possible à cet endroit.

Créer un marqueur est relativement simple. Voici un exemple basé sur le marqueur utilisé pour les lignes de bus :

local function createBusMarker(markerName)
  local marker = createObject('TSStatic')
  marker:setField('shapeName', 0, "art/shapes/interface/position_marker.dae")
  marker:setPosition(vec3(0, 0, 0))
  marker.scale = vec3(1, 1, 1)
  marker:setField('rotation', 0, '1 0 0 0')
  marker.useInstanceRenderData = true
  marker:setField('instanceColor', 0, '1 1 1 0')
  marker:setField('collisionType', 0, "Collision Mesh")
  marker:setField('decalType', 0, "Collision Mesh")
  marker:setField('playAmbient', 0, "1")
  marker:setField('allowPlayerStep', 0, "1")
  marker:setField('canSave', 0, "0")
  marker:setField('canSaveDynamicFields', 0, "1")
  marker:setField('renderNormals', 0, "0")
  marker:setField('meshCulling', 0, "0")
  marker:setField('originSort', 0, "0")
  marker:setField('forceDetail', 0, "-1")
  marker.canSave = false
  marker:registerObject(markerName)
  scenetree.MissionGroup:addObject(marker)
  return marker
end

-- Peut ensuite être appelé dans une boucle pour configurer vos marqueurs.
-- REMARQUE : vous ne devez effectuer cette opération qu'une seule fois
-- lors de l'initialisation et non à chaque image/frame.
if #markers == 0 then
  for k,v in pairs(nameMarkers) do
    local mk = scenetree.findObject(v)
    if mk == nil then
      log('I', logTag,'Creating marker '..tostring(v))
      mk = createBusMarker(v)
      ScenarioObjectsGroup:addObject(mk.obj)
    end
    table.insert(markers, mk)
  end
end

Voici un exemple de marqueur personnalisé provenant de BeamNG-FuelStations :

local stations = [
  { "location": [ -778.813,  485.973, 23.46 ], "type":"gas" },
  { "location": [  617.164, -192.107, 53.2  ], "type":"ev"  },
]

local function IsEntityInsideArea(pos1, pos2, radius)
  return pos1:distance(pos2) < radius
end

local onUpdate = function (dt)
  for k, spot in pairs(stations) do -- Parcourt tous les emplacements sur la carte actuelle
    local bottomPos = vec3(spot.location[1], spot.location[2], spot.location[3])
    local topPos = bottomPos + vec3(0,0,2) -- Décalage vertical pour obtenir une hauteur de 2 m

    local spotInRange = false -- Le véhicule est-il à portée ? Utilisé pour la couleur
    local spotCompatible = false -- Cet emplacement est-il compatible ?

    if activeVeh then -- Nous avons un véhicule et il nous appartient (en multijoueur)
      local vehPos = activeVeh:getPosition()

      spotInRange = IsEntityInsideArea(vec3(vehPos.x, vehPos.y,vehPos.z), bottomPos, 1.5)

      spotCompatible = activeFuelType == "any" or spot.type == "any" or activeFuelType == spot.type
    end

    local spotColor = (spotInRange and spotCompatible) and activeColorMap[spot.type] or inactiveColorMap[spot.type] or ColorF(1,1,1,0.5)

    debugDrawer:drawCylinder(bottomPos:toPoint3F(), topPos:toPoint3F(), 1, spotColor) -- bas, haut, rayon, couleur
  end
end

Interface utilisateur

Notifications Toast — en haut à droite de l'écran

-- guihooks.trigger('toastrMsg', {type, title, msg, config = {timeOut}})
guihooks.trigger('toastrMsg', {
  type = "info",
  title = "Info Message:",
  msg = "Info Message Text Here",
  config = {timeOut = 5000}
})

guihooks.trigger('toastrMsg', {
  type = "warning",
  title = "Warning Message:",
  msg = "Warning Message Text Here",
  config = {timeOut = 5000}
})

guihooks.trigger('toastrMsg', {
  type = "error",
  title = "Error Message:",
  msg = "Error Message Text Here",
  config = {timeOut = 5000}
})

Notifications — en haut à gauche par défaut dans l'application Messages

Cela nécessite l'application UI « Messages » ou « Messages & Tasks ».

Les icônes se trouvent dans :

ui\ui-vue\src\assets\fonts\bngIcons\svg\
-- guihooks.trigger('Message', {msg, ttl, category, icon})
-- ui_message(msg, ttl, category, icon)

guihooks.trigger('Message', {
  msg = "Message Text Here",
  ttl = 5.0,
  category = "arrow_upward",
  icon = "arrow_upward"
})

guihooks.trigger('Message', {
  msg = "Message Text Here",
  ttl = 5.0,
  category = "arrow_downward",
  icon = "arrow_downward"
})

guihooks.trigger('Message', {
  msg = "Message Text Here",
  ttl = 5.0,
  category = "flag",
  icon = "flag"
})

guihooks.trigger('Message', {
  msg = "Message Text Here",
  ttl = 5.0,
  category = "check",
  icon = "check"
})

guihooks.trigger('Message', {
  msg = "Message Text Here",
  ttl = 5.0,
  category = "check_circle",
  icon = "check_circle"
})

guihooks.trigger('Message', {
  msg = "Message Text Here",
  ttl = 5.0,
  category = "warning",
  icon = "warning"
})

guihooks.trigger('Message', {
  msg = "Message Text Here",
  ttl = 5.0,
  category = "error",
  icon = "error"
})

guihooks.trigger('Message', {
  msg = "Message Text Here",
  ttl = 5.0,
  category = "directions_car",
  icon = "directions_car"
})

guihooks.trigger('Message', {
  msg = "Message Text Here",
  ttl = 5.0,
  category = "star",
  icon = "star"
})

guihooks.trigger('Message', {
  msg = "Message Text Here",
  ttl = 5.0,
  category = "timeline",
  icon = "timeline"
})

guihooks.trigger('Message', {
  msg = "Message Text Here",
  ttl = 5.0,
  category = "save",
  icon = "save"
})

guihooks.trigger('Message', {
  msg = "Message Text Here",
  ttl = 5.0,
  category = "settings",
  icon = "settings"
})

Affichage central temporaire — grand ou petit

-- guihooks.trigger('ScenarioFlashMessage', {{msg, ttl, sound, big}})
-- nécessite l'application UI RaceCountdown

guihooks.trigger('ScenarioFlashMessage', {{"Message", 5.0, 0, true}})
guihooks.trigger('ScenarioFlashMessage', {{"Message Text Here", 5.0, 0, false}})

Exemple de compte à rebours

Lorsque tous les messages sont exécutés immédiatement, ils sont placés dans une file d'attente et affichés les uns après les autres une fois que la durée (ttl) du précédent est écoulée.

guihooks.trigger('ScenarioFlashMessage', {{
  "3",
  1.0,
  "Engine.Audio.playOnce('AudioGui', 'event:UI_Countdown1')",
  true
}})

guihooks.trigger('ScenarioFlashMessage', {{
  "2",
  1.0,
  "Engine.Audio.playOnce('AudioGui', 'event:UI_Countdown2')",
  true
}})

guihooks.trigger('ScenarioFlashMessage', {{
  "1",
  1.0,
  "Engine.Audio.playOnce('AudioGui', 'event:UI_Countdown3')",
  true
}})

guihooks.trigger('ScenarioFlashMessage', {{
  "GO!",
  3.0,
  "Engine.Audio.playOnce('AudioGui', 'event:UI_CountdownGo')",
  true
}})

Autre exemple avec un son

guihooks.trigger('ScenarioFlashMessage', {{
  "Teleported!",
  3.0,
  "Engine.Audio.playOnce('AudioGui', 'event:UI_Checkpoint')",
  false
}})

Affichage central permanent de taille moyenne

Cela nécessite l'application UI « Race Realtime Display ».

-- guihooks.trigger('ScenarioRealtimeDisplay', {msg = msg})
-- nécessite l'application UI Race Realtime Display

guihooks.trigger('ScenarioRealtimeDisplay', {
  msg = "Message Text Here"
})

-- Ces messages restent affichés.
-- Pour les supprimer, envoyez une chaîne vide.

guihooks.trigger('ScenarioRealtimeDisplay', {
  msg = ""
})

Ce système est particulièrement pratique pour afficher des informations mises à jour régulièrement, comme :

  • un chronomètre ;
  • une distance ;
  • une vitesse ;
  • un compteur ;
  • des données en temps réel.

Boîte de dialogue de confirmation

ConfirmationDialog est une fenêtre contextuelle simple pouvant contenir jusqu'à deux boutons.

-- Ouvre une ConfirmationDialog avec un titre,
-- un texte et jusqu'à deux boutons.

guihooks.trigger(
  "ConfirmationDialogOpen",
  "Example Title",
  "Example Body Text",
  "Okay",
  "", -- gelua
  "Cancel",
  "" -- gelua
)

-- Ferme une ConfirmationDialog ouverte avec le titre fourni.

guihooks.trigger(
  "ConfirmationDialogClose",
  "Example Title"
)

Les deux paramètres d'un bouton doivent être des chaînes de caractères pour que le bouton soit affiché.

Si le bouton OK est fourni, l'action OK / Action principale est équivalente à l'appui sur ce bouton.

Si le bouton Cancel est fourni, l'action Menu est équivalente à l'appui sur ce bouton.

Le HTML est pris en charge et peut notamment être utilisé pour ajouter des images ou des icônes.

Plusieurs dialogues peuvent être affichés ; ils seront alors affichés les uns après les autres.

Bug

Ne fournir aucun bouton empêche le joueur de quitter la boîte de dialogue sans utiliser la console.

Bug

Les éléments SDF de l'application UI Minimap restent visibles lorsqu'une ConfirmationDialog est active.

Une solution de contournement consiste à utiliser :
guihooks.trigger('ShowApps', false)

introPopupTutorial

introPopupTutorial est une fenêtre contextuelle hautement personnalisable, principalement définie à l'aide de HTML intégré.

Il est courant de charger le contenu depuis un fichier HTML indépendant situé dans :

/gameplay/tutorials/pages/*/content.html

Exemple :

guihooks.trigger("introPopupTutorial", {
  {
    content = readFile(
      "/gameplay/tutorials/pages/template/content.html"
    ):gsub("\r\n",""),
    flavour = "onlyOk"
  }
})

guihooks.trigger("introPopupClose")

Le paramètre flavour détermine quels boutons sont affichés.

Il existe quatre variantes :

Variante Boutons
withLogbook Career Logbook, OK
onlyOk OK
onlyLogbook Career Logbook
noButtons Aucun bouton

Warning

Avec la variante `noButtons`, si aucun JavaScript supplémentaire n'est présent dans la page pour fermer la fenêtre, le joueur peut rester bloqué.

Les pages ne sont pas regroupées dans une seule fenêtre avec cette variante. Son utilisation n'est donc pas recommandée.

Si plusieurs pages sont fournies, ou si le hook est appelé plusieurs fois, les pages sont regroupées dans la même fenêtre.

Si le hook est appelé alors qu'un introPopup est déjà actif, ou qu'un autre type d'introPopup a déjà été déclenché, le nouveau popup sera affiché séparément après la fermeture du précédent.


introPopupCareer

introPopupCareer est une fenêtre contextuelle facile à utiliser et très flexible, qui permet également d'intégrer du HTML si nécessaire.

Le paramètre flavour détermine les boutons affichés ainsi que le rapport d'aspect par défaut de l'image.

Il existe quatre variantes :

default

  • Rapport d'aspect par défaut : 16x9
  • Boutons : Later, Okay

welcome

  • Rapport d'aspect par défaut : 16x9
  • Boutons : Career Logbook, Okay

branch-info

  • Rapport d'aspect par défaut : 16x9
  • Boutons : Career Logbook, Okay

garage

  • Boutons : Later, Okay

Exemple :

guihooks.trigger("introPopupCareer", {
  {
    title   = "Example title",
    text    = "Example text",
    image   = "/gameplay/tutorials/pages/template/image.jpg",
    ratio   = "16x9",
    flavour = "default"
  }
})

guihooks.trigger("introPopupClose")

Comme avec introPopupTutorial, plusieurs pages peuvent être regroupées dans le même popup.

Si un popup est déjà actif, un nouveau popup sera affiché après la fermeture du précédent.

Bug

Le flou d'arrière-plan possède une hauteur minimale. Les fenêtres contenant peu de contenu peuvent donc avoir une zone de flou inutilement grande sous leur fenêtre.

Deux solutions de contournement principales existent :

  • répéter \n et terminer avec <div /> jusqu'à ce que la fenêtre couvre correctement le flou ;
  • utiliser une image vide ou inexistante et ajuster le rapport d'aspect jusqu'à ce que la fenêtre couvre correctement le flou.

introPopupMission

introPopupMission est très similaire à introPopupCareer, mais contrairement à celui-ci, les boutons doivent être définis manuellement.

Les styles des boutons sont construits avec :

bng-button-{style}

Styles disponibles :

Style Apparence
main Orange
secondary Cyan
attention Rouge
white Blanc
link Translucide
outline Contour orange

Exemple :

guihooks.trigger('introPopupMission', {
  title   = "introPopupMission title",
  text    = "introPopupMission description",
  image   = "/gameplay/tutorials/pages/template/image.jpg",
  ratio   = "16x9",

  buttons = {
    {
      default = true,
      class = "main",
      label = "main button",
      clickLua = ""
    },

    {
      default = false,
      class = "secondary",
      label = "secondary button",
      clickLua = ""
    },

    {
      default = false,
      class = "attention",
      label = "attention button",
      clickLua = ""
    },

    {
      default = false,
      class = "white",
      label = "white button",
      clickLua = ""
    },

    {
      default = false,
      class = "link",
      label = "link button",
      clickLua = ""
    },

    {
      default = false,
      class = "outline",
      label = "outline button",
      clickLua = ""
    }
  }
})

guihooks.trigger("introPopupClose")

Comme pour les autres types de popup, plusieurs pages peuvent être regroupées dans la même fenêtre.

Bug

Le flou d'arrière-plan possède une hauteur minimale, ce qui peut provoquer une zone de flou excessive sous les petites fenêtres.

Les deux solutions de contournement sont :

  • répéter \n et terminer avec <div /> ;
  • utiliser une image vide ou inexistante et ajuster le rapport d'aspect.

Dialogue

Le système Dialogue est utilisé notamment dans la campagne A Rocky Start pour afficher les informations relatives à une mission.

Il s'agit d'une fenêtre centrée verticalement avec une présentation spécifique.

Contrairement aux autres popups présentés ci-dessus, elle ne prend pas en charge l'intégration de HTML.

ui_missionInfo.openDialogue({
  title = "Dialogue title",
  type = "Custom", -- n'est pas réellement affiché
  typeName = "typeName",

  data = {
    {
      label = "objective",
      value = "reward"
    }
    -- Ajouter d'autres éléments...
  },

  buttons = {
    {
      action = "accept",
      text = "Accept",
      cmd = ""
    },

    {
      action = "decline",
      text = "Decline",
      cmd = ""
    }

    -- Ajouter d'autres boutons...
  }
})

ui_missionInfo.closeDialogue()

Un seul Dialogue peut être affiché à la fois.

Tout dialogue déjà ouvert sera remplacé.

Info

Vous devez utiliser :
ui_missionInfo.closeDialogue()

pour fermer un dialogue.

Assurez-vous d'appeler cette fonction lorsqu'un bouton est pressé.