# Show your Pi Some LÖVE by Seth Kenlon The Raspberry Pi is famous for introducing kids to open source software and programming. It's an affordable, practical introduction to professional-grade computing, disguised as hackable fun. The application that's done the most to get young children started in programming has been Mitch Resnick's Scratch (which was fortunately [forked by the Pi Foundation](http://github.com/raspberrypi/scratch) when Scratch 2 switched to the non-open Adobe Air), but an inevitable question is what someone should graduate to after they've outgrown drag-and-drop programming. There are many candidates for the next level of programming after a drag-and-drop intro like Scratch. There's the excellent [PyGame](http://pygame.org), a Java subset called [Processing](http://processing.org), the [Godot](http://godotengine.org), and many others. The trick is to find an engine that is easy enough to ease the transition from the instant gratification of drag-and-drop, but complex enough that it accurately represents what professional programmers actually do all day. A particularly robust game engine is called [LÖVE](http://love2d.org). It uses the scripting language [Lua](http://lua.org), which doesn't get as much attention as Python, but is actually [heavily embedded](https://developer.valvesoftware.com/wiki/Dota_2_Workshop_Tools/Lua_Abilities_and_Modifiers) (both literally and figuratively) in the modern video game industry. It's listed as a skill by almost all the major game studios, it's an option in the proprietary Unity and Unreal game engines, and it generally pops up in all kinds of unexpected places in the real world. To make a long story short, Lua is a language worth learning, especially if you're planning on going into game development. As for as the LÖVE engine is concerned, it's about as good as the PyGame framework in terms of features, and since it has no IDE, it is very lightweight and, in a way, less complex to learn than something like Godot. Better yet, LÖVE runs on the Pi natively, but its projects can be open and run on any platform that Lua does. That includes Linux, Windows, and Mac, but also Android and even the oh-so-closed iOS. In other words, it's not a bad platform to get started in mobile development, too. ![Mr. Rescue, an open source game available on itch.io](love_rescue.png) Now that I've sold you on LÖVE as the next step up from Scratch on the Pi, let's dig in and see how it works. ## Intalling LÖVE As usual, installing LÖVE is just one easy command on the Raspberry Pi: $ sudo apt install love2d If you're running [Fedora on your Pi](https://opensource.com/article/17/3/how-install-fedora-on-raspberry-pi), use `dnf` instead: $ sudo dnf install love2d Either way, the package management system pulls in Lua, SDL, and other dependencies that LÖVE needs to run. ## Hello World There's not much to see in the LÖVE engine. It's really just a framework, meaning you can use whatever text editor you prefer. First thing to try is a "hello world" program, just to make sure it launches, and to introduce you to the basic syntax of both the Lua language and the LÖVE framework. Open a text editor and enter this text: cwide = 520 chigh = 333 love.window.setTitle(' Hello Wörld ') love.window.setMode(cwide, chigh) function love.load() love.graphics.setBackgroundColor(177, 106, 248) -- font = love.graphics.setNewFont("SkirtGirl.ttf", 72) end function love.draw() love.graphics.setColor(255, 33, 78) love.graphics.print("Hello World", cwide/4, chigh/3.33) end Save that as `main.lua`. A distributable LÖVE package is just a zip file, with the `.love` extension. The main file, which must always be named `main.lua`, must be at the top level of the zip file. Create it like this: $ zip hello.love main.lua And launch it: $ love ./hello.love ![Hello World](love_hello.png) The code is pretty easy to understand. The `cwide` and `chigh` variables are global, available to the whole script, and they set the width and height of the game world. In the first block of code (called a `function`), the background color is set, and in the second function the "hello world" gets printed to the screen. The line preceded by two dashes (--) is a comment. Including a `.ttf` in the `.love` package and uncommenting the `setNewFont` line renders text in that font face. $ zip hello.love main.lua SkirtGirl.ttf And launch it: $ love ./hello.love ## The Basics One big difference between Scratch and a professional programming language is that with Scratch, you can learn a few basic principles and then just randomly explore until you've discovered all the other features. With lower-level programming languages like Lua, the things you learn first aren't things you can just copy and paste to produce a playable game. You learn how to use the language, and then you look up functions that accomplish actions that you want your game to feature. Until you learn how the language works, it's difficult to blindly stumble your way into a working game world. Luckily, you can learn the language as you build the start of a game. Along the way, you pick up some tricks that you can reuse later to make your game work the way you want it to work. To begin with, you need to know the three main functions in the core LÖVE engine: **function love.load()** is the function that gets triggered when you launch a LÖVE game (it's called the "init" or "void setup()" function in other languages). It's used to set up the groundwork for your game world. It only gets executed once, so the only thing it contains is code that persists throughout your game. It's, more or less, the equivalent of the **When Green Flag is Clicked** block and the **Stage** script area in Scratch. **function love.update(dt)** is the function that gets constantly updated during game play. Anything in a love.update(dt) function is refreshed as the game is played. If you've played any game, whether it's Five Nights at Freddy's or Skyrim or anything else, and you've monitored your framerate (fps), everything happening as the frames tick would be in an update loop (not literally, since those games weren't in LÖVE, but something like an update function). **function love.draw()** is a function that causes the engine to instantiate the graphical components that you created in the **love.load()** function of your game. You can load a sprite or create a mountain, but if you don't draw it then it never shows up in your game. Those are the three functions that serve as the foundation for any element you create in LÖVE. You can create new functions of your own, too. First, let's explore the LÖVE framework. ## Sprites The basics of the "Hello, World" is a good starting point. The same basic three functions still serve as the basic skeleton of the application. Unlike rendering simple text on the screen, though, this time we create a sprite using a graphic from [openclipart.org](https://openclipart.org). The code examples and assets for this example are available in a [git repository](https://notabug.org/seth/lessons_love2d). Clone it for yourself if you want to follow along: git clone https://notabug.org/seth/lessons_love2d.git Most objects in LÖVE are stored in arrays. An array is a little like a list in Scratch; it's a bunch of characteristics placed into a common container. Generally, when you create a sprite in LÖVE, you create an array to hold attributes about the sprite, and then list the attributes in `love.load` that you want the object to have. In the `love.draw` function, the sprite gets drawn to the screen. fish = {} cwide = 520 chigh = 333 love.window.setMode(cwide, chigh) love.window.setTitle(' Collide ') function love.load() fish.x = 0 fish.y = 0 fish.img = love.graphics.newImage('images/fish.png') end function love.update(dt) -- this is a comment end function love.draw() love.graphics.draw(fish.img, fish.x, fish.y, 0, 1, 1, 0, 0) end The fish's natural predator is one of the most vicious creatures of the antarctic: the penguin. Create a penguin sprite in the same way as the fish sprite was created, with the addition of a `player.speed`, which is how many pixels the penguin will move once we get around to setting up player controls. fish = {} player= {} cwide = 520 chigh = 333 love.window.setMode(cwide, chigh) love.window.setTitle(' Collide ') function love.load() fish.x = 0 fish.y = 0 fish.img = love.graphics.newImage('images/fish.png') player.x = 100 player.y = 100 player.img = love.graphics.newImage('images/tux.png') player.speed = 10 end function love.update(dt) -- this is a comment end function love.draw() love.graphics.draw(fish.img, fish.x, fish.y, 0,1,1,0, 0) love.graphics.draw(player.img,player.x,player.y,0,1,1,0, 0) end To keep things tidy, place the `png` files in an `images` directory. To test the game as it is so far, zip up the `main.lua` and `png` files. $ zip game.zip main.lua -r images $ mv game.zip game.love $ love ./game.love ![Our current cast of characters.](love_sprites.png) ## Movement There are several ways to implement movement in LÖVE, including functions for mouse, joystick, and keyboard. We've already established variables for the player's `x` and `y` positions, and for how many pixels the player moves, so use an if/then statement to detect key presses, and then redefine the variable holding the position of the player sprite using simple maths: player = {} fish = {} cwide = 520 chigh = 333 love.window.setMode(cwide, chigh) love.window.setTitle(' Collide ') function love.load() fish.x = 0 fish.y = 0 fish.img = love.graphics.newImage( 'images/fish.png' ) player.x = 100 player.y = 100 player.img = love.graphics.newImage('images/tux.png') player.speed = 10 end function love.update(dt) if love.keyboard.isDown("right") then player.x = player.x+player.speed elseif love.keyboard.isDown("left") then player.x = player.x-player.speed elseif love.keyboard.isDown("up") then player.y = player.y-player.speed elseif love.keyboard.isDown("down") then player.y = player.y+player.speed end end function love.draw() love.graphics.draw(player.img,player.x,player.y,0,1,1,0, 0) love.graphics.draw(fish.img, fish.x, fish.y, 0, 1, 1, 0, 0) end Test the code to confirm that movement works as expected. Remember to re-zip all the files before testing so that you don't accidentally test the old version of the game. To make the movement make a little more sense, we can create functions to change the direction a sprite is facing depending on what key is pressed. This is the equivalent of these kinds of Scratch code blocks: ![Looking and moving in Scratch](scratch-right.png) Generate a flipped version of the player sprite using Image Magick: $ convert tux.png -flop tuxleft.png And then write a function to swap out the image. You need two functions: one to swap out the image, and another to restore it to the original: function rotate_left() player.img = love.graphics.newImage('images/tuxleft.png') end function rotate_right() player.img = love.graphics.newImage('images/tux.png' ) end Use these functions where appropriate: function love.update(dt) if love.keyboard.isDown("right") then player.x = player.x+player.speed rotate_right() elseif love.keyboard.isDown("left") then player.x = player.x-player.speed rotate_left() elseif love.keyboard.isDown("up") then player.y = player.y-player.speed elseif love.keyboard.isDown("down") then player.y = player.y+player.speed end end ## Automated movement Using the same convention of the sprite's `x` value, and a little mathematics, you can make the fish automatically move across the screen from edge to edge. This is the equivalent of doing this in Scratch: ![Automated movement in Scratch](scratch-edge.png) There is no `if on edge, bounce` in LÓVE, but by checking whether the fish's `x` value has reached the far left of the screen, which is `0` pixels, or the far right, which is the same as the width of the canvas (the `cwide` variable), you can determine whether the sprite has gone off the edge or not. If the fish is at the far left, then it has reached the edge of the screen, so you increment the fish's position forcing it to move right. If the fish is at the far right, then decrement the fish's position, making it move left. function automove(obj,x,y,ox,oy) if obj.x == cwide then local edgeright = 0 elseif obj.x == 0 then local edgeright = 1 end if edgeright == 1 then obj.x = obj.x + x else obj.x = obj.x - x end end There's a sequence of events that happens to be important in this case. In the first `if` block, you check for the value of `x` and assign a temporary (local) variable to indicate where the fish needs to go next. Then in a second, separate `if` block, you move the fish. For bonus points, try doing it all in one `if` statement and see if you can understand why it fails. For more bonus points, see if you can figure out a different way of moving the fish. To implement the fish's movement function, call the function at the bottom of the `love.update` loop: automove(fish,1,0,fish.img:getWidth(),fish.img:getHeight() ) If you test the script, you'll notice that the fish goes all the way off the screen when it hits the right edge. It's doing this because a sprite's `x` value is based on its upper left pixel. I'll leave it as an exercise for you to figure out what variable you should subtract from `cwide` when checking for the fish's position. ## Collision detection Video games are all about collisions. It's when things bump into each other, whether those things are an unfortunate hero taking a dive into a lava pit, or a bad guy getting blasted with a mana bolt, stuff is supposed to happen. Before detecting a collision, let's decide what we want to have happen when a collision happens. Since you already know how to change a sprite's appearance, we'll create two functions: one to change the fish to just the bones left over after the penguin has caught it, and the other to change the fish back to life any time the penguin isn't around. function falive() fish.img = love.graphics.newImage('images/fish.png') end function fdead() fish.img = love.graphics.newImage('images/fishbones.png') end With these functions set up, it's time to calculate collisions. In Scratch, there are code blocks to check whether two sprites are touching. ![Scratch collision](scratch-collision.png) The same concept, in principle, applies in LÖVE. There are several different ways to detect collisions, including external libraries like [HC](https://github.com/vrld/HC) and `love.physics`, but a good compromise between the two is a custom function to detect an overlap in sprite boundaries. function CheckCollision(x1,y1,w1,h1, x2,y2,w2,h2) return x1 < x2+w2 and x2 < x1+w1 and y1 < y2+h2 and y2 < y1+h1 end The math is complex, but it makes sense if you take a moment to think about it. The goal is detect whether two images are overlapping. If the images are overlapping, they can be said to have collided. Here's an illustration of two boxes *not* overlapping, with just some sample `y` values to keep things simple: ![Two boxes](overlap_no.png) Take a line from the function and crunch the numbers: y2 < y1 + h1 110 < 0 + 100 That's obviously a false statement, so the function must return `false`. In other words, the boxes have not collided. Now look at the same logic with two overlapping boxes: ![Overlapping boxes](overlap.png) y2 < y1 + h1 50 < 0 + 100 This one is clearly true. Assuming all statements in the function are also true (and they would be, had I bothered adding `x` values), then the function returns `true`. To leverage the collision check, just evaluate it with an `if` statement. You know now that if the `CheckCollision` function returns `true`, then there is a collision. The `CheckCollision` function has been written very generically, so when calling it, you need to feed it the appropriate values so it knows which object is which. Most of the values are intuitive. You need LÖVE to use the `x` and `y` positions of each object being checked for collision, and the size of the object. The only values that are special in this case is the one that gets swapped out depending on the collision state. For those, hard code the size of the dead fish rather than the live fish, or else the state of the collision will get changed in the middle of the collision detection. In fact, if you want to see it glitch, you can do it the wrong way first: if CheckCollision(fish.x,fish.y,fish.img:getWidth(),fish.img:getHeight(), player.x,player.y,player.img:getWidth(),player.img:getHeight() ) then fdead() else falive() end The right way is to get the size of the smaller sprite image. You can do this with Image Magick: $ identify images/fishbones.png images/fishbones.png PNG 150x61 [...] Then hard coded the "hot spot" with the appropriate dimensions: if CheckCollision(fish.x,fish.y,150,61, player.x,player.y,player.img:getWidth(),player.img:getHeight() ) then fdead() else falive() end The side effect of hard coding the collision detection area is that when the penguin touches just the edge of the fish, the fish doesn't get gobbled up. For more precise collision detection, explore the [HC](https://github.com/vrld/HC) library or `love.physics`. ## Final code It's not much of a game, but it demonstrates the important elements of video games: player = {} fish = {} cwide = 520 chigh = 333 love.window.setTitle(' Hello Game Wörld ') love.window.setMode(cwide, chigh) function love.load() fish.x = 0 fish.y = 0 fish.img = love.graphics.newImage( 'images/fish.png' ) player.x = 150 player.y = 150 player.img = love.graphics.newImage('images/tux.png') player.speed = 10 end function love.update(dt) if love.keyboard.isDown("right") then player.x = player.x+player.speed elseif love.keyboard.isDown("left") then player.x = player.x-player.speed elseif love.keyboard.isDown("up") then player.y = player.y-player.speed elseif love.keyboard.isDown("down") then player.y = player.y+player.speed end if CheckCollision(fish.x,fish.y,151,61, player.x,player.y,player.img:getWidth(),player.img:getHeight() ) then fdead() else falive() end automove(fish,1,0,fish.wide,fish.high) end function love.draw() love.graphics.draw(player.img,player.x,player.y,0,1,1,0, 0) love.graphics.draw(fish.img, fish.x, fish.y, 0, 1, 1, 0, 0) end function automove(obj,x,y,ox,oy) if obj.x == cwide-fish.img:getWidth() then edgeright = 0 elseif obj.x == 0 then edgeright = 1 end if edgeright == 1 then obj.x = obj.x + x else obj.x = obj.x - x end end function CheckCollision(x1,y1,w1,h1, x2,y2,w2,h2) return x1 < x2+w2 and x2 < x1+w1 and y1 < y2+h2 and y2 < y1+h1 end function rotate_left() player.img = love.graphics.newImage('images/tuxleft.png') end function rotate_right() player.img = love.graphics.newImage('images/tux.png' ) end function falive() fish.img = love.graphics.newImage('images/fish.png') end function fdead() fish.img = love.graphics.newImage('images/fishbones.png') end From here you can use the principles you've learned to create more exciting work. Collisions are the basis for most interactions in video games, whether it's to trigger a conversation with an NPC, manage combat, pick up items, set off traps, or most anything else, so if you master that, the rest is repetition and elbow grease. So go and make a video game! Share it with friends, play it on mobile, and, as always, keep leveling up.