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.
  1. Sjoerd Visscher spacer August 26th, 2008
    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?

    ElementA(document).next(EventA('keydown'))
    .next(movePaddle.repeat)
    .until(EventA('keyup'))
    .next(Repeat).repeat()
    .run();
    
  2. Renรฉ Ghosh spacer August 26th, 2008
    10:24 UTC

    Excellent little introduction to arrows! Nothing makes things understandable like a good example, and this one is succinct and striking.

  3. Artyom Shalkhakov spacer August 26th, 2008
    13:36 UTC

    This is pretty cool.

    It surely blows jQuery’s/Ext’s/etc. pants off. :)

  4. Reinier Lamers spacer August 28th, 2008
    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?

  5. Chris Eidhof spacer August 29th, 2008
    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!

  6. Introducing Path Projection and Arrowlets (and me) « Khoo Yit Phang October 16th, 2008
    21:57 UTC

    [...] should thank Chris Eidhof and Adam Turoff for blogging about [...]


About

Tupil creates native iOS apps for iPhone and iPad. See tupil.com for everything about us.

Recently

  • 06.21 Adding Hyphenation to NSString
  • 09.2 MoProPro: a single command to add testers to iPhone provisioning profiles
  • 04.27 Building commercial Haskell applications
  • 04.19 Running Happstack applications with FastCGI
  • 10.23 On unit testing and type checking
  • 09.2 Grid Computing using Javascript
  • 08.25 Look ma, no callbacks!
  • 07.30 Formlets in Haskell
  • 07.19 Stemming with Haskell reloaded
  • 07.14 Stemming with Haskell

@tupil on Twitter

  • @jvtjvt Goed om te horen! De features die je noemt staan ook in ons vizier. Stay tuned.
  • RT @beamer_app: Just released: Beamer 1.2, adding support for multiple Apple TVs and password protected AirPlay. Upgrade now! t.c ...
  • RT @beamer_app: Just released: Beamer 1.1. Adds support for Snow Leopard, a couple more file extensions and a nice way to enter and view ...
  • @AlexanderNL We zetten je op de lijst!
  • @AlexanderNL Graag gedaan! :) Leuk om te horen.

Tags

abstraction Apache AppleScript applicative arrows cloud computing Cocoa FastCGI formlets functional programming game github grid computing gui hackathon HAppS Happstack Haskell haskell commercial happstack Hunspell Hyphenation iPhone Javascript Justification lastfm library mailfile mashup Objective-C OS X Ruby Snowball stemmer TDD Testing TouchXML Types Typesetting upcoming

gipoco.com is neither affiliated with the authors of this page nor responsible for its contents. This is a safe-cache copy of the original web site.