// the game itself var game; // global options var gameOptions = { // score panel height / game height scorePanelHeight: 0.08, // launch panel height / game height launchPanelHeight: 0.18, // ball size / game width ballSize: 0.04, // ball speed, in pixels/second ballSpeed: 1000, // block sports per line blocksPerLine: 7, // maximum amount of blocks per line maxBlocksPerLine: 4 } // when the window loads... window.onload = function() { // game creation game = new Phaser.Game(640, 960, Phaser.CANVAS); // add "PlayGame" state and execute it game.state.add("PlayGame", playGame, true); } // "PlayGame" state var playGame = function(){} playGame.prototype = { // when the state preloads preload: function(){ // load graphic assets game.load.image("ball", "ball.png"); game.load.image("panel", "panel.png"); game.load.image("trajectory", "trajectory.png"); game.load.image("block", "block.png"); }, // once the state has been created create: function(){ // scale and background settings game.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL; game.scale.pageAlignHorizontally = true; game.scale.pageAlignVertically = true; game.stage.backgroundColor = 0x202020; // start ARCADE physics system game.physics.startSystem(Phaser.Physics.ARCADE); // place score panel this.scorePanel = game.add.sprite(0, 0, "panel"); this.scorePanel.width = game.width; this.scorePanel.height = Math.round(game.height * gameOptions.scorePanelHeight); // enable ARCADE physics on score panel game.physics.enable(this.scorePanel, Phaser.Physics.ARCADE); // score panel will not move this.scorePanel.body.immovable = true; // place launch panel this.launchPanel = game.add.sprite(0, game.height, "panel"); this.launchPanel.width = game.width; this.launchPanel.height = Math.round(game.height * gameOptions.launchPanelHeight); this.launchPanel.anchor.set(0, 1); // enable ARCADE physics on launch panel game.physics.enable(this.launchPanel, Phaser.Physics.ARCADE); // launch panel will not move this.launchPanel.body.immovable = true; // place the ball var ballSize = game.width * gameOptions.ballSize; this.ball = game.add.sprite(game.width / 2, game.height - this.launchPanel.height - ballSize / 2, "ball"); this.ball.width = ballSize; this.ball.height = ballSize; this.ball.anchor.set(0.5); // enable ARCADE physics on the ball game.physics.enable(this.ball, Phaser.Physics.ARCADE); // the ball will collide on bounds this.ball.body.collideWorldBounds=true; this.ball.body.bounce.set(1); // place the trajectory this.trajectory = game.add.sprite(this.ball.x, this.ball.y, "trajectory"); this.trajectory.anchor.set(0.5, 1); this.trajectory.visible = false; // wait for player input game.input.onDown.add(this.aimBall, this); game.input.onUp.add(this.shootBall, this); game.input.addMoveCallback(this.adjustBall, this); // the player is not aiming this.aiming = false; // the player is not shooting this.shooting = false; // add the group where all blocks will be placed this.blockGroup = game.add.group(); // place a new line of boxes this.placeLine(); }, placeLine: function(){ // determine block size var blockSize = game.width / gameOptions.blocksPerLine; // array of positions already picked up by a block var placedBlocks = []; // repeat "maxBlocksPerLine" times for(var i = 0; i < gameOptions.maxBlocksPerLine; i++){ // choose a random position var blockPosition = game.rnd.between(0, gameOptions.blocksPerLine - 1); // if the random position is free... if(placedBlocks.indexOf(blockPosition) == -1){ // insert the position into the array of already picked positions placedBlocks.push(blockPosition); // add the block var block = game.add.sprite(blockPosition * blockSize + blockSize / 2, blockSize / 2 + game.height * gameOptions.scorePanelHeight, "block"); block.width = blockSize; block.height = blockSize; block.anchor.set(0.5); // enable ARCADE physics on block game.physics.enable(block, Phaser.Physics.ARCADE); // block will not move block.body.immovable = true; // custom property. Block starts at row 1 block.row = 1; // add block to block group this.blockGroup.add(block); } } }, aimBall: function(e){ // if the player is not shooting... if(!this.shooting){ // the player is aiming this.aiming = true; } }, adjustBall: function(e){ // if the player is aiming... if(this.aiming){ // check distance between initial and current input position var distX = e.position.x - e.positionDown.x; var distY = e.position.y - e.positionDown.y; // a vertical distance of at least 10 pixels is required if(distY > 10){ // place the trajectory over the ball this.trajectory.position.set(this.ball.x, this.ball.y); // show trajectory this.trajectory.visible = true; // calculate direction this.direction = Phaser.Math.angleBetween(e.position.x, e.position.y, e.positionDown.x, e.positionDown.y); // adjust trajectory angle according to direction, in degrees this.trajectory.angle = Phaser.Math.radToDeg(this.direction) + 90; } else{ // hide trajectory this.trajectory.visible = false; } } }, shootBall: function(){ // if the trajectory is visible... if(this.trajectory.visible){ // get angle of fire in radians var angleOfFire = Phaser.Math.degToRad(this.trajectory.angle - 90); // set ball velocity this.ball.body.velocity.set(gameOptions.ballSpeed * Math.cos(angleOfFire), gameOptions.ballSpeed * Math.sin(angleOfFire)); // the player is shooting! this.shooting = true; } // do not aim anymore this.aiming = false; // do not show the trajectory anymore this.trajectory.visible = false; }, update: function(){ // if the player is shooting... if(this.shooting){ // check for collision between the ball and the score panel. Just check and make it bounce game.physics.arcade.collide(this.ball, this.scorePanel); // check for collision between the ball and blockGroup children game.physics.arcade.collide(this.ball, this.blockGroup, function(ball, block){ // destroy the block block.destroy(); }, null, this); // check for collision between the ball and the launch panel game.physics.arcade.collide(this.ball, this.launchPanel, function(){ // stop the ball this.ball.body.velocity.set(0); // use a tween to scroll down blockGroup group var scrollTween = game.add.tween(this.blockGroup).to({ y: this.blockGroup.y + game.width / gameOptions.blocksPerLine }, 200, Phaser.Easing.Linear.None, true); // once the tween is completed... scrollTween.onComplete.add(function(){ // the player is not shooting this.shooting = false; // put the group in its original position this.blockGroup.y = 0; // loop through all blockGroup children this.blockGroup.forEach(function(i){ // adjust vertical position i.y += game.width / gameOptions.blocksPerLine; // increment row property i.row++; // if a block is too close to the ball... if(i.row == gameOptions.blocksPerLine){ // restart the gameOptions game.state.start("PlayGame"); } }, this); // add a new line this.placeLine(); }, this) }, null, this); } } }Will I be able to make the entire game fit in less than 200 lines, comments excluded? The answer in next step, meanwhile download the source code.
Build a HTML5 game like “Ballz” using Phaser and ARCADE physics – adding and destroying blocks
Read all posts about "Ballz" game
Here we go with the second step of Ballz HTML5 prototype.
In first step we created a bouncing ball with a fake predictive trajectory.
Now it’s time to add the blocks you will destroy by launching the ball at them.
Blocks are ARCADE physics bodies, all children of the same group, since it’s easy to check for collision between groups, and it’s also easy to move all group children with a single tween, when it’s time to scroll down the blocks.
Have a look at the game:
Tap/click and drag to the bottom to aim the ball, release to launch it, if you have a mobile device, you can play it directly from this link.
At the moment blocks do not have a value since there isn’t the “multiball” mode yet, but it will be added in next step. But there’s already a “game over” condition when blocks are too close to the ball.
This is the commented source code: