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();
}
}
}
|