Thursday, March 20, 2014

Son of CSS Shell (Game Programming)

As I get further into programming for Firefox OS, I find that I want to go back to basics from time to time. One of my favorite things is to create a shell that can be used to build games on top of. A simple run of code that has all the basics in place. Taking what I've learned, I've created a new shell for you to use for CSS games. When I say CSS games, I am thinking of what some people call CSS sprites. Essentially you move images around on the screen by taking advantage of basic properties of HTML5, CSS, and JavaScript. Not CSS3, mind you, which has become a huge ball of stuff that looks cool but doesn't hang together quite yet.

So here is what I recommend for starting out with CSS sprites. You can read more about those at these earlier posts:

Bouncing a Ball in CSS
http://firefoxosgaming.blogspot.com/2013/10/bouncing-ball-in-css-game-programming.html

Earlier CSS Shell
http://firefoxosgaming.blogspot.com/2013/11/css-shell-game-programming.html

CSS Collision Detection
http://firefoxosgaming.blogspot.com/2013/11/css-detecting-collisions-game.html

The shell has a simple bouncing ball, which is easy to see how it works, but just as easy to remove the bouncing ball part and add your own type of game.

Screen Shots

So, here's what this shell looks like, running on the ZTE Open.


Next, here is the same game running in the App Manager simulator.


And, finally, here's what it looks like in the nightly Firefox desktop browser with the debugger open and resized to a smaller-than-normal size.


The last is pretty useful if you want to instantly simulate different screen sizes and look at the debugging information. The screen is only 343x237, but the ball bounces just fine against the four edges but doesn't enter the debugger space.

The Code

Here is the code:

<!DOCTYPE HTML>
<html>
<head>
  <meta charset="UTF-8">
  <title>
    New CSS Shell
  </title>

  <style>
     
    #ball
    {
      width: 20px;
      height: 20px;
      position: absolute;
      top: 200px;
      left: 100px;
      background-image: url(ball.png);
    }

  </style>

  <div id="ball"></div>   

  <script>

    // Global variables   
    var boardWidth = window.innerWidth;
    var boardHeight = window.innerHeight;   
    var ballHor = boardWidth / 2;
    var ballVer = boardHeight /2;    
    var changeHor = 10;
    var changeVer = 10;
   
    // Log initial board height & width.
    console.log("Board width = "
      + boardWidth);
    console.log("Board height = "
      + boardHeight);
  
    // requestAnimationFrame variations
    var requestAnimationFrame =
    window.requestAnimationFrame ||
    window.mozRequestAnimationFrame ||
    window.webkitRequestAnimationFrame ||
    window.msRequestAnimationFrame;
   
    document.documentElement.
      style.overflow = 'hidden';
  
    // Load event listener
    window.addEventListener("load",
      getLoaded, false);
     
    // Resize event listener
    window.addEventListener("resize",
      resizeMe, false);

    // Get ball information.
    var myBall =
      document.querySelector("#ball");
  
    // Function called on page load.
    function getLoaded() {
   
      // Lock screen orientation to portrait.
      // Uses "moz" prefix.
      window.screen.
        mozLockOrientation("portrait"); 
      console.log("Locked to portrait");
     
      // Background color
      document.body.style.
        backgroundColor = "Peru";
  
      // Start game loop.
      gameLoop();   
      
      // Page loaded message
      console.log("The page is loaded.");
     

      }
  
    // Game loop
    function gameLoop() {
  
      // Repeat game loop infinitely.
      requestAnimationFrame(gameLoop);
  
      // Move the ball once.
      ballMove();      
      }
  

    // Calculate and move the ball. 
    function ballMove() {
  
      // Changes are calculated but do not
      // take effect until next time.     
      myBall.style.left = ballHor + "px";
      myBall.style.top = ballVer + "px";
      
      // Calculate new vertical component.
      ballVer = ballVer + changeVer;

      // Top hit, reverse direction.
      if (ballVer + changeVer < -10)
        changeVer = -changeVer;
    
      // Bottom hit, reverse direction.
      if (ballVer + changeVer >
        boardHeight - 10)
          changeVer = -changeVer;

      // Calculate new horizontal component.
      ballHor = ballHor + changeHor;

      // Left edge hit, reverse direction.
      if (ballHor + changeHor < -10)
        changeHor = -changeHor;
    
      // Right edge hit, reverse direction.
      if (ballHor + changeHor >
        boardWidth - 10)
          changeHor = -changeHor;
    }

    // Testing in desktop browser only.
    // Changes board size to match new.
    function resizeMe() {
     
      //Change board size.
      boardWidth = window.innerWidth;
      boardHeight = window.innerHeight;  
     
      // Log initial board height & width.
      console.log("Board width = "
        + boardWidth);
      console.log("Board height = "
        + boardHeight);
    }
   
  </script>
