Pages

Wednesday, August 24, 2011

Simon’s Google Doodle – HTML5 + JS jQuery Game


 
Simon's Google Doodle – HTML5 + JS jQuery Game

Dude, you've heard a lot about Google doodles haven't you? What about that Pacman one? It is just addictive. Company owners know how a funny game and unlimited search powers could be dangerous for productivity. And how it can be a marketing boost. The most amazing thing is that we can create our own addictive HTML game. Actually, this is why we are here today. With this tutorial you'll see how to use some of JavaScript and HTML capabilities to build simple yet funny games.

So, let's rock!

Demo first, please

You can download or try our online demo. Please, don't forget to come back here if you want to just go playing first :)

"Well, actually I don't know what a Doodle is.."

Google Doodles started when Larry and Sergey played with the corporate logo to indicate their attendance at the Burning Man festival in the Nevada desert. Since that day we've seen a lot of funny Doodles, and Google's homepage is used to remind us of all kind of important facts.

But they are more than just paintings, games or experiments. They are made with the best current practices (some of them, created specifically for a Doodle). And Google being as supportive as they are of open source technology, usually release the commented source code, so you can just take a look and try to understand what those amazing guys are doing.

Oh, and I don't know if you knew about it, but today is Simon's day according to 1stWebdesigner's official fake holiday calendar. Seems important, right? This is why we'll build a game for it.

So, what will we be doing?

Simon is one of my favorite games (just after Sudoku and Gammon). Can you think of an easier game to code? You know, four buttons, easy actions. I just can't think of a better starting point to learn HTML5 gaming.

Well, first of all you need to know what this Simon thing looks like. I just found a picture of it at Mercado Livre (kind of a Brazilian e-bay) and made my own version in Photoshop.

Since we are doing it via HTML5, the colors shouldn't be directly on image (otherwise you won't be able to change colors, make your own console…). So I've used Chris Coyier's technique for color animation. Basically we make our non-colored elements, and leave everything else transparent. Then our HTML5 will take care of the color itself (via CSS, background-color). Our final game background will be something like this:

Ok, then. Let's start the fun part.

Basic HTML + CSS

Since our target isn't basic HTML itself, I just grab a copy of HTML5 Boilerplate with a few edits to make it cleaner:

                     

Now we need our gaming HTML stuff. As we've done it ready for different colors we will need to have two layers:

  • Background color- sets the color and will be animated when you activate a button
  • Link – activates all actions for each colored button.
  • Alerts – since they are part of the game itself, we'll put them with all other game controls (level up, new game, game over)

Our HTML for this part will be something like this:

	
 
 
 
 
       

Ready? let's rock!

Oh Yeah!

Oh Noes... Almost there!

As you can see, the four "div.box" elements are the background color for each button (red, blue, green, red).

So when someone clicks #box-click-0 it will "activate" the button #box-0. We have a lot of id's because they result in better JS performance.

Our CSS for that will be:

#main { 	position: relative; 	left: 155px; 	width: 200px; 	height: 200px; } 	.box { 		display: block; 		float: left; 		width: 100px; 		height: 100px; 	} 	a.box { 		position: absolute; 		text-decoration: none; 		background-image: url("../img/bg_game.png"); 		background-repeat: no-repeat; 	} 	a.box:focus { 		outline: none; 	} 	.btnTL { background-position: top left; } 	.btnTR { background-position: top right; } 	.btnBL { background-position: bottom left; } 	.btnBR { background-position: bottom right; } 		 .btnTL, .btnTR { 			top: 0; 		 } 		 .btnBL, .btnBR { 			bottom: 0; 		 } 		 .btnTL, .btnBL { 			left: 0; 		 } 		 .btnTR, .btnBR { 			right: 0; 		 } 	#box-0 { 		background: #c00000; 	} 	#box-1 { 		background: #1506a3; 	} 	#box-2 { 		background: #046606; 	} 	#box-3 { 		background: #ea8f0c; 	} 

Here is our effect: all our links have the console background, bg_game.png (positioned according to what it should be, of course). Since they are on top of divs, what we will see is the bg_game.png AND the element's color ( #c00000, #1506a3…).

Ok, we won't be long on this. Maybe this is enough topic for a single article :)

Search form

