ROBOT-SB dev blog – “pixel perfect” part 1

Welcome to the start of a dev blog for ROBOT-SB, a retro game concept I’m currently working on. It’ll be a low-resolution game, which presents some challenges, and I thought I’d start by talking about setting up the display to support it.

One of the initial challenges is getting a “pixel-perfect” display across all of the target devices, given the wide variety of display resolutions (and thus wide variety of content scaling factors, using Corona SDK’s lingo). Internally, the game should deal with low-resolution “content” dimensions which are then scaled up (and aspect-adjusted, if necessary) to fill the device pixels.

That is, I don’t want high-resolution content dimensions, faking the pixel look with “blocky” high-resolution assets. Rather, I’d like native @1x pixel-art assets like this…

…to scale up as necessary and look like this on device…

…without having to think too much about it.

Well, for starters, a “pixel-perfect” display occurs when there is a integral (integer) scaling factor (or its reciprocal, depending on how you arrange the terms) between device pixels and content coordinates. For example, imagine a 320×480 pixel device with 160×240 content, giving a 2.0 (or 0.5) scaling ratio:

At this point, a developer might be tempted to just dive right in and start tweaking their “config.lua” file (the file used by Corona SDK to set content dimensions, et al) to achieve this effect. Simply set width=160, height=240 and done, right? Well, maybe – that would at least work on an actual 320×480 device, but may not work as expected on other device resolutions or aspect ratios… yet.

Because it’s not just the scale, but also the overall alignment of those two “grids”, and letterboxing and centering and other such things can get in the way of perfect alignment. (in Corona SDK you’d see negative values for screenOriginX|Y when this occurs) So, before we jump into config.lua, let’s think about alignment by pondering a few things worth noting in the diagrams above…

Device pixels are physical things, each having a non-zero area. The coordinate of a pixel, say the pixel at [0,0], represents the entire pixel “cell”. You can speak theoretically of fractional pixel coordinates, for example when discussing aliasing effects, but they don’t actually exist. For example, there is no discrete hardware pixel at the coordinate [0.5,0.5]. The diagram above shows the device pixel coordinates at the half-pixel location, to imply that the entire hardware pixel “cell” is represented by those integer coordinates.

Content coordinates are non-physical things, mathematical abstractions derived from the underlying OpenGL view model, and represent precise zero-area locations along each axis. Fractional content coordinates do exist, unlike fractional pixel coordinates. For example, it is entirely reasonable to talk about the content coordinate [0.25,0.25], which at the scale indicated would represent the center of device pixel [0,0]. The diagram above shows the content coordinates “on” the lines because that is how they function within OpenGL.

For example, given the scale indicated above, to draw a “perfect pixel” using a rectangle (we’ll keep it simple with “pure geometry” and put off a discussion of raster images and texture filtering until later) you could do the following:


Remember that the scaling factor is 2:1, that’s why we create a rectangle with content dimensions 0.5×0.5 to get device dimensions of 1×1. Also remember that, by default in Corona SDK, display objects are positioned via their centers – content coordinate [0.75,0.75] is at the center of device pixel [1,1]. So, this will create a single “perfect pixel” at device coordinates [1,1].

Well then, what would have happened if we had instead created that rectangle centered on integer content coordinates? That’s the failure scenario, resulting in a non-pixel-perfect display, and it’s instructive to understand why:


When the device’s GPU attempts to rasterize that geometry it’s going to find that it partially overlaps four hardware pixels, and (by default) will generate a filtered image, whereby a portion of the geometry contributes to each of those four pixels.

So now let’s get away from “diagrams” and present actual on-device results. Using a config.lua where width and height are not specified will give content dimensions that match the device – and this is a great place to start experimenting because it’s “easy” to then address specific device pixels with content coordinates and test for alignment/aliasing effects.

I used this code to compare properly and improperly aligned images against the device pixel grid:

Actual device output:

And scaled up 4X (without filtering) just to see better:

