Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you scale a design resolution to other resolutions with Pygame?

So in every Pygame example I've seen, there seems to be a resolution the game is designed to be played on and that's it. No options for setting higher or lower resolutions. If you do change the resolution via display.set_mode, then the scale of the game textures/graphics get out of whack and the game becomes unplayable.

In all the examples I've seen, it looks like you would have to actually create different sized texture sets for each target resolution... Which seems ridiculous. That leads me to my question.

If I design a game based on a standard resolution of 480x270, is it possible to simply scale the output of that surface to 1080p (or 1440p, 4k, etc) while still utilizing the game assets built for 480x270? If so, can anyone post an example of how this can be accomplished? Any tips or pointers would also be appreciated.

like image 411
Matthew Avatar asked Apr 03 '17 23:04

Matthew


3 Answers

You could make a dummy surface at the design resolution and scale it to the resolution the game is running at:

window = pygame.set_mode([user_x, user_y])
w = pygame.Surface([design_x, design_y])

def draw():
    frame = pygame.transform.scale(w, (user_x, user_y))
    window.blit(frame, frame.get_rect())
    pygame.display.flip()

Draw everything on the dummy surface and it will be scaled to the screen. This requires a fixed screen height to screen width ratio.

like image 192
object-Object Avatar answered Nov 15 '22 15:11

object-Object


Spent some time tinkering and put together a demo showcasing how you could go about tackling this problem. Find yourself an image for testing and put the string path to that image in the script for the value of imagePath.

The functionality of this script is simple. As you hit either left or right on the arrow keys, the screen resolution cycles through a tuple of acceptable resolutions and the screen resizes accordingly while scaling your test image to the new resolution.

import pygame,sys
from pygame import *
from pygame.locals import *


displayIndex = 0
pygame.init()

##standard 16:9 display ratios
DISPLAYS = [(1024,576),(1152,648),(1280,720),(1600,900),(1920,1080),(2560,1440)] 
screen = pygame.display.set_mode(DISPLAYS[displayIndex])
screen.fill((0,0,0))
### change image path to a string that names an image you'd like to load for testing. I just used a smiley face from google image search.
### Put it in the same place as this file or set up your paths appropriately
imagePath = "Smiley-icon.png"


class Icon(pygame.sprite.Sprite):
    def __init__(self,x,y):
        pygame.sprite.Sprite.__init__(self)
        self.smileyImage = pygame.image.load(imagePath)
        self.image = self.smileyImage.convert_alpha()
        ### need to assume a default scale, DISPLAYS[0] will be default for us
        self.rect = self.image.get_rect()
        self.posX = x
        self.posY = y
        self.rect.x = x
        self.rect.y = y
        self.defaultx = (float(self.rect[2])/DISPLAYS[0][0])*100
        self.defaulty = (float(self.rect[3])/DISPLAYS[0][1])*100
        ## this is the percent of the screen that the image should take up in the x and y planes



    def updateSize(self,):
        self.image = ImageRescaler(self.smileyImage,(self.defaultx,self.defaulty))
        self.rect = self.image.get_rect()
        self.rect.x = self.posX
        self.rect.y = self.posY


def ImageRescaler(image,originalScaleTuple): #be sure to restrict to only proper ratios
    newImage = pygame.transform.scale(image,(int(DISPLAYS[displayIndex][0]*(originalScaleTuple[0]/100)),
                                         int(DISPLAYS[displayIndex][1]*(originalScaleTuple[1]/100))))
    return newImage


def resizeDisplay():
    screen = pygame.display.set_mode(DISPLAYS[displayIndex])
    ## this is where you'd have'd probably want your sprite groups set to resize themselves
    ## Just gonna call it on icon here
    icon.updateSize()


icon = Icon(100,100)
while True:
    screen.fill((0,0,0))
    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            sys.exit()

        if event.type == KEYDOWN:
            if event.key == K_LEFT:
                displayIndex -=1
                if displayIndex < 0:
                    displayIndex = 5
                resizeDisplay()
            elif event.key == K_RIGHT:
                displayIndex+=1
                if displayIndex > 5:
                    displayIndex = 0
                resizeDisplay()




    screen.blit(icon.image,(icon.rect.x,icon.rect.y))  
    pygame.display.update()
like image 33
oxrock Avatar answered Nov 15 '22 15:11

oxrock


The best way to go about this is by downscaling images, in order to preserve image quality. Here are two options:

Option 1

This method is probably the fastest.

  1. Create images to be compatible with the largest resolution you are intending to support.
  2. Create a screen with the user's desired size.

    screen = pygame.display.set_mode((user_x, user_y))
    
  3. Downscale images at load time

    image = pygame.image.load("a.png").convert_alpha()
    pygame.transform.scale(image, (screen.width() / your_max_width, screen.height() / your_max_height), DestSurface=image)
    
  4. Now, just blit them normally. It should run at normal speeds.

Option 2

  1. Create a screen with the user's desired size.

    screen = pygame.display.set_mode((user_x, user_y))
    
  2. Next, create a pygame.Surface with the highest resolution you are intending to support.

    surface = pygame.Surface((1920, 1080))
    
  3. Then, blit everything to that surface.

    surface.blit(image, rect)
    # Etc...
    
  4. After all blits are completed, downscale the surface (as necessary) to whatever resolution the user desires. Preferably, you would only allow resolutions with the same aspect ratio.

    pygame.transform.scale(surface, ((screen.width() / your_max_width, screen.height() / your_max_height), DestSurface=surface)
    
  5. Finally, blit the surface to the screen, and display it.

    screen.blit(surface, (0, 0))
    pygame.display.update()
    

This process (downscaling) allows you to preserve image quality while still allowing the user to choose their screen resolution. It will be slower because you are constantly manipulating images.

like image 31
Remolten Avatar answered Nov 15 '22 16:11

Remolten