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

@ -1,13 +1,15 @@
/*******************************************************************************************
*
* raylib [text] example - Codepoints loading
* raylib [text] example - codepoints loading
*
* Example originally created with raylib 4.2, last time updated with raylib 2.5
* Example complexity rating: [] 3/4
*
* Example originally created with raylib 4.2, last time updated with raylib 4.2
*
* 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) 2022-2023 Ramon Santamaria (@raysan5)
* Copyright (c) 2022-2025 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
@ -19,32 +21,37 @@ using static Raylib_cs.Raylib;
namespace Examples.Text;
class CodepointsLoading
public partial class CodepointsLoading : IExample
{
public unsafe static int Main()
private const int screenWidth = 800;
private const int screenHeight = 450;
// Text to be displayed, must be UTF-8 (save this code file as UTF-8)
// NOTE: It can contain all the required text for the game,
// this text will be scanned to get all the required codepoints
private const string text =
"いろはにほへと ちりぬるを\nわかよたれそ つねならむ\nうゐのおくやま けふこえて\nあさきゆめみし ゑひもせす";
public string Name => "Text / Codepoints Loading";
public string Title => "raylib [text] example - codepoints loading";
private List<int> codepoints;
private int[] codepointsNoDuplicates;
private Font font;
private bool showFontAtlas;
public void Init()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [text] example - codepoints loading");
// Text to be displayed, must be UTF-8 (save this code file as UTF-8)
// NOTE: It can contain all the required text for the game,
// this text will be scanned to get all the required codepoints
const string text =
"いろはにほへと ちりぬるを\nわかよたれそ つねならむ\nうゐのおくやま けふこえて\nあさきゆめみし ゑひもせす";
// Get codepoints from text
List<int> codepoints = GetCodePoints(text);
codepoints = GetCodePoints(text);
// Remove duplicate codepoints to generate smaller font atlas
int[] codepointsNoDuplicates = codepoints.Distinct().ToArray();
codepointsNoDuplicates = codepoints.Distinct().ToArray();
// Load font containing all the provided codepoint glyphs
// A texture font atlas is automatically generated
Font font = LoadFontEx(
font = LoadFontEx(
"resources/fonts/DotGothic16-Regular.ttf",
36,
codepointsNoDuplicates,
@ -54,64 +61,58 @@ class CodepointsLoading
// Set bilinear scale filter for better font scaling
SetTextureFilter(font.Texture, TextureFilter.Bilinear);
bool showFontAtlas = false;
SetTextLineSpacing(20); // Set line spacing for multiline text (when line breaks are included '\n')
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
showFontAtlas = false;
}
// Main game loop
while (!WindowShouldClose())
public void Update()
{
// Update
//----------------------------------------------------------------------------------
if (IsKeyPressed(KeyboardKey.Space))
{
// Update
//----------------------------------------------------------------------------------
if (IsKeyPressed(KeyboardKey.Space))
{
showFontAtlas = !showFontAtlas;
}
//----------------------------------------------------------------------------------
showFontAtlas = !showFontAtlas;
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
ClearBackground(Color.RayWhite);
DrawRectangle(0, 0, GetScreenWidth(), 70, Color.Black);
DrawText($"Total codepoints contained in provided text: {codepoints.Count}", 10, 10, 20, Color.Green);
DrawText(
$"Total codepoints required for font atlas (duplicates excluded): {codepointsNoDuplicates.Length}",
10,
40,
20,
Color.Green
);
DrawRectangle(0, 0, GetScreenWidth(), 70, Color.Black);
DrawText($"Total codepoints contained in provided text: {codepoints.Count}", 10, 10, 20, Color.Green);
DrawText(
$"Total codepoints required for font atlas (duplicates excluded): {codepointsNoDuplicates.Length}",
10,
40,
20,
Color.Green
);
if (showFontAtlas)
{
// Draw generated font texture atlas containing provided codepoints
DrawTexture(font.Texture, 150, 100, Color.Black);
DrawRectangleLines(150, 100, font.Texture.Width, font.Texture.Height, Color.Black);
}
else
{
// Draw provided text with laoded font, containing all required codepoint glyphs
DrawTextEx(font, text, new Vector2(160, 110), 48, 5, Color.Black);
}
DrawText("Press SPACE to toggle font atlas view!", 10, GetScreenHeight() - 30, 20, Color.Gray);
EndDrawing();
//----------------------------------------------------------------------------------
if (showFontAtlas)
{
// Draw generated font texture atlas containing provided codepoints
DrawTexture(font.Texture, 150, 100, Color.Black);
DrawRectangleLines(150, 100, font.Texture.Width, font.Texture.Height, Color.Black);
}
else
{
// Draw provided text with loaded font, containing all required codepoint glyphs
DrawTextEx(font, text, new Vector2(160, 110), 48, 5, Color.Black);
}
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadFont(font);
DrawText("Press SPACE to toggle font atlas view!", 10, GetScreenHeight() - 30, 20, Color.Gray);
CloseWindow();
//--------------------------------------------------------------------------------------
EndDrawing();
//----------------------------------------------------------------------------------
}
return 0;
public void Unload()
{
UnloadFont(font); // Unload font
}
private static List<int> GetCodePoints(string text)
@ -119,14 +120,42 @@ class CodepointsLoading
List<int> codePoints = new();
StringInfo stringInfo = new(text);
TextElementEnumerator enumerator = StringInfo.GetTextElementEnumerator(text);
var enumerator = StringInfo.GetTextElementEnumerator(text);
while (enumerator.MoveNext())
{
int codePoint = char.ConvertToUtf32(enumerator.Current.ToString(), 0);
var codePoint = char.ConvertToUtf32(enumerator.Current.ToString(), 0);
codePoints.Add(codePoint);
}
return codePoints;
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [text] example - codepoints loading");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new CodepointsLoading();
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;
}
}

View file

@ -1,15 +1,19 @@
/*******************************************************************************************
*
* raylib [text] example - Font filters
* raylib [text] example - font filters
*
* After font loading, font texture atlas filter could be configured for a softer
* Example complexity rating: [] 2/4
*
* NOTE: After font loading, font texture atlas filter could be configured for a softer
* display of the font when scaling it to different sizes, that way, it's not required
* to generate multiple fonts at multiple sizes (as long as the scaling is not very different)
*
* This example has been created using raylib 1.3.0 (www.raylib.com)
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
* Example originally created with raylib 1.3, last time updated with raylib 4.2
*
* Copyright (c) 2015 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) 2015-2025 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
@ -18,129 +22,164 @@ using static Raylib_cs.Raylib;
namespace Examples.Text;
public class FontFilters
public partial class FontFilters : IExample
{
public static int Main()
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Text / Font Filters";
public string Title => "raylib [text] example - font filters";
private string msg;
private Font font;
private float fontSize;
private Vector2 fontPosition;
private Vector2 textSize;
private TextureFilter currentFontFilter;
public void Init()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [text] example - font filters");
string msg = "Loaded Font";
msg = "Loaded Font";
// NOTE: Textures/Fonts MUST be loaded after Window initialization (OpenGL context is required)
// TTF Font loading with custom generation parameters
Font font = LoadFontEx("resources/fonts/KAISG.ttf", 96, null, 0);
font = LoadFontEx("resources/fonts/KAISG.ttf", 96, null, 0);
// Generate mipmap levels to use trilinear filtering
// NOTE: On 2D drawing it won't be noticeable, it looks like TEXTURE_FILTER_BILINEAR
// NOTE: On 2D drawing it won't be noticeable, it looks like FILTER_BILINEAR
GenTextureMipmaps(ref font.Texture);
float fontSize = font.BaseSize;
Vector2 fontPosition = new(40, screenHeight / 2 - 80);
Vector2 textSize = new(0.0f, 0.0f);
fontSize = font.BaseSize;
fontPosition = new(40, screenHeight / 2 - 80);
textSize = new(0.0f, 0.0f);
// Setup texture scaling filter
SetTextureFilter(font.Texture, TextureFilter.Point);
TextureFilter currentFontFilter = TextureFilter.Point;
currentFontFilter = TextureFilter.Point; // TEXTURE_FILTER_POINT
}
SetTargetFPS(60);
public void Update()
{
// Update
//----------------------------------------------------------------------------------
fontSize += GetMouseWheelMove() * 4.0f;
// Choose font texture filter method
if (IsKeyPressed(KeyboardKey.One))
{
SetTextureFilter(font.Texture, TextureFilter.Point);
currentFontFilter = TextureFilter.Point;
}
else if (IsKeyPressed(KeyboardKey.Two))
{
SetTextureFilter(font.Texture, TextureFilter.Bilinear);
currentFontFilter = TextureFilter.Bilinear;
}
else if (IsKeyPressed(KeyboardKey.Three))
{
// NOTE: Trilinear filter won't be noticed on 2D drawing
SetTextureFilter(font.Texture, TextureFilter.Trilinear);
currentFontFilter = TextureFilter.Trilinear;
}
textSize = MeasureTextEx(font, msg, fontSize, 0);
if (IsKeyDown(KeyboardKey.Left))
{
fontPosition.X -= 10;
}
else if (IsKeyDown(KeyboardKey.Right))
{
fontPosition.X += 10;
}
#if BROWSER
// NOTE: drag-and-drop font loading is not supported in the browser host; default loaded font is kept.
#else
// Load a dropped TTF file dynamically (at current fontSize)
if (IsFileDropped())
{
var files = Raylib.GetDroppedFiles();
// NOTE: We only support first ttf file dropped
if (IsFileExtension(files[0], ".ttf"))
{
UnloadFont(font);
font = LoadFontEx(files[0], (int)fontSize, null, 0);
}
}
#endif
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
DrawText("Use mouse wheel to change font size", 20, 20, 10, Color.Gray);
DrawText("Use KEY_RIGHT and KEY_LEFT to move text", 20, 40, 10, Color.Gray);
DrawText("Use 1, 2, 3 to change texture filter", 20, 60, 10, Color.Gray);
DrawText("Drop a new TTF font for dynamic loading", 20, 80, 10, Color.DarkGray);
DrawTextEx(font, msg, fontPosition, fontSize, 0, Color.Black);
// TODO: It seems texSize measurement is not accurate due to chars offsets...
//DrawRectangleLines((int)fontPosition.X, (int)fontPosition.Y, (int)textSize.X, (int)textSize.Y, Color.Red);
DrawRectangle(0, screenHeight - 80, screenWidth, 80, Color.LightGray);
DrawText($"Font size: {fontSize:00.00}", 20, screenHeight - 50, 10, Color.DarkGray);
DrawText($"Text size: [{textSize.X:00.00}, {textSize.Y:00.00}]", 20, screenHeight - 30, 10, Color.DarkGray);
DrawText("CURRENT TEXTURE FILTER:", 250, 400, 20, Color.Gray);
if (currentFontFilter == TextureFilter.Point)
{
DrawText("POINT", 570, 400, 20, Color.Black);
}
else if (currentFontFilter == TextureFilter.Bilinear)
{
DrawText("BILINEAR", 570, 400, 20, Color.Black);
}
else if (currentFontFilter == TextureFilter.Trilinear)
{
DrawText("TRILINEAR", 570, 400, 20, Color.Black);
}
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadFont(font); // Font unloading
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [text] example - font filters");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new FontFilters();
game.Init();
// Main game loop
while (!WindowShouldClose())
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
fontSize += GetMouseWheelMove() * 4.0f;
// Choose font texture filter method
if (IsKeyPressed(KeyboardKey.One))
{
SetTextureFilter(font.Texture, TextureFilter.Point);
currentFontFilter = TextureFilter.Point;
}
else if (IsKeyPressed(KeyboardKey.Two))
{
SetTextureFilter(font.Texture, TextureFilter.Bilinear);
currentFontFilter = TextureFilter.Bilinear;
}
else if (IsKeyPressed(KeyboardKey.Three))
{
// NOTE: Trilinear filter won't be noticed on 2D drawing
SetTextureFilter(font.Texture, TextureFilter.Trilinear);
currentFontFilter = TextureFilter.Trilinear;
}
textSize = MeasureTextEx(font, msg, fontSize, 0);
if (IsKeyDown(KeyboardKey.Left))
{
fontPosition.X -= 10;
}
else if (IsKeyDown(KeyboardKey.Right))
{
fontPosition.X += 10;
}
// Load a dropped TTF file dynamically (at current fontSize)
if (IsFileDropped())
{
string[] files = Raylib.GetDroppedFiles();
// NOTE: We only support first ttf file dropped
if (IsFileExtension(files[0], ".ttf"))
{
UnloadFont(font);
font = LoadFontEx(files[0], (int)fontSize, null, 0);
}
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
DrawText("Use mouse wheel to change font size", 20, 20, 10, Color.Gray);
DrawText("Use KEY_RIGHT and KEY_LEFT to move text", 20, 40, 10, Color.Gray);
DrawText("Use 1, 2, 3 to change texture filter", 20, 60, 10, Color.Gray);
DrawText("Drop a new TTF font for dynamic loading", 20, 80, 10, Color.DarkGray);
DrawTextEx(font, msg, fontPosition, fontSize, 0, Color.Black);
DrawRectangle(0, screenHeight - 80, screenWidth, 80, Color.LightGray);
DrawText("CURRENT TEXTURE FILTER:", 250, 400, 20, Color.Gray);
if (currentFontFilter == TextureFilter.Point)
{
DrawText("POINT", 570, 400, 20, Color.Black);
}
else if (currentFontFilter == TextureFilter.Point)
{
DrawText("BILINEAR", 570, 400, 20, Color.Black);
}
else if (currentFontFilter == TextureFilter.Trilinear)
{
DrawText("TRILINEAR", 570, 400, 20, Color.Black);
}
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadFont(font);
CloseWindow();
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}
}

View file

@ -1,20 +1,24 @@
/*******************************************************************************************
*
* raylib [text] example - Font loading
* raylib [text] example - font loading
*
* raylib can load fonts from multiple file formats:
* Example complexity rating: [] 1/4
*
* NOTE: raylib can load fonts from multiple input file formats:
*
* - TTF/OTF > Sprite font atlas is generated on loading, user can configure
* some of the generation parameters (size, characters to include)
* - BMFonts > Angel code font fileformat, sprite font image must be provided
* together with the .fnt file, font generation cna not be configured
* together with the .fnt file, font generation can not be configured
* - XNA Spritefont > Sprite font image, following XNA Spritefont conventions,
* Characters in image must follow some spacing and order rules
*
* This example has been created using raylib 2.6 (www.raylib.com)
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
* Example originally created with raylib 1.4, last time updated with raylib 3.0
*
* Copyright (c) 2016-2019 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) 2016-2025 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
@ -23,78 +27,105 @@ using static Raylib_cs.Raylib;
namespace Examples.Text;
public class FontLoading
public partial class FontLoading : IExample
{
public static int Main()
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Text / Font Loading";
public string Title => "raylib [text] example - font loading";
private string msg;
private Font fontBm;
private Font fontTtf;
private bool useTtf;
public void Init()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [text] example - font loading");
// Define characters to draw
// NOTE: raylib supports UTF-8 encoding, following list is actually codified as UTF8 internally
string msg = "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHI\nJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmn\nopqrstuvwxyz{|}~¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓ\nÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷\nøùúûüýþÿ";
msg = "!#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHI\nJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmn\nopqrstuvwxyz{|}~¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓ\nÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷\nøùúûüýþÿ";
// NOTE: Textures/Fonts MUST be loaded after Window initialization (OpenGL context is required)
// BMFont (AngelCode) : Font data and image atlas have been generated using external program
Font fontBm = LoadFont("resources/fonts/pixantiqua.fnt");
fontBm = LoadFont("resources/fonts/pixantiqua.fnt"); // Requires "resources/fonts/pixantiqua.png"
// TTF font : Font data and atlas are generated directly from TTF
// NOTE: We define a font base size of 32 pixels tall and up-to 250 characters
Font fontTtf = LoadFontEx("resources/fonts/pixantiqua.ttf", 32, null, 250);
fontTtf = LoadFontEx("resources/fonts/pixantiqua.ttf", 32, null, 250);
bool useTtf = false;
SetTextLineSpacing(16); // Set line spacing for multiline text (when line breaks are included '\n')
SetTargetFPS(60);
useTtf = false;
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
if (IsKeyDown(KeyboardKey.Space))
{
useTtf = true;
}
else
{
useTtf = false;
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
DrawText("Hold SPACE to use TTF generated font", 20, 20, 20, Color.LightGray);
if (!useTtf)
{
DrawTextEx(fontBm, msg, new Vector2(20.0f, 100.0f), fontBm.BaseSize, 2, Color.Maroon);
DrawText("Using BMFont (Angelcode) imported", 20, GetScreenHeight() - 30, 20, Color.Gray);
}
else
{
DrawTextEx(fontTtf, msg, new Vector2(20.0f, 100.0f), fontTtf.BaseSize, 2, Color.Lime);
DrawText("Using TTF font generated", 20, GetScreenHeight() - 30, 20, Color.Gray);
}
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadFont(fontBm); // AngelCode Font unloading
UnloadFont(fontTtf); // TTF Font unloading
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [text] example - font loading");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new FontLoading();
game.Init();
// Main game loop
while (!WindowShouldClose())
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
if (IsKeyDown(KeyboardKey.Space))
{
useTtf = true;
}
else
{
useTtf = false;
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
DrawText("Hold SPACE to use TTF generated font", 20, 20, 20, Color.LightGray);
if (!useTtf)
{
DrawTextEx(fontBm, msg, new Vector2(20.0f, 100.0f), fontBm.BaseSize, 2, Color.Maroon);
DrawText("Using BMFont (Angelcode) imported", 20, GetScreenHeight() - 30, 20, Color.Gray);
}
else
{
DrawTextEx(fontTtf, msg, new Vector2(20.0f, 100.0f), fontTtf.BaseSize, 2, Color.Lime);
DrawText("Using TTF font generated", 20, GetScreenHeight() - 30, 20, Color.Gray);
}
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadFont(fontBm);
UnloadFont(fontTtf);
CloseWindow();
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;

View file

@ -1,11 +1,15 @@
/*******************************************************************************************
*
* raylib [text] example - TTF loading and usage
* raylib [text] example - font sdf
*
* This example has been created using raylib 1.3.0 (www.raylib.com)
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
* Example complexity rating: [] 3/4
*
* Copyright (c) 2015 Ramon Santamaria (@raysan5)
* Example originally created with raylib 1.3, last time updated with raylib 4.0
*
* 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) 2015-2025 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
@ -16,23 +20,44 @@ using static Raylib_cs.Raylib;
namespace Examples.Text;
public class FontSdf
public partial class FontSdf : IExample
{
public unsafe static int Main()
private const int screenWidth = 800;
private const int screenHeight = 450;
#if BROWSER
const int GlslVersion = 100; // WebGL1 needs GLSL ES 100
#else
private const int GlslVersion = 330;
#endif
public string Name => "Text / Font SDF";
public string Title => "raylib [text] example - font sdf";
private string msg;
private Font fontDefault;
private Font fontSDF;
private Shader shader;
private Vector2 fontPosition;
private Vector2 textSize;
private float fontSize;
private int currentFont;
public unsafe void Init()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [text] example - SDF fonts");
// NOTE: Textures/Fonts MUST be loaded after Window initialization (OpenGL context is required)
string msg = "Signed Distance Fields";
msg = "Signed Distance Fields";
// Loading file to memory
int fileSize = 0;
byte* fileData = LoadFileData("resources/fonts/anonymous_pro_bold.ttf", ref fileSize);
var fileSize = 0;
var fileData = LoadFileData("resources/fonts/anonymous_pro_bold.ttf", ref fileSize);
// Build the fonts in locals first: taking the address of a struct's field (&font.GlyphCount,
// &font.Recs) is only allowed for a stack local, not a heap field. Assign to the fields after.
// Default font generation from TTF font
Font fontDefault = new();
@ -40,121 +65,141 @@ public class FontSdf
fontDefault.GlyphCount = 95;
// Loading font data from memory data
// Parameters > font size: 16, no chars array provided (0), chars count: 95 (autogenerate chars array)
// Parameters > font size: 16, no glyphs array provided (0), glyphs count: 95 (autogenerate chars array)
fontDefault.Glyphs = LoadFontData(fileData, (int)fileSize, 16, null, 95, FontType.Default, &fontDefault.GlyphCount);
// Parameters > chars count: 95, font size: 16, chars padding in image: 4 px, pack method: 0 (default)
Image atlas = GenImageFontAtlas(fontDefault.Glyphs, &fontDefault.Recs, 95, 16, 4, 0);
// Parameters > glyphs count: 95, font size: 16, glyphs padding in image: 4 px, pack method: 0 (default)
var atlas = GenImageFontAtlas(fontDefault.Glyphs, &fontDefault.Recs, 95, 16, 4, 0);
fontDefault.Texture = LoadTextureFromImage(atlas);
UnloadImage(atlas);
this.fontDefault = fontDefault;
// SDF font generation from TTF font
Font fontSDF = new();
fontSDF.BaseSize = 16;
fontSDF.GlyphCount = 95;
// Parameters > font size: 16, no chars array provided (0), chars count: 0 (defaults to 95)
fontSDF.Glyphs = LoadFontData(fileData, (int)fileSize, 16, null, 0, FontType.Sdf, &fontDefault.GlyphCount);
// Parameters > chars count: 95, font size: 16, chars padding in image: 0 px, pack method: 1 (Skyline algorythm)
// Parameters > font size: 16, no glyphs array provided (0), glyphs count: 0 (defaults to 95)
fontSDF.Glyphs = LoadFontData(fileData, (int)fileSize, 16, null, 0, FontType.Sdf, &fontSDF.GlyphCount);
// Parameters > glyphs count: 95, font size: 16, glyphs padding in image: 0 px, pack method: 1 (Skyline algorythm)
atlas = GenImageFontAtlas(fontSDF.Glyphs, &fontSDF.Recs, 95, 16, 0, 1);
fontSDF.Texture = LoadTextureFromImage(atlas);
UnloadImage(atlas);
this.fontSDF = fontSDF;
// Free memory from loaded file
UnloadFileData(fileData);
UnloadFileData(fileData); // Free memory from loaded file
// Load SDF required shader (we use default vertex shader)
Shader shader = LoadShader(null, "resources/shaders/glsl330/sdf.fs");
// Required for SDF font
SetTextureFilter(fontSDF.Texture, TextureFilter.Bilinear);
shader = LoadShader(null, $"resources/shaders/glsl{GlslVersion}/sdf.fs");
SetTextureFilter(fontSDF.Texture, TextureFilter.Bilinear); // Required for SDF font
Vector2 fontPosition = new(40, screenHeight / 2 - 50);
Vector2 textSize = new(0.0f);
float fontSize = 16.0f;
// 0 - fontDefault, 1 - fontSDF
int currentFont = 0;
fontPosition = new(40, screenHeight / 2.0f - 50);
textSize = new(0.0f);
fontSize = 16.0f;
currentFont = 0; // 0 - fontDefault, 1 - fontSDF
}
SetTargetFPS(60);
public void Update()
{
// Update
//----------------------------------------------------------------------------------
fontSize += GetMouseWheelMove() * 8.0f;
if (fontSize < 6)
{
fontSize = 6;
}
if (IsKeyDown(KeyboardKey.Space))
{
currentFont = 1;
}
else
{
currentFont = 0;
}
if (currentFont == 0)
{
textSize = MeasureTextEx(fontDefault, msg, fontSize, 0);
}
else
{
textSize = MeasureTextEx(fontSDF, msg, fontSize, 0);
}
fontPosition.X = GetScreenWidth() / 2 - textSize.X / 2;
fontPosition.Y = GetScreenHeight() / 2 - textSize.Y / 2 + 80;
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
if (currentFont == 1)
{
// NOTE: SDF fonts require a custom SDf shader to compute fragment color
BeginShaderMode(shader); // Activate SDF font shader
DrawTextEx(fontSDF, msg, fontPosition, fontSize, 0, Color.Black);
EndShaderMode(); // Activate our default shader for next drawings
DrawTexture(fontSDF.Texture, 10, 10, Color.Black);
}
else
{
DrawTextEx(fontDefault, msg, fontPosition, fontSize, 0, Color.Black);
DrawTexture(fontDefault.Texture, 10, 10, Color.Black);
}
if (currentFont == 1)
{
DrawText("SDF!", 320, 20, 80, Color.Red);
}
else
{
DrawText("default font", 315, 40, 30, Color.Gray);
}
DrawText("FONT SIZE: 16.0", GetScreenWidth() - 240, 20, 20, Color.DarkGray);
DrawText($"RENDER SIZE: {fontSize:00.00}", GetScreenWidth() - 240, 50, 20, Color.DarkGray);
DrawText("Use MOUSE WHEEL to SCALE TEXT!", GetScreenWidth() - 240, 90, 10, Color.DarkGray);
DrawText("HOLD SPACE to USE SDF FONT VERSION!", 340, GetScreenHeight() - 30, 20, Color.Maroon);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadFont(fontDefault); // Default font unloading
UnloadFont(fontSDF); // SDF font unloading
UnloadShader(shader); // Unload SDF shader
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [text] example - font sdf");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new FontSdf();
game.Init();
// Main game loop
while (!WindowShouldClose())
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
fontSize += GetMouseWheelMove() * 8.0f;
if (fontSize < 6)
{
fontSize = 6;
}
if (IsKeyDown(KeyboardKey.Space))
{
currentFont = 1;
}
else
{
currentFont = 0;
}
if (currentFont == 0)
{
textSize = MeasureTextEx(fontDefault, msg, fontSize, 0);
}
else
{
textSize = MeasureTextEx(fontSDF, msg, fontSize, 0);
}
fontPosition.X = GetScreenWidth() / 2 - textSize.X / 2;
fontPosition.Y = GetScreenHeight() / 2 - textSize.Y / 2 + 80;
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
if (currentFont == 1)
{
// NOTE: SDF fonts require a custom SDf shader to compute fragment color
BeginShaderMode(shader);
DrawTextEx(fontSDF, msg, fontPosition, fontSize, 0, Color.Black);
EndShaderMode();
DrawTexture(fontSDF.Texture, 10, 10, Color.Black);
}
else
{
DrawTextEx(fontDefault, msg, fontPosition, fontSize, 0, Color.Black);
DrawTexture(fontDefault.Texture, 10, 10, Color.Black);
}
if (currentFont == 1)
{
DrawText("SDF!", 320, 20, 80, Color.Red);
}
else
{
DrawText("default font", 315, 40, 30, Color.Gray);
}
DrawText("FONT SIZE: 16.0", GetScreenWidth() - 240, 20, 20, Color.DarkGray);
DrawText($"RENDER SIZE: {fontSize:2F}", GetScreenWidth() - 240, 50, 20, Color.DarkGray);
DrawText("Use MOUSE WHEEL to SCALE TEXT!", GetScreenWidth() - 240, 90, 10, Color.DarkGray);
DrawText("PRESS SPACE to USE SDF FONT VERSION!", 340, GetScreenHeight() - 30, 20, Color.Maroon);
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadFont(fontDefault);
UnloadFont(fontSDF);
UnloadShader(shader);
CloseWindow();
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;

View file

@ -1,20 +1,25 @@
/*******************************************************************************************
*
* raylib [text] example - Sprite font loading
* raylib [text] example - font spritefont
*
* Example complexity rating: [] 1/4
*
* NOTE: Sprite fonts should be generated following this conventions:
*
* Loaded sprite fonts have been generated following XNA SpriteFont conventions:
* - Characters must be ordered starting with character 32 (Space)
* - Every character must be contained within the same Rectangle height
* - Every character and every line must be separated the same distance
* - Rectangles must be defined by a magenta color background
* - Every character and every line must be separated by the same distance (margin/padding)
* - Rectangles must be defined by a MAGENTA color background
*
* If following this constraints, a font can be provided just by an image,
* this is quite handy to avoid additional information files (like BMFonts use).
* Following those constraints, a font can be provided just by an image,
* this is quite handy to avoid additional font descriptor files (like BMFonts use)
*
* This example has been created using raylib 1.0 (www.raylib.com)
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
* Example originally created with raylib 1.0, last time updated with raylib 1.0
*
* Copyright (c) 2014 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) 2014-2025 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
@ -23,72 +28,104 @@ using static Raylib_cs.Raylib;
namespace Examples.Text;
public class FontSpritefont
public partial class FontSpritefont : IExample
{
public static int Main()
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Text / Font Spritefont";
public string Title => "raylib [text] example - font spritefont";
private string msg1;
private string msg2;
private string msg3;
private Font font1;
private Font font2;
private Font font3;
private Vector2 fontPosition1;
private Vector2 fontPosition2;
private Vector2 fontPosition3;
public void Init()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [text] example - sprite font loading");
string msg1 = "THIS IS A custom SPRITE FONT...";
string msg2 = "...and this is ANOTHER CUSTOM font...";
string msg3 = "...and a THIRD one! GREAT! :D";
msg1 = "THIS IS A custom SPRITE FONT...";
msg2 = "...and this is ANOTHER CUSTOM font...";
msg3 = "...and a THIRD one! GREAT! :D";
// NOTE: Textures/Fonts MUST be loaded after Window initialization (OpenGL context is required)
Font font1 = LoadFont("resources/fonts/custom_mecha.png");
Font font2 = LoadFont("resources/fonts/custom_alagard.png");
Font font3 = LoadFont("resources/fonts/custom_jupiter_crash.png");
font1 = LoadFont("resources/custom_mecha.png"); // Font loading
font2 = LoadFont("resources/custom_alagard.png"); // Font loading
font3 = LoadFont("resources/custom_jupiter_crash.png"); // Font loading
Vector2 fontPosition1 = new(
fontPosition1 = new(
screenWidth / 2 - MeasureTextEx(font1, msg1, font1.BaseSize, -3).X / 2,
screenHeight / 2 - font1.BaseSize / 2 - 80
);
Vector2 fontPosition2 = new(
fontPosition2 = new(
screenWidth / 2 - MeasureTextEx(font2, msg2, font2.BaseSize, -2).X / 2,
screenHeight / 2 - font2.BaseSize / 2 - 10
);
Vector2 fontPosition3 = new(
fontPosition3 = new(
screenWidth / 2 - MeasureTextEx(font3, msg3, font3.BaseSize, 2).X / 2,
screenHeight / 2 - font3.BaseSize / 2 + 50
);
}
SetTargetFPS(60);
public void Update()
{
// Update
//----------------------------------------------------------------------------------
// TODO: Update variables here...
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
DrawTextEx(font1, msg1, fontPosition1, font1.BaseSize, -3, Color.White);
DrawTextEx(font2, msg2, fontPosition2, font2.BaseSize, -2, Color.White);
DrawTextEx(font3, msg3, fontPosition3, font3.BaseSize, 2, Color.White);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadFont(font1); // Font unloading
UnloadFont(font2); // Font unloading
UnloadFont(font3); // Font unloading
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [text] example - font spritefont");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new FontSpritefont();
game.Init();
// Main game loop
while (!WindowShouldClose())
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
// TODO: Update variables here...
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
DrawTextEx(font1, msg1, fontPosition1, font1.BaseSize, -3, Color.White);
DrawTextEx(font2, msg2, fontPosition2, font2.BaseSize, -2, Color.White);
DrawTextEx(font3, msg3, fontPosition3, font3.BaseSize, 2, Color.White);
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadFont(font1);
UnloadFont(font2);
UnloadFont(font3);
CloseWindow();
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;

View file

@ -1,11 +1,15 @@
/*******************************************************************************************
*
* raylib [text] example - Text formatting
* raylib [text] example - format text
*
* This example has been created using raylib 1.1 (www.raylib.com)
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
* Example complexity rating: [] 1/4
*
* Copyright (c) 2014 Ramon Santamaria (@raysan5)
* Example originally created with raylib 1.1, last time updated with raylib 3.0
*
* 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) 2014-2025 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
@ -13,49 +17,72 @@ using static Raylib_cs.Raylib;
namespace Examples.Text;
public class FormatText
public partial class FormatText : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Text / Format Text";
public string Title => "raylib [text] example - format text";
private int score;
private int hiscore;
private int lives;
public void Init()
{
score = 100020;
hiscore = 200450;
lives = 5;
}
public void Update()
{
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
DrawText($"Score: {score:D8}", 200, 80, 20, Color.Red);
DrawText($"HiScore: {hiscore:D8}", 200, 120, 20, Color.Green);
DrawText($"Lives: {lives:D2}", 200, 160, 40, Color.Blue);
DrawText($"Elapsed Time: {GetFrameTime() * 1000:F2} ms", 200, 220, 20, Color.Black);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [text] example - format text");
InitWindow(screenWidth, screenHeight, "raylib [text] example - text formatting");
int score = 100020;
int hiscore = 200450;
int lives = 5;
SetTargetFPS(60);
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new FormatText();
game.Init();
// Main game loop
while (!WindowShouldClose())
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
// TODO: Update your variables here
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
DrawText($"Score: {score}", 200, 80, 20, Color.Red);
DrawText($"HiScore: {hiscore}", 200, 120, 20, Color.Green);
DrawText($"Lives: {lives}", 200, 160, 40, Color.Blue);
DrawText($"Elapsed Time: {GetFrameTime() * 1000} ms", 200, 220, 20, Color.Black);
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow();
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;

View file

@ -0,0 +1,312 @@
/*******************************************************************************************
*
* raylib [text] example - inline styling
*
* Example complexity rating: [] 3/4
*
* Example originally created with raylib 6.0, last time updated with raylib 6.0
*
* Example contributed by Wagner Barongello (@SultansOfCode) 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 Wagner Barongello (@SultansOfCode) and Ramon Santamaria (@raysan5)
*
********************************************************************************************/
using System;
using System.Numerics;
using System.Text;
using Raylib_cs;
using static Raylib_cs.Raylib;
namespace Examples.Text;
public partial class InlineStyling : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Text / Inline Styling";
public string Title => "raylib [text] example - inline styling";
private Vector2 textSize; // Measure text box for provided font and text
private Color colRandom; // Random color used on text
private int frameCounter; // Used to generate a new random color every certain frames
public void Init()
{
textSize = new(0, 0);
colRandom = Color.Red;
frameCounter = 0;
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
frameCounter++;
if ((frameCounter % 20) == 0)
{
colRandom.R = (byte)GetRandomValue(0, 255);
colRandom.G = (byte)GetRandomValue(0, 255);
colRandom.B = (byte)GetRandomValue(0, 255);
colRandom.A = 255;
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
// Text inline styling strategy used: [ ] delimiters for format
// - Define foreground color: [cRRGGBBAA]
// - Define background color: [bRRGGBBAA]
// - Reset formating: [r]
// Colors defined with [cRRGGBBAA] or [bRRGGBBAA] are multiplied by the base color alpha
// This allows global transparency control while keeping per-section styling (ex. text fade effects)
// Example: [bAA00AAFF][cFF0000FF]red text on gray background[r] normal text
DrawTextStyled(GetFontDefault(), "This changes the [cFF0000FF]foreground color[r] of provided text!!!",
new Vector2(100, 80), 20.0f, 2.0f, Color.Black);
DrawTextStyled(GetFontDefault(), "This changes the [bFF00FFFF]background color[r] of provided text!!!",
new Vector2(100, 120), 20.0f, 2.0f, Color.Black);
DrawTextStyled(GetFontDefault(), "This changes the [c00ff00ff][bff0000ff]foreground and background colors[r]!!!",
new Vector2(100, 160), 20.0f, 2.0f, Color.Black);
DrawTextStyled(GetFontDefault(), "This changes the [c00ff00ff]alpha[r] relative [cffffffff][b000000ff]from source[r] [cff000088]color[r]!!!",
new Vector2(100, 200), 20.0f, 2.0f, new Color(0, 0, 0, 100));
// Get pointer to formated text
string text = $"Let's be [c{colRandom.R:x2}{colRandom.G:x2}{colRandom.B:x2}FF]CREATIVE[r] !!!";
DrawTextStyled(GetFontDefault(), text, new Vector2(100, 240), 40.0f, 2.0f, Color.Black);
textSize = MeasureTextStyled(GetFontDefault(), text, 40.0f, 2.0f);
DrawRectangleLines(100, 240, (int)textSize.X, (int)textSize.Y, Color.Green);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
}
//----------------------------------------------------------------------------------
// Module Functions Definition
//----------------------------------------------------------------------------------
// Draw text using inline styling
// PARAM: color is the default text color, background color is BLANK by default
// NOTE: Using input color as the base alpha multiplied to inline styles
private static unsafe void DrawTextStyled(Font font, string text, Vector2 position, float fontSize, float spacing, Color color)
{
// Text inline styling strategy used: [ ] delimiters for format
// - Define foreground color: [cRRGGBBAA]
// - Define background color: [bRRGGBBAA]
// - Reset formating: [r]
// Example: [bAA00AAFF][cFF0000FF]red text on gray background[r] normal text
if (font.Texture.Id == 0)
{
font = GetFontDefault();
}
using var textNative = new Utf8Buffer(text);
sbyte* t = textNative.AsPointer();
int textLen = Encoding.UTF8.GetByteCount(text);
Color colFront = color;
Color colBack = Color.Blank;
int backRecPadding = 4; // Background rectangle padding
float textOffsetY = 0.0f;
float textOffsetX = 0.0f;
float textLineSpacing = 0.0f;
float scaleFactor = fontSize / font.BaseSize;
for (int i = 0; i < textLen;)
{
int codepointByteCount = 0;
int codepoint = GetCodepointNext(&t[i], &codepointByteCount);
if (codepoint == '\n')
{
textOffsetY += (fontSize + textLineSpacing);
textOffsetX = 0.0f;
}
else
{
if (codepoint == '[') // Process pipe styling
{
if (((i + 2) < textLen) && ((char)t[i + 1] == 'r') && ((char)t[i + 2] == ']')) // Reset styling
{
colFront = color;
colBack = Color.Blank;
i += 3; // Skip "[r]"
continue; // Do not draw characters
}
else if (((i + 1) < textLen) && (((char)t[i + 1] == 'c') || ((char)t[i + 1] == 'b')))
{
i += 2; // Skip "[c" or "[b" to start parsing color
// Parse following color
var colHexText = new StringBuilder();
int colHexCount = 0;
while ((i + colHexCount < textLen) && (t[i + colHexCount] != 0) && ((char)t[i + colHexCount] != ']'))
{
char ch = (char)t[i + colHexCount];
if (((ch >= '0') && (ch <= '9')) ||
((ch >= 'A') && (ch <= 'F')) ||
((ch >= 'a') && (ch <= 'f')))
{
colHexText.Append(ch);
colHexCount++;
}
else break; // Only affects while loop
}
// Convert hex color text into actual Color
uint colHexValue = colHexText.Length > 0 ? Convert.ToUInt32(colHexText.ToString(), 16) : 0;
if ((char)t[i - 1] == 'c')
{
colFront = GetColor(colHexValue);
}
else if ((char)t[i - 1] == 'b')
{
colBack = GetColor(colHexValue);
}
i += (colHexCount + 1); // Skip color value retrieved and ']'
continue; // Do not draw characters
}
}
int index = GetGlyphIndex(font, codepoint);
float increaseX = 0.0f;
if (font.Glyphs[index].AdvanceX == 0) increaseX = (font.Recs[index].Width * scaleFactor + spacing);
else increaseX += (font.Glyphs[index].AdvanceX * scaleFactor + spacing);
// Draw background rectangle color (if required)
if (colBack.A > 0) DrawRectangleRec(new Rectangle(position.X + textOffsetX, position.Y + textOffsetY - backRecPadding, increaseX, fontSize + 2 * backRecPadding), colBack);
if ((codepoint != ' ') && (codepoint != '\t'))
{
DrawTextCodepoint(font, codepoint, new Vector2(position.X + textOffsetX, position.Y + textOffsetY), fontSize, colFront);
}
textOffsetX += increaseX;
}
i += codepointByteCount;
}
}
// Measure inline styled text
// NOTE: Measuring styled text requires skipping styling data
// WARNING: Not considering line breaks
private static unsafe Vector2 MeasureTextStyled(Font font, string text, float fontSize, float spacing)
{
Vector2 textSize = new(0, 0);
if ((font.Texture.Id == 0) || (text == null) || (text.Length == 0)) return textSize; // Security check
using var textNative = new Utf8Buffer(text);
sbyte* t = textNative.AsPointer();
int textLen = Encoding.UTF8.GetByteCount(text); // Get size in bytes of text
float textWidth = 0.0f;
float textHeight = fontSize;
float scaleFactor = fontSize / (float)font.BaseSize;
int codepoint = 0; // Current character
int index = 0; // Index position in sprite font
int validCodepointCounter = 0;
for (int i = 0; i < textLen;)
{
int codepointByteCount = 0;
codepoint = GetCodepointNext(&t[i], &codepointByteCount);
if (codepoint == '[') // Ignore pipe inline styling
{
if (((i + 2) < textLen) && ((char)t[i + 1] == 'r') && ((char)t[i + 2] == ']')) // Reset styling
{
i += 3; // Skip "[r]"
continue; // Do not measure characters
}
else if (((i + 1) < textLen) && (((char)t[i + 1] == 'c') || ((char)t[i + 1] == 'b')))
{
i += 2; // Skip "[c" or "[b" to start parsing color
int colHexCount = 0;
while ((i + colHexCount < textLen) && (t[i + colHexCount] != 0) && ((char)t[i + colHexCount] != ']'))
{
char ch = (char)t[i + colHexCount];
if (((ch >= '0') && (ch <= '9')) ||
((ch >= 'A') && (ch <= 'F')) ||
((ch >= 'a') && (ch <= 'f')))
{
colHexCount++;
}
else break; // Only affects while loop
}
i += (colHexCount + 1); // Skip color value retrieved and ']'
continue; // Do not measure characters
}
}
else if (codepoint != '\n')
{
index = GetGlyphIndex(font, codepoint);
if (font.Glyphs[index].AdvanceX > 0) textWidth += font.Glyphs[index].AdvanceX;
else textWidth += (font.Recs[index].Width + font.Glyphs[index].OffsetX);
validCodepointCounter++;
i += codepointByteCount;
}
}
textSize.X = textWidth * scaleFactor + (validCodepointCounter - 1) * spacing;
textSize.Y = textHeight;
return textSize;
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [text] example - inline styling");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new InlineStyling();
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;
}
}

