-
Notifications
You must be signed in to change notification settings - Fork 5
Simple Platformer
Start a new game folder with the following structure inside the Programs directory of Leikr:
SimplePlat/
../Code/SimplePlat.groovy
../Maps/map.tmx
../Sprites/Sprites.png
../program.properties
This is the default sprite file used by Leikr. It must be named Sprites.png
. For this demo, I just drew a green 8x8 box (I used GIMP - GNU Image Manipulation Program )
For the map file, I use the program Tiled Map Editor It is a very easy to use map creation tool. I then create a new tileset (file -> new -> new tileset) using Sprites.png
from the Sprites
folder.
If you don't want to download anything yet, here is the code you can use a text editor for and put into map.tmx
directly.
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.2" tiledversion="1.2.0" orientation="orthogonal" renderorder="right-down" width="30" height="20" tilewidth="8" tileheight="8" infinite="0" nextlayerid="2" nextobjectid="1">
<tileset firstgid="1" name="Sprites" tilewidth="8" tileheight="8" tilecount="256" columns="16">
<image source="../Sprites/Sprites.png" width="128" height="128"/>
</tileset>
<layer id="1" name="Tile Layer 1" width="30" height="20">
<data encoding="csv">
1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,1,
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
1,0,0,0,0,0,1,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
1,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,1,1,0,0,0,0,0,1,0,0,1,
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,
1,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,1,
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
1,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,1,
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
1,0,0,1,1,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,1,1,1,0,0,1,
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
</data>
</layer>
</map>
For the code I followed the fantastic Simple Platformer tutorial on the TIC-80 wiki. But since TIC-80 is in Lua and follows a different API for that console, some things needed to be altered to work with Leikr.
Firstly we need to set up the Boilerplate code of the game file.
class SimplePlat extends leikr.Engine{
}
There is a fair bit going on here. Our game file is in a class
block followed by the name of the game. This is the outermost block that holds everything required by the SimplePlat
game.
In order to gain access to the Leikr system and API, we need to extend
the Leikr Engine class. Instead of doing an import we can simply find it by the package name followed by the class name (in this case Engine
).
Now we have everything we need to get going.
Following the TIC-80 tutorial, our player is going to be a very simple structure. Instead of tables like Lua, we are going to use a Groovy Map
def p = [x: 120, y: 60, vx: 0, vy: 0, w: 8, h: 8]
This simple line of code defines a p
variable with a map of key value pairs. Firstly, some x and y coordinates, followed by the velocity
variables that correspond to the x and y. Then since we are just drawing a box, it is easy to add the desired width and height to the variable as well.
In Leikr, there are 3 methods that are very important for games. create, update and render
. In this tutorial we don't use update
because it is really simple and we don't have a performance hit. Typically anything that is not drawn to the screen would be inside update
.
When a game is started, we need to initialize the loading of some assets. In the case of this game, the map.tmx
file needs to be loaded on start. To do that, we need to override
the Leikr Engine create
method to make it do what we want.
void create(){loadMap("map")}
In this code, all we want to do is loadMap("map")
. Very simple.
To determine if our character is on a solid
tile, we can use a simple one line method.
def solid(x,y){
getMapTileId(floor(x/8), floor(y/8)) == 1
}
In Groovy, we are not required to assign the Type of parameters in a method. This means all we have to do is name the parameters (in this case x and y). This is a blessing and a curse, and could cause some pretty serious issues if misused at runtime (like passing in a parameter type that doesn't work with the floor()
method). But for our case, we will be fine.
getMapTileId(x,y)
requires the cell position
of a tile. Maps in Leikr are cell grid based, not based on position of the screen (like the sprites). That is why we need to divide by 8, and then round the result. I use floor(x/8)
here to shave off the remainder of the result of dividing x
by 8
(8 is the size of our tiles).
Then we want to check if the tile at that position is the ID of the solid map tile (map tiles are 1 indexed, so 1 is our first and only tile).
In groovy, the result of the last line of a method is returned by default. This means that when we are checking the ID of the tile, we will get either true
if it is 1 or false
otherwise. This is nice and simple. We could add a return
keyword for readability. But that is optional.
Since we are doing something simple, we can do it all in the render loop. To start, we want to draw our map that we loaded.
drawMap()
Well that was easy, wasn't it.
Controls are pretty simple in Leikr. Checking if a player is pressing a directional key just requires the key(name)
command.
if(key("Left")) p.vx = -1
else if (key("Right")) p.vx = 1
else p.vx = 0
if the player is pressing the key "Left" we set the x velocity to -1. If pressing "Right" we set it to 1. Otherwise if neither is pressed the x and y velocity should be 0.
To check if the player is hitting a wall or is on the ground, we simply use the solid(x,y)
method we created and check a few positions around our player character
if(solid(p.x+p.vx,p.y+p.vy) || solid(p.x+7+p.vx,p.y+p.vy) ||
solid(p.x+p.vx,p.y+7+p.vy) || solid(p.x+7+p.vx,p.y+7+p.vy)){
p.vx = 0
}
if(solid(p.x,p.y+8+p.vy) || solid(p.x+7,p.y+8+p.vy)) p.vy = 0
else p.vy=p.vy+0.2
For the falling portion, since the Leikr screen is 0 at the top, we need to add to the player's y
value to make it fall down. This is why we add 0.2
to the y velocity each time through the render()
method if the player is not on the ground.
Like the falling method, jumping is simply applying a negative value to the players y
velocity.
if(keyPress("Up")) p.vy = -3.5
Here we use the keyPress(name)
command so that the player has to press "Up" to jump multiple times, instead of key(name)
and flying off the screen! (Which is fun!)
When jumping, we don't want the player to be able to jump through solid tiles! So we better implement something for that as well
if(p.vy<0 && (solid(p.x+p.vx,p.y+p.vy) || solid(p.x+7+p.vx,p.y+p.vy))){
p.vy = 0
}
This checks, if the player's y velocity is less than 0 (so we are going up) and we hit a solid tile, then we need to set the y velocity to 0. Not too bad.
Now that we have managed all of our velocity logic and input, we need to add those values to the player's coordinate values x,y.
p.x += p.vx
p.y += p.vy
The +=
is the same as writing p.x = p.x + p.vx
but much shorter and easier to manage.
To draw our character, we want to set the drawing color (I chose a sort of blue, which is Leikr color ID 10). Then, since all I want as a character is a simple box, I use the `fillRect(c,x,y,w,h) command passing in the player character's corresponding values from our map.
fillRect(10, p.x, p.y, p.w, p.h)
Super simple! Now with Leikr booted up, just use the command run SimplePlat
and we will have our game! You can bop around the map and test it out. FUN!
This simple tutorial can be expanded in a lot of fun ways to make some pretty interesting games quickly and easily. I actually used the original TIC-80 tutorial as a sort of starting point for WizRobo.
class SimplePlat extends leikr.Engine {
def p = [x: 120, y: 100, vx: 0, vy: 0, w: 8, h: 8]
void create(){loadMap("map")}
def solid(x,y){
getMapTile(floor(x/8), floor(y/8)) == 1
}
void render(){
drawMap()
if(key("Left")) p.vx = -1
else if (key("Right")) p.vx = 1
else p.vx = 0
if(solid(p.x+p.vx,p.y+p.vy) || solid(p.x+7+p.vx,p.y+p.vy) ||
solid(p.x+p.vx,p.y+7+p.vy) || solid(p.x+7+p.vx,p.y+7+p.vy)){
p.vx = 0
}
if(solid(p.x,p.y+8+p.vy) || solid(p.x+7,p.y+8+p.vy)) p.vy = 0
else p.vy=p.vy+0.2
if(keyPress("Up")) p.vy = -3.5
if(p.vy<0 && (solid(p.x+p.vx,p.y+p.vy) || solid(p.x+7+p.vx,p.y+p.vy))){
p.vy = 0
}
p.x += p.vx
p.y += p.vy
fillRect(10, p.x, p.y, p.w, p.h)
}
}