An important thing to note is that simply turning off bilinear filtering is not enough to completely solve the problem (note the left side of the right-most “badSound” image). What really matters is alignment. With proper alignment (as with “goodMusic” and “goodSound”) it doesn’t matter whether you’re using nearest-neighbor or bilinear texture filtering because the image texels and device pixels match perfectly, so no filtering is even needed!

The lesson to be learned is that if you render a non-aligned “pixel” for whatever reason, then you’re going to end up with some sort of unwanted artifacts. Fortunately, now that we’re armed with a bit of understanding about how “fractional pixels” arise in the first place, we can bake the solution (or at least most of it – accounting for the hardware side of things) into our “config.lua” file. More on that next time.

Corona Quick Thought – Survey of Lua OOP Patterns

One of the issues that seems to confuse beginners is the bewildering variety of Object-Oriented Programming approaches possible with Lua. Though Lua itself has no native/intrinsic OOP facilities, it does offer all the raw features you need to implement something that acts like a traditional OOP language (to one degree or another).

As far as Lua is concerned, an “object” is merely yet another table containing methods and properties. Once this sinks in, it becomes clear that there are numerous ways you might create and populate such a table, as well as countless minor variations on each of the major strategies — thus the many and varied OOP approaches.

This is intended only to be a quick “survey” of some of the more common OOP strategies in Lua – particularly as it applies to Corona developers. It is not intended to be a “how to” (Google is your friend for that) so little explanation of the implementations will be provided. A thoughtful read and a bit of investigation should easily reveal each strategy’s “trick” though.

Implementations are provided in bare-minimum “do-it-yourself” form, but are completely self-contained and run-able as-is. Complete sample source code here.

Disclaimer: The sample code is meant for illustrative purposes only as material to accompany this “survey” — you are on your own if you insist on treating it as production code despite this disclaimer.

Those looking for a more polished and complete OOP system should look for one of the third-party libraries such as 30log or middleclass or others. Usage of any of these OOP libraries is not covered here. (though each such library likely follows one or more of these ‘patterns’ or variations thereof)

As a sample “case study”, each implementation below will consider a game scenerio, with two game “entities”: a “player” and an “enemy”, each with its own display object for visual representation, as well as some custom properties (fe name) and methods, as well as getters/setters (fe position) to show the approach for interfacing with the display object. the display object will just be a simple colored rectangle to eliminate the need for image files.

I’ll start with what I consider to be the simpler approaches, then move toward the more complex approaches.

1. Decorated Display Objects
Probably the simplest way to add custom behavior to a display object — just create a display object then add your custom properties and methods to it directly. This may tend to produce a lot of duplicated code if your objects are closely related, and doesn’t yet support the creation of multiple instances, but might be suitable if only a few unique “singletons” are needed.

2. Factory Function with Closures
This strategy typically develops out of the desire to eliminate some of the duplicated code that can occur with 1) above by supporting “instances”. The use of closures also offers an opportunity to support truly private members, though each individual instance still remains a bit “heavy” with its own unique copy of each method.

3. Factory Function with Closures and Statics
This strategy typically develops out of the desire to retain private closure members from 2) above, while reducing the “weight” of each individual instance by referencing a set of “static” function for non-closure members.

4A. Factory Function via Prototype
This strategy typically develops from prior “factory” methods just as a means to simplify populating a newly created instance with its members from a predefined prototype or “pattern”.

4B. Factory Function via Prototype (Alternate Syntax)
This strategy is equivalent to 4A above, just worded a bit differently where the prototype is declared. Those wishing to implement a “prototype” approach will often want to pass in some other “class” which serves as the “pattern”, and the syntax used to establish the “pattern” in this variation perhaps better illustrates that approach. (you may also want a “deep” copy, rather than the shallow one here – an implementation detail beyond the scope of this survey, just mentioned in passing)

5. Wrapper Class
This strategy typically develops once some need for more-elaborate inheritance is identified. At some point it may become more tedious to continue “decorating” display objects directly (as all previous methods have done), and instead treat the “view” (the display object) as separate from its “model” (the Lua class). Something along these lines is a typical first “baby step” implementation of metatable classes.

6. Metatable Class Inheritance
This strategy typically develops directly from the needs identified in 5) above. Now that a metatable-based class system is in place, it is now possible to consolidate shared functionality in a super-class and derived special-purpose subclasses from it.

