Erstellung einer einfachen Online High-Score-Liste für Android Spiele (1)


Erstellung einer einfachen Online High-Score-Liste für Android Spiele (1)

Da ich schon einmal die Fragestellung zur einfachen Verwaltung von Online High-Score-Listen gesehen habe, und nun auch direkt von einem freundlichen Blog-Leser dazu gefragt worden bin, habe ich mich entschlossen die Thematik mal etwas zu beleuchten.
In einer kleinen Serie möchte ich vorstellen, wie ich an das Thema der Online High-Score-Liste heran gegangen bin und wie meine Lösung aussieht. Es gibt bestimmt bessere oder professionellere Lösungen, aber meine Lösung hat sich als sehr flexibel herausgestellt.
Die Lösung basiert auf der LevelStatsDBConnector-Klasse von Andengine (http://code.google.com/p/andengine/source/browse/src/org/anddev/andengine/util/levelstats/LevelStatsDBConnector.java).
Da der Artikel sehr lang geworden wäre, habe ich ihn in mehre logische Abschnitte aufgeteilt.
In dem Beispiel gehe ich davon aus das die Engine Andengine verwendet wird. Diese wird z.B. bei der Umwandlung von dem HTTP-Request, den SharedPreferences und den Zufallszahlen verwendet. Es sollte aber kein allzu großes Problem sein diese Methoden durch eigene zu ersetzen.

Statistiken Sammeln

Damit eine Online High-Score-Liste genutzt werden kann, müssen natürlich zunächst die relevanten Daten gesammelt werden. Bei der Punktzahl ist dies natürlich naheliegende. Aber weitere Werte können ebenfalls interessant sein und können ebenfalls erfasst werden wie z.B. durchschnittliche Dauer für einen Zug, Spieldauer, Anzahl der Interaktionen, Anzahl fehlerhafter Interaktionen etc.

Eindeutige Spieler-ID erstellen

Damit man einzelne Spieler in einer Datenbank unterscheiden kann, sollten jeder Spieler eine eindeutige ID erhalten. Diese ID sollte man lokale auf dem Gerät speichern (z.B. unter Verwendung von SharedPreferences wie in meinem Beispiel), damit sie bei jedem Spielstart gleich bleibt.
Der Einfachheit nutze ich eine INT-Zufallszahl als Spielder ID. Es gibt diverse andere Möglichkeiten, die wirklich eindeutige ID’s für ein Gerät bereit stellen. Aber da der Spieler zusätzlich einen Namen einträgt sollte die Eindeutigkeit weiterhin gegeben sein – es ist eher eine 90% Lösung. Der Konstruktor der StatsDB-Klasse liest diese ID aus oder erzeugt eine entsprechende neue. auch der Spielername wird hier bereits aus den SharedPreferences ausgelesen.

public class StatsDBConnector  {
    private final int mPlayerID;
    private Context context;

    public StatsDBConnector(final Context pContext) {
        context = pContext;
        final SharedPreferences simplePreferences = SimplePreferences.getInstance(pContext);
        final int playerID = simplePreferences.getInt(
                PREFERENCES_LEVELSTATSDBCONNECTOR_PLAYERID_ID, -1);
        if (playerID != -1) {
            this.mPlayerID = playerID;
        } else {
            this.mPlayerID = MathUtils.random(1000000000, Integer.MAX_VALUE);
            SimplePreferences
                    .getEditorInstance(context)
                    .putInt(PREFERENCES_LEVELSTATSDBCONNECTOR_PLAYERID_ID,
                            this.mPlayerID).commit();
        }
        playerName = simplePreferences.getString(
                PREFERENCES_LEVELSTATSDBCONNECTOR_PLAYERID_NAME, "-");
    }
}

Die Datenbank-Struktur und was dafür notwendig ist

Damit die Daten in einer online-High-Score-Liste gespeichert werden können benötigt man einen Web-Server und eine Online-Datenbank. Mein Beispiel verwendet eine PHP & MySQL-Lösung.
Die Datenbankstruktur ist sehr einfach gehalten. Es wird eine Tabelle benötigt, die mindestens folgende Felder enthält:

Feld        Typ
player_id    bigint
name        varchar(20)
score        int
date        varchar(20) // hier ist natürlich auch ein Datumsfeld oder ähnliches möglich
ID        bigint, autoincrement

Es können natürlich weitere Felder hinzu gefügt werden, je nach Bedarf. Felder die interessant sein könnten: Spieldauer, Status (Completed, Game over, Cancelled), Level, Schwierigkeitsgrad, Version, etc.
Der Primärschlüssel ist die ID, das Feld Player_ID sollte indiziert werden.

Hier der SQL-Code zum erstellen der Tabellenstruktur:

CREATE TABLE IF NOT EXISTS `gameresults` (
  `player_id` bigint(20) NOT NULL,
  `score` int(11) NOT NULL,
  `date` varchar(20) NOT NULL,
  `name` varchar(20) DEFAULT NULL,
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  PRIMARY KEY (`id`),
  KEY `level_id` (`level_id`,`player_id`)
)

Die Übertragung der Statistik-Informationen in eine Web-Datenbank

Aus dem Spiel senden wir einfach bei dem entsprechenden Event (z.B. Game Over) die für das tracking notwendigen Informationen per POST-Nachricht an eine Empfänger-Webseite. Dies passiert wie folgt:

Senden von der Android-App

Hier ist das senden der Daten beispielhaft dargestellt. Das Senden eines HTTP-Requests wird in einem separaten Thread ausgeführt, da es dauern kann,bis die Antwort vom Web-Server geliefert. In diesem Beispiel sind noch keine Sicherheits-Mechanismen eingebunden, diese werden in einem späteren Artikel behandelt.

new Thread(new Runnable() {
            @Override
            public void run() {
                try {
final HttpClient httpClient = new DefaultHttpClient();
final HttpPost httpPost = new HttpPost("http://servername/order/insert.php");
final List nameValuePairs = new ArrayList(4);
nameValuePairs.add(new BasicNameValuePair("player_id",String.valueOf(StatsDBConnector.this.mPlayerID)));
nameValuePairs.add(new BasicNameValuePair("score",String.valueOf(StatsDBConnector.this.mPlayerID)));
nameValuePairs.add(new BasicNameValuePair("name",String.valueOf(StatsDBConnector.this.mPlayerID)));

httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
final int statusCode = httpResponse.getStatusLine().getStatusCode();
if (statusCode == HttpStatus.SC_OK) {
   final String response = StreamUtils.readFully(httpResponse.getEntity().getContent());
} else {
   Debug.e("http: send not possilbe");
}
} catch (final IOException e) {
        Debug.e(e);
   }
   }
   }).start();

Empfangen der Daten Server-Seitig

Auf dem Empfänger-Server liegt ein PHP-Script. Dieses überprüft zunächst ob die Parameter alle vorhanden sind und ob diese gültigkeit haben, sonst nutzt ein Hacker ggf. SQL-Code Injection um die Datenbank zu kompromittieren.
Anschließend wird eine SQL-Verbindung aufgebaut und die Daten in der Datenbank gespeichert.
Es kann sich noch anbieten in dieser Phase sofort noch die aktuellen Online-High-Score-Daten auszulesen, schließlich will der Spieler ja sofort sehen, wenn er in der High-Score Liste eingetragen worden ist.

"; // message to be displayed if something went wrong

// function to Check if the name is a valid string - only alphanumierc and space, dash, underline are allowed
// A good regular expression would be the better way to do this.
    function validateName($inName,$id){  
        $aValid = array(' ', '-',"_",",");
        if(!ctype_alnum(str_replace($aValid, '', $inName))) {
            $inName="player-" . ($id % 10000 );
        }
        return $inName;
    }

    if (
            isset($_POST['player_id']) &&
            isset($_POST['score']) &&
            isset($_POST['name']) &&
            is_numeric($_POST['player_id']) &&
            is_numeric($_POST['score'])){ // Check if all required varialbles are set and if the numeric ones are numeric

            mysql_connect($host, $us, $pw) or die($fail . mysql_error());
            mysql_select_db($db) or die($fail . mysql_error());

            $currDate = date("Y.m.d H:i");

            $name = validateName($_POST['name'],$_POST['player_id']);
            $sql = "INSERT INTO `gameresults` (
                                `player_id` ,
                                `score` ,
                                `date` ,
                                `name`
                            )
                            VALUES ('" . $_POST['player_id'] .
                            "', '" . $_POST['score'] .
                            "', '$name " .
                            "', '$currDate'" .
                            "');";
            mysql_query($sql) or die($fail . mysql_error());
            echo "";
    } else {
        echo $fail;
    }
?>

Ausblick

Im nächsten Beitrag behandle ich das Thema wie die Daten aus der Web-Datenbank ausgelessen werden können.

Neues Projekt gestartet: Tower Defense Spiel für Android

Letztes Wochenende habe ich ein neues Projekt gestartet. Ich habe begonnen ein Tower Defense Spiel für Android zu entwickeln. Bei dem Spiel wird es sich um ein klassisches Tower Defense handeln, bei dem die Gegner vordefinierten Wegen folgen. Deine Aufgabe als Spieler ist es mit Verteidigungsanlagen, die Ihr kaufen könnt und auf der Karte platziert die Gegner davon abzuhalteneuer Schloss anzugreifen. Noch ist kein Name für das Spiel gefunden, aber der wird sicher bald folgen.

Bei der Entwicklung werde ich nun die Game Engine Andengine nutzen und meine Erfahrungen damit hier teilen.

Erste Schritte

Auch wenn der Start mit Andengine etwas komplizierter war als mit e3roid, gerade was Texturen, Atals etc. betrifft, macht das ganz doch einen besseren Eindruck. Viele Herausforderungen, an denen in ich e3roid länger arbeiten musste, werden in Andeninge bereits durch Standard-Funktionen erledigt (z.B. einen Gegner einen definierten Weg laufen lassen). Das sollte die Entwicklungszeit stark verkürzen.

 Projekt Status

Hier schon mal der erste Screenshot, in dem ein paar Gegner und Vereidigungstürme zu sehen sind. Die Grafik ist noch sehr rudimentär, vor allem, was das Leveldesign angeht, aber bisher handelt es sich noch um ein Demolevel für reine Funktionstests.

StoneSetter trifft Geocaching – erst spielen, dann cachen

Ende letzter Woche habe ich die Web-Variante des StoneSetter-Spiels als Mystery-Cache auf Geocaching.com (GC3A7DQ) veröffentlicht . Die Cache-Koordinate wird dabei erst sichtbar, wenn eine gewisse Punktzahl im Spiel erreicht wurde. Dabei sind schon erstaunliche Punktestände erspielt worden:

 

 

Geocaching Quiz Template – Rätselcaches mal anders

Geocaching Quiz ScreenshotHeute mal etwas zum Thema Geocahing.

Seid Ihr es auch Leid bei Mystery Geocaches immer so viel zu rechnen und dann noch im GeoChecker zu prüfen ob Ihr euch nicht verrechnet habt?
Hier ein Beispiel, wie man es auch lösen kann. Ich habe ein kleines PHP-Tool entwickelt, mit dem jeder leicht sein eigenen Quiz erstellen kann.

Einen Beispiel Cache, der das Quiz nutzt findet ihr hier: http://coord.info/GC11H4D (DVD Cache)

Das Besondere dabei:

Es können freitext-Antworten gegeben werden und es können unterschiedliche Schreibweisen als richtig gewertet werden.

Voraussetzungen

Voraussetzung dazu ist jedoch, dass man Zugriff auf einen Web-Server hat der PHP unterstützt, auf dem das ausgefüllte Template dann hochgeladen wird. Eine mySQL Datenbank wird nicht benötigt, weil ich keine im Zugriff hatte, als ich das Tool entwicklet habe.

Und so wird das Tool verwendet

  1. Ihr laded euch die Quellen runter: http://www.mattwien.de/download/quiz.zip
    Ausprobieren könnt ihr das Beispiel hier: http://www.mattwien.de/geocaching/sample/fragen.php
    Continue reading

Kostenlose Android Spiele die ich gerne spiele

Heute möchte ich mal ein paar Spiele vorstellen, die ich gerne auf dem Android spiele und ich hiermit empfehlen möchte:

Jewels
Einfaches Knobelspiel für zwischendurch, welches mir aber immer wieder Spaß macht.
https://market.android.com/details?id=org.mhgames.jewels

Medieval Castle Defense
Das beste Tower-Defense-Spiel, dass ich bisher gefunden habe.
https://market.android.com/details?id=com.nova.root

Meganoid FREE
Retro Jump & Run Spaß
https://market.android.com/details?id=com.meganoid.engine

Move it! Free
Kniffliges Rätselspiel, in dem Ihr Blöcke in einen Zielbereich schieben müsst.
https://market.android.com/details?id=uk.co.aifactory.moveitfree

Klempner
Drehe die Rohre so, dass das Wasser abfließen kann
https://market.android.com/details?id=com.monstergame.plumber

Defender
Verteidige deine Burg gegen Wellen heranstürmender Gegner.
https://market.android.com/details?id=com.droidhen.defender

Block Puzzle / Baustein Rätsel
Knobelspaß: Versuche Tetris-ähnliche Steine so auf einem Spielbrett anzuordnen, dass alles Passt.
https://market.android.com/details?id=biz.mtoy.phitdroid.sixth

Wer weitere Spiele empfehlen möchte, kann dies in den Kommentaren tun.

Verwendung von Stand-by und Wakelock bei Android Spielen


Wann sollte man den Stand-by Modus bei Spielen unterdrücken?

Das kommt wohl sehr auf das Spiel an. Wenn der Spieler dauerhaft in das Spiel eingreifen muss oder nur eine Begrenzte Zeit für einen Zug hat, muss man den Stand-by Modus sicher nicht deaktivieren.
Relevant wird das ganze nur wenn man z.B. Zwischensequenzen oder Videos anzeigt, bei denen der Spieler nicht selbst aktiv werden muss.
Wichtig ist nur die Reaktion des Spiels auf den Stand-by Modus. Am Komfortabelsten ist es, wenn ein Spiel beim Stand-by automatisch in ein Pause-Menü geht und von wo aus das Spiel fortgesetzt werden kann.

Wie kann der Stand-by Modus unterdrückt werden?

Bei Android Geräten werden sogenannte “Wakelocks” verwendet um den Stand-by Modus zu unterdrücken.
Hier seht ihr eine Beispielimplementierung unter Verwendung der Game-Engine e3roid. Sehr wichtig ist, dass beim Beenden des Spiels die Wakelocks wieder freigegeben werden.

Manifest:

Als erstes die Berechtigungen eintragen:

<uses-permission android:name="android.permission.WAKE_LOCK" />

Hauptklasse:

public class StoneSetter extends E3Activity {
private PowerManager.WakeLock wl;
public E3Scene onLoadScene() {
   ...
   PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
   wl = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK, "SomeTag");
   wl.acquire();
}
 
public boolean onKeyDown(E3Scene in_scene, int keyCode, KeyEvent event) {
if((keyCode == KeyEvent.KEYCODE_MENU || keyCode == KeyEvent.KEYCODE_BACK)){
   exitGame();
   return true;
}else{
   return false;
}
}
 
public void exitGame(){
  wl.release();
  finish();
}
 
}

