Win an Ipad
  Developer   Tutorials   Breakout   Game scenes
Lesson 0

Game scenes

This chapter explains how to implement Scenes. In this game, we will be implementing two scenes - Menu scene and Game scene.

Menu Scene

Menu scene is Game2D scene that includes game buttons. Each button is set to call different game action. At the start of the game, buttons are displayed with animation effect where the size of the buttons is increased and decreased in a short time interval.

Buttons:

  • Play - starts New game from level 1
  • Continue - resumes the game
  • Quit - ends the game

include "lib://game2d/gameButton.ms";
include "lib://game2d/sceneTransitions/slideToBottom.ms";
include "app://scene/gameScene.ms"; class MenuScene : Scene
{
    function init()
    {
        super.init();         this._paint = new Paint();
        this.clickable = true;         // load unnecesarry sources
        this._bg = res.img.menuBg;
        this._logo = res.img.logo;         this._elements = [];         var gap = 2*res.img.playButton.height/3;
        var top = System.height - 2*res.img.playButton.height;         var slide = new SlideToBottom();         this._btnPlay = new GameButton({image: res.img.playButton, x:System.width/2, y: top, frameWidth: res.img.playButton.width, frameHeight: res.img.playButton.height / 2});
        this.add(this._btnPlay);
        app.pulse(this._btnPlay);
        this._btnPlay.onClick = function(s)
        {
            app.game = GameScene.create(0.0, 0.5, { calledClass: GameScene } );
            app.game.removeAllBricks();
            app.game.visible = true;
            app.game.level = 1;
            app.game.restart();             app.push(app.game, slide);
        }         this._btnContinue = new GameButton({image:res.img.continueButton, x:System.width/2, y : top + gap, frameWidth: res.img.continueButton.width, frameHeight: res.img.continueButton.height / 2 });
        this.add(this._btnContinue);
        app.pulse(this._btnContinue);
        this._btnContinue.visible = true;
        this._btnContinue.onClick = function(s)
        {
            if(app.game instanceof PhysicsScene){
                app.game.visible = true;
                app.push(app.game, slide);
            }
        }         this._btnQuit = new GameButton({image:res.img.quitButton, x: System.width/2 , y : top + 2*gap, frameWidth:res.img.quitButton.width, frameHeight: res.img.quitButton.height /2 });
        this.add(this._btnQuit);
        app.pulse(this._btnQuit);
        this._btnQuit.onClick = function(s)
        {
            app.quit();
        }     }     function exit()                             // fade in then pulse
    {
        this.clickable = false;
        var animator = new Animator({
            transition : Animator.Transition.easeIn, // start up slowly and then quickly speed up at the end of the animation
            duration : res.values.formAnimationTime,                     // length of animation in miliseconds
        });
        animator.addSubject(function(state) {      // state starts from 1.0 to 0.0            var self = this super;
           self.alpha = (state*255).toInteger();
           self._btnPlay.alpha = (state*255).toInteger();
           self._btnContinue.alpha = (state*255).toInteger();
           self._btnQuit.alpha = (state*255).toInteger();
        })
        animator.options.onComplete = function()
        {
            var self = this super;
            self.visible = false;
        }
        animator.reverse();
    }     function enter()                             // fade in then pulse
    {
        this.clickable = true;
        var animator = new Animator({
            transition: Animator.Transition.easeInOut, // start up slowly and then quickly speed up at the end of the animation
            duration : res.values.formAnimationTime,                     // length of animation in miliseconds
        });
        animator.addSubject(function(state) {      // state starts from 1.0 to 0.0            var self = this super;
           self.alpha = (state*255).toInteger();
           self._btnPlay.alpha = (state*255).toInteger();
           self._btnContinue.alpha = (state*255).toInteger();
           self._btnQuit.alpha = (state*255).toInteger();
        });
        animator.options.onComplete = function()
        {
            var self = this super;
        }
        animator.play();
    }     function _onPointerPressedHandler(sender, x, y)
    {
        if (this.clickable)
            if (Math.abs(x - System.width / 2) < this._btnPlay.width / 2) {
                if (Math.abs(y - this._btnPlay.y) < this._btnPlay.height / 2) {
                    this._btnPlay.clicked = true;
                    this.native.invalidate();
                } else if (Math.abs(y - this._btnContinue.y) < this._btnContinue.height / 2) {
                    this._btnContinue.clicked = true;
                    this.native.invalidate();
                } else if (Math.abs(y - this._btnQuit.y) < this._btnQuit.height / 2) {
                    this._btnQuit.clicked = true;
                    this.native.invalidate();
                }
            }     }     function _onPointerReleasedHandler(sender, x, y)
    {
        if(this.clickable)
            if (Math.abs(x - System.width / 2) < this._btnPlay.width / 2) {
                if (Math.abs(y - this._btnPlay.y) < this._btnPlay.height / 2) {
                    this._btnPlay.clicked = false;
                    this.native.invalidate();
                    this._btnPlay.onClick(this._btnPlay);
                } else if (Math.abs(y - this._btnContinue.y) < this._btnContinue.height / 2) {
                    this._btnContinue.clicked = false;
                    this.native.invalidate();
                    this._btnContinue.onClick(this._btnContinue);
                } else if (Math.abs(y - this._btnQuit.y) < this._btnQuit.height / 2) {
                    this._btnQuit.clicked = false;
                    this.native.invalidate();
                    this._btnQuit.onClick(this._btnQuit);
                }
            }
    }
    function draw(canvas)
    {
        canvas.drawBitmapRect(this._bg, 0, 0, this._bg.width, this._bg.height, 0, 0, System.width, System.height, this._paint);
        canvas.drawBitmap(this._logo, (System.width - this._logo.width) / 2, System.height / 48, this._paint);         super.draw(canvas);
    }
}

 

