Do you like my tutorials?

Then consider supporting me on Ko-fi

Talking about Facebook, Game development, HTML5, Javascript and Phaser.

If you are following this series about Facebook Instant Games API, you already learned how to integrate Facebook Instant Games API in your Phaser game and how to display player name and profile picture. Now it’s time to see how to save some game information, such as the best score. In an normal HTML5 game, you use local storage to save your best score across browser sessions. Obviously this way to save information is obsolete when talking about Facebook. You must have your information saved no matter the device you use to access to your Facebook account. Obviously, there’s an API call for it. You will read and save game information pretty much the same way you did using local storage, you’re only using Facebook methods. Have a look at the source code, with the new lines highlighted:
var game;
var savedData = {
    bestScore: 0
};

var gameOptions = {
    gameHeight: 1334,
    backgroundColor: 0x08131a,
    visibleTargets: 9,
    ballDistance: 120,
    rotationSpeed: 4,
    angleRange: [25, 155],
    localStorageName: "riskystepsgame"
}

var facebookStuff = {
    name: "",
    picture: ""
}


FBInstant.initializeAsync().then(function(){
    FBInstant.setLoadingProgress(100);
    FBInstant.startGameAsync().then(function(){
        facebookStuff.name = FBInstant.player.getName();
        facebookStuff.picture = FBInstant.player.getPhoto();
        var windowWidth = window.innerWidth;
        var windowHeight = window.innerHeight;
        if(windowWidth > windowHeight){
            windowWidth = windowHeight / 1.8;
        }
        var gameWidth = windowWidth * gameOptions.gameHeight / windowHeight;
        game = new Phaser.Game(gameWidth, gameOptions.gameHeight, Phaser.CANVAS);
        game.state.add("Boot", boot);
        game.state.add("Preload", preload);
        game.state.add("TitleScreen", titleScreen);
        game.state.add("PlayGame", playGame);
        game.state.start("Boot");
    })
})

var boot = function(){};
boot.prototype = {
  	preload: function(){
        game.load.image("loading", "assets/sprites/loading.png");
	},
  	create: function(){
        game.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL;
        game.scale.pageAlignHorizontally = true;
        game.scale.pageAlignVertically = true;
        game.stage.disableVisibilityChange = true;
        game.stage.backgroundColor = gameOptions.backgroundColor;
        game.state.start("Preload");
	}
}

var preload = function(){};
preload.prototype = {
    preload: function(){
        var loadingBar = this.add.sprite(game.width / 2, game.height / 2, "loading");
        loadingBar.anchor.setTo(0.5);
        game.load.setPreloadSprite(loadingBar);
        game.load.image("title", "assets/sprites/title.png");
        game.load.image("playbutton", "assets/sprites/playbutton.png");
        game.load.image("ball", "assets/sprites/ball.png");
        game.load.image("target", "assets/sprites/target.png");
        game.load.image("arm", "assets/sprites/arm.png");
        game.load.image("homebutton", "assets/sprites/homebutton.png");
        game.load.image("tap", "assets/sprites/tap.png");
        game.load.image("fog", "assets/sprites/fog.png");
        game.load.image("logo", "assets/sprites/logo.png");
        game.load.image("profilepicture", facebookStuff.picture);
        game.load.bitmapFont("font", "assets/fonts/font.png", "assets/fonts/font.fnt");
        game.load.bitmapFont("whitefont", "assets/fonts/whitefont.png", "assets/fonts/whitefont.fnt");
        game.load.audio("failsound", ["assets/sounds/fail.mp3", "assets/sounds/fail.ogg"]);
        game.load.audio("hitsound", ["assets/sounds/hit.mp3", "assets/sounds/hit.mp3"]);
        game.load.audio("hit2sound", ["assets/sounds/hit2.mp3", "assets/sounds/hit2.ogg"]);
    },
    create: function(){
        game.state.start("TitleScreen");
    }
}

var titleScreen = function(){};

