Logic vs Render, the separation of concerns

Apr 26, 2011 by     13 Comments    Posted under: Tutorials

On many occasions, I saw people wondering what magic they should cast at a physics engine to make it properly render its simulated objects on screen. This post aims at helping you understand how to make a clear separation of concerns, and why.

If you are a beginner in the game development world, I hope you’ll find this tutorial helpful !

Introduction

When designing a game, there are two major concerns that should be separated: LOGIC, and RENDER. Here is a quick sum-up:

  • Rendering code is where you draw on screen. Anything related to textures, camera, viewport, meshes, sprites, etc. should be handled in this part of your application. You will most certainly use a rendering framework to help you (I strongly recommend the very powerful LibGDX framework for desktop and android targets).
  • Logic code is where you create and update your simulation code, your object models. You may use simple positioning and collision detection, or even a complete physics engine. Measures will most certainly be related to real-world references (meters, kilograms, etc.).

Therefore, logic and rendering parts do not have anything in common. Well, actually they do have something, because your rendering code has to present to the user what your logic code is computing.

Why do I need to separate render and logic ?

Because it is the best way to make your game mechanisms robust and efficient. Also, it will greatly help you to support a lot of different targets with different resolutions.

Indeed, by separating our concerns, the logic part (defining the gameplay of your game) will be universal. Build it once, and you won’t have to mess with it thereafter. Only the render part is target dependent.

Logic part: it’s all about models

You define your objects and you update the game state  in the logic part. This does not involve any platform target or rendering attributes. This part should be universal. Measures are expressed in a static, target-independent way. You can use your own arbitrary units, or the SI units (meters, seconds, etc.).

A simple interface for the logic part would at least contain the following:

public interface Simulation {
	/**
	 * Objects are defined with target-independent units.
	 */
	public void createObjects();

	/**
	 * Objects are updated according to the simulation laws.
	 * If using a physics engine like Box2D, this method
	 * would at least call "world.step()".
	 */
	public void updateGameState(float ellapsedSeconds);
}

Render part: know your targets!

Now that you got some objects interacting with each other, you need to present them to the user. However, there are nearly as many desktop/phone resolution as there are potential users. Kidding, but that’s quite right.

The target resolution

First, you need to define what will be your main target resolution. The entire game will be developed with this resolution. It may correspond to a real device or be an arbitrary resolution. Let’s say we use 480×320.

This resolution on its own is not sufficient to display your game. Indeed, your objects have sizes expressed in meters. Thus, you need to define a PIXELS_PER_METER constant, which will link the logic part and the render part.

// With the following value, our 480x320 target resolution
// corresponds to a 15m x 10m visible world size.
public static final float PIXELS_PER_METER = 32;

Last thing to do is to define your assets. If you made an enemy 1m tall, create an image for it with a 32px height.

Supporting other resolutions

OpenGL, as every rendering layer, will happily stretch your target display to fit any other resolution. Therefore, the width/height ratio of different targets is important. Indeed, our 480×320 target resolution will perfectly fit in a 720×480 resolution. However, a 800×480 target will trigger a width deformation.

Solutions are as follows:

  1. Either accept this stretching if it has no impact on the game visual quality , or
  2. Let players see different portions of the game depending on their device aspect ratio

In the first case, it will result in different PIXELS_PER_METER ratios for the X axis and the Y axis. In the second case, this ratio will stay the same for each axis, but the player viewport will depend on its resolution. Either add some black areas to fill the screen, or let the player show a little more or a little less of the current level. It really depends on what game you are designing.

Pixel-perfect everywhere

Pixel-perfect rendering or 1:1 pixel mapping corresponds to making a pixel of an asset cover one and exactly one pixel of the target device.

If you designed your game with a 480×320 target resolution, and you want to support 800×480 resolutions, your assets will be scaled up (see the illustration below). Therefore, there won’t be pixel-perfect matching anymore, and visuals won’t look as sharp as with the target resolution.

It may be ok for many games, but you may want to overcome this. The only solution is to create as many assets as supported resolutions. For instance, if your enemy is still 1m x 1m, you made a 32×32 px image for your 480×320 target resolution. Therefore, you also need to make a 48×48 px image for 800×480 and 854×480 resolution (if you chose the show black lines or a bigger portion of the level), etc.

Interaction with the user