Game Scene


Game scene requires a physics  behavior, therefore is created as extension of PhysicsScene class. PhysicsScene combines the advantages of Game2D scene and Moscrif’s native b2Word library.
It creates instances of Disk, Ball, Brick and Barriers. Barriers are important to make game boundaries. They are set to border game area that is identical to the size of the device screen.

 

include "lib://game2d/scene.ms"
include "lib://game2d/gameButton.ms";
include "lib://game2d/sceneTransitions/slideToTop.ms";
include "app://sprite/brick.ms";
include "app://sprite/disk.ms";
include "app://sprite/ball.ms";
include "app://sprite/level.ms"; class GameScene : PhysicsScene
{
    function init()
    {
        super.init();         this._level = 1;
        this.paused = false;
        this._visible = true;         // game options
        this._gameStarted = false;         this._bg = res.img.gameBg;
        this._bg = this._bg.resize(System.width, System.height);
        this._level = level;         this._enableSounds = true;
        this._bodiesToDestory = [];
        this._bodiesToInactive = [];         this._btnMenu = new GameButton({image:res.img.menuButton, y: System.height / 20, x:System.width - res.img.menuButton.width});
        this._btnMenu.intersectsPoint = function (x, y)
        {
            if (x > this._x - res.img.menuButton.width && x < this._x + res.img.menuButton.width &&
                y < this._y + res.img.menuButton.height && y > this._y - res.img.menuButton.height)
                return true;
            else
                return false;
            /*var tx = this._x - res.img.menuButton.width * 2;
            var ty = this._y - res.img.menuButton.height * 2;
            return (
                x >= tx && x <= tx + this._frameWidth &&
                y >= ty && y <= ty + this._frameHeight
            );*/
            //return true;
        }
        this.add(this._btnMenu);
        app.pulse(this._btnMenu);
        this._btnMenu.onClick = function(s)
        {
           this super.visible = false;
           app.pop(new SlideToTop());
        }         this._gameOver = new Sprite({image:res.img.gameOver, x: System.width/2, y:System.height/2})
        this._gameOver.visible = false;
        this.add(this._gameOver);
        this._levelSprite = new Level({image:res.img.levels, x: System.width/2, y:System.height/2, frameWidth : res.img.levels.width, frameHeight : res.img.levels.height / 4, sequence : [0, 1, 2, 3]})
        this._levelSprite.visible = false;
        this.add(this._levelSprite);     }     function afterInit()
    {
        super.afterInit();         this._loadGameSounds();
        this._createMantinels();
        //this._createBricks();
        this._createBall();
        this._createPaddle();
    }     property paddle(v)
    {
        return this._paddle;
    }     property ball(v)
    {
        return this._ball;
    }     property level(v)
    {
        get return this._level;
        set {
            this._level = v;
        }
    }
    property visible(v)
    {
        get return this._visible;
        set {
            this._visible = v;
            if (v == false) {
                this.paused = true;
                this._paddle._timer.stop();
            } else {
                this.paused = false;
                this._paddle._timer.start(res.values.electricShockMin + rand(res.values.electricShockGap));
            }
        }
    }     function restart()
    {
        if (this._ball != null){
            this.destroyBody(this._ball);
        }         this.paused = true;
        this._gameStarted = false;
        this._createBall();
        //this._createBricks();
        this._paddle.setPosition(this._paddleX, this._paddleY);         var delay = new Timer(1, 1);
        delay.onTick = function(sender) { this super._createBricks();  this super.paused = false;}
        delay.start(600);
    }     function draw(canvas)
    {
        if(this.visible == false)
            return false;         canvas.drawBitmap(this._bg, 0, 0);
        super.draw(canvas);
    }
    function process()
    {
        if (!this.paused)
            this.step(1.0 / 60.0, 4, 8);         if (this._bodiesToDestory.length != 0)
            this._removeBricks();         if (this._bodiesToInactive.length != 0)
            this._inactiveBricks();         if (Brick.count == 0 && !this.paused){
            this._nextLevel();
            this.paused = true;
        }
    }     function pointerPressed(x, y)
    {
        super.pointerPressed(x,y);         if (this.paused)
            return;         // Initial impluse to ball
        if (this._gameStarted == false && Brick.count > 0) {
            var (bx, by) = this._paddle.getWorldCenter();
            var vx = bx - this._ball.width / 2;//rand(2*System.width) - System.width;
            var vy = 2*System.height + rand(System.height);
            this._ball.setLinearVelocity(vx, vy);
            this._gameStarted = true;
        }
    }
   
    function pointerDragged(x, y)
    {
        super.pointerDragged(x,y);         if (this.paused)
            return;         var (px, py) = this._paddle.getPosition();
        this._paddle.setPosition(x, py);
    }     function detach()
    {
        this._timer.dispose();
        this.native.detach();
    }     function removeAllBricks()
    {
        for (var i = 0; i < this._bricks.length; i++) {
            if (this._bricks[i]) {
                this._bricks[i].destroy();
                this.destroyBody(this._bricks[i]);
            }
        }
        this._bricks = new Array();
    }     function _createMantinels()
    {
        // Ground
        var (width, height) = (System.width, 1);
        this._ground = this.addPolygonBody(null, #static, 0.0, 0.0, 1.0, width, height); // density, friction, bounce
        this._ground.setPosition(System.width/2, System.height - (this._ground.height/2));
       
        // Left mantinel
        var (widthML, heightML) = (1, System.height);
        this._leftMantinel = this.addPolygonBody(null, #static, 0.0, 0.0, 1.0, widthML, heightML);
        this._leftMantinel.setPosition(this._leftMantinel.width/2, System.height/2);
       
        // Righ mantinel
        var (widthMR, heightMR) = (1, System.height);
        this._rightMantinel = this.addPolygonBody(null, #static, 0.0, 0.0, 1.0, widthMR, heightMR);
        this._rightMantinel.setPosition(System.width - (this._rightMantinel.width/2), System.height/2);
       
        // Top mantinel
        var (widthMT, heightMT) = (System.width, 1);
        this._topMantinel = this.addPolygonBody(null, #static, 0.0, 0.0, 1.0, widthMT, heightMT);
        this._topMantinel.setPosition(System.width/2, (this._topMantinel.height/2));
    }     function _createPaddle()
    {
        var (bw, bh) = (128.0, 32.0);
        this._paddleY =  System.height - (System.height / 5);
        this._paddleX =  System.width / 2;
        this._paddle = new Disk({scene:this, enableSound : this._enableSounds});
        this._paddle._createBody();
        this._paddle.setPosition(this._paddleX, this._paddleY);         return this._paddle;
    }     function _createBall()
    {
        this._ball = new Ball({scene:this});
        this._ball._createBody();
        this._ball.setPosition(System.width / 2, System.height / 2);         return this._ball;
    }     function _createBricks()
    {
        var (brickWidth, brickHeight) = (53, 17);
        var brickRows = 5;
        var brickColumns = (System.width / brickWidth) - 1;
        var bricksInRows = [2 * brickColumns / 3, brickColumns, brickColumns, brickColumns, brickColumns, brickColumns, 2 * brickColumns / 3, brickColumns / 3];         var startX = (System.width - (brickWidth*brickColumns))/2 + brickWidth/2;
        var startY = (System.height - (System.height/2) - (brickHeight*brickRows))/2;         this._bricks = [];
        for (var r = 0; r             for (var c = (brickColumns - bricksInRows[r]) / 2; c < brickColumns - (brickColumns - bricksInRows[r]) / 2; c++){
                var brickBody = new Brick({scene:this, state:rand(this.level), shape:b2PolygonShape.fromRect((53/2)/this.scale, (17/2)/this.scale)});
                brickBody._createBody();
                brickBody.setPosition(startX+(c*brickWidth), startY+(r*brickHeight));
                this._bricks.push(brickBody);
            }
        }
    }     function _loadGameSounds()
    {
        // Loading sounds
        if (this._enableSounds) {
            this._wavBall = new AudioPlayer();
            this._wavBall.openFile(res.sounds.ball);
   
            this._wavPaddle = new AudioPlayer();
            this._wavPaddle.openFile(res.sounds.strikeball);         }
    }     function _nextLevel()
    {
        if (this.level > 3)
            this._gameOver.visible = true;
        else {
            this._levelSprite.frame = this.level;
            this._levelSprite.visible = true;
            this.level = this.level+1;
    nbsp;nbsp; }quotnbsp;br /nbsp; nbsp;p ;lib://game2d/gameButton.msnbsp;nbsp;nbsp;nbsp;nbsp;nbsp;nbsp;nbsp;nbsp;nbsp;nbsp;nbsp; nbsp; this.destroyBody(this._bricks[i]);;        this.restart();
            this._ball.visible = false;
            var delay = new Timer(1, 1);
            delay.onTick = function(sender) {this super.paused = false;  this super._levelSprite.hide(); this super._ball.visible = true;}
            delay.start(1500);
        }     }     function _removeBricks()
    {
        // Remove touched bricks
        for(var body in this._bodiesToDestory) {
        var existing = this._bricks.filter(:x { return x == body; });
        if (existing.length != 0)
            this._bricks.removeByValue(body);
            this.destroyBody(body);
            // GAME OVER
             if (this._ball == body) {
                this._gameStarted = false;
                this._createBall();
                this._paddle.setPosition( this._paddleX, this._paddleY);
            }
         }         this._bodiesToDestory = [];
    }     function _inactiveBricks()
    {
        // Remove touched bricks
        for(var body in this._bodiesToInactive) {
            body.native.active = false;
        }
        this._bodiesToInactive = [];
    }     function _onBeginContactHandler(sender, contact)
    {
        if (this._ball == null) return;
        var current = contact;
        while (current != null) {
            var bodyA = current.getBodyA();
            var bodyB = current.getBodyB();
            if (bodyA == this._ground || bodyB == this._ground)
                this._bodiesToDestory.push(this._ball);
            current = current.getNext();
        }
    }     function _onEndContactHandler(sender, contact)
    {
        var self = this;
        if (self._ball == null) return;
        var current = contact;
        while (current != null) {
            var bodyA = current.getBodyA();
            var bodyB = current.getBodyB();
            var existing = self._bricks.filter(:x { return x == bodyA; }); // lamba function, the same as ".filter(function(x) { return x == bodyA; })"
            if (existing.length != 0) {
                bodyA.hit();
                if (self._enableSounds) self._wavPaddle.play();
                return;
            }
            existing = self._bricks.filter(:x { return x == bodyB; });
            if (existing.length != 0) {
                bodyB.hit();
                if (self._enableSounds) self._wavPaddle.play();
                return;
            }
            if (self._enableSounds && (bodyA == self._paddle || bodyB == self._paddle))
                self._wavBall.play();
            current = current.getNext();
        }
    }
}

 

 Game sprites   Game scenes   User interface 
Tutorial details and chapters
Technology:
Difficulty:
Completion:
Breakout chapters