titleScreen.prototype = {
    create: function(){
        FBInstant.player.getDataAsync(["score"]).then(function(data){
            if(typeof data["score"] !== "undefined"){
                savedData.bestScore = data["score"];
            }
            game.add.bitmapText(game.width / 2, 440, "whitefont", savedData.bestScore.toString(), 60).anchor.set(0.5, 1);
        });
        var title = game.add.image(game.width / 2, 50, "title");
        title.anchor.set(0.5, 0);
        var playButton = game.add.button(game.width / 2, game.height / 2, "playbutton", this.startGame);
        playButton.anchor.set(0.5);
        var tween = game.add.tween(playButton.scale).to({
            x: 0.8,
            y: 0.8
        }, 500, Phaser.Easing.Linear.None, true, 0, -1);
        tween.yoyo(true);
        var logoButton = game.add.button(game.width / 2, game.height - 40, "logo", function(){

        }, this);
        logoButton.anchor.set(0.5, 1);
        game.add.bitmapText(game.width / 2, 320, "whitefont", "WELCOME " + facebookStuff.name.toUpperCase(), 48).anchor.set(0.5, 1);
        game.add.bitmapText(game.width / 2, 380, "font", "BEST SCORE", 48).anchor.set(0.5, 1);
        var profileImage = game.add.image(game.width / 2, playButton.y + 250, "profilepicture");
        profileImage.anchor.set(0.5);
        profileImage.width = 200;
        profileImage.height = 200;

    },
    startGame: function(){
        game.state.start("PlayGame");
    }
}

var playGame = function(){};