We never talked about user interaction, and you need it to make not just a fantastic game, but an awesome one. However, when the user touches or clicks on the screen, you will usually get an input in pixels coordinates, relative to the screen origin.

It means that you need to process the inputs before sending them to the logic part, since the latter do not want anything related with screens and pixels.

Hence, you got your complete separation of concerns. On each frame, inputs are processed and sent to the logic part in a compatible format. Then, simulation code is updated according to these user interactions and to internal mechanisms (gravity, forces, torques, collisions, etc.). Finally, objects are drawn according to the PIXELS_PER_METER constant as well as the current camera state.

Conclusion

You should now be able to understand why and how to separate things to make your code cleaner. Separation of concerns does not imply different classes for rendering, simulation and inputs, it just means that you should process things in a certain order.

Of course, this tutorial is based on my past experience. Do not hesitate to tell me how YOU plan your games! ;-)

NB: to go further, I greatly suggest you to read the first comment of this article!

13 Comments + Add Comment

  • Note: This comment and the reply were about a previous version of the tutorial.

    Great stuff, i enjoyed reading it. The only “complaint” i have is that you actually don’t even want to go the pixel route when rendering. The reason: different devices/screens have different screen resolutions. You have to come up with a resolution independent way to render your stuff at some point anyways.

    So here’s how you could solve that: define your PIXELS_PER_METER for creating your assets, e.g. set it to 32. Next design your objects rough physical size, e.g. a certain enemy is 2×1 meters in size, an item is 0.5×0.5 meters in size and so on. Based on PIXELS_PER_METER you know how much space each object will take up in a texture, e.g. 64×32 pixels or 16×16 pixels and so on.

    Next up you define your target resolution. This resolution must not equal the resolution of the actual screen, OpenGL will happily stretch and scale automatically. Given the PIXELS_PER_METER as above you could use 480×320 for your target resolution, From that you can derrive the size of your camera’s viewport which is 16×10 meters.

    Now you can work in the same coordinate system when defining your logical features (position, rotation, scale, bounding shape(s)) and rendering features (mesh vertices, texture regions etc.) of your objects.

    If the screen your game runs on has the target resolution rendering will be pixel perfect. On lower resolution screens rendering will not be perfect anymore, the same is true for higher resolution screens. Besides this problem you also have differing aspect ratios (screen width / height). A 320×240 screen has an aspect ratio of 1.33, a 480×320 screen has an aspect ratio of 1.5 and a 800×480 screen has an aspect ratio of 1.66.

    To solve this you have to make one decision: should all players see the same portion of the game world or should screens with a higher aspect ratio show more of the world?

    In the first case you have no other option than to stretch the viewport. Say your target resolution is 480×320 and your target viewport has a size of 16×10 meters. On a 480×320 screen you get pixel perfect rendering with PIXELS_PER_METER = 32 on each axis. On a 320×240 screen you have to stretch on the y-axis a little (PIXELS_PER_METER on x-axis = 320/16 = 20, PIXELS_PER_METER on y = 240 / 10 = 24). On a 800×480 screen you have to squeeze on the y-axis a little (PIXELS_PER_METER on x = 800 / 16 = 50, PIXELS_PER_METER on y = 480 / 10 = 48).

    In the second case, where it is OK for your game to show smaller or larger regions of the game world depending on the screen resolution you have to refit your viewport. Take the bigger dimension of your viewport (e.g. 16 meters in case of a 16×10 meter viewport) and recalculate the lower dimension. In case of a 320×240 screen and an initial viewport of 16×10 meters the y-dimension of the viewport can be resized to 240 / (320 / 16) = 12 meters (you see slightly more). On a 480×320 screen the viewport stays the same (320 / (480/16) = 10 meters). On a 800×480 screen you get a new viewport height of 480 / (800 / 16) = 9.6 meters, you see slightly less. Whether you see more or less on a given screen depends on its aspect ratio in relation to the aspect ratio of your default viewport. If the screen aspect ratio is higher you see less, if it is lower you see more. If this trade off works for your game you gain a benefit: your PIXELS_PER_METER constant will be the same on each axis again. On a 320×240 screen it’s 320/16 = 20, on a 480×320 screen it’s 480/16 = 32 and on a 800×480 screen it’s 800 / 16 = 50.

    In either case you have multiple PIXELS_PER_METER constants. To make your rendering truely pixel perfect while still using the logical coordinate system for rendering you have to create multiple versions of your assets, one for each screen resolution you want to handle. The general workflow should thus be like this:

    1) Define the units to be used in your game, e.g. meters
    2) Define the rough size of each object in your game in these units, e.g. 2×1 meters, 0.5×05 meters and so on.
    3) Define the size of your camera’s viewport in these units, e.g. you want to show a 16×10 meter region of your world on screen.
    4) Chose whether you want stretching or if you want to show more or less of your world depending on the screen resolution and aspect ratio.
    4a) for the first option recalculate PIXELS_PER_METER on the x and y axis separately for each screen resolution you want to handle, PPM_X = SCREEN_WIDTH / VIEWPORT_WIDTH, PPM_Y = SCREEN_HEIGHT / VIEWPORT_HEIGHT.
    4b) for the second option recalculate the smaller viewport size for each screen resolution. Assuming the height is smaller you can calculate the resized viewport height as NEW_VIEWPORT_HEIGHT = VIEWPORT_HEIGHT / (SCREEN_WIDTH / VIEWPORT_WIDTH). Then recalculate the PIXELS_PER_METER for each screen resolution with PPM_X = PPM_Y = SCREEN_WIDTH / VIEWPORT_WIDTH. The PPM_X and PPM_Y will be the same since we don’t stretch.
    5) Draw your assets for each PIXELS_PER_METER constant (pair in case of 4a), e.g. a sprite has 3 versions, one at 20×20 on a 320×2480 screen, one at 32×32 on a 480×320 screen and one at 50×50 on a 800×480 screen, using 4b (viewport resizing).
    6) At render time set your viewport and use whichever asset version fits the given screen resolution. For screen resultions you don’t have assets for (e.g. 854×480 on a Droid) just use the closest one and stretch a little. The difference won’t be noticeable.

    My 0.02€ :)

    • Nice comment ! That’s totally how I design my games: to be target/resolution-independant. Actually, you’re perfectly right on everything. Think I need to rework this stuff. Figure 3 seems to make an assumption that the world size is the same as the screen dimensions, whereas I wanted to say that a world has no “size”, just an origin. What is shown on screen only depends on the render part of the code.

      Thus, the renderer is the only place where choices about viewports, streches and dots-per-inch have to be made.

      I’ll update this post to try to stick more with the logic part, and will make a next tutorial to reflect your comment, this time speaking about the rendering part, and all the decisions coming with it.

      Thanks a lot :-)

  • wow this is such a very good post especially for the beginners. i really like the detailed explanation of the first comments.

  • Terrific info! Thank you. One comment, a bit less substantive than badlogic’s:

    >Either ass some black areas to fill the screen…
    I think you meant “add”.

    Look forward to reading more.

  • [...] = 'wpp-261'; var addthis_config = {"data_track_clickback":true};Aurelion Ribon has a great post on his blog explaining this concept. The idea is pretty simple – you want to keep your model [...]

  • Very good article.
    This one it’s a “must read” for every game developer beginner.

    Thank you for your hard work!

  • [...] and is not bound to a specific resolution. That’s the most important thing to remember from this previous article! This simple code sample, once launched, creates the following [...]

  • [...] result are projects lacking in what is called separation of concerns. It’s not just about experience. Without a clear architectural model frequently reminding you [...]

  • [...] result are projects lacking in what is called separation of concerns. It’s not just about experience. Without a clear architectural model frequently reminding you to [...]

  • I am having a problem in understanding.I want to know, how to specify texture width and hight according to view port means if I am using 16X10 in view port size then how to pick texture of different sizes and how to use draw means how would I specify width and height of texture.
    basically I want to know what to put in screen_width and screen_height
    camera.setToOrtho(false, screen_height, screen_width);
    16X10 or 480X320

    please give example

  • [...] to something like the size in Meters that you want to display (excellent Guide & Comment Here: http://www.aurelienribon.com/blog/2011/04/logic-vs-render-separation-of-concerns/ read the article and the 1st [...]

  • Nice, thank you for this cool article, I’m totally agree with most of it, but I want to add a notice for readers :
    PLEASE AVOID THE STRETCHING SOLUTION.

Got anything to say? Go ahead and leave a comment!

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>