View file

@ -1,11 +1,15 @@
/*******************************************************************************************
*
* raylib [text] example - Input Box
* raylib [text] example - input box
*
* This example has been created using raylib 1.7 (www.raylib.com)
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
* Example complexity rating: [] 2/4
*
* Copyright (c) 2017 Ramon Santamaria (@raysan5)
* Example originally created with raylib 1.7, last time updated with raylib 3.5
*
* 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) 2017-2025 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
@ -13,149 +17,179 @@ using static Raylib_cs.Raylib;
namespace Examples.Text;
public class InputBox
public partial class InputBox : IExample
{
public const int MaxInputChars = 9;
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Text / Input Box";
public string Title => "raylib [text] example - input box";
private char[] name;
private int letterCount;
private Rectangle textBox;
private bool mouseOnText;
private int framesCounter;
public void Init()
{
// NOTE: One extra space required for null terminator char '\0'
name = new char[MaxInputChars + 1];
letterCount = 0;
textBox = new(screenWidth / 2 - 100, 180, 225, 50);
mouseOnText = false;
framesCounter = 0;
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
if (CheckCollisionPointRec(GetMousePosition(), textBox))
{
mouseOnText = true;
}
else
{
mouseOnText = false;
}
if (mouseOnText)
{
// Set the window's cursor to the I-Beam
SetMouseCursor(MouseCursor.IBeam);
// Get char pressed (unicode character) on the queue
var key = GetCharPressed();
// Check if more characters have been pressed on the same frame
while (key > 0)
{
// NOTE: Only allow keys in range [32..125]
if ((key >= 32) && (key <= 125) && (letterCount < MaxInputChars))
{
name[letterCount] = (char)key;
name[letterCount + 1] = '\0'; // Add null terminator at the end of the string
letterCount++;
}
key = GetCharPressed(); // Check next character in the queue
}
if (IsKeyPressed(KeyboardKey.Backspace))
{
letterCount -= 1;
if (letterCount < 0)
{
letterCount = 0;
}
name[letterCount] = '\0';
}
}
else
{
SetMouseCursor(MouseCursor.Default);
}
if (mouseOnText)
{
framesCounter += 1;
}
else
{
framesCounter = 0;
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
DrawText("PLACE MOUSE OVER INPUT BOX!", 240, 140, 20, Color.Gray);
DrawRectangleRec(textBox, Color.LightGray);
if (mouseOnText)
{
DrawRectangleLines(
(int)textBox.X,
(int)textBox.Y,
(int)textBox.Width,
(int)textBox.Height,
Color.Red
);
}
else
{
DrawRectangleLines(
(int)textBox.X,
(int)textBox.Y,
(int)textBox.Width,
(int)textBox.Height,
Color.DarkGray
);
}
DrawText(new string(name), (int)textBox.X + 5, (int)textBox.Y + 8, 40, Color.Maroon);
DrawText($"INPUT CHARS: {letterCount}/{MaxInputChars}", 315, 250, 20, Color.DarkGray);
if (mouseOnText)
{
if (letterCount < MaxInputChars)
{
// Draw blinking underscore char
if ((framesCounter / 20 % 2) == 0)
{
DrawText(
"_",
(int)textBox.X + 8 + MeasureText(new string(name), 40),
(int)textBox.Y + 12,
40,
Color.Maroon
);
}
}
else
{
DrawText("Press BACKSPACE to delete chars...", 230, 300, 20, Color.Gray);
}
}
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [text] example - input box");
// NOTE: One extra space required for line ending char '\0'
char[] name = new char[MaxInputChars];
int letterCount = 0;
Rectangle textBox = new(screenWidth / 2 - 100, 180, 225, 50);
bool mouseOnText = false;
int framesCounter = 0;
SetTargetFPS(60);
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new InputBox();
game.Init();
// Main game loop
while (!WindowShouldClose())
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
if (CheckCollisionPointRec(GetMousePosition(), textBox))
{
mouseOnText = true;
}
else
{
mouseOnText = false;
}
if (mouseOnText)
{
// Set the window's cursor to the I-Beam
SetMouseCursor(MouseCursor.IBeam);
// Check if more characters have been pressed on the same frame
int key = GetCharPressed();
while (key > 0)
{
// NOTE: Only allow keys in range [32..125]
if ((key >= 32) && (key <= 125) && (letterCount < MaxInputChars))
{
name[letterCount] = (char)key;
letterCount++;
}
// Check next character in the queue
key = GetCharPressed();
}
if (IsKeyPressed(KeyboardKey.Backspace))
{
letterCount -= 1;
if (letterCount < 0)
{
letterCount = 0;
}
name[letterCount] = '\0';
}
}
else
{
SetMouseCursor(MouseCursor.Default);
}
if (mouseOnText)
{
framesCounter += 1;
}
else
{
framesCounter = 0;
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
DrawText("PLACE MOUSE OVER INPUT BOX!", 240, 140, 20, Color.Gray);
DrawRectangleRec(textBox, Color.LightGray);
if (mouseOnText)
{
DrawRectangleLines(
(int)textBox.X,
(int)textBox.Y,
(int)textBox.Width,
(int)textBox.Height,
Color.Red
);
}
else
{
DrawRectangleLines(
(int)textBox.X,
(int)textBox.Y,
(int)textBox.Width,
(int)textBox.Height,
Color.DarkGray
);
}
DrawText(new string(name), (int)textBox.X + 5, (int)textBox.Y + 8, 40, Color.Maroon);
DrawText($"INPUT CHARS: {letterCount}/{MaxInputChars}", 315, 250, 20, Color.DarkGray);
if (mouseOnText)
{
if (letterCount < MaxInputChars)
{
// Draw blinking underscore char
if ((framesCounter / 20 % 2) == 0)
{
DrawText(
"_",
(int)textBox.X + 8 + MeasureText(new string(name), 40),
(int)textBox.Y + 12,
40,
Color.Maroon
);
}
}
else
{
DrawText("Press BACKSPACE to delete chars...", 230, 300, 20, Color.Gray);
}
}
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow();

View file

@ -1,14 +1,18 @@
/*******************************************************************************************
*
* raylib [text] example - raylib font loading and usage
* raylib [text] example - sprite fonts
*
* Example complexity rating: [] 1/4
*
* NOTE: raylib is distributed with some free to use fonts (even for commercial pourposes!)
* To view details and credits for those fonts, check raylib license file
*
* This example has been created using raylib 1.7 (www.raylib.com)
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
* Example originally created with raylib 1.7, last time updated with raylib 3.7
*
* Copyright (c) 2017 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) 2017-2025 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
@ -17,32 +21,38 @@ using static Raylib_cs.Raylib;
namespace Examples.Text;
public class RaylibFonts
public partial class RaylibFonts : IExample
{
public const int MaxFonts = 8;
public static int Main()
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Text / Raylib Fonts";
public string Title => "raylib [text] example - sprite fonts";
private Font[] fonts;
private string[] messages;
private int[] spacings;
private Vector2[] positions;
private Color[] colors;
public void Init()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [text] example - raylib fonts");
// NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required)
Font[] fonts = new Font[MaxFonts];
fonts = new Font[MaxFonts];
fonts[0] = LoadFont("resources/fonts/alagard.png");
fonts[1] = LoadFont("resources/fonts/pixelplay.png");
fonts[2] = LoadFont("resources/fonts/mecha.png");
fonts[3] = LoadFont("resources/fonts/setback.png");
fonts[4] = LoadFont("resources/fonts/romulus.png");
fonts[5] = LoadFont("resources/fonts/pixantiqua.png");
fonts[6] = LoadFont("resources/fonts/alpha_beta.png");
fonts[7] = LoadFont("resources/fonts/jupiter_crash.png");
fonts[0] = LoadFont("resources/sprite_fonts/alagard.png");
fonts[1] = LoadFont("resources/sprite_fonts/pixelplay.png");
fonts[2] = LoadFont("resources/sprite_fonts/mecha.png");
fonts[3] = LoadFont("resources/sprite_fonts/setback.png");
fonts[4] = LoadFont("resources/sprite_fonts/romulus.png");
fonts[5] = LoadFont("resources/sprite_fonts/pixantiqua.png");
fonts[6] = LoadFont("resources/sprite_fonts/alpha_beta.png");
fonts[7] = LoadFont("resources/sprite_fonts/jupiter_crash.png");
string[] messages = new string[MaxFonts] {
messages = new string[MaxFonts] {
"ALAGARD FONT designed by Hewett Tsoi",
"PIXELPLAY FONT designed by Aleksander Shevchuk",
"MECHA FONT designed by Captain Falcon",
@ -53,12 +63,12 @@ public class RaylibFonts
"JUPITER_CRASH FONT designed by Brian Kent (AEnigma)"
};
int[] spacings = new int[MaxFonts] { 2, 4, 8, 4, 3, 4, 4, 1 };
Vector2[] positions = new Vector2[MaxFonts];
spacings = new int[MaxFonts] { 2, 4, 8, 4, 3, 4, 4, 1 };
positions = new Vector2[MaxFonts];
for (int i = 0; i < MaxFonts; i++)
for (var i = 0; i < MaxFonts; i++)
{
float halfWidth = MeasureTextEx(fonts[i], messages[i], fonts[i].BaseSize * 2, spacings[i]).X / 2;
var halfWidth = MeasureTextEx(fonts[i], messages[i], fonts[i].BaseSize * 2, spacings[i]).X / 2;
positions[i].X = screenWidth / 2 - halfWidth;
positions[i].Y = 60 + fonts[i].BaseSize + 45 * i;
}
@ -68,7 +78,7 @@ public class RaylibFonts
positions[4].Y += 2;
positions[7].Y -= 8;
Color[] colors = new Color[MaxFonts] {
colors = new Color[MaxFonts] {
Color.Maroon,
Color.Orange,
Color.DarkGreen,
@ -78,44 +88,61 @@ public class RaylibFonts
Color.Gold,
Color.Red
};
//--------------------------------------------------------------------------------------
}
// Main game loop
while (!WindowShouldClose())
public void Update()
{
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
DrawText("free sprite fonts included with raylib", 220, 20, 20, Color.DarkGray);
DrawLine(220, 50, 600, 50, Color.DarkGray);
for (var i = 0; i < MaxFonts; i++)
{
// Update
//----------------------------------------------------------------------------------
// TODO: Update your variables here
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
DrawText("free fonts included with raylib", 250, 20, 20, Color.DarkGray);
DrawLine(220, 50, 590, 50, Color.DarkGray);
for (int i = 0; i < MaxFonts; i++)
{
DrawTextEx(fonts[i], messages[i], positions[i], fonts[i].BaseSize * 2, spacings[i], colors[i]);
}
EndDrawing();
//----------------------------------------------------------------------------------
DrawTextEx(fonts[i], messages[i], positions[i], fonts[i].BaseSize * 2, spacings[i], colors[i]);
}
// De-Initialization
//--------------------------------------------------------------------------------------
for (int i = 0; i < MaxFonts; i++)
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
// Fonts unloading
for (var i = 0; i < MaxFonts; i++)
{
UnloadFont(fonts[i]);
}
}
CloseWindow();
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [text] example - sprite fonts");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new RaylibFonts();
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;
}
}

View file

@ -1,153 +1,172 @@
/*******************************************************************************************
*
* raylib [text] example - rectangle bounds
*
* Example complexity rating: [] 4/4
*
* Example originally created with raylib 2.5, last time updated with raylib 4.0
*
* Example contributed by Vlad Adrian (@demizdor) 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) 2018-2025 Vlad Adrian (@demizdor) and Ramon Santamaria (@raysan5)
*
********************************************************************************************/
using System.Numerics;
using static Raylib_cs.Raylib;
namespace Examples.Text;
public class RectangleBounds
public partial class RectangleBounds : IExample
{
public static int Main()
private const int screenWidth = 800;
private const int screenHeight = 450;
// Minimum width and heigh for the container rectangle
private const float minWidth = 60;
private const float minHeight = 60;
private const float maxWidth = screenWidth - 50.0f;
private const float maxHeight = screenHeight - 160.0f;
public string Name => "Text / Rectangle Bounds";
public string Title => "raylib [text] example - rectangle bounds";
private string text;
private bool resizing;
private bool wordWrap;
private Rectangle container;
private Rectangle resizer;
private Vector2 lastMouse;
private Color borderColor;
private Font font;
public void Init()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
text = "Text cannot escape\tthis container\t...word wrap also works when active so here's " +
"a long text for testing.\n\nLorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod " +
"tempor incididunt ut labore et dolore magna aliqua. Nec ullamcorper sit amet risus nullam eget felis eget.";
InitWindow(screenWidth, screenHeight, "raylib [text] example - draw text inside a rectangle");
resizing = false;
wordWrap = true;
string text = "";
text += "Text cannot escape this container ...word wrap also works when active so here's a long text for testing.";
text += "\n\nLorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor ";
text += "incididunt ut labore et dolore magna aliqua. Nec ullamcorper sit amet risus nullam eget felis eget.";
bool resizing = false;
bool wordWrap = true;
Rectangle container = new(25, 25, screenWidth - 50, screenHeight - 250);
Rectangle resizer = new(
container = new(25.0f, 25.0f, screenWidth - 50.0f, screenHeight - 250.0f);
resizer = new(
container.X + container.Width - 17,
container.Y + container.Height - 17,
14,
14
);
// Minimum width and heigh for the container rectangle
const int minWidth = 60;
const int minHeight = 60;
const int maxWidth = screenWidth - 50;
const int maxHeight = screenHeight - 160;
lastMouse = new(0.0f, 0.0f); // Stores last mouse coordinates
borderColor = Color.Maroon; // Container border color
font = GetFontDefault(); // Get default system font
}
Vector2 lastMouse = new(0.0f, 0.0f);
Color borderColor = Color.Maroon;
Font font = GetFontDefault();
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
// Main game loop
while (!WindowShouldClose())
public void Update()
{
// Update
//----------------------------------------------------------------------------------
if (IsKeyPressed(KeyboardKey.Space))
{
// Update
//----------------------------------------------------------------------------------
if (IsKeyPressed(KeyboardKey.Space))
{
wordWrap = !wordWrap;
}
Vector2 mouse = GetMousePosition();
// Check if the mouse is inside the container and toggle border color
if (CheckCollisionPointRec(mouse, container))
{
borderColor = ColorAlpha(Color.Maroon, 0.4f);
}
else if (!resizing)
{
borderColor = Color.Maroon;
}
// Container resizing logic
if (resizing)
{
if (IsMouseButtonReleased(MouseButton.Left))
{
resizing = false;
}
int width = (int)(container.Width + (mouse.X - lastMouse.X));
container.Width = (width > minWidth) ? ((width < maxWidth) ? width : maxWidth) : minWidth;
int height = (int)(container.Height + (mouse.Y - lastMouse.Y));
container.Height = (height > minHeight) ? ((height < maxHeight) ? height : maxHeight) : minHeight;
}
else
{
// Check if we're resizing
if (IsMouseButtonDown(MouseButton.Left) && CheckCollisionPointRec(mouse, resizer))
{
resizing = true;
}
}
// Move resizer rectangle properly
resizer.X = container.X + container.Width - 17;
resizer.Y = container.Y + container.Height - 17;
lastMouse = mouse; // Update mouse
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
// Draw container border
DrawRectangleLinesEx(container, 3, borderColor);
// Draw text in container (add some padding)
DrawTextBoxed(
font,
text,
new Rectangle(container.X + 4, container.Y + 4, container.Width - 4, container.Height - 4),
20.0f,
2.0f,
wordWrap,
Color.Gray
);
DrawRectangleRec(resizer, borderColor);
// Draw bottom info
DrawRectangle(0, screenHeight - 54, screenWidth, 54, Color.Gray);
DrawRectangleRec(new Rectangle(382, screenHeight - 34, 12, 12), Color.Maroon);
DrawText("Word Wrap: ", 313, screenHeight - 115, 20, Color.Black);
if (wordWrap)
{
DrawText("ON", 447, screenHeight - 115, 20, Color.Red);
}
else
{
DrawText("OFF", 447, screenHeight - 115, 20, Color.Black);
}
DrawText("Press [SPACE] to toggle word wrap", 218, screenHeight - 86, 20, Color.Gray);
DrawText("Click hold & drag the to resize the container", 155, screenHeight - 38, 20, Color.RayWhite);
EndDrawing();
//----------------------------------------------------------------------------------
wordWrap = !wordWrap;
}
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow();
//--------------------------------------------------------------------------------------
var mouse = GetMousePosition();
return 0;
// Check if the mouse is inside the container and toggle border color
if (CheckCollisionPointRec(mouse, container))
{
borderColor = Fade(Color.Maroon, 0.4f);
}
else if (!resizing)
{
borderColor = Color.Maroon;
}
// Container resizing logic
if (resizing)
{
if (IsMouseButtonReleased(MouseButton.Left))
{
resizing = false;
}
var width = container.Width + (mouse.X - lastMouse.X);
container.Width = (width > minWidth) ? ((width < maxWidth) ? width : maxWidth) : minWidth;
var height = container.Height + (mouse.Y - lastMouse.Y);
container.Height = (height > minHeight) ? ((height < maxHeight) ? height : maxHeight) : minHeight;
}
else
{
// Check if we're resizing
if (IsMouseButtonDown(MouseButton.Left) && CheckCollisionPointRec(mouse, resizer))
{
resizing = true;
}
}
// Move resizer rectangle properly
resizer.X = container.X + container.Width - 17;
resizer.Y = container.Y + container.Height - 17;
lastMouse = mouse; // Update mouse
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
DrawRectangleLinesEx(container, 3, borderColor); // Draw container border
// Draw text in container (add some padding)
DrawTextBoxed(
font,
text,
new Rectangle(container.X + 4, container.Y + 4, container.Width - 4, container.Height - 4),
20.0f,
2.0f,
wordWrap,
Color.Gray
);
DrawRectangleRec(resizer, borderColor); // Draw the resize box
// Draw bottom info
DrawRectangle(0, screenHeight - 54, screenWidth, 54, Color.Gray);
DrawRectangleRec(new Rectangle(382, screenHeight - 34, 12, 12), Color.Maroon);
DrawText("Word Wrap: ", 313, screenHeight - 115, 20, Color.Black);
if (wordWrap)
{
DrawText("ON", 447, screenHeight - 115, 20, Color.Red);
}
else
{
DrawText("OFF", 447, screenHeight - 115, 20, Color.Black);
}
DrawText("Press [SPACE] to toggle word wrap", 218, screenHeight - 86, 20, Color.Gray);
DrawText("Click hold & drag the to resize the container", 155, screenHeight - 38, 20, Color.RayWhite);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
}
// Draw text using font inside rectangle limits
static void DrawTextBoxed(
private static void DrawTextBoxed(
Font font,
string text,
Rectangle rec,
@ -161,7 +180,7 @@ public class RectangleBounds
}
// Draw text using font inside rectangle limits with support for text selection
static unsafe void DrawTextBoxedSelectable(
private static unsafe void DrawTextBoxedSelectable(
Font font,
string text,
Rectangle rec,
@ -175,37 +194,37 @@ public class RectangleBounds
Color selectBackTint
)
{
int length = text.Length;
var length = text.Length;
// Offset between lines (on line break '\n')
float textOffsetY = 0;
// Offset X to next character to draw
float textOffsetX = 0.0f;
var textOffsetX = 0.0f;
// Character rectangle scaling factor
float scaleFactor = fontSize / (float)font.BaseSize;
var scaleFactor = fontSize / (float)font.BaseSize;
// Word/character wrapping mechanism variables
bool shouldMeasure = wordWrap;
var shouldMeasure = wordWrap;
// Index where to begin drawing (where a line begins)
int startLine = -1;
var startLine = -1;
// Index where to stop drawing (where a line ends)
int endLine = -1;
var endLine = -1;
// Holds last value of the character position
int lastk = -1;
var lastk = -1;
using var textNative = new Utf8Buffer(text);
for (int i = 0, k = 0; i < length; i++, k++)
{
// Get next codepoint from byte string and glyph index in font
int codepointByteCount = 0;
int codepoint = GetCodepoint(&textNative.AsPointer()[i], &codepointByteCount);
int index = GetGlyphIndex(font, codepoint);
var codepointByteCount = 0;
var codepoint = GetCodepoint(&textNative.AsPointer()[i], &codepointByteCount);
var index = GetGlyphIndex(font, codepoint);
// NOTE: Normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f)
// but we need to draw all of the bad bytes using the '?' symbol moving one byte
@ -275,7 +294,7 @@ public class RectangleBounds
glyphWidth = 0;
// Save character position when we switch states
int tmp = lastk;
var tmp = lastk;
lastk = k - 1;
k = tmp;
}
@ -305,7 +324,7 @@ public class RectangleBounds
}
// Draw selection background
bool isGlyphSelected = false;
var isGlyphSelected = false;
if ((selectStart >= 0) && (k >= selectStart) && (k < (selectStart + selectLength)))
{
DrawRectangleRec(
@ -354,4 +373,32 @@ public class RectangleBounds
}
}
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [text] example - rectangle bounds");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new RectangleBounds();
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;
}
}

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;
}
}