7. Metatable Class Inheritence with Function Helper
This strategy typically develops out of an attempt to better “organize” the functionality of 6) above in preparation for an even more elaborate inheritance hierarchy. Some type of helper functions (as here) or a “base class” with similar functions, is used to create/extend classes instead of inline code. This helps prep for storing class names (for identification) or a reference to the superclass (for instanceOf or super calls) within this class creation “framework”. (if you’re still writing DIY code at this point, then it’s perhaps time to start seriously considering whether one of the third-party libraries mentioned up top might save you some effort)

8. Display Object Metatable Chaining
This strategy typically develops out of a need to “monkey patch” something deep within the internals of a Corona object. It is not typically seen in usage for more general-purpose OOP needs where a simpler decorator pattern would likely suffice. As such, the sample used throughout this post is really not an appropriate use for this strategy – it is provided merely to close out the metatable portion of this survey.

Conclusion
So, is this a full and complete list of all possible approaches to Lua OOP? Hah! Nope. Not even close. But it’s a pretty good start on some of the more common ‘fundamentals’ used by more elaborate approaches.

Once you grasp these ‘fundamentals’ then it becomes more obvious how you might extend them further to support other OOP concepts. (though again, as previously stated several times, there are existing third-party libraries that you might want to investigate and potentially save some development effort if you can find one that fits your needs)

I’ll suggest that there is no single best approach for all users, under all circumstances, particularly given the vast range of experience levels among Corona developers. So just select an approach that best works for you. Hopefully this survey might help a bit in weeding through those choices.

Corona Quick Thought – more on easing functions

A follow-up as prequel to the previous quick thought (regarding Robert Penner’s easing equations in Corona)

This is just intended as some casual/beginner “background material” on easing, maybe (?) helpful if you might be exploring/creating your own curves at some point.

Let’s take a step back, and ask: what IS easing? Essentially, it’s just a linear interpolation, where the interpolation factor (typically as “t”) has been “tweaked” so that it now has a non-linear response curve.

Let’s consider basic linear interpolation, which is often formulated so:

Where “a” is the initial value, “b” is the target value, and “t” is the parameter on [0..1]. When t==0, a is returned; when t==1, b is returned; when t is some intermediate value, the corresponding intermediate value is returned.

(Aside: The “(b-a)” portion is the “delta” between target and initial values. Often in animation system usage, as with Corona’s implementation, you’ll find the delta pre-calculated instead of passing the target value. Both approaches are equivalent.)

Let’s now “tweak” our lerp function to allow for easing:

So far, not very exciting. All we’ve done is add a “wrapper” around t to potentially allow for it to be altered. Our easing function will take an input value on [0..1] and return a potentially reshaped output value, again on [0..1]. Right now, all we have is a return of the original value, again producing a linear response.

Here’s the input/output curve of our current easing function:

Linear

Again, not yet very exciting. So let’s alter the easing function slightly:

Which now produces a quadratic response that looks this this:

inQuadratic

This curve, when used in an animation system, will cause slower initial movement, gradually speeding up until reaching the target at a more rapid velocity.

We can exaggerate that effect even further by simply using a cubic function (t*t*t). (or even higher order quartic, quintic, sextic/hexic, septic/heptic, etc – though there comes a point of diminishing returns for practical usage)

So far I’ve been presenting just the “in” response curve, but I’ll switch to the in/out response curves below, as I think it’s helpful to start visualizing the symmetry.

CubicSymmetry

See the code in the previous quick thought for implementation details on how the symmetry was accomplished for the in/out curve.

Essentially, the diagonal symmetry of the out curve is achieved by inverting both axes (tin and tout) using 1-easing(1-t). Then the in/out curve is achieved by scaling the in curve into the lower-left quadrant, and scaling/translating the out curve into the upper-right quadrant.

Now let’s look at the “Back” family of curves, as implemented in the previous article, which have an initial “overshoot” (or “undershoot”, depending on your perspective) resulting from differencing a polynomial of one degree from another polynomial of a different degree. That curve looks like this (when s==2.6):

