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
1 change: 1 addition & 0 deletions examples/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,7 @@ CORE = \
SHAPES = \
shapes/shapes_basic_shapes \
shapes/shapes_bouncing_ball \
shapes/shapes_bullet_hell \
shapes/shapes_circle_sector_drawing \
shapes/shapes_collision_area \
shapes/shapes_colors_palette \
Expand Down
4 changes: 4 additions & 0 deletions examples/Makefile.Web
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,7 @@ CORE = \
SHAPES = \
shapes/shapes_basic_shapes \
shapes/shapes_bouncing_ball \
shapes/shapes_bullet_hell \
shapes/shapes_circle_sector_drawing \
shapes/shapes_collision_area \
shapes/shapes_colors_palette \
Expand Down Expand Up @@ -807,6 +808,9 @@ shapes/shapes_basic_shapes: shapes/shapes_basic_shapes.c
shapes/shapes_bouncing_ball: shapes/shapes_bouncing_ball.c
$(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM)

shapes/shapes_bullet_hell: shapes/shapes_bullet_hell.c
$(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM)

shapes/shapes_circle_sector_drawing: shapes/shapes_circle_sector_drawing.c
$(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM)

Expand Down
5 changes: 3 additions & 2 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ You may find it easier to use than other toolchains, especially when it comes to
- `zig build [module]` to compile all examples for a module (e.g. `zig build core`)
- `zig build [example]` to compile _and run_ a particular example (e.g. `zig build core_basic_window`)

## EXAMPLES COLLECTION [TOTAL: 164]
## EXAMPLES COLLECTION [TOTAL: 165]

### category: core [38]

