I prompted AI (a cheap one) for a sequence of classic games to make.
The idea is that completing this progression should teach me (and you) the fundamentals of game development.
Let me start by explaining how I read code.
I am going to explain this as if I were teaching you, it feels more natural to write that way.
I am not trying to establish myself as an authority on programming.
We can use the first game in the sequence as an example.
Puzzle over this AI generated code for a bit:
#include "raylib.h"
#include <cstdlib>
#include <ctime>
int main() {
// Initialization
const int screenWidth = 800;
const int screenHeight = 600;
InitWindow(screenWidth, screenHeight, "Dot Game - Catch the Dot");
SetTargetFPS(60);
// Player dot
Vector2 playerPos = {screenWidth / 2.0f, screenHeight / 2.0f};
const float playerRadius = 15.0f;
const float playerSpeed = 200.0f;
// Target dot
Vector2 targetPos = {static_cast<float>(rand() % screenWidth),
static_cast<float>(rand() % screenHeight)};
const float targetRadius = 10.0f;
// Score
int score = 0;
// Seed random number generator
std::srand(std::time(nullptr));
while (!WindowShouldClose()) {
// Delta time for smooth movement
float deltaTime = GetFrameTime();
// Player movement
if (IsKeyDown(KEY_W))
playerPos.y -= playerSpeed * deltaTime;
if (IsKeyDown(KEY_S))
playerPos.y += playerSpeed * deltaTime;
if (IsKeyDown(KEY_A))
playerPos.x -= playerSpeed * deltaTime;
if (IsKeyDown(KEY_D))
playerPos.x += playerSpeed * deltaTime;
// Keep player within screen bounds
if (playerPos.x < playerRadius)
playerPos.x = playerRadius;
if (playerPos.x > screenWidth - playerRadius)
playerPos.x = screenWidth - playerRadius;
if (playerPos.y < playerRadius)
playerPos.y = playerRadius;
if (playerPos.y > screenHeight - playerRadius)
playerPos.y = screenHeight - playerRadius;
// Collision detection
if (CheckCollisionCircles(playerPos, playerRadius, targetPos,
targetRadius)) {
score++;
targetPos = {static_cast<float>(rand() % screenWidth),
static_cast<float>(rand() % screenHeight)};
}
// Drawing
BeginDrawing();
ClearBackground(RAYWHITE);
DrawText("Catch the moving dot!", 10, 10, 20, DARKGRAY);
DrawText(TextFormat("Score: %d", score), 10, 40, 20, DARKGRAY);
DrawCircleV(playerPos, playerRadius, BLUE);
DrawCircleV(targetPos, targetRadius, RED);
EndDrawing();
}
CloseWindow();
return 0;
}
Was it overwhelming?
Here's how I break it down when I read it:
#include stuff
int main() {
... do stuff ...
}
#include
stuff?Most programming languages I'm aware of have some way of importing, requiring, or including code from other sources. #include
is just how C++ and C do it.
When you are reading code you need to learn to compartmentalize it in your mind - take note of repeated patterns and obvious "blocks" of code - in C++ and C they are indicated by {}
brackets.
#include ...
Should just mean "We're going to be using stuff from somewhere else." - leave it at that.
Don't go digging into it until you need to.
main()
thing?This is the "main" function. C and C++ both look for a "main" function and execute that first.
It's a reusable block of code.
In C and C++ we know we are dealing with a function because of the ()
parenthesis following the name of it.
Note that sometimes there will be code between the parenthesis.
When you're reading a main function understand that this is the beginning and the end of the scope of this program. The "flow" of the program might leave the file you are currently on, but realize that all roads will lead back here (barring some error).
#include stuff
int main() {
...
while (!WindowShouldClose()) {
...
}
CloseWindow();
return 0;
}
You may have heard of a "black box" - the idea is that in a black box you do not have to understand how it works inside, you only need to be aware of the inputs and outputs of the system.
This is a useful way to look at code. Don't look deeper than the current level you are at. The level we are currently at is called the game loop. The while
keyword just means that "while" the code within the following ()
parenthesis results in "true" the loop should continue looping. The code within the {}
brackets will be repeatedly run until the "expression" no longer yields "true".
We can infer from the name of the functions being called that the game loop is primarily concerned with window management. And we know that games happen inside of windows. It therefore follows that the game loop controls the game. That's logic.
The game loop is going to require something to loop over - it does no good to do an action or sequence of actions repeatedly unless you have SOMETHING to do them on. In this case the code appears to specify window size, initialize the window, set a framerate, set up some data for the dots in the game, a player score, and a random number generator. But does this matter? No, not yet. At this stage of reading code its all about the high points, the broad strokes. Here's some more:
#include stuff
int main() {
...
while (!WindowShouldClose()) {
// Delta time for smooth movement
...
// Player movement
...
// Keep player within screen bounds
...
// Collision detection
...
// Drawing
...
}
CloseWindow();
return 0;
}
In this case we were lucky enough to have comments to establish our landmarks. But what if it had been written by my coworkers? Well, then there would be no comments! That's where pattern recognition comes in. I'll give one example:
if (IsKeyDown(KEY_W))
playerPos.y -= playerSpeed * deltaTime;
if (IsKeyDown(KEY_S))
playerPos.y += playerSpeed * deltaTime;
if (IsKeyDown(KEY_A))
playerPos.x -= playerSpeed * deltaTime;
if (IsKeyDown(KEY_D))
playerPos.x += playerSpeed * deltaTime;
We should be able to tell at a glance that a pattern is being applied. playerPos
keeps being mentioned, -=
and +=
keeps being used. if
keeps getting called. This is when I would go in and add comments to lighten my own cognitive load. Correction: This is when I would highlight the block of code and have my AI fill in comments to lighten my cognitive load.
It's handling input, limiting movement, and rendering the results of the computations.
Yeah thats just cleaning up and returning "0" to the operating system to inform it that the code executed with no errors.
So now we know that our program imports some code, sets up some stuff, loops over the stuff, then cleans up after itself. And if it was 1978 and we were making a game on our Commodore 64 for our little sister to play then we could call it a day.
But we're not.
And do you know what? This code sucks.
I'll have the AI make the necessary changes to compile it for HTML5 and let you experience it.
LATER - That's going to have to wait - I put some time into compiling for HTML5 - it's a pain - might as well document the journey.