Do you like my tutorials?

Then consider supporting me on Ko-fi

Talking about Bubbles 2 game, Game development, Javascript and Users contributions.

Following the example published in HTML5 game creation prototype post, Esteban Gallardo from Free Creation Games shares with us a more compex prototype to create a game like Bubbles 2.

This is what you’ll get (not on IE):

And this is the commented javascript to manage the whole game:

// ************************************
// Class Ball
//		The user will control the ball movements 
// with the control keys.
// ************************************
function Ball()
{
	// CONSTRUCTOR
	this.x = 0;
	this.y = 0;
	this.vx = 0;
	this.vy = 0;
	this.radius = 10;
	this.color = "#000000";
	this.iterator =0;

	// ------------------------------------
	// Ball reset
	this.ResetBall = function() 
	{
		this.x = 0;
		this.y = 0;
		this.vx = 0;
		this.vy = 0;
		this.radius = 10;
		this.color = "#000000";
		this.iterator =0;
	}
	
	// ------------------------------------
	// Ball's logic
	this.Update = function() 
	{
		if (m_controlMode==CONTROL_MODE_MOUSE)
		{
			if (m_mouseDown)
			{
				this.vx=(m_mousePositionX-this.x)/5;			
				this.vy=(m_mousePositionY-this.y)/5;
			}
		}
		else
		{
			// PROCESS INPUT
			if (m_keyPressedLeft)	this.vx--;
			if (m_keyPressedRight)	this.vx++;
			if (m_keyPressedUp)	this.vy--;
			if (m_keyPressedDown)	this.vy++;
		}

		// BALL'S LOGIC
		this.x+=this.vx;
		this.y+=this.vy;
		this.vx*=friction;		
		this.vy*=friction;
		
		// CONTROL LIMITS
		if (this.xSCREEN_WIDTH)	this.x=SCREEN_WIDTH-this.radius;
		if (this.y+this.radius>SCREEN_HEIGHT)	this.y=SCREEN_HEIGHT-this.radius;
	}

	// ------------------------------------
	// Ball's Render
	this.Render = function() 
	{
		// FILL AREA
		/*
		m_context.beginPath();
		m_context.fillStyle=this.color;
		m_context.arc(this.x,this.y,this.radius,0,Math.PI*2,true);
		m_context.closePath();
		m_context.fill();
		*/

		// DRAW BUBBLE
		m_context.drawImage(m_bubbleImage, this.x-this.radius, this.y-this.radius, this.radius*2, this.radius*2);
	}
}

// ************************************
// Class Item
//	  Represent the items that the user's ball can collide to
// ************************************
function Item(type)
{
	// var TYPE_ITEM_BUBBLE	 	= 1;
	// var TYPE_ITEM_ENEMY 		= 2;

	// CONSTRUCTOR
	this.type = type;
	this.x = 0;
	this.y = 0;
	this.vx = 0;
	this.vy = 0;
	this.radius = 0;
	this.color = "#000000";
	this.iterator = 0;
	this.isAlive = false;

	// ------------------------------------
	// Item Initialitzation
	this.Init = function(radiusIni, radiusEnd, speedIni, speedEnd) 
	{
		this.isAlive = true;
		this.radius = radiusIni+Math.floor(Math.random()*(radiusEnd-radiusIni));
		this.x = Math.floor(Math.random()*SCREEN_WIDTH);
		this.y = 0;
		this.vx = 0;
		this.vy = speedIni+Math.floor(Math.random()*(speedEnd-speedIni));
		
		// alert("Item::Init: Initialitzation of a bubble::this.radius="+this.radius);
	}

	
	// ------------------------------------
	// Reset the object to its initial values
	this.ResetItem = function() 
	{	
		this.x = 0;
		this.y = 0;
		this.vx = 0;
		this.vy = 0;
		this.isAlive = false;
	}
	
	// ------------------------------------
	// Check if the bubble collide with the bubble of the player
	this.IsCollision = function() 
	{
		if (this.isAlive)
		{
			if (Math.abs(this.x-m_ball.x)+Math.abs(this.y-m_ball.y)x)&&(this.y+this.widthText+10>y));

			case 'center':
				return ((this.x-5-(this.widthText/2)x)&&(this.y+this.widthText+10>y));
				
			default:
				return ((this.x-5x)&&(this.y+this.widthText+10>y));
				break;
		}
	}

	// ------------------------------------
	// Render the text
	this.Render = function() 
	{
		// ++ RENDER ++
		m_context.font = this.font;
		m_context.textAlign = this.textAlign;
		m_context.textBaseline = this.textBaseline;

		this.sizeText = m_context.measureText(this.text);
		this.widthText = this.sizeText.width;
		
		// DRAW RECTANGLE IN THE BACKGROUND OF TEXT
		if (enableBackground)
		{
			m_context.beginPath();
			m_context.strokeStyle = '#000000';
			m_context.fillStyle = this.color;
			switch (align)
			{
				case 'left':
					m_context.rect(this.x-5, this.y-5, this.widthText+10, this.heightText+10);		
					break;

				case 'center':
					m_context.rect(this.x-5-(this.widthText/2), this.y-5, this.widthText+10, this.heightText+10);
					break;
					
				case 'right':
					break;
			}
			m_context.fill();
			m_context.stroke();
		}
		
		// DRAW TEXT
		if (enableBackground)
		{
			m_context.fillStyle = '#000000';
		}
		else
		{
			m_context.fillStyle = this.color;
		}
		m_context.fillText(this.text, this.x, this.y);
		m_context.closePath();
	}
}