739
Examples/Text/Text3D.cs Normal file
View file

@ -0,0 +1,739 @@
/*******************************************************************************************
*
* raylib [text] example - 3d drawing
*
* Example complexity rating: [] 4/4
*
* NOTE: Draw a 2D text in 3D space, each letter is drawn in a quad (or 2 quads if backface is set)
* where the texture coodinates of each quad map to the texture coordinates of the glyphs
* inside the font texture
*
* A more efficient approach, i believe, would be to render the text in a render texture and
* map that texture to a plane and render that, or maybe a shader but my method allows more
* flexibility...for example to change position of each letter individually to make somethink
* like a wavy text effect
*
* Special thanks to:
* @Nighten for the DrawTextStyle() code https://github.com/NightenDushi/Raylib_DrawTextStyle
* Chris Camacho (codifies - http://bedroomcoders.co.uk/) for the alpha discard shader
*
* Example originally created with raylib 3.5, last time updated with raylib 4.0
*
* Example contributed by Vlad Adrian (@demizdor) 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) 2021-2025 Vlad Adrian (@demizdor)
*
********************************************************************************************/
using System;
using System.Numerics;
using System.Text;
using Raylib_cs;
using static Raylib_cs.Raylib;
namespace Examples.Text;
public partial class Text3D : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
#if BROWSER
const int GlslVersion = 100; // WebGL1 needs GLSL ES 100
#else
private const int GlslVersion = 330;
#endif
//--------------------------------------------------------------------------------------
// Global variables
//--------------------------------------------------------------------------------------
private const float LetterBoundrySize = 0.25f;
private const int TextMaxLayers = 32;
private static readonly Color LetterBoundryColor = Color.Violet;
private bool showLetterBoundry;
private bool showTextBoundry;
//--------------------------------------------------------------------------------------
// Types and Structures Definition
//--------------------------------------------------------------------------------------
// Configuration structure for waving the text
private struct WaveTextConfig
{
public Vector3 WaveRange;
public Vector3 WaveSpeed;
public Vector3 WaveOffset;
}
public string Name => "Text / Text 3D";
public string Title => "raylib [text] example - 3d drawing";
public ConfigFlags ConfigFlags => ConfigFlags.Msaa4xHint | ConfigFlags.VSyncHint;
public bool CursorDisabled => true;
private bool spin; // Spin the camera?
private bool multicolor; // Multicolor mode
private Camera3D camera;
private CameraMode cameraMode;
private Vector3 cubePosition;
private Vector3 cubeSize;
private Font font;
private float fontSize;
private float fontSpacing;
private float lineSpacing;
private string text;
private Vector3 tbox;
private int layers;
private int quads;
private float layerDistance;
private WaveTextConfig wcfg;
private float time;
private Color light;
private Color dark;
private Shader alphaDiscard;
private Color[] multi;
public void Init()
{
spin = true; // Spin the camera?
multicolor = false; // Multicolor mode
showLetterBoundry = false;
showTextBoundry = false;
// Define the camera to look into our 3d world
camera = new();
camera.Position = new Vector3(-10.0f, 15.0f, -10.0f); // Camera position
camera.Target = new Vector3(0.0f, 0.0f, 0.0f); // Camera looking at point
camera.Up = new Vector3(0.0f, 1.0f, 0.0f); // Camera up vector (rotation towards target)
camera.FovY = 45.0f; // Camera field-of-view Y
camera.Projection = CameraProjection.Perspective; // Camera projection type
cameraMode = CameraMode.Orbital;
cubePosition = new Vector3(0.0f, 1.0f, 0.0f);
cubeSize = new Vector3(2.0f, 2.0f, 2.0f);
// Use the default font
font = GetFontDefault();
fontSize = 0.8f;
fontSpacing = 0.05f;
lineSpacing = -0.1f;
// Set the text (using markdown!)
text = "Hello ~~World~~ in 3D!";
tbox = new Vector3(0, 0, 0);
layers = 1;
quads = 0;
layerDistance = 0.01f;
wcfg = new WaveTextConfig();
wcfg.WaveSpeed.X = wcfg.WaveSpeed.Y = 3.0f; wcfg.WaveSpeed.Z = 0.5f;
wcfg.WaveOffset.X = wcfg.WaveOffset.Y = wcfg.WaveOffset.Z = 0.35f;
wcfg.WaveRange.X = wcfg.WaveRange.Y = wcfg.WaveRange.Z = 0.45f;
time = 0.0f;
// Setup a light and dark color
light = Color.Maroon;
dark = Color.Red;
// Load the alpha discard shader
alphaDiscard = LoadShader(null, $"resources/shaders/glsl{GlslVersion}/alpha_discard.fs");
// Array filled with multiple random colors (when multicolor mode is set)
multi = new Color[TextMaxLayers];
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, cameraMode);
// Handle font files dropped
if (IsFileDropped())
{
string[] droppedFiles = GetDroppedFiles();
// NOTE: We only support first ttf file dropped
if (IsFileExtension(droppedFiles[0], ".ttf"))
{
UnloadFont(font);
font = LoadFontEx(droppedFiles[0], (int)fontSize, null, 0);
}
else if (IsFileExtension(droppedFiles[0], ".fnt"))
{
UnloadFont(font);
font = LoadFont(droppedFiles[0]);
fontSize = (float)font.BaseSize;
}
}
// Handle Events
if (IsKeyPressed(KeyboardKey.F1)) showLetterBoundry = !showLetterBoundry;
if (IsKeyPressed(KeyboardKey.F2)) showTextBoundry = !showTextBoundry;
if (IsKeyPressed(KeyboardKey.F3))
{
// Handle camera change
spin = !spin;
// we need to reset the camera when changing modes
camera = new();
camera.Target = new Vector3(0.0f, 0.0f, 0.0f); // Camera looking at point
camera.Up = new Vector3(0.0f, 1.0f, 0.0f); // Camera up vector (rotation towards target)
camera.FovY = 45.0f; // Camera field-of-view Y
camera.Projection = CameraProjection.Perspective; // Camera mode type
if (spin)
{
camera.Position = new Vector3(-10.0f, 15.0f, -10.0f); // Camera position
cameraMode = CameraMode.Orbital;
}
else
{
camera.Position = new Vector3(10.0f, 10.0f, -10.0f); // Camera position
cameraMode = CameraMode.Free;
}
}
// Handle clicking the cube
if (IsMouseButtonPressed(MouseButton.Left))
{
Ray ray = GetScreenToWorldRay(GetMousePosition(), camera);
// Check collision between ray and box
RayCollision collision = GetRayCollisionBox(ray,
new BoundingBox(
new Vector3(cubePosition.X - cubeSize.X / 2, cubePosition.Y - cubeSize.Y / 2, cubePosition.Z - cubeSize.Z / 2),
new Vector3(cubePosition.X + cubeSize.X / 2, cubePosition.Y + cubeSize.Y / 2, cubePosition.Z + cubeSize.Z / 2)));
if (collision.Hit)
{
// Generate new random colors
light = GenerateRandomColor(0.5f, 0.78f);
dark = GenerateRandomColor(0.4f, 0.58f);
}
}
// Handle text layers changes
if (IsKeyPressed(KeyboardKey.Home)) { if (layers > 1) --layers; }
else if (IsKeyPressed(KeyboardKey.End)) { if (layers < TextMaxLayers) ++layers; }
// Handle text changes
if (IsKeyPressed(KeyboardKey.Left)) fontSize -= 0.5f;
else if (IsKeyPressed(KeyboardKey.Right)) fontSize += 0.5f;
else if (IsKeyPressed(KeyboardKey.Up)) fontSpacing -= 0.1f;
else if (IsKeyPressed(KeyboardKey.Down)) fontSpacing += 0.1f;
else if (IsKeyPressed(KeyboardKey.PageUp)) lineSpacing -= 0.1f;
else if (IsKeyPressed(KeyboardKey.PageDown)) lineSpacing += 0.1f;
else if (IsKeyDown(KeyboardKey.Insert)) layerDistance -= 0.001f;
else if (IsKeyDown(KeyboardKey.Delete)) layerDistance += 0.001f;
else if (IsKeyPressed(KeyboardKey.Tab))
{
multicolor = !multicolor; // Enable /disable multicolor mode
if (multicolor)
{
// Fill color array with random colors
for (int i = 0; i < TextMaxLayers; i++)
{
multi[i] = GenerateRandomColor(0.5f, 0.8f);
multi[i].A = (byte)GetRandomValue(0, 255);
}
}
}
// Handle text input
int ch = GetCharPressed();
if (IsKeyPressed(KeyboardKey.Backspace))
{
// Remove last char
if (text.Length > 0) text = text.Substring(0, text.Length - 1);
}
else if (IsKeyPressed(KeyboardKey.Enter))
{
// handle newline
if (text.Length < 63) text += '\n';
}
else
{
// append only printable chars
if ((ch != 0) && (text.Length < 63)) text += char.ConvertFromUtf32(ch);
}
// Measure 3D text so we can center it
tbox = MeasureTextWave3D(font, text, fontSize, fontSpacing, lineSpacing);
quads = 0; // Reset quad counter
time += GetFrameTime(); // Update timer needed by `DrawTextWave3D()`
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawCubeV(cubePosition, cubeSize, dark);
DrawCubeWires(cubePosition, 2.1f, 2.1f, 2.1f, light);
DrawGrid(10, 2.0f);
// Use a shader to handle the depth buffer issue with transparent textures
// NOTE: more info at https://bedroomcoders.co.uk/posts/198
BeginShaderMode(alphaDiscard);
// Draw the 3D text above the red cube
Rlgl.PushMatrix();
Rlgl.Rotatef(90.0f, 1.0f, 0.0f, 0.0f);
Rlgl.Rotatef(90.0f, 0.0f, 0.0f, -1.0f);
for (int i = 0; i < layers; i++)
{
Color clr = light;
if (multicolor) clr = multi[i];
DrawTextWave3D(font, text, new Vector3(-tbox.X / 2.0f, layerDistance * i, -4.5f), fontSize, fontSpacing, lineSpacing, true, wcfg, time, clr);
}
// Draw the text boundry if set
if (showTextBoundry) DrawCubeWiresV(new Vector3(0.0f, 0.0f, -4.5f + tbox.Z / 2), tbox, dark);
Rlgl.PopMatrix();
// Don't draw the letter boundries for the 3D text below
bool slb = showLetterBoundry;
showLetterBoundry = false;
// Draw 3D options (use default font)
//-------------------------------------------------------------------------
Rlgl.PushMatrix();
Rlgl.Rotatef(180.0f, 0.0f, 1.0f, 0.0f);
string opt = $"< SIZE: {fontSize:0.0} >";
quads += opt.Length;
Vector2 m = MeasureTextEx(GetFontDefault(), opt, 0.8f, 0.1f);
Vector3 pos = new(-m.X / 2.0f, 0.01f, 2.0f);
DrawText3D(GetFontDefault(), opt, pos, 0.8f, 0.1f, 0.0f, false, Color.Blue);
pos.Z += 0.5f + m.Y;
opt = $"< SPACING: {fontSpacing:0.0} >";
quads += opt.Length;
m = MeasureTextEx(GetFontDefault(), opt, 0.8f, 0.1f);
pos.X = -m.X / 2.0f;
DrawText3D(GetFontDefault(), opt, pos, 0.8f, 0.1f, 0.0f, false, Color.Blue);
pos.Z += 0.5f + m.Y;
opt = $"< LINE: {lineSpacing:0.0} >";
quads += opt.Length;
m = MeasureTextEx(GetFontDefault(), opt, 0.8f, 0.1f);
pos.X = -m.X / 2.0f;
DrawText3D(GetFontDefault(), opt, pos, 0.8f, 0.1f, 0.0f, false, Color.Blue);
pos.Z += 0.5f + m.Y;
opt = $"< LBOX: {(slb ? "ON" : "OFF"),3} >";
quads += opt.Length;
m = MeasureTextEx(GetFontDefault(), opt, 0.8f, 0.1f);
pos.X = -m.X / 2.0f;
DrawText3D(GetFontDefault(), opt, pos, 0.8f, 0.1f, 0.0f, false, Color.Red);
pos.Z += 0.5f + m.Y;
opt = $"< TBOX: {(showTextBoundry ? "ON" : "OFF"),3} >";
quads += opt.Length;
m = MeasureTextEx(GetFontDefault(), opt, 0.8f, 0.1f);
pos.X = -m.X / 2.0f;
DrawText3D(GetFontDefault(), opt, pos, 0.8f, 0.1f, 0.0f, false, Color.Red);
pos.Z += 0.5f + m.Y;
opt = $"< LAYER DISTANCE: {layerDistance:0.000} >";
quads += opt.Length;
m = MeasureTextEx(GetFontDefault(), opt, 0.8f, 0.1f);
pos.X = -m.X / 2.0f;
DrawText3D(GetFontDefault(), opt, pos, 0.8f, 0.1f, 0.0f, false, Color.DarkPurple);
Rlgl.PopMatrix();
//-------------------------------------------------------------------------
// Draw 3D info text (use default font)
//-------------------------------------------------------------------------
opt = "All the text displayed here is in 3D";
quads += 36;
m = MeasureTextEx(GetFontDefault(), opt, 1.0f, 0.05f);
pos = new Vector3(-m.X / 2.0f, 0.01f, 2.0f);
DrawText3D(GetFontDefault(), opt, pos, 1.0f, 0.05f, 0.0f, false, Color.DarkBlue);
pos.Z += 1.5f + m.Y;
opt = "press [Left]/[Right] to change the font size";
quads += 44;
m = MeasureTextEx(GetFontDefault(), opt, 0.6f, 0.05f);
pos.X = -m.X / 2.0f;
DrawText3D(GetFontDefault(), opt, pos, 0.6f, 0.05f, 0.0f, false, Color.DarkBlue);
pos.Z += 0.5f + m.Y;
opt = "press [Up]/[Down] to change the font spacing";
quads += 44;
m = MeasureTextEx(GetFontDefault(), opt, 0.6f, 0.05f);
pos.X = -m.X / 2.0f;
DrawText3D(GetFontDefault(), opt, pos, 0.6f, 0.05f, 0.0f, false, Color.DarkBlue);
pos.Z += 0.5f + m.Y;
opt = "press [PgUp]/[PgDown] to change the line spacing";
quads += 48;
m = MeasureTextEx(GetFontDefault(), opt, 0.6f, 0.05f);
pos.X = -m.X / 2.0f;
DrawText3D(GetFontDefault(), opt, pos, 0.6f, 0.05f, 0.0f, false, Color.DarkBlue);
pos.Z += 0.5f + m.Y;
opt = "press [F1] to toggle the letter boundry";
quads += 39;
m = MeasureTextEx(GetFontDefault(), opt, 0.6f, 0.05f);
pos.X = -m.X / 2.0f;
DrawText3D(GetFontDefault(), opt, pos, 0.6f, 0.05f, 0.0f, false, Color.DarkBlue);
pos.Z += 0.5f + m.Y;
opt = "press [F2] to toggle the text boundry";
quads += 37;
m = MeasureTextEx(GetFontDefault(), opt, 0.6f, 0.05f);
pos.X = -m.X / 2.0f;
DrawText3D(GetFontDefault(), opt, pos, 0.6f, 0.05f, 0.0f, false, Color.DarkBlue);
//-------------------------------------------------------------------------
showLetterBoundry = slb;
EndShaderMode();
EndMode3D();
// Draw 2D info text & stats
//-------------------------------------------------------------------------
DrawText("Drag & drop a font file to change the font!\nType something, see what happens!\n\n" +
"Press [F3] to toggle the camera", 10, 35, 10, Color.Black);
quads += TextLengthUtf8(text) * 2 * layers;
string tmp = $"{layers,2} layer(s) | {(spin ? "ORBITAL" : "FREE")} camera | {quads,4} quads ({quads * 4,4} verts)";
int width = MeasureText(tmp, 10);
DrawText(tmp, screenWidth - 20 - width, 10, 10, Color.DarkGreen);
tmp = "[Home]/[End] to add/remove 3D text layers";
width = MeasureText(tmp, 10);
DrawText(tmp, screenWidth - 20 - width, 25, 10, Color.DarkGray);
tmp = "[Insert]/[Delete] to increase/decrease distance between layers";
width = MeasureText(tmp, 10);
DrawText(tmp, screenWidth - 20 - width, 40, 10, Color.DarkGray);
tmp = "click the [CUBE] for a random color";
width = MeasureText(tmp, 10);
DrawText(tmp, screenWidth - 20 - width, 55, 10, Color.DarkGray);
tmp = "[Tab] to toggle multicolor mode";
width = MeasureText(tmp, 10);
DrawText(tmp, screenWidth - 20 - width, 70, 10, Color.DarkGray);
//-------------------------------------------------------------------------
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadFont(font);
}
//--------------------------------------------------------------------------------------
// Module Functions Definitions
//--------------------------------------------------------------------------------------
// Get the total length in bytes of a UTF-8 string (raylib TextLength equivalent)
private static int TextLengthUtf8(string text)
{
return Encoding.UTF8.GetByteCount(text);
}
// Draw codepoint at specified position in 3D space
private unsafe void DrawTextCodepoint3D(Font font, int codepoint, Vector3 position, float fontSize, bool backface, Color tint)
{
// Character index position in sprite font
// NOTE: In case a codepoint is not available in the font, index returned points to '?'
int index = GetGlyphIndex(font, codepoint);
float scale = fontSize / (float)font.BaseSize;
// Character destination rectangle on screen
// NOTE: We consider charsPadding on drawing
position.X += (font.Glyphs[index].OffsetX - font.GlyphPadding) * scale;
position.Z += (font.Glyphs[index].OffsetY - font.GlyphPadding) * scale;
// Character source rectangle from font texture atlas
// NOTE: We consider chars padding when drawing, it could be required for outline/glow shader effects
Rectangle srcRec = new(font.Recs[index].X - font.GlyphPadding, font.Recs[index].Y - font.GlyphPadding,
font.Recs[index].Width + 2.0f * font.GlyphPadding, font.Recs[index].Height + 2.0f * font.GlyphPadding);
float width = (font.Recs[index].Width + 2.0f * font.GlyphPadding) * scale;
float height = (font.Recs[index].Height + 2.0f * font.GlyphPadding) * scale;
if (font.Texture.Id > 0)
{
const float x = 0.0f;
const float y = 0.0f;
const float z = 0.0f;
// normalized texture coordinates of the glyph inside the font texture (0.0f -> 1.0f)
float tx = srcRec.X / font.Texture.Width;
float ty = srcRec.Y / font.Texture.Height;
float tw = (srcRec.X + srcRec.Width) / font.Texture.Width;
float th = (srcRec.Y + srcRec.Height) / font.Texture.Height;
if (showLetterBoundry) DrawCubeWiresV(new Vector3(position.X + width / 2, position.Y, position.Z + height / 2), new Vector3(width, LetterBoundrySize, height), LetterBoundryColor);
Rlgl.CheckRenderBatchLimit(4 + 4 * (backface ? 1 : 0));
Rlgl.SetTexture(font.Texture.Id);
Rlgl.PushMatrix();
Rlgl.Translatef(position.X, position.Y, position.Z);
Rlgl.Begin(DrawMode.Quads);
Rlgl.Color4ub(tint.R, tint.G, tint.B, tint.A);
// Front Face
Rlgl.Normal3f(0.0f, 1.0f, 0.0f); // Normal Pointing Up
Rlgl.TexCoord2f(tx, ty); Rlgl.Vertex3f(x, y, z); // Top Left Of The Texture and Quad
Rlgl.TexCoord2f(tx, th); Rlgl.Vertex3f(x, y, z + height); // Bottom Left Of The Texture and Quad
Rlgl.TexCoord2f(tw, th); Rlgl.Vertex3f(x + width, y, z + height); // Bottom Right Of The Texture and Quad
Rlgl.TexCoord2f(tw, ty); Rlgl.Vertex3f(x + width, y, z); // Top Right Of The Texture and Quad
if (backface)
{
// Back Face
Rlgl.Normal3f(0.0f, -1.0f, 0.0f); // Normal Pointing Down
Rlgl.TexCoord2f(tx, ty); Rlgl.Vertex3f(x, y, z); // Top Right Of The Texture and Quad
Rlgl.TexCoord2f(tw, ty); Rlgl.Vertex3f(x + width, y, z); // Top Left Of The Texture and Quad
Rlgl.TexCoord2f(tw, th); Rlgl.Vertex3f(x + width, y, z + height); // Bottom Left Of The Texture and Quad
Rlgl.TexCoord2f(tx, th); Rlgl.Vertex3f(x, y, z + height); // Bottom Right Of The Texture and Quad
}
Rlgl.End();
Rlgl.PopMatrix();
Rlgl.SetTexture(0);
}
}
// Draw a 2D text in 3D space
private unsafe void DrawText3D(Font font, string text, Vector3 position, float fontSize, float fontSpacing, float lineSpacing, bool backface, Color tint)
{
using var textNative = new Utf8Buffer(text);
sbyte* t = textNative.AsPointer();
int length = Encoding.UTF8.GetByteCount(text); // Total length in bytes of the text, scanned by codepoints in loop
float textOffsetY = 0.0f; // Offset between lines (on line break '\n')
float textOffsetX = 0.0f; // Offset X to next character to draw
float scale = fontSize / (float)font.BaseSize;
for (int i = 0; i < length;)
{
// Get next codepoint from byte string and glyph index in font
int codepointByteCount = 0;
int codepoint = GetCodepoint(&t[i], &codepointByteCount);
int index = GetGlyphIndex(font, codepoint);
// NOTE: Normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f)
// but we need to draw all of the bad bytes using the '?' symbol moving one byte
if (codepoint == 0x3f) codepointByteCount = 1;
if (codepoint == '\n')
{
// NOTE: Fixed line spacing of 1.5 line-height
// TODO: Support custom line spacing defined by user
textOffsetY += fontSize + lineSpacing;
textOffsetX = 0.0f;
}
else
{
if ((codepoint != ' ') && (codepoint != '\t'))
{
DrawTextCodepoint3D(font, codepoint, new Vector3(position.X + textOffsetX, position.Y, position.Z + textOffsetY), fontSize, backface, tint);
}
if (font.Glyphs[index].AdvanceX == 0) textOffsetX += font.Recs[index].Width * scale + fontSpacing;
else textOffsetX += font.Glyphs[index].AdvanceX * scale + fontSpacing;
}
i += codepointByteCount; // Move text bytes counter to next codepoint
}
}
// Draw a 2D text in 3D space and wave the parts that start with `~~` and end with `~~`
// This is a modified version of the original code by @Nighten found here https://github.com/NightenDushi/Raylib_DrawTextStyle
private unsafe void DrawTextWave3D(Font font, string text, Vector3 position, float fontSize, float fontSpacing, float lineSpacing, bool backface, WaveTextConfig config, float time, Color tint)
{
using var textNative = new Utf8Buffer(text);
sbyte* t = textNative.AsPointer();
int length = Encoding.UTF8.GetByteCount(text); // Total length in bytes of the text, scanned by codepoints in loop
float textOffsetY = 0.0f; // Offset between lines (on line break '\n')
float textOffsetX = 0.0f; // Offset X to next character to draw
float scale = fontSize / (float)font.BaseSize;
bool wave = false;
for (int i = 0, k = 0; i < length; ++k)
{
// Get next codepoint from byte string and glyph index in font
int codepointByteCount = 0;
int codepoint = GetCodepoint(&t[i], &codepointByteCount);
int index = GetGlyphIndex(font, codepoint);
// NOTE: Normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f)
// but we need to draw all of the bad bytes using the '?' symbol moving one byte
if (codepoint == 0x3f) codepointByteCount = 1;
if (codepoint == '\n')
{
// NOTE: Fixed line spacing of 1.5 line-height
// TODO: Support custom line spacing defined by user
textOffsetY += fontSize + lineSpacing;
textOffsetX = 0.0f;
k = 0;
}
else if (codepoint == '~')
{
if (GetCodepoint(&t[i + 1], &codepointByteCount) == '~')
{
codepointByteCount += 1;
wave = !wave;
}
}
else
{
if ((codepoint != ' ') && (codepoint != '\t'))
{
Vector3 pos = position;
if (wave) // Apply the wave effect
{
pos.X += MathF.Sin(time * config.WaveSpeed.X - k * config.WaveOffset.X) * config.WaveRange.X;
pos.Y += MathF.Sin(time * config.WaveSpeed.Y - k * config.WaveOffset.Y) * config.WaveRange.Y;
pos.Z += MathF.Sin(time * config.WaveSpeed.Z - k * config.WaveOffset.Z) * config.WaveRange.Z;
}
DrawTextCodepoint3D(font, codepoint, new Vector3(pos.X + textOffsetX, pos.Y, pos.Z + textOffsetY), fontSize, backface, tint);
}
if (font.Glyphs[index].AdvanceX == 0) textOffsetX += font.Recs[index].Width * scale + fontSpacing;
else textOffsetX += font.Glyphs[index].AdvanceX * scale + fontSpacing;
}
i += codepointByteCount; // Move text bytes counter to next codepoint
}
}
// Measure a text in 3D ignoring the `~~` chars
private unsafe Vector3 MeasureTextWave3D(Font font, string text, float fontSize, float fontSpacing, float lineSpacing)
{
using var textNative = new Utf8Buffer(text);
sbyte* t = textNative.AsPointer();
int len = Encoding.UTF8.GetByteCount(text);
int tempLen = 0; // Used to count longer text line num chars
int lenCounter = 0;
float tempTextWidth = 0.0f; // Used to count longer text line width
float scale = fontSize / (float)font.BaseSize;
float textHeight = scale;
float textWidth = 0.0f;
int letter = 0; // Current character
int index = 0; // Index position in sprite font
for (int i = 0; i < len; i++)
{
int next = 0;
letter = GetCodepoint(&t[i], &next);
index = GetGlyphIndex(font, letter);
// NOTE: normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f)
// but we need to draw all of the bad bytes using the '?' symbol so to not skip any we set next = 1
if (letter == 0x3f) next = 1;
i += next - 1;
if (letter != '\n')
{
if (letter == '~' && GetCodepoint(&t[i + 1], &next) == '~')
{
i++;
}
else
{
lenCounter++;
if (font.Glyphs[index].AdvanceX != 0) textWidth += font.Glyphs[index].AdvanceX * scale;
else textWidth += (font.Recs[index].Width + font.Glyphs[index].OffsetX) * scale;
}
}
else
{
if (tempTextWidth < textWidth) tempTextWidth = textWidth;
lenCounter = 0;
textWidth = 0.0f;
textHeight += fontSize + lineSpacing;
}
if (tempLen < lenCounter) tempLen = lenCounter;
}
if (tempTextWidth < textWidth) tempTextWidth = textWidth;
Vector3 vec = new(0, 0, 0);
vec.X = tempTextWidth + ((tempLen - 1) * fontSpacing); // Adds chars spacing to measure
vec.Y = 0.25f;
vec.Z = textHeight;
return vec;
}
// Generates a nice color with a random hue
private static Color GenerateRandomColor(float s, float v)
{
const float Phi = 0.618033988749895f; // Golden ratio conjugate
float h = (float)GetRandomValue(0, 360);
h = (h + h * Phi) % 360.0f;
return ColorFromHSV(h, s, v);
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
SetConfigFlags(ConfigFlags.Msaa4xHint | ConfigFlags.VSyncHint);
InitWindow(screenWidth, screenHeight, "raylib [text] example - 3d drawing");
DisableCursor(); // Limit cursor to relative movement inside the window
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new Text3D();
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;
}
}

