Communication bidirectionnelle entre Unreal Engine 4 et interfaces Web via JavaScript

Configuration initiale du moteur

L'intégration d'une interface Web au sein d'Unreal Engine 4 nécessite l'activation préalable du module WebBrowser. Pour permettre l'interaction entre le moteur et le contenu JavaScript, modifiez le fichier de configuration DefaultEngine.ini de votre projet :

[/Script/WebBrowser.WebBrowser]
bEnableWebInterface=true

Développement du pont de communication C++

Pour établir une passerelle robuste, nous créons une classe dérivée de UObject. Cette classe servira d'intermédiaire pour recevoir les appels venant du navigateur et envoyer des données vers la couche UI.

UCLASS(Blueprintable)
class UNetworkInterfaceBridge : public UObject
{
    GENERATED_BODY()

public:
    // Envoie une notification brute vers le JavaScript
    UFUNCTION(BlueprintCallable, Category = "WebUI|Core")
    void DispatchToWeb(const FString& Payload);

    // Permet d'exposer une fonction spécifique pour le binding
    UFUNCTION(BlueprintCallable, Category = "WebUI|Core")
    void RegisterScriptMethod(const FString& MethodName);
};

Encapsulation de la couche JavaScript

Côté client (Web), il est essentiel de structurer la communication pour gérer l'asynchronisme. Le code suivant définit un gestionnaire d'appels utilisant des Promesses pour suivre les requêtes envoyées au moteur.

class UnrealEngineConnector {
    constructor() {
        this.pendingRequests = {};
        this.initializeListener();
    }

    initializeListener() {
        window.addEventListener('message', (event) => {
            const response = JSON.parse(event.data);
            if (response.correlationId && this.pendingRequests[response.correlationId]) {
                this.pendingRequests[response.correlationId].resolve(response.content);
                delete this.pendingRequests[response.correlationId];
            }
        });
    }

    async invokeEngineFunction(method, args) {
        const requestId = `req_${Date.now()}_${Math.floor(Math.random() * 1000)}`;
        
        return new Promise((resolve, reject) => {
            this.pendingRequests[requestId] = { resolve, reject };

            if (window.ue && window.ue.interface) {
                window.ue.interface[method](JSON.stringify({
                    id: requestId,
                    data: args
                }));
            } else {
                reject("Interface UE non détectée");
            }
        });
    }
}

Implémentation de la logique de routage en Blueprint

Une fois l'objet lié (Binding), le Blueprint doit traiter les chaînes JSON entrantes. La structure type d'un système de routage efficace repose sur les étapes suivantes :

  • Réception : L'événement OnMessageReceived capture la chaîne brute.
  • Parsing : Extraction de l'identifiant de commande et des paramètres.
  • Exécution : Utilisation d'un nœud Switch on String pour rediriger vers les fonctions spécifiques (ex: Mise à jour inventaire, Points de vie).
  • Réponse : Utilisation de la fonction Execute JavaScript sur le composant WebBrowser pour renvoyer le résultat.

Cas pratique : Système de boutique dynamiuqe

Imaginons un flux de transaction pour un magasin en jeu. Le processus se décompose comme suit :

  1. Le WebUI sollicite la liste des prix via invokeEngineFunction('GetStoreCatalog').
  2. Le moteur interroge sa base de données locale ou un serveur distant.
  3. Le moteur injecte le catalogue via une commande JavaScript : WebBrowser->ExecuteJavascript("updateStoreView(...)").
  4. Lors d'un achat, le WebUI envoie l'ID de l'objet. Le Blueprint vérifie les ressources du joueur et renvoie un succès ou une erreur.

Optimisation des performances et fréquence d'échantillonnage

Pour éviter de saturer le thread principal lors de mises à jour fréquentes (comme une barre de progression ou une bousssole), il est recommandé d'implémenter un système de "Throttling" côté moteur :

// Exemple de mise à jour limitée à 15 FPS côté C++
void AGameHUD::UpdateInterface(float DeltaTime)
{
    TimerAccumulator += DeltaTime;
    if (TimerAccumulator >= 0.066f) 
    {
        RefreshUIElements();
        TimerAccumulator = 0.0f;
    }
}

Le regroupement (Batching) des données est également préférable : au lieu d'envoyer la santé, puis l'armure, puis l'endurance séparément, envoyez un objet JSON unique contenant l'état complet du personnage.

Sécurité et validation des données

La communication entre un navigateur et le moteur de jeu présente des risques d'injection. Il est impératif de :

  • Valider systématiquement le format JSON avant le traitement dans Unreal.
  • Échapper les caractères spéciaux dans les chaînes de texte envoyées depuis le JavaScript.
  • Utiliser des types de données stricts lors du cast des variables dans les Blueprints.

Étiquettes: Unreal Engine 4 JavaScript Blueprints C++ WebBrowser

Publié le 8 juin à 20h42