Adjusting visuals for different resolutions

Discussion in 'Programming' started by MiniSiets, Jul 7, 2019.

  1. MiniSiets

    MiniSiets Member

    Joined:
    Jul 7, 2019
    Posts:
    3
    Hey everyone,

    I've got a game I've been developing in GMS, and thus far everything has been going fairly smoothly for me except for one nagging issue that I can't seem to resolve. That is of course as the title suggests, I'm having difficulty getting the game to adjust to different display resolutions without the scaling causing things to look janky.

    Here's some basic info about what I'm trying to achieve. This is a 2D pixel art game that is attempting to emulate a 16-bit SNES style aesthetic. As such I wanted to target an internal resolution that matches pretty closely to the SNES, but at the same time I also wanted to modernize it a little bit so that it would be 16:9 widescreen aspect ratio. Therefore I settled on a resolution of 400x225, which is 16:9 while otherwise resembling a pixel size close to the SNES' original resolution. Because of the way certain game mechanics work, the game needs to maintain a fixed aspect ratio at all times, so I'm not concerned with black bars appearing on displays that are not 16:9, and I'm not dynamically supporting them.

    (Also note, my work environment right now is Win7 64-bit and GMS 1.4)

    Now that we got that out of the way, here's my problem. My actual display is 1920x1080, but even though both 400x225 and 1920x1080 are 16:9 aspect ratios, 400x225 doesn't upscale evenly into 1920x1080 (1080p is 4.8 times the resolution of 400x225, not a rounded integer), which seems to cause some slight pixel distortion in full screen, which is distracting.

    I do not get this problem if I set the game to windowed mode at 1600x900, as this resolution divides evenly into 400x225, so it can upscale the resolution pixel-perfect.

    This led me to doing a little research on possible solutions so I could support multiple resolutions, which had me come across PixelatedPope's tutorials on creating a display manager. I tried his method, but this seems to cause movement in my game to become extremely choppy when upscaled to 1920x1080, as it seems to just stretch the 400x225 surface to my display without accounting for the fact there are a lot more in-between pixels after being stretched to 1920x1080, so during motion it just draws the object to the next nearest pixel within that stretched 400x225 space.

    My next thought was to look into possibly implementing some kind of sub-pixel movement as a workaround for this, but I'm not sure how much this would complicate my code or if it would even work.

    My other thought was that I could just change the resolution to something that would fit evenly into 1920x1080, like 480x270. This would certainly be doable without much reworking of my code, but other resolutions like 1280x720, 1600x900, and so on are still quite common and I would just run into the same problem with them all over again since 480x270 doesn't divide evenly into these resolutions. I feel like there should be some way to scale these pixels up with minimal distortion to support multiple screen resolutions, especially if I intend to keep a fixed aspect ratio and I'm not even concerned with dynamically supporting other stuff.

    Is there any way to fix this?
     
    Last edited: Jul 7, 2019
  2. NightFrost

    NightFrost Member

    Joined:
    Jun 24, 2016
    Posts:
    1,859
    If you don't want everything force-aligning to the 400x225 grid, you'll have to scale up and draw stuff separately. I've explained the method I use in this post. Since you're doing a platformer you could also use the other method I mention there: draw the entire background to 401x226 surface first (one pixel bigger than view) then scale it up and offset (which is why it is 1 pixel larger) when you draw it to app surface, and have creature draws scale up with same value. This lets you adjust everything by 1 display pixel precision.
     
  3. JackTurbo

    JackTurbo Member

    Joined:
    Oct 19, 2016
    Posts:
    821
    If you're using views I'd suggest scaling by an integer value and then cropping it with the view. You'll have partial "game pixels" at the edge of the view but that's better than the art distorting imho
     
  4. MiniSiets

    MiniSiets Member

    Joined:
    Jul 7, 2019
    Posts:
    3
    A few questions about your method NightFrost.

    - I see how you're calculating the scaling factor, but what is the stretch factor? In my case would that be 1.2? Since I'm guessing the way it would work is, my scaling factor would round down to 4 from 4.8, which would set my view area to 900p. Then in order to stretch it to 1080p from there it would have to stretch by a factor of 1.2, correct?

    - Suppose someone wants to pause the game, open a settings menu, and change display settings. Won't this cause all your in-game values to break since the objects have already spawned with values attuned to the previous resolution and your creation event has already executed?

    - Wouldn't you also have to multiply the x/y coordinates of tiles and objects on the screen in addition to their draw scales so that they're properly spaced out and not overlapping each other after being blown up by the scaling factor?

    - Any chance I could see some sample code of this as well? Would probably save me a lot of headaches trying to code it totally from scratch.

    EDIT: Upon testing some setups, I'm finding that no matter what there seems to be a "shimmering" effect happening to the pixel art if I try to set the game's internal view to something like 1600x900 and then stretch it to 1920x1080 from there. What values do I need to have for the surface area, view, port, and window for it to scale up without this shimmering effect? Only way I've been able to get the shimmering to stop so far is by rendering everything natively in 1080p and scaling sprites up by a factor of 5 to stretch them slightly above 1080p, which of course causes some of the pixels to run off-screen, and is not ideal for my setup.

    I also feel like something is amiss when I used PixelatedPope's method. Despite that I implemented my code nearly identical to his (only adjusting the ideal_height to match my res, 225), in his video demonstration it seemed like movement was a lot smoother than how it came out in mine, which makes no sense to me if the code is virtually the same, just a slightly different ideal height. I wonder if this is somehow an issue with how GM engine runs on Windows 7 versus Windows 10, because beyond that I don't see how any difference would affect it.
     
    Last edited: Jul 7, 2019
  5. NightFrost

    NightFrost Member

    Joined:
    Jun 24, 2016
    Posts:
    1,859
    Yes, the stretch would become 1.2 as that's what is needed to bring the app surface to full display size. You could of course skip this step and just center it instead, if you don't want any pixel stretching (in this case, most stretched pixels are 4 wide but there would be a few that are 5. In practise this is pretty much unnoticeable).
    Yeah my code isn't really built for that, since the stuff I make is intended to be played at resolution it is coded for (ie 480x270) and made to fill the display. It doesn't have a thing as "changing the resolution" because every resolution is the same. On the other hand, I can't immediately recall any pixelart-based game that has a resolution setting, they all just sniff out your display size and fit their intended content to that space.
    Yes, computing the scale factor and then using it everywhere would cover that too. There's a singular problem when it comes to tiles though, and I haven't found a way around yet: you cannot scale tiles. You can however scale sprite layer sprites, and that's what I've been using. I've mostly just done procgenned caves or predefined building blocks under this method so it hasn't been a roadblock to me. But if you want to draw your level in GMS room editor using tiles, then you will have a problem getting the tiles scaled up. (If you compare Tilemap Layer Functions list to Sprite Layer Functions you'll notice one difference: there is nothing to either stretch the timemap or individual tiles, but sprites have layer_sprite_xscale and layer_sprite_yscale).
    Not at my dev machine now, but I can post some code later. It is however just PixelatedPope's code with some minor changes to make it work with my method.
     
  6. MiniSiets

    MiniSiets Member

    Joined:
    Jul 7, 2019
    Posts:
    3
    Well, I was just using that as a hypothetical. My ideal implementation would be that there's a settings menu where you could adjust the zoom on the fly to something like 1x - 4x or stretch to fit as options. I saw AM2R (which was also developed in GMS) essentially use this setup, but I have no idea how they were able to implement it without either running into 1) shimmering pixel effect or 2) choppy movement, because this seems to be the case with every method I've tried so far.

    AM2R had an internal resolution of 320x240, and 240 does not divide evenly into 1080, yet somehow when I scale the game up to full screen I don't get shimmering pixels, and object movement is smooth. On top of this you can adjust the zoom scaling in the pause menu at any time. How did they do that?
     
    Last edited: Jul 7, 2019

Share This Page

  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice