Scaling a 2D game ?

3D games can be scaled to any resolution, because of the way vectors work.
The menus are often 2D and in a fixed resolution. (resolution only changes in-game)

In 2D games, we have pixel-art, which shall not be transformed - because even if you maintain aspect ration - it looses quality, one way or the other.

So what 2D games did, back then, was to have only 1 resolution. And thats it.
Even Starcraft and Diablo had only 1 resolution and no window mode.

Right now in my game its like this: Someone with a bigger resolution can see more, a small one sees less - nothing is stretched or scaled-down.

Someone from my team says that this seems unfair (well its not multiplayer) and it may change how someone plays the game depending on the resolution.
While I think that this is true, I don’t think it’s an issue - and I don’t see any quick fixes…

What do you guys do, with your 2D games ?

Enable interpolation and scale the sprites. It won’t preserve the SNES style pixelized style, but it will make it look really smooth and nice IMO and it works perfectly with floating point coordinates scaled to any resolution. You’ll need a transparent border around your 2D images to avoid aliasing. For a 2D game I made earlier, I decided on an ‘internal’ resolution and aspect ratio. When the screen size changed I calculated values to send to glOrtho() to keep the aspect ratio of the game by adding black borders, and then used the scissor test to disable drawing into these borders. However, to the internal functions of the game I had a constant resolution. Now, I’m assuming you’re using OpenGL since you mentioned 3D, so here’s the function I made:


private static final int INTERNAL_WIDTH = 800, INTERNAL_HEIGHT = 600;
private static final double INTERNAL_ASPECT = (double) INTERNAL_WIDTH / INTERNAL_HEIGHT;


private void setWindowSettings(int screenWidth, int screenHeight) {
    double aspectRatio = (double) screenWidth / screenHeight;
    if (aspectRatio > INTERNAL_ASPECT) {
        double width = INTERNAL_HEIGHT * aspectRatio;
        double displacement = (INTERNAL_WIDTH - width) * 0.5;
        GL11.glOrtho(displacement, width + displacement, INTERNAL_HEIGHT, 0, -1, 1);
    } else {
        double height = INTERNAL_WIDTH / aspectRatio;
        double displacement = (INTERNAL_HEIGHT - height) * 0.5;
        GL11.glOrtho(0, INTERNAL_WIDTH , height + displacement, displacement, -1, 1);
    }
    GL11.glViewport(0, 0, screenWidth, screenHeight);

    GL11.glEnable(GL11.GL_SCISSOR_TEST);
    if (aspectRatio > INTERNAL_ASPECT) {
        int padding = (int) (screenWidth - screenHeight * INTERNAL_ASPECT) / 2;
        GL11.glScissor(padding, 0, screenWidth - padding * 2, screenHeight);
        inputManager.update(padding, 0, screenWidth - padding * 2, screenHeight);
    } else {
        int padding = (int) (screenHeight - screenWidth / INTERNAL_ASPECT) / 2;
        GL11.glScissor(0, padding, screenWidth, screenHeight - padding * 2);
        inputManager.update(0, padding, screenWidth, screenHeight - padding * 2);
    }
}

The first part (the glOrtho() part) is pretty straightforward, but the scissor test part is pretty messy IMO. ._. The point of this method is that it allows you to draw to any resolution without having to scale your coordinates manually like when drawing in 3D. The position at (INTERNAL_WIDTH / 2, INTERNAL_HEIGHT / 2) = (400, 300) will always be the center of the screen, regardless of the screen’s (or window’s) actual aspect ratio or resolutions. Oh, and inputManager is just a class that handles input and translates screen coordinates to the internal resolution.

g.drawImage(imgDbl, 0, 0, screenWidth, screenHeight, null);

I draw everything to imgDbl, then it is stretched to the screenWidth and screenHeight, which are set when the applet is resized. It acts as a way to scale and a double buffer :slight_smile:

yeah no, technically thats all well - I know how to do it

but obviously if you stretch 2d pixel art its not crisp anymore - you cant do that, you’re raping the sprites which took long to get like this, by transforming them

question wasn’t how - but if
and if not - what then ?
because I mean right now, like I said, you can just see more with a bigger resolution, and personally I don’t think its bad

Scaling old-school pixel art is a bit of, well, an art. The best looking scaling algorithms use heuristics that do antialiasing on edges without interpolating colors everywhere. Google for the “Scale2x Algorithm”, and check out http://xu4.sourceforge.net/screenshots.html for some examples of scaling algorithms applied to an old favorite.