View file

@ -1,228 +1,226 @@
/*******************************************************************************************
*
* raylib [text] example - Using unicode with raylib
* raylib [text] example - unicode emojis
*
* This example has been created using raylib 2.5 (www.raylib.com)
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
* Example complexity rating: [] 4/4
*
* Example originally created with raylib 2.5, last time updated with raylib 4.0
*
* Example contributed by Vlad Adrian (@demizdor) and reviewed by Ramon Santamaria (@raysan5)
*
* Copyright (c) 2019 Vlad Adrian (@demizdor) and 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) 2019-2025 Vlad Adrian (@demizdor) and Ramon Santamaria (@raysan5)
*
********************************************************************************************/
using System.Numerics;
using System.Text;
using Raylib_cs;
using static Raylib_cs.Raylib;
using static Raylib_cs.Color;
namespace Examples.Text
namespace Examples.Text;
public partial class Unicode : IExample
{
public class Unicode
{
const int EMOJI_PER_WIDTH = 8;
const int EMOJI_PER_HEIGHT = 4;
private const int screenWidth = 800;
private const int screenHeight = 450;
// String containing 180 emoji codepoints separated by a '\0' char
string emojiCodepoints = @"\xF0\x9F\x8C\x80\x00\xF0\x9F\x98\x80\x00\xF0\x9F\x98\x82\x00\xF0\x9F\xA4\xA3\x00\xF0\x9F\x98\x83\x00\xF0\x9F\x98\x86\x00\xF0\x9F\x98\x89\x00"
"\xF0\x9F\x98\x8B\x00\xF0\x9F\x98\x8E\x00\xF0\x9F\x98\x8D\x00\xF0\x9F\x98\x98\x00\xF0\x9F\x98\x97\x00\xF0\x9F\x98\x99\x00\xF0\x9F\x98\x9A\x00\xF0\x9F\x99\x82\x00"
"\xF0\x9F\xA4\x97\x00\xF0\x9F\xA4\xA9\x00\xF0\x9F\xA4\x94\x00\xF0\x9F\xA4\xA8\x00\xF0\x9F\x98\x90\x00\xF0\x9F\x98\x91\x00\xF0\x9F\x98\xB6\x00\xF0\x9F\x99\x84\x00"
"\xF0\x9F\x98\x8F\x00\xF0\x9F\x98\xA3\x00\xF0\x9F\x98\xA5\x00\xF0\x9F\x98\xAE\x00\xF0\x9F\xA4\x90\x00\xF0\x9F\x98\xAF\x00\xF0\x9F\x98\xAA\x00\xF0\x9F\x98\xAB\x00"
"\xF0\x9F\x98\xB4\x00\xF0\x9F\x98\x8C\x00\xF0\x9F\x98\x9B\x00\xF0\x9F\x98\x9D\x00\xF0\x9F\xA4\xA4\x00\xF0\x9F\x98\x92\x00\xF0\x9F\x98\x95\x00\xF0\x9F\x99\x83\x00"
"\xF0\x9F\xA4\x91\x00\xF0\x9F\x98\xB2\x00\xF0\x9F\x99\x81\x00\xF0\x9F\x98\x96\x00\xF0\x9F\x98\x9E\x00\xF0\x9F\x98\x9F\x00\xF0\x9F\x98\xA4\x00\xF0\x9F\x98\xA2\x00"
"\xF0\x9F\x98\xAD\x00\xF0\x9F\x98\xA6\x00\xF0\x9F\x98\xA9\x00\xF0\x9F\xA4\xAF\x00\xF0\x9F\x98\xAC\x00\xF0\x9F\x98\xB0\x00\xF0\x9F\x98\xB1\x00\xF0\x9F\x98\xB3\x00"
"\xF0\x9F\xA4\xAA\x00\xF0\x9F\x98\xB5\x00\xF0\x9F\x98\xA1\x00\xF0\x9F\x98\xA0\x00\xF0\x9F\xA4\xAC\x00\xF0\x9F\x98\xB7\x00\xF0\x9F\xA4\x92\x00\xF0\x9F\xA4\x95\x00"
"\xF0\x9F\xA4\xA2\x00\xF0\x9F\xA4\xAE\x00\xF0\x9F\xA4\xA7\x00\xF0\x9F\x98\x87\x00\xF0\x9F\xA4\xA0\x00\xF0\x9F\xA4\xAB\x00\xF0\x9F\xA4\xAD\x00\xF0\x9F\xA7\x90\x00"
"\xF0\x9F\xA4\x93\x00\xF0\x9F\x98\x88\x00\xF0\x9F\x91\xBF\x00\xF0\x9F\x91\xB9\x00\xF0\x9F\x91\xBA\x00\xF0\x9F\x92\x80\x00\xF0\x9F\x91\xBB\x00\xF0\x9F\x91\xBD\x00"
"\xF0\x9F\x91\xBE\x00\xF0\x9F\xA4\x96\x00\xF0\x9F\x92\xA9\x00\xF0\x9F\x98\xBA\x00\xF0\x9F\x98\xB8\x00\xF0\x9F\x98\xB9\x00\xF0\x9F\x98\xBB\x00\xF0\x9F\x98\xBD\x00"
"\xF0\x9F\x99\x80\x00\xF0\x9F\x98\xBF\x00\xF0\x9F\x8C\xBE\x00\xF0\x9F\x8C\xBF\x00\xF0\x9F\x8D\x80\x00\xF0\x9F\x8D\x83\x00\xF0\x9F\x8D\x87\x00\xF0\x9F\x8D\x93\x00"
"\xF0\x9F\xA5\x9D\x00\xF0\x9F\x8D\x85\x00\xF0\x9F\xA5\xA5\x00\xF0\x9F\xA5\x91\x00\xF0\x9F\x8D\x86\x00\xF0\x9F\xA5\x94\x00\xF0\x9F\xA5\x95\x00\xF0\x9F\x8C\xBD\x00"
"\xF0\x9F\x8C\xB6\x00\xF0\x9F\xA5\x92\x00\xF0\x9F\xA5\xA6\x00\xF0\x9F\x8D\x84\x00\xF0\x9F\xA5\x9C\x00\xF0\x9F\x8C\xB0\x00\xF0\x9F\x8D\x9E\x00\xF0\x9F\xA5\x90\x00"
"\xF0\x9F\xA5\x96\x00\xF0\x9F\xA5\xA8\x00\xF0\x9F\xA5\x9E\x00\xF0\x9F\xA7\x80\x00\xF0\x9F\x8D\x96\x00\xF0\x9F\x8D\x97\x00\xF0\x9F\xA5\xA9\x00\xF0\x9F\xA5\x93\x00"
"\xF0\x9F\x8D\x94\x00\xF0\x9F\x8D\x9F\x00\xF0\x9F\x8D\x95\x00\xF0\x9F\x8C\xAD\x00\xF0\x9F\xA5\xAA\x00\xF0\x9F\x8C\xAE\x00\xF0\x9F\x8C\xAF\x00\xF0\x9F\xA5\x99\x00"
"\xF0\x9F\xA5\x9A\x00\xF0\x9F\x8D\xB3\x00\xF0\x9F\xA5\x98\x00\xF0\x9F\x8D\xB2\x00\xF0\x9F\xA5\xA3\x00\xF0\x9F\xA5\x97\x00\xF0\x9F\x8D\xBF\x00\xF0\x9F\xA5\xAB\x00"
"\xF0\x9F\x8D\xB1\x00\xF0\x9F\x8D\x98\x00\xF0\x9F\x8D\x9D\x00\xF0\x9F\x8D\xA0\x00\xF0\x9F\x8D\xA2\x00\xF0\x9F\x8D\xA5\x00\xF0\x9F\x8D\xA1\x00\xF0\x9F\xA5\x9F\x00"
"\xF0\x9F\xA5\xA1\x00\xF0\x9F\x8D\xA6\x00\xF0\x9F\x8D\xAA\x00\xF0\x9F\x8E\x82\x00\xF0\x9F\x8D\xB0\x00\xF0\x9F\xA5\xA7\x00\xF0\x9F\x8D\xAB\x00\xF0\x9F\x8D\xAF\x00"
"\xF0\x9F\x8D\xBC\x00\xF0\x9F\xA5\x9B\x00\xF0\x9F\x8D\xB5\x00\xF0\x9F\x8D\xB6\x00\xF0\x9F\x8D\xBE\x00\xF0\x9F\x8D\xB7\x00\xF0\x9F\x8D\xBB\x00\xF0\x9F\xA5\x82\x00"
"\xF0\x9F\xA5\x83\x00\xF0\x9F\xA5\xA4\x00\xF0\x9F\xA5\xA2\x00\xF0\x9F\x91\x81\x00\xF0\x9F\x91\x85\x00\xF0\x9F\x91\x84\x00\xF0\x9F\x92\x8B\x00\xF0\x9F\x92\x98\x00"
"\xF0\x9F\x92\x93\x00\xF0\x9F\x92\x97\x00\xF0\x9F\x92\x99\x00\xF0\x9F\x92\x9B\x00\xF0\x9F\xA7\xA1\x00\xF0\x9F\x92\x9C\x00\xF0\x9F\x96\xA4\x00\xF0\x9F\x92\x9D\x00"
const int EmojiPerWidth = 8;
const int EmojiPerHeight = 4;
public string Name => "Text / Unicode Emojis";
public string Title => "raylib [text] example - unicode emojis";
public ConfigFlags ConfigFlags => ConfigFlags.Msaa4xHint | ConfigFlags.VSyncHint;
// Arrays that holds the random emojis
struct EmojiInfo
{
public int Index; // Index inside `emojiCodepoints`
public int Message; // Message index
public Color Color; // Emoji color
}
private EmojiInfo[] emoji;
private int hovered;
private int selected;
struct Message
{
public string Text;
public string Language;
public Message(string text, string language)
{
Text = text;
Language = language;
}
}
// String containing 180 emoji codepoints separated by a '\0' char
const string EmojiCodepoints =
"\xF0\x9F\x8C\x80\x00\xF0\x9F\x98\x80\x00\xF0\x9F\x98\x82\x00\xF0\x9F\xA4\xA3\x00\xF0\x9F\x98\x83\x00\xF0\x9F\x98\x86\x00\xF0\x9F\x98\x89\x00" +
"\xF0\x9F\x98\x8B\x00\xF0\x9F\x98\x8E\x00\xF0\x9F\x98\x8D\x00\xF0\x9F\x98\x98\x00\xF0\x9F\x98\x97\x00\xF0\x9F\x98\x99\x00\xF0\x9F\x98\x9A\x00\xF0\x9F\x99\x82\x00" +
"\xF0\x9F\xA4\x97\x00\xF0\x9F\xA4\xA9\x00\xF0\x9F\xA4\x94\x00\xF0\x9F\xA4\xA8\x00\xF0\x9F\x98\x90\x00\xF0\x9F\x98\x91\x00\xF0\x9F\x98\xB6\x00\xF0\x9F\x99\x84\x00" +
"\xF0\x9F\x98\x8F\x00\xF0\x9F\x98\xA3\x00\xF0\x9F\x98\xA5\x00\xF0\x9F\x98\xAE\x00\xF0\x9F\xA4\x90\x00\xF0\x9F\x98\xAF\x00\xF0\x9F\x98\xAA\x00\xF0\x9F\x98\xAB\x00" +
"\xF0\x9F\x98\xB4\x00\xF0\x9F\x98\x8C\x00\xF0\x9F\x98\x9B\x00\xF0\x9F\x98\x9D\x00\xF0\x9F\xA4\xA4\x00\xF0\x9F\x98\x92\x00\xF0\x9F\x98\x95\x00\xF0\x9F\x99\x83\x00" +
"\xF0\x9F\xA4\x91\x00\xF0\x9F\x98\xB2\x00\xF0\x9F\x99\x81\x00\xF0\x9F\x98\x96\x00\xF0\x9F\x98\x9E\x00\xF0\x9F\x98\x9F\x00\xF0\x9F\x98\xA4\x00\xF0\x9F\x98\xA2\x00" +
"\xF0\x9F\x98\xAD\x00\xF0\x9F\x98\xA6\x00\xF0\x9F\x98\xA9\x00\xF0\x9F\xA4\xAF\x00\xF0\x9F\x98\xAC\x00\xF0\x9F\x98\xB0\x00\xF0\x9F\x98\xB1\x00\xF0\x9F\x98\xB3\x00" +
"\xF0\x9F\xA4\xAA\x00\xF0\x9F\x98\xB5\x00\xF0\x9F\x98\xA1\x00\xF0\x9F\x98\xA0\x00\xF0\x9F\xA4\xAC\x00\xF0\x9F\x98\xB7\x00\xF0\x9F\xA4\x92\x00\xF0\x9F\xA4\x95\x00" +
"\xF0\x9F\xA4\xA2\x00\xF0\x9F\xA4\xAE\x00\xF0\x9F\xA4\xA7\x00\xF0\x9F\x98\x87\x00\xF0\x9F\xA4\xA0\x00\xF0\x9F\xA4\xAB\x00\xF0\x9F\xA4\xAD\x00\xF0\x9F\xA7\x90\x00" +
"\xF0\x9F\xA4\x93\x00\xF0\x9F\x98\x88\x00\xF0\x9F\x91\xBF\x00\xF0\x9F\x91\xB9\x00\xF0\x9F\x91\xBA\x00\xF0\x9F\x92\x80\x00\xF0\x9F\x91\xBB\x00\xF0\x9F\x91\xBD\x00" +
"\xF0\x9F\x91\xBE\x00\xF0\x9F\xA4\x96\x00\xF0\x9F\x92\xA9\x00\xF0\x9F\x98\xBA\x00\xF0\x9F\x98\xB8\x00\xF0\x9F\x98\xB9\x00\xF0\x9F\x98\xBB\x00\xF0\x9F\x98\xBD\x00" +
"\xF0\x9F\x99\x80\x00\xF0\x9F\x98\xBF\x00\xF0\x9F\x8C\xBE\x00\xF0\x9F\x8C\xBF\x00\xF0\x9F\x8D\x80\x00\xF0\x9F\x8D\x83\x00\xF0\x9F\x8D\x87\x00\xF0\x9F\x8D\x93\x00" +
"\xF0\x9F\xA5\x9D\x00\xF0\x9F\x8D\x85\x00\xF0\x9F\xA5\xA5\x00\xF0\x9F\xA5\x91\x00\xF0\x9F\x8D\x86\x00\xF0\x9F\xA5\x94\x00\xF0\x9F\xA5\x95\x00\xF0\x9F\x8C\xBD\x00" +
"\xF0\x9F\x8C\xB6\x00\xF0\x9F\xA5\x92\x00\xF0\x9F\xA5\xA6\x00\xF0\x9F\x8D\x84\x00\xF0\x9F\xA5\x9C\x00\xF0\x9F\x8C\xB0\x00\xF0\x9F\x8D\x9E\x00\xF0\x9F\xA5\x90\x00" +
"\xF0\x9F\xA5\x96\x00\xF0\x9F\xA5\xA8\x00\xF0\x9F\xA5\x9E\x00\xF0\x9F\xA7\x80\x00\xF0\x9F\x8D\x96\x00\xF0\x9F\x8D\x97\x00\xF0\x9F\xA5\xA9\x00\xF0\x9F\xA5\x93\x00" +
"\xF0\x9F\x8D\x94\x00\xF0\x9F\x8D\x9F\x00\xF0\x9F\x8D\x95\x00\xF0\x9F\x8C\xAD\x00\xF0\x9F\xA5\xAA\x00\xF0\x9F\x8C\xAE\x00\xF0\x9F\x8C\xAF\x00\xF0\x9F\xA5\x99\x00" +
"\xF0\x9F\xA5\x9A\x00\xF0\x9F\x8D\xB3\x00\xF0\x9F\xA5\x98\x00\xF0\x9F\x8D\xB2\x00\xF0\x9F\xA5\xA3\x00\xF0\x9F\xA5\x97\x00\xF0\x9F\x8D\xBF\x00\xF0\x9F\xA5\xAB\x00" +
"\xF0\x9F\x8D\xB1\x00\xF0\x9F\x8D\x98\x00\xF0\x9F\x8D\x9D\x00\xF0\x9F\x8D\xA0\x00\xF0\x9F\x8D\xA2\x00\xF0\x9F\x8D\xA5\x00\xF0\x9F\x8D\xA1\x00\xF0\x9F\xA5\x9F\x00" +
"\xF0\x9F\xA5\xA1\x00\xF0\x9F\x8D\xA6\x00\xF0\x9F\x8D\xAA\x00\xF0\x9F\x8E\x82\x00\xF0\x9F\x8D\xB0\x00\xF0\x9F\xA5\xA7\x00\xF0\x9F\x8D\xAB\x00\xF0\x9F\x8D\xAF\x00" +
"\xF0\x9F\x8D\xBC\x00\xF0\x9F\xA5\x9B\x00\xF0\x9F\x8D\xB5\x00\xF0\x9F\x8D\xB6\x00\xF0\x9F\x8D\xBE\x00\xF0\x9F\x8D\xB7\x00\xF0\x9F\x8D\xBB\x00\xF0\x9F\xA5\x82\x00" +
"\xF0\x9F\xA5\x83\x00\xF0\x9F\xA5\xA4\x00\xF0\x9F\xA5\xA2\x00\xF0\x9F\x91\x81\x00\xF0\x9F\x91\x85\x00\xF0\x9F\x91\x84\x00\xF0\x9F\x92\x8B\x00\xF0\x9F\x92\x98\x00" +
"\xF0\x9F\x92\x93\x00\xF0\x9F\x92\x97\x00\xF0\x9F\x92\x99\x00\xF0\x9F\x92\x9B\x00\xF0\x9F\xA7\xA1\x00\xF0\x9F\x92\x9C\x00\xF0\x9F\x96\xA4\x00\xF0\x9F\x92\x9D\x00" +
"\xF0\x9F\x92\x9F\x00\xF0\x9F\x92\x8C\x00\xF0\x9F\x92\xA4\x00\xF0\x9F\x92\xA2\x00\xF0\x9F\x92\xA3\x00";
struct {
string text;
string language;
}
const messages[] = { // Array containing all of the emojis messages
{"\x46\x61\x6C\x73\x63\x68\x65\x73\x20\xC3\x9C\x62\x65\x6E\x20\x76\x6F\x6E\x20\x58\x79\x6C\x6F\x70\x68\x6F\x6E\x6D\x75\x73\x69\x6B\x20\x71\x75\xC3\xA4\x6C"
"\x74\x20\x6A\x65\x64\x65\x6E\x20\x67\x72\xC3\xB6\xC3\x9F\x65\x72\x65\x6E\x20\x5A\x77\x65\x72\x67", "German"},
{"\x42\x65\x69\xC3\x9F\x20\x6E\x69\x63\x68\x74\x20\x69\x6E\x20\x64\x69\x65\x20\x48\x61\x6E\x64\x2C\x20\x64\x69\x65\x20\x64\x69\x63\x68\x20\x66\xC3\xBC\x74"
"\x74\x65\x72\x74\x2E", "German"},
{"\x41\x75\xC3\x9F\x65\x72\x6F\x72\x64\x65\x6E\x74\x6C\x69\x63\x68\x65\x20\xC3\x9C\x62\x65\x6C\x20\x65\x72\x66\x6F\x72\x64\x65\x72\x6E\x20\x61\x75\xC3\x9F"
"\x65\x72\x6F\x72\x64\x65\x6E\x74\x6C\x69\x63\x68\x65\x20\x4D\x69\x74\x74\x65\x6C\x2E", "German"},
{"\xD4\xBF\xD6\x80\xD5\xB6\xD5\xA1\xD5\xB4\x20\xD5\xA1\xD5\xBA\xD5\xA1\xD5\xAF\xD5\xAB\x20\xD5\xB8\xD6\x82\xD5\xBF\xD5\xA5\xD5\xAC\x20\xD6\x87\x20\xD5\xAB"
"\xD5\xB6\xD5\xAE\xD5\xAB\x20\xD5\xA1\xD5\xB6\xD5\xB0\xD5\xA1\xD5\xB6\xD5\xA3\xD5\xAB\xD5\xBD\xD5\xBF\x20\xD5\xB9\xD5\xA8\xD5\xB6\xD5\xA5\xD6\x80", "Armenian"},
{"\xD4\xB5\xD6\x80\xD5\xA2\x20\xD5\xB8\xD6\x80\x20\xD5\xAF\xD5\xA1\xD6\x81\xD5\xAB\xD5\xB6\xD5\xA8\x20\xD5\xA5\xD5\xAF\xD5\xA1\xD6\x82\x20\xD5\xA1\xD5\xB6\xD5"
"\xBF\xD5\xA1\xD5\xBC\x2C\x20\xD5\xAE\xD5\xA1\xD5\xBC\xD5\xA5\xD6\x80\xD5\xA8\x20\xD5\xA1\xD5\xBD\xD5\xA1\xD6\x81\xD5\xAB\xD5\xB6\x2E\x2E\x2E\x20\xC2\xAB\xD4\xBF"
"\xD5\xB8\xD5\xBF\xD5\xA8\x20\xD5\xB4\xD5\xA5\xD6\x80\xD5\xB8\xD5\xB6\xD6\x81\xD5\xAB\xD6\x81\x20\xD5\xA7\x3A\xC2\xBB", "Armenian"},
{"\xD4\xB3\xD5\xA1\xD5\xBC\xD5\xA8\xD5\x9D\x20\xD5\xA3\xD5\xA1\xD6\x80\xD5\xB6\xD5\xA1\xD5\xB6\x2C\x20\xD5\xB1\xD5\xAB\xD6\x82\xD5\xB6\xD5\xA8\xD5\x9D\x20\xD5"
"\xB1\xD5\xB4\xD5\xBC\xD5\xA1\xD5\xB6", "Armenian"},
{"\x4A\x65\xC5\xBC\x75\x20\x6B\x6C\xC4\x85\x74\x77\x2C\x20\x73\x70\xC5\x82\xC3\xB3\x64\xC5\xBA\x20\x46\x69\x6E\x6F\x6D\x20\x63\x7A\xC4\x99\xC5\x9B\xC4\x87"
"\x20\x67\x72\x79\x20\x68\x61\xC5\x84\x62\x21", "Polish"},
{"\x44\x6F\x62\x72\x79\x6D\x69\x20\x63\x68\xC4\x99\x63\x69\x61\x6D\x69\x20\x6A\x65\x73\x74\x20\x70\x69\x65\x6B\xC5\x82\x6F\x20\x77\x79\x62\x72\x75\x6B\x6F"
"\x77\x61\x6E\x65\x2E", "Polish"},
{"\xC3\x8E\xC8\x9B\x69\x20\x6D\x75\x6C\xC8\x9B\x75\x6D\x65\x73\x63\x20\x63\xC4\x83\x20\x61\x69\x20\x61\x6C\x65\x73\x20\x72\x61\x79\x6C\x69\x62\x2E\x0A\xC8\x98"
"\x69\x20\x73\x70\x65\x72\x20\x73\xC4\x83\x20\x61\x69\x20\x6F\x20\x7A\x69\x20\x62\x75\x6E\xC4\x83\x21", "Romanian"},
{"\xD0\xAD\xD1\x85\x2C\x20\xD1\x87\xD1\x83\xD0\xB6\xD0\xB0\xD0\xBA\x2C\x20\xD0\xBE\xD0\xB1\xD1\x89\xD0\xB8\xD0\xB9\x20\xD1\x81\xD1\x8A\xD1\x91\xD0\xBC\x20"
"\xD1\x86\xD0\xB5\xD0\xBD\x20\xD1\x88\xD0\xBB\xD1\x8F\xD0\xBF\x20\x28\xD1\x8E\xD1\x84\xD1\x82\xD1\x8C\x29\x20\xD0\xB2\xD0\xB4\xD1\x80\xD1\x8B\xD0\xB7\xD0\xB3\x21", "Russian"},
{"\xD0\xAF\x20\xD0\xBB\xD1\x8E\xD0\xB1\xD0\xBB\xD1\x8E\x20\x72\x61\x79\x6C\x69\x62\x21", "Russian"},
{"\xD0\x9C\xD0\xBE\xD0\xBB\xD1\x87\xD0\xB8\x2C\x20\xD1\x81\xD0\xBA\xD1\x80\xD1\x8B\xD0\xB2\xD0\xB0\xD0\xB9\xD1\x81\xD1\x8F\x20\xD0\xB8\x20\xD1\x82\xD0\xB0\xD0\xB8"
"\x0A\xD0\x98\x20\xD1\x87\xD1\x83\xD0\xB2\xD1\x81\xD1\x82\xD0\xB2\xD0\xB0\x20\xD0\xB8\x20\xD0\xBC\xD0\xB5\xD1\x87\xD1\x82\xD1\x8B\x20\xD1\x81\xD0\xB2\xD0\xBE\xD0\xB8\x20"
"\xE2\x80\x93\x0A\xD0\x9F\xD1\x83\xD1\x81\xD0\xBA\xD0\xB0\xD0\xB9\x20\xD0\xB2\x20\xD0\xB4\xD1\x83\xD1\x88\xD0\xB5\xD0\xB2\xD0\xBD\xD0\xBE\xD0\xB9\x20\xD0\xB3\xD0\xBB\xD1"
"\x83\xD0\xB1\xD0\xB8\xD0\xBD\xD0\xB5\x0A\xD0\x98\x20\xD0\xB2\xD1\x81\xD1\x85\xD0\xBE\xD0\xB4\xD1\x8F\xD1\x82\x20\xD0\xB8\x20\xD0\xB7\xD0\xB0\xD0\xB9\xD0\xB4\xD1\x83\xD1"
"\x82\x20\xD0\xBE\xD0\xBD\xD0\xB5\x0A\xD0\x9A\xD0\xB0\xD0\xBA\x20\xD0\xB7\xD0\xB2\xD0\xB5\xD0\xB7\xD0\xB4\xD1\x8B\x20\xD1\x8F\xD1\x81\xD0\xBD\xD1\x8B\xD0\xB5\x20\xD0\xB2"
"\x20\xD0\xBD\xD0\xBE\xD1\x87\xD0\xB8\x2D\x0A\xD0\x9B\xD1\x8E\xD0\xB1\xD1\x83\xD0\xB9\xD1\x81\xD1\x8F\x20\xD0\xB8\xD0\xBC\xD0\xB8\x20\xE2\x80\x93\x20\xD0\xB8\x20\xD0\xBC"
"\xD0\xBE\xD0\xBB\xD1\x87\xD0\xB8\x2E", "Russian"},
{"\x56\x6F\x69\x78\x20\x61\x6D\x62\x69\x67\x75\xC3\xAB\x20\x64\xE2\x80\x99\x75\x6E\x20\x63\xC5\x93\x75\x72\x20\x71\x75\x69\x20\x61\x75\x20\x7A\xC3\xA9\x70"
"\x68\x79\x72\x20\x70\x72\xC3\xA9\x66\xC3\xA8\x72\x65\x20\x6C\x65\x73\x20\x6A\x61\x74\x74\x65\x73\x20\x64\x65\x20\x6B\x69\x77\x69", "French"},
{"\x42\x65\x6E\x6A\x61\x6D\xC3\xAD\x6E\x20\x70\x69\x64\x69\xC3\xB3\x20\x75\x6E\x61\x20\x62\x65\x62\x69\x64\x61\x20\x64\x65\x20\x6B\x69\x77\x69\x20\x79\x20"
"\x66\x72\x65\x73\x61\x3B\x20\x4E\x6F\xC3\xA9\x2C\x20\x73\x69\x6E\x20\x76\x65\x72\x67\xC3\xBC\x65\x6E\x7A\x61\x2C\x20\x6C\x61\x20\x6D\xC3\xA1\x73\x20\x65\x78"
"\x71\x75\x69\x73\x69\x74\x61\x20\x63\x68\x61\x6D\x70\x61\xC3\xB1\x61\x20\x64\x65\x6C\x20\x6D\x65\x6E\xC3\xBA\x2E", "Spanish"},
{"\xCE\xA4\xCE\xB1\xCF\x87\xCE\xAF\xCF\x83\xCF\x84\xCE\xB7\x20\xCE\xB1\xCE\xBB\xCF\x8E\xCF\x80\xCE\xB7\xCE\xBE\x20\xCE\xB2\xCE\xB1\xCF\x86\xCE\xAE\xCF\x82\x20"
"\xCF\x88\xCE\xB7\xCE\xBC\xCE\xAD\xCE\xBD\xCE\xB7\x20\xCE\xB3\xCE\xB7\x2C\x20\xCE\xB4\xCF\x81\xCE\xB1\xCF\x83\xCE\xBA\xCE\xB5\xCE\xBB\xCE\xAF\xCE\xB6\xCE\xB5\xCE"
"\xB9\x20\xCF\x85\xCF\x80\xCE\xAD\xCF\x81\x20\xCE\xBD\xCF\x89\xCE\xB8\xCF\x81\xCE\xBF\xCF\x8D\x20\xCE\xBA\xCF\x85\xCE\xBD\xCF\x8C\xCF\x82", "Greek"},
{"\xCE\x97\x20\xCE\xBA\xCE\xB1\xCE\xBB\xCF\x8D\xCF\x84\xCE\xB5\xCF\x81\xCE\xB7\x20\xCE\xAC\xCE\xBC\xCF\x85\xCE\xBD\xCE\xB1\x20\xCE\xB5\xCE\xAF\xCE\xBD"
"\xCE\xB1\xCE\xB9\x20\xCE\xB7\x20\xCE\xB5\xCF\x80\xCE\xAF\xCE\xB8\xCE\xB5\xCF\x83\xCE\xB7\x2E", "Greek"},
{"\xCE\xA7\xCF\x81\xCF\x8C\xCE\xBD\xCE\xB9\xCE\xB1\x20\xCE\xBA\xCE\xB1\xCE\xB9\x20\xCE\xB6\xCE\xB1\xCE\xBC\xCE\xAC\xCE\xBD\xCE\xB9\xCE\xB1\x21", "Greek"},
{"\xCE\xA0\xCF\x8E\xCF\x82\x20\xCF\x84\xCE\xB1\x20\xCF\x80\xCE\xB1\xCF\x82\x20\xCF\x83\xCE\xAE\xCE\xBC\xCE\xB5\xCF\x81\xCE\xB1\x3B", "Greek"},
{"\xE6\x88\x91\xE8\x83\xBD\xE5\x90\x9E\xE4\xB8\x8B\xE7\x8E\xBB\xE7\x92\x83\xE8\x80\x8C\xE4\xB8\x8D\xE4\xBC\xA4\xE8\xBA\xAB\xE4\xBD\x93\xE3\x80\x82", "Chinese"},
{"\xE4\xBD\xA0\xE5\x90\x83\xE4\xBA\x86\xE5\x90\x97\xEF\xBC\x9F", "Chinese"},
{"\xE4\xB8\x8D\xE4\xBD\x9C\xE4\xB8\x8D\xE6\xAD\xBB\xE3\x80\x82", "Chinese"},
{"\xE6\x9C\x80\xE8\xBF\x91\xE5\xA5\xBD\xE5\x90\x97\xEF\xBC\x9F", "Chinese"},
{"\xE5\xA1\x9E\xE7\xBF\x81\xE5\xA4\xB1\xE9\xA9\xAC\xEF\xBC\x8C\xE7\x84\x89\xE7\x9F\xA5\xE9\x9D\x9E\xE7\xA6\x8F\xE3\x80\x82", "Chinese"},
{"\xE5\x8D\x83\xE5\x86\x9B\xE6\x98\x93\xE5\xBE\x97\x2C\x20\xE4\xB8\x80\xE5\xB0\x86\xE9\x9A\xBE\xE6\xB1\x82", "Chinese"},
{"\xE4\xB8\x87\xE4\xBA\x8B\xE5\xBC\x80\xE5\xA4\xB4\xE9\x9A\xBE\xE3\x80\x82", "Chinese"},
{"\xE9\xA3\x8E\xE6\x97\xA0\xE5\xB8\xB8\xE9\xA1\xBA\xEF\xBC\x8C\xE5\x85\xB5\xE6\x97\xA0\xE5\xB8\xB8\xE8\x83\x9C\xE3\x80\x82", "Chinese"},
{"\xE6\xB4\xBB\xE5\x88\xB0\xE8\x80\x81\xEF\xBC\x8C\xE5\xAD\xA6\xE5\x88\xB0\xE8\x80\x81\xE3\x80\x82", "Chinese"},
{"\xE4\xB8\x80\xE8\xA8\x80\xE6\x97\xA2\xE5\x87\xBA\xEF\xBC\x8C\xE9\xA9\xB7\xE9\xA9\xAC\xE9\x9A\xBE\xE8\xBF\xBD\xE3\x80\x82", "Chinese"},
{"\xE8\xB7\xAF\xE9\x81\xA5\xE7\x9F\xA5\xE9\xA9\xAC\xE5\x8A\x9B\xEF\xBC\x8C\xE6\x97\xA5\xE4\xB9\x85\xE8\xA7\x81\xE4\xBA\xBA\xE5\xBF\x83", "Chinese"},
{"\xE6\x9C\x89\xE7\x90\x86\xE8\xB5\xB0\xE9\x81\x8D\xE5\xA4\xA9\xE4\xB8\x8B\xEF\xBC\x8C\xE6\x97\xA0\xE7\x90\x86\xE5\xAF\xB8\xE6\xAD\xA5\xE9\x9A\xBE\xE8\xA1\x8C\xE3\x80\x82", "Chinese"},
{"\xE7\x8C\xBF\xE3\x82\x82\xE6\x9C\xA8\xE3\x81\x8B\xE3\x82\x89\xE8\x90\xBD\xE3\x81\xA1\xE3\x82\x8B", "Japanese"},
{"\xE4\xBA\x80\xE3\x81\xAE\xE7\x94\xB2\xE3\x82\x88\xE3\x82\x8A\xE5\xB9\xB4\xE3\x81\xAE\xE5\x8A\x9F", "Japanese"},
{"\xE3\x81\x86\xE3\x82\x89\xE3\x82\x84\xE3\x81\xBE\xE3\x81\x97\x20\x20\xE6\x80\x9D\xE3\x81\xB2\xE5\x88\x87\xE3\x82\x8B\xE6\x99\x82\x20\x20\xE7\x8C\xAB\xE3\x81\xAE\xE6\x81\x8B", "Japanese"},
{"\xE8\x99\x8E\xE7\xA9\xB4\xE3\x81\xAB\xE5\x85\xA5\xE3\x82\x89\xE3\x81\x9A\xE3\x82\x93\xE3\x81\xB0\xE8\x99\x8E\xE5\xAD\x90\xE3\x82\x92\xE5\xBE\x97\xE3\x81\x9A\xE3\x80\x82", "Japanese"},
{"\xE4\xBA\x8C\xE5\x85\x8E\xE3\x82\x92\xE8\xBF\xBD\xE3\x81\x86\xE8\x80\x85\xE3\x81\xAF\xE4\xB8\x80\xE5\x85\x8E\xE3\x82\x92\xE3\x82\x82\xE5\xBE\x97\xE3\x81\x9A\xE3\x80\x82", "Japanese"},
{"\xE9\xA6\xAC\xE9\xB9\xBF\xE3\x81\xAF\xE6\xAD\xBB\xE3\x81\xAA\xE3\x81\xAA\xE3\x81\x8D\xE3\x82\x83\xE6\xB2\xBB\xE3\x82\x89\xE3\x81\xAA\xE3\x81\x84\xE3\x80\x82", "Japanese"},
{"\xE6\x9E\xAF\xE9\x87\x8E\xE8\xB7\xAF\xE3\x81\xAB\xE3\x80\x80\xE5\xBD\xB1\xE3\x81\x8B\xE3\x81\x95\xE3\x81\xAA\xE3\x82\x8A\xE3\x81\xA6\xE3\x80\x80\xE3\x82\x8F\xE3\x81\x8B\xE3\x82\x8C\xE3\x81\x91\xE3\x82\x8A", "Japanese"},
{"\xE7\xB9\xB0\xE3\x82\x8A\xE8\xBF\x94\xE3\x81\x97\xE9\xBA\xA6\xE3\x81\xAE\xE7\x95\x9D\xE7\xB8\xAB\xE3\x81\xB5\xE8\x83\xA1\xE8\x9D\xB6\xE5\x93\x89", "Japanese"},
{"\xEC\x95\x84\xEB\x93\x9D\xED\x95\x9C\x20\xEB\xB0\x94\xEB\x8B\xA4\x20\xEC\x9C\x84\xEC\x97\x90\x20\xEA\xB0\x88\xEB\xA7\xA4\xEA\xB8\xB0\x20\xEB\x91\x90\xEC\x97\x87\x20"
"\xEB\x82\xA0\xEC\x95\x84\x20\xEB\x8F\x88\xEB\x8B\xA4\x2E\x0A\xEB\x84\x88\xED\x9B\x8C\xEB\x84\x88\xED\x9B\x8C\x20\xEC\x8B\x9C\xEB\xA5\xBC\x20\xEC\x93\xB4\xEB\x8B\xA4\x2E"
"\x20\xEB\xAA\xA8\xEB\xA5\xB4\xEB\x8A\x94\x20\xEB\x82\x98\xEB\x9D\xBC\x20\xEA\xB8\x80\xEC\x9E\x90\xEB\x8B\xA4\x2E\x0A\xEB\x84\x90\xEB\x94\xB0\xEB\x9E\x80\x20\xED\x95\x98"
"\xEB\x8A\x98\x20\xEB\xB3\xB5\xED\x8C\x90\xEC\x97\x90\x20\xEB\x82\x98\xEB\x8F\x84\x20\xEA\xB0\x99\xEC\x9D\xB4\x20\xEC\x8B\x9C\xEB\xA5\xBC\x20\xEC\x93\xB4\xEB\x8B\xA4\x2E", "Korean"},
{"\xEC\xA0\x9C\x20\xEB\x88\x88\xEC\x97\x90\x20\xEC\x95\x88\xEA\xB2\xBD\xEC\x9D\xB4\xEB\x8B\xA4", "Korean"},
{"\xEA\xBF\xA9\x20\xEB\xA8\xB9\xEA\xB3\xA0\x20\xEC\x95\x8C\x20\xEB\xA8\xB9\xEB\x8A\x94\xEB\x8B\xA4", "Korean"},
{"\xEB\xA1\x9C\xEB\xA7\x88\xEB\x8A\x94\x20\xED\x95\x98\xEB\xA3\xA8\xEC\x95\x84\xEC\xB9\xA8\xEC\x97\x90\x20\xEC\x9D\xB4\xEB\xA3\xA8\xEC\x96\xB4\xEC\xA7\x84\x20\xEA\xB2\x83\xEC\x9D\xB4"
"\x20\xEC\x95\x84\xEB\x8B\x88\xEB\x8B\xA4", "Korean"},
{"\xEA\xB3\xA0\xEC\x83\x9D\x20\xEB\x81\x9D\xEC\x97\x90\x20\xEB\x82\x99\xEC\x9D\xB4\x20\xEC\x98\xA8\xEB\x8B\xA4", "Korean"},
{"\xEA\xB0\x9C\xEC\xB2\x9C\xEC\x97\x90\xEC\x84\x9C\x20\xEC\x9A\xA9\x20\xEB\x82\x9C\xEB\x8B\xA4", "Korean"},
{"\xEC\x95\x88\xEB\x85\x95\xED\x95\x98\xEC\x84\xB8\xEC\x9A\x94\x3F", "Korean"},
{"\xEB\xA7\x8C\xEB\x82\x98\xEC\x84\x9C\x20\xEB\xB0\x98\xEA\xB0\x91\xEC\x8A\xB5\xEB\x8B\x88\xEB\x8B\xA4", "Korean"},
{"\xED\x95\x9C\xEA\xB5\xAD\xEB\xA7\x90\x20\xED\x95\x98\xEC\x8B\xA4\x20\xEC\xA4\x84\x20\xEC\x95\x84\xEC\x84\xB8\xEC\x9A\x94\x3F", "Korean"},
// Array containing all of the emojis messages
static Message[] messages = new Message[]
{
new Message("\x46\x61\x6C\x73\x63\x68\x65\x73\x20\xC3\x9C\x62\x65\x6E\x20\x76\x6F\x6E\x20\x58\x79\x6C\x6F\x70\x68\x6F\x6E\x6D\x75\x73\x69\x6B\x20\x71\x75\xC3\xA4\x6C\x74\x20\x6A\x65\x64\x65\x6E\x20\x67\x72\xC3\xB6\xC3\x9F\x65\x72\x65\x6E\x20\x5A\x77\x65\x72\x67", "German"),
new Message("\x42\x65\x69\xC3\x9F\x20\x6E\x69\x63\x68\x74\x20\x69\x6E\x20\x64\x69\x65\x20\x48\x61\x6E\x64\x2C\x20\x64\x69\x65\x20\x64\x69\x63\x68\x20\x66\xC3\xBC\x74\x74\x65\x72\x74\x2E", "German"),
new Message("\x41\x75\xC3\x9F\x65\x72\x6F\x72\x64\x65\x6E\x74\x6C\x69\x63\x68\x65\x20\xC3\x9C\x62\x65\x6C\x20\x65\x72\x66\x6F\x72\x64\x65\x72\x6E\x20\x61\x75\xC3\x9F\x65\x72\x6F\x72\x64\x65\x6E\x74\x6C\x69\x63\x68\x65\x20\x4D\x69\x74\x74\x65\x6C\x2E", "German"),
new Message("\xD4\xBF\xD6\x80\xD5\xB6\xD5\xA1\xD5\xB4\x20\xD5\xA1\xD5\xBA\xD5\xA1\xD5\xAF\xD5\xAB\x20\xD5\xB8\xD6\x82\xD5\xBF\xD5\xA5\xD5\xAC\x20\xD6\x87\x20\xD5\xAB\xD5\xB6\xD5\xAE\xD5\xAB\x20\xD5\xA1\xD5\xB6\xD5\xB0\xD5\xA1\xD5\xB6\xD5\xA3\xD5\xAB\xD5\xBD\xD5\xBF\x20\xD5\xB9\xD5\xA8\xD5\xB6\xD5\xA5\xD6\x80", "Armenian"),
new Message("\xD4\xB5\xD6\x80\xD5\xA2\x20\xD5\xB8\xD6\x80\x20\xD5\xAF\xD5\xA1\xD6\x81\xD5\xAB\xD5\xB6\xD5\xA8\x20\xD5\xA5\xD5\xAF\xD5\xA1\xD6\x82\x20\xD5\xA1\xD5\xB6\xD5\xBF\xD5\xA1\xD5\xBC\x2C\x20\xD5\xAE\xD5\xA1\xD5\xBC\xD5\xA5\xD6\x80\xD5\xA8\x20\xD5\xA1\xD5\xBD\xD5\xA1\xD6\x81\xD5\xAB\xD5\xB6\x2E\x2E\x2E\x20\xC2\xAB\xD4\xBF\xD5\xB8\xD5\xBF\xD5\xA8\x20\xD5\xB4\xD5\xA5\xD6\x80\xD5\xB8\xD5\xB6\xD6\x81\xD5\xAB\xD6\x81\x20\xD5\xA7\x3A\xC2\xBB", "Armenian"),
new Message("\xD4\xB3\xD5\xA1\xD5\xBC\xD5\xA8\xD5\x9D\x20\xD5\xA3\xD5\xA1\xD6\x80\xD5\xB6\xD5\xA1\xD5\xB6\x2C\x20\xD5\xB1\xD5\xAB\xD6\x82\xD5\xB6\xD5\xA8\xD5\x9D\x20\xD5\xB1\xD5\xB4\xD5\xBC\xD5\xA1\xD5\xB6", "Armenian"),
new Message("\x4A\x65\xC5\xBC\x75\x20\x6B\x6C\xC4\x85\x74\x77\x2C\x20\x73\x70\xC5\x82\xC3\xB3\x64\xC5\xBA\x20\x46\x69\x6E\x6F\x6D\x20\x63\x7A\xC4\x99\xC5\x9B\xC4\x87\x20\x67\x72\x79\x20\x68\x61\xC5\x84\x62\x21", "Polish"),
new Message("\x44\x6F\x62\x72\x79\x6D\x69\x20\x63\x68\xC4\x99\x63\x69\x61\x6D\x69\x20\x6A\x65\x73\x74\x20\x70\x69\x65\x6B\xC5\x82\x6F\x20\x77\x79\x62\x72\x75\x6B\x6F\x77\x61\x6E\x65\x2E", "Polish"),
new Message("\xC3\x8E\xC8\x9B\x69\x20\x6D\x75\x6C\xC8\x9B\x75\x6D\x65\x73\x63\x20\x63\xC4\x83\x20\x61\x69\x20\x61\x6C\x65\x73\x20\x72\x61\x79\x6C\x69\x62\x2E\x0A\xC8\x98\x69\x20\x73\x70\x65\x72\x20\x73\xC4\x83\x20\x61\x69\x20\x6F\x20\x7A\x69\x20\x62\x75\x6E\xC4\x83\x21", "Romanian"),
new Message("\xD0\xAD\xD1\x85\x2C\x20\xD1\x87\xD1\x83\xD0\xB6\xD0\xB0\xD0\xBA\x2C\x20\xD0\xBE\xD0\xB1\xD1\x89\xD0\xB8\xD0\xB9\x20\xD1\x81\xD1\x8A\xD1\x91\xD0\xBC\x20\xD1\x86\xD0\xB5\xD0\xBD\x20\xD1\x88\xD0\xBB\xD1\x8F\xD0\xBF\x20\x28\xD1\x8E\xD1\x84\xD1\x82\xD1\x8C\x29\x20\xD0\xB2\xD0\xB4\xD1\x80\xD1\x8B\xD0\xB7\xD0\xB3\x21", "Russian"),
new Message("\xD0\xAF\x20\xD0\xBB\xD1\x8E\xD0\xB1\xD0\xBB\xD1\x8E\x20\x72\x61\x79\x6C\x69\x62\x21", "Russian"),
new Message("\xD0\x9C\xD0\xBE\xD0\xBB\xD1\x87\xD0\xB8\x2C\x20\xD1\x81\xD0\xBA\xD1\x80\xD1\x8B\xD0\xB2\xD0\xB0\xD0\xB9\xD1\x81\xD1\x8F\x20\xD0\xB8\x20\xD1\x82\xD0\xB0\xD0\xB8\x0A\xD0\x98\x20\xD1\x87\xD1\x83\xD0\xB2\xD1\x81\xD1\x82\xD0\xB2\xD0\xB0\x20\xD0\xB8\x20\xD0\xBC\xD0\xB5\xD1\x87\xD1\x82\xD1\x8B\x20\xD1\x81\xD0\xB2\xD0\xBE\xD0\xB8\x20\xE2\x80\x93\x0A\xD0\x9F\xD1\x83\xD1\x81\xD0\xBA\xD0\xB0\xD0\xB9\x20\xD0\xB2\x20\xD0\xB4\xD1\x83\xD1\x88\xD0\xB5\xD0\xB2\xD0\xBD\xD0\xBE\xD0\xB9\x20\xD0\xB3\xD0\xBB\xD1\x83\xD0\xB1\xD0\xB8\xD0\xBD\xD0\xB5\x0A\xD0\x98\x20\xD0\xB2\xD1\x81\xD1\x85\xD0\xBE\xD0\xB4\xD1\x8F\xD1\x82\x20\xD0\xB8\x20\xD0\xB7\xD0\xB0\xD0\xB9\xD0\xB4\xD1\x83\xD1\x82\x20\xD0\xBE\xD0\xBD\xD0\xB5\x0A\xD0\x9A\xD0\xB0\xD0\xBA\x20\xD0\xB7\xD0\xB2\xD0\xB5\xD0\xB7\xD0\xB4\xD1\x8B\x20\xD1\x8F\xD1\x81\xD0\xBD\xD1\x8B\xD0\xB5\x20\xD0\xB2\x20\xD0\xBD\xD0\xBE\xD1\x87\xD0\xB8\x2D\x0A\xD0\x9B\xD1\x8E\xD0\xB1\xD1\x83\xD0\xB9\xD1\x81\xD1\x8F\x20\xD0\xB8\xD0\xBC\xD0\xB8\x20\xE2\x80\x93\x20\xD0\xB8\x20\xD0\xBC\xD0\xBE\xD0\xBB\xD1\x87\xD0\xB8\x2E", "Russian"),
new Message("\x56\x6F\x69\x78\x20\x61\x6D\x62\x69\x67\x75\xC3\xAB\x20\x64\xE2\x80\x99\x75\x6E\x20\x63\xC5\x93\x75\x72\x20\x71\x75\x69\x20\x61\x75\x20\x7A\xC3\xA9\x70\x68\x79\x72\x20\x70\x72\xC3\xA9\x66\xC3\xA8\x72\x65\x20\x6C\x65\x73\x20\x6A\x61\x74\x74\x65\x73\x20\x64\x65\x20\x6B\x69\x77\x69", "French"),
new Message("\x42\x65\x6E\x6A\x61\x6D\xC3\xAD\x6E\x20\x70\x69\x64\x69\xC3\xB3\x20\x75\x6E\x61\x20\x62\x65\x62\x69\x64\x61\x20\x64\x65\x20\x6B\x69\x77\x69\x20\x79\x20\x66\x72\x65\x73\x61\x3B\x20\x4E\x6F\xC3\xA9\x2C\x20\x73\x69\x6E\x20\x76\x65\x72\x67\xC3\xBC\x65\x6E\x7A\x61\x2C\x20\x6C\x61\x20\x6D\xC3\xA1\x73\x20\x65\x78\x71\x75\x69\x73\x69\x74\x61\x20\x63\x68\x61\x6D\x70\x61\xC3\xB1\x61\x20\x64\x65\x6C\x20\x6D\x65\x6E\xC3\xBA\x2E", "Spanish"),
new Message("\xCE\xA4\xCE\xB1\xCF\x87\xCE\xAF\xCF\x83\xCF\x84\xCE\xB7\x20\xCE\xB1\xCE\xBB\xCF\x8E\xCF\x80\xCE\xB7\xCE\xBE\x20\xCE\xB2\xCE\xB1\xCF\x86\xCE\xAE\xCF\x82\x20\xCF\x88\xCE\xB7\xCE\xBC\xCE\xAD\xCE\xBD\xCE\xB7\x20\xCE\xB3\xCE\xB7\x2C\x20\xCE\xB4\xCF\x81\xCE\xB1\xCF\x83\xCE\xBA\xCE\xB5\xCE\xBB\xCE\xAF\xCE\xB6\xCE\xB5\xCE\xB9\x20\xCF\x85\xCF\x80\xCE\xAD\xCF\x81\x20\xCE\xBD\xCF\x89\xCE\xB8\xCF\x81\xCE\xBF\xCF\x8D\x20\xCE\xBA\xCF\x85\xCE\xBD\xCF\x8C\xCF\x82", "Greek"),
new Message("\xCE\x97\x20\xCE\xBA\xCE\xB1\xCE\xBB\xCF\x8D\xCF\x84\xCE\xB5\xCF\x81\xCE\xB7\x20\xCE\xAC\xCE\xBC\xCF\x85\xCE\xBD\xCE\xB1\x20\xCE\xB5\xCE\xAF\xCE\xBD\xCE\xB1\xCE\xB9\x20\xCE\xB7\x20\xCE\xB5\xCF\x80\xCE\xAF\xCE\xB8\xCE\xB5\xCF\x83\xCE\xB7\x2E", "Greek"),
new Message("\xCE\xA7\xCF\x81\xCF\x8C\xCE\xBD\xCE\xB9\xCE\xB1\x20\xCE\xBA\xCE\xB1\xCE\xB9\x20\xCE\xB6\xCE\xB1\xCE\xBC\xCE\xAC\xCE\xBD\xCE\xB9\xCE\xB1\x21", "Greek"),
new Message("\xCE\xA0\xCF\x8E\xCF\x82\x20\xCF\x84\xCE\xB1\x20\xCF\x80\xCE\xB1\xCF\x82\x20\xCF\x83\xCE\xAE\xCE\xBC\xCE\xB5\xCF\x81\xCE\xB1\x3B", "Greek"),
new Message("\xE6\x88\x91\xE8\x83\xBD\xE5\x90\x9E\xE4\xB8\x8B\xE7\x8E\xBB\xE7\x92\x83\xE8\x80\x8C\xE4\xB8\x8D\xE4\xBC\xA4\xE8\xBA\xAB\xE4\xBD\x93\xE3\x80\x82", "Chinese"),
new Message("\xE4\xBD\xA0\xE5\x90\x83\xE4\xBA\x86\xE5\x90\x97\xEF\xBC\x9F", "Chinese"),
new Message("\xE4\xB8\x8D\xE4\xBD\x9C\xE4\xB8\x8D\xE6\xAD\xBB\xE3\x80\x82", "Chinese"),
new Message("\xE6\x9C\x80\xE8\xBF\x91\xE5\xA5\xBD\xE5\x90\x97\xEF\xBC\x9F", "Chinese"),
new Message("\xE5\xA1\x9E\xE7\xBF\x81\xE5\xA4\xB1\xE9\xA9\xAC\xEF\xBC\x8C\xE7\x84\x89\xE7\x9F\xA5\xE9\x9D\x9E\xE7\xA6\x8F\xE3\x80\x82", "Chinese"),
new Message("\xE5\x8D\x83\xE5\x86\x9B\xE6\x98\x93\xE5\xBE\x97\x2C\x20\xE4\xB8\x80\xE5\xB0\x86\xE9\x9A\xBE\xE6\xB1\x82", "Chinese"),
new Message("\xE4\xB8\x87\xE4\xBA\x8B\xE5\xBC\x80\xE5\xA4\xB4\xE9\x9A\xBE\xE3\x80\x82", "Chinese"),
new Message("\xE9\xA3\x8E\xE6\x97\xA0\xE5\xB8\xB8\xE9\xA1\xBA\xEF\xBC\x8C\xE5\x85\xB5\xE6\x97\xA0\xE5\xB8\xB8\xE8\x83\x9C\xE3\x80\x82", "Chinese"),
new Message("\xE6\xB4\xBB\xE5\x88\xB0\xE8\x80\x81\xEF\xBC\x8C\xE5\xAD\xA6\xE5\x88\xB0\xE8\x80\x81\xE3\x80\x82", "Chinese"),
new Message("\xE4\xB8\x80\xE8\xA8\x80\xE6\x97\xA2\xE5\x87\xBA\xEF\xBC\x8C\xE9\xA9\xB7\xE9\xA9\xAC\xE9\x9A\xBE\xE8\xBF\xBD\xE3\x80\x82", "Chinese"),
new Message("\xE8\xB7\xAF\xE9\x81\xA5\xE7\x9F\xA5\xE9\xA9\xAC\xE5\x8A\x9B\xEF\xBC\x8C\xE6\x97\xA5\xE4\xB9\x85\xE8\xA7\x81\xE4\xBA\xBA\xE5\xBF\x83", "Chinese"),
new Message("\xE6\x9C\x89\xE7\x90\x86\xE8\xB5\xB0\xE9\x81\x8D\xE5\xA4\xA9\xE4\xB8\x8B\xEF\xBC\x8C\xE6\x97\xA0\xE7\x90\x86\xE5\xAF\xB8\xE6\xAD\xA5\xE9\x9A\xBE\xE8\xA1\x8C\xE3\x80\x82", "Chinese"),
new Message("\xE7\x8C\xBF\xE3\x82\x82\xE6\x9C\xA8\xE3\x81\x8B\xE3\x82\x89\xE8\x90\xBD\xE3\x81\xA1\xE3\x82\x8B", "Japanese"),
new Message("\xE4\xBA\x80\xE3\x81\xAE\xE7\x94\xB2\xE3\x82\x88\xE3\x82\x8A\xE5\xB9\xB4\xE3\x81\xAE\xE5\x8A\x9F", "Japanese"),
new Message("\xE3\x81\x86\xE3\x82\x89\xE3\x82\x84\xE3\x81\xBE\xE3\x81\x97\x20\x20\xE6\x80\x9D\xE3\x81\xB2\xE5\x88\x87\xE3\x82\x8B\xE6\x99\x82\x20\x20\xE7\x8C\xAB\xE3\x81\xAE\xE6\x81\x8B", "Japanese"),
new Message("\xE8\x99\x8E\xE7\xA9\xB4\xE3\x81\xAB\xE5\x85\xA5\xE3\x82\x89\xE3\x81\x9A\xE3\x82\x93\xE3\x81\xB0\xE8\x99\x8E\xE5\xAD\x90\xE3\x82\x92\xE5\xBE\x97\xE3\x81\x9A\xE3\x80\x82", "Japanese"),
new Message("\xE4\xBA\x8C\xE5\x85\x8E\xE3\x82\x92\xE8\xBF\xBD\xE3\x81\x86\xE8\x80\x85\xE3\x81\xAF\xE4\xB8\x80\xE5\x85\x8E\xE3\x82\x92\xE3\x82\x82\xE5\xBE\x97\xE3\x81\x9A\xE3\x80\x82", "Japanese"),
new Message("\xE9\xA6\xAC\xE9\xB9\xBF\xE3\x81\xAF\xE6\xAD\xBB\xE3\x81\xAA\xE3\x81\xAA\xE3\x81\x8D\xE3\x82\x83\xE6\xB2\xBB\xE3\x82\x89\xE3\x81\xAA\xE3\x81\x84\xE3\x80\x82", "Japanese"),
new Message("\xE6\x9E\xAF\xE9\x87\x8E\xE8\xB7\xAF\xE3\x81\xAB\xE3\x80\x80\xE5\xBD\xB1\xE3\x81\x8B\xE3\x81\x95\xE3\x81\xAA\xE3\x82\x8A\xE3\x81\xA6\xE3\x80\x80\xE3\x82\x8F\xE3\x81\x8B\xE3\x82\x8C\xE3\x81\x91\xE3\x82\x8A", "Japanese"),
new Message("\xE7\xB9\xB0\xE3\x82\x8A\xE8\xBF\x94\xE3\x81\x97\xE9\xBA\xA6\xE3\x81\xAE\xE7\x95\x9D\xE7\xB8\xAB\xE3\x81\xB5\xE8\x83\xA1\xE8\x9D\xB6\xE5\x93\x89", "Japanese"),
new Message("\xEC\x95\x84\xEB\x93\x9D\xED\x95\x9C\x20\xEB\xB0\x94\xEB\x8B\xA4\x20\xEC\x9C\x84\xEC\x97\x90\x20\xEA\xB0\x88\xEB\xA7\xA4\xEA\xB8\xB0\x20\xEB\x91\x90\xEC\x97\x87\x20\xEB\x82\xA0\xEC\x95\x84\x20\xEB\x8F\x88\xEB\x8B\xA4\x2E\x0A\xEB\x84\x88\xED\x9B\x8C\xEB\x84\x88\xED\x9B\x8C\x20\xEC\x8B\x9C\xEB\xA5\xBC\x20\xEC\x93\xB4\xEB\x8B\xA4\x2E\x20\xEB\xAA\xA8\xEB\xA5\xB4\xEB\x8A\x94\x20\xEB\x82\x98\xEB\x9D\xBC\x20\xEA\xB8\x80\xEC\x9E\x90\xEB\x8B\xA4\x2E\x0A\xEB\x84\x90\xEB\x94\xB0\xEB\x9E\x80\x20\xED\x95\x98\xEB\x8A\x98\x20\xEB\xB3\xB5\xED\x8C\x90\xEC\x97\x90\x20\xEB\x82\x98\xEB\x8F\x84\x20\xEA\xB0\x99\xEC\x9D\xB4\x20\xEC\x8B\x9C\xEB\xA5\xBC\x20\xEC\x93\xB4\xEB\x8B\xA4\x2E", "Korean"),
new Message("\xEC\xA0\x9C\x20\xEB\x88\x88\xEC\x97\x90\x20\xEC\x95\x88\xEA\xB2\xBD\xEC\x9D\xB4\xEB\x8B\xA4", "Korean"),
new Message("\xEA\xBF\xA9\x20\xEB\xA8\xB9\xEA\xB3\xA0\x20\xEC\x95\x8C\x20\xEB\xA8\xB9\xEB\x8A\x94\xEB\x8B\xA4", "Korean"),
new Message("\xEB\xA1\x9C\xEB\xA7\x88\xEB\x8A\x94\x20\xED\x95\x98\xEB\xA3\xA8\xEC\x95\x84\xEC\xB9\xA8\xEC\x97\x90\x20\xEC\x9D\xB4\xEB\xA3\xA8\xEC\x96\xB4\xEC\xA7\x84\x20\xEA\xB2\x83\xEC\x9D\xB4\x20\xEC\x95\x84\xEB\x8B\x88\xEB\x8B\xA4", "Korean"),
new Message("\xEA\xB3\xA0\xEC\x83\x9D\x20\xEB\x81\x9D\xEC\x97\x90\x20\xEB\x82\x99\xEC\x9D\xB4\x20\xEC\x98\xA8\xEB\x8B\xA4", "Korean"),
new Message("\xEA\xB0\x9C\xEC\xB2\x9C\xEC\x97\x90\xEC\x84\x9C\x20\xEC\x9A\xA9\x20\xEB\x82\x9C\xEB\x8B\xA4", "Korean"),
new Message("\xEC\x95\x88\xEB\x85\x95\xED\x95\x98\xEC\x84\xB8\xEC\x9A\x94\x3F", "Korean"),
new Message("\xEB\xA7\x8C\xEB\x82\x98\xEC\x84\x9C\x20\xEB\xB0\x98\xEA\xB0\x91\xEC\x8A\xB5\xEB\x8B\x88\xEB\x8B\xA4", "Korean"),
new Message("\xED\x95\x9C\xEA\xB5\xAD\xEB\xA7\x90\x20\xED\x95\x98\xEC\x8B\xA4\x20\xEC\xA4\x84\x20\xEC\x95\x84\xEC\x84\xB8\xEC\x9A\x94\x3F", "Korean"),
};
//--------------------------------------------------------------------------------------
// Module functions declaration
//--------------------------------------------------------------------------------------
static void RandomizeEmoji(void); // Fills the emoji array with random emojis
private Font fontDefault;
private Font fontAsian;
private Font fontEmoji;
//--------------------------------------------------------------------------------------
// Global variables
//--------------------------------------------------------------------------------------
// Arrays that holds the random emojis
struct emoji
private Vector2 hoveredPos;
private Vector2 selectedPos;
public void Init()
{
int index; // Index inside `emojiCodepoints`
int message; // Message index
Color color; // Emoji color
emoji = new EmojiInfo[EmojiPerWidth * EmojiPerHeight];
hovered = -1;
selected = -1;
// Load the font resources
// NOTE: fontAsian is for asian languages,
// fontEmoji is the emojis and fontDefault is used for everything else
fontDefault = LoadFont("resources/fonts/dejavu.fnt"); // Requires "resources/fonts/dejavu.png"
fontAsian = LoadFont("resources/fonts/noto_cjk.fnt"); // Requires "resources/fonts/noto_cjk.png"
fontEmoji = LoadFont("resources/fonts/symbola.fnt"); // Requires "resources/fonts/symbola.png"
hoveredPos = new(0.0f, 0.0f);
selectedPos = new(0.0f, 0.0f);
// Set a random set of emojis when starting up
RandomizeEmoji();
}
emoji[EMOJI_PER_WIDTH * EMOJI_PER_HEIGHT] = { 0 };
static int hovered = -1, selected = -1;
int main(int argc, char** argv)
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
SetConfigFlags(FLAG_MSAA_4X_HINT | FLAG_VSYNC_HINT);
InitWindow(screenWidth, screenHeight, "raylib [text] example - unicode");
// Load the font resources
// NOTE: fontAsian is for asian languages,
// fontEmoji is the emojis and fontDefault is used for everything else
Font fontDefault = LoadFont("resources/fonts/dejavu.fnt");
Font fontAsian = LoadFont("resources/fonts/notoCJK.fnt");
Font fontEmoji = LoadFont("resources/fonts/emoji.fnt");
Vector2 hoveredPos = new(0.0f, 0.0f);
Vector2 selectedPos = new(0.0f, 0.0f);
// Set a random set of emojis when starting up
RandomizeEmoji();
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
// Main loop
while (!WindowShouldClose()) // Detect window close button or ESC key
public void Update()
{
// Update
//----------------------------------------------------------------------------------
// Add a new set of emojis when SPACE is pressed
if (IsKeyPressed(KEY_SPACE)) RandomizeEmoji();
if (IsKeyPressed(KeyboardKey.Space))
{
RandomizeEmoji();
}
// Set the selected emoji and copy its text to clipboard
if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON) && (hovered != -1) && (hovered != selected))
// Set the selected emoji
if (IsMouseButtonPressed(MouseButton.Left) && (hovered != -1) && (hovered != selected))
{
selected = hovered;
selectedPos = hoveredPos;
SetClipboardText(messages[emoji[selected].message].text);
}
Vector2 mouse = GetMousePosition();
Vector2 pos = new(28.8f, 10.0f);
Vector2 position = new(28.8f, 10.0f);
hovered = -1;
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(RAYWHITE);
ClearBackground(Color.RayWhite);
// Draw random emojis in the background
//------------------------------------------------------------------------------
for (int i = 0; i < SIZEOF(emoji); ++i)
for (int i = 0; i < emoji.Length; i++)
{
const char* txt = ref[emoji[i].index];
Rectangle emojiRect = new(pos.X, pos.Y, fontEmoji.BaseSize, fontEmoji.BaseSize);
string txt = GetEmojiAt(emoji[i].Index);
Rectangle emojiRect = new(position.X, position.Y, fontEmoji.BaseSize, fontEmoji.BaseSize);
if (!CheckCollisionPointRec(mouse, emojiRect))
{
DrawTextEx(fontEmoji, txt, pos, fontEmoji.BaseSize, 1.0, selected == i ? emoji[i].Color : ColorAlpha(LIGHTGRAY, 0.4f));
DrawTextEx(fontEmoji, txt, position, fontEmoji.BaseSize, 1.0f, selected == i ? emoji[i].Color : Fade(Color.LightGray, 0.4f));
}
else
{
DrawTextEx(fontEmoji, txt, pos, fontEmoji.BaseSize, 1.0, emoji[i].Color);
DrawTextEx(fontEmoji, txt, position, fontEmoji.BaseSize, 1.0f, emoji[i].Color);
hovered = i;
hoveredPos = pos;
hoveredPos = position;
}
if ((i != 0) && (i % EMOJI_PER_WIDTH == 0)) { pos.Y += fontEmoji.BaseSize + 24.25f; pos.X = 28.8f; }
else pos.X += fontEmoji.BaseSize + 28.8f;
if ((i != 0) && (i % EmojiPerWidth == 0))
{
position.Y += fontEmoji.BaseSize + 24.25f;
position.X = 28.8f;
}
else
{
position.X += fontEmoji.BaseSize + 28.8f;
}
}
//------------------------------------------------------------------------------
@ -230,19 +228,30 @@ int main(int argc, char** argv)
//------------------------------------------------------------------------------
if (selected != -1)
{
const int message = emoji[selected].message;
const int horizontalPadding = 20, verticalPadding = 30;
Font* font = ref;
int message = emoji[selected].Message;
const int horizontalPadding = 20;
const int verticalPadding = 30;
Font font = fontDefault;
// Set correct font for asian languages
if (TextIsEqual(messages[message].language, "Chinese") ||
TextIsEqual(messages[message].language, "Korean") ||
TextIsEqual(messages[message].language, "Japanese")) font = ref;
if ((messages[message].Language == "Chinese") ||
(messages[message].Language == "Korean") ||
(messages[message].Language == "Japanese"))
{
font = fontAsian;
}
// Calculate size for the message box (approximate the height and width)
Vector2 sz = MeasureTextEx(*font, messages[message].text, font->baseSize, 1.0f);
if (sz.X > 300) { sz.Y *= sz.X / 300; sz.X = 300; }
else if (sz.X < 160) sz.X = 160;
Vector2 sz = MeasureTextEx(font, messages[message].Text, font.BaseSize, 1.0f);
if (sz.X > 300)
{
sz.Y *= sz.X / 300;
sz.X = 300;
}
else if (sz.X < 160)
{
sz.X = 160;
}
Rectangle msgRect = new(selectedPos.X - 38.8f, selectedPos.Y, 2 * horizontalPadding + sz.X, 2 * verticalPadding + sz.Y);
msgRect.Y -= msgRect.Height;
@ -253,7 +262,11 @@ int main(int argc, char** argv)
Vector2 c = new(a.X + 10, a.Y);
// Don't go outside the screen
if (msgRect.X < 10) msgRect.X += 28;
if (msgRect.X < 10)
{
msgRect.X += 28;
}
if (msgRect.Y < 10)
{
msgRect.Y = selectedPos.Y + 84;
@ -266,65 +279,269 @@ int main(int argc, char** argv)
a = b;
b = tmp;
}
if (msgRect.X + msgRect.Width > screenWidth) msgRect.X -= (msgRect.X + msgRect.Width) - screenWidth + 10;
if (msgRect.X + msgRect.Width > screenWidth)
{
msgRect.X -= (msgRect.X + msgRect.Width) - screenWidth + 10;
}
// Draw chat bubble
DrawRectangleRec(msgRect, emoji[selected].Color);
DrawTriangle(a, b, c, emoji[selected].Color);
// Draw the main text message
Rectangle textRect = new(msgRect.X + horizontalPadding / 2, msgRect.Y + verticalPadding / 2, msgRect.Width - horizontalPadding, msgRect.Height);
DrawTextRec(*font, messages[message].text, textRect, font->baseSize, 1.0f, true, WHITE);
Rectangle textRect = new(msgRect.X + (float)horizontalPadding / 2, msgRect.Y + (float)verticalPadding / 2, msgRect.Width - horizontalPadding, msgRect.Height);
DrawTextBoxed(font, messages[message].Text, textRect, font.BaseSize, 1.0f, true, Color.White);
// Draw the info text below the main message
int size = strlen(messages[message].text);
int len = GetCodepointsCount(messages[message].text);
const char* info = TextFormat("%s %u characters %i bytes", messages[message].language, len, size);
int size = Encoding.UTF8.GetByteCount(messages[message].Text);
int length = GetCodepointCount(messages[message].Text);
string info = $"{messages[message].Language} {length} characters {size} bytes";
sz = MeasureTextEx(GetFontDefault(), info, 10, 1.0f);
Vector2 pos = new(textRect.X + textRect.Width - sz.X, msgRect.Y + msgRect.Height - sz.Y - 2);
DrawText(info, pos.X, pos.Y, 10, RAYWHITE);
DrawText(info, (int)(textRect.X + textRect.Width - sz.X), (int)(msgRect.Y + msgRect.Height - sz.Y - 2), 10, Color.RayWhite);
}
//------------------------------------------------------------------------------
// Draw the info text
DrawText("These emojis have something to tell you, click each to find out!", (screenWidth - 650) / 2, screenHeight - 40, 20, GRAY);
DrawText("Each emoji is a unicode character from a font, not a texture... Press [SPACEBAR] to refresh", (screenWidth - 484) / 2, screenHeight - 16, 10, GRAY);
DrawText("These emojis have something to tell you, click each to find out!", (screenWidth - 650) / 2, screenHeight - 40, 20, Color.Gray);
DrawText("Each emoji is a unicode character from a font, not a texture... Press [SPACEBAR] to refresh", (screenWidth - 484) / 2, screenHeight - 16, 10, Color.Gray);
EndDrawing();
//----------------------------------------------------------------------------------
}
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadFont(fontDefault); // Unload font resource
UnloadFont(fontAsian); // Unload font resource
UnloadFont(fontEmoji); // Unload font resource
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}
// Fills the emoji array with random emoji (only those emojis present in fontEmoji)
static void RandomizeEmoji(void)
{
hovered = selected = -1;
int start = GetRandomValue(45, 360);
for (int i = 0; i < SIZEOF(emoji); ++i)
public void Unload()
{
// 0-179 emoji codepoints (from emoji char array) each 4bytes + null char
emoji[i].index = GetRandomValue(0, 179) * 5;
UnloadFont(fontDefault); // Unload font resource
UnloadFont(fontAsian); // Unload font resource
UnloadFont(fontEmoji); // Unload font resource
}
// Generate a random color for this emoji
Vector3 hsv = new((start * (i + 1)) % 360, 0.6f, 0.85f);
emoji[i].Color = ColorAlpha(ColorFromHSV(hsv), 0.8f);
// Fills the emoji array with random emoji (only those emojis present in fontEmoji)
void RandomizeEmoji()
{
hovered = selected = -1;
int start = GetRandomValue(45, 360);
// Set a random message for this emoji
emoji[i].message = GetRandomValue(0, SIZEOF(messages) - 1);
for (int i = 0; i < emoji.Length; i++)
{
// 0-179 emoji codepoints (from emoji char array) each 4bytes + null char
emoji[i].Index = GetRandomValue(0, 179) * 5;
// Generate a random color for this emoji
emoji[i].Color = Fade(ColorFromHSV((start * (i + 1)) % 360, 0.6f, 0.85f), 0.8f);
// Set a random message for this emoji
emoji[i].Message = GetRandomValue(0, messages.Length - 1);
}
}
// Get the emoji codepoint (4 bytes + null) located at the given byte index inside EmojiCodepoints
static string GetEmojiAt(int index)
{
// Each emoji takes 4 bytes followed by a null separator, codepoints are stored as raw bytes
var sb = new StringBuilder();
for (int i = index; (i < EmojiCodepoints.Length) && (EmojiCodepoints[i] != '\0'); i++)
{
sb.Append(EmojiCodepoints[i]);
}
return sb.ToString();
}
//--------------------------------------------------------------------------------------
// Module Functions Definition
//--------------------------------------------------------------------------------------
// Draw text using font inside rectangle limits
static void DrawTextBoxed(Font font, string text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint)
{
DrawTextBoxedSelectable(font, text, rec, fontSize, spacing, wordWrap, tint, 0, 0, Color.White, Color.White);
}
// Draw text using font inside rectangle limits with support for text selection
static unsafe void DrawTextBoxedSelectable(Font font, string text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint, int selectStart, int selectLength, Color selectTint, Color selectBackTint)
{
int length = text.Length; // Total length in bytes of the text, scanned by codepoints in loop
float textOffsetY = 0.0f; // Offset between lines (on line break '\n')
float textOffsetX = 0.0f; // Offset X to next character to draw
float scaleFactor = fontSize / (float)font.BaseSize; // Character rectangle scaling factor
// Word/character wrapping mechanism variables
const int MeasureState = 0;
const int DrawState = 1;
int state = wordWrap ? MeasureState : DrawState;
int startLine = -1; // Index where to begin drawing (where a line begins)
int endLine = -1; // Index where to stop drawing (where a line ends)
int lastk = -1; // Holds last value of the character position
using var textNative = new Utf8Buffer(text);
for (int i = 0, k = 0; i < length; i++, k++)
{
// Get next codepoint from byte string and glyph index in font
int codepointByteCount = 0;
int codepoint = GetCodepoint(&textNative.AsPointer()[i], &codepointByteCount);
int index = GetGlyphIndex(font, codepoint);
// NOTE: Normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f)
// but we need to draw all of the bad bytes using the '?' symbol moving one byte
if (codepoint == 0x3f)
{
codepointByteCount = 1;
}
i += (codepointByteCount - 1);
float glyphWidth = 0;
if (codepoint != '\n')
{
glyphWidth = (font.Glyphs[index].AdvanceX == 0) ?
font.Recs[index].Width * scaleFactor :
font.Glyphs[index].AdvanceX * scaleFactor;
if (i + 1 < length)
{
glyphWidth = glyphWidth + spacing;
}
}
// NOTE: When wordWrap is ON we first measure how much of the text we can draw before going outside of the rec container
// We store this info in startLine and endLine, then we change states, draw the text between those two variables
// and change states again and again recursively until the end of the text (or until we get outside of the container)
// When wordWrap is OFF we don't need the measure state so we go to the drawing state immediately
// and begin drawing on the next line before we can get outside the container
if (state == MeasureState)
{
// TODO: There are multiple types of spaces in UNICODE, maybe it's a good idea to add support for more
// Ref: http://jkorpela.fi/chars/spaces.html
if ((codepoint == ' ') || (codepoint == '\t') || (codepoint == '\n'))
{
endLine = i;
}
if ((textOffsetX + glyphWidth) > rec.Width)
{
endLine = (endLine < 1) ? i : endLine;
if (i == endLine)
{
endLine -= codepointByteCount;
}
if ((startLine + codepointByteCount) == endLine)
{
endLine = (i - codepointByteCount);
}
state = 1 - state;
}
else if ((i + 1) == length)
{
endLine = i;
state = 1 - state;
}
else if (codepoint == '\n')
{
state = 1 - state;
}
if (state == DrawState)
{
textOffsetX = 0;
i = startLine;
glyphWidth = 0;
// Save character position when we switch states
int tmp = lastk;
lastk = k - 1;
k = tmp;
}
}
else
{
if (codepoint == '\n')
{
if (!wordWrap)
{
textOffsetY += (font.BaseSize + font.BaseSize / 2) * scaleFactor;
textOffsetX = 0;
}
}
else
{
if (!wordWrap && ((textOffsetX + glyphWidth) > rec.Width))
{
textOffsetY += (font.BaseSize + font.BaseSize / 2) * scaleFactor;
textOffsetX = 0;
}
// When text overflows rectangle height limit, just stop drawing
if ((textOffsetY + font.BaseSize * scaleFactor) > rec.Height)
{
break;
}
// Draw selection background
bool isGlyphSelected = false;
if ((selectStart >= 0) && (k >= selectStart) && (k < (selectStart + selectLength)))
{
DrawRectangleRec(new Rectangle(rec.X + textOffsetX - 1, rec.Y + textOffsetY, glyphWidth, (float)font.BaseSize * scaleFactor), selectBackTint);
isGlyphSelected = true;
}
// Draw current character glyph
if ((codepoint != ' ') && (codepoint != '\t'))
{
DrawTextCodepoint(font, codepoint, new Vector2(rec.X + textOffsetX, rec.Y + textOffsetY), fontSize, isGlyphSelected ? selectTint : tint);
}
}
if (wordWrap && (i == endLine))
{
textOffsetY += (font.BaseSize + font.BaseSize / 2) * scaleFactor;
textOffsetX = 0;
startLine = endLine;
endLine = -1;
glyphWidth = 0;
selectStart += lastk - k;
k = lastk;
state = 1 - state;
}
}
textOffsetX += glyphWidth;
}
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
SetConfigFlags(ConfigFlags.Msaa4xHint | ConfigFlags.VSyncHint);
InitWindow(screenWidth, screenHeight, "raylib [text] example - unicode emojis");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new Unicode();
game.Init();
// Main loop
while (!WindowShouldClose()) // Detect window close button or ESC key
{
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}
}
}
}

