(this is targeting older versions of kaboom, some interfaces might have changed, https://github.com/replit/kaboom/releases)
Hello everyone, since we were in the middle of the KABOOM JAM, I decided to write a Kaboom.js tutorial to help all the participants who are willing to give it a try.
First of all, however, I believe you should know what you're learning before you actually start learning it. So, what is kaboom.js?
For this tutorial, we will create a classic snake game (since it's a tutorial, I thought it'd be best to use a game whose concepts are familiar to most people). The final product (don't flame didn't have an artist) is available at - https://replit.com/@TheDrone7/Kaboomjs
There are also kaboom repls but we won't be using that since that is specific to repl.it only and you won't be getting all those GUI features at other places. Not to mention that mode also limits your development to just the game and you won't be able to have a backend. That mode is best suited for small games only.
You can also use a node.js repl or a python repl or a repl in any language if you want to make your own backend and add features to the game that would require one but for the sake of simplicity of a tutorial, we won't have one of those.
In the newly created repl, you will see 3 files -
style.css. First of all, we will configure the index.html for our basic needs of the game.
Change the value inside the
Add the following script tags inside the
<body>tag to import kaboom.js
Note that the following lines of code need to go at the bottom of the body tag but right above the tag that imports
Here, the first line of code will import the base library of kaboom.js
The next 3 import "kits" that let's just say, "make life easy".
Your HTML might look similar to this
- Now we're ready to use kaboom.js in our project!
Initialize a new game
We have everything set up and are ready to start creating a new game. Kaboom.js is a js library so we will be writing js code so head into your
script.js file and follow along!
To access everything kaboom.js has to offer, we first need to create an instance or just make everything global. In our case, we will go with the 2nd choice and inside our
script.js, write our first line of code as
And it will make everything kaboom.js contains global.
Now that we have access to all of kaboom.js, let's get started with actually creating the game! To create a new game, we use the
initfunction that initializes a new canvas for our game. Just below the import line, write the following code.
This code will initialize a new canvas with its width set to
480pxand its height set to
scalefactor multiplies all sizes by the provided number.
In our case, it multiplies all sizes by 2. So our actual canvas will be of the width
Why we use the
scalefactor instead of directly setting the width to
960and height to
720? Because it multiplies all sizes with that factor. What that basically means is, it will also increase the font size and the size of the asset.
Before we get into making the actual game, we will first create a menu to get to learn some more concepts of kaboom.js
Heading back to
script.js, after initializing a new game, we will create a new
scene. Scenes are usually different parts of a game such as different levels, menus, or game-over screens. In this instance, our first scene will be the menu scene which gives the player 2 options only -
Play game and
Learn more about Kaboom.
Let us get to it then.
Write the following piece of code at the end of your
Now, let's break down what this piece of code is doing. Firstly, we are creating a new
scene()function which takes in 2 parameters:
The first is the name of the scene, in our case, we call it "menu". We will eventually use this name to tell kaboom which scene to run and when to run it.
The second parameter is a function that defines what the scene contains and how it works. This function is looped to update the scene as per requirement.
We then add a
game objectto our scene using the
addfunction, which takes in 1 argument which is a list of
components that defines our game object.
Our game object here, contains a text component that says "Snake game" (defined using the
text()function), and a position component makes it draw on the horizontal middle of the scene (480 / 2 = 240) and a little above the vertical middle. (defined by the
pos()function), and a scale component that scales it by
scalefunction). This will scale it by 6 times its normal size (since our
initmethod also contained a scale factor of 2).
Next, we will design the 2 buttons that we need for user interaction.
For this, we will need to add 4 more game objects to the scene as follows
Here, for each button, we add 2 game objects - one is the rectangle in the background and the other is the text displayed over it.
We also have the string
"button"as a parameter for the rectangle background of the buttons, such strings are called
tagsand these can be very helpful as we will see in the next step.
These buttons look and feel sort of dull, so let's add some interactivity to them. Sticking to keeping it simple, we will just darken the background color of these buttons when the user pointer hovers over them. For this, we will need something we call
actionsare functions, that can be optionally bound to a
game object, that runs every frame.
Let's add some more code for that inside our scene.
Here, we define a new
actionthat runs for all
game objectswith the tag
button. This helps us make sure it selects the button game objects that we defined above only. Inside we use the
.isHovered()method on the button (provided by
rect()component) and change the color of the button if it's currently being hovered, we change their color (using the
color()function) to a light shade of grey, if the button is being hovered.
Now that our buttons look fine (no, they don't look great but they do look fine), the next step is to make them respond to mouse clicks and do something! We can use another convenient function called
isClicked()to check if the button is being clicked the last frame
Then we'll need a way to assign an action to each button, we can do this by assigning custom fields to them:
so if you give a plain js object in the components array, it'll assign all fields of it directly to the added/returned game object, here we use that function in the "button" action responding to mouse click
When the "Play" button is clicked, it will load up the
game scene (we have not declared or worked on this so don't worry) using the
When the "Learn" button is clicked, a new tab will be opened and the user will be taken to https://kaboomjs.com - the kaboom.js docs.
By now, your
script.js should be looking similar to this
The game scene
We have the game menu set up completely, thus, comes the next part - the game itself!
We first define the game scene at the end of the
But before we go any further, you will notice, you won't be seeing the menu when running the game. This is because we have not told kaboom.js what scene to load by default. We can do this by simply using the
At the end of your
script.js, add the following line of code: -
This will tell kaboom.js to load the
"menu"scene after initializing the game.
And that is exactly what we want to see when the user loads the page. You can try clicking on the
Learn Kaboom.jsbutton and it should take you to the kaboom.js documentation website.
Play gamewill take you to a new blank scene that we had just defined above.
We have prepared the codebase for our new scene. Let's get to work on the scene! First, inside the scene, add the following code: -
This will create a green background for our game (green representing grass).
Next up, we need a visual border that our player does not cross. For this border, we will use the following image: -
Download that picture and upload it to your repl, name it
border.png. Keep it at the root directory alongside all other files.
We will then import this file into our code. For this, we will create a new
sprite. To achieve this, add the following line of code at the beginning of your
script.jsright after importing kaboom: -
This will look for a file named
border.pngat the root directory and then prepare it to be used as a sprite and name the sprite
Head back in the
gamescene and after adding the background, write the following piece of code: -
Since the size of our border is 40px, which after scaling by
0.5becomes 20px, we loop over our scene (divided into squares with side 20px) and if either the x or y coordinate is the last one, add the border sprite, positioned accordingly, scaled by
0.5and has the tag
"wall". We will use this tag to detect collisions.
You can optionally set the default scene to
gametemporarily so you directly see that scene while testing.
Now we have a visual border that the player is not supposed to cross alongside a decent game background.
Next up, we will work on creating our player! Our player consists of 2 parts - the head, and a lot of tails. We will start our player off with 3 tails and 1 head. Let's create the player object then.
Here, we first define an array called
tails. This will help us keep track of all the tails that the player has.
Then we add a
10x10square object for our head (the color defaults to white which works perfectly for us). We also give the
"head"tag will allow us to check for collisions between the head and other objects.
Nextly, we define the user's tails. A for loop makes 3 tails, right behind each other behind the head. We make the tails to the left of the head and add "right" to all the tails as well. Since tails can be moving in a different direction from the head at some point in time, we need to keep track of each tail's directions and points of turning separately. For each tail, we store 3 pieces of information: the tail object that is displayed to the player, the positions at which the tail needs to move in a different direction, and the new direction in which it needs to move after it turns.
If you run the game now, you'll see your player in the center of the screen. Next step - give it the moves.
For which, we will add the following piece of code within our scene:
setIntervalexcept it takes the interval first and callback second and it takes the interval in seconds instead of milliseconds.
Inside the callback that is run every
1second, we first check which of the 4 directional tags (using the
gameObject.is()method) our head and each of the tail objects have, then move each body part of our snake by 10px (the width of each square) to make a smooth moving transition.
Additionally, before moving the tail parts, we also check if they have reached a point where it needs to turn, it checks the respective co-ordinates and if they match, it removes all directional tags and adds the new direction tag from the array of directions.
Note that we use
.shift()so we also get rid of the first entry in the arrays after having made the turn.
Also, we check only one co-ordinate (either x or y) of the tail before making the turn. This is because by the time the turning point was added to the array, the head may have already moved to some new point perpendicular to the line in which the tail part is moving so it may never reach the turning point and get separated from the head (obviously not something we want so... yeah).
This part won't work, for now, we'll make it work in the next step where we let the player take control of the snake.
We set the position of each object manually here instead of using the
gameObject.move()method to create a smoother moving transition (if it moved by 1 pixel instead of 10 pixels, the snake taking turns would be awfully ugly). You can try doing that by replacing:
.pos.x += 10with
.pos.x -= 10with
.pos.y += 10with
.pos.y -= 10with
Fair warning: you may want to unsee what you see after you do this.
Running the game now will move your snake to the right side. You can try if the other direction work by changing the default tag your snake's head and tails contain to something other than
Now that we have a snake on our screen that can move. We need to let the player control its movements. For this, we will be using the
keyPress()function that takes in 2 parameters, the first is the key that on pressing triggers the callback provided as the second parameter. We will first create a separate function that handles making the turn then use
keyPress()4 times for each direction. Basically, add the following code at the end of your
makeTurnfunction takes in 2 parameters - the first is the new direction the player wants the snake to move in and the second parameter is the direction opposite to the one the player wants the snake to move in.
This is because we want to make sure that the player cannot move the snake in the direction opposite to its current direction of movement. Basically, we don't want the snake to go through itself.
The first line of code inside the function handles this check. After that, we make a clone of the snake's head's current position and then tell all tails to make a turn in the new direction when they reach that position (handled in the last step up above). Then we make the head make the turn right away since the head doesn't have to follow anything.
Then we use the
keyPressfunction 4 times, each time it checks for one of the 4 arrow keys and then runs the
makeTurnfunction as needed.
AND WE ALREADY HAVE A SNAKE THAT YOU CAN CONTROL!!!
This is what your game might look like (with enough big brain applied)
Normally the next part would be to add points, but before that, there is one thing that would be best addressed now and not later.
In a snake game, the head of the snake is not supposed to collide with the border or one of its tail parts, therefore, we will first add collision detection of these parts and if they do end up colliding, we will end the game.
We will work on a game-over screen later. For now, we will only pause the game when that happens.
To achieve this, we will need
collidersand this is where tags come into play! Kaboom.js lets you detect collisions with tags.
Add the following code at the end of your
This will check for any collisions between objects with the tag
"head". We don't need to worry about the tails since the tails colliding with the walls will never reach a point that the head doesn't.
Next, we have another collider for the head and tails. Since the first tail and head are always in contact, we don't want to say it's game over as soon as it starts, that's where the
.isOverlappedmethod comes in. Normal colliders are triggered when 2 objects are even touching each other. But we only want our game to end if the head goes over a tail, thus we use the
.isOverlappedmethod to check for that and only pause the game when that is the case.
Now our player is almost truly complete.
The next thing we are going to have to deal with, is spawning fruits. Some things to keep in mind
- Do not spawn over a wall.
- Do not spawn under the head.
Keeping these 2 things in mind let's develop a function that can properly spawn fruits. Our fruits here will just be red
10x10squares. For this, we write the following code at the end of the
Here, we first define a recursive function, that first generates random coordinates on the map, checks if the head is on the same coordinates, if it is, redo it, else, spawn a new fruit there.
Then we run the function once to generate the first fruit for the player to collect.
Now that we can spawn fruits, we need to let our player collect them and we also need to keep a track of how many fruits has our player collected. For this, first, create a new variable named
scoreand set its value to
gamescene. For example: -
and then at the end of the scene, add a new collider as shown below: -
Here, we again first check if the head and fruit are overlapping, then we first add to the player's score, then we destroy the fruit since it has been consumed and spawn a new fruit using the function we defined above. After this, we create a replica of the last tail of the snake and place it right behind the current last tail on the canvas and then also add it to the tails array we defined above. This will increase the snake's length every time the snake consumes a fruit.
And we now have a full-fledged snake game!
But before we call this game complete, we still have a few things left.
Most importantly, even though our player is collecting fruits and increasing the score, the score is not shown to the user at all. Let's create a new display for the score in the top-left corner of the game scene. For this, at the end of your screen add the following code
We first create a black background in the top-left corner for us to display the score properly, then we write over it the user's score in the format
Score: 0and look for updates every frame using the
This will update the displayed score every time the player eats a fruit. Now our game is truly complete!!!
Game Over Screen
We finally are done with our game! But when the game is over, the player is stuck with a paused screen that shows... nothing. It looks as if the game crashed, so we will create a game-over scene so the player knows the game is over and can move on.
For this scene, however, we won't be writing most of our code. Duplicate the
menu scene that we first created. And then rename this new scene to
In the title, change the text displayed from
Snake Game to
Change the text of the
Play game button to
Play again and the text of the
Learn Kaboom.js button to
mouseClick handler, change the handler of the Learn button. Remove the following line
And we're almost done!
Just for a little gimmick, we will also show the score of the player on this screen. For this, first head back to the
game scene and at both places where we check for collisions, replace
And done! Come back to the
game-over scene to actually display this score. The score will now be passed as a parameter to our scene function, making it look something like
score is the same value as the player's
score from our game scene. To display this, we just add the following code to our
And it will now beautifully display our scores on the game over screen.
Even though the game is ready to be played, it's not perfect (and it was never intended to be). However, there are some improvements we can make right now to make it a better experience. The things we're adding are listed below
- Bugfix (tail detaches from the head at one special scenario)
- High scores.
So let's fix that bug. The bug is - if the player presses one directional key and presses another before the head has moved, the tails end up getting detached. To fix this, we can simply pause input for a duration of 0.75 seconds. To achieve this, we first define a new variable at the top of our game scene called
lastInput as shown below
Now inside the
makeTurn() function that we created, we can simply add a new condition and turn it into a new function as shown below: -
and now it will only run once every 0.75 seconds at the fastest.
And for the last part, let's add high scores. These high scores will be of the player themselves only, not a public leaderboard and we will simply store it in their browser's localStorage. We will be displaying the high score only on the game-over scene so let's head to that.
At the beginning of the scene, before displaying anything, we will first look for any stored high scores, check if the stored high score is higher than the player's current score, change it to the current score if it isn't. Let it be if it is. Then display both the current score and the high score. This can be achieved with just 4 simple lines of code (other than the displaying).
Change the text object that shows your current score be smaller and be positioned a little higher so we can also display the high score as shown below
And that's the end of this tutorial! Note that this tutorial was made while Kaboom.js was in beta so don't be surprised if the code doesn't work for you. If you have any questions, feel free to ask in the comments section although the best option would be to check the documentation.