Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
336 changes: 336 additions & 0 deletions _posts/2025-05-13-GameDev-Raylib-CLion.markdown
Original file line number Diff line number Diff line change
@@ -0,0 +1,336 @@
---
layout: post
title: "Level Up Your C++ Game Dev: raylib, the Free CLion, and Conan!"
description: "Explore how to use raylib for game development with the newly free CLion for non-commercial use and manage dependencies with the Conan C++ package manager plugin."
meta_title: "Level Up Your C++ Game Dev - Conan Blog"
categories: [cpp, gamedev, clion, conan, raylib]
---

Great news for C++ enthusiasts and aspiring game developers! JetBrains [recently
announced](https://blog.jetbrains.com/clion/2025/05/clion-is-now-free-for-non-commercial-use/)
that **CLion**, their C++ IDE, is now **free for non-commercial use**!

This is the perfect opportunity to dive into game development with C++ using
**Conan** and **CLion**. In this post, we'll explore [raylib](https://www.raylib.com/),
a simple and fun C library for game programming. We'll show you how to set up a
project for a small [runner game](https://en.wikipedia.org/wiki/Endless_runner)
and manage its dependencies seamlessly using the [CMake
presets](https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html)
generated by Conan.

<div style="text-align: center;">
<img src="{{ site.baseurl }}/assets/post_images/2025-05-13/jump-to-survive.gif"
alt="Jump to Survive Mini-Game"/>
</div>

<br>

## About raylib

Created by Ramon Santamaria, **raylib** is an excellent choice for starting your
game development journey. It offers a straightforward, easy-to-use C library
ideal for beginners and rapid prototyping. It's cross-platform (Windows, Linux,
macOS, Android, HTML5, etc.) and uses hardware-accelerated OpenGL for rendering.
Some of its most relevant features include 2D/3D graphics, audio processing, a
powerful math module, input handling, and [extensive
examples](https://github.com/raysan5/raylib/tree/master/examples) to learn from.

## Our Project: A Simple Runner Game with raylib

To showcase **raylib** in action, we'll build a classic 2D runner game. The
player, a blue rectangle, must jump over red rectangular obstacles that approach
from the right. The goal is to survive as long as possible, with the score
increasing for each successfully avoided obstacle. To make it a bit more
challenging, the width of the obstacles and the space between them will be
randomized.

You can find all the code for the project in the Conan 2 examples repository. To
get the code, clone the repo and navigate to the example's folder:

{% highlight bash %}
$ git clone https://github.com/conan-io/examples2
$ cd examples2/examples/libraries/raylib/introduction
{% endhighlight %}

Before diving into the specifics of the code, it's helpful to understand
raylib's 2D coordinate system. By default, the origin (0,0) is at the **top-left
corner** of the window. The X-axis increases to the right, and the Y-axis
increases downwards. This is a common convention in 2D graphics libraries.

<div style="text-align: center;">
<img src="{{ site.baseurl }}/assets/post_images/2025-05-13/raylib-coordinate-system.png"
alt="raylib 2D Coordinate System"/>
</div>
<br>

Now, let's dive into the code.

### Code Structure and Game Loop Overview

Most games, including ours, follow a fundamental structure:

1. **Initialization**: Set up everything needed before the game starts (window,
variables, etc.).
2. **Game Loop**: The core of the game that runs repeatedly. In each iteration
(frame), we process user input, update the game world based on that input
and internal logic, and then draw the new state of the world.
3. **Cleanup**: Release resources when the game ends.

Here's a simplified overview of what happens in our `main()` function:

{% highlight cpp %}
initialize_everything(); // 1) SETUP – assets, variables, window…
while (game_is_running) // 2) GAME LOOP – repeats ~60 times per sec
{
float dt = time_since_last_frame(); // Get time difference for smooth updates

update_world(dt); // a) PROCESS INPUT + APPLY LOGIC + HANDLE PHYSICS
draw_world(); // b) RENDER the current state of the game
}
release_resources(); // 3) CLEANUP – free memory, close application
{% endhighlight %}

#### 1. Creating the World: Initialization

Every **raylib** game begins by setting up the main window. The `InitWindow()`
function defines its dimensions and title. Our player is a simple rectangle, and
we define its initial position (`x`, `y` from the top-left) and size, along with
variables for its physics. We also define the ground's vertical coordinate and
initialize variables for dynamically adding obstacles during the game loop.
Finally, we set a target frame rate using `SetTargetFPS()` for consistent game
speed.

{% highlight cpp %}
// --- Initialization ---
const int screenW = 800;
const int screenH = 450;
InitWindow(screenW, screenH, "Jump to Survive!"); // Create window

// --- Player Setup ---
Rectangle player = { 100, screenH - 80, 40, 60 }; // Define player: {x, y, width, height}
float vy = 0; // Player's vertical velocity
const float gravity = 1000.0f; // Downward acceleration
const float jumpImpulse = -450.0f; // Upward force for jump

// --- Ground Definition ---
const int groundY = screenH - 20; // Y-coordinate of the ground

// --- Obstacle Management ---
std::vector<Rectangle> obstacles; // To store active obstacles
float spawnTimer = 0.0f; // Timer for spawning new obstacles
float spawnInterval = 1.2f; // Initial interval between spawns
const float obstacleSpeed = 300.0f; // How fast obstacles move

// Parameters for randomizing obstacles
const float minSpawnInterval = 0.8f;
const float maxSpawnInterval = 1.6f;
const int minObsWidth = 40;
const int maxObsWidth = 120;

// --- Game State Variables ---
int score = 0;
bool gameOver = false;

SetTargetFPS(60); // Aim for 60 frames per second
{% endhighlight %}

#### 2. The Game Loop — Update First, Then Draw

The game loop is where all the action happens, frame after frame. We first
handle updates to the game state (movement, collisions) and then draw
everything.

**Player Movement and Physics**

The player's movement starts by checking for jump input using `IsKeyPressed()`.
If the spacebar is pressed and the player is on the ground, an upward
`jumpImpulse` is applied. Gravity is then applied consistently using `deltaTime`
(obtained from `GetFrameTime()`), which represents the time elapsed since the
last frame, ensuring physics are independent of frame rate. Finally, we check
for ground collision to prevent the player from falling through the floor.

{% highlight cpp %}
// Inside the main game loop, if (!gameOver)
if (IsKeyPressed(KEY_SPACE) && player.y + player.height >= groundY) {
vy = jumpImpulse; // Apply upward force for the jump
}

// Apply gravity
vy += gravity * dt; // Update vertical velocity
player.y += vy * dt; // Update player's y-position (positive Y is downwards)

// Ground collision check
if (player.y + player.height > groundY) {
player.y = groundY - player.height; // Snap player's bottom to ground level
vy = 0; // Reset vertical speed
}
{% endhighlight %}

**Obstacle Spawning and Management**

Obstacles are managed in a `std::vector`. We use a `spawnTimer` and
`spawnInterval` to control their appearance. To add unpredictability, both the
`spawnInterval` for the *next* obstacle and the `width` of the *current*
obstacle are randomized using `GetRandomValue()`.

{% highlight cpp %}
// Inside the game loop, if (!gameOver)
spawnTimer += dt; // Increment timer
if (spawnTimer >= spawnInterval) { // Time to spawn a new one?
spawnTimer = 0.0f; // Reset timer
// Recalculate the next spawn interval randomly
spawnInterval = GetRandomValue(int(minSpawnInterval*100), int(maxSpawnInterval*100)) / 100.0f;
// Determine a random width for the new obstacle
int w = GetRandomValue(minObsWidth, maxObsWidth);
// Spawn obstacle at the right edge, resting on the ground, with the random width
obstacles.push_back({ (float)screenW, (float)(groundY - 40), (float)w, 40.0f });
}
{% endhighlight %}

**Obstacle Movement and Collision Detection**

Each obstacle in the `obstacles` vector is moved to the left based on
`obstacleSpeed` and `dt`. We use `CheckCollisionRecs()` to detect if the
player's rectangle collides with any obstacle rectangle. If a collision occurs,
the `gameOver` flag is set.

{% highlight cpp %}
// Still inside the game loop, iterating through obstacles
for (int i = 0; i < (int)obstacles.size(); i++) {
obstacles[i].x -= obstacleSpeed * dt; // Move obstacle left
if (CheckCollisionRecs(player, obstacles[i])) {
gameOver = true; // Set game over state upon collision
}
}
{% endhighlight %}

Obstacles that move completely off-screen to the left are removed from the
vector to save resources, and the player's `score` is incremented.

{% highlight cpp %}
// After iterating through obstacles
if (!obstacles.empty() && obstacles.front().x + obstacles.front().width < 0) {
obstacles.erase(obstacles.begin()); // Remove the first (oldest) obstacle if off-screen
score++; // Increment score
}
{% endhighlight %}

**Drawing the Scene**

All drawing operations must occur between `BeginDrawing()` and `EndDrawing()`.
`ClearBackground()` wipes the previous frame. Then, we use raylib's `Draw...`
functions to render the ground, player, obstacles, and text elements like the
score and game over message. `TextFormat()` is useful for creating strings with
dynamic content. You can find more drawing functions in the [raylib
cheatsheet](https://www.raylib.com/cheatsheet/cheatsheet.html).

{% highlight cpp %}
// This entire block is inside the main while(!WindowShouldClose()) loop
BeginDrawing(); // Start the drawing phase for the current frame
ClearBackground(RAYWHITE); // Clear the screen to a background color

DrawRectangle(0, groundY, screenW, 20, DARKGRAY); // Draw the ground
DrawRectangleRec(player, BLUE); // Draw the player

// Draw all current obstacles
for (auto &obs : obstacles) {
DrawRectangleRec(obs, RED);
}

DrawText(TextFormat("Score: %d", score), 10, 10, 20, BLACK); // Display current score

// If game is over, show the game over message
if (gameOver) {
DrawText("GAME OVER! Press R to restart", 200, screenH/2 - 20, 20, MAROON);
}
EndDrawing(); // End the drawing phase and display the frame
{% endhighlight %}

**Game Over and Restart Logic**

When `gameOver` is true, the main game update logic is skipped. If the player
presses 'R' (`IsKeyPressed(KEY_R)`), the game state is reset: player position,
velocity, obstacles are cleared, timers and score are reset, and `gameOver` is
set back to `false`. Resetting `spawnInterval` to a default value ensures a fair
restart.

{% highlight cpp %}
// Inside the game loop, in the 'else' branch of 'if (!gameOver)'
if (IsKeyPressed(KEY_R)) {
// Reset all necessary game variables to their initial state
player.y = screenH - 80; // Reset player's Y position
vy = 0; // Reset vertical velocity
obstacles.clear(); // Remove all obstacles
spawnTimer = 0.0f; // Reset spawn timer
spawnInterval = 1.2f; // Reset spawn interval to initial/average
score = 0; // Reset score
gameOver = false; // Set game state back to active
}
{% endhighlight %}

#### 3. Cleanup

Finally, when the game loop (the `while` loop) exits, `CloseWindow()` is called.
This is essential for properly releasing all resources used by raylib, such as
the OpenGL context and any loaded assets.

{% highlight cpp %}
CloseWindow(); // Unload all loaded data and close the game window
return 0; // Indicate successful program termination
{% endhighlight %}

## Building and running our project

We have previously discussed working with Conan in **CLion** [using the Conan CLion
Plugin](https://blog.conan.io/introducing-new-conan-clion-plugin/). This time,
we'll demonstrate a different approach: manually invoking Conan to generate
CMake presets using the `CMakeToolchain` generator, and then letting CLion
detect and use these presets for building.

1. **Open Project**: First, start CLion and go to *File → Open* to open the
project folder you cloned from the examples repository.
2. **Generate Presets**: Open a terminal in the project's root directory (where
the `conanfile.py` is located) and run `conan install . --build=missing`.
This command will install **raylib** (building it from source if a
pre-compiled binary isn't available for your system in the Conan cache) and
generate the necessary CMake preset files (e.g., `CMakeUserPresets.json`).
3. **Reload CMake Project in CLion**: In CLion, right-click on the
`CMakeLists.txt` file in the project view and select "Reload CMake Project".
CLion should detect and load the presets.
4. **Select and Enable Preset**: Go to *CLion → Settings... → Build, Execution,
Deployment → CMake*. In the "CMake Presets" section, you should see the
presets generated by Conan (e.g., `conan-release` or `conan-debug`). Select
the desired preset (e.g., `conan-release`) to make it active for your build
configuration.
5. **Build and Play**: Now, click the Build button (hammer icon) in CLion. Once
the build is successful, click the Run button (play icon) to start the game!

## Next Steps: Your Turn to Create!

Now that you have the basic runner game up and running, the fun really begins!
This project serves as a great starting point. Consider these ideas to get you
started:

* **New Mechanics**: Transform the game into a "Flappy Bird" style by changing
obstacle spawning to create gaps and modifying player movement for repeated
"flaps".
* **Add Depth**: Introduce power-ups (like invincibility or higher jumps),
diverse obstacle types (circles, polygons, sprites with varied behaviors), or
a better scoring system.
* **Polish**: Enhance the game with improved visuals like textures, scrolling
backgrounds, particle effects, and sound effects.

<div style="text-align: center;">
<img src="{{ site.baseurl }}/assets/post_images/2025-05-13/flappy-loco.gif"
alt="Flappy Loco"/>
</div>

## Conclusion

Whether you're a student taking your first steps into coding, a hobbyist with a
cool game idea, or an open-source developer, now is a fantastic time to explore
what you can create. So, [download CLion](https://www.jetbrains.com/clion/),
grab **raylib** using Conan (either via the plugin or CMake presets), and start
building your dream game today!

Happy coding!
Binary file added assets/post_images/2025-05-13/flappy-loco.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.