{"id":760,"date":"2019-12-09T06:26:44","date_gmt":"2019-12-09T06:26:44","guid":{"rendered":"https:\/\/www.danielparente.net\/en\/2019\/12\/09\/build-a-retro-game-with-pico-8-for-raspberry-pi-the-magpi-magazine\/"},"modified":"2019-12-09T06:26:44","modified_gmt":"2019-12-09T06:26:44","slug":"build-a-retro-game-with-pico-8-for-raspberry-pi-the-magpi-magazine","status":"publish","type":"post","link":"https:\/\/www.danielparente.net\/en\/2019\/12\/09\/build-a-retro-game-with-pico-8-for-raspberry-pi-the-magpi-magazine\/","title":{"rendered":"Build a Retro game with PICO-8 for Raspberry Pi \u2014 The MagPi magazine"},"content":{"rendered":"<p> [ad_1]<br \/>\n<\/p>\n<div>\n<p>Coding in PICO-8 is done in a lightweight and easy-to-learn language called Lua. It\u2019s quick, powerful, and is by far the most popular scripting language in game development today, having been used in everything from Dark Souls to World of Warcraft. So even if you\u2019re just a little bit interested in game dev, it\u2019s a good skill to have. This tutorial will walk you through using Raspberry Pi and PICO-8 to make a simple retro space-shooter, a great foundation for things to come.<\/p>\n<p>This tutorial was written by Dan Lambton-Howard and first appeared in The MagPi issue #84. Get a <a href=\"http:\/\/magpi.cc\/subscribe\" target=\"_blank\" rel=\"noopener\">free Raspberry Pi computer<\/a> with a 12-month subscription to The MagPi.<\/p>\n<p>See also: <a href=\"https:\/\/www.raspberrypi.org\/magpi\/pico-8-raspberry-pi-starter-guide\/\" target=\"_blank\" rel=\"noopener\">PICO-8 for Raspberry Pi starter guide<\/a><\/p>\n<h2>PICO-8 for Raspberry Pi: Launch sequence initiated<\/h2>\n<p>First things first, launch PICO-8 and, from the console, hit ESC. You should now be staring at the code editor. It isn\u2019t the most beautiful text editor, but you\u2019ll sure grow to love it! We want to start with a blank slate, so if you already have a cart loaded you might need to reboot in the console. Before we start with the code, two things to note: PICO-8 doesn\u2019t use upper case letters, everything is lower case (so hands off that Caps-Lock). Secondly, similar to Python, there is no need for semicolons to end lines.<\/p>\n<p><a href=\"https:\/\/rpi-magazines.s3-eu-west-1.amazonaws.com\/magpi\/legacy-assets\/2019\/08\/Lua-code-pico8-raspberry-pi.jpg\" target=\"_blank\" rel=\"noopener\"><img decoding=\"async\" src=\"https:\/\/rpi-magazines.s3-eu-west-1.amazonaws.com\/magpi\/legacy-assets\/2019\/08\/Lua-code-pico8-raspberry-pi.jpg\" alt=\" PICO-8 has a strict limit on code complexity, great for avoiding feature creep!\"\/><\/a><\/p>\n<h3>The holy trinity of PICO-8<\/h3>\n<p>PICO-8 has three special functions that structure any PICO-8 program. The first, _init(), is run once at program startup, whilst _update() and _draw() are called 30 times a second, meaning games are 30\u2009fps by default. Define these three functions in your code, as in Figure 1. You can also give your game a title by using &#8212; to comment. We\u2019ve chosen something suitably B-movie for our retro space-shooter. Hit ESC to return to the terminal and type save yourgamename to save your cart (you should do this often), then ESC again to hop back to the code editor.<\/p>\n<h3>Ready Player One<\/h3>\n<p>No space-shooter is complete without a solitary pilot flying a super-advanced experimental warfighter. Switch to the sprite editor (using the tabs at the top right) and draw our ship. Don\u2019t worry too much about graphics as we\u2019ll be covering that in a later tutorial. Doodle a spaceship facing right in sprite slot 001. Write the following code into your _init() function to declare the player as a table: player = {x=20,y=64,sprite=1}. Tables are very useful in Lua; this one contains a reference to your player\u2019s x and y coordinates, as well as what sprite to draw.<\/p>\n<h2>Moving the player<\/h2>\n<p>Now, within the _draw() function, add\u00a0cls().\u00a0Then, on a new line, add<br \/>spr(player.sprite,player.x,player.y). This will tell PICO-8 to clear the screen each frame, then draw the player at the x and y coordinates stored in the player table. You can test this by hitting CTRL+R. You should see your little ship on the screen. Now let\u2019s get them moving! The following code placed in the _update() function should move the player when the direction keys are pressed.<\/p>\n<p><code>if btn(0) then player.x-=2 end<\/code><br \/><code>if btn(1) then player.x+=2 end<\/code><br \/><code>if btn(2) then player.y-=2 end<\/code><br \/><code>if btn(3) then player.y+=2 end<\/code><\/p>\n<h2>The enemy reveal themselves<\/h2>\n<p>But what are we fighting against? Those evil green blobs from outer space, that\u2019s who! Draw a suitably alien-looking creature in sprite slot 002. We want our enemies to be attacking in waves. You can see the full code in the source, but briefly we are declaring a new empty table in <u>init() named\u00a0enemies. Then we write a new function create<\/u>enemies() which creates a new enemy (similar to how we created the player) and then adds it to the enemies table. Lastly, a new function create_wave() spawns a number of enemies.<\/p>\n<p><a href=\"https:\/\/rpi-magazines.s3-eu-west-1.amazonaws.com\/magpi\/legacy-assets\/2019\/08\/pico8-raspberry-pi-blog.jpg\" target=\"_blank\" rel=\"noopener\"><img decoding=\"async\" src=\"https:\/\/rpi-magazines.s3-eu-west-1.amazonaws.com\/magpi\/legacy-assets\/2019\/08\/pico8-raspberry-pi-blog.jpg\" alt=\" Those blobs came from the moon! Don\u2019t worry too much about graphics at this stage\"\/><\/a><\/p>\n<h3>They\u2019re coming for us!<\/h3>\n<p>To actually draw the enemies, we need to write a for loop in <u>draw(). This loops over all the enemies in our enemies table, once per frame, and draws them on the screen. Aliens are no threat if they just sit there, so we need them to come towards the player. A simple way of doing this is to write another loop in _update()that alters each enemy\u2019s x value per frame. Now let\u2019s actually spawn some. Add create<\/u>wave(rnd(6)+5) into _init(). This will call our enemy wave function that we wrote earlier, and create five to ten aliens on startup.<\/p>\n<h3>Our pilot strikes back<\/h3>\n<p>Run your game and you should be immediately swarmed by aliens. We need some way of fighting back! Let\u2019s code some lasers. We do this in a very similar way to enemies, by declaring an empty lasers table, making a new function to create a laser, and writing a for loop to update each laser\u2019s position, and one to draw each laser (as a red rectangle). The difference is we add if btnp(4) then create_laser(player.x+5,player.y+3) end after our player movement. This creates a new laser in front of the player when they press X (or B button on a controller).<\/p>\n<h2>High-speed collision detection in PICO-8<\/h2>\n<p>You\u2019ve probably noticed that our lasers are entirely ineffective against the alien scum. That\u2019s because we haven\u2019t coded any collision detection. There are many ways to do this \u2013 entire books have been written about the topic \u2013 but let\u2019s keep things simple. We\u2019ll declare a new enemy_collision() function that checks if a point is inside an 8\u00d78 pixel square around an enemy. If so, it returns true. Next, within our enemies update loop (step\u00a06) we\u2019ll also loop through the lasers table to check collisions; if so, we delete both the laser and the enemy, destroying them both. Kerpow!<\/p>\n<p><a href=\"https:\/\/rpi-magazines.s3-eu-west-1.amazonaws.com\/magpi\/legacy-assets\/2019\/08\/pico8-raspberry-pi-game.jpg\" target=\"_blank\" rel=\"noopener\"><img decoding=\"async\" src=\"https:\/\/rpi-magazines.s3-eu-west-1.amazonaws.com\/magpi\/legacy-assets\/2019\/08\/pico8-raspberry-pi-game.jpg\" alt=\" It\u2019s amazing how quickly you can get a game up and running on PICO-8\"\/><\/a><\/p>\n<h3>Game over<\/h3>\n<p>The tides of battle have turned, but it\u2019s hardly a fair fight. Let\u2019s reuse the same collision function to check if an enemy has struck the player. Again, within the enemy update loop, we check for collision with a point in the player\u2019s ship. If we find a collision, we\u2019ll declare a new variable gameover = true (cue dramatic music). We will then wrap the player move and draw code in a conditional if not gameover then [code] end, so that the player can\u2019t keep playing, and a print statement in _draw() to really hammer the point home.<\/p>\n<h3>They just keep coming<\/h3>\n<p>So now we have our pilot, lasers, aliens, and some collisions. But let\u2019s increase the tempo and have aliens arriving in ever-increasing waves. To do this we will create a timer that increments each frame, and spawn a new wave every three seconds. Declare wavetimer = 0 and waveintensity = 5 in _init() and then, in _update(), increment the timer by one. Let\u2019s also include a conditional that spawns a new wave, and increases the intensity, when the timer hits 90 (30 frames per second \u00d7 3).<\/p>\n<h3>Space debris<\/h3>\n<p>Now we need to do a bit of tidying up. For example, those lasers you\u2019ve been firing? They don\u2019t actually stop off screen, you know. They continue forever, and will eventually start slowing down PICO-8 as it tries to process thousands of off-screen lasers. The same for aliens. To fix this, within the laser and enemy update loops, check if each is out of screen bounds (0\u2013127 for both x and y) and delete any strays. Additionally, to prevent\u00a0the player from going off screen, add player.x = mid(0,player.x,120), and the same for y, in\u00a0_update().<\/p>\n<h2>Add a high score to your PICO-8 game<\/h2>\n<p>Survival is one thing, but high scores are better. To cap this tutorial off, create a new variable score = 0 in _init() and add a new line when an alien is destroyed that adds to score. Choose whatever amount you want, but 100 sounds good, doesn\u2019t it? Adding print(&#8216;score: &#8216;..score,2,2,7) to the end of _draw() should show the score on screen. That\u2019s all for now, but we\u2019ll be looking at graphics and sound in the next few issues, as well as giving our little space-shooter some more oomph!<\/p>\n<p><a href=\"https:\/\/github.com\/danhowardgames\/Pico8RaspberryPi\" target=\"_blank\" rel=\"noopener\">Click here to download the SpaceShooter code from GitHub<\/a><\/p>\n<pre class=\"lang:default decode:true\" title=\"SpaceShooter.p8\" data-url=\"magpi.cc\/eDDppk\">--attack of the green blobs\n--by dan lambton-howard\nfunction _init() -- called once at start\n   player = {x=20, y=64, sprite=1} --player table\n   enemies = {}\n   lasers = {} \n   create_wave(rnd(6)+5) --start game with a wave\n   wavetimer = 0\n   waveintensity = 5\n   score = 0\nend\n\nfunction _update() -- called 30 times per second\n   wavetimer+=1\n   if not gameover then --only move the player if not gameover\n      if btn(0) then player.x-=2 end\n      if btn(1) then player.x+=2 end\n      if btn(2) then player.y-=2 end\n      if btn(3) then player.y+=2 end   \n      if btnp(4) then create_laser(player.x+5,player.y+3) end   \n   end\n   --stop player going off screen edges\n   player.x=mid(0,player.x,120)\n   player.y=mid(0,player.y,120)\n\n   for enemy in all(enemies) do --enemy update loop\n      enemy.x-=enemy.speed --move enemy left   \n      for laser in all(lasers) do --check collision w.laser\n         if enemy_collision(\nlaser.x,laser.y,enemy) then\n            del(enemies,enemy)\n            del(lasers,laser)\n            score+=100\n         end\n      end\n      --check collision w\/ player\n      if enemy_collision(\nplayer.x+4,player.y+4,enemy) then\n         gameover = true\n      end\n      --delete enemy if off screen\n      if enemy.x&lt;-8 then\n         del(enemies,enemy)\n      end    \n   end\n   \n   for laser in all(lasers) do --laser update loop\n      laser.x+=3 --move laser to the right\n      if laser.x&gt;130 then --delete laser if off screen\n         del(lasers,laser)\n      end\n   end\n   \n   if wavetimer==90 then --every 3 seconds spawn wave\n      create_wave(rnd(6)+waveintensity)\n      wavetimer=0 -- reset timer\n      waveintensity+=1\n   end\nend\n\nfunction _draw() --called 30 times per second\n   cls() --clear screen\n   if not gameover then\n      spr(player.sprite,player.x,player.y) --draw player\n   end\n   \n   for enemy in all(enemies) do --draw enemies\n      spr(enemy.sprite,enemy.x,enemy.y)\n   end\n   \n   for laser in all(lasers) do --draw lasers\n      rect(laser.x,laser.y,laser.x+2,laser.y+1,8)\n   end\n   \n   if gameover then --print game over to screen\n      print('game over',50,64,7)\n   end\n   print('score: '..score,2,2,7) --show score on screen\nend\n--creates an enemy at x,y with random speed 1-2\nfunction create_enemy(x,y)\n   enemy={x=x,y=y,speed=rnd(1)+1,sprite=2}\n   add(enemies,enemy)\nend\n--spawns a wave of enemies off screen\nfunction create_wave(size)\n   for i=1,size do create_enemy(256,rnd(128)) end\nend\n\nfunction create_laser(x,y)\n   laser = {x=x,y=y}   \n   add(lasers,laser)\nend\n--returns true if x,y are within a 8x8 rectangle around enemy\nfunction enemy_collision(x,y,enemy)\n   if x&gt;=enemy.x and x&lt;=enemy.x+8 and y&gt;=enemy.y and y&lt;=enemy.y+8 then\n      return true\n   end\n   return false\nend<\/pre>\n<p>\u00a0<\/p>\n<p>\u00a0<\/p>\n<\/p><\/div>\n<p>[ad_2]<br \/>\n<br \/><a href=\"https:\/\/magpi.raspberrypi.org\/articles\/build-retro-game-pico-8-raspberry-pi\" target=\"_blank\" rel=\"noopener\">Source link <\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>[ad_1] Coding in PICO-8 is done in a lightweight and easy-to-learn language called Lua. It\u2019s quick, powerful, and is by far the most popular scripting language in game development today, having been used in everything from Dark Souls to World of Warcraft. So even if you\u2019re just a little bit interested in game dev, it\u2019s [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":761,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":"","jetpack_post_was_ever_published":false},"categories":[1],"tags":[],"class_list":["post-760","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized"],"blocksy_meta":[],"jetpack_featured_media_url":"https:\/\/e928cfdc7rs.exactdn.com\/info\/uploads\/sites\/3\/2019\/12\/Build-a-Retro-game-with-PICO-8-for-Raspberry-Pi-\u2014.jpg?strip=all","jetpack_shortlink":"https:\/\/wp.me\/p2TFCd-cg","jetpack_sharing_enabled":true,"jetpack-related-posts":[],"_links":{"self":[{"href":"https:\/\/www.danielparente.net\/en\/wp-json\/wp\/v2\/posts\/760","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.danielparente.net\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.danielparente.net\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.danielparente.net\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.danielparente.net\/en\/wp-json\/wp\/v2\/comments?post=760"}],"version-history":[{"count":0,"href":"https:\/\/www.danielparente.net\/en\/wp-json\/wp\/v2\/posts\/760\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.danielparente.net\/en\/wp-json\/wp\/v2\/media\/761"}],"wp:attachment":[{"href":"https:\/\/www.danielparente.net\/en\/wp-json\/wp\/v2\/media?parent=760"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.danielparente.net\/en\/wp-json\/wp\/v2\/categories?post=760"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.danielparente.net\/en\/wp-json\/wp\/v2\/tags?post=760"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}