View file

@ -0,0 +1,225 @@
/*******************************************************************************************
*
* raylib [text] example - unicode ranges
*
* Example complexity rating: [] 4/4
*
* Example originally created with raylib 5.5, last time updated with raylib 5.6
*
* Example contributed by Vadim Gunko (@GuvaCode) 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 Vadim Gunko (@GuvaCode) and Ramon Santamaria (@raysan5)
*
********************************************************************************************/
using System.Numerics;
using Raylib_cs;
using static Raylib_cs.Raylib;
namespace Examples.Text;
public partial class UnicodeRanges : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
// path differs from upstream: font lives under resources/fonts/
private const string FontPath = "resources/fonts/NotoSansTC-Regular.ttf";
public string Name => "Text / Unicode Ranges";
public string Title => "raylib [text] example - unicode ranges";
private Font font;
private int unicodeRange; // Track the ranges of codepoints added to font
private int prevUnicodeRange; // Previous Unicode range to avoid reloading every frame
public void Init()
{
// Load font with default Unicode range: Basic ASCII [32-127]
font = LoadFont(FontPath);
SetTextureFilter(font.Texture, TextureFilter.Bilinear);
unicodeRange = 0;
prevUnicodeRange = 0;
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
if (unicodeRange != prevUnicodeRange)
{
UnloadFont(font);
// Load font with default Unicode range: Basic ASCII [32-127]
font = LoadFont(FontPath);
// Add required ranges to loaded font
// NOTE: The upstream switch uses fall-through so range N also loads all lower
// ranges, in the same order (4 -> 3 -> 2 -> 1); the descending if-chain preserves that.
/*
if (unicodeRange >= 5)
{
// Unicode range: Devanari, Arabic, Hebrew
// WARNING: Glyphs not available on provided font!
AddCodepointRange(ref font, FontPath, 0x900, 0x97f); // Devanagari
AddCodepointRange(ref font, FontPath, 0x600, 0x6ff); // Arabic
AddCodepointRange(ref font, FontPath, 0x5d0, 0x5ea); // Hebrew
}
*/
if (unicodeRange >= 4)
{
// Unicode range: CJK (Japanese and Chinese)
// WARNING: Loading thousands of codepoints requires lot of time!
// A better strategy is prefilter the required codepoints for the text
// in the game and just load the required ones
AddCodepointRange(ref font, FontPath, 0x4e00, 0x9fff);
AddCodepointRange(ref font, FontPath, 0x3400, 0x4dbf);
AddCodepointRange(ref font, FontPath, 0x3000, 0x303f);
AddCodepointRange(ref font, FontPath, 0x3040, 0x309f);
AddCodepointRange(ref font, FontPath, 0x30A0, 0x30ff);
AddCodepointRange(ref font, FontPath, 0x31f0, 0x31ff);
AddCodepointRange(ref font, FontPath, 0xff00, 0xffef);
AddCodepointRange(ref font, FontPath, 0xac00, 0xd7af);
AddCodepointRange(ref font, FontPath, 0x1100, 0x11ff);
}
if (unicodeRange >= 3)
{
// Unicode range: Cyrillic
AddCodepointRange(ref font, FontPath, 0x400, 0x4ff);
AddCodepointRange(ref font, FontPath, 0x500, 0x52f);
AddCodepointRange(ref font, FontPath, 0x2de0, 0x2Dff);
AddCodepointRange(ref font, FontPath, 0xa640, 0xA69f);
}
if (unicodeRange >= 2)
{
// Unicode range: Greek
AddCodepointRange(ref font, FontPath, 0x370, 0x3ff);
AddCodepointRange(ref font, FontPath, 0x1f00, 0x1fff);
}
if (unicodeRange >= 1)
{
// Unicode range: European Languages
AddCodepointRange(ref font, FontPath, 0xc0, 0x17f);
AddCodepointRange(ref font, FontPath, 0x180, 0x24f);
//AddCodepointRange(ref font, FontPath, 0x1e00, 0x1eff);
//AddCodepointRange(ref font, FontPath, 0x2c60, 0x2c7f);
}
prevUnicodeRange = unicodeRange;
SetTextureFilter(font.Texture, TextureFilter.Bilinear); // Set font atlas scale filter
}
if (IsKeyPressed(KeyboardKey.Zero)) unicodeRange = 0;
else if (IsKeyPressed(KeyboardKey.One)) unicodeRange = 1;
else if (IsKeyPressed(KeyboardKey.Two)) unicodeRange = 2;
else if (IsKeyPressed(KeyboardKey.Three)) unicodeRange = 3;
else if (IsKeyPressed(KeyboardKey.Four)) unicodeRange = 4;
//else if (IsKeyPressed(KeyboardKey.Five)) unicodeRange = 5;
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
DrawText("ADD CODEPOINTS: [1][2][3][4]", 20, 20, 20, Color.Maroon);
// Render test strings in different languages
DrawTextEx(font, "> English: Hello World!", new Vector2(50, 70), 32, 1, Color.DarkGray); // English
DrawTextEx(font, "> Español: Hola mundo!", new Vector2(50, 120), 32, 1, Color.DarkGray); // Spanish
DrawTextEx(font, "> Ελληνικά: Γειά σου κόσμε!", new Vector2(50, 170), 32, 1, Color.DarkGray); // Greek
DrawTextEx(font, "> Русский: Привет мир!", new Vector2(50, 220), 32, 0, Color.DarkGray); // Russian
DrawTextEx(font, "> 中文: 你好世界!", new Vector2(50, 270), 32, 1, Color.DarkGray); // Chinese
DrawTextEx(font, "> 日本語: こんにちは世界!", new Vector2(50, 320), 32, 1, Color.DarkGray); // Japanese
//DrawTextEx(font, "देवनागरी: होला मुंडो!", new Vector2(50, 350), 32, 1, Color.DarkGray); // Devanagari (glyphs not available in font)
// Draw font texture scaled to screen
float atlasScale = 380.0f / font.Texture.Width;
DrawRectangleRec(new Rectangle(400.0f, 16.0f, font.Texture.Width * atlasScale, font.Texture.Height * atlasScale), Color.Black);
DrawTexturePro(font.Texture, new Rectangle(0, 0, font.Texture.Width, font.Texture.Height),
new Rectangle(400.0f, 16.0f, font.Texture.Width * atlasScale, font.Texture.Height * atlasScale), new Vector2(0, 0), 0.0f, Color.White);
DrawRectangleLines(400, 16, 380, 380, Color.Red);
DrawText($"ATLAS SIZE: {font.Texture.Width}x{font.Texture.Height} px (x{atlasScale:00.00})", 20, 380, 20, Color.Blue);
DrawText($"CODEPOINTS GLYPHS LOADED: {font.GlyphCount}", 20, 410, 20, Color.Lime);
// Display font attribution
DrawText("Font: Noto Sans TC. License: SIL Open Font License 1.1", screenWidth - 300, screenHeight - 20, 10, Color.Gray);
if (prevUnicodeRange != unicodeRange)
{
DrawRectangle(0, 0, screenWidth, screenHeight, Fade(Color.White, 0.8f));
DrawRectangle(0, 125, screenWidth, 200, Color.Gray);
DrawText("GENERATING FONT ATLAS...", 120, 210, 40, Color.Black);
}
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadFont(font); // Unload font resource
}
//--------------------------------------------------------------------------------------
// Module Functions Definition
//--------------------------------------------------------------------------------------
// Add codepoint range to existing font
private static unsafe void AddCodepointRange(ref Font font, string fontPath, int start, int stop)
{
int rangeSize = stop - start + 1;
int currentRangeSize = font.GlyphCount;
// TODO: Load glyphs from provided vector font (if available),
// add them to existing font, regenerating font image and texture
int updatedCodepointCount = currentRangeSize + rangeSize;
int[] updatedCodepoints = new int[updatedCodepointCount];
// Get current codepoint list
for (int i = 0; i < currentRangeSize; i++) updatedCodepoints[i] = font.Glyphs[i].Value;
// Add new codepoints to list (provided range)
for (int i = currentRangeSize; i < updatedCodepointCount; i++)
updatedCodepoints[i] = start + (i - currentRangeSize);
UnloadFont(font);
font = LoadFontEx(fontPath, 32, updatedCodepoints, updatedCodepointCount);
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [text] example - unicode ranges");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new UnicodeRanges();
game.Init();
// Main loop
while (!WindowShouldClose()) // Detect window close button or ESC key
{
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}
}

