Watch
2
0
Fork
You've already forked raylib-cs
0

feat: backporting existing raylib-cs examples and new official raylib examples to WASM, adopting raylib's original code style

This commit is contained in:
tiger tiger tiger 2026-07-17 08:22:54 +02:00
commit f804ab7773
234 changed files with 38997 additions and 10578 deletions

View file

@ -0,0 +1,530 @@
/*******************************************************************************************
*
* raylib [text] example - strings management
*
* Example complexity rating: [] 3/4
*
* Example originally created with raylib 6.0, last time updated with raylib 6.0
*
* 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)
*
********************************************************************************************/
using System;
using System.Collections.Generic;
using System.Numerics;
using System.Text;
using Raylib_cs;
using static Raylib_cs.Raylib;
namespace Examples.Text;
public partial class StringsManagement : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
private const int MaxTextLength = 100;
private const int MaxTextParticles = 100;
private const int FontSize = 30;
public string Name => "Text / Strings Management";
public string Title => "raylib [text] example - strings management";
//----------------------------------------------------------------------------------
// Types and Structures Definition
//----------------------------------------------------------------------------------
private class TextParticle
{
public string Text;
public Rectangle Rect; // Boundary
public Vector2 Vel; // Velocity
public Vector2 Ppos; // Previous position
public float Padding;
public float BorderWidth;
public float Friction;
public float Elasticity;
public Color Color;
public bool Grabbed;
}
private List<TextParticle> textParticles;
private TextParticle grabbedTextParticle;
private Vector2 pressOffset;
public void Init()
{
textParticles = new();
grabbedTextParticle = null;
pressOffset = new(0, 0);
PrepareFirstTextParticle("raylib => fun videogames programming!");
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
float delta = GetFrameTime();
Vector2 mousePos = GetMousePosition();
// Checks if a text particle was grabbed
if (IsMouseButtonPressed(MouseButton.Left))
{
for (int i = textParticles.Count - 1; i >= 0; i--)
{
TextParticle tp = textParticles[i];
if (CheckCollisionPointRec(mousePos, tp.Rect))
{
pressOffset.X = mousePos.X - tp.Rect.X;
pressOffset.Y = mousePos.Y - tp.Rect.Y;
tp.Grabbed = true;
grabbedTextParticle = tp;
break;
}
}
}
// Releases any text particle the was grabbed
if (IsMouseButtonReleased(MouseButton.Left))
{
if (grabbedTextParticle != null)
{
grabbedTextParticle.Grabbed = false;
grabbedTextParticle = null;
}
}
// Slice os shatter a text particle
if (IsMouseButtonPressed(MouseButton.Right))
{
for (int i = textParticles.Count - 1; i >= 0; i--)
{
TextParticle tp = textParticles[i];
if (CheckCollisionPointRec(mousePos, tp.Rect))
{
if (IsKeyDown(KeyboardKey.LeftShift))
{
ShatterTextParticle(tp, i);
}
else
{
SliceTextParticle(tp, i, tp.Text.Length / 2);
}
break;
}
}
}
// Shake text particles
if (IsMouseButtonPressed(MouseButton.Middle))
{
for (int i = 0; i < textParticles.Count; i++)
{
if (!textParticles[i].Grabbed)
{
textParticles[i].Vel = new Vector2(GetRandomValue(-2000, 2000), GetRandomValue(-2000, 2000));
}
}
}
// Reset using TextTo* functions
if (IsKeyPressed(KeyboardKey.One)) PrepareFirstTextParticle("raylib => fun videogames programming!");
if (IsKeyPressed(KeyboardKey.Two)) PrepareFirstTextParticle(TextToUpper("raylib => fun videogames programming!"));
if (IsKeyPressed(KeyboardKey.Three)) PrepareFirstTextParticle(TextToLower("raylib => fun videogames programming!"));
if (IsKeyPressed(KeyboardKey.Four)) PrepareFirstTextParticle(TextToPascal("raylib_fun_videogames_programming"));
if (IsKeyPressed(KeyboardKey.Five)) PrepareFirstTextParticle(TextToSnake("RaylibFunVideogamesProgramming"));
if (IsKeyPressed(KeyboardKey.Six)) PrepareFirstTextParticle(TextToCamel("raylib_fun_videogames_programming"));
// Slice by char pressed only when we have one text particle
int charPressed = GetCharPressed();
if ((charPressed >= 'A') && (charPressed <= 'z') && (textParticles.Count == 1))
{
SliceTextParticleByChar(textParticles[0], (char)charPressed);
}
// Updates each text particle state
for (int i = 0; i < textParticles.Count; 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(KeyboardKey.LeftControl))
{
for (int j = 0; j < textParticles.Count; j++)
{
if (textParticles[j] != grabbedTextParticle && grabbedTextParticle.Grabbed)
{
if (CheckCollisionRecs(grabbedTextParticle.Rect, textParticles[j].Rect))
{
GlueTextParticles(grabbedTextParticle, textParticles[j]);
grabbedTextParticle = textParticles[textParticles.Count - 1];
}
}
}
}
}
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
for (int i = 0; i < textParticles.Count; i++)
{
TextParticle tp = textParticles[i];
DrawRectangleRec(new Rectangle(tp.Rect.X - tp.BorderWidth, tp.Rect.Y - tp.BorderWidth, tp.Rect.Width + tp.BorderWidth * 2, tp.Rect.Height + tp.BorderWidth * 2), Color.Black);
DrawRectangleRec(tp.Rect, tp.Color);
DrawText(tp.Text, (int)(tp.Rect.X + tp.Padding), (int)(tp.Rect.Y + tp.Padding), FontSize, Color.Black);
}
DrawText("grab a text particle by pressing with the mouse and throw it by releasing", 10, 10, 10, Color.DarkGray);
DrawText("slice a text particle by pressing it with the mouse right button", 10, 30, 10, Color.DarkGray);
DrawText("shatter a text particle keeping left shift pressed and pressing it with the mouse right button", 10, 50, 10, Color.DarkGray);
DrawText("glue text particles by grabbing than and keeping left control pressed", 10, 70, 10, Color.DarkGray);
DrawText("1 to 6 to reset", 10, 90, 10, Color.DarkGray);
DrawText("when you have only one text particle, you can slice it by pressing a char", 10, 110, 10, Color.DarkGray);
DrawText($"TEXT PARTICLE COUNT: {textParticles.Count}", 10, GetScreenHeight() - 30, 20, Color.Black);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
}
//----------------------------------------------------------------------------------
// Module Functions Definition
//----------------------------------------------------------------------------------
private void PrepareFirstTextParticle(string text)
{
TextParticle first = CreateTextParticle(
text,
GetScreenWidth() / 2.0f,
GetScreenHeight() / 2.0f,
Color.RayWhite
);
textParticles.Clear();
textParticles.Add(first);
}
private static TextParticle CreateTextParticle(string text, float x, float y, Color color)
{
TextParticle tp = new()
{
Text = "",
Rect = new Rectangle(x, y, 30, 30),
Vel = new Vector2(GetRandomValue(-200, 200), GetRandomValue(-200, 200)),
Ppos = new Vector2(0, 0),
Padding = 5.0f,
BorderWidth = 5.0f,
Friction = 0.99f,
Elasticity = 0.9f,
Color = color,
Grabbed = false
};
// Emulate C TextCopy() into a fixed size buffer
if (text.Length > MaxTextLength - 1)
{
text = text.Substring(0, MaxTextLength - 1);
}
tp.Text = text;
tp.Rect.Width = MeasureText(tp.Text, FontSize) + tp.Padding * 2;
tp.Rect.Height = FontSize + tp.Padding * 2;
return tp;
}
private void SliceTextParticle(TextParticle tp, int particlePos, int sliceLength)
{
int length = tp.Text.Length;
if ((length > 1) && ((textParticles.Count + length) < MaxTextParticles))
{
for (int i = 0; i < length; i += sliceLength)
{
string text = sliceLength == 1 ? tp.Text[i].ToString() : Subtext(tp.Text, i, sliceLength);
textParticles.Add(CreateTextParticle(
text,
tp.Rect.X + i * tp.Rect.Width / length,
tp.Rect.Y,
new Color(GetRandomValue(0, 255), GetRandomValue(0, 255), GetRandomValue(0, 255), 255)
));
}
RealocateTextParticles(particlePos);
}
}
private void SliceTextParticleByChar(TextParticle tp, char charToSlice)
{
string[] tokens = tp.Text.Split(charToSlice);
int tokenCount = tokens.Length;
if (tokenCount > 1)
{
int textLength = tp.Text.Length;
for (int i = 0; i < textLength; i++)
{
if (tp.Text[i] == charToSlice)
{
textParticles.Add(CreateTextParticle(
charToSlice.ToString(),
tp.Rect.X,
tp.Rect.Y,
new Color(GetRandomValue(0, 255), GetRandomValue(0, 255), GetRandomValue(0, 255), 255)
));
}
}
for (int i = 0; i < tokenCount; i++)
{
int tokenLength = tokens[i].Length;
textParticles.Add(CreateTextParticle(
tokens[i],
tp.Rect.X + i * tp.Rect.Width / tokenLength,
tp.Rect.Y,
new Color(GetRandomValue(0, 255), GetRandomValue(0, 255), GetRandomValue(0, 255), 255)
));
}
RealocateTextParticles(0);
}
}
private void ShatterTextParticle(TextParticle tp, int particlePos)
{
SliceTextParticle(tp, particlePos, 1);
}
private void GlueTextParticles(TextParticle grabbed, TextParticle target)
{
int p1 = textParticles.IndexOf(grabbed);
int p2 = textParticles.IndexOf(target);
if ((p1 != -1) && (p2 != -1))
{
TextParticle tp = CreateTextParticle(
grabbed.Text + target.Text,
grabbed.Rect.X,
grabbed.Rect.Y,
Color.RayWhite
);
tp.Grabbed = true;
textParticles.Add(tp);
grabbed.Grabbed = false;
if (p1 < p2)
{
RealocateTextParticles(p2);
RealocateTextParticles(p1);
}
else
{
RealocateTextParticles(p1);
RealocateTextParticles(p2);
}
}
}
private void RealocateTextParticles(int particlePos)
{
textParticles.RemoveAt(particlePos);
}
// Extract a substring, clamping length to the available characters (like raylib TextSubtext)
private static string Subtext(string text, int position, int length)
{
if (position >= text.Length)
{
return "";
}
int maxLength = text.Length - position;
if (length > maxLength)
{
length = maxLength;
}
return text.Substring(position, length);
}
// C# equivalents of raylib TextTo* helpers (behaviour kept identical)
private static string TextToUpper(string text)
{
var sb = new StringBuilder(text.Length);
foreach (char c in text)
{
sb.Append((c >= 'a' && c <= 'z') ? (char)(c - 32) : c);
}
return sb.ToString();
}
private static string TextToLower(string text)
{
var sb = new StringBuilder(text.Length);
foreach (char c in text)
{
sb.Append((c >= 'A' && c <= 'Z') ? (char)(c + 32) : c);
}
return sb.ToString();
}
private static string TextToPascal(string text)
{
var sb = new StringBuilder(text.Length);
if (text.Length > 0)
{
sb.Append(char.ToUpperInvariant(text[0]));
for (int i = 1; i < text.Length; i++)
{
if (text[i] == '_' && (i + 1) < text.Length)
{
sb.Append(char.ToUpperInvariant(text[i + 1]));
i++;
}
else
{
sb.Append(text[i]);
}
}
}
return sb.ToString();
}
private static string TextToSnake(string text)
{
var sb = new StringBuilder(text.Length);
for (int i = 0; i < text.Length; i++)
{
char c = text[i];
if (c >= 'A' && c <= 'Z')
{
if (i > 0)
{
sb.Append('_');
}
sb.Append((char)(c + 32));
}
else
{
sb.Append(c);
}
}
return sb.ToString();
}
private static string TextToCamel(string text)
{
var sb = new StringBuilder(text.Length);
if (text.Length > 0)
{
sb.Append(char.ToLowerInvariant(text[0]));
for (int i = 1; i < text.Length; i++)
{
if (text[i] == '_' && (i + 1) < text.Length)
{
sb.Append(char.ToUpperInvariant(text[i + 1]));
i++;
}
else
{
sb.Append(text[i]);
}
}
}
return sb.ToString();
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [text] example - strings management");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new StringsManagement();
game.Init();
// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
{
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}
}