PVG
1. Creating the Main Game Structure — Program Video Games

1. Creating the Main Game Structure

You may hear game developers say things like "you don't know the shape of the game until you work on it". Or, "iteration is the key to good game design".

Most of the time, the game design changes during development.

If the design changes, the code must change.

We must design code in a way that doesn't assume too much about the future of the design so that it can be changed easily.

That doesn't mean creating super generic functionality, but rather the opposite: looking a few steps ahead and creating exactly what we need to explore that "fog of war".

So, though I have planned out this vertical slice, there's a chance the design may change based on feedback and experience in the project.

With that in mind, let's plan a few steps ahead.

We're making a 2D metroidvania demo.

We have a pretty good idea of things we'll need based on other games.

Since we're using pixel art, a safe bet is to use a tile-based level approach.

We'll need zone data, sprites + a player character, movement, collision detection and resolution.

We know we'll probably want to add combat and enemies, though maybe some metroidvania design has no enemies.

That's enough to get us started with a kind of structure.

Let's consider what kind of memory lifetimes these things require.

Memory lifetime is the time and/or conditions that some data needs to be around for us to access. For example: Physics calculation results only need to live as long as the current physics update. The font data used for the UI needs to live as long as the program.

Zone data only needs to be loaded for as long as the player is in the zone.
Depending on the design, we may want to preload adjacent rooms/zones so there is no transition time.

For now we'll leave this a question mark and just put it in the permanent storage.

Player character is pretty much always a thing except in the main menu, so we'll put that into permanent storage.

Collision detection and resolution data is used immediately then discarded. We can put that into temporary storage.

We know there's stuff we want access to all across the game, so in my view there's only 2 sane ways to do this.

  1. Global variable
  2. Local variable passed by reference into every procedure that needs it

We're going to go with the global variable in this example.

In my opinion the dangers of globals are overblown.

We can use a debugger to watch for when any memory is updated.

Feel free to use the 2nd method if you aren't convinced.

So, without further ado, let's get our platformer set up.

Copying the first module folder to a new one called "platformer" yields this structure:

platformer/
├── assets
│   └── textures
│       ├── bg_clouds_back.png
│       └── tileset.png
└── src
    └── main.odin

I've removed most of the code:

package main

import rl "vendor:raylib"

WINDOW_WIDTH :: 1280
WINDOW_HEIGHT :: 720
ZOOM :: 2
BG_COLOR :: rl.BLACK

main :: proc() {
    rl.InitWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "Program Video Games!")

    camera := rl.Camera2D{
        zoom = ZOOM,
    }

    for !rl.WindowShouldClose() {
        rl.BeginDrawing()
        rl.BeginMode2D(camera)
        rl.ClearBackground(BG_COLOR)

        rl.EndMode2D()
        rl.EndDrawing()
    }
}

Next up, we'll look at creating a simple level.