text_strings_management.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400
  1. /*******************************************************************************************
  2. *
  3. * raylib [text] example - strings management
  4. *
  5. * Example complexity rating: [★★★☆] 3/4
  6. *
  7. * Example originally created with raylib 5.6-dev, last time updated with raylib 5.6-dev
  8. *
  9. * Example contributed by David Buzatto (@davidbuzatto) and reviewed by Ramon Santamaria (@raysan5)
  10. *
  11. * Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
  12. * BSD-like license that allows static linking with closed source software
  13. *
  14. * Copyright (c) 2025 David Buzatto (@davidbuzatto)
  15. *
  16. ********************************************************************************************/
  17. #include "raylib.h"
  18. #include <stdlib.h>
  19. #define MAX_TEXT_LENGTH 100
  20. #define MAX_TEXT_PARTICLES 100
  21. #define FONT_SIZE 30
  22. //----------------------------------------------------------------------------------
  23. // Types and Structures Definition
  24. //----------------------------------------------------------------------------------
  25. typedef struct TextParticle {
  26. char text[MAX_TEXT_LENGTH];
  27. Rectangle rect; // Boundary
  28. Vector2 vel; // Velocity
  29. Vector2 ppos; // Previous position
  30. float padding;
  31. float borderWidth;
  32. float friction;
  33. float elasticity;
  34. Color color;
  35. bool grabbed;
  36. } TextParticle;
  37. //----------------------------------------------------------------------------------
  38. // Module Functions Declaration
  39. //----------------------------------------------------------------------------------
  40. void PrepareFirstTextParticle(const char* text, TextParticle *tps, int *particleCount);
  41. TextParticle CreateTextParticle(const char *text, float x, float y, Color color);
  42. void SliceTextParticle(TextParticle *tp, int particlePos, int sliceLength, TextParticle *tps, int *particleCount);
  43. void SliceTextParticleByChar(TextParticle *tp, char charToSlice, TextParticle *tps, int *particleCount);
  44. void ShatterTextParticle(TextParticle *tp, int particlePos, TextParticle *tps, int *particleCount);
  45. void GlueTextParticles(TextParticle *grabbed, TextParticle *target, TextParticle *tps, int *particleCount);
  46. void RealocateTextParticles(TextParticle *tps, int particlePos, int *particleCount);
  47. //------------------------------------------------------------------------------------
  48. // Program main entry point
  49. //------------------------------------------------------------------------------------
  50. int main(void)
  51. {
  52. // Initialization
  53. //--------------------------------------------------------------------------------------
  54. const int screenWidth = 800;
  55. const int screenHeight = 450;
  56. InitWindow(screenWidth, screenHeight, "raylib [text] example - strings management");
  57. TextParticle textParticles[MAX_TEXT_PARTICLES] = { 0 };
  58. int particleCount = 0;
  59. TextParticle *grabbedTextParticle = NULL;
  60. Vector2 pressOffset = {0};
  61. PrepareFirstTextParticle("raylib => fun videogames programming!", textParticles, &particleCount);
  62. SetTargetFPS(60); // Set our game to run at 60 frames-per-second
  63. //---------------------------------------------------------------------------------------
  64. // Main game loop
  65. while (!WindowShouldClose()) // Detect window close button or ESC key
  66. {
  67. // Update
  68. //----------------------------------------------------------------------------------
  69. float delta = GetFrameTime();
  70. Vector2 mousePos = GetMousePosition();
  71. // Checks if a text particle was grabbed
  72. if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT))
  73. {
  74. for (int i = particleCount - 1; i >= 0; i--)
  75. {
  76. TextParticle *tp = &textParticles[i];
  77. pressOffset.x = mousePos.x - tp->rect.x;
  78. pressOffset.y = mousePos.y - tp->rect.y;
  79. if (CheckCollisionPointRec(mousePos, tp->rect))
  80. {
  81. tp->grabbed = true;
  82. grabbedTextParticle = tp;
  83. break;
  84. }
  85. }
  86. }
  87. // Releases any text particle the was grabbed
  88. if (IsMouseButtonReleased(MOUSE_BUTTON_LEFT))
  89. {
  90. if (grabbedTextParticle != NULL)
  91. {
  92. grabbedTextParticle->grabbed = false;
  93. grabbedTextParticle = NULL;
  94. }
  95. }
  96. // Slice os shatter a text particle
  97. if (IsMouseButtonPressed(MOUSE_BUTTON_RIGHT))
  98. {
  99. for (int i = particleCount - 1; i >= 0; i--)
  100. {
  101. TextParticle *tp = &textParticles[i];
  102. if (CheckCollisionPointRec(mousePos, tp->rect))
  103. {
  104. if (IsKeyDown(KEY_LEFT_SHIFT))
  105. {
  106. ShatterTextParticle(tp, i, textParticles, &particleCount);
  107. }
  108. else
  109. {
  110. SliceTextParticle(tp, i, TextLength(tp->text)/2, textParticles, &particleCount);
  111. }
  112. break;
  113. }
  114. }
  115. }
  116. // Shake text particles
  117. if (IsMouseButtonPressed(MOUSE_BUTTON_MIDDLE))
  118. {
  119. for (int i = 0; i < particleCount; i++)
  120. {
  121. if (!textParticles[i].grabbed) textParticles[i].vel = (Vector2){ GetRandomValue(-2000, 2000), GetRandomValue(-2000, 2000) };
  122. }
  123. }
  124. // Reset using TextTo* functions
  125. if (IsKeyPressed(KEY_ONE)) PrepareFirstTextParticle("raylib => fun videogames programming!", textParticles, &particleCount);
  126. if (IsKeyPressed(KEY_TWO)) PrepareFirstTextParticle(TextToUpper("raylib => fun videogames programming!"), textParticles, &particleCount);
  127. if (IsKeyPressed(KEY_THREE)) PrepareFirstTextParticle(TextToLower("raylib => fun videogames programming!"), textParticles, &particleCount);
  128. if (IsKeyPressed(KEY_FOUR)) PrepareFirstTextParticle(TextToPascal("raylib_fun_videogames_programming"), textParticles, &particleCount);
  129. if (IsKeyPressed(KEY_FIVE)) PrepareFirstTextParticle(TextToSnake("RaylibFunVideogamesProgramming"), textParticles, &particleCount);
  130. if (IsKeyPressed(KEY_SIX)) PrepareFirstTextParticle(TextToCamel("raylib_fun_videogames_programming"), textParticles, &particleCount);
  131. // Slice by char pressed only when we have one text particle
  132. char charPressed = GetCharPressed();
  133. if ((charPressed >= 'A') && (charPressed <= 'z') && (particleCount == 1))
  134. {
  135. SliceTextParticleByChar(&textParticles[0], charPressed, textParticles, &particleCount);
  136. }
  137. // Updates each text particle state
  138. for (int i = 0; i < particleCount; i++)
  139. {
  140. TextParticle *tp = &textParticles[i];
  141. // The text particle is not grabbed
  142. if (!tp->grabbed)
  143. {
  144. // text particle repositioning using the velocity
  145. tp->rect.x += tp->vel.x * delta;
  146. tp->rect.y += tp->vel.y * delta;
  147. // Does the text particle hit the screen right boundary?
  148. if ((tp->rect.x + tp->rect.width) >= screenWidth)
  149. {
  150. tp->rect.x = screenWidth - tp->rect.width; // Text particle repositioning
  151. tp->vel.x = -tp->vel.x*tp->elasticity; // Elasticity makes the text particle lose 10% of its velocity on hit
  152. }
  153. // Does the text particle hit the screen left boundary?
  154. else if (tp->rect.x <= 0)
  155. {
  156. tp->rect.x = 0.0f;
  157. tp->vel.x = -tp->vel.x*tp->elasticity;
  158. }
  159. // The same for y axis
  160. if ((tp->rect.y + tp->rect.height) >= screenHeight)
  161. {
  162. tp->rect.y = screenHeight - tp->rect.height;
  163. tp->vel.y = -tp->vel.y*tp->elasticity;
  164. }
  165. else if (tp->rect.y <= 0)
  166. {
  167. tp->rect.y = 0.0f;
  168. tp->vel.y = -tp->vel.y*tp->elasticity;
  169. }
  170. // Friction makes the text particle lose 1% of its velocity each frame
  171. tp->vel.x = tp->vel.x*tp->friction;
  172. tp->vel.y = tp->vel.y*tp->friction;
  173. }
  174. else
  175. {
  176. // Text particle repositioning using the mouse position
  177. tp->rect.x = mousePos.x - pressOffset.x;
  178. tp->rect.y = mousePos.y - pressOffset.y;
  179. // While the text particle is grabbed, recalculates its velocity
  180. tp->vel.x = (tp->rect.x - tp->ppos.x)/delta;
  181. tp->vel.y = (tp->rect.y - tp->ppos.y)/delta;
  182. tp->ppos.x = tp->rect.x;
  183. tp->ppos.y = tp->rect.y;
  184. // Glue text particles when dragging and pressing left ctrl
  185. if (IsKeyDown(KEY_LEFT_CONTROL))
  186. {
  187. for (int i = 0; i < particleCount; i++)
  188. {
  189. if (&textParticles[i] != grabbedTextParticle && grabbedTextParticle->grabbed)
  190. {
  191. if (CheckCollisionRecs(grabbedTextParticle->rect, textParticles[i].rect))
  192. {
  193. GlueTextParticles(grabbedTextParticle, &textParticles[i], textParticles, &particleCount);
  194. grabbedTextParticle = &textParticles[particleCount-1];
  195. }
  196. }
  197. }
  198. }
  199. }
  200. }
  201. //----------------------------------------------------------------------------------
  202. // Draw
  203. //----------------------------------------------------------------------------------
  204. BeginDrawing();
  205. ClearBackground(RAYWHITE);
  206. for (int i = 0; i < particleCount; i++)
  207. {
  208. TextParticle *tp = &textParticles[i];
  209. DrawRectangle(tp->rect.x-tp->borderWidth, tp->rect.y-tp->borderWidth, tp->rect.width+tp->borderWidth*2, tp->rect.height+tp->borderWidth*2, BLACK);
  210. DrawRectangleRec(tp->rect, tp->color);
  211. DrawText(tp->text, tp->rect.x+tp->padding, tp->rect.y+tp->padding, FONT_SIZE, BLACK);
  212. }
  213. DrawText("grab a text particle by pressing with the mouse and throw it by releasing", 10, 10, 10, DARKGRAY);
  214. DrawText("slice a text particle by pressing it with the mouse right button", 10, 30, 10, DARKGRAY);
  215. DrawText("shatter a text particle keeping left shift pressed and pressing it with the mouse right button", 10, 50, 10, DARKGRAY);
  216. DrawText("glue text particles by grabbing than and keeping left control pressed", 10, 70, 10, DARKGRAY);
  217. DrawText("1 to 6 to reset", 10, 90, 10, DARKGRAY);
  218. DrawText("when you have only one text particle, you can slice it by pressing a char", 10, 110, 10, DARKGRAY);
  219. DrawText(TextFormat("TEXT PARTICLE COUNT: %d", particleCount), 10, GetScreenHeight() - 30, 20, BLACK);
  220. EndDrawing();
  221. //----------------------------------------------------------------------------------
  222. }
  223. // De-Initialization
  224. //--------------------------------------------------------------------------------------
  225. CloseWindow(); // Close window and OpenGL context
  226. //--------------------------------------------------------------------------------------
  227. return 0;
  228. }
  229. //----------------------------------------------------------------------------------
  230. // Module Functions Definition
  231. //----------------------------------------------------------------------------------
  232. void PrepareFirstTextParticle(const char* text, TextParticle *tps, int *particleCount)
  233. {
  234. tps[0] = CreateTextParticle(
  235. text,
  236. GetScreenWidth()/2,
  237. GetScreenHeight()/2,
  238. RAYWHITE
  239. );
  240. *particleCount = 1;
  241. }
  242. TextParticle CreateTextParticle(const char *text, float x, float y, Color color)
  243. {
  244. TextParticle tp = {
  245. .text = "",
  246. .rect = { x, y, 30, 30 },
  247. .vel = { GetRandomValue(-200, 200), GetRandomValue(-200, 200) },
  248. .ppos = { 0 },
  249. .padding = 5.0f,
  250. .borderWidth = 5.0f,
  251. .friction = 0.99,
  252. .elasticity = 0.9,
  253. .color = color,
  254. .grabbed = false
  255. };
  256. TextCopy(tp.text, text);
  257. tp.rect.width = MeasureText(tp.text, FONT_SIZE)+tp.padding*2;
  258. tp.rect.height = FONT_SIZE+tp.padding*2;
  259. return tp;
  260. }
  261. void SliceTextParticle(TextParticle *tp, int particlePos, int sliceLength, TextParticle *tps, int *particleCount)
  262. {
  263. int length = TextLength(tp->text);
  264. if((length > 1) && ((*particleCount+length) < MAX_TEXT_PARTICLES))
  265. {
  266. for (int i = 0; i < length; i += sliceLength)
  267. {
  268. const char *text = sliceLength == 1 ? TextFormat("%c", tp->text[i]) : TextSubtext(tp->text, i, sliceLength);
  269. tps[(*particleCount)++] = CreateTextParticle(
  270. text,
  271. tp->rect.x + i * tp->rect.width/length,
  272. tp->rect.y,
  273. (Color) { GetRandomValue(0, 255), GetRandomValue(0, 255), GetRandomValue(0, 255), 255 }
  274. );
  275. }
  276. RealocateTextParticles(tps, particlePos, particleCount);
  277. }
  278. }
  279. void SliceTextParticleByChar(TextParticle *tp, char charToSlice, TextParticle *tps, int *particleCount)
  280. {
  281. int tokenCount = 0;
  282. char **tokens = TextSplit(tp->text, charToSlice, &tokenCount);
  283. if (tokenCount > 1)
  284. {
  285. int textLength = TextLength(tp->text);
  286. for (int i = 0; i < textLength; i++)
  287. {
  288. if (tp->text[i] == charToSlice)
  289. {
  290. tps[(*particleCount)++] = CreateTextParticle(
  291. TextFormat("%c", charToSlice),
  292. tp->rect.x,
  293. tp->rect.y,
  294. (Color) { GetRandomValue(0, 255), GetRandomValue(0, 255), GetRandomValue(0, 255), 255 }
  295. );
  296. }
  297. }
  298. for (int i = 0; i < tokenCount; i++)
  299. {
  300. int tokenLength = TextLength(tokens[i]);
  301. tps[(*particleCount)++] = CreateTextParticle(
  302. TextFormat("%s", tokens[i]),
  303. tp->rect.x + i * tp->rect.width/tokenLength,
  304. tp->rect.y,
  305. (Color) { GetRandomValue(0, 255), GetRandomValue(0, 255), GetRandomValue(0, 255), 255 }
  306. );
  307. }
  308. if (tokenCount)
  309. {
  310. RealocateTextParticles(tps, 0, particleCount);
  311. }
  312. }
  313. }
  314. void ShatterTextParticle(TextParticle *tp, int particlePos, TextParticle *tps, int *particleCount)
  315. {
  316. SliceTextParticle(tp, particlePos, 1, tps, particleCount);
  317. }
  318. void GlueTextParticles(TextParticle *grabbed, TextParticle *target, TextParticle *tps, int *particleCount)
  319. {
  320. int p1 = -1;
  321. int p2 = -1;
  322. for (int i = 0; i < *particleCount; i++)
  323. {
  324. if (&tps[i] == grabbed) p1 = i;
  325. if (&tps[i] == target) p2 = i;
  326. }
  327. if ((p1 != -1) && (p2 != -1))
  328. {
  329. TextParticle tp = CreateTextParticle(
  330. TextFormat( "%s%s", grabbed->text, target->text),
  331. grabbed->rect.x,
  332. grabbed->rect.y,
  333. RAYWHITE
  334. );
  335. tp.grabbed = true;
  336. tps[(*particleCount)++] = tp;
  337. grabbed->grabbed = false;
  338. if (p1 < p2)
  339. {
  340. RealocateTextParticles(tps, p2, particleCount);
  341. RealocateTextParticles(tps, p1, particleCount);
  342. }
  343. else
  344. {
  345. RealocateTextParticles(tps, p1, particleCount);
  346. RealocateTextParticles(tps, p2, particleCount);
  347. }
  348. }
  349. }
  350. void RealocateTextParticles(TextParticle *tps, int particlePos, int *particleCount)
  351. {
  352. for (int i = particlePos+1; i < *particleCount; i++)
  353. {
  354. tps[i-1] = tps[i];
  355. }
  356. (*particleCount)--;
  357. }