# Common pattern to scale “real units” to pixels?

This is a follow-up question to this other one.

I would like to know if there is a common/typical/best pattern to scale my representation of the world (currently 160Kmx160Km) to fit it to the drawing area (currently 800x600 pixels).

I can think of at least four different approaches:

Naïve one (the way I did it so far). I implemented a global function sc(vector) that will simply return a scaled down copy of the vector passed in. This of course works, but obliges me to write code like:

drawCircle(sc(radius), sc(position))


Wrapping functions. I could define several functions, each of them wrapping the original middleware one. For example I could define myDrawCircle that would first scale the arguments that need scaling, and then call drawCircle with the latter. This would make my code perhaps more readable and easier to maintain, but I should write a lot of wrapping functions, which sound silly.

Function decorator. I could simply decorate the existing middleware functions, providing automatic scaling for all the parameters that are an instantiation of the class Vector3D, but the problem is that those functions also work with the same parameters being list or Vector2D too, and the decorator would have no way to know which lists need to be scaled (the radius for example) and which not (the RGB values).

Surface initialisation. When defining the surface I am going to draw on, I could define the scaling factor (so that I would then use metres and not pixels as parameters). This would work transparently for me and would be my solution of choice, but of course it should be implemented in the middleware, so it's not an real option.

...anyhow: since this is a very common problem, I wonder if there is an established pattern that elegantly solves this problem that I failed to find.

PS: For this project I am using python (with pygame), but - although a python/pygame-specific answer is very appreciated, I'm more interested in the general/high-level description of the pattern rather than its concrete implementation.

The standard way of doing that is to set up a transform matrix to do the conversion during rendering. For 3D rendering it's the view transform matrix for the camera that does it.

Most 2D APIs have a way of specifying a view transform too, as either a 2x3 matrix or as a separate scale, translation and rotation.

A quick look at the pygame documentation suggests you'll have to implement a view transform matrix yourself. Does it let you derive your own class from it's surface class to enable you to add that functionality?

• You answer is very useful (+1). I can subclass my surface class, but I need more time to explore if I can achieve what indicated in your post. I'm very new to game programming and graphics in general, do you have some link to recommend that explain this matrix approach (or even only the math involved using a matrix to scale a surface?). Thank you! – mac Jul 23 '11 at 15:26
• Since it's 2D and there's no rotation involved @mac is basically saying to wrap functions and you can use your simple scale divide in those wrappers. The reason a full matrix is normally used is that most API have a matrix controlling the viewport, but it appears that pygame does not so you have to do it yourself one layer up. – Patrick Hughes Jul 23 '11 at 17:00

since this is a very common problem, I wonder if there is an established pattern that elegantly solves this problem that I failed to find.

Usually people do not attempt to perform run-time scaling in 2D games. I don't agree that it's a common problem. If people want a mini-map or some other fixed rescaling, there is often a new set of graphics done for this purpose. It's rare that you need any single routine to run at 2 different zoom levels in a 2D game.

If you use a 3D API, you can get this functionality for free - just change the camera/viewport parameters.

PyGame is a very old API and unfortunately is only really suited to 2D. Even if you could work out adequate scaling systems for the different zoom levels, the performance will not be good enough and the appearance is likely to be unacceptably bad.

I would advise that if you want to zoom in and out a lot, you move to a modern 3D API as soon as possible. I would recommend pyglet but there are likely to be others too.

• Thank you for your answer (+1). I already used pyglets in the past for a 3D simulation. It's surely more capable than pygame, but support for 2D is fairly lower-level than the one in pygame. The documentation/examples available are also less detailed, which is s bummer for me as I am a beginner in game development, and I really need to understand the basis of most commonly performed operations. :) As for the "age" of pygame: you are right. A modernisation is in progress however! :) – mac Jul 25 '11 at 14:00
• As for: "It's rare that you need any single routine to run at 2 different zoom levels"... I thought that was what it would be used when changing the size of a window / the resolution of a full-screen game. From your comment I understood it is not: any pointer to what is "common" instead? – mac Jul 25 '11 at 14:04
• Which 2D operations are you performing where pyglet is more low-level than pygame? Both let you draw a sprite with 1 line of code. When you change the window size of a 2D game, the pixel-to-world-unit ratio almost always stays constant. So you just end up drawing more sprites: you don't draw them differently. – Kylotan Jul 25 '11 at 18:06
• On the low-level side of things: I'm not truly an expert on either of the two libs, but in pyglets I could not replicate out-of-the box the group rendering behaviours offered by pygame (as in pygame.sprite.LayeredUpdates for example). As for the unchanging ratio: I got what you mean. The behaviour you describe is not the one I want to replicate, but I understood what you mean, thanks for the clarification! :) – mac Jul 25 '11 at 23:05
• pyglet has OrderedGroup objects - docs are here if you or anybody else is interested: pyglet.org/doc/programming_guide/displaying_images.html#sprites – Kylotan Jul 26 '11 at 0:04