Tutorials
Step-by-step guides to help you master SSOEngine and build amazing games
🎓 Learning Path
Follow these tutorials in order to build a solid foundation with SSOEngine:
Getting Started
Setup and your first game
Player Movement
Input and character control
Camera System
Following and effects
UI & Menus
Building interfaces
Tutorial 1: Getting Started
🎯 Your First SSOEngine Game
In this tutorial, you'll create a simple game that displays a colored window and responds to basic input.
Step 1: Setup Your Project
Make sure you have SSOEngine installed and working:
- Navigate to
SSOEngine/01_Core - Run
build.batto test the build system - Open
game.hin your favorite editor - Open
main.cppto see the entry point
Step 2: Understanding the Structure
The basic SSOEngine project has these key files:
main.cpp // Game entry point and main loop
game.h // Game logic and global variables
scripts/ // Game-specific code (player, enemies, etc.)
tools/ // Engine subsystems (camera, timer, UI, etc.)
assets/ // Your game assets (images, sounds, etc.)
build.bat // Automated build script
Step 3: Basic Game Loop
Let's examine the main game loop in main.cpp:
#include "raylib.h"
#include "game.h"
int main(void) {
// 1. Initialize window
const int screenWidth = 1280;
const int screenHeight = 720;
InitWindow(screenWidth, screenHeight, "My First Game");
SetTargetFPS(60);
// 2. Start game
Start();
// 3. Main game loop
while (!WindowShouldClose()) {
float deltaTime = GetFrameTime();
// Update phase
Update(deltaTime);
// Render phase
BeginDrawing();
ClearBackground(SKYBLUE);
// Draw game objects here
DrawGame();
EndDrawing();
}
// 4. Cleanup
CloseWindow();
return 0;
}
Step 4: Game Logic in game.h
Now let's look at game.h and modify it:
#ifndef GAME_H
#define GAME_H
#include "raylib.h"
// Game state
struct Player {
Vector2 position;
float radius;
Color color;
};
inline Player player = {{640, 360}, 20, BLUE};
// Game functions
inline void Start() {
// Initialize game here
player.position = {640, 360};
player.radius = 20;
player.color = BLUE;
}
inline void Update(float deltaTime) {
// Update game logic here
// Move player with arrow keys
if (IsKeyDown(KEY_RIGHT)) player.position.x += 5;
if (IsKeyDown(KEY_LEFT)) player.position.x -= 5;
if (IsKeyDown(KEY_UP)) player.position.y -= 5;
if (IsKeyDown(KEY_DOWN)) player.position.y += 5;
// Keep player on screen
player.position.x = Clamp(player.position.x, player.radius, 1280 - player.radius);
player.position.y = Clamp(player.position.y, player.radius, 720 - player.radius);
}
inline void DrawGame() {
// Draw game objects
DrawCircleV(player.position, player.radius, player.color);
// Draw some text
DrawText("Use arrow keys to move!", 10, 10, 20, BLACK);
DrawText(TextFormat("Position: %.0f, %.0f", player.position.x, player.position.y),
10, 40, 20, BLACK);
}
#endif
Step 5: Build and Run
Save your changes and run the build:
build.bat
Your game should now run with a blue circle that you can move with arrow keys!
✅ Congratulations!
You've created your first SSOEngine game! You've learned:
- Basic project structure
- Main game loop
- Input handling
- Basic rendering
- Building and running
Tutorial 2: Advanced Player Movement
🎮 Smooth Character Control
In this tutorial, you'll implement smooth player movement with acceleration, deceleration, and delta-time independence.
Step 1: Enhanced Player Structure
Update your player structure in game.h:
struct Player {
Vector2 position;
Vector2 velocity;
Vector2 acceleration;
float maxSpeed;
float accelerationRate;
float friction;
float radius;
Color color;
};
inline Player player = {
.position = {640, 360},
.velocity = {0, 0},
.acceleration = {0, 0},
.maxSpeed = 300.0f,
.accelerationRate = 1000.0f,
.friction = 800.0f,
.radius = 20,
.color = BLUE
};
Step 2: Physics-Based Movement
Implement smooth movement in the Update function:
inline void Update(float deltaTime) {
// Reset acceleration
player.acceleration = {0, 0};
// Apply input acceleration
if (IsKeyDown(KEY_RIGHT)) player.acceleration.x += player.accelerationRate;
if (IsKeyDown(KEY_LEFT)) player.acceleration.x -= player.accelerationRate;
if (IsKeyDown(KEY_UP)) player.acceleration.y -= player.accelerationRate;
if (IsKeyDown(KEY_DOWN)) player.acceleration.y += player.accelerationRate;
// Apply friction (opposing force)
if (player.velocity.x != 0) {
player.acceleration.x -= (player.velocity.x / fabs(player.velocity.x)) * player.friction;
}
if (player.velocity.y != 0) {
player.acceleration.y -= (player.velocity.y / fabs(player.velocity.y)) * player.friction;
}
// Update velocity (v = v0 + a*t)
player.velocity.x += player.acceleration.x * deltaTime;
player.velocity.y += player.acceleration.y * deltaTime;
// Limit to max speed
float speed = sqrt(player.velocity.x * player.velocity.x + player.velocity.y * player.velocity.y);
if (speed > player.maxSpeed) {
player.velocity.x = (player.velocity.x / speed) * player.maxSpeed;
player.velocity.y = (player.velocity.y / speed) * player.maxSpeed;
}
// Update position (x = x0 + v*t)
player.position.x += player.velocity.x * deltaTime;
player.position.y += player.velocity.y * deltaTime;
// Screen boundaries
player.position.x = Clamp(player.position.x, player.radius, 1280 - player.radius);
player.position.y = Clamp(player.position.y, player.radius, 720 - player.radius);
// Stop at boundaries
if (player.position.x <= player.radius || player.position.x >= 1280 - player.radius) {
player.velocity.x = 0;
}
if (player.position.y <= player.radius || player.position.y >= 720 - player.radius) {
player.velocity.y = 0;
}
}
Step 3: Visual Feedback
Add visual indicators for movement:
inline void DrawGame() {
// Draw trail effect
static Vector2 trailPosition[10];
static int trailIndex = 0;
trailPosition[trailIndex] = player.position;
trailIndex = (trailIndex + 1) % 10;
// Draw trail
for (int i = 0; i < 10; i++) {
float alpha = (float)i / 10.0f;
Color trailColor = Fade(player.color, alpha * 0.3f);
DrawCircleV(trailPosition[i], player.radius * (0.5f + alpha * 0.5f), trailColor);
}
// Draw player
DrawCircleV(player.position, player.radius, player.color);
// Draw velocity indicator
if (fabs(player.velocity.x) > 1 || fabs(player.velocity.y) > 1) {
Vector2 endPos = {
player.position.x + player.velocity.x * 0.1f,
player.position.y + player.velocity.y * 0.1f
};
DrawLineV(player.position, endPos, RED);
}
// Draw UI
DrawText("Smooth Movement Demo", 10, 10, 20, BLACK);
DrawText(TextFormat("Speed: %.1f",
sqrt(player.velocity.x * player.velocity.x + player.velocity.y * player.velocity.y)),
10, 40, 20, BLACK);
DrawText("Use arrow keys for smooth movement!", 10, 70, 20, BLACK);
}
Step 4: Add Diagonal Movement Fix
Prevent faster diagonal movement:
// In Update function, after applying input acceleration:
// Normalize diagonal input
if (player.acceleration.x != 0 && player.acceleration.y != 0) {
float length = sqrt(player.acceleration.x * player.acceleration.x +
player.acceleration.y * player.acceleration.y);
player.acceleration.x = (player.acceleration.x / length) * player.accelerationRate;
player.acceleration.y = (player.acceleration.y / length) * player.accelerationRate;
}
🎯 What You've Learned
- Physics-based movement with acceleration and friction
- Delta-time independent game logic
- Speed limiting and boundary collision
- Visual feedback with trail effects
- Diagonal movement normalization
Tutorial 3: Camera System
📹 Dynamic Camera Following
Learn to use SSOEngine's camera system for smooth following, zoom, and screen effects.
Step 1: Include Camera System
Add the camera header to game.h:
#include "raylib.h"
#include "tools/sso_camera.h"
#include "tools/sso_timer.h"
// Camera setup
SSO::Camera mainCam({0, 0}, 1280, 720);
// Game world (larger than screen)
inline Rectangle worldBounds = {-1000, -1000, 3000, 3000};
Step 2: Initialize Camera
Set up the camera in the Start function:
inline void Start() {
// Initialize player
player.position = {0, 0}; // Center of world
// Initialize camera
mainCam = SSO::Camera({0, 0}, 1280, 720);
mainCam.SetBounds(worldBounds);
mainCam.SetZoom(1.0f);
// Camera smoothing (0.0 = instant, 1.0 = very smooth)
mainCam.SetSmoothing(0.1f);
}
Step 3: Update Camera
Update camera to follow the player:
inline void Update(float deltaTime) {
// Update player movement (from previous tutorial)
UpdatePlayer(deltaTime);
// Camera controls
// Follow player
mainCam.Follow(player.position);
// Zoom controls
if (IsKeyPressed(KEY_EQUAL)) { // + key
float currentZoom = mainCam.GetZoom();
mainCam.SetZoom(currentZoom + 0.1f);
}
if (IsKeyPressed(KEY_MINUS)) {
float currentZoom = mainCam.GetZoom();
mainCam.SetZoom(currentZoom - 0.1f);
}
// Screen shake on spacebar
if (IsKeyPressed(KEY_SPACE)) {
mainCam.Shake(10.0f, 0.3f);
}
// Update camera state
mainCam.Update(deltaTime);
}
// Separate player update function
inline void UpdatePlayer(float deltaTime) {
// (Same movement code from previous tutorial)
// ... player movement logic ...
}
Step 4: Render with Camera
Use camera for game rendering:
inline void DrawGame() {
// Begin camera mode
BeginMode2D(mainCam.GetCamera2D());
// Draw world boundaries
DrawRectangleLines(worldBounds.x, worldBounds.y, worldBounds.width, worldBounds.height, GREEN);
// Draw grid for reference
for (int x = -1000; x <= 2000; x += 100) {
DrawLine(x, -1000, x, 2000, Fade(GRAY, 0.3f));
}
for (int y = -1000; y <= 2000; y += 100) {
DrawLine(-1000, y, 2000, y, Fade(GRAY, 0.3f));
}
// Draw player
DrawCircleV(player.position, player.radius, player.color);
// Draw some world objects
DrawRectangle(-200, -100, 100, 200, RED);
DrawRectangle(300, 200, 150, 150, YELLOW);
DrawCircle(500, -300, 80, PURPLE);
// End camera mode
EndMode2D();
// Draw UI (not affected by camera)
DrawText("Camera Demo", 10, 10, 20, BLACK);
DrawText(TextFormat("Position: %.0f, %.0f", player.position.x, player.position.y), 10, 40, 20, BLACK);
DrawText(TextFormat("Zoom: %.1f", mainCam.GetZoom()), 10, 70, 20, BLACK);
DrawText("Controls: Arrow Keys = Move, +/- = Zoom, Space = Shake", 10, 100, 20, BLACK);
}
Step 5: Advanced Camera Features
Add more sophisticated camera behavior:
// Add to Update function for dynamic zoom based on speed
float playerSpeed = sqrt(player.velocity.x * player.velocity.x + player.velocity.y * player.velocity.y);
float targetZoom = 1.0f + (playerSpeed / player.maxSpeed) * 0.3f; // Zoom out when moving fast
float currentZoom = mainCam.GetZoom();
mainCam.SetZoom(Lerp(currentZoom, targetZoom, 0.05f));
// Add look-ahead feature (camera looks where player is moving)
Vector2 lookAhead = {
player.velocity.x * 0.1f,
player.velocity.y * 0.1f
};
Vector2 targetPos = {player.position.x + lookAhead.x, player.position.y + lookAhead.y};
mainCam.Follow(targetPos);
🎯 Camera Features Learned
- Camera following with smooth interpolation
- Dynamic zoom control
- Screen shake effects
- Camera boundaries and constraints
- Look-ahead and dynamic zoom based on movement
- Separating world rendering from UI rendering
🚀 Next Steps
Continue Your Journey
You've completed the basic tutorials! Here's what to explore next:
📚 Advanced Topics
- • Enemy AI and behavior
- Collision detection system
- Particle effects and animations
- Audio and sound management
- Save/load game systems
🎮 Game Ideas
- • Platformer with physics
- Top-down shooter
- Puzzle game with timers
- Racing game with camera
- RPG with inventory system