playGame.prototype = {
    create: function(){
        this.failSound = game.add.audio("failsound");
        this.hitSound = [game.add.audio("hitsound"), game.add.audio("hit2sound")];
        this.runUpdate = true;
        this.score = 0;
        this.destroy = false;
        this.rotationSpeed = gameOptions.rotationSpeed;
        this.targetArray = [];
        this.steps = 0;
        this.rotatingDirection = game.rnd.between(0, 1);
        this.gameGroup = game.add.group();
        this.targetGroup = game.add.group();
        this.ballGroup = game.add.group();
        this.gameGroup.add(this.targetGroup);
        this.gameGroup.add(this.ballGroup);
        this.arm = game.add.sprite(game.width / 2, 900, "arm");
        this.arm.anchor.set(0, 0.5);
        this.arm.angle = 90;
        this.ballGroup.add(this.arm);
        this.balls = [
            game.add.sprite(game.width / 2, 900, "ball"),
            game.add.sprite(game.width / 2, 900 + gameOptions.ballDistance, "ball")
        ]
        this.balls[0].anchor.set(0.5);
        this.balls[1].anchor.set(0.5);
        this.ballGroup.add(this.balls[0]);
        this.ballGroup.add(this.balls[1]);
        this.rotationAngle = 0;
        this.rotatingBall = 1;
        var target = game.add.sprite(0, 0, "target");
        target.anchor.set(0.5);
        target.x = this.balls[0].x;
        target.y = this.balls[0].y;
        this.targetGroup.add(target);
        this.targetArray.push(target);
        game.input.onDown.add(this.changeBall, this);
        for(var i = 0; i < gameOptions.visibleTargets; i++){
            this.addTarget();
        }
        this.homeButton = game.add.button(game.width / 2, game.height, "homebutton", function(){
            game.state.start("TitleScreen");
        });
        this.homeButton.anchor.set(0.5, 1);
        this.tap = game.add.sprite(game.width / 8, this.balls[0].y - 50, "tap");
        this.tap.anchor.set(0.5);
        var fog = game.add.image(0, 0, "fog");
        fog.width = game.width;
        this.scoreText = game.add.bitmapText(20, 20, "whitefont", "0", 60);
    },
    update: function(){
        if(this.runUpdate){
            var distanceFromTarget = this.balls[this.rotatingBall].position.distance(this.targetArray[1].position);
            if(distanceFromTarget > 90 && this.destroy && this.steps > gameOptions.visibleTargets){
                var ohnoText = game.add.bitmapText(0, 100, "whitefont", "TOO LATE!!", 48);
                ohnoText.anchor.set(0.5);
                this.targetArray[0].addChild(ohnoText);
                this.gameOver();
            }
            if(distanceFromTarget < 25 && !this.destroy){
                this.destroy = true;
            }
            if(this.steps == gameOptions.visibleTargets){
                if(distanceFromTarget < 20){
                    this.tap.alpha = 1;
                }
                else{
                    this.tap.alpha = 0.2;
                }
            }
            this.rotationAngle = (this.rotationAngle + this.rotationSpeed * (this.rotatingDirection * 2 - 1)) % 360;
            this.arm.angle = this.rotationAngle + 90;
            this.balls[this.rotatingBall].x = this.balls[1 - this.rotatingBall].x - gameOptions.ballDistance * Math.sin(Phaser.Math.degToRad(this.rotationAngle));
            this.balls[this.rotatingBall].y = this.balls[1 - this.rotatingBall].y + gameOptions.ballDistance * Math.cos(Phaser.Math.degToRad(this.rotationAngle));
            var distanceX = this.balls[1 - this.rotatingBall].worldPosition.x - game.width / 2;
            var distanceY = this.balls[1 - this.rotatingBall].worldPosition.y - 900;
            this.gameGroup.x = Phaser.Math.linearInterpolation([this.gameGroup.x, this.gameGroup.x - distanceX], 0.05);
            this.gameGroup.y = Phaser.Math.linearInterpolation([this.gameGroup.y, this.gameGroup.y - distanceY], 0.05);
        }
    },
    changeBall:function(e){
        if(this.tap != null){
            this.tap.destroy();
            this.tap = null;
        }
        var homeBounds = this.homeButton.getBounds();
        if(homeBounds.contains(e.position.x, e.position.y)){
            this.runUpdate = false;
            return;
        }
        this.hitSound[this.rotatingBall].play();
        this.destroy = false;
        var distanceFromTarget = this.balls[this.rotatingBall].position.distance(this.targetArray[1].position);
        if(distanceFromTarget < 20){
            var points = Math.floor((20 - distanceFromTarget) / 2);
            this.score += points;
            this.scoreText.text = this.score.toString();
            this.rotatingDirection = game.rnd.between(0, 1);
            var fallingTarget = this.targetArray.shift();
            var tween = game.add.tween(fallingTarget).to({
                alpha: 0,
                width: 0,
                height: 0
            }, 2500, Phaser.Easing.Cubic.Out, true);
            tween.onComplete.add(function(target){
                target.destroy();
            }, this)
            this.arm.position = this.balls[this.rotatingBall].position;
            this.rotatingBall = 1 - this.rotatingBall;
            this.rotationAngle = this.balls[1 - this.rotatingBall].position.angle(this.balls[this.rotatingBall].position, true) - 90;
            this.arm.angle = this.rotationAngle + 90;
            this.addTarget();
        }
        else{
            var ohnoText = game.add.bitmapText(0, 100, "whitefont", "MISSED!!", 48);
            ohnoText.anchor.set(0.5);
            this.targetArray[0].addChild(ohnoText);
            this.gameOver();
        }
    },
    addTarget: function(){
        this.steps++;
        startX = this.targetArray[this.targetArray.length - 1].x;
        startY = this.targetArray[this.targetArray.length - 1].y;
        var target = game.add.sprite(0, 0, "target");
        var randomAngle = game.rnd.between(gameOptions.angleRange[0] + 90, gameOptions.angleRange[1] + 90);
        target.anchor.set(0.5);
        target.x = startX + gameOptions.ballDistance * Math.sin(Phaser.Math.degToRad(randomAngle));
        target.y = startY + gameOptions.ballDistance * Math.cos(Phaser.Math.degToRad(randomAngle));
        var stepText = game.add.bitmapText(0, 0, "whitefont", this.steps.toString(), 32);
        stepText.anchor.set(0.5);
        target.addChild(stepText);
        this.targetGroup.add(target);
        this.targetArray.push(target);
    },
    gameOver: function(){
        this.failSound.play();
        var finalSteps = this.steps - gameOptions.visibleTargets;
        this.scoreText.text = this.score.toString() + " * " + finalSteps + " = " + (this.score * finalSteps).toString();
        this.score *= finalSteps;
        FBInstant.player.setDataAsync({
            "score": Math.max(savedData.bestScore, this.score)
        });
        this.runUpdate = false;
        game.input.onDown.remove(this.changeBall, this);
        this.rotationSpeed = 0;
        this.arm.destroy();
        var gameOverTween = game.add.tween(this.balls[1 - this.rotatingBall]).to({
            alpha: 0
        }, 1500, Phaser.Easing.Cubic.Out, true);
        gameOverTween.onComplete.add(function(){
            game.state.start("PlayGame");
        },this);
    }
}
Here you will see how to read and write game information, let’s have a look at the new lines: Lines 2-4: savedData object is initialized with the default value, setting the best score to zero. Line 88: getDataAsync method rRetrieve data from the designated cloud storage of the current player. Line 89: if a score is found… Line 90: … we update bestScore value. Line 92: at this time we add the bitmap text displaying the best score, which could be the zero default value or the value we found with getDataAsync method Line 262: setDataAsync sets data to be saved to the designated cloud storage of the current player. You can save a maximum of 1MB of data for each player. There’s plenty of stuff to save with 1MB!! And this is the result: my best scored saved in Facebook cloud storage and accessible to each device which I use to play the game logged with my account: Our game is using more and more Facebook API methods but it’s not over yet, as we will be using more and more features to make our game more social and hopefully viral, meanwhile download the source code.

Never miss an update! Subscribe, and I will bother you by email only when a new game or full source code comes out.