Spiele-Apps für Chromecast – Teil 2: Game Manager API

In dieser Serie möchte ich zusammenfassen, wie man Spiele für Googles Chromecast baut. Der Chromecast ist eine verdammt günstige Variante, um auf einem großen Fernseher Inhalte vom Smartphone darzustellen und eben auch darauf Spiele zu spielen. Google Cast, also die passende API, ist auch in Android TV oder anderen kompatiblen Geräten verfügbar. Spiele mit Chromecast sind momentan noch sehr rar. Die meisten Apps zielen auf Streaming von Multimediainhalten ab, wie Videos, Fotos oder Musik. Wir wollen das jetzt ändern. Ziel der Serie soll ein Spiel sein, welches man mit mehreren Mitspielern gleichzeitig auf einem TV-Gerät spielen kann, wobei das Smartphone als Eingabegerät gilt.

Teil 2 soll euch eine Einführung in die Game Manager API geben, die von Google auf der I/O 2015 im Sommer vorgestellt wurde und die Entwicklung von Apps drastisch vereinfachen sollen. Ein paar Kerngebiete der Game Manager API sind:

  • Kommunikation zwischen den Spielern
  • Synchronisation der Spielstände zwischen den Sendern
  • Beitreten und Verlassen durch weitere Spieler

Vorbereitung

Ihr solltet euch bereits mit der Entwicklung von Chromecast-Apps vertraut gemacht haben. Wenn nicht, könnt ihr euch zunächst Teil 1: Hallo Welt durchlesen. Zum Testen der Multiplayer-Funktionen, ist es hilfreich ein zweites Android-Gerät zu besitzen, mit dem Ihr ebenfalls dem Spiel beitreten könnt.

Das Tutorial verwendet als Grundlage den Source Code aus Teil 1: Hallo Welt und erweitert ihn um die Game Manager API.

Game Manager API vs. Remote Display API

Die Game Manager API grenzt sich von der Remote Display API dahingehend ab, dass das Spielgeschehen im Receiver implementiert ist und somit auch die gesamte Logik dort stattfindet. Remote Display Apps hingegen erweitern die Darstellung um einen zweiten Bildschirm, wobei der Inhalt dessen vollständig auf dem Smartphone gerendert wird und lediglich auf dem Chromecast angezeigt wird. Das Display des Smartphones kann in beiden Fällen dann für Steuerelemente genutzt werden.

Die Remote Display API darf aber nicht mit der Screen Mirroring-Funktion verwechselt werden. Technologien wie Miracast oder Screen Cast spiegeln den Smartphoneinhalt auf das Anzeigegerät. Wenn das Display des Smartphones aus ist, so ist auch der Inhalt des Anzeigegeräts in der Regel schwarz. Ein anderer Inhalt auf den jeweiligen Displays geht auch nicht.

Sender aufräumen

Das Besondere an der Game Manager API ist, dass er sich auch um die Channels kümmert, über die die Kommunikation abläuft. Wir brauchen uns also keine Gedanken um irgendwelche Namespaces machen. Wir können also einiges aus unserem Source Code entfernen:

  • die Eigenschaft mMyChannel
  • die Klasse MyChannel
  • die Methoden registerChannel(), unregisterChannel() und sendMessage()
  • alle Funktionsaufrufe der vorher genannten Methoden
  • ungenutzte Imports

GameManagerClient im Sender initialisieren

Der GameManagerClient erledigt sämtliche Arbeit für uns. Er ist relativ einfach zu initialisieren:

    ...
    private GameManagerClient mGameManagerClient;

    ...

    private class CastResultCallback implements ResultCallback<Cast.ApplicationConnectionResult> {
        @Override
        public void onResult(Cast.ApplicationConnectionResult applicationConnectionResult) {
            Status status = applicationConnectionResult.getStatus();
            if (status.isSuccess()) {
                ...

                mSessionId = applicationConnectionResult.getSessionId();
                
                ...

                GameManagerClient.getInstanceFor(mApiClient, mSessionId)
                        .setResultCallback(new GameManagerClientResultCallback());
            } else {
                teardown();
            }
        }
    }

    ...

    private class GameManagerClientResultCallback
            implements ResultCallback<GameManagerClient.GameManagerInstanceResult> {
        @Override
        public void onResult(
                GameManagerClient.GameManagerInstanceResult gameManagerInstanceResult) {
            mGameManagerClient = gameManagerInstanceResult.getGameManagerClient();
        }
    }

    ...

    private void teardown() {
        ...
                if (mApiClient.isConnected() || mApiClient.isConnecting()) {
                    try {
                        Cast.CastApi.stopApplication(mApiClient, mSessionId);
                        mGameManagerClient.dispose();
                    } catch (Exception e) {
                        Log.e(TAG, "Failed to stop application", e);
                    }
                    mApiClient.disconnect();
                }
        ...
    }