StoneSetter für Android: neue Version und Demo-Video

Seit gestern ist die neueste Version des Spiels StoneSetter im Android Market veröffentlich.

 

Zusätzlich gibt es jetzt ein kurzes Einführungs-Video zu StoneSetter bei dem Ihr einen guten Einblick zum Gameplay erhaltet:

Android is a trademark of Google Inc.

Überlegungen zur Spiele-Entwicklung

Möchte man ein Spiel entwickeln muss man sich zunächst überlegen, in welchem Genre man sich bewegen möchte. Soll es eher ein Action oder ein Puzzle/Brett Spiel sein? Aus der Beantwortung der Frage ergeben sich sehr schnell weitere Fragen / Aufgaben.

Bei einem Action-Spiel (z.B. Jump&Run) muss man auf sehr viele Dinge achten, die man bei einem langsamen Rundenbasierten Brett-Spiel außer Acht lassen kann. So muss man bei einem Action-Spiel immer darauf achten, dass die Programmierung performant ist und eine hohe anzahl an Bildern pro Sekunde auf den Bildschirm darstellen kann. Bei einem Brett-Spiel (in der Regel rundenbasiert) ist dies nicht so tragisch, da darf eine Berechnung ruhig mal etwas länger dauern. Einen sehr guten Einblick in das Thema erhält man z.B. in dem Artikel Real Time vs Turn Based Games.

