Autofit Portrait vs Landscape

Monkey Forums/Monkey Programming/Autofit Portrait vs Landscape

erebel55(Posted 2014) [#1]
-


erebel55(Posted 2014) [#2]
I am using autofit and am still trying to understand how to make my game properly fit the screen when it is in portrait and landscape modes for multiple devices.

Currently, I am modifying CONFIG.MONKEY for the glfw target to replicate this

Here are my results for portrait
#GLFW_WINDOW_WIDTH=640
#GLFW_WINDOW_HEIGHT=960



Here are my results for landscape
#GLFW_WINDOW_WIDTH=960
#GLFW_WINDOW_HEIGHT=640



I am satisfied with the results for landscape, as it is filling the entire screen. However, I would like portrait to fill the entire screen as well. The height isn't being adjusted to fill the entire screen.

Would I need to modify the tile map's width/height when the device changes to portrait? Or how would you go about doing this?

Here is my cut down code with placeholder images


Here are the placeholder images you will need
https://dl.dropboxusercontent.com/u/106998981/images.zip

My background image is 960x640 and my tiles are 32x32 (without padding)

Also, on a side note I am trying to make it so the control buttons (left/right/jump) aren't scaled with everything else. I don't think it makes since to stretch these, as your fingers don't change size. (I already opened a thread for that topic here)

I appreciate the help as always :)
Thank you!


Nobuyuki(Posted 2014) [#3]
I do believe DeviceWidth() / DeviceHeight() update when auto rotating at least on Android. Can't say about ios.

I also believe that autofit auto crops the game to the scissor rectangle specified by the screen dimensions you feed it in SetVirtualDisplay. You would probably have to change this value when you detect a screen rotation to "break out" of that box. Then, you'll have to re-adjust pretty much everything else. Or, you can kill the scissoring Autofit does and work "outside" the screen dimensions.

With AutoScale, no cropping is done without your explicit letterboxing. You can use DeviceX() / DeviceY() to specify coordinates relative to a position within the actual device dimensions, instead of the virtual screen. If you did switch, however, I can't give you in-depth tech support to solve your problem at this time. I can just tell you that it's possible, and wouldn't require hacking the module.


erebel55(Posted 2014) [#4]
Thanks for the input Nobuyuki. I took a look at Autoscale and dropped it in. It seemed to give the same results, so I assume I need to make a custom letterbox.

Yeah my first attempt at getting it to work with autofit is as follows.

I added a function to autofit to surface device_changed
Function GetDisplayChanged:Int()
	Return VirtualDisplay.Display.device_changed
End Function


With that I can check to see when the display changed and calculate the orientation (landscape or portrait) correctly.

I store two background images (one 960x640 and one 640x960)
When I detect a change in the display I change the background image to the appropriate one. That all is working fine, however I'm not sure how to go about converting my tilemap data.
Method ChangeOrientation:Void()
    ' if orientation has changed
    If GetDisplayChanged() Or currentBackgroundImage = Null
        ' landscape
        If game.screenOrientation = 1 
            currentBackgroundImage = backgroundImage[0]
            SetVirtualDisplay WIDTH, HEIGHT
        ' portrait
        Else
            currentBackgroundImage = backgroundImage[1]
            SetVirtualDisplay HEIGHT, WIDTH
        Endif
    Endif
End Method


Was hoping someone else has done this and there is a better way :/


erebel55(Posted 2014) [#5]
Has everyone else really just locked the user in one orientation mode?


ElectricBoogaloo(Posted 2014) [#6]
Link to one of my games

No, don't be silly. Give the player options, that's what they're there for.

I don't use the standard "Autofit" method. I wrote my own rescaling gubbins from scratch, and it's allowed me to do a few extra things.
The main important thing that I do, though, is that I don't confine my game to "640 x 480" or any other specifically predefined resolution.

The way I do it is approximately this...
I "suggest" an approximate width/height that I'd like to use. My default is the standard 640x480.
in OnRender, the code finds the current DisplayWidth and DisplayHeight, and figures out an approximate scale that neatly transforms my 640x480 into the size available.
It takes aspect ratios and other such things into account, and results in a very approximate new set of co-ordinates and scaling numbers to ensure my game scales into the given screensize.
An Orientation global is set to either 0 (portrait) or 1 (landscape)
Next, it divides the screen resolution by the scaler, to give me a new resolution (more globals - jscrw and jscrh) which I then make use of during my coding..
0,0 = top left
jscrw,jscrh = bottom right.
and everything else scales neatly inbetween.

You need to stop focussing on your expected co-ordinate system, and instead learn to work with a more freeform system. Although I can roughly assume there's going to be around about 640 pixels wide, if the player plays in portrait, my system instead focuses on the 480 height more than the width. I need to take things like that into consideration when designing multi-orientation games.
The game linked above is an incredibly complex one, since it scales a grid into the screen's size, and focusses the gameplay within the arena. Generally I tend to go for scrolling games, nowadays, as they're much less confined to unknown screen resolutions!

I've said it many times on this forum, and will say it again.
Make a testcard!
If you click the AGameAWeek logo on my game's main menu, you'll see a testcard pop up.
The testcard is made up of an approximate number of squares, some circles, and a bunch of differently shaded coloured blocks along the edges.
You can click on any of the resolutions on the right of the screen, and should find the testcard redefines itself to fit snuggly into whatever resolution you throw at it.
It took an alarming amount of time and effort to make the testcard work as well as it does, and as I tried various different things, I learned little skills about how to line things up, and how to keep things looking roughly "in the right place" no matter what the resolution.
It was a FANTASTIC learning experience, and I'm really glad I did it.
Give it a go.


erebel55(Posted 2014) [#7]
Well I assumed it was something that DruggedBunny or therevills have done with autofit. But if not, that would be nice to know as well. I don't really have the free time to go off and code an autofit system of my own, which is why I picked up DruggedBunny's. Please let me know.


erebel55(Posted 2014) [#8]
I am still interested in others experience with orientation and autofit. MikeHart, therevills, DruggedBunny??


erebel55(Posted 2014) [#9]
Up


therevills(Posted 2014) [#10]
I decide on the screen's orientation for the game and then code the game based on that. If you want the game to be played in landscape and portrait you would need to design your game screens to look good in both.

With AutoFit, it will add "black bars" to keep the ratio correct so I don't think AutoFit is the solution you want to use. When setting the virtual resolution within Diddy there is an extra parameter called "useAspectRatio" which uses the aspect ratio if set to true, if you set it to false the screen will use the entire device view.... but it will stretch your graphics since it does not care about the aspect ratio.

It all comes down to a game design decision.


erebel55(Posted 2014) [#11]
Thanks for the input :)

I am hoping to get rid of the blackbars and instead use the entire device view. I will look at useAspectRatio.

Edit:
Ahh just realized you said in diddy. I assume autofit doesn't do this by itself. I will take a look.


therevills(Posted 2014) [#12]
Just quickly knocked up this example:
Strict

Import mojo

Function Main:Int()
	New MyApp()
	Return True
End

Class MyApp Extends App
	Field deviceWidth:Int
	Field deviceHeight:Int
	Field screenHeight:Int
	Field screenWidth:Int
	Field screenWidthRatio:Float
	Field screenHeightRatio:Float
		
	Method OnCreate:Int()
		SetScreenSize(320, 160)
		SetUpdateRate(60)
		Return True
	End
	
	Method OnRender:Int()
		PushMatrix
			Scale screenWidthRatio, screenHeightRatio
			
			Cls(100, 100, 200)
			
			' Draw an X		
			SetColor(255, 255, 255)
			DrawLine(0, 0, screenWidth, screenHeight)
			SetColor(255, 0, 255)
			DrawLine(0, screenHeight, screenWidth, 0)
			
			' Draw Shapes
			SetColor(255, 0, 0)
			DrawRect(200, 100, 100, 100)
			SetColor(255, 255, 0)
			DrawOval(200, 300, 100, 100)
			
		PopMatrix
		
		SetAlpha(0.5)
		DrawText("Screen Width        = " + screenWidth, 10, 10)
		DrawText("Screen Height       = " + screenHeight, 10, 20)
		DrawText("Device Width        = " + deviceWidth, 10, 30)
		DrawText("Device Height       = " + deviceHeight, 10, 40)
		DrawText("Screen Width Ratio  = " + screenWidthRatio, 10, 50)
		DrawText("Screen Height Ratio = " + screenHeightRatio, 10, 60)
		
		DrawText("MouseX              = " + MouseX(), 10, 80)
		DrawText("MouseY              = " + MouseY(), 10, 90)
		DrawText("Virtual MouseX      = " + MouseX() / screenWidthRatio, 10, 100)
		DrawText("Virtual MouseY      = " + MouseY() / screenHeightRatio, 10, 110)
		
		DrawText("Press 1 to set screen to 320x160", 10, 130)
		DrawText("Press 2 to set screen to 640x480", 10, 140)
		DrawText("Press 3 to set screen to 1920x1080", 10, 150)
		SetAlpha(1)
		
		Return True
	End
	
	Method OnUpdate:Int()
		If KeyHit(KEY_1)
			SetScreenSize(320, 160)
		Else If KeyHit(KEY_2)
			SetScreenSize(640, 480)
		Else If KeyHit(KEY_3)
			SetScreenSize(1920, 1080)
		End
		Return True
	End
	
	Method SetScreenSize:Void(w:Int, h:Int)
		' set the screen width and height
		screenWidth = w
		screenHeight = h
		
		' set the device width and height
		deviceWidth = DeviceWidth()
		deviceHeight = DeviceHeight()
		
		' calculate the ratios
		screenWidthRatio = Float(deviceWidth) / screenWidth
		screenHeightRatio = Float(deviceHeight) / screenHeight
	End
End


So this will always fill the device's screen. Press 1-3 to change the virtual screen size.


erebel55(Posted 2014) [#13]
Thank you!!! I will try this out :)


erebel55(Posted 2014) [#14]
Your code worked as advertised. I see what you mean with the image distortion because of the aspect ratio now though. If I wanted the images to still look good without black bars, would I need two sets of graphics? One for landscape and one for portrait? Or is there some other way to handle this?

For example, the pink square in the middle of this screenshot is a 32x32 image that gets skewed because of the ratio.



therevills(Posted 2014) [#15]
Again, it comes down to a game design decision and how it will affect gameplay.

There are a few blog posts relating to this issue:

* http://blog.gemserk.com/2013/01/22/our-solution-to-handle-multiple-screen-sizes-in-android-part-one/
* http://anidea.com/technology/designer%E2%80%99s-guide-to-supporting-multiple-android-device-screens/
* http://wiki.starling-framework.org/manual/multi-resolution_development

Also another approach is show more of the game, Replica Island does this - it is a tile based platform game and if you have a larger screen it scales but also shows more of the level.

Coping with Different Aspect Ratios

Replica Island serves as a very useful example of the aspect ratio problem. The game was originally designed to fit on a 480×320-pixel screen, including all “the sprites,” such as the robot and the doctor, the tiles of “the world,” and the UI elements (the buttons at the bottom left and the status info at the top of the screen). When the game is rendered on a Hero, each pixel in the sprite bitmaps maps to exactly one pixel on the screen. On a Nexus One, everything is scaled up while rendering, so one pixel of a sprite actually takes up 1.5 pixels on the screen. In other words, a 32×32-pixel sprite will be 48×48 pixels on the screen. This scaling factor is easily calculated using the following equations:

scaling factor (on x-axis) = screen width in pixels / target width in pixels
scaling factor (on y-axis) = screen height in pixels / target height in pixels

The target width and height are equal to the screen resolution for which the graphical assets were designed; in Replica Island, the dimensions are 480×320 pixels. For the Nexus One, there is a scaling factor of 1.66 on the x axis and a scaling factor of 1.5 on the y axis. Why are the scaling factors on the two axes different?

This is due to the fact that two screen resolutions have different aspect ratios. If we simply stretch a 480×320-pixel image to an 800×480-pixel image, the original image is stretched on the x axis. For most games, this will be insignificant, so we can simply draw our graphical assets for a specific target resolution and stretch them to the actual screen resolution while rendering (remember the Bitmap.drawBitmap() method).

However, for some games, you might want to use a more complicated method. Figure 5-3 shows Replica Island scaled up from 480×320 to 800×480 pixels and overlaid with a faint image of how it actually looks.

Replica Island performs normal stretching on the y axis using the scaling factor we just calculated (1.5), but instead of using the x-axis scaling factor (1.66), which would squish the image, it uses the y-axis scaling factor. This trick allows all objects on the screen to keep their aspect ratio. A 32×32-pixel sprite becomes 48×48 pixels instead of 53×48 pixels. However, this also means that our coordinate system is no longer bounded between (0,0) and (479,319); instead, it ranges from (0,0) to (533,319). This is why we see more of Replica Island on a Nexus One than on an HTC Hero.

Note, however, that using this fancy method might be inappropriate for some games. For example, if the world size depends on the screen aspect ratio, players with wider screens could have an unfair advantage. This would be the case for a game like StarCraft 2. Finally, if you want the entire game to fit onto a single screen, like in Mr. Nom, it is better to use the simpler stretching method; if we use the second version, there will be blank space left over on wider screens.


* Apress Beginning Android Games 2nd Edition


DruggedBunny(Posted 2014) [#16]
Another couple of options:

1) Use UpdateVirtualDisplay True, False and set the zoom so that the edge with borders touches the outside of the display (which means the display is zoomed in and you place the other two edges outside of the display -- so as therevills says, you have to decide if you can work your gameplay to fit that inner area. On aufofitdemo.monkey, for example, the smaller border touches the outer edge when zoomed to 1.2; I never quite figured out the maths behind this, so you'd have to figure that out if you need to target 'any' display. Not ideal, since everything would be zoomed in, but of course it depends on the game and its graphics.

2) Customise autofit.monkey so that it doesn't clear the outer area (look at VirtualDisplay.UpdateVirtualDisplay -- near the end there is a section commented "Draw borders at full device size", followed by a SetScissor and a Cls -- this is what draws the black borders. You could then call a function to draw your own custom borders just after the Cls. You can access all of the VirtualDisplay fields from there (they're all commented as to what they do) and pass anything you need to your function. (The SetScissor/Scale/Translate after that are where the virtual display is set.)

If you go with the latter, you may need to import some of your game files at the top of autofit.monkey so it can see external functions, images, etc. Also, remember you need to use DeviceWidth/DeviceHeight and not VDeviceWidth/VDeviceHeight if you draw in that section. The fields widthborder/heightborder and offx/offy should help you draw only in the right area, from memory...

Just make a backup and have a play with it!


erebel55(Posted 2014) [#17]
My game is also a tile-based platformer, however it doesn't scroll.

@therevills: My game design choice is like Mr. Nom. I want the entire game to fit onto the screen regardless of the screen size or orientation. I like the code you provided me however if I want to support both portrait and landscape; the aspect ratio problem makes the graphics look unprofessional in this case.

@DruggedBunny: I had looked into UpdateVirtualDisplay True, False. However, since I wanted the entire game to fit onto the screen regardless of the screen size or orientation that didn't seem to work. Since when you zoom in you lose some of the game. I think I may be able to live with blackbars but with my code right now I have a virtual display size of 960x640 and the blackbars are HUGE when in portrait.


therevills(Posted 2014) [#18]
the aspect ratio problem makes the graphics look unprofessional in this case


Maybe try doing it the Replica Island way:




erebel55(Posted 2014) [#19]
Thank you again therevills, you have been very kind.

Let me restate what I am doing, just to make sure I am being clear on everything
- Single screen game (no scrolling)
- Right now I am coding against a 960x640 resolution (no reason behind this except that it is my iphone4's resolution in landscape)
- I want the game to work in both landscape and portrait without noticeably distorting graphics or changing the size of the playing field.
- Since this is a single screen game, everything must fit on the screen regardless of orientation or resolution.
- Background image should scale to fill the screen.
- Controls are at the bottom of the screen.

Here is what I'm working with now, when using 960x640 (aspectRatio = false) it is cutting off a lot of the playing field to the right. And the background isn't being scaled.

Portrait:


Landscape:




Images
https://dl.dropboxusercontent.com/u/106998981/images2.zip

I am changing between the following within CONFIG.MONKEY to represent the different orientations

Portrait
#GLFW_WINDOW_WIDTH=640
#GLFW_WINDOW_HEIGHT=960


Landscape
#GLFW_WINDOW_WIDTH=960
#GLFW_WINDOW_HEIGHT=640



therevills(Posted 2014) [#20]
I get what you are trying to do and it sounds like the holy grail :)

It depends on the type of game - have a look at Candy Crush and see how they deal with changing the orientation.

Landscape:


Portrait:



erebel55(Posted 2014) [#21]
Yeah, it looks like the change the ui all around for candy crush.

It is frustrating that something this seemingly simple is taking me so long.

Let me explain my game design, maybe that will help.

I have a background image that should fill the entire screen.
On top of that I randomly place platforms (using tiles)
The player jumps from platform to platform (like doodle jump)
No scrolling.
Three control buttons at the bottom of the screen.
The user should be free to change orientations while the game is running.

If I cant do this with scaling, would i instead need to covert the coordinates for the player and tiles when changing orientations?

I am hoping not to lock the player in one specific orientation, however if I can't figure this out soon I may be forced to :/


therevills(Posted 2014) [#22]
Yeah, it looks like the change the ui all around for candy crush.


Not only that but it looks like they change the size of the grid:


On top of that I randomly place platforms (using tiles)
The player jumps from platform to platform (like doodle jump)

It sounds doable... but of course in the landscape mode there will be more platforms horizontally and vice visa for portrait, which will affect game play.

A lot of games have one huge background which fits landscape, but crops it and centers it for portrait.


erebel55(Posted 2014) [#23]
My current background has vitals on the edges so I'm not sure if I could just crop it. Scaling the background actually doesn't look very skewed. Its the tiles and player that looked skewed.

I think I may be over complicating things :(

I liked the Replica Island approach. I also liked the Virtual Viewport approach in the link you provided http://blog.gemserk.com/2013/01/22/our-solution-to-handle-multiple-screen-sizes-in-android-part-one/

These seem to fix the issue with varying screen sizes. However, since the screen size varies so much between landscape and portrait they don't seem to address that :(

I also found this link
http://v-play.net/doc/vplay-different-screen-sizes/#approaches-for-content-scaling


erebel55(Posted 2014) [#24]
Going back to you Replica Island implementation. Did I bork something when I added my images and numbers in? The results weren't as I expected.

Also, rikman seems to have gotten this working; however his link is dead as it is very old :/
http://www.monkeycoder.co.nz/Community/posts.php?topic=2756


erebel55(Posted 2014) [#25]
Or if I have to have blackbars, how big would these be? Is there an equation to calculate this? Is there anyway to limit these?

The blackbars in my original example, were HUGE and took up about as much space as the game. This is obviously unacceptable.


Raph(Posted 2014) [#26]
erebel,

Have you done a mockup in a graphics program of what the screen should look like in both portrait and landscape? I am having real trouble picturing how it would play given the description you gave.

One of my games (not done yet) does exactly what Candy Crush does. What I do is that I detect which of the two it is, and I move UI elements around based on the orientation. The playfield changes size also, and it's based on grid squares being a percentage of the screen size.

It was enough of a pain that I have started making my own GUI system with anchoring logic and all placement and sizes done in percentages. Still debugging that though.


therevills(Posted 2014) [#27]
My current background has vitals on the edges so I'm not sure if I could just crop it.

What do you mean by vitals?

Did I bork something when I added my images and numbers in?

Nope, that technique is designed to work when you increase the viewport not the orientation.

Or if I have to have blackbars, how big would these be?

All depends on the device.

The way you have described your game, sounds to me like it should be set to be either landscape or portrait, not both - as it will affect the gameplay too much. Whereas something like a Match 3 game is confined to a playable grid, your game needs the whole screen.


erebel55(Posted 2014) [#28]
By vitals, I just mean the background image has leaves on the left and right edges that I would rather not crop out.

So there arent any techniques for doing this for orientation as well? :(

I only have a few graphics, maybe I should include these in different sizes? No idea what sizes I would need to cover all devices though. This isnt ideal, but it would be nice to know if it could work.

I could limit the playable grid; but it seems like I would have to limit it too much since the ratios differ so much between orientations. For example, the safe area while in portrait is pretty small vertically compared to while in landscape.


erebel55(Posted 2014) [#29]
Is there anyway I could mess with the projection matrix to get this working?

Something like this
http://www.monkeycoder.co.nz/Community/posts.php?topic=5384

I haven't used the opengl module yet and am not really sure how it mixes with DrawImage and such.


DruggedBunny(Posted 2014) [#30]
I actually think Raph asked a very relevant question -- it is hard to picture how you intend the game to appear in horizontal mode versus portrait mode.


erebel55(Posted 2014) [#31]
I agree DruggedBunny, I will see if I can mock something like that up.

Currently when my game is in portrait mode it is very small and unplayable.

After thinking about it some more this is what I am thinking..

- Background should be scaled to fit entirely on the screen (I think we can ignore aspect ratio here because the image is so big)
- Platforms should remain on the screen regardless of position when the orientation changes. Aspect ratio is important here because the tiles are 32x32 and look very odd when skewed.
- Player should remain on the screen regardless of position when the orientation changes. Aspect ratio is important here as well because the image is fairly small and any skewing is noticeable.
- UI elements such as control buttons and hud elements (such as score). These need to remain in the same area of the screen regardless of orientation and remain close to the same size.


erebel55(Posted 2014) [#32]
I have attempted to draw this out in a paint program..

Landscape


Portrait (option 1)


Portrait (option 2)


I want to keep the tiles square and fit the background to the screen. And keep the platforms in the same relative position in each orientation.


therevills(Posted 2014) [#33]
So you want to be able to change the orientation at run time and have the platforms in the same relative position... how is that not going to affect game play?

For example, say your platform is at 800, 100 in landscape mode then the user rotates the device, now the platform is at 533, 150. Are you going to change the player's overall speed and jump amount to compensate or keep them the same?

It could be a funky game mechanic if you don't and the player has to keep changing orientation to jump on the platforms.... although it might annoy them too much too ;)


erebel55(Posted 2014) [#34]
Well I was hoping to just scale it all, so I didnt have to worry about converting coordinates, speed, and things like that. But it doesnt look possible to support landscape and portrait without either skewing all the graphics (because of the aspect ratio), introducing huge black bars, or cropping the playing field :(