CubicOvershootSymmetry

This curve is a pretty good basis for further exploration, as it has “just enough” complexity to inspire further modifications, yet not so much complexity that it hinders comprehension.

The most obvious tweak has already been addressed: alter the relative scaling between two polynomials by introducing a user-adjustable constant (“s”).

Perhaps the next most “obvious” experiment would be to change the degrees of the polynomials. For example, consider this function:

Producing this response curve:

QuadraticUndershootSymmetry

Having the animation effect of quickly moving to a bit more than halfway, then backing off a bit, then continuing on to the final value. A sort of “briefly unsure”, “moment of doubt”, “hesitant” or “reluctant” motion, if we anthropomorphize it. (as if it were saying: “Wait, did I leave the oven on at home? I’d better go back and check. No, no… I’m sure I turned it off – now I’d better hurry to make up for lost time!” :D)

You can insert that equation into the icurve() function of the prior article if you’d like to tinker further. (and, again, you can adjust the degree of overshoot by adjusting “s” to suit)

All for now.

Corona Quick Thought – a custom inOutBack easing function

I was reminded by a recent post in the forum that the calling convention for Corona’s easing functions isn’t well-documented. It’s fairly easy to “reverse-engineer” by experimenting, if you’re at all familiar with Robert Penner’s easing equations, and doing so will allow you to potentially create your own custom easing formulae.

At some point the subject should be treated with more depth, but I’m not feeling particularly ambitious today. 😀 So without further ado, here’s the aforementioned function:

Note that this function probably doesn’t exactly duplicate Corona/Penner, so I renamed it to avoid “claiming” that it did. Still, it’s mathematically very similar.

For readability, I’ve left it unoptimized to reveal its internal workings and 2t^3-t^2 origin — you can refactor and optimize further as you like. As written, it’s (intended to be) useful as a test-bed for developing new curves – just alter the icurve() function. Once you understand how all the “parts” work together, then you can inline all those unnecessary function calls.

The component in and out curves are in there as well, so if you don’t want the inOut version then just replace the call to iocurve() in the return statement with either icurve() or ocurve(). (or create separate versions of all three)

Test it like this:

Now, for the fun part: See that “s = 2.6” in there? That’s what controls “how much” overshoot occurs, and the 2.6 gives it about a 10% overshoot. If you’d like less overshoot, then reduce the value of s; if you’d like more overshoot, then increase the value of s.

Corona Quick Tip – the ternary operator re boolean parameter defaults

I saw this being discussed in Corona Geek Episode 115 and thought I’d add my two cents..

The issue comes about when passing boolean values as optional arguments to a function, and how to efficiently test if nil and provide a default value. The common pattern for defaulting other types of values is:

This is a shortcut test for nil, using the fact that nil evaluates to false in logical statements, so the expression after “or” is returned instead.

However, if you’re passing a boolean value, this test is insufficient, because a legitimately supplied “false” value will always fail that test (and thus always return the default instead).

The “flaw” is in the binary logical test – as it’s essentially testing the wrong thing. It shouldn’t be testing for an implied false, it should be testing for an explicit nil. What would be handy is a ternary operator, to directly test for nil, then selecting either the passed value or the default. Some languages like C/C++ have a true ternary operator. Lua does not, though something nearly equivalent can be crafted from slightly more complex logical expressions:

The parenthesis surrounding the nil test are optional, but I added them to more closely imply the general form of the ternary pattern: a and b or c
But it’s not a “true” ternary operator, and there are some “gotchas”, particularly if “b” evaluates to false. That’s why foo() and bar() above are not identical – the logic must be reversed if your default value is intended to be false.

But with a bit of “practice” to keep the logic straight, defaulting booleans can again become a simple “one-liner”.

For further info, see the Lua Wiki entry on this topic.

Corona Quick Thought – meta tricks, example: user data

Uh oh, you know you’re in for trouble when the first thing is a disclaimer: This is meant to demo a *technique*, not necessarily a practical application of that technique, fair?

