Saturday, December 22, 2012

Koi Live Wallpaper v1.6

  - New Feature: Shadows on fish and plants!
  - New Feature: Time of day light color
  - New Feature: Daydream support on 4.2+ devices

This updates adds shadows to all the fish, which really brings some depth to the scene in mt opinion.  As it looked odd otherwise, all the plants cast a soft shadow as well.  In addition, you can now enable time of day, and have the light color shift accordingly.

As a bonus, Daydream support is now implemented and working on any 4.2+ device!

Wednesday, December 19, 2012

Holiday Lights Live Wallpaper v2.0

  - New Feature: Simulated scrolling on some devices
  - New Feature: Daydream support on 4.2+ devices
  - Update: 2013 support for 'new york' scene

This brings Holiday Lights forward to our newest framework, and as a result simulated scrolling and Daydream support are now a reality!  In addition, you'll properly get a 2013 display when it comes to it.  :)

Monday, December 17, 2012

Jungle Waterfall Live Wallpaper v2.0

  - Update: Now using OpenGL ES 2.0
  - New Feature: Simulated scrolling on some devices
  - New Feature: Adjust individual time of day colors

This update brings Jungle Waterfall forward to our newest framework, which should give most folks a framerate boost, in addition to adding some nice bonuses like simulated scrolling, better stability and so on.  In addition, you can now individually adjust the colors for the fog based on the time of day!

Enjoy!

Wednesday, December 12, 2012

A Simple 3D Renderer in HTML5, Part 4

For the final installment, we will look at JSLint, accelerometer input on mobile devices, and audio.

(Previous entries: Part 1, Part 2, Part 3)

Code Error Checking, Standards, and Obfuscation

As we continued the project, we realized we needed to have at least some validation for our code so it wouldn't take so long to debug problems that turned out to be simple syntax errors or otherwise mis-typed values.  The best method we found for this was JSLint:

http://www.jslint.com/

Not only would this check our code, but it also introduced a lot of useful standards to keep anyone programming Javascript from getting themselves in trouble accidentally.  For our project's purposes, though, there are a few standard JSLint checks we decided to ignore:

  • Tolerate messy whitespace (white): This puts a lot of strictures on code formatting that don't comply with our own coding styles (most notably spaces around parenthesized expressions and line breaks before opening new curly braces).
  • Tolerate ++ and -- (plusplus): This is meant to keep developers out of trouble if they're expecting to use these operators and their side effects.  But for our purposes, we don't code with the expectation to use said side effects, and using these operators for loops is highly convenient.
  • Tolerate continue (continue): We're not quite sure why this is a standard JSLint check, but some loops are easier to write with the continue statement, so we turned this off.

JSLint has a number of quite useful standards that we revised our code to comply with:

All comparisons are done with the === and !== operators, because they force a type check as well as a value check.  If we just use the == and != operator, Javascript will try to be clever and may convert a string to its equivalent integer value or whatnot, which is likely not intended behavior.

As a nice carryover from standard Java, variables and normal functions are named with initial lowercase letters.  Only functions that are actually classes begin with uppercase letters.

All variables are defined at the beginning of a function, rather as a throwback to C.  In Javascript, variables are scoped within a function, but they are not scoped within a subset of curly braces, unlike most language derivatives of C.  So if you define a variable within a loop or conditional, it will be available for the rest of the function.  Forcing variables to be defined at the beginning of the function serves as a good reminder of this behavior.

With all of code JSLint-checked, the next trick was to be able to optimize and obfuscate the final version of the code for deployment to the web.  For this, the best recommendation was the Google Closure Compiler.

https://developers.google.com/closure/compiler/

This provides a very simple command line to put in your input file or files, and get optimized and obfuscated code out.  Plus, it will do analysis on the code and output errors and warnings.

Accelerometer Input

