This phenomenon is largely thanks to HTML5 games which have filled the gap to keep up with these eager gamers. In addition, thanks to the advancement of technology and new frameworks, HTML5 game development has reached extensive heights, allowing for multiplayer experiences, 3D graphics, and more! It is not unwise to say that gaming is now just as viable via a browser as it is on many other devices!
Assuming this sounds like an exciting prospect to you as a game developer, in this guide we’re going to delve into the topic of HTML5 game development and give you the run-down of how you can make your own games! Sit back, relax, and prepare to enhance your game development skills in entirely new ways!
Let’s start from total zero. What is HTML5? that’s a tricky question. There is an official definition of HTML5, which simply stands for the latest revision of HTML (the markup language used all over the world to build websites), and the more hyped definition (what most people understand when HTML5 is mentioned) which is all the “new” features of the web technologies that have come out in the last few years (JavaScript API’s like the Canvas or WebAudio, semantic HTML tags, etc).
For our purpose, we’ll use bits of the two. HTML5 is HTML in its latest version, which includes a whole bunch of cool features that make web technologies an open standard with endless possibilities combining HTML, CSS and JavaScript.
Having HTML along with all these superpowers that go beyond making a simple website allows us to make, among other things, games. These are HTML5 games.
The very basic building blocks of a HTML5 game are those of the web:
Similarly to what happens with HTML5, when people talk about CSS3 they usually refer to the new things that come with CSS’s latest specifications, but in an analog manner, CSS3 is simply the latest CSS. Ignoring for a second the semantics of these definitions and thinking of the hyped versions of these terms, we also may need, in order to make HTML5 games:
With the above you can make awesome games that will run on modern web browsers on mobile and desktop, but some games might require more features, so there are more building blocks that you can add.
For instance, you may want to make 3D games. If that is the case there is also WebGL, which is a JavaScript API to render 2D and 3D graphics on the browser, using the GPU for greater performance.
If you want your games to saved data remotely you’ll need a server-side for your game. You can develop your own backend using any server-side language, you’ll need well a server in this case.
Or you can use a third-party Backend-as-a-Service provider such as Firebase or Parse. Some have free versions you can use and they’ll start charging you once you surpass certain limits. Some of these providers are particularly focused on games, some are mostly conceived for mobile apps but can be used for games too.
The easiest way to distribute a HTML5 is to simply put it out there! By being built as a website, you can just embed it in on a page and publish it. Just like that.
If you want to distribute it through proprietary platforms you have to go through a process called wrapping. Basically, you create a native app for the platform you wanna distribute it to (iOS, Android, etc) and put your game inside so that this app acts like a web browser and “runs” your game.
For desktop platforms such as Windows, Mac or Linux there is a tool called NWjs that allows you to pack your HTML5 games for these platforms.
We can only cover the basics here, but we encourage you to read our more in-depth advice about publishing games.
Most games share some concepts, that of sprites (graphic elements that represent enemies, players, elements in your game), scenes or stages, animations, sound, loading graphic assets, etc. Since most game developers want to focus on their actual game and not in creating this whole abstraction layer, it is recommended you use a HTML5 game frameworks.
HTML5 game frameworks and libraries that contain building components you can use to create your own games. These libraries are Open Source projects created and maintained by people who want to contribute to the HTML5 gamedev environment. In many cases they created the frameworks for their own games, and after realizing that other people would want to not only use it but also contribute to it they released them as Open Source code, so everybody wins.
Picking what game engine to use is an important decision, so make sure you do proper research before making your choice. No matter what engine you pick, you will have to get familiar with its code and inner working if you want to use properly, so they shouldn’t be treated as black boxes.
What can help you make your choice:
Sometimes looking at real games gives you more insight than just words. This project compares different engines by making the exact same Breakout game in all of them.
Some popular free frameworks are:
Video courses are a great way to learn new technologies. The main difference between a video course and just watching YouTube videos is that there is more structure. Good courses have a clear goal and build on to it step by step. Below a list of courses and tutorials by Zenva that can give you the tools you need to create HTML5 games.
General Game Development
Phaser
WebGL, 3D, and XR
HTML5 Skills
At the GameDev Academy, as you know already we have a bunch of HTML5 game development tutorials, mostly on Phaser, LimeJs, Quintus and BabylonJS. There are other great places to find good quality HTML5 gamedev tuts:
Are you a teacher looking to help students get into frameworks like Phaser or teach them core and relevant web development skills? Try out Zenva Schools – an online platform offering coding-based courses for use in the classroom. The platform comes with video lessons, text summaries, quizzes, classroom management features, reporting, and more to help support learning HTML5!
You can find plenty of active communities on the Internet, some focus on gamedev in general and some others just in HTML5 gamedev.
Web:
Facebook:
Other Communities:
I just know Lostcast, a podcast created by the guys from Lost Decade Games (whom we’ve interviewed in the past). In the podcast episodes they talk about they HTML5 games and game development in general.
]]>
You should be familiar with HTML, CSS, JavaScript and basic object oriented concepts. I will be using limeJS, which is the one most used game frameworks. Please check the documentation on their
web site and install it.
You can download the game files here. The root folder contains the non-compiled files, which you can open and read. You’ll need limeJS installed if you want to run them. There is also compiled version, in the “compiled” folder, which you can run standalone in your web browser (“compiled” in the limeJS lingo really means “minified” so that all the dependencies are in a single JavaScript file). Now we will browse through the files and I will explain how this all works.
Here is the list of files that we will be using:
and a short explanation:
maze.Level_1
. All you have to do is populate the array this.rooms
, in the constructor, with maze.Room
objects.maze.Level
. Based on the list of rooms, in the array this.rooms
, it will create a maze and set up navigation events/controls.That’s it.
maze.js
maze.start = function() { // create game director to manage screens var director = new lime.Director(document.getElementById('maze'), maze.size.width, maze.size.height); // create maze first level var level = new maze.Level_1(); // in constructor you set up the maze rooms, see maze.Level_1 class level.create(); // we start the engine to build the maze director.replaceScene(level); // show it! director.setDisplayFPS(false); }
Nothing special here, just create and show the maze.
maze.Level_1.js
/** * @constructor * @extends {maze.Level} */ maze.Level_1 = function() { goog.base(this); this.rooms = [ new maze.Room([maze.direction.UP, maze.direction.RIGHT], [0, 0]), new maze.Room([maze.direction.RIGHT, maze.direction.LEFT], [0, 1]), new maze.Room([maze.direction.LEFT], [0, 2]), new maze.Room([maze.direction.UP, maze.direction.DOWN], [1, 0]), new maze.Room([maze.direction.RIGHT, maze.direction.DOWN], [2, 0]), new maze.Room([maze.direction.UP, maze.direction.LEFT], [2, 1]), new maze.Room([maze.direction.RIGHT, maze.direction.DOWN], [3, 1]), new maze.Room([maze.direction.UP, maze.direction.LEFT], [3, 2]), new maze.Room([maze.direction.UP, maze.direction.DOWN], [4, 2]), new maze.Room([maze.direction.RIGHT], [5, 0]), new maze.Room([maze.direction.RIGHT, maze.direction.LEFT], [5, 1]), new maze.Room([maze.direction.DOWN, maze.direction.LEFT], [5, 2]) ]; }
This is the maze setup. The maze is a set of “rooms”, where we see only one at the time (see demo). Every maze.Room
object has to know where is the exit (to up, right, down or left) to other rooms (first parameter array) and its position in the maze (second parameter array).
For direction, we use enum maze.direction
/** * Constants for direction * @enum {string} */ maze.direction = { UP: 'up', RIGHT: 'right', DOWN: 'down', LEFT: 'left' }
In order for this to work, you have to use directions always in the same order: up, right, down or left. So [maze.direction.LEFT, maze.direction.DOWN]
is not going to work, but [maze.direction.DOWN, maze.direction.LEFT]
is OK. I like to use enums like this because then you make less errors while typing.
Here is the maze map, the letter Z:
To create this structure in the code, we use an array with 2 dimensions for the room position – the first index is for row, and the second for column. It’s like we are building the table with rows and columns, from bottom to top:
[0, 0]
means 1st row, 1st column,[0, 1]
means 1st row, 2nd column,[0, 2]
means 1st row, 3rd column,[1, 0]
means 2nd row, 1st column,Putting it all together:
new maze.Room([maze.direction.UP, maze.direction.RIGHT], [0, 0])
means that this room has 2 directions, to UP and RIGHT, and its position is 1st row, 1st column. This is the maze starting room, the bottom left room. By default, the starting room is always the first item in this.rooms
array.
Class maze.Level
will, based on it’s subclass maze.Level_1
, create the maze. maze.Level_1
is just a setup or configuration, but maze.Level
is the engine. Let see some of the methods:
/** * Create maze. */ maze.Level.prototype.create = function() { /** @type {maze.Room} */ var room; for (var i = 0; i < this.rooms.length; i++) { room = this.rooms[i]; // set room image and move room outside of the screen room.setFill(room.getImage()).setPosition(-1000, 0).setSize(480, 320).setAnchorPoint(0, 0); if (i == 0) { // set first room as starting point room.setPosition(0, 0); maze.Level.currentRoom = room; } // set room neighbours, so we can know which room to show based on direction room.setNeighbours(this.getNeighbours(room, i)); this.appendChild(room); } // add click/touch navigation this.setTouchNav(); }
This is the place where maze is created. It loops through all rooms and add them to the screen.
/** * A flag which tells whether room animation is in progress. * We need this to NOT interrupt moving to another room. * @type {bool} */ maze.Level.moving = false; /** * Move to another room based on direction. * @param {maze.direction} direction */ maze.Level.prototype.move = function(direction) { // check if room has this direction and if animation is in progress if (goog.array.contains(maze.Level.currentRoom.directions, direction) === false || maze.Level.moving === true) { return; } var nextRoom = maze.Level.currentRoom.neighbours[direction]; /** @type {goog.math.Coordinate} */ var coordinate; // based on direction, set next room starting position and coordinates where should animation end switch(direction) { case maze.direction.LEFT: nextRoom.setPosition(-maze.size.width, 0); coordinate = new goog.math.Coordinate(maze.size.width, 0); break; case maze.direction.RIGHT: nextRoom.setPosition(maze.size.width, 0); coordinate = new goog.math.Coordinate(-maze.size.width, 0); break; case maze.direction.UP: nextRoom.setPosition(0, -maze.size.height); coordinate = new goog.math.Coordinate(0, maze.size.height); break; case maze.direction.DOWN: nextRoom.setPosition(0, maze.size.height); coordinate = new goog.math.Coordinate(0, -maze.size.height); break; } // move both, next and current, rooms // we can see current room is going away and next room is coming var moveAction = new lime.animation.MoveBy(coordinate).setDuration(0.5); moveAction.addTarget(maze.Level.currentRoom); moveAction.addTarget(nextRoom); moveAction.play(); // we don't want to interrupt current animation maze.Level.moving = true; // set event to know when animation is finished // so we can set next room to current and maze.Level.moving that animation is finished goog.events.listen( moveAction, lime.animation.Event.STOP, function(){ maze.Level.moving = false; maze.Level.currentRoom = nextRoom; }); }
This method is responsible for the moving animation. Based on room neighbours, it will set next room and move it along with the current one, so we get a felling that we are moving through the maze.
Now you can try to make your own maze. Create maze.Level_2
and populate this.rooms
array with maze.Room
. Ok, that’s fine, now we know how to create maze, but this is not a game yet. We are missing the game logic. Maybe in a future, we can add some bad guys, monsters or any other obstacle to beat and play.
If you have any questions, you can contact me on Twitter: @bmilakovic
Other LimeJS tutorials at the GameDev Academy:
]]>Before we begin the interview, I’d like to invite you all to check out and support his current crowdfunding campaign at Indigogo.
Can you tell us about your project Dash? what is it and who is it aimed for?
DASH is a visual programming tool and a game creator. It is HTML5 based software and web service which can run on any device. DASH is a complete set of tools and courses that can help people learn how to write code in a simple and creative way, age no bar. This tool is idle for students, teachers, hobbyists and designers.
We are also going to make a number of video courses for teaching game programming fundamentals using Dash.
What kinds of games can you make with Dash?
Dash is right now well suited for creating simple 2D arcade titles. DASH supports following features:
Using these features player can create any kind of 2D game except multi-player game which DASH doesn’t support yet.
We have created various demos at DASH website to showcase its capabilities.
Can the HTML5 code of the game be downloaded as well?
We are planning to add this feature to accompany the Video Courses. One or more courses will be focused on understanding this code and how some of it is applicable to other game engines in general.
What tools are you using to build Dash? (languages, programs)
Dash is built entirely in JavaScript and uses various features of HTML5. This allows it to run on any device with an HTML5 enabled browser.
The tool itself is made in the JavaScript game engine LimeJS. Games made with Dash run in the Gamvas game engine. Both LimeJS and Gamvas are open source engines.
Our development and testing environment consists of Notepad++ and the excellent Developer tools that are bundled with most modern browsers.
How is it different from other drag and drop tools to make games?
1. Multi-platform support. DASH is the first software which supports so many platforms. User can create games on any platform for any platform.
2. Close to real world programming language. Unlike others software we tried to keep the flow of DASH as close to object oriented programming language.
3. 2D Physics. DASH is using box2d physics engine for creating physics based games
4. Scene and Animation Editor: DASH inbuilt scene and animation editor makes it different and powerful than other software’s.
When will it be available to try?
Currently we are in BETA stage and access is available on invitation only.
But others can get BETA access by simply supporting our crowdfunding initiative
You can visit http://www.dashplay.net to try it out. You can try it as a guest but your project will not be saved on our server.
How can people get in touch with you about this great initiative?
Visit us at – http://www.dashplay.net Even if you haven’t received a BETA invite you can still use the forum.
Please use the Indiegogo website to provide feedback on our crowdfunding initiative.
]]>This course will guide you through the creation of games that run on iPhone, iPad, Android and Desktop using the open source LimeJS HTML5 game development framework.
The course is a 100% video based tutorial so that you can see in real time how games are created from scratch. Several game demos of different game genres are included in this course.
You’ve always wanted to make your own games. Don’t keep on postponing it. Start TODAY with HTML5 game development for iOS, Android, BlackBerry 10 and Windows 8.
Some of the things you’ll be learning here are:
More great stuff:
https://academy.zenva.com
]]>Once upon a time, kids used to play with real, “flesh and bone” animals like dogs, cats and hamsters. Today’s kids play with elaborated algorithms that simulate living beings and carry out actions such as eating, playing and even popping. We call these misguided inventions “virtual pets”.
I’m exaggerating. Kids still play with real animals and yes, they play with virtual pets too just like many grown ups do. And there is nothing wrong with that. Virtual pets are one more of the many types of video games you can find nowadays, and this tutorial is going to teach you how to make a simple virtual pet using which will run on any HTML5 supporting device.
One of the great things about HTML5 is that all you need to make games are a text editor, a web browser and of course a program to create the artwork. That part is not even necessary anymore with sites like OpenGameArt.org that provide awesome game artwork you can use for free.
We’ll be building this simple virtual pet game using a JS framework called LimeJS. You can download this framework and install it following the official guide at www.limejs.com.
The official guide provides you with a good starting point when it comes to the installation and usage of the framework. The community is also a great place to find information.
If you are looking for a guided and comprehensive alternative which teaches you all the basics of this great framework starting from scratch (well, assuming you have basic JS knowledge), I’ve prepared an online course titled HTML5 Mobile Game Development for Beginners, which you are welcome to check out (using the provided link will give you 30% off the course price).
After installing LimeJS as per the official guide or my online course, our next step will be to create the project. We are gonna open our terminal, cd to the limejs folder any type:
bin/lime.py create virtual_pet
This will create a folder called “virtual_pet” inside our limejs folder with some startup files. Let’s open virtual_pet.html and add some styling tags to the head section. This will give us a black background on the page and a little hack to fix an issue when changing scenes (we won’t use this in this tutorial, but it’s something I always add).
.lime-scene {display:block !important;} body {background-color:black;}
Open virtual_pet.js and replace it’s contents by the following:
//set main namespace goog.provide('virtual_pet'); //get requirements goog.require('lime.Director'); goog.require('lime.Scene'); goog.require('lime.Layer'); // entrypoint virtual_pet.start = function(){ //object to store game-level properties var gameObj = { width: 320, height: 480, renderer: lime.Renderer.CANVAS }; var director = new lime.Director(document.body,gameObj.width,gameObj.height); var gameScene = new lime.Scene().setRenderer(gameObj.renderer) var gameLayer = new lime.Layer().setAnchorPoint(0,0); gameScene.appendChild(gameLayer); director.makeMobileWebAppCapable(); director.replaceScene(gameScene); } //this is required for outside access after code is compiled in ADVANCED_COMPILATIONS mode goog.exportSymbol('virtual_pet.start', virtual_pet.start);
This will give us an object called “gameObj” that we’ll use to store game-level properties and the basic elements all LimeJS projects have, like a director, a scene and a layer. The director takes care of the big picture, just like in a movie. Things like telling the app what scene is currently being shown are carried out by the director (“replaceScene”). Layers are containers that allow us to place elements into. The elements are placed in the layer’s own coordinates.
I’m sure you have a cute pet in mind, something like a cartoonish hamster or a dog. Maybe some of you are thinking of out of the ordinary virtual pets, like a politician or Psy. No worries, the pet we’ll be having is something much simpler: how about a circle?
Our pet (lets call it Zenvita) will have two numeric properties:
Lets create a new file called pet.js with the following content:
goog.provide('virtual_pet.Pet'); goog.require('lime.Circle'); virtual_pet.Pet = function(gameObj) { goog.base(this); this.gameObj = gameObj; this.happiness = 100; this.health = 100; this.setPosition(this.gameObj.width/2, this.gameObj.height/2); this.updateLook(); }; goog.inherits(virtual_pet.Pet,lime.Circle); /** * update the pet's look according to it's happiness and health */ virtual_pet.Pet.prototype.updateLook = function() { //size of the pet according to the health var petSize = this.gameObj.maxPetSize * this.health/100; this.setSize(petSize, petSize); //color according to the happiness (between green and red) var redAmount = parseInt((100-this.happiness)/100*255); //255 if 0 health var greenAmount = parseInt((this.happiness)/100*255); //255 if 100 health this.setFill(redAmount,greenAmount, 0); };
After adding a new file don’t forget to “require” this new object back in virtual_pet.js:
goog.require('virtual_pet.Pet');
Add this to the gameObj object definition in virtual_pet.js:
maxPetSize: 200,
And run the following command on the terminal to update the project dependencies:
bin/lime.py update
Ok lets look into the pet.js file now. We are defining a new object called virtual_pet.Pet, which inherits from a Circle. LimeJS allows you to use the Google Closure inheritance capabilities. This basically means that a virtual_pet.Pet object will have the same properties and methods of lime.Circle, plus the new stuff we add in.
In the constructor we are passing it the gameObj object with game-level stuff so that it can be accessed or modified. We are giving the pet a default value of 100 for both happiness and health. The initial location of Zenvita will be the center of the screen. Finally, we are calling a method called updateLook(), which is defined further down.
updateLook() does the following:
Let’s create the new pet in virtual_pet.js and see the result. Add the following after “gameScene.appendChild(gameLayer);”:
//create pet var pet = new virtual_pet.Pet(gameObj, gameLayer); gameLayer.appendChild(pet);
If you reload your browser you should see the pet with a black background. If you change the health and happiness of the pet and reload the page you should be seeing the increase in size and the color changes.
Let’s make it so that if Zenvita reaches 0 health or 0 happiness, it dies. And let’s make it a very demanding pet that needs to be constantly entertained and fed. Zenvita will lose 1 unit of health and happiness per second, which means if you leave it still for 100 seconds it will die.
We’ll use the scheduler manager function to setup a timer, so that it’s executed every 0.1 seconds. Now why not every second? well, I’m planning on using this same timer later on for another feature which requires a shorter span, so 0.1 seconds will do for both.
In the virtual_pet.Pet constructor, add the following bellow “updateLook();”:
var dt = 100; lime.scheduleManager.scheduleWithDelay(function() { this.happiness -= 0.1; this.health -= 0.1; //game over if(this.happiness <= 0 || this.health <= 0) { alert('Game over!'); location.reload(); } this.updateLook(); }, this, dt);
What does piece of code does, it runs every 100 milliseconds, and on each run it diminishes the pet’s health and happiness and updates the pet’s look. If any of the properties reaches a value of 0 or lower, the game is over and the page is reloaded (you can replace that with a proper ending/sad screen).
Let’s add a background color to the game and a menu area. Add the following in virtual_pet.js after the gameLayer definition, which will give us basic rectangles to represent these areas:
var background = new lime.Sprite().setSize(gameObj.width,gameObj.height*4/5). setFill('#F3E2A9').setAnchorPoint(0,0).setPosition(0,0); var menuArea = new lime.Sprite().setSize(gameObj.width,gameObj.height/5). setFill('#8B5A00').setPosition(gameObj.width/2,gameObj.height*9/10) gameLayer.appendChild(background); gameLayer.appendChild(menuArea);
After reloading, the game should be looking like this:
Ok so far there isn’t much going on except making our pet hungry and upset. We need items like apples, ice creams and toys to get this more interesting.
We’ll create a new file called item.js, don’t forget to add a “require” statement for it in virtual_pet.js and to run the LimeJS project update command that we ran before. Use the following for the file’s contents:
goog.provide('virtual_pet.Item'); goog.require('lime.Sprite'); virtual_pet.Item = function(happiness, health) { goog.base(this); this.happiness = happiness; this.health = health; } goog.inherits(virtual_pet.Item,lime.Sprite);
Things to keep in mind:
Now in our menu area we will be adding button to give the pet some items. In a more polished version there could be a screen to pick among hundreds of items with different features, you could have them loaded from the web and make the user buy them with real money .. in this demo we will take a much simpler approach and hard-code three items for the user to pick.
Let’s add the following code to virtual_pet.js, after the menu area was appended to the Layer:
var appleButton = new lime.Sprite().setSize(gameObj.height/10,gameObj.height/10). setPosition(gameObj.width/4,gameObj.height*9/10).setFill('images/apple.png'); goog.events.listen(appleButton, ['touchstart', 'mousedown'], function(e) { e.stopPropagation(); gameObj.currentItem = { width: gameObj.height/10, height: gameObj.height/10, fill: 'images/apple.png', happiness: -5, health: 20 }; }); var icecreamButton = new lime.Sprite().setSize(gameObj.height/20,gameObj.height/10). setPosition(gameObj.width/2,gameObj.height*9/10).setFill('images/icecream.png'); goog.events.listen(icecreamButton, ['touchstart', 'mousedown'], function(e) { e.stopPropagation(); gameObj.currentItem = { width: gameObj.height/20, height: gameObj.height/10, fill: 'images/icecream.png', happiness: 20, health: -10 }; }); var toyButton = new lime.Sprite().setSize(gameObj.height/15,gameObj.height/10). setPosition(gameObj.width*3/4,gameObj.height*9/10).setFill('images/toy.png'); goog.events.listen(toyButton, ['touchstart', 'mousedown'], function(e) { e.stopPropagation(); gameObj.currentItem = { width: gameObj.height/15, height: gameObj.height/10, fill: 'images/toy.png', happiness: 10, health: 0 }; }); gameLayer.appendChild(appleButton); gameLayer.appendChild(icecreamButton); gameLayer.appendChild(toyButton);
Note: if you want to make this something more abstract instead of repeating the code like this, you can look at my farming game tutorial on Binpress for a more polished implementation of this feature.
What we are doing with the buttons is:
See how the apple will add health but take happiness away (apples are a bit boring, lets face it), icecreams will bring happiness but make it sick, and toys will just add a bit of fun to Zenvita’s life.
Having given the user the ability to pick an item, we need now to give them the ability to place these items on the game background, so that Zenvita can come by and nom nom nom…
Firstly, let’s add another property to the gameObj object definition, which should look like this:
var gameObj = { width: 320, height: 480, renderer: lime.Renderer.DOM, maxPetSize: 200, items: [] };
The items array will contain all the items that are dropped on the game background. Let’s add a click/touch event binding to the background, in virtual_pet.js, right after the background definition, so that new items are created when the user clicks on it:
goog.events.listen(background, ['touchstart', 'mousedown'], function(e) { if(gameObj.currentItem) { var pos = e.position; var newItem = new virtual_pet.Item(gameObj.currentItem.happiness,gameObj.currentItem.health) .setPosition(pos) .setSize(gameObj.currentItem.width, gameObj.currentItem.height) .setFill(gameObj.currentItem.fill); gameLayer.appendChild(newItem); gameObj.items.push(newItem); var movement = new lime.animation.MoveTo(e.position.x,e.position.y).setDuration(2); pet.runAction(movement); gameObj.currentItem = null; } });
This will create the item object and place it where we click (or touch). We are also creating an animated movement so that the pet goes to where the item has been dropped. The current item selection is cleared by setting this property to null.
In pet.js, inside our scheduler function, we will add collision detection so that items are consumed by the pet if they are occupying the same space (otherwise, the pet will go towards the food but it will not be eaten). The scheduler function will look like this:
var dt = 100; var i, arrayLen, toRemove; lime.scheduleManager.scheduleWithDelay(function() { this.happiness -= 0.1; this.health -= 0.1; //game over if(this.happiness <= 0 || this.health <= 0) { alert('Game over!'); location.reload(); } //check for collision with items toRemove = new Array(); for(i = 0, arrayLen = this.gameObj.items.length; i<arrayLen; i++) { if(goog.math.Box.intersects(this.gameObj.items[i].getBoundingBox(), this.getBoundingBox())) { this.happiness = Math.min(this.happiness+this.gameObj.items[i].happiness,100); this.health = Math.min(this.health+this.gameObj.items[i].health,100); toRemove.push(i); } } //remove picked up items for(i = toRemove.length; i > 0; i--) { this.gameLayer.removeChild(this.gameObj.items[toRemove[i-1]]); this.gameObj.items.splice(toRemove[i-1],1); } this.updateLook(); }, this, dt);
What’s going on here:
Let’s add one final thing to this game. Pets like to be petted, so how about we make it so that when the pet is dragged around it increases it’s happiness?
Add the following code to the constructor in pet.js, after the scheduler. Using “e.swallow” can be used in other events in LimeJS and it allows you to run code when the event is finished.
//drag it around to make it happier goog.events.listen(this,['mousedown','touchstart'],function(e){ e.startDrag(true); var pet = this; e.swallow(['mouseup','touchend'],function(){ this.happiness = Math.min(this.happiness+5,100); }); });
You can download the code and images of this tutorial using this link.
There are many ways you could improve this game demo and turn it into a blockbuster hit:
I hope you enjoyed this tutorial and are eager to learn more on HTML5 app and game development. At Zenva, we have many courses on app and game development, feel free to check them out here.
And don’t forget that by using the following link, you get a nice discount on the course Mobile HTML5 Game Development for Beginners, which uses the LimeJS framework to create many cool game demos such as this one. The course will also guide you through the steps you need to go through in order to pack your HTML5 game into a native app for iOS and Android.
]]>Over the last few years, HTML5 has come out as a great alternative to develop cross-platform app using a single code base. A HTML5 app can run on iOS, Android, Windows Mobile, Blackberry.. and of course good old Desktop.
For amateurs and professional developers it’s quite easy to get started with HTML5 (which comprises HTML, CSS and Javascript) as all you need is a text editor and a web browser.
In this tutorial you’ll learn the basics on how to create a simple farming game that emulates those big hits you’ve played on Facebook. We’ll be using an open source HTML5 game dev framework called LimeJS.
Farming games are a big phenomena that knows no limits when it comes to nationalities and generations. Fortunes have been made around them and people have created variations from the original concept. It’s not strange to find medieval, tropical, futuristic, even zombie themed farming games in the various app stores out there.
What is the reason behind this success? do we miss plowing the land with out bare hands in this post-modern urban civilisation? is this related to the urban farms people now grow on their balconies? Well who knows! ..
I’m not gonna tell you what the reasons are for their success (not that I know them anyway) but I am, on the other hand, gonna teach you how to make a simple farming game using HTML5 and an open source JS framework called LimeJS.
LimeJS is a Javascript game development framework that allows you to create cross-platform HTML5 games that work well on most platforms (specially iOS and newer Android devices).
You can dowload LimeJS from their home page. Installation instructions and a good intro can be found in the Getting Started Guide.
If you want step by step, video-based instructions on how to install this framework in Mac, Windows or Ubuntu and get a good hold of the basics feel free to check my online course, which teaches you all the basics of game development using LimeJS and other free tools.
If you want more info about my courses ask me on the comments section or on Twitter (@ZenvaTweets).
Our game will have the following elements:
In this tutorial we won’t get into making it social or pervasive, but it might be on the horizon for future tutorials
Time to stop talking and dip our hands into the code. Oh yes. Assuming you have successfully downloaded and installed LimeJS (you should be able to run the examples that come with it) we’ll create a new project:
bin/lime.py create farming
This will create a folder called “farming” inside of the folder where LimeJS was installed. If you installed LimeJS correctly you should be able to find a file called “farming.html”. Open up the file and add the following below the script tag:
<style>
.lime-scene {display:block !important;}
body {background-color:black;}
</style>
All this is doing is setting the background to black and applying a little hack for a known bug on some devices. Now open farming.js and replace it’s contents with the following code:
//set main namespace goog.provide('farming'); //get requirements goog.require('lime.Director'); goog.require('lime.Scene'); goog.require('lime.Layer'); //entrypoint farming.start = function(){ //game object var gameObj = { width: 320, height: 480, } var director = new lime.Director(document.body,gameObj.width,gameObj.height); director.makeMobileWebAppCapable(); director.setDisplayFPS(false); var gameScene = new lime.Scene().setRenderer(lime.Renderer.CANVAS); var landLayer = new lime.Layer().setAnchorPoint(0, 0); var controlsLayer = new lime.Layer().setAnchorPoint(0, 0); gameScene.appendChild(landLayer); gameScene.appendChild(controlsLayer); director.replaceScene(gameScene); }
That is just a basic boilerplate (if you open farming.html you should see a black screen). Firstly, we are providing the namespace “farming” which is required to do when working in LimeJS. We are then including relevant framework modules. Our farming.start() is the first thing that is run when the game loads (see how this is being called in farming.html).
We can define an object called gameObj (or however you want) to store some game-level variables. We will be passing this object around to our game elements so that these properties can be accessed from everywhere.
In the next blocks we are defininig our “director” object which does a similar role as real life directiors: define which scenes are playing, etc. Our game will have two layers (which are areas where content will be placed). landLayer will host the land and the crops. controlsLayer will have the status area and the options for the user, which in this case is the button to enter the shop.
Let’s include another file that provides a quick button:
goog.require('lime.GlossyButton');
We can add more properties to our gameObj that will contain some dimensions. It is recommended to do it this way instead of putting “magic numbers” in the code. Some of the properties I’m adding now will only make sense later in the tutorial so don’t worry if you don’t see them in the code right away:
//game object var gameObj = { width: 320, height: 480, tile_size: 64, num_tiles_x: 5, num_tiles_y: 6, landLayer_w: 64*5, landLayer_h: 64*6, controlsLayer_w: 64*5, controlsLayer_h: 64*1.5, costPlowing: 5, //shop shop_margin_x: 50, shop_margin_y: 20 }
Besides our game object, we also want to create an object for the player (just a design choice). The object will store the money and the currently selected crop (this will make sense later on). Add the following after the game object creation:
//player object var playerObj = { money: 300, currentCrop: 0 }
Now add the following code below “gameScene.appendChild(controlsLayer);”:
//controls area var controlArea = new lime.Sprite().setAnchorPoint(0,0) .setPosition(0, gameObj.height-gameObj.controlsLayer_h) .setSize(gameObj.controlsLayer_w, gameObj.controlsLayer_h) .setFill('#0D0D0D') controlsLayer.appendChild(controlArea); //shop button var shopButton = new lime.GlossyButton().setColor('#133242').setText('Shop') .setPosition(60, gameObj.height-gameObj.controlsLayer_h/2) .setSize(80, 40); controlsLayer.appendChild(shopButton); //money var moneyLabel = new lime.Label().setText('$'+playerObj.money).setFontColor('#E8FC08') .setPosition(gameObj.controlsLayer_w-50, gameObj.height-gameObj.controlsLayer_h/2); controlsLayer.appendChild(moneyLabel);
What we just did was adding a grey container on the bottom, a button that says “Shop” and a text label showing the player’s current money. You should be seeing the following:
The tiles used in this game have been adapted from Daneeklu’s artwork which has been released under the CC-BY-SA license, allowing us to use it for this purpose.
You can download all the game files from here.
Back to work. To represent a unit of land we’ll create a new file called “land.js” and give it the following code:
goog.provide('farming.Land'); goog.require('lime.Sprite'); /** * Land elements * * @param {} gameObj */ farming.Land = function(gameObj, playerObj) { goog.base(this); this.setAnchorPoint(0, 0); this.setSize(gameObj.tile_size,gameObj.tile_size); this.setFill('images/bare_land.png'); } goog.inherits(farming.Land,lime.Sprite);
What we are doing here is creating a new type of object as per the LimeJS specs. This object “inherits” from the lime.Sprite object, so it behaves like a sprite in all ways. See how we are providing the namespace like we did before. We are passing the game and the player objects in the constructor, and we are setting up some basic properties so that we don’t have to enter them each time we create a farming.Land object.
Every time you add a new file to the project you have to run this command for the framework to keep it all registered:
bin/lime.py update
In farming.js add the following to the “require’s” section:
goog.require('farming.Land');
Now, we want to populate the land layer we created previously with our land lots. We’ll do that with an iteration and some of the game object properties that have been setup. Add the following under the money indicator definition:
//create land elements for(var i=0; i<gameObj.num_tiles_x; i++) { for(var j=0; j<gameObj.num_tiles_y; j++) { var landElement = new farming.Land(gameObj, playerObj).setPosition(i*gameObj.tile_size, j*gameObj.tile_size); landLayer.appendChild(landElement); } }
If you reload farming.html on your browser you should be getting this (provided you downloaded the images):
The first thing you do in a farming game is to plow the land so that it’s ready to receive the seeds. After you plant the seeds, they grow until ripe. We have then a total of 4 possible states for a lot of land (empty, plowed, growing and ripe). Let’s represent that by adding these states in our land.js file right after the “goog.inherits” part.
//states farming.Land.prototype.EMPTY = 0; farming.Land.prototype.PLOWED = 1; farming.Land.prototype.GROWING = 2; farming.Land.prototype.READY = 3;
And this line after “this.setFill” (inside farming.Land) to define the default state:
this.state = this.EMPTY;
Every time the player clicks on a land element, we want it to be plowed (provided the land is “empty” and the user has enough money to pay for it). Let’s add an event listener inside the farming.Land function:
var land = this; goog.events.listen(this,['mousedown', 'touchstart'], function(e) { e.event.stopPropagation(); if(land.state == land.EMPTY && playerObj.money >= gameObj.costPlowing) { //plow land land.setFill('images/plowed.png') land.state = land.PLOWED; //update player money playerObj.money -= gameObj.costPlowing; gameObj.updateMoney(); } });
What this does is attach the events “mousedown” and “touchstart” to the statements inside the listener. See how easily LimeJS allows us to create something that works in both touch and non-touch devices from day one.
We then check the player’s money. If the “show me the money” is all good, we’ll plow the land, change it’s state and image. The cash is taken out of the player’s pockets and we update the money status, for which we need to add the following in farming.js, right after the moneyLabel definition:
//updating money indicator gameObj.updateMoney = function() { moneyLabel.setText('$'+playerObj.money); };
You should now be able to plow land and spend money:
;
Ok plowing land can be fun, but to really make it as a farmer you need to buy seeds and grow your stuff. Lets start by putting all the crops in our game object. Add the following in farming.js after the player object definition. See how you can easily change the parameters and tweak it however you want.
gameObj.crops = [ { name: 'tomato', cost: 10, revenue: 18, time_to_ripe: 10, //secods time_to_death: 30, //second from when it's ripe image: 'tomato.png' }, { name: 'artichoke', cost: 20, revenue: 38, time_to_ripe: 60, time_to_death: 60, image: 'artichoke.png' }, { name: 'lettuce', cost: 15, revenue: 26, time_to_ripe: 30, time_to_death: 60, image: 'lettuce.png' }, { name: 'eggplant', cost: 30, revenue: 78, time_to_ripe: 120, time_to_death: 120, image: 'eggplant.png' }, { name: 'peppers', cost: 40, revenue: 82, time_to_ripe: 180, time_to_death: 180, image: 'peppers.png' } ];
Each type of crop has then a name, a cost, a revenue that brings in when harvested, the time it takes it to be ready (in seconds), the time it takes it to die after it is ripe (in second), and an image file.
We’ll implement the shop as a new “scene” (think of the analogy of movie scenes). Add the following after the director.replace(…) part:
//shop var shopScene = new lime.Scene().setRenderer(lime.Renderer.CANVAS); var shopLayer = new lime.Layer().setAnchorPoint(0, 0); var shopBackground = new lime.Sprite().setAnchorPoint(0,0).setPosition(0,0) .setSize(gameObj.width, gameObj.height).setFill('#0D0D0D'); shopLayer.appendChild(shopBackground); shopScene.appendChild(shopLayer); //close button var closeButton = new lime.GlossyButton().setColor('#133242').setText('Back') .setPosition(gameObj.width/2, gameObj.height-25) .setSize(80, 40); shopLayer.appendChild(closeButton); //launch shop event goog.events.listen(shopButton,['mousedown', 'touchstart'], function(e) { director.replaceScene(shopScene); }); //close shop event goog.events.listen(closeButton,['mousedown', 'touchstart'], function(e) { director.replaceScene(gameScene); });
This creates a new scene for the shop, links our “Shop” button to the opening of this new scene. It also adds a “Close” button to the shop. You should now be able to open and close and empty shop.
The following code (which goes right after what we just added) will display the crops in our shop, and allow the player to pick one (that will be stored as a property of the player).
//shop items for(var i=0; i<gameObj.crops.length; i++) { var item = new lime.Sprite().setAnchorPoint(0,0).setPosition(gameObj.shop_margin_x, gameObj.shop_margin_y + (gameObj.shop_margin_y + gameObj.tile_size)*i) .setFill('images/'+gameObj.crops[i].image).setSize(gameObj.tile_size, gameObj.tile_size); shopLayer.appendChild(item); var timeLabel = new lime.Label().setText(gameObj.crops[i].name+' ('+gameObj.crops[i].time_to_ripe+' days)').setFontColor('#E8FC08') .setPosition(gameObj.shop_margin_x+150, gameObj.shop_margin_y*1.5 + (gameObj.shop_margin_y + gameObj.tile_size)*i); shopLayer.appendChild(timeLabel); var costLabel = new lime.Label().setText('cost: $'+gameObj.crops[i].cost).setFontColor('#E8FC08') .setPosition(gameObj.shop_margin_x+150, gameObj.shop_margin_y*2.5 + (gameObj.shop_margin_y + gameObj.tile_size)*i); shopLayer.appendChild(costLabel); var label = new lime.Label().setText('revenue: $'+gameObj.crops[i].revenue).setFontColor('#E8FC08') .setPosition(gameObj.shop_margin_x+150, gameObj.shop_margin_y*3.4 + (gameObj.shop_margin_y + gameObj.tile_size)*i); shopLayer.appendChild(label); //pick crop (function(item, i) { goog.events.listen(item,['mousedown', 'touchstart'], function(e) { playerObj.currentCrop = i; director.replaceScene(gameScene); }); })(item, i); }
The only complicated part of the code above is the use of a closure (the function inside the brackets). This is a Javascript pattern that allows us to attach events inside a loop without the change of “i” affecting the event callback of the previous elements. You should now be able to open the shop and pick a crop by clicking on it’s image. See how we are calling “days” what in reality is just seconds.
Now that we’ve picked a crop, it’s time to plant it. Lets go back to land.js and add the following statement to the event listener that we use for plowing:
else if(land.state == land.PLOWED && playerObj.money >= gameObj.crops[playerObj.currentCrop].cost) { //plant land.setFill('images/growing.png'); land.state = land.GROWING; //store crop and left time for it to be ready and to die land.crop = playerObj.currentCrop; land.ripeTime = gameObj.crops[playerObj.currentCrop].time_to_ripe * 1000; land.deathTime = gameObj.crops[playerObj.currentCrop].time_to_death * 1000; //update player money playerObj.money -= gameObj.crops[playerObj.currentCrop].cost; gameObj.updateMoney(); }
We are checking that the land has been plowed and the player has enough cash. If so, we are planting our seeds, changing the state of the land element, storing the relevant times (in milliseconds) and updating the money. You can start plating now!
Each crop has their own time to maturity and time to death. We want to check that and replace the images and states of the land elements when these times are reached. When the crop is ready for harvest, we want it’s image to be that of the crop. When a crop dies, the land is set back to empty.
LimeJS comes with a scheduler helper that allows us to check for events every X number of milliseconds. The following code will take care of the growth and death of the plants, add it after the event listener in land.js.
//growing plants dt = 1000; lime.scheduleManager.scheduleWithDelay(function() { if(this.state == land.GROWING) { if(this.ripeTime <= 0) { this.state = land.READY; this.setFill('images/'+gameObj.crops[this.crop].image); } else { this.ripeTime -= dt; } } else if(this.state == land.READY) { if(this.deathTime <= 0) { this.state = land.EMPTY; this.setFill('images/bare_land.png'); } else { this.deathTime -= dt; } } }, this, dt);
You are now able to plant.. but you can’t harvest yet.
Adding the harvesting part is very easy just add the following the our event listener in land.js:
else if(land.state == land.READY ) { //harvest land.setFill('images/bare_land.png'); land.state = land.EMPTY; //update player money playerObj.money += gameObj.crops[land.crop].revenue; gameObj.updateMoney(); }
You can download the complete game from the here.
I hope you’ve enjoyed making this simple farming game. Things that can be added to expand it further are progress saving, for which you can use something as simple as local storage, and of course social integration, so that you can share your farm on Facebook or Twitter.
I would like to encourage you to keep on learning HTML5 game development and to check out my game dev courses, which are designed for beginners and will save you hours of learning on your own.
I’ve prepared a comprehensive online course which will guide you through the creation of HTML5 crossplatform games using LimeJS game development framework. The course is 100% video based so that you can see in real time how games are created from scratch. Each lesson comes with its own zip file with all the code so that you can have a play with the examples and used them as starters for your own games.
A lot of extra topics are covered in this course, such as adding animations, sound, 2D physics, using tile-based maps, and transforming your HTML5 game into a native app so you can make money with it.
]]>In this tutorial, I’ll show you how to create a (very) simple RPG demo that works in all HTML5 supporting platforms, using the Open Source Javascript framework LimeJS.
This framework allows you to quickly create simple games that work regardless of the screen resolution of your device (laptop, phone, tv). You can play these games through HTML5 supporting web browsers, or you can turn them into native mobile apps using Titanium or Phonegapp, and submit them to the various “app stores” out there.
What is the most important aspect of an RPG game? I’m sure each one will have their own opinion on this. Some might say the characters, their sometimes erratic evolution and deceptive plots. Some others might say exploring huge bizarre worlds. Or perhaps fighting weird creatures created by some tripping mind for our enjoyment. No matter the answer to this question, I think we can all agree that the following elements are no less than important:
We will thus cover these items, except for the “Story” one, as it’s really up to you to come up with something exciting.
We’ll be using an awesome HTML5 game dev framework called LimeJS. This framework makes it easy to get started making your own games, as it comes with good support for aspects such as rendering images, user interaction for touch and non-touch screens, and many other game dev aspects (animations, 2D physics, etc). Moreover, LimeJS has a great and open community.
In this tutorial we will not go very deeply into installing the framework or using all of it’s power. You can get the installation instructions from the official guide here.
If you want a more detailed explanation of the installation process, I have created a full chapter on how to install the framework for Windows, Mac and Ubuntu in my video-based course [HTML5 Mobile Game Development for Beginners, where you can also learn a lot more about how to create games with this framework. I’ll talk more about this course at the end of this tutorial.
(this will download a bunch of dependencies) If you run into trouble installing LimeJS, the community is another excellent place to look for help and OS-specific guides.
Once LimeJS has been installed, we can create a new project by going to our limejs folder using the terminal and typing:
bin/lime.py create rpg_tutorial
This will create a folder called “rpg_tutorial” in the limejs folder. That is where all of our files will reside.
Keep in mind that if you move this folder anywhere else, the game will not run. This is because while we are developing, our project will need the libraries in our limejs folder. Once we are finished with a project, you need to “compile” it, and the resulting JS files you can put anywhere you want.
We’ll start by creating our world, which is an image made with this tileset. The tileset has been created by Sharm and released with the licence CC By 3.0, which means we are free to use this for any purpose we want as long as we keep this notice and give attribution to Sharm (thank you Sharm, whoever you are!).
The game will have a resolution of 352×256. The game map has that same resolution (we won’t do scrolling in this tutorial). You can download the game map image from this link and copy it to your “rpg_tutorial” folder.
Let’s do some coding. Open the file rpg_tutorial.js, delete all of it’s contents (which is a sample game project, it’s worth checking out to get an intro to the framework) and copy the following:
//set main namespace goog.provide('rpg_tutorial'); //get requirements goog.require('lime.Director'); goog.require('lime.Scene'); goog.require('lime.Layer'); //entrypoint rpg_tutorial.start = function(){ var director = new lime.Director(document.body,352,256); director.makeMobileWebAppCapable(); director.setDisplayFPS(false); var mapScene = new lime.Scene(); director.replaceScene(mapScene); }
What I’ve entered there is pretty much a basic skeleton for a new project. For a good introduction to all these elements there is the official guide.
Mainly, what is happening here is:
So far this doesn’t do anything, so let’s show our map after the scene has been declared:
var mapScene = new lime.Scene(); var mapLayer = new lime.Layer().setPosition(0,0).setRenderer(lime.Renderer.CANVAS).setAnchorPoint(0,0); var gameMap = new lime.Sprite().setSize(352,256).setFill('map.png').setPosition(0,0).setAnchorPoint(0,0); mapLayer.appendChild(gameMap); mapScene.appendChild(mapLayer);
Now open the file rpg_tutorial.html in your web browser, you should be seeing the game map image. What we are doing here is creating a new Sprite object, which can be an image or just a rectangle with some filling (colours, gradients). An anchor point of (0,0) means that we’ll use the top left corner of the image to set it’s position (coordinates go from the top left corner of things in this framework, and with working with the Canvas in general). Position (0,0) means we are setting the image’s anchor point to the top left corner of the scene. Basically, the image will cover the entire screen.
Notice how if you resize the browser window, the image will resize as well. Cool stuff. What I usually do as well is giving the “body” tag in the HTML file the colour black using CSS, that’s just my preference here.
The “layer” that we created is an invisible container where we put the elements. A layer itself can be animated, moved, resized, etc. We are using the Canvas on this layer, as opposed to using the DOM.
What would it be of RPGs without a hero? Who will then endure the vicissitudes of a decadent world full of monsters? Well, let’s create it then. What we’ll do is create a hero and have him move to wherever we click or touch on the map, how does that sound?
We will use another Sprite object to represent the hero (I encourage you to create custom objects for each game entity). The image file we’ll use is labelled a public domain and can be downloaded here. Copy it to your rpg_tutorial folder.
The code of the rpg_tutorial.start object will now be:
rpg_tutorial.start = function(){ var director = new lime.Director(document.body,352,256); director.makeMobileWebAppCapable(); director.setDisplayFPS(false); var mapScene = new lime.Scene(); var mapLayer = new lime.Layer().setPosition(0,0).setRenderer(lime.Renderer.CANVAS).setAnchorPoint(0,0); var gameMap = new lime.Sprite().setSize(352,256).setFill('map.png').setPosition(0,0).setAnchorPoint(0,0); var hero = new lime.Sprite().setSize(40,36).setFill('hero.png').setPosition(100,100); mapLayer.appendChild(gameMap); mapLayer.appendChild(hero); mapScene.appendChild(mapLayer); director.replaceScene(mapScene); }
We want the hero to move to where we click or touch, for which we need to bind the “click” and “touch” event in our map, so that the hero moves wherever the user clicked (or touched). This is done in LimeJS with the following code, which we’ll add after our hero definition:
goog.events.listen(gameMap, ['mousedown','touchstart'], function(e) { var movement = new lime.animation.MoveTo(e.position.x,e.position.y).setDuration(1); hero.runAction(movement); });
Some comments:
Ideally, one should create separate files for each game object. We will do that in a bit, but for the sake of simplicity and length I will leave it up to you to organise the code better. The next thing we’ll do to our character is giving it some characteristics:
hero.life = 20; hero.money = 100; hero.attack = 5;
Time for evilness to appear. Let’s create a monster and put in on the map. The image file is a public domain one and you can get it here. After hour hero definition let’s add the monster sprite and give it some properties:
var monster = new lime.Sprite().setSize(40,36).setFill('monster.png').setPosition(200,200); monster.life = 15; monster.money = 10; monster.attack = 1; mapScene.appendChild(monster);
Let’s make it so that if the user touches the monster, we’ll be taken to a “fight scene”. It’s not gonna be as elaborated as you expect, but it can be used as a ground for your own further enhancements. Let’s first create the fight scene. We need to include the LinearGradient file to fill out our sky with a linear colour transition:
goog.require('lime.fill.LinearGradient');
After the monster definition add the following code to show the fighting scene with our hero and monster (again, your homework here is to make this code modular, etc):
var fightScene = new lime.Scene().setRenderer(); var fightLayer = new lime.Layer().setPosition(0,0).setRenderer(lime.Renderer.CANVAS).setAnchorPoint(0,0); var sky_gradient = new lime.fill.LinearGradient().setDirection(0,0,1,1) .addColorStop(0,'#B2DFEE').addColorStop(1, '#0000CD'); var sky = new lime.Sprite().setSize(352,128).setPosition(0,0).setAnchorPoint(0,0).setFill(sky_gradient); var grass = new lime.Sprite().setSize(352,128).setPosition(0,128).setAnchorPoint(0,0).setFill('rgb(0,125,0)'); fightLayer.appendChild(sky); fightLayer.appendChild(grass); //show the images of the monster and hero var fighterOne = new lime.Sprite().setSize(hero.getSize()).setFill(hero.getFill()).setPosition(50,210); var fighterTwo = new lime.Sprite().setSize(monster.getSize()).setFill(monster.getFill()).setPosition(280,210); fightLayer.appendChild(fighterOne); fightLayer.appendChild(fighterTwo); fightScene.appendChild(fightLayer);
We can check how it’s looking by having the Director putting it in place using replaceScene as we did earlier with “mapScene”.
Ok! let’s add now collision detection so that if our brave hero touches the monster, the fighting scene comes into play. Include this file, which contains the Google Closure library math methods:
goog.require('goog.math');
Then add this code after the fight scene definition:
hero.inFightScene = false; lime.scheduleManager.schedule(function(dt) { if(!this.inFightScene) { if(goog.math.Box.intersects(this.getBoundingBox(),monster.getBoundingBox())) { director.replaceScene(fightScene); fightLayer.setDirty(255) hero.inFightScene = true; } } }, hero);
There is a lot happening here guys!
All ready! We have a pretty fancy fight scene in place, but what about the fighting itself? Well, you can create your own fighting algorithms involving all sorts of player choices, character attributes, items, etc. There is a lot of material online about RPG fights that you can use as inspiration. What I’ll do now is make up a really simple fighting algorithm, which will be basically tossing a random number and taking the “attack” attribute out of the enemy’s life attribute. But before that, let’s show the characters’ attributes in our fight scene, and add some fighting options.
Let’s include this file to create simple buttons:
goog.require('lime.GlossyButton');
The fight scene definition will look like this:
var fightScene = new lime.Scene().setRenderer(); var fightLayer = new lime.Layer().setPosition(0,0).setRenderer(lime.Renderer.CANVAS).setAnchorPoint(0,0); var sky_gradient = new lime.fill.LinearGradient().setDirection(0,0,1,1) .addColorStop(0,'#B2DFEE').addColorStop(1, '#0000CD'); var sky = new lime.Sprite().setSize(352,128).setPosition(0,0).setAnchorPoint(0,0).setFill(sky_gradient); var grass = new lime.Sprite().setSize(352,128).setPosition(0,128).setAnchorPoint(0,0).setFill('rgb(0,125,0)'); fightLayer.appendChild(sky); fightLayer.appendChild(grass); //show the images of the monster and hero var fighterOne = new lime.Sprite().setSize(hero.getSize()).setFill(hero.getFill()).setPosition(50,210); var fighterTwo = new lime.Sprite().setSize(monster.getSize()).setFill(monster.getFill()).setPosition(280,210); //labels with characters stats var labelFighterOneLife = new lime.Label().setText('Life:'+hero.life).setPosition(50,150); var labelFighterOneAttack = new lime.Label().setText('Attack:'+hero.attack).setPosition(50,170); var labelFighterTwoLife = new lime.Label().setText('Life:'+monster.life).setPosition(280,150); var labelFighterTwoAttack = new lime.Label().setText('Attack:'+monster.attack).setPosition(280,170); //some options var attackButton = new lime.GlossyButton().setSize(70,20).setPosition(40,10) .setText('Attack').setColor('#B0171F'); var runButton = new lime.GlossyButton().setSize(70,20).setPosition(120,10) .setText('Run').setColor('#00CD00'); fightLayer.appendChild(fighterOne); fightLayer.appendChild(fighterTwo); fightLayer.appendChild(labelFighterOneLife); fightLayer.appendChild(labelFighterOneAttack); fightLayer.appendChild(labelFighterTwoLife); fightLayer.appendChild(labelFighterTwoAttack); fightLayer.appendChild(attackButton); fightLayer.appendChild(runButton); fightScene.appendChild(fightLayer);
Let’s implement the “Run” behavior by binding the button “click” or “touch” with going back to the map scene. Add the following after the fight scene definition.
//run away, coward goog.events.listen(runButton, ['mousedown','touchstart'], function(e) { //go back to the map director.replaceScene(mapScene); mapLayer.setDirty(255) //move the hero away from the monster, or the fight scene will be triggered again! //this is just a quick, non-elegant way of doing this currentPos = hero.getPosition(); hero.setPosition(currentPos.x-60, currentPos.y-60); hero.inFightScene = false; });
Righto. We have a brave hero who can get into fights and run away from them. Sweet.
Now let’s man up a bit and get into the fighting itself with this ugly, vicious creature/monster/evil king once and for all. The simple fighting algorithm will work as follows:
We’ll implement this by binding the attack button to the “click” and “touch” events, and by running this simple fighting algorithm when any of those events are triggered. As I’ve mentioned earlier, you are very welcome to transform this code into something modular and scalable, and if you do so and want to share it with the world just let me know and I’ll add it to the tutorial, or you can also do it through the Github repo.
//fighting algorithm goog.events.listen(attackButton, ['mousedown','touchstart'], function(e) { //generate random number var randomStuff = Math.random(); //the player hits! if(randomStuff < 0.5) { monster.life -= hero.attack; //is he dead yet? if(monster.life <= 0) { console.log('monster dead'); //get the monster money hero.money += monster.money; //go back to the map director.replaceScene(mapScene); mapLayer.setDirty(255); hero.inFightScene = false; //delete the monster object monster.setHidden(true); delete monster; } } else { hero.life -= monster.attack; //have you been killed? if(hero.life <= 0) { var labelGameOver = new lime.Label().setText('GAME OVER!!!').setPosition(160,100); fightLayer.appendChild(labelGameOver); } } //update stats labelFighterOneLife.setText('Life:'+hero.life); labelFighterTwoLife.setText('Life:'+monster.life); });
Key points here:
To prevent the fight scene to be loaded again from the bounding box of the monster, change the line where we checked for collision detection, for this one, so that only alive monsters trigger this:
if(monster.life >0 && goog.math.Box.intersects(this.getBoundingBox(),monster.getBoundingBox())) {
You can download the complete game files from this link. As I mentioned earlier, running these files outside the limejs folder is not gonna work. In order to deploy a LimeJS project you have to follow this guide.
After doing this tutorial you are all good to get started with this awesome framework. The first thing I reckon you should look into is on making this more modular, for instance putting each game object in a separate file, and add some methods so that you are not repeating the code. Also, the fighting scene display and fight algorithm should be made generic to “any” monster instead of having one monster hard coded. Getting you into Game Dev was my goal here, not teaching OOP design patterns (there are plenty of tutorials for that out there!).
Now where should you go next? I have an idea.
I’ve prepared a comprehensive online course which will guide you through the creation of HTML5 crossplatform games using LimeJS game development framework. The course is 100% video based so that you can see in real time how games are created from scratch. Each lesson comes with its own zip file with all the code so that you can have a play with the examples and used them as starters for your own games.
]]>