// ++++++++++++++++++++++++++++++++++++++++++
// ++++++++++++++++++++++++++++++++++++++++++
// ++++++++++++++++++++++++++++++++++++++++++
// ++++++++++++++++++++++++++++++++++++++++++
// ++++++++++++++++++++++++++++++++++++++++++
// ++++++++++++++++++++++++++++++++++++++++++
// ++++++++++++++++++++++++++++++++++++++++++
// GLOBAL CODE
// ++++++++++++++++++++++++++++++++++++++++++
// ++++++++++++++++++++++++++++++++++++++++++
// ++++++++++++++++++++++++++++++++++++++++++
// ++++++++++++++++++++++++++++++++++++++++++
// ++++++++++++++++++++++++++++++++++++++++++
// ++++++++++++++++++++++++++++++++++++++++++

// ++ CONSTANTS ++
var SCREEN_WIDTH 	= 500;
var SCREEN_HEIGHT 	= 500;

var STATE_LOAD 	= 0;
var STATE_MENU	= 1;
var STATE_RUN 	= 2;
var STATE_END 	= 3;

var CONTROL_MODE_KEYBOARD 	= 0;
var CONTROL_MODE_MOUSE	 	= 1;

var TYPE_ITEM_BUBBLE	 	= 1;
var TYPE_ITEM_ENEMY 		= 2;

var TOTAL_NUMBER_BUBBLES 	= 20;
var TOTAL_NUMBER_ENEMIES 	= 20;

// ++ MEMBERS ++
var m_context;				// Document context

var m_state = STATE_LOAD;	// Main game state
var m_iterator = 0;			
var m_lastTime = 0;			// The time of the last frame
var m_difTime = 0;			// The time spent between two frames
var m_askToLose = false;
var m_score = 0;

var m_controlMode;			// Select between two control modes (KEYBOARD,MOUSE)
var m_mouseDown;			// Reports when the mouse is being pressed
var m_mousePositionX;		// X coordinate of the pressed mouse
var m_mousePositionY;		// Y coordinate of the pressed mouse

// BUTTONS
var m_playWidthMouse;
var m_playWidthKeyboard;
var m_pressHereToPlayAgain;

// IMAGES
var m_bgMenuImage;
var m_bgGameImage;
var m_bubbleImage;
var m_thornsImage;


// ACTORS
var m_ball; 

var m_bubbles;							// List of bubbles
var m_bubbleTimeGeneration=0;			// Time acumulator
var m_bubbleTimeGeneration_Limit=2000;	// Time needed between two bubble generations
var m_bubbleTimeGeneration_Factor=1.1;	// Decrease factor in the generation bubble time

var m_enemies;
var m_enemyTimeGeneration=0;			// Time acumulator
var m_enemyTimeGeneration_Limit=2000;	// Time needed between two enemies generations
var m_enemyTimeGeneration_Factor=0.8;	// Time decrease factor in the generation of an enemy 

var m_level=1;							// Game level
var m_timeLevelUp=0;					// Time acumulator
var m_timeLevelUp_Limit = 15000;		// Time to reach a level up
var m_timeLevelUp_Factor = 1.1;			// Grow time factor for level up
var m_timeoutLevelUpTitle = 0;			// Grow time factor for level up

// INPUT
var m_keyPressedLeft=false;
var m_keyPressedRight=false;
var m_keyPressedUp=false;
var m_keyPressedDown=false;
var friction=0.9;

// ------------------------------------
// OnKeyDown
document.onkeydown = function(event) 
{
     var key_pressed; 
     if(event == null)
	 {
          key_pressed = window.event.keyCode; 
     }
     else 
	 {
          key_pressed = event.keyCode; 
     }
     switch(key_pressed)
	 {
          case 37:
               m_keyPressedLeft=true;
               break; 
          case 38:
               m_keyPressedUp=true;
               break; 
          case 39:
               m_keyPressedRight=true;
               break;
          case 40:
               m_keyPressedDown=true;
               break; 
     } 
}