Expand Down Expand Up @@ -64,14 +64,15 @@ Examples using raylib[core](../src/rcore.c) platform functionality like window c
| [core_undo_redo](core/core_undo_redo.c) | <img src="core/core_undo_redo.png" alt="core_undo_redo" width="80"> | ⭐⭐⭐☆ | 5.5 | 5.6 | [Ramon Santamaria](https://github.com/raysan5) |
| [core_input_actions](core/core_input_actions.c) | <img src="core/core_input_actions.png" alt="core_input_actions" width="80"> | ⭐⭐☆☆ | 5.5 | 5.6 | [Jett](https://github.com/JettMonstersGoBoom) |

### category: shapes [20]
### category: shapes [21]

Examples using raylib shapes drawing functionality, provided by raylib [shapes](../src/rshapes.c) module.

| example | image | difficulty<br>level | version<br>created | last version<br>updated | original<br>developer |
|-----------|--------|:-------------------:|:------------------:|:-----------------------:|:----------------------|
| [shapes_basic_shapes](shapes/shapes_basic_shapes.c) | <img src="shapes/shapes_basic_shapes.png" alt="shapes_basic_shapes" width="80"> | ⭐☆☆☆ | 1.0 | 4.2 | [Ramon Santamaria](https://github.com/raysan5) |
| [shapes_bouncing_ball](shapes/shapes_bouncing_ball.c) | <img src="shapes/shapes_bouncing_ball.png" alt="shapes_bouncing_ball" width="80"> | ⭐☆☆☆ | 2.5 | 2.5 | [Ramon Santamaria](https://github.com/raysan5) |
| [shapes_bullet_hell](shapes/shapes_bullet_hell.c) | <img src="shapes/shapes_bullet_hell.png" alt="shapes_bullet_hell" width="80"> | ⭐☆☆☆ | 5.6 | 5.6 | [Zero](https://github.com/zerohorsepower) |
| [shapes_colors_palette](shapes/shapes_colors_palette.c) | <img src="shapes/shapes_colors_palette.png" alt="shapes_colors_palette" width="80"> | ⭐⭐☆☆ | 1.0 | 2.5 | [Ramon Santamaria](https://github.com/raysan5) |
| [shapes_logo_raylib](shapes/shapes_logo_raylib.c) | <img src="shapes/shapes_logo_raylib.png" alt="shapes_logo_raylib" width="80"> | ⭐☆☆☆ | 1.0 | 1.0 | [Ramon Santamaria](https://github.com/raysan5) |
| [shapes_logo_raylib_anim](shapes/shapes_logo_raylib_anim.c) | <img src="shapes/shapes_logo_raylib_anim.png" alt="shapes_logo_raylib_anim" width="80"> | ⭐⭐☆☆ | 2.5 | 4.0 | [Ramon Santamaria](https://github.com/raysan5) |
Expand Down
1 change: 1 addition & 0 deletions examples/examples_list.txt
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ core;core_undo_redo;★★★☆;5.5;5.6;2025;2025;"Ramon Santamaria";@raysan5
core;core_input_actions;★★☆☆;5.5;5.6;2025;2025;"Jett";@JettMonstersGoBoom
shapes;shapes_basic_shapes;★☆☆☆;1.0;4.2;2014;2025;"Ramon Santamaria";@raysan5
shapes;shapes_bouncing_ball;★☆☆☆;2.5;2.5;2013;2025;"Ramon Santamaria";@raysan5
shapes;shapes_bullet_hell;★☆☆☆;5.6;5.6;2025;2025;"Zero";@zerohorsepower
shapes;shapes_colors_palette;★★☆☆;1.0;2.5;2014;2025;"Ramon Santamaria";@raysan5
shapes;shapes_logo_raylib;★☆☆☆;1.0;1.0;2014;2025;"Ramon Santamaria";@raysan5
shapes;shapes_logo_raylib_anim;★★☆☆;2.5;4.0;2014;2025;"Ramon Santamaria";@raysan5
Expand Down
286 changes: 286 additions & 0 deletions examples/shapes/shapes_bullet_hell.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,286 @@
/*******************************************************************************************
*
* raylib [shapes] example - bullet hell
*
* Example complexity rating: [★☆☆☆] 1/4
*
* Example originally created with raylib 5.6, last time updated with raylib 5.6
*
* Example contributed by Zero (@zerohorsepower) and reviewed by Ramon Santamaria (@raysan5)
*
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
* BSD-like license that allows static linking with closed source software
*
* Copyright (c) 2025-2025 Zero (@zerohorsepower)
*
********************************************************************************************/

#include "raylib.h"
#include <stdlib.h> // Required for: malloc(), free()
#include <math.h> // Required for: cosf(), sinf()

#define MAX_BULLETS 500000 // Max bullets that 800x450 can keep on minimum settings is 130.000 bullets

//----------------------------------------------------------------------------------
// Types and Structures Definition
//----------------------------------------------------------------------------------
typedef struct Bullet {
Vector2 position;
Vector2 acceleration; // the amount of pixels to be incremented to position every frame
bool disabled; // skip processing and draw case out of screen
Color color;
} Bullet;

//----------------------------------------------------------------------------------
// Module Functions Declaration
//----------------------------------------------------------------------------------


//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
int main(void)
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;

InitWindow(screenWidth, screenHeight, "raylib [shapes] example - bullet hell");

// Bullet
Bullet *bullets = (Bullet *)malloc(MAX_BULLETS*sizeof(Bullet)); // Bullets array
int bulletCount = 0;
int bulletDisabledCount = 0; // Used to calculate how many bullets are on screen
int bulletRadius = 10;
float bulletSpeed = 3.0f;
int bulletRows = 6;
Color bulletColor[2] = { RED, BLUE };

// Spawner
float baseDirection = 0;
int angleIncrement = 5; // After spawn all bullet rows, increment this value on the baseDirection for next the frame
float spawnCooldown = 2;
float spawnCooldownTimer = spawnCooldown;

// Magic circle
float magicCircleRotation = 0;

// Used on performance drawing
RenderTexture bulletTexture = LoadRenderTexture(24, 24);

// Draw circle to bullet texture, then draw bullet using DrawTexture()
// This is being done to improve the performance, since DrawCircle() is slow
BeginDrawing();
BeginTextureMode(bulletTexture);
DrawCircle(12, 12, bulletRadius, WHITE);
DrawCircleLines(12, 12, bulletRadius, BLACK);
EndTextureMode();
EndDrawing();

bool drawInPerformanceMode = true;

SetTargetFPS(60);
//--------------------------------------------------------------------------------------

// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------

// Reset the bullet index
// New bullets will replace the old ones that are already disabled due to out-of-screen
if (bulletCount >= MAX_BULLETS)
{
bulletCount = 0;
bulletDisabledCount = 0;
}

spawnCooldownTimer--;
if (spawnCooldownTimer < 0)
{
spawnCooldownTimer = spawnCooldown;

// Spawn bullets
float degreesPerRow = 360.0f / bulletRows;
for (int row = 0; row < bulletRows; row++)
{

if (bulletCount < MAX_BULLETS)
{

bullets[bulletCount].position = (Vector2){(float) screenWidth/2, (float) screenHeight/2};
bullets[bulletCount].disabled = false;
bullets[bulletCount].color = bulletColor[row % 2];

float bulletDirection = baseDirection + (degreesPerRow * row);

// bullet speed * bullet direction, this will determine how much pixels will be incremented/decremented
// from the bullet position every frame. Since the bullets doesn't change its direction and speed,
// only need to calculate it at the spawning time.
// 0 degrees = right, 90 degrees = down, 180 degrees = left and 270 degrees = up, basically clockwise.
// Case you want it to be anti-clockwise, add "* -1" at the y acceleration
bullets[bulletCount].acceleration = (Vector2){
bulletSpeed * cosf(bulletDirection * DEG2RAD),
bulletSpeed * sinf(bulletDirection * DEG2RAD)
};

bulletCount++;
}
}

baseDirection += angleIncrement;

}


// Update bullets position based on its acceleration
for (int i = 0; i < bulletCount; i++)
{

// Only update bullet if inside the screen
if (!bullets[i].disabled)
{

bullets[i].position.x += bullets[i].acceleration.x;
bullets[i].position.y += bullets[i].acceleration.y;

// Disable bullet if out of screen
if
(
bullets[i].position.x < -bulletRadius*2 ||
bullets[i].position.x > screenWidth + bulletRadius*2 ||
bullets[i].position.y < -bulletRadius*2 ||
bullets[i].position.y > screenHeight + bulletRadius*2
)
{
bullets[i].disabled = true;
bulletDisabledCount++;
}
}
}

// Input
if ((IsKeyPressed(KEY_RIGHT) || IsKeyPressed(KEY_D)) && bulletRows < 359) bulletRows++;
if ((IsKeyPressed(KEY_LEFT) || IsKeyPressed(KEY_A)) && bulletRows > 1) bulletRows--;
if (IsKeyPressed(KEY_UP) || IsKeyPressed(KEY_W)) bulletSpeed += 0.25f;
if ((IsKeyPressed(KEY_DOWN) || IsKeyPressed(KEY_S)) && bulletSpeed > 0.50f) bulletSpeed -= 0.25f;
if (IsKeyPressed(KEY_Z) && spawnCooldown > 1) spawnCooldown--;
if (IsKeyPressed(KEY_X)) spawnCooldown++;
if (IsKeyPressed(KEY_ENTER)) drawInPerformanceMode = !drawInPerformanceMode;

if (IsKeyDown(KEY_SPACE))
{
angleIncrement += 1;
angleIncrement %= 360;
}

if (IsKeyPressed(KEY_C))
{
bulletCount = 0;
bulletDisabledCount = 0;
}

// Draw
//----------------------------------------------------------------------------------
BeginDrawing();

ClearBackground(RAYWHITE);

// Draw magic circle
magicCircleRotation++;
DrawRectanglePro(
(Rectangle) { (float) screenWidth/2, (float) screenHeight/2, 120, 120 },
(Vector2) { 60, 60 },
magicCircleRotation,
PURPLE
);
DrawRectanglePro(
(Rectangle) { (float) screenWidth/2, (float) screenHeight/2, 120, 120 },
(Vector2) { 60, 60 },
magicCircleRotation + 45,
PURPLE
);
DrawCircleLines(screenWidth/2, screenHeight/2, 70, BLACK);
DrawCircleLines(screenWidth/2, screenHeight/2, 50, BLACK);
DrawCircleLines(screenWidth/2, screenHeight/2, 30, BLACK);


// Draw bullets
// DrawInPerformanceMode = draw bullets using DrawTexture, DrawCircle is vary slow
if (drawInPerformanceMode)
{
for (int i = 0; i < bulletCount; i++)
{
// Do not draw disabled bullets (out of screen)
if (!bullets[i].disabled)
{
DrawTexture(
bulletTexture.texture,
bullets[i].position.x - bulletTexture.texture.width*0.5f,
bullets[i].position.y - bulletTexture.texture.height*0.5f,
bullets[i].color
);
}
}
} else {

for (int i = 0; i < bulletCount; i++)
{
// Do not draw disabled bullets (out of screen)
if (!bullets[i].disabled)
{
DrawCircleV(bullets[i].position, bulletRadius, bullets[i].color);
DrawCircleLinesV(bullets[i].position, bulletRadius, BLACK);
}
}
}

// Draw UI
DrawRectangle(10, 10, 280, 150, (Color){0,0, 0, 200 });
DrawText("Controls:", 20, 20, 10, LIGHTGRAY);
DrawText("- Right/Left or A/D: Change rows number", 40, 40, 10, LIGHTGRAY);
DrawText("- Up/Down or W/S: Change bullet speed", 40, 60, 10, LIGHTGRAY);
DrawText("- Z or X: Change spawn cooldown", 40, 80, 10, LIGHTGRAY);
DrawText("- Space (Hold): Change the angle increment", 40, 100, 10, LIGHTGRAY);
DrawText("- Enter: Switch draw method (Performance)", 40, 120, 10, LIGHTGRAY);
DrawText("- C: Clear bullets", 40, 140, 10, LIGHTGRAY);

DrawRectangle(610, 10, 170, 30, (Color){0,0, 0, 200 });
if (drawInPerformanceMode)
{
DrawText("Draw method: DrawTexture(*)", 620, 20, 10, GREEN);
} else {
DrawText("Draw method: DrawCircle(*)", 620, 20, 10, RED);
}


DrawRectangle(135, 410, 530, 30, (Color){0,0, 0, 200 });
DrawText(
TextFormat(
"[ FPS: %d, Bullets: %d, Rows: %d, Bullet speed: %.2f, Angle increment per frame: %d, Cooldown: %.0f ]",
GetFPS(), bulletCount - bulletDisabledCount, bulletRows, bulletSpeed, angleIncrement, spawnCooldown
),
155,
420,
10,
GREEN
);

EndDrawing();
//----------------------------------------------------------------------------------
}

// De-Initialization
//--------------------------------------------------------------------------------------

UnloadRenderTexture(bulletTexture);

free(bullets);

CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------

return 0;
}
Binary file added examples/shapes/shapes_bullet_hell.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.