How I Scale Fuzeboy Resolution on Mobile and Desktop Devices
Fuzeboy’s still in development so it’s only natural that sometimes I take time to rewrite stuff, to fix things, to experiment and so on. We try, we break, we fix, we extend, we change. We evolve.
One issue we faced from the start, is the game resolution. What we knew was that we wanted pixel perfect scaling no matter what. Remember that this game will be both for mobile and desktop.
Here’s my solution as of today.
Please note!
Though parts of this article are still valid, we’re no longer actively developing Fuzeboy for mobile. Indeed we’re targeting the PC platform.
Not only that; I also wrote about a different way to scale surfaces in such a way I can avoid Pixel Decimation and achieve near perfect results with no notable distortions.
The Ideal Game Resolution
Fuzeboy cares about its height. The width depends on the monitor aspect ratio (within reasonable limits). The ideal height of Fuzeboy is around 240px. Keep this in mind.
Pixel Perfect, Full Screen, No Black Bars (didn’t work)
This approach was the first I used. Since not every device has a vertical resolution that’s a multiple of 240px, I allowed different devices to show more or less “game world”.
First I get the display vertical resolution. Then I cycle from 200px to 300px to see which one fits perfectly the device vertical resolution. If I don’t find a perfect fit, I give up and just use 240px.
Fuzeboy on Devices, method 1
Device | Resolution (vertical) | Game Vertical Resolution | Multiplier |
---|---|---|---|
Amazon Kindle Fire 7" | 1024 x 600 (600) | 300 | 2 |
Samsung S3 Mini | 800 x 480 (480) | 240 | 2 |
Asus MeMO Pad 7 | 1280 x 800 (800) | 200 | 4 |
This doesn’t work. It’s pixel perfect most of the times, there are no black bars… but you might end up seeing an unplayable 200px vertical resolution on a 7″ tablet.
Let’s have a look at an Amazon Kindle Fire 7″
There’s a huge 100px difference in height from the Asus to the Kindle. The Fire users would have been able to see 50% more game world than the Asus players. That’s wrong. Especially since Asus has a higher screen resolution than the Amazon Fire.
Pixel Perfect, Black Bars (in use as of today)
First of all I decided to restrict the range of vertical resolution of the game. Now it’s from 230px to 260px. Still a 30px difference but it’s bearable.
So I still ask for the device vertical resolution, of course. Then I check which number, from 230 to 260, fits best. By that I mean it either fits perfectly or has the lowest remainder.
This is the initialization script. It goes inside the create event of the very first object created in the game.
I also leave the view settings in the room editor as they are. Disabled. I enable them via code.
///scr_init_display() // Let's disable the drawing of the App Surface application_surface_draw_enable(false); dw = display_get_width() // Device Display Width dh = display_get_height() // Device display height ar = dw / dh // Aspect Ratio min_h = 230 // Minimum Height max_h = 260 // Maximum Height height = min_h // We start from the minimum fract = frac(dh / height) // This is the fractional part mult = floor(dh / height) // This is integer multiplier // We cycle from min_h to max_h for (var h = min_h; h < max_h + 1; h++) { var new_fract = frac(dh / h) var new_mult = floor(dh / h) // If we have a lower remainder, we store // the multiplier and the height we're testing if new_fract < fract { fract = new_fract height = h mult = new_mult } } // This will show you the found resolution in the debug console show_debug_message("Found resolution: " + string(height)) // Width gets decided with a simple division width = floor(dw / mult) // And made divisible by 2 if width mod 2 != 0 width-- /*************************************************** SET THE VIEW AND THE PORT FOR ALL ROOMS ***************************************************/ var i = true; var rm = room_next(room); while (i) { room_set_view(rm, 0, true, 0, 0, width, height, 0, 0, width * mult, height * mult, 0, 0, -1, -1, -1) room_set_view_enabled(rm, true) if (rm == room_last) i = false else rm = room_next(rm) } // Resize the application surface surface_resize(application_surface, width, height); // Let the GUI layer be as big as the device screen display_set_gui_size(dw, dh) gw = display_get_gui_width() // GUI width variable gh = display_get_gui_height() // GUI height variable // We'll need these to figure out the touch commands coordinates wscale = width / (dw / mult) hscale = height / (dh / mult) // Let's figure out the App Surface offset (we want it centered) Xoffset = floor((dw - (width * mult)) / 2); // Horizontal Offset Yoffset = floor((dh - (height * mult)) / 2); // Vertical Offset /// Go Fullscreen on desktop if os_type == os_windows { window_set_fullscreen(true) }
Then we need to draw the App Surface. So in the game controller object, I have the following code in a Post Draw event.
///Draw the App Surface with correct offset // This line prevents strange artifacts in Fuzeboy. draw_enable_alphablend(false); // The real drawing. draw_surface_ext(application_surface, Xoffset, Yoffset, mult, mult, 0, c_white, 1);
If I run the game on the Amazon tablet, the result is this:
Fuzeboy on Devices, method 2
Device | Resolution (vertical) | Game Vertical Resolution | Multiplier | Black bars top/bottom |
---|---|---|---|---|
Amazon Kindle Fire 7" | 1024 x 600 (600) | 260 | 2 | 20px |
Samsung S3 Mini | 800 x 480 (480) | 240 | 2 | 0 |
Asus MeMO Pad 7 | 1280 x 800 (800) | 260 | 3 | 10px |
Now the game scales much better.
Let’s fix the Touch Controls.
Those touch buttons share the obj_touch parent so I can do this in the controller Begin Step event.
// Add these in the display init script xoffsetmult = (Xoffset / mult) yoffsetmult = (Yoffset / mult)
// Touch Controls for (var dev = 0; dev < 4; dev++) { touch_dev = dev var _xpos = (device_mouse_x_to_gui(dev) / mult) + view_xview - xoffsetmult var _ypos = (device_mouse_y_to_gui(dev) / mult) + view_yview - yoffsetmult var this_button = instance_position(_xpos, _ypos, obj_touch); if this_button != noone { if device_mouse_check_button(dev, mb_left) { with(this_button) { touch_press_action() } } if device_mouse_check_button_pressed(dev, mb_left) { with(this_button) { touch_pressed_action() } } if device_mouse_check_button_released(dev, mb_left) { with(this_button) { touch_released_action() } } } }
The Future
As of today, I still experiment with the resolution of the game. I haven’t found a perfect way to scale low res pixel graphic fullscreen with no black bars and no distortion… simply because such a way doesn’t exist.
Things change frequently around here, so we’ll see if I’m going to stick with this method or not. Feel free to let me know your ideas on this matter.