// ------------------------------------
// OnKeyUp
document.onkeyup = function(event) 
{
     var key_pressed; 
     if(event == null)
	 {
          key_pressed = window.event.keyCode; 
     }
     else 
	 {
          key_pressed = event.keyCode; 
     }
     switch(key_pressed)
	 {
          case 37:
               m_keyPressedLeft=false;
               break; 
          case 38:
               m_keyPressedUp=false;
               break; 
          case 39:
               m_keyPressedRight=false;
               break;
          case 40:
               m_keyPressedDown=false;
               break; 
     } 
}

// ------------------------------------
// Ask to create a new bubble
function AskNewBubble()
{
	var i=0;
	for (i=0;im_bubbleTimeGeneration_Limit)
	{
		m_bubbleTimeGeneration=0;
		if (m_timeoutLevelUpTitle<=0) AskNewBubble();
	}
	
	// UPDATE BUBBLE
	for (i=0;im_enemyTimeGeneration_Limit)
	{
		m_enemyTimeGeneration=0;		
		if (m_timeoutLevelUpTitle<=0) AskNewEnemy();
	}
	
	// UPDATE ENEMIES
	for (i=0;im_timeLevelUp_Limit)
	{
		m_level++;
		m_timeoutLevelUpTitle = 2000;
		m_timeLevelUp = 0;		
		m_timeLevelUp_Limit*=m_timeLevelUp_Factor;
		
		// CHANGE BUBBLE RATE
		m_bubbleTimeGeneration_Limit*=m_bubbleTimeGeneration_Factor;
	
		// CHANGE ENEMY RATE
		m_enemyTimeGeneration_Limit*=m_enemyTimeGeneration_Factor;
	}
	if (m_timeoutLevelUpTitle>0)
	{
		m_timeoutLevelUpTitle -= m_difTime;
	}

	// EVALUATE MAIN GAME STATE
	switch (m_state)
	{
		/////////////////////////
		case STATE_LOAD:
			switch (m_iterator)
			{
				case 1:
					// INIT GRAPHICS
					m_context=game_area.getContext('2d');

					// LOAD IMAGES
					m_bgGameImage = new Image();
					m_bgGameImage.src = 'images/bg_game.jpg';
					
					m_bubbleImage = new Image();
					m_bubbleImage.src = 'images/bubble.png';
					
					m_thornsImage = new Image();
					m_thornsImage.src = 'images/thorns.png';
					
					m_bgMenuImage = new Image();
					m_bgMenuImage.onload = function() {  m_iterator=100; };
					m_bgMenuImage.src = 'images/bg_menu.jpg';

					// INIT GAME ELEMENTS
					m_ball = new Ball();
					
					// BUBBLES INIT
					m_bubbles=new Array(TOTAL_NUMBER_BUBBLES);
					var i;
					for (i=0;i0)
			{
				var titleLevelUp = new MyText('LEVEL '+m_level, SCREEN_WIDTH/2, (SCREEN_HEIGHT/4), 24, '#ffffff', '50px sans-serif', 'center', 'top', false);
				titleLevelUp.Render();
			}
			
			// ++ END CONDITIONS ++
			if (m_askToLose)
			{
				m_askToLose=true;
				ChangeState(STATE_END);
			}
			break;
			
		/////////////////////////
		case STATE_END:
			switch (m_iterator)
			{
				case 1:
					// INIT TEXT
					var titleGameOver = new MyText('GAME OVER',SCREEN_WIDTH/2,SCREEN_HEIGHT/6,24,'#ff0000', '30px sans-serif', 'center', 'top', true);
					var titleFinalScore = new MyText('YOUR SCORE: '+m_score,SCREEN_WIDTH/2,SCREEN_HEIGHT/2,24,'#ffffff', '30px sans-serif', 'center', 'top', false);
					m_pressHereToPlayAgain = new MyText('Press here to go to menu screen',SCREEN_WIDTH/2,5*SCREEN_HEIGHT/6,24,'#00ff00', '20px sans-serif', 'center', 'top', true);
					
					// RENDER TEXT
					titleGameOver.Render();
					titleFinalScore.Render();
					m_pressHereToPlayAgain.Render();
					break;
			}
			
			// CHECK BUTTON SELECTION
			if (m_mouseDown)
			{
				if (m_pressHereToPlayAgain.IsInside(m_mousePositionX,m_mousePositionY))
				{
					m_mouseDown=false;
					ChangeState(STATE_MENU);
				}
			}
			break;
	}
}

// SET MAIN LOOP
setInterval(Update, 30);

Download the source code with all images.

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