Solving Aspect Ratio Problems For Good!!
The Problem: Inconsistent Gameplay Across Devices
BallBuilder is a physics puzzle game. Levels contain various components—triangles, rectangles, fans, portals—that players interact with to guide a ball to the goal. Consistency matters. A puzzle that's solvable on one device must be solvable on another, using the same approach.
Early in development, I was building on an iPhone. When I used Xcode's device emulator to see how the game looked on an iPad, I was immediately disappointed. The layouts were wrong. Components appeared in different relative positions. The carefully crafted levels didn't translate.
This was a fundamental problem that I needed to solve.
First Attempt: Percentage-Based Positioning
My initial approach used fixed pixel coordinates. This was obviously wrong—devices have different pixel counts, so a component placed at pixel 100 would appear in completely different locations across devices.
The first real improvement was switching to percentage-based positioning. Instead of "place this at pixel 100," I used "place this at 5% of the screen width." This way, regardless of the device's pixel count, components would occupy the same relative position.
This helped, but it didn't solve everything.
The Rotation Problem
Percentage-based positioning works well for non-rotated objects. A rectangle that's 5% of the screen width takes up the same proportional space on any device.
The problem emerges with rotation. Take an object that's 5% of screen width on a wide device (like an iPad). Rotate it 90 degrees, and it now occupies vertical space proportional to that 5% of width. On a narrower, taller device (like an iPhone), that same 5% of width is a smaller absolute value, so the rotated object occupies less vertical space.
The same level, with the same rotated piece, would look and play differently across devices.
Attempted Solution: Aspect-Aware Resizing
I implemented a system where objects would dynamically resize as they rotated, attempting to compensate for aspect ratio differences. As you rotated an object, it would shrink or grow to maintain consistent proportions across devices.
This mostly worked for simple cases. But it introduced new problems:
- Tall objects failed: A triangle rotating would never look identical across devices. The compensation ratio was tied to screen dimensions, creating subtle but real differences.
- Poor editing experience: Players can create their own levels in BallBuilder. Having objects resize during rotation made the level editor frustrating to use.
- Physics inconsistencies: This was perhaps the biggest issue. The physics engine behaved differently across devices.
The Physics Problem
A spring track in BallBuilder launches the ball upward with a set impulse. On an iPhone, the ball might travel upward 20% of the screen height. On an iPad, with the same impulse value, it might travel 35%.
I implemented device-specific impulse adjustments—essentially multiplying forces by different values depending on the device. This produced comparable results, but it was a hack. The experiences were close, but never identical.
Death by a Thousand Cuts
Each individual problem was manageable in isolation. But collectively, they degraded the experience:
- Layouts looked different
- Rotated objects behaved differently
- Physics produced different outcomes
- UI elements had spacing issues
- Art assets stretched incorrectly (requiring separate asset groups per aspect ratio)
The breaking point came when I discovered a level that was solvable on iPhone but not on iPad. A rotated rectangle that aligned with a triangle on one device didn't align on the other, due to the cumulative effect of aspect ratio differences. The same puzzle had different solutions depending on your hardware.
That was unacceptable for a puzzle game.
The Solution I Should Have Used From the Start
Three months earlier, when I first researched this problem, one option was letterboxing: adding black bars to force a consistent aspect ratio. I dismissed it as ugly. I wanted to use the full screen on every device.
After living with the consequences of that decision, I reconsidered. Letterboxing is a standard solution used by many games, and there's a reason for that. It guarantees consistency.
Implementation
By this point, the codebase was roughly 100,000 lines. Aspect ratio handling was scattered throughout—conditionals checking device type and adjusting behavior accordingly. Most of this logic was encapsulated in helper functions, but it still took a full day to refactor.
The process:
- Add black bars to constrain the iPad to the iPhone's aspect ratio
- Verify nothing is catastrophically broken
- Remove all device-specific conditional logic for sizing, physics, and layout
- Adjust the physics engine bounds to match the new constrained game area
- Update UI components (level preview, skill bar, etc.) that had iPad-specific adjustments
- Revisit all 20 existing levels to fix layouts that had been designed around the old compressed view
Because I had primarily developed on iPhone and only occasionally applied fixes for iPad, most of the game already looked correct once the aspect ratio was locked. The iPad-specific adjustments were the outliers that needed removal.
Results
The game now provides an identical experience on iPhone and iPad:
- Same layouts: Components appear in exactly the same positions
- Same physics: Forces produce identical results
- Same art: No stretching, no need for multiple asset groups
- Same solutions: A puzzle solved one way on iPhone is solved the same way on iPad
The zoom feature remains functional, so iPad players who want to see more detail can zoom in. The black bars are a minor visual tradeoff for a dramatically more consistent experience.
Lesson Learned
I spent months working around aspect ratio differences with increasingly complex solutions. The simple, standard approach—letterboxing—would have saved significant development time and produced a better result.
Sometimes the "ugly" solution is the correct one. Consistency matters more than using every available pixel.
Comments
Post a Comment