Have you noticed the recent (ok, maybe not that recent) Google's switch to real time results? That's pretty cool, yeah, I know. But it makes a little bit harder to link directly to a search results page.

Since our demo has a search form and we want to make it work, let's put a little JS magic on it:

	 

Javascript:

$(function(){ 	$("#search").submit( function(event) { event.preventDefault(); googleRedirect(); } ); }); function googleRedirect() { 	var query = $("#query").attr('value'); 	var url = "http://www.google.com/#q=" + query + "&fp=1"; 	window.location = url; } 

This way it will work ;)

Cookies? No, no, local storage instead!

Well, Cookies are great for some things, but this time they aren't the very best option. Here we will make use of Local Storage. This is basically the same idea as Cookies, it allows you to store data related to your site, the difference is that it's faster and very useful in some situations.

As its name suggest it is local, so it won't be sent to the server (you save a few kbytes and processing). Cookies are limited to 4kB and 20 Cookies per domain. With local storage you can record up to 5MB of user data.

The only downside is that it works only on real browsers and IE8+. So, forget about IE7 this time (maybe later with some jQuery data fallback).

Another huge advantage is that it is surprisingly easy to set / remove;

localStorage["level"] = 15; // create it with 15 as value var level = localStorage["level"];  //now var level is 15 localStorage.removeItem( "level" ); //remove it! 

So, this is why we will use it this time.

A few options

Then we need to have a way to start the game, see which level we're playing, these sort of trivial things. For this, we'll just put a simple HTML box with Google's logo:

Well, what is important here is that our counters have id's wrapping them, so we can easily change their text.

Let the game begin

Random sequence

The very first thing to do when you start this game should be to generate a random sequence. Because this sequence will be the "right path" to be followed by players. We could generate it on-the-fly as our gamer reaches new levels, but it will be easier to do this way.

So we will create a simple function that just generate a random sequence with 50 digits (levels) and only with numbers from 0 to 3 (our colors!):

