PacPong – The Game

PacPong – The Game

Intro

Inspired by a T-shirt as sold by Okimono.nl, I created a playable version of the game as depicted on their T-shirt. It is a cross-over between Pac-Man and Pong. Pac-Man became 40 this year!

Game playable on: https://pacpong.okimono.nl/ and https://www.kaper.com/pacpong/, GitHub source code Repository: https://github.com/atkaper/pacpong-the-game

Disclaimer: This game was created just for fun – not meant seriously in any way. It might or might not work as good on your device as on the ones I tested with. There are no high-scores also.

Credits

How To Play

You can either play the game on a PC/Laptop in a browser, or on a mobile phone or tablet. I have tested it on Linux using Firefox and Chrome, on Samsung Galaxy S8 mobile (using Chrome), on Motorola Moto G8 Plus (Chrome), on Samsung Galaxy Tab A 10.5 (SMT-590) tablet (Chrome), on iPhone using Safari. And it has been tested on some more browsers/devices by others.

On mobile or tablet, you are supposed to play in portrait mode. You tap on the picture of the “Any” key which is shown on the screen, and tilt the phone back and forth to go up and down. It uses the built-in motion sensor for this.

The moment you tap the “Any” key, it takes the position of the phone at that time as neutral/center location. You can hit the “Any” key during play also to reset the middle position.

If you are using Safari on IOS (iPhone/iPad), you need to first give your browser permission to use the motion sensor. To do that, go to: Settings > Safari > Motion & Orientation Access (and reload the page afterwards).
iPhone also dims the screen after some time of not touching it. In that case, just tap the screen shortly during game play.

On PC/Laptop, you can play the game using your keyboard. Just hit any key on the keyboard to start. You can move the Pac-Man using the arrow keys, or using w/s/a/d for up/down/left/right. Note: when playing using a keyboard, you can also move Pac-Man left and right! Not sure if that helps in winning, but just give it a try 😉 The escape key will quit the game, as will leaving focus (switching to another tab or application) on the browse also will do. There are also two cheat (test) keys, but I’ll leave that to you to find them in the source code.

Known Issues/Defects

  • on iPhone the screen dims after a while – workaround; tap the screen when that happens.
  • on Laptop I sometimes have seen the speed of the game double – no clue why/how, could also be effect of me updating code while running it 😉 Just hit reload if that happens.
  • on PC/Laptop, if your browser window is too small (using specific aspect ratio), the game “thinks” you are running on mobile in landscape. It will rotate the screen 90 degrees to get you to go portrait again. Workaround; make browser window bigger or change aspect ration until it looks right again.
  • on PC/Laptop sometimes the sound was not working. Workaround, reload page, click on it to focus properly, and hit any key to start.

The Code Changes

I started from the original Pong game, and made these changes:

  • Colors / font / screen layout.
  • Score changed from normal font to self drawn block font, to fit PacPong style.
  • Left paddle replaced by animated pixel style Pac-Man.
  • Added left and right moves for Pac-Man.
  • Added motion control.
  • Updated sounds, and added different sounds on different occasions.
  • Added some usage statistics.
  • Added round change-over message + short pause.
  • Changed round color not to be random.
  • Added eating the ball, with a message (random text from a selection of texts).
  • Added dynamic screen resize.
  • Added bounce counter.
  • Added escape key to quit.
  • Added cheat keys to move to ball, and skip to next level.
  • Added detection for leaving the game to stop it.
  • Added iPhone/Safari tweaks (sound/motion).
  • Attempted to add portrait/landscape detection to suggest mobile users to use portrait. Not 100% perfect.
  • Inlined CSS/JS in the HTML page for faster load (and prevents mobile file cache on code changes).
  • And perhaps some other small changes…

As this was just for fun, I did not really keep the code in the nice structure as the original. I did add some separate functions and variables outside of the OO-Styled code. Sorry 😉

Some code snippets. For full code, see GitHub Source Code, where the script is in the game.js file.

// example code fragment on drawing score digits:
// If you look from far away, you recognize the 0 1 2 3 4 5 .. 9.
// Each # is a pixel.
digit[0] = "###   # ### ### # # ### ### ### ### ###";
digit[1] = "# #   #   #   # # # #   #     # # # # #";
digit[2] = "# #   # ### ### ### ### ###   # ### ###";
digit[3] = "# #   # #     #   #   # # #   # # #   #";
digit[4] = "###   # ### ###   # ### ###   # ### ###";
// See "printscore: function" on how it is rendered.
// It draws blocks of 18x18 pixels.

