mirror of
https://github.com/raysan5/raylib.git
synced 2025-12-06 06:13:10 +00:00
[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
This commit is contained in:
parent
78a81bf407
commit
b1f8cde329
400
examples/text/text_strings_management.c
Normal file
400
examples/text/text_strings_management.c
Normal file
@ -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
Normal file
BIN
examples/text/text_strings_management.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
Loading…
x
Reference in New Issue
Block a user