function randomSequence() { 	var chars = "0123"; // four colors! 	var string_length = 50; //pretty hard, isn't it? 	var sequence = ""; 	for (var i=0; i < string_length; i++) { //we'll have from sequence[0] to sequence[49] 		var rnum = Math.floor(Math.random() * chars.length); 		sequence += chars.substring(rnum,rnum+1); 	} 	return sequence; } 

Binding clicks and hovers

Now we need to prepare our all a.box elements to recognize clicks and to animate properly our div.box when you hover any of them.

This time we will need jQuery .hover() and .click() functions:

$(function(){ //caching our elements for better performance box0 = $("#box-0"); box1 = $("#box-1"); box2 = $("#box-2"); box3 = $("#box-3"); clickbox0 = $("#box-click-0"); clickbox1 = $("#box-click-1"); clickbox2 = $("#box-click-2"); clickbox3 = $("#box-click-3"); clickbox0.click( 	function(event){ 		event.preventDefault(); 		clicked(0); 	} ); clickbox1.click( 	function(event){ 		event.preventDefault(); 		clicked(1); 	} ); clickbox2.click( 	function(event){ 		event.preventDefault(); 		clicked(2); 	} ); clickbox3.click( 	function(event){ 		event.preventDefault(); 		clicked(3); 	} ); var minHover = 0.7; var maxHover = 1; var timeHover = 500; clickbox0.hover( 	function(){ 		box0.stop().animate({opacity: minHover}, timeHover); 	}, function() { 		box0.stop().animate({opacity: maxHover}, timeHover); 	} ); clickbox1.hover( 	function(){ 		box1.stop().animate({opacity: minHover}, timeHover); 	}, function() { 		box1.stop().animate({opacity: maxHover}, timeHover); 	} ); clickbox2.hover( 	function(){ 		box2.stop().animate({opacity: minHover}, timeHover); 	}, function() { 		box2.stop().animate({opacity: maxHover}, timeHover); 	} ); clickbox3.hover( 	function(){ 		box3.stop().animate({opacity: minHover}, timeHover); 	}, function() { 		box3.stop().animate({opacity: maxHover}, timeHover); 	} ); }); 

It is pretty important caching all our elements, since it improves a lot our performance.

Magical JS variable variables and easy error detection

At this point you have coded all the hover and click elements, but when you click them nothing happens, of course. Let's fix it:

// yeah, someone is playing it! function clicked( color ) { 	// refresh my memory, how long have you gone so far? 	var level = localStorage["level"]; // this is our current level 	var clicks = localStorage["clicks"]; //this is our current item INSIDE one level 	var speed = localStorage["speed"]; //to make it more fun, when you get right it goes faster 	// blink it! 	window['box' + color].stop().animate({ opacity: 0.3 }, (speed * 0.5) ).animate({ "opacity": "1" }, (speed * 0.3) ); //variable variables in js via window[varname] :) 	// well, this time you've got it right? 	if ( localStorage["sequence"][clicks] == color) { 		//oh, yeah! 		localStorage["clicks"]++; 		//have you finished this level? 		if ( level == clicks) { 			nextLevel(); 		} 	} else { 		//oh, noes! 		endGame(); 	} } 

Every variable in javascript can be accessed via its own name, like box1 OR as an index inside window array, just like window['box1']. With this we can easily select which is the right element to blink (when you click any button, it should blink, right?).

Ok, you are right! Let's go to the next level

When you get it right, the game should get you to the next level and the events should accelerate a little bit.

Then when you get it right you should also know which is the right sequence at your current level + 1, so let's do it:

function nextLevel() { 	//Oh, Yes 	showBox("#ohYes", 1000); 	// you deserve it, let's move on 	localStorage["level"]++; 	levelCount.text(localStorage["level"]);  	localStorage["clicks"] = 0; 	//let's show you how it'll be 	current = 0; //since it's used just for animation, let's leave it as global var 	if ( localStorage["speed"] > 300 && (localStorage["level"] / 5) == 0 ) { //our maximum speed, and we'll only acelerate it each 5 levels 		localStorage["speed"] = localStorage["speed"] * 0.8; 	} 	setTimeout( function() { rightPath(); } , localStorage["speed"] ); } 

Function showBox just shows our div and hides it after one second.

As you can see this piece of code isn't complete, we will need the function rightPath() to see what comes next. Let's go:

function rightPath() { 	if ( current <= localStorage["level"] ) { 		var sel = localStorage["sequence"][current]; 		window['box' + sel].stop().animate({ "opacity": "0.5" }, (localStorage["speed"] * 0.5) ).animate({ "opacity": "1" }, (localStorage["speed"] * 0.3) ); 		setTimeout( 			function() { 				rightPath(); 			}, 			(localStorage["speed"] * (1.5) ) 		); 		current++; 	} 	for (var i=0; i <= localStorage["level"] ; i++) { 		var time = 1000 * (i + 1); 	} 	i = 0; } 

This is kind of crazy, isn't it? This is a simple recursive function. It will call itself, blink the right button for one position and then go to the next until we reach user's current level.

What if you get it wrong?

Maybe instead of click the right button you can just click the wrong button. Then we need to punish you and ban you from the entire world wide web. Let's see our function for that:

function endGame() { 	//let's see if you have a high score, and save it 	if ( localStorage["hs"] == undefined || localStorage["level"] > localStorage["hs"] ) { 		localStorage["hs"] = localStorage["level"]; 		$("#hs").text(localStorage["hs"]); 	}  	localStorage.removeItem( "level" ); 	// Oh, noes! 	showBox("#ohNoes", 2000); } 

Well, here we just check if you've done something better than this play and then remove your current level so you can restart a new game and try a little harder :)

By the way, did I mention how will you start a game? No? Sorry, my bad.

Sorry, NOW the game will begin

What do we have to do in order to start a match is to reset almost anything we have here and then show what is the right path.

// just like starting over! function newGame() { 	current = 0; 	levelCount.text(1); 	//let's call our sequence and memorize it 	localStorage["sequence"] = randomSequence(); 	//wow, we haven't got anything right until now :) 	localStorage["level"] = 0; 	//clicks binding 	localStorage["clicks"] = 0; 	//let's make things slow for now 	localStorage["speed"] = 1500; 	//let's show our welcome warning 	showBox("#letsRock", 2000); 	//and let's show the first color 	rightPath(); } 

What is your best score?

Hope you enjoy this quick HTML5 game tutorial and that you find some easter eggs, you know, since it's a JS game we can't hide a lot of things on it :)

But the most important thing here is: what is your best score?




Sent from my iPhone

No comments:

Post a Comment