Game Basics Answers

65. One option available is to just multiply the screen by non-integer numbers. To fit the base resolution of 480x270 in a screen size of 1366x768, we would need to set sx and sy to be 2.84. The problem with this approach is that for different resolutions sx and sy might be different, which would lead to distortions, and even if they are the same, multiplying by a non-integer number can make the screen look off in a number of ways.

Another option is to always multiply by integers but then fill the screen with borders whenever there are pixels left. For instance, suppose the user has a screen size of 1440x900 and we want to fit our game into it. We'll go to the closest possible integer multiplication, which is 1440x810 with the integer being 3, and then we'll see how many pixels are left horizontally and vertically. In this case horizontally it fits perfectly, but vertically there's about 90 pixels left. So what we do is draw the canvas offset by 45, and then draw our borders above and below it, each covering the entire size of the screen in width, and with 45 as height. The problem with this approach is that it can look a little weird.

66. The game I chose was FTL. The base resolution of it appears to be 1280x720, as this image shows:

This game deals with resolution issues in 4 ways. The first is that the game is just not fullscreened and happens entirely on a window that's smaller than the monitor. This is the easiest solution. The second is that it stretches the screen to fit the full screen size. This makes the game look a little ugly. The third is that it uses horizontal and vertical borders and keeps the game at the minimum resolution possible, that looks like this:

Because my full screen size is 1920x1080, the base resolution of 1280x720 only fits once, which means that it can't be expanded further, so the black borders cover the rest of the screen. This solution looks off too. The final solution is to just fullscreen the game and force a friendly native resolution on the computer of the player whenever the game is fullscreened. This essentially changes the monitor's resolution when the game has to focus to a certain target size.

Most of these solutions are not ideal but they're about the best that can be done. Most pixel art games that I've seen face issues like this and there's no one size fits all solution that is perfect. It's all about compromises here and there.

67. If the y component of gravity is changed to 512 then the Player object will simply fall down outside of the screen. If it was changed to -512, for instance, then it would just go up. The same applies to the x component but it would change from left/right instead.

68. The third argument serves to tell if bodies can sleep or not. Bodies sleep when they stop moving completely and so sleeping bodies aren't updated by the physics engine. If that argument is set to true then the bodies will be allowed to sleep, otherwise they won't. By default that argument is always set to true.

The advantages that come with letting bodies sleep is that once they're slept no computation will be spent on them, which means that performance will be better. The disadvantage, although a small one, is that sleeping bodies will only be woken by collisions, which means that in certain situations where they should move they won't. For instance, it can happen sometimes that if the ground the body was standing on is removed the sleeping body won't fall like it should and will remain asleep, just floating in the air. This happens rarely though and is mostly not a concern.

As for not letting bodies sleep, I can't think of any real advantages other than countering the small disadvantage that can happen when bodies sleep in certain situations. The disadvantages though is that all is that all bodies will be computed always, which generally means lower performance.

69.

 

70. The acceleration attribute does not necessarily need to exist. Generally the reason it does is because it makes acceleration and deceleration of the object smoother, which feels slightly better than if the object's velocity went from 0 to 100 in one frame.

The update function would look like this if it didn't exist:

 

v would be a variable that never really changes based on a, so the previous line before setLinearVelocity wouldn't need to be there.

71. The target position is:

 

This is a simple use of the pattern that I explained previously.

Generally whenever you want to get a position B that is distance units away from position A such that position B is positioned at a specific angle in relation to position A, the pattern is something like: bx = ax + distance*math.cos(angle) and by = ay + distance*math.sin(angle)

72. This exercise is asking for how to get to yet another point given that all you have between the points are distances and angles to work with. We can start with by stating what we know, which is the position of B:

 

Now we want to know the position of C given that we have a distance and an angle to point to it from B. So what we can do is this:

 

And this follows the same logic as the last exercise. But we also know what Bx, By parses to, and when we substitute those values we get this:

 

And this turns out to be the final result and the position of C.

73. In general, when you want to get to some other point and all you have is an initial point and intermediate points with distances and angles between each other describing them, you just add them all together like was done in the previous exercise. Add all distance*math.cos(angle) pairs for the x component, and add all distance*math.sin(angle) pairs for the y component.

This only works if the points are described exactly like this (in distances and angles between one another). But it turns out that most of the time you want to place things in relation to some initial point A, you'll work towards the destination in terms of distances and angles like this because it's the easiest thing to do. We'll see a real example of this in action in the next article so hopefully it becomes less abstract than this.

74. In these situations where you have two representations of an object, you want to decide which representation dominates the other and in which circumstances. For instance, the Collider dominates the Player when it comes to its position. We update x and y based on the Collider's internal position, which means that we take information from it. However, when it comes to velocity the situation is reversed. We have a velocity and an angle that we defined and then we use setLinearVelocity to directly change the Collider, which means that we give information to it.

The case with the angle is one that is similar to the one with velocity except that we don't really do anything to change the Collider's angle. We define the angle in the Player object but then we do nothing about it because the angle of the Collider actually doesn't matter, since it's a circle.

Even if it wasn't a circle and it was a rectangle, let's say, we could just set setFixedRotation to true, which means that the physics object itself would never rotate and would always remain at a constant angle. And even if we didn't do that, the fact that the physics object itself rotates is somewhat irrelevant because we're setting its velocity directly, which means that whenever it collides with something it won't actually respond in the way you would expect it to behave because we're hijacking its velocity components for our own ends.

The point is, the angle of the Collider itself is kind of irrelevant and plays no part in the gameplay whatsoever, so it doesn't matter what happens to it.

75. This is a simple matter of binding f2 to a gotoRoom call:

 

76. As explained in the article, we can just call destroy on a room to destroy it. The additional thing we have to do here is set the current_room variable to nil otherwise the room won't be collected:

 

77. This is the amount of memory used before any rooms were created:

And this is after f2 and f3 were pressed exactly 10 times each:

It's about 8Kb bigger so it's not the same, but it's not something that I would worry about personally. With using a managed language there's always going to be some degree of leaking that you can't control and you shouldn't try to go super crazy over it. If you were leaking 200Kb per room then it would be a very serious problem, but this is not the case here so there's not much to worry about.

78. This is the amount of memory used before any rooms were created:

And this is after f2 and f3 were pressed exactly 10 times each:

This is about double the initial amount, so 400Kb bigger. Over only 10 rooms this would actually be something to worry about a lot and I actually am not quite sure why it's so much bigger. However, over the process of creating and deleting each room I checked to see what the memory usage was like (and you can try to do it yourself to see if it happens in the same way) and in every room it always oscillated between 800Kb and 1.17Mb, which means that for whatever reason for some rooms things weren't being collected until they were, but never below an initial 400Kb increase. I'm really not sure why this is the case but it never seems to increase beyond this value, even after creating and destroying hundreds of rooms, so it seems to be mostly fine.