I guess I still misunderstand the question. You don’t want to scale the graphics (in a zoom sort of way) and you don’t want to minimize the viewing size for people with larger resolutions. Are you looking for someone to agree with you and say you’re right and your teammates shouldn’t worry about it?

I guess. I mean yeah its an advantage and it changes the way someone plays the game, but its not multiplayer and there are obviously minimal and maximum resolutions
The game experience should be similar from a game design point of view - There are of course worries that level design and scripted events could clash with slightly varying resolutions

I’m not against rewarding someone with a larger resolution than intended, but I certainly wouldn’t give anyone with a smaller resolution a disadvantage. If you allow scrolling so that people with smaller resolutions can look around (like in a dungeon crawler or RTS type game), then I don’t think it would be necessary to change what you’re doing now. What kind of game is this?

Sidescroller, think Castlevania

I guess, then, the questions you need to ask yourselves, Is the game play on larger resolutions far too different than what you intended? and Are the sprites going to seem unbearably small in a large resolution?

Is there any exploration involved? I could see a sort of “cheating” factor if I can see a dead end before it was intended, or if I’m falling down a pit I could see obstacles that needed avoiding sooner than intended. Being able to see more may influence whether or not I want to exchange my Banshee Boomerang for Holy Water. And that would be more frustrating for people with low resolutions.

Ever played Settlers 4? Higher resolution gave you a larger view of the map at any given time. And at Max resolution you got an additional GUI Tool (Bottom Left ‘mini camera’ in second pic) that allowed you to “focus” on a unit or given point in the map and everything within that focus area was visible to you from anywhere on the map at any given time.

Compare 1024

http://img356.imageshack.us/img356/8641/settlersiv1024x768.jpg

vs 12xx

I don’t think anyone ever whined.

I think the best you can do is to play around with some scaling algorithms (http://en.wikipedia.org/wiki/Image_scaling) and convert your art to the desired resolution on init.

The scaling technique shown here looks really nice.

Indeed.

However why even upscale ? I mean if I really wanna scale, I could just create all the graphics for the max resolution and then scale down if needed.

I just have a greater width and height und render everything based on them :o IF I want people to see more if there want bigger resolutions (like Terraria). Since I use Slick2D I can also just combine the CanvasGameContainer and the ScalableGame Class to let the user scale the game :o

Well I’m not doing it like that because I want to make sure that whatever resolution the player is using, is also a valid fullscreen resolution.
Which is also weird because I think 1280x720, 720p, is the ideal resolution, and I have a 1080p 16:9 monitor - so it works; but in linux, the drivers doesnt list that resolution as a valid resolution and thus cant fullscreen to it, in Linux. Even though its the same aspect ratio as the native resolution - and it shouldnt be OS dependent what resolutions you can fullscreen too, since hardware stays the same
Oh well, Linux… ::slight_smile:

Fullscreen on the desktop resolution, draw to an FBO or a Pbuffer and stretch it to the screen with a scaling algorithm of your choice?

Ah, IF I want to make it that way I just get the list of resolutions with fullscreen option from the Display class :smiley: If it’s below your set native resolution you just scale (in 2D this can be done ith a simple glscale!) and if it’s greater you work with this resoultion and render more tiles or whatever :slight_smile:
As for me I will start with a fixes resolution, because most gamers play the game in fullscreen anyway :0

I’ve been doing a 2D game with NES palette and resolution. I blit to a 256x240 canvas for all operations, and just use bilinear interpolation and fake scanlines to make it look presentable. I know a lot of people may not like the look of scanlines, but to me that’s what gave those old games a distinct look. We preserve the 4x3 aspect ratio, and even the NES uneven 256x224 (NTSC) look. If you have a newer Java, and a decent graphics card, you can fake it pretty well.

This isn’t fancy, but it’s simple, and what we used:

Upscale 4x3 Nintendo style graphics to whatever native resolution.


Graphics2D g = (Graphics2D)  some_graphics_object;

int nes_height=240;
nes_width = (int)(((float)screen_width/((float)screen_height/(float)240))*.8);
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
int nes_remainder = (int)((float) screen_width-(float)screen_width*(float)256/(float)nes_width)/2;
g.drawImage(nesImage, nes_remainder,0,screen_width-nes_remainder,screen_height,0,0,256,240,this);

Fake scanlines


int scanline_count = 224;
int s_size = screen_height/scanline_count;
			
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.1f));
g.setColor(new Color(0,0,0));
for (int i=0;i<screen_height;i++) {
	if (i%s_size==0) {
		g.drawLine(0,i,screen_width,i);
	}
}

Before

After

For pixel art I like nearest-neighbor interpolation, which, I think, is the kind of scaling where every pixel becomes 2 pixels big.