UserApp-Entwicklung/Tutorials/HTML-UI

Einleitung

In diesem Tutorial lernst du, wie du eine grafische Oberfläche in deinen UserApps verwenden kannst, um z.B. grafische Spiele für Knuddels zu programmieren. Du solltest schon einmal etwas mit HTML und JavaScript gemacht haben, um folgen zu können.

Der Server

Der Server-Code deiner UserApp steht in der main.js. Wenn du nur in der main.js programmierst, läuft der ganze Code deiner UserApp ausschließlich auf den Servern von Knuddels.
Damit kann man zwar schon viel machen, aber für mehr Interaktionsmöglichkeiten brauchst du eine grafische Oberfläche.

Der Client

Der Code für den Client befindet sich im www-Unterordner deiner UserApp. Den Einstieg bildet eine HTML-Datei, die du nennen kannst, wie du willst. Dort kannst du dann auch JavaScript-Dateien einbinden, um Code auf dem Gerät deines Besuchers auszuführen.

Die Struktur deiner UserApp könnte dann so aussehen:

KnuddelJumper/
    www/
        game.html
        game.js
        player.png
        enemy.png
    main.js

Die HTML-Datei kann dann z.B. so eine Struktur haben:

<html>
    <head>
        <script src="game.js"/>
    </head>
    <body>
        <button>Hier klicken</button>
    </body>
</html>

Während der Entwicklung wirst du allerdings oft Änderungen an deinem Script machen. Da Knuddels dein Script zwischenspeichert, wenn du es einfach mit dem <script>-Tag einbindest, werden deine Änderungen oft nicht übernommen. Um Knuddels dazu zu zwingen immer die aktuellste Version deines Scripts zu laden, musst du das Knuddels stattdessen mit Client.includeJS() mitteilen:

<html>
    <head>
        <script>
            Client.includeJS("game.js");
        </script>
    </head>
    <body>
        <button>Hier klicken</button>
    </body>
</html>

Kommunikation zwischen Server und Client

AppContent senden

Die HTML-Datei und der restliche Inhalt des www-Unterordners wird AppContent genannt. Das ist die grafische Oberfäche deiner UserApp.
Der AppContent erscheint aber nicht automatisch bei dem Besucher deines Channels! Du musst den AppContent erst an den Besucher (den Client) senden. In der Regel macht man das direkt wenn der Besucher den Channel betritt, also in App.onUserJoined:

var App = (new function() {

    let htmlFile = new HTMLFile("game.html");
    let appContent = AppContent.overlayContent(htmlFile, 640, 480);

    this.onUserJoined = function(user) {
        user.sendAppContent(appContent);
    }
}());

Der AppContent wird so nur einmal von der App geladen und dann einfach an jeden Besucher mit User.sendAppContent() gesendet.

 Hinweis: new HTMLFile() sucht die angegebene Datei im www-Unterordner.

Damit ist dieses Tutorial eigentlich fertig: Deine Besucher sehen jetzt eine grafische Oberfläche, wenn sie deinen Channel betreten und du kannst wie auf Webseiten Scripts einbinden. Ein paar wichtige Hinweise kommen aber noch!


Der AppContent kann verschiedene ViewModes annehmen, je nachdem welche Methode du verwendest:


Tipp: Nicht alle Geräte können jeden ViewMode anzeigen. Daher ist es gute Praxis, wenn du mit User.canSendAppContent() entscheidest, welchen AppContent du sendest. Beispiel:

var App = (new function() {

    let htmlFile = new HTMLFile("game.html");
    let overlay = AppContent.overlayContent(htmlFile, 640, 480);
    let popup  = AppContent.popupContent(htmlFile, 640, 480);
    let headerbar = AppContent.headerbarContent(htmlFile, 480);

    this.onUserJoined = function(user) {

        if (user.canSendAppContent(overlay)) {
            user.sendAppContent(overlay);

        } else if (user.canSendAppContent(popup)) {
            user.sendAppContent(popup);

        } else if (user.canSendAppContent(headerbar)) {
            user.sendAppContent(headerbar);

        } else {
            user.sendPrivateMessage("Die UserApp kann auf deinem Gerät leider nicht angezeigt werden.");
        }
    }
}());

