senäh

17senäh und so…

HTML/CSS/JS
25. Jul 2011
Kommentare: 0

Eine einfache Preroll für Spiele mit jQuery

Kategorien: HTML/CSS/JS | 25. Jul 2011 | Kommentare: 0

Ich gehöre zu den wenigen Entwicklern, die wohl erst an die Preroll und das Spielmenü denken und danach erst an das eigentliche Spiel. Ich kann irgendwie nicht anders als chronologisch arbeiten 😉 Darum habe ich in einem kleinen Test eine erste Preroll in JavaScript programmiert. Eine Preroll ist quasi eine Slideshow, die vor dem eigentlich Spiel gezeigt wird. Sie zeigt im einfachsten Fall das Logo der Entwickler, häufig aber auch verwendete Engines und Techniken oder Werbung. Für die Fading-Effekte habe ich übrigens zum ersten Mal das sagenumwobene jQuery benutzt. Schickes Framework.

Für die Preroll verwende ich eine überarbeitete Version des AssetManagers. Hier habe ich einen kleinen Bug gefixt, weil der onError-Callback nicht aufgerufen wurde und habe ihn gleichzeitig dahingehend erweitert, dass er bei einem Fehler ein Array mit allen nicht-geladenen Bilder zurückgibt.

Die Verwendung ist ganz simpel. Ihr bindet die Preroll.js ein und ruft im <body>-Tag „oninit=new Preroll().init()'“ mit folgenden Parametern auf:

  • Pfad zu einer Ladegrafik (z.B. von hier)
  • Array mit Pfaden zu allem Bildern der Preroll
  • Anzeigedauer für Preroll-Bilder in ms
  • Dauer des Ein-/Ausblendens in ms
  • was nach der Preroll angezeigt werden soll (z.B. ein <div>-Element mit dem Hauptmenü des Spiels)

Die Bilder werden dabei zentriert angezeigt und gleichmäßig ein- und ausgeblendet. So soll es ungefähr aussehen:

Preroll mit jQuery

Preroll mit jQuery

Und hier der Code – die Pfade im Array solltet ihr euren eigenen Bildern entsprechend anpassen:

index.html

<!DOCTYPE html>
<html lang="de">

<!--
You can use this code as you wish,
but please recommend our site http://www.senaeh.de.
@author Philipp Zins/Donald Pipowitch
-->

<head>
    <title>Preroll Beispiel</title>

    <meta charset="UTF-8"/>
    <meta name="description" content="Ein einfacher Test eines Prerolls."/>
    <meta name="keywords" content="HTML5, Preroll"/>
    <meta name="author" content="Philipp Zins/Donald Pipowitch"/>

    <link rel="stylesheet" type="text/css" href="css/style.css"/>

    <script type="text/javascript" src="js/Preroll.js"></script>
</head>

<body onload='new Preroll().init(
    "img/loader/loader.gif", ["img/preroll/17.png", "img/preroll/ditto.png", "img/preroll/senaeh.png"],
    2000, 1000, document.getElementById("menu"))'>
    <div id="menu">
        Das ist das Hauptmenü! Hier könnt ihr das Spiel starten,
        Optionen einstellen oder die Highscores ansehen.
    </div>
</body>

</html>

style.css

/**
 * You can use this code as you wish for non-commercial projects,
 * but please recommend our site http://www.senaeh.de.
 * @author Philipp Zins/Donald Pipowitch
 */

body
{
    background-color: #999999;
}

Preroll.js

/**
 * You can use this code as you wish for non-commercial projects,
 * but please recommend our site http://www.senaeh.de.
 * @author Philipp Zins/Donald Pipowitch
 */

/**
 * "Imports"
 */

document.write('<script type="text/javascript" src="js/AssetManager.js"></script>');
document.write('<script type="text/javascript" src="js/jquery-1.6.2.min.js"></script>');

/**
 * Preroll
 *
 * Ver.: 0.0.1:
 * Loads images for prerolls and a loader graphic.
 * Shows preroll images centered and with fading effects.
 * Shows a specific target after preroll.
 */
function Preroll()
{
    this.assetManager = new AssetManager();
    this.loaderImgPath;
    this.loaderImg;
    this.prerollImgPaths;
    this.prerollImg;
    this.displayDuration;
    this.fadeDuration;
    this.target;
    this.width;
    this.height;
}

/**
 * Show a prerol of different images.
 * @param loaderImgPath The path to a typical loader graphic.
 * @param prerollImgPaths The paths to all images which should appear in the preroll
 * @param displayDuration How long should the image be seen (in ms).
 * @param fadeDuration How fast should the image fade in/out (in ms).
 * @param target Which DOM element should be displayed after preroll.
 */
