Skip to content
Lesson 1.3Computer, Game, and Code Foundations

Game Fundamentals

Follow actions through a game's entities, checks, space, and time; distinguish live state, display copies, server decisions, saves, and bytes.

Lesson 1.3
Read or listenPrint chapter or book
About 39 minutes

Keyboard shortcuts

?
Open this guide
Alt + B
Show or hide the lesson list
Alt + O
Show or fold the lesson outline
Alt + N
Open or close notes
Alt + H
Toggle semantic colours
Alt + D
Toggle page brightness
Alt + C
Toggle code brightness
← →
Move through a focused code trace
Home End
First or final step in a focused trace
Escape
Close this guide or an open panel

Reader shortcuts pause while you type in a text field.

Cheatsheet for Lesson 1.3Key ideas, reference and questions

A one-page summary of “Game Fundamentals” you can keep beside your notes. Plain Markdown.

Lesson 1.3 — Game Fundamentals: cheatsheet

  • Remember: A class defines fields and methods. Each instance holds its own field values; the receiver identifies which instance a method uses.
  • Also useful: Bytes carry no label. The same four bytes could be live gold, a display copy, a saved value or a server result, so ask what role it plays.

Key terms

  • engine: An engine is reusable software for common game jobs.
  • instance: An instance is one particular use of a definition or resource in the running world.
  • comment: The text after // is a comment for the reader.
  • rendering: The work that makes an image of the current world is rendering.
  • event: The game can also announce a particular occurrence: “Ada collected coin 42.” Such an occurrence is an event.
  • game-data model: This chosen representation is a game-data model.

Formulas and patterns

  • player:; gold = 100; health = 80; max_health = 100

Review

  • Q: Why keep Ada and Bo as player records in a collection when both collect one coin? A: One loop can apply the same gold update to each player's record
  • Q: Why does editing a number on the screen not change how much gold a game thinks a player has? A: The screen only reports the state; the stored state and rules decide the outcome
Download cheatsheet (.md)

Lesson 1.2 followed a game action through the CPU and a simple loop. To understand why a displayed number changes, we need a model of the game around it: what the player does, which check or calculation runs, what the game remembers, and what it shows. The byte-level memory model comes just before our first scan in Lesson 1.9.

Our small game has players, coins, and a shop. Call one player Ada. We will follow her purchases and movement, then ask what the game must keep while she plays and what it must save when she leaves. The examples describe chosen checks and updates for this game; another game can model the same activity differently.

A game turns actions into a changing world

Section titled “A game turns actions into a changing world”

A player supplies input, such as clicking the Buy button or pressing a movement key. The game interprets that input as a requested action. It checks its current state—the information it remembers about the world—and calculates any accepted change. It then draws or plays an output that the player can perceive. A common game loop repeats those steps as play continues:

input: buy an item
update: can the player pay?
state: gold 100 → 75
draw: show Gold: 75
next input and update

The example takes the successful purchase path. If the player has only 10 gold and the item costs 25, the affordability check fails and gold stays 10. The screen reports what the game decided; it does not decide the purchase itself. Later in this lesson we will see why the screen may even hold a separate copy of that result.

The purchase checkChange the gold or the price. The comparison runs first; the subtraction happens only when it says the player can pay.

Compare first, then subtract only if allowed

The check

Stored values
gold 100
cost 25
Can the player pay?
100 ≥ 25: true

Accepted

Subtract
100 − 25 = 75
Gold afterward
75stored back in gold

100 ≥ 25 is true, so the branch runs: 100 − 25 = 75 is stored as the new gold.

Games can also update enemies, timers, motion, or music without a fresh button press. On each update, the game can subtract time from a countdown, move a projectile by its speed and elapsed time, or choose an enemy action. It then draws the changed world or plays a sound. An engine is reusable software for common game jobs. It commonly supplies the repeating loop, drawing, and loading resources such as images and sounds; the game supplies particular behavior, such as looking up an item price and rejecting a purchase when gold is below that price. Finding the right place to change a game starts by asking which side owns the behavior. Lesson 4.1 develops that boundary.