View file

@ -0,0 +1,167 @@
/*******************************************************************************************
*
* raylib [text] example - words alignment
*
* Example complexity rating: [] 1/4
*
* Example originally created with raylib 6.0, last time updated with raylib 6.0
*
* 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)
*
********************************************************************************************/
using System.Numerics;
using static Raylib_cs.Raylib;
using static Raylib_cs.Raymath; // Required for: Lerp()
namespace Examples.Text;
public partial class WordsAlignment : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
// TextAlignment values: Left/Top = 0, Centre/Middle = 1, Right/Bottom = 2
public string Name => "Text / Words Alignment";
public string Title => "raylib [text] example - words alignment";
// Define the rectangle we will draw the text in
private Rectangle textContainerRect;
// Some text to display the current alignment
private static readonly string[] textAlignNameH = { "Left", "Centre", "Right" };
private static readonly string[] textAlignNameV = { "Top", "Middle", "Bottom" };
// Define the text we're going to draw in the rectangle
private int wordIndex;
private int wordCount;
private string[] words;
// Initialize the font size we're going to use
private int fontSize;
// And of course the font...
private Font font;
// Initialize the alignment variables
private int hAlign;
private int vAlign;
public void Init()
{
// Define the rectangle we will draw the text in
textContainerRect = new Rectangle((float)screenWidth / 2 - (float)screenWidth / 4, (float)screenHeight / 2 - (float)screenHeight / 3, (float)screenWidth / 2, (float)screenHeight * 2 / 3);
// Define the text we're going to draw in the rectangle
wordIndex = 0;
words = "raylib is a simple and easy-to-use library to enjoy videogames programming".Split(' ');
wordCount = words.Length;
// Initialize the font size we're going to use
fontSize = 40;
// And of course the font...
font = GetFontDefault();
// Initialize the alignment variables
hAlign = 1; // TEXT_ALIGN_CENTRE
vAlign = 1; // TEXT_ALIGN_MIDDLE
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
if (IsKeyPressed(KeyboardKey.Left))
{
if (hAlign > 0) hAlign = hAlign - 1;
}
if (IsKeyPressed(KeyboardKey.Right))
{
hAlign = hAlign + 1;
if (hAlign > 2) hAlign = 2;
}
if (IsKeyPressed(KeyboardKey.Up))
{
if (vAlign > 0) vAlign = vAlign - 1;
}
if (IsKeyPressed(KeyboardKey.Down))
{
vAlign = vAlign + 1;
if (vAlign > 2) vAlign = 2;
}
// One word per second
if (wordCount > 0) wordIndex = (int)GetTime() % wordCount;
else wordIndex = 0;
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.DarkBlue);
DrawText("Use Arrow Keys to change the text alignment", 20, 20, 20, Color.LightGray);
DrawText($"Alignment: Horizontal = {textAlignNameH[hAlign]}, Vertical = {textAlignNameV[vAlign]}", 20, 40, 20, Color.LightGray);
DrawRectangleRec(textContainerRect, Color.Blue);
// Get the size of the text to draw
Vector2 textSize = MeasureTextEx(font, words[wordIndex], fontSize, fontSize * .1f);
// Calculate the top-left text position based on the rectangle and alignment
Vector2 textPos = new Vector2(
textContainerRect.X + Lerp(0.0f, textContainerRect.Width - textSize.X, hAlign * 0.5f),
textContainerRect.Y + Lerp(0.0f, textContainerRect.Height - textSize.Y, vAlign * 0.5f)
);
// Draw the text
DrawTextEx(font, words[wordIndex], textPos, fontSize, fontSize * .1f, Color.RayWhite);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [text] example - words alignment");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new WordsAlignment();
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;
}
}