Preroll.prototype.init = function(loaderImgPath, prerollImgPaths, displayDuration, fadeDuration, target)
{
    // params
    this.loaderImgPath = loaderImgPath;
    this.prerollImgPaths = prerollImgPaths;
    this.displayDuration = displayDuration;
    this.fadeDuration = fadeDuration;
    this.target = target;
    // width, height
    this.width = window.innerWidth;
    this.height = window.innerHeight;
    // hide target
    $(this.target).css("opacity", "0.0");
    // load and show loaderImg first
    this.assetManager.addAssetPath(loaderImgPath);
    var that = this;
    this.assetManager.loadAssets(function()
    {
        that.showLoadingScreen();
    }, function()
    {
        that.onAssetLoadingError();
    });
}

/**
 * Show the loading image and load preroll images.
 */
Preroll.prototype.showLoadingScreen = function()
{
    // loader image
    this.loaderImg = this.assetManager.getAsset(this.loaderImgPath);
    //var size = Math.max(this.width, this.height) * 0.1;
    //this.loaderImg.width = loaderImg.height = size;
    this.loaderImg.style.position = "absolute";
    this.loaderImg.style.left = (this.width / 2 - this.loaderImg.width / 2) + "px";
    this.loaderImg.style.top = (this.height / 2 - this.loaderImg.height / 2) + "px";
    document.body.appendChild(this.loaderImg);
    // preroll images
    this.assetManager.addAssetPathArray(this.prerollImgPaths);
    var that = this;
    this.assetManager.loadAssets(function()
    {
        that.showPrerollImages();
    }, function()
    {
        that.onAssetLoadingError();
    });
}

/**
 * Show the preroll images. Show target after all images.
 */
Preroll.prototype.showPrerollImages = function()
{
    // remove loaderImg or old prerollImg
    if (this.loaderImg !== null)
    {
        document.body.removeChild(this.loaderImg);
        this.loaderImg = null;
    }
    else
        document.body.removeChild(this.prerollImg);
    // show every prerollImg
    if (this.prerollImgPaths.length > 0)
    {
        // get and place prerollImg
        this.prerollImg = this.assetManager.getAsset(this.prerollImgPaths[0]);
        this.prerollImgPaths.splice(0, 1);
        this.prerollImg.style.position = "absolute";
        this.prerollImg.style.left = (this.width / 2 - this.prerollImg.width / 2) + "px";
        this.prerollImg.style.top = (this.height / 2 - this.prerollImg.height / 2) + "px";
        document.body.appendChild(this.prerollImg);
        // fade in and out with prerollImg with jQuery!
        $(this.prerollImg).css("opacity", "0.0");
        var that = this;
        $(this.prerollImg).animate({opacity: 1.0}, this.fadeDuration).
                delay(this.displayDuration).animate({opacity: 0.0}, this.fadeDuration, function()
                {
                    that.showPrerollImages()
                });
    }
    // show target after all prerollImages
    else
        $(this.target).animate({opacity: 1.0}, this.fadeDuration);
}

/**
 * Will be called if at least one asset couldn't be loaded.
 * @param loadAssetErrors Array of paths to all images which couldn't be loaded.
 */
Preroll.prototype.onAssetLoadingError = function(loadAssetErrors)
{
    alert("Couldn't load the following assets: " + loadAssetErrors);
}

AssetManager.js (neue Version)

/**
 * You can use this code as you wish for non-commercial projects,
 * but please recommend our site http://www.senaeh.de.
 * @author Philipp Zins/Donald Pipowitch
 */

/**
 * Thanks to http://www.html5rocks.com/en/tutorials/games/assetmanager/,
 * which helped a lot.
 */

/**
 * AssetManager
 *
 * Ver.: 0.0.1:
 * Loads images. Can return loaded images.
 *
 * Ver.: 0.0.2:
 * Little bug fix, because onLoadAssetsErrorCallback() wasn't called.
 * onLoadAssetsErrorCallback() passes an array of all images which couldn't be loaded.
 */