Events senden

Dein Code auf dem Server und dein Code auf dem Client laufen jetzt praktisch unabhängig voneinander. Meistens ist es aber hilfreich, wenn Server und Client miteinander kommunizieren können.

Client → Server

Beispiel: Wenn der Nutzer auf der grafischen Oberfläche einen Knopf drückt, soll der AppBot eine öffentliche Nachricht in den Channel schreiben.
Du kannst aber nur von der main.js (also vom Server aus) Chat-Nachrichten schicken. BotUser.sendPublicMessage() funktioniert im Client nicht.

Der Client muss also dem Server mitteilen, dass der Nutzer einen Button gedrückt hat. Das ist mit Client.sendEvent() möglich:

Client.sendEvent("buttonPressed", {});

Dabei können noch zusätzliche Daten in Form eines JavaScript-Objekts mitgegeben werden.

Der Server kann dann in der main.js in App.onEventReceived() reagieren:

var App = (new function() {
    
    //...
    
    this.onEventReceived = function(user, type, data, appContentSession) {
        
        if (type == "buttonPressed") {
            let bot = KnuddelsServer.getDefaultBotUser();
            bot.sendPublicMessage(user.getProfileLink()+" hat den Knopf gedrückt!");
        }
    }
}());


Server → alle Clients

Beispiel: In der main.js wird eine zufällige Zahl generiert. Diese Zahl soll an alle Besucher des Channels geschickt werden, sodass diese dann in der HTML-UI angezeigt werden kann.

Mit AppContent.sendEvent() kannst du ein Event an alle Besucher schicken, bei denen dieser AppContent aktiv ist:

let randomNumber = Math.floor(10 * Math.random());
appContent.sendEvent("showNumber", {randomNumber: number});

Im Client kannst du mit Client.addEventListener() auf die Events vom Server reagieren:

Client.addEventListener("showNumber", function(event) {
        
        let number = event.data.number;
        
        document.open();
        document.write("Die magische Zahl heißt: "+number);
        document.close()
});

Server → ein bestimmter Client

Beispiel: Du wählst einen Besucher zufällig aus und möchtest, dass sich bei diesem Besucher die Farbe der HTML-UI ändert.

Dazu musst du vom Server aus (also in der main.js) diesem Client ein Event senden.
Du kannst aber nicht einfach User.sendEvent() aufrufen (diesen Befehl gibt es nicht). Eine UserApp kann nämlich verschiedene ViewModes auf einmal verwenden (sogenannte AppContentSessions). Du musst der API also sagen, an welche AppContentSession du das Event senden möchtest.

Wenn du weißt, welchen ViewMode der Besucher verwendet (z.B. weil du sowieso immer nur ein Popup sendest), kannst du einfach mit dem Befehl User.getAppContentSession die AppContentSession erhalten und dann das Event senden. Beispiel:

let appContentSession = user.getAppContentSession(AppViewMode.Popup);
appContentSession.sendEvent("changeColor", {red:255, green:0, blue:255});


Wenn dein Client allerdings gleichzeitig mehrere aktive AppContentSessions haben könnte (z.B. eine Headerbar und ein Popup), kannst du eine Liste aller aktiven AppContentSessions eines Besuchers mit User.getAppContentSessions() erhalten. Beispiel:

let appContentSessions = user.getAppContentSessions();

for (let i = 0; i < appContentSessions.length; i++) {
    let appContentSession = appContentSessions[i];
    appContentSession.sendEvent("changeColor", {red:255, green:0, blue:255});
}

Auch hier kannst du im Client wieder mit Client.addEventListener() auf die Events reagieren.

Limits

Für die Daten, die du mit den Events zwischen Client und Server sendest, gibt es gewisse Limits. Diese Limits werden hier beschrieben.