chore: clean recommit
This commit is contained in:
parent
8024c6ac40
commit
60ad2e7fb1
122 changed files with 23950 additions and 323 deletions
312
Examples/Text/InlineStyling.cs
Normal file
312
Examples/Text/InlineStyling.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
530
Examples/Text/StringsManagement.cs
Normal file
530
Examples/Text/StringsManagement.cs
Normal 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
739
Examples/Text/Text3D.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -22,11 +22,20 @@ using static Raylib_cs.Raylib;
|
|||
|
||||
namespace Examples.Text;
|
||||
|
||||
public class Unicode
|
||||
public partial class Unicode : IExample
|
||||
{
|
||||
private const int screenWidth = 800;
|
||||
private const int screenHeight = 450;
|
||||
|
||||
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
|
||||
{
|
||||
|
|
@ -35,10 +44,10 @@ public class Unicode
|
|||
public Color Color; // Emoji color
|
||||
}
|
||||
|
||||
static EmojiInfo[] emoji = new EmojiInfo[EmojiPerWidth * EmojiPerHeight];
|
||||
private EmojiInfo[] emoji;
|
||||
|
||||
static int hovered = -1;
|
||||
static int selected = -1;
|
||||
private int hovered;
|
||||
private int selected;
|
||||
|
||||
struct Message
|
||||
{
|
||||
|
|
@ -130,191 +139,187 @@ public class Unicode
|
|||
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"),
|
||||
};
|
||||
|
||||
public static int Main()
|
||||
{
|
||||
// Initialization
|
||||
//--------------------------------------------------------------------------------------
|
||||
const int screenWidth = 800;
|
||||
const int screenHeight = 450;
|
||||
private Font fontDefault;
|
||||
private Font fontAsian;
|
||||
private Font fontEmoji;
|
||||
|
||||
SetConfigFlags(ConfigFlags.Msaa4xHint | ConfigFlags.VSyncHint);
|
||||
InitWindow(screenWidth, screenHeight, "raylib [text] example - unicode emojis");
|
||||
private Vector2 hoveredPos;
|
||||
private Vector2 selectedPos;
|
||||
|
||||
public void Init()
|
||||
{
|
||||
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
|
||||
Font fontDefault = LoadFont("resources/fonts/dejavu.fnt"); // Requires "resources/fonts/dejavu.png"
|
||||
Font fontAsian = LoadFont("resources/fonts/noto_cjk.fnt"); // Requires "resources/fonts/noto_cjk.png"
|
||||
Font fontEmoji = LoadFont("resources/fonts/symbola.fnt"); // Requires "resources/fonts/symbola.png"
|
||||
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"
|
||||
|
||||
Vector2 hoveredPos = new(0.0f, 0.0f);
|
||||
Vector2 selectedPos = new(0.0f, 0.0f);
|
||||
hoveredPos = new(0.0f, 0.0f);
|
||||
selectedPos = new(0.0f, 0.0f);
|
||||
|
||||
// Set a random set of emojis when starting up
|
||||
RandomizeEmoji();
|
||||
}
|
||||
|
||||
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
// 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(KeyboardKey.Space))
|
||||
{
|
||||
// Update
|
||||
//----------------------------------------------------------------------------------
|
||||
// Add a new set of emojis when SPACE is pressed
|
||||
if (IsKeyPressed(KeyboardKey.Space))
|
||||
{
|
||||
RandomizeEmoji();
|
||||
}
|
||||
|
||||
// Set the selected emoji
|
||||
if (IsMouseButtonPressed(MouseButton.Left) && (hovered != -1) && (hovered != selected))
|
||||
{
|
||||
selected = hovered;
|
||||
selectedPos = hoveredPos;
|
||||
}
|
||||
|
||||
Vector2 mouse = GetMousePosition();
|
||||
Vector2 position = new(28.8f, 10.0f);
|
||||
hovered = -1;
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
// Draw
|
||||
//----------------------------------------------------------------------------------
|
||||
BeginDrawing();
|
||||
ClearBackground(Color.RayWhite);
|
||||
|
||||
// Draw random emojis in the background
|
||||
//------------------------------------------------------------------------------
|
||||
for (int i = 0; i < emoji.Length; i++)
|
||||
{
|
||||
string txt = GetEmojiAt(emoji[i].Index);
|
||||
Rectangle emojiRect = new(position.X, position.Y, fontEmoji.BaseSize, fontEmoji.BaseSize);
|
||||
|
||||
if (!CheckCollisionPointRec(mouse, emojiRect))
|
||||
{
|
||||
DrawTextEx(fontEmoji, txt, position, fontEmoji.BaseSize, 1.0f, selected == i ? emoji[i].Color : Fade(Color.LightGray, 0.4f));
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawTextEx(fontEmoji, txt, position, fontEmoji.BaseSize, 1.0f, emoji[i].Color);
|
||||
hovered = i;
|
||||
hoveredPos = position;
|
||||
}
|
||||
|
||||
if ((i != 0) && (i % EmojiPerWidth == 0))
|
||||
{
|
||||
position.Y += fontEmoji.BaseSize + 24.25f;
|
||||
position.X = 28.8f;
|
||||
}
|
||||
else
|
||||
{
|
||||
position.X += fontEmoji.BaseSize + 28.8f;
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Draw the message when a emoji is selected
|
||||
//------------------------------------------------------------------------------
|
||||
if (selected != -1)
|
||||
{
|
||||
int message = emoji[selected].Message;
|
||||
const int horizontalPadding = 20;
|
||||
const int verticalPadding = 30;
|
||||
Font font = fontDefault;
|
||||
|
||||
// Set correct font for asian languages
|
||||
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;
|
||||
}
|
||||
|
||||
Rectangle msgRect = new(selectedPos.X - 38.8f, selectedPos.Y, 2 * horizontalPadding + sz.X, 2 * verticalPadding + sz.Y);
|
||||
msgRect.Y -= msgRect.Height;
|
||||
|
||||
// Coordinates for the chat bubble triangle
|
||||
Vector2 a = new(selectedPos.X, msgRect.Y + msgRect.Height);
|
||||
Vector2 b = new(a.X + 8, a.Y + 10);
|
||||
Vector2 c = new(a.X + 10, a.Y);
|
||||
|
||||
// Don't go outside the screen
|
||||
if (msgRect.X < 10)
|
||||
{
|
||||
msgRect.X += 28;
|
||||
}
|
||||
|
||||
if (msgRect.Y < 10)
|
||||
{
|
||||
msgRect.Y = selectedPos.Y + 84;
|
||||
a.Y = msgRect.Y;
|
||||
c.Y = a.Y;
|
||||
b.Y = a.Y - 10;
|
||||
|
||||
// Swap values so we can actually render the triangle :(
|
||||
Vector2 tmp = a;
|
||||
a = b;
|
||||
b = tmp;
|
||||
}
|
||||
|
||||
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 + (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 = 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);
|
||||
|
||||
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, 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();
|
||||
//----------------------------------------------------------------------------------
|
||||
RandomizeEmoji();
|
||||
}
|
||||
|
||||
// De-Initialization
|
||||
//--------------------------------------------------------------------------------------
|
||||
// Set the selected emoji
|
||||
if (IsMouseButtonPressed(MouseButton.Left) && (hovered != -1) && (hovered != selected))
|
||||
{
|
||||
selected = hovered;
|
||||
selectedPos = hoveredPos;
|
||||
}
|
||||
|
||||
Vector2 mouse = GetMousePosition();
|
||||
Vector2 position = new(28.8f, 10.0f);
|
||||
hovered = -1;
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
// Draw
|
||||
//----------------------------------------------------------------------------------
|
||||
BeginDrawing();
|
||||
ClearBackground(Color.RayWhite);
|
||||
|
||||
// Draw random emojis in the background
|
||||
//------------------------------------------------------------------------------
|
||||
for (int i = 0; i < emoji.Length; i++)
|
||||
{
|
||||
string txt = GetEmojiAt(emoji[i].Index);
|
||||
Rectangle emojiRect = new(position.X, position.Y, fontEmoji.BaseSize, fontEmoji.BaseSize);
|
||||
|
||||
if (!CheckCollisionPointRec(mouse, emojiRect))
|
||||
{
|
||||
DrawTextEx(fontEmoji, txt, position, fontEmoji.BaseSize, 1.0f, selected == i ? emoji[i].Color : Fade(Color.LightGray, 0.4f));
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawTextEx(fontEmoji, txt, position, fontEmoji.BaseSize, 1.0f, emoji[i].Color);
|
||||
hovered = i;
|
||||
hoveredPos = position;
|
||||
}
|
||||
|
||||
if ((i != 0) && (i % EmojiPerWidth == 0))
|
||||
{
|
||||
position.Y += fontEmoji.BaseSize + 24.25f;
|
||||
position.X = 28.8f;
|
||||
}
|
||||
else
|
||||
{
|
||||
position.X += fontEmoji.BaseSize + 28.8f;
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Draw the message when a emoji is selected
|
||||
//------------------------------------------------------------------------------
|
||||
if (selected != -1)
|
||||
{
|
||||
int message = emoji[selected].Message;
|
||||
const int horizontalPadding = 20;
|
||||
const int verticalPadding = 30;
|
||||
Font font = fontDefault;
|
||||
|
||||
// Set correct font for asian languages
|
||||
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;
|
||||
}
|
||||
|
||||
Rectangle msgRect = new(selectedPos.X - 38.8f, selectedPos.Y, 2 * horizontalPadding + sz.X, 2 * verticalPadding + sz.Y);
|
||||
msgRect.Y -= msgRect.Height;
|
||||
|
||||
// Coordinates for the chat bubble triangle
|
||||
Vector2 a = new(selectedPos.X, msgRect.Y + msgRect.Height);
|
||||
Vector2 b = new(a.X + 8, a.Y + 10);
|
||||
Vector2 c = new(a.X + 10, a.Y);
|
||||
|
||||
// Don't go outside the screen
|
||||
if (msgRect.X < 10)
|
||||
{
|
||||
msgRect.X += 28;
|
||||
}
|
||||
|
||||
if (msgRect.Y < 10)
|
||||
{
|
||||
msgRect.Y = selectedPos.Y + 84;
|
||||
a.Y = msgRect.Y;
|
||||
c.Y = a.Y;
|
||||
b.Y = a.Y - 10;
|
||||
|
||||
// Swap values so we can actually render the triangle :(
|
||||
Vector2 tmp = a;
|
||||
a = b;
|
||||
b = tmp;
|
||||
}
|
||||
|
||||
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 + (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 = 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);
|
||||
|
||||
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, 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();
|
||||
//----------------------------------------------------------------------------------
|
||||
}
|
||||
|
||||
public void Unload()
|
||||
{
|
||||
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 RandomizeEmoji()
|
||||
{
|
||||
hovered = selected = -1;
|
||||
int start = GetRandomValue(45, 360);
|
||||
|
|
@ -510,4 +515,33 @@ public class Unicode
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
225
Examples/Text/UnicodeRanges.cs
Normal file
225
Examples/Text/UnicodeRanges.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
167
Examples/Text/WordsAlignment.cs
Normal file
167
Examples/Text/WordsAlignment.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue