Scaling Strategy 2: Stage Dimensions

Instead of the current screen’s resolution, you could look at the stage dimensions.

I know that the “screen resolution” method means that one inch on your screen is one inch on theirs. That can be nice to have, especially when dealing with tablets. On the other hand, it’s not so great for small screens. A one-inch-wide button on a low-end device could fill a quarter of the screen!

Plus, you can’t even get at the screen resolution in Flash, and HTML5 may or may not be able to access that information.

The “stage dimensions” method, on the other hand, works on all platforms, handles smaller screens gracefully, and is much easier to test.

Using the stage’s dimensions

Let’s say I’ve convinced you to give this method a shot. How does it work?

The first step is to take the stage dimensions and convert them into scaleX and scaleY values. Then scale all your objects using these values.

Flash defines four stage scale modes, each representing a different way to generate scaleX and scaleY. Let’s take a look.

NO_SCALE

Click and drag in this demo to get a sense of the NO_SCALE option. (Requires JavaScript.)

Somehow I doubt this is the one you’re looking for.

Implementing NO_SCALE

You already implemented it. Moving on!

EXACT_FIT

Quick: what’s the simplest way you can think of to handle scaling?

Remember, you have access to stageWidth and stageHeight, and you can set scaleX and scaleY.

If you guessed “scale x and y independently,” you’re right!

Hey, I never said it was a good way to handle scaling. I just said that it’s simple.

Implementing EXACT_FIT

That’s it!

Note: you can’t set stage.scaleX and stage.scaleY. In most cases, Lib.current works equally well, which is why I used that. (Just avoid adding children directly to the stage.)

NO_BORDER

Obviously, you don’t want to stretch your content like that. Somehow, you have to make sure that each individual object keeps the same proportions.

There are two main ways to do this, and NO_BORDER is one of them.

Implementing NO_BORDER

NO_BORDER considers the same two values as EXACT_FIT, but it picks only one. Specifically, it picks the larger one.

Imagine your stage is made wider, so that scaleX comes out to 1.55, and scaleY is still 1.

NO_BORDER will choose the larger of the two values (1.55), making the stage the right horizontal size, but too large vertically.

SHOW_ALL

If NO_BORDER tends to make objects too large, SHOW_ALL errs on the side of keeping them small.

Implementing SHOW_ALL

Like NO_BORDER, except with Math.min().

Given the choice between 1.55 and 1, SHOW_ALL will choose the smaller value. This ensures that everything will fit, though there will also be extra space.

In most apps, extra space is better than objects being cut off, making SHOW_ALL the clear winner.

Scaling Strategy 1: Pixel Density

In my previous post, I explained why the Layout library isn’t suited for high-resolution screens. The short version is, that library is designed for a constant pixel density. Any layout that looked good on a high-resolution screen would look terrible on a low-resolution one, and vice versa.

The SimpleSWFLayout sample project, as it appears on a high-resolution device.
The SimpleSWFLayout sample project, as it appears on a high-resolution device.

Here, the header is not scaled vertically, which is why it appears so short. Ideally, we’d want a way to increase its height based on the device’s resolution.

Fortunately, OpenFL provides a way to access the pixel density of the current screen: Capabilities.screenDPI. Now all we need is a layout library that supports pixel density scaling. Conveniently, I happen to have made one!

To convert SimpleSWFLayout to my library, follow these instructions.

Enabling pixel-density-based scaling

Add that somewhere, and you’re done!

Also make sure not to compile for Flash or HTML5. Capabilities.screenDPI just doesn’t work on those platforms. :/

SimpleSWFLayout with DPI scaling

Same device as before, but now I can actually see the details!

Ok, so there aren’t any details to see. But if there were details, I’d totally be able to see them!

Not-so-simple SWF Layout

OpenFL provides a sample project named SimpleSWFLayout, demonstrating how you can set up a layout that scales along with the window.

How the SimpleSWFLayout looks

