This is a part of a series of notes. You can find notes to other lectures here Please feel free to bring to my attention mistakes, other interesting resources and feedback via the comments section. I’m all ears and will do my best to incorporate them into the blog post.
- Some Games Made Using Love - The list is quite interesting. If you feel Love and Lua aren’t worth your time, please do have a look.
- What is VSync? - Read the first three paragraphs.
- 2 Min Video on Anti Aliasing.
- Nearest Neighbour Interpolation - The Wikipedia article is actually quite understandable. This is used to create the box like retro feel.
- State Machines - Amazing series on the “Theory of Computation”. The First 2-3 videos should suffice to get an intuitive feel of State Machines.
- Table Syntax in Lua - Have a quick glance if you have the time.
- Ternary Operator in Lua.
- Simple Algorithm to Generate Random Numbers - If you are interested in getting a feel of how random numbers are generated. This will no way help you with this particular course though.
Love 2D is a game development framework written in C++ which uses Lua as it’s scripting language. Fast prototyping and support all platforms (even web and mobile).
It is essential to understand Game Loops. Games are an infinite loop which keep collecting user input (key presses, mouse movement, joystick movement), process them and render changed stuff on the screen. All this keeps happening as long as the user keeps playing. Link Mentioned in the Slides.
2D Coordinate System
2D Games use a 2D Coordinate System that is very similar to the one you are very familiar with. The origin is at the top left corner of the screen though and Y keep increasing as you keep going down.
Pong-0 “The Day-0 Update”
You can follow along the progress of the game by opening
pong-1 and so on in order. Small incremental changes are made in every update.
love.load() -- This is used for initialising stuff like game state. -- [[ Called every frame. dt is given the value since -- the elapse of the last frame.]] love.update(dt) love.draw() -- called each frame. Use it to draw things. -- Draw stuff on the screen. Can also align stuff love.graphics.printf(text, x, y, [width], [align]) -- Window related stuff. fullscreen, vsync, window resizable or not love.window.setMode(width, height, params) -- Love 2D looks for a main.lua file to execute the game
function love.draw() love.graphics.printf( 'Hello Pong!', -- text to render 0, -- starting X (0 since we're going to center it based on width) WINDOW_HEIGHT / 2 - 6, -- starting Y (halfway down the screen) WINDOW_WIDTH, -- number of pixels to center within (the entire screen here) 'center') -- alignment mode, can be 'center', 'left', or 'right' end
6 is subtracted (line number 5 in the above snippet) to accommodate space (height) taken by the font i.e 12 units.
Check resources for VSync.
Pong-1 “The Low Res Update”
View Slide #21 for some more functions.
Steps are taken to give the font a retro like feel. I don’t understand how texture filtering algorithms work but in simple terms they decide how to scale textures. You have to choose the best filter for your needs. Textures like images lose quality and become blurry when zoomed in or may seem quirky when scaled down.
He showed a few texture filters and an appropriate filter that can give the desired retro fill.
They also start using a simple library called
Push that takes care of scaling and resizing according to window dimensions. The official repository says push is a simple resolution-handling library that allows you to focus on making your game with a fixed resolution. You basically design the game for one screen size (called virtual width and height in the lecture) and push automatically scales stuff so that they look consistent on various screens. You can checkout the GitHub repository for more details.
Check the resources section and read about anti aliasing and nearest neighbour interpolation.
Here's some extra information about filters from an authoritative source.
- nearest-neighbour filtering is always used to preserve pixelation when downscaling and upscaling textures. Information is often lost while downscaling but it’s always used to give those boxes (retro like feel).
- bilinear filtering interpolates between pixels to create subpixels when upscaling particularly, resulting in blurriness.
Pong-2 “The Rectangle Update”
See slide #25 for the documentation of the functions used.
Fonts are immutable objects in Love 2D. So you have to load your font with respective sizes. You can’t use the same font with different sizes with one load. It probably loads the font with the specified size and stores it as some texture hence the need to load the font again even if you just want to change the size.
Every time you print something to the screen using printf, it uses the last set colour and font. It’s very similar to the way OpenGL operates.
Check the resources section and read about State Machines.
Pong-3 “The Paddle Update”
We poll to check if certain keys are held down every frame. It is important to understand key presses are not the same as holding the key. If the movement keys are held on we move the paddle by changing it’s X and Y positions. Also we need to store the X and Y coordinates of the paddle in variables.
PaddleSpeed is set to a value 200. To ensure that the speed of movement remains constant at all Frame Rates we don’t move it by a fixed value. Instead we want a smooth and even movement. So we multiple 200 by the dt (in fraction of a second) between each frame. Say our system is quite slow and the frame gets updated only 40 times every second so dt is 0.025 seconds. 0.025 * 200 gives 5. So the paddle ends up moving 5 units every frame.
You have to instantiate the same font with the sizes you want to use it. Say you want to use ‘Arial’ in the heading and also for smaller text you will have to load it twice with the respective sizes. Love 2D probably converts a font into some kind of texture once they are loaded. So remember that fonts are immutable in Love 2D.
Pong-4 “The Ball Update”
Pseudorandom generators are used to initialise the position of the ball. They normally use the time as the seed. Time is represented using a large number and therefore is a nice seed.
Math.max() is used to confine the paddle within the boundaries of the box. The position is updated either to the current position + delta movement or to the value where it touches the box. You can check lines 84 - 91 for the implementation.
Also the ball’s dx and dy can be changed randomly to deflect the ball at different angles.
A variable called gamestate is introduced to uniquely identify each state. For instance a key might do different things when the game is paused as against when the user is actually playing. An open menu, the pause screen, the victory screen are all examples of different states that a game can transition to.
Check the resources section and read about Pseudorandom Generators.
Pong-5 “The Class Update”
Classes:- Kind of similar in spirit to structs in C. Relevant variables are grouped together with associated functions. They help write modular code especially when things have an internal state to maintain. Bundles of data and code that are together. All variables related to a particular object (Ball, Paddle) and functions (move, collision detection etc) are refactored to their respective Classes.
The native way of using Classes in Lua is quite complex and isn’t similar to other programming languages that you might be familiar with. So they use a library called Class to create an abstraction that can enable you to use classes in a similar spirit.
Also it’s a convention in programming to capitalise the first letter of Class names and class related files for easy identification.
Pong-6 “The FPS Update”
.. operator can be used to concatenate strings in Lua.
He starts displaying the number of frames our computer can render every second. It takes a second or two to calculate the value before displaying the correct FPS.
Pong-7 “The Collision Update”
Axis Aligned Bounding Boxes. It works when objects aren’t rotated and it’s an extremely simple collision detection algorithm. The axis of the object should perfectly align with our main axis. No edge of the desired box should exceed or cross the edge of the other box.
- The ball accelerates as the game progresses. It’s done by the way of multiplying dx by 1.03 every time we detect a paddle collision.
- When we detect a collision we abruptly shift the ball by 4-5 units in the other direction. Remember that we run 60 frames per second and in the future frames it might be possible that the paddle continues to collide, direction is changed again and this cycle might continue endlessly.
- Change in dy value for randomness in angles.
Pong-8 “The Score Update”
It was skipped. He accidentally updated Pong-9 with the relevant code.
Pong-9 “The Serve Update”
- Started keeping track of game score.
- Thinking of games as State Machines are quite useful.
- Can be used to thoroughly inspect, understand transitions and refactor the game at large. Also can be used to represent all actions and possible screens. Transitions between states happen when user gives an input (mouse click, keyboard presses etc).
Pong-10 “The Victory Update”
Score checking and transition to a new state called “done”. They use this state for distinguishing it from other states and thus enable different behaviour using if statements. Larger applications use modular ways to handle states. In the case of this small application they could do away with strings.
Pong-11 “The Audio Update”
Static assets like audio files can be stored in memory and played or they can be streamed from a file every time you want to play. Normally smaller files are loaded into memory during initialisation as opening files and streaming them is a slower process. For larger audio tracks it’s better to stream them as they can be many megabytes in size and might increase RAM usage unnecessarily.
You could even loop music so that it plays infinitely for cases like background music for the entire game or a particular level.
He also recommends using an application called Bfxr to create simple game audio.
Check the resources section and read about Lua Tables.
Pong-12 “The Resize Update”
Push takes in a texture and scales it. It needs a reference height and width to preserve aspect ratio. It also letter boxes (add some margin or padding) in case one of the dimensions is too large. This way you can avoid distorted aspect ratios and make your game look decent in all device sizes.