Space Shooter Game with SDL2 and Odin - Part 1 - The Game Loop
This is part 1 in a series on how to create a simple Space Shooter game using SDL2 and the Odin programming language.
If you’re new to game programming, or any programming for that matter, it’s important to build small projects that you can finish in a short amount of time. Arranging a fast feedback loop will accelerate your learning.
You can checkout the first tag to see where we finish off.
git checkout part-1-game-loop
Or you can start from scratch and follow along.
SDL2 - Some Functions Used
We call this using the
SDL.INIT_VIDEO flag to initialize the video subsystem. The events subsystem is also initialized automatically.
Shuts down the initialized subsystems.
SDL.CreateWindow() && SDL.DestroyWindow()
There are a number of flags available for creating a window, but at this stage we'll only use
SDL.WINDOW_SHOWN which makes the window visible.
According to the documentation this particular flag isn't necessary, but I've put it here anyway for learning purposes.
SDL.CreateRenderer() && SDL.DestroyRenderer()
A Renderer is a rendering context for the life of the game. You only need ONE renderer for all graphics in your game.
SDL.CreateRenderer() accepts a number of flags to configure the renderer you want to create. At this point in my learning, I do not enable vsync so I can see the impact of using an enforced frame rate in my game loop.
SDL.GetPerformanceCounter() && SDL.GetPerformanceFrequency()
Used together, these functions allow us to get timestamps for the start and end of our game loop. These timestamps are used to enforce a frame rate, making things like physics easier to manage.
This frame rate timing is also used to modify movement. The "delta time" modifier ensures movement speed will be independent of frame rate speed. This is covered in other videos on the Handmade Games channel.
This function returns an array of bytes that tells us which keys are currently pressed on the keyboard.
We can then use the keyboard state to handle things like character movement.
Keyboard events like
KEYDOWN are queued.
SDL.PollEvent() gets the latest event and uses the data to populate the given
For now, we're just handling
QUIT events, but in the future we'll handle others like fire weapons, pause game, etc.
In a future “update and render” section of our game loop, we'll be drawing a scene in the background that won't be presented to the screen until it is finished (at the end our loop).
This function replaces or "flips" the old scene from the previous frame with the new one we just drew in the present frame.
SDL.RenderDrawColor() && SDL.RenderClear()
SDL.RenderClear() will clear the background scene to get it ready for the next drawing. The old scene will be cleared to whatever color is set by
Many code examples you'll find (like the one here) will demonstrate using
SDL.RenderPresent(). This is because it draws the entire scene using
SDL.RenderCopy() in a step distinct from the update portion of the code. We won't be doing that. Instead, we'll be updating and rendering our scene as close together as possible. This means we clear our background scene after the presentation, making it ready for our new scene at the beginning of our next frame.
Odin - Some Functions Used
For explanations of keywords, etc. look at comments in main.odin.
Not to be confused with
#assert() which is a compile-time assertion,
assert() exits the program and displays the given error string in the console when the given expression is false.
defer statement defers the execution of a statement until the end of the scope it is in.
println() prints a line to the console.
Game Loop Explained
Before the game loop starts, I initialize variables for my timestamps, any event, and the keyboard state.
game.perf_frequency = f64(SDL.GetPerformanceFrequency()) start : f64 end : f64 event : SDL.Event state : [^]u8
Before doing anything else, we get the
start time for the loop, and only after all the work is done do we get the
In between our
end points we perform the following operations in order:
Get our current keyboard state
Handle any input events
Update and Render
Updates and Rendering should only happen after current keyboard state and events are known. These variables will drive how we update our game state.
Finally, as mentioned above, updates and rendering should happen at the same time. Separating these operations arbitrarily has a big performance cost.
In the next post, I'll cover rendering our main player entity.
Subscribe to Handmade Games if you're interested in learning how to program games from scratch using SDL2 and the Odin programming language.