SpaceJS

spaceJS_screenshotAufbauend auf dem Code vom letzen Post habe ich ein zweites JavaScript Spiel erstellt.
Dieses macht ein bisschen mehr her war aber immernoch relativ leicht umzusetzen. Ich habe noch ein bisschen das Problem einen guten Fluss von Bewegung und Zeitintervall hin zu bekommen deswegen kann es ab und zu ein bisschen ruckeln.
Diesmal wird die Maus verwendet um die Spielfigur (Raumschiff) zu steuern. Für die Umsetzung habe ich nur 2 zusätzliche JavaScript Objekte benötigt, eins für die Kugeln (Bullet.js) und eins für die Explosion (Explosion.js). Bei einigen Positionsangaben war ich zu faul sie dynamisch zu ermitteln, also immer wenn ihr eine „Magic Number“ im Code entdeckt ist es ein Abstand von oben/unten/links. 😉 Da der Code dem Ablauf und Aufbau des anderen Spiels gleicht, verzichte ich hier mal auf eine Erklärung, falls ihr aber noch ein paar Fragen habt immer raus damit.

Wie immer dürft ihr das Spiel gerne runterladen und verändern und erweitern. Ich würde mich auch freuen wenn ihr mir ein Kommentar schreibt wie viele Abschüsse ihr gelandet und welches Level ihr erreicht habt.

spielen / runterladen

Objektorientiertes JavaScript Spiel

Dieses kleine Spiel soll mit einfachen Mittel zeigen das man auch in JavaScript gutlesbaren, objektorientierten Code schreiben kann.

Als Unterstützung verwende ich die jQuery Bibilothekt um mir das arbeiten mit dem DOM und das Positionieren zu erleichtern.

Für das ganze Spiel werden nur 4 Objekte benötigt, wobei eines nur ein Containerobjekt für Tastencode-Zuweisungen ist. Ich habe für alle Objekte eine eigene Datei erstellt um eine gute Lesbarkeit zu erreichen. Immer wenn die Datei ein JavaScript-Objekt repräsentiert schreibe ich den Dateiname groß.

Unser Startpunkt ist die Datei Game.js. Als Erstes wird für jedes Element mit der Klasse „.game“ eine Game-Objekt erzeugt, dadurch werden auch die im Konstruktor definierten Variablen zugewiesen und Methoden ausgeführt. Ich initialisiere einen KeyListener um auf Tastatureingaben zu reagieren. In der Datei „util.js“ ist das Containerobjekt mit den Tastencode-Zuweisungen.

In der Methode „run()“ wird das Spiel gestartet. Mit der setInerval Funktion des Window-Objekts erreichen wir eine dauerhafte Ausführung unseres Spiels. In jedem Durchlauf können wir die Hindernisse bewegen, Kollision zwischen Spieler und Gegenständen feststellen und gegeben Falls darauf reagieren.

Im Konstuktor des Game-Objektes wird außerdem für jedes Hinderniss und den Spieler jeweils Objekte erstellt. Diese sind dem Game-Objekt bekannt um in der „run()“
Methode mit ihnen zu arbeiten.

$(document).ready(function () {

    $('.game').each(function () {
        new Game($(this));
    });

});

function Game($game) {
    this.$board = $game.find('.board');
    this.speed = 10;
    this.pause = false;
    this.gameOver = false;
    this.player = new Player(this.$board);
    this.$text = $game.find(".text");
    this.barriers = this.initBarrier();
    this.initKeyListener();
    this.run();
}

Game.prototype.run = function () {
    var game = this;
    var $player = game.player.$player;

    window.setInterval(function () {

        if (game.gameOver || game.pause) {
            return;
        }

        $.each(game.barriers, function (i) {

            this.move();

            if ($player.collidesWith(this.$barrier).length) {
                game.gameOver = true;
                game.$text.text("GAME OVER!");
            }

            if ($player.offset().top > (game.$board.height() + $player.height())) {
                game.pause = true;
                game.$text.text("WIN!");
            }

        });

    }, game.speed);
};

Game.prototype.initKeyListener = function () {
    var game = this;
    $(window).bind('keydown', function (e) {
        switch (e.keyCode) {

            // Reset
        case Keys.ESC:
            game.player.reset();
            game.pause = false;
            game.gameOver = false;
            game.$text.text("");
            break;

            // Pause
        case Keys.SPACE:
            game.pause = !game.pause;
            break;

            // Spieler zurücksetzen
        case Keys.POS1:
            if (!game.pause && !game.gameOver) game.player.reset();
            break;

        case Keys.LEFT:
            if (!game.pause && !game.gameOver) game.player.moveLeft();
            break;

        case Keys.UP:
            if (!game.pause && !game.gameOver) game.player.moveUp();
            break;

        case Keys.RIGHT:
            if (!game.pause && !game.gameOver) game.player.moveRight();
            break;

        case Keys.DOWN:
            if (!game.pause && !game.gameOver) game.player.moveDown();
            break;

        default:
            break;
        }
    });
};

