I have been developing a 2D side-scrolling platformer for a while now. This is my first proper game, and it is almost finished. I haven't given much thought about resolutions until now.
My game is tile-based, most tiles and the player sprite are .PNG-files with dimensions of 40x40 pixels. The default screen height and width are 1280x720. Most games nowadays, to my knowledge, have lots of different resolution choices with different aspect ratios.
I would want to give the player couple choices of resolutions, like 800x600, 1024x768 and 1280x720. This needs to be done so the player can see equal amount of tiles in the game, whichever the resolution is, to not give unfair advantages or disadvantages to using a resolution. How would be the best way to achieve this?
I have thought about scaling the sprites of the game according to a given resolution, using
self.image = pygame.transform.scale(self.image, (30, 30))
self.image = pygame.transform.smoothscale(self.image, (30, 30))
This means that the sprites are scaled down, with 30x30 px. being the target dimensions.
I got them working properly, but the problem with both are the loss of quality. Less with pygame.transform.smoothscale, but still noticeable enough to my eyes.
This makes me wonder, is the best way to approach this really be making new art assets of the same files, but with different dimensions? It seems like a lot of work, more so when you have multiple choices of resolutions. I also thought about making a default screen resolution, sticking with it and not let the player choose any. But I rather not do that.
Most games nowadays, to my knowledge, have lots of different resolution choices with different aspect ratios
But bear in mind that most games nowadays aren't based on 576 tiles! It's a lot easier to rescale things when you aren't working with static art assets.
Think about this; if you've developed your (square) assets for a 16:9 format monitor (1280x720), should the tiles be rectangular (and the characters thinner) on a 4:3 format monitor (e.g. 1024x768)? Simply scaling in this way will give you an odd-looking game. And as you say, creating a range of art assets will be an enormous job, given how many "common" resolutions there are.
I would recommend you concentrate on making the game look good in a base resolution that will fit within most other common resolutions (e.g. 800x600) and occupy any spare screen real estate with black if not running windowed. You won't be using the whole screen, but players will concentrate on the bit you are using if the gameplay's good!
If you really do want to support multiple resolutions, I would pick one aspect ratio, and definitely create separate assets for each size; as you've already noticed, simply scaling from e.g. 40x40 to 30x30 doesn't look very good.
Related
I am creating a infinity scrolling 2d space battle game in pygame. I have just implemented a parallax star background, but my framerate is now 30fps. Do you how to get more performance, i've tried creating blitting instead of draw.circle, and hwsurface, but cant increase fps.
Download zip
I would suggest that you do not create the star background on the fly the way it sounds like you are doing. Instead create a surface that contains it at the start and then scroll through that. Wrap it around on itself so that as you go past the edge it continues from the beginning. You will want to make sure that the two edges blend and appear seamless.
Then you can just blit the part of the background onto the screen and you will not find it has much impact on your performance vs generating it on the fly all the time.
In fact if you are doing it ahead of time you can do something fancier than just circles. You can do stars with different sizes and brightness (color). Maybe even a glare effect.
Edited after comment from OP:
I thought from what you described you were scrolling it not rotating. If you are doing rotations, I assume that you are using pygame.transform.rotate() for the rotations. One thing I have done in the past if I need rotated images is again to rotate them before hand and save them. That can work well if you are just rotating (even saving 360 rotated images is not that bad memory wise), but if you are scrolling and rotating then that would not work
I have designed my whole pygame to work for 1920x1080 resolution.
However, I have to adapt it for smaller resoltion.
There is a lot of hardcoded value in the code.
Is there a simple way to change the resolution, like resizing the final image at the end of each loop, just before drawing it ?
You can use this : pygame.transform.scale or better (but less efficient) pygame.transform.smoothscale.
To do that, just change the reference surface where you draw (screen) to a generic surface. And after, just resize it, and put it on screen.
I can show you some code if you don't understand how it's work. Just ask.
i usually create a base resolution and then whenever the screen is resized, i scale all the assets and surfaces by ratios.
This works well if you have assets that are of large resolution and you have scaled then down but would pixelate for smaller images.
you can also create multiple assets file for each resolution and when ever your resolution goes above one of the available asset resolution you can change the image. you can think in in context of css media query to better understand.
I'm drawing a map of a real world floor with dimensions roughly 100,000mm x 200,000mm.
My initial code contained a function that converted any millimeter based position to screen positioning using the window size of my pygame map, but after digging through some of the pygame functions, I realized that the pygame transformation functions are quite powerful.
Instead, I'd like to create a surface that is 1:1 scale of real world and then scale it right before i blit it to the screen.
Is this the right way to be doing this? I get an error that says Width or Height too large. Is this a limit of pygame?
I dont fully understand your question, but to attempt to answer it here is the following.
No you should not fully draw to the screen then scale it. This is the wrong approach. You should tile very large surfaces and only draw the relevant tiles. If you need a very large view, you should use a scaled down image (pre-scaled). Probably because the amount of memory required to draw an extremely large surface is prohibitive, and scaling it will be slow.
Convert the coordinates to the tiled version using some sort of global matrix that scales everything to the size you expect. So you should also filter out sprites that are not visible by testing their inclusion inside the bounding box of your view port. Keep track of your view port position. You will be able to calculate where in the view port each sprite should be located based on its "world" coordinates.
If your map is not dynamic, I would suggest draw a map outside the game and load it in game.
If you plan on converting the game environment into a map, It might be difficult for a large environment. 100,000mm x 200,000mm is a very large area when converting into a pixels. I would suggest you to scale it down before loading.
As for scaling in-game, you can use pygame.transform.rotozoom or pygame.transform.smoothscale.
Also like the first answer mentions, scaling can take significant memory and time for very large images. Scaling a very large image to a very small image can make the image incomprehensible.
So I've been making a game using Python, specifically the PyGame module. Everything has been going fairly well (except Python's speed, am I right :P), and I've got a nice list of accomplishments from this, but I just ran into a... speedbump. Maybe a mountain. I'm not to sure yet. The problem is:
How do I go about implementing a Camera with my current engine?
That probably means nothing to you, though, so let me explain what my current engine is doing: I have a spritesheet that I use for all images. The map is made up of a double array of Tile objects, which fills up the display (800 x 640). The map also contains references to all Entity's and Particles. So now I want to create a a camera, so that the map object can be Larger than the display. To do this I've devised that I'll need some kind of camera that follows the player (with the player at the center of the screen). I've seen this implemented before in games, and even read a few other similar posts, but I need to also know Will I have to restructure all game code to work this in? My first attempt was to make all object move on the screen when the player moves, but I feel that there is a better way to do this, as this screws up collision detection and such.
So, if anyone knows any good references to problems like this, or a way to fix it, I'm all ears... er.. eyes.
Thanks
You may find this link to be of interest.
In essence, what you need to do is to distinguish between the "actual" coordinates, and the "display" coordinates of each object.
What you would do is do the bulk of the work using the actual coordinates of each entity in your game. If it helps, imagine that you have a gigantic screen that can show everything at once, and calculate everything as normal. It might help if you also designed the camera to be an entity, so that you can update the position of your camera just like any other object.
Once everything is updated, you go to the camera object, and determine what tiles, objects, particles, etc. are visible within the window, and convert their actual, world coordinates to the pixel coordinates you need to display them correctly.
If this is done correctly, you can also do things like scale and otherwise modify the image your camera is displaying without affecting gameplay.
In essence, you want to have a very clear distinction between gameplay and physics logic/code, and your rendering/display code, so your game can do whatever it wants, and you can render it however you want, with minimal crossover between the two.
So the good news is, you probably don't need to change anything about how your game itself works. The bad news is, you'll probably have to go in and rewrite your rendering/drawing code so that everything is drawn relative to the camera, not to the world.
Since I can't have a look into your code, I can't assess how useful this answer will be for you.
My approach for side scroller, moveable maps, etc. is to blit all tiles onto a pygame.Surface spanning the dimensions of the whole level/map/ etc. or at least a big chunk of it. This way I have to blit only one surface per frame which is already prepared.
For collision detection I keep the x/y values (not the entire rect) of the tiles involved in a separate list. Updating is then mainly shifting numbers around and not surfaces anymore.
Feel free to ask for more details, if you deem it useful :)
I am trying to decide if it is better to use a pre-rendered large image for a scrolling map game or to render the tiles individual on screen each frame. I have tried to program the game both ways and don't see any obvious difference in speed, but that might be due to my lack of experiences.
Besides for memory, is there a speed reasons to not use a pre-rendered map?
The only reason I can think of for picking one over the other on modern hardware (anything as fast and with as much ram as, say, an iPhone), would be technical ones that make the game code itself easier to follow. There's not much performance wise to distinguish them.
One exception I can think of, is if you are using a truly massive background, and doing tile rendering in a GPU, tiles can be textures and you'll get a modest speed bump since you don't need to push much data between cpu and gpu per frame, and it'll use very little video ram.
Memory and speed are closely related. If your tile set fits in video memory, but the pre-rendered map doesn't, speed will suffer.
Maybe it really depends of the map's size but this shouldn't be a problem even with a low-level-computer.
The problem with big images is that it takes a lot of time to redraw all the stuff on it so you will get an unflexible "map".
But a real advantage with an optimized image(use convert()-function and 16 bit) is are the fast blittings.
I work with big images as well on a maybe middle-good-computer and I have around 150 FPS by blitting huge images which require just ~ 100? MB RAM
image = image.convert()#video system has to be initialed
The following code creates an image(5000*5000), draws something on it, (blit this to the screen, fill the screen)*50 times and at the end it tells how long it took to do one blit and one flip.
def draw(dr,image,count,radius,r):
for i in range(0,5000,5000//count):
for j in range(0,5000,5000//count):
dr.circle(image,(r.randint(0,255),r.randint(0,255),r.randint(0,255)),[i,j],radius,0)
def geschw_test(screen,image,p):
t1 = p.time.get_ticks()
screen.blit(image,(-100,-100))
p.display.flip()
return p.time.get_ticks() - t1
import pygame as p
import random as r
p.init()
image = p.Surface([5000,5000])
image.fill((255,255,255))
image.set_colorkey((255,255,255))
screen = p.display.set_mode([1440,900],p.SWSURFACE,16)
image = image.convert()#extremely efficient
screen.fill((70,200,70))
draw(p.draw,image,65,50,r)#draw on surface
zahler = 0
anz = 20
speed_arr = []
while zahler < anz:
zahler += 1
screen.fill((0,0,0))
speed_arr.append(geschw_test(screen,image,p))
p.quit()
speed = 0
for i in speed_arr:
speed += i
print(round(speed/anz,1),"miliseconds per blit with flip")
Depends on the size of the map you want to make, however, with the actual technologies it's very hard to see a tile-map "rendered" to take longer than expected, tiled based games are almost extinguished, however is always a good practice and a starting point to the world of game programming