A game remembers more than one number. It might track one player’s gold, current health, and maximum health at the same time. How can it keep those values together and apply the same calculation to many players?

Suppose Ada starts with 100 gold, 80 health, and a maximum of 100 health. These values are part of the changing game state.

player:
gold = 100
health = 80
max_health = 100

The labels tell us what each value means. If the player buys an item for 25 gold, gold becomes 75, while health stays 80. If the player then takes 20 damage, health becomes 60, while gold stays 75. The changes belong to one player, but the purchase uses gold and the damage calculation uses health.

A group of related named values is a record. Each value inside it is a field. The record lets the program talk about a player’s gold without confusing it with that player’s health. It also gives later memory work a question to ask: which nearby stored values belong to the same player?

gold75
health60
max_health100
one player's related fields
After the purchase and the hit, three different values still belong to one player record.

Give things identities and responsibilities

Section titled “Give things identities and responsibilities”

Gold and health describe Ada, but the game also needs to distinguish Ada from another player, a coin, or the shop door. A thing the game treats as having its own identity is an entity. Depending on the game, entities can include characters, pickups, projectiles, doors, or even an invisible object that controls a round.

The game can assign Ada an identifier, such as player ID 17. The ID answers “which player?” Her gold and health answer “what is true about that player now?” Buying an item changes her gold; it does not turn her into a different player. A display name is another field and need not be unique. Two players called Ada can still have different IDs.

An entity can also need several kinds of information. Ada has health, money, a position, and a picture the game can draw. Games often group related information into components, each describing one responsibility:

Part of Ada’s model Information it needs Work that uses it
Health Current health and maximum health Apply damage or healing
Wallet Gold Check a purchase
Position Location in the world Move Ada and test nearby objects
Appearance Which image or model to use Draw Ada

A component here means a part of the game’s model, not necessarily one particular programming technique. One game might put all these fields in a single player record; another might keep separate component records associated with player ID 17. Both can implement the same gameplay. We need this distinction because the information visible on one character need not occupy one uninterrupted block of memory.

An entity also has a lifetime: the interval from its creation to its removal. A coin entity stops being available after Ada picks it up. A later coin can reuse its storage without being the same coin. Some games reuse IDs too, so an ID is meaningful only within the identifier assignment and reuse policy of that game. Later memory lessons develop how to recognize a currently valid entity; the first question is already useful: does this information still belong to the thing we intended to observe?

The game does not need a different image file for every coin. It can load one coin image and use it to draw many coin entities. An asset is a resource the game uses, such as an image, a three-dimensional model, a sound, a map, or a set of starting values. An instance is one particular use of a definition or resource in the running world.

Suppose two coins use the same image. One coin is beside Ada and one is near the shop. Each has its own identity, position, and availability. If Ada collects the nearby coin, that instance disappears and her gold rises by one. The coin image remains loaded, and the coin near the shop remains available. Changing a shared image can change how both coins look; removing one coin instance affects that one coin.

The same distinction applies to player setup. A character definition might specify a starting maximum health of 100. Ada’s current health of 60 is state belonging to her running instance. A definition’s default and an instance’s current value can be equal when the instance starts, then diverge during play.

A class defines fields and methods; an instance holds values

Section titled “A class defines fields and methods; an instance holds values”

In a language such as C++, a class describes a kind of object: which fields it has and which operations can use those fields. A function called through an object is a method. A player instance is one particular object created from that definition. Ada and Bo can use the same damage method while holding different health values.

This explanatory pseudocode names the receiver self: the specific instance whose method was called. It is a sketch, not runnable Rust or C++:

class Player:
gold
health
damage(amount):
self.health = max(0, self.health - amount)
Ada = Player(gold=75, health=60)
Bo = Player(gold=40, health=50)
Ada.damage(20) # Ada's health becomes 40; Bo's stays 50.

max(0, value) selects whichever is larger, so damage cannot make health negative in this model. Ada.damage(20) chooses Ada as self; it does not subtract 20 from every Player. A class definition does not mean that all instances share one set of field values. Games can also organize data using components rather than one class per entity, as we saw above.

Choose a player or a damage amount below. Read the result first if useful, then step through how the receiver selects the changed field.