Das erste Android-Spiele-Projekt war ein solches Action-Spiel. Die Technik und Performance stellte erst mal nicht das größte Problem dar (nachdem ich entsprechende Game-Engines gefunden hatte). Das größte Problem für mich war das Level Design, welches sehr Zeitaufwändig ist, weswegen ich das erste Projekte auch wieder auf Eis gelegt habe. Die Aufteilung des Zeitaufwandes für das Leveldesing inklusive Grafik und der eigentlichen Programmierung betrug nachher ca. 80% Leveldesing und 20% Programmierung.

(Graifken größtenteils von http://www.reinerstilesets.de/de/ – eine wirklich gute qulle für Grafiken und mehr)

Continue reading

Android Spiel StoneSetter (Beta) veröffentlicht

Gestern abend war es endlich so weit. Ich habe die  erste Version des Android-Spiels StoneSetter im Android Market veröffentlicht.

Ihr findet weitere Informationen zum Spiel auf der Andorid: Stonesetter Seite oder direkt im Andorid Market.

Wer kein Android Gerät hat, kann sich auf der Seite “Online-Spiel: StoneSetter” grob das Spielprinzip ansehen.

e3roid Beispiel: Score verwalten und Punktzahlen anzeigen

Was jedes klassische Spiel benötigt, ist die Anzeige der erreichten Punktzahl. Ein einfacher Text sieht in der Regel nicht “spielerisch” genug aus – somit wollte ich kein normales “Textsprite” verwenden.

Unten seht Ihr meine Implementierung für die Engine e3roid auf Basis von Animierten Sprites. Eigentlich hätten auch “TiledSprites” genügt, aber ich habe noch die Idee die Zahlen animiert erscheinen und verschwinden zu lassen, was aber noch nicht umgesetzt ist.

Zum Einsatz muss natürlich erst der Rumpf vorhanden sein der e3roid verwendet. Den Code könnt ihr einfach in das Tutorial “Handle touch events” von der e3roid-Seite einbinden. Bei jeder “touch”-aktion sollte dann eure Punktzahl steigen.

Hier seht Ihr eine Beispielgrafik mit der das Coding funktionieren würde:

Continue reading