</head>

<body>
  <footer>
    <small>
      Copyright &copy; 2014 Bob Thulfram
    </small>
  </footer>
</body>
</html>


What I Have Added

After thinking about it over the past few weeks, here are a few things I've added:
  1. Screen Orientation. The game plays best in portrait mode.
  2. Responsive Design. The game should play on any size phone or tablet.
  3. More Responsive Design. I wanted to test how this looks in browsers on desktops.
  4. I wanted to add a copyright notice in the code.
  5. I felt like a background color helps the screen shot stand out in a blog post. 

Screen Orientation

I've written about Screen Orientation for Firefox OS at http://firefoxosgaming.blogspot.com/2013/11/screen-orientation-and-moz-prefixes.html. And it works! Of course, you need to test this on a real phone. When I tilt the phone with my code, the screen doesn't tilt, it stays oriented in portrait.

However, this doesn't seem to work on the App Manager simulator. There's a little button next to the home button at the bottom that looks like two folders overlapping. Click on that and the screen turns sideways. But it shouldn't! This might be a bug or it might be that this isn't supported yet in the simulator. But it works fine on a phone!

Here's the code:

      window.screen.
        mozLockOrientation("portrait"); 
      console.log("Locked to portrait");


Note that this needs the "moz" prefix for now.

Responsive Design

One thing that I've noticed on a lot of the games I've reviewed is that they don't look the same on different phones. The ZTE Open and the Geeksphone Peak have different sizes! But you want to design your game so it works on any size phone, tablet, or whatever. Maybe not watches, though.

So what I'm doing with this code is to get the actual screen size and make that the size of my game board. I wrote about this in an early topics called How Big A I? at http://firefoxosgaming.blogspot.com/2013/10/how-big-am-i-game-programming.html.

And here is how I implement it. These lines set up the game board to match the actual screen. Instead of a fixed value, I created board size by seeing what the actual screen size is. This is in the global variable section.

    var boardWidth = window.innerWidth;
    var boardHeight = window.innerHeight;   


I also added this to make sure what actually happens:

    console.log("Board width = "
      + boardWidth);
    console.log("Board height = "
      + boardHeight);


It's always a good idea to track what is happening, especially when testing different sized phones.

Really Responsive Design

I often run my code first in the latest Firefox nightly browser. I recommend this, even though you need to be careful because not everything in the desktop browser is in Firefox OS! But it makes the easy development cycle even easier and you can save the phone-specific parts for testing on an actual phone (like Screen Orientation).

I thought it would be fun to resize the browser so I can just drag it to see it small or large. And it is also easy to see the debugged output on a desktop browser.

So I added this code to handle the event of the browser being resized.

    // Resize event listener
    window.addEventListener("resize",
      resizeMe, false);


This will call the function resizeMe every time the browser is resized. I also added this code to prevent the scroll bars from trying to display when the ball bounces into the edge.

    document.documentElement.
      style.overflow = 'hidden';


And here's the function that is called when the window is resized.

    function resizeMe() {
     
      //Change board size.
      boardWidth = window.innerWidth;
      boardHeight = window.innerHeight;  
     
      // Log initial board height & width.
      console.log("Board width = "
        + boardWidth);
      console.log("Board height = "
        + boardHeight);
    }


This is the same code as was used in the global variables, but you need to call it again when the window is resized. And I like outputting the new width and height of the board so I can see what is going on. The third screen shot above shows this working beautifully.

Copyright

Not everyone cares, but I care about copyright. I'm happy to have you use my code in your own games, but I'd like to keep control over how it is published and where it appears. This doesn't have a standard yet, but a growing convention is to use the HTML5 tag small inside a footer tag. So I've added this to the code at the very end, the only thing that is in the actual body of the code.

  <footer>
    <small>
      Copyright &copy; 2014 Bob Thulfram
    </small>
  </footer>


The &copy; will produce a copyright symbol (c).

Background Color

I've used this before here and there, so here it is again.

      document.body.style.
        backgroundColor = "Peru";


I picked the color Peru for no particular reason except that's a nice country! Get your color names at https://developer.mozilla.org/en-US/docs/Web/CSS/color_value. When I did this, I realized that my bouncing ball PNG image had a white background, which didn't look good, so I had to redraw it by filling in all the white bits with Peru color. Which the MDN page will happily tell you is rgb(205, 133,  63). What this means is that the red, green, and blue percents are those three numbers. I plugged those numbers into Paint Shop Pro and the PNG now looks like this, in all its pixel-y greatness.


Wrapping Up

So this is the shell I will be using to create new CSS game bits and I recommend you use the parts you want. I'll do the same for Canvas and CSS but I might do some cool CSS stuff sooner, and there's games to review, and, and, and!