Look ma, no callbacks!
In this article, we will see how we can use arrows in Javascript. Arrows are a concept from functional programming, and we’ll see how they can make our life in Javascript a lot easier. Our code uses the excellent Arrowlets library. It’s still alpha code, but it’s already quite useful.
We’re going to build a small paddle game, where you can control the paddle with your keyboard. The first thing we’re going to build is the paddle. This is straightforward, if the user presses a key, we’ll move sideways, depending on the key that’s pressed. First, listening to a key press:
ElementA(document).next(EventA('keypress')) .next(movePaddle) .next(Repeat).repeat() .run();
You should read the first two lines of the code like this: if the ‘keypress’ event happens on the document, we’re going to move the paddle. This is very different from regular javascript events, because there you attach an event and do the movePaddle every time there is a keypress. However, using arrowlets, the event is only caught once. Therefore we also need to have the next line that says: do this forever. The last line actually starts the code.
The body of the movePaddle function is only two lines:
function movePaddle(event) { var offset = (event.keyCode == 37) ? -4 : (event.keyCode == 39 ? 4 : 0); paddle.style.left = Math.max(0, Math.min(paddleLeft() + offset, 230)) + "px"; }
When the user presses a button “Start” that will start the game. Before we actually start animating, we’ll have to reset the field and place the ball. The ElementA function will find an element in the DOM, and the next resetGameOver updates the status.
ElementA('start').next(EventA('click')) .next(ElementA('gameOver')).next(resetGameOver)
The function resetGameOver code looks like this:
var resetGameOver = function(el) { el.innerHTML = ''; }
Next, we’ll reset the position of the ball and tell it to start moving every 30ms:
.next(ElementA('ball')) .next(resetBall) .next(moveBall.animate(30))
The code for resetBall simply resets the position and direction of the ball:
var resetBall = function(ball) { dir = {x: 4, y: -4} startPos = (100 + Math.random() * 40) + "px"; ball.style.left = startPos; ball.style.top = "120px"; return ball; }
Note that it returns the ball, so the next function can make use of it:
var moveBall = function(ball) { var newLeft = parseInt(ball.style.left) + dir.x; var newTop = parseInt(ball.style.top) + dir.y; if(newLeft <= 0 || newLeft >= 260) dir.x = 0 - dir.x; if(newTop <= 0) dir.y = 0 - dir.y; ball.style.left = newLeft + "px"; ball.style.top = newTop + "px"; if(newTop > 240) { if(newLeft > paddleLeft() -5 && newLeft < paddleLeft() + 45) { dir.y = 0 - dir.y; return Repeat(ball); } else return Done (ball); } else return Repeat(ball); }
The first two lines calculate a new left and top. If the ball bounces against one of the side walls, we invert the x-direction. Similarly for the top. If, however, we reach the bottom of the screen, things a bit more complicated: if the ball hits the paddle, we invert the y-direction and return Repeat(ball). The Repeat means that we’re not finished with our animation. If the ball misses the paddle, we return Done(ball). This means our animation is finished an the user’s game is over.
Now we can go on with the next part of our game handling code:
.next(ElementA('gameOver')).next(gameOver) .next(Repeat).repeat()
We show the gameOver status message, and then repeat this infinitely, so the user can start a new game. And that’s it, that’s all the code. Here’s a link to the game, and also a link to the source code..
Disclaimer: The code only works in Firefox and Opera. In Safari there’s no keypress event for the arrow keys, and the arrowlets library currently doesn’t support IE.
About this entry
You’re currently reading “Look ma, no callbacks!,” an entry on Tupil Code Blog
- Published:
- Monday, August 25th, 2008 at 22:46
- Author:
- Chris Eidhof
- Category:
- Code
- Tags:
- arrows, functional programming, game, gui, Haskell, Javascript
0:51 UTC
Cool stuff. Always nice to do Haskell in JS!
If I remember correctly Safari reserves the keypress event for text input, just like IE. Simply use the keydown event, it repeats as well.
Or, even better from gameplay point of view, use keydown and keyup to move the paddle while a key is down. Then you won’t depend on the key repeat settings of the user.
Is there something like until?
10:24 UTC
Excellent little introduction to arrows! Nothing makes things understandable like a good example, and this one is succinct and striking.
13:36 UTC
This is pretty cool.
It surely blows jQuery’s/Ext’s/etc. pants off. :)
13:52 UTC
I always thought of arrows as something for people who write academic papers and not programs.
And in what way are movePaddle and moveBall not callbacks?
0:22 UTC
Reinier, you are completely right ;). It normally just takes some time before these theoretical things get a nice practical application.
movePaddle and moveBall are callbacks, but they’re not really explicit as they are with raw event handling. You don’t have to build all kinds of closures and such, but you’re completely right: in the end, you could definitely call them callbacks. Thanks for keeping us sharp!
21:57 UTC
[...] should thank Chris Eidhof and Adam Turoff for blogging about [...]