jQuery Vibration Game (1) - Idee und grundlegende Features

An der Vibrations API ist besonders bemerkenswert, dass diese neue Schnittstelle explizit im Hinblick auf eine mögliche Verwendung in Spielen erdacht wurde. Da wollen wir uns also gar nicht groß Lumpen lassen und hauen gleich mal eine Spielidee raus. Wir wollen die Vibration API aber nicht nur einfach nutzen, sondern wir machen sie gleich zum Kernelement unseres kleinen Spielchens. Hierfür verwenden wir neben jQuery, auch direkt die in meinem vorherigen Beitrag vorgestellte Vibration API Wrapper-Funktion.

Stellen wir uns einmal vor, wir wären ein begnadeter Schlossknacker. Unsere Spezialität sind elektronische Schlösser, denn wir besitzen eine besondere Fähigkeit. Wir können die richtige Kombination ertasten. Wenn wir mit unserem Finger eine Taste leicht berühren, merken wir sofort, ob diese zur Kombination gehört. Die Schwierigkeit beim Schlossknacken besteht für uns also nicht mehr darin das Schloss zu öffnen, sondern richtig schnell dabei zu sein. Denn warten wir zu lange, geht der Alarm los und wir werden geschnappt. Nicht nur das, je öfter wir versuchen Schlösser zu öffnen, desto schwieriger wird das Ganze, denn die Schlösser passen sich unserem Verhalten an.

Nochmal mit Gefühl

Wir platzieren eine frei definierbare Anzahl von Containern auf dem Bildschirm, diese sollen uns als Tasten dienen. Einige dieser Tasten sind Teil der Kombination. Tippt der Spieler mit dem Finger auf eine Kombinationstaste, so vibriert sein Gerät kurz. Das ist das Äquivalent der Schlossknacker-Fähigkeit. Drückt er nun noch einmal auf die Taste, so rastet diese ein. Hat er alle Tasten gefunden und gedrückt, so öffnet sich das Schloss und er hat gewonnen. Klingt einfach, wäre da nicht der Timer, der im Hintergrund läuft. Läuft der Timer ab, bevor alle Tasten gedrückt wurden, ist das Spiel verloren.

Hierzu gibt es ein paar Besonderheiten. Das erste Spiel gilt als Freiversuch. Der Spieler kann ohne Zeitdruck die Tasten suchen und drücken. Spielt er jetzt noch einmal, wird seine benötigte Zeit als Timer verwendet (das Schloss hat dazugelernt). Dies geschiet bei jedem Sieg. Die Bestzeit wird immer als neuer Timer verwendet, dadurch erhalten wir einen Wiederspielbarkeitswert. Seine Bestzeit kann der Spieler nach einem Sieg in eine Rangliste eintragen und/oder auf einem Social Network seiner Wahl teilen.

Damit wir uns das alles auch merken können, habe ich mit Omnigraffle gleich mal ein kleines Diagrämmchen erstellt. Wenn an dieser Stelle noch nicht alles sofort ersichtlich ist, nicht verzagen. Das wird im Laufe der Blogreihe (hoffentlich) deutlich werden.

jQuery Vibration Game - Flow chart

Und los geht's

Wir fangen ganz klassisch an, indem wir erst einmal unsere Variablen definieren. Und zwar Breite (vgWidth) und Höhe (vgHeight) des Tastenfeldes, außerdem die maximale Codelänge (vgDigits). Ob wir das jetzt in einer eigenen Datei machen oder direkt zwischen die script-Tags tackern, soll uns erst einmal egal sein.

var vgWidth    = 3;
var vgHeight   = 4;
var vgDigits   = 5;

Da unser Spielchen sowieso vor allem auf Mobilgeräten laufen soll, machen wir das Tastenfeld etwas höher als breit. So kennt ist das der Spieler von seinem Handy gewohnt. Außerdem müssen wir jetzt noch einen Container (vgContainer) definieren, in den wir unsere Tasten reinpacken können.