One Player definition, two independent players. This step-by-step trace needs JavaScript. Here is the code and where the run ends.

class Player: gold, health
  damage(amount):
    self.health = max(0, self.health - amount)
Ada = Player(gold=75, health=60)
Bo = Player(gold=40, health=50)
target.damage(amount)

Ada now has 40 health. Bo keeps 50 health, and both gold fields stay unchanged.

Final values: target = Ada; amount = 20; Ada.health = 40; Bo.health = 50; self = Ada.

Read every step of the lesson’s example
  1. Step 1, line 4. The two instances start with different fields. Declaring Player did not make their health one shared value.

    target = Ada; amount = 20; Ada.health = 60; Bo.health = 50; self = not selected yet.

    Memory: Ada.gold = 75; Ada.health = 60; Bo.gold = 40; Bo.health = 50.

  2. Step 2, line 6. Calling Ada.damage selects Ada as self. The method body is shared; the receiver is specific.

    target = Ada; amount = 20; Ada.health = 60; Bo.health = 50; self = Ada.

    Memory: Ada.gold = 75; Ada.health = 60; Bo.gold = 40; Bo.health = 50.

  3. Step 3, line 3. Ada's health becomes max(0, 60 - 20) = 40. Gold is not used by this calculation.

    target = Ada; amount = 20; Ada.health = 40; Bo.health = 50; self = Ada.

    Memory: Ada.gold = 75; Ada.health = 40; Bo.gold = 40; Bo.health = 50.

  4. Step 4, line 6. Ada now has 40 health. Bo keeps 50 health, and both gold fields stay unchanged.

    target = Ada; amount = 20; Ada.health = 40; Bo.health = 50; self = Ada.

    Memory: Ada.gold = 75; Ada.health = 40; Bo.gold = 40; Bo.health = 50.

For the table below, add a second player called Bo. Bo has 40 gold and 50 health. Copying the first player’s field names into gold2, health2, and so on would work for two players, but every new player would demand more special-case code. Instead, the game can keep several player records in a collection:

Player Gold Health Maximum health
Ada 75 60 100
Bo 40 50 80

The table is a picture of two records, not a claim that RAM stores a table with headings. A compiled game stores values as bytes; the labels here tell us what each value means. Lesson 1.8 will show how a type tells code how to read those bytes.

Suppose both players collect one coin. We could write “Ada’s gold becomes 76; Bo’s gold becomes 41.” For many players, repeating one calculation is simpler:

for each player in the collection:
add 1 to that player's gold

That repeated pass is a loop. One pass handles Ada (75 becomes 76); the next handles Bo (40 becomes 41). The add-one calculation stays the same when the collection grows. This is the reason to introduce a collection and a loop, rather than a list of data-structure names to memorize.

add one coin
Ada: 75 → 76
Bo: 40 → 41
next player, if any

Class cheatsheet: a class defines fields and operations; an instance holds one object’s current values; the receiver selects which instance a method changes.

Rust gives a record type a name with struct. We can describe the three numeric fields from our example. This fragment declares a shape for player values; it does not yet run a purchase or create a player:

struct Player {
gold: u32, // Amount an accepted purchase can spend.
health: u32, // Current health after damage or healing.
max_health: u32, // Upper limit for a health update.
}

Player is the record type. Each line inside the braces names a field, then gives its type after a colon. u32 means a non-negative whole number held in 32 bits, or four of the eight-bit bytes from Lesson 1.2. The comma separates fields. This declaration describes the shape of a player; it does not create Ada or Bo. The text after // is a comment for the reader. It explains a field’s purpose and does not add another field or change the program’s behavior. Lesson 1.4 will create values, change fields, and write a loop in Rust.

A game’s actual layout may differ from this source-level sketch. A memory tool has to infer fields from the bytes and the instructions that use them. Lessons 3.8–3.10 develop that recovery in detail, including alternative layouts and objects that move or disappear.

Checks keep health inside its permitted range

Section titled “Checks keep health inside its permitted range”

Having fields together also lets the game check relationships between them. Ada’s health of 60 fits her maximum of 100. A health value of 120 with a maximum of 100 would exceed the permitted range