Three items are being resized here:

  • The background scales to fit the screen.
  • The header scales horizontally to take up almost all of the window (except for a small margin on both sides). It doesn’t scale vertically.
  • The column scales vertically to take up most of the space below the header, and it doesn’t scale horizontally. It has a larger margin below it than above it.

All this is automatic, courtesy of the Layout library!

How it works

The Layout library uses some clever tricks to make this easier on you. But if you don’t know that these tricks exist, you can’t really take advantage of them, can you? Let’s fix that.

The project is called SimpleSWFLayout because it loads its three assets from a SWF file (layout.swf, found in the asset folder). For convenience, it also includes the layout.fla file used to generate the swf.

If you have Flash Professional, you can open up layout.fla and see that it has all three items already arranged correctly. The header already has a 12 pixel margin above it, and a 16 pixel margin on each side. The column is already placed 20 pixels below the header, 16 pixels from the left, and 54 pixels above the bottom.

So all you need to do is tell the library that you want to keep these margins constant. That’s what’s going on when the example specifies TOP, STRETCH for the header and STRETCH, LEFT for the column. (Traditionally, x comes before y, but for some reason the opposite is true here. I don’t know why.)

The base stage dimensions

If you’re paying really close attention, you may notice that the background in layout.fla is 800×600, while the SimpleSWFLayout project starts at 800×500.

Don’t worry! It doesn’t need to match.

The background is what defines the “base” stage size. The margins will be calculated based on the size of the background, and then scaled to fit the actual stage size. It’s just like if the window started at 800×600 and was promptly scaled down to 800×500.

Whenever you add a new object, it will use the base stage size to figure out what margins it needs. Even if you’ve already scaled the window, it’ll refer to the background’s original width and height.

Other alignments

The example demonstrates how to align something to the left and top. But that’s not much of a demonstration, is it? To keep an object 16 pixels away from the left edge, all the Layout library has to do is not change its x coordinate.

But you can align objects to the right, bottom, or center of the screen too. If you align to the right, the Layout library will update the x coordinate so that the object moves with the right edge of the screen. However far it started from the right, that’s how far it will be after resizing. Same with aligning to the bottom.

(Well, mostly. There’s some inconsistent behavior for small windows, but I won’t get into that.)

If you choose to align an object to the center of the screen, the Layout library will remember how far it was from the center, and keep it that far. I like to think of this as “pinning” the object to the center, because it’s like you stick a pin in the object wherever it crosses the center line. Whenever the center line moves, that point on the object follows that point on the center line. (The object doesn’t actually have to touch the center line to be “pinned,” but I still like the metaphor.)

One of my first criticisms of this library was that it didn’t allow you to place objects relative to one another, and keep them that way. But that just isn’t true.

Let’s say your main menu has three buttons right next to one another. You put all three in a line just below the center, and pin them (both horizontally and vertically). When you resize the stage, all three buttons will move by the same amount in the same direction.

In a small window. In a large window.

They’re staying in the same position relative to one another, and they’re staying just below the center. Looks good!

The problem

There’s something wrong with this implementation. Have you figured it out yet?

Hint: there are only two options for scaling. Either you keep the size constant, or you fill the stage, minus a constant value.

Take a moment to think about it.

(Or just scroll down if you aren’t in the mood for games.)

The SimpleSWFLayout sample project, rendered in an enormous window.
The SimpleSWFLayout sample project, as it appears in an extra-large window. This screenshot was taken on a Nexus 6.

That right there is a lot of empty space.

The problem is those constant values I mentioned. A constant 16-pixel margin looks good enough on screens around 800×600, but when the screen is over 2000 pixels wide, it just gets lost. And a constant width and height for the column and header respectively means they take up less and less of the screen as it gets bigger.

In short, the Layout library is not, in itself, enough for your mobile layout needs. Fortunately, I’ve created a more advanced alternative. Documentation coming soon!