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
Comments are closed
Comments are currently closed on this entry.