Introduction to the Reactive Extensions for JavaScript – Refactoring a Game

We’ve covered a lot of ground so far in this series including a lot of the combinators as well as the integration into third party libraries.  Most of these examples have been from a green field mindset where we have an idea and then walk through how you would accomplish it using the Reactive Extensions for JavaScript (RxJS).  This time, we’re going to take a game that’s already been written and take parts of it and migrate it to use RxJS.

Before we get started, let’s get caught up to where we are today:

Refactoring Pong

During this series, I’ve been asked to take examples from other sources and show how those ideas can be expressed using RxJS.  In this series, I’m going to take an existing game from an article entitled “An Introduction to Game Programming with JavaScript” where the pong example is written in plain ol’ JavaScript and involves some events and callback scenarios.  What I will do is take this example and walk through step by step how to think differently about the application and how RxJS, with the addition of jQuery, can help.  After all, just sprinkling in a little jQuery everywhere can’t hurt, right?  Even for the simple case of adding two numbers together

Anyhow, let’s start with the beginning to understand how this application works.  We have a simple page with a paddle and a ball and a place to display the score.

<body onLoad="init()">
    <h1>Rebound</h1>
    <div id="playingArea">
        <div id="paddle">
            <img src="paddle.gif">
        </div>
        <div id="ball">
            <img src="ball.gif">
        </div>
        <div id="score">
            Score: 0
        </div>
    </div>
</body>

As you may notice, we have a body.onLoad defined which calls in init function.  This init function initializes the elements for the paddle, ball and score, registers the event handler for the keydown event and then starts the main loop.

function init(){
    //instantiate HTML object instance vars
    ball = document.getElementById('ball');
    paddle = document.getElementById('paddle');
    score = document.getElementById('score');
    //register key listener with document object
    document.onkeydown = keyListener;
    //start the game loop
    start();
}

Let’s refactor this a little bit so that we use jQuery selectors, as well as refactoring how the keydown events are handled.  You’ll notice we created a function to initialize our handlers which I’ll cover shortly.

$(document).ready(function() {

    ball = $("#ball");
    paddle = $("#paddle");
    score = $("#score");
    
    initializeHandlers();
    
    start();
}

Previously, we hooked our keydown event handlers on our document by using the keyListener function.  This function listened for either the left or right arrow and then moved the paddle appropriately by four pixels depending on the direction desired.

function keyListener(e){
  if(!e){
      //for IE
      e = window.event;
  }
  if(e.keyCode==37 && paddleLeft > 0){
      //keyCode 37 is left arrow
      paddleLeft -= 4;
      paddle.style.left = paddleLeft + 'px';
  }
  if(e.keyCode==39 && paddleLeft < 436){
      //keyCode 39 is right arrow
      paddleLeft += 4;
      paddle.style.left = paddleLeft + 'px';
  }
}

Instead, what we’re going to do is separate the concerns of each key, the left and right arrow, into different observables and then when we subscribe, we modify the location of the paddle in the appropriate direction.  To do this, we’ll get the keyDown observable by taking our document and calling toObservable.  Then for the left arrow handler, we’ll filter out all keys but the left arrow while we’re still in bounds, and then in the Subscribe, we move the paddle to the left by 4 pixels.  The same applies to the right arrow as well, but the difference is that we move the paddle to the right by 4 pixels.

function initializeHandlers() {
    var keyDown = $(document).toObservable("keydown");
    
    // Left arrow
    keyDown
        .Where(
            function(ev) { 
                return ev.keyCode===37 && paddleLeft > 0; 
            })
        .Subscribe(
            function() {
                paddleLeft -= 4;
                paddle.css("left", paddleLeft + "px");
            });

    // Right arrow
    keyDown
        .Where(
            function(ev) { 
                return ev.keyCode===39 && paddleLeft < 436; 
            })
        .Subscribe(
            function() {
                paddleLeft += 4;
                paddle.css("left", paddleLeft + "px");
            });
        
}

Next, let’s look at the overall game loop.  This sets the ball in motion so that we can react with our paddle to ensure that we hit it and continue the game until we miss and the game ends.  This function first detects if there are collisions, then renders our movement and then increases the difficulty of the game (hint: the ball moves faster).  If by the end of this, the ball is still in play, then we wait for 50 milliseconds and then start the loop all over again, else we say that the game is over.

function start() {
    //game loop
    detectCollisions();
    render();
    difficulty();
    
    //end conditions
    if(ballTop < 470){
        //still in play - keep the loop going
        timer = setTimeout('start()',50);
    }
    else {
        gameOver();
    }
}

Now when it comes to porting this to the Reactive Extensions for JavaScript, we have to turn the problem on its ear a little bit.  What are we doing anyways?  At specified intervals, we’re doing a certain action which modifies our environment while our ball is in play and if it somehow isn’t, we end the game.  And just as easily as I described it above, we can write this in RxJS.  First, we start out at an interval of 50 milliseconds by calling Rx.Observable.Interval, and at each interval, we want to perform some actions, like detecting collisions and so forth that we did above.  We can accomplish that with the Do function which handles those three function calls.  Now we need to check if we’re still in bounds, so we can use the TakeWhile function to specify what is in bounds, and in this case, the top of the ball is less than 470.  And finally, once our TakeWhile no longer returns true, we’ll want to end the game, and we can do that by using the Finally function to invoke the gameOver function.

function start() {
    //start the game loop
  Rx.Observable.Interval(50)
      .Do(
            function() {
              detectCollisions();
              render();
              difficulty();
          })
      .TakeWhile(function() { return ballTop < 470; })
      .Finally(function() { gameOver(); })
      .Subscribe();
}

And there you have it, we can react just the same as we did above, although we did tack on an additional 50 ms to the front end.  I’ll cover more of how we can use RxJS to break the problem into smaller building blocks and using the combinators of RxJS, put it back together.

Conclusion

What we’re able to do now is to break our problems into logical pieces and by using the Reactive Extensions for JavaScript functions, we can glue them together to create composable solutions.  At some points, it can be a little difficult, just as it was for many to move from imperative looping over collections to declarative composable actions over these collections, but the reward is just as great.

So with that, download it, and give the team feedback!

1 Comment

Comments have been disabled for this content.