Nicht wundern, ich steh bei JavaScript auf die caMel-Schreibweise. Wenn man alles klein schreibt und mit Unterstrichen trennt, sieht der Code irgendwie so zerrupft aus. Aber das ist nur meine Meinung und das kann natürlich jeder machen, wie er will. Hier erstmal unser Container:

var vgContainer = '#vibration-game';

Entsprechend schreiben wir unser (im Geiste natürlich bereits erstelltes HTML-Dokument:

<div id="vibration-game"></div>

Nun brauchen wir noch eine Funktion mit der wir das Spiel erst einmal zusammen bauen können. Diese platziert jetzt Breite x Höhe (also 12) Buttons in unserem Spielcontainer. Um das Ganze hinterher besser stylen zu können, platzieren wir dafür dann noch einen Wrapper im Container.

function vgInitGame() {
  $('<div/>')
    .addClass('vg-buttons')
    .appendTo($vgContainer);

  for (var y = 0; y < vgHeight; y++) {
    for (var x = 0; x < vgWidth; x++) {
      $('<div/>')
        .attr({'id' : 'vg-button-' + x + '-' + y})
        .addClass('vg-button')
        .appendTo($('.vg-buttons', $vgContainer));
    }
    $('<div/>')
      .addClass('vg-clear')
      .appendTo($('.vg-buttons', $vgContainer));
  }
}

$(document).ready(function() {
  var $vgContainer = $(vgContainer);
  vgInitGame();
});

Bitte nicht bewegen, Sie werden gescannt

Wer sich mit jQuery noch nicht so richtig auskennt, der konsultiert an dieser Stelle am besten einmal die grandiose Dokumentation. Jetzt lungern die Buttons natürlich gänzlich sinnlos im Container herum, weswegen wir Ihnen mittels Eventhandler Leben einhauchen werden. Dies tun wir direkt in der vbInitGame-Funktion.

var vgButtonScanned = 'vg-button-scanned';

$('.vg-button', $vgContainer).click(function() {
  if ($(this).hasClass(vgButtonScanned)) {
    vgPressButton($(this));
  }
  else {
    vgScanButton($(this));
  }
});

Aber woher kommt jetzt plötzlich die Variable vbButtonScanned? Die haben wir natürlich idealerweise vorher irgendwo definiert. Sie beinhaltet einfach die Klasse, die wir uns für Buttons ausgedacht haben, die bereits gescannt wurden. Wir erinnern uns, der Spieler kann den Code erfühlen. Beim ersten Berühren scannt er den Button lediglich, beim zweiten drückt er ihn. Wir müssen ihn also irgendwie als gescannt markieren und dies geschiet mit der Klasse.

var vgButtonActive = 'vg-button-active';
var vgDuration = 250;

function vgScanButton($vgButton) {
  if ($vgButton.hasClass(vgButtonActive)) {
   navigator.vibrate(vgDuration);
 }

  $('.vg-button').removeClass(vgButtonScanned);
  $vgButton.addClass(vgButtonScanned);
}

Wir beschäftigen uns aber zunächst einmal mit dem Scannen. Wenn es sich bei dem Button um den richtigen handelt, soll das Gerät des Spielers kurz vibrieren (bei uns 250ms). Den richtigen Button haben wir vorher mit einer Klasse markiert. In jedem Fall markieren wir den Button aber nun als gescannt und heben die Markierung für alle anderen Buttons wieder auf. Das hat nicht wirklich einen Sinn, sondern soll nur die Schwierigkeit erhöhen.

Ein Button wird gedrückt

Kommen wir aber mal endlich zum Kernstück des Spieles. Der Spieler soll ja auch irgendwie mal den Code knacken und das Schloss öffnen.

var vgProgress = 0;

function vgPressButton($vgButton) {
  if ($vgButton.hasClass(vgButtonActive)) {
    vgProgress++;
  }
  else {
    vgProgress = 0;
  }
  vgUpdateGame();
}

Dies führt uns zur zweiten Funktion, die durch den Onclick-Handler aufgerufen werden kann. Klickt/drückt der Spieler noch einmal auf den gescannten Button, wird überprüft, ob er richtig gelegen hat. Wenn nicht, wird sein Fortschritt zurückgesetzt. Also die Anzahl richtiger Buttons, die er bereits gedrückt hat. Dadurch muss er wieder von vorne anfangen. Dies definieren wir in vgProgress. Erwischt er einen richtigen Button, erhöhen wir die Variable um 1. Nun rufen wir unsere wichtigste Funktion auf: vgUpdate.

function vgUpdateGame() {
  if (vgProgress >= vgDigits) {
    vgHandleWin();
    return;
  }
  else {
    $('.vg-button', $vgContainer)
      .removeClass(vgButtonActive)
      .removeClass(vgButtonScanned);

    var randomX = Math.floor(Math.random() * vgWidth) + 1;
    var randomY = Math.floor(Math.random() * vgHeight) + 1;
    $('#vg-button-' + randomX + '-' + randomY, $vgContainer)
      .addClass(vgButtonActive);
  }
}

function vgHandleWin() {
  alert('Gewonnen!');
}

Hier überprüfen wir direkt, ob wir bereits genug Buttons gedrückt haben. Wenn dem so ist, feuern wir unsere Siegfunktion raus. Bisher ist dies nur ein einfacher Alert. Wenn wir aber noch weiterspielen müssen/dürfen, nehmen wir erst einmal alle Button-Markierungen und platzieren dann zufällig eine neue Aktivmarkierung auf einem der Buttons. Fertig? Nein Moment, natürlich darf auch ein vgUpdateGame-Aufruf in unserer vgGameInit-Funktion nicht fehlen. So werfen wir den Mechanismus in Gang und haben unseren Kreislauf komplettiert.

Unsere JavaScript-Datei sieht dann erst einmal so aus:

var vgWidth    = 3;
var vgHeight   = 4;
var vgDigits   = 5;

var vgDuration = 250;

var vgProgress = 0;

var $vgContainer    = null;
var vgContainer     = '#vibration-game';
var vgButtonScanned = 'vg-button-scanned';
var vgButtonActive  = 'vg-button-active';

function vgInitGame() {
  $('<div/>')
    .addClass('vg-buttons')
    .appendTo($vgContainer);

  for (var y = 0; y < vgHeight; y++) {
    for (var x = 0; x < vgWidth; x++) {
      $('<div/>').attr({'id' : 'vg-button-' + x + '-' + y})
        .addClass('vg-button')
        .appendTo($('.vg-buttons', $vgContainer));
    }
    $('<div/>')
      .addClass('vg-clear')
      .appendTo($('.vg-buttons', $vgContainer));
  }

  $('.vg-button', $vgContainer).click(function() {
    if ($(this).hasClass(vgButtonScanned)) {
      vgPressButton($(this));
    }
    else {
      vgScanButton($(this));
    }
  });
}

function vgScanButton($vgButton) {
  if ($vgButton.hasClass(vgButtonActive)) {
   navigator.vibrate(vgDuration);
}

  $('.vg-button').removeClass(vgButtonScanned);
  $vgButton.addClass(vgButtonScanned);
}

function vgPressButton($vgButton) {
  if ($vgButton.hasClass(vgButtonActive)) {
    vgProgress++;
  }
  else {
    vgProgress = 0;
  }
  vgUpdateGame();
}

function vgUpdateGame() {
  if (vgProgress >= vgDigits) {
    vgHandleWin();
    return;
  }
  else {
    $('.vg-button', $vgContainer)
      .removeClass(vgButtonActive)
      .removeClass(vgButtonScanned);

    var randomX = Math.floor(Math.random() * vgWidth);
    var randomY = Math.floor(Math.random() * vgHeight);
    $('#vg-button-' + randomX + '-' + randomY, $vgContainer)
      .addClass(vgButtonActive);
  }
}

function vgHandleWin() {
  alert('Gewonnen!');
}

$(document).ready(function() {
  var $vgContainer = $(vgContainer);
  vgInitGame();
});

Noch keine Kommentare vorhanden.