Code Examples
Ready-to-use code snippets and complete examples for SSOEngine
📋 Quick Navigation
🎯 Basic Examples
Window Setup and Basic Loop
#include "raylib.h"
#include "tools/sso_window.h"
int main() {
// Initialize window
SSO::Window window("My Game", 1280, 720);
window.SetVSync(true);
window.Center();
// Main game loop
while (!window.ShouldClose()) {
// Update logic here
// Rendering
BeginDrawing();
ClearBackground(RAYWHITE);
DrawText("Hello SSOEngine!", 10, 10, 20, BLACK);
EndDrawing();
}
return 0;
}
Basic Input Handling
#include "raylib.h"
struct InputState {
bool up;
bool down;
bool left;
bool right;
bool action;
Vector2 mousePosition;
bool mouseClicked;
};
inline InputState input;
void UpdateInput() {
// Keyboard input
input.up = IsKeyDown(KEY_UP) || IsKeyDown(KEY_W);
input.down = IsKeyDown(KEY_DOWN) || IsKeyDown(KEY_S);
input.left = IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_A);
input.right = IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_D);
input.action = IsKeyPressed(KEY_SPACE) || IsMouseButtonPressed(MOUSE_LEFT_BUTTON);
// Mouse input
input.mousePosition = GetMousePosition();
input.mouseClicked = IsMouseButtonPressed(MOUSE_LEFT_BUTTON);
}
// Usage in game loop
void UpdateGame() {
UpdateInput();
// Move player based on input
Vector2 movement = {0, 0};
if (input.left) movement.x -= 5;
if (input.right) movement.x += 5;
if (input.up) movement.y -= 5;
if (input.down) movement.y += 5;
player.position.x += movement.x;
player.position.y += movement.y;
}
Basic Drawing Operations
#include "raylib.h"
void DrawShapes() {
// Basic shapes
DrawCircle(100, 100, 50, RED);
DrawRectangle(200, 50, 100, 100, BLUE);
DrawTriangle({350, 150}, {400, 50}, {450, 150}, GREEN);
// Lines and borders
DrawLine(0, 0, 1280, 720, YELLOW);
DrawRectangleLines(500, 200, 150, 100, PURPLE);
// Text rendering
DrawText("Hello World!", 10, 10, 20, BLACK);
DrawText(TextFormat("Score: %d", score), 10, 40, 20, BLACK);
// Custom colors and transparency
Color customColor = {255, 128, 64, 255}; // Orange
DrawCircle(700, 100, 30, customColor);
// Transparent shapes
DrawRectangle(800, 50, 100, 100, Fade(RED, 0.5f));
}
void DrawWithTexture() {
// Load texture (usually done once at startup)
Texture2D texture = LoadTexture("assets/player.png");
// Draw texture
DrawTexture(texture, 100, 100, WHITE);
// Draw scaled texture
DrawTextureEx(texture, {200, 200}, 0, 2.0f, WHITE);
// Draw part of texture (source rectangle)
Rectangle sourceRect = {0, 0, 32, 32}; // First 32x32 pixels
DrawTextureRec(texture, sourceRect, {300, 300}, WHITE);
// Don't forget to unload when done
UnloadTexture(texture);
}
🎮 Game Mechanics
Smooth Player Movement
#include "raylib.h"
#include "tools/sso_math.h"
struct Player {
Vector2 position;
Vector2 velocity;
float speed;
float radius;
Color color;
};
inline Player player = {{640, 360}, {0, 0}, 300.0f, 20.0f, BLUE};
void UpdatePlayer(float deltaTime) {
// Get input
Vector2 input = {0, 0};
if (IsKeyDown(KEY_RIGHT)) input.x += 1;
if (IsKeyDown(KEY_LEFT)) input.x -= 1;
if (IsKeyDown(KEY_UP)) input.y -= 1;
if (IsKeyDown(KEY_DOWN)) input.y += 1;
// Normalize diagonal movement
if (input.x != 0 && input.y != 0) {
input = SSO::Math::Normalize(input);
}
// Apply acceleration
Vector2 acceleration = SSO::Math::Scale(input, 1000.0f);
player.velocity = SSO::Math::Lerp(player.velocity,
SSO::Math::Add(player.velocity, acceleration),
deltaTime * 10.0f);
// Limit speed
float currentSpeed = SSO::Math::Magnitude(player.velocity);
if (currentSpeed > player.speed) {
player.velocity = SSO::Math::Scale(player.velocity, player.speed / currentSpeed);
}
// Update position
player.position = SSO::Math::Add(player.position,
SSO::Math::Scale(player.velocity, deltaTime));
// 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);
}
Collision Detection System
#include "raylib.h"
#include "tools/sso_math.h"
#include
struct GameObject {
Vector2 position;
float radius;
Color color;
bool active;
};
inline std::vector enemies;
// Circle-circle collision
bool CheckCircleCollision(Vector2 pos1, float radius1, Vector2 pos2, float radius2) {
float distance = SSO::Math::Distance(pos1, pos2);
return distance < (radius1 + radius2);
}
// Rectangle-circle collision
bool CheckRectCircleCollision(Rectangle rect, Vector2 circlePos, float radius) {
// Find closest point on rectangle to circle center
Vector2 closest = {
Clamp(circlePos.x, rect.x, rect.x + rect.width),
Clamp(circlePos.y, rect.y, rect.y + rect.height)
};
// Check if closest point is within circle
float distance = SSO::Math::Distance(circlePos, closest);
return distance < radius;
}
// Line-circle collision
bool CheckLineCircleCollision(Vector2 start, Vector2 end, Vector2 circlePos, float radius) {
Vector2 lineVec = SSO::Math::Subtract(end, start);
Vector2 toCircle = SSO::Math::Subtract(circlePos, start);
float lineLength = SSO::Math::Magnitude(lineVec);
Vector2 lineDir = SSO::Math::Normalize(lineVec);
// Project circle center onto line
float projection = SSO::Math::Dot(toCircle, lineDir);
projection = Clamp(projection, 0, lineLength);
Vector2 closestPoint = SSO::Math::Add(start, SSO::Math::Scale(lineDir, projection));
float distance = SSO::Math::Distance(circlePos, closestPoint);
return distance < radius;
}
void UpdateCollisions() {
// Check player-enemy collisions
for (auto& enemy : enemies) {
if (enemy.active && CheckCircleCollision(player.position, player.radius,
enemy.position, enemy.radius)) {
// Collision detected!
TakeDamage();
enemy.active = false;
}
}
// Check bullet-enemy collisions
for (auto& bullet : bullets) {
if (!bullet.active) continue;
for (auto& enemy : enemies) {
if (enemy.active && CheckCircleCollision(bullet.position, bullet.radius,
enemy.position, enemy.radius)) {
bullet.active = false;
enemy.active = false;
CreateExplosion(enemy.position);
score += 100;
}
}
}
}
Advanced Camera Following
#include "raylib.h"
#include "tools/sso_camera.h"
#include "tools/sso_math.h"
SSO::Camera mainCam({0, 0}, 1280, 720);
void UpdateCamera(float deltaTime) {
// Basic following
mainCam.Follow(player.position);
// Dynamic zoom based on player speed
float playerSpeed = SSO::Math::Magnitude(player.velocity);
float targetZoom = 1.0f + (playerSpeed / player.maxSpeed) * 0.3f;
float currentZoom = mainCam.GetZoom();
mainCam.SetZoom(SSO::Math::Lerp(currentZoom, targetZoom, 0.05f));
// Look-ahead based on velocity
Vector2 lookAhead = SSO::Math::Scale(player.velocity, 0.2f);
Vector2 targetPos = SSO::Math::Add(player.position, lookAhead);
mainCam.Follow(targetPos);
// Screen shake on events
if (explosionActive) {
mainCam.Shake(15.0f, 0.3f);
explosionActive = false;
}
// Camera boundaries
Rectangle worldBounds = {-2000, -2000, 4000, 4000};
mainCam.SetBounds(worldBounds);
// Update camera
mainCam.Update(deltaTime);
}
void RenderGame() {
BeginMode2D(mainCam.GetCamera2D());
// Draw world grid
for (int x = -2000; x <= 2000; x += 100) {
DrawLine(x, -2000, x, 2000, Fade(GRAY, 0.3f));
}
for (int y = -2000; y <= 2000; y += 100) {
DrawLine(-2000, y, 2000, y, Fade(GRAY, 0.3f));
}
// Draw game objects
DrawPlayer();
DrawEnemies();
DrawBullets();
EndMode2D();
// Draw UI (not affected by camera)
DrawUI();
}
Simple Particle System
#include "raylib.h"
#include "tools/sso_math.h"
#include
struct Particle {
Vector2 position;
Vector2 velocity;
float life;
float maxLife;
Color color;
float size;
};
inline std::vector particles;
void CreateExplosion(Vector2 position, int count = 20) {
for (int i = 0; i < count; i++) {
Particle particle;
particle.position = position;
// Random velocity in all directions
float angle = (float)i / count * 2.0f * PI;
float speed = SSO::Math::RandomRange(50.0f, 200.0f);
particle.velocity = {
cos(angle) * speed,
sin(angle) * speed
};
particle.life = 1.0f;
particle.maxLife = SSO::Math::RandomRange(0.5f, 1.5f);
particle.color = {
(unsigned char)SSO::Math::RandomRange(200, 255),
(unsigned char)SSO::Math::RandomRange(50, 200),
0,
255
};
particle.size = SSO::Math::RandomRange(2.0f, 8.0f);
particles.push_back(particle);
}
}
void UpdateParticles(float deltaTime) {
for (auto& particle : particles) {
// Update position
particle.position = SSO::Math::Add(particle.position,
SSO::Math::Scale(particle.velocity, deltaTime));
// Apply gravity
particle.velocity.y += 200.0f * deltaTime;
// Apply friction
particle.velocity = SSO::Math::Scale(particle.velocity, 0.98f);
// Update life
particle.life -= deltaTime / particle.maxLife;
}
// Remove dead particles
particles.erase(
std::remove_if(particles.begin(), particles.end(),
[](const Particle& p) { return p.life <= 0; }),
particles.end()
);
}
void DrawParticles() {
for (const auto& particle : particles) {
float alpha = particle.life;
Color color = Fade(particle.color, alpha);
float size = particle.size * particle.life;
DrawCircleV(particle.position, size, color);
}
}
🕹️ Complete Game Examples
Pong Game
A complete Pong game implementation:
#include "raylib.h"
#include "tools/sso_camera.h"
#include "tools/sso_math.h"
#include "tools/sso_ui.h"
struct Paddle {
Rectangle rect;
float speed;
Color color;
};
struct Ball {
Vector2 position;
Vector2 velocity;
float radius;
Color color;
};
inline Paddle player1 = {{50, 300, 15, 100}, 400, WHITE};
inline Paddle player2 = {{1215, 300, 15, 100}, 400, WHITE};
inline Ball ball = {{640, 360}, {200, 150}, 10, WHITE};
inline int player1Score = 0;
inline int player2Score = 0;
inline bool gamePaused = false;
void InitPong() {
ball.position = {640, 360};
ball.velocity = {(float)GetRandomValue(-200, 200), (float)GetRandomValue(-150, 150)};
player1.rect.y = 300;
player2.rect.y = 300;
}
void UpdatePaddles(float deltaTime) {
// Player 1 controls (W/S)
if (IsKeyDown(KEY_W)) {
player1.rect.y -= player1.speed * deltaTime;
}
if (IsKeyDown(KEY_S)) {
player1.rect.y += player1.speed * deltaTime;
}
// Player 2 controls (UP/DOWN arrows)
if (IsKeyDown(KEY_UP)) {
player2.rect.y -= player2.speed * deltaTime;
}
if (IsKeyDown(KEY_DOWN)) {
player2.rect.y += player2.speed * deltaTime;
}
// Keep paddles on screen
player1.rect.y = Clamp(player1.rect.y, 0, 720 - player1.rect.height);
player2.rect.y = Clamp(player2.rect.y, 0, 720 - player2.rect.height);
}
void UpdateBall(float deltaTime) {
if (gamePaused) return;
// Update position
ball.position.x += ball.velocity.x * deltaTime;
ball.position.y += ball.velocity.y * deltaTime;
// Top and bottom wall collision
if (ball.position.y - ball.radius <= 0 || ball.position.y + ball.radius >= 720) {
ball.velocity.y = -ball.velocity.y;
ball.position.y = Clamp(ball.position.y, ball.radius, 720 - ball.radius);
}
// Paddle collisions
Rectangle ballRect = {
ball.position.x - ball.radius,
ball.position.y - ball.radius,
ball.radius * 2,
ball.radius * 2
};
if (CheckCollisionRecs(ballRect, player1.rect)) {
ball.velocity.x = fabs(ball.velocity.x); // Move right
ball.position.x = player1.rect.x + player1.rect.width + ball.radius;
// Add spin based on paddle hit position
float hitPos = (ball.position.y - (player1.rect.y + player1.rect.height/2)) / (player1.rect.height/2);
ball.velocity.y += hitPos * 100;
}
if (CheckCollisionRecs(ballRect, player2.rect)) {
ball.velocity.x = -fabs(ball.velocity.x); // Move left
ball.position.x = player2.rect.x - ball.radius;
// Add spin based on paddle hit position
float hitPos = (ball.position.y - (player2.rect.y + player2.rect.height/2)) / (player2.rect.height/2);
ball.velocity.y += hitPos * 100;
}
// Score detection
if (ball.position.x < 0) {
player2Score++;
InitPong();
}
if (ball.position.x > 1280) {
player1Score++;
InitPong();
}
}
void DrawPong() {
// Draw center line
for (int y = 0; y < 720; y += 20) {
DrawRectangle(635, y, 10, 10, WHITE);
}
// Draw paddles
DrawRectangleRec(player1.rect, player1.color);
DrawRectangleRec(player2.rect, player2.color);
// Draw ball
DrawCircleV(ball.position, ball.radius, ball.color);
// Draw scores
DrawText(TextFormat("%d", player1Score), 580, 50, 60, WHITE);
DrawText(TextFormat("%d", player2Score), 660, 50, 60, WHITE);
// Draw pause indicator
if (gamePaused) {
DrawText("PAUSED", 550, 350, 40, WHITE);
DrawText("Press SPACE to resume", 520, 400, 20, WHITE);
}
}
void UpdatePongGame(float deltaTime) {
// Handle pause
if (IsKeyPressed(KEY_SPACE)) {
gamePaused = !gamePaused;
}
UpdatePaddles(deltaTime);
UpdateBall(deltaTime);
}
int main() {
InitWindow(1280, 720, "SSOEngine - Pong");
SetTargetFPS(60);
InitPong();
while (!WindowShouldClose()) {
float deltaTime = GetFrameTime();
UpdatePongGame(deltaTime);
BeginDrawing();
ClearBackground(BLACK);
DrawPong();
DrawText("ESC to quit", 10, 690, 20, GRAY);
EndDrawing();
}
CloseWindow();
return 0;
}
Space Shooter
Basic space shooter with enemies and shooting:
#include "raylib.h"
#include "tools/sso_camera.h"
#include "tools/sso_math.h"
#include
struct Bullet {
Vector2 position;
Vector2 velocity;
bool active;
float lifetime;
};
struct Enemy {
Vector2 position;
Vector2 velocity;
bool active;
float health;
float shootCooldown;
};
inline std::vector bullets;
inline std::vector enemies;
inline Vector2 playerPos = {640, 600};
inline float playerSpeed = 300.0f;
inline float shootCooldown = 0.0f;
inline int score = 0;
inline bool gameOver = false;
void InitGame() {
playerPos = {640, 600};
bullets.clear();
enemies.clear();
score = 0;
gameOver = false;
shootCooldown = 0.0f;
// Spawn initial enemies
for (int i = 0; i < 5; i++) {
Enemy enemy;
enemy.position = {(float)GetRandomValue(100, 1180), (float)GetRandomValue(50, 200)};
enemy.velocity = {(float)GetRandomValue(-50, 50), 0};
enemy.active = true;
enemy.health = 2.0f;
enemy.shootCooldown = 0.0f;
enemies.push_back(enemy);
}
}
void UpdatePlayer(float deltaTime) {
// Movement
Vector2 input = {0, 0};
if (IsKeyDown(KEY_LEFT)) input.x -= 1;
if (IsKeyDown(KEY_RIGHT)) input.x += 1;
if (IsKeyDown(KEY_UP)) input.y -= 1;
if (IsKeyDown(KEY_DOWN)) input.y += 1;
input = SSO::Math::Normalize(input);
playerPos = SSO::Math::Add(playerPos, SSO::Math::Scale(input, playerSpeed * deltaTime));
// Keep player on screen
playerPos.x = Clamp(playerPos.x, 20, 1260);
playerPos.y = Clamp(playerPos.y, 20, 700);
// Shooting
shootCooldown -= deltaTime;
if (IsKeyDown(KEY_SPACE) && shootCooldown <= 0) {
Bullet bullet;
bullet.position = playerPos;
bullet.velocity = {0, -500};
bullet.active = true;
bullet.lifetime = 2.0f;
bullets.push_back(bullet);
shootCooldown = 0.2f;
}
}
void UpdateBullets(float deltaTime) {
for (auto& bullet : bullets) {
if (!bullet.active) continue;
bullet.position = SSO::Math::Add(bullet.position,
SSO::Math::Scale(bullet.velocity, deltaTime));
bullet.lifetime -= deltaTime;
// Remove if off screen or expired
if (bullet.position.y < 0 || bullet.lifetime <= 0) {
bullet.active = false;
}
}
}
void UpdateEnemies(float deltaTime) {
for (auto& enemy : enemies) {
if (!enemy.active) continue;
// Movement
enemy.position = SSO::Math::Add(enemy.position,
SSO::Math::Scale(enemy.velocity, deltaTime));
// Bounce off screen edges
if (enemy.position.x <= 20 || enemy.position.x >= 1260) {
enemy.velocity.x = -enemy.velocity.x;
}
// Enemy shooting
enemy.shootCooldown -= deltaTime;
if (enemy.shootCooldown <= 0) {
// Simple AI: shoot when aligned with player
if (fabs(enemy.position.x - playerPos.x) < 50) {
Bullet bullet;
bullet.position = enemy.position;
bullet.velocity = {0, 200};
bullet.active = true;
bullet.lifetime = 3.0f;
bullets.push_back(bullet);
enemy.shootCooldown = 2.0f;
}
}
}
}
void CheckCollisions() {
// Player bullets vs enemies
for (auto& bullet : bullets) {
if (!bullet.active) continue;
for (auto& enemy : enemies) {
if (!enemy.active) continue;
if (SSO::Math::Distance(bullet.position, enemy.position) < 20) {
bullet.active = false;
enemy.health -= 1.0f;
if (enemy.health <= 0) {
enemy.active = false;
score += 100;
// Spawn new enemy
Enemy newEnemy;
newEnemy.position = {(float)GetRandomValue(100, 1180), 50};
newEnemy.velocity = {(float)GetRandomValue(-50, 50), 20};
newEnemy.active = true;
newEnemy.health = 2.0f;
newEnemy.shootCooldown = 0.0f;
enemies.push_back(newEnemy);
}
break;
}
}
}
// Enemy bullets vs player
for (auto& bullet : bullets) {
if (!bullet.active) continue;
// Check if it's an enemy bullet (moving down)
if (bullet.velocity.y > 0) {
if (SSO::Math::Distance(bullet.position, playerPos) < 20) {
bullet.active = false;
gameOver = true;
}
}
}
// Enemies vs player
for (auto& enemy : enemies) {
if (!enemy.active) continue;
if (SSO::Math::Distance(enemy.position, playerPos) < 30) {
gameOver = true;
}
}
}
void DrawGame() {
// Draw player
DrawTriangle(
{playerPos.x, playerPos.y - 15},
{playerPos.x - 10, playerPos.y + 15},
{playerPos.x + 10, playerPos.y + 15},
WHITE
);
// Draw enemies
for (const auto& enemy : enemies) {
if (enemy.active) {
DrawRectangle(enemy.position.x - 15, enemy.position.y - 10, 30, 20, RED);
}
}
// Draw bullets
for (const auto& bullet : bullets) {
if (bullet.active) {
Color bulletColor = (bullet.velocity.y < 0) ? YELLOW : ORANGE;
DrawCircleV(bullet.position, 3, bulletColor);
}
}
// Draw UI
DrawText(TextFormat("Score: %d", score), 10, 10, 30, WHITE);
if (gameOver) {
DrawText("GAME OVER", 550, 350, 60, RED);
DrawText(TextFormat("Final Score: %d", score), 570, 420, 30, WHITE);
DrawText("Press R to restart", 560, 470, 20, WHITE);
}
}
int main() {
InitWindow(1280, 720, "SSOEngine - Space Shooter");
SetTargetFPS(60);
InitGame();
while (!WindowShouldClose()) {
float deltaTime = GetFrameTime();
if (!gameOver) {
UpdatePlayer(deltaTime);
UpdateBullets(deltaTime);
UpdateEnemies(deltaTime);
CheckCollisions();
} else {
if (IsKeyPressed(KEY_R)) {
InitGame();
}
}
BeginDrawing();
ClearBackground(BLACK);
DrawGame();
EndDrawing();
}
CloseWindow();
return 0;
}
💡 Tips and Best Practices
Performance Tips
- • Use delta-time for all movement calculations
- • Pool objects instead of creating/destroying
- • Batch similar drawing operations
- • Use spatial partitioning for many objects
- • Profile your game regularly
- • Optimize collision checks with early-outs
Code Organization
- • Separate game logic from rendering
- • Use header files for declarations
- • Group related functionality
- • Keep functions small and focused
- • Use meaningful variable names
- • Comment complex algorithms
Game Design Tips
- • Start with simple mechanics
- • Focus on core gameplay loop
- • Add feedback for all actions
- • Balance difficulty progression
- • Test with different players
- • Iterate based on feedback
Debugging Tips
- • Add visual debug information
- • Use logging for important events
- • Test edge cases thoroughly
- • Use breakpoints for complex bugs
- • Save game state for testing
- • Profile memory usage