Sniff is a "Scratch-like" programming language that's designed to help Scratchers move gently from Scratch to more conventional languages. They can start writing programs, without having to learn a new language because Sniff is based on Scratch. They learn a little more about variables, compiling, syntax errors (!), and they can have fun controlling real hardware while they're doing it.

Saturday, 28 November 2015

Flappy Bird

Earlier in the week @Dr Hannah posted on twitter looking for a project for a Yr6 class. Last year she'd done the TechWillSaveUs DIY Gamer Kit, and found it hard work... To be honest I think if she could get a group of primary school kids through this at all, then they're some smart kids and they've been taught well, as the project is massively ambitious for this age group.

However looking at the kit I wondered if we could do something better in Sniff. For a start the kit is £65. While that includes some nice things like pre-cut plastic sheets to produce something pretty sweet looking, I think we could get the same functionality for about £10.

  • Arduino Uno (clone): £5
  • LED Matrix display £2
  • Buttons: £1
  • Buzzer: £0.50
  • Battery Clip: £0.50
  • IR Receiver: £0.50
  • IR Transmitter: £0.20
  • LDR: £0.10

To that we would need to add some kind of case - if you've got access to a laser cutter then you could make something really cool for a couple of pounds. Alternatively get creative and cut up some ice cream tubs. We'd also need some way of assembling the electronics. If its a permanent build you could solder it to a piece of breadboard (50p), and I'd look at using an Arduino Nano which is more suited to permanent builds than the Uno (and cheaper, but otherwise identical). For a "lego" build were I'll probably tear it apart and build something else tomorrow I'd use sensor shield (V4 - about £2).

Of course it's nice to have it all picked up with a case, but its up to you if that's worth the £55 difference.

However things really fall down when you start programming the thing in C++... This is advertised as ages 12+. OK, I'm sure you're going to tell me how you were toggling bits of raw machine code into a  Pdp11 at the age of seven, but lets be realistic... C++ is not a nice language (and I say that as a long experienced C programmer, who's payed the bills using C++ for at least some of the last 25 years).

One of the examples is a Flappy Bird tutorial, which begins "open the completed Flappy Bird" code. Continues with "change some constants", and ends with "look at the code". In C++ that's about the limit of what we can do, but lets try writing flappy bird in Sniff for Arduino.

Lets start with the hardware. We need a 8x8 LED Matrix. You can by these separately but you can get a board with a max7912 driver chip on it which makes everything nice and easy to hook up. In addition to the power (5v) you need to connect Clock to D13, Data to D11, and CS to D10 on the Arduino. We're also going to need a button. We could get some cheap/pcb mount button for a few pennies, but you can get real arcade machine buttons/switches for about £2, and as we only need 1, lets splash out (we're not going to need any of the other stuff so out build is still going to be under £10).

To wire them up connect 5V to the NO terminal, 0V to NC terminal and connect COM to D3 on the Arduino. If you get one that lights up it will have additional terminals on each side - connect 5V across these!

Now to the code. Lets start by defining all of that hardware:
make spi device
make display maxMatrix device D10
make displayX number
make displayY number
make displayColor number
make displayFlush boolean
make message string

make button digital input D3

We create an SPI bus, which is used by the maxMatrix device which we're calling "display". Then we set up the variables that we're going to use to talk to the display. Finally place the button on D3.

Now to the game code. Lets start by thinking about the variables in the game: 

make wallX number
make wallY number

make birdX number
make birdY number
make birdVy number

make gameOver boolean

The wall is going to move form right to left, so has an X position, and a Y height. The bird as an X position, and a Y position. It also has a Y velocity - how fast its falling! Finally gameOver is YES if you're dead, and NO of there's a game in progress... We'll see how we need this in a while.

We could start looking at the rest of the code from the beginning, but just like telling a story, that's not always the best place to start, so lets look at the core mechanic first:

when moveBird
.repeat until gameOver
..change birdVy by -0.005
..change birdY by birdVy
..if birdY<0.5
...set gameOver to yes
..wait 0.01 secs

The Bird has a velocity, and each time round this loop that velocity becomes more negative - this is GRAVITY. If you throw a ball up it slows down, until it stops, then it starts coming  back down. At each instant gravity is taking away some of its upward speed (even when its already moving down!). Here we take away 0.005 from the birds velocity. Similarly each time the bird moves by its velocity. We wait 1/100th of a second and do it again.  This is also a good place to check if the bird has hit the ground, in which case it's gameOver.

The next step is to make the bird flap:

when checkButton
.repeat until gameOver
..wait until  button
..set birdVy to 0.15
..wait until not button

Again we're going to keep doing this until the game ends. We wait until the button is pressed, and set the birds velocity to be 0.15 - its moving UP! You could experiment here - what if a flap changes the velocity, so if you're already falling, then it might just slow you down? We need to wait until we stop pressing the button, so we only get one flap per press.

Thats' the bird done, so now the wall:

when moveWall
.repeat until gameOver
..wait 0.2 secs
..change wallX by -1
..if wallX=0
...set wallX to pick random 8 to 10
...set wallY to pick random 1 to 4
..if wallX=birdX
...if birdY<wallY+0.5 or birdY>wallY+3.5
....set gameOver to yes

Each time around we move the wall left one step. If it reaches 0 then its offscreen, so make a new wall on the right. If the bird and the wall are in the same column, then  check that the bird's Y position is in the gap, otherwise its gameOver again.

The only real work left to do is draw everything:

when drawScreen
.set displayFlush to no
.repeat until gameOver
..set displayColor to 0
..tell display to "clear"
..set displayColor to 1
..set displayX to wallX
..set displayY to 1
..tell display to "move"
..set displayY to wallY
..tell display to "draw"
..change displayY by 4
..tell display to "move"
..set displayY to 8
..tell display to "draw"
..set displayX to 1
..set displayY to 1
..tell display to "move"
..set displayX to 8
..tell display to "draw"
..set displayX to birdX
..set displayY to birdY
..tell display to "set pixel"
..tell display to "flush"
..wait 0.05 secs

Setting displayFlush to be NO lets us draw the whole screen before making it visible, which looks nicer for games. Clear the screen to black, then draw the wall, draw the ground and finally draw the bird before flushing the whole thing out to the display.

So we just set everything in motion:

when start
..wait until button
..wait 0.1 secs
..set wallX to 9
..set wallY to 4
..set birdY to 3
..set birdY to 5
..set birdVy to 0.1
..set gameOver to no
..broadcast drawScreen
..broadcast moveBird
..broadcast moveWall
..broadcast checkButton
..wait until gameOver
..wait 0.5 secs
..set displayColor to -
..tell display to "clear"
..tell display to "flush"

We wait for the button to be pressed, set the initial positions of everything, and then kick off the 4 scripts we've already written. All we have to do now is wait for the player to kill themselves (10 seconds tops!), clear the screen and then go round again.

The Sniff version doesn't do as much as the C++ version, which keeps score and makes sounds but its about half the length (about 100 lines Vs over 200), and adding those things in is something that older kids should be able to do, and the whole thing should be pretty readable and manageable to kids at a KS3 level.

Unfortunately as stands the Sniff version of Flappy Bird won't run on a real TWSU Gamer Kit, as they use different kind of display, but if someone wants to send me one it would be simple to write a new display device for it, so you'd simply replace the maxMatrix line with twsuMatrix. Until then you'll have to build your own...

No comments:

Post a Comment