Der Weg zum eigenen Browsergame - Die Rangliste
Wer ist besser als ich und, noch wichtiger, wer ist schechter? Das lässt sich schnell mit einer Rangliste beantworten. Für Aktionen im Spiel gibt es Punkte die auf der Rangliste in Summe dargestellt werden. Nach Punkten sortiert lässt sich damit der Erfolg der Spieler in einer einfachen und verständlichen Form anzeigen. Ohne eine solche erfolgsorientierte Bewertung fehlt es vielen Spielern an Motivation und hören auf.
Ist doch einfach, warum dieses Tutorial?
Bei der Rangliste gibt es ein paar knifflige Details die es zu beachten gilt.
Gleicher Punktestand
Was tun wenn zwei Spieler den gleichen Punktestand haben?
Entweder man sortiert die Spieler mit der gleichen Anzahl an Punkten nach der Spieler-ID (wer zuerst kam malt zuerst), oder man speichert zu den Punkten noch das Datum der letzten Aktualisierung ab und vergleicht bei Punktegleichheit wer zuerst diese Punkte erreicht hat.
Die zweite Variante ist ganz klar zu bevorzugen, denn sie ist fairer.
Ranglistenposition eines Spielers
Möchte man einem Spieler seine aktuelle Position innerhalb der Rangliste anzeigen, müsste man die Punkte aller Spieler vergleichen, die sortierten Daten aus der Datenbank geben lassen und mit PHP die aktuelle Ranglistenposition ermitteln.
Folgendes Codebeispiel zeigt den Weg zur aktuellen Ranglistenposition eines Spielers.
define(CURRENT_USER_ID, 1); $db = new MyDB(); // Siehe die MyDB Klasse aus dem Tutorial "Die Datenbank" $db->connect('localhost', 'database', 'username', 'password'); $sql = 'SELECT user_id, points FROM user_points ORDER BY points DESC, last_update ASC'; $data = $db->query($sql, 'assoc'); $pos = 1; foreach($data as $dataset) { if (CURRENT_USER_ID==$dataset['user_id']) { echo 'Du bist mit '.$dataset['points'].' Punkten auf Ranglistenposition '.$pos.'.'; break; }; $pos++; };
Wenn in dem Spiel beispielsweise 10.000 Spieler spielen, ist diese Art der Positionsermittlung sehr unperformant und denkbar ungeeignet.
Ranglistenposition eines Spielers (Version 2)
Um diese langsame Ermittlung der Ranglistenposition so selten wie nur möglich durchführen zu müssen, verwenden wir eine weitere Tabelle, in der wir die vorberechneten Ranglistenwerte abspeichern.
Wir brauchen also eine Tabelle zum erfassen der aktuellen Punkte (user_points) und eine Tabelle für die vorberechnete Rangliste (user_ranking).
Erhält ein Spieler Punkte für eine Aktion im Spiel, ergänzen wir den Wert in der Tabelle user_points durch die hinzukommenden Punkte und setzen den Zeitpunkt der letzten Aktualisierung auf "jetzt".
define(CURRENT_USER_ID, 1); $db = new MyDB(); // Siehe die MyDB Klasse aus dem Tutorial "Die Datenbank" $db->connect('localhost', 'database', 'username', 'password'); $now = time(); $new_points = 1; $sql = "UPDATE user_points SET points = points + {$new_points}, last_update = {$now} WHERE user_id = ".CURRENT_USER_ID; $db->query($sql);
In regelmäßigen Abständen aktualisieren wir nun die Tabelle user_ranking. Das erfolgt in drei Schritten.
- Wir leeren die Tabelle user_ranking.
- Wir initialisieren eine MySql-Variable @pos die bei 1 beginnt und mit jedem Datensatz um 1 nach oben gezählt wird.
- Wir sortieren die user_points Tabelle nach points und last_update, und schreiben die ermittelten Daten, sowie den aktuellen Wert für @pos, in die Tabelle user_ranking.
define(CURRENT_USER_ID, 1); $db = new MyDB(); // Siehe die MyDB Klasse aus dem Tutorial "Die Datenbank" $db->connect('localhost', 'database', 'username', 'password'); $sql = "TRUNCATE user_ranking"; $db->query($sql); $sql = "SET @pos=0;"; $db->query($sql); $sql = "INSERT INTO user_ranking (user_id, points, last_update, position) SELECT user_id, points, last_update, @pos := @pos+1 FROM user_points ORDER BY points DESC, last_update ASC"; $db->query($sql);
Dieses Skript könnte beispielsweise alle 10 Minuten per Cronjob aufgerufen werden.
Die Vorteile der 2. Version
- Es muss nicht immer die gesamte Rangliste geladen werden um die Ranglistenposition eines Spielers zu erhalten.
- Die "aufwendige" Abfrage zur Beschaffung der sortierten Rangliste wird nur alle paar Minuten/Stunden ausgeführt und nicht bei jedem Klick eines Spielers.
- Durch die Trennung der zwei Tabellen user_points und user_ranking ersparen wir uns die langsamen Datensatzsperrungen. Denn auf user_points wird nur schreibend zugegriffen und auf user_ranking nur lesend. Ausgenommen ist natürlich die Aktualisierung der Tabelle user_ranking. Hier werden aber mit nur einer Abfrage alle Daten auf einmal in die Tabelle übernommen. Ein dazwischenfunken durch ein anderes Skript ist somit ausgeschlossen.
- Weil in user_ranking lediglich die Daten zur Ausgabe aufbereitet werden, ist es nicht schlimm wenn diese Tabelle bei einem Neustart des Servers geleert würde. Da wir beim Beschreiben der Tabelle keine Transaktionen benötigen, bietet sich also an diese im Tabellenformat "MEMORY" anzulegen. Das bedeutet, dass die in der Tabelle enthaltenen Daten lediglich im Speicher liegen und nicht auf einer Festplatte gespeichert werden. Das erhöht die Zugriffsgeschwindigkeit nochmals enorm - vorausgesetzt es ist genügend Speicher vorhanden.
Packen wir alles in eine "schönere" Form
Machen wir es mal wie ein Marktschreier, der noch ein bisschen mehr in die Tüte packt um sein Zeugs an den Mann zu bringen. Die bisherigen Funktionen sind eine tolle Sache, aber packen wir einfach mal noch ein bisschen was obendrauf um die Klasse noch attraktiver zu machen.
Die Klasse MyRanking:
class MyRanking { private $db; public function init($db) { $this->db = $db; } public function update() { $sql = "TRUNCATE user_ranking"; $this->db->query($sql); $sql = "SET @pos=0;"; $this->db->query($sql); $sql = "INSERT INTO user_ranking (user_id, points, last_update, position) SELECT user_id, points, last_update, @pos := @pos+1 FROM user_points ORDER BY points DESC, last_update ASC"; $this->db->query($sql); } public function addUserPoints($user_id, $add_points) { $now = time(); $sql = "UPDATE user_points SET points = points + {$add_points}, last_update = {$now} WHERE user_id = {$user_id}"; $db->query($sql); } public function getUserPosition($user_id) { $sql = "SELECT position FROM user_ranking WHERE user_id = {$user_id}"; $result = $this->db->query($sql, 'assoc'); if (!$result) return false; return $result[0]['position']; } public function getUser($user_id) { $sql = "SELECT * FROM user_ranking WHERE user_id = {$user_id}"; $result = $this->db->query($sql, 'assoc'); if (!$result) return false; return $result[0]; } public function getUserRange($user_id, $range=5) { $position = $this->getUserPosition($user_id); $min_position = $position-$range; $max_position = $position+$range; $sql = "SELECT * FROM user_ranking WHERE position >= {$min_position} AND position <= {$max_position} ORDER BY position ASC"; return $this->db->query($sql, 'assoc'); } }
Und so wird sie verwendet.
define(CURRENT_USER_ID, 1); $db = new MyDB(); // Siehe die MyDB Klasse aus dem Tutorial "Die Datenbank" $db->connect('localhost', 'database', 'username', 'password'); $rank = new MyRanking(); $rank->init($db); // Dem aktuellen Spieler 2 Punkte geben. $rank->addUserPoints(CURRENT_USER_ID, 2); // Tabelle user_ranking aktualisieren. $rank->update(); // Ausgehend vom aktuellen Spieler die 4 Spieler über ihm, ihn selbst, und die 4 unter ihm anzeigen. $data = $rank->getUserRange(CURRENT_USER_ID, 4); foreach($data as $dataset) echo 'ID:'.$dataset['user_id'].' ist '.$dataset['position'].'.<'.'br>';
Schlusswort
Eine zweiteilige Datenhaltung ist für eine Rangliste absolut zu empfehlen. Sie bietet uns viele Vorteile und ist zudem auch noch recht übersichtlich. So ist die Positionsbestimmung eines Spielers ein einfacher SELECT auf die Tabelle user_ranking. Ansonsten müsste man es wie im ersten Codebeispiel machen, was wesentlich unübersichtlicher ist.
War of Titans
![]()
War of Titans - das Gladiatoren-Browsergame
Kämpfe online gegen andere Gladiatoren und erlebe die Action in der Arena.
Weiterlesen...Caribic Islands
Caribic Islands - Aufbausimulation auf der Insel
Werde in Caribic Islands der unangefochtener Herrscher über die sieben Weltmeere.
Weiterlesen...Seafight - Piraten-Action auf dem Meer
Seafight - Piraten-Action auf dem Meer
Spiele auf den Weltmeeren im actiongeladenen Piraten-Browserspiel.
Weiterlesen...Der Weg zum eigenen Browsergame - Das Eventsystem

Der Weg zum eigenen Browsergame
Thema: Das Eventsystem
Weiterlesen...Der Weg zum eigenen Browsergame - Die Rangliste

Der Weg zum eigenen Browsergame
Thema: Die Rangliste
Weiterlesen...