For this particular game, we needed to use the accelerometer to control the car when it was run on a mobile device.  There are a few different events available to get accelerometer input, but again, their availability varies depending on the device and the software version.  Android version 2.3.3 and below does not seem to support any of the accelerometer events we tried.

window.ondeviceorientation

This provides three rotation angles for the device, labeled alpha, beta, and gamma.  It's supported on all our target devices, and so we ended up using it for our control input.

window.ondevicemotion

This provides actual acceleration vector values, and depending on the device, separated gravity values.  It was not supported on all the Android devices we tested, though, as it's originally an iOS specification.

We also needed to track window.orientation, which gives values (from our tests) of -90, 0, 90, and 180.  This lets us know the screen's orientation, since the actual values returned from the accelerometer events are independent of the screen orientation.

Audio

Audio was the last major piece of the puzzle for this HTML5 experiment.  The HTML5 audio object is limited, especially on mobile devices.  On the surface, most of the usual functionality seems to be there: loading different types of files, playing them, seeking to points in them.  Looping isn't yet a first-class citizen; you have to set a delayed function call (often through setTimeout()) to restart the audio object.  A good discussion of what's available can be found here:

http://24ways.org/2010/the-state-of-html5-audio

iOS, however, throws all of this expected functionality out the window and severely limits what you can do with audio.  Apple explains their stance here:

http://developer.apple.com/library/safari/#documentation/AudioVideo/Conceptual/Using_HTML5_Audio_Video/Device-SpecificConsiderations/Device-SpecificConsiderations.html

Essentially, they don't want users accidentally overrunning their bandwidth allotments because the particular HTML5 application downloads a bunch of unexpected video and audio data.  So they don't let anything download unless it's called directly from a user click action.  So far, the only solution that got us close to normal behavior on iOS is here:

http://remysharp.com/2010/12/23/audio-sprites/

We group all of our sound effects into a single audio file that's loaded when the user clicks the "Play Game" button.  Then, when we actually want a sound effect to play, we seek the audio file to its time marker and start it playing, then set a delay timer to stop it playing after its time length has run out.  This does limit us to playing a single sound effect at a time, because iOS only allows one audio object in memory at a time.  Also, in order to get maximum performance on iOS devices, we have to encode the audio in Apple's native AIFF wrapper, using the IMA-ADPCM codec within it, since iOS devices decode that natively.  MP3 is also supported, but playback is much slower.

Ultimately, audio is a giant pain to deal with if you want your HTML5 application to run on iOS.  It slows down what is otherwise a very well-optimized implementation of HTML5.

The End

And that wraps up the series about our first foray into HTML5 development.  There are a lot of powerful things here, but also a lot of pitfalls, and a lot more platform-specific issues to work out than one might expect.

Still, there will be plenty of opportunities to use these technologies in future projects.

Saturday, December 8, 2012

Snowfall and Child's Play

One of the things we did back when we first released Snowfall was a charity drive, giving half the sales proceeds to Child's Play.  That worked out pretty awesome, and as it's that time of year again we're looking to do it again!

So, from now until the 14th, any purchases of Snowfall will have 50% of their revenue donated to Child's Play!

Friday, December 7, 2012

A Simple 3D Renderer in HTML5, Part 3

Architecting the Source Code

The next major issue to tackle was a basic architecture for the code.  Javascript is pretty liberal with global values and just running functions in place, as well as not caring whether things exist until the particular piece of code is actually executed.

http://www.wooga.com/2012/06/woogas-html5-adventure/

We spent some time reading through this excellent postmortem, as well as many of the links it provides.  Actually looking at the source code for the game provided some interesting insights for the code architecture.  The big one was to create a single global variable that would behave essentially like a namespace, though in reality is just holds a bunch of function pointers.  An entire class file would be encased in the following code:

( function()
{
    // All class code
} () /* Not only are we defining the function, but we're immediately executing it */
);