Damit sind wir auch schon komplett und könnten mit den Funktionen des GameManagerClients die Spiellogik umsetzen. Fehlt noch der Receiver.

GameManager im Receiver initialisieren

Auf der Receiver-Seite ist der GameManager die wichtigste Klasse, die wir für unser Spiel benötigen. Auch hier bereinigen wir unseren Code von den alten Sachen, die wir nicht mehr brauchen:

  • die Variable namespace
  • die window.messageBus und window.messageBus.onMessage-Objekte
  • die onMessage()-Funktion

Im Anschluss initialisieren wir den GameManager :

...
<head>
  <script src="//www.gstatic.com/cast/sdk/libs/receiver/2.0.0/cast_receiver.js"></script>
  <script src="//www.gstatic.com/cast/sdk/libs/games/1.0.0/cast_games_receiver.js"></script>
  <style type="text/css">
...

  <script>

    var gameManager;
  
    window.onload = function() {
      cast.receiver.logger.setLevelValue(0);
      window.castReceiverManager = cast.receiver.CastReceiverManager.getInstance();       

      var gameConfig = new cast.receiver.games.GameManagerConfig();
      gameConfig.applicationName = 'My Game';
      gameConfig.maxPlayers = 8;
      gameManager = new cast.receiver.games.GameManager(gameConfig);

      // Starte den Receiver.
      window.castReceiverManager.start({statusText: "Application is starting"});
      console.log('Receiver Manager started');
    };

    ...

  </script>

Status in der App ausgeben

Wir sind mit dem Einbinden der GameManager-API damit schon fertig, da die weiteren Schritte nun konkret auf die Implementierung der Spiellogik eingehen werden. Stattdessen wollen wir zunächst lediglich anzeigen lassen, dass der GameManager tatsächlich schon funktioniert. Das Textlabel in der content_main.xml passen wir so an, dass wir es im Code wiederfinden:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    ...
    tools:showIn="@layout/activity_main" tools:context=".MainActivity">

    <TextView
        android:id="@+id/status"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</RelativeLayout>

Dann erstellen wir eine Funktion, die den Status des Spiels ermittelt und im Textlabel ausgibt:

    ...

    private void onGameConnected() {
        TextView tv = (TextView) findViewById(R.id.status);

        GameManagerState state = mGameManagerClient.getCurrentState();

        String status = state.getApplicationName() + "\n" +
                "Status: " + state.getGameStatusText() + "\n" +
                "Gameplay state: " + state.getGameplayState() + "\n";

        tv.setText(status);
    }

    ...

und rufen diese auf, wenn der GameManagerClient gesetzt wurde:

    ...

    private class GameManagerClientResultCallback
            implements ResultCallback<GameManagerClient.GameManagerInstanceResult> {
        @Override
        public void onResult(
                GameManagerClient.GameManagerInstanceResult gameManagerInstanceResult) {
            mGameManagerClient = gameManagerInstanceResult.getGameManagerClient();
            onGameConnected();
        }
    }

    ...

Wenn wir die App nun starten, sollten wir folgendes Ergebnis sehen:

Screenshot_20151110-220411

Zusammenfassung

Das Einbinden des GameManagers ist im Vergleich zur Einbindung des Chromecast-SDKs schon fast ein Kinderspiel. Die weiteren Schritte sind nun, sich eine sinnvolle Spiellogik auszudenken und über die GameManager des Senders und Receivers abzudecken. Dies soll in den weiteren Folgen betrachtet werden.

Die MainActivity ist inzwischen so stark angewachsen und unübersichtlich mit all deren Listener und Callback-Klassen, dass ich diese auslagern werde. Dies wird in den Samples von Google ebenfalls gemacht. Der Vorteil besteht darin, dass man in der MainActivity dann nur noch den tatsächlich spielrelevanten Code hat und die Schnittstellenthematik in einer separaten Klasse behandelt wird.

Die Sourcen des gesamten Tutorials, wie es hier beschrieben ist, könnt ihr euch hier herunterladen: chromecast_game_tutorial.zip. Die Quellen und dieses Tutorial sind unter der Apache 2.0 Lizenz entwickelt worden.

Referenzen

Für die Ausarbeitung dieses Tutorials habe ich folgende Quellen verwendet:

Google Cast Game Manager API

weitere Quellen sind wie immer die Samples von Google auf GitHub.

Leider wird es dann schon recht rar mit guten Quellen für die Entwicklung von Spieleapps mit Chromecast.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.