Room and Areas Answers

44. This questions asks for the implementation of 3 rooms and some way to change between them. From the example of a Stage room defined at the start of the article we can see that rooms are just objects with an update and draw function, and that those functions are called if the room is currently active. So first, for the room definitions:

 
 
 

These three should be created as CircleRoom.lua, RectangleRoom.lua and PolygonRoom.lua in the rooms folder. Then, similarly to how we automatically loaded the objects folder we should do it for the rooms one:

 

Then, finally, we can bind the keys with the functions needed to change from one room to another:

 

And then pressing f1, f2 or f3 should do this:

If the Simple Room setup is being used then whenever those keys are pressed, a new entire room is being created and the previous one is deleted. It's important to understand this idea.

45. In Unity Rooms are called Scenes. The functions around a Scene are here and they mostly appear to have the same functionality as explained previously. Although they have additional things such as merging scenes and asynchronous loading of scenes, things which we could do in Lua if necessary.


In Godot Rooms are also called Scenes. The functions around a Scene are here and they seem to be the same as well without anything that stands out. There are functions to change from scene to scene, as well as functions to reload scenes and then to communicate with the editor.


In HaxeFlixel Rooms are called States. The functions around a FlxState seem to be what would be expected, except with a few differences that are worth noting. For instance, it has persistentDraw and persistentUpdate functions, which will run even when the state is not an active one. This can be useful in a number of situations like the page explains.

The functions that switch/reload states are here in this sort of global object that has a bunch of assorted functionality in it. While a bit less organized than the previous engines, this generally works fine (and it's what we're doing as well, after all our state changing functions are global functions in a random file).


In Construct 2 Rooms are called Layouts. Construct 2 seems to be mostly UI based so it's hard to get a handle on how things work exactly, but changing between layouts can be done by using events, apparently. It seems hard to get more concrete information based on the documentation alone, though.


In Phaser Rooms are called States. And the functions that operate on states are on a StateManager. This is probably the most standard way of doing things I've seen. Note that each state here also has a reference to a Game object as well as a World object. The World object is the equivalent of an Area (although there's always only one world for Phaser, while in our case we can create multiple areas), and the Game object has no equivalent, but it would be everything that we defined and will define in the main.lua file.

46. The first game I'll focus on is Rogue Legacy. It's very similar to The Binding of Isaac in a number of ways. This will be a simplified version of the game loop since depending on the situation it's a bit different but this is a good enough approximation. At first there's a starting screen:

Then you get to choose the character you wanna play with:

Then you get to spend gold on your skill tree:

After that you get to spend gold on other stuff (you can also go back to the skill tree screen from here):

And then once in the game you go from room to room inside a dungeon filled with rooms:

And you do that until you die:

After this death screen then the game loop restarts.

The way I would separate this in terms of rooms is: MainMenu, CharacterSelect, SkillTree, PreCastle and Castle. Inside the castle, each room would be a persistent room of its own, like in Isaac, and then whenever the player reaches the end of one room that connects to another a transition to another room would be added. All rooms inside the castle are added previously with addRoom. All other rooms that are not in the castle can be achieved by just creating a new one with the appropriate data each time the player enters it.


The next game is One Finger Death Punch. This game is rather simple and straightforward. First there's a main menu:

Then you get to pick which game mode you wanna play:

After that there's an overworld that acts as a level select of sorts (as you beat one node you can advance to nearby ones until you go through the whole map):

Then after that the gameplay itself happens:

And once that's over there's a score screen:

The way I would divide this in terms of rooms would be: MainMenu, GameModeSelect, LevelSelect, Game and Score. The transition from each room to the next is rather straightforward and in the game it happens whenever the walls close in towards the center of the screen as a transition effect. All rooms can also use the simple mode instead of the persistent one and it would still work fine. The only room that could be persistent is the overworld map, but that can also work without being persistent so it's just a matter of preference really.