So when we actually get down to it, we are able to create individual files for each class in our code.  Each class is encapsulated in a function that's immediately run, so it effectively isolates all the local information about the class.  At the end of the function, the main class function is assigned to an appropriate member of the global namespace variable so it can be accessed anywhere.

For inheritance purposes, we still need to include the class files in a specific order (super classes before those that inherit from them).  When the class's prototype is assigned to a new instance of its superclass, that superclass function is executed immediately.  If it doesn't exist yet, the assignment won't work.

In this first go-around with Javascript classes, we ended up making all class members and functions public.  Javascript classes and inheritance are haphazard at best, and they are only able to represent public and private class members, not protected.  This made the code easier to read in many cases, but it also led to shortcutting some functionality since there was direct access to normally private or protected member variables.

A lot of information about object oriented programming in Javascript can be found here:

http://phrogz.net/js/classes/OOPinJS2.html

Later on, well after the completion of this project, we also stumbled upon this article:

http://msdn.microsoft.com/en-us/magazine/ff852808.aspx

The Game Loop and Performance

Back in the actual game, we looked at ways of creating a proper game loop.  There are a couple standard ways of doing this:

setTimeout()

This simply runs the requested function after the specified number of milliseconds have passed.  So for a game loop, you would write the loop function, and then as the last command, call setTimeout() with the loop function to run again.

setInterval()

This runs the requested function repeatedly, separated by the specified time interval in milliseconds.  This saves the trouble of needing to call the loop function again at the end, as the Javascript engine will do that itself.

We also ran across an updated way to handle the game loop, which we ended up using in the end.

http://paulirish.com/2011/requestanimationframe-for-smart-animating/

This is similar to setTimeout(), but it's more optimized for browsers that support it, especially mobile browsers.  If the browser doesn't support it, it falls back on setTimeout().

One big issue we had to contend with - and that we never fully solved - is that even with the best of intentions and optimizations in our code, we're still at the mercy of the particular Javascript engine for frame updates.  Sending off any of the timing functions allows for a good chance of them running again at the desired time, but not necessarily.  Chrome and iOS Safari run quite nicely, but Firefox tends to hitch periodically, especially if you have lots of other tabs open.  We do our best to limit the maximum frame delta, but this can still cause problems later (check out the upcoming audio post).

We do use one big trick to try to squeeze more performance out of the browsers that support it:

http://developers.facebook.com/html5/blog/post/2012/04/17/making-a-speedy-html5-game/

Specifically, wrap the overall HTML document object hierarchy with a 3D CSS3 transformation that does nothing (like scale3d: 1, 1, 1).  This will request that some browsers (specifically mobile ones) attempt to use their graphics chip to speed up rendering.

Coming up, JSLint, mobile device accelerometer support, and audio.

Aquarium Live Wallpaper v3.1

  - New Feature: Daydream support on 4.2+ devices

Again, primarily a maintenance release.  As promised, Daydream support is included and works well as a nightstand decoration.  :)

(Google Play Link)

Thunderstorm Live Wallpaper v2.15

  -New Feature: Daydream support on 4.2+ devices

This is primarily a maintenance update that also adds Daydream support.  Enjoy!

(Google Play Link)

Thursday, December 6, 2012

Prismatic Live Wallpaper v1.25

  - New Feature: Random theme!

This updates adds a new feature requested by a user -- the ability to have the theme change randomly.  This will select a new theme (at random) once per hour, with the theme changing when the wallpaper becomes active after having been off-screen.

(Google Play Link)

Monday, December 3, 2012

Clock Tower Live Wallpaper v1.15

  - New Feature: Time of day shifting!
  - New Feature: Disable the central axel

Several folks have asked for the ability to disable the central axel piece that turns the hands, and with this update they're free to do just that.  Head to the settings screen and remove the obstruction!

In addition, this update also supports having the light color and intensity shift based on the current time of day.  This looks quite good in my opinion and fits in very well with the overall tone of the scene.  :)

(Google Play Link)