While designing a game, you would want to create the setting and the place where it all happens. Many games are divided into levels, often getting more difficult with each level upgradation. For some other games such as RPGs (Role Playing Games), there is no such thing as a level because it implies a certain cutoff with often no way back. Throughout the book, a space that can be saved as a single file in the Weltmeister will be referred to as a level.
The Weltmeister is literally the tool to master your world in ImpactJS. If you installed ImpactJS correctly you should be able to access the level editor shown in the next screenshot by placing the following address in your browser:
http://localhost/myfirstawesomegame/weltmeister.html
Creating levels for a game is one of the most enjoyable things to do in game design. The Weltmeister is so well put together that you will spend hours messing around with it just because you can.
On opening Weltmeister (by default), it starts with a clean slate; there is a vast amount of emptiness for you to fill. Soon we will get to building a level from scratch but for now we should load the level1
level. Press the Load button in the top-right corner of your Weltmeister and select it in the levels
folder. If you copied it in the beginning of the chapter it should be right there, otherwise copy it to the Weltmeister now.
Level1
is a rather original name for a first level but let's personalize it a bit by saving it as myfirstepiclevel
. Press the Save As button in the top-right corner and save it in the same directory. Now we have a copy to work on and mess around with.
Before we actually use the level called myfirstepiclevel
, we will need to make a change in the main.js
script's code:
Open the main.js
script in your preferred script editor.
In the main.js
script, you will see a call to the loadLevel()
function.
this.loadLevel(LevelLevel1);
This call is situated in the init()
function of the game (ig.game.init
). This means one of the first things the main.js
script will do (at initiation, thus init
) is load the level, Level1
. Obviously, we don't want that anymore, as we have our own level called myfirstepiclevel
now. In order to let the game know that it has to include this level, you will need to add it to the .requires()
function as shown in the following code lines:
'game.levels.level1', 'game.levels.myfirstepiclevel',
Also, change the call to the loadLevel()
function so it calls the level, myfirstepiclevel
, instead of Level1
as shown in the following snippet:
this.loadLevel(LevelMyfirstepiclevel1);
As you might have noticed, you always have to put the word Level
before your actual level name. In addition you will always need to write both Level
and your level name with a capital letter. Failure to comply with either of those will result in an epic crash at game load. Putting Level
in front of the actual level name is a rather odd convention, especially since functions like loadlevel()
are built to expect a level file. It is possible that this mandatory prefix is removed in the future versions of ImpactJS. But for now, failing to insert the word Level
before your actual level name or writing both Level
and your level name with a capital letter will result in the display of the following error:
The other buttons in the Weltmeister are Save, New, and Reload Images. The Save button just saves the file you are working on, and the New button will open a new and empty file. The Reload Images button is a refresh button for your tilesets. The tilesets of a game are collections of images. All the graphics of a single theme can be on a single tileset, for example, the outdoor
tileset. Because several images are stored in a single overall image called a tileset, it is easier to create your level while working in the Weltmeister. You can look at it as the color palette of an artist, but as a level creator you have as many palettes at your disposal as you have tilesets.
Summing up everything we came across, we can conclude that:
You can access the Weltmeister by typing the following address in your browser while your server is turned on: localhost/myfirstawesomegame/weltmeister.html
Open level1
by using the Load button
Save it again as myfirstepiclevel
with the Save As button
Include the new level in the main.js
script by adding myfirstepiclevel
to the include()
function
With the level opened, you can see the different elements and layers of which it is comprised. Let's first have a look at the Layers menu on the right-hand side of the editor.
Select the collision layer and you will see the standard properties that need to be filled out for a layer in order to exist. All layers (except for the entities layer) have a name, tileset, tilesize, dimensions, and distance.
Tilesets are basically chains of square-formed images, which when put together well enough, form your idyllic looking landscape or scary dungeon. The tilesize is the width and height of one tile measured in pixels. Since all tiles are squares, you only need to fill out a number. The dimensions of a layer are the width and height of the entire map on which the layer needs to be present, measured in number of tiles. So a layer with a tilesize of 8, a width of 20, and a height of 30 is composed of 4800 (8 x 20 x 30) pixels. Take this into account while working with mobile devices. A level with a resolution of 160 (8 x 20) x 240 (8 x 30) will fit most devices. However, if the tilesize is 32 you will need a viewport that automatically follows your playable character in order to show off your level. This view-port is rather easy to incorporate and will be explained later in this chapter. To create a new tileset follow these steps:
Try creating a new layer by clicking on the plus (+) sign at the top of the layer selection menu.
Type in a name for the layer; let's say astonishinglayer
or tree
, whatever you like.
Now select the tree tileset from the media
folder by clicking the empty box next to the Tileset field. If you can't reach it by using the Weltmeister menu, you just type media/Tree.png
in the tileset box. Set the tilesize to 32
and the dimensions to 30 x 20
(width x height). You can see the border of your layer change accordingly.
It is a common mistake to have one layer smaller than the other and then not be able to add objects on one part of your map. So let's say you intend your level to be a map with dimensions of 30 x 20 and a tilesize of 32, and you add a layer like this, and fill it with grass. You want to add a bench on the grass so you add another layer and make the dimensions 30 x 20. Because your bench is a 32 x 16 image, you set the tilesize to 16. If you do this you will be able to draw your bench quite accurately but only in the top-left corner of your level. You will need to change the dimensions to 60 x 40 in order to occupy the same space as the grass layer does.
Distance is the speed at which the layer moves relative to your game's screen position. A value of 1
in the Distance: field means it moves at an equal speed, whereas the value 2
means the layer moves at half the speed. By setting this parameter higher than 1
, it becomes possible to make things appear further away; this is ideal for your nice cloudy background in a side scrolling (or parallax) game, such as Mario. Go to your game and walk your character from the far left edge of the game towards the right boundary to observe the effect of altering the value of the Distance: field.
Now return to the Weltmeister and try setting the value of the Distance: field to 2
. Save and reload the game, run your character from one edge to another edge of the level, and see what happens. Part of the game will appear to move slower than the rest. This is useful in side scroller games as a background, but it is also used in top-down games to create the impression of a terrifying abyss. Underneath, you have the options Is Collision Layer, Pre-Render in Game, Repeat, and Link with Collision. Turning them on and off can be done by clicking on the white squares (which turn black to indicate the option is switched off).
The Is collision layer option will tell the level editor that the objects in the layer you are drawing are impenetrable. Pre-rendering a layer will cause the game to cluster tiles while loading. This will increase the initial load time but decrease the number of draws the game requires and, thus, increase performance while running.
The Repeat option is used for background layers. For example, your background clouds can be repeated over and over again if they are a pattern.
Finally the Link with Collision option will make sure that, for every object you draw, collision squares are added to the collision layer. You can delete them from the collision layer later on, but it is a useful tool to speed up the drawing of walls and other impassable terrain.
Layers can be rearranged within the Layers menu by dragging them up or down the list. By dragging a layer to the top or down to the bottom of the list, you define its place on the z axis. You should look at the z axis as the level's third dimension, just as the world we live in has an x axis (width), a y axis (height), and a z axis (depth). The game you build is not really 3D in the conventional sense, but since the 2D graphics are layers stacked on top of one another, there is an actual third dimension at work here. The graphical layer at the top of the list will always be visible and will even hide entities. The bottom layer can only be visible when nothing else is on the way. The collision layer is never visible but dragging it to the top will enable you to make modifications to it more easily.
Try rearranging the layers and check out what happens. Save your game and reload. Depending on what crazy stuff you did with the layers, the world is now a very different place indeed.
Instead of dragging a layer to the top of the stack in order to be able to view it, you can also turn layers on and off. This is done by clicking on the square in front of the layer name. This will not have any effect in the actual game; it is visible only in Weltmeister. This is very useful for the collision layer. Try dragging the collision layer to the top of the stack and turn it on and off at your will. You will notice that this is the best place for the collision layer to be while working with the Weltmeister. This is because the collision layer itself doesn't actually have graphics while playing the game, so it can't obscure anything else.
Summing up the details we came across, we conclude that:
A level is made up of different layers with properties such as tilesize, distance, and whether it is a collision layer
Add a new layer using the (+) sign in the Layers menu and give it the name astonishinglayer
Add a tileset media/tree.png
to the layer. Set its dimension to 30 x 20
and its tilesize to 32
Try messing around with all the properties you can find on the layer including dragging the layer up or down
Save the level and reload your game in the browser every time you adjust a parameter
There are three big types of layers: entities, collision, and any other layer. For entities and dead objects, the entities and graphical layers are of interest.
The entities layer holds all the entities that are present in the entity
folder and are called upon by the main.js
script. An entity can be anything, from the character the player uses, to an invisible trap that kills everything that dares to come close. All functionalities and the level's AI are in these entities. It can hold enemies, triggers, level changes, randomly flying objects, fireable projectiles, and everything that can be interacted with.
If you have critical bugs in these entities or some non-existing ones are included in your main.js
script, the Weltmeister will not even load. So make sure at all times that these entities are bug-free (or not included) when you want to build a level.
Some entities, such as the player, are already present in the level. First select the entities layer in the Layers menu and then select the player entity in order to see its properties. The x: and y: properties are its current location and are always present while putting a new entity in the level.
Try moving the player entity around by selecting him and dragging him somewhere else. The x: and y: coordinates now change.
Let's add an enemy entity to the level. Select the entities layer and press the Space bar while your mouse hovers over the level. A menu will appear next to your mouse; select the enemy entity in this menu. An enemy just appeared at your mouse's location! You will now be able to go crazy and basically paint every square with enemy entities, but this might be a little bit of overkill, so let's just place one enemy for now. Save and reload your game. Now tremble in fear as your enemy attacks you or stare at it unimpressed, your choice.
If you added too many enemies to safely roam about, remove them from the game by first selecting the entities layer in Weltmeister, then the enemies you want to get rid of and simply press the Delete key.
It's a good habit to have both the game and the Weltmeister open to check the changes you made. If, for some reason, an entity you added is corrupt and the game refuses to load, at least you know the problem lies in the last changes that you made. Of course, you still have the Chrome or Firefox debuggers that will also point you in the right direction.
Adding objects is different from adding entities. Dead objects, which cannot be interacted with but are just a graphical thing, can simply be painted, for example, a square patch of grass, a fountain, or a castle wall. Complex interactions with these objects can be done but only with the use of entities. Here we will have a look at how to add a simple object, without interaction, to a level.
Although the level looks pretty neat, we will need to give it a makeover. Let's select the grass layer from the Layers menu. Hover your mouse over the map and press the Space bar key. A tileset will appear; you can make it disappear by tapping the Space bar key again. If this tileset does not fit your screen you can either hover your mouse to a more central location and bring it up there or zoom out with the scroll wheel of your mouse. If you do not have a scroll wheel, you can use the Ctrl + - (minus) key combination to zoom out and the Ctrl key with the plus sign key (+) to zoom back in. Now you can see your entire grass tileset. Select the grass and start painting it everywhere by clicking and holding the left mouse button.
A little trick for painting large areas with a single tile is to first paint only a small area on the map. Then click on the Shift + left mouse button and select this freshly drawn bigger area of tiles from the level itself. You can now paint with this new selection of tiles and cover more area in less time.
If you want to delete something from a given layer, simply select an empty square that is empty for that particular layer. If you already have graphics from other layers on a certain spot, but not from the layer you are currently working on, that square can be considered empty. Now paint with this empty square and the formerly chosen tiles will magically disappear. Try deleting some of your grass now.
Grass is at the bottom of everything. If you have an object, any object, it will always be on top of the grass, never underneath (except maybe in some crazy mole world). To make it so, you must drag your grass layer to the bottom of the layer stack.
Let's add something else to the scene. We still have the layer that we created, astonishinglayer
, ready to go, so let's draw a tree with it. In order to select the entire tree at once, select the tree by clicking on the Shift + left mouse button key combination. Depending on where you have put your layer, the tree will now always appear either in front of or behind the player. If you dragged the layer to the bottom of the list, it might even be invisible. It's a strange result and we will deal with this later. Save your level and reload to check out your first level creativity.
Summing up the process of adding and removing entities and objects, we conclude that:
The entity layer provides the choice of all your game entities
You can add some of the present entities to the level, and then save and reload the game
The collision layer is a special layer that is not predefined when you open the Weltmeister from scratch. It is special because it is an invisible layer that marks impassable areas. For example, if you draw a wall on your map by using a graphical layer, all your entities will be able to go right through it as if it isn't even there. If you want a wall that is actually capable of stopping the player and his enemies, draw a line in the collision layer where the wall is.
Your game is still open; try drawing a wall (or any other object) and then running through it at the bottom of the level. You should find it peculiarly easy to just stroll through something that looks this solid. Select the collision layer, drag it to the top of the list if it isn't already done and make sure it's visibility option is turned on. All tiles are now clearly visible and as you can see, there are none at the bottom wall. Hover your mouse over the level's canvas and press the Space bar in order to bring up the collision tileset. Select a square and draw a line where the wall is. Deleting collision blocks is just like deleting graphics. Select an area on the map (with or without holding the Shift key) where no collision blocks are present and use this selection to delete the ones that are present. Save the level and reload the game. Now try walking through the wall again; it has become quite impossible; hurray for that!
Summing up the previous process:
Select the collision layer in the Welmeister
Draw some tiles with it
Save and reload the game to see what happens if you want to walk where you drew the collision tiles
Now that we have a sense of how to build a level by adding some graphics such as grass, trees, a player, and some enemies, it is time to have a look at how levels get connected.
For this, load the inside level into the Weltmeister. The inside level is situated inside a building (didn't see that coming, did you?). As we had to do with myfirstepiclevel
, we need to change the call to the loadlevel()
function in the main.js
script, as shown in the next code snippet. This time, however, the level itself is already included in the main.require
script.
this.loadLevel(LevelInside);
Again, don't forget the capital letters.
Load both the Weltmeister and the game itself to see if everything has been set up correctly.
In the Weltmeister, have a look at the level's entities by selecting the entities layer. If you can't get a good view on the entities present in the map, feel free to turn off the other layers by clicking on their white square. Alternatively you can press the Space bar while hovering over the map to bring up the entities selection menu. As always, we have a player entity so we can move around the place, but in the menu you should notice some extra entities such as Void, Trigger, and Levelchange:
The Void entity is a rather simple one; it is nothing but a box with a name and some coordinates
The Trigger entity will trigger the code of any other entity to which it is linked if a certain type of entity (such as the player) collides with it
The LevelChange entity will make the game load another level
By intelligently combining these three entities you can connect levels, so let's do just that:
Make sure the entities layer is one of the top ones, so you can see what you add.
Start by selecting the Trigger entity and place it on the map near the door. It will be nothing but a small square at first, so make it a bit bigger so as to fit the exit. You do this by selecting the box, moving your mouse to one of its edges until you see a double arrow (double arrow symbol), and dragging it to make it bigger (the same way you would resize any window object on your PC). In choosing your size, your goal is to detect the player when he wants to use the door to get out.
Now add a Levelchange entity. If you select the Levelchange entity, you will see its properties on the right-hand side. For now, this is just its location on the map (x and y coordinates) and its dimensions, in case you reshaped the box. Give the Levelchange entity a name by typing name
in the key box and ToOutside as its value. Press the Enter key to confirm. Now you will see that the entity has an extra attribute (a name) with the value of ToOutside. Only by giving it a name can it be uniquely identified and that's what we need. We also need to tell him what level is needed to be loaded. Add the key level with a value outside and press the Enter key.
The Trigger and Levelchange entities are now both in the level but they are unaware of each other's existence yet; that's pretty important if we want them to cooperate.
Return to the trigger entity and give it a target. You do this by typing target.1
as a key and ToOutside as a value. Notice the dot (.
) after the word target
; without it, it will not work. Now press the Enter key and watch as the two pretty squares get linked to each other by a white line as shown in the next figure. The Trigger entity now knows it is the Levelchange entity; it will have to trigger when touched by a player.
Save this and load the level. Walk your player towards the trigger location; the location of your Levelchange entity is irrelevant. If everything goes well, you should now be able to move to the next level by walking up to the door!
Strangely enough, when you enter the outside world, you are not placed next to the building. That is simply odd, even for a video game. Also, there is no way to go back inside when reaching for the door, you are stuck outside forever, or until you reload.
This is because no spawnpoint, Trigger, or Levelchange entities were added to the outside level. We will make up for that, but let's first add a spawn-point to the inside level.
To do this we will need the Void entity. Add the Void entity to the level and put it in front of the door, but past the trigger. Putting it too close to (or on top of) the trigger will cause the player to be zapped back outside. Although it's fun to make an eternal loop that zaps the player back and forth between levels, eternal loops (like dividing by zero) have a chance of destroying the world. Name the Void entity insideSpawn
. Select the Levelchange entity and add the key spawn with value OutsideSpawn.
We are done with the inside level but now need to set up the outside level as its mirror opposite. So again, add a Void, Levelchange, and Trigger entity. Name the Void entity OutsideDoor
since the Levelchange entity will look for this. Name the Levelchange entity ToInside
and target the trigger to it. Also add the Level and spawn properties to the Levelchange entity. The values for these are (as you probably guessed) Inside for the Level property and InsideDoor for the spawn property.
Save and reload the game. If everything goes well you should now be able to move between both levels like a real pro.
Summing up the complete process of connecting two levels:
Load the level inside in the Weltmeister
Add three entities to the level, Trigger, Levelchange, and Void
Give every entity a name
Make the trigger point to the Levelchange entity
Add this information to the Levelchange entity: the level it needs to load and the spawnpoint it will have to use
Save inside, load the level outside, and repeat the exercise there
Make sure both levels are saved and reload the game in the browser