// example code fragment on drawing pac-man:
// Each # is a pixel. There are 5 of these images.
pac[0][0] = "    #####    ";
pac[0][1] = "  #########  ";
pac[0][2] = " ########### ";
pac[0][3] = "   ######### ";
pac[0][4] = "     ########";
pac[0][5] = "       ######";
pac[0][6] = "        #####";
pac[0][7] = "       ######";
pac[0][8] = "     ########";
pac[0][9] = "   ######### ";
pac[0][10] = " ########### ";
pac[0][11] = "  #########  ";
pac[0][12] = "    #####    ";
// See "drawpacman: function" on how it is rendered.
// It rotates the image array in the proper direction of your move
// in case you play using the keyboard.

// Motion sensor handling:
window.addEventListener("deviceorientation", handleOrientation, true);
// See function "function handleOrientation(event)" for details.
// It shows the mobile start button, if motion events come in.
// It reads tilt sensor data, and computes Pac-Man Y position.
// The center position is stored when clicking the start button.

For rendering the HTML, I used a PHP file to include the CSS and JS dynamically, like this:

...
<style>
   <?php echo file_get_contents("game.css"); ?>
</style>
...
<script>
   <?php echo file_get_contents("game.js"); ?>
</script>

If you want to run this yourself, without php, you can use these lines in the HTML page also, instead of the above ones:

...
<link rel="stylesheet" type="text/css" href="game.css?v=1593465914" media="all"/>
...
<script type='text/javascript' src='game.js?v=1593465914'></script>
...

Where I did replace the number behind the ?v= on each edit (to bust the cache) using a linux shell script to put the current date/time in seconds in there, like this:

sed -i -E "s/\?v=[0-9]+/?v=`date +%s`/g" index.html

The Mobile Quirks And fixes

Motion sensor:

As there is no keyboard visible on the mobile phone or tablet in a normal web page, I wanted to use the motion sensor to control the game. Easy enough – just add:

window.addEventListener("deviceorientation", ...);

To start listening on motion sensor events. And it worked quite nice on my android phone and tablet!

Until it was tried on an iPhone in Safari. It just would not work… Talking to Maarten, and a bit of googling showed that an iPhone user needs to explicitly enable the motion sensor somewhere in the Safari Phone Settings. Unfortunately there is no way (yet) to trigger a permission question for this. You have to instruct the user to change it for you. To help in this, I added a bit of device/browser detection to find the combination which needs to show the user the explanation:

var isSafariIOS = (!!navigator.userAgent.match(/Version\/[\d\.]+.*Safari/)) && (/iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream);

This one combined with the not working or working motion sensor data, was used in a condition to show or hide a help-text popup. Problem solved.

Sound Effects

A game is always better with sound effects! And… the original Pong game did have some of those. So I just searched for some Pac-Man sounds, and put them in there. Worked great when using PC/Laptop. But… on iPhone/Safari (mobile) not so much. Fully silent. Some more Maarten & Google later, a solution was found. It appears that you can only play a sound, if it was initially triggered by a user click on screen. But we only have one click to start the game, and afterwards all sounds are based on events happening in the game. Workaround; when clicking start game, we start playing ALL the sounds. But immediately pause them, and set play index back to zero. So you don’t hear a thing, but afterwards the game is free to play all of those sounds without any further user clicks! 😉 Nice! Problem solved. Example code:

var chomp = new Audio("pacman_chomp.wav");

// the startMobile function is connected to the game start button
function startMobile() {
    chomp.play(); chomp.pause(); chomp.currentTime = 0;
    ...
}

Screen Dimming

Again the iPhone is behaving a bit nasty… When you do not touch the phone for a while (while playing the PacPong game), it starts to dim the screen. To get out of that, you have to touch the screen shortly. I have not tried waiting a bit more, but I assume that if you do not touch the screen, it will shut down / lock the phone.

For this issue I have not tried implementing a solution yet. I did find a library which says it can fix it, but that one was quite big, and I wanted to keep this game code small.

The idea I have to tackle this issue, is to start playing a silent video in 1 by 1 pixel frame, or hidden outside of the display area. In the hope that will keep the screen on.

But for now, I’ll leave it as is, and you just have to touch the screen every so often. Sorry. Problem not yet solved.

Conclusion

I mostly develop the back-end (server side) of websites or applications, or work on computer infrastructure and architecture. This time I tried my luck again on a bit of front-end, Java-Script, and CSS. Fun to do, fun to play, but there are quite some nasty differences between devices, browsers, and brands 😉

Happy playing! Or just buy the T-Shirt if still available (they are not always in stock, they print small batches every once in a while, or they go for short pre-order periods).

Thijs Kaper, 9th of July, 2020.

Leave a Reply

Your email address will not be published. Required fields are marked *