47. Whenever there are no more references pointing to a variable then eventually that variable will be collected. So if we want an object to be collected in Lua, all we have to do is set the variable that is holding that object to nil. If the object is being held inside a table then all we have to do is remove it from the table. If the object is both inside a table and being pointed to with a variable, then we must do both. If we just remove it from the table or just set the variable to nil then the object won't be collected.

Memory leaks can happen when the developer forgets to remove references to an object, like in the previous example of only removing it from the table that holds it or only setting the variable that points to it to nil. Preventing this from happening is mostly a matter of paying attention and constantly checking to see if there are any execution paths that consistently lead to abnormal memory usage. Further on in the tutorial I'll go over some code that can help with this.

48. The first thing needed is the creation of the Stage room with an Area in it:

 

And then the creation of the Circle game object:

 

As required, it inherits from GameObject, receives an area, position and optional arguments as arguments. Similarly, it calls the new and update functions from the its parent class. If you don't remember how that works go back to the OOP section in the last article.

After that we can add the functionality where the circle instance kills itself after 2-4 seconds. One of the challenges in doing this is that earlier I said we should only use love.math.random for getting random numbers, but this function only returns integers if we pass in a range. We want a number between 2-4 seconds that includes all non-integer values as well. The way to solve this is through this function (place in utils.lua):

 

love.math.random() returns a real number between 0 and 1 if no range is passed in, so what this function is doing is taking the min and max values, multiplying the 0-1 value by their difference and then offsetting by the lower amount. Essentially, lets say we called it as random(2, 4), then the love.math.random()*(max - min) + min line parses as [real value between 0 and 1]*(2) + 2, which means that if the value generated is 0.4, for instance, it would become 0.4*2 + 2 -> 2.8, which is a value between 2 and 4 line we wanted.

Anyway, now that this is sorted out we can go back to the Circle class:

 

And so this makes it that the instance kills itself after 2-4 seconds. After that we need to create one instance of Circle at a random position every 2 seconds:

 

And then after that all that's left is activating the Stage room in main.lua with gotoRoom('Stage').

49. This exercise is essentially asking us to do the Area/GameObject binding manually from scratch. First, we need to create a Stage room with no Area in it:

 

After this we need to create a Circle object that doesn't inherit from GameObject:

 

This is essentially just creating an object normally. The only thing we do here already is to make sure that the object has x and y attributes and that it is drawn as a circle. Now we need to add the functionality that can make these circle objects be added and removed from the Stage. This is just adding the same code that already exists in the Area object, but doing it manually and directly in the Stage room:

 

And with this, whenever dead is set to true inside the circle, it will be removed from the game. We also need to add the dead attribute to the Circle class itself:

 

With this, we can now add an instance of Circle to the Stage at a random position every 2 seconds:

 

And now to make the Circle instance kill itself after a random amount between 2 and 4 seconds:

 

And then after that all that's left is activating the Stage room in main.lua with gotoRoom('Stage').

50. The random function right now looks like this:

 

The first thing we wanna do is change it so that it can take only one value, and when only that value is taken then the number generated must be a random one between 0 and the value:

 

With this we get a value between 0 and min if only one value is passed in. The if not max conditional is simply checking to see if the max value was defined or not. If we call random(2) it means that max wasn't defined and it's nil, which means that not max will return true, which is what we wanted in the first place. Now for the next thing the question asks, we need to augment the function so that min and max can be in any order:

 

Here we simply add a conditional that checks if min is bigger than max, and if it is, then we just swap both values. In Lua you can perform an in-place swap of two variables because it supports multiple assignments.

51. local opts = opts or {} serves to define the opts variable locally even if it wasn't defined by the caller. For instance, the if caller does something like addGameObject('ClassName', x, y), the opts table will be nil, which is a problem because various things inside the GameObject class definition assume that opts exists as a table. To prevent complications from this we simply say that opts will be the opts variable that was passed in, or if none was passed in, then it will be an empty table. This works because of how the or operator works in Lua (which is something we went over in previous exercises and will go over in the future too, so make sure you understand why this is the case).