0 <= health <= max_health

Here, <= means “less than or equal to”: health must be at least zero and no greater than the maximum. Our damage and healing calculations should keep it inside that range.

We do not need a new data structure to express this condition. It is a relationship the game should preserve when it applies damage or healing. Lesson 1.4 keeps both fields in its working player record. Later chapters call a condition that must remain true an invariant and show how to test one when recovering a game’s data.

An address alone does not prove that a player record is still the same player. The game can remove a player and use that memory for someone else. For this first pass, remember that an address tells you where to look now; it does not guarantee an object’s identity forever. Lesson 3.10 develops ways to recognize identity and lifetime.

We have said “Ada buys an item,” but a computer first receives something more mechanical: a mouse click, a key press, or a controller button. The game maps that input to an action, such as “buy this item” or “move right.” Keeping the action separate from its physical input allows the same purchase handler to work with several devices and different key bindings.

An action is a request. The purchase handler still compares the price with Ada’s gold before accepting it. Context matters too: pressing the right arrow might move Ada during play but select the next item while a shop menu is open. The same physical input can request different actions in different parts of the game.

A game also distinguishes a button being pressed now from being held down. In our game, one press of the buy button requests one purchase. Holding the movement button requests continued movement. If the purchase handler ran on every update while the button was held, one press could accidentally buy many items. This makes the input’s timing part of the behavior we need to understand.

To move Ada, the game needs a way to describe where she is. In a small two-dimensional world, use two numbers: x for horizontal position and y for vertical position. Together, (x, y) are coordinates. Our example chooses positive x toward the right, positive y upward, and one unit as one tile’s width. Another game can choose these conventions differently.

Ada starts at (2, 3). Moving one unit right gives (3, 3): x changes from 2 to 3 and y stays 3. These are world coordinates, describing Ada’s location in the game. Her screen coordinates describe where her picture appears on the display. If the camera follows Ada, she can move through the world while staying near the middle of the screen. Moving the camera changes the view without moving Ada in the world.

Movement also takes time. Suppose Ada’s speed is 4 world units per second and she moves right for one quarter of a second. The distance is 4 × 0.25 = 1 unit. Starting at (2, 3), she ends at (3, 3) if nothing blocks her. This pseudocode shows the calculation:

# Inputs: x = 2, speed = 4 units/second, elapsed_seconds = 0.25.
distance = speed * elapsed_seconds
x = x + distance
# Result: distance = 1 unit and x = 3; y stays 3.

The # lines describe the inputs and result, and * means multiply. The elapsed time tells the game how much movement to apply. Lesson 4.4 adds a third axis and turns the difference between two positions into a distance and a direction; Lesson 7.1 follows a world position onto the screen.

The work that advances movement, health, timers, and other changing state is the simulation. The work that makes an image of the current world is rendering. One displayed picture is a frame. A simulation update is often called a tick. These names let us distinguish advancing the world from drawing it.

Why does the distinction matter? Suppose a game moves Ada one unit each time it draws a frame. A computer drawing 120 frames in one second would move her twice as far as one drawing 60. Our speed calculation instead asks how much game time passed. Sixty updates of 1/60 second each produce the same four units of unobstructed movement as 120 updates of 1/120 second each: both add up to one second.

A game can use the measured time since the last update, or advance the simulation in fixed steps, such as 1/60 second. With fixed steps, it can run zero, one, or several simulation updates while preparing the next picture. These are two useful scheduling models; real games also decide how to handle stalls and paused time. A frame count alone cannot tell us how many gameplay updates occurred.

This also explains change without input. Once a projectile has a speed, the simulation can move it on later ticks. A timer can count down, and an enemy can act. The player does not need to press a new button for each change. A pause menu might stop the simulation while still rendering the menu, so a visible animation is not proof that every game timer is running.

Contact tests can block movement, award gold, or apply damage

Section titled “Contact tests can block movement, award gold, or apply damage”

Our movement calculation proposes a new position. It has not yet checked whether Ada would pass through a wall or touch a coin. The game gives objects simplified shapes for detecting contact. The test for whether these shapes meet or overlap is collision detection.