Game.prototype.initBarrier = function () {
    var $board = this.$board;
    var barriers = [];
    $board.find('.barrier').each(function () {
        barriers.push(new Barrier($board, $(this)));
    });
    return barriers;
};

Das Player-Objekt wird lediglich vom KeyListener angesprochen. Je nach Tastatureingabe wird das enthaltene HTML bzw. jQuery Objekt bewegt.

function Player($board) {
    var p = this;
    p.speed = 5;
    p.$board = $board;
    p.$player = p.$board.find('.player:eq(0)');
}

Player.prototype.reset = function () {
    this.$player.css("margin", "0");
};

Player.prototype.moveUp = function () {
    var p = this;
    if (p.$player.offset().top > p.$board.offset().top) {
        p.$player.css("margin-top", "-=" + p.speed + "px");
    }
};

Player.prototype.moveDown = function () {
    var p = this;
    if (p.$player.offset().top < (p.$board.height() + p.$player.height())) {
        p.$player.css("margin-top", "+=" + p.speed + "px");
    }
};

Player.prototype.moveLeft = function () {
    var p = this;
    if (p.$player.offset().left > p.$board.offset().left) {
        p.$player.css("margin-left", "-=" + p.speed + "px");
    }
};

Player.prototype.moveRight = function () {
    var p = this;
    if (p.$player.offset().left - p.speed < p.$board.width()) {
        p.$player.css("margin-left", "+=" + p.speed + "px");
    }
};

Die „move()“ Methode des Barrier Objektes wird in jedem Durchlauf der „run()“ Methode aufgerufen, dadurch bewegt es sich automatisch und wird so „animiert“.

function Barrier($board, $barrier) {
    var b = this;
    b.$board = $board;
    b.$barrier = $barrier;
    b.moveLeft = false;
    b.sign = "+";
    b.range = 1;
}

Barrier.prototype.move = function () {
    var b = this;

    // Move left
    if (b.nachLinks || (b.$barrier.offset().left + b.$board.position().left >= b.$board.width())) {
        b.sign = "-";
        b.nachLinks = true;
    }

    // Move right
    if (b.$barrier.offset().left <= b.$board.offset().left) {
        b.sign = "+";
        b.nachLinks = false;
    }

    b.$barrier.css("margin-left", b.sign + "=" + b.range + "px");

};

Hier könnt ihr meine Sourcen Downloaden um sie euch genauer anzusehen und vielleicht selber noch ein bisschen damit rum zu spielen.

Download

[Snippet/Javascript] toggleClass ohne jQuery

Ich habe vor kurzem eine Javascript Funktion gebracht um schnell CSS KLassen zu Tauschen. In jQuery gibt es dafür die Funktion „toggleClass“. Leider stand mir in dieser Situation das Framework nicht zur Verfügung also habe ich mir die Funktion kurzerhand selber geschrieben.

Das Problem bei der Sache ist das man nicht einfach den Attribut Classname ersetzen darf da das HTML-Element vielleicht noch andere Klassen besitzt. In meiner Funktion können entweder zwei oder eine CSS-Klasse übergeben werden.
Bei zwei werden sie, falls vorhanden, getauscht, wenn noch keine von beiden vorhanden ist, wird die erste angefügt.
Bei dem Fall das nur eine Klasse übergeben wurde, wird zuerst geguckt ob die diese schon gesetzt ist. Falls ja wird sie mit nichts ersetzt andernfalls wird sie angefügt. Meine Funtion müsste sich also genauso wie das jQuery Pendant verhalten.

/**
 *
 * Wechselt die CSS-Klasse eines übergebenen Elements
 *
 * @param HTMLElement Element dessen Klasse gewechselt wird
 * @param cls1 Klasse die gesetzt oder weg gemacht wird
 * @param cls2 Klasse mit der getauscht wird. Falls dieser Parameter
 *             nicht gesetzt ist wird cls1 durch nichts ersetzt
 */

function toggleClass(elm, cls1, cls2) {
    if (!cls2) cls2 = "";
    elm.ersetzeKlasse = function (haystack, needle) {
        var re = new RegExp(haystack, "g");
        this.className = this.className.replace(haystack, needle);
    }

    if (elm.className.indexOf(cls1) > -1) elm.ersetzeKlasse(cls1, cls2);
    else if (elm.className.indexOf(cls2) > -1) elm.ersetzeKlasse(cls2, cls1);
    else elm.className += " " + cls1;
}