Bläddra i källkod

[examples] Added: `text_strings_management` (#5379)

* new shapes example - penrose tile

* stack cleanup

* proper use of strnlen, strncat and strncpy

* typo correction

* update screenshot of shapes_penrose_tile example

* new example for strings management

* Improved structure for text_strings_management
David Buzatto 2 veckor sedan
förälder
incheckning
b1f8cde329
2 ändrade filer med 400 tillägg och 0 borttagningar
  1. 400 0
      examples/text/text_strings_management.c
  2. BIN
      examples/text/text_strings_management.png

+ 400 - 0
examples/text/text_strings_management.c

@@ -0,0 +1,400 @@
+/*******************************************************************************************
+*
+*   raylib [text] example - strings management
+*
+*   Example complexity rating: [★★★☆] 3/4
+*
+*   Example originally created with raylib 5.6-dev, last time updated with raylib 5.6-dev
+*
+*   Example contributed by David Buzatto (@davidbuzatto) 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 David Buzatto (@davidbuzatto)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#include <stdlib.h>
+
+#define MAX_TEXT_LENGTH      100
+#define MAX_TEXT_PARTICLES   100
+#define FONT_SIZE             30
+
+//----------------------------------------------------------------------------------
+// Types and Structures Definition
+//----------------------------------------------------------------------------------
+typedef struct TextParticle {
+    char text[MAX_TEXT_LENGTH];
+    Rectangle rect;    // Boundary
+    Vector2 vel;       // Velocity
+    Vector2 ppos;      // Previous position
+    float padding;
+    float borderWidth;
+    float friction;   
+    float elasticity;
+    Color color;
+    bool grabbed;
+} TextParticle;
+
+//----------------------------------------------------------------------------------
+// Module Functions Declaration
+//----------------------------------------------------------------------------------
+void PrepareFirstTextParticle(const char* text, TextParticle *tps, int *particleCount);
+TextParticle CreateTextParticle(const char *text, float x, float y, Color color);
+void SliceTextParticle(TextParticle *tp, int particlePos, int sliceLength, TextParticle *tps, int *particleCount);
+void SliceTextParticleByChar(TextParticle *tp, char charToSlice, TextParticle *tps, int *particleCount);
+void ShatterTextParticle(TextParticle *tp, int particlePos, TextParticle *tps, int *particleCount);
+void GlueTextParticles(TextParticle *grabbed, TextParticle *target, TextParticle *tps, int *particleCount);
+void RealocateTextParticles(TextParticle *tps, int particlePos, int *particleCount);
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [shapes] example - strings management");
+
+    TextParticle textParticles[MAX_TEXT_PARTICLES] = { 0 };
+    int particleCount = 0;
+    TextParticle *grabbedTextParticle = NULL;
+    Vector2 pressOffset = {0};
+
+    PrepareFirstTextParticle("raylib => fun videogames programming!", textParticles, &particleCount);
+
+    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
+        //----------------------------------------------------------------------------------
+        float delta = GetFrameTime();
+        Vector2 mousePos = GetMousePosition();
+
+        // Checks if a text particle was grabbed
+        if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT))
+        {
+            for (int i = particleCount - 1; i >= 0; i--)
+            {
+                TextParticle *tp = &textParticles[i];
+                pressOffset.x = mousePos.x - tp->rect.x;
+                pressOffset.y = mousePos.y - tp->rect.y;
+                if (CheckCollisionPointRec(mousePos, tp->rect))
+                {
+                    tp->grabbed = true;
+                    grabbedTextParticle = tp;
+                    break;
+                }
+            }
+        }
+
+        // Releases any text particle the was grabbed
+        if (IsMouseButtonReleased(MOUSE_BUTTON_LEFT))
+        {
+            if (grabbedTextParticle != NULL)
+            {
+                grabbedTextParticle->grabbed = false;
+                grabbedTextParticle = NULL;
+            }
+        }
+
+        // Slice os shatter a text particle
+        if (IsMouseButtonPressed(MOUSE_BUTTON_RIGHT))
+        {
+            for (int i = particleCount - 1; i >= 0; i--)
+            {
+                TextParticle *tp = &textParticles[i];
+                if (CheckCollisionPointRec(mousePos, tp->rect))
+                {
+                    if (IsKeyDown(KEY_LEFT_SHIFT))
+                    {
+                        ShatterTextParticle(tp, i, textParticles, &particleCount);
+                    } 
+                    else
+                    {
+                        SliceTextParticle(tp, i, TextLength(tp->text)/2, textParticles, &particleCount);
+                    }
+                    break;
+                }
+            }
+        }
+
+        // Shake text particles
+        if (IsMouseButtonPressed(MOUSE_BUTTON_MIDDLE))
+        {
+            for (int i = 0; i < particleCount; i++)
+            {
+                if (!textParticles[i].grabbed) textParticles[i].vel = (Vector2){ GetRandomValue(-2000, 2000), GetRandomValue(-2000, 2000) };
+            }
+        }
+
+        // Reset using TextTo* functions
+        if (IsKeyPressed(KEY_ONE)) PrepareFirstTextParticle("raylib => fun videogames programming!", textParticles, &particleCount);
+        if (IsKeyPressed(KEY_TWO)) PrepareFirstTextParticle(TextToUpper("raylib => fun videogames programming!"), textParticles, &particleCount);
+        if (IsKeyPressed(KEY_THREE)) PrepareFirstTextParticle(TextToLower("raylib => fun videogames programming!"), textParticles, &particleCount);
+        if (IsKeyPressed(KEY_FOUR)) PrepareFirstTextParticle(TextToPascal("raylib_fun_videogames_programming"), textParticles, &particleCount);
+        if (IsKeyPressed(KEY_FIVE)) PrepareFirstTextParticle(TextToSnake("RaylibFunVideogamesProgramming"), textParticles, &particleCount);
+        if (IsKeyPressed(KEY_SIX)) PrepareFirstTextParticle(TextToCamel("raylib_fun_videogames_programming"), textParticles, &particleCount);
+
+        // Slice by char pressed only when we have one text particle
+        char charPressed = GetCharPressed();
+        if ((charPressed >= 'A') && (charPressed <= 'z') && (particleCount == 1))
+        {
+            SliceTextParticleByChar(&textParticles[0], charPressed, textParticles, &particleCount);
+        }
+
+        // Updates each text particle state
+        for (int i = 0; i < particleCount; i++)
+        {
+            TextParticle *tp = &textParticles[i];
+
+            // The text particle is not grabbed
+            if (!tp->grabbed) 
+            {
+                // text particle repositioning using the velocity
+                tp->rect.x += tp->vel.x * delta;
+                tp->rect.y += tp->vel.y * delta;
+
+                // Does the text particle hit the screen right boundary?
+                if ((tp->rect.x + tp->rect.width) >= screenWidth) 
+                {
+                    tp->rect.x = screenWidth - tp->rect.width; // Text particle repositioning
+                    tp->vel.x = -tp->vel.x*tp->elasticity;  // Elasticity makes the text particle lose 10% of its velocity on hit
+                } 
+                // Does the text particle hit the screen left boundary?
+                else if (tp->rect.x <= 0)
+                { 
+                    tp->rect.x = 0.0f;
+                    tp->vel.x = -tp->vel.x*tp->elasticity;
+                }
+
+                // The same for y axis
+                if ((tp->rect.y + tp->rect.height) >= screenHeight) 
+                {
+                    tp->rect.y = screenHeight - tp->rect.height;
+                    tp->vel.y = -tp->vel.y*tp->elasticity;
+                } 
+                else if (tp->rect.y <= 0) 
+                { 
+                    tp->rect.y = 0.0f;
+                    tp->vel.y = -tp->vel.y*tp->elasticity;
+                }
+
+                // Friction makes the text particle lose 1% of its velocity each frame
+                tp->vel.x = tp->vel.x*tp->friction;
+                tp->vel.y = tp->vel.y*tp->friction;
+            }
+            else
+            {
+                // Text particle repositioning using the mouse position
+                tp->rect.x = mousePos.x - pressOffset.x;
+                tp->rect.y = mousePos.y - pressOffset.y;
+
+                // While the text particle is grabbed, recalculates its velocity
+                tp->vel.x = (tp->rect.x - tp->ppos.x)/delta;
+                tp->vel.y = (tp->rect.y - tp->ppos.y)/delta;
+                tp->ppos.x = tp->rect.x;
+                tp->ppos.y = tp->rect.y;
+
+                // Glue text particles when dragging and pressing left ctrl
+                if (IsKeyDown(KEY_LEFT_CONTROL))
+                {
+                    for (int i = 0; i < particleCount; i++)
+                    {
+                        if (&textParticles[i] != grabbedTextParticle && grabbedTextParticle->grabbed)
+                        {
+                            if (CheckCollisionRecs(grabbedTextParticle->rect, textParticles[i].rect))
+                            {
+                                GlueTextParticles(grabbedTextParticle, &textParticles[i], textParticles, &particleCount);
+                                grabbedTextParticle = &textParticles[particleCount-1];
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(RAYWHITE);
+
+            for (int i = 0; i < particleCount; i++)
+            {
+                TextParticle *tp = &textParticles[i];
+                DrawRectangle(tp->rect.x-tp->borderWidth, tp->rect.y-tp->borderWidth, tp->rect.width+tp->borderWidth*2, tp->rect.height+tp->borderWidth*2, BLACK);
+                DrawRectangleRec(tp->rect, tp->color);
+                DrawText(tp->text, tp->rect.x+tp->padding, tp->rect.y+tp->padding, FONT_SIZE, BLACK);
+            }
+
+            DrawText("grab a text particle by pressing with the mouse and throw it by releasing", 10, 10, 10, DARKGRAY);
+            DrawText("slice a text particle by pressing it with the mouse right button", 10, 30, 10, DARKGRAY);
+            DrawText("shatter a text particle keeping left shift pressed and pressing it with the mouse right button", 10, 50, 10, DARKGRAY);
+            DrawText("glue text particles by grabbing than and keeping left control pressed", 10, 70, 10, DARKGRAY);
+            DrawText("1 to 6 to reset", 10, 90, 10, DARKGRAY);
+            DrawText("when you have only one text particle, you can slice it by pressing a char", 10, 110, 10, DARKGRAY);
+            DrawText(TextFormat("TEXT PARTICLE COUNT: %d", particleCount), 10, GetScreenHeight() - 30, 20, BLACK);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    CloseWindow();        // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition
+//----------------------------------------------------------------------------------
+void PrepareFirstTextParticle(const char* text, TextParticle *tps, int *particleCount)
+{
+    tps[0] = CreateTextParticle(
+        text, 
+        GetScreenWidth()/2, 
+        GetScreenHeight()/2, 
+        RAYWHITE
+    );
+    *particleCount = 1;
+}
+
+TextParticle CreateTextParticle(const char *text, float x, float y, Color color)
+{
+    TextParticle tp = {
+        .text = "",
+        .rect = { x, y, 30, 30 },
+        .vel = { GetRandomValue(-200, 200), GetRandomValue(-200, 200) },
+        .ppos = { 0 },
+        .padding = 5.0f,
+        .borderWidth = 5.0f,
+        .friction = 0.99,
+        .elasticity = 0.9,
+        .color = color,
+        .grabbed = false
+    };
+
+    TextCopy(tp.text, text);
+    tp.rect.width = MeasureText(tp.text, FONT_SIZE)+tp.padding*2;
+    tp.rect.height = FONT_SIZE+tp.padding*2;
+    return tp;
+}
+
+void SliceTextParticle(TextParticle *tp, int particlePos, int sliceLength, TextParticle *tps, int *particleCount)
+{
+    int length = TextLength(tp->text);
+
+    if((length > 1) && ((*particleCount+length) < MAX_TEXT_PARTICLES))
+    {
+        for (int i = 0; i < length; i += sliceLength)
+        {
+            const char *text = sliceLength == 1 ? TextFormat("%c", tp->text[i]) : TextSubtext(tp->text, i, sliceLength);
+            tps[(*particleCount)++] = CreateTextParticle(
+                text,
+                tp->rect.x + i * tp->rect.width/length,
+                tp->rect.y,
+                (Color) { GetRandomValue(0, 255), GetRandomValue(0, 255), GetRandomValue(0, 255), 255 }
+            );
+        }
+        RealocateTextParticles(tps, particlePos, particleCount);
+    }
+}
+
+void SliceTextParticleByChar(TextParticle *tp, char charToSlice, TextParticle *tps, int *particleCount)
+{
+    int tokenCount = 0;
+    const char **tokens = TextSplit(tp->text, charToSlice, &tokenCount);
+    
+    if (tokenCount > 1)
+    {
+        int textLength = TextLength(tp->text);
+        for (int i = 0; i < textLength; i++)
+        {
+            if (tp->text[i] == charToSlice)
+            {
+                tps[(*particleCount)++] = CreateTextParticle(
+                    TextFormat("%c", charToSlice),
+                    tp->rect.x,
+                    tp->rect.y,
+                    (Color) { GetRandomValue(0, 255), GetRandomValue(0, 255), GetRandomValue(0, 255), 255 }
+                );
+            }
+        }
+        for (int i = 0; i < tokenCount; i++)
+        {
+            int tokenLength = TextLength(tokens[i]);
+            tps[(*particleCount)++] = CreateTextParticle(
+                TextFormat("%s", tokens[i]),
+                tp->rect.x + i * tp->rect.width/tokenLength,
+                tp->rect.y,
+                (Color) { GetRandomValue(0, 255), GetRandomValue(0, 255), GetRandomValue(0, 255), 255 }
+            );
+        }
+        if (tokenCount)
+        {
+            RealocateTextParticles(tps, 0, particleCount);
+        }
+    }
+}
+
+void ShatterTextParticle(TextParticle *tp, int particlePos, TextParticle *tps, int *particleCount)
+{
+    SliceTextParticle(tp, particlePos, 1, tps, particleCount);
+}
+
+void GlueTextParticles(TextParticle *grabbed, TextParticle *target, TextParticle *tps, int *particleCount)
+{
+    int p1 = -1;
+    int p2 = -1;
+
+    for (int i = 0; i < *particleCount; i++)
+    {
+        if (&tps[i] == grabbed) p1 = i;
+        if (&tps[i] == target) p2 = i;
+    }
+
+    if ((p1 != -1) && (p2 != -1))
+    {
+        TextParticle tp = CreateTextParticle(
+            TextFormat( "%s%s", grabbed->text, target->text),
+            grabbed->rect.x,
+            grabbed->rect.y,
+            RAYWHITE
+        );
+        tp.grabbed = true;
+        tps[(*particleCount)++] = tp;
+        grabbed->grabbed = false;
+        if (p1 < p2)
+        {
+            RealocateTextParticles(tps, p2, particleCount);
+            RealocateTextParticles(tps, p1, particleCount);
+        }
+        else
+        {
+            RealocateTextParticles(tps, p1, particleCount);
+            RealocateTextParticles(tps, p2, particleCount);
+        }
+    }
+}
+
+void RealocateTextParticles(TextParticle *tps, int particlePos, int *particleCount)
+{
+    for (int i = particlePos+1; i < *particleCount; i++)
+    {
+        tps[i-1] = tps[i];
+    }
+    (*particleCount)--;
+}

BIN
examples/text/text_strings_management.png