For a simple worked example, treat Ada as a point starting at x = 2 and place a wall at x = 3. She requests movement to x = 4, keeping the same y. Looking only at her destination could miss the wall: the point at 4 is past it. Testing the movement from 2 to 4 finds the crossing. Our game then stops Ada just before the wall. Actual characters have width, but the point example shows why a path and a final position answer different questions.

Detecting contact and deciding its consequence are separate jobs. A wall blocks movement. A coin allows movement but awards gold and removes its own entity. A trap allows movement but applies damage. All three can begin with a contact test and then produce different changes to the world.

A physics system can calculate motion using speed, forces, gravity, and contact responses. A game can also use simple movement calculations without a general physics system. The drawn image and the collision shape may have different sizes or levels of detail, so touching an image is not always the same as touching the shape the contact test uses.

The same contact need not have the same outcome in every situation. Our game gives Ada three modes: alive, defeated, and respawning. At health zero, she changes from alive to defeated and movement requests are ignored. After a two-second wait, she enters respawning. Placing her at the spawn point and restoring her health makes her alive again.

A change between modes is a transition. A set of allowed modes and conditions and actions for moving between them is a state machine. Here, “state” names one behavior mode. Earlier, “game state” meant all the information the game remembers, including gold, health, position, and this mode. The mode is one part of that larger state. This game keeps Ada’s player identity through the respawn; another could remove her character entity and create a new one, and the visible respawn alone does not tell us which.

The game can also announce a particular occurrence: “Ada collected coin 42.” Such an occurrence is an event. An action requests something, current state describes what is true, and an event reports what happened. “Collect coin 42” is the request; “Ada has 76 gold” is the resulting state; “coin 42 was collected” is the event. If the coin was already removed, the game can reject a second request instead of awarding gold again. Lesson 4.9 turns repeated readings of state into events and shows when to act on one.

After Ada collects a coin, the game may store 76 as her current gold and also prepare the text "Gold: 76" for the screen. The text is derived from the number, but it is not necessarily the number that the purchase handler reads.

Ada collects one coin
gameplay gold: 75 becomes 76
purchase check reads 76
display text: Gold: 76

If someone changes only the display text to "Gold: 999", the screen may show 999 while the purchase check still sees 76. Changing a displayed copy can affect appearance without changing gameplay. This is why observing a matching number in memory is only the start of an investigation. Later lessons compare what changes after a purchase with what the game actually uses to decide whether the purchase is allowed.

In our local example, the purchase check reads Ada’s gameplay gold. That value is authoritative for the purchase: the check uses it as the amount Ada actually has. The display copy reports the result but does not decide it. Authority describes this role, not a special numeric type.

In a multiplayer game, a client is the program a player uses to take part. A server is a program that receives player requests over a connection, checks them against the shared game state it maintains, and sends accepted results to the participating clients. In a common shop design, the client sends the item ID Ada wants. The server looks up that item’s price, compares it with the gold it stores for Ada, and either rejects the request or subtracts the price and records the purchased item.

Suppose the server has 76 gold for Ada and the client’s display shows 999. The server accepts one 25-gold purchase and updates its value to 51. It sends the accepted result back to the client, which can then show 51. The display’s 999 did not give the server more gold. A network delay can also leave a client temporarily showing an older result, even when neither program has a bug. Different games divide responsibility differently, so ask which side has authority over each particular decision. Lesson 9.1 follows such requests and replies, including a client that shows a predicted result and then corrects it, and Lesson 9.2 checks a purchase against server-owned state.

The running game holds live state: what it needs for play now. Closing the game ends that running instance. To continue later, it writes selected information to storage. Keeping information beyond the running process is persistence; a save file is one way to provide it.

After Ada reaches 76 gold and 60 health, a save could keep those values, her maximum health, position, and which coins she has collected. It need not keep the pickup sound’s playback position or the prepared text "Gold: 76"; the game can recreate the display from gold when it loads.

Saving selects state at a particular moment. If the file stores 76 gold and Ada later spends 25 without saving again, live gold becomes 51 while the file still records 76. A save file and live state can disagree simply because they describe different moments.