function AssetManager()
{
    this.successCount = 0;
    this.errorCount = 0;
    this.downloadQueue = [];
    this.downloadQueueLength = 0;
    this.cache = [];
    this.isLoading = false;
    this.loadAssetErrorList = [];

    this.onLoadAssetsCompleteCallback = null;
    this.onLoadAssetsErrorCallback = null;

    this.onLoadAssetSuccess = onLoadAssetSuccess;
    this.onLoadAssetError = onLoadAssetError;
    this.onLoadAssetsComplete = onLoadAssetsComplete;
    this.isLoadAssetsComplete = isLoadAssetsComplete;
    this.addAssetPath = addAssetPath;
    this.addAssetPathArray = addAssetPathArray;
    this.loadAssets = loadAssets;
    this.getAsset = getAsset;
}

/**
 * Will be called if an assets was successfully loaded.
 */
function onLoadAssetSuccess()
{
    this.successCount++;
    if (this.isLoadAssetsComplete())
        this.onLoadAssetsComplete();
}

/**
 * Will be called if an assets wasn't successfully loaded.
 * @param path The path of the image which couldn't be loaded.
 */
function onLoadAssetError(path)
{
    this.errorCount++;
    this.loadAssetErrorList.push(path);
    if (this.isLoadAssetsComplete())
        this.onLoadAssetsComplete();
}

/**
 * Check if all assets were loaded.
 * @return Returns true, if all assets were loaded.
 */
function isLoadAssetsComplete()
{
    return this.downloadQueueLength == this.successCount + this.errorCount;
}

/**
 * Will be called if all assets were loaded.
 */
function onLoadAssetsComplete()
{
    var foundErrors = this.errorCount > 0;
    // reset parameter
    this.isLoading = false;
    this.successCount = 0;
    this.errorCount = 0;
    var tmpLoadAssetErrorList = this.loadAssetErrorList.copy();
    this.loadAssetErrorList.splice(0, this.loadAssetErrorList.length);
    this.downloadQueue.splice(0, this.downloadQueueLength);
    this.downloadQueue.lengh = 0;
    this.downloadQueueLength = 0;
    // callback
    if (this.onLoadAssetsCompleteCallback !== null && !foundErrors)
        this.onLoadAssetsCompleteCallback();
    else if (this.onLoadAssetsErrorCallback !== null && foundErrors)
        this.onLoadAssetsErrorCallback(tmpLoadAssetErrorList);
}

/**
 * Add a path to an asset to the download queue.
 * @param path The path to an asset.
 */
function addAssetPath(path)
{
    this.downloadQueue.push(path);

}

/**
 * Add an array of asset-paths to the download queue.
 * @param pathArray The path to an asset.
 */
function addAssetPathArray(pathArray)
{
    this.downloadQueue = this.downloadQueue.concat(pathArray);

}

/**
 * Downloads all assets in the download queue.
 * @param onComplete The function which will be called, if all assets were downloaded.
 */
function loadAssets(onComplete, onError)
{
    if (this.isLoading)
    {
        alert("You're allready downloading assets! Please wait until "
                + "the last download is finished!")
        return;
    }
    this.onLoadAssetsCompleteCallback = onComplete;
    this.onLoadAssetsErrorCallback = onError;
    this.isLoading = true;
    this.downloadQueueLength = this.downloadQueue.length;
    if (this.downloadQueueLength == 0)
        this.onLoadAssetsComplete();
    var that = this;
    for (var i = 0; i < this.downloadQueueLength; i++)
    {
        var path = this.downloadQueue[i];
        if (!this.cache[path])    // load image only if it wasn't loaded before
        {
            var img = new Image();
            img.addEventListener("load", function()
            {
                that.onLoadAssetSuccess()
            }, false);
            img.addEventListener("error", function()
            {
                that.onLoadAssetError(path)
            }, false);
            img.src = path;
            this.cache[path] = img;
        }
    }
}

/**
 * Get a specific asset from path.
 * @param path The path to an asset.
 * @return Returns the asset, if it was downloaded before.
 */
function getAsset(path)
{
    return this.cache[path];
}

/**
 * Add copy() function to Array.
 */
Array.prototype.copy = function ()
{
    return new Array().concat(this);
};

jquery-1.6.2.min.js

Gibt’s hier.

That’s it. Ist noch recht einfach, aber sieht ganz schick aus. War eine nette kleine Übung, um mal bei jQuery reinzuschnuppern, nachdem Enno schon so oft davon geschwärmt hatte 🙂

Zum Schluss wie immer das Live-Beispiel.

Autor: Pipo

...kommt ursprünglich aus der Designerecke, ist aber im Laufe seines Studiums in die Webentwicklung gestolpert. Kann sich seit dem nicht so richtig entscheiden wo er hingehört und wagt deswegen vielleicht die Flucht nach vorne in ein komplett neues Gebiet. Vielleicht Management? Niemand weiß es. Auch er nicht.