View file

@ -1,11 +1,15 @@
/*******************************************************************************************
*
* raylib [text] example - Text Writing Animation
* raylib [text] example - writing anim
*
* This example has been created using raylib 1.4 (www.raylib.com)
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
* Example complexity rating: [] 2/4
*
* Copyright (c) 2016 Ramon Santamaria (@raysan5)
* Example originally created with raylib 1.4, last time updated with raylib 1.4
*
* 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) 2016-2025 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
@ -13,60 +17,85 @@ using static Raylib_cs.Raylib;
namespace Examples.Text;
public class WritingAnim
public partial class WritingAnim : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Text / Writing Animation";
public string Title => "raylib [text] example - writing anim";
private string message;
private int framesCounter;
public void Init()
{
message = "This sample illustrates a text writing\nanimation effect! Check it out! ;)";
framesCounter = 0;
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
if (IsKeyDown(KeyboardKey.Space))
{
framesCounter += 8;
}
else
{
framesCounter += 1;
}
if (IsKeyPressed(KeyboardKey.Enter))
{
framesCounter = 0;
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
DrawText(message.SubText(0, framesCounter / 10), 210, 160, 20, Color.Maroon);
DrawText("PRESS [ENTER] to RESTART!", 240, 260, 20, Color.LightGray);
DrawText("HOLD [SPACE] to SPEED UP!", 239, 300, 20, Color.LightGray);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [text] example - writing anim");
InitWindow(screenWidth, screenHeight, "raylib [text] example - text writing anim");
string message = "This sample illustrates a text writing\nanimation effect! Check it out! ;)";
int framesCounter = 0;
SetTargetFPS(60);
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new WritingAnim();
game.Init();
// Main game loop
while (!WindowShouldClose())
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
if (IsKeyDown(KeyboardKey.Space))
{
framesCounter += 8;
}
else
{
framesCounter += 1;
}
if (IsKeyPressed(KeyboardKey.Enter))
{
framesCounter = 0;
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
DrawText(message.SubText(0, framesCounter / 10), 210, 160, 20, Color.Maroon);
DrawText("PRESS [ENTER] to RESTART!", 240, 260, 20, Color.LightGray);
DrawText("PRESS [SPACE] to SPEED UP!", 239, 300, 20, Color.LightGray);
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow();
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;