Loading creates new running records. A saved player ID can help the game reconnect related information, but a past RAM address does not give the new process a valid player record. Lesson 10.1 follows a real save from running memory to a file and back.

We can now connect the meanings we have used. First, a designer chooses an idea from a real or imagined world: a character owns coins and can spend them. The game represents the information its calculations need as entities and fields, such as player ID 17 with 76 gold. This chosen representation is a game-data model. Ada’s health number likewise models how the game’s damage and healing calculations use; it is not a complete description of a person’s body.

To save that information or send it to another program, the game chooses a format: a specification of which fields are included and how to write and read them. A save might include gold and collected coins. A movement message might include only a player ID and the requested direction. They serve different purposes, so they need not contain the same information or resemble the live record’s layout.

An encoding assigns byte patterns to the chosen values. Here is a small invented format for a player gold record. It accepts IDs and gold amounts from 0 to 255, writes the ID first, and uses one byte for each field. Given player ID 17 and gold 76, it produces two bytes:

# Toy format: one byte of player ID, followed by one byte of gold.
meaning: player_id = 17 gold = 76
field order: player_id gold
hex bytes: 11 4C

Lesson 1.2 taught hexadecimal: 0x11 is 1 × 16 + 1 = 17, and 0x4C is 4 × 16 + 12 = 76. The reader of this toy format knows the first byte means an ID and the second means gold because the format says so. The two bytes alone do not label their own meanings. Writing the text digits "76" or choosing a four-byte number would require a different encoding. Real formats can include additional fields, larger values, and checks; this example only shows how a declared format gives bytes meaning.

Watch it happenFrom an idea about coins to the two bytes 11 4C

A world idea, a character owning coins, becomes a game-data model with player ID 17 and gold 76. Each number is copied into a slot of a two-byte format, then converted to hexadecimal with the arithmetic shown, giving the saved bytes 11 4C.

Read the steps
  1. A designer starts from an idea about a world: a character owns coins it can spend.
  2. The game keeps only the parts its rules need. Here that is player ID 17 and 76 gold. This chosen representation is the game-data model.
  3. To save or send this, the game picks a format: which fields to write, in what order. This toy format writes the player ID, then the gold, one byte each. Each number is copied into its slot.
  4. An encoding turns each number into a byte pattern. In hexadecimal, 17 is 1 × 16 + 1 = 0x11, and 76 is 4 × 16 + 12 = 0x4C, because C stands for 12.
  5. The file now holds the two bytes 11 4C. Nothing in the bytes says which one is the player ID; the format does.

Design chooses the model; a format and an encoding say how chosen information becomes saved or transmitted bytes.

The animation follows the meaning through those design choices. Live fields in RAM are already represented by bytes too; a game does not have to turn its whole world into a save file on every tick. In memory, code uses a known layout and types. In a save or message, code uses that format’s field order, widths, and encodings. Lesson 1.8 develops the memory representation in detail.

Keep two questions separate when investigating a value: how is it represented? and what role does it have? A number stored as four bytes could be current gameplay gold, a display copy, a saved value, or a server result. Its byte representation alone does not establish its authority or tell us when it was last updated.

Ada’s input requests an action. Purchase checks and damage calculations read her current state and update the relevant fields. Simulation advances that world through time; rendering shows a view of it. Identity tells the game which entity a value belongs to, components organize its responsibilities, and assets can be shared by many separate instances. A collection lets one loop visit several player records.

The gameplay amount, displayed text, server’s gold value, and saved value can all describe Ada’s gold while serving different roles. Understanding those roles comes before deciding what a matching number in memory means. The game model explains the information; its memory layout or save/message format explains how code represents it as bytes.

We have described the update in words. Lesson 1.4 turns the same player’s values and calculations into a small Rust program.

Knowledge check

Update several players

Why keep Ada and Bo as player records in a collection when both collect one coin?

Finished Lesson 1.3?

Mark it done and the lesson list shows a beside it. A chapter whose lessons are all done is marked done too. Progress is saved in this browser only.

Chapter 1: 10 lessons

This book grew out of the original Game Hacking Academy by attilathedud and has been rewritten as a beginner-first course. Like the original repository, its source is published under the Apache License 2.0.