Merge branch 'raysan5:master' into shapes_math_sine_cosine
This commit is contained in:
commit
3b22765662
17 changed files with 16580 additions and 1142 deletions
|
|
@ -68,8 +68,8 @@ int main(void)
|
|||
// Draw circle to bullet texture, then draw bullet using DrawTexture()
|
||||
// NOTE: This is done to improve the performance, since DrawCircle() is very slow
|
||||
BeginTextureMode(bulletTexture);
|
||||
DrawCircle(12, 12, bulletRadius, WHITE);
|
||||
DrawCircleLines(12, 12, bulletRadius, BLACK);
|
||||
DrawCircle(12, 12, (float)bulletRadius, WHITE);
|
||||
DrawCircleLines(12, 12, (float)bulletRadius, BLACK);
|
||||
EndTextureMode();
|
||||
|
||||
bool drawInPerformanceMode = true; // Switch between DrawCircle() and DrawTexture()
|
||||
|
|
@ -192,8 +192,8 @@ int main(void)
|
|||
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,
|
||||
(int)(bullets[i].position.x - bulletTexture.texture.width*0.5f),
|
||||
(int)(bullets[i].position.y - bulletTexture.texture.height*0.5f),
|
||||
bullets[i].color);
|
||||
}
|
||||
}
|
||||
|
|
@ -206,8 +206,8 @@ int main(void)
|
|||
// 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);
|
||||
DrawCircleV(bullets[i].position, (float)bulletRadius, bullets[i].color);
|
||||
DrawCircleLinesV(bullets[i].position, (float)bulletRadius, BLACK);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
227
examples/shapes/shapes_clock_of_clocks.c
Normal file
227
examples/shapes/shapes_clock_of_clocks.c
Normal file
|
|
@ -0,0 +1,227 @@
|
|||
/*******************************************************************************************
|
||||
*
|
||||
* raylib [shapes] example - clock of clocks
|
||||
*
|
||||
* Example complexity rating: [★★☆☆] 2/4
|
||||
*
|
||||
* Example originally created with raylib 5.5, last time updated with raylib 5.6-dev
|
||||
*
|
||||
* Example contributed by JP Mortiboys (@themushroompirates) 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 JP Mortiboys (@themushroompirates)
|
||||
*
|
||||
********************************************************************************************/
|
||||
|
||||
#include "raylib.h"
|
||||
|
||||
#include "raymath.h" // Required for: Lerp()
|
||||
#include <time.h> // Required for: time(), localtime()
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
// Program main entry point
|
||||
//------------------------------------------------------------------------------------
|
||||
int main(void)
|
||||
{
|
||||
// Initialization
|
||||
//--------------------------------------------------------------------------------------
|
||||
const int screenWidth = 800;
|
||||
const int screenHeight = 450;
|
||||
|
||||
SetConfigFlags(FLAG_MSAA_4X_HINT);
|
||||
InitWindow(screenWidth, screenHeight, "raylib [shapes] example - clock of clocks");
|
||||
|
||||
const Color bgColor = ColorLerp(DARKBLUE, BLACK, 0.75f);
|
||||
const Color handsColor = ColorLerp(YELLOW, RAYWHITE, .25f);
|
||||
|
||||
const float clockFaceSize = 24;
|
||||
const float clockFaceSpacing = 8.0f;
|
||||
const float sectionSpacing = 16.0f;
|
||||
|
||||
const Vector2 TL = (Vector2){ 0.0f, 90.0f }; // Top-left corner
|
||||
const Vector2 TR = (Vector2){ 90.0f, 180.0f }; // Top-right corner
|
||||
const Vector2 BR = (Vector2){ 180.0f, 270.0f }; // Bottom-right corner
|
||||
const Vector2 BL = (Vector2){ 0.0f, 270.0f }; // Bottom-left corner
|
||||
const Vector2 HH = (Vector2){ 0.0f, 180.0f }; // Horizontal line
|
||||
const Vector2 VV = (Vector2){ 90.0f, 270.0f }; // Vertical line
|
||||
const Vector2 ZZ = (Vector2){ 135.0f, 135.0f }; // Not relevant
|
||||
|
||||
const Vector2 digitAngles[10][24] = {
|
||||
/* 0 */ { TL,HH,HH,TR, /* */ VV,TL,TR,VV,/* */ VV,VV,VV,VV,/* */ VV,VV,VV,VV,/* */ VV,BL,BR,VV,/* */ BL,HH,HH,BR },
|
||||
/* 1 */ { TL,HH,TR,ZZ, /* */ BL,TR,VV,ZZ,/* */ ZZ,VV,VV,ZZ,/* */ ZZ,VV,VV,ZZ,/* */ TL,BR,BL,TR,/* */ BL,HH,HH,BR },
|
||||
/* 2 */ { TL,HH,HH,TR, /* */ BL,HH,TR,VV,/* */ TL,HH,BR,VV,/* */ VV,TL,HH,BR,/* */ VV,BL,HH,TR,/* */ BL,HH,HH,BR },
|
||||
/* 3 */ { TL,HH,HH,TR, /* */ BL,HH,TR,VV,/* */ TL,HH,BR,VV,/* */ BL,HH,TR,VV,/* */ TL,HH,BR,VV,/* */ BL,HH,HH,BR },
|
||||
/* 4 */ { TL,TR,TL,TR, /* */ VV,VV,VV,VV,/* */ VV,BL,BR,VV,/* */ BL,HH,TR,VV,/* */ ZZ,ZZ,VV,VV,/* */ ZZ,ZZ,BL,BR },
|
||||
/* 5 */ { TL,HH,HH,TR, /* */ VV,TL,HH,BR,/* */ VV,BL,HH,TR,/* */ BL,HH,TR,VV,/* */ TL,HH,BR,VV,/* */ BL,HH,HH,BR },
|
||||
/* 6 */ { TL,HH,HH,TR, /* */ VV,TL,HH,BR,/* */ VV,BL,HH,TR,/* */ VV,TL,TR,VV,/* */ VV,BL,BR,VV,/* */ BL,HH,HH,BR },
|
||||
/* 7 */ { TL,HH,HH,TR, /* */ BL,HH,TR,VV,/* */ ZZ,ZZ,VV,VV,/* */ ZZ,ZZ,VV,VV,/* */ ZZ,ZZ,VV,VV,/* */ ZZ,ZZ,BL,BR },
|
||||
/* 8 */ { TL,HH,HH,TR, /* */ VV,TL,TR,VV,/* */ VV,BL,BR,VV,/* */ VV,TL,TR,VV,/* */ VV,BL,BR,VV,/* */ BL,HH,HH,BR },
|
||||
/* 9 */ { TL,HH,HH,TR, /* */ VV,TL,TR,VV,/* */ VV,BL,BR,VV,/* */ BL,HH,TR,VV,/* */ TL,HH,BR,VV,/* */ BL,HH,HH,BR },
|
||||
};
|
||||
// Time for the hands to move to the new position (in seconds); this must be <1s
|
||||
const float handsMoveDuration = .5f;
|
||||
|
||||
// We store the previous seconds value so we can see if the time has changed
|
||||
int prevSeconds = -1;
|
||||
|
||||
// This represents the real position where the hands are right now
|
||||
Vector2 currentAngles[6][24] = { 0 };
|
||||
|
||||
// This is the position where the hands were moving from
|
||||
Vector2 srcAngles[6][24] = { 0 };
|
||||
// This is the position where the hands are moving to
|
||||
Vector2 dstAngles[6][24] = { 0 };
|
||||
|
||||
// Current animation timer
|
||||
float handsMoveTimer = 0.0f;
|
||||
|
||||
// 12 or 24 hour mode
|
||||
int hourMode = 24;
|
||||
|
||||
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
// Main game loop
|
||||
while (!WindowShouldClose()) // Detect window close button or ESC key
|
||||
{
|
||||
// Update
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
// Get the current time
|
||||
time_t rawtime;
|
||||
struct tm *timeinfo;
|
||||
|
||||
time(&rawtime);
|
||||
timeinfo = localtime(&rawtime);
|
||||
|
||||
if (timeinfo->tm_sec != prevSeconds) {
|
||||
// The time has changed, so we need to move the hands to the new positions
|
||||
prevSeconds = timeinfo->tm_sec;
|
||||
|
||||
// Format the current time so we can access the individual digits
|
||||
const char *clockDigits = TextFormat("%02d%02d%02d", timeinfo->tm_hour % hourMode, timeinfo->tm_min, timeinfo->tm_sec);
|
||||
|
||||
// Fetch where we want all the hands to be
|
||||
for (int digit = 0; digit < 6; digit++) {
|
||||
for (int cell = 0; cell < 24; cell++) {
|
||||
srcAngles[digit][cell] = currentAngles[digit][cell];
|
||||
dstAngles[digit][cell] = digitAngles[ clockDigits[digit] - '0' ][cell];
|
||||
|
||||
// Quick exception for 12h mode
|
||||
if (digit == 0 && hourMode == 12 && clockDigits[0] == '0') {
|
||||
dstAngles[digit][cell] = ZZ;
|
||||
}
|
||||
|
||||
if (srcAngles[digit][cell].x > dstAngles[digit][cell].x) {
|
||||
srcAngles[digit][cell].x -= 360.0f;
|
||||
}
|
||||
if (srcAngles[digit][cell].y > dstAngles[digit][cell].y) {
|
||||
srcAngles[digit][cell].y -= 360.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reset the timer
|
||||
handsMoveTimer = -GetFrameTime();
|
||||
}
|
||||
|
||||
// Now let's animate all the hands if we need to
|
||||
if (handsMoveTimer < handsMoveDuration) {
|
||||
// Increase the timer but don't go above the maximum
|
||||
handsMoveTimer = Clamp(handsMoveTimer + GetFrameTime(), 0, handsMoveDuration);
|
||||
|
||||
// Calculate the % completion of the animation
|
||||
float t = handsMoveTimer / handsMoveDuration;
|
||||
|
||||
// A little cheeky smoothstep
|
||||
t = t * t * (3.0f - 2.0f * t);
|
||||
|
||||
for (int digit = 0; digit < 6; digit++) {
|
||||
for (int cell = 0; cell < 24; cell++) {
|
||||
currentAngles[digit][cell].x = Lerp(srcAngles[digit][cell].x, dstAngles[digit][cell].x, t);
|
||||
currentAngles[digit][cell].y = Lerp(srcAngles[digit][cell].y, dstAngles[digit][cell].y, t);
|
||||
}
|
||||
}
|
||||
|
||||
if (handsMoveTimer == handsMoveDuration) {
|
||||
// The animation has now finished
|
||||
}
|
||||
}
|
||||
|
||||
// Handle input
|
||||
|
||||
// Toggle between 12 and 24 hour mode with space
|
||||
if (IsKeyPressed(KEY_SPACE)) {
|
||||
hourMode = 36 - hourMode;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
// Draw
|
||||
//----------------------------------------------------------------------------------
|
||||
BeginDrawing();
|
||||
|
||||
ClearBackground(bgColor);
|
||||
|
||||
DrawText(TextFormat("%d-h mode, space to change", hourMode), 10, 30, 20, RAYWHITE);
|
||||
|
||||
float xOffset = 4.0f;
|
||||
|
||||
for (int digit = 0; digit < 6; digit++) {
|
||||
|
||||
for (int row = 0; row < 6; row++) {
|
||||
for (int col = 0; col < 4; col++) {
|
||||
Vector2 centre = (Vector2){
|
||||
xOffset + col*(clockFaceSize+clockFaceSpacing) + clockFaceSize * .5f,
|
||||
100 + row*(clockFaceSize+clockFaceSpacing) + clockFaceSize * .5f
|
||||
};
|
||||
DrawRing(centre, clockFaceSize * 0.5f - 2.0f, clockFaceSize * 0.5f, 0, 360, 24, DARKGRAY);
|
||||
|
||||
// Big hand
|
||||
DrawRectanglePro(
|
||||
(Rectangle){centre.x, centre.y, clockFaceSize*.5f+4.0f, 4.0f},
|
||||
(Vector2){ 2.0f, 2.0f },
|
||||
currentAngles[digit][row*4+col].x,
|
||||
handsColor
|
||||
);
|
||||
|
||||
// Little hand
|
||||
DrawRectanglePro(
|
||||
(Rectangle){centre.x, centre.y, clockFaceSize*.5f+2.0f, 4.0f},
|
||||
(Vector2){ 2.0f, 2.0f },
|
||||
currentAngles[digit][row*4+col].y,
|
||||
handsColor
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
xOffset += (clockFaceSize+clockFaceSpacing) * 4;
|
||||
if (digit % 2 == 1) {
|
||||
|
||||
DrawRing((Vector2){xOffset + 4.0f, 160.0f}, 6.0f, 8.0f, 0.0f, 360.0f, 24, handsColor);
|
||||
DrawRing((Vector2){xOffset + 4.0f, 225.0f}, 6.0f, 8.0f, 0.0f, 360.0f, 24, handsColor);
|
||||
|
||||
xOffset += sectionSpacing;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
DrawFPS(10, 10);
|
||||
|
||||
|
||||
EndDrawing();
|
||||
//----------------------------------------------------------------------------------
|
||||
}
|
||||
|
||||
// De-Initialization
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
CloseWindow(); // Close window and OpenGL context
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
return 0;
|
||||
}
|
||||
BIN
examples/shapes/shapes_clock_of_clocks.png
Normal file
BIN
examples/shapes/shapes_clock_of_clocks.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
|
|
@ -68,7 +68,7 @@ int main(void)
|
|||
ClearBackground(RAYWHITE);
|
||||
|
||||
// Draw the dashed line with the current properties
|
||||
DrawLineDashed(lineStartPosition, lineEndPosition, dashLength, blankLength, lineColors[colorIndex]);
|
||||
DrawLineDashed(lineStartPosition, lineEndPosition, (int)dashLength, (int)blankLength, lineColors[colorIndex]);
|
||||
|
||||
// Draw UI and Instructions
|
||||
DrawRectangle(5, 5, 265, 95, Fade(SKYBLUE, 0.5f));
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@
|
|||
*
|
||||
********************************************************************************************/
|
||||
|
||||
#define _CRT_SECURE_NO_WARNINGS // Disable some Visual Studio warnings
|
||||
|
||||
#include "raylib.h"
|
||||
|
||||
#include <math.h> // Required for: cosf(), sinf()
|
||||
|
|
@ -158,7 +160,7 @@ static void UpdateClock(Clock *clock)
|
|||
clock->minute.value = timeinfo->tm_min;
|
||||
clock->hour.value = timeinfo->tm_hour;
|
||||
|
||||
clock->hour.angle = (timeinfo->tm_hour%12)*180.0/6.0f;
|
||||
clock->hour.angle = (timeinfo->tm_hour%12)*180.0f/6.0f;
|
||||
clock->hour.angle += (timeinfo->tm_min%60)*30/60.0f;
|
||||
clock->hour.angle -= 90;
|
||||
|
||||
|
|
@ -175,8 +177,8 @@ static void UpdateClock(Clock *clock)
|
|||
static void DrawClockAnalog(Clock clock, Vector2 position)
|
||||
{
|
||||
// Draw clock base
|
||||
DrawCircleV(position, clock.second.length + 40, LIGHTGRAY);
|
||||
DrawCircleV(position, 12, GRAY);
|
||||
DrawCircleV(position, clock.second.length + 40.0f, LIGHTGRAY);
|
||||
DrawCircleV(position, 12.0f, GRAY);
|
||||
|
||||
// Draw clock minutes/seconds lines
|
||||
for (int i = 0; i < 60; i++)
|
||||
|
|
@ -192,15 +194,15 @@ static void DrawClockAnalog(Clock clock, Vector2 position)
|
|||
}
|
||||
|
||||
// Draw hand seconds
|
||||
DrawRectanglePro((Rectangle){ position.x, position.y, clock.second.length, clock.second.thickness },
|
||||
DrawRectanglePro((Rectangle){ position.x, position.y, (float)clock.second.length, (float)clock.second.thickness },
|
||||
(Vector2){ 0.0f, clock.second.thickness/2.0f }, clock.second.angle, clock.second.color);
|
||||
|
||||
// Draw hand minutes
|
||||
DrawRectanglePro((Rectangle){ position.x, position.y, clock.minute.length, clock.minute.thickness },
|
||||
DrawRectanglePro((Rectangle){ position.x, position.y, (float)clock.minute.length, (float)clock.minute.thickness },
|
||||
(Vector2){ 0.0f, clock.minute.thickness/2.0f }, clock.minute.angle, clock.minute.color);
|
||||
|
||||
// Draw hand hours
|
||||
DrawRectanglePro((Rectangle){ position.x, position.y, clock.hour.length, clock.hour.thickness },
|
||||
DrawRectanglePro((Rectangle){ position.x, position.y, (float)clock.hour.length, (float)clock.hour.thickness },
|
||||
(Vector2){ 0.0f, clock.hour.thickness/2.0f }, clock.hour.angle, clock.hour.color);
|
||||
}
|
||||
|
||||
|
|
@ -212,14 +214,14 @@ static void DrawClockDigital(Clock clock, Vector2 position)
|
|||
DrawDisplayValue((Vector2){ position.x, position.y }, clock.hour.value/10, RED, Fade(LIGHTGRAY, 0.3f));
|
||||
DrawDisplayValue((Vector2){ position.x + 120, position.y }, clock.hour.value%10, RED, Fade(LIGHTGRAY, 0.3f));
|
||||
|
||||
DrawCircle(position.x + 240, position.y + 70, 12, (clock.second.value%2)? RED : Fade(LIGHTGRAY, 0.3f));
|
||||
DrawCircle(position.x + 240, position.y + 150, 12, (clock.second.value%2)? RED : Fade(LIGHTGRAY, 0.3f));
|
||||
DrawCircle((int)position.x + 240, (int)position.y + 70, 12, (clock.second.value%2)? RED : Fade(LIGHTGRAY, 0.3f));
|
||||
DrawCircle((int)position.x + 240, (int)position.y + 150, 12, (clock.second.value%2)? RED : Fade(LIGHTGRAY, 0.3f));
|
||||
|
||||
DrawDisplayValue((Vector2){ position.x + 260, position.y }, clock.minute.value/10, RED, Fade(LIGHTGRAY, 0.3f));
|
||||
DrawDisplayValue((Vector2){ position.x + 380, position.y }, clock.minute.value%10, RED, Fade(LIGHTGRAY, 0.3f));
|
||||
|
||||
DrawCircle(position.x + 500, position.y + 70, 12, (clock.second.value%2)? RED : Fade(LIGHTGRAY, 0.3f));
|
||||
DrawCircle(position.x + 500, position.y + 150, 12, (clock.second.value%2)? RED : Fade(LIGHTGRAY, 0.3f));
|
||||
DrawCircle((int)position.x + 500, (int)position.y + 70, 12, (clock.second.value%2)? RED : Fade(LIGHTGRAY, 0.3f));
|
||||
DrawCircle((int)position.x + 500, (int)position.y + 150, 12, (clock.second.value%2)? RED : Fade(LIGHTGRAY, 0.3f));
|
||||
|
||||
DrawDisplayValue((Vector2){ position.x + 520, position.y }, clock.second.value/10, RED, Fade(LIGHTGRAY, 0.3f));
|
||||
DrawDisplayValue((Vector2){ position.x + 640, position.y }, clock.second.value%10, RED, Fade(LIGHTGRAY, 0.3f));
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
|
||||
// Constant for Simulation
|
||||
#define SIMULATION_STEPS 30
|
||||
#define G 9.81
|
||||
#define G 9.81f
|
||||
|
||||
//----------------------------------------------------------------------------------
|
||||
// Module Functions Declaration
|
||||
|
|
@ -43,9 +43,9 @@ int main(void)
|
|||
InitWindow(screenWidth, screenHeight, "raylib [shapes] example - double pendulum");
|
||||
|
||||
// Simulation Paramters
|
||||
float l1 = 15, m1 = 0.2, theta1 = DEG2RAD*170, w1 = 0;
|
||||
float l2 = 15, m2 = 0.1, theta2 = DEG2RAD*0, w2 = 0;
|
||||
float lengthScaler = 0.1;
|
||||
float l1 = 15.0f, m1 = 0.2f, theta1 = DEG2RAD*170, w1 = 0;
|
||||
float l2 = 15.0f, m2 = 0.1f, theta2 = DEG2RAD*0, w2 = 0;
|
||||
float lengthScaler = 0.1f;
|
||||
float totalM = m1 + m2;
|
||||
|
||||
Vector2 previousPosition = CalculateDoublePendulumEndPoint(l1, theta1, l2, theta2);
|
||||
|
|
@ -57,8 +57,8 @@ int main(void)
|
|||
float L2 = l2*lengthScaler;
|
||||
|
||||
// Draw parameters
|
||||
int lineThick = 20, trailThick = 2;
|
||||
float fateAlpha = 0.01;
|
||||
float lineThick = 20, trailThick = 2;
|
||||
float fateAlpha = 0.01f;
|
||||
|
||||
// Create framebuffer
|
||||
RenderTexture2D target = LoadRenderTexture(screenWidth, screenHeight);
|
||||
|
|
@ -129,15 +129,15 @@ int main(void)
|
|||
ClearBackground(BLACK);
|
||||
|
||||
// Draw trails texture
|
||||
DrawTextureRec(target.texture, (Rectangle){ 0, 0, target.texture.width, -target.texture.height }, (Vector2){ 0, 0 }, WHITE);
|
||||
DrawTextureRec(target.texture, (Rectangle){ 0, 0, (float)target.texture.width, (float)-target.texture.height }, (Vector2){ 0, 0 }, WHITE);
|
||||
|
||||
// Draw double pendulum
|
||||
DrawRectanglePro((Rectangle){ screenWidth/2, screenHeight/2 - 100, 10*l1, lineThick },
|
||||
(Vector2){0, lineThick*0.5}, 90 - RAD2DEG*theta1, RAYWHITE);
|
||||
DrawRectanglePro((Rectangle){ screenWidth/2.0f, screenHeight/2.0f - 100, 10*l1, lineThick },
|
||||
(Vector2){0, lineThick*0.5f}, 90 - RAD2DEG*theta1, RAYWHITE);
|
||||
|
||||
Vector2 endpoint1 = CalculatePendulumEndPoint(l1, theta1);
|
||||
DrawRectanglePro((Rectangle){ screenWidth/2 + endpoint1.x, screenHeight/2 - 100 + endpoint1.y, 10*l2, lineThick },
|
||||
(Vector2){0, lineThick*0.5}, 90 - RAD2DEG*theta2, RAYWHITE);
|
||||
DrawRectanglePro((Rectangle){ screenWidth/2.0f + endpoint1.x, screenHeight/2.0f - 100 + endpoint1.y, 10*l2, lineThick },
|
||||
(Vector2){0, lineThick*0.5f}, 90 - RAD2DEG*theta2, RAYWHITE);
|
||||
|
||||
EndDrawing();
|
||||
//----------------------------------------------------------------------------------
|
||||
|
|
@ -159,7 +159,7 @@ int main(void)
|
|||
// Calculate pendulum end point
|
||||
static Vector2 CalculatePendulumEndPoint(float l, float theta)
|
||||
{
|
||||
return (Vector2){ 10*l*sin(theta), 10*l*cos(theta) };
|
||||
return (Vector2){ 10*l*sinf(theta), 10*l*cosf(theta) };
|
||||
}
|
||||
|
||||
// Calculate double pendulum end point
|
||||
|
|
|
|||
102
examples/shapes/shapes_mouse_trail.c
Normal file
102
examples/shapes/shapes_mouse_trail.c
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
/*******************************************************************************************
|
||||
*
|
||||
* raylib [shapes] example - Draw a mouse trail (position history)
|
||||
*
|
||||
* Example complexity rating: [★☆☆☆] 1/4
|
||||
*
|
||||
* Example originally created with raylib 5.6
|
||||
*
|
||||
* Example contributed by Balamurugan R (@Bala050814]) 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 Balamurugan R (@Bala050814)
|
||||
*
|
||||
********************************************************************************************/
|
||||
|
||||
#include "raylib.h"
|
||||
|
||||
#include "raymath.h"
|
||||
|
||||
// Define the maximum number of positions to store in the trail
|
||||
#define MAX_TRAIL_LENGTH 30
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
// Program main entry point
|
||||
//------------------------------------------------------------------------------------
|
||||
int main(void)
|
||||
{
|
||||
// Initialization
|
||||
//--------------------------------------------------------------------------------------
|
||||
const int screenWidth = 800;
|
||||
const int screenHeight = 450;
|
||||
|
||||
InitWindow(screenWidth, screenHeight, "raylib [shapes] example - mouse trail");
|
||||
|
||||
// Array to store the history of mouse positions (our fixed-size queue)
|
||||
Vector2 trailPositions[MAX_TRAIL_LENGTH] = { 0 };
|
||||
|
||||
SetTargetFPS(60);
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
// Main game loop
|
||||
while (!WindowShouldClose()) // Detect window close button or ESC key
|
||||
{
|
||||
// Update
|
||||
//----------------------------------------------------------------------------------
|
||||
Vector2 mousePosition = GetMousePosition();
|
||||
|
||||
// Shift all existing positions backward by one slot in the array
|
||||
// The last element (the oldest position) is dropped
|
||||
for (int i = MAX_TRAIL_LENGTH - 1; i > 0; i--)
|
||||
{
|
||||
trailPositions[i] = trailPositions[i - 1];
|
||||
}
|
||||
|
||||
// Store the new, current mouse position at the start of the array (Index 0)
|
||||
trailPositions[0] = mousePosition;
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
// Draw
|
||||
//----------------------------------------------------------------------------------
|
||||
BeginDrawing();
|
||||
|
||||
ClearBackground(BLACK);
|
||||
|
||||
// Draw the trail by looping through the history array
|
||||
for (int i = 0; i < MAX_TRAIL_LENGTH; i++)
|
||||
{
|
||||
// Ensure we skip drawing if the array hasn't been fully filled on startup
|
||||
if ((trailPositions[i].x != 0.0f) || (trailPositions[i].y != 0.0f))
|
||||
{
|
||||
// Calculate relative trail strength (ratio is near 1.0 for new, near 0.0 for old)
|
||||
float ratio = (float)(MAX_TRAIL_LENGTH - i) / MAX_TRAIL_LENGTH;
|
||||
|
||||
// Fade effect: oldest positions are more transparent
|
||||
// Fade (color, alpha) - alpha is 0.5 to 1.0 based on ratio
|
||||
Color trailColor = Fade(SKYBLUE, ratio*0.5f + 0.5f);
|
||||
|
||||
// Size effect: oldest positions are smaller
|
||||
float trailRadius = 15.0f*ratio;
|
||||
|
||||
DrawCircleV(trailPositions[i], trailRadius, trailColor);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw a distinct white circle for the current mouse position (Index 0)
|
||||
DrawCircleV(mousePosition, 15.0f, WHITE);
|
||||
|
||||
DrawText("Move the mouse to see the trail effect!", 10, screenHeight - 30, 20, LIGHTGRAY);
|
||||
|
||||
EndDrawing();
|
||||
//----------------------------------------------------------------------------------
|
||||
}
|
||||
|
||||
// De-Initialization
|
||||
//--------------------------------------------------------------------------------------
|
||||
CloseWindow(); // Close window and OpenGL context
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
return 0;
|
||||
}
|
||||
BIN
examples/shapes/shapes_mouse_trail.png
Normal file
BIN
examples/shapes/shapes_mouse_trail.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.4 KiB |
228
examples/shapes/shapes_pie_chart.c
Normal file
228
examples/shapes/shapes_pie_chart.c
Normal file
|
|
@ -0,0 +1,228 @@
|
|||
/*******************************************************************************************
|
||||
*
|
||||
* raylib [shapes] example - pie chart
|
||||
*
|
||||
* Example complexity rating: [★★★☆] 3/4
|
||||
*
|
||||
* Example originally created with raylib 5.5, last time updated with raylib 5.6
|
||||
*
|
||||
* Example contributed by Gideon Serfontein (@GideonSerf) 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 Gideon Serfontein (@GideonSerf)
|
||||
*
|
||||
********************************************************************************************/
|
||||
|
||||
#include "raylib.h"
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define RAYGUI_IMPLEMENTATION
|
||||
#include "raygui.h"
|
||||
|
||||
#define MAX_PIE_SLICES 10 // Max pie slices
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
// Program main entry point
|
||||
//------------------------------------------------------------------------------------
|
||||
int main(void)
|
||||
{
|
||||
// Initialization
|
||||
//--------------------------------------------------------------------------------------
|
||||
const int screenWidth = 800;
|
||||
const int screenHeight = 450;
|
||||
|
||||
InitWindow(screenWidth, screenHeight, "raylib [shapes] example - pie chart");
|
||||
|
||||
int sliceCount = 7;
|
||||
float donutInnerRadius = 25.0f;
|
||||
float values[MAX_PIE_SLICES] = { 300.0f, 100.0f, 450.0f, 350.0f, 600.0f, 380.0f, 750.0f }; // Initial slice values
|
||||
char labels[MAX_PIE_SLICES][32] = { 0 };
|
||||
bool editingLabel[MAX_PIE_SLICES] = { 0 };
|
||||
|
||||
for (int i = 0; i < MAX_PIE_SLICES; i++)
|
||||
snprintf(labels[i], 32, "Slice %02i", i + 1);
|
||||
|
||||
bool showValues = true;
|
||||
bool showPercentages = false;
|
||||
bool showDonut = false;
|
||||
int hoveredSlice = -1;
|
||||
Rectangle scrollPanelBounds = {0};
|
||||
Vector2 scrollContentOffset = {0};
|
||||
Rectangle view = { 0 };
|
||||
|
||||
// UI layout parameters
|
||||
const int panelWidth = 270;
|
||||
const int panelMargin = 5;
|
||||
|
||||
// UI Panel top-left anchor
|
||||
const Vector2 panelPos = {
|
||||
(float)screenWidth - panelMargin - panelWidth,
|
||||
(float)panelMargin
|
||||
};
|
||||
|
||||
// UI Panel rectangle
|
||||
const Rectangle panelRect = {
|
||||
panelPos.x, panelPos.y,
|
||||
(float)panelWidth,
|
||||
(float)screenHeight - 2.0f*panelMargin
|
||||
};
|
||||
|
||||
// Pie chart geometry
|
||||
const Rectangle canvas = { 0, 0, panelPos.x, (float)screenHeight };
|
||||
const Vector2 center = { canvas.width/2.0f, canvas.height/2.0f};
|
||||
const float radius = 205.0f;
|
||||
|
||||
// Total value for percentage calculations
|
||||
float totalValue = 0.0f;
|
||||
|
||||
SetTargetFPS(60);
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
// Main game loop
|
||||
while (!WindowShouldClose())
|
||||
{
|
||||
// Update
|
||||
//----------------------------------------------------------------------------------
|
||||
// Calculate total value for percentage calculations
|
||||
totalValue = 0.0f;
|
||||
for (int i = 0; i < sliceCount; i++) totalValue += values[i];
|
||||
|
||||
// Check for mouse hover over slices
|
||||
hoveredSlice = -1; // Reset hovered slice
|
||||
Vector2 mousePos = GetMousePosition();
|
||||
if (CheckCollisionPointRec(mousePos, canvas)) // Only check if mouse is inside the canvas
|
||||
{
|
||||
float dx = mousePos.x - center.x;
|
||||
float dy = mousePos.y - center.y;
|
||||
float distance = sqrtf(dx*dx + dy*dy);
|
||||
|
||||
if (distance <= radius) // Inside the pie radius
|
||||
{
|
||||
float angle = atan2f(dy, dx)*RAD2DEG;
|
||||
if (angle < 0) angle += 360;
|
||||
|
||||
float currentAngle = 0.0f;
|
||||
for (int i = 0; i < sliceCount; i++)
|
||||
{
|
||||
float sweep = (totalValue > 0)? (values[i]/totalValue)*360.0f : 0.0f;
|
||||
|
||||
if ((angle >= currentAngle) && (angle < (currentAngle + sweep)))
|
||||
{
|
||||
hoveredSlice = i;
|
||||
break;
|
||||
}
|
||||
|
||||
currentAngle += sweep;
|
||||
}
|
||||
}
|
||||
}
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
// Draw
|
||||
//----------------------------------------------------------------------------------
|
||||
BeginDrawing();
|
||||
ClearBackground(RAYWHITE);
|
||||
|
||||
// Draw the pie chart on the canvas
|
||||
float startAngle = 0.0f;
|
||||
for (int i = 0; i < sliceCount; i++)
|
||||
{
|
||||
float sweepAngle = (totalValue > 0)? (values[i]/totalValue)*360.0f : 0.0f;
|
||||
float midAngle = startAngle + sweepAngle/2.0f; // Middle angle for label positioning
|
||||
|
||||
Color color = ColorFromHSV((float)i/sliceCount*360.0f, 0.75f, 0.9f);
|
||||
float currentRadius = radius;
|
||||
|
||||
// Make the hovered slice pop out by adding 5 pixels to its radius
|
||||
if (i == hoveredSlice) currentRadius += 20.0f;
|
||||
|
||||
// Draw the pie slice using raylib's DrawCircleSector function
|
||||
DrawCircleSector(center, currentRadius, startAngle, startAngle + sweepAngle, 120, color);
|
||||
|
||||
// Draw the label for the current slice
|
||||
if (values[i] > 0)
|
||||
{
|
||||
char labelText[64] = { 0 };
|
||||
if (showValues && showPercentages) snprintf(labelText, 64, "%.1f (%.0f%%)", values[i], (values[i]/totalValue)*100.0f);
|
||||
else if (showValues) snprintf(labelText, 64, "%.1f", values[i]);
|
||||
else if (showPercentages) snprintf(labelText, 64, "%.0f%%", (values[i]/totalValue)*100.0f);
|
||||
else labelText[0] = '\0';
|
||||
|
||||
Vector2 textSize = MeasureTextEx(GetFontDefault(), labelText, 20, 1);
|
||||
float labelRadius = radius*0.7f;
|
||||
Vector2 labelPos = { center.x + cosf(midAngle*DEG2RAD)*labelRadius - textSize.x/2.0f,
|
||||
center.y + sinf(midAngle*DEG2RAD)*labelRadius - textSize.y/2.0f };
|
||||
DrawText(labelText, (int)labelPos.x, (int)labelPos.y, 20, WHITE);
|
||||
}
|
||||
|
||||
// Draw inner circle to create donut effect
|
||||
// TODO: This is a hacky solution, better use DrawRing()
|
||||
if (showDonut) DrawCircle(center.x, center.y, donutInnerRadius, RAYWHITE);
|
||||
|
||||
startAngle += sweepAngle;
|
||||
}
|
||||
|
||||
// UI control panel
|
||||
DrawRectangleRec(panelRect, Fade(LIGHTGRAY, 0.5f));
|
||||
DrawRectangleLinesEx(panelRect, 1.0f, GRAY);
|
||||
|
||||
GuiSpinner((Rectangle){ panelPos.x + 95, (float)panelPos.y + 12, 125, 25 }, "Slices ", &sliceCount, 1, MAX_PIE_SLICES, false);
|
||||
GuiCheckBox((Rectangle){ panelPos.x + 20, (float)panelPos.y + 12 + 40, 20, 20 }, "Show Values", &showValues);
|
||||
GuiCheckBox((Rectangle){ panelPos.x + 20, (float)panelPos.y + 12 + 70, 20, 20 }, "Show Percentages", &showPercentages);
|
||||
GuiCheckBox((Rectangle){ panelPos.x + 20, (float)panelPos.y + 12 + 100, 20, 20 }, "Make Donut", &showDonut);
|
||||
|
||||
if (showDonut) GuiDisable();
|
||||
GuiSliderBar((Rectangle){ panelPos.x + 80, (float)panelPos.y + 12 + 130, panelRect.width - 100, 30 },
|
||||
"Inner Radius", NULL, &donutInnerRadius, 5.0f, radius - 10.0f);
|
||||
GuiEnable();
|
||||
|
||||
GuiLine((Rectangle){ panelPos.x + 10, (float)panelPos.y + 12 + 170, panelRect.width - 20, 1 }, NULL);
|
||||
|
||||
// Scrollable area for slice editors
|
||||
scrollPanelBounds = (Rectangle){
|
||||
panelPos.x + panelMargin,
|
||||
(float)panelPos.y + 12 + 190,
|
||||
panelRect.width - panelMargin*2,
|
||||
panelRect.y + panelRect.height - panelPos.y + 12 + 190 - panelMargin
|
||||
};
|
||||
int contentHeight = sliceCount*35;
|
||||
|
||||
GuiScrollPanel(scrollPanelBounds, NULL,
|
||||
(Rectangle){ 0, 0, panelRect.width - 25, (float)contentHeight },
|
||||
&scrollContentOffset, &view);
|
||||
|
||||
const float contentX = view.x + scrollContentOffset.x; // Left of content
|
||||
const float contentY = view.y + scrollContentOffset.y; // Top of content
|
||||
|
||||
BeginScissorMode((int)view.x, (int)view.y, (int)view.width, (int)view.height);
|
||||
|
||||
for (int i = 0; i < sliceCount; i++)
|
||||
{
|
||||
const int rowY = (int)(contentY + 5 + i*35);
|
||||
|
||||
// Color indicator
|
||||
Color color = ColorFromHSV((float)i/sliceCount*360.0f, 0.75f, 0.9f);
|
||||
DrawRectangle((int)(contentX + 15), rowY + 5, 20, 20, color);
|
||||
|
||||
// Label textbox
|
||||
if (GuiTextBox((Rectangle){ contentX + 45, (float)rowY, 75, 30 }, labels[i], 32, editingLabel[i])) editingLabel[i] = !editingLabel[i];
|
||||
|
||||
GuiSliderBar((Rectangle){ contentX + 130, (float)rowY, 110, 30 }, NULL, NULL, &values[i], 0.0f, 1000.0f);
|
||||
}
|
||||
|
||||
EndScissorMode();
|
||||
|
||||
EndDrawing();
|
||||
//----------------------------------------------------------------------------------
|
||||
}
|
||||
|
||||
// De-Initialization
|
||||
//--------------------------------------------------------------------------------------
|
||||
CloseWindow(); // Close window and OpenGL context
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
return 0;
|
||||
}
|
||||
BIN
examples/shapes/shapes_pie_chart.png
Normal file
BIN
examples/shapes/shapes_pie_chart.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
|
|
@ -1,6 +1,6 @@
|
|||
/*******************************************************************************************
|
||||
*
|
||||
* raylib [shapes] example - shapes recursive tree
|
||||
* raylib [shapes] example - recursive tree
|
||||
*
|
||||
* Example complexity rating: [★★★☆] 3/4
|
||||
*
|
||||
|
|
@ -11,7 +11,7 @@
|
|||
* 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) 2018-2025 Jopestpe (@jopestpe)
|
||||
* Copyright (c) 2025 Jopestpe (@jopestpe)
|
||||
*
|
||||
********************************************************************************************/
|
||||
|
||||
|
|
@ -31,11 +31,6 @@ typedef struct {
|
|||
float length;
|
||||
} Branch;
|
||||
|
||||
//----------------------------------------------------------------------------------
|
||||
// Module Functions Declaration
|
||||
//----------------------------------------------------------------------------------
|
||||
static Vector2 CalculateBranchEnd(Vector2 start, float angle, float length);
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
// Program main entry point
|
||||
//------------------------------------------------------------------------------------
|
||||
|
|
@ -46,9 +41,9 @@ int main(void)
|
|||
const int screenWidth = 800;
|
||||
const int screenHeight = 450;
|
||||
|
||||
InitWindow(screenWidth, screenHeight, "raylib [shapes] example - shapes recursive tree");
|
||||
InitWindow(screenWidth, screenHeight, "raylib [shapes] example - recursive tree");
|
||||
|
||||
Vector2 start = { (screenWidth/2.0f) - 125.0f, screenHeight };
|
||||
Vector2 start = { (screenWidth/2.0f) - 125.0f, (float)screenHeight };
|
||||
float angle = 40.0f;
|
||||
float thick = 1.0f;
|
||||
float treeDepth = 10.0f;
|
||||
|
|
@ -64,13 +59,12 @@ int main(void)
|
|||
{
|
||||
// Update
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
float theta = angle*DEG2RAD;
|
||||
int maxBranches = (int)(powf(2, (int)(treeDepth)));
|
||||
Branch branches[1024] = { 0 };
|
||||
int maxBranches = (int)(powf(2, floorf(treeDepth)));
|
||||
Branch branches[1030] = { 0 };
|
||||
int count = 0;
|
||||
|
||||
Vector2 initialEnd = CalculateBranchEnd(start, 0.0f, length);
|
||||
Vector2 initialEnd = { start.x + length*sinf(0.0f), start.y - length*cosf(0.0f) };
|
||||
branches[count++] = (Branch){start, initialEnd, 0.0f, length};
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
|
|
@ -84,14 +78,16 @@ int main(void)
|
|||
{
|
||||
Vector2 branchStart = branch.end;
|
||||
|
||||
Vector2 branchEnd1 = CalculateBranchEnd(branchStart, branch.angle + theta, nextLength);
|
||||
Vector2 branchEnd2 = CalculateBranchEnd(branchStart, branch.angle - theta, nextLength);
|
||||
|
||||
branches[count++] = (Branch){branchStart, branchEnd1, branch.angle + theta, nextLength};
|
||||
branches[count++] = (Branch){branchStart, branchEnd2, branch.angle - theta, nextLength};
|
||||
float angle1 = branch.angle + theta;
|
||||
Vector2 branchEnd1 = { branchStart.x + nextLength*sinf(angle1), branchStart.y - nextLength*cosf(angle1) };
|
||||
branches[count++] = (Branch){branchStart, branchEnd1, angle1, nextLength};
|
||||
|
||||
float angle2 = branch.angle - theta;
|
||||
Vector2 branchEnd2 = { branchStart.x + nextLength*sinf(angle2), branchStart.y - nextLength*cosf(angle2) };
|
||||
branches[count++] = (Branch){branchStart, branchEnd2, angle2, nextLength};
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------
|
||||
// Draw
|
||||
//----------------------------------------------------------------------------------
|
||||
BeginDrawing();
|
||||
|
|
@ -103,8 +99,8 @@ int main(void)
|
|||
Branch branch = branches[i];
|
||||
if (branch.length >= 2)
|
||||
{
|
||||
if (!bezier) DrawLineEx(branch.start, branch.end, thick, RED);
|
||||
else DrawLineBezier(branch.start, branch.end, thick, RED);
|
||||
if (bezier) DrawLineBezier(branch.start, branch.end, thick, RED);
|
||||
else DrawLineEx(branch.start, branch.end, thick, RED);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -133,9 +129,4 @@ int main(void)
|
|||
//--------------------------------------------------------------------------------------
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Vector2 CalculateBranchEnd(Vector2 start, float angle, float length)
|
||||
{
|
||||
return (Vector2){ start.x + length*sinf(angle), start.y - length*cosf(angle) };
|
||||
}
|
||||
284
examples/shapes/shapes_simple_particles.c
Normal file
284
examples/shapes/shapes_simple_particles.c
Normal file
|
|
@ -0,0 +1,284 @@
|
|||
/*******************************************************************************************
|
||||
*
|
||||
* raylib [shapes] example - simple particles
|
||||
*
|
||||
* Example complexity rating: [★★☆☆] 2/4
|
||||
*
|
||||
* Example originally created with raylib 5.6, last time updated with raylib 5.6
|
||||
*
|
||||
* Example contributed by Jordi Santonja (@JordSant)
|
||||
*
|
||||
* 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 Jordi Santonja (@JordSant)
|
||||
*
|
||||
********************************************************************************************/
|
||||
|
||||
#include "raylib.h"
|
||||
|
||||
#include <stdlib.h> // Required for: calloc(), free()
|
||||
#include <math.h> // Required for: cosf(), sinf()
|
||||
|
||||
#define MAX_PARTICLES 3000 // Max number of particles
|
||||
|
||||
//----------------------------------------------------------------------------------
|
||||
// Types and Structures Definition
|
||||
//----------------------------------------------------------------------------------
|
||||
typedef enum ParticleType {
|
||||
WATER = 0,
|
||||
SMOKE,
|
||||
FIRE
|
||||
} ParticleType;
|
||||
|
||||
static const char particleTypeNames[3][10] = { "WATER", "SMOKE", "FIRE" };
|
||||
|
||||
typedef struct Particle {
|
||||
ParticleType type; // Particle type (WATER, SMOKE, FIRE)
|
||||
Vector2 position; // Particle position on screen
|
||||
Vector2 velocity; // Particle current speed and direction
|
||||
float radius; // Particle radius
|
||||
Color color; // Particle color
|
||||
|
||||
float lifeTime; // Particle life time
|
||||
bool alive; // Particle alive: inside screen and life time
|
||||
} Particle;
|
||||
|
||||
typedef struct CircularBuffer {
|
||||
int head; // Index for the next write
|
||||
int tail; // Index for the next read
|
||||
Particle *buffer; // Particle buffer array
|
||||
} CircularBuffer;
|
||||
|
||||
//----------------------------------------------------------------------------------
|
||||
// Module Functions Declaration
|
||||
//----------------------------------------------------------------------------------
|
||||
static void EmitParticle(CircularBuffer *circularBuffer, Vector2 emitterPosition, ParticleType type);
|
||||
static Particle *AddToCircularBuffer(CircularBuffer *circularBuffer);
|
||||
static void UpdateParticles(CircularBuffer *circularBuffer, int screenWidth, int screenHeight);
|
||||
static void UpdateCircularBuffer(CircularBuffer *circularBuffer);
|
||||
static void DrawParticles(CircularBuffer *circularBuffer);
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
// Program main entry point
|
||||
//------------------------------------------------------------------------------------
|
||||
int main(void)
|
||||
{
|
||||
// Initialization
|
||||
//--------------------------------------------------------------------------------------
|
||||
const int screenWidth = 800;
|
||||
const int screenHeight = 450;
|
||||
|
||||
InitWindow(screenWidth, screenHeight, "raylib [shapes] example - simple particles");
|
||||
|
||||
// Definition of particles
|
||||
Particle *particles = (Particle*)RL_CALLOC(MAX_PARTICLES, sizeof(Particle)); // Particle array
|
||||
CircularBuffer circularBuffer = { 0, 0, particles };
|
||||
|
||||
// Particle emitter parameters
|
||||
int emissionRate = -2; // Negative: on average every -X frames. Positive: particles per frame
|
||||
ParticleType currentType = WATER;
|
||||
Vector2 emitterPosition = { screenWidth/2.0f, screenHeight/2.0f };
|
||||
|
||||
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
// Main game loop
|
||||
while (!WindowShouldClose()) // Detect window close button or ESC key
|
||||
{
|
||||
// Update
|
||||
//----------------------------------------------------------------------------------
|
||||
// Emit new particles: when emissionRate is 1, emit every frame
|
||||
if (emissionRate < 0)
|
||||
{
|
||||
if (rand()%(-emissionRate) == 0) EmitParticle(&circularBuffer, emitterPosition, currentType);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i <= emissionRate; ++i) EmitParticle(&circularBuffer, emitterPosition, currentType);
|
||||
}
|
||||
|
||||
// Update the parameters of each particle
|
||||
UpdateParticles(&circularBuffer, screenWidth, screenHeight);
|
||||
|
||||
// Remove dead particles from the circular buffer
|
||||
UpdateCircularBuffer(&circularBuffer);
|
||||
|
||||
// Change Particle Emission Rate (UP/DOWN arrows)
|
||||
if (IsKeyPressed(KEY_UP)) emissionRate++;
|
||||
if (IsKeyPressed(KEY_DOWN)) emissionRate--;
|
||||
|
||||
// Change Particle Type (LEFT/RIGHT arrows)
|
||||
if (IsKeyPressed(KEY_RIGHT)) (currentType == FIRE)? (currentType = WATER) : currentType++;
|
||||
if (IsKeyPressed(KEY_LEFT)) (currentType == WATER)? (currentType = FIRE) : currentType--;
|
||||
|
||||
if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) emitterPosition = GetMousePosition();
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
// Draw
|
||||
//----------------------------------------------------------------------------------
|
||||
BeginDrawing();
|
||||
|
||||
ClearBackground(RAYWHITE);
|
||||
|
||||
// Call the function with a loop to draw all particles
|
||||
DrawParticles(&circularBuffer);
|
||||
|
||||
// Draw UI and Instructions
|
||||
DrawRectangle(5, 5, 315, 75, Fade(SKYBLUE, 0.5f));
|
||||
DrawRectangleLines(5, 5, 315, 75, BLUE);
|
||||
|
||||
DrawText("CONTROLS:", 15, 15, 10, BLACK);
|
||||
DrawText("UP/DOWN: Change Particle Emission Rate", 15, 35, 10, BLACK);
|
||||
DrawText("LEFT/RIGHT: Change Particle Type (Water, Smoke, Fire)", 15, 55, 10, BLACK);
|
||||
|
||||
if (emissionRate < 0) DrawText(TextFormat("Particles every %d frames | Type: %s", -emissionRate, particleTypeNames[currentType]), 15, 95, 10, DARKGRAY);
|
||||
else DrawText(TextFormat("%d Particles per frame | Type: %s", emissionRate + 1, particleTypeNames[currentType]), 15, 95, 10, DARKGRAY);
|
||||
|
||||
DrawFPS(screenWidth - 80, 10);
|
||||
|
||||
EndDrawing();
|
||||
//----------------------------------------------------------------------------------
|
||||
}
|
||||
|
||||
// De-Initialization
|
||||
//--------------------------------------------------------------------------------------
|
||||
RL_FREE(particles); // Free particles array data
|
||||
|
||||
CloseWindow(); // Close window and OpenGL context
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------
|
||||
// Module Functions Definition
|
||||
//----------------------------------------------------------------------------------
|
||||
static void EmitParticle(CircularBuffer *circularBuffer, Vector2 emitterPosition, ParticleType type)
|
||||
{
|
||||
Particle *newParticle = AddToCircularBuffer(circularBuffer);
|
||||
|
||||
// If buffer is full, newParticle is NULL
|
||||
if (newParticle != NULL)
|
||||
{
|
||||
// Fill particle properties
|
||||
newParticle->position = emitterPosition;
|
||||
newParticle->alive = true;
|
||||
newParticle->lifeTime = 0.0f;
|
||||
newParticle->type = type;
|
||||
float speed = (float)(rand()%10)/5.0f;
|
||||
switch (type)
|
||||
{
|
||||
case WATER:
|
||||
{
|
||||
newParticle->radius = 5.0f;
|
||||
newParticle->color = BLUE;
|
||||
} break;
|
||||
case SMOKE:
|
||||
{
|
||||
newParticle->radius = 7.0f;
|
||||
newParticle->color = GRAY;
|
||||
} break;
|
||||
case FIRE:
|
||||
{
|
||||
newParticle->radius = 10.0f;
|
||||
newParticle->color = YELLOW;
|
||||
speed /= 10.0f;
|
||||
} break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
float direction = (float)(rand()%360);
|
||||
newParticle->velocity = (Vector2){ speed*cosf(direction*DEG2RAD), speed*sinf(direction*DEG2RAD) };
|
||||
}
|
||||
}
|
||||
|
||||
static Particle *AddToCircularBuffer(CircularBuffer *circularBuffer)
|
||||
{
|
||||
Particle *particle = NULL;
|
||||
|
||||
// Check if buffer full
|
||||
if (((circularBuffer->head + 1)%MAX_PARTICLES) != circularBuffer->tail)
|
||||
{
|
||||
// Add new particle to the head position and advance head
|
||||
particle = &circularBuffer->buffer[circularBuffer->head];
|
||||
circularBuffer->head = (circularBuffer->head + 1)%MAX_PARTICLES;
|
||||
}
|
||||
|
||||
return particle;
|
||||
}
|
||||
|
||||
static void UpdateParticles(CircularBuffer *circularBuffer, int screenWidth, int screenHeight)
|
||||
{
|
||||
for (int i = circularBuffer->tail; i != circularBuffer->head; i = (i + 1)%MAX_PARTICLES)
|
||||
{
|
||||
// Update particle life and positions
|
||||
circularBuffer->buffer[i].lifeTime += 1.0f/60.0f; // 60 FPS -> 1/60 seconds per frame
|
||||
|
||||
switch (circularBuffer->buffer[i].type)
|
||||
{
|
||||
case WATER:
|
||||
{
|
||||
circularBuffer->buffer[i].position.x += circularBuffer->buffer[i].velocity.x;
|
||||
circularBuffer->buffer[i].velocity.y += 0.2f; // Gravity
|
||||
circularBuffer->buffer[i].position.y += circularBuffer->buffer[i].velocity.y;
|
||||
} break;
|
||||
case SMOKE:
|
||||
{
|
||||
circularBuffer->buffer[i].position.x += circularBuffer->buffer[i].velocity.x;
|
||||
circularBuffer->buffer[i].velocity.y -= 0.05f; // Upwards
|
||||
circularBuffer->buffer[i].position.y += circularBuffer->buffer[i].velocity.y;
|
||||
circularBuffer->buffer[i].radius += 0.5f; // Increment radius: smoke expands
|
||||
circularBuffer->buffer[i].color.a -= 4; // Decrement alpha: smoke fades
|
||||
|
||||
// If alpha transparent, particle dies
|
||||
if (circularBuffer->buffer[i].color.a < 4) circularBuffer->buffer[i].alive = false;
|
||||
} break;
|
||||
case FIRE:
|
||||
{
|
||||
// Add a little horizontal oscillation to fire particles
|
||||
circularBuffer->buffer[i].position.x += circularBuffer->buffer[i].velocity.x + cosf(circularBuffer->buffer[i].lifeTime*215.0f);
|
||||
circularBuffer->buffer[i].velocity.y -= 0.05f; // Upwards
|
||||
circularBuffer->buffer[i].position.y += circularBuffer->buffer[i].velocity.y;
|
||||
circularBuffer->buffer[i].radius -= 0.15f; // Decrement radius: fire shrinks
|
||||
circularBuffer->buffer[i].color.g -= 3; // Decrement green: fire turns reddish starting from yellow
|
||||
|
||||
// If radius too small, particle dies
|
||||
if (circularBuffer->buffer[i].radius <= 0.02f) circularBuffer->buffer[i].alive = false;
|
||||
} break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
// Disable particle when out of screen
|
||||
Vector2 center = circularBuffer->buffer[i].position;
|
||||
float radius = circularBuffer->buffer[i].radius;
|
||||
|
||||
if ((center.x < -radius) || (center.x > (screenWidth + radius)) ||
|
||||
(center.y < -radius) || (center.y > (screenHeight + radius)))
|
||||
{
|
||||
circularBuffer->buffer[i].alive = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void UpdateCircularBuffer(CircularBuffer *circularBuffer)
|
||||
{
|
||||
// Update circular buffer: advance tail over dead particles
|
||||
while ((circularBuffer->tail != circularBuffer->head) && !circularBuffer->buffer[circularBuffer->tail].alive)
|
||||
{
|
||||
circularBuffer->tail = (circularBuffer->tail + 1)%MAX_PARTICLES;
|
||||
}
|
||||
}
|
||||
|
||||
static void DrawParticles(CircularBuffer *circularBuffer)
|
||||
{
|
||||
for (int i = circularBuffer->tail; i != circularBuffer->head; i = (i + 1)%MAX_PARTICLES)
|
||||
{
|
||||
if (circularBuffer->buffer[i].alive)
|
||||
{
|
||||
DrawCircleV(circularBuffer->buffer[i].position,
|
||||
circularBuffer->buffer[i].radius,
|
||||
circularBuffer->buffer[i].color);
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
examples/shapes/shapes_simple_particles.png
Normal file
BIN
examples/shapes/shapes_simple_particles.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 32 KiB |
146
examples/shapes/shapes_starfield_effect.c
Normal file
146
examples/shapes/shapes_starfield_effect.c
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
/*******************************************************************************************
|
||||
*
|
||||
* raylib [shapes] example - starfield effect
|
||||
*
|
||||
* Example complexity rating: [★★☆☆] 2/4
|
||||
*
|
||||
* Example originally created with raylib 5.5, last time updated with raylib 5.6-dev
|
||||
*
|
||||
* Example contributed by JP Mortiboys (@themushroompirates) 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 JP Mortiboys (@themushroompirates)
|
||||
*
|
||||
********************************************************************************************/
|
||||
|
||||
#include "raylib.h"
|
||||
|
||||
#include "raymath.h" // Required for: Lerp()
|
||||
|
||||
#define STAR_COUNT 420
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
// Program main entry point
|
||||
//------------------------------------------------------------------------------------
|
||||
int main(void)
|
||||
{
|
||||
// Initialization
|
||||
//--------------------------------------------------------------------------------------
|
||||
const int screenWidth = 800;
|
||||
const int screenHeight = 450;
|
||||
|
||||
InitWindow(screenWidth, screenHeight, "raylib [shapes] example - starfield effect");
|
||||
|
||||
Color bgColor = ColorLerp(DARKBLUE, BLACK, 0.69f);
|
||||
|
||||
// Speed at which we fly forward
|
||||
float speed = 10.0f/9.0f;
|
||||
|
||||
// We're either drawing lines or circles
|
||||
bool drawLines = true;
|
||||
|
||||
Vector3 stars[STAR_COUNT] = { 0 };
|
||||
Vector2 starsScreenPos[STAR_COUNT] = { 0 };
|
||||
|
||||
// Setup the stars with a random position
|
||||
for (int i = 0; i < STAR_COUNT; i++)
|
||||
{
|
||||
stars[i].x = GetRandomValue(-screenWidth*0.5f, screenWidth*0.5f);
|
||||
stars[i].y = GetRandomValue(-screenHeight*0.5f, screenHeight*0.5f);
|
||||
stars[i].z = 1.0f;
|
||||
}
|
||||
|
||||
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
// Main game loop
|
||||
while (!WindowShouldClose()) // Detect window close button or ESC key
|
||||
{
|
||||
// Update
|
||||
//----------------------------------------------------------------------------------
|
||||
// Change speed based on mouse
|
||||
float mouseMove = GetMouseWheelMove();
|
||||
if ((int)mouseMove != 0) speed += 2.0f*mouseMove/9.0f;
|
||||
if (speed < 0.0f) speed = 0.1f;
|
||||
else if (speed > 2.0f) speed = 2.0f;
|
||||
|
||||
// Toggle lines / points with space bar
|
||||
if (IsKeyPressed(KEY_SPACE)) drawLines = !drawLines;
|
||||
|
||||
float dt = GetFrameTime();
|
||||
for (int i = 0; i < STAR_COUNT; i++)
|
||||
{
|
||||
// Update star's timer
|
||||
stars[i].z -= dt*speed;
|
||||
|
||||
// Calculate the screen position
|
||||
starsScreenPos[i] = (Vector2){
|
||||
screenWidth*0.5f + stars[i].x/stars[i].z,
|
||||
screenHeight*0.5f + stars[i].y/stars[i].z,
|
||||
};
|
||||
|
||||
// If the star is too old, or offscreen, it dies and we make a new random one
|
||||
if ((stars[i].z < 0.0f) || (starsScreenPos[i].x < 0) || (starsScreenPos[i].y < 0.0f) ||
|
||||
(starsScreenPos[i].x > screenWidth) || (starsScreenPos[i].y > screenHeight))
|
||||
{
|
||||
stars[i].x = GetRandomValue(-screenWidth*0.5f, screenWidth*0.5f);
|
||||
stars[i].y = GetRandomValue(-screenHeight*0.5f, screenHeight*0.5f);
|
||||
stars[i].z = 1.0f;
|
||||
}
|
||||
}
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
// Draw
|
||||
//----------------------------------------------------------------------------------
|
||||
BeginDrawing();
|
||||
|
||||
ClearBackground(bgColor);
|
||||
|
||||
for (int i = 0; i < STAR_COUNT; i++)
|
||||
{
|
||||
if (drawLines)
|
||||
{
|
||||
// Get the time a little while ago for this star, but clamp it
|
||||
float t = Clamp(stars[i].z + 1.0f/32.0f, 0.0f, 1.0f);
|
||||
|
||||
// If it's different enough from the current time, we proceed
|
||||
if ((t - stars[i].z) > 1e-3)
|
||||
{
|
||||
// Calculate the screen position of the old point
|
||||
Vector2 startPos = (Vector2){
|
||||
screenWidth*0.5f + stars[i].x/t,
|
||||
screenHeight*0.5f + stars[i].y/t,
|
||||
};
|
||||
|
||||
// Draw a line connecting the old point to the current point
|
||||
DrawLineV(startPos, starsScreenPos[i], RAYWHITE);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Make the radius grow as the star ages
|
||||
float radius = Lerp(stars[i].z, 1.0f, 5.0f);
|
||||
|
||||
// Draw the circle
|
||||
DrawCircleV(starsScreenPos[i], radius, RAYWHITE);
|
||||
}
|
||||
}
|
||||
|
||||
DrawText(TextFormat("[MOUSE WHEEL] Current Speed: %.0f", 9.0f*speed/2.0f), 10, 40, 20, RAYWHITE);
|
||||
DrawText(TextFormat("[SPACE] Current draw mode: %s", drawLines ? "Lines" : "Circles"), 10, 70, 20, RAYWHITE);
|
||||
|
||||
DrawFPS(10, 10);
|
||||
|
||||
EndDrawing();
|
||||
//----------------------------------------------------------------------------------
|
||||
}
|
||||
|
||||
// De-Initialization
|
||||
//--------------------------------------------------------------------------------------
|
||||
CloseWindow(); // Close window and OpenGL context
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
return 0;
|
||||
}
|
||||
BIN
examples/shapes/shapes_starfield_effect.png
Normal file
BIN
examples/shapes/shapes_starfield_effect.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
|
|
@ -11,7 +11,7 @@
|
|||
* 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) 2018-2025 Jopestpe (@jopestpe)
|
||||
* Copyright (c) 2025 Jopestpe (@jopestpe)
|
||||
*
|
||||
********************************************************************************************/
|
||||
|
||||
|
|
@ -33,7 +33,7 @@ int main(void)
|
|||
|
||||
InitWindow(screenWidth, screenHeight, "raylib [shapes] example - triangle strip");
|
||||
|
||||
Vector2 points[120] = { 0 };
|
||||
Vector2 points[122] = { 0 };
|
||||
Vector2 center = { (screenWidth/2.0f) - 125.f, screenHeight/2.0f };
|
||||
float segments = 6.0f;
|
||||
float insideRadius = 100.0f;
|
||||
|
|
@ -69,17 +69,22 @@ int main(void)
|
|||
|
||||
ClearBackground(RAYWHITE);
|
||||
|
||||
for (int i = 0, i2 = 0; i < pointCount; i++, i2 += 2)
|
||||
for (int i = 0; i < pointCount; i++)
|
||||
{
|
||||
float angle1 = i*angleStep;
|
||||
Color color = ColorFromHSV(angle1*RAD2DEG, 1.0f, 1.0f);
|
||||
DrawTriangle(points[i2 + 2], points[i2 + 1], points[i2], color);
|
||||
if (outline) DrawTriangleLines(points[i2], points[i2 + 1], points[i2 + 2], BLACK);
|
||||
Vector2 a = points[i*2];
|
||||
Vector2 b = points[i*2 + 1];
|
||||
Vector2 c = points[i*2 + 2];
|
||||
Vector2 d = points[i*2 + 3];
|
||||
|
||||
float angle2 = angle1 + angleStep/2.0f;
|
||||
color = ColorFromHSV(angle2*RAD2DEG, 1.0f, 1.0f);
|
||||
DrawTriangle(points[i2 + 3], points[i2 + 1], points[i2 + 2], color);
|
||||
if (outline) DrawTriangleLines(points[i2 + 2], points[i2 + 1], points[i2 + 3], BLACK);
|
||||
float angle1 = i*angleStep;
|
||||
DrawTriangle(c, b, a, ColorFromHSV(angle1*RAD2DEG, 1.0f, 1.0f));
|
||||
DrawTriangle(d, b, c, ColorFromHSV((angle1 + angleStep/2)*RAD2DEG, 1.0f, 1.0f));
|
||||
|
||||
if (outline)
|
||||
{
|
||||
DrawTriangleLines(a, b, c, BLACK);
|
||||
DrawTriangleLines(c, b, d, BLACK);
|
||||
}
|
||||
}
|
||||
|
||||
DrawLine(580, 0, 580, GetScreenHeight(), (Color){ 218, 218, 218, 255 });
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ int main(void)
|
|||
|
||||
InitWindow(screenWidth, screenHeight, "raylib [shapes] example - vector angle");
|
||||
|
||||
Vector2 v0 = { screenWidth/2, screenHeight/2 };
|
||||
Vector2 v0 = { screenWidth/2.0f, screenHeight/2.0f };
|
||||
Vector2 v1 = Vector2Add(v0, (Vector2){ 100.0f, 80.0f });
|
||||
Vector2 v2 = { 0 }; // Updated with mouse position
|
||||
|
||||
|
|
@ -97,17 +97,17 @@ int main(void)
|
|||
DrawCircleSector(v0, 40.0f, startangle, startangle - angle, 32, Fade(GREEN, 0.6f));
|
||||
}
|
||||
|
||||
DrawText("v0", v0.x, v0.y, 10, DARKGRAY);
|
||||
DrawText("v0", (int)v0.x, (int)v0.y, 10, DARKGRAY);
|
||||
|
||||
// If the line from v0 to v1 would overlap the text, move it's position up 10
|
||||
if (angleMode == 0 && Vector2Subtract(v0, v1).y > 0.0f) DrawText("v1", v1.x, v1.y-10.0f, 10, DARKGRAY);
|
||||
if (angleMode == 0 && Vector2Subtract(v0, v1).y < 0.0f) DrawText("v1", v1.x, v1.y, 10, DARKGRAY);
|
||||
if (angleMode == 0 && Vector2Subtract(v0, v1).y > 0.0f) DrawText("v1", (int)v1.x, (int)v1.y-10, 10, DARKGRAY);
|
||||
if (angleMode == 0 && Vector2Subtract(v0, v1).y < 0.0f) DrawText("v1", (int)v1.x, (int)v1.y, 10, DARKGRAY);
|
||||
|
||||
// If angle mode 1, use v1 to emphasize the horizontal line
|
||||
if (angleMode == 1) DrawText("v1", v0.x + 40.0f, v0.y, 10, DARKGRAY);
|
||||
if (angleMode == 1) DrawText("v1", (int)v0.x + 40, (int)v0.y, 10, DARKGRAY);
|
||||
|
||||
// position adjusted by -10 so it isn't hidden by cursor
|
||||
DrawText("v2", v2.x-10.0f, v2.y-10.0f, 10, DARKGRAY);
|
||||
DrawText("v2", (int)v2.x-10, (int)v2.y-10, 10, DARKGRAY);
|
||||
|
||||
DrawText("Press SPACE to change MODE", 460, 10, 20, DARKGRAY);
|
||||
DrawText(TextFormat("ANGLE: %2.2f", angle), 10, 70, 20, LIME);
|
||||
|
|
|
|||
Loading…
Reference in a new issue