This was motivated by a brief discussion with @PANCSoftware. It may or may not be even close to what he’s trying to achieve, but it seemed something worth demonstrating anyway.

HERE IS THE SOURCE if you’d like to follow along. I don’t plan on writing a whole lot about the code, comments are there for those interested enough to study it. (‘cuz if not interested enough to study it, then no point in me writing at length about it, right?)

What the code is intended to demonstrate is a metamethod whereby a containing class can serve as proxy for several different data categories and keep track of them all in a manner transparent to the end-user (aka developer).

(This will likely seem trivial nonsense if you’re coming here as a Lua expert. On the other hand might seem like black magic if you’re coming here as a typical Corona newbie. So the intended audience is somewhere in the middle of those two extremes.)

The scenario is a game’s user-data. Games often have two distinct categories of data:

  • Temporary/Transient “in game” data, for example current score
  • Permanent/Persistent “on disk” data, for example high score
  • (and of course there are others, like music/sound settings that might need to persist, etc)

    The technique used here is to provide a “prototype” to the class, indicating which properties are intended to be temporary and which are persistent. The class is agnostic – it doesn’t really know or care anything about your user data, other than what you “tell it” via the prototype.

    The class then hides those details from you and provides “simple” access to those properties, for example userdata.score and userdata.hiscore. Internally those are stored separately, so that when it comes time to serialize to disk, the class “knows” which properties to include.

    Take a look at the code if interested, that’s all for now. I may expand upon this in the future, maybe provide some code walk-through or etc, if seems needed.

    Corona Quick Thought – “cheap” antialiased lines

    “Cheap” as in “it will work with Starter”. 😀 (on the other hand, if you have a Pro+ subscription, then you have access to other alternatives)

    I was motivated to share this by a conversation started here, in the forum. Hope it’ll be helpful to someone somewhere.

    One of the less-than-pretty things about Corona is that vector geometry is rendered aliased (those “jaggy edges” arising from not antialiasing .. and i will not antiapologize for using double-negatives).

    But there are workarounds that might be suitable in certain instances, particularly if it’s just single line segments you’re after..

    bitmap_lines_sample_output_zoomThose are 2X pixel zoom crops from actual output, vector lines on the left, bitmap lines on the right. Which do you prefer? (or click below for full image at 1X if you’d rather)
    bitmap_lines_sample_output

    The “trick” used here is to create the line segments using pre-rendered bitmaps. Bitmaps do render antialiased due to the bilinear texture sampling that occurs.

    stroke4onmagenta
    The bitmaps are just a single-pixel-wide portion of a line with the desired stroke width. (height of the bitmap is just whatever is necessary to fit the designed stroke width) For example, at left is a 4px stroke in a 1x8px bitmap, magnified 40X, against dark magenta to reveal transparency.

    I’ll keep this short for now.. here’s a functional demo with code you can experiment with.

    Look for the function named newBitmappedLine() within, read its docs, then see how used in the demonstration code later on.

    Corona Quick Thought – performance: use native methods whenever you can

    I recently saw a Corona tutorial of the painter’s algorithm, where the author demonstrated how they had depth-sorted their sprites by screen y-coordinate in order to simulate depth in 2D.

    The effect works well, but I was surprised to see that the author had implemented their own insertion sort in pure Lua rather than rely on Lua’s built-in table.sort() (a quicksort implementation).

    (I’ll avoid mentioning where to find that tutorial, lest this seem like my intent is just to criticize that work, ‘cuz it isn’t. I’ll try to use it as a case study for a general principle, while trying to avoid nitpicking it as a specific case. Unless that author wants a link back, then sure, just let me know! Fair?)

    I won’t delve into comparing the merits of various sort algorithms (much), except to say that the choice of an insertion sort was probably a good one. If the number of sprites is relatively small, and if they remain “mostly” sorted from frame to frame, then those are nearly ideal conditions for an insertion sort.

    However, …, it’s really hard to beat native code! Especially on a mobile device. If there’s an existing library routine that already does something, you are almost always better off using the built-in rather than rolling your own with interpreted Lua. Even if the built-in isn’t quite a perfect “fit” for your usage. (only in an extreme case with a known-optimal solution might you “win” via interpreted code versus a more generic solution in native code)

    The example here is that a somewhat-less-than-optimal-quicksort in native code is going to beat the pants off (aka orders of magnitude) even a more-optimal-insertion-sort if in interpreted code. Even with a relatively small list (50-ish sprites or so?), and even if already mostly sorted. (and certainly even moreso in general cases where those ideal conditions for an insertion sort aren’t met, giving the advantage to quicksort anyway)

    You can apply this principle across the board, whenever/wherever possible. For example, it’s not uncommon to see “tedious” string parsing being done with interpreted Lua that could have been accomplished so much more efficiently via string.gsub() and/or string.find(). Granted that Lua’s pattern-matching syntax can seem a bit “obscure”, but it’s well worth the time to learn the libraries.

    And performance is not the only issue here. The Lua libraries have been battle-tested, so usually there’s no concern about the “correctness” of the implementation – you can just rely on it working. Huge time saver, why reinvent the wheel?

    Back to sorting, let’s whip up an example for those who might not be familiar with table.sort().

    table.sort() can work directly on a list of atomic values (numbers, strings), or can accept a comparison function to deal with a list of arbitrary table (aka an “object” in typical usage) values having sortable properties within. (a comparison function can also be used to handle primary-secondary type sorts on multiple properties as well)

    (and for advanced readers, note that table.sort() can operate directly on a list of tables, provided they’ve got a metamethod for the less-than operator)

    A quick silly example just to illustrate some various bits:

    Corona Quick Thought – more Windows simulator skins

    I mentioned elsewhere how you can customize the list of available devices in Corona’s Windows simulator.

    I also mentioned elsewhere some reasons why you might want to do so, for example: taking screenshots at various resolutions.

    (I suppose this mainly applies if you have a fast action -type game where taking screenshots on-device would be tricky to capture the exact moment you were after. For a slower-paced app it’s easier to just capture from device.)

    Another handy little trick is to take your screenshot at double the intended resolution, then resample it down to improve the antialiasing. The Windows simulator doesn’t do nearly as good a job rendering as an actual device, so this trick will yield far better quality screenshots. So, for example, let’s say you need 800×1280 images for Google Play, you’d run a 1600×2560 simulated device… um, except that there isn’t one… unless you do it yourself… so…

    HERE is collection of 45 “generic” android devices (that is, no frame image and not intended to directly mimic any specific real-world device, just their resolutions) in a wide range of standard resolutions.

    You probably will not want to use all of them, or your “View As” menu will become enormous! 😀 In particular, there a half-a-dozen or so very low resolution ones that are of very little use, since any phones out there with those resolutions (fe Samsung Galaxy Spica??) probably have too old an OS to run Corona apps anyway – they’re provided mainly just for completeness (and silliness).

    The most useful ones are probably 1600×2560 (WQXVGA) as discussed above, and those that match iOS resolutions, but also includes the full list of resolutions cited in the Android docs for testing.

    So pick the several that seem useful and copy them into the \Program Files (x86)\Corona Labs\Corona SDK\Resources\Skins folder. (typically, your install location may vary, and you’ll likely need administrator rights to do so) Then restart the simulator.

    Corona Quick Thought – Photoshop Icon Resizer Script

    I mentioned elsewhere my preference for Photoshop’s results (among the other methods considered) when resizing a single “master” 1024×1024 icon to all the various sizes needed for iOS/Android devices. (That is, assuming you’re a shoe-string operation and don’t have an artist on-staff to produce 20+ unique icons, hand-optimized for each resolution.)

    Here is a script that can help automate that process, There are other such scripts floating around out there if you search for them, the only thing different here is that it’s specifically for Corona use, using their file-naming, and supporting all (currently) known sizes for both iOS/Android. (and it ought to be clear from a brief look at the source where you’d modify it to add additional sizes if/when they become necessary)

    Save it somewhere locally, then File-Scripts-Browse… to it.
    It’ll ask for a single high-resolution 1024×1024 master and an output folder, then just wait while it works.

    Tested on CS2 and CS6. (ought to work on any >= CS2)