-
-
Notifications
You must be signed in to change notification settings - Fork 3.2k
Working with raylib
headshot54325-maker edited this page Jul 19, 2026
·
1 revision
#include "raylib.h" #include #include
const int SCREEN_WIDTH = 640; const int SCREEN_HEIGHT = 480; const int VIRTUAL_WIDTH = 320; // Retro pixel feel const int VIRTUAL_HEIGHT = 240;
enum GameState { EXPLORATION, BATTLE, MENU };
struct Button { Rectangle rect; std::string text; bool pressed = false; };
int main() { SetConfigFlags(FLAG_WINDOW_RESIZABLE); InitWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "Undertale Fangame - Prototype"); SetTargetFPS(60);
// Camera for pixel perfect scaling
RenderTexture2D target = LoadRenderTexture(VIRTUAL_WIDTH, VIRTUAL_HEIGHT);
GameState state = EXPLORATION;
// Player Soul
Vector2 playerPos = {160, 160};
float speed = 2.0f;
Color soulColor = RED;
// Boss
Vector2 bossPos = {200, 60};
float bossHP = 100;
// Bullets
std::vector<Vector2> bullets;
float bulletTimer = 0;
// Virtual Controls
Button dpadUp = {{20, 140, 40, 40}, "^"};
Button dpadDown = {{20, 200, 40, 40}, "v"};
Button dpadLeft = {{5, 170, 40, 40}, "<"};
Button dpadRight = {{55, 170, 40, 40}, ">"};
Button btnZ = {{VIRTUAL_WIDTH - 110, VIRTUAL_HEIGHT - 60, 45, 45}, "Z"};
Button btnX = {{VIRTUAL_WIDTH - 60, VIRTUAL_HEIGHT - 60, 45, 45}, "X"};
Button btnC = {{VIRTUAL_WIDTH - 160, VIRTUAL_HEIGHT - 60, 45, 45}, "C"};
bool inBattle = false;
while (!WindowShouldClose()) {
// Update touch input
Vector2 touchPos = GetTouchPosition(0);
bool touchActive = (GetTouchPointCount() > 0);
// Movement
Vector2 dir = {0, 0};
if (IsKeyDown(KEY_UP) || (touchActive && CheckCollisionPointRec(touchPos, dpadUp.rect))) dir.y -= 1;
if (IsKeyDown(KEY_DOWN) || (touchActive && CheckCollisionPointRec(touchPos, dpadDown.rect))) dir.y += 1;
if (IsKeyDown(KEY_LEFT) || (touchActive && CheckCollisionPointRec(touchPos, dpadLeft.rect))) dir.x -= 1;
if (IsKeyDown(KEY_RIGHT) || (touchActive && CheckCollisionPointRec(touchPos, dpadRight.rect))) dir.x += 1;
if (dir.x != 0 || dir.y != 0) {
dir = Vector2Normalize(dir);
playerPos.x += dir.x * speed;
playerPos.y += dir.y * speed;
}
// Button presses
bool pressZ = IsKeyPressed(KEY_Z) || (touchActive && IsMouseButtonPressed(MOUSE_LEFT_BUTTON) &&
CheckCollisionPointRec(touchPos, btnZ.rect));
bool pressX = IsKeyPressed(KEY_X) ||**✅ Got it.** Here's a **C++ raylib Undertale-style fangame prototype** with:
- Pixel art aesthetic
- Virtual D-Pad + on-screen Z / X / C buttons (mobile touch friendly)
- Exploration mode
- Boss fight (simple bullet hell with one basic boss)
Follow the official raylib setup for your platform:
https://github.com/raysan5/raylib/wiki/Working-with-raylib
For Android, use the Android template from raylib.
#include "raylib.h"
#include <vector>
#include <string>
const int SCREEN_WIDTH = 640;
const int SCREEN_HEIGHT = 480;
const int VIRTUAL_WIDTH = 320; // Retro pixel feel
const int VIRTUAL_HEIGHT = 240;
enum GameState { EXPLORATION, BATTLE, MENU };
struct Button {
Rectangle rect;
std::string text;
bool pressed = false;
};
int main() {
SetConfigFlags(FLAG_WINDOW_RESIZABLE);
InitWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "Undertale Fangame - Prototype");
SetTargetFPS(60);
// Camera for pixel perfect scaling
RenderTexture2D target = LoadRenderTexture(VIRTUAL_WIDTH, VIRTUAL_HEIGHT);
GameState state = EXPLORATION;
// Player Soul
Vector2 playerPos = {160, 160};
float speed = 2.0f;
Color soulColor = RED;
// Boss
Vector2 bossPos = {200, 60};
float bossHP = 100;
// Bullets
std::vector<Vector2> bullets;
float bulletTimer = 0;
// Virtual Controls
Button dpadUp = {{20, 140, 40, 40}, "^"};
Button dpadDown = {{20, 200, 40, 40}, "v"};
Button dpadLeft = {{5, 170, 40, 40}, "<"};
Button dpadRight = {{55, 170, 40, 40}, ">"};
Button btnZ = {{VIRTUAL_WIDTH - 110, VIRTUAL_HEIGHT - 60, 45, 45}, "Z"};
Button btnX = {{VIRTUAL_WIDTH - 60, VIRTUAL_HEIGHT - 60, 45, 45}, "X"};
Button btnC = {{VIRTUAL_WIDTH - 160, VIRTUAL_HEIGHT - 60, 45, 45}, "C"};
bool inBattle = false;
while (!WindowShouldClose()) {
// Update touch input
Vector2 touchPos = GetTouchPosition(0);
bool touchActive = (GetTouchPointCount() > 0);
// Movement
Vector2 dir = {0, 0};
if (IsKeyDown(KEY_UP) || (touchActive && CheckCollisionPointRec(touchPos, dpadUp.rect))) dir.y -= 1;
if (IsKeyDown(KEY_DOWN) || (touchActive && CheckCollisionPointRec(touchPos, dpadDown.rect))) dir.y += 1;
if (IsKeyDown(KEY_LEFT) || (touchActive && CheckCollisionPointRec(touchPos, dpadLeft.rect))) dir.x -= 1;
if (IsKeyDown(KEY_RIGHT) || (touchActive && CheckCollisionPointRec(touchPos, dpadRight.rect))) dir.x += 1;
if (dir.x != 0 || dir.y != 0) {
dir = Vector2Normalize(dir);
playerPos.x += dir.x * speed;
playerPos.y += dir.y * speed;
}
// Button presses
bool pressZ = IsKeyPressed(KEY_Z) || (touchActive && IsMouseButtonPressed(MOUSE_LEFT_BUTTON) &&
CheckCollisionPointRec(touchPos, btnZ.rect));
bool pressX = IsKeyPressed(KEY_X) || (touchActive && CheckCollisionPointRec(touchPos, btnX.rect));
bool pressC = IsKeyPressed(KEY_C) || (touchActive && CheckCollisionPointRec(touchPos, btnC.rect));
if (pressC) state = (state == MENU) ? EXPLORATION : MENU;
// Simple Battle Trigger (press Z near center)
if (pressZ && !inBattle && state == EXPLORATION) {
if (Vector2Distance(playerPos, (Vector2){160, 100}) < 60) {
state = BATTLE;
inBattle = true;
playerPos = {100, 180}; // Move to battle position
}
}
// Battle Logic
if (state == BATTLE) {
soulColor = YELLOW; // Fight mode soul
bulletTimer += GetFrameTime();
// Simple bullet pattern
if (bulletTimer > 0.3f) {
bullets.push_back({bossPos.x + GetRandomValue(-40,40), bossPos.y + 30});
bulletTimer = 0;
}
// Update bullets
for (auto& b : bullets) b.y += 3;
// Collision with player
for (size_t i = 0; i < bullets.size(); i++) {
if (CheckCollisionCircles(playerPos, 8, bullets[i], 6)) {
// Hit!
bullets.erase(bullets.begin() + i);
break;
}
}
} else {
soulColor = RED;
}
// Drawing
BeginTextureMode(target);
ClearBackground(BLACK);
if (state == EXPLORATION) {
DrawRectangle(40, 40, 240, 160, DARKGRAY); // Floor
DrawText("A mysterious room...", 50, 50, 10, WHITE);
DrawCircleV(playerPos, 10, soulColor); // Soul
DrawCircleV({160, 100}, 15, PURPLE); // NPC / Boss trigger
}
else if (state == BATTLE) {
DrawText("BOSS FIGHT", 100, 20, 20, RED);
DrawCircleV(bossPos, 25, PURPLE);
DrawCircleV(playerPos, 10, soulColor);
for (auto& b : bullets) {
DrawCircleV(b, 6, WHITE);
}
DrawText(TextFormat("Boss HP: %.0f", bossHP), 20, 40, 10, WHITE);
}
else if (state == MENU) {
DrawRectangle(60, 60, 200, 120, DARKBLUE);
DrawText("MENU\n\nHP: 20/20\n\nPress C to close", 80, 80, 12, WHITE);
}
EndTextureMode();
// Render with pixel scaling
BeginDrawing();
ClearBackground(BLACK);
DrawTexturePro(target.texture,
{0, 0, (float)target.texture.width, -(float)target.texture.height},
{0, 0, (float)GetScreenWidth(), (float)GetScreenHeight()},
{0,0}, 0, WHITE);
// Draw virtual controls
DrawRectangleRec(dpadUp.rect, Fade(LIGHTGRAY, 0.6f));
DrawRectangleRec(dpadDown.rect, Fade(LIGHTGRAY, 0.6f));
DrawRectangleRec(dpadLeft.rect, Fade(LIGHTGRAY, 0.6f));
DrawRectangleRec(dpadRight.rect, Fade(LIGHTGRAY, 0.6f));
DrawText(dpadUp.text.c_str(), dpadUp.rect.x + 12, dpadUp.rect.y + 8, 20, BLACK);
DrawText(dpadDown.text.c_str(), dpadDown.rect.x + 12, dpadDown.rect.y + 8, 20, BLACK);
DrawText(dpadLeft.text.c_str(), dpadLeft.rect.x + 12, dpadLeft.rect.y + 8, 20, BLACK);
DrawText(dpadRight.text.c_str(), dpadRight.rect.x + 12, dpadRight.rect.y + 8, 20, BLACK);
DrawRectangleRec(btnZ.rect, BLUE); DrawText("Z", btnZ.rect.x + 15, btnZ.rect.y + 12, 20, WHITE);
DrawRectangleRec(btnX.rect, RED); DrawText("X", btnX.rect.x + 15, btnX.rect.y + 12, 20, WHITE);
DrawRectangleRec(btnC.rect, GREEN); DrawText("C", btnC.rect.x + 15, btnC.rect.y + 12, 20, WHITE);
EndDrawing();
}
UnloadRenderTexture(target);
CloseWindow();
return 0;
}www.raylib.com | itch.io | GitHub | Discord | YouTube
- Architecture
- Syntax analysis
- Data structures
- Enumerated types
- Static memory requirements
- External dependencies
- GLFW dependency
- libc dependency
- Platforms and graphics
- Input system
- Default shader
- Custom shaders
- Coding conventions
- Integration with other libs
- Working on Windows
- Working on macOS
- Working on GNU Linux
- Working on Chrome OS
- Working on FreeBSD
- Working on Raspberry Pi
- Working for Android
- Working for Web (HTML5)
- Working on exaequOS Web Computer
- Creating Discord Activities
- Working anywhere with CMake
- CMake Build Options
- raylib templates: Get started easily
- How To: Quick C/C++ Setup in Visual Studio 2022, GCC or MinGW
- How To: C# Visual Studio Setup
- How To: VSCode
- How To: Eclipse
- How To: Sublime Text
- How To: Code::Blocks