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

WASM examples (+ backports of new official examples) (#344)

* chg: New build system that uses the officially distributed binaries, bumped version to 8.0.0, simplified git workflow, removed deprecated OpenGL 1.1 functionality.

* chg: Modernize CI workflow, enable SourceLink

- Bump workflow actions to latest majors (Node 24); drop deprecated softprops/action-gh-release@v1
- Trigger push builds on main instead of master
- Create local nuget feed dir before pack (fixes NU1301)
- Enable Microsoft.SourceLink.GitHub for debugging symbols (ref PR #340)

* fix: centralized version data in Directory.build.props, and fixed various interop details that had incorrect function signatures

* chore: updated readme

* fix: version the native extract marker and chain download via DependsOnTargets

The .extracted marker now includes the raylib package name, so bumping
TargetRaylibTag re-extracts the new archive instead of silently keeping
(and packing/copying) the previous version's files.

_PrepareNativeLibrary and _StageWasmNative now depend directly on
_DownloadAndExtractInternal instead of CallTarget-ing it; dependency
targets run in the same project instance, so the resolved properties
(RaylibPackageName etc.) propagate naturally.

* fix: let the binding build for browser-wasm on both net8.0 and net10.0

The net8-era wasm workload (Microsoft.NET.Runtime.WebAssembly.Sdk 8.0.x,
auto-imported for RID browser-wasm) treats every browser-wasm project as
a wasm app: it forces OutputType=Exe after project evaluation (CS5001
for a classlib) and hooks its app-bundle build after Build, which errors
because a library has no assemblies to bundle. Opt Raylib-cs out via
DisableAutoWasmBuildApp (props time, before the workload defaults its
trigger) and pin OutputType back to Library in Directory.Build.targets
(evaluated after the workload props, so the assignment wins). net10's
wasm SDK needs neither workaround.

* chore: readme updated

* chg: simplifying build logic - a simple line in the documentation should save us the code here

* fix: Wrong signature of FrameBufferComplete

* chore: readme update

* feat: samples default to local project reference, and can optionally use the nuget package

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

* chore: readme, gitignore, and targets backport.

* fix: Examples.csproj runs the download task when building locally

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

* chore: readme, gitignore, and targets backport.

* chore: clean up linter warnings

* feat: html harness focuses the example and allows quick navigation with J/K instead.

* chore: readme mentions the property to use nuget vs. the local project reference

* feat: replaced the J/K navigation with good old HTML buttons

* chore: run dotnet format scoped default (was previously scoped to just 'style')
This commit is contained in:
tiger tiger tiger 2026-07-30 19:34:34 +02:00 • committed by GitHub
commit 8c22e68c2a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
236 changed files with 40405 additions and 10896 deletions

View file

@ -0,0 +1,273 @@
/*******************************************************************************************
*
* raylib [audio] example - amp envelope
*
* Example complexity rating: [★☆☆☆] 1/4
*
* Example originally created with raylib 6.0, last time updated with raylib 6.0
*
* Example contributed by Arbinda Rizki Muhammad (@arbipink) 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) 2026 Arbinda Rizki Muhammad (@arbipink)
*
********************************************************************************************/
namespace Examples.Audio;
public unsafe partial class AmpEnvelope : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
private const int BUFFER_SIZE = 4096;
private const int SAMPLE_RATE = 44100;
// Wave state
private enum ADSRState
{
Idle,
Attack,
Decay,
Sustain,
Release
}
// Grouping all ADSR parameters and state into a struct
private struct Envelope
{
public float AttackTime;
public float DecayTime;
public float SustainLevel;
public float ReleaseTime;
public float CurrentValue;
public ADSRState State;
}
public string Name => "Audio / Amp Envelope";
public string Title => "raylib [audio] example - amp envelope";
private float[] buffer;
private AudioStream stream;
private float audioTime;
private Envelope env;
public void Init()
{
InitAudioDevice();
// Set the number of samples the stream will keep in memory at a time to BUFFER_SIZE
SetAudioStreamBufferSizeDefault(BUFFER_SIZE);
buffer = new float[BUFFER_SIZE];
// Init raw audio stream (sample rate: 44100, sample size: 32bit-float, channels: 1-mono)
stream = LoadAudioStream(SAMPLE_RATE, 32, 1);
// Init Phase
audioTime = 0.0f;
// Initialize the struct
env = new Envelope
{
AttackTime = 1.0f,
DecayTime = 1.0f,
SustainLevel = 0.5f,
ReleaseTime = 1.0f,
CurrentValue = 0.0f,
State = ADSRState.Idle
};
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
if (IsKeyPressed(KeyboardKey.Space))
{
env.State = ADSRState.Attack;
}
if (IsKeyReleased(KeyboardKey.Space) && (env.State != ADSRState.Idle))
{
env.State = ADSRState.Release;
}
if (IsAudioStreamProcessed(stream))
{
if ((env.State != ADSRState.Idle) || (env.CurrentValue > 0.0f))
{
for (int i = 0; i < BUFFER_SIZE; i++)
{
UpdateEnvelope(ref env);
FillAudioBuffer(i, buffer, env.CurrentValue, ref audioTime);
}
}
else
{
// Clear buffer if silent to avoid looping noise
for (int i = 0; i < BUFFER_SIZE; i++)
{
buffer[i] = 0;
}
audioTime = 0.0f;
}
fixed (float* bufferPtr = buffer)
{
UpdateAudioStream(stream, bufferPtr, BUFFER_SIZE);
}
}
if (!IsAudioStreamPlaying(stream))
{
PlayAudioStream(stream);
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
// NOTE: raygui is not bound in raylib-cs, so the sliders below are left as reference.
// The envelope keeps its default parameters (Attack/Decay/Release = 1.0s, Sustain = 0.5).
//GuiSliderBar(new Rectangle( 100, 60, 400, 30 ), "Attack (s)", TextFormat("%2.2fs", env.AttackTime), ref env.AttackTime, 0.1f, 3.0f);
//GuiSliderBar(new Rectangle( 100, 100, 400, 30 ), "Decay (s)", TextFormat("%2.2fs", env.DecayTime), ref env.DecayTime, 0.1f, 3.0f);
//GuiSliderBar(new Rectangle( 100, 140, 400, 30 ), "Sustain", TextFormat("%2.2f", env.SustainLevel), ref env.SustainLevel, 0.0f, 1.0f);
//GuiSliderBar(new Rectangle( 100, 180, 400, 30 ), "Release (s)", TextFormat("%2.2fs", env.ReleaseTime), ref env.ReleaseTime, 0.1f, 3.0f);
DrawADSRGraph(ref env, new Rectangle(100, 250, 400, 100));
DrawCircleV(new Vector2(520, 350 - (env.CurrentValue * 100)), 5, Color.Maroon);
DrawText($"Current Gain: {env.CurrentValue:F2}", 535, (int)(345 - (env.CurrentValue * 100)), 10, Color.Maroon);
DrawText("Press SPACE to PLAY the sound!", 200, 400, 20, Color.LightGray);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadAudioStream(stream);
CloseAudioDevice();
}
//------------------------------------------------------------------------------------
// Module Functions Definition
//------------------------------------------------------------------------------------
private static void FillAudioBuffer(int i, float[] buffer, float envelopeValue, ref float audioTime)
{
int frequency = 440;
buffer[i] = envelopeValue * MathF.Sin(2.0f * MathF.PI * frequency * audioTime);
audioTime += (1.0f / SAMPLE_RATE);
}
private static void UpdateEnvelope(ref Envelope env)
{
// Calculate the time delta for ONE sample (1/44100)
float sampleTime = 1.0f / SAMPLE_RATE;
switch (env.State)
{
case ADSRState.Attack:
{
env.CurrentValue += (1.0f / env.AttackTime) * sampleTime;
if (env.CurrentValue >= 1.0f)
{
env.CurrentValue = 1.0f;
env.State = ADSRState.Decay;
}
}
break;
case ADSRState.Decay:
{
env.CurrentValue -= ((1.0f - env.SustainLevel) / env.DecayTime) * sampleTime;
if (env.CurrentValue <= env.SustainLevel)
{
env.CurrentValue = env.SustainLevel;
env.State = ADSRState.Sustain;
}
}
break;
case ADSRState.Sustain:
{
env.CurrentValue = env.SustainLevel;
}
break;
case ADSRState.Release:
{
env.CurrentValue -= (env.SustainLevel / env.ReleaseTime) * sampleTime;
if (env.CurrentValue <= 0.001f) // Use a small threshold to avoid infinite tail
{
env.CurrentValue = 0.0f;
env.State = ADSRState.Idle;
}
}
break;
default:
break;
}
}
private static void DrawADSRGraph(ref Envelope env, Rectangle bounds)
{
DrawRectangleRec(bounds, Fade(Color.LightGray, 0.3f));
DrawRectangleLinesEx(bounds, 1, Color.Gray);
// Fixed visual width for sustain stage since it's an amplitude not a time value
float sustainWidth = 1.0f;
// Total time to visualize (sum of A, D, R + a padding for Sustain)
float totalTime = env.AttackTime + env.DecayTime + sustainWidth + env.ReleaseTime;
float scaleX = bounds.Width / totalTime;
float scaleY = bounds.Height;
Vector2 start = new Vector2(bounds.X, bounds.Y + bounds.Height);
Vector2 peak = new Vector2(start.X + (env.AttackTime * scaleX), bounds.Y);
Vector2 sustain = new Vector2(peak.X + (env.DecayTime * scaleX), bounds.Y + (1.0f - env.SustainLevel) * scaleY);
Vector2 rel = new Vector2(sustain.X + (sustainWidth * scaleX), sustain.Y);
Vector2 end = new Vector2(rel.X + (env.ReleaseTime * scaleX), bounds.Y + bounds.Height);
DrawLineV(start, peak, Color.SkyBlue);
DrawLineV(peak, sustain, Color.Blue);
DrawLineV(sustain, rel, Color.DarkBlue);
DrawLineV(rel, end, Color.Orange);
DrawText("ADSR Visualizer", (int)bounds.X, (int)bounds.Y - 20, 10, Color.DarkGray);
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [audio] example - amp envelope");
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
var game = new AmpEnvelope();
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,177 @@
/*******************************************************************************************
*
* raylib [audio] example - mixed processor
*
* Example complexity rating: [★★★★] 4/4
*
* Example originally created with raylib 4.2, last time updated with raylib 4.2
*
* Example contributed by hkc (@hatkidchan) 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) 2023-2025 hkc (@hatkidchan)
*
********************************************************************************************/
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace Examples.Audio;
[ExcludeFromBrowser("AttachAudioMixedProcessor callback is unreliable on the wasm audio backend")]
public unsafe partial class MixedProcessor : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Audio / Mixed Processor";
public string Title => "raylib [audio] example - mixed processor";
private static float exponent = 1.0f; // Audio exponentiation value
private static readonly float[] averageVolume = new float[400]; // Average volume history
private Music music;
private Sound sound;
//------------------------------------------------------------------------------------
// Audio processing function
//------------------------------------------------------------------------------------
[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
private static void ProcessAudio(void* buffer, uint frames)
{
float* samples = (float*)buffer; // Samples internally stored as <float>s
float average = 0.0f; // Temporary average volume
for (uint frame = 0; frame < frames; frame++)
{
float* left = &samples[frame * 2 + 0];
float* right = &samples[frame * 2 + 1];
*left = MathF.Pow(MathF.Abs(*left), exponent) * ((*left < 0.0f) ? -1.0f : 1.0f);
*right = MathF.Pow(MathF.Abs(*right), exponent) * ((*right < 0.0f) ? -1.0f : 1.0f);
average += MathF.Abs(*left) / frames; // accumulating average volume
average += MathF.Abs(*right) / frames;
}
// Moving history to the left
for (int i = 0; i < 399; i++)
{
averageVolume[i] = averageVolume[i + 1];
}
averageVolume[399] = average; // Adding last average value
}
public void Init()
{
InitAudioDevice(); // Initialize audio device
exponent = 1.0f;
Array.Clear(averageVolume, 0, averageVolume.Length);
AttachAudioMixedProcessor(&ProcessAudio);
music = LoadMusicStream("resources/audio/country.mp3");
sound = LoadSound("resources/audio/coin.wav");
PlayMusicStream(music);
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
UpdateMusicStream(music); // Update music buffer with new stream data
// Modify processing variables
//----------------------------------------------------------------------------------
if (IsKeyPressed(KeyboardKey.Left))
{
exponent -= 0.05f;
}
if (IsKeyPressed(KeyboardKey.Right))
{
exponent += 0.05f;
}
if (exponent <= 0.5f)
{
exponent = 0.5f;
}
if (exponent >= 3.0f)
{
exponent = 3.0f;
}
if (IsKeyPressed(KeyboardKey.Space))
{
PlaySound(sound);
}
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
DrawText("MUSIC SHOULD BE PLAYING!", 255, 150, 20, Color.LightGray);
DrawText($"EXPONENT = {exponent:F2}", 215, 180, 20, Color.LightGray);
DrawRectangle(199, 199, 402, 34, Color.LightGray);
for (int i = 0; i < 400; i++)
{
DrawLine(201 + i, 232 - (int)(averageVolume[i] * 32), 201 + i, 232, Color.Maroon);
}
DrawRectangleLines(199, 199, 402, 34, Color.Gray);
DrawText("PRESS SPACE TO PLAY OTHER SOUND", 200, 250, 20, Color.LightGray);
DrawText("USE LEFT AND RIGHT ARROWS TO ALTER DISTORTION", 140, 280, 20, Color.LightGray);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadMusicStream(music); // Unload music stream buffers from RAM
DetachAudioMixedProcessor(&ProcessAudio); // Disconnect audio processor
CloseAudioDevice(); // Close audio device (music streaming is automatically stopped)
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [audio] example - mixed processor");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new MixedProcessor();
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,26 +1,34 @@
/*******************************************************************************************
*
* raylib [audio] example - Module playing (streaming)
* raylib [audio] example - module playing
*
* NOTE: This example requires OpenAL Soft library installed
* Example complexity rating: [★☆☆☆] 1/4
*
* This example has been created using raylib 1.5 (www.raylib.com)
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
* Example originally created with raylib 1.5, last time updated with raylib 3.5
*
* Copyright (c) 2016 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)
*
********************************************************************************************/
using System.Numerics;
using static Raylib_cs.Raylib;
namespace Examples.Audio;
public class ModulePlaying
public partial class ModulePlaying : IExample
{
const int MaxCircles = 64;
private const int screenWidth = 800;
private const int screenHeight = 450;
struct CircleWave
private const int MaxCircles = 64;
public string Name => "Audio / Module Playing";
public string Title => "raylib [audio] example - module playing";
public ConfigFlags ConfigFlags => ConfigFlags.Msaa4xHint;
private struct CircleWave
{
public Vector2 Position;
public float Radius;
@ -29,20 +37,18 @@ public class ModulePlaying
public Color Color;
}
public static int Main()
private Color[] colors;
private CircleWave[] circles;
private Music music;
private float pitch;
private float timePlayed;
private bool pause;
public void Init()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitAudioDevice(); // Initialize audio device
SetConfigFlags(ConfigFlags.Msaa4xHint); // NOTE: Try to enable MSAA 4X
InitWindow(screenWidth, screenHeight, "raylib [audio] example - module playing (streaming)");
InitAudioDevice();
Color[] colors = new Color[14] {
colors = new Color[14] {
Color.Orange,
Color.Red,
Color.Gold,
@ -59,133 +65,163 @@ public class ModulePlaying
Color.Beige
};
// Creates ome circles for visual effect
CircleWave[] circles = new CircleWave[MaxCircles];
// Creates some circles for visual effect
circles = new CircleWave[MaxCircles];
for (int i = MaxCircles - 1; i >= 0; i--)
for (var i = MaxCircles - 1; i >= 0; i--)
{
circles[i].Alpha = 0.0f;
circles[i].Radius = GetRandomValue(10, 40);
circles[i].Position.X = GetRandomValue((int)circles[i].Radius, screenWidth - (int)circles[i].Radius);
circles[i].Position.Y = GetRandomValue((int)circles[i].Radius, screenHeight - (int)circles[i].Radius);
circles[i].Speed = (float)GetRandomValue(1, 100) / 20000.0f;
circles[i].Speed = (float)GetRandomValue(1, 100) / 2000.0f;
circles[i].Color = colors[GetRandomValue(0, 13)];
}
Music music = LoadMusicStream("resources/audio/mini1111.xm");
music = LoadMusicStream("resources/audio/mini1111.xm");
music.Looping = false;
float pitch = 1.0f;
pitch = 1.0f;
PlayMusicStream(music);
float timePlayed = 0.0f;
bool pause = false;
timePlayed = 0.0f;
pause = false;
}
SetTargetFPS(60);
public void Update()
{
// Update
//----------------------------------------------------------------------------------
UpdateMusicStream(music); // Update music buffer with new stream data
// Restart music playing (stop and play)
if (IsKeyPressed(KeyboardKey.Space))
{
StopMusicStream(music);
PlayMusicStream(music);
}
// Pause/Resume music playing
if (IsKeyPressed(KeyboardKey.P))
{
pause = !pause;
if (pause)
{
PauseMusicStream(music);
}
else
{
ResumeMusicStream(music);
}
}
if (IsKeyDown(KeyboardKey.Down))
{
pitch -= 0.01f;
}
else if (IsKeyDown(KeyboardKey.Up))
{
pitch += 0.01f;
}
SetMusicPitch(music, pitch);
// Get timePlayed scaled to bar dimensions
timePlayed = GetMusicTimePlayed(music) / GetMusicTimeLength(music) * (screenWidth - 40);
// Color circles animation
for (var i = MaxCircles - 1; (i >= 0) && !pause; i--)
{
circles[i].Alpha += circles[i].Speed;
circles[i].Radius += circles[i].Speed * 10.0f;
if (circles[i].Alpha > 1.0f)
{
circles[i].Speed *= -1;
}
if (circles[i].Alpha <= 0.0f)
{
circles[i].Alpha = 0.0f;
circles[i].Radius = GetRandomValue(10, 40);
circles[i].Position.X = GetRandomValue(
(int)circles[i].Radius,
screenWidth - (int)circles[i].Radius
);
circles[i].Position.Y = GetRandomValue(
(int)circles[i].Radius,
screenHeight - (int)circles[i].Radius
);
circles[i].Color = colors[GetRandomValue(0, 13)];
circles[i].Speed = (float)GetRandomValue(1, 100) / 2000.0f;
}
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
for (var i = MaxCircles - 1; i >= 0; i--)
{
DrawCircleV(
circles[i].Position,
circles[i].Radius,
Fade(circles[i].Color, circles[i].Alpha)
);
}
// Draw time bar
DrawRectangle(20, screenHeight - 20 - 12, screenWidth - 40, 12, Color.LightGray);
DrawRectangle(20, screenHeight - 20 - 12, (int)timePlayed, 12, Color.Maroon);
DrawRectangleLines(20, screenHeight - 20 - 12, screenWidth - 40, 12, Color.Gray);
// Draw help instructions
DrawRectangle(20, 20, 425, 145, Color.White);
DrawRectangleLines(20, 20, 425, 145, Color.Gray);
DrawText("PRESS SPACE TO RESTART MUSIC", 40, 40, 20, Color.Black);
DrawText("PRESS P TO PAUSE/RESUME", 40, 70, 20, Color.Black);
DrawText("PRESS UP/DOWN TO CHANGE SPEED", 40, 100, 20, Color.Black);
DrawText($"SPEED: {pitch:F6}", 40, 130, 20, Color.Maroon);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadMusicStream(music); // Unload music stream buffers from RAM
CloseAudioDevice(); // Close audio device (music streaming is automatically stopped)
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
SetConfigFlags(ConfigFlags.Msaa4xHint); // NOTE: Try to enable MSAA 4X
InitWindow(screenWidth, screenHeight, "raylib [audio] example - module playing");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new ModulePlaying();
game.Init();
// Main game loop
while (!WindowShouldClose())
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
UpdateMusicStream(music); // Update music buffer with new stream data
// Restart music playing (stop and play)
if (IsKeyPressed(KeyboardKey.Space))
{
StopMusicStream(music);
PlayMusicStream(music);
}
// Pause/Resume music playing
if (IsKeyPressed(KeyboardKey.P))
{
pause = !pause;
if (pause)
{
PauseMusicStream(music);
}
else
{
ResumeMusicStream(music);
}
}
if (IsKeyDown(KeyboardKey.Down))
{
pitch -= 0.01f;
}
else if (IsKeyDown(KeyboardKey.Up))
{
pitch += 0.01f;
}
SetMusicPitch(music, pitch);
// Get timePlayed scaled to bar dimensions
timePlayed = GetMusicTimePlayed(music) / GetMusicTimeLength(music) * (screenWidth - 40);
// Color circles animation
for (int i = MaxCircles - 1; (i >= 0) && !pause; i--)
{
circles[i].Alpha += circles[i].Speed;
circles[i].Radius += circles[i].Speed * 10.0f;
if (circles[i].Alpha > 1.0f)
{
circles[i].Speed *= -1;
}
if (circles[i].Alpha <= 0.0f)
{
circles[i].Alpha = 0.0f;
circles[i].Radius = GetRandomValue(10, 40);
circles[i].Position.X = GetRandomValue(
(int)circles[i].Radius,
screenWidth - (int)circles[i].Radius
);
circles[i].Position.Y = GetRandomValue(
(int)circles[i].Radius,
screenHeight - (int)circles[i].Radius
);
circles[i].Color = colors[GetRandomValue(0, 13)];
circles[i].Speed = (float)GetRandomValue(1, 100) / 2000.0f;
}
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
for (int i = MaxCircles - 1; i >= 0; i--)
{
DrawCircleV(
circles[i].Position,
circles[i].Radius,
ColorAlpha(circles[i].Color, circles[i].Alpha)
);
}
// Draw time bar
DrawRectangle(20, screenHeight - 20 - 12, screenWidth - 40, 12, Color.LightGray);
DrawRectangle(20, screenHeight - 20 - 12, (int)timePlayed, 12, Color.Maroon);
DrawRectangleLines(20, screenHeight - 20 - 12, screenWidth - 40, 12, Color.Gray);
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadMusicStream(music);
CloseAudioDevice();
CloseWindow();
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;

View file

@ -1,104 +1,190 @@
/*******************************************************************************************
*
* raylib [audio] example - IntPtr playing (streaming)
* raylib [audio] example - music stream
*
* NOTE: This example requires OpenAL Soft library installed
* Example complexity rating: [★☆☆☆] 1/4
*
* This example has been created using raylib 1.3 (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)
*
********************************************************************************************/
using static Raylib_cs.Raylib;
namespace Examples.Audio;
public class MusicStreamDemo
public partial class MusicStreamDemo : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Audio / Music Stream Demo";
public string Title => "raylib [audio] example - music stream";
public int TargetFps => 30;
private Music music;
private float timePlayed;
private bool pause;
private float pan;
private float volume;
public void Init()
{
InitAudioDevice(); // Initialize audio device
music = LoadMusicStream("resources/audio/country.mp3");
PlayMusicStream(music);
timePlayed = 0.0f; // Time played normalized [0.0f..1.0f]
pause = false; // Music playing paused
pan = 0.0f; // Default audio pan center [-1.0f..1.0f]
SetMusicPan(music, pan);
volume = 0.8f; // Default audio volume [0.0f..1.0f]
SetMusicVolume(music, volume);
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
UpdateMusicStream(music); // Update music buffer with new stream data
// Restart music playing (stop and play)
if (IsKeyPressed(KeyboardKey.Space))
{
StopMusicStream(music);
PlayMusicStream(music);
}
// Pause/Resume music playing
if (IsKeyPressed(KeyboardKey.P))
{
pause = !pause;
if (pause)
{
PauseMusicStream(music);
}
else
{
ResumeMusicStream(music);
}
}
// Set audio pan
if (IsKeyDown(KeyboardKey.Left))
{
pan -= 0.05f;
if (pan < -1.0f)
{
pan = -1.0f;
}
SetMusicPan(music, pan);
}
else if (IsKeyDown(KeyboardKey.Right))
{
pan += 0.05f;
if (pan > 1.0f)
{
pan = 1.0f;
}
SetMusicPan(music, pan);
}
// Set audio volume
if (IsKeyDown(KeyboardKey.Down))
{
volume -= 0.05f;
if (volume < 0.0f)
{
volume = 0.0f;
}
SetMusicVolume(music, volume);
}
else if (IsKeyDown(KeyboardKey.Up))
{
volume += 0.05f;
if (volume > 1.0f)
{
volume = 1.0f;
}
SetMusicVolume(music, volume);
}
// Get normalized time played for current music stream
timePlayed = GetMusicTimePlayed(music) / GetMusicTimeLength(music);
if (timePlayed > 1.0f)
{
timePlayed = 1.0f; // Make sure time played is no longer than music
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
DrawText("MUSIC SHOULD BE PLAYING!", 255, 150, 20, Color.LightGray);
DrawText("LEFT-RIGHT for PAN CONTROL", 320, 74, 10, Color.DarkBlue);
DrawRectangle(300, 100, 200, 12, Color.LightGray);
DrawRectangleLines(300, 100, 200, 12, Color.Gray);
DrawRectangle((int)(300 + (pan + 1.0f) / 2.0f * 200 - 5), 92, 10, 28, Color.DarkGray);
DrawRectangle(200, 200, 400, 12, Color.LightGray);
DrawRectangle(200, 200, (int)(timePlayed * 400.0f), 12, Color.Maroon);
DrawRectangleLines(200, 200, 400, 12, Color.Gray);
DrawText("PRESS SPACE TO RESTART MUSIC", 215, 250, 20, Color.LightGray);
DrawText("PRESS P TO PAUSE/RESUME MUSIC", 208, 280, 20, Color.LightGray);
DrawText("UP-DOWN for VOLUME CONTROL", 320, 334, 10, Color.DarkGreen);
DrawRectangle(300, 360, 200, 12, Color.LightGray);
DrawRectangleLines(300, 360, 200, 12, Color.Gray);
DrawRectangle((int)(300 + volume * 200 - 5), 352, 10, 28, Color.DarkGray);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadMusicStream(music); // Unload music stream buffers from RAM
CloseAudioDevice(); // Close audio device (music streaming is automatically stopped)
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [audio] example - music stream");
InitWindow(screenWidth, screenHeight, "raylib [audio] example - music playing (streaming)");
InitAudioDevice();
Music music = LoadMusicStream("resources/audio/country.mp3");
PlayMusicStream(music);
float timePlayed = 0.0f;
bool pause = false;
SetTargetFPS(60);
SetTargetFPS(30); // Set our game to run at 30 frames-per-second
//--------------------------------------------------------------------------------------
var game = new MusicStreamDemo();
game.Init();
// Main game loop
while (!WindowShouldClose())
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
UpdateMusicStream(music); // Update music buffer with new stream data
// Restart music playing (stop and play)
if (IsKeyPressed(KeyboardKey.Space))
{
StopMusicStream(music);
PlayMusicStream(music);
}
// Pause/Resume music playing
if (IsKeyPressed(KeyboardKey.P))
{
pause = !pause;
if (pause)
{
PauseMusicStream(music);
}
else
{
ResumeMusicStream(music);
}
}
// Get timePlayed scaled to bar dimensions (400 pixels)
timePlayed = GetMusicTimePlayed(music) / GetMusicTimeLength(music) * 400;
if (timePlayed > 400)
{
StopMusicStream(music);
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
DrawText("MUSIC SHOULD BE PLAYING!", 255, 150, 20, Color.LightGray);
DrawRectangle(200, 200, 400, 12, Color.LightGray);
DrawRectangle(200, 200, (int)timePlayed, 12, Color.Maroon);
DrawRectangleLines(200, 200, 400, 12, Color.Gray);
DrawText("PRESS SPACE TO RESTART MUSIC", 215, 250, 20, Color.LightGray);
DrawText("PRESS P TO PAUSE/RESUME MUSIC", 208, 280, 20, Color.LightGray);
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadMusicStream(music);
CloseAudioDevice();
CloseWindow();
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;

192
Examples/Audio/RawStream.cs Normal file
View file

@ -0,0 +1,192 @@
/*******************************************************************************************
*
* raylib [audio] example - raw stream
*
* Example complexity rating: [★★★☆] 3/4
*
* Example originally created with raylib 1.6, last time updated with raylib 6.0
*
* Example created by Ramon Santamaria (@raysan5) and reviewed by James Hofmann (@triplefox)
*
* 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-2026 Ramon Santamaria (@raysan5) and James Hofmann (@triplefox)
*
********************************************************************************************/
namespace Examples.Audio;
public unsafe partial class RawStream : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
private const int BUFFER_SIZE = 4096;
private const int SAMPLE_RATE = 44100;
public string Name => "Audio / Raw Stream";
public string Title => "raylib [audio] example - raw stream";
public int TargetFps => 30;
private float[] buffer;
private AudioStream stream;
private float pan;
private int sineFrequency;
private int newSineFrequency;
private int sineIndex;
private double sineStartTime;
public void Init()
{
InitAudioDevice();
// Set the number of samples the stream will keep in memory at a time to BUFFER_SIZE
SetAudioStreamBufferSizeDefault(BUFFER_SIZE);
buffer = new float[BUFFER_SIZE];
// Init raw audio stream (sample rate: 44100, sample size: 32bit-float, channels: 1-mono)
stream = LoadAudioStream(SAMPLE_RATE, 32, 1);
pan = 0.0f;
SetAudioStreamPan(stream, pan);
PlayAudioStream(stream);
sineFrequency = 440;
newSineFrequency = 440;
sineIndex = 0;
sineStartTime = 0.0;
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
if (IsKeyDown(KeyboardKey.Up))
{
newSineFrequency += 10;
if (newSineFrequency > 12500)
{
newSineFrequency = 12500;
}
}
if (IsKeyDown(KeyboardKey.Down))
{
newSineFrequency -= 10;
if (newSineFrequency < 20)
{
newSineFrequency = 20;
}
}
if (IsKeyDown(KeyboardKey.Left))
{
pan -= 0.01f;
if (pan < -1.0f)
{
pan = -1.0f;
}
SetAudioStreamPan(stream, pan);
}
if (IsKeyDown(KeyboardKey.Right))
{
pan += 0.01f;
if (pan > 1.0f)
{
pan = 1.0f;
}
SetAudioStreamPan(stream, pan);
}
if (IsAudioStreamProcessed(stream))
{
for (int i = 0; i < BUFFER_SIZE; i++)
{
int wl = SAMPLE_RATE / sineFrequency;
buffer[i] = MathF.Sin(2 * MathF.PI * sineIndex / wl);
sineIndex++;
if (sineIndex >= wl)
{
sineFrequency = newSineFrequency;
sineIndex = 0;
sineStartTime = GetTime();
}
}
fixed (float* bufferPtr = buffer)
{
UpdateAudioStream(stream, bufferPtr, BUFFER_SIZE);
}
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
DrawText($"sine frequency: {sineFrequency}", screenWidth - 220, 10, 20, Color.Red);
DrawText($"pan: {pan:F2}", screenWidth - 220, 30, 20, Color.Red);
DrawText("Up/down to change frequency", 10, 10, 20, Color.DarkGray);
DrawText("Left/right to pan", 10, 30, 20, Color.DarkGray);
int windowStart = (int)((GetTime() - sineStartTime) * SAMPLE_RATE);
int windowSize = (int)(0.1f * SAMPLE_RATE);
int wavelength = SAMPLE_RATE / sineFrequency;
// Draw a sine wave with the same frequency as the one being sent to the audio stream
for (int i = 0; i < screenWidth; i++)
{
int t0 = windowStart + i * windowSize / screenWidth;
int t1 = windowStart + (i + 1) * windowSize / screenWidth;
Vector2 startPos = new Vector2(i, 250 + 50 * MathF.Sin(2 * MathF.PI * t0 / wavelength));
Vector2 endPos = new Vector2(i + 1, 250 + 50 * MathF.Sin(2 * MathF.PI * t1 / wavelength));
DrawLineV(startPos, endPos, Color.Red);
}
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadAudioStream(stream); // Close raw audio stream and delete buffers from RAM
CloseAudioDevice(); // Close audio device (music streaming is automatically stopped)
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [audio] example - raw stream");
SetTargetFPS(30);
//--------------------------------------------------------------------------------------
var game = new RawStream();
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,74 +1,98 @@
/*******************************************************************************************
*
* raylib [audio] example - Sound loading and playing
* raylib [audio] example - sound loading
*
* NOTE: This example requires OpenAL Soft library installed
* Example complexity rating: [★☆☆☆] 1/4
*
* 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.1, last time updated with raylib 3.5
*
* 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)
*
********************************************************************************************/
using static Raylib_cs.Raylib;
namespace Examples.Audio;
public class SoundLoading
public partial class SoundLoading : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Audio / Sound Loading";
public string Title => "raylib [audio] example - sound loading";
private Sound fxWav;
private Sound fxOgg;
public void Init()
{
InitAudioDevice(); // Initialize audio device
fxWav = LoadSound("resources/audio/sound.wav"); // Load WAV audio file
fxOgg = LoadSound("resources/audio/target.ogg"); // Load OGG audio file
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
if (IsKeyPressed(KeyboardKey.Space))
{
PlaySound(fxWav); // Play WAV sound
}
if (IsKeyPressed(KeyboardKey.Enter))
{
PlaySound(fxOgg); // Play OGG sound
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
DrawText("Press SPACE to PLAY the WAV sound!", 200, 180, 20, Color.LightGray);
DrawText("Press ENTER to PLAY the OGG sound!", 200, 220, 20, Color.LightGray);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadSound(fxWav); // Unload sound data
UnloadSound(fxOgg); // Unload sound data
CloseAudioDevice(); // Close audio device
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [audio] example - sound loading");
InitWindow(screenWidth, screenHeight, "raylib [audio] example - sound loading and playing");
InitAudioDevice();
Sound fxWav = LoadSound("resources/audio/sound.wav");
Sound fxOgg = LoadSound("resources/audio/target.ogg");
SetTargetFPS(60);
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new SoundLoading();
game.Init();
// Main game loop
while (!WindowShouldClose())
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
if (IsKeyPressed(KeyboardKey.Space))
{
PlaySound(fxWav);
}
if (IsKeyPressed(KeyboardKey.Enter))
{
PlaySound(fxOgg);
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
DrawText("Press SPACE to PLAY the WAV sound!", 200, 180, 20, Color.LightGray);
DrawText("Press ENTER to PLAY the OGG sound!", 200, 220, 20, Color.LightGray);
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadSound(fxWav);
UnloadSound(fxOgg);
CloseAudioDevice();
CloseWindow();
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;

View file

@ -0,0 +1,122 @@
/*******************************************************************************************
*
* raylib [audio] example - sound multi
*
* Example complexity rating: [★★☆☆] 2/4
*
* Example originally created with raylib 5.0, last time updated with raylib 5.0
*
* Example contributed by Jeffery Myers (@JeffM2501) 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) 2023-2025 Jeffery Myers (@JeffM2501)
*
********************************************************************************************/
namespace Examples.Audio;
public partial class SoundMulti : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
private const int MAX_SOUNDS = 10;
public string Name => "Audio / Sound Multi";
public string Title => "raylib [audio] example - sound multi";
private Sound[] soundArray = new Sound[MAX_SOUNDS];
private int currentSound;
public void Init()
{
InitAudioDevice(); // Initialize audio device
// Load audio file into the first slot as the 'source' sound,
// this sound owns the sample data
soundArray[0] = LoadSound("resources/audio/sound.wav");
// Load an alias of the sound into slots 1-9. These do not own the sound data, but can be played
for (int i = 1; i < MAX_SOUNDS; i++)
{
soundArray[i] = LoadSoundAlias(soundArray[0]);
}
currentSound = 0; // Set the sound list to the start
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
if (IsKeyPressed(KeyboardKey.Space))
{
PlaySound(soundArray[currentSound]); // Play the next open sound slot
currentSound++; // Increment the sound slot
// If the sound slot is out of bounds, go back to 0
if (currentSound >= MAX_SOUNDS)
{
currentSound = 0;
}
// NOTE: Another approach would be to look at the list for the first sound
// that is not playing and use that slot
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
DrawText("Press SPACE to PLAY a WAV sound!", 200, 180, 20, Color.LightGray);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
for (int i = 1; i < MAX_SOUNDS; i++)
{
UnloadSoundAlias(soundArray[i]); // Unload sound aliases
}
UnloadSound(soundArray[0]); // Unload source sound data
CloseAudioDevice(); // Close audio device
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [audio] example - sound multi");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new SoundMulti();
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,156 @@
/*******************************************************************************************
*
* raylib [audio] example - sound positioning
*
* Example complexity rating: [★★☆☆] 2/4
*
* Example originally created with raylib 5.5, last time updated with raylib 5.5
*
* Example contributed by Le Juez Victor (@Bigfoot71) 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 Le Juez Victor (@Bigfoot71)
*
********************************************************************************************/
namespace Examples.Audio;
public partial class SoundPositioning : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Audio / Sound Positioning";
public string Title => "raylib [audio] example - sound positioning";
public bool CursorDisabled => true;
private Sound sound;
private Camera3D camera;
public void Init()
{
InitAudioDevice();
sound = LoadSound("resources/audio/coin.wav");
camera = new Camera3D
{
Position = new Vector3(0, 5, 5),
Target = new Vector3(0, 0, 0),
Up = new Vector3(0, 1, 0),
FovY = 60,
Projection = CameraProjection.Perspective
};
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Free);
float th = (float)GetTime();
Vector3 spherePos = new Vector3(
5.0f * MathF.Cos(th),
0.0f,
5.0f * MathF.Sin(th)
);
SetSoundPosition(camera, sound, spherePos, 1.0f);
if (!IsSoundPlaying(sound))
{
PlaySound(sound);
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawGrid(10, 2);
DrawSphere(spherePos, 0.5f, Color.Red);
EndMode3D();
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadSound(sound);
CloseAudioDevice(); // Close audio device
}
//------------------------------------------------------------------------------------
// Module Functions Definition
//------------------------------------------------------------------------------------
// Set sound 3d position
private static void SetSoundPosition(Camera3D listener, Sound sound, Vector3 position, float maxDist)
{
// Calculate direction vector and distance between listener and sound source
Vector3 direction = Vector3.Subtract(position, listener.Position);
float distance = direction.Length();
// Apply logarithmic distance attenuation and clamp between 0-1
float attenuation = 1.0f / (1.0f + (distance / maxDist));
attenuation = Math.Clamp(attenuation, 0.0f, 1.0f);
// Calculate normalized vectors for spatial positioning
Vector3 normalizedDirection = Vector3.Normalize(direction);
Vector3 forward = Vector3.Normalize(Vector3.Subtract(listener.Target, listener.Position));
Vector3 right = Vector3.Normalize(Vector3.Cross(listener.Up, forward));
// Reduce volume for sounds behind the listener
float dotProduct = Vector3.Dot(forward, normalizedDirection);
if (dotProduct < 0.0f)
{
attenuation *= (1.0f + dotProduct * 0.5f);
}
// Set stereo panning based on sound position relative to listener
float pan = 0.5f + 0.5f * Vector3.Dot(normalizedDirection, right);
// Apply final sound properties
SetSoundVolume(sound, attenuation);
SetSoundPan(sound, pan);
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [audio] example - sound positioning");
DisableCursor();
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
var game = new SoundPositioning();
game.Init();
// Main game loop
while (!WindowShouldClose())
{
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}
}

View file

@ -0,0 +1,369 @@
/*******************************************************************************************
*
* raylib [audio] example - stream callback
*
* Example complexity rating: [★★★☆] 3/4
*
* Example originally created with raylib 6.0, last time updated with raylib 6.0
*
* Example created by Dan Hoang (@dan-hoang) and reviewed by Ramon Santamaria (@raysan5)
*
* NOTE: Example sends a wave to the audio device,
* user gets the choice of four waves: sine, square, triangle, and sawtooth
* A stream is set up to play to the audio device; stream is hooked to a callback that
* generates a wave, that is determined by user choice
*
* 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) 2026 Dan Hoang (@dan-hoang)
*
********************************************************************************************/
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace Examples.Audio;
[ExcludeFromBrowser("SetAudioStreamCallback is unreliable on the wasm audio backend")]
public unsafe partial class StreamCallback : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
private const int BUFFER_SIZE = 4096;
private const int SAMPLE_RATE = 44100;
// Wave type
private enum WaveType
{
Sine,
Square,
Triangle,
Sawtooth
}
public string Name => "Audio / Stream Callback";
public string Title => "raylib [audio] example - stream callback";
public int TargetFps => 30;
private static int waveFrequency = 440;
private static int newWaveFrequency = 440;
private static int waveIndex = 0;
// Buffer to keep the last second of uploaded audio,
// part of which will be drawn on the screen
private static readonly float[] buffer = new float[SAMPLE_RATE];
private static readonly string[] waveTypesAsString = { "sine", "square", "triangle", "sawtooth" };
private AudioStream stream;
private WaveType waveType;
public void Init()
{
InitAudioDevice();
waveFrequency = 440;
newWaveFrequency = 440;
waveIndex = 0;
Array.Clear(buffer, 0, buffer.Length);
// Set the number of samples the stream will keep in memory at a time to BUFFER_SIZE
SetAudioStreamBufferSizeDefault(BUFFER_SIZE);
// Init raw audio stream (sample rate: 44100, sample size: 32bit-float, channels: 1-mono)
stream = LoadAudioStream(SAMPLE_RATE, 32, 1);
PlayAudioStream(stream);
// Configure it so that the callback for waveType is called whenever stream is out of samples
waveType = WaveType.Sine;
SetWaveCallback();
}
// Attach the callback matching the current waveType to the stream
private void SetWaveCallback()
{
switch (waveType)
{
case WaveType.Sine:
SetAudioStreamCallback(stream, &SineCallback);
break;
case WaveType.Square:
SetAudioStreamCallback(stream, &SquareCallback);
break;
case WaveType.Triangle:
SetAudioStreamCallback(stream, &TriangleCallback);
break;
case WaveType.Sawtooth:
SetAudioStreamCallback(stream, &SawtoothCallback);
break;
}
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
if (IsKeyDown(KeyboardKey.Up))
{
newWaveFrequency += 10;
if (newWaveFrequency > 12500)
{
newWaveFrequency = 12500;
}
}
if (IsKeyDown(KeyboardKey.Down))
{
newWaveFrequency -= 10;
if (newWaveFrequency < 20)
{
newWaveFrequency = 20;
}
}
if (IsKeyPressed(KeyboardKey.Left))
{
if (waveType == WaveType.Sine)
{
waveType = WaveType.Sawtooth;
}
else if (waveType == WaveType.Square)
{
waveType = WaveType.Sine;
}
else if (waveType == WaveType.Triangle)
{
waveType = WaveType.Square;
}
else
{
waveType = WaveType.Triangle;
}
SetWaveCallback();
}
if (IsKeyPressed(KeyboardKey.Right))
{
if (waveType == WaveType.Sine)
{
waveType = WaveType.Square;
}
else if (waveType == WaveType.Square)
{
waveType = WaveType.Triangle;
}
else if (waveType == WaveType.Triangle)
{
waveType = WaveType.Sawtooth;
}
else
{
waveType = WaveType.Sine;
}
SetWaveCallback();
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
DrawText($"frequency: {newWaveFrequency}", screenWidth - 220, 10, 20, Color.Red);
DrawText($"wave type: {waveTypesAsString[(int)waveType]}", screenWidth - 220, 30, 20, Color.Red);
DrawText("Up/down to change frequency", 10, 10, 20, Color.DarkGray);
DrawText("Left/right to change wave type", 10, 30, 20, Color.DarkGray);
// Draw the last 10 ms of uploaded audio
for (int i = 0; i < screenWidth; i++)
{
Vector2 startPos = new Vector2(i, 250 - 50 * buffer[WaveSampleIndex(i)]);
Vector2 endPos = new Vector2(i + 1, 250 - 50 * buffer[WaveSampleIndex(i + 1)]);
DrawLineV(startPos, endPos, Color.Red);
}
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadAudioStream(stream); // Close raw audio stream and delete buffers from RAM
CloseAudioDevice(); // Close audio device (music streaming is automatically stopped)
}
// Maps a screen column to a sample index in the last 10 ms of the buffer. The final column
// maps one sample past the end of the buffer; upstream C reads out of bounds there, so we
// clamp to the last valid sample (visually identical, but safe under C# bounds checking).
private static int WaveSampleIndex(int column)
{
int index = SAMPLE_RATE - SAMPLE_RATE / 100 + column * SAMPLE_RATE / 100 / screenWidth;
return index < SAMPLE_RATE ? index : SAMPLE_RATE - 1;
}
//------------------------------------------------------------------------------------
// Module Functions Definition
//------------------------------------------------------------------------------------
[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
private static void SineCallback(void* framesOut, uint frameCount)
{
int fc = (int)frameCount;
int wavelength = SAMPLE_RATE / waveFrequency;
float* frames = (float*)framesOut;
// Synthesize the sine wave
for (int i = 0; i < fc; i++)
{
frames[i] = MathF.Sin(2 * MathF.PI * waveIndex / wavelength);
waveIndex++;
if (waveIndex >= wavelength)
{
waveFrequency = newWaveFrequency;
waveIndex = 0;
}
}
// Save the synthesized samples for later drawing
for (int i = 0; i < SAMPLE_RATE - fc; i++)
{
buffer[i] = buffer[i + fc];
}
for (int i = 0; i < fc; i++)
{
buffer[SAMPLE_RATE - fc + i] = frames[i];
}
}
[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
private static void SquareCallback(void* framesOut, uint frameCount)
{
int fc = (int)frameCount;
int wavelength = SAMPLE_RATE / waveFrequency;
float* frames = (float*)framesOut;
// Synthesize the square wave
for (int i = 0; i < fc; i++)
{
frames[i] = (waveIndex < wavelength / 2) ? 1 : -1;
waveIndex++;
if (waveIndex >= wavelength)
{
waveFrequency = newWaveFrequency;
waveIndex = 0;
}
}
// Save the synthesized samples for later drawing
for (int i = 0; i < SAMPLE_RATE - fc; i++)
{
buffer[i] = buffer[i + fc];
}
for (int i = 0; i < fc; i++)
{
buffer[SAMPLE_RATE - fc + i] = frames[i];
}
}
[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
private static void TriangleCallback(void* framesOut, uint frameCount)
{
int fc = (int)frameCount;
int wavelength = SAMPLE_RATE / waveFrequency;
float* frames = (float*)framesOut;
// Synthesize the triangle wave
for (int i = 0; i < fc; i++)
{
frames[i] = (waveIndex < wavelength / 2) ? (-1 + 2.0f * waveIndex / (wavelength / 2)) : (1 - 2.0f * (waveIndex - wavelength / 2) / (wavelength / 2));
waveIndex++;
if (waveIndex >= wavelength)
{
waveFrequency = newWaveFrequency;
waveIndex = 0;
}
}
// Save the synthesized samples for later drawing
for (int i = 0; i < SAMPLE_RATE - fc; i++)
{
buffer[i] = buffer[i + fc];
}
for (int i = 0; i < fc; i++)
{
buffer[SAMPLE_RATE - fc + i] = frames[i];
}
}
[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
private static void SawtoothCallback(void* framesOut, uint frameCount)
{
int fc = (int)frameCount;
int wavelength = SAMPLE_RATE / waveFrequency;
float* frames = (float*)framesOut;
// Synthesize the sawtooth wave
for (int i = 0; i < fc; i++)
{
frames[i] = -1 + 2.0f * waveIndex / wavelength;
waveIndex++;
if (waveIndex >= wavelength)
{
waveFrequency = newWaveFrequency;
waveIndex = 0;
}
}
// Save the synthesized samples for later drawing
for (int i = 0; i < SAMPLE_RATE - fc; i++)
{
buffer[i] = buffer[i + fc];
}
for (int i = 0; i < fc; i++)
{
buffer[SAMPLE_RATE - fc + i] = frames[i];
}
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [audio] example - stream callback");
SetTargetFPS(30);
//--------------------------------------------------------------------------------------
var game = new StreamCallback();
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,248 @@
/*******************************************************************************************
*
* raylib [audio] example - stream effects
*
* Example complexity rating: [★★★★] 4/4
*
* Example originally created with raylib 4.2, last time updated with raylib 5.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) 2022-2025 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace Examples.Audio;
[ExcludeFromBrowser("AttachAudioStreamProcessor is unreliable on the wasm audio backend")]
public unsafe partial class StreamEffects : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Audio / Stream Effects";
public string Title => "raylib [audio] example - stream effects";
//----------------------------------------------------------------------------------
// Global Variables Definition
//----------------------------------------------------------------------------------
private static float[] delayBuffer = null;
private static uint delayBufferSize = 0;
private static uint delayReadIndex = 2;
private static uint delayWriteIndex = 0;
// Low-pass filter state (was a function-local static in the C example)
private static readonly float[] low = { 0.0f, 0.0f };
private Music music;
private float timePlayed;
private bool pause;
private bool enableEffectLPF;
private bool enableEffectDelay;
public void Init()
{
InitAudioDevice(); // Initialize audio device
music = LoadMusicStream("resources/audio/country.mp3");
// Allocate buffer for the delay effect
delayBufferSize = 48000 * 2; // 1 second delay (device sampleRate*channels)
delayBuffer = new float[delayBufferSize];
delayReadIndex = 2;
delayWriteIndex = 0;
low[0] = 0.0f;
low[1] = 0.0f;
PlayMusicStream(music);
timePlayed = 0.0f; // Time played normalized [0.0f..1.0f]
pause = false; // Music playing paused
enableEffectLPF = false; // Enable effect low-pass-filter
enableEffectDelay = false; // Enable effect delay (1 second)
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
UpdateMusicStream(music); // Update music buffer with new stream data
// Restart music playing (stop and play)
if (IsKeyPressed(KeyboardKey.Space))
{
StopMusicStream(music);
PlayMusicStream(music);
}
// Pause/Resume music playing
if (IsKeyPressed(KeyboardKey.P))
{
pause = !pause;
if (pause)
{
PauseMusicStream(music);
}
else
{
ResumeMusicStream(music);
}
}
// Add/Remove effect: lowpass filter
if (IsKeyPressed(KeyboardKey.F))
{
enableEffectLPF = !enableEffectLPF;
if (enableEffectLPF)
{
AttachAudioStreamProcessor(music.Stream, &AudioProcessEffectLPF);
}
else
{
DetachAudioStreamProcessor(music.Stream, &AudioProcessEffectLPF);
}
}
// Add/Remove effect: delay
if (IsKeyPressed(KeyboardKey.D))
{
enableEffectDelay = !enableEffectDelay;
if (enableEffectDelay)
{
AttachAudioStreamProcessor(music.Stream, &AudioProcessEffectDelay);
}
else
{
DetachAudioStreamProcessor(music.Stream, &AudioProcessEffectDelay);
}
}
// Get normalized time played for current music stream
timePlayed = GetMusicTimePlayed(music) / GetMusicTimeLength(music);
if (timePlayed > 1.0f)
{
timePlayed = 1.0f; // Make sure time played is no longer than music
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
DrawText("MUSIC SHOULD BE PLAYING!", 245, 150, 20, Color.LightGray);
DrawRectangle(200, 180, 400, 12, Color.LightGray);
DrawRectangle(200, 180, (int)(timePlayed * 400.0f), 12, Color.Maroon);
DrawRectangleLines(200, 180, 400, 12, Color.Gray);
DrawText("PRESS SPACE TO RESTART MUSIC", 215, 230, 20, Color.LightGray);
DrawText("PRESS P TO PAUSE/RESUME MUSIC", 208, 260, 20, Color.LightGray);
DrawText($"PRESS F TO TOGGLE LPF EFFECT: {(enableEffectLPF ? "ON" : "OFF")}", 200, 320, 20, Color.Gray);
DrawText($"PRESS D TO TOGGLE DELAY EFFECT: {(enableEffectDelay ? "ON" : "OFF")}", 180, 350, 20, Color.Gray);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadMusicStream(music); // Unload music stream buffers from RAM
CloseAudioDevice(); // Close audio device (music streaming is automatically stopped)
delayBuffer = null; // Free delay buffer
}
//------------------------------------------------------------------------------------
// Module Functions Definition
//------------------------------------------------------------------------------------
// Audio effect: lowpass filter
[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
private static void AudioProcessEffectLPF(void* buffer, uint frames)
{
const float cutoff = 70.0f / 44100.0f; // 70 Hz lowpass filter
const float k = cutoff / (cutoff + 0.1591549431f); // RC filter formula
// Converts the buffer data before using it
float* bufferData = (float*)buffer;
for (uint i = 0; i < frames * 2; i += 2)
{
float l = bufferData[i];
float r = bufferData[i + 1];
low[0] += k * (l - low[0]);
low[1] += k * (r - low[1]);
bufferData[i] = low[0];
bufferData[i + 1] = low[1];
}
}
// Audio effect: delay
[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
private static void AudioProcessEffectDelay(void* buffer, uint frames)
{
float* bufferData = (float*)buffer;
fixed (float* delay = delayBuffer)
{
for (uint i = 0; i < frames * 2; i += 2)
{
float leftDelay = delay[delayReadIndex++]; // ERROR: Reading buffer -> WHY??? Maybe thread related???
float rightDelay = delay[delayReadIndex++];
if (delayReadIndex == delayBufferSize)
{
delayReadIndex = 0;
}
bufferData[i] = 0.5f * bufferData[i] + 0.5f * leftDelay;
bufferData[i + 1] = 0.5f * bufferData[i + 1] + 0.5f * rightDelay;
delay[delayWriteIndex++] = bufferData[i];
delay[delayWriteIndex++] = bufferData[i + 1];
if (delayWriteIndex == delayBufferSize)
{
delayWriteIndex = 0;
}
}
}
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [audio] example - stream effects");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new StreamEffects();
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,419 @@
/*******************************************************************************************
*
* raylib [core] example - automation events
*
* Example complexity rating: [★★★☆] 3/4
*
* Example originally created with raylib 5.0, last time updated with raylib 5.0
*
* Example based on 2d_camera_platformer example by arvyy (@arvyy)
*
* 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) 2023-2025 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
namespace Examples.Core;
public partial class AutomationEvents : IExample
{
private const int GRAVITY = 400;
private const float PLAYER_JUMP_SPD = 350.0f;
private const float PLAYER_HOR_SPD = 200.0f;
private const int MAX_ENVIRONMENT_ELEMENTS = 5;
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Core / Automation Events";
public string Title => "raylib [core] example - automation events";
private struct Player
{
public Vector2 Position;
public float Speed;
public bool CanJump;
}
private struct EnvElement
{
public Rectangle Rect;
public int Blocking;
public Color Color;
}
private Player player;
private EnvElement[] envElements;
private Camera2D camera;
private AutomationEventList aelist;
private bool eventRecording;
private bool eventPlaying;
private uint frameCounter;
private uint playFrameCounter;
private uint currentPlayFrame;
public unsafe void Init()
{
// Define player
player = new Player();
player.Position = new Vector2(400, 280);
player.Speed = 0;
player.CanJump = false;
// Define environment elements (platforms)
envElements = new EnvElement[MAX_ENVIRONMENT_ELEMENTS]
{
new EnvElement { Rect = new Rectangle(0, 0, 1000, 400), Blocking = 0, Color = Color.LightGray },
new EnvElement { Rect = new Rectangle(0, 400, 1000, 200), Blocking = 1, Color = Color.Gray },
new EnvElement { Rect = new Rectangle(300, 200, 400, 10), Blocking = 1, Color = Color.Gray },
new EnvElement { Rect = new Rectangle(250, 300, 100, 10), Blocking = 1, Color = Color.Gray },
new EnvElement { Rect = new Rectangle(650, 300, 100, 10), Blocking = 1, Color = Color.Gray }
};
// Define camera
camera = new Camera2D();
camera.Target = player.Position;
camera.Offset = new Vector2(screenWidth / 2.0f, screenHeight / 2.0f);
camera.Rotation = 0.0f;
camera.Zoom = 1.0f;
// Automation events
aelist = LoadAutomationEventList((sbyte*)null); // Initialize list of automation events to record new events
SetAutomationEventList(ref aelist);
eventRecording = false;
eventPlaying = false;
frameCounter = 0;
playFrameCounter = 0;
currentPlayFrame = 0;
}
public unsafe void Update()
{
// Update
//----------------------------------------------------------------------------------
float deltaTime = 0.015f;//GetFrameTime();
// Dropped files logic
//----------------------------------------------------------------------------------
#if BROWSER
// NOTE: drag-and-drop event-list loading (.txt/.rae) is not supported in the browser host.
#else
if (IsFileDropped())
{
FilePathList droppedFiles = LoadDroppedFiles();
// Supports loading .rgs style files (text or binary) and .png style palette images
if (IsFileExtension(droppedFiles[0], ".txt;.rae"))
{
UnloadAutomationEventList(aelist);
aelist = LoadAutomationEventList(droppedFiles[0]);
eventRecording = false;
// Reset scene state to play
eventPlaying = true;
playFrameCounter = 0;
currentPlayFrame = 0;
player.Position = new Vector2(400, 280);
player.Speed = 0;
player.CanJump = false;
camera.Target = player.Position;
camera.Offset = new Vector2(screenWidth / 2.0f, screenHeight / 2.0f);
camera.Rotation = 0.0f;
camera.Zoom = 1.0f;
}
UnloadDroppedFiles(droppedFiles); // Unload filepaths from memory
}
#endif
//----------------------------------------------------------------------------------
// Update player
//----------------------------------------------------------------------------------
if (IsKeyDown(KeyboardKey.Left))
{
player.Position.X -= PLAYER_HOR_SPD * deltaTime;
}
if (IsKeyDown(KeyboardKey.Right))
{
player.Position.X += PLAYER_HOR_SPD * deltaTime;
}
if (IsKeyDown(KeyboardKey.Space) && player.CanJump)
{
player.Speed = -PLAYER_JUMP_SPD;
player.CanJump = false;
}
int hitObstacle = 0;
for (int i = 0; i < MAX_ENVIRONMENT_ELEMENTS; i++)
{
EnvElement element = envElements[i];
if (element.Blocking != 0 &&
element.Rect.X <= player.Position.X &&
element.Rect.X + element.Rect.Width >= player.Position.X &&
element.Rect.Y >= player.Position.Y &&
element.Rect.Y <= player.Position.Y + player.Speed * deltaTime)
{
hitObstacle = 1;
player.Speed = 0.0f;
player.Position.Y = element.Rect.Y;
}
}
if (hitObstacle == 0)
{
player.Position.Y += player.Speed * deltaTime;
player.Speed += GRAVITY * deltaTime;
player.CanJump = false;
}
else
{
player.CanJump = true;
}
if (IsKeyPressed(KeyboardKey.R))
{
// Reset game state
player.Position = new Vector2(400, 280);
player.Speed = 0;
player.CanJump = false;
camera.Target = player.Position;
camera.Offset = new Vector2(screenWidth / 2.0f, screenHeight / 2.0f);
camera.Rotation = 0.0f;
camera.Zoom = 1.0f;
}
//----------------------------------------------------------------------------------
// Events playing
// NOTE: Logic must be before Camera update because it depends on mouse-wheel value,
// that can be set by the played event... but some other inputs could be affected
//----------------------------------------------------------------------------------
if (eventPlaying)
{
// NOTE: Multiple events could be executed in a single frame
while (playFrameCounter == aelist.Events[currentPlayFrame].Frame)
{
PlayAutomationEvent(aelist.Events[currentPlayFrame]);
currentPlayFrame++;
if (currentPlayFrame == aelist.Count)
{
eventPlaying = false;
currentPlayFrame = 0;
playFrameCounter = 0;
TraceLog(TraceLogLevel.Info, "FINISH PLAYING!");
break;
}
}
playFrameCounter++;
}
//----------------------------------------------------------------------------------
// Update camera
//----------------------------------------------------------------------------------
camera.Target = player.Position;
camera.Offset = new Vector2(screenWidth / 2.0f, screenHeight / 2.0f);
float minX = 1000, minY = 1000, maxX = -1000, maxY = -1000;
// WARNING: On event replay, mouse-wheel internal value is set
camera.Zoom += ((float)GetMouseWheelMove() * 0.05f);
if (camera.Zoom > 3.0f)
{
camera.Zoom = 3.0f;
}
else if (camera.Zoom < 0.25f)
{
camera.Zoom = 0.25f;
}
for (int i = 0; i < MAX_ENVIRONMENT_ELEMENTS; i++)
{
EnvElement element = envElements[i];
minX = MathF.Min(element.Rect.X, minX);
maxX = MathF.Max(element.Rect.X + element.Rect.Width, maxX);
minY = MathF.Min(element.Rect.Y, minY);
maxY = MathF.Max(element.Rect.Y + element.Rect.Height, maxY);
}
Vector2 max = GetWorldToScreen2D(new Vector2(maxX, maxY), camera);
Vector2 min = GetWorldToScreen2D(new Vector2(minX, minY), camera);
if (max.X < screenWidth)
{
camera.Offset.X = screenWidth - (max.X - (float)screenWidth / 2);
}
if (max.Y < screenHeight)
{
camera.Offset.Y = screenHeight - (max.Y - (float)screenHeight / 2);
}
if (min.X > 0)
{
camera.Offset.X = (float)screenWidth / 2 - min.X;
}
if (min.Y > 0)
{
camera.Offset.Y = (float)screenHeight / 2 - min.Y;
}
//----------------------------------------------------------------------------------
// Events management
if (IsKeyPressed(KeyboardKey.S)) // Toggle events recording
{
if (!eventPlaying)
{
if (eventRecording)
{
StopAutomationEventRecording();
eventRecording = false;
ExportAutomationEventList(aelist, "automation.rae");
TraceLog(TraceLogLevel.Info, $"RECORDED FRAMES: {aelist.Count}");
}
else
{
SetAutomationEventBaseFrame(180);
StartAutomationEventRecording();
eventRecording = true;
}
}
}
else if (IsKeyPressed(KeyboardKey.A)) // Toggle events playing (WARNING: Starts next frame)
{
if (!eventRecording && (aelist.Count > 0))
{
// Reset scene state to play
eventPlaying = true;
playFrameCounter = 0;
currentPlayFrame = 0;
player.Position = new Vector2(400, 280);
player.Speed = 0;
player.CanJump = false;
camera.Target = player.Position;
camera.Offset = new Vector2(screenWidth / 2.0f, screenHeight / 2.0f);
camera.Rotation = 0.0f;
camera.Zoom = 1.0f;
}
}
if (eventRecording || eventPlaying)
{
frameCounter++;
}
else
{
frameCounter = 0;
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.LightGray);
BeginMode2D(camera);
// Draw environment elements
for (int i = 0; i < MAX_ENVIRONMENT_ELEMENTS; i++)
{
DrawRectangleRec(envElements[i].Rect, envElements[i].Color);
}
// Draw player rectangle
DrawRectangleRec(new Rectangle(player.Position.X - 20, player.Position.Y - 40, 40, 40), Color.Red);
EndMode2D();
// Draw game controls
DrawRectangle(10, 10, 290, 145, Fade(Color.SkyBlue, 0.5f));
DrawRectangleLines(10, 10, 290, 145, Fade(Color.Blue, 0.8f));
DrawText("Controls:", 20, 20, 10, Color.Black);
DrawText("- RIGHT | LEFT: Player movement", 30, 40, 10, Color.DarkGray);
DrawText("- SPACE: Player jump", 30, 60, 10, Color.DarkGray);
DrawText("- R: Reset game state", 30, 80, 10, Color.DarkGray);
DrawText("- S: START/STOP RECORDING INPUT EVENTS", 30, 110, 10, Color.Black);
DrawText("- A: REPLAY LAST RECORDED INPUT EVENTS", 30, 130, 10, Color.Black);
// Draw automation events recording indicator
if (eventRecording)
{
DrawRectangle(10, 160, 290, 30, Fade(Color.Red, 0.3f));
DrawRectangleLines(10, 160, 290, 30, Fade(Color.Maroon, 0.8f));
DrawCircle(30, 175, 10, Color.Maroon);
if (((frameCounter / 15) % 2) == 1)
{
DrawText($"RECORDING EVENTS... [{aelist.Count}]", 50, 170, 10, Color.Maroon);
}
}
else if (eventPlaying)
{
DrawRectangle(10, 160, 290, 30, Fade(Color.Lime, 0.3f));
DrawRectangleLines(10, 160, 290, 30, Fade(Color.DarkGreen, 0.8f));
DrawTriangle(new Vector2(20, 155 + 10), new Vector2(20, 155 + 30), new Vector2(40, 155 + 20), Color.DarkGreen);
if (((frameCounter / 15) % 2) == 1)
{
DrawText($"PLAYING RECORDED EVENTS... [{currentPlayFrame}]", 50, 170, 10, Color.DarkGreen);
}
}
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadAutomationEventList(aelist);
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [core] example - automation events");
var game = new AutomationEvents();
game.Init();
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
// Main game loop
while (!WindowShouldClose())
{
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}
}

View file

@ -1,29 +1,23 @@
/*******************************************************************************************
*
* raylib [core] example - Basic window
* raylib [core] example - basic screen manager
*
* Welcome to raylib!
* Example complexity rating: [★☆☆☆] 1/4
*
* To test examples, just press F6 and execute raylib_compile_execute script
* Note that compiled executable is placed in the same folder as .c file
* NOTE: This example illustrates a very simple screen manager based on a states machines
*
* You can find all basic examples on C:\raylib\raylib\examples folder or
* raylib official webpage: www.raylib.com
* Example originally created with raylib 4.0, last time updated with raylib 4.0
*
* Enjoy using raylib. :)
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
* BSD-like license that allows static linking with closed source software
*
* 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)
*
* Copyright (c) 2013-2016 Ramon Santamaria (@raysan5)
* Copyright (c) 2021-2025 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
using static Raylib_cs.Raylib;
namespace Examples.Core;
enum GameScreen
internal enum GameScreen
{
Logo = 0,
Title,
@ -31,143 +25,160 @@ enum GameScreen
Ending
}
public class BasicScreenManager
public partial class BasicScreenManager : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Core / Basic Screen Manager";
public string Title => "raylib [core] example - basic screen manager";
private GameScreen currentScreen;
private int framesCounter;
public void Init()
{
currentScreen = GameScreen.Logo;
// TODO: Initialize all required variables and load all required data here!
framesCounter = 0; // Useful to count frames
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
switch (currentScreen)
{
case GameScreen.Logo:
{
// TODO: Update LOGO screen variables here!
framesCounter++; // Count frames
// Wait for 2 seconds (120 frames) before jumping to TITLE screen
if (framesCounter > 120)
{
currentScreen = GameScreen.Title;
}
}
break;
case GameScreen.Title:
{
// TODO: Update TITLE screen variables here!
// Press enter to change to GAMEPLAY screen
if (IsKeyPressed(KeyboardKey.Enter) || IsGestureDetected(Gesture.Tap))
{
currentScreen = GameScreen.Gameplay;
}
}
break;
case GameScreen.Gameplay:
{
// TODO: Update GAMEPLAY screen variables here!
// Press enter to change to ENDING screen
if (IsKeyPressed(KeyboardKey.Enter) || IsGestureDetected(Gesture.Tap))
{
currentScreen = GameScreen.Ending;
}
}
break;
case GameScreen.Ending:
{
// TODO: Update ENDING screen variables here!
// Press enter to return to TITLE screen
if (IsKeyPressed(KeyboardKey.Enter) || IsGestureDetected(Gesture.Tap))
{
currentScreen = GameScreen.Title;
}
}
break;
default:
break;
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
switch (currentScreen)
{
case GameScreen.Logo:
{
// TODO: Draw LOGO screen here!
DrawText("LOGO SCREEN", 20, 20, 40, Color.LightGray);
DrawText("WAIT for 2 SECONDS...", 290, 220, 20, Color.Gray);
}
break;
case GameScreen.Title:
{
// TODO: Draw TITLE screen here!
DrawRectangle(0, 0, screenWidth, screenHeight, Color.Green);
DrawText("TITLE SCREEN", 20, 20, 40, Color.DarkGreen);
DrawText("PRESS ENTER or TAP to JUMP to GAMEPLAY SCREEN", 120, 220, 20, Color.DarkGreen);
}
break;
case GameScreen.Gameplay:
{
// TODO: Draw GAMEPLAY screen here!
DrawRectangle(0, 0, screenWidth, screenHeight, Color.Purple);
DrawText("GAMEPLAY SCREEN", 20, 20, 40, Color.Maroon);
DrawText("PRESS ENTER or TAP to JUMP to ENDING SCREEN", 130, 220, 20, Color.Maroon);
}
break;
case GameScreen.Ending:
{
// TODO: Draw ENDING screen here!
DrawRectangle(0, 0, screenWidth, screenHeight, Color.Blue);
DrawText("ENDING SCREEN", 20, 20, 40, Color.DarkBlue);
DrawText("PRESS ENTER or TAP to RETURN to TITLE SCREEN", 120, 220, 20, Color.DarkBlue);
}
break;
default:
break;
}
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
// TODO: Unload all loaded data (textures, fonts, audio) here!
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [core] example - basic screen manager");
GameScreen currentScreen = GameScreen.Logo;
// TODO: Initialize all required variables and load all required data here!
// Useful to count frames
int framesCounter = 0;
SetTargetFPS(60); // Set desired framerate (frames-per-second)
//--------------------------------------------------------------------------------------
var game = new BasicScreenManager();
game.Init();
// Main game loop
while (!WindowShouldClose())
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
switch (currentScreen)
{
case GameScreen.Logo:
{
// TODO: Update LOGO screen variables here!
// Count frames
framesCounter++;
// Wait for 2 seconds (120 frames) before jumping to TITLE screen
if (framesCounter > 120)
{
currentScreen = GameScreen.Title;
}
}
break;
case GameScreen.Title:
{
// TODO: Update TITLE screen variables here!
// Press enter to change to GAMEPLAY screen
if (IsKeyPressed(KeyboardKey.Enter) || IsGestureDetected(Gesture.Tap))
{
currentScreen = GameScreen.Gameplay;
}
}
break;
case GameScreen.Gameplay:
{
// TODO: Update GAMEPLAY screen variables here!
// Press enter to change to ENDING screen
if (IsKeyPressed(KeyboardKey.Enter) || IsGestureDetected(Gesture.Tap))
{
currentScreen = GameScreen.Ending;
}
}
break;
case GameScreen.Ending:
{
// TODO: Update ENDING screen variables here!
// Press enter to return to TITLE screen
if (IsKeyPressed(KeyboardKey.Enter) || IsGestureDetected(Gesture.Tap))
{
currentScreen = GameScreen.Title;
}
}
break;
default:
break;
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
switch (currentScreen)
{
case GameScreen.Logo:
{
// TODO: Draw LOGO screen here!
DrawText("LOGO SCREEN", 20, 20, 40, Color.LightGray);
DrawText("WAIT for 2 SECONDS...", 290, 220, 20, Color.Gray);
}
break;
case GameScreen.Title:
{
// TODO: Draw TITLE screen here!
DrawRectangle(0, 0, screenWidth, screenHeight, Color.Green);
DrawText("TITLE SCREEN", 20, 20, 40, Color.DarkGreen);
DrawText("PRESS ENTER or TAP to JUMP to GAMEPLAY SCREEN", 120, 220, 20, Color.DarkGreen);
}
break;
case GameScreen.Gameplay:
{
// TODO: Draw GAMEPLAY screen here!
DrawRectangle(0, 0, screenWidth, screenHeight, Color.Purple);
DrawText("GAMEPLAY SCREEN", 20, 20, 40, Color.Maroon);
DrawText("PRESS ENTER or TAP to JUMP to ENDING SCREEN", 130, 220, 20, Color.Maroon);
}
break;
case GameScreen.Ending:
{
// TODO: Draw ENDING screen here!
DrawRectangle(0, 0, screenWidth, screenHeight, Color.Blue);
DrawText("ENDING SCREEN", 20, 20, 40, Color.DarkBlue);
DrawText("PRESS ENTER or TAP to RETURN to TITLE SCREEN", 120, 220, 20, Color.DarkBlue);
}
break;
default:
break;
}
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
// TODO: Unload all loaded data (textures, fonts, audio) here!
CloseWindow();
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;

View file

@ -26,50 +26,58 @@
*
********************************************************************************************/
using System.Numerics;
using System;
using System.Text;
using static Raylib_cs.Raylib;
namespace Examples.Core;
public class BasicWindow
public partial class BasicWindow : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Core / Basic Window";
public string Title => "raylib [core] example - basic window";
public void Init()
{
}
public void Update()
{
BeginDrawing();
ClearBackground(Color.RayWhite);
DrawText("Congrats! You created your first window!", 190, 200, 20, Color.LightGray);
EndDrawing();
}
public void Unload()
{
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [core] example - basic window");
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
var game = new BasicWindow();
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("Congrats! You created your first window!", 190, 200, 20, Color.LightGray);
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow();
//--------------------------------------------------------------------------------------
CloseWindow(); // Close window and OpenGL context
return 0;
}

View file

@ -13,31 +13,33 @@
*
********************************************************************************************/
using System.Numerics;
using static Raylib_cs.Raylib;
namespace Examples.Core;
public class Camera2dDemo
public partial class Camera2dDemo : IExample
{
public const int MaxBuildings = 100;
public static int Main()
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Core / Camera 2D Demo";
public string Title => "raylib [core] example - 2d camera";
private Rectangle player;
private Rectangle[] buildings;
private Color[] buildColors;
private Camera2D camera;
public void Init()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
player = new(400, 280, 40, 40);
buildings = new Rectangle[MaxBuildings];
buildColors = new Color[MaxBuildings];
InitWindow(screenWidth, screenHeight, "raylib [core] example - 2d camera");
var spacing = 0;
Rectangle player = new(400, 280, 40, 40);
Rectangle[] buildings = new Rectangle[MaxBuildings];
Color[] buildColors = new Color[MaxBuildings];
int spacing = 0;
for (int i = 0; i < MaxBuildings; i++)
for (var i = 0; i < MaxBuildings; i++)
{
buildings[i].Width = GetRandomValue(50, 200);
buildings[i].Height = GetRandomValue(100, 800);
@ -54,124 +56,139 @@ public class Camera2dDemo
);
}
Camera2D camera = new();
camera = new();
camera.Target = new Vector2(player.X + 20, player.Y + 20);
camera.Offset = new Vector2(screenWidth / 2, screenHeight / 2);
camera.Offset = new Vector2(screenWidth / 2.0f, screenHeight / 2.0f);
camera.Rotation = 0.0f;
camera.Zoom = 1.0f;
}
SetTargetFPS(60);
public void Update()
{
// Update
//----------------------------------------------------------------------------------
// Player movement
if (IsKeyDown(KeyboardKey.Right))
{
player.X += 2;
}
else if (IsKeyDown(KeyboardKey.Left))
{
player.X -= 2;
}
// Camera target follows player
camera.Target = new Vector2(player.X + 20, player.Y + 20);
// Camera rotation controls
if (IsKeyDown(KeyboardKey.A))
{
camera.Rotation--;
}
else if (IsKeyDown(KeyboardKey.S))
{
camera.Rotation++;
}
// Limit camera rotation to 80 degrees (-40 to 40)
if (camera.Rotation > 40)
{
camera.Rotation = 40;
}
else if (camera.Rotation < -40)
{
camera.Rotation = -40;
}
// Camera zoom controls
// Uses log scaling to provide consistent zoom speed
camera.Zoom = MathF.Exp(MathF.Log(camera.Zoom) + ((float)GetMouseWheelMove() * 0.1f));
if (camera.Zoom > 3.0f)
{
camera.Zoom = 3.0f;
}
else if (camera.Zoom < 0.1f)
{
camera.Zoom = 0.1f;
}
// Camera reset (zoom and rotation)
if (IsKeyPressed(KeyboardKey.R))
{
camera.Zoom = 1.0f;
camera.Rotation = 0.0f;
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode2D(camera);
DrawRectangle(-6000, 320, 13000, 8000, Color.DarkGray);
for (var i = 0; i < MaxBuildings; i++)
{
DrawRectangleRec(buildings[i], buildColors[i]);
}
DrawRectangleRec(player, Color.Red);
DrawLine((int)camera.Target.X, -screenHeight * 10, (int)camera.Target.X, screenHeight * 10, Color.Green);
DrawLine(-screenWidth * 10, (int)camera.Target.Y, screenWidth * 10, (int)camera.Target.Y, Color.Green);
EndMode2D();
DrawText("SCREEN AREA", 640, 10, 20, Color.Red);
DrawRectangle(0, 0, screenWidth, 5, Color.Red);
DrawRectangle(0, 5, 5, screenHeight - 10, Color.Red);
DrawRectangle(screenWidth - 5, 5, 5, screenHeight - 10, Color.Red);
DrawRectangle(0, screenHeight - 5, screenWidth, 5, Color.Red);
DrawRectangle(10, 10, 250, 113, Fade(Color.SkyBlue, 0.5f));
DrawRectangleLines(10, 10, 250, 113, Color.Blue);
DrawText("Free 2D camera controls:", 20, 20, 10, Color.Black);
DrawText("- Right/Left to move player", 40, 40, 10, Color.DarkGray);
DrawText("- Mouse Wheel to Zoom in-out", 40, 60, 10, Color.DarkGray);
DrawText("- A / S to Rotate", 40, 80, 10, Color.DarkGray);
DrawText("- R to reset Zoom and Rotation", 40, 100, 10, Color.DarkGray);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [core] example - 2d camera");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new Camera2dDemo();
game.Init();
// Main game loop
while (!WindowShouldClose())
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
// Player movement
if (IsKeyDown(KeyboardKey.Right))
{
player.X += 2;
}
else if (IsKeyDown(KeyboardKey.Left))
{
player.X -= 2;
}
// Camera3D target follows player
camera.Target = new Vector2(player.X + 20, player.Y + 20);
// Camera3D rotation controls
if (IsKeyDown(KeyboardKey.A))
{
camera.Rotation--;
}
else if (IsKeyDown(KeyboardKey.S))
{
camera.Rotation++;
}
// Limit camera rotation to 80 degrees (-40 to 40)
if (camera.Rotation > 40)
{
camera.Rotation = 40;
}
else if (camera.Rotation < -40)
{
camera.Rotation = -40;
}
// Camera3D zoom controls
camera.Zoom += ((float)GetMouseWheelMove() * 0.05f);
if (camera.Zoom > 3.0f)
{
camera.Zoom = 3.0f;
}
else if (camera.Zoom < 0.1f)
{
camera.Zoom = 0.1f;
}
// Camera3D reset (zoom and rotation)
if (IsKeyPressed(KeyboardKey.R))
{
camera.Zoom = 1.0f;
camera.Rotation = 0.0f;
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode2D(camera);
DrawRectangle(-6000, 320, 13000, 8000, Color.DarkGray);
for (int i = 0; i < MaxBuildings; i++)
{
DrawRectangleRec(buildings[i], buildColors[i]);
}
DrawRectangleRec(player, Color.Red);
DrawRectangle((int)camera.Target.X, -500, 1, (int)(screenHeight * 4), Color.Green);
DrawLine(
(int)(-screenWidth * 10),
(int)camera.Target.Y,
(int)(screenWidth * 10),
(int)camera.Target.Y,
Color.Green
);
EndMode2D();
DrawText("SCREEN AREA", 640, 10, 20, Color.Red);
DrawRectangle(0, 0, (int)screenWidth, 5, Color.Red);
DrawRectangle(0, 5, 5, (int)screenHeight - 10, Color.Red);
DrawRectangle((int)screenWidth - 5, 5, 5, (int)screenHeight - 10, Color.Red);
DrawRectangle(0, (int)screenHeight - 5, (int)screenWidth, 5, Color.Red);
DrawRectangle(10, 10, 250, 113, ColorAlpha(Color.SkyBlue, 0.5f));
DrawRectangleLines(10, 10, 250, 113, Color.Blue);
DrawText("Free 2d camera controls:", 20, 20, 10, Color.Black);
DrawText("- Right/Left to move Offset", 40, 40, 10, Color.DarkGray);
DrawText("- Mouse Wheel to Zoom in-out", 40, 60, 10, Color.DarkGray);
DrawText("- A / S to Rotate", 40, 80, 10, Color.DarkGray);
DrawText("- R to reset Zoom and Rotation", 40, 100, 10, Color.DarkGray);
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow();
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;

View file

@ -0,0 +1,181 @@
/*******************************************************************************************
*
* raylib [core] example - 2d camera mouse zoom
*
* Example complexity rating: [★★☆☆] 2/4
*
* Example originally created with raylib 4.2, last time updated with raylib 4.2
*
* Example contributed by Jeffery Myers (@JeffM2501) 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) 2022-2025 Jeffery Myers (@JeffM2501)
*
********************************************************************************************/
using static Raylib_cs.Raymath;
namespace Examples.Core;
public partial class Camera2dMouseZoom : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Core / Camera 2D Mouse Zoom";
public string Title => "raylib [core] example - 2d camera mouse zoom";
private Camera2D camera;
private int zoomMode; // 0-Mouse Wheel, 1-Mouse Move
public void Init()
{
camera = new Camera2D();
camera.Zoom = 1.0f;
zoomMode = 0;
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
if (IsKeyPressed(KeyboardKey.One))
{
zoomMode = 0;
}
else if (IsKeyPressed(KeyboardKey.Two))
{
zoomMode = 1;
}
// Translate based on mouse right click
if (IsMouseButtonDown(MouseButton.Left))
{
Vector2 delta = GetMouseDelta();
delta = Vector2Scale(delta, -1.0f / camera.Zoom);
camera.Target = Vector2Add(camera.Target, delta);
}
if (zoomMode == 0)
{
// Zoom based on mouse wheel
float wheel = GetMouseWheelMove();
if (wheel != 0)
{
// Get the world point that is under the mouse
Vector2 mouseWorldPos = GetScreenToWorld2D(GetMousePosition(), camera);
// Set the offset to where the mouse is
camera.Offset = GetMousePosition();
// Set the target to match, so that the camera maps the world space point
// under the cursor to the screen space point under the cursor at any zoom
camera.Target = mouseWorldPos;
// Zoom increment
// Uses log scaling to provide consistent zoom speed
float scale = 0.2f * wheel;
camera.Zoom = Clamp(MathF.Exp(MathF.Log(camera.Zoom) + scale), 0.125f, 64.0f);
}
}
else
{
// Zoom based on mouse right click
if (IsMouseButtonPressed(MouseButton.Right))
{
// Get the world point that is under the mouse
Vector2 mouseWorldPos = GetScreenToWorld2D(GetMousePosition(), camera);
// Set the offset to where the mouse is
camera.Offset = GetMousePosition();
// Set the target to match, so that the camera maps the world space point
// under the cursor to the screen space point under the cursor at any zoom
camera.Target = mouseWorldPos;
}
if (IsMouseButtonDown(MouseButton.Right))
{
// Zoom increment
// Uses log scaling to provide consistent zoom speed
float deltaX = GetMouseDelta().X;
float scale = 0.005f * deltaX;
camera.Zoom = Clamp(MathF.Exp(MathF.Log(camera.Zoom) + scale), 0.125f, 64.0f);
}
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode2D(camera);
// Draw the 3d grid, rotated 90 degrees and centered around 0,0
// just so we have something in the XY plane
Rlgl.PushMatrix();
Rlgl.Translatef(0, 25 * 50, 0);
Rlgl.Rotatef(90, 1, 0, 0);
DrawGrid(100, 50);
Rlgl.PopMatrix();
// Draw a reference circle
DrawCircle(GetScreenWidth() / 2, GetScreenHeight() / 2, 50, Color.Maroon);
EndMode2D();
// Draw mouse reference
//Vector2 mousePos = GetWorldToScreen2D(GetMousePosition(), camera)
DrawCircleV(GetMousePosition(), 4, Color.DarkGray);
DrawTextEx(GetFontDefault(), $"[{GetMouseX()}, {GetMouseY()}]",
Vector2Add(GetMousePosition(), new Vector2(-44, -24)), 20, 2, Color.Black);
DrawText("[1][2] Select mouse zoom mode (Wheel or Move)", 20, 20, 20, Color.DarkGray);
if (zoomMode == 0)
{
DrawText("Mouse left button drag to move, mouse wheel to zoom", 20, 50, 20, Color.DarkGray);
}
else
{
DrawText("Mouse left button drag to move, mouse press and move to zoom", 20, 50, 20, Color.DarkGray);
}
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [core] example - 2d camera mouse zoom");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new Camera2dMouseZoom();
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

@ -2,36 +2,44 @@
*
* raylib [core] example - 2d camera platformer
*
* 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: [★★★☆] 3/4
*
* Example originally created with raylib 2.5, last time updated with raylib 3.0
*
* Example contributed by arvyy (@arvyy) and reviewed by Ramon Santamaria (@raysan5)
*
* Copyright (c) 2019 arvyy (@arvyy)
* 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 arvyy (@arvyy)
*
********************************************************************************************/
using System;
using System.Numerics;
using static Raylib_cs.Raylib;
using static Raylib_cs.Raymath;
namespace Examples.Core;
public class Camera2dPlatformer
public partial class Camera2dPlatformer : IExample
{
const int G = 400;
const float PlayerJumpSpeed = 350.0f;
const float PlayerHorSpeed = 200.0f;
private const int screenWidth = 800;
private const int screenHeight = 450;
struct Player
private const int G = 400;
private const float PlayerJumpSpeed = 350.0f;
private const float PlayerHorSpeed = 200.0f;
public string Name => "Core / 2D Camera Platformer";
public string Title => "raylib [core] example - 2d camera platformer";
private struct Player
{
public Vector2 Position;
public float Speed;
public bool CanJump;
}
struct EnvItem
private struct EnvItem
{
public Rectangle Rect;
public int Blocking;
@ -45,7 +53,7 @@ public class Camera2dPlatformer
}
}
delegate void CameraUpdaterCallback(
private delegate void CameraUpdaterCallback(
ref Camera2D camera,
ref Player player,
EnvItem[] envItems,
@ -54,21 +62,22 @@ public class Camera2dPlatformer
int height
);
public static int Main()
private Player player;
private EnvItem[] envItems;
private Camera2D camera;
private CameraUpdaterCallback[] cameraUpdaters;
private int cameraOption;
private int cameraUpdatersLength;
private string[] cameraDescriptions;
public void Init()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [core] example - 2d camera");
Player player = new();
player = new();
player.Position = new Vector2(400, 280);
player.Speed = 0;
player.CanJump = false;
EnvItem[] envItems = new EnvItem[]
envItems = new EnvItem[]
{
new EnvItem(new Rectangle(0, 0, 1000, 400), 0, Color.LightGray),
new EnvItem(new Rectangle(0, 400, 1000, 200), 1, Color.Gray),
@ -77,14 +86,14 @@ public class Camera2dPlatformer
new EnvItem(new Rectangle(650, 300, 100, 10), 1, Color.Gray)
};
Camera2D camera = new();
camera = new();
camera.Target = player.Position;
camera.Offset = new Vector2(screenWidth / 2, screenHeight / 2);
camera.Offset = new Vector2(screenWidth / 2.0f, screenHeight / 2.0f);
camera.Rotation = 0.0f;
camera.Zoom = 1.0f;
// Store callbacks to the multiple update camera functions
CameraUpdaterCallback[] cameraUpdaters = new CameraUpdaterCallback[]
// Store pointers to the multiple update camera functions
cameraUpdaters = new CameraUpdaterCallback[]
{
UpdateCameraCenter,
UpdateCameraCenterInsideMap,
@ -93,93 +102,89 @@ public class Camera2dPlatformer
UpdateCameraPlayerBoundsPush
};
int cameraOption = 0;
int cameraUpdatersLength = cameraUpdaters.Length;
cameraOption = 0;
cameraUpdatersLength = cameraUpdaters.Length;
string[] cameraDescriptions = new string[]{
cameraDescriptions = new string[]{
"Follow player center",
"Follow player center, but clamp to map edges",
"Follow player center; smoothed",
"Follow player center horizontally; update player center vertically after landing",
"Player push camera on getting too close to screen edge"
};
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
// Main game loop
while (!WindowShouldClose())
{
// Update
//----------------------------------------------------------------------------------
float deltaTime = GetFrameTime();
UpdatePlayer(ref player, envItems, deltaTime);
camera.Zoom += ((float)GetMouseWheelMove() * 0.05f);
if (camera.Zoom > 3.0f)
{
camera.Zoom = 3.0f;
}
else if (camera.Zoom < 0.25f)
{
camera.Zoom = 0.25f;
}
if (IsKeyPressed(KeyboardKey.R))
{
camera.Zoom = 1.0f;
player.Position = new Vector2(400, 280);
}
if (IsKeyPressed(KeyboardKey.C))
{
cameraOption = (cameraOption + 1) % cameraUpdatersLength;
}
// Call update camera function by its pointer
cameraUpdaters[cameraOption](ref camera, ref player, envItems, deltaTime, screenWidth, screenHeight);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.LightGray);
BeginMode2D(camera);
for (int i = 0; i < envItems.Length; i++)
{
DrawRectangleRec(envItems[i].Rect, envItems[i].Color);
}
Rectangle playerRect = new(player.Position.X - 20, player.Position.Y - 40, 40, 40);
DrawRectangleRec(playerRect, Color.Red);
EndMode2D();
DrawText("Controls:", 20, 20, 10, Color.Black);
DrawText("- Right/Left to move", 40, 40, 10, Color.DarkGray);
DrawText("- Space to jump", 40, 60, 10, Color.DarkGray);
DrawText("- Mouse Wheel to Zoom in-out, R to reset zoom", 40, 80, 10, Color.DarkGray);
DrawText("- C to change camera mode", 40, 100, 10, Color.DarkGray);
DrawText("Current camera mode:", 20, 120, 10, Color.Black);
DrawText(cameraDescriptions[cameraOption], 40, 140, 10, Color.DarkGray);
EndDrawing();
//----------------------------------------------------------------------------------
}
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow();
//--------------------------------------------------------------------------------------
return 0;
}
static void UpdatePlayer(ref Player player, EnvItem[] envItems, float delta)
public void Update()
{
// Update
//----------------------------------------------------------------------------------
var deltaTime = GetFrameTime();
UpdatePlayer(ref player, envItems, deltaTime);
camera.Zoom += ((float)GetMouseWheelMove() * 0.05f);
if (camera.Zoom > 3.0f)
{
camera.Zoom = 3.0f;
}
else if (camera.Zoom < 0.25f)
{
camera.Zoom = 0.25f;
}
if (IsKeyPressed(KeyboardKey.R))
{
camera.Zoom = 1.0f;
player.Position = new Vector2(400, 280);
}
if (IsKeyPressed(KeyboardKey.C))
{
cameraOption = (cameraOption + 1) % cameraUpdatersLength;
}
// Call update camera function by its pointer
cameraUpdaters[cameraOption](ref camera, ref player, envItems, deltaTime, screenWidth, screenHeight);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.LightGray);
BeginMode2D(camera);
for (var i = 0; i < envItems.Length; i++)
{
DrawRectangleRec(envItems[i].Rect, envItems[i].Color);
}
Rectangle playerRect = new(player.Position.X - 20, player.Position.Y - 40, 40.0f, 40.0f);
DrawRectangleRec(playerRect, Color.Red);
DrawCircleV(player.Position, 5.0f, Color.Gold);
EndMode2D();
DrawText("Controls:", 20, 20, 10, Color.Black);
DrawText("- Right/Left to move", 40, 40, 10, Color.DarkGray);
DrawText("- Space to jump", 40, 60, 10, Color.DarkGray);
DrawText("- Mouse Wheel to Zoom in-out", 40, 80, 10, Color.DarkGray);
DrawText("- R to reset position + zoom", 40, 100, 10, Color.DarkGray);
DrawText("- C to change camera mode", 40, 120, 10, Color.DarkGray);
DrawText("Current camera mode:", 20, 140, 10, Color.Black);
DrawText(cameraDescriptions[cameraOption], 40, 160, 10, Color.DarkGray);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
}
private static void UpdatePlayer(ref Player player, EnvItem[] envItems, float delta)
{
if (IsKeyDown(KeyboardKey.Left))
{
@ -197,11 +202,11 @@ public class Camera2dPlatformer
player.CanJump = false;
}
int hitObstacle = 0;
for (int i = 0; i < envItems.Length; i++)
var hitObstacle = 0;
for (var i = 0; i < envItems.Length; i++)
{
EnvItem ei = envItems[i];
Vector2 p = player.Position;
var ei = envItems[i];
var p = player.Position;
if (ei.Blocking != 0 &&
ei.Rect.X <= p.X &&
ei.Rect.X + ei.Rect.Width >= p.X &&
@ -211,6 +216,7 @@ public class Camera2dPlatformer
hitObstacle = 1;
player.Speed = 0.0f;
player.Position.Y = ei.Rect.Y;
break;
}
}
@ -226,7 +232,7 @@ public class Camera2dPlatformer
}
}
static void UpdateCameraCenter(
private static void UpdateCameraCenter(
ref Camera2D camera,
ref Player player,
EnvItem[] envItems,
@ -235,11 +241,11 @@ public class Camera2dPlatformer
int height
)
{
camera.Offset = new Vector2(width / 2, height / 2);
camera.Offset = new Vector2(width / 2.0f, height / 2.0f);
camera.Target = player.Position;
}
static void UpdateCameraCenterInsideMap(
private static void UpdateCameraCenterInsideMap(
ref Camera2D camera,
ref Player player,
EnvItem[] envItems,
@ -248,43 +254,43 @@ public class Camera2dPlatformer
int height)
{
camera.Target = player.Position;
camera.Offset = new Vector2(width / 2, height / 2);
camera.Offset = new Vector2(width / 2.0f, height / 2.0f);
float minX = 1000, minY = 1000, maxX = -1000, maxY = -1000;
for (int i = 0; i < envItems.Length; i++)
for (var i = 0; i < envItems.Length; i++)
{
EnvItem ei = envItems[i];
var ei = envItems[i];
minX = Math.Min(ei.Rect.X, minX);
maxX = Math.Max(ei.Rect.X + ei.Rect.Width, maxX);
minY = Math.Min(ei.Rect.Y, minY);
maxY = Math.Max(ei.Rect.Y + ei.Rect.Height, maxY);
}
Vector2 max = GetWorldToScreen2D(new Vector2(maxX, maxY), camera);
Vector2 min = GetWorldToScreen2D(new Vector2(minX, minY), camera);
var max = GetWorldToScreen2D(new Vector2(maxX, maxY), camera);
var min = GetWorldToScreen2D(new Vector2(minX, minY), camera);
if (max.X < width)
{
camera.Offset.X = width - (max.X - width / 2);
camera.Offset.X = width - (max.X - width / 2.0f);
}
if (max.Y < height)
{
camera.Offset.Y = height - (max.Y - height / 2);
camera.Offset.Y = height - (max.Y - height / 2.0f);
}
if (min.X > 0)
{
camera.Offset.X = width / 2 - min.X;
camera.Offset.X = width / 2.0f - min.X;
}
if (min.Y > 0)
{
camera.Offset.Y = height / 2 - min.Y;
camera.Offset.Y = height / 2.0f - min.Y;
}
}
static void UpdateCameraCenterSmoothFollow(
private static void UpdateCameraCenterSmoothFollow(
ref Camera2D camera,
ref Player player,
EnvItem[] envItems,
@ -297,18 +303,18 @@ public class Camera2dPlatformer
const float minEffectLength = 10;
const float fractionSpeed = 0.8f;
camera.Offset = new Vector2(width / 2, height / 2);
Vector2 diff = Vector2Subtract(player.Position, camera.Target);
float length = Vector2Length(diff);
camera.Offset = new Vector2(width / 2.0f, height / 2.0f);
var diff = Vector2Subtract(player.Position, camera.Target);
var length = Vector2Length(diff);
if (length > minEffectLength)
{
float speed = Math.Max(fractionSpeed * length, minSpeed);
var speed = Math.Max(fractionSpeed * length, minSpeed);
camera.Target = Vector2Add(camera.Target, Vector2Scale(diff, speed * delta / length));
}
}
static void UpdateCameraEvenOutOnLanding(
private static void UpdateCameraEvenOutOnLanding(
ref Camera2D camera,
ref Player player,
EnvItem[] envItems,
@ -318,10 +324,10 @@ public class Camera2dPlatformer
)
{
float evenOutSpeed = 700;
int eveningOut = 0;
float evenOutTarget = 0.0f;
var eveningOut = 0;
var evenOutTarget = 0.0f;
camera.Offset = new Vector2(width / 2, height / 2);
camera.Offset = new Vector2(width / 2.0f, height / 2.0f);
camera.Target.X = player.Position.X;
if (eveningOut != 0)
@ -357,7 +363,7 @@ public class Camera2dPlatformer
}
}
static void UpdateCameraPlayerBoundsPush(
private static void UpdateCameraPlayerBoundsPush(
ref Camera2D camera,
ref Player player,
EnvItem[] envItems,
@ -368,11 +374,11 @@ public class Camera2dPlatformer
{
Vector2 bbox = new(0.2f, 0.2f);
Vector2 bboxWorldMin = GetScreenToWorld2D(
var bboxWorldMin = GetScreenToWorld2D(
new Vector2((1 - bbox.X) * 0.5f * width, (1 - bbox.Y) * 0.5f * height),
camera
);
Vector2 bboxWorldMax = GetScreenToWorld2D(
var bboxWorldMax = GetScreenToWorld2D(
new Vector2((1 + bbox.X) * 0.5f * width,
(1 + bbox.Y) * 0.5f * height),
camera
@ -399,4 +405,32 @@ public class Camera2dPlatformer
camera.Target.Y = bboxWorldMin.Y + (player.Position.Y - bboxWorldMax.Y);
}
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [core] example - 2d camera platformer");
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
var game = new Camera2dPlatformer();
game.Init();
// Main game loop
while (!WindowShouldClose())
{
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}
}

View file

@ -2,183 +2,215 @@
*
* raylib [core] example - 3d camera first person
*
* This example has been created using raylib 1.3 (www.raylib.com)
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
* Example complexity rating: [★★☆☆] 2/4
*
* Copyright (c) 2015 Ramon Santamaria (@raysan5)
* Example originally created with raylib 1.3, last time updated with raylib 1.3
*
* 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)
*
********************************************************************************************/
using System.Numerics;
using static Raylib_cs.Raylib;
namespace Examples.Core;
public class Camera3dFirstPerson
public partial class Camera3dFirstPerson : IExample
{
public const int MaxColumns = 20;
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Core / Camera 3D First Person";
public string Title => "raylib [core] example - 3d camera first person";
public bool CursorDisabled => true;
private Camera3D camera;
private CameraMode cameraMode;
private float[] heights;
private Vector3[] positions;
private Color[] colors;
public void Init()
{
// Define the camera to look into our 3d world (position, target, up vector)
camera = new();
camera.Position = new Vector3(0.0f, 2.0f, 4.0f); // Camera position
camera.Target = new Vector3(0.0f, 2.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 = 60.0f; // Camera field-of-view Y
camera.Projection = CameraProjection.Perspective; // Camera projection type
cameraMode = CameraMode.FirstPerson;
// Generates some random columns
heights = new float[MaxColumns];
positions = new Vector3[MaxColumns];
colors = new Color[MaxColumns];
for (var i = 0; i < MaxColumns; i++)
{
heights[i] = (float)GetRandomValue(1, 12);
positions[i] = new Vector3(GetRandomValue(-15, 15), heights[i] / 2.0f, GetRandomValue(-15, 15));
colors[i] = new Color(GetRandomValue(20, 255), GetRandomValue(10, 55), 30, 255);
}
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
// Switch camera mode
if (IsKeyPressed(KeyboardKey.One))
{
cameraMode = CameraMode.Free;
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
}
if (IsKeyPressed(KeyboardKey.Two))
{
cameraMode = CameraMode.FirstPerson;
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
}
if (IsKeyPressed(KeyboardKey.Three))
{
cameraMode = CameraMode.ThirdPerson;
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
}
if (IsKeyPressed(KeyboardKey.Four))
{
cameraMode = CameraMode.Orbital;
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
}
// Switch camera projection
if (IsKeyPressed(KeyboardKey.P))
{
if (camera.Projection == CameraProjection.Perspective)
{
// Create isometric view
cameraMode = CameraMode.ThirdPerson;
// Note: The target distance is related to the render distance in the orthographic projection
camera.Position = new Vector3(0.0f, 2.0f, -100.0f);
camera.Target = new Vector3(0.0f, 2.0f, 0.0f);
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
camera.Projection = CameraProjection.Orthographic;
camera.FovY = 20.0f; // near plane width in CAMERA_ORTHOGRAPHIC
// CameraYaw(&camera, -135 * DEG2RAD, true);
// CameraPitch(&camera, -45 * DEG2RAD, true, true, false);
}
else if (camera.Projection == CameraProjection.Orthographic)
{
// Reset to default view
cameraMode = CameraMode.ThirdPerson;
camera.Position = new Vector3(0.0f, 2.0f, 10.0f);
camera.Target = new Vector3(0.0f, 2.0f, 0.0f);
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
camera.Projection = CameraProjection.Perspective;
camera.FovY = 60.0f;
}
}
// Update camera computes movement internally depending on the camera mode
// Some default standard keyboard/mouse inputs are hardcoded to simplify use
// For advanced camera controls, it's recommended to compute camera movement manually
UpdateCamera(ref camera, cameraMode); // Update camera
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
// Draw ground
DrawPlane(new Vector3(0.0f, 0.0f, 0.0f), new Vector2(32.0f, 32.0f), Color.LightGray);
// Draw a blue wall
DrawCube(new Vector3(-16.0f, 2.5f, 0.0f), 1.0f, 5.0f, 32.0f, Color.Blue);
// Draw a green wall
DrawCube(new Vector3(16.0f, 2.5f, 0.0f), 1.0f, 5.0f, 32.0f, Color.Lime);
// Draw a yellow wall
DrawCube(new Vector3(0.0f, 2.5f, 16.0f), 32.0f, 5.0f, 1.0f, Color.Gold);
// Draw some cubes around
for (var i = 0; i < MaxColumns; i++)
{
DrawCube(positions[i], 2.0f, heights[i], 2.0f, colors[i]);
DrawCubeWires(positions[i], 2.0f, heights[i], 2.0f, Color.Maroon);
}
// Draw player cube
if (cameraMode == CameraMode.ThirdPerson)
{
DrawCube(camera.Target, 0.5f, 0.5f, 0.5f, Color.Purple);
DrawCubeWires(camera.Target, 0.5f, 0.5f, 0.5f, Color.DarkPurple);
}
EndMode3D();
// Draw info boxes
DrawRectangle(5, 5, 330, 100, Fade(Color.SkyBlue, 0.5f));
DrawRectangleLines(5, 5, 330, 100, Color.Blue);
DrawText("Camera controls:", 15, 15, 10, Color.Black);
DrawText("- Move keys: W, A, S, D, Space, Left-Ctrl", 15, 30, 10, Color.Black);
DrawText("- Look around: arrow keys or mouse", 15, 45, 10, Color.Black);
DrawText("- Camera mode keys: 1, 2, 3, 4", 15, 60, 10, Color.Black);
DrawText("- Zoom keys: num-plus, num-minus or mouse scroll", 15, 75, 10, Color.Black);
DrawText("- Camera projection key: P", 15, 90, 10, Color.Black);
DrawRectangle(600, 5, 195, 100, Fade(Color.SkyBlue, 0.5f));
DrawRectangleLines(600, 5, 195, 100, Color.Blue);
DrawText("Camera status:", 610, 15, 10, Color.Black);
DrawText($"- Mode: {cameraMode}", 610, 30, 10, Color.Black);
DrawText($"- Projection: {camera.Projection}", 610, 45, 10, Color.Black);
DrawText($"- Position: {camera.Position}", 610, 60, 10, Color.Black);
DrawText($"- Target: {camera.Target}", 610, 75, 10, Color.Black);
DrawText($"- Up: {camera.Up}", 610, 90, 10, Color.Black);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [core] example - 3d camera first person");
// Define the camera to look into our 3d world (position, target, up vector)
Camera3D camera = new();
camera.Position = new Vector3(4.0f, 2.0f, 4.0f);
camera.Target = new Vector3(0.0f, 1.8f, 0.0f);
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
camera.FovY = 60.0f;
camera.Projection = CameraProjection.Perspective;
DisableCursor(); // Limit cursor to relative movement inside the window
// Generates some random columns
float[] heights = new float[MaxColumns];
Vector3[] positions = new Vector3[MaxColumns];
Color[] colors = new Color[MaxColumns];
for (int i = 0; i < MaxColumns; i++)
{
heights[i] = (float)GetRandomValue(1, 12);
positions[i] = new Vector3(GetRandomValue(-15, 15), heights[i] / 2, GetRandomValue(-15, 15));
colors[i] = new Color(GetRandomValue(20, 255), GetRandomValue(10, 55), 30, 255);
}
CameraMode cameraMode = CameraMode.FirstPerson;
SetTargetFPS(60);
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new Camera3dFirstPerson();
game.Init();
// Main game loop
while (!WindowShouldClose())
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
// Switch camera mode
if (IsKeyPressed(KeyboardKey.One))
{
cameraMode = CameraMode.Free;
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
}
if (IsKeyPressed(KeyboardKey.Two))
{
cameraMode = CameraMode.FirstPerson;
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
}
if (IsKeyPressed(KeyboardKey.Three))
{
cameraMode = CameraMode.ThirdPerson;
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
}
if (IsKeyPressed(KeyboardKey.Four))
{
cameraMode = CameraMode.Orbital;
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
}
// Switch camera projection
if (IsKeyPressed(KeyboardKey.P))
{
if (camera.Projection == CameraProjection.Perspective)
{
// Create isometric view
cameraMode = CameraMode.ThirdPerson;
// Note: The target distance is related to the render distance in the orthographic projection
camera.Position = new Vector3(0.0f, 2.0f, -100.0f);
camera.Target = new Vector3(0.0f, 2.0f, 0.0f);
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
camera.Projection = CameraProjection.Orthographic;
camera.FovY = 20.0f; // near plane width in CAMERA_ORTHOGRAPHIC
// CameraYaw(&camera, -135 * DEG2RAD, true);
// CameraPitch(&camera, -45 * DEG2RAD, true, true, false);
}
else if (camera.Projection == CameraProjection.Orthographic)
{
// Reset to default view
cameraMode = CameraMode.ThirdPerson;
camera.Position = new Vector3(0.0f, 2.0f, 10.0f);
camera.Target = new Vector3(0.0f, 2.0f, 0.0f);
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
camera.Projection = CameraProjection.Perspective;
camera.FovY = 60.0f;
}
}
// Update camera computes movement internally depending on the camera mode
// Some default standard keyboard/mouse inputs are hardcoded to simplify use
// For advance camera controls, it's reecommended to compute camera movement manually
UpdateCamera(ref camera, cameraMode);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
// Draw ground
DrawPlane(new Vector3(0.0f, 0.0f, 0.0f), new Vector2(32.0f, 32.0f), Color.LightGray);
// Draw a blue wall
DrawCube(new Vector3(-16.0f, 2.5f, 0.0f), 1.0f, 5.0f, 32.0f, Color.Blue);
// Draw a green wall
DrawCube(new Vector3(16.0f, 2.5f, 0.0f), 1.0f, 5.0f, 32.0f, Color.Lime);
// Draw a yellow wall
DrawCube(new Vector3(0.0f, 2.5f, 16.0f), 32.0f, 5.0f, 1.0f, Color.Gold);
// Draw some cubes around
for (int i = 0; i < MaxColumns; i++)
{
DrawCube(positions[i], 2.0f, heights[i], 2.0f, colors[i]);
DrawCubeWires(positions[i], 2.0f, heights[i], 2.0f, Color.Maroon);
}
// Draw player cube
if (cameraMode == CameraMode.ThirdPerson)
{
DrawCube(camera.Target, 0.5f, 0.5f, 0.5f, Color.Purple);
DrawCubeWires(camera.Target, 0.5f, 0.5f, 0.5f, Color.DarkPurple);
}
EndMode3D();
// Draw info boxes
DrawRectangle(5, 5, 330, 100, ColorAlpha(Color.SkyBlue, 0.5f));
DrawRectangleLines(10, 10, 330, 100, Color.Blue);
DrawText("Camera controls:", 15, 15, 10, Color.Black);
DrawText("- Move keys: W, A, S, D, Space, Left-Ctrl", 15, 30, 10, Color.Black);
DrawText("- Look around: arrow keys or mouse", 15, 45, 10, Color.Black);
DrawText("- Camera mode keys: 1, 2, 3, 4", 15, 60, 10, Color.Black);
DrawText("- Zoom keys: num-plus, num-minus or mouse scroll", 15, 75, 10, Color.Black);
DrawText("- Camera projection key: P", 15, 90, 10, Color.Black);
DrawRectangle(600, 5, 195, 100, Fade(Color.SkyBlue, 0.5f));
DrawRectangleLines(600, 5, 195, 100, Color.Blue);
DrawText("Camera status:", 610, 15, 10, Color.Black);
DrawText($"- Mode: {cameraMode}", 610, 30, 10, Color.Black);
DrawText($"- Projection: {camera.Projection}", 610, 45, 10, Color.Black);
DrawText($"- Position: {camera.Position}", 610, 60, 10, Color.Black);
DrawText($"- Target: {camera.Target}", 610, 75, 10, Color.Black);
DrawText($"- Up: {camera.Up}", 610, 90, 10, Color.Black);
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow();
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;

View file

@ -0,0 +1,360 @@
/*******************************************************************************************
*
* raylib [core] example - 3d camera fps
*
* Example complexity rating: [★★★☆] 3/4
*
* Example originally created with raylib 5.5, last time updated with raylib 5.5
*
* Example contributed by Agnis Aldiņš (@nezvers) 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 Agnis Aldiņš (@nezvers)
*
********************************************************************************************/
using static Raylib_cs.Raymath;
namespace Examples.Core;
public partial class Camera3dFps : IExample
{
// Movement constants
private const float GRAVITY = 32.0f;
private const float MAX_SPEED = 20.0f;
private const float CROUCH_SPEED = 5.0f;
private const float JUMP_FORCE = 12.0f;
private const float MAX_ACCEL = 150.0f;
// Grounded drag
private const float FRICTION = 0.86f;
// Increasing air drag, increases strafing speed
private const float AIR_DRAG = 0.98f;
// Responsiveness for turning movement direction to looked direction
private const float CONTROL = 15.0f;
private const float CROUCH_HEIGHT = 0.0f;
private const float STAND_HEIGHT = 1.0f;
private const float BOTTOM_HEIGHT = 0.5f;
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Core / 3D Camera FPS";
public string Title => "raylib [core] example - 3d camera fps";
public bool CursorDisabled => true;
// Body structure
private struct Body
{
public Vector3 Position;
public Vector3 Velocity;
public Vector3 Dir;
public bool IsGrounded;
}
// State that was global in the C example
private readonly Vector2 sensitivity = new Vector2(0.001f, 0.001f);
private Body player;
private Vector2 lookRotation;
private float headTimer;
private float walkLerp;
private float headLerp;
private Vector2 lean;
private Camera3D camera;
public void Init()
{
player = new Body();
lookRotation = new Vector2(0, 0);
headTimer = 0.0f;
walkLerp = 0.0f;
headLerp = STAND_HEIGHT;
lean = new Vector2(0, 0);
// Initialize camera variables
// NOTE: UpdateCameraFPS() takes care of the rest
camera = new Camera3D();
camera.FovY = 60.0f;
camera.Projection = CameraProjection.Perspective;
camera.Position = new Vector3(
player.Position.X,
player.Position.Y + (BOTTOM_HEIGHT + headLerp),
player.Position.Z);
UpdateCameraFPS(ref camera); // Update camera parameters
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
Vector2 mouseDelta = GetMouseDelta();
lookRotation.X -= mouseDelta.X * sensitivity.X;
lookRotation.Y += mouseDelta.Y * sensitivity.Y;
int sideway = (IsKeyDown(KeyboardKey.D) ? 1 : 0) - (IsKeyDown(KeyboardKey.A) ? 1 : 0);
int forward = (IsKeyDown(KeyboardKey.W) ? 1 : 0) - (IsKeyDown(KeyboardKey.S) ? 1 : 0);
bool crouching = IsKeyDown(KeyboardKey.LeftControl);
UpdateBody(ref player, lookRotation.X, sideway, forward, IsKeyPressed(KeyboardKey.Space), crouching);
float delta = GetFrameTime();
headLerp = Lerp(headLerp, (crouching ? CROUCH_HEIGHT : STAND_HEIGHT), 20.0f * delta);
camera.Position = new Vector3(
player.Position.X,
player.Position.Y + (BOTTOM_HEIGHT + headLerp),
player.Position.Z);
if (player.IsGrounded && ((forward != 0) || (sideway != 0)))
{
headTimer += delta * 3.0f;
walkLerp = Lerp(walkLerp, 1.0f, 10.0f * delta);
camera.FovY = Lerp(camera.FovY, 55.0f, 5.0f * delta);
}
else
{
walkLerp = Lerp(walkLerp, 0.0f, 10.0f * delta);
camera.FovY = Lerp(camera.FovY, 60.0f, 5.0f * delta);
}
lean.X = Lerp(lean.X, sideway * 0.02f, 10.0f * delta);
lean.Y = Lerp(lean.Y, forward * 0.015f, 10.0f * delta);
UpdateCameraFPS(ref camera);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawLevel();
EndMode3D();
// Draw info box
DrawRectangle(5, 5, 330, 75, Fade(Color.SkyBlue, 0.5f));
DrawRectangleLines(5, 5, 330, 75, Color.Blue);
DrawText("Camera controls:", 15, 15, 10, Color.Black);
DrawText("- Move keys: W, A, S, D, Space, Left-Ctrl", 15, 30, 10, Color.Black);
DrawText("- Look around: arrow keys or mouse", 15, 45, 10, Color.Black);
float velLen = Vector2Length(new Vector2(player.Velocity.X, player.Velocity.Z));
DrawText($"- Velocity Len: ({velLen:00.000})", 15, 60, 10, Color.Black);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
}
// Update body considering current world state
private void UpdateBody(ref Body body, float rot, int side, int forward, bool jumpPressed, bool crouchHold)
{
Vector2 input = new Vector2((float)side, (float)-forward);
// Upstream guards this with `#if defined(NORMALIZE_INPUT)`, which is always true given the
// `#define NORMALIZE_INPUT 0` above it (defined() tests definedness, not the value), so the
// diagonal-movement normalization is active.
// Slow down diagonal movement
if ((side != 0) && (forward != 0))
{
input = Vector2Normalize(input);
}
float delta = GetFrameTime();
if (!body.IsGrounded)
{
body.Velocity.Y -= GRAVITY * delta;
}
if (body.IsGrounded && jumpPressed)
{
body.Velocity.Y = JUMP_FORCE;
body.IsGrounded = false;
// Sound can be played at this moment
//SetSoundPitch(fxJump, 1.0f + (GetRandomValue(-100, 100)*0.001));
//PlaySound(fxJump);
}
Vector3 front = new Vector3(MathF.Sin(rot), 0.0f, MathF.Cos(rot));
Vector3 right = new Vector3(MathF.Cos(-rot), 0.0f, MathF.Sin(-rot));
Vector3 desiredDir = new Vector3(
input.X * right.X + input.Y * front.X,
0.0f,
input.X * right.Z + input.Y * front.Z);
body.Dir = Vector3Lerp(body.Dir, desiredDir, CONTROL * delta);
float decel = (body.IsGrounded ? FRICTION : AIR_DRAG);
Vector3 hvel = new Vector3(body.Velocity.X * decel, 0.0f, body.Velocity.Z * decel);
float hvelLength = Vector3Length(hvel); // Magnitude
if (hvelLength < (MAX_SPEED * 0.01f))
{
hvel = new Vector3(0, 0, 0);
}
// This is what creates strafing
float speed = Vector3DotProduct(hvel, body.Dir);
// Whenever the amount of acceleration to add is clamped by the maximum acceleration constant,
// a Player can make the speed faster by bringing the direction closer to horizontal velocity angle
// More info here: https://youtu.be/v3zT3Z5apaM?t=165
float maxSpeed = (crouchHold ? CROUCH_SPEED : MAX_SPEED);
float accel = Clamp(maxSpeed - speed, 0.0f, MAX_ACCEL * delta);
hvel.X += body.Dir.X * accel;
hvel.Z += body.Dir.Z * accel;
body.Velocity.X = hvel.X;
body.Velocity.Z = hvel.Z;
body.Position.X += body.Velocity.X * delta;
body.Position.Y += body.Velocity.Y * delta;
body.Position.Z += body.Velocity.Z * delta;
// Fancy collision system against the floor
if (body.Position.Y <= 0.0f)
{
body.Position.Y = 0.0f;
body.Velocity.Y = 0.0f;
body.IsGrounded = true; // Enable jumping
}
}
// Update camera for FPS behaviour
private void UpdateCameraFPS(ref Camera3D camera)
{
Vector3 up = new Vector3(0.0f, 1.0f, 0.0f);
Vector3 targetOffset = new Vector3(0.0f, 0.0f, -1.0f);
// Left and right
Vector3 yaw = Vector3RotateByAxisAngle(targetOffset, up, lookRotation.X);
// Clamp view up
float maxAngleUp = Vector3Angle(up, yaw);
maxAngleUp -= 0.001f; // Avoid numerical errors
if (-(lookRotation.Y) > maxAngleUp)
{
lookRotation.Y = -maxAngleUp;
}
// Clamp view down
float maxAngleDown = Vector3Angle(Vector3Negate(up), yaw);
maxAngleDown *= -1.0f; // Downwards angle is negative
maxAngleDown += 0.001f; // Avoid numerical errors
if (-(lookRotation.Y) < maxAngleDown)
{
lookRotation.Y = -maxAngleDown;
}
// Up and down
Vector3 right = Vector3Normalize(Vector3CrossProduct(yaw, up));
// Rotate view vector around right axis
float pitchAngle = -lookRotation.Y - lean.Y;
pitchAngle = Clamp(pitchAngle, -MathF.PI / 2 + 0.0001f, MathF.PI / 2 - 0.0001f); // Clamp angle so it doesn't go past straight up or straight down
Vector3 pitch = Vector3RotateByAxisAngle(yaw, right, pitchAngle);
// Head animation
// Rotate up direction around forward axis
float headSin = MathF.Sin(headTimer * MathF.PI);
float headCos = MathF.Cos(headTimer * MathF.PI);
const float stepRotation = 0.01f;
camera.Up = Vector3RotateByAxisAngle(up, pitch, headSin * stepRotation + lean.X);
// Camera BOB
const float bobSide = 0.1f;
const float bobUp = 0.15f;
Vector3 bobbing = Vector3Scale(right, headSin * bobSide);
bobbing.Y = MathF.Abs(headCos * bobUp);
camera.Position = Vector3Add(camera.Position, Vector3Scale(bobbing, walkLerp));
camera.Target = Vector3Add(camera.Position, pitch);
}
// Draw game level
private void DrawLevel()
{
const int floorExtent = 25;
const float tileSize = 5.0f;
Color tileColor1 = new Color(150, 200, 200, 255);
// Floor tiles
for (int y = -floorExtent; y < floorExtent; y++)
{
for (int x = -floorExtent; x < floorExtent; x++)
{
if ((y & 1) != 0 && (x & 1) != 0)
{
DrawPlane(new Vector3(x * tileSize, 0.0f, y * tileSize), new Vector2(tileSize, tileSize), tileColor1);
}
else if ((y & 1) == 0 && (x & 1) == 0)
{
DrawPlane(new Vector3(x * tileSize, 0.0f, y * tileSize), new Vector2(tileSize, tileSize), Color.LightGray);
}
}
}
Vector3 towerSize = new Vector3(16.0f, 32.0f, 16.0f);
Color towerColor = new Color(150, 200, 200, 255);
Vector3 towerPos = new Vector3(16.0f, 16.0f, 16.0f);
DrawCubeV(towerPos, towerSize, towerColor);
DrawCubeWiresV(towerPos, towerSize, Color.DarkBlue);
towerPos.X *= -1;
DrawCubeV(towerPos, towerSize, towerColor);
DrawCubeWiresV(towerPos, towerSize, Color.DarkBlue);
towerPos.Z *= -1;
DrawCubeV(towerPos, towerSize, towerColor);
DrawCubeWiresV(towerPos, towerSize, Color.DarkBlue);
towerPos.X *= -1;
DrawCubeV(towerPos, towerSize, towerColor);
DrawCubeWiresV(towerPos, towerSize, Color.DarkBlue);
// Red sun
DrawSphere(new Vector3(300.0f, 300.0f, 0.0f), 100.0f, new Color(255, 0, 0, 255));
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [core] example - 3d camera fps");
DisableCursor(); // Limit cursor to relative movement inside the window
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new Camera3dFps();
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,87 +1,114 @@
/*******************************************************************************************
*
* raylib [core] example - Initialize 3d camera free
* raylib [core] example - 3d camera free
*
* This example has been created using raylib 1.3 (www.raylib.com)
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
* Example complexity rating: [★☆☆☆] 1/4
*
* Copyright (c) 2015 Ramon Santamaria (@raysan5)
* Example originally created with raylib 1.3, last time updated with raylib 1.3
*
* 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)
*
********************************************************************************************/
using System.Numerics;
using static Raylib_cs.Raylib;
namespace Examples.Core;
public class Camera3dFree
public partial class Camera3dFree : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Core / Camera 3D Free";
public string Title => "raylib [core] example - 3d camera free";
public bool CursorDisabled => true;
private Camera3D camera;
private Vector3 cubePosition;
public void Init()
{
// Define the camera to look into our 3d world
camera = new();
camera.Position = new Vector3(10.0f, 10.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
cubePosition = new(0.0f, 0.0f, 0.0f);
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Free);
if (IsKeyPressed(KeyboardKey.Z))
{
camera.Target = new Vector3(0.0f, 0.0f, 0.0f);
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawCube(cubePosition, 2.0f, 2.0f, 2.0f, Color.Red);
DrawCubeWires(cubePosition, 2.0f, 2.0f, 2.0f, Color.Maroon);
DrawGrid(10, 1.0f);
EndMode3D();
DrawRectangle(10, 10, 320, 93, Fade(Color.SkyBlue, 0.5f));
DrawRectangleLines(10, 10, 320, 93, Color.Blue);
DrawText("Free camera default controls:", 20, 20, 10, Color.Black);
DrawText("- Mouse Wheel to Zoom in-out", 40, 40, 10, Color.DarkGray);
DrawText("- Mouse Wheel Pressed to Pan", 40, 60, 10, Color.DarkGray);
DrawText("- Z to zoom to (0, 0, 0)", 40, 80, 10, Color.DarkGray);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [core] example - 3d camera free");
// Define the camera to look into our 3d world
Camera3D camera;
camera.Position = new Vector3(10.0f, 10.0f, 10.0f);
camera.Target = new Vector3(0.0f, 0.0f, 0.0f);
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
camera.FovY = 45.0f;
camera.Projection = CameraProjection.Perspective;
DisableCursor(); // Limit cursor to relative movement inside the window
Vector3 cubePosition = new(0.0f, 0.0f, 0.0f);
SetTargetFPS(60);
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new Camera3dFree();
game.Init();
// Main game loop
while (!WindowShouldClose())
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Free);
if (IsKeyDown(KeyboardKey.Z))
{
camera.Target = new Vector3(0.0f, 0.0f, 0.0f);
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawCube(cubePosition, 2.0f, 2.0f, 2.0f, Color.Red);
DrawCubeWires(cubePosition, 2.0f, 2.0f, 2.0f, Color.Maroon);
DrawGrid(10, 1.0f);
EndMode3D();
DrawRectangle(10, 10, 320, 133, ColorAlpha(Color.SkyBlue, 0.5f));
DrawRectangleLines(10, 10, 320, 133, Color.Blue);
DrawText("Free camera default controls:", 20, 20, 10, Color.Black);
DrawText("- Mouse Wheel to Zoom in-out", 40, 40, 10, Color.DarkGray);
DrawText("- Mouse Wheel Pressed to Pan", 40, 60, 10, Color.DarkGray);
DrawText("- Alt + Mouse Wheel Pressed to Rotate", 40, 80, 10, Color.DarkGray);
DrawText("- Alt + Ctrl + Mouse Wheel Pressed for Smooth Zoom", 40, 100, 10, Color.DarkGray);
DrawText("- Z to zoom to (0, 0, 0)", 40, 120, 10, Color.DarkGray);
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow();
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;

View file

@ -1,76 +1,96 @@
/*******************************************************************************************
*
* raylib [core] example - Initialize 3d camera mode
* raylib [core] example - 3d camera mode
*
* 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 complexity rating: [★☆☆☆] 1/4
*
* Copyright (c) 2014 Ramon Santamaria (@raysan5)
* Example originally created with raylib 1.0, last time updated with raylib 1.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)
*
********************************************************************************************/
using System.Numerics;
using static Raylib_cs.Raylib;
namespace Examples.Core;
public class Camera3dMode
public partial class Camera3dMode : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
private Camera3D camera;
private Vector3 cubePosition;
public string Name => "Core / Camera 3D Mode";
public string Title => "raylib [core] example - 3d camera mode";
public void Init()
{
// Define the camera to look into our 3d world
camera = new();
camera.Position = new Vector3(0.0f, 10.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 mode type
cubePosition = new(0.0f, 0.0f, 0.0f);
}
public void Update()
{
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawCube(cubePosition, 2.0f, 2.0f, 2.0f, Color.Red);
DrawCubeWires(cubePosition, 2.0f, 2.0f, 2.0f, Color.Maroon);
DrawGrid(10, 1.0f);
EndMode3D();
DrawText("Welcome to the third dimension!", 10, 40, 20, Color.DarkGray);
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [core] example - 3d camera mode");
// Define the camera to look into our 3d world
Camera3D camera = new();
camera.Position = new Vector3(0.0f, 10.0f, 10.0f);
camera.Target = new Vector3(0.0f, 0.0f, 0.0f);
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
camera.FovY = 45.0f;
camera.Projection = CameraProjection.Perspective;
Vector3 cubePosition = new(0.0f, 0.0f, 0.0f);
SetTargetFPS(60);
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new Camera3dMode();
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);
BeginMode3D(camera);
DrawCube(cubePosition, 2.0f, 2.0f, 2.0f, Color.Red);
DrawCubeWires(cubePosition, 2.0f, 2.0f, 2.0f, Color.Maroon);
DrawGrid(10, 1.0f);
EndMode3D();
DrawText("Welcome to the third dimension!", 10, 40, 20, Color.DarkGray);
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow();
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;

View file

@ -0,0 +1,311 @@
/*******************************************************************************************
*
* raylib [core] example - clipboard text
*
* Example complexity rating: [★★☆☆] 2/4
*
* Example originally created with raylib 6.0, last time updated with raylib 6.0
*
* Example contributed by Ananth S (@Ananth1839) 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 Ananth S (@Ananth1839)
*
********************************************************************************************/
// NOTE: The upstream C example uses raygui (GuiTextBox/GuiButton/GuiLabel) for its UI.
// raygui is not bound in raylib-cs, so the widgets below are minimal re-implementations
// using plain raylib drawing/input. Clipboard behaviour (cut/copy/paste and CTRL+X/C/V
// shortcuts) matches the original.
using System.Text;
namespace Examples.Core;
public partial class ClipboardText : IExample
{
private const int MaxTextSamples = 5;
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Core / Clipboard Text";
public string Title => "raylib [core] example - clipboard text";
private string[] sampleTexts;
private string clipboardText;
private StringBuilder inputBuffer;
// UI required variables
private bool textBoxEditMode;
private bool btnCutPressed;
private bool btnCopyPressed;
private bool btnPastePressed;
private bool btnClearPressed;
private bool btnRandomPressed;
private int framesCounter;
public void Init()
{
// Define some sample texts
sampleTexts = new string[]
{
"Hello from raylib!",
"The quick brown fox jumps over the lazy dog",
"Clipboard operations are useful!",
"raylib is a simple and easy-to-use library",
"Copy and paste me!"
};
clipboardText = null;
inputBuffer = new StringBuilder("Hello from raylib!"); // Random initial string
// UI required variables
textBoxEditMode = false;
btnCutPressed = false;
btnCopyPressed = false;
btnPastePressed = false;
btnClearPressed = false;
btnRandomPressed = false;
framesCounter = 0;
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
framesCounter++;
// Handle button interactions
if (btnCutPressed)
{
SetClipboardText(inputBuffer.ToString());
clipboardText = GetClipboardText_();
inputBuffer.Clear(); // Quick solution to clear text
}
if (btnCopyPressed)
{
SetClipboardText(inputBuffer.ToString()); // Copy text to clipboard
clipboardText = GetClipboardText_(); // Get text from clipboard
}
if (btnPastePressed)
{
// Paste text from clipboard
clipboardText = GetClipboardText_();
if (clipboardText != null)
{
SetInputBuffer(clipboardText);
}
}
if (btnClearPressed)
{
inputBuffer.Clear(); // Quick solution to clear text
}
if (btnRandomPressed)
{
// Get random text from sample list
SetInputBuffer(sampleTexts[GetRandomValue(0, MaxTextSamples - 1)]);
}
// Quick cut/copy/paste with keyboard shortcuts
if (IsKeyDown(KeyboardKey.LeftControl) || IsKeyDown(KeyboardKey.RightControl))
{
if (IsKeyPressed(KeyboardKey.X))
{
SetClipboardText(inputBuffer.ToString());
inputBuffer.Clear(); // Quick solution to clear text
}
if (IsKeyPressed(KeyboardKey.C))
{
SetClipboardText(inputBuffer.ToString());
}
if (IsKeyPressed(KeyboardKey.V))
{
clipboardText = GetClipboardText_();
if (clipboardText != null)
{
SetInputBuffer(clipboardText);
}
}
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
// Draw instructions
GuiLabel(new Rectangle(50, 20, 700, 36), "Use the BUTTONS or KEY SHORTCUTS:");
DrawText("[CTRL+X] - CUT | [CTRL+C] COPY | [CTRL+V] | PASTE", 50, 60, 20, Color.Maroon);
// Draw text box
if (GuiTextBox(new Rectangle(50, 120, 652, 40), inputBuffer, 256, textBoxEditMode))
{
textBoxEditMode = !textBoxEditMode;
}
// Random text button
btnRandomPressed = GuiButton(new Rectangle(50 + 652 + 8, 120, 40, 40), "RND");
// Draw buttons
btnCutPressed = GuiButton(new Rectangle(50, 180, 158, 40), "CUT");
btnCopyPressed = GuiButton(new Rectangle(50 + 165, 180, 158, 40), "COPY");
btnPastePressed = GuiButton(new Rectangle(50 + 165 * 2, 180, 158, 40), "PASTE");
btnClearPressed = GuiButton(new Rectangle(50 + 165 * 3, 180, 158, 40), "CLEAR");
// Draw clipboard status
GuiLabel(new Rectangle(50, 260, 700, 40), "Clipboard current text data:");
GuiTextBoxReadOnly(new Rectangle(50, 300, 700, 40), clipboardText);
GuiLabel(new Rectangle(50, 360, 700, 40), "Try copying text from other applications and pasting here!");
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
}
// Replace input buffer contents (equivalent to raylib TextCopy into a fixed buffer)
private void SetInputBuffer(string text)
{
inputBuffer.Clear();
if (text != null)
{
if (text.Length > 255)
{
text = text.Substring(0, 255);
}
inputBuffer.Append(text);
}
}
//----------------------------------------------------------------------------------
// Minimal raygui-like widgets (plain raylib re-implementation)
//----------------------------------------------------------------------------------
private static void GuiLabel(Rectangle bounds, string text)
{
DrawText(text, (int)bounds.X, (int)(bounds.Y + (bounds.Height - 20) / 2), 20, Color.DarkGray);
}
private static bool GuiButton(Rectangle bounds, string text)
{
bool pressed = false;
Vector2 mouse = GetMousePosition();
bool hover = CheckCollisionPointRec(mouse, bounds);
Color fill = hover ? (IsMouseButtonDown(MouseButton.Left) ? Color.SkyBlue : Color.LightGray) : Color.RayWhite;
DrawRectangleRec(bounds, fill);
DrawRectangleLinesEx(bounds, 1, hover ? Color.Blue : Color.Gray);
int textWidth = MeasureText(text, 20);
DrawText(text, (int)(bounds.X + (bounds.Width - textWidth) / 2), (int)(bounds.Y + (bounds.Height - 20) / 2), 20, Color.DarkGray);
if (hover && IsMouseButtonReleased(MouseButton.Left))
{
pressed = true;
}
return pressed;
}
private bool GuiTextBox(Rectangle bounds, StringBuilder text, int maxChars, bool editMode)
{
bool pressed = false;
Vector2 mouse = GetMousePosition();
bool hover = CheckCollisionPointRec(mouse, bounds);
if (editMode)
{
// Get char pressed (unicode character) on the queue
int key = GetCharPressed();
while (key > 0)
{
if ((key >= 32) && (key <= 125) && (text.Length < maxChars - 1))
{
text.Append((char)key);
}
key = GetCharPressed();
}
if (IsKeyPressed(KeyboardKey.Backspace) && (text.Length > 0))
{
text.Remove(text.Length - 1, 1);
}
}
DrawRectangleRec(bounds, Color.RayWhite);
DrawRectangleLinesEx(bounds, editMode ? 2 : 1, editMode ? Color.Red : (hover ? Color.Blue : Color.Gray));
string content = text.ToString();
DrawText(content, (int)bounds.X + 4, (int)(bounds.Y + (bounds.Height - 20) / 2), 20, Color.DarkGray);
// Draw blinking cursor while editing
if (editMode && ((framesCounter / 20) % 2 == 0))
{
DrawText("_", (int)bounds.X + 4 + MeasureText(content, 20), (int)(bounds.Y + (bounds.Height - 20) / 2), 20, Color.DarkGray);
}
if (hover && IsMouseButtonPressed(MouseButton.Left))
{
pressed = true;
}
return pressed;
}
private static void GuiTextBoxReadOnly(Rectangle bounds, string text)
{
DrawRectangleRec(bounds, Color.LightGray);
DrawRectangleLinesEx(bounds, 1, Color.Gray);
if (text != null)
{
DrawText(text, (int)bounds.X + 4, (int)(bounds.Y + (bounds.Height - 20) / 2), 20, Color.Gray);
}
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [core] example - clipboard text");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new ClipboardText();
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,276 @@
/*******************************************************************************************
*
* raylib [core] example - compute hash
*
* Example complexity rating: [★★☆☆] 2/4
*
* Example originally created with raylib 6.0, last time updated with raylib 6.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) 2025 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
// NOTE: The upstream C example uses raygui (GuiTextBox/GuiButton/GuiLabel) for its UI.
// raygui is not bound in raylib-cs, so the widgets below are minimal re-implementations
// using plain raylib drawing/input. The hashing/Base64 logic is a faithful port.
using System.Text;
namespace Examples.Core;
public unsafe partial class ComputeHash : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Core / Compute Hash";
public string Title => "raylib [core] example - compute hash";
// UI controls variables
private StringBuilder textInput;
private bool textBoxEditMode;
private bool btnComputeHashes;
// Data hash values
private uint hashCRC32;
private uint[] hashMD5;
private uint[] hashSHA1;
private uint[] hashSHA256;
// Base64 encoded data
private string base64Text;
private int framesCounter;
public void Init()
{
textInput = new StringBuilder("The quick brown fox jumps over the lazy dog.");
textBoxEditMode = false;
btnComputeHashes = false;
hashCRC32 = 0;
hashMD5 = null;
hashSHA1 = null;
hashSHA256 = null;
base64Text = null;
framesCounter = 0;
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
framesCounter++;
if (btnComputeHashes)
{
byte[] bytes = Encoding.UTF8.GetBytes(textInput.ToString());
int textInputLen = bytes.Length;
fixed (byte* textInputPtr = bytes)
{
int base64TextSize;
// Encode data to Base64 string (includes NULL terminator), memory must be MemFree()
sbyte* base64 = EncodeDataBase64(textInputPtr, textInputLen, &base64TextSize);
base64Text = (base64 != null) ? new string(base64) : null;
MemFree(base64); // Free Base64 text data (kept managed above)
hashCRC32 = ComputeCRC32(textInputPtr, textInputLen); // Compute CRC32 hash code (4 bytes)
hashMD5 = CopyHash(ComputeMD5(textInputPtr, textInputLen), 4); // Compute MD5 hash code, returns static int[4] (16 bytes)
hashSHA1 = CopyHash(ComputeSHA1(textInputPtr, textInputLen), 5); // Compute SHA1 hash code, returns static int[5] (20 bytes)
hashSHA256 = CopyHash(ComputeSHA256(textInputPtr, textInputLen), 8); // Compute SHA256 hash code, returns static int[8] (32 bytes)
}
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
GuiLabel(new Rectangle(40, 26, 720, 32), "INPUT DATA (TEXT):", 20);
if (GuiTextBox(new Rectangle(40, 64, 720, 32), textInput, 95, textBoxEditMode, 10))
{
textBoxEditMode = !textBoxEditMode;
}
btnComputeHashes = GuiButton(new Rectangle(40, 64 + 40, 720, 32), "COMPUTE INPUT DATA HASHES", 10);
GuiLabel(new Rectangle(40, 160, 720, 32), "INPUT DATA HASH VALUES:", 20);
GuiLabel(new Rectangle(40, 200, 120, 32), "CRC32 [32 bit]:", 10);
GuiTextBoxReadOnly(new Rectangle(40 + 120, 200, 720 - 120, 32), GetDataAsHexText(new uint[] { hashCRC32 }, 1), 10);
GuiLabel(new Rectangle(40, 200 + 36, 120, 32), "MD5 [128 bit]:", 10);
GuiTextBoxReadOnly(new Rectangle(40 + 120, 200 + 36, 720 - 120, 32), GetDataAsHexText(hashMD5, 4), 10);
GuiLabel(new Rectangle(40, 200 + 36 * 2, 120, 32), "SHA1 [160 bit]:", 10);
GuiTextBoxReadOnly(new Rectangle(40 + 120, 200 + 36 * 2, 720 - 120, 32), GetDataAsHexText(hashSHA1, 5), 10);
GuiLabel(new Rectangle(40, 200 + 36 * 3, 120, 32), "SHA256 [256 bit]:", 10);
GuiTextBoxReadOnly(new Rectangle(40 + 120, 200 + 36 * 3, 720 - 120, 32), GetDataAsHexText(hashSHA256, 8), 10);
GuiLabel(new Rectangle(40, 200 + 36 * 5 - 30, 320, 32), "BONUS - BAS64 ENCODED STRING:", 10);
GuiLabel(new Rectangle(40, 200 + 36 * 5, 120, 32), "BASE64 ENCODING:", 10);
GuiTextBoxReadOnly(new Rectangle(40 + 120, 200 + 36 * 5, 720 - 120, 32), base64Text, 10);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
}
//----------------------------------------------------------------------------------
// Module Functions Definition
//----------------------------------------------------------------------------------
private static uint[] CopyHash(uint* data, int count)
{
if (data == null)
{
return null;
}
uint[] result = new uint[count];
for (int i = 0; i < count; i++)
{
result[i] = data[i];
}
return result;
}
private static string GetDataAsHexText(uint[] data, int dataSize)
{
if ((data != null) && (dataSize > 0) && (dataSize < ((128 / 8) - 1)))
{
StringBuilder text = new StringBuilder();
for (int i = 0; i < dataSize; i++)
{
text.Append(data[i].ToString("X8"));
}
return text.ToString();
}
return "00000000";
}
//----------------------------------------------------------------------------------
// Minimal raygui-like widgets (plain raylib re-implementation)
//----------------------------------------------------------------------------------
private static void GuiLabel(Rectangle bounds, string text, int fontSize)
{
DrawText(text, (int)bounds.X, (int)(bounds.Y + (bounds.Height - fontSize) / 2), fontSize, Color.DarkGray);
}
private static bool GuiButton(Rectangle bounds, string text, int fontSize)
{
bool pressed = false;
Vector2 mouse = GetMousePosition();
bool hover = CheckCollisionPointRec(mouse, bounds);
Color fill = hover ? (IsMouseButtonDown(MouseButton.Left) ? Color.SkyBlue : Color.LightGray) : Color.RayWhite;
DrawRectangleRec(bounds, fill);
DrawRectangleLinesEx(bounds, 1, hover ? Color.Blue : Color.Gray);
int textWidth = MeasureText(text, fontSize);
DrawText(text, (int)(bounds.X + (bounds.Width - textWidth) / 2), (int)(bounds.Y + (bounds.Height - fontSize) / 2), fontSize, Color.DarkGray);
if (hover && IsMouseButtonReleased(MouseButton.Left))
{
pressed = true;
}
return pressed;
}
private bool GuiTextBox(Rectangle bounds, StringBuilder text, int maxChars, bool editMode, int fontSize)
{
bool pressed = false;
Vector2 mouse = GetMousePosition();
bool hover = CheckCollisionPointRec(mouse, bounds);
if (editMode)
{
int key = GetCharPressed();
while (key > 0)
{
if ((key >= 32) && (key <= 125) && (text.Length < maxChars - 1))
{
text.Append((char)key);
}
key = GetCharPressed();
}
if (IsKeyPressed(KeyboardKey.Backspace) && (text.Length > 0))
{
text.Remove(text.Length - 1, 1);
}
}
DrawRectangleRec(bounds, Color.RayWhite);
DrawRectangleLinesEx(bounds, editMode ? 2 : 1, editMode ? Color.Red : (hover ? Color.Blue : Color.Gray));
string content = text.ToString();
DrawText(content, (int)bounds.X + 4, (int)(bounds.Y + (bounds.Height - fontSize) / 2), fontSize, Color.DarkGray);
if (editMode && ((framesCounter / 20) % 2 == 0))
{
DrawText("_", (int)bounds.X + 4 + MeasureText(content, fontSize), (int)(bounds.Y + (bounds.Height - fontSize) / 2), fontSize, Color.DarkGray);
}
if (hover && IsMouseButtonPressed(MouseButton.Left))
{
pressed = true;
}
return pressed;
}
private static void GuiTextBoxReadOnly(Rectangle bounds, string text, int fontSize)
{
DrawRectangleRec(bounds, Color.LightGray);
DrawRectangleLinesEx(bounds, 1, Color.Gray);
if (text != null)
{
DrawText(text, (int)bounds.X + 4, (int)(bounds.Y + (bounds.Height - fontSize) / 2), fontSize, Color.Gray);
}
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [core] example - compute hash");
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
var game = new ComputeHash();
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,208 @@
/*******************************************************************************************
*
* raylib [core] example - custom frame control
*
* Example complexity rating: [★★★★] 4/4
*
* NOTE: WARNING: This is an example for advanced users willing to have full control over
* the frame processes. By default, EndDrawing() calls the following processes:
* 1. Draw remaining batch data: rlDrawRenderBatchActive()
* 2. SwapScreenBuffer()
* 3. Frame time control: WaitTime()
* 4. PollInputEvents()
*
* To avoid steps 2, 3 and 4, flag SUPPORT_CUSTOM_FRAME_CONTROL can be enabled in
* config.h (it requires recompiling raylib). This way those steps are up to the user
*
* Note that enabling this flag invalidates some functions:
* - GetFrameTime()
* - SetTargetFPS()
* - GetFPS()
*
* Example originally created with raylib 4.0, 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) 2021-2025 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
namespace Examples.Core;
// NOTE: This example is intended to run against a raylib built with SUPPORT_CUSTOM_FRAME_CONTROL,
// where EndDrawing() does NOT swap buffers, wait or poll input, leaving those to the user code
// below. The stock raylib-cs native library is built WITHOUT that flag, so EndDrawing() still
// performs those steps and the manual SwapScreenBuffer()/WaitTime()/PollInputEvents() calls here
// run in addition to them. The port is kept faithful to upstream regardless.
[ExcludeFromBrowser("manual PollInputEvents/SwapScreenBuffer/WaitTime clashes with the emscripten main loop")]
public partial class CustomFrameControl : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Core / Custom Frame Control";
public string Title => "raylib [core] example - custom frame control";
// Custom timming variables
private double previousTime; // Previous time measure
private double currentTime; // Current time measure
private double updateDrawTime; // Update + Draw time
private double waitTime; // Wait time (if target fps required)
private float deltaTime; // Frame time (Update + Draw + Wait time)
private float timeCounter; // Accumulative time counter (seconds)
private float position; // Circle position
private bool pause; // Pause control flag
private int targetFPS; // Our initial target fps
public void Init()
{
previousTime = GetTime();
currentTime = 0.0;
updateDrawTime = 0.0;
waitTime = 0.0;
deltaTime = 0.0f;
timeCounter = 0.0f;
position = 0.0f;
pause = false;
targetFPS = 60;
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
#if !BROWSER
// NOTE: On non web platforms the PollInputEvents just works before the inputs checks
PollInputEvents(); // Poll input events (SUPPORT_CUSTOM_FRAME_CONTROL)
#endif
if (IsKeyPressed(KeyboardKey.Space))
{
pause = !pause;
}
if (IsKeyPressed(KeyboardKey.Up))
{
targetFPS += 20;
}
else if (IsKeyPressed(KeyboardKey.Down))
{
targetFPS -= 20;
}
if (targetFPS < 0)
{
targetFPS = 0;
}
if (!pause)
{
position += 200 * deltaTime; // We move at 200 pixels per second
if (position >= GetScreenWidth())
{
position = 0;
}
timeCounter += deltaTime; // We count time (seconds)
}
#if BROWSER
// NOTE: On web platform for some reason the PollInputEvents only works after the inputs
// check, so just call it after check all your inputs (on web)
PollInputEvents(); // Poll input events (SUPPORT_CUSTOM_FRAME_CONTROL)
#endif
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
for (int i = 0; i < GetScreenWidth() / 200; i++)
{
DrawRectangle(200 * i, 0, 1, GetScreenHeight(), Color.SkyBlue);
}
DrawCircle((int)position, GetScreenHeight() / 2 - 25, 50, Color.Red);
DrawText($"{timeCounter * 1000.0f:000} ms", (int)position - 40, GetScreenHeight() / 2 - 100, 20, Color.Maroon);
DrawText($"PosX: {position:000}", (int)position - 50, GetScreenHeight() / 2 + 40, 20, Color.Black);
DrawText("Circle is moving at a constant 200 pixels/sec,\nindependently of the frame rate.", 10, 10, 20, Color.DarkGray);
DrawText("PRESS SPACE to PAUSE MOVEMENT", 10, GetScreenHeight() - 60, 20, Color.Gray);
DrawText("PRESS UP | DOWN to CHANGE TARGET FPS", 10, GetScreenHeight() - 30, 20, Color.Gray);
DrawText($"TARGET FPS: {targetFPS}", GetScreenWidth() - 220, 10, 20, Color.Lime);
if (deltaTime != 0)
{
DrawText($"CURRENT FPS: {(int)(1.0f / deltaTime)}", GetScreenWidth() - 220, 40, 20, Color.Green);
}
EndDrawing();
// NOTE: In case raylib is configured to SUPPORT_CUSTOM_FRAME_CONTROL,
// Events polling, screen buffer swap and frame time control must be managed by the user
SwapScreenBuffer(); // Flip the back buffer to screen (front buffer)
currentTime = GetTime();
updateDrawTime = currentTime - previousTime;
if (targetFPS > 0) // We want a fixed frame rate
{
waitTime = (1.0f / (float)targetFPS) - updateDrawTime;
if (waitTime > 0.0)
{
WaitTime((float)waitTime);
currentTime = GetTime();
deltaTime = (float)(currentTime - previousTime);
}
}
else
{
deltaTime = (float)updateDrawTime; // Framerate could be variable
}
previousTime = currentTime;
//----------------------------------------------------------------------------------
}
public void Unload()
{
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [core] example - custom frame control");
// NOTE: Not calling SetTargetFPS(): this example manages frame timing manually with
// WaitTime() (SetTargetFPS/GetFrameTime/GetFPS are invalidated by SUPPORT_CUSTOM_FRAME_CONTROL)
//--------------------------------------------------------------------------------------
var game = new CustomFrameControl();
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,27 +1,41 @@
/*******************************************************************************************
*
* raylib [core] example - Custom logging
* raylib [core] example - custom logging
*
* This example has been created using raylib 2.1 (www.raylib.com)
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
* Example complexity rating: [★★★☆] 3/4
*
* Example originally created with raylib 2.5, last time updated with raylib 2.5
*
* Example contributed by Pablo Marcos Oltra (@pamarcos) and reviewed by Ramon Santamaria (@raysan5)
*
* Copyright (c) 2018 Pablo Marcos Oltra (@pamarcos) 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) 2018-2025 Pablo Marcos Oltra (@pamarcos) and Ramon Santamaria (@raysan5)
*
********************************************************************************************/
using System;
using System.Runtime.InteropServices;
using static Raylib_cs.Raylib;
namespace Examples.Core;
public unsafe class CustomLogging
public unsafe partial class CustomLogging : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Core / Custom Logging";
public string Title => "raylib [core] example - custom logging";
[UnmanagedCallersOnly(CallConvs = new[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })]
private static void LogCustom(int logLevel, sbyte* text, sbyte* args)
{
#if BROWSER
// WebAssembly can't invoke the C varargs vsprintf/vsnprintf that Logging.GetLogMessage relies on
// (the wasm runtime traps with "function signature mismatch"), so log the raw, unformatted text.
string message = Marshal.PtrToStringUTF8(new IntPtr(text)) ?? string.Empty;
#else
var message = Logging.GetLogMessage(new IntPtr(text), new IntPtr(args));
/*Console.ForegroundColor = (TraceLogLevel)logLevel switch
@ -36,50 +50,64 @@ public unsafe class CustomLogging
TraceLogLevel.LOG_NONE => ConsoleColor.White,
_ => throw new ArgumentOutOfRangeException(nameof(logLevel), logLevel, null)
};*/
#endif
Console.WriteLine($"Custom " + message);
// Console.ResetColor();
}
public void Init()
{
// First thing we do is setting our custom logger to ensure everything raylib logs
// will use our own logger instead of its internal one
SetTraceLogCallback(&LogCustom);
}
public void Update()
{
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
DrawText("Check out the console output to see the custom logger in action!", 60, 200, 20, Color.LightGray);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
#if !BROWSER
// Restore the default formatting logger on desktop. On WebAssembly we leave the (wasm-safe)
// custom logger in place — Logging.LogConsole formats via vsprintf, which traps on wasm.
SetTraceLogCallback(&Logging.LogConsole);
#endif
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
// First thing we do is setting our custom logger to ensure everything raylib logs
// will use our own logger instead of its internal one
SetTraceLogCallback(&LogCustom);
InitWindow(screenWidth, screenHeight, "raylib [core] example - custom logging");
SetTargetFPS(60);
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new CustomLogging();
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("Check out the console output to see the custom logger in action!", 60, 200, 20, Color.LightGray);
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow();
SetTraceLogCallback(&Logging.LogConsole);
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;

View file

@ -15,114 +15,141 @@
*
********************************************************************************************/
using System.Numerics;
namespace Examples.Core;
using static Raylib_cs.Raylib;
public class DeltaTime
public partial class DeltaTime : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
// The speed applied to both circles
private const float speed = 10.0f;
private const float circleRadius = 32.0f;
private int currentFps;
// Store the position for the both of the circles
private Vector2 deltaCircle;
private Vector2 frameCircle;
public string Name => "Core / Delta Time";
public string Title => "raylib [core] example - delta time";
public void Init()
{
currentFps = 60;
// Store the position for the both of the circles
deltaCircle = new Vector2(0, (float)screenHeight / 3.0f);
frameCircle = new Vector2(0, (float)screenHeight * (2.0f / 3.0f));
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
// Adjust the FPS target based on the mouse wheel
var mouseWheel = GetMouseWheelMove();
if (mouseWheel != 0)
{
currentFps += (int)mouseWheel;
if (currentFps < 0)
{
currentFps = 0;
}
SetTargetFPS(currentFps);
}
// GetFrameTime() returns the time it took to draw the last frame, in seconds (usually called delta time)
// Uses the delta time to make the circle look like it's moving at a "consistent" speed regardless of FPS
// Multiply by 6.0 (an arbitrary value) in order to make the speed
// visually closer to the other circle (at 60 fps), for comparison
deltaCircle.X += GetFrameTime() * 6.0f * speed;
// This circle can move faster or slower visually depending on the FPS
frameCircle.X += 0.1f * speed;
// If either circle is off the screen, reset it back to the start
if (deltaCircle.X > screenWidth)
{
deltaCircle.X = 0;
}
if (frameCircle.X > screenWidth)
{
frameCircle.X = 0;
}
// Reset both circles positions
if (IsKeyPressed(KeyboardKey.R))
{
deltaCircle.X = 0;
frameCircle.X = 0;
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
// Draw both circles to the screen
DrawCircleV(deltaCircle, circleRadius, Color.Red);
DrawCircleV(frameCircle, circleRadius, Color.Blue);
// Draw the help text
// Determine what help text to show depending on the current FPS target
var fpsText = "";
if (currentFps <= 0)
{
fpsText = $"FPS: unlimited ({GetFPS()})";
}
else
{
fpsText = $"FPS: {GetFPS()} (target: {currentFps})";
}
DrawText(fpsText, 10, 10, 20, Color.DarkGray);
DrawText($"Frame time: {GetFrameTime():F2} ms", 10, 30, 20, Color.DarkGray);
DrawText("Use the scroll wheel to change the fps limit, r to reset", 10, 50, 20, Color.DarkGray);
// Draw the text above the circles
DrawText("FUNC: x += GetFrameTime()*speed", 10, 90, 20, Color.Red);
DrawText("FUNC: x += speed", 10, 240, 20, Color.Blue);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [core] example - delta time");
int currentFps = 60;
Vector2 deltaCircle = new Vector2(0, (float)screenHeight / 3.0f);
Vector2 frameCircle = new Vector2(0, (float)screenHeight * (2.0f / 3.0f));
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
const float speed = 10.0f;
const float circleRadius = 32.0f;
var game = new DeltaTime();
game.Init();
// Main game loop
while (!WindowShouldClose())
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
// Adjust the FPS target based on the mouse wheel
float mouseWheel = GetMouseWheelMove();
if (mouseWheel != 0)
{
currentFps += (int)mouseWheel;
if (currentFps < 0)
{
currentFps = 0;
}
SetTargetFPS(currentFps);
}
// GetFrameTime() returns the time it took to draw the last frame, in seconds (usually called delta time)
// Uses the delta time to make the circle look like it's moving at a "consistent" speed regardless of FPS
// Multiply by 6.0 (an arbitrary value) in order to make the speed
// visually closer to the other circle (at 60 fps), for comparison
deltaCircle.X += GetFrameTime() * 6.0f * speed;
// This circle can move faster or slower visually depending on the FPS
frameCircle.X += 0.1f * speed;
// If either circle is off the screen, reset it back to the start
if (deltaCircle.X > screenWidth)
{
deltaCircle.X = 0;
}
if (frameCircle.X > screenWidth)
{
frameCircle.X = 0;
}
// Reset both circles positions
if (IsKeyPressed(KeyboardKey.R))
{
deltaCircle.X = 0;
frameCircle.X = 0;
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
// Draw both circles to the screen
DrawCircleV(deltaCircle, circleRadius, Color.Red);
DrawCircleV(frameCircle, circleRadius, Color.Blue);
// Draw the help text
// Determine what help text to show depending on the current FPS target
var fpsText = "";
if (currentFps <= 0)
{
fpsText = $"FPS: unlimited ({GetFPS()})";
}
else
{
fpsText = $"FPS: {GetFPS()} (target: {currentFps})";
}
DrawText(fpsText, 10, 10, 20, Color.DarkGray);
DrawText($"Frame time: {GetFrameTime():F2} ms", 10, 30, 20, Color.DarkGray);
DrawText("Use the scroll wheel to change the fps limit, r to reset", 10, 50, 20, Color.DarkGray);
// Draw the text above the circles
DrawText("FUNC: x += GetFrameTime()*speed", 10, 90, 20, Color.Red);
DrawText("FUNC: x += speed", 10, 240, 20, Color.Blue);
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow();
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;

View file

@ -0,0 +1,201 @@
/*******************************************************************************************
*
* raylib [core] example - directory files
*
* Example complexity rating: [★☆☆☆] 1/4
*
* Example originally created with raylib 5.5, last time updated with raylib 5.6
*
* Example contributed by Hugo ARNAL (@hugoarnal) 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 Hugo ARNAL (@hugoarnal)
*
********************************************************************************************/
// NOTE: The original example relies on raygui (GuiButton, GuiLabel, GuiListViewEx) for its UI.
// raygui is not part of the raylib-cs bindings, so the back button, directory label and file
// list view are reimplemented here with plain raylib primitives. The directory navigation
// behaviour (enter directories, go back) is preserved.
namespace Examples.Core;
public partial class DirectoryFiles : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
private const string FileFilter = "DIRS*;.png;.c";
public string Name => "Core / Directory Files";
public string Title => "raylib [core] example - directory files";
private string directory;
private FilePathList files;
private bool btnBackPressed;
private int listScrollIndex;
private int listItemActive;
private int listItemFocused;
public void Init()
{
directory = GetWorkingDirectoryAsString();
// Load file-paths on current working directory
// NOTE: LoadDirectoryFiles() loads files and directories by default,
// use LoadDirectoryFilesEx() for custom filters and recursive directories loading
//files = LoadDirectoryFiles(directory);
files = LoadDirectoryFilesEx(directory, FileFilter, false);
btnBackPressed = false;
listScrollIndex = 0;
listItemActive = -1;
listItemFocused = -1;
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
if (btnBackPressed)
{
directory = GetPrevDirectoryPath(directory);
UnloadDirectoryFiles(files);
files = LoadDirectoryFilesEx(directory, FileFilter, false);
listScrollIndex = 0;
listItemActive = -1;
listItemFocused = -1;
}
if ((listItemActive >= 0) && (listItemActive < (int)files.Count))
{
string selected = files[(uint)listItemActive];
bool isDirectory = DirectoryExists(selected);
if (isDirectory)
{
directory = selected;
UnloadDirectoryFiles(files);
files = LoadDirectoryFilesEx(directory, FileFilter, false);
listScrollIndex = 0;
listItemActive = -1;
listItemFocused = -1;
}
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
Vector2 mouse = GetMousePosition();
// Back button "<"
Rectangle backBounds = new Rectangle(40.0f, 10.0f, 48, 28);
bool backHover = CheckCollisionPointRec(mouse, backBounds);
DrawRectangleRec(backBounds, backHover ? Color.SkyBlue : Color.LightGray);
DrawRectangleLinesEx(backBounds, 1, Color.Gray);
int backTextWidth = MeasureText("<", 20);
DrawText("<", (int)(backBounds.X + (backBounds.Width - backTextWidth) / 2), (int)(backBounds.Y + 4), 20, Color.DarkGray);
btnBackPressed = backHover && IsMouseButtonReleased(MouseButton.Left);
// Current directory label
DrawText(directory, 40 + 48 + 10, 16, 20, Color.DarkGray);
// File list view
Rectangle listBounds = new Rectangle(0, 50, GetScreenWidth(), GetScreenHeight() - 50);
DrawRectangleRec(listBounds, Color.RayWhite);
DrawRectangleLinesEx(listBounds, 1, Color.Gray);
int count = (int)files.Count;
float rowHeight = 28;
int visibleRows = (int)(listBounds.Height / rowHeight);
bool mouseInList = CheckCollisionPointRec(mouse, listBounds);
if (mouseInList)
{
listScrollIndex -= (int)GetMouseWheelMove();
}
int maxScroll = Math.Max(0, count - visibleRows);
listScrollIndex = Math.Clamp(listScrollIndex, 0, maxScroll);
listItemFocused = -1;
for (int i = 0; i < visibleRows; i++)
{
int itemIndex = listScrollIndex + i;
if (itemIndex >= count)
{
break;
}
Rectangle rowRec = new Rectangle(listBounds.X + 1, listBounds.Y + 1 + i * rowHeight, listBounds.Width - 2, rowHeight);
bool rowHover = mouseInList && CheckCollisionPointRec(mouse, rowRec);
if (itemIndex == listItemActive)
{
DrawRectangleRec(rowRec, Fade(Color.SkyBlue, 0.7f));
}
else if (rowHover)
{
DrawRectangleRec(rowRec, Fade(Color.SkyBlue, 0.3f));
}
if (rowHover)
{
listItemFocused = itemIndex;
if (IsMouseButtonReleased(MouseButton.Left))
{
listItemActive = itemIndex;
}
}
DrawText(files[(uint)itemIndex], (int)rowRec.X + 40, (int)rowRec.Y + 8, 10, Color.DarkGray);
}
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadDirectoryFiles(files);
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [core] example - directory files");
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
var game = new DirectoryFiles();
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,83 +1,122 @@
/*******************************************************************************************
*
* raylib [core] example - Windows drop files
* raylib [core] example - drop files
*
* This example only works on platforms that support drag ref drop (Windows, Linux, OSX, Html5?)
* Example complexity rating: [★★☆☆] 2/4
*
* This example has been created using raylib 1.3 (www.raylib.com)
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
* NOTE: This example only works on platforms that support drag & drop (Windows, Linux, OSX, Html5?)
*
* Copyright (c) 2015 Ramon Santamaria (@raysan5)
* Example originally created with raylib 1.3, 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) 2015-2025 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
using static Raylib_cs.Raylib;
namespace Examples.Core;
public class DropFiles
[ExcludeFromBrowser]
public partial class DropFiles : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
public const int MaxFilepathRecorded = 4096;
public string Name => "Core / Drop Files";
public string Title => "raylib [core] example - drop files";
private List<string> filePaths;
public void Init()
{
// We will register a maximum of filepaths
filePaths = new();
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
if (IsFileDropped())
{
var droppedFiles = GetDroppedFiles();
for (var i = 0; i < droppedFiles.Length; i++)
{
if (filePaths.Count < (MaxFilepathRecorded - 1))
{
filePaths.Add(droppedFiles[i]);
}
}
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
if (filePaths.Count == 0)
{
DrawText("Drop your files to this window!", 100, 40, 20, Color.DarkGray);
}
else
{
DrawText("Dropped files:", 100, 40, 20, Color.DarkGray);
for (var i = 0; i < filePaths.Count; i++)
{
if (i % 2 == 0)
{
DrawRectangle(0, 85 + 40 * i, screenWidth, 40, Fade(Color.LightGray, 0.5f));
}
else
{
DrawRectangle(0, 85 + 40 * i, screenWidth, 40, Fade(Color.LightGray, 0.3f));
}
DrawText(filePaths[i], 120, 100 + 40 * i, 10, Color.Gray);
}
DrawText("Drop new files...", 100, 110 + 40 * filePaths.Count, 20, Color.DarkGray);
}
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [core] example - drop files");
string[] files = new string[0];
SetTargetFPS(60);
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new DropFiles();
game.Init();
// Main game loop
while (!WindowShouldClose())
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
if (IsFileDropped())
{
files = Raylib.GetDroppedFiles();
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
if (files.Length == 0)
{
DrawText("Drop your files to this window!", 100, 40, 20, Color.DarkGray);
}
else
{
DrawText("Dropped files:", 100, 40, 20, Color.DarkGray);
for (int i = 0; i < files.Length; i++)
{
if (i % 2 == 0)
{
DrawRectangle(0, 85 + 40 * i, screenWidth, 40, ColorAlpha(Color.LightGray, 0.5f));
}
else
{
DrawRectangle(0, 85 + 40 * i, screenWidth, 40, ColorAlpha(Color.LightGray, 0.3f));
}
DrawText(files[i], 120, 100 + 40 * i, 10, Color.Gray);
}
DrawText("Drop new files...", 100, 110 + 40 * files.Length, 20, Color.DarkGray);
}
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow();
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;

View file

@ -0,0 +1,169 @@
/*******************************************************************************************
*
* raylib [core] example - highdpi demo
*
* Example complexity rating: [★★☆☆] 2/4
*
* Example originally created with raylib 5.0, last time updated with raylib 5.5
*
* Example contributed by Jonathan Marler (@marler8997) 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 Jonathan Marler (@marler8997)
*
********************************************************************************************/
namespace Examples.Core;
[ExcludeFromBrowser("monitor/DPI APIs are meaningless on the fixed wasm canvas")]
public partial class HighDpiDemo : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Core / High DPI Demo";
public string Title => "raylib [core] example - highdpi demo";
public ConfigFlags ConfigFlags => ConfigFlags.HighDpiWindow | ConfigFlags.ResizableWindow;
private int logicalGridDescY;
private int logicalGridLabelY;
private int logicalGridTop;
private int logicalGridBottom;
private int pixelGridTop;
private int pixelGridBottom;
private int pixelGridLabelY;
private int pixelGridDescY;
private int cellSize;
private float cellSizePx;
public void Init()
{
SetWindowMinSize(450, 450);
logicalGridDescY = 120;
logicalGridLabelY = logicalGridDescY + 30;
logicalGridTop = logicalGridLabelY + 30;
logicalGridBottom = logicalGridTop + 80;
pixelGridTop = logicalGridBottom - 20;
pixelGridBottom = pixelGridTop + 80;
pixelGridLabelY = pixelGridBottom + 30;
pixelGridDescY = pixelGridLabelY + 30;
cellSize = 50;
cellSizePx = (float)cellSize;
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
int monitorCount = GetMonitorCount();
if ((monitorCount > 1) && IsKeyPressed(KeyboardKey.N))
{
SetWindowMonitor((GetCurrentMonitor() + 1) % monitorCount);
}
int currentMonitor = GetCurrentMonitor();
Vector2 dpiScale = GetWindowScaleDPI();
cellSizePx = ((float)cellSize) / dpiScale.X;
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
int windowCenter = GetScreenWidth() / 2;
DrawTextCenter($"Dpi Scale: {dpiScale.X:F6}", windowCenter, 30, 40, Color.DarkGray);
DrawTextCenter($"Monitor: {currentMonitor + 1}/{monitorCount} ([N] next monitor)", windowCenter, 70, 20, Color.LightGray);
DrawTextCenter($"Window is {GetScreenWidth()} \"logical points\" wide", windowCenter, logicalGridDescY, 20, Color.Orange);
bool odd = true;
for (int i = cellSize; i < GetScreenWidth(); i += cellSize, odd = !odd)
{
if (odd)
{
DrawRectangle(i, logicalGridTop, cellSize, logicalGridBottom - logicalGridTop, Color.Orange);
}
DrawTextCenter($"{i}", i, logicalGridLabelY, 10, Color.LightGray);
DrawLine(i, logicalGridLabelY + 10, i, logicalGridBottom, Color.Gray);
}
odd = true;
const int minTextSpace = 30;
int lastTextX = -minTextSpace;
for (int i = cellSize; i < GetRenderWidth(); i += cellSize, odd = !odd)
{
int x = (int)(((float)i) / dpiScale.X);
if (odd)
{
DrawRectangle(x, pixelGridTop, (int)cellSizePx, pixelGridBottom - pixelGridTop, new Color(0, 121, 241, 100));
}
DrawLine(x, pixelGridTop, (int)(((float)i) / dpiScale.X), pixelGridLabelY - 10, Color.Gray);
if ((x - lastTextX) >= minTextSpace)
{
DrawTextCenter($"{i}", x, pixelGridLabelY, 10, Color.LightGray);
lastTextX = x;
}
}
DrawTextCenter($"Window is {GetRenderWidth()} \"physical pixels\" wide", windowCenter, pixelGridDescY, 20, Color.Blue);
string text = "Can you see this?";
Vector2 size = MeasureTextEx(GetFontDefault(), text, 20, 3);
Vector2 pos = new Vector2(GetScreenWidth() - size.X - 5, GetScreenHeight() - size.Y - 5);
DrawTextEx(GetFontDefault(), text, pos, 20, 3, Color.LightGray);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
}
// Draw text centered on the given position
private static void DrawTextCenter(string text, int x, int y, int fontSize, Color color)
{
Vector2 size = MeasureTextEx(GetFontDefault(), text, (float)fontSize, 3);
Vector2 pos = new Vector2(x - size.X / 2, y - size.Y / 2);
DrawTextEx(GetFontDefault(), text, pos, (float)fontSize, 3, color);
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
SetConfigFlags(ConfigFlags.HighDpiWindow | ConfigFlags.ResizableWindow);
InitWindow(screenWidth, screenHeight, "raylib [core] example - highdpi demo");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new HighDpiDemo();
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,141 @@
/*******************************************************************************************
*
* raylib [core] example - highdpi testbed
*
* Example complexity rating: [★☆☆☆] 1/4
*
* Example originally created with raylib 6.0, last time updated with raylib 6.0
*
* Example contributed by Ramon Santamaria (@raysan5) 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 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
namespace Examples.Core;
[ExcludeFromBrowser("fullscreen/borderless/monitor APIs are meaningless on the fixed wasm canvas")]
public partial class HighDpiTestbed : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Core / High DPI Testbed";
public string Title => "raylib [core] example - highdpi testbed";
public ConfigFlags ConfigFlags => ConfigFlags.ResizableWindow | ConfigFlags.HighDpiWindow;
private Vector2 scaleDpi;
private Vector2 mousePos;
private int currentMonitor;
private Vector2 windowPos;
private int gridSpacing; // Grid spacing in pixels
public void Init()
{
scaleDpi = GetWindowScaleDPI();
mousePos = GetMousePosition();
currentMonitor = GetCurrentMonitor();
windowPos = GetWindowPosition();
gridSpacing = 40;
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
mousePos = GetMousePosition();
currentMonitor = GetCurrentMonitor();
scaleDpi = GetWindowScaleDPI();
windowPos = GetWindowPosition();
if (IsKeyPressed(KeyboardKey.Space))
{
ToggleBorderlessWindowed();
}
if (IsKeyPressed(KeyboardKey.F))
{
ToggleFullscreen();
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
// Draw grid
for (int h = 0; h < GetScreenHeight() / gridSpacing + 1; h++)
{
DrawText($"{h * gridSpacing:D2}", 4, h * gridSpacing - 4, 10, Color.Gray);
DrawLine(24, h * gridSpacing, GetScreenWidth(), h * gridSpacing, Color.LightGray);
}
for (int v = 0; v < GetScreenWidth() / gridSpacing + 1; v++)
{
DrawText($"{v * gridSpacing:D2}", v * gridSpacing - 10, 4, 10, Color.Gray);
DrawLine(v * gridSpacing, 20, v * gridSpacing, GetScreenHeight(), Color.LightGray);
}
// Draw UI info
DrawText($"CURRENT MONITOR: {currentMonitor + 1}/{GetMonitorCount()} ({GetMonitorWidth(currentMonitor)}x{GetMonitorHeight(currentMonitor)})", 50, 50, 20, Color.DarkGray);
DrawText($"WINDOW POSITION: {(int)windowPos.X}x{(int)windowPos.Y}", 50, 90, 20, Color.DarkGray);
DrawText($"SCREEN SIZE: {GetScreenWidth()}x{GetScreenHeight()}", 50, 130, 20, Color.DarkGray);
DrawText($"RENDER SIZE: {GetRenderWidth()}x{GetRenderHeight()}", 50, 170, 20, Color.DarkGray);
DrawText($"SCALE FACTOR: {scaleDpi.X:F2}x{scaleDpi.Y:F2}", 50, 210, 20, Color.Gray);
// Draw reference rectangles, top-left and bottom-right corners
DrawRectangle(0, 0, 30, 60, Color.Red);
DrawRectangle(GetScreenWidth() - 30, GetScreenHeight() - 60, 30, 60, Color.Blue);
// Draw mouse position
DrawCircleV(GetMousePosition(), 20, Color.Maroon);
DrawRectangleRec(new Rectangle(mousePos.X - 25, mousePos.Y, 50, 2), Color.Black);
DrawRectangleRec(new Rectangle(mousePos.X, mousePos.Y - 25, 2, 50), Color.Black);
DrawText($"[{GetMouseX()},{GetMouseY()}]", (int)mousePos.X - 44,
(mousePos.Y > GetScreenHeight() - 60) ? (int)mousePos.Y - 46 : (int)mousePos.Y + 30, 20, Color.Black);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
// TODO: Unload all loaded resources at this point
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
SetConfigFlags(ConfigFlags.ResizableWindow | ConfigFlags.HighDpiWindow);
InitWindow(screenWidth, screenHeight, "raylib [core] example - highdpi testbed");
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
var game = new HighDpiTestbed();
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,255 @@
/*******************************************************************************************
*
* raylib [core] example - input actions
*
* Example complexity rating: [★★☆☆] 2/4
*
* Example originally created with raylib 5.5, last time updated with raylib 5.6
*
* Example contributed by Jett (@JettMonstersGoBoom) 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 Jett (@JettMonstersGoBoom)
*
********************************************************************************************/
// Simple example for decoding input as actions, allowing remapping of input to different keys or gamepad buttons
// For example instead of using `IsKeyDown(KEY_LEFT)`, you can use `IsActionDown(ACTION_LEFT)`
// which can be reassigned to e.g. KEY_A and also assigned to a gamepad button. the action will trigger with either gamepad or keys
namespace Examples.Core;
public partial class InputActions : IExample
{
//----------------------------------------------------------------------------------
// Types and Structures Definition
//----------------------------------------------------------------------------------
private enum ActionType
{
NoAction = 0,
ActionUp,
ActionDown,
ActionLeft,
ActionRight,
ActionFire,
MaxAction
}
// Key and button inputs
private struct ActionInput
{
public KeyboardKey Key;
public GamepadButton Button;
}
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Core / Input Actions";
public string Title => "raylib [core] example - input actions";
private int gamepadIndex; // Gamepad default index
private ActionInput[] actionInputs;
private int actionSet;
private bool releaseAction;
private Vector2 position;
private Vector2 size;
public void Init()
{
gamepadIndex = 0;
actionInputs = new ActionInput[(int)ActionType.MaxAction];
// Set default actions
actionSet = 0;
SetActionsDefault();
releaseAction = false;
position = new Vector2(400.0f, 200.0f);
size = new Vector2(40.0f, 40.0f);
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
gamepadIndex = 0; // Set gamepad being checked
if (IsActionDown(ActionType.ActionUp))
{
position.Y -= 2;
}
if (IsActionDown(ActionType.ActionDown))
{
position.Y += 2;
}
if (IsActionDown(ActionType.ActionLeft))
{
position.X -= 2;
}
if (IsActionDown(ActionType.ActionRight))
{
position.X += 2;
}
if (IsActionPressed(ActionType.ActionFire))
{
position.X = (screenWidth - size.X) / 2;
position.Y = (screenHeight - size.Y) / 2;
}
// Register release action for one frame
releaseAction = false;
if (IsActionReleased(ActionType.ActionFire))
{
releaseAction = true;
}
// Switch control scheme by pressing TAB
if (IsKeyPressed(KeyboardKey.Tab))
{
actionSet = (actionSet == 0) ? 1 : 0;
if (actionSet == 0)
{
SetActionsDefault();
}
else
{
SetActionsCursor();
}
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.Gray);
DrawRectangleV(position, size, releaseAction ? Color.Blue : Color.Red);
DrawText((actionSet == 0) ? "Current input set: WASD (default)" : "Current input set: Arrow keys", 10, 10, 20, Color.White);
DrawText("Use TAB key to toggles Actions keyset", 10, 50, 20, Color.Green);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
}
//----------------------------------------------------------------------------------
// Module Functions Definition
//----------------------------------------------------------------------------------
// Check action key/button pressed
// NOTE: Combines key pressed and gamepad button pressed in one action
private bool IsActionPressed(ActionType action)
{
bool result = false;
if (action < ActionType.MaxAction)
{
result = (IsKeyPressed(actionInputs[(int)action].Key) || IsGamepadButtonPressed(gamepadIndex, actionInputs[(int)action].Button));
}
return result;
}
// Check action key/button released
// NOTE: Combines key released and gamepad button released in one action
private bool IsActionReleased(ActionType action)
{
bool result = false;
if (action < ActionType.MaxAction)
{
result = (IsKeyReleased(actionInputs[(int)action].Key) || IsGamepadButtonReleased(gamepadIndex, actionInputs[(int)action].Button));
}
return result;
}
// Check action key/button down
// NOTE: Combines key down and gamepad button down in one action
private bool IsActionDown(ActionType action)
{
bool result = false;
if (action < ActionType.MaxAction)
{
result = (IsKeyDown(actionInputs[(int)action].Key) || IsGamepadButtonDown(gamepadIndex, actionInputs[(int)action].Button));
}
return result;
}
// Set the "default" keyset
// NOTE: Here WASD and gamepad buttons on the left side for movement
private void SetActionsDefault()
{
actionInputs[(int)ActionType.ActionUp].Key = KeyboardKey.W;
actionInputs[(int)ActionType.ActionDown].Key = KeyboardKey.S;
actionInputs[(int)ActionType.ActionLeft].Key = KeyboardKey.A;
actionInputs[(int)ActionType.ActionRight].Key = KeyboardKey.D;
actionInputs[(int)ActionType.ActionFire].Key = KeyboardKey.Space;
actionInputs[(int)ActionType.ActionUp].Button = GamepadButton.LeftFaceUp;
actionInputs[(int)ActionType.ActionDown].Button = GamepadButton.LeftFaceDown;
actionInputs[(int)ActionType.ActionLeft].Button = GamepadButton.LeftFaceLeft;
actionInputs[(int)ActionType.ActionRight].Button = GamepadButton.LeftFaceRight;
actionInputs[(int)ActionType.ActionFire].Button = GamepadButton.RightFaceDown;
}
// Set the "alternate" keyset
// NOTE: Here cursor keys and gamepad buttons on the right side for movement
private void SetActionsCursor()
{
actionInputs[(int)ActionType.ActionUp].Key = KeyboardKey.Up;
actionInputs[(int)ActionType.ActionDown].Key = KeyboardKey.Down;
actionInputs[(int)ActionType.ActionLeft].Key = KeyboardKey.Left;
actionInputs[(int)ActionType.ActionRight].Key = KeyboardKey.Right;
actionInputs[(int)ActionType.ActionFire].Key = KeyboardKey.Space;
actionInputs[(int)ActionType.ActionUp].Button = GamepadButton.RightFaceUp;
actionInputs[(int)ActionType.ActionDown].Button = GamepadButton.RightFaceDown;
actionInputs[(int)ActionType.ActionLeft].Button = GamepadButton.RightFaceLeft;
actionInputs[(int)ActionType.ActionRight].Button = GamepadButton.RightFaceRight;
actionInputs[(int)ActionType.ActionFire].Button = GamepadButton.LeftFaceDown;
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [core] example - input actions");
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
var game = new InputActions();
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,32 +1,31 @@
/*******************************************************************************************
*
* raylib [core] example - input gamepad
*
* Example complexity rating: [★☆☆☆] 1/4
*
* NOTE: This example requires a Gamepad connected to the system
* raylib is configured to work with the following gamepads:
* - Xbox 360 Controller (Xbox 360, Xbox One)
* - PLAYSTATION(R)3 Controller
* Check raylib.h for buttons configuration
*
* Example originally created with raylib 1.1, 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) 2013-2025 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
using System.Numerics;
using static Raylib_cs.Raylib;
*
* raylib [core] example - input gamepad
*
* Example complexity rating: [★☆☆☆] 1/4
*
* NOTE: This example requires a Gamepad connected to the system
* raylib is configured to work with the following gamepads:
* - Xbox 360 Controller (Xbox 360, Xbox One)
* - PLAYSTATION(R)3 Controller
* Check raylib.h for buttons configuration
*
* Example originally created with raylib 1.1, 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) 2013-2025 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
namespace Examples.Core;
public class InputGamepad
public partial class InputGamepad : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
// NOTE: Gamepad name ID depends on drivers and OS
// These are some possible names the gamepads could have.
public const string XBOX_ALIAS_1 = "xbox";
@ -34,320 +33,503 @@ public class InputGamepad
public const string PS_ALIAS_1 = "playstation";
public const string PS_ALIAS_2 = "sony";
// Set axis deadzones
private const float leftStickDeadzoneX = 0.1f;
private const float leftStickDeadzoneY = 0.1f;
private const float rightStickDeadzoneX = 0.1f;
private const float rightStickDeadzoneY = 0.1f;
private const float leftTriggerDeadzone = -0.9f;
private const float rightTriggerDeadzone = -0.9f;
private Texture2D texPs3Pad;
private Texture2D texXboxPad;
private Rectangle vibrateButton;
private int gamepad; // which gamepad to display
public string Name => "Core / Input Gamepad";
public string Title => "raylib [core] example - input gamepad";
public ConfigFlags ConfigFlags => ConfigFlags.Msaa4xHint;
public void Init()
{
texPs3Pad = LoadTexture("resources/ps3.png");
texXboxPad = LoadTexture("resources/xbox.png");
vibrateButton = new Rectangle();
gamepad = 0; // which gamepad to display
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
if (IsKeyPressed(KeyboardKey.Left) && gamepad > 0)
{
gamepad--;
}
if (IsKeyPressed(KeyboardKey.Right))
{
gamepad++;
}
var mousePosition = GetMousePosition();
vibrateButton = new Rectangle(10, 70.0f + 20 * GetGamepadAxisCount(gamepad) + 20, 75, 24);
if (IsMouseButtonPressed(MouseButton.Left) && CheckCollisionPointRec(mousePosition, vibrateButton))
{
SetGamepadVibration(gamepad, 1.0f, 1.0f, 1.0f);
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
if (IsGamepadAvailable(gamepad))
{
var gamepadName = GetGamepadName_(gamepad);
DrawText($"GP{gamepad}: {gamepadName}", 10, 10, 10, Color.Black);
// Get axis values
var leftStickX = GetGamepadAxisMovement(gamepad, GamepadAxis.LeftX);
var leftStickY = GetGamepadAxisMovement(gamepad, GamepadAxis.LeftY);
var rightStickX = GetGamepadAxisMovement(gamepad, GamepadAxis.RightX);
var rightStickY = GetGamepadAxisMovement(gamepad, GamepadAxis.RightY);
var leftTrigger = GetGamepadAxisMovement(gamepad, GamepadAxis.LeftTrigger);
var rightTrigger = GetGamepadAxisMovement(gamepad, GamepadAxis.RightTrigger);
// Calculate deadzones
if (leftStickX > -leftStickDeadzoneX && leftStickX < leftStickDeadzoneX)
{
leftStickX = 0.0f;
}
if (leftStickY > -leftStickDeadzoneY && leftStickY < leftStickDeadzoneY)
{
leftStickY = 0.0f;
}
if (rightStickX > -rightStickDeadzoneX && rightStickX < rightStickDeadzoneX)
{
rightStickX = 0.0f;
}
if (rightStickY > -rightStickDeadzoneY && rightStickY < rightStickDeadzoneY)
{
rightStickY = 0.0f;
}
if (leftTrigger < leftTriggerDeadzone)
{
leftTrigger = -1.0f;
}
if (rightTrigger < rightTriggerDeadzone)
{
rightTrigger = -1.0f;
}
if (gamepadName.Contains(XBOX_ALIAS_1, StringComparison.OrdinalIgnoreCase) ||
gamepadName.Contains(XBOX_ALIAS_2, StringComparison.OrdinalIgnoreCase))
{
DrawTexture(texXboxPad, 0, 0, Color.DarkGray);
// Draw buttons: xbox home
if (IsGamepadButtonDown(gamepad, GamepadButton.Middle))
{
DrawCircle(394, 89, 19, Color.Red);
}
// Draw buttons: basic
if (IsGamepadButtonDown(gamepad, GamepadButton.MiddleRight))
{
DrawCircle(436, 150, 9, Color.Red);
}
if (IsGamepadButtonDown(gamepad, GamepadButton.MiddleLeft))
{
DrawCircle(352, 150, 9, Color.Red);
}
if (IsGamepadButtonDown(gamepad, GamepadButton.RightFaceLeft))
{
DrawCircle(501, 151, 15, Color.Blue);
}
if (IsGamepadButtonDown(gamepad, GamepadButton.RightFaceDown))
{
DrawCircle(536, 187, 15, Color.Lime);
}
if (IsGamepadButtonDown(gamepad, GamepadButton.RightFaceRight))
{
DrawCircle(572, 151, 15, Color.Maroon);
}
if (IsGamepadButtonDown(gamepad, GamepadButton.RightFaceUp))
{
DrawCircle(536, 115, 15, Color.Gold);
}
// Draw buttons: d-pad
DrawRectangle(317, 202, 19, 71, Color.Black);
DrawRectangle(293, 228, 69, 19, Color.Black);
if (IsGamepadButtonDown(gamepad, GamepadButton.LeftFaceUp))
{
DrawRectangle(317, 202, 19, 26, Color.Red);
}
if (IsGamepadButtonDown(gamepad, GamepadButton.LeftFaceDown))
{
DrawRectangle(317, 202 + 45, 19, 26, Color.Red);
}
if (IsGamepadButtonDown(gamepad, GamepadButton.LeftFaceLeft))
{
DrawRectangle(292, 228, 25, 19, Color.Red);
}
if (IsGamepadButtonDown(gamepad, GamepadButton.LeftFaceRight))
{
DrawRectangle(292 + 44, 228, 26, 19, Color.Red);
}
// Draw buttons: left-right back
if (IsGamepadButtonDown(gamepad, GamepadButton.LeftTrigger1))
{
DrawCircle(259, 61, 20, Color.Red);
}
if (IsGamepadButtonDown(gamepad, GamepadButton.RightTrigger1))
{
DrawCircle(536, 61, 20, Color.Red);
}
// Draw axis: left joystick
var leftGamepadColor = Color.Black;
if (IsGamepadButtonDown(gamepad, GamepadButton.LeftThumb))
{
leftGamepadColor = Color.Red;
}
DrawCircle(259, 152, 39, Color.Black);
DrawCircle(259, 152, 34, Color.LightGray);
DrawCircle(259 + (int)(leftStickX * 20), 152 + (int)(leftStickY * 20), 25, leftGamepadColor);
// Draw axis: right joystick
var rightGamepadColor = Color.Black;
if (IsGamepadButtonDown(gamepad, GamepadButton.RightThumb))
{
rightGamepadColor = Color.Red;
}
DrawCircle(461, 237, 38, Color.Black);
DrawCircle(461, 237, 33, Color.LightGray);
DrawCircle(461 + (int)(rightStickX * 20), 237 + (int)(rightStickY * 20), 25, rightGamepadColor);
// Draw axis: left-right triggers
DrawRectangle(170, 30, 15, 70, Color.Gray);
DrawRectangle(604, 30, 15, 70, Color.Gray);
DrawRectangle(170, 30, 15, (int)(((1 + leftTrigger) / 2) * 70), Color.Red);
DrawRectangle(604, 30, 15, (int)(((1 + rightTrigger) / 2) * 70), Color.Red);
}
else if (gamepadName.Contains(PS_ALIAS_1, StringComparison.OrdinalIgnoreCase) || gamepadName.Contains(PS_ALIAS_2, StringComparison.OrdinalIgnoreCase))
{
DrawTexture(texPs3Pad, 0, 0, Color.DarkGray);
// Draw buttons: ps
if (IsGamepadButtonDown(gamepad, GamepadButton.Middle))
{
DrawCircle(396, 222, 13, Color.Red);
}
// Draw buttons: basic
if (IsGamepadButtonDown(gamepad, GamepadButton.MiddleLeft))
{
DrawRectangle(328, 170, 32, 13, Color.Red);
}
if (IsGamepadButtonDown(gamepad, GamepadButton.MiddleRight))
{
DrawTriangle(
new Vector2(436, 168),
new Vector2(436, 185),
new Vector2(464, 177),
Color.Red
);
}
if (IsGamepadButtonDown(gamepad, GamepadButton.RightFaceUp))
{
DrawCircle(557, 144, 13, Color.Lime);
}
if (IsGamepadButtonDown(gamepad, GamepadButton.RightFaceRight))
{
DrawCircle(586, 173, 13, Color.Red);
}
if (IsGamepadButtonDown(gamepad, GamepadButton.RightFaceDown))
{
DrawCircle(557, 203, 13, Color.Violet);
}
if (IsGamepadButtonDown(gamepad, GamepadButton.RightFaceLeft))
{
DrawCircle(527, 173, 13, Color.Pink);
}
// Draw buttons: d-pad
DrawRectangle(225, 132, 24, 84, Color.Black);
DrawRectangle(195, 161, 84, 25, Color.Black);
if (IsGamepadButtonDown(gamepad, GamepadButton.LeftFaceUp))
{
DrawRectangle(225, 132, 24, 29, Color.Red);
}
if (IsGamepadButtonDown(gamepad, GamepadButton.LeftFaceDown))
{
DrawRectangle(225, 132 + 54, 24, 30, Color.Red);
}
if (IsGamepadButtonDown(gamepad, GamepadButton.LeftFaceLeft))
{
DrawRectangle(195, 161, 30, 25, Color.Red);
}
if (IsGamepadButtonDown(gamepad, GamepadButton.LeftFaceRight))
{
DrawRectangle(195 + 54, 161, 30, 25, Color.Red);
}
// Draw buttons: left-right back buttons
if (IsGamepadButtonDown(gamepad, GamepadButton.LeftTrigger1))
{
DrawCircle(239, 82, 20, Color.Red);
}
if (IsGamepadButtonDown(gamepad, GamepadButton.RightTrigger1))
{
DrawCircle(557, 82, 20, Color.Red);
}
// Draw axis: left joystick
var leftGamepadColor = Color.Black;
if (IsGamepadButtonDown(gamepad, GamepadButton.LeftThumb))
{
leftGamepadColor = Color.Red;
}
DrawCircle(319, 255, 35, Color.Black);
DrawCircle(319, 255, 31, Color.LightGray);
DrawCircle(319 + (int)(leftStickX * 20), 255 + (int)(leftStickY * 20), 25, leftGamepadColor);
// Draw axis: right joystick
var rightGamepadColor = Color.Black;
if (IsGamepadButtonDown(gamepad, GamepadButton.RightThumb))
{
rightGamepadColor = Color.Red;
}
DrawCircle(475, 255, 35, Color.Black);
DrawCircle(475, 255, 31, Color.LightGray);
DrawCircle(475 + (int)(rightStickX * 20), 255 + (int)(rightStickY * 20), 25, rightGamepadColor);
// Draw axis: left-right triggers
DrawRectangle(169, 48, 15, 70, Color.Gray);
DrawRectangle(611, 48, 15, 70, Color.Gray);
DrawRectangle(169, 48, 15, (int)(((1 + leftTrigger) / 2) * 70), Color.Red);
DrawRectangle(611, 48, 15, (int)(((1 + rightTrigger) / 2) * 70), Color.Red);
}
else
{
// Draw background: generic
DrawRectangleRounded(new Rectangle(175, 110, 460, 220), 0.3f, 16, Color.DarkGray);
// Draw buttons: basic
DrawCircle(365, 170, 12, Color.RayWhite);
DrawCircle(405, 170, 12, Color.RayWhite);
DrawCircle(445, 170, 12, Color.RayWhite);
DrawCircle(516, 191, 17, Color.RayWhite);
DrawCircle(551, 227, 17, Color.RayWhite);
DrawCircle(587, 191, 17, Color.RayWhite);
DrawCircle(551, 155, 17, Color.RayWhite);
if (IsGamepadButtonDown(gamepad, GamepadButton.MiddleLeft))
{
DrawCircle(365, 170, 10, Color.Red);
}
if (IsGamepadButtonDown(gamepad, GamepadButton.Middle))
{
DrawCircle(405, 170, 10, Color.Green);
}
if (IsGamepadButtonDown(gamepad, GamepadButton.MiddleRight))
{
DrawCircle(445, 170, 10, Color.Blue);
}
if (IsGamepadButtonDown(gamepad, GamepadButton.RightFaceLeft))
{
DrawCircle(516, 191, 15, Color.Gold);
}
if (IsGamepadButtonDown(gamepad, GamepadButton.RightFaceDown))
{
DrawCircle(551, 227, 15, Color.Blue);
}
if (IsGamepadButtonDown(gamepad, GamepadButton.RightFaceRight))
{
DrawCircle(587, 191, 15, Color.Green);
}
if (IsGamepadButtonDown(gamepad, GamepadButton.RightFaceUp))
{
DrawCircle(551, 155, 15, Color.Red);
}
// Draw buttons: d-pad
DrawRectangle(245, 145, 28, 88, Color.RayWhite);
DrawRectangle(215, 174, 88, 29, Color.RayWhite);
DrawRectangle(247, 147, 24, 84, Color.Black);
DrawRectangle(217, 176, 84, 25, Color.Black);
if (IsGamepadButtonDown(gamepad, GamepadButton.LeftFaceUp))
{
DrawRectangle(247, 147, 24, 29, Color.Red);
}
if (IsGamepadButtonDown(gamepad, GamepadButton.LeftFaceDown))
{
DrawRectangle(247, 147 + 54, 24, 30, Color.Red);
}
if (IsGamepadButtonDown(gamepad, GamepadButton.LeftFaceLeft))
{
DrawRectangle(217, 176, 30, 25, Color.Red);
}
if (IsGamepadButtonDown(gamepad, GamepadButton.LeftFaceRight))
{
DrawRectangle(217 + 54, 176, 30, 25, Color.Red);
}
// Draw buttons: left-right back
DrawRectangleRounded(new Rectangle(215, 98, 100, 10), 0.5f, 16, Color.DarkGray);
DrawRectangleRounded(new Rectangle(495, 98, 100, 10), 0.5f, 16, Color.DarkGray);
if (IsGamepadButtonDown(gamepad, GamepadButton.LeftTrigger1))
{
DrawRectangleRounded(new Rectangle(215, 98, 100, 10), 0.5f, 16, Color.Red);
}
if (IsGamepadButtonDown(gamepad, GamepadButton.RightTrigger1))
{
DrawRectangleRounded(new Rectangle(495, 98, 100, 10), 0.5f, 16, Color.Red);
}
// Draw axis: left joystick
var leftGamepadColor = Color.Black;
if (IsGamepadButtonDown(gamepad, GamepadButton.LeftThumb))
{
leftGamepadColor = Color.Red;
}
DrawCircle(345, 260, 40, Color.Black);
DrawCircle(345, 260, 35, Color.LightGray);
DrawCircle(345 + (int)(leftStickX * 20), 260 + (int)(leftStickY * 20), 25, leftGamepadColor);
// Draw axis: right joystick
var rightGamepadColor = Color.Black;
if (IsGamepadButtonDown(gamepad, GamepadButton.RightThumb))
{
rightGamepadColor = Color.Red;
}
DrawCircle(465, 260, 40, Color.Black);
DrawCircle(465, 260, 35, Color.LightGray);
DrawCircle(465 + (int)(rightStickX * 20), 260 + (int)(rightStickY * 20), 25, rightGamepadColor);
// Draw axis: left-right triggers
DrawRectangle(151, 110, 15, 70, Color.Gray);
DrawRectangle(644, 110, 15, 70, Color.Gray);
DrawRectangle(151, 110, 15, (int)(((1 + leftTrigger) / 2) * 70), Color.Red);
DrawRectangle(644, 110, 15, (int)(((1 + rightTrigger) / 2) * 70), Color.Red);
}
DrawText($"DETECTED AXIS [{GetGamepadAxisCount(gamepad)}]:", 10, 50, 10, Color.Maroon);
for (var i = 0; i < GetGamepadAxisCount(gamepad); i++)
{
DrawText(
$"AXIS {i}: {GetGamepadAxisMovement(gamepad, (GamepadAxis)i):F2}",
20,
70 + 20 * i,
10,
Color.DarkGray
);
}
// Draw vibrate button
DrawRectangleRec(vibrateButton, Color.SkyBlue);
DrawText("VIBRATE", (int)(vibrateButton.X + 14), (int)(vibrateButton.Y + 1), 10, Color.DarkGray);
if (GetGamepadButtonPressed() != (int)GamepadButton.Unknown)
{
DrawText($"DETECTED BUTTON: {GetGamepadButtonPressed()}", 10, 430, 10, Color.Red);
}
else
{
DrawText("DETECTED BUTTON: NONE", 10, 430, 10, Color.Gray);
}
}
else
{
DrawText($"GP{gamepad}: NOT DETECTED", 10, 10, 10, Color.Gray);
DrawTexture(texXboxPad, 0, 0, Color.LightGray);
}
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadTexture(texPs3Pad);
UnloadTexture(texXboxPad);
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
SetConfigFlags(ConfigFlags.Msaa4xHint); // Set MSAA 4X hint before windows creation
// Set MSAA 4X hint before windows creation
SetConfigFlags(ConfigFlags.Msaa4xHint);
InitWindow(screenWidth, screenHeight, "raylib [core] example - gamepad input");
InitWindow(screenWidth, screenHeight, "raylib [core] example - input gamepad");
Texture2D texPs3Pad = LoadTexture("resources/ps3.png");
Texture2D texXboxPad = LoadTexture("resources/xbox.png");
SetTargetFPS(60);
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
int gamepad = 0;
Rectangle vibrateButton = new Rectangle();
var game = new InputGamepad();
game.Init();
// Main game loop
while (!WindowShouldClose())
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
if (IsKeyPressed(KeyboardKey.Left) && gamepad > 0)
{
gamepad--;
}
if (IsKeyPressed(KeyboardKey.Right))
{
gamepad++;
}
Vector2 mousePosition = GetMousePosition();
vibrateButton = new Rectangle(10, 70.0f + 20 * GetGamepadAxisCount(gamepad) + 20, 75, 24);
if (IsMouseButtonPressed(MouseButton.Left) && CheckCollisionPointRec(mousePosition, vibrateButton))
{
SetGamepadVibration(gamepad, 1.0f, 1.0f, 1.0f);
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
if (IsGamepadAvailable(gamepad))
{
string gamepadName = GetGamepadName_(gamepad);
DrawText($"GP{gamepad}: {gamepadName}", 10, 10, 10, Color.Black);
if (gamepadName.Contains(XBOX_ALIAS_1, StringComparison.OrdinalIgnoreCase) ||
gamepadName.Contains(XBOX_ALIAS_2, StringComparison.OrdinalIgnoreCase))
{
DrawTexture(texXboxPad, 0, 0, Color.DarkGray);
// Draw buttons: xbox home
if (IsGamepadButtonDown(gamepad, GamepadButton.Middle))
{
DrawCircle(394, 89, 19, Color.Red);
}
// Draw buttons: basic
if (IsGamepadButtonDown(gamepad, GamepadButton.MiddleRight))
{
DrawCircle(436, 150, 9, Color.Red);
}
if (IsGamepadButtonDown(gamepad, GamepadButton.MiddleLeft))
{
DrawCircle(352, 150, 9, Color.Red);
}
if (IsGamepadButtonDown(gamepad, GamepadButton.RightFaceLeft))
{
DrawCircle(501, 151, 15, Color.Blue);
}
if (IsGamepadButtonDown(gamepad, GamepadButton.RightFaceDown))
{
DrawCircle(536, 187, 15, Color.Lime);
}
if (IsGamepadButtonDown(gamepad, GamepadButton.RightFaceRight))
{
DrawCircle(572, 151, 15, Color.Maroon);
}
if (IsGamepadButtonDown(gamepad, GamepadButton.RightFaceUp))
{
DrawCircle(536, 115, 15, Color.Gold);
}
// Draw buttons: d-pad
DrawRectangle(317, 202, 19, 71, Color.Black);
DrawRectangle(293, 228, 69, 19, Color.Black);
if (IsGamepadButtonDown(gamepad, GamepadButton.LeftFaceUp))
{
DrawRectangle(317, 202, 19, 26, Color.Red);
}
if (IsGamepadButtonDown(gamepad, GamepadButton.LeftFaceDown))
{
DrawRectangle(317, 202 + 45, 19, 26, Color.Red);
}
if (IsGamepadButtonDown(gamepad, GamepadButton.LeftFaceLeft))
{
DrawRectangle(292, 228, 25, 19, Color.Red);
}
if (IsGamepadButtonDown(gamepad, GamepadButton.LeftFaceRight))
{
DrawRectangle(292 + 44, 228, 26, 19, Color.Red);
}
// Draw buttons: left-right back
if (IsGamepadButtonDown(gamepad, GamepadButton.LeftTrigger1))
{
DrawCircle(259, 61, 20, Color.Red);
}
if (IsGamepadButtonDown(gamepad, GamepadButton.RightTrigger1))
{
DrawCircle(536, 61, 20, Color.Red);
}
// Draw axis: left joystick
DrawCircle(259, 152, 39, Color.Black);
DrawCircle(259, 152, 34, Color.LightGray);
DrawCircle(
259 + (int)(GetGamepadAxisMovement(gamepad, GamepadAxis.LeftX) * 20),
152 + (int)(GetGamepadAxisMovement(gamepad, GamepadAxis.LeftY) * 20),
25,
Color.Black
);
// Draw axis: right joystick
DrawCircle(461, 237, 38, Color.Black);
DrawCircle(461, 237, 33, Color.LightGray);
DrawCircle(
461 + (int)(GetGamepadAxisMovement(gamepad, GamepadAxis.RightX) * 20),
237 + (int)(GetGamepadAxisMovement(gamepad, GamepadAxis.RightY) * 20),
25, Color.Black
);
// Draw axis: left-right triggers
float leftTriggerX = GetGamepadAxisMovement(gamepad, GamepadAxis.LeftTrigger);
float rightTriggerX = GetGamepadAxisMovement(gamepad, GamepadAxis.RightTrigger);
DrawRectangle(170, 30, 15, 70, Color.Gray);
DrawRectangle(604, 30, 15, 70, Color.Gray);
DrawRectangle(170, 30, 15, (int)(((1.0f + leftTriggerX) / 2.0f) * 70), Color.Red);
DrawRectangle(604, 30, 15, (int)(((1.0f + rightTriggerX) / 2.0f) * 70), Color.Red);
}
else if (gamepadName.Contains(PS_ALIAS_1, StringComparison.OrdinalIgnoreCase) || gamepadName.Contains(PS_ALIAS_2, StringComparison.OrdinalIgnoreCase))
{
DrawTexture(texPs3Pad, 0, 0, Color.DarkGray);
// Draw buttons: ps
if (IsGamepadButtonDown(gamepad, GamepadButton.Middle))
{
DrawCircle(396, 222, 13, Color.Red);
}
// Draw buttons: basic
if (IsGamepadButtonDown(gamepad, GamepadButton.MiddleLeft))
{
DrawRectangle(328, 170, 32, 13, Color.Red);
}
if (IsGamepadButtonDown(gamepad, GamepadButton.MiddleRight))
{
DrawTriangle(
new Vector2(436, 168),
new Vector2(436, 185),
new Vector2(464, 177),
Color.Red
);
}
if (IsGamepadButtonDown(gamepad, GamepadButton.RightFaceUp))
{
DrawCircle(557, 144, 13, Color.Lime);
}
if (IsGamepadButtonDown(gamepad, GamepadButton.RightFaceRight))
{
DrawCircle(586, 173, 13, Color.Red);
}
if (IsGamepadButtonDown(gamepad, GamepadButton.RightFaceDown))
{
DrawCircle(557, 203, 13, Color.Violet);
}
if (IsGamepadButtonDown(gamepad, GamepadButton.RightFaceLeft))
{
DrawCircle(527, 173, 13, Color.Pink);
}
// Draw buttons: d-pad
DrawRectangle(225, 132, 24, 84, Color.Black);
DrawRectangle(195, 161, 84, 25, Color.Black);
if (IsGamepadButtonDown(gamepad, GamepadButton.LeftFaceUp))
{
DrawRectangle(225, 132, 24, 29, Color.Red);
}
if (IsGamepadButtonDown(gamepad, GamepadButton.LeftFaceDown))
{
DrawRectangle(225, 132 + 54, 24, 30, Color.Red);
}
if (IsGamepadButtonDown(gamepad, GamepadButton.LeftFaceLeft))
{
DrawRectangle(195, 161, 30, 25, Color.Red);
}
if (IsGamepadButtonDown(gamepad, GamepadButton.LeftFaceRight))
{
DrawRectangle(195 + 54, 161, 30, 25, Color.Red);
}
// Draw buttons: left-right back buttons
if (IsGamepadButtonDown(gamepad, GamepadButton.LeftTrigger1))
{
DrawCircle(239, 82, 20, Color.Red);
}
if (IsGamepadButtonDown(gamepad, GamepadButton.RightTrigger1))
{
DrawCircle(557, 82, 20, Color.Red);
}
// Draw axis: left joystick
DrawCircle(319, 255, 35, Color.Black);
DrawCircle(319, 255, 31, Color.LightGray);
DrawCircle(
319 + (int)(GetGamepadAxisMovement(gamepad, GamepadAxis.LeftX) * 20),
255 + (int)(GetGamepadAxisMovement(gamepad, GamepadAxis.LeftY) * 20),
25,
Color.Black
);
// Draw axis: right joystick
DrawCircle(475, 255, 35, Color.Black);
DrawCircle(475, 255, 31, Color.LightGray);
DrawCircle(
475 + (int)(GetGamepadAxisMovement(gamepad, GamepadAxis.RightX) * 20),
255 + (int)(GetGamepadAxisMovement(gamepad, GamepadAxis.RightY) * 20),
25,
Color.Black
);
// Draw axis: left-right triggers
float leftTriggerX = GetGamepadAxisMovement(gamepad, GamepadAxis.LeftTrigger);
float rightTriggerX = GetGamepadAxisMovement(gamepad, GamepadAxis.RightTrigger);
DrawRectangle(169, 48, 15, 70, Color.Gray);
DrawRectangle(611, 48, 15, 70, Color.Gray);
DrawRectangle(169, 48, 15, (int)(((1.0f - leftTriggerX) / 2.0f) * 70), Color.Red);
DrawRectangle(611, 48, 15, (int)(((1.0f - rightTriggerX) / 2.0f) * 70), Color.Red);
}
else
{
DrawText("- GENERIC GAMEPAD -", 280, 180, 20, Color.Gray);
// TODO: Draw generic gamepad
}
DrawText($"DETECTED AXIS [{GetGamepadAxisCount(gamepad)}]:", 10, 50, 10, Color.Maroon);
for (int i = 0; i < GetGamepadAxisCount(gamepad); i++)
{
DrawText(
$"AXIS {i}: {GetGamepadAxisMovement(gamepad, (GamepadAxis)i)}",
20,
70 + 20 * i,
10,
Color.DarkGray
);
}
DrawRectangleRec(vibrateButton, Color.SkyBlue);
DrawText("VIBRATE", (int)(vibrateButton.X + 14), (int)(vibrateButton.Y + 1), 10, Color.DarkGray);
if (GetGamepadButtonPressed() != (int)GamepadButton.Unknown)
{
DrawText($"DETECTED BUTTON: {GetGamepadButtonPressed()}", 10, 430, 10, Color.Red);
}
else
{
DrawText("DETECTED BUTTON: NONE", 10, 430, 10, Color.Gray);
}
}
else
{
DrawText($"GP{gamepad}: NOT DETECTED", 10, 10, 10, Color.Gray);
DrawTexture(texXboxPad, 0, 0, Color.LightGray);
}
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadTexture(texPs3Pad);
UnloadTexture(texXboxPad);
CloseWindow();
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;

View file

@ -13,150 +13,178 @@
*
********************************************************************************************/
using System.Numerics;
using static Raylib_cs.Raylib;
namespace Examples.Core;
public class InputGestures
public partial class InputGestures : IExample
{
public const int MaxGestureStrings = 20;
private const int screenWidth = 800;
private const int screenHeight = 450;
private Vector2 touchPosition;
private Rectangle touchArea;
private int gesturesCount;
private string[] gestureStrings;
private Gesture currentGesture;
private Gesture lastGesture;
public string Name => "Core / Input Gestures";
public string Title => "raylib [core] example - input gestures";
public void Init()
{
touchPosition = new(0, 0);
touchArea = new(220, 10, screenWidth - 230, screenHeight - 20);
gesturesCount = 0;
gestureStrings = new string[MaxGestureStrings];
currentGesture = Gesture.None;
lastGesture = Gesture.None;
// SetGesturesEnabled(0b0000000000001001); // Enable only some gestures to be detected
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
lastGesture = currentGesture;
currentGesture = GetGestureDetected();
touchPosition = GetTouchPosition(0);
if (CheckCollisionPointRec(touchPosition, touchArea) && (currentGesture != Gesture.None))
{
if (currentGesture != lastGesture)
{
// Store gesture string
switch ((Gesture)currentGesture)
{
case Gesture.Tap:
gestureStrings[gesturesCount] = "GESTURE TAP";
break;
case Gesture.DoubleTap:
gestureStrings[gesturesCount] = "GESTURE DOUBLETAP";
break;
case Gesture.Hold:
gestureStrings[gesturesCount] = "GESTURE HOLD";
break;
case Gesture.Drag:
gestureStrings[gesturesCount] = "GESTURE DRAG";
break;
case Gesture.SwipeRight:
gestureStrings[gesturesCount] = "GESTURE SWIPE RIGHT";
break;
case Gesture.SwipeLeft:
gestureStrings[gesturesCount] = "GESTURE SWIPE LEFT";
break;
case Gesture.SwipeUp:
gestureStrings[gesturesCount] = "GESTURE SWIPE UP";
break;
case Gesture.SwipeDown:
gestureStrings[gesturesCount] = "GESTURE SWIPE DOWN";
break;
case Gesture.PinchIn:
gestureStrings[gesturesCount] = "GESTURE PINCH IN";
break;
case Gesture.PinchOut:
gestureStrings[gesturesCount] = "GESTURE PINCH OUT";
break;
default:
break;
}
gesturesCount++;
// Reset gestures strings
if (gesturesCount >= MaxGestureStrings)
{
for (var i = 0; i < MaxGestureStrings; i++)
{
gestureStrings[i] = " ";
}
gesturesCount = 0;
}
}
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
DrawRectangleRec(touchArea, Color.Gray);
DrawRectangle(225, 15, screenWidth - 240, screenHeight - 30, Color.RayWhite);
DrawText("GESTURES TEST AREA", screenWidth - 270, screenHeight - 40, 20, Fade(Color.Gray, 0.5f));
for (var i = 0; i < gesturesCount; i++)
{
if (i % 2 == 0)
{
DrawRectangle(10, 30 + 20 * i, 200, 20, Fade(Color.LightGray, 0.5f));
}
else
{
DrawRectangle(10, 30 + 20 * i, 200, 20, Fade(Color.LightGray, 0.3f));
}
if (i < gesturesCount - 1)
{
DrawText(gestureStrings[i], 35, 36 + 20 * i, 10, Color.DarkGray);
}
else
{
DrawText(gestureStrings[i], 35, 36 + 20 * i, 10, Color.Maroon);
}
}
DrawRectangleLines(10, 29, 200, screenHeight - 50, Color.Gray);
DrawText("DETECTED GESTURES", 50, 15, 10, Color.Gray);
if (currentGesture != Gesture.None)
{
DrawCircleV(touchPosition, 30, Color.Maroon);
}
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [core] example - input gestures");
Vector2 touchPosition = new(0, 0);
Rectangle touchArea = new(220, 10, screenWidth - 230, screenHeight - 20);
int gesturesCount = 0;
string[] gestureStrings = new string[MaxGestureStrings];
Gesture currentGesture = Gesture.None;
Gesture lastGesture = Gesture.None;
// SetGesturesEnabled(0b0000000000001001); // Enable only some gestures to be detected
SetTargetFPS(60);
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new InputGestures();
game.Init();
// Main game loop
while (!WindowShouldClose())
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
lastGesture = currentGesture;
currentGesture = GetGestureDetected();
touchPosition = GetTouchPosition(0);
if (CheckCollisionPointRec(touchPosition, touchArea) && (currentGesture != Gesture.None))
{
if (currentGesture != lastGesture)
{
// Store gesture string
switch ((Gesture)currentGesture)
{
case Gesture.Tap:
gestureStrings[gesturesCount] = "GESTURE TAP";
break;
case Gesture.DoubleTap:
gestureStrings[gesturesCount] = "GESTURE DOUBLETAP";
break;
case Gesture.Hold:
gestureStrings[gesturesCount] = "GESTURE HOLD";
break;
case Gesture.Drag:
gestureStrings[gesturesCount] = "GESTURE DRAG";
break;
case Gesture.SwipeRight:
gestureStrings[gesturesCount] = "GESTURE SWIPE RIGHT";
break;
case Gesture.SwipeLeft:
gestureStrings[gesturesCount] = "GESTURE SWIPE LEFT";
break;
case Gesture.SwipeUp:
gestureStrings[gesturesCount] = "GESTURE SWIPE UP";
break;
case Gesture.SwipeDown:
gestureStrings[gesturesCount] = "GESTURE SWIPE DOWN";
break;
case Gesture.PinchIn:
gestureStrings[gesturesCount] = "GESTURE PINCH IN";
break;
case Gesture.PinchOut:
gestureStrings[gesturesCount] = "GESTURE PINCH OUT";
break;
default:
break;
}
gesturesCount++;
// Reset gestures strings
if (gesturesCount >= MaxGestureStrings)
{
for (int i = 0; i < MaxGestureStrings; i++)
{
gestureStrings[i] = " ";
}
gesturesCount = 0;
}
}
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
DrawRectangleRec(touchArea, Color.Gray);
DrawRectangle(225, 15, screenWidth - 240, screenHeight - 30, Color.RayWhite);
DrawText("GESTURES TEST AREA", screenWidth - 270, screenHeight - 40, 20, ColorAlpha(Color.Gray, 0.5f));
for (int i = 0; i < gesturesCount; i++)
{
if (i % 2 == 0)
{
DrawRectangle(10, 30 + 20 * i, 200, 20, ColorAlpha(Color.LightGray, 0.5f));
}
else
{
DrawRectangle(10, 30 + 20 * i, 200, 20, ColorAlpha(Color.LightGray, 0.3f));
}
if (i < gesturesCount - 1)
{
DrawText(gestureStrings[i], 35, 36 + 20 * i, 10, Color.DarkGray);
}
else
{
DrawText(gestureStrings[i], 35, 36 + 20 * i, 10, Color.Maroon);
}
}
DrawRectangleLines(10, 29, 200, screenHeight - 50, Color.Gray);
DrawText("DETECTED GESTURES", 50, 15, 10, Color.Gray);
if (currentGesture != Gesture.None)
{
DrawCircleV(touchPosition, 30, Color.Maroon);
}
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow();
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}
}

View file

@ -1,406 +1,450 @@
/*******************************************************************************************
*
* raylib [core] example - input gestures testbed
*
* Example complexity rating: [★★★☆] 3/4
*
* Example originally created with raylib 5.0, last time updated with raylib 6.0
*
* Example contributed by ubkp (@ubkp) 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) 2023-2025 ubkp (@ubkp)
*
********************************************************************************************/
using System.Numerics;
*
* raylib [core] example - input gestures testbed
*
* Example complexity rating: [★★★☆] 3/4
*
* Example originally created with raylib 5.0, last time updated with raylib 6.0
*
* Example contributed by ubkp (@ubkp) 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) 2023-2025 ubkp (@ubkp)
*
********************************************************************************************/
namespace Examples.Core;
using static Raylib_cs.Raylib;
public class InputGesturesTestBed
public partial class InputGesturesTestBed : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
public const int GESTURE_LOG_SIZE = 20;
public const int MAX_TOUCH_COUNT = 32;
//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
public string Name => "Core / Input Gestures Test Bed";
public static int Main()
public string Title => "raylib [core] example - input gestures testbed";
private Vector2 messagePosition;
// Last gesture variables definitions
private Gesture lastGesture;
private Vector2 lastGesturePosition;
// Gesture log variables definitions
// NOTE: The gesture log uses an array (as an inverted circular queue) to store the performed gestures
private string[] gestureLog;
// NOTE: The index for the inverted circular queue (moving from last to first direction, then looping around)
private int gestureLogIndex;
private Gesture previousGesture;
// Log mode values:
// - 0 shows repeated events
// - 1 hides repeated events
// - 2 shows repeated events but hide hold events
// - 3 hides repeated events and hide hold events
private int logMode;
private Color gestureColor;
private Rectangle logButton1;
private Rectangle logButton2;
private Vector2 gestureLogPosition;
// Protractor variables definitions
private float angleLength;
private float currentAngleDegrees;
private Vector2 finalVector;
private Vector2 protractorPosition;
public void Init()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [core] example - input gestures testbed");
Vector2 messagePosition = new Vector2(160, 7);
messagePosition = new Vector2(160, 7);
// Last gesture variables definitions
Gesture lastGesture = 0;
Vector2 lastGesturePosition = new Vector2(165, 130);
lastGesture = 0;
lastGesturePosition = new Vector2(165, 130);
// Gesture log variables definitions
// NOTE: The gesture log uses an array (as an inverted circular queue) to store the performed gestures
string[] gestureLog = new string[GESTURE_LOG_SIZE + 1];
for (int i = 0; i < GESTURE_LOG_SIZE; i++)
gestureLog = new string[GESTURE_LOG_SIZE + 1];
for (var i = 0; i < GESTURE_LOG_SIZE; i++)
{
gestureLog[i] = new string(new char[12]);
}
;
// NOTE: The index for the inverted circular queue (moving from last to first direction, then looping around)
int gestureLogIndex = GESTURE_LOG_SIZE;
Gesture previousGesture = 0;
gestureLogIndex = GESTURE_LOG_SIZE;
previousGesture = 0;
// Log mode values:
// - 0 shows repeated events
// - 1 hides repeated events
// - 2 shows repeated events but hide hold events
// - 3 hides repeated events and hide hold events
int logMode = 1;
logMode = 1;
Color gestureColor = new Color(0, 0, 0, 255);
Rectangle logButton1 = new Rectangle(53, 7, 48, 26);
Rectangle logButton2 = new Rectangle(108, 7, 36, 26);
Vector2 gestureLogPosition = new Vector2(10, 10);
gestureColor = new Color(0, 0, 0, 255);
logButton1 = new Rectangle(53, 7, 48, 26);
logButton2 = new Rectangle(108, 7, 36, 26);
gestureLogPosition = new Vector2(10, 10);
// Protractor variables definitions
float angleLength = 90.0f;
float currentAngleDegrees = 0.0f;
Vector2 finalVector = new Vector2(0.0f, 0.0f);
Vector2 protractorPosition = new Vector2(266.0f, 315.0f);
angleLength = 90.0f;
currentAngleDegrees = 0.0f;
finalVector = new Vector2(0.0f, 0.0f);
protractorPosition = new Vector2(266.0f, 315.0f);
}
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
public void Update()
{
// Update
//--------------------------------------------------------------------------------------
// Handle common gestures data
int i, ii; // Iterators that will be reused by all for loops
var currentGesture = GetGestureDetected();
var currentDragDegrees = GetGestureDragAngle();
var currentPitchDegrees = GetGesturePinchAngle();
var touchCount = GetTouchPointCount();
// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
// Handle last gesture
if ((currentGesture != 0) && ((int)currentGesture != 4) && (currentGesture != previousGesture))
{
// Update
//--------------------------------------------------------------------------------------
// Handle common gestures data
int i, ii; // Iterators that will be reused by all for loops
Gesture currentGesture = GetGestureDetected();
float currentDragDegrees = GetGestureDragAngle();
float currentPitchDegrees = GetGesturePinchAngle();
int touchCount = GetTouchPointCount();
lastGesture = currentGesture; // Filter the meaningful gestures (1, 2, 8 to 512) for the display
}
// Handle last gesture
if ((currentGesture != 0) && ((int)currentGesture != 4) && (currentGesture != previousGesture))
// Handle gesture log
if (IsMouseButtonReleased(MouseButton.Left))
{
if (CheckCollisionPointRec(GetMousePosition(), logButton1))
{
lastGesture = currentGesture; // Filter the meaningful gestures (1, 2, 8 to 512) for the display
}
// Handle gesture log
if (IsMouseButtonReleased(MouseButton.Left))
{
if (CheckCollisionPointRec(GetMousePosition(), logButton1))
switch (logMode)
{
switch (logMode)
{
case 3:
logMode = 2;
break;
case 2:
logMode = 3;
break;
case 1:
logMode = 0;
break;
default:
logMode = 1;
break;
}
}
else if (CheckCollisionPointRec(GetMousePosition(), logButton2))
{
switch (logMode)
{
case 3:
logMode = 1;
break;
case 2:
logMode = 0;
break;
case 1:
logMode = 3;
break;
default:
logMode = 2;
break;
}
case 3:
logMode = 2;
break;
case 2:
logMode = 3;
break;
case 1:
logMode = 0;
break;
default:
logMode = 1;
break;
}
}
int fillLog = 0; // Gate variable to be used to allow or not the gesture log to be filled
if (currentGesture != 0)
else if (CheckCollisionPointRec(GetMousePosition(), logButton2))
{
if (logMode == 3) // 3 hides repeated events and hide hold events
switch (logMode)
{
if ((((int)currentGesture != 4) && (currentGesture != previousGesture)) || ((int)currentGesture < 3))
{
fillLog = 1;
}
case 3:
logMode = 1;
break;
case 2:
logMode = 0;
break;
case 1:
logMode = 3;
break;
default:
logMode = 2;
break;
}
else if (logMode == 2) // 2 shows repeated events but hide hold events
{
if ((int)currentGesture != 4)
{
fillLog = 1;
}
}
else if (logMode == 1) // 1 hides repeated events
{
if (currentGesture != previousGesture)
{
fillLog = 1;
}
}
else // 0 shows repeated events
}
}
var fillLog = 0; // Gate variable to be used to allow or not the gesture log to be filled
if (currentGesture != 0)
{
if (logMode == 3) // 3 hides repeated events and hide hold events
{
if ((((int)currentGesture != 4) && (currentGesture != previousGesture)) || ((int)currentGesture < 3))
{
fillLog = 1;
}
}
if (fillLog > 0) // If one of the conditions from logMode was met, fill the gesture log
else if (logMode == 2) // 2 shows repeated events but hide hold events
{
previousGesture = currentGesture;
gestureColor = GetGestureColor((int)currentGesture);
if (gestureLogIndex <= 0)
if ((int)currentGesture != 4)
{
gestureLogIndex = GESTURE_LOG_SIZE;
}
gestureLogIndex--;
// Copy the gesture respective name to the gesture log array
gestureLog[gestureLogIndex] = GetGestureName((int)currentGesture);
}
// Handle protractor
if ((int)currentGesture > 255)
{
currentAngleDegrees = currentPitchDegrees; // Pinch In and Pinch Out
}
else if ((int)currentGesture > 15)
{
currentAngleDegrees = currentDragDegrees; // Swipe Right, Swipe Left, Swipe Up and Swipe Down
}
else if (currentGesture > 0)
{
currentAngleDegrees = 0.0f; // Tap, Doubletap, Hold and Grab
}
float currentAngleRadians =
((currentAngleDegrees + 90.0f) * MathF.PI / 180); // Convert the current angle to Radians
// Calculate the final vector for display
finalVector = new Vector2(
(angleLength * MathF.Sin(currentAngleRadians)) + protractorPosition.X,
(angleLength * MathF.Cos(currentAngleRadians)) + protractorPosition.Y
)
;
// Handle touch and mouse pointer points
Vector2[] touchPosition = new Vector2[MAX_TOUCH_COUNT];
Vector2 mousePosition = Vector2.Zero;
if (currentGesture != Gesture.None)
{
if (touchCount != 0)
{
for (i = 0; i < touchCount; i++)
{
touchPosition[i] = GetTouchPosition(i); // Fill the touch positions
}
}
else
{
mousePosition = GetMousePosition();
fillLog = 1;
}
}
//--------------------------------------------------------------------------------------
// Draw
//--------------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
// Draw common elements
DrawText("*", (int)messagePosition.X + 5, (int)messagePosition.Y + 5, 10, Color.Black);
DrawText("Example optimized for Web/HTML5\non Smartphones with Touch Screen.", (int)messagePosition.X + 15,
(int)messagePosition.Y + 5, 10, Color.Black);
DrawText("*", (int)messagePosition.X + 5, (int)messagePosition.Y + 35, 10, Color.Black);
DrawText("While running on Desktop Web Browsers,\ninspect and turn on Touch Emulation.",
(int)messagePosition.X + 15, (int)messagePosition.Y + 35, 10, Color.Black);
// Draw last gesture
DrawText("Last gesture", (int)lastGesturePosition.X + 33, (int)lastGesturePosition.Y - 47, 20, Color.Black);
DrawText("Swipe Tap Pinch Touch", (int)lastGesturePosition.X + 17,
(int)lastGesturePosition.Y - 18, 10, Color.Black);
DrawRectangle((int)lastGesturePosition.X + 20, (int)lastGesturePosition.Y, 20, 20,
lastGesture == Gesture.SwipeUp ? Color.Red : Color.LightGray);
DrawRectangle((int)lastGesturePosition.X, (int)lastGesturePosition.Y + 20, 20, 20,
lastGesture == Gesture.SwipeLeft ? Color.Red : Color.LightGray);
DrawRectangle((int)lastGesturePosition.X + 40, (int)lastGesturePosition.Y + 20, 20, 20,
lastGesture == Gesture.SwipeRight ? Color.Red : Color.LightGray);
DrawRectangle((int)lastGesturePosition.X + 20, (int)lastGesturePosition.Y + 40, 20, 20,
lastGesture == Gesture.SwipeDown ? Color.Red : Color.LightGray);
DrawCircle((int)lastGesturePosition.X + 80, (int)lastGesturePosition.Y + 16, 10,
lastGesture == Gesture.Tap ? Color.Blue : Color.LightGray);
DrawRing(new Vector2(
lastGesturePosition.X + 103, lastGesturePosition.Y + 16
), 6.0f, 11.0f, 0.0f, 360.0f, 0, lastGesture == Gesture.Drag ? Color.Lime : Color.LightGray);
DrawCircle((int)lastGesturePosition.X + 80, (int)lastGesturePosition.Y + 43, 10,
lastGesture == Gesture.DoubleTap ? Color.SkyBlue : Color.LightGray);
DrawCircle((int)lastGesturePosition.X + 103, (int)lastGesturePosition.Y + 43, 10,
lastGesture == Gesture.DoubleTap ? Color.SkyBlue : Color.LightGray);
DrawTriangle(new Vector2(
lastGesturePosition.X + 122, lastGesturePosition.Y + 16
), new Vector2(
lastGesturePosition.X + 137, lastGesturePosition.Y + 26
), new Vector2(
lastGesturePosition.X + 137, lastGesturePosition.Y + 6
), lastGesture == Gesture.PinchOut ? Color.Orange : Color.LightGray);
DrawTriangle(new Vector2(
lastGesturePosition.X + 147, lastGesturePosition.Y + 6
), new Vector2(
lastGesturePosition.X + 147, lastGesturePosition.Y + 26
), new Vector2(
lastGesturePosition.X + 162, lastGesturePosition.Y + 16
), lastGesture == Gesture.PinchOut ? Color.Orange : Color.Gray);
DrawTriangle(new Vector2(
lastGesturePosition.X + 125, lastGesturePosition.Y + 33
), new Vector2(
lastGesturePosition.X + 125, lastGesturePosition.Y + 53
), new Vector2(
lastGesturePosition.X + 140, lastGesturePosition.Y + 43
), lastGesture == Gesture.PinchIn ? Color.Violet : Color.LightGray);
DrawTriangle(new Vector2(
lastGesturePosition.X + 144, lastGesturePosition.Y + 43
), new Vector2(
lastGesturePosition.X + 159, lastGesturePosition.Y + 53
), new Vector2(
lastGesturePosition.X + 159, lastGesturePosition.Y + 33
), lastGesture == Gesture.PinchIn ? Color.Violet : Color.LightGray);
for (i = 0; i < 4; i++)
else if (logMode == 1) // 1 hides repeated events
{
DrawCircle((int)lastGesturePosition.X + 180, (int)lastGesturePosition.Y + 7 + i * 15, 5,
touchCount <= i ? Color.LightGray : gestureColor);
}
// Draw gesture log
DrawText("Log", (int)gestureLogPosition.X, (int)gestureLogPosition.Y, 20, Color.Black);
// Loop in both directions to print the gesture log array in the inverted order (and looping around if the index started somewhere in the middle)
for (i = 0, ii = gestureLogIndex; i < GESTURE_LOG_SIZE; i++, ii = (ii + 1) % GESTURE_LOG_SIZE)
{
DrawText(gestureLog[ii], (int)gestureLogPosition.X, (int)gestureLogPosition.Y + 410 - i * 20, 20,
(i == 0 ? gestureColor : Color.LightGray));
}
Color logButton1Color, logButton2Color;
switch (logMode)
{
case 3:
logButton1Color = Color.Maroon;
logButton2Color = Color.Maroon;
break;
case 2:
logButton1Color = Color.Gray;
logButton2Color = Color.Maroon;
break;
case 1:
logButton1Color = Color.Maroon;
logButton2Color = Color.Gray;
break;
default:
logButton1Color = Color.Gray;
logButton2Color = Color.Gray;
break;
}
DrawRectangleRec(logButton1, logButton1Color);
DrawText("Hide", (int)logButton1.X + 7, (int)logButton1.Y + 3, 10, Color.White);
DrawText("Repeat", (int)logButton1.X + 7, (int)logButton1.Y + 13, 10, Color.White);
DrawRectangleRec(logButton2, logButton2Color);
DrawText("Hide", (int)logButton1.X + 62, (int)logButton1.Y + 3, 10, Color.White);
DrawText("Hold", (int)logButton1.X + 62, (int)logButton1.Y + 13, 10, Color.White);
// Draw protractor
DrawText("Angle", (int)protractorPosition.X + 55, (int)protractorPosition.Y + 76, 10, Color.Black);
// Note: Official it's using raylibs functions for string manipulation. But in C# it will end up in an unsafe handling.
string angleString = currentAngleDegrees.ToString("F3");
int angleStringDot = angleString.IndexOf('.');
string angleStringTrim = angleString.Substring(0, angleStringDot + 3);
DrawText(angleStringTrim, (int)protractorPosition.X + 55, (int)protractorPosition.Y + 92, 20, gestureColor);
DrawCircleV(protractorPosition, 80.0f, Color.White);
DrawLineEx(new Vector2(
protractorPosition.X - 90, protractorPosition.Y
), new Vector2(
protractorPosition.X + 90, protractorPosition.Y
), 3.0f, Color.LightGray);
DrawLineEx(new Vector2(
protractorPosition.X, protractorPosition.Y - 90
), new Vector2(
protractorPosition.X, protractorPosition.Y + 90
), 3.0f, Color.LightGray);
DrawLineEx(new Vector2(
protractorPosition.X - 80, protractorPosition.Y - 45
), new Vector2(
protractorPosition.X + 80, protractorPosition.Y + 45
), 3.0f, Color.Green);
DrawLineEx(new Vector2(
protractorPosition.X - 80, protractorPosition.Y + 45
), new Vector2(
protractorPosition.X + 80, protractorPosition.Y - 45
), 3.0f, Color.Green);
DrawText("0", (int)protractorPosition.X + 96, (int)protractorPosition.Y - 9, 20, Color.Black);
DrawText("30", (int)protractorPosition.X + 74, (int)protractorPosition.Y - 68, 20, Color.Black);
DrawText("90", (int)protractorPosition.X - 11, (int)protractorPosition.Y - 110, 20, Color.Black);
DrawText("150", (int)protractorPosition.X - 100, (int)protractorPosition.Y - 68, 20, Color.Black);
DrawText("180", (int)protractorPosition.X - 124, (int)protractorPosition.Y - 9, 20, Color.Black);
DrawText("210", (int)protractorPosition.X - 100, (int)protractorPosition.Y + 50, 20, Color.Black);
DrawText("270", (int)protractorPosition.X - 18, (int)protractorPosition.Y + 92, 20, Color.Black);
DrawText("330", (int)protractorPosition.X + 72, (int)protractorPosition.Y + 50, 20, Color.Black);
if (currentAngleDegrees != 0.0f)
{
DrawLineEx(protractorPosition, finalVector, 3.0f, gestureColor);
}
// Draw touch and mouse pointer points
if (currentGesture != Gesture.None)
{
if (touchCount != 0)
if (currentGesture != previousGesture)
{
for (i = 0; i < touchCount; i++)
{
DrawCircleV(touchPosition[i], 50.0f, Fade(gestureColor, 0.5f));
DrawCircleV(touchPosition[i], 5.0f, gestureColor);
}
if (touchCount == 2)
{
DrawLineEx(touchPosition[0], touchPosition[1], (((int)currentGesture == 512) ? 8.0f : 12.0f),
gestureColor);
}
}
else
{
DrawCircleV(mousePosition, 35.0f, Fade(gestureColor, 0.5f));
DrawCircleV(mousePosition, 5.0f, gestureColor);
fillLog = 1;
}
}
EndDrawing();
//--------------------------------------------------------------------------------------
else // 0 shows repeated events
{
fillLog = 1;
}
}
if (fillLog > 0) // If one of the conditions from logMode was met, fill the gesture log
{
previousGesture = currentGesture;
gestureColor = GetGestureColor((int)currentGesture);
if (gestureLogIndex <= 0)
{
gestureLogIndex = GESTURE_LOG_SIZE;
}
gestureLogIndex--;
// Copy the gesture respective name to the gesture log array
gestureLog[gestureLogIndex] = GetGestureName((int)currentGesture);
}
// Handle protractor
if ((int)currentGesture > 255)
{
currentAngleDegrees = currentPitchDegrees; // Pinch In and Pinch Out
}
else if ((int)currentGesture > 15)
{
currentAngleDegrees = currentDragDegrees; // Swipe Right, Swipe Left, Swipe Up and Swipe Down
}
else if (currentGesture > 0)
{
currentAngleDegrees = 0.0f; // Tap, Doubletap, Hold and Grab
}
var currentAngleRadians =
((currentAngleDegrees + 90.0f) * MathF.PI / 180); // Convert the current angle to Radians
// Calculate the final vector for display
finalVector = new Vector2(
(angleLength * MathF.Sin(currentAngleRadians)) + protractorPosition.X,
(angleLength * MathF.Cos(currentAngleRadians)) + protractorPosition.Y
);
// Handle touch and mouse pointer points
var touchPosition = new Vector2[MAX_TOUCH_COUNT];
var mousePosition = Vector2.Zero;
if (currentGesture != Gesture.None)
{
if (touchCount != 0)
{
for (i = 0; i < touchCount; i++)
{
touchPosition[i] = GetTouchPosition(i); // Fill the touch positions
}
}
else
{
mousePosition = GetMousePosition();
}
}
//--------------------------------------------------------------------------------------
// Draw
//--------------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
// Draw common elements
DrawText("*", (int)messagePosition.X + 5, (int)messagePosition.Y + 5, 10, Color.Black);
DrawText("Example optimized for Web/HTML5\non Smartphones with Touch Screen.", (int)messagePosition.X + 15,
(int)messagePosition.Y + 5, 10, Color.Black);
DrawText("*", (int)messagePosition.X + 5, (int)messagePosition.Y + 35, 10, Color.Black);
DrawText("While running on Desktop Web Browsers,\ninspect and turn on Touch Emulation.",
(int)messagePosition.X + 15, (int)messagePosition.Y + 35, 10, Color.Black);
// Draw last gesture
DrawText("Last gesture", (int)lastGesturePosition.X + 33, (int)lastGesturePosition.Y - 47, 20, Color.Black);
DrawText("Swipe Tap Pinch Touch", (int)lastGesturePosition.X + 17,
(int)lastGesturePosition.Y - 18, 10, Color.Black);
DrawRectangle((int)lastGesturePosition.X + 20, (int)lastGesturePosition.Y, 20, 20,
lastGesture == Gesture.SwipeUp ? Color.Red : Color.LightGray);
DrawRectangle((int)lastGesturePosition.X, (int)lastGesturePosition.Y + 20, 20, 20,
lastGesture == Gesture.SwipeLeft ? Color.Red : Color.LightGray);
DrawRectangle((int)lastGesturePosition.X + 40, (int)lastGesturePosition.Y + 20, 20, 20,
lastGesture == Gesture.SwipeRight ? Color.Red : Color.LightGray);
DrawRectangle((int)lastGesturePosition.X + 20, (int)lastGesturePosition.Y + 40, 20, 20,
lastGesture == Gesture.SwipeDown ? Color.Red : Color.LightGray);
DrawCircle((int)lastGesturePosition.X + 80, (int)lastGesturePosition.Y + 16, 10,
lastGesture == Gesture.Tap ? Color.Blue : Color.LightGray);
DrawRing(new Vector2(
lastGesturePosition.X + 103, lastGesturePosition.Y + 16
), 6.0f, 11.0f, 0.0f, 360.0f, 0, lastGesture == Gesture.Drag ? Color.Lime : Color.LightGray);
DrawCircle((int)lastGesturePosition.X + 80, (int)lastGesturePosition.Y + 43, 10,
lastGesture == Gesture.DoubleTap ? Color.SkyBlue : Color.LightGray);
DrawCircle((int)lastGesturePosition.X + 103, (int)lastGesturePosition.Y + 43, 10,
lastGesture == Gesture.DoubleTap ? Color.SkyBlue : Color.LightGray);
DrawTriangle(new Vector2(
lastGesturePosition.X + 122, lastGesturePosition.Y + 16
), new Vector2(
lastGesturePosition.X + 137, lastGesturePosition.Y + 26
), new Vector2(
lastGesturePosition.X + 137, lastGesturePosition.Y + 6
), lastGesture == Gesture.PinchOut ? Color.Orange : Color.LightGray);
DrawTriangle(new Vector2(
lastGesturePosition.X + 147, lastGesturePosition.Y + 6
), new Vector2(
lastGesturePosition.X + 147, lastGesturePosition.Y + 26
), new Vector2(
lastGesturePosition.X + 162, lastGesturePosition.Y + 16
), lastGesture == Gesture.PinchOut ? Color.Orange : Color.Gray);
DrawTriangle(new Vector2(
lastGesturePosition.X + 125, lastGesturePosition.Y + 33
), new Vector2(
lastGesturePosition.X + 125, lastGesturePosition.Y + 53
), new Vector2(
lastGesturePosition.X + 140, lastGesturePosition.Y + 43
), lastGesture == Gesture.PinchIn ? Color.Violet : Color.LightGray);
DrawTriangle(new Vector2(
lastGesturePosition.X + 144, lastGesturePosition.Y + 43
), new Vector2(
lastGesturePosition.X + 159, lastGesturePosition.Y + 53
), new Vector2(
lastGesturePosition.X + 159, lastGesturePosition.Y + 33
), lastGesture == Gesture.PinchIn ? Color.Violet : Color.LightGray);
for (i = 0; i < 4; i++)
{
DrawCircle((int)lastGesturePosition.X + 180, (int)lastGesturePosition.Y + 7 + i * 15, 5,
touchCount <= i ? Color.LightGray : gestureColor);
}
// Draw gesture log
DrawText("Log", (int)gestureLogPosition.X, (int)gestureLogPosition.Y, 20, Color.Black);
// Loop in both directions to print the gesture log array in the inverted order (and looping around if the index started somewhere in the middle)
for (i = 0, ii = gestureLogIndex; i < GESTURE_LOG_SIZE; i++, ii = (ii + 1) % GESTURE_LOG_SIZE)
{
DrawText(gestureLog[ii], (int)gestureLogPosition.X, (int)gestureLogPosition.Y + 410 - i * 20, 20,
(i == 0 ? gestureColor : Color.LightGray));
}
Color logButton1Color, logButton2Color;
switch (logMode)
{
case 3:
logButton1Color = Color.Maroon;
logButton2Color = Color.Maroon;
break;
case 2:
logButton1Color = Color.Gray;
logButton2Color = Color.Maroon;
break;
case 1:
logButton1Color = Color.Maroon;
logButton2Color = Color.Gray;
break;
default:
logButton1Color = Color.Gray;
logButton2Color = Color.Gray;
break;
}
DrawRectangleRec(logButton1, logButton1Color);
DrawText("Hide", (int)logButton1.X + 7, (int)logButton1.Y + 3, 10, Color.White);
DrawText("Repeat", (int)logButton1.X + 7, (int)logButton1.Y + 13, 10, Color.White);
DrawRectangleRec(logButton2, logButton2Color);
DrawText("Hide", (int)logButton1.X + 62, (int)logButton1.Y + 3, 10, Color.White);
DrawText("Hold", (int)logButton1.X + 62, (int)logButton1.Y + 13, 10, Color.White);
// Draw protractor
DrawText("Angle", (int)protractorPosition.X + 55, (int)protractorPosition.Y + 76, 10, Color.Black);
// Note: Official it's using raylibs functions for string manipulation. But in C# it will end up in an unsafe handling.
var angleString = currentAngleDegrees.ToString("F3");
var angleStringDot = angleString.IndexOf('.');
var angleStringTrim = angleString[..(angleStringDot + 3)];
DrawText(angleStringTrim, (int)protractorPosition.X + 55, (int)protractorPosition.Y + 92, 20, gestureColor);
DrawCircleV(protractorPosition, 80.0f, Color.White);
DrawLineEx(new Vector2(
protractorPosition.X - 90, protractorPosition.Y
), new Vector2(
protractorPosition.X + 90, protractorPosition.Y
), 3.0f, Color.LightGray);
DrawLineEx(new Vector2(
protractorPosition.X, protractorPosition.Y - 90
), new Vector2(
protractorPosition.X, protractorPosition.Y + 90
), 3.0f, Color.LightGray);
DrawLineEx(new Vector2(
protractorPosition.X - 80, protractorPosition.Y - 45
), new Vector2(
protractorPosition.X + 80, protractorPosition.Y + 45
), 3.0f, Color.Green);
DrawLineEx(new Vector2(
protractorPosition.X - 80, protractorPosition.Y + 45
), new Vector2(
protractorPosition.X + 80, protractorPosition.Y - 45
), 3.0f, Color.Green);
DrawText("0", (int)protractorPosition.X + 96, (int)protractorPosition.Y - 9, 20, Color.Black);
DrawText("30", (int)protractorPosition.X + 74, (int)protractorPosition.Y - 68, 20, Color.Black);
DrawText("90", (int)protractorPosition.X - 11, (int)protractorPosition.Y - 110, 20, Color.Black);
DrawText("150", (int)protractorPosition.X - 100, (int)protractorPosition.Y - 68, 20, Color.Black);
DrawText("180", (int)protractorPosition.X - 124, (int)protractorPosition.Y - 9, 20, Color.Black);
DrawText("210", (int)protractorPosition.X - 100, (int)protractorPosition.Y + 50, 20, Color.Black);
DrawText("270", (int)protractorPosition.X - 18, (int)protractorPosition.Y + 92, 20, Color.Black);
DrawText("330", (int)protractorPosition.X + 72, (int)protractorPosition.Y + 50, 20, Color.Black);
if (currentAngleDegrees != 0.0f)
{
DrawLineEx(protractorPosition, finalVector, 3.0f, gestureColor);
}
// Draw touch and mouse pointer points
if (currentGesture != Gesture.None)
{
if (touchCount != 0)
{
for (i = 0; i < touchCount; i++)
{
DrawCircleV(touchPosition[i], 50.0f, Fade(gestureColor, 0.5f));
DrawCircleV(touchPosition[i], 5.0f, gestureColor);
}
if (touchCount == 2)
{
DrawLineEx(touchPosition[0], touchPosition[1], (((int)currentGesture == 512) ? 8.0f : 12.0f),
gestureColor);
}
}
else
{
DrawCircleV(mousePosition, 35.0f, Fade(gestureColor, 0.5f));
DrawCircleV(mousePosition, 5.0f, gestureColor);
}
}
EndDrawing();
//--------------------------------------------------------------------------------------
}
public void Unload()
{
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [core] example - input gestures testbed");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new InputGesturesTestBed();
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
@ -409,7 +453,8 @@ public class InputGesturesTestBed
return 0;
}
static string GetGestureName(int gesture)
// Get text string for gesture value
private static string GetGestureName(int gesture)
{
switch (gesture)
{
@ -441,7 +486,7 @@ public class InputGesturesTestBed
}
// Get color for gesture value
static Color GetGestureColor(int gesture)
private static Color GetGestureColor(int gesture)
{
switch (gesture)
{

View file

@ -13,69 +13,89 @@
*
********************************************************************************************/
using System.Numerics;
using static Raylib_cs.Raylib;
namespace Examples.Core;
public class InputKeys
public partial class InputKeys : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Core / Input Keys";
public string Title => "raylib [core] example - input keys";
private Vector2 ballPosition;
public void Init()
{
ballPosition = new((float)screenWidth / 2, (float)screenHeight / 2);
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
if (IsKeyDown(KeyboardKey.Right))
{
ballPosition.X += 2.0f;
}
if (IsKeyDown(KeyboardKey.Left))
{
ballPosition.X -= 2.0f;
}
if (IsKeyDown(KeyboardKey.Up))
{
ballPosition.Y -= 2.0f;
}
if (IsKeyDown(KeyboardKey.Down))
{
ballPosition.Y += 2.0f;
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
DrawText("move the ball with arrow keys", 10, 10, 20, Color.DarkGray);
DrawCircleV(ballPosition, 50, Color.Maroon);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [core] example - input keys");
InitWindow(screenWidth, screenHeight, "raylib [core] example - keyboard input");
Vector2 ballPosition = new((float)screenWidth / 2, (float)screenHeight / 2);
SetTargetFPS(60); // Set target frames-per-second
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new InputKeys();
game.Init();
// Main game loop
while (!WindowShouldClose())
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
if (IsKeyDown(KeyboardKey.Right))
{
ballPosition.X += 2.0f;
}
if (IsKeyDown(KeyboardKey.Left))
{
ballPosition.X -= 2.0f;
}
if (IsKeyDown(KeyboardKey.Up))
{
ballPosition.Y -= 2.0f;
}
if (IsKeyDown(KeyboardKey.Down))
{
ballPosition.Y += 2.0f;
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
DrawText("move the ball with arrow keys", 10, 10, 20, Color.DarkGray);
DrawCircleV(ballPosition, 50, Color.Maroon);
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow();
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;

View file

@ -14,100 +14,124 @@
********************************************************************************************/
using System.Numerics;
using static Raylib_cs.Raylib;
namespace Examples.Core;
public class InputMouse
public partial class InputMouse : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Core / Input Mouse";
public string Title => "raylib [core] example - input mouse";
private Vector2 ballPosition;
private Color ballColor;
public void Init()
{
ballPosition = new(-100.0f, -100.0f);
ballColor = Color.DarkBlue;
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
if (IsKeyPressed(KeyboardKey.H))
{
if (IsCursorHidden())
{
ShowCursor();
}
else
{
HideCursor();
}
}
ballPosition = GetMousePosition();
if (IsMouseButtonPressed(MouseButton.Left))
{
ballColor = Color.Maroon;
}
else if (IsMouseButtonPressed(MouseButton.Middle))
{
ballColor = Color.Lime;
}
else if (IsMouseButtonPressed(MouseButton.Right))
{
ballColor = Color.DarkBlue;
}
else if (IsMouseButtonPressed(MouseButton.Side))
{
ballColor = Color.Purple;
}
else if (IsMouseButtonPressed(MouseButton.Extra))
{
ballColor = Color.Yellow;
}
else if (IsMouseButtonPressed(MouseButton.Forward))
{
ballColor = Color.Orange;
}
else if (IsMouseButtonPressed(MouseButton.Back))
{
ballColor = Color.Beige;
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
DrawCircleV(ballPosition, 40, ballColor);
DrawText("move ball with mouse and click mouse button to change color", 10, 10, 20, Color.DarkGray);
DrawText("Press 'H' to toggle cursor visibility", 10, 30, 20, Color.DarkGray);
if (IsCursorHidden())
{
DrawText("CURSOR HIDDEN", 20, 60, 20, Color.Red);
}
else
{
DrawText("CURSOR VISIBLE", 20, 60, 20, Color.Lime);
}
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [core] example - input mouse");
InitWindow(screenWidth, screenHeight, "raylib [core] example - mouse input");
Vector2 ballPosition = new(-100.0f, -100.0f);
Color ballColor = Color.DarkBlue;
SetTargetFPS(60);
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//---------------------------------------------------------------------------------------
var game = new InputMouse();
game.Init();
// Main game loop
while (!WindowShouldClose())
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
if (IsKeyPressed(KeyboardKey.H))
{
if (IsCursorHidden())
{
ShowCursor();
}
else
{
HideCursor();
}
}
ballPosition = GetMousePosition();
if (IsMouseButtonPressed(MouseButton.Left))
{
ballColor = Color.Maroon;
}
else if (IsMouseButtonPressed(MouseButton.Middle))
{
ballColor = Color.Lime;
}
else if (IsMouseButtonPressed(MouseButton.Right))
{
ballColor = Color.DarkBlue;
}
else if (IsMouseButtonPressed(MouseButton.Extra))
{
ballColor = Color.Yellow;
}
else if (IsMouseButtonPressed(MouseButton.Forward))
{
ballColor = Color.Orange;
}
else if (IsMouseButtonPressed(MouseButton.Back))
{
ballColor = Color.Beige;
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
DrawCircleV(ballPosition, 40, ballColor);
DrawText("move ball with mouse and click mouse button to change color", 10, 10, 20, Color.DarkGray);
DrawText("Press 'H' to toggle cursor visibility", 10, 30, 20, Color.DarkGray);
if (IsCursorHidden())
{
DrawText("CURSOR HIDDEN", 20, 60, 20, Color.Red);
}
else
{
DrawText("CURSOR VISIBLE", 20, 60, 20, Color.Lime);
}
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow();
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;

View file

@ -13,52 +13,74 @@
*
********************************************************************************************/
using static Raylib_cs.Raylib;
namespace Examples.Core;
public class InputMouseWheel
public partial class InputMouseWheel : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Core / Mouse Wheel";
public string Title => "raylib [core] example - input mouse wheel";
private int boxPositionY;
private int scrollSpeed; // Scrolling speed in pixels
public void Init()
{
boxPositionY = screenHeight / 2 - 40;
scrollSpeed = 4; // Scrolling speed in pixels
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
boxPositionY -= (int)(GetMouseWheelMove() * scrollSpeed);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
DrawRectangle(screenWidth / 2 - 40, boxPositionY, 80, 80, Color.Maroon);
DrawText("Use mouse wheel to move the cube up and down!", 10, 10, 20, Color.Gray);
DrawText($"Box position Y: {boxPositionY:000}", 10, 40, 20, Color.LightGray);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [core] example - input mouse wheel");
int boxPositionY = screenHeight / 2 - 40;
int scrollSpeed = 4; // Scrolling speed in pixels
SetTargetFPS(60);
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new InputMouseWheel();
game.Init();
// Main game loop
while (!WindowShouldClose())
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
boxPositionY -= (int)(GetMouseWheelMove() * scrollSpeed);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
DrawRectangle(screenWidth / 2 - 40, boxPositionY, 80, 80, Color.Maroon);
DrawText("Use mouse wheel to move the cube up and down!", 10, 10, 20, Color.Gray);
DrawText($"Box position Y: {boxPositionY}", 10, 40, 20, Color.LightGray);
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow();
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;

View file

@ -15,79 +15,100 @@
*
********************************************************************************************/
using System.Numerics;
using static Raylib_cs.Raylib;
namespace Examples.Core;
public class InputMultitouch
public partial class InputMultitouch : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
private const int MaxTouchPoints = 10;
public string Name => "Core / Input Multitouch";
public string Title => "raylib [core] example - input multitouch";
private Vector2[] touchPositions;
public void Init()
{
touchPositions = new Vector2[MaxTouchPoints];
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
// Get the touch point count ( how many fingers are touching the screen )
var tCount = GetTouchPointCount();
// Clamp touch points available ( set the maximum touch points allowed )
if (tCount > MaxTouchPoints)
{
tCount = MaxTouchPoints;
}
// Get touch points positions
for (var i = 0; i < tCount; i++)
{
touchPositions[i] = GetTouchPosition(i);
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
for (var i = 0; i < tCount; i++)
{
// Make sure point is not (0, 0) as this means there is no touch for it
if ((touchPositions[i].X > 0) && (touchPositions[i].Y > 0))
{
// Draw circle and touch index number
DrawCircleV(touchPositions[i], 34, Color.Orange);
DrawText(i.ToString(),
(int)touchPositions[i].X - 10,
(int)touchPositions[i].Y - 70,
40,
Color.Black
);
}
}
DrawText("touch the screen at multiple locations to get multiple balls", 10, 10, 20, Color.DarkGray);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [core] example - input multitouch");
const int MaxTouchPoints = 10;
Vector2[] touchPositions = new Vector2[MaxTouchPoints];
SetTargetFPS(60);
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//---------------------------------------------------------------------------------------
var game = new InputMultitouch();
game.Init();
// Main game loop
while (!WindowShouldClose())
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
// Get the touch point count (how many fingers are touching the screen )
int tCount = GetTouchPointCount();
// Clamp touch points available (set the maximum touch points allowed )
if (tCount > MaxTouchPoints)
{
tCount = MaxTouchPoints;
}
// Get touch points positions
for (int i = 0; i < tCount; i++)
{
touchPositions[i] = GetTouchPosition(i);
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
for (int i = 0; i < tCount; i++)
{
// Make sure point is not (0, 0) as this means there is no touch for it
if ((touchPositions[i].X > 0) && (touchPositions[i].Y > 0))
{
// Draw circle and touch index number
DrawCircleV(touchPositions[i], 34, Color.Orange);
DrawText(i.ToString(),
(int)touchPositions[i].X - 10,
(int)touchPositions[i].Y - 70,
40,
Color.Black
);
}
}
DrawText("touch the screen at multiple locations to get multiple balls", 10, 10, 20, Color.DarkGray);
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow();
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;

View file

@ -1,27 +1,23 @@
/*******************************************************************************************
*
* raylib [core] example - input virtual controls
*
* Example complexity rating: [★★☆☆] 2/4
*
* Example originally created with raylib 5.0, last time updated with raylib 5.0
*
* Example contributed by GreenSnakeLinux (@GreenSnakeLinux),
* reviewed by Ramon Santamaria (@raysan5), oblerion (@oblerion) and danilwhale (@danilwhale)
*
* 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) 2024-2025 GreenSnakeLinux (@GreenSnakeLinux) and Ramon Santamaria (@raysan5)
*
********************************************************************************************/
using System.Numerics;
*
* raylib [core] example - input virtual controls
*
* Example complexity rating: [★★☆☆] 2/4
*
* Example originally created with raylib 5.0, last time updated with raylib 5.0
*
* Example contributed by GreenSnakeLinux (@GreenSnakeLinux),
* reviewed by Ramon Santamaria (@raysan5), oblerion (@oblerion) and danilwhale (@danilwhale)
*
* 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) 2024-2025 GreenSnakeLinux (@GreenSnakeLinux) and Ramon Santamaria (@raysan5)
*
********************************************************************************************/
namespace Examples.Core;
using static Raylib_cs.Raylib;
public enum PadButton
{
BUTTON_NONE = -1,
@ -32,21 +28,31 @@ public enum PadButton
BUTTON_MAX
}
public class InputVirtualControls
public partial class InputVirtualControls : IExample
{
public static int Main()
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Core / Input Virtual Controls";
public string Title => "raylib [core] example - input virtual controls";
private Vector2 padPosition;
private float buttonRadius;
private Vector2[] buttonPositions;
private Vector2[][] arrowTris;
private Color[] buttonLabelColors;
private int pressedButton;
private Vector2 inputPosition;
private Vector2 playerPosition;
private float playerSpeed;
public void Init()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
padPosition = new Vector2(100, 350);
buttonRadius = 30;
InitWindow(screenWidth, screenHeight, "raylib [core] example - input virtual controls");
Vector2 padPosition = new Vector2(100, 350);
float buttonRadius = 30;
Vector2[] buttonPositions =
buttonPositions =
[
new Vector2(
padPosition.X,padPosition.Y - buttonRadius * 1.5f
@ -62,7 +68,7 @@ public class InputVirtualControls
) // Down
];
Vector2[][] arrowTris = [
arrowTris = [
// Up
[
new Vector2(
@ -114,107 +120,126 @@ public class InputVirtualControls
]
;
Color[] buttonLabelColors = [
buttonLabelColors = [
Color.Yellow, // Up
Color.Blue, // Left
Color.Red, // Right
Color.Green // Down
];
int pressedButton = (int)PadButton.BUTTON_NONE;
Vector2 inputPosition = new Vector2(0, 0);
pressedButton = (int)PadButton.BUTTON_NONE;
inputPosition = new Vector2(0, 0);
Vector2 playerPosition = new Vector2((float)screenWidth / 2, (float)screenHeight / 2);
float playerSpeed = 75f;
playerPosition = new Vector2((float)screenWidth / 2, (float)screenHeight / 2);
playerSpeed = 75f;
}
public void Update()
{
// Update
//--------------------------------------------------------------------------
if ((GetTouchPointCount() > 0))
{
inputPosition = GetTouchPosition(0); // Use touch position
}
else
{
inputPosition = GetMousePosition(); // Use mouse position
}
// Reset pressed button to none
pressedButton = (int)PadButton.BUTTON_NONE;
// Make sure user is pressing left mouse button if they're from desktop
if ((GetTouchPointCount() > 0) ||
((GetTouchPointCount() == 0) && IsMouseButtonDown(MouseButton.Left)))
{
// Find nearest D-Pad button to the input position
for (var i = 0; i < (int)PadButton.BUTTON_MAX; i++)
{
var distX = MathF.Abs(buttonPositions[i].X - inputPosition.X);
var distY = MathF.Abs(buttonPositions[i].Y - inputPosition.Y);
if ((distX + distY < buttonRadius))
{
pressedButton = i;
break;
}
}
}
// Move player according to pressed button
switch ((PadButton)pressedButton)
{
case PadButton.BUTTON_UP:
playerPosition.Y -= playerSpeed * GetFrameTime();
break;
case PadButton.BUTTON_LEFT:
playerPosition.X -= playerSpeed * GetFrameTime();
break;
case PadButton.BUTTON_RIGHT:
playerPosition.X += playerSpeed * GetFrameTime();
break;
case PadButton.BUTTON_DOWN:
playerPosition.Y += playerSpeed * GetFrameTime();
break;
default:
break;
}
//--------------------------------------------------------------------------
// Draw
//--------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
// Draw world
DrawCircleV(playerPosition, 50, Color.Maroon);
// Draw GUI
for (var i = 0; i < (int)PadButton.BUTTON_MAX; i++)
{
DrawCircleV(buttonPositions[i], buttonRadius, (i == pressedButton) ? Color.DarkGray : Color.Black);
DrawTriangle(
arrowTris[i][0],
arrowTris[i][1],
arrowTris[i][2],
buttonLabelColors[i]
);
}
DrawText("move the player with D-Pad buttons", 10, 10, 20, Color.DarkGray);
EndDrawing();
//--------------------------------------------------------------------------
}
public void Unload()
{
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [core] example - input virtual controls");
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
var game = new InputVirtualControls();
game.Init();
// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//--------------------------------------------------------------------------
if ((GetTouchPointCount() > 0))
{
inputPosition = GetTouchPosition(0); // Use touch position
}
else
{
inputPosition = GetMousePosition(); // Use mouse position
}
// Reset pressed button to none
pressedButton = (int)PadButton.BUTTON_NONE;
// Make sure user is pressing left mouse button if they're from desktop
if ((GetTouchPointCount() > 0) ||
((GetTouchPointCount() == 0) && IsMouseButtonDown(MouseButton.Left)))
{
// Find nearest D-Pad button to the input position
for (int i = 0; i < (int)PadButton.BUTTON_MAX; i++)
{
float distX = MathF.Abs(buttonPositions[i].X - inputPosition.X);
float distY = MathF.Abs(buttonPositions[i].Y - inputPosition.Y);
if ((distX + distY < buttonRadius))
{
pressedButton = i;
break;
}
}
}
// Move player according to pressed button
switch ((PadButton)pressedButton)
{
case PadButton.BUTTON_UP:
playerPosition.Y -= playerSpeed * GetFrameTime();
break;
case PadButton.BUTTON_LEFT:
playerPosition.X -= playerSpeed * GetFrameTime();
break;
case PadButton.BUTTON_RIGHT:
playerPosition.X += playerSpeed * GetFrameTime();
break;
case PadButton.BUTTON_DOWN:
playerPosition.Y += playerSpeed * GetFrameTime();
break;
default:
break;
}
;
//--------------------------------------------------------------------------
// Draw
//--------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
// Draw world
DrawCircleV(playerPosition, 50, Color.Maroon);
// Draw GUI
for (int i = 0; i < (int)PadButton.BUTTON_MAX; i++)
{
DrawCircleV(buttonPositions[i], buttonRadius, (i == pressedButton) ? Color.DarkGray : Color.Black);
DrawTriangle(
arrowTris[i][0],
arrowTris[i][1],
arrowTris[i][2],
buttonLabelColors[i]
);
}
DrawText("move the player with D-Pad buttons", 10, 10, 20, Color.DarkGray);
EndDrawing();
//--------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow(); // Close window and OpenGL context

View file

@ -0,0 +1,510 @@
/*******************************************************************************************
*
* raylib [core] example - keyboard testbed
*
* Example complexity rating: [★★☆☆] 2/4
*
* NOTE: raylib defined keys refer to ENG-US Keyboard layout,
* mapping to other layouts is up to the user
*
* Example originally created with raylib 5.6, last time updated with raylib 5.6
*
* 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) 2026 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
namespace Examples.Core;
public partial class KeyboardTestbed : IExample
{
private const int KeyRecSpacing = 4; // Space in pixels between key rectangles
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Core / Keyboard Testbed";
public string Title => "raylib [core] example - keyboard testbed";
private int[] line01KeyWidths;
private int[] line01Keys;
private int[] line02KeyWidths;
private int[] line02Keys;
private int[] line03KeyWidths;
private int[] line03Keys;
private int[] line04KeyWidths;
private int[] line04Keys;
private int[] line05KeyWidths;
private int[] line05Keys;
private int[] line06KeyWidths;
private int[] line06Keys;
private Vector2 keyboardOffset;
public void Init()
{
SetExitKey(KeyboardKey.Null); // Avoid exit on KEY_ESCAPE
// Keyboard line 01
line01KeyWidths = new int[15];
for (int i = 0; i < 15; i++)
{
line01KeyWidths[i] = 45;
}
line01KeyWidths[13] = 62; // PRINTSCREEN
line01Keys = new int[]
{
(int)KeyboardKey.Escape, (int)KeyboardKey.F1, (int)KeyboardKey.F2, (int)KeyboardKey.F3, (int)KeyboardKey.F4, (int)KeyboardKey.F5,
(int)KeyboardKey.F6, (int)KeyboardKey.F7, (int)KeyboardKey.F8, (int)KeyboardKey.F9, (int)KeyboardKey.F10, (int)KeyboardKey.F11,
(int)KeyboardKey.F12, (int)KeyboardKey.PrintScreen, (int)KeyboardKey.Pause
};
// Keyboard line 02
line02KeyWidths = new int[15];
for (int i = 0; i < 15; i++)
{
line02KeyWidths[i] = 45;
}
line02KeyWidths[0] = 25; // GRAVE
line02KeyWidths[13] = 82; // BACKSPACE
line02Keys = new int[]
{
(int)KeyboardKey.Grave, (int)KeyboardKey.One, (int)KeyboardKey.Two, (int)KeyboardKey.Three, (int)KeyboardKey.Four,
(int)KeyboardKey.Five, (int)KeyboardKey.Six, (int)KeyboardKey.Seven, (int)KeyboardKey.Eight, (int)KeyboardKey.Nine,
(int)KeyboardKey.Zero, (int)KeyboardKey.Minus, (int)KeyboardKey.Equal, (int)KeyboardKey.Backspace, (int)KeyboardKey.Delete
};
// Keyboard line 03
line03KeyWidths = new int[15];
for (int i = 0; i < 15; i++)
{
line03KeyWidths[i] = 45;
}
line03KeyWidths[0] = 50; // TAB
line03KeyWidths[13] = 57; // BACKSLASH
line03Keys = new int[]
{
(int)KeyboardKey.Tab, (int)KeyboardKey.Q, (int)KeyboardKey.W, (int)KeyboardKey.E, (int)KeyboardKey.R, (int)KeyboardKey.T, (int)KeyboardKey.Y,
(int)KeyboardKey.U, (int)KeyboardKey.I, (int)KeyboardKey.O, (int)KeyboardKey.P, (int)KeyboardKey.LeftBracket,
(int)KeyboardKey.RightBracket, (int)KeyboardKey.Backslash, (int)KeyboardKey.Insert
};
// Keyboard line 04
line04KeyWidths = new int[14];
for (int i = 0; i < 14; i++)
{
line04KeyWidths[i] = 45;
}
line04KeyWidths[0] = 68; // CAPS
line04KeyWidths[12] = 88; // ENTER
line04Keys = new int[]
{
(int)KeyboardKey.CapsLock, (int)KeyboardKey.A, (int)KeyboardKey.S, (int)KeyboardKey.D, (int)KeyboardKey.F, (int)KeyboardKey.G,
(int)KeyboardKey.H, (int)KeyboardKey.J, (int)KeyboardKey.K, (int)KeyboardKey.L, (int)KeyboardKey.Semicolon,
(int)KeyboardKey.Apostrophe, (int)KeyboardKey.Enter, (int)KeyboardKey.PageUp
};
// Keyboard line 05
line05KeyWidths = new int[14];
for (int i = 0; i < 14; i++)
{
line05KeyWidths[i] = 45;
}
line05KeyWidths[0] = 80; // LSHIFT
line05KeyWidths[11] = 76; // RSHIFT
line05Keys = new int[]
{
(int)KeyboardKey.LeftShift, (int)KeyboardKey.Z, (int)KeyboardKey.X, (int)KeyboardKey.C, (int)KeyboardKey.V, (int)KeyboardKey.B,
(int)KeyboardKey.N, (int)KeyboardKey.M, (int)KeyboardKey.Comma, (int)KeyboardKey.Period, /*KEY_MINUS*/
(int)KeyboardKey.Slash, (int)KeyboardKey.RightShift, (int)KeyboardKey.Up, (int)KeyboardKey.PageDown
};
// Keyboard line 06
line06KeyWidths = new int[11];
for (int i = 0; i < 11; i++)
{
line06KeyWidths[i] = 45;
}
line06KeyWidths[0] = 80; // LCTRL
line06KeyWidths[3] = 208; // SPACE
line06KeyWidths[7] = 60; // RCTRL
line06Keys = new int[]
{
(int)KeyboardKey.LeftControl, (int)KeyboardKey.LeftSuper, (int)KeyboardKey.LeftAlt,
(int)KeyboardKey.Space, (int)KeyboardKey.RightAlt, 162, (int)KeyboardKey.Null,
(int)KeyboardKey.RightControl, (int)KeyboardKey.Left, (int)KeyboardKey.Down, (int)KeyboardKey.Right
};
keyboardOffset = new Vector2(26, 80);
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
int key = GetKeyPressed(); // Get pressed keycode
if (key > 0)
{
TraceLog(TraceLogLevel.Info, $"KEYBOARD TESTBED: KEY PRESSED: {key}");
}
int ch = GetCharPressed(); // Get pressed char for text input, using OS mapping
if (ch > 0)
{
TraceLog(TraceLogLevel.Info, $"KEYBOARD TESTBED: CHAR PRESSED: {(char)ch} ({ch})");
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
DrawText("KEYBOARD LAYOUT: ENG-US", 26, 38, 20, Color.LightGray);
// Keyboard line 01 - 15 keys
// ESC, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, IMP, CLOSE
for (int i = 0, recOffsetX = 0; i < 15; i++)
{
GuiKeyboardKey(new Rectangle(keyboardOffset.X + recOffsetX, keyboardOffset.Y, (float)line01KeyWidths[i], 30.0f), line01Keys[i]);
recOffsetX += line01KeyWidths[i] + KeyRecSpacing;
}
// Keyboard line 02 - 15 keys
// `, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, -, =, BACKSPACE, DEL
for (int i = 0, recOffsetX = 0; i < 15; i++)
{
GuiKeyboardKey(new Rectangle(keyboardOffset.X + recOffsetX, keyboardOffset.Y + 30 + KeyRecSpacing, (float)line02KeyWidths[i], 38.0f), line02Keys[i]);
recOffsetX += line02KeyWidths[i] + KeyRecSpacing;
}
// Keyboard line 03 - 15 keys
// TAB, Q, W, E, R, T, Y, U, I, O, P, [, ], \, INS
for (int i = 0, recOffsetX = 0; i < 15; i++)
{
GuiKeyboardKey(new Rectangle(keyboardOffset.X + recOffsetX, keyboardOffset.Y + 30 + 38 + KeyRecSpacing * 2, (float)line03KeyWidths[i], 38.0f), line03Keys[i]);
recOffsetX += line03KeyWidths[i] + KeyRecSpacing;
}
// Keyboard line 04 - 14 keys
// MAYUS, A, S, D, F, G, H, J, K, L, ;, ', ENTER, REPAG
for (int i = 0, recOffsetX = 0; i < 14; i++)
{
GuiKeyboardKey(new Rectangle(keyboardOffset.X + recOffsetX, keyboardOffset.Y + 30 + 38 * 2 + KeyRecSpacing * 3, (float)line04KeyWidths[i], 38.0f), line04Keys[i]);
recOffsetX += line04KeyWidths[i] + KeyRecSpacing;
}
// Keyboard line 05 - 14 keys
// LSHIFT, Z, X, C, V, B, N, M, ,, ., /, RSHIFT, UP, AVPAG
for (int i = 0, recOffsetX = 0; i < 14; i++)
{
GuiKeyboardKey(new Rectangle(keyboardOffset.X + recOffsetX, keyboardOffset.Y + 30 + 38 * 3 + KeyRecSpacing * 4, (float)line05KeyWidths[i], 38.0f), line05Keys[i]);
recOffsetX += line05KeyWidths[i] + KeyRecSpacing;
}
// Keyboard line 06 - 11 keys
// LCTRL, WIN, LALT, SPACE, ALTGR, \, FN, RCTRL, LEFT, DOWN, RIGHT
for (int i = 0, recOffsetX = 0; i < 11; i++)
{
GuiKeyboardKey(new Rectangle(keyboardOffset.X + recOffsetX, keyboardOffset.Y + 30 + 38 * 4 + KeyRecSpacing * 5, (float)line06KeyWidths[i], 38.0f), line06Keys[i]);
recOffsetX += line06KeyWidths[i] + KeyRecSpacing;
}
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
}
//------------------------------------------------------------------------------------
// Module Functions Definition
//------------------------------------------------------------------------------------
// Get keyboard keycode as text (US keyboard)
// NOTE: Mapping for other keyboard layouts can be done here
private static string GetKeyText(int key)
{
switch ((KeyboardKey)key)
{
case KeyboardKey.Apostrophe:
return "'"; // Key: '
case KeyboardKey.Comma:
return ","; // Key: ,
case KeyboardKey.Minus:
return "-"; // Key: -
case KeyboardKey.Period:
return "."; // Key: .
case KeyboardKey.Slash:
return "/"; // Key: /
case KeyboardKey.Zero:
return "0"; // Key: 0
case KeyboardKey.One:
return "1"; // Key: 1
case KeyboardKey.Two:
return "2"; // Key: 2
case KeyboardKey.Three:
return "3"; // Key: 3
case KeyboardKey.Four:
return "4"; // Key: 4
case KeyboardKey.Five:
return "5"; // Key: 5
case KeyboardKey.Six:
return "6"; // Key: 6
case KeyboardKey.Seven:
return "7"; // Key: 7
case KeyboardKey.Eight:
return "8"; // Key: 8
case KeyboardKey.Nine:
return "9"; // Key: 9
case KeyboardKey.Semicolon:
return ";"; // Key: ;
case KeyboardKey.Equal:
return "="; // Key: =
case KeyboardKey.A:
return "A"; // Key: A | a
case KeyboardKey.B:
return "B"; // Key: B | b
case KeyboardKey.C:
return "C"; // Key: C | c
case KeyboardKey.D:
return "D"; // Key: D | d
case KeyboardKey.E:
return "E"; // Key: E | e
case KeyboardKey.F:
return "F"; // Key: F | f
case KeyboardKey.G:
return "G"; // Key: G | g
case KeyboardKey.H:
return "H"; // Key: H | h
case KeyboardKey.I:
return "I"; // Key: I | i
case KeyboardKey.J:
return "J"; // Key: J | j
case KeyboardKey.K:
return "K"; // Key: K | k
case KeyboardKey.L:
return "L"; // Key: L | l
case KeyboardKey.M:
return "M"; // Key: M | m
case KeyboardKey.N:
return "N"; // Key: N | n
case KeyboardKey.O:
return "O"; // Key: O | o
case KeyboardKey.P:
return "P"; // Key: P | p
case KeyboardKey.Q:
return "Q"; // Key: Q | q
case KeyboardKey.R:
return "R"; // Key: R | r
case KeyboardKey.S:
return "S"; // Key: S | s
case KeyboardKey.T:
return "T"; // Key: T | t
case KeyboardKey.U:
return "U"; // Key: U | u
case KeyboardKey.V:
return "V"; // Key: V | v
case KeyboardKey.W:
return "W"; // Key: W | w
case KeyboardKey.X:
return "X"; // Key: X | x
case KeyboardKey.Y:
return "Y"; // Key: Y | y
case KeyboardKey.Z:
return "Z"; // Key: Z | z
case KeyboardKey.LeftBracket:
return "["; // Key: [
case KeyboardKey.Backslash:
return "\\"; // Key: '\'
case KeyboardKey.RightBracket:
return "]"; // Key: ]
case KeyboardKey.Grave:
return "`"; // Key: `
case KeyboardKey.Space:
return "SPACE"; // Key: Space
case KeyboardKey.Escape:
return "ESC"; // Key: Esc
case KeyboardKey.Enter:
return "ENTER"; // Key: Enter
case KeyboardKey.Tab:
return "TAB"; // Key: Tab
case KeyboardKey.Backspace:
return "BACK"; // Key: Backspace
case KeyboardKey.Insert:
return "INS"; // Key: Ins
case KeyboardKey.Delete:
return "DEL"; // Key: Del
case KeyboardKey.Right:
return "RIGHT"; // Key: Cursor right
case KeyboardKey.Left:
return "LEFT"; // Key: Cursor left
case KeyboardKey.Down:
return "DOWN"; // Key: Cursor down
case KeyboardKey.Up:
return "UP"; // Key: Cursor up
case KeyboardKey.PageUp:
return "PGUP"; // Key: Page up
case KeyboardKey.PageDown:
return "PGDOWN"; // Key: Page down
case KeyboardKey.Home:
return "HOME"; // Key: Home
case KeyboardKey.End:
return "END"; // Key: End
case KeyboardKey.CapsLock:
return "CAPS"; // Key: Caps lock
case KeyboardKey.ScrollLock:
return "LOCK"; // Key: Scroll down
case KeyboardKey.NumLock:
return "NUMLOCK"; // Key: Num lock
case KeyboardKey.PrintScreen:
return "PRINTSCR"; // Key: Print screen
case KeyboardKey.Pause:
return "PAUSE"; // Key: Pause
case KeyboardKey.F1:
return "F1"; // Key: F1
case KeyboardKey.F2:
return "F2"; // Key: F2
case KeyboardKey.F3:
return "F3"; // Key: F3
case KeyboardKey.F4:
return "F4"; // Key: F4
case KeyboardKey.F5:
return "F5"; // Key: F5
case KeyboardKey.F6:
return "F6"; // Key: F6
case KeyboardKey.F7:
return "F7"; // Key: F7
case KeyboardKey.F8:
return "F8"; // Key: F8
case KeyboardKey.F9:
return "F9"; // Key: F9
case KeyboardKey.F10:
return "F10"; // Key: F10
case KeyboardKey.F11:
return "F11"; // Key: F11
case KeyboardKey.F12:
return "F12"; // Key: F12
case KeyboardKey.LeftShift:
return "LSHIFT"; // Key: Shift left
case KeyboardKey.LeftControl:
return "LCTRL"; // Key: Control left
case KeyboardKey.LeftAlt:
return "LALT"; // Key: Alt left
case KeyboardKey.LeftSuper:
return "WIN"; // Key: Super left
case KeyboardKey.RightShift:
return "RSHIFT"; // Key: Shift right
case KeyboardKey.RightControl:
return "RCTRL"; // Key: Control right
case KeyboardKey.RightAlt:
return "ALTGR"; // Key: Alt right
case KeyboardKey.RightSuper:
return "RSUPER"; // Key: Super right
case KeyboardKey.KeyboardMenu:
return "KBMENU"; // Key: KB menu
case KeyboardKey.Kp0:
return "KP0"; // Key: Keypad 0
case KeyboardKey.Kp1:
return "KP1"; // Key: Keypad 1
case KeyboardKey.Kp2:
return "KP2"; // Key: Keypad 2
case KeyboardKey.Kp3:
return "KP3"; // Key: Keypad 3
case KeyboardKey.Kp4:
return "KP4"; // Key: Keypad 4
case KeyboardKey.Kp5:
return "KP5"; // Key: Keypad 5
case KeyboardKey.Kp6:
return "KP6"; // Key: Keypad 6
case KeyboardKey.Kp7:
return "KP7"; // Key: Keypad 7
case KeyboardKey.Kp8:
return "KP8"; // Key: Keypad 8
case KeyboardKey.Kp9:
return "KP9"; // Key: Keypad 9
case KeyboardKey.KpDecimal:
return "KPDEC"; // Key: Keypad .
case KeyboardKey.KpDivide:
return "KPDIV"; // Key: Keypad /
case KeyboardKey.KpMultiply:
return "KPMUL"; // Key: Keypad *
case KeyboardKey.KpSubtract:
return "KPSUB"; // Key: Keypad -
case KeyboardKey.KpAdd:
return "KPADD"; // Key: Keypad +
case KeyboardKey.KpEnter:
return "KPENTER"; // Key: Keypad Enter
case KeyboardKey.KpEqual:
return "KPEQU"; // Key: Keypad =
default:
return "";
}
}
// Draw keyboard key
private static void GuiKeyboardKey(Rectangle bounds, int key)
{
if (key == (int)KeyboardKey.Null)
{
DrawRectangleLinesEx(bounds, 2.0f, Color.LightGray);
}
else
{
if (IsKeyDown((KeyboardKey)key))
{
DrawRectangleLinesEx(bounds, 2.0f, Color.Maroon);
DrawText(GetKeyText(key), (int)(bounds.X + 4), (int)(bounds.Y + 4), 10, Color.Maroon);
}
else
{
DrawRectangleLinesEx(bounds, 2.0f, Color.DarkGray);
DrawText(GetKeyText(key), (int)(bounds.X + 4), (int)(bounds.Y + 4), 10, Color.DarkGray);
}
}
if (CheckCollisionPointRec(GetMousePosition(), bounds))
{
DrawRectangleRec(bounds, Fade(Color.Red, 0.2f));
DrawRectangleLinesEx(bounds, 3.0f, Color.Red);
}
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [core] example - keyboard testbed");
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
var game = new KeyboardTestbed();
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,183 @@
/*******************************************************************************************
*
* raylib example - loading thread
* raylib [core] example - loading thread
*
* This example has been created using raylib 2.5 (www.raylib.com)
* NOTE: raylib is NOT thread-safe: the loading thread only updates plain data
* (progress counter and loaded flag); all raylib calls happen on the main thread.
*
* Example originally created with raylib 2.5 (www.raylib.com)
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
*
* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5)
* Copyright (c) 2014-2025 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
using System.Threading;
using static Raylib_cs.Raylib;
using static Raylib_cs.Color;
using static Raylib_cs.KeyboardKey;
using System.Diagnostics;
namespace Examples.Core;
enum State
[ExcludeFromBrowser("System.Threading.Thread is unsupported on single-threaded wasm")]
public partial class LoadingThread : IExample
{
STATE_WAITING,
STATE_LOADING,
STATE_FINISHED
}
const int screenWidth = 800;
const int screenHeight = 450;
public class LoadingThread
{
// C# bool is atomic. Used for synchronization
// https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/variables#atomicity-of-variable-references
// Data Loaded completion indicator
static bool dataLoaded = false;
public string Name => "Core / Loading Thread";
// Data progress accumulator
static int dataProgress = 0;
public string Title => "raylib [core] example - loading thread";
enum State
{
Waiting,
Loading,
Finished
}
// Loading data thread; a Thread can only be started once, so a fresh one
// is created for every load
Thread loadingThread;
// Data loaded completion indicator; volatile so the main thread sees the
// background thread's writes
volatile bool dataLoaded;
// Data progress accumulator (0..500, the progress bar width in pixels)
volatile int dataProgress;
State state;
int framesCounter;
public void Init()
{
loadingThread = null;
dataLoaded = false;
dataProgress = 0;
state = State.Waiting;
framesCounter = 0;
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
switch (state)
{
case State.Waiting:
if (IsKeyPressed(KeyboardKey.Enter))
{
loadingThread = new Thread(LoadDataThread) { IsBackground = true };
loadingThread.Start();
TraceLog(TraceLogLevel.Info, "Loading thread initialized successfully");
state = State.Loading;
}
break;
case State.Loading:
framesCounter++;
if (dataLoaded)
{
framesCounter = 0;
loadingThread.Join();
TraceLog(TraceLogLevel.Info, "Loading thread terminated");
state = State.Finished;
}
break;
case State.Finished:
if (IsKeyPressed(KeyboardKey.Enter))
{
// Reset everything to launch again
dataLoaded = false;
dataProgress = 0;
state = State.Waiting;
}
break;
default:
break;
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
switch (state)
{
case State.Waiting:
DrawText("PRESS ENTER to START LOADING DATA", 150, 170, 20, Color.DarkGray);
break;
case State.Loading:
DrawRectangle(150, 200, dataProgress, 60, Color.SkyBlue);
if ((framesCounter / 15) % 2 == 0)
{
DrawText("LOADING DATA...", 240, 210, 40, Color.DarkBlue);
}
break;
case State.Finished:
DrawRectangle(150, 200, 500, 60, Color.Lime);
DrawText("DATA LOADED!", 250, 210, 40, Color.Green);
break;
default:
break;
}
DrawRectangleLines(150, 200, 500, 60, Color.DarkGray);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [core] example - loading thread");
// Loading data thread id
Thread thread = new(new ThreadStart(LoadDataThread));
State state = State.STATE_WAITING;
int framesCounter = 0;
SetTargetFPS(60);
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new LoadingThread();
game.Init();
// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
switch (state)
{
case State.STATE_WAITING:
{
if (IsKeyPressed(KEY_ENTER))
{
thread.Start();
//int error = pthread_create(ref, NULL, ref, NULL);
//if (error != 0) TraceLog(TraceLogLevel.LOG_ERROR, "Error creating loading thread");
//else TraceLog(TraceLogLevel.LOG_INFO, "Loading thread initialized successfully");
state = State.STATE_LOADING;
}
}
break;
case State.STATE_LOADING:
{
framesCounter++;
if (dataLoaded)
{
framesCounter = 0;
state = State.STATE_FINISHED;
}
}
break;
case State.STATE_FINISHED:
{
if (IsKeyPressed(KEY_ENTER))
{
// Reset everything to launch again
// atomic_store(ref, false);
dataProgress = 0;
state = State.STATE_WAITING;
}
}
break;
default: break;
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(RAYWHITE);
switch (state)
{
case State.STATE_WAITING:
DrawText("PRESS ENTER to START LOADING DATA", 150, 170, 20, DARKGRAY);
break;
case State.STATE_LOADING:
{
DrawRectangle(150, 200, dataProgress, 60, SKYBLUE);
if ((framesCounter / 15) % 2 == 0) DrawText("LOADING DATA...", 240, 210, 40, DARKBLUE);
}
break;
case State.STATE_FINISHED:
{
DrawRectangle(150, 200, 500, 60, LIME);
DrawText("DATA LOADED!", 250, 210, 40, GREEN);
}
break;
default: break;
}
DrawRectangleLines(150, 200, 500, 60, DARKGRAY);
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow(); // Close window and OpenGL context
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}
// Loading data thread function definition
static void LoadDataThread()
void LoadDataThread()
{
int timeCounter = 0; // Time counted in ms
// clock_t prevTime = clock(); // Previous time
int timeCounter = 0; // Time counted in ms
var stopwatch = Stopwatch.StartNew();
// We simulate data loading with a time counter for 5 seconds
while (timeCounter < 5000)
{
//clock_t currentTime = clock() - prevTime;
//timeCounter = currentTime*1000/CLOCKS_PER_SEC;
timeCounter += 1;
timeCounter = (int)stopwatch.ElapsedMilliseconds;
// We accumulate time over a global variable to be used in
// main thread as a progress bar
@ -158,4 +188,3 @@ public class LoadingThread
dataLoaded = true;
}
}

View file

@ -0,0 +1,205 @@
/*******************************************************************************************
*
* raylib [core] example - monitor detector
*
* Example complexity rating: [★☆☆☆] 1/4
*
* Example originally created with raylib 5.5, last time updated with raylib 5.6
*
* Example contributed by Maicon Santana (@maiconpintoabreu) 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 Maicon Santana (@maiconpintoabreu)
*
********************************************************************************************/
namespace Examples.Core;
[ExcludeFromBrowser("GetMonitorCount() is not implemented on the wasm target")]
public partial class MonitorDetector : IExample
{
private const int MaxMonitors = 10;
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Core / Monitor Detector";
public string Title => "raylib [core] example - monitor detector";
// Monitor info
private struct MonitorInfo
{
public Vector2 Position;
public string Name;
public int Width;
public int Height;
public int PhysicalWidth;
public int PhysicalHeight;
public int RefreshRate;
}
private MonitorInfo[] monitors;
private int currentMonitorIndex;
private int monitorCount;
public void Init()
{
monitors = new MonitorInfo[MaxMonitors];
currentMonitorIndex = GetCurrentMonitor();
monitorCount = 0;
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
// Variables to find the max x and Y to calculate the scale
int maxWidth = 1;
int maxHeight = 1;
// Monitor offset is to fix when monitor position x is negative
int monitorOffsetX = 0;
// Rebuild monitors array every frame
monitorCount = GetMonitorCount();
for (int i = 0; i < monitorCount; i++)
{
monitors[i] = new MonitorInfo
{
Position = GetMonitorPosition(i),
Name = GetMonitorName_(i),
Width = GetMonitorWidth(i),
Height = GetMonitorHeight(i),
PhysicalWidth = GetMonitorPhysicalWidth(i),
PhysicalHeight = GetMonitorPhysicalHeight(i),
RefreshRate = GetMonitorRefreshRate(i)
};
if (monitors[i].Position.X < monitorOffsetX)
{
monitorOffsetX = -(int)monitors[i].Position.X;
}
int width = (int)monitors[i].Position.X + monitors[i].Width;
int height = (int)monitors[i].Position.Y + monitors[i].Height;
if (maxWidth < width)
{
maxWidth = width;
}
if (maxHeight < height)
{
maxHeight = height;
}
}
if (IsKeyPressed(KeyboardKey.Enter) && (monitorCount > 1))
{
currentMonitorIndex += 1;
// Set index to 0 if the last one
if (currentMonitorIndex == monitorCount)
{
currentMonitorIndex = 0;
}
SetWindowMonitor(currentMonitorIndex); // Move window to currentMonitorIndex
}
else
{
currentMonitorIndex = GetCurrentMonitor(); // Get currentMonitorIndex if manually moved
}
float monitorScale = 0.6f;
if (maxHeight > (maxWidth + monitorOffsetX))
{
monitorScale *= ((float)screenHeight / (float)maxHeight);
}
else
{
monitorScale *= ((float)screenWidth / (float)(maxWidth + monitorOffsetX));
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
DrawText("Press [Enter] to move window to next monitor available", 20, 20, 20, Color.DarkGray);
DrawRectangleLines(20, 60, screenWidth - 40, screenHeight - 100, Color.DarkGray);
// Draw Monitor Rectangles with information inside
for (int i = 0; i < monitorCount; i++)
{
// Calculate retangle position and size using monitorScale
Rectangle rec = new Rectangle(
(monitors[i].Position.X + monitorOffsetX) * monitorScale + 140,
monitors[i].Position.Y * monitorScale + 80,
monitors[i].Width * monitorScale,
monitors[i].Height * monitorScale
);
// Draw monitor name and information inside the rectangle
DrawText($"[{i}] {monitors[i].Name}", (int)rec.X + 10, (int)rec.Y + (int)(100 * monitorScale), (int)(120 * monitorScale), Color.Blue);
DrawText(
$"Resolution: [{monitors[i].Width}px x {monitors[i].Height}px]\nRefreshRate: [{monitors[i].RefreshRate}hz]\nPhysical Size: [{monitors[i].PhysicalWidth}mm x {monitors[i].PhysicalHeight}mm]\nPosition: {monitors[i].Position.X,3:F0} x {monitors[i].Position.Y,3:F0}",
(int)rec.X + 10, (int)rec.Y + (int)(200 * monitorScale), (int)(120 * monitorScale), Color.DarkGray);
// Highlight current monitor
if (i == currentMonitorIndex)
{
DrawRectangleLinesEx(rec, 5, Color.Red);
Vector2 windowPosition = new Vector2((GetWindowPosition().X + monitorOffsetX) * monitorScale + 140, GetWindowPosition().Y * monitorScale + 80);
// Draw window position based on monitors
DrawRectangleV(windowPosition, new Vector2(screenWidth * monitorScale, screenHeight * monitorScale), Fade(Color.Green, 0.5f));
}
else
{
DrawRectangleLinesEx(rec, 5, Color.Gray);
}
}
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [core] example - monitor detector");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new MonitorDetector();
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,119 +1,162 @@
/*******************************************************************************************
*
* raylib [core] example - Picking in 3d mode
* raylib [core] example - 3d picking
*
* This example has been created using raylib 1.3 (www.raylib.com)
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
* Example complexity rating: [★★☆☆] 2/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)
*
********************************************************************************************/
using System.Numerics;
using static Raylib_cs.Raylib;
namespace Examples.Core;
public class Picking3d
public partial class Picking3d : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Core / Picking 3D";
public string Title => "raylib [core] example - 3d picking";
private Camera3D camera;
private Vector3 cubePosition;
private Vector3 cubeSize;
private Ray ray; // Picking line ray
private RayCollision collision; // Ray collision hit info
public void Init()
{
// Define the camera to look into our 3d world
camera = new Camera3D();
camera.Position = new Vector3(10.0f, 10.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
cubePosition = new(0.0f, 1.0f, 0.0f);
cubeSize = new(2.0f, 2.0f, 2.0f);
ray = new(); // Picking line ray
collision = new(); // Ray collision hit info
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
if (IsCursorHidden())
{
UpdateCamera(ref camera, CameraMode.FirstPerson);
}
// Toggle camera controls
if (IsMouseButtonPressed(MouseButton.Right))
{
if (IsCursorHidden())
{
EnableCursor();
}
else
{
DisableCursor();
}
}
if (IsMouseButtonPressed(MouseButton.Left))
{
if (!collision.Hit)
{
ray = GetScreenToWorldRay(GetMousePosition(), camera);
// Check collision between ray and box
BoundingBox box = new(
cubePosition - cubeSize / 2,
cubePosition + cubeSize / 2
);
collision = GetRayCollisionBox(ray, box);
}
else
{
collision.Hit = false;
}
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
if (collision.Hit)
{
DrawCube(cubePosition, cubeSize.X, cubeSize.Y, cubeSize.Z, Color.Red);
DrawCubeWires(cubePosition, cubeSize.X, cubeSize.Y, cubeSize.Z, Color.Maroon);
DrawCubeWires(cubePosition, cubeSize.X + 0.2f, cubeSize.Y + 0.2f, cubeSize.Z + 0.2f, Color.Green);
}
else
{
DrawCube(cubePosition, cubeSize.X, cubeSize.Y, cubeSize.Z, Color.Gray);
DrawCubeWires(cubePosition, cubeSize.X, cubeSize.Y, cubeSize.Z, Color.DarkGray);
}
DrawRay(ray, Color.Maroon);
DrawGrid(10, 1.0f);
EndMode3D();
DrawText("Try clicking on the box with your mouse!", 240, 10, 20, Color.DarkGray);
if (collision.Hit)
{
var posX = (screenWidth - MeasureText("BOX SELECTED", 30)) / 2;
DrawText("BOX SELECTED", posX, (int)(screenHeight * 0.1f), 30, Color.Green);
}
DrawText("Right click mouse to toggle camera controls", 10, 430, 10, Color.Gray);
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [core] example - 3d picking");
// Define the camera to look into our 3d world
Camera3D camera;
camera.Position = new Vector3(10.0f, 10.0f, 10.0f);
camera.Target = new Vector3(0.0f, 0.0f, 0.0f);
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
camera.FovY = 45.0f;
camera.Projection = CameraProjection.Perspective;
Vector3 cubePosition = new(0.0f, 1.0f, 0.0f);
Vector3 cubeSize = new(2.0f, 2.0f, 2.0f);
// Picking line ray
Ray ray = new(new Vector3(0.0f, 0.0f, 0.0f), Vector3.Zero);
RayCollision collision = new();
SetTargetFPS(60);
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new Picking3d();
game.Init();
// Main game loop
while (!WindowShouldClose())
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Free);
if (IsMouseButtonPressed(MouseButton.Left))
{
if (!collision.Hit)
{
ray = GetScreenToWorldRay(GetMousePosition(), camera);
// Check collision between ray and box
BoundingBox box = new(
cubePosition - cubeSize / 2,
cubePosition + cubeSize / 2
);
collision = GetRayCollisionBox(ray, box);
}
else
{
collision.Hit = false;
}
ray = GetScreenToWorldRay(GetMousePosition(), camera);
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
if (collision.Hit)
{
DrawCube(cubePosition, cubeSize.X, cubeSize.Y, cubeSize.Z, Color.Red);
DrawCubeWires(cubePosition, cubeSize.X, cubeSize.Y, cubeSize.Z, Color.Maroon);
DrawCubeWires(cubePosition, cubeSize.X + 0.2f, cubeSize.Y + 0.2f, cubeSize.Z + 0.2f, Color.Green);
}
else
{
DrawCube(cubePosition, cubeSize.X, cubeSize.Y, cubeSize.Z, Color.Gray);
DrawCubeWires(cubePosition, cubeSize.X, cubeSize.Y, cubeSize.Z, Color.DarkGray);
}
DrawRay(ray, Color.Maroon);
DrawGrid(10, 1.0f);
EndMode3D();
DrawText("Try selecting the box with mouse!", 240, 10, 20, Color.DarkGray);
if (collision.Hit)
{
int posX = (screenWidth - MeasureText("BOX SELECTED", 30)) / 2;
DrawText("BOX SELECTED", posX, (int)(screenHeight * 0.1f), 30, Color.Green);
}
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow();
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;

View file

@ -0,0 +1,180 @@
/*******************************************************************************************
*
* raylib [core] example - random sequence
*
* Example complexity rating: [★☆☆☆] 1/4
*
* Example originally created with raylib 5.0, last time updated with raylib 5.0
*
* Example contributed by Dalton Overmyer (@REDl3east) 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) 2023-2025 Dalton Overmyer (@REDl3east)
*
********************************************************************************************/
using static Raylib_cs.Raymath;
namespace Examples.Core;
public partial class RandomSequence : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Core / Random Sequence";
public string Title => "raylib [core] example - random sequence";
private struct ColorRect
{
public Color Color;
public Rectangle Rect;
}
private int rectCount;
private float rectSize;
private ColorRect[] rectangles;
public void Init()
{
rectCount = 20;
rectSize = (float)screenWidth / rectCount;
rectangles = GenerateRandomColorRectSequence(rectCount, rectSize, screenWidth, 0.75f * screenHeight);
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
if (IsKeyPressed(KeyboardKey.Space))
{
ShuffleColorRectSequence(rectangles, rectCount);
}
if (IsKeyPressed(KeyboardKey.Up))
{
rectCount++;
rectSize = (float)screenWidth / rectCount;
// Re-generate random sequence with new count
rectangles = GenerateRandomColorRectSequence(rectCount, rectSize, screenWidth, 0.75f * screenHeight);
}
if (IsKeyPressed(KeyboardKey.Down))
{
if (rectCount >= 4)
{
rectCount--;
rectSize = (float)screenWidth / rectCount;
// Re-generate random sequence with new count
rectangles = GenerateRandomColorRectSequence(rectCount, rectSize, screenWidth, 0.75f * screenHeight);
}
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
for (int i = 0; i < rectCount; i++)
{
DrawRectangleRec(rectangles[i].Rect, rectangles[i].Color);
DrawText("Press SPACE to shuffle the current sequence", 10, screenHeight - 96, 20, Color.Black);
DrawText("Press UP to add a rectangle and generate a new sequence", 10, screenHeight - 64, 20, Color.Black);
DrawText("Press DOWN to remove a rectangle and generate a new sequence", 10, screenHeight - 32, 20, Color.Black);
}
DrawText($"Count: {rectCount} rectangles", 10, 10, 20, Color.Maroon);
DrawFPS(screenWidth - 80, 10);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
}
private static Color GenerateRandomColor()
{
return new Color(
GetRandomValue(0, 255),
GetRandomValue(0, 255),
GetRandomValue(0, 255),
255
);
}
private static ColorRect[] GenerateRandomColorRectSequence(float rectCount, float rectWidth, float screenWidth, float screenHeight)
{
ColorRect[] rectangles = new ColorRect[(int)rectCount];
int[] seq = GetRandomSequence((uint)rectCount, 0, (int)rectCount - 1);
float rectSeqWidth = rectCount * rectWidth;
float startX = (screenWidth - rectSeqWidth) * 0.5f;
for (int i = 0; i < rectCount; i++)
{
int rectHeight = (int)Remap(seq[i], 0, rectCount - 1, 0, screenHeight);
rectangles[i].Color = GenerateRandomColor();
rectangles[i].Rect = new Rectangle(startX + i * rectWidth, screenHeight - rectHeight, rectWidth, rectHeight);
}
return rectangles;
}
private static void ShuffleColorRectSequence(ColorRect[] rectangles, int rectCount)
{
int[] seq = GetRandomSequence((uint)rectCount, 0, rectCount - 1);
for (int i1 = 0; i1 < rectCount; i1++)
{
int i2 = seq[i1];
// Swap only the color and height
ColorRect tmp = rectangles[i1];
rectangles[i1].Color = rectangles[i2].Color;
rectangles[i1].Rect.Height = rectangles[i2].Rect.Height;
rectangles[i1].Rect.Y = rectangles[i2].Rect.Y;
rectangles[i2].Color = tmp.Color;
rectangles[i2].Rect.Height = tmp.Rect.Height;
rectangles[i2].Rect.Y = tmp.Rect.Y;
}
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [core] example - random sequence");
SetTargetFPS(60);
var game = new RandomSequence();
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,69 +1,95 @@
/*******************************************************************************************
*
* raylib [core] example - Generate random values
* raylib [core] example - random values
*
* 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 1.1
*
* 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)
*
********************************************************************************************/
using static Raylib_cs.Raylib;
namespace Examples.Core;
public class RandomValues
public partial class RandomValues : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Core / Random Values";
public string Title => "raylib [core] example - random values";
private int randValue; // Get a random integer number between -8 and 5 (both included)
private int framesCounter; // Variable used to count frames
public void Init()
{
// SetRandomSeed(0xaabbccff); // Set a custom random seed if desired, by default: "time(NULL)"
randValue = GetRandomValue(-8, 5); // Get a random integer number between -8 and 5 (both included)
framesCounter = 0; // Variable used to count frames
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
framesCounter++;
// Every two seconds (120 frames) a new random value is generated
if (((framesCounter / 120) % 2) == 1)
{
randValue = GetRandomValue(-8, 5);
framesCounter = 0;
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
DrawText("Every 2 seconds a new random value is generated:", 130, 100, 20, Color.Maroon);
DrawText($"{randValue}", 360, 180, 80, Color.LightGray);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [core] example - random values");
InitWindow(screenWidth, screenHeight, "raylib [core] example - generate random values");
// Variable used to count frames
int framesCounter = 0;
// Get a random integer number between -8 and 5 (both included)
int randValue = GetRandomValue(-8, 5);
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new RandomValues();
game.Init();
// Main game loop
while (!WindowShouldClose())
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
framesCounter++;
// Every two seconds (120 frames) a new random value is generated
if (((framesCounter / 120) % 2) == 1)
{
randValue = GetRandomValue(-8, 5);
framesCounter = 0;
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
DrawText("Every 2 seconds a new random value is generated:", 130, 100, 20, Color.Maroon);
DrawText($"{randValue}", 360, 180, 80, Color.LightGray);
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow();
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;

View file

@ -0,0 +1,135 @@
/*******************************************************************************************
*
* raylib [core] example - render texture
*
* Example complexity rating: [★☆☆☆] 1/4
*
* Example originally created with raylib 6.0, last time updated with raylib 6.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) 2025 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
namespace Examples.Core;
public partial class RenderTextureDemo : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
// Define a render texture to render
private const int renderTextureWidth = 300;
private const int renderTextureHeight = 300;
public string Name => "Core / Render Texture";
public string Title => "raylib [core] example - render texture";
private RenderTexture2D target;
private Vector2 ballPosition;
private Vector2 ballSpeed;
private int ballRadius;
private float rotation;
public void Init()
{
target = LoadRenderTexture(renderTextureWidth, renderTextureHeight);
ballPosition = new Vector2(renderTextureWidth / 2.0f, renderTextureHeight / 2.0f);
ballSpeed = new Vector2(5.0f, 4.0f);
ballRadius = 20;
rotation = 0.0f;
}
public void Update()
{
// Update
//-----------------------------------------------------
// Ball movement logic
ballPosition.X += ballSpeed.X;
ballPosition.Y += ballSpeed.Y;
// Check walls collision for bouncing
if ((ballPosition.X >= (renderTextureWidth - ballRadius)) || (ballPosition.X <= ballRadius))
{
ballSpeed.X *= -1.0f;
}
if ((ballPosition.Y >= (renderTextureHeight - ballRadius)) || (ballPosition.Y <= ballRadius))
{
ballSpeed.Y *= -1.0f;
}
// Render texture rotation
rotation += 0.5f;
//-----------------------------------------------------
// Draw
//-----------------------------------------------------
// Draw our scene to the render texture
BeginTextureMode(target);
ClearBackground(Color.SkyBlue);
DrawRectangle(0, 0, 20, 20, Color.Red);
DrawCircleV(ballPosition, ballRadius, Color.Maroon);
EndTextureMode();
// Draw render texture to main framebuffer
BeginDrawing();
ClearBackground(Color.RayWhite);
// Draw our render texture with rotation applied
// NOTE 1: We set the origin of the texture to the center of the render texture
// NOTE 2: We flip vertically the texture setting negative source rectangle height
DrawTexturePro(target.Texture,
new Rectangle(0, 0, target.Texture.Width, -target.Texture.Height),
new Rectangle(screenWidth / 2.0f, screenHeight / 2.0f, target.Texture.Width, target.Texture.Height),
new Vector2(target.Texture.Width / 2.0f, target.Texture.Height / 2.0f), rotation, Color.White);
DrawText("DRAWING BOUNCING BALL INSIDE RENDER TEXTURE!", 10, screenHeight - 40, 20, Color.Black);
DrawFPS(10, 10);
EndDrawing();
//-----------------------------------------------------
}
public void Unload()
{
UnloadRenderTexture(target);
}
public static int Main()
{
// Initialization
//---------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [core] example - render texture");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//----------------------------------------------------------
var game = new RenderTextureDemo();
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,82 +1,108 @@
/*******************************************************************************************
*
* raylib [core] example - Scissor test
* raylib [core] example - scissor test
*
* 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: [★☆☆☆] 1/4
*
* Example originally created with raylib 2.5, last time updated with raylib 3.0
*
* Example contributed by Chris Dill (@MysteriousSpace) and reviewed by Ramon Santamaria (@raysan5)
*
* Copyright (c) 2019 Chris Dill (@MysteriousSpace)
* 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 Chris Dill (@MysteriousSpace)
*
********************************************************************************************/
using static Raylib_cs.Raylib;
namespace Examples.Core;
public class ScissorTest
public partial class ScissorTest : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Core / Scissor Test";
public string Title => "raylib [core] example - scissor test";
private Rectangle scissorArea;
private bool scissorMode;
public void Init()
{
scissorArea = new(0, 0, 300, 300);
scissorMode = true;
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
if (IsKeyPressed(KeyboardKey.S))
{
scissorMode = !scissorMode;
}
// Centre the scissor area around the mouse position
scissorArea.X = GetMouseX() - scissorArea.Width / 2;
scissorArea.Y = GetMouseY() - scissorArea.Height / 2;
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
if (scissorMode)
{
BeginScissorMode((int)scissorArea.X, (int)scissorArea.Y, (int)scissorArea.Width, (int)scissorArea.Height);
}
// Draw full screen rectangle and some text
// NOTE: Only part defined by scissor area will be rendered
DrawRectangle(0, 0, GetScreenWidth(), GetScreenHeight(), Color.Red);
DrawText("Move the mouse around to reveal this text!", 190, 200, 20, Color.LightGray);
if (scissorMode)
{
EndScissorMode();
}
DrawRectangleLinesEx(scissorArea, 1, Color.Black);
DrawText("Press S to toggle scissor test", 10, 10, 20, Color.Black);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [core] example - scissor test");
Rectangle scissorArea = new(0, 0, 300, 300);
bool scissorMode = true;
SetTargetFPS(60);
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new ScissorTest();
game.Init();
// Main game loop
while (!WindowShouldClose())
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
if (IsKeyPressed(KeyboardKey.S))
{
scissorMode = !scissorMode;
}
// Centre the scissor area around the mouse position
scissorArea.X = GetMouseX() - scissorArea.Width / 2;
scissorArea.Y = GetMouseY() - scissorArea.Height / 2;
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
if (scissorMode)
{
BeginScissorMode((int)scissorArea.X, (int)scissorArea.Y, (int)scissorArea.Width, (int)scissorArea.Height);
}
// Draw full screen rectangle and some text
// NOTE: Only part defined by scissor area will be rendered
DrawRectangle(0, 0, GetScreenWidth(), GetScreenHeight(), Color.Red);
DrawText("Move the mouse around to reveal this text!", 190, 200, 20, Color.LightGray);
if (scissorMode)
{
EndScissorMode();
}
DrawRectangleLinesEx(scissorArea, 1, Color.Black);
DrawText("Press S to toggle scissor test", 10, 10, 20, Color.Black);
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow();
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;

View file

@ -0,0 +1,394 @@
/*******************************************************************************************
*
* raylib [core] example - screen recording
*
* Example complexity rating: [★★☆☆] 2/4
*
* Example originally created with raylib 6.0, last time updated with raylib 6.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) 2025 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
namespace Examples.Core;
// NOTE: The upstream C example records frames into an animated GIF using the bundled msf_gif.h
// single-header library. raylib-cs does not bind msf_gif, so this port replaces it with a small,
// self-contained GIF89a encoder (GifRecorder, below) that uses a fixed 3-3-2 RGB palette. The
// rest of the example (rendering, CTRL+R toggle, saving to <appdir>/screenrecording.gif) mirrors
// upstream. Frame capture via LoadImageFromScreen() is slow and can cause stuttering, as noted
// upstream.
[ExcludeFromBrowser("desktop screen capture + gif file export, no web equivalent")]
public partial class ScreenRecording : IExample
{
private const int GIF_RECORD_FRAMERATE = 5; // Record framerate, we get a frame every N frames
private const int MAX_SINEWAVE_POINTS = 256;
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Core / Screen Recording";
public string Title => "raylib [core] example - screen recording";
private bool gifRecording; // GIF recording state
private uint gifFrameCounter; // GIF frames counter
private GifRecorder gifState; // GIF context state
private Vector2 circlePosition;
private float timeCounter;
private Vector2[] sinePoints;
public void Init()
{
gifRecording = false;
gifFrameCounter = 0;
gifState = new GifRecorder();
circlePosition = new Vector2(0.0f, screenHeight / 2.0f);
timeCounter = 0.0f;
// Get sine wave points for line drawing
sinePoints = new Vector2[MAX_SINEWAVE_POINTS];
for (int i = 0; i < MAX_SINEWAVE_POINTS; i++)
{
sinePoints[i].X = i * GetScreenWidth() / 180.0f;
sinePoints[i].Y = screenHeight / 2.0f + 150 * MathF.Sin((2 * MathF.PI / 1.5f) * (1.0f / 60.0f) * (float)i); // Calculate for 60 fps
}
}
public unsafe void Update()
{
// Update
//----------------------------------------------------------------------------------
// Update circle sinusoidal movement
timeCounter += GetFrameTime();
circlePosition.X += GetScreenWidth() / 180.0f;
circlePosition.Y = screenHeight / 2.0f + 150 * MathF.Sin((2 * MathF.PI / 1.5f) * timeCounter);
if (circlePosition.X > screenWidth)
{
circlePosition.X = 0.0f;
circlePosition.Y = screenHeight / 2.0f;
timeCounter = 0.0f;
}
// Start-Stop GIF recording on CTRL+R
if (IsKeyDown(KeyboardKey.LeftControl) && IsKeyPressed(KeyboardKey.R))
{
if (gifRecording)
{
// Stop current recording and save file
gifRecording = false;
byte[] result = gifState.End();
SaveFileData(result, $"{GetApplicationDirectoryString()}/screenrecording.gif");
TraceLog(TraceLogLevel.Info, "Finish animated GIF recording");
}
else
{
// Start a new recording
gifRecording = true;
gifFrameCounter = 0;
gifState.Begin(GetRenderWidth(), GetRenderHeight());
TraceLog(TraceLogLevel.Info, "Start animated GIF recording");
}
}
if (gifRecording)
{
gifFrameCounter++;
// NOTE: We record one gif frame depending on the desired gif framerate
if (gifFrameCounter > GIF_RECORD_FRAMERATE)
{
// Get image data for the current frame (from backbuffer)
// WARNING: This process is quite slow, it can generate stuttering
Image imScreen = LoadImageFromScreen();
// Add the frame to the gif recording, providing and "estimated" time for display in centiseconds
int delayCs = (int)((1.0f / 60.0f) * GIF_RECORD_FRAMERATE) / 10;
gifState.AddFrame((byte*)imScreen.Data, imScreen.Width, imScreen.Height, imScreen.Width * 4, delayCs);
gifFrameCounter = 0;
UnloadImage(imScreen); // Free image data
}
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
for (int i = 0; i < (MAX_SINEWAVE_POINTS - 1); i++)
{
DrawLineV(sinePoints[i], sinePoints[i + 1], Color.Maroon);
DrawCircleV(sinePoints[i], 3, Color.Maroon);
}
DrawCircleV(circlePosition, 30, Color.Red);
DrawFPS(10, 10);
/*
// Draw record indicator
// WARNING: If drawn here, it will appear in the recorded image,
// use a render texture instead for the recording and LoadImageFromTexture(rt.texture)
if (gifRecording)
{
// Display the recording indicator every half-second
if ((int)(GetTime()/0.5)%2 == 1)
{
DrawCircle(30, GetScreenHeight() - 20, 10, Color.Maroon);
DrawText("GIF RECORDING", 50, GetScreenHeight() - 25, 10, Color.Red);
}
}
*/
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
// If still recording a GIF on close window, just finish
if (gifRecording)
{
gifState.End();
gifRecording = false;
}
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [core] example - screen recording");
var game = new ScreenRecording();
game.Init();
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
// 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;
}
// Minimal self-contained animated GIF89a encoder (replacement for msf_gif.h)
// Uses a fixed 3-3-2 RGB global palette and standard GIF-variant LZW compression.
private class GifRecorder
{
private List<byte> output;
private int width;
private int height;
// LZW bit-packing state (per-frame)
private int bitBuffer;
private int bitCount;
private List<byte> subBlock;
public void Begin(int w, int h)
{
width = w;
height = h;
output = new List<byte>();
// Header
output.AddRange(new byte[] { (byte)'G', (byte)'I', (byte)'F', (byte)'8', (byte)'9', (byte)'a' });
// Logical Screen Descriptor
WriteU16(width);
WriteU16(height);
output.Add(0xF7); // Global color table present, 8-bit color res, 256-entry table
output.Add(0x00); // Background color index
output.Add(0x00); // Pixel aspect ratio
// Global Color Table: 256 entries, 3-3-2 RGB
for (int k = 0; k < 256; k++)
{
int r3 = (k >> 5) & 0x7;
int g3 = (k >> 2) & 0x7;
int b2 = k & 0x3;
output.Add((byte)((r3 << 5) | (r3 << 2) | (r3 >> 1)));
output.Add((byte)((g3 << 5) | (g3 << 2) | (g3 >> 1)));
output.Add((byte)((b2 << 6) | (b2 << 4) | (b2 << 2) | b2));
}
// NETSCAPE2.0 application extension (loop forever)
output.Add(0x21);
output.Add(0xFF);
output.Add(0x0B);
output.AddRange(new byte[] { (byte)'N', (byte)'E', (byte)'T', (byte)'S', (byte)'C', (byte)'A', (byte)'P', (byte)'E', (byte)'2', (byte)'.', (byte)'0' });
output.Add(0x03);
output.Add(0x01);
WriteU16(0); // Loop count (0 = forever)
output.Add(0x00);
}
public unsafe void AddFrame(byte* data, int w, int h, int stride, int delayCs)
{
if (output == null)
{
return;
}
// Graphic Control Extension
output.Add(0x21);
output.Add(0xF9);
output.Add(0x04);
output.Add(0x00); // No transparency, disposal method 0
WriteU16(delayCs);
output.Add(0x00); // Transparent color index
output.Add(0x00); // Block terminator
// Image Descriptor
output.Add(0x2C);
WriteU16(0); // Left
WriteU16(0); // Top
WriteU16(w);
WriteU16(h);
output.Add(0x00); // No local color table, not interlaced
// Map pixels to palette indices (3-3-2)
byte[] indices = new byte[w * h];
for (int y = 0; y < h; y++)
{
int row = y * stride;
int dst = y * w;
for (int x = 0; x < w; x++)
{
byte r = data[row + x * 4 + 0];
byte g = data[row + x * 4 + 1];
byte b = data[row + x * 4 + 2];
indices[dst + x] = (byte)((r & 0xE0) | ((g & 0xE0) >> 3) | (b >> 6));
}
}
// LZW image data
const int minCodeSize = 8;
output.Add((byte)minCodeSize);
bitBuffer = 0;
bitCount = 0;
subBlock = new List<byte>();
int clearCode = 1 << minCodeSize; // 256
int stopCode = clearCode + 1; // 257
int keySize = minCodeSize + 1; // 9
int nkeys = clearCode + 2; // 258
var dict = new Dictionary<int, int>();
WriteBits(clearCode, keySize);
int key = indices[0];
for (int i = 1; i < indices.Length; i++)
{
int p = indices[i];
int combined = (key << 8) | p;
if (dict.TryGetValue(combined, out int existing))
{
key = existing;
}
else
{
WriteBits(key, keySize);
dict[combined] = nkeys;
nkeys++;
if (nkeys == (1 << keySize))
{
if (keySize < 12)
{
keySize++;
}
}
if (nkeys == 0x1000)
{
WriteBits(clearCode, keySize);
dict.Clear();
keySize = minCodeSize + 1;
nkeys = clearCode + 2;
}
key = p;
}
}
WriteBits(key, keySize);
WriteBits(stopCode, keySize);
// Flush remaining bits
if (bitCount > 0)
{
subBlock.Add((byte)(bitBuffer & 0xFF));
bitBuffer = 0;
bitCount = 0;
}
if (subBlock.Count > 0)
{
FlushSubBlock();
}
output.Add(0x00); // Image data block terminator
}
public byte[] End()
{
if (output == null)
{
return Array.Empty<byte>();
}
output.Add(0x3B); // Trailer
byte[] result = output.ToArray();
output = null;
return result;
}
private void WriteBits(int code, int len)
{
bitBuffer |= code << bitCount;
bitCount += len;
while (bitCount >= 8)
{
subBlock.Add((byte)(bitBuffer & 0xFF));
bitBuffer >>= 8;
bitCount -= 8;
if (subBlock.Count == 255)
{
FlushSubBlock();
}
}
}
private void FlushSubBlock()
{
output.Add((byte)subBlock.Count);
output.AddRange(subBlock);
subBlock.Clear();
}
private void WriteU16(int value)
{
output.Add((byte)(value & 0xFF));
output.Add((byte)((value >> 8) & 0xFF));
}
}
}

View file

@ -1,134 +1,210 @@
/*******************************************************************************************
*
* raylib [core] example - smooth pixel-perfect camera
* raylib [core] example - smooth pixelperfect
*
* This example has been created using raylib 3.7 (www.raylib.com)
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
* Example complexity rating: [★★★☆] 3/4
*
* Example originally created with raylib 3.7, last time updated with raylib 4.0
*
* Example contributed by Giancamillo Alessandroni (@NotManyIdeasDev) and
* reviewed by Ramon Santamaria (@raysan5)
*
* Copyright (c) 2021 Giancamillo Alessandroni (@NotManyIdeasDev) 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) 2021-2025 Giancamillo Alessandroni (@NotManyIdeasDev) and Ramon Santamaria (@raysan5)
*
********************************************************************************************/
using System;
using System.Numerics;
using static Raylib_cs.Raylib;
namespace Examples.Core;
public static class SmoothPixelPerfect
public partial class SmoothPixelPerfect : IExample
{
public static int Main()
private const int screenWidth = 800;
private const int screenHeight = 450;
private const int virtualScreenWidth = 160;
private const int virtualScreenHeight = 90;
private const float virtualRatio = (float)screenWidth / (float)virtualScreenWidth;
public string Name => "Core / Smooth Pixelperfect";
public string Title => "raylib [core] example - smooth pixelperfect";
private Camera2D worldSpaceCamera; // Game world camera
private Camera2D screenSpaceCamera; // Smoothing camera
private RenderTexture2D target;
private Rectangle rec01;
private Rectangle rec02;
private Rectangle rec03;
private Rectangle sourceRec;
private Rectangle destRec;
private Vector2 origin;
private float rotation;
private float cameraX;
private float cameraY;
private bool smoothOn;
private bool overscan;
public void Init()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
const int virtualScreenWidth = 160;
const int virtualScreenHeight = 90;
const float virtualRatio = (float)screenWidth / (float)virtualScreenWidth;
InitWindow(screenWidth, screenHeight, "raylib [core] example - smooth pixel-perfect camera");
// Game world camera
Camera2D worldSpaceCamera = new();
worldSpaceCamera = new(); // Game world camera
worldSpaceCamera.Zoom = 1.0f;
// Smoothing camera
Camera2D screenSpaceCamera = new();
screenSpaceCamera = new(); // Smoothing camera
screenSpaceCamera.Zoom = 1.0f;
// This is where we'll draw all our objects.
RenderTexture2D target = LoadRenderTexture(virtualScreenWidth, virtualScreenHeight);
// Load render texture to draw all our objects
target = LoadRenderTexture(virtualScreenWidth, virtualScreenHeight);
Rectangle rec01 = new(70.0f, 35.0f, 20.0f, 20.0f);
Rectangle rec02 = new(90.0f, 55.0f, 30.0f, 10.0f);
Rectangle rec03 = new(80.0f, 65.0f, 15.0f, 25.0f);
rec01 = new(70.0f, 35.0f, 20.0f, 20.0f);
rec02 = new(90.0f, 55.0f, 30.0f, 10.0f);
rec03 = new(80.0f, 65.0f, 15.0f, 25.0f);
// The target's height is flipped (in the source Rectangle), due to OpenGL reasons
Rectangle sourceRec = new(
sourceRec = new(
0.0f,
0.0f,
(float)target.Texture.Width,
-(float)target.Texture.Height
);
Rectangle destRec = new(
-virtualRatio,
-virtualRatio,
screenWidth + (virtualRatio * 2),
screenHeight + (virtualRatio * 2)
destRec = new(
(screenWidth - screenWidth / 1.25f) / 2.0f,
(screenHeight - screenHeight / 1.25f) / 2.0f,
screenWidth / 1.25f,
screenHeight / 1.25f
);
Vector2 origin = new(0.0f, 0.0f);
origin = new(0.0f, 0.0f);
float rotation = 0.0f;
rotation = 0.0f;
float cameraX = 0.0f;
float cameraY = 0.0f;
cameraX = 0.0f;
cameraY = 0.0f;
smoothOn = true;
overscan = false;
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
rotation += 60.0f * GetFrameTime(); // Rotate the rectangles, 60 degrees per second
// Make the camera move to demonstrate the effect
cameraX = (MathF.Sin((float)GetTime()) * 50.0f) - 10.0f;
cameraY = MathF.Cos((float)GetTime()) * 30.0f;
// Set the camera's target to the values computed above
screenSpaceCamera.Target = new Vector2(cameraX, cameraY);
// Round worldSpace coordinates, keep decimals into screenSpace coordinates
worldSpaceCamera.Target.X = MathF.Truncate(screenSpaceCamera.Target.X);
screenSpaceCamera.Target.X -= worldSpaceCamera.Target.X;
screenSpaceCamera.Target.X *= virtualRatio;
worldSpaceCamera.Target.Y = MathF.Truncate(screenSpaceCamera.Target.Y);
screenSpaceCamera.Target.Y -= worldSpaceCamera.Target.Y;
screenSpaceCamera.Target.Y *= virtualRatio;
if (IsKeyPressed(KeyboardKey.S))
{
smoothOn = !smoothOn;
}
if (IsKeyPressed(KeyboardKey.O))
{
overscan = !overscan;
}
if (overscan)
{
destRec = new Rectangle(
-virtualRatio,
-virtualRatio,
screenWidth + (virtualRatio * 2),
screenHeight + (virtualRatio * 2)
);
}
else
{
destRec = new Rectangle(
(screenWidth - screenWidth / 1.25f) / 2.0f,
(screenHeight - screenHeight / 1.25f) / 2.0f,
screenWidth / 1.25f,
screenHeight / 1.25f
);
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginTextureMode(target);
ClearBackground(Color.RayWhite);
BeginMode2D(worldSpaceCamera);
DrawRectanglePro(rec01, origin, rotation, Color.Black);
DrawRectanglePro(rec02, origin, -rotation, Color.Red);
DrawRectanglePro(rec03, origin, rotation + 45.0f, Color.Blue);
EndMode2D();
EndTextureMode();
BeginDrawing();
ClearBackground(Color.LightGray);
if (smoothOn)
{
BeginMode2D(screenSpaceCamera);
DrawTexturePro(target.Texture, sourceRec, destRec, origin, 0.0f, Color.White);
EndMode2D();
}
else
{
DrawTexturePro(target.Texture, sourceRec, destRec, origin, 0.0f, Color.White);
}
DrawText($"Screen resolution: {screenWidth}x{screenHeight}", 10, 10, 20, Color.DarkBlue);
DrawText($"World resolution: {virtualScreenWidth}x{virtualScreenHeight}", 10, 40, 20, Color.DarkGreen);
DrawText($"Smooth: {(smoothOn ? "ON" : "OFF")}", 10, screenHeight - 60, 20, Color.Red);
DrawText($"Overscan: {(overscan ? "ON" : "OFF")}", 10, screenHeight - 30, 20, Color.Red);
DrawFPS(GetScreenWidth() - 95, 10);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadRenderTexture(target); // Unload render texture
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [core] example - smooth pixelperfect");
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
var game = new SmoothPixelPerfect();
game.Init();
// Main game loop
while (!WindowShouldClose())
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
rotation += 60.0f * GetFrameTime(); // Rotate the rectangles, 60 degrees per second
// Make the camera move to demonstrate the effect
cameraX = (MathF.Sin((float)GetTime()) * 50.0f) - 10.0f;
cameraY = MathF.Cos((float)GetTime()) * 30.0f;
// Set the camera's target to the values computed above
screenSpaceCamera.Target = new Vector2(cameraX, cameraY);
// Round worldSpace coordinates, keep decimals into screenSpace coordinates
worldSpaceCamera.Target.X = (int)screenSpaceCamera.Target.X;
screenSpaceCamera.Target.X -= worldSpaceCamera.Target.X;
screenSpaceCamera.Target.X *= virtualRatio;
worldSpaceCamera.Target.Y = (int)screenSpaceCamera.Target.Y;
screenSpaceCamera.Target.Y -= worldSpaceCamera.Target.Y;
screenSpaceCamera.Target.Y *= virtualRatio;
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginTextureMode(target);
ClearBackground(Color.RayWhite);
BeginMode2D(worldSpaceCamera);
DrawRectanglePro(rec01, origin, rotation, Color.Black);
DrawRectanglePro(rec02, origin, -rotation, Color.Red);
DrawRectanglePro(rec03, origin, rotation + 45.0f, Color.Blue);
EndMode2D();
EndTextureMode();
BeginDrawing();
ClearBackground(Color.Red);
BeginMode2D(screenSpaceCamera);
DrawTexturePro(target.Texture, sourceRec, destRec, origin, 0.0f, Color.White);
EndMode2D();
DrawText($"Screen resolution: {screenWidth}x{screenHeight}", 10, 10, 20, Color.DarkBlue);
DrawText($"World resolution: {virtualScreenWidth}x{virtualScreenHeight}", 10, 40, 20, Color.DarkGreen);
DrawFPS(GetScreenWidth() - 95, 10);
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadRenderTexture(target);
CloseWindow();
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;

View file

@ -1,40 +1,50 @@
/*******************************************************************************************
*
* raylib [core] example - split screen
* raylib [core] example - 3d camera split screen
*
* This example has been created using raylib 3.7 (www.raylib.com)
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
* Example complexity rating: [★★★☆] 3/4
*
* Example originally created with raylib 3.7, last time updated with raylib 4.0
*
* Example contributed by Jeffery Myers (@JeffM2501) and reviewed by Ramon Santamaria (@raysan5)
*
* Copyright (c) 2021 Jeffery Myers (@JeffM2501)
* 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 Jeffery Myers (@JeffM2501)
*
********************************************************************************************/
using System.Numerics;
using static Raylib_cs.Raylib;
namespace Examples.Core;
public unsafe class SplitScreen
public partial class SplitScreen : IExample
{
static Texture2D TextureGrid;
static Camera3D CameraPlayer1;
static Camera3D CameraPlayer2;
private const int screenWidth = 800;
private const int screenHeight = 450;
private Camera3D CameraPlayer1;
private Camera3D CameraPlayer2;
private RenderTexture2D screenPlayer1;
private RenderTexture2D screenPlayer2;
private Rectangle splitScreenRect;
public string Name => "Core / Split Screen";
public string Title => "raylib [core] example - 3d camera split screen";
// Scene drawing
static void DrawScene()
private void DrawScene()
{
int count = 5;
var count = 5;
float spacing = 4;
// Grid of cube trees on a plane to make a "world"
// Simple world plane
DrawPlane(new Vector3(0, 0, 0), new Vector2(50, 50), Color.Beige);
// Draw scene: grid of cube trees on a plane to make a "world"
DrawPlane(new Vector3(0, 0, 0), new Vector2(50, 50), Color.Beige); // Simple world plane
for (float x = -count * spacing; x <= count * spacing; x += spacing)
for (var x = -count * spacing; x <= count * spacing; x += spacing)
{
for (float z = -count * spacing; z <= count * spacing; z += spacing)
for (var z = -count * spacing; z <= count * spacing; z += spacing)
{
DrawCube(new Vector3(x, 1.5f, z), 1, 1, 1, Color.Lime);
DrawCube(new Vector3(x, 0.5f, z), 0.25f, 1, 0.25f, Color.Brown);
@ -46,22 +56,8 @@ public unsafe class SplitScreen
DrawCube(CameraPlayer2.Position, 1, 1, 1, Color.Blue);
}
public static int Main()
public void Init()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [core] example - split screen");
// Generate a simple texture to use for trees
Image img = GenImageChecked(256, 256, 32, 32, Color.DarkGray, Color.White);
TextureGrid = LoadTextureFromImage(img);
UnloadImage(img);
SetTextureFilter(TextureGrid, TextureFilter.Anisotropic16X);
SetTextureWrap(TextureGrid, TextureWrap.Clamp);
// Setup player 1 camera and screen
CameraPlayer1.FovY = 45.0f;
CameraPlayer1.Up.Y = 1.0f;
@ -69,7 +65,7 @@ public unsafe class SplitScreen
CameraPlayer1.Position.Z = -3.0f;
CameraPlayer1.Position.Y = 1.0f;
RenderTexture2D screenPlayer1 = LoadRenderTexture(screenWidth / 2, screenHeight);
screenPlayer1 = LoadRenderTexture(screenWidth / 2, screenHeight);
// Setup player two camera and screen
CameraPlayer2.FovY = 45.0f;
@ -78,97 +74,119 @@ public unsafe class SplitScreen
CameraPlayer2.Position.X = -3.0f;
CameraPlayer2.Position.Y = 3.0f;
RenderTexture2D screenPlayer2 = LoadRenderTexture(screenWidth / 2, screenHeight);
screenPlayer2 = LoadRenderTexture(screenWidth / 2, screenHeight);
// Build a flipped rectangle the size of the split view to use for drawing later
Rectangle splitScreenRect = new(
splitScreenRect = new(
0.0f,
0.0f,
(float)screenPlayer1.Texture.Width,
(float)-screenPlayer1.Texture.Height
);
}
SetTargetFPS(60);
public void Update()
{
// Update
//----------------------------------------------------------------------------------
// If anyone moves this frame, how far will they move based on the time since the last frame
// this moves things at 10 world units per second, regardless of the actual FPS
var offsetThisFrame = 10.0f * GetFrameTime();
// Move Player1 forward and backwards (no turning)
if (IsKeyDown(KeyboardKey.W))
{
CameraPlayer1.Position.Z += offsetThisFrame;
CameraPlayer1.Target.Z += offsetThisFrame;
}
else if (IsKeyDown(KeyboardKey.S))
{
CameraPlayer1.Position.Z -= offsetThisFrame;
CameraPlayer1.Target.Z -= offsetThisFrame;
}
// Move Player2 forward and backwards (no turning)
if (IsKeyDown(KeyboardKey.Up))
{
CameraPlayer2.Position.X += offsetThisFrame;
CameraPlayer2.Target.X += offsetThisFrame;
}
else if (IsKeyDown(KeyboardKey.Down))
{
CameraPlayer2.Position.X -= offsetThisFrame;
CameraPlayer2.Target.X -= offsetThisFrame;
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
// Draw Player1 view to the render texture
BeginTextureMode(screenPlayer1);
ClearBackground(Color.SkyBlue);
BeginMode3D(CameraPlayer1);
DrawScene();
EndMode3D();
DrawRectangle(0, 0, GetScreenWidth() / 2, 40, Fade(Color.RayWhite, 0.8f));
DrawText("PLAYER1: W/S to move", 10, 10, 20, Color.Maroon);
EndTextureMode();
// Draw Player2 view to the render texture
BeginTextureMode(screenPlayer2);
ClearBackground(Color.SkyBlue);
BeginMode3D(CameraPlayer2);
DrawScene();
EndMode3D();
DrawRectangle(0, 0, GetScreenWidth() / 2, 40, Fade(Color.RayWhite, 0.8f));
DrawText("PLAYER2: UP/DOWN to move", 10, 10, 20, Color.DarkBlue);
EndTextureMode();
// Draw both views render textures to the screen side by side
BeginDrawing();
ClearBackground(Color.Black);
DrawTextureRec(screenPlayer1.Texture, splitScreenRect, new Vector2(0, 0), Color.White);
DrawTextureRec(screenPlayer2.Texture, splitScreenRect, new Vector2(screenWidth / 2.0f, 0), Color.White);
DrawRectangle(GetScreenWidth() / 2 - 2, 0, 4, GetScreenHeight(), Color.LightGray);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadRenderTexture(screenPlayer1); // Unload render texture
UnloadRenderTexture(screenPlayer2); // Unload render texture
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [core] example - 3d camera split screen");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new SplitScreen();
game.Init();
// Main game loop
while (!WindowShouldClose())
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
// If anyone moves this frame, how far will they move based on the time since the last frame
// this moves thigns at 10 world units per second, regardless of the actual FPS
float offsetThisFrame = 10.0f * GetFrameTime();
// Move Player1 forward and backwards (no turning)
if (IsKeyDown(KeyboardKey.W))
{
CameraPlayer1.Position.Z += offsetThisFrame;
CameraPlayer1.Target.Z += offsetThisFrame;
}
else if (IsKeyDown(KeyboardKey.S))
{
CameraPlayer1.Position.Z -= offsetThisFrame;
CameraPlayer1.Target.Z -= offsetThisFrame;
}
// Move Player2 forward and backwards (no turning)
if (IsKeyDown(KeyboardKey.Up))
{
CameraPlayer2.Position.X += offsetThisFrame;
CameraPlayer2.Target.X += offsetThisFrame;
}
else if (IsKeyDown(KeyboardKey.Down))
{
CameraPlayer2.Position.X -= offsetThisFrame;
CameraPlayer2.Target.X -= offsetThisFrame;
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
// Draw Player1 view to the render texture
BeginTextureMode(screenPlayer1);
ClearBackground(Color.SkyBlue);
BeginMode3D(CameraPlayer1);
DrawScene();
EndMode3D();
DrawText("PLAYER 1 W/S to move", 10, 10, 20, Color.Red);
EndTextureMode();
// Draw Player2 view to the render texture
BeginTextureMode(screenPlayer2);
ClearBackground(Color.SkyBlue);
BeginMode3D(CameraPlayer2);
DrawScene();
EndMode3D();
DrawText("PLAYER 2 UP/DOWN to move", 10, 10, 20, Color.Blue);
EndTextureMode();
// Draw both views render textures to the screen side by side
BeginDrawing();
ClearBackground(Color.Black);
DrawTextureRec(screenPlayer1.Texture, splitScreenRect, new Vector2(0, 0), Color.White);
DrawTextureRec(screenPlayer2.Texture, splitScreenRect, new Vector2(screenWidth / 2.0f, 0), Color.White);
EndDrawing();
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadRenderTexture(screenPlayer1);
UnloadRenderTexture(screenPlayer2);
UnloadTexture(TextureGrid);
CloseWindow();
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}
}

View file

@ -0,0 +1,227 @@
/*******************************************************************************************
*
* raylib [core] example - 2d camera split screen
*
* Example complexity rating: [★★★★] 4/4
*
* Addapted from the core_3d_camera_split_screen example:
* https://github.com/raysan5/raylib/blob/master/examples/core/core_3d_camera_split_screen.c
*
* Example originally created with raylib 4.5, last time updated with raylib 4.5
*
* Example contributed by Gabriel dos Santos Sanches (@gabrielssanches) 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) 2023-2025 Gabriel dos Santos Sanches (@gabrielssanches)
*
********************************************************************************************/
namespace Examples.Core;
public partial class SplitScreen2D : IExample
{
private const int PLAYER_SIZE = 40;
private const int screenWidth = 800;
private const int screenHeight = 440;
public string Name => "Core / 2D Camera Split Screen";
public string Title => "raylib [core] example - 2d camera split screen";
public int Width => screenWidth;
public int Height => screenHeight;
private Rectangle player1;
private Rectangle player2;
private Camera2D camera1;
private Camera2D camera2;
private RenderTexture2D screenCamera1;
private RenderTexture2D screenCamera2;
private Rectangle splitScreenRect;
public void Init()
{
player1 = new Rectangle(200, 200, PLAYER_SIZE, PLAYER_SIZE);
player2 = new Rectangle(250, 200, PLAYER_SIZE, PLAYER_SIZE);
camera1 = new Camera2D();
camera1.Target = new Vector2(player1.X, player1.Y);
camera1.Offset = new Vector2(200.0f, 200.0f);
camera1.Rotation = 0.0f;
camera1.Zoom = 1.0f;
camera2 = new Camera2D();
camera2.Target = new Vector2(player2.X, player2.Y);
camera2.Offset = new Vector2(200.0f, 200.0f);
camera2.Rotation = 0.0f;
camera2.Zoom = 1.0f;
screenCamera1 = LoadRenderTexture(screenWidth / 2, screenHeight);
screenCamera2 = LoadRenderTexture(screenWidth / 2, screenHeight);
// Build a flipped rectangle the size of the split view to use for drawing later
splitScreenRect = new Rectangle(0.0f, 0.0f, (float)screenCamera1.Texture.Width, (float)-screenCamera1.Texture.Height);
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
if (IsKeyDown(KeyboardKey.S))
{
player1.Y += 3.0f;
}
else if (IsKeyDown(KeyboardKey.W))
{
player1.Y -= 3.0f;
}
if (IsKeyDown(KeyboardKey.D))
{
player1.X += 3.0f;
}
else if (IsKeyDown(KeyboardKey.A))
{
player1.X -= 3.0f;
}
if (IsKeyDown(KeyboardKey.Up))
{
player2.Y -= 3.0f;
}
else if (IsKeyDown(KeyboardKey.Down))
{
player2.Y += 3.0f;
}
if (IsKeyDown(KeyboardKey.Right))
{
player2.X += 3.0f;
}
else if (IsKeyDown(KeyboardKey.Left))
{
player2.X -= 3.0f;
}
camera1.Target = new Vector2(player1.X, player1.Y);
camera2.Target = new Vector2(player2.X, player2.Y);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginTextureMode(screenCamera1);
ClearBackground(Color.RayWhite);
BeginMode2D(camera1);
// Draw full scene with first camera
for (int i = 0; i < screenWidth / PLAYER_SIZE + 1; i++)
{
DrawLineV(new Vector2((float)PLAYER_SIZE * i, 0), new Vector2((float)PLAYER_SIZE * i, (float)screenHeight), Color.LightGray);
}
for (int i = 0; i < screenHeight / PLAYER_SIZE + 1; i++)
{
DrawLineV(new Vector2(0, (float)PLAYER_SIZE * i), new Vector2((float)screenWidth, (float)PLAYER_SIZE * i), Color.LightGray);
}
for (int i = 0; i < screenWidth / PLAYER_SIZE; i++)
{
for (int j = 0; j < screenHeight / PLAYER_SIZE; j++)
{
DrawText($"[{i},{j}]", 10 + PLAYER_SIZE * i, 15 + PLAYER_SIZE * j, 10, Color.LightGray);
}
}
DrawRectangleRec(player1, Color.Red);
DrawRectangleRec(player2, Color.Blue);
EndMode2D();
DrawRectangle(0, 0, GetScreenWidth() / 2, 30, Fade(Color.RayWhite, 0.6f));
DrawText("PLAYER1: W/S/A/D to move", 10, 10, 10, Color.Maroon);
EndTextureMode();
BeginTextureMode(screenCamera2);
ClearBackground(Color.RayWhite);
BeginMode2D(camera2);
// Draw full scene with second camera
for (int i = 0; i < screenWidth / PLAYER_SIZE + 1; i++)
{
DrawLineV(new Vector2((float)PLAYER_SIZE * i, 0), new Vector2((float)PLAYER_SIZE * i, (float)screenHeight), Color.LightGray);
}
for (int i = 0; i < screenHeight / PLAYER_SIZE + 1; i++)
{
DrawLineV(new Vector2(0, (float)PLAYER_SIZE * i), new Vector2((float)screenWidth, (float)PLAYER_SIZE * i), Color.LightGray);
}
for (int i = 0; i < screenWidth / PLAYER_SIZE; i++)
{
for (int j = 0; j < screenHeight / PLAYER_SIZE; j++)
{
DrawText($"[{i},{j}]", 10 + PLAYER_SIZE * i, 15 + PLAYER_SIZE * j, 10, Color.LightGray);
}
}
DrawRectangleRec(player1, Color.Red);
DrawRectangleRec(player2, Color.Blue);
EndMode2D();
DrawRectangle(0, 0, GetScreenWidth() / 2, 30, Fade(Color.RayWhite, 0.6f));
DrawText("PLAYER2: UP/DOWN/LEFT/RIGHT to move", 10, 10, 10, Color.DarkBlue);
EndTextureMode();
// Draw both views render textures to the screen side by side
BeginDrawing();
ClearBackground(Color.Black);
DrawTextureRec(screenCamera1.Texture, splitScreenRect, new Vector2(0, 0), Color.White);
DrawTextureRec(screenCamera2.Texture, splitScreenRect, new Vector2(screenWidth / 2.0f, 0), Color.White);
DrawRectangle(GetScreenWidth() / 2 - 2, 0, 4, GetScreenHeight(), Color.LightGray);
EndDrawing();
}
public void Unload()
{
UnloadRenderTexture(screenCamera1); // Unload render texture
UnloadRenderTexture(screenCamera2); // Unload render texture
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [core] example - 2d camera split screen");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new SplitScreen2D();
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,91 +1,118 @@
/*******************************************************************************************
*
* raylib [core] example - Storage save/load values
* raylib [core] example - storage values
*
* 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) 2015 Ramon Santamaria (@raysan5)
* Example originally created with raylib 1.4, 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) 2015-2025 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
using static Raylib_cs.Raylib;
namespace Examples.Core;
public class StorageValues
public partial class StorageValues : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
private const string storageDataFile = "storage.data";
// NOTE: Storage positions must start with 0, directly related to file memory layout
enum StorageData
private enum StorageData
{
Score,
HiScore
}
public string Name => "Core / Storage Values";
public string Title => "raylib [core] example - storage values";
private int score = 0;
private int hiscore = 0;
private int framesCounter = 0;
public void Init()
{
score = 0;
hiscore = 0;
framesCounter = 0;
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
if (IsKeyPressed(KeyboardKey.R))
{
score = GetRandomValue(1000, 2000);
hiscore = GetRandomValue(2000, 4000);
}
if (IsKeyPressed(KeyboardKey.Enter))
{
SaveStorageValue(storageDataFile, (int)StorageData.Score, score);
SaveStorageValue(storageDataFile, (int)StorageData.HiScore, hiscore);
}
else if (IsKeyPressed(KeyboardKey.Space))
{
// NOTE: If requested position could not be found, value 0 is returned
score = LoadStorageValue(storageDataFile, (int)StorageData.Score);
hiscore = LoadStorageValue(storageDataFile, (int)StorageData.HiScore);
}
framesCounter++;
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
DrawText($"SCORE: {score}", 280, 130, 40, Color.Maroon);
DrawText($"HI-SCORE: {hiscore}", 210, 200, 50, Color.Black);
DrawText($"frames: {framesCounter}", 10, 10, 20, Color.Lime);
DrawText("Press R to generate random numbers", 220, 40, 20, Color.LightGray);
DrawText("Press ENTER to SAVE values", 250, 310, 20, Color.LightGray);
DrawText("Press SPACE to LOAD values", 252, 350, 20, Color.LightGray);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
const string storageDataFile = "storage.data";
InitWindow(screenWidth, screenHeight, "raylib [core] example - storage values");
InitWindow(screenWidth, screenHeight, "raylib [core] example - storage save/load values");
int score = 0;
int hiscore = 0;
int framesCounter = 0;
SetTargetFPS(60);
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new StorageValues();
game.Init();
// Main game loop
while (!WindowShouldClose())
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
if (IsKeyPressed(KeyboardKey.R))
{
score = GetRandomValue(1000, 2000);
hiscore = GetRandomValue(2000, 4000);
}
if (IsKeyPressed(KeyboardKey.Enter))
{
SaveStorageValue(storageDataFile, (int)StorageData.Score, score);
SaveStorageValue(storageDataFile, (int)StorageData.HiScore, hiscore);
}
else if (IsKeyPressed(KeyboardKey.Space))
{
// NOTE: If requested position could not be found, value 0 is returned
score = LoadStorageValue(storageDataFile, (int)StorageData.Score);
hiscore = LoadStorageValue(storageDataFile, (int)StorageData.HiScore);
}
framesCounter++;
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
DrawText($"SCORE: {score}", 280, 130, 40, Color.Maroon);
DrawText($"HI-SCORE: {hiscore}", 210, 200, 50, Color.Black);
DrawText($"frames: {framesCounter}", 10, 10, 20, Color.Lime);
DrawText("Press R to generate random numbers", 220, 40, 20, Color.LightGray);
DrawText("Press ENTER to SAVE values", 250, 310, 20, Color.LightGray);
DrawText("Press SPACE to LOAD values", 252, 350, 20, Color.LightGray);
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow();
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
@ -97,11 +124,11 @@ public class StorageValues
{
using var fileNameBuffer = fileName.ToUtf8Buffer();
bool success = false;
int dataSize = 0;
int newDataSize = 0;
var success = false;
var dataSize = 0;
var newDataSize = 0;
byte* fileData = LoadFileData(fileNameBuffer.AsPointer(), &dataSize);
var fileData = LoadFileData(fileNameBuffer.AsPointer(), &dataSize);
byte* newFileData = null;
if (fileData != null)
@ -115,13 +142,13 @@ public class StorageValues
if (newFileData != null)
{
// RL_REALLOC succeded
int* dataPtr = (int*)newFileData;
var dataPtr = (int*)newFileData;
dataPtr[position] = value;
}
else
{
// RL_REALLOC failed
int positionInBytes = position * sizeof(int);
var positionInBytes = position * sizeof(int);
TraceLog(
TraceLogLevel.Warning,
@$"FILEIO: [{fileName}] Failed to realloc data ({dataSize}),
@ -140,7 +167,7 @@ public class StorageValues
newDataSize = dataSize;
// Replace value on selected position
int* dataPtr = (int*)newFileData;
var dataPtr = (int*)newFileData;
dataPtr[position] = value;
}
@ -155,7 +182,7 @@ public class StorageValues
dataSize = (position + 1) * sizeof(int);
fileData = (byte*)MemAlloc((uint)dataSize);
int* dataPtr = (int*)fileData;
var dataPtr = (int*)fileData;
dataPtr[position] = value;
success = SaveFileData(fileNameBuffer.AsPointer(), fileData, dataSize);
@ -173,9 +200,9 @@ public class StorageValues
{
using var fileNameBuffer = fileName.ToUtf8Buffer();
int value = 0;
int dataSize = 0;
byte* fileData = LoadFileData(fileNameBuffer.AsPointer(), &dataSize);
var value = 0;
var dataSize = 0;
var fileData = LoadFileData(fileNameBuffer.AsPointer(), &dataSize);
if (fileData != null)
{
@ -188,7 +215,7 @@ public class StorageValues
}
else
{
int* dataPtr = (int*)fileData;
var dataPtr = (int*)fileData;
value = dataPtr[position];
}

View file

@ -0,0 +1,212 @@
/*******************************************************************************************
*
* raylib [core] example - text file loading
*
* Example complexity rating: [★☆☆☆] 1/4
*
* Example originally created with raylib 5.5, last time updated with raylib 5.6
*
* Example contributed by Aanjishnu Bhattacharyya (@NimComPoo-04) 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) 0 Aanjishnu Bhattacharyya (@NimComPoo-04)
*
********************************************************************************************/
using static Raylib_cs.Raymath;
namespace Examples.Core;
// NOTE: The upstream C code mutates the raw char buffers returned by LoadTextLines() in place,
// temporarily null-terminating to reuse MeasureText for word wrapping. This C# port keeps the
// same algorithm but works on managed char arrays: LoadFileText + split on '\n' reproduces
// raylib's LoadTextLines('\n') behaviour, and '\n' characters are inserted where lines wrap.
public partial class TextFileLoading : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Core / Text File Loading";
public string Title => "raylib [core] example - text file loading";
private Camera2D cam;
private string fileName;
private string[] lines;
private int lineCount;
private int fontSize;
private int textTop;
private int wrapWidth;
private int textHeight;
private Rectangle scrollBar;
public void Init()
{
// Setting up the camera
cam = new Camera2D();
cam.Offset = new Vector2(0, 0);
cam.Target = new Vector2(0, 0);
cam.Rotation = 0;
cam.Zoom = 1;
// Loading text file from resources/text_file.txt
fileName = "resources/text_file.txt";
string text = LoadFileText(fileName);
// Loading all the text lines (raylib's LoadTextLines splits on '\n')
lines = text.Split('\n');
lineCount = lines.Length;
// Stylistic choises
fontSize = 20;
textTop = 25 + fontSize; // Top of the screen from where the text is rendered
wrapWidth = screenWidth - 20;
// Wrap the lines as needed
for (int i = 0; i < lineCount; i++)
{
char[] chars = lines[i].ToCharArray();
int len = chars.Length;
int j = 0;
int lastSpace = 0; // Keeping track of last valid space to insert '\n'
int lastWrapStart = 0; // Keeping track of the start of this wrapped line.
while (j <= len)
{
char cur = (j < len) ? chars[j] : '\0';
if (cur == ' ' || cur == '\0')
{
// Making a C style string by "cutting" at the required location so that we can use MeasureText
string sub = new string(chars, lastWrapStart, j - lastWrapStart);
// Checking if the text has crossed the wrapWidth, then going back and inserting a newline
if (MeasureText(sub, fontSize) > wrapWidth)
{
chars[lastSpace] = '\n';
// Since we added a newline the place of wrap changed so we update our lastWrapStart
lastWrapStart = lastSpace + 1;
}
lastSpace = j; // Since we encountered a new space we update our last encountered space location
}
j++;
}
lines[i] = new string(chars);
}
// Calculating the total height so that we can show a scrollbar
textHeight = 0;
for (int i = 0; i < lineCount; i++)
{
Vector2 size = MeasureTextEx(GetFontDefault(), lines[i], (float)fontSize, 2);
textHeight += (int)size.Y + 10;
}
// A simple scrollbar on the side to show how far we have read into the file
scrollBar = new Rectangle(
(float)screenWidth - 5,
0,
5,
screenHeight * 100.0f / (textHeight - screenHeight)); // Scrollbar height is just a percentage
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
float scroll = GetMouseWheelMove();
cam.Target.Y -= scroll * fontSize * 1.5f; // Choosing an arbitrary speed for scroll
if (cam.Target.Y < 0)
{
cam.Target.Y = 0; // Snapping to 0 if we go too far back
}
// Ensuring that the camera does not scroll past all text
if (cam.Target.Y > textHeight - screenHeight + textTop)
{
cam.Target.Y = (float)textHeight - screenHeight + textTop;
}
// Computing the position of the scrollBar depending on the percentage of text covered
scrollBar.Y = Lerp((float)textTop, (float)screenHeight - scrollBar.Height, (float)(cam.Target.Y - textTop) / (textHeight - screenHeight));
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode2D(cam);
// Going through all the read lines
for (int i = 0, t = textTop; i < lineCount; i++)
{
// Each time we go through and calculate the height of the text to move the cursor appropriately
Vector2 size;
if (lines[i] != "")
{
size = MeasureTextEx(GetFontDefault(), lines[i], (float)fontSize, 2);
}
else
{
// Fix for empty line in the text file
size = MeasureTextEx(GetFontDefault(), " ", (float)fontSize, 2);
}
DrawText(lines[i], 10, t, fontSize, Color.Red);
// Inserting extra space for real newlines,
// wrapped lines are rendered closer together
t += (int)size.Y + 10;
}
EndMode2D();
// Header displaying which file is being read currently
DrawRectangle(0, 0, screenWidth, textTop - 10, Color.Beige);
DrawText($"File: {fileName}", 10, 10, fontSize, Color.Maroon);
DrawRectangleRec(scrollBar, Color.Maroon);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [core] example - text file loading");
var game = new TextFileLoading();
game.Init();
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
// 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;
}
}

389
Examples/Core/UndoRedo.cs Normal file
View file

@ -0,0 +1,389 @@
/*******************************************************************************************
*
* raylib [core] example - undo redo
*
* Example complexity rating: [★★★☆] 3/4
*
* Example originally created with raylib 5.5, last time updated with raylib 5.6
*
* Example contributed 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 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
namespace Examples.Core;
public partial class UndoRedo : IExample
{
private const int MAX_UNDO_STATES = 26; // Maximum undo states supported for the ring buffer
private const int GRID_CELL_SIZE = 24;
private const int MAX_GRID_CELLS_X = 30;
private const int MAX_GRID_CELLS_Y = 13;
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Core / Undo Redo";
public string Title => "raylib [core] example - undo redo";
// Point struct, like Vector2 but using int
private struct Point
{
public int X;
public int Y;
}
// Player state struct
// NOTE: Contains all player data that needs to be affected by undo/redo
private struct PlayerState
{
public Point Cell;
public Color Color;
}
// Undo/redo system variables
private int currentUndoIndex;
private int firstUndoIndex;
private int lastUndoIndex;
private int undoFrameCounter;
private Vector2 undoInfoPos;
private PlayerState player;
private PlayerState[] states;
// Grid variables
private Vector2 gridPosition;
// Compare two player states (replaces memcmp)
private static bool SameState(in PlayerState a, in PlayerState b)
{
return (a.Cell.X == b.Cell.X) && (a.Cell.Y == b.Cell.Y) &&
(a.Color.R == b.Color.R) && (a.Color.G == b.Color.G) &&
(a.Color.B == b.Color.B) && (a.Color.A == b.Color.A);
}
public void Init()
{
currentUndoIndex = 0;
firstUndoIndex = 0;
lastUndoIndex = 0;
undoFrameCounter = 0;
undoInfoPos = new Vector2(110, 400);
// Init current player state and undo/redo recorded states array
player = new PlayerState();
player.Cell = new Point { X = 10, Y = 10 };
player.Color = Color.Red;
// Init undo buffer to store MAX_UNDO_STATES states
states = new PlayerState[MAX_UNDO_STATES];
// Init all undo states to current state
for (int i = 0; i < MAX_UNDO_STATES; i++)
{
states[i] = player;
}
// Grid variables
gridPosition = new Vector2(40, 60);
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
// Player movement logic
if (IsKeyPressed(KeyboardKey.Right))
{
player.Cell.X++;
}
else if (IsKeyPressed(KeyboardKey.Left))
{
player.Cell.X--;
}
else if (IsKeyPressed(KeyboardKey.Up))
{
player.Cell.Y--;
}
else if (IsKeyPressed(KeyboardKey.Down))
{
player.Cell.Y++;
}
// Make sure player does not go out of bounds
if (player.Cell.X < 0)
{
player.Cell.X = 0;
}
else if (player.Cell.X >= MAX_GRID_CELLS_X)
{
player.Cell.X = MAX_GRID_CELLS_X - 1;
}
if (player.Cell.Y < 0)
{
player.Cell.Y = 0;
}
else if (player.Cell.Y >= MAX_GRID_CELLS_Y)
{
player.Cell.Y = MAX_GRID_CELLS_Y - 1;
}
// Player color change logic
if (IsKeyPressed(KeyboardKey.Space))
{
player.Color.R = (byte)GetRandomValue(20, 255);
player.Color.G = (byte)GetRandomValue(20, 220);
player.Color.B = (byte)GetRandomValue(20, 240);
}
// Undo state change logic
undoFrameCounter++;
// Waiting a number of frames before checking if we should store a new state snapshot
if (undoFrameCounter >= 2) // Checking every 2 frames
{
if (!SameState(states[currentUndoIndex], player))
{
// Move cursor to next available position of the undo ring buffer to record state
currentUndoIndex++;
if (currentUndoIndex >= MAX_UNDO_STATES)
{
currentUndoIndex = 0;
}
if (currentUndoIndex == firstUndoIndex)
{
firstUndoIndex++;
}
if (firstUndoIndex >= MAX_UNDO_STATES)
{
firstUndoIndex = 0;
}
states[currentUndoIndex] = player;
lastUndoIndex = currentUndoIndex;
}
undoFrameCounter = 0;
}
// Recover previous state from buffer: CTRL+Z
if (IsKeyDown(KeyboardKey.LeftControl) && IsKeyPressed(KeyboardKey.Z))
{
if (currentUndoIndex != firstUndoIndex)
{
currentUndoIndex--;
if (currentUndoIndex < 0)
{
currentUndoIndex = MAX_UNDO_STATES - 1;
}
if (!SameState(states[currentUndoIndex], player))
{
player = states[currentUndoIndex];
}
}
}
// Recover next state from buffer: CTRL+Y
if (IsKeyDown(KeyboardKey.LeftControl) && IsKeyPressed(KeyboardKey.Y))
{
if (currentUndoIndex != lastUndoIndex)
{
int nextUndoIndex = currentUndoIndex + 1;
if (nextUndoIndex >= MAX_UNDO_STATES)
{
nextUndoIndex = 0;
}
if (nextUndoIndex != firstUndoIndex)
{
currentUndoIndex = nextUndoIndex;
if (!SameState(states[currentUndoIndex], player))
{
player = states[currentUndoIndex];
}
}
}
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
// Draw controls info
DrawText("[ARROWS] MOVE PLAYER - [SPACE] CHANGE PLAYER COLOR", 40, 20, 20, Color.DarkGray);
// Draw player visited cells recorded by undo
// NOTE: Remember we are using a ring buffer approach so,
// some cells info could start at the end of the array and end at the beginning
if (lastUndoIndex > firstUndoIndex)
{
for (int i = firstUndoIndex; i < currentUndoIndex; i++)
{
DrawRectangleRec(new Rectangle(gridPosition.X + states[i].Cell.X * GRID_CELL_SIZE, gridPosition.Y + states[i].Cell.Y * GRID_CELL_SIZE,
GRID_CELL_SIZE, GRID_CELL_SIZE), Color.LightGray);
}
}
else if (firstUndoIndex > lastUndoIndex)
{
if ((currentUndoIndex < MAX_UNDO_STATES) && (currentUndoIndex > lastUndoIndex))
{
for (int i = firstUndoIndex; i < currentUndoIndex; i++)
{
DrawRectangleRec(new Rectangle(gridPosition.X + states[i].Cell.X * GRID_CELL_SIZE, gridPosition.Y + states[i].Cell.Y * GRID_CELL_SIZE,
GRID_CELL_SIZE, GRID_CELL_SIZE), Color.LightGray);
}
}
else
{
for (int i = firstUndoIndex; i < MAX_UNDO_STATES; i++)
{
DrawRectangle((int)gridPosition.X + states[i].Cell.X * GRID_CELL_SIZE, (int)gridPosition.Y + states[i].Cell.Y * GRID_CELL_SIZE,
GRID_CELL_SIZE, GRID_CELL_SIZE, Color.LightGray);
}
for (int i = 0; i < currentUndoIndex; i++)
{
DrawRectangle((int)gridPosition.X + states[i].Cell.X * GRID_CELL_SIZE, (int)gridPosition.Y + states[i].Cell.Y * GRID_CELL_SIZE,
GRID_CELL_SIZE, GRID_CELL_SIZE, Color.LightGray);
}
}
}
// Draw game grid
for (int y = 0; y <= MAX_GRID_CELLS_Y; y++)
{
DrawLine((int)gridPosition.X, (int)gridPosition.Y + y * GRID_CELL_SIZE,
(int)gridPosition.X + MAX_GRID_CELLS_X * GRID_CELL_SIZE, (int)gridPosition.Y + y * GRID_CELL_SIZE, Color.Gray);
}
for (int x = 0; x <= MAX_GRID_CELLS_X; x++)
{
DrawLine((int)gridPosition.X + x * GRID_CELL_SIZE, (int)gridPosition.Y,
(int)gridPosition.X + x * GRID_CELL_SIZE, (int)gridPosition.Y + MAX_GRID_CELLS_Y * GRID_CELL_SIZE, Color.Gray);
}
// Draw player
DrawRectangle((int)gridPosition.X + player.Cell.X * GRID_CELL_SIZE, (int)gridPosition.Y + player.Cell.Y * GRID_CELL_SIZE,
GRID_CELL_SIZE + 1, GRID_CELL_SIZE + 1, player.Color);
// Draw undo system buffer info
DrawText("UNDO STATES:", (int)undoInfoPos.X - 85, (int)undoInfoPos.Y + 9, 10, Color.DarkGray);
DrawUndoBuffer(undoInfoPos, firstUndoIndex, lastUndoIndex, currentUndoIndex, 24);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
}
// Draw undo system visualization logic
// NOTE: Visualizing the ring buffer array, every square can store a player state
private static void DrawUndoBuffer(Vector2 position, int firstUndoIndex, int lastUndoIndex, int currentUndoIndex, int slotSize)
{
// Draw index marks
DrawRectangle((int)position.X + 8 + slotSize * currentUndoIndex, (int)position.Y - 10, 8, 8, Color.Red);
DrawRectangleLines((int)position.X + 2 + slotSize * firstUndoIndex, (int)position.Y + 27, 8, 8, Color.Black);
DrawRectangle((int)position.X + 14 + slotSize * lastUndoIndex, (int)position.Y + 27, 8, 8, Color.Black);
// Draw background gray slots
for (int i = 0; i < MAX_UNDO_STATES; i++)
{
DrawRectangle((int)position.X + slotSize * i, (int)position.Y, slotSize, slotSize, Color.LightGray);
DrawRectangleLines((int)position.X + slotSize * i, (int)position.Y, slotSize, slotSize, Color.Gray);
}
// Draw occupied slots: firstUndoIndex --> lastUndoIndex
if (firstUndoIndex <= lastUndoIndex)
{
for (int i = firstUndoIndex; i < lastUndoIndex + 1; i++)
{
DrawRectangle((int)position.X + slotSize * i, (int)position.Y, slotSize, slotSize, Color.SkyBlue);
DrawRectangleLines((int)position.X + slotSize * i, (int)position.Y, slotSize, slotSize, Color.Blue);
}
}
else if (lastUndoIndex < firstUndoIndex)
{
for (int i = firstUndoIndex; i < MAX_UNDO_STATES; i++)
{
DrawRectangle((int)position.X + slotSize * i, (int)position.Y, slotSize, slotSize, Color.SkyBlue);
DrawRectangleLines((int)position.X + slotSize * i, (int)position.Y, slotSize, slotSize, Color.Blue);
}
for (int i = 0; i < lastUndoIndex + 1; i++)
{
DrawRectangle((int)position.X + slotSize * i, (int)position.Y, slotSize, slotSize, Color.SkyBlue);
DrawRectangleLines((int)position.X + slotSize * i, (int)position.Y, slotSize, slotSize, Color.Blue);
}
}
// Draw occupied slots: firstUndoIndex --> currentUndoIndex
if (firstUndoIndex < currentUndoIndex)
{
for (int i = firstUndoIndex; i < currentUndoIndex; i++)
{
DrawRectangle((int)position.X + slotSize * i, (int)position.Y, slotSize, slotSize, Color.Green);
DrawRectangleLines((int)position.X + slotSize * i, (int)position.Y, slotSize, slotSize, Color.Lime);
}
}
else if (currentUndoIndex < firstUndoIndex)
{
for (int i = firstUndoIndex; i < MAX_UNDO_STATES; i++)
{
DrawRectangle((int)position.X + slotSize * i, (int)position.Y, slotSize, slotSize, Color.Green);
DrawRectangleLines((int)position.X + slotSize * i, (int)position.Y, slotSize, slotSize, Color.Lime);
}
for (int i = 0; i < currentUndoIndex; i++)
{
DrawRectangle((int)position.X + slotSize * i, (int)position.Y, slotSize, slotSize, Color.Green);
DrawRectangleLines((int)position.X + slotSize * i, (int)position.Y, slotSize, slotSize, Color.Lime);
}
}
// Draw current selected UNDO slot
DrawRectangle((int)position.X + slotSize * currentUndoIndex, (int)position.Y, slotSize, slotSize, Color.Gold);
DrawRectangleLines((int)position.X + slotSize * currentUndoIndex, (int)position.Y, slotSize, slotSize, Color.Orange);
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [core] example - undo redo");
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
var game = new UndoRedo();
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,380 @@
/*******************************************************************************************
*
* raylib [core] example - viewport scaling
*
* Example complexity rating: [★★☆☆] 2/4
*
* Example originally created with raylib 5.5, last time updated with raylib 5.5
*
* Example contributed by Agnis Aldiņš (@nezvers) 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 Agnis Aldiņš (@nezvers)
*
********************************************************************************************/
namespace Examples.Core;
public partial class ViewportScaling : IExample
{
private const int ResolutionCount = 4; // For iteration purposes and teaching example
private enum ViewportType
{
// Only upscale, useful for pixel art
KeepAspectInteger,
KeepHeightInteger,
KeepWidthInteger,
// Can also downscale
KeepAspect,
KeepHeight,
KeepWidth,
// For itteration purposes and as a teaching example
ViewportTypeCount,
}
// For displaying on GUI
private static readonly string[] ViewportTypeNames = new string[]
{
"KEEP_ASPECT_INTEGER",
"KEEP_HEIGHT_INTEGER",
"KEEP_WIDTH_INTEGER",
"KEEP_ASPECT",
"KEEP_HEIGHT",
"KEEP_WIDTH",
};
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Core / Viewport Scaling";
public string Title => "raylib [core] example - viewport scaling";
public ConfigFlags ConfigFlags => ConfigFlags.ResizableWindow;
// Mutable window size (tracked from GetScreenWidth/GetScreenHeight)
private int curScreenWidth;
private int curScreenHeight;
private Vector2[] resolutionList;
private int resolutionIndex;
private int gameWidth;
private int gameHeight;
private RenderTexture2D target;
private Rectangle sourceRect;
private Rectangle destRect;
private ViewportType viewportType;
// Button rectangles
private Rectangle decreaseResolutionButton;
private Rectangle increaseResolutionButton;
private Rectangle decreaseTypeButton;
private Rectangle increaseTypeButton;
public void Init()
{
curScreenWidth = screenWidth;
curScreenHeight = screenHeight;
// Preset resolutions that could be created by subdividing screen resolution
resolutionList = new Vector2[]
{
new Vector2(64, 64),
new Vector2(256, 240),
new Vector2(320, 180),
// 4K doesn't work with integer scaling but included for example purposes with non-integer scaling
new Vector2(3840, 2160),
};
resolutionIndex = 0;
gameWidth = 64;
gameHeight = 64;
target = default;
sourceRect = default;
destRect = default;
viewportType = ViewportType.KeepAspectInteger;
ResizeRenderSize(viewportType, ref curScreenWidth, ref curScreenHeight, gameWidth, gameHeight, ref sourceRect, ref destRect, ref target);
// Button rectangles
decreaseResolutionButton = new Rectangle(200, 30, 10, 10);
increaseResolutionButton = new Rectangle(215, 30, 10, 10);
decreaseTypeButton = new Rectangle(200, 45, 10, 10);
increaseTypeButton = new Rectangle(215, 45, 10, 10);
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
if (IsWindowResized())
{
ResizeRenderSize(viewportType, ref curScreenWidth, ref curScreenHeight, gameWidth, gameHeight, ref sourceRect, ref destRect, ref target);
}
Vector2 mousePosition = GetMousePosition();
bool mousePressed = IsMouseButtonPressed(MouseButton.Left);
// Check buttons and rescale
if (CheckCollisionPointRec(mousePosition, decreaseResolutionButton) && mousePressed)
{
resolutionIndex = (resolutionIndex + ResolutionCount - 1) % ResolutionCount;
gameWidth = (int)resolutionList[resolutionIndex].X;
gameHeight = (int)resolutionList[resolutionIndex].Y;
ResizeRenderSize(viewportType, ref curScreenWidth, ref curScreenHeight, gameWidth, gameHeight, ref sourceRect, ref destRect, ref target);
}
if (CheckCollisionPointRec(mousePosition, increaseResolutionButton) && mousePressed)
{
resolutionIndex = (resolutionIndex + 1) % ResolutionCount;
gameWidth = (int)resolutionList[resolutionIndex].X;
gameHeight = (int)resolutionList[resolutionIndex].Y;
ResizeRenderSize(viewportType, ref curScreenWidth, ref curScreenHeight, gameWidth, gameHeight, ref sourceRect, ref destRect, ref target);
}
if (CheckCollisionPointRec(mousePosition, decreaseTypeButton) && mousePressed)
{
viewportType = (ViewportType)(((int)viewportType + (int)ViewportType.ViewportTypeCount - 1) % (int)ViewportType.ViewportTypeCount);
ResizeRenderSize(viewportType, ref curScreenWidth, ref curScreenHeight, gameWidth, gameHeight, ref sourceRect, ref destRect, ref target);
}
if (CheckCollisionPointRec(mousePosition, increaseTypeButton) && mousePressed)
{
viewportType = (ViewportType)(((int)viewportType + 1) % (int)ViewportType.ViewportTypeCount);
ResizeRenderSize(viewportType, ref curScreenWidth, ref curScreenHeight, gameWidth, gameHeight, ref sourceRect, ref destRect, ref target);
}
Vector2 textureMousePosition = Screen2RenderTexturePosition(mousePosition, sourceRect, destRect);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
// Draw our scene to the render texture
BeginTextureMode(target);
ClearBackground(Color.White);
DrawCircleV(textureMousePosition, 20.0f, Color.Lime);
EndTextureMode();
// Draw render texture to main framebuffer
BeginDrawing();
ClearBackground(Color.Black);
// Draw our render texture with rotation applied
DrawTexturePro(target.Texture, sourceRect, destRect, new Vector2(0.0f, 0.0f), 0.0f, Color.White);
// Draw Native resolution (GUI or anything)
// Draw info box
Rectangle infoRect = new Rectangle(5, 5, 330, 105);
DrawRectangleRec(infoRect, Fade(Color.LightGray, 0.7f));
DrawRectangleLinesEx(infoRect, 1, Color.Blue);
DrawText($"Window Resolution: {curScreenWidth} x {curScreenHeight}", 15, 15, 10, Color.Black);
DrawText($"Game Resolution: {gameWidth} x {gameHeight}", 15, 30, 10, Color.Black);
DrawText($"Type: {ViewportTypeNames[(int)viewportType]}", 15, 45, 10, Color.Black);
Vector2 scaleRatio = new Vector2(destRect.Width / sourceRect.Width, -destRect.Height / sourceRect.Height);
if (scaleRatio.X < 0.001f || scaleRatio.Y < 0.001f)
{
DrawText("Scale ratio: INVALID", 15, 60, 10, Color.Black);
}
else
{
DrawText($"Scale ratio: {scaleRatio.X:F2} x {scaleRatio.Y:F2}", 15, 60, 10, Color.Black);
}
DrawText($"Source size: {sourceRect.Width:F2} x {-sourceRect.Height:F2}", 15, 75, 10, Color.Black);
DrawText($"Destination size: {destRect.Width:F2} x {destRect.Height:F2}", 15, 90, 10, Color.Black);
// Draw buttons
DrawRectangleRec(decreaseTypeButton, Color.SkyBlue);
DrawRectangleRec(increaseTypeButton, Color.SkyBlue);
DrawRectangleRec(decreaseResolutionButton, Color.SkyBlue);
DrawRectangleRec(increaseResolutionButton, Color.SkyBlue);
DrawText("<", (int)decreaseTypeButton.X + 3, (int)decreaseTypeButton.Y + 1, 10, Color.Black);
DrawText(">", (int)increaseTypeButton.X + 3, (int)increaseTypeButton.Y + 1, 10, Color.Black);
DrawText("<", (int)decreaseResolutionButton.X + 3, (int)decreaseResolutionButton.Y + 1, 10, Color.Black);
DrawText(">", (int)increaseResolutionButton.X + 3, (int)increaseResolutionButton.Y + 1, 10, Color.Black);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadRenderTexture(target);
}
//--------------------------------------------------------------------------------------
// Module Functions Definition
//--------------------------------------------------------------------------------------
private static void KeepAspectCenteredInteger(int screenWidth, int screenHeight, int gameWidth, int gameHeight, ref Rectangle sourceRect, ref Rectangle destRect)
{
sourceRect.X = 0.0f;
sourceRect.Y = (float)gameHeight;
sourceRect.Width = (float)gameWidth;
sourceRect.Height = (float)-gameHeight;
int ratioX = (screenWidth / gameWidth);
int ratioY = (screenHeight / gameHeight);
float resizeRatio = (float)((ratioX < ratioY) ? ratioX : ratioY);
destRect.X = (float)(int)((screenWidth - (gameWidth * resizeRatio)) * 0.5f);
destRect.Y = (float)(int)((screenHeight - (gameHeight * resizeRatio)) * 0.5f);
destRect.Width = (float)(int)(gameWidth * resizeRatio);
destRect.Height = (float)(int)(gameHeight * resizeRatio);
}
private static void KeepHeightCenteredInteger(int screenWidth, int screenHeight, int gameWidth, int gameHeight, ref Rectangle sourceRect, ref Rectangle destRect)
{
float resizeRatio = (float)screenHeight / gameHeight;
sourceRect.X = 0.0f;
sourceRect.Y = 0.0f;
sourceRect.Width = (float)(int)(screenWidth / resizeRatio);
sourceRect.Height = (float)-gameHeight;
destRect.X = (float)(int)((screenWidth - (sourceRect.Width * resizeRatio)) * 0.5f);
destRect.Y = (float)(int)((screenHeight - (gameHeight * resizeRatio)) * 0.5f);
destRect.Width = (float)(int)(sourceRect.Width * resizeRatio);
destRect.Height = (float)(int)(gameHeight * resizeRatio);
}
private static void KeepWidthCenteredInteger(int screenWidth, int screenHeight, int gameWidth, int gameHeight, ref Rectangle sourceRect, ref Rectangle destRect)
{
float resizeRatio = (float)screenWidth / gameWidth;
sourceRect.X = 0.0f;
sourceRect.Y = 0.0f;
sourceRect.Width = (float)gameWidth;
sourceRect.Height = (float)(int)(screenHeight / resizeRatio);
destRect.X = (float)(int)((screenWidth - (gameWidth * resizeRatio)) * 0.5f);
destRect.Y = (float)(int)((screenHeight - (sourceRect.Height * resizeRatio)) * 0.5f);
destRect.Width = (float)(int)(gameWidth * resizeRatio);
destRect.Height = (float)(int)(sourceRect.Height * resizeRatio);
sourceRect.Height *= -1.0f;
}
private static void KeepAspectCentered(int screenWidth, int screenHeight, int gameWidth, int gameHeight, ref Rectangle sourceRect, ref Rectangle destRect)
{
sourceRect.X = 0.0f;
sourceRect.Y = (float)gameHeight;
sourceRect.Width = (float)gameWidth;
sourceRect.Height = (float)-gameHeight;
float ratioX = ((float)screenWidth / (float)gameWidth);
float ratioY = ((float)screenHeight / (float)gameHeight);
float resizeRatio = (ratioX < ratioY ? ratioX : ratioY);
destRect.X = (float)(int)((screenWidth - (gameWidth * resizeRatio)) * 0.5f);
destRect.Y = (float)(int)((screenHeight - (gameHeight * resizeRatio)) * 0.5f);
destRect.Width = (float)(int)(gameWidth * resizeRatio);
destRect.Height = (float)(int)(gameHeight * resizeRatio);
}
private static void KeepHeightCentered(int screenWidth, int screenHeight, int gameWidth, int gameHeight, ref Rectangle sourceRect, ref Rectangle destRect)
{
float resizeRatio = ((float)screenHeight / (float)gameHeight);
sourceRect.X = 0.0f;
sourceRect.Y = 0.0f;
sourceRect.Width = (float)(int)((float)screenWidth / resizeRatio);
sourceRect.Height = (float)-gameHeight;
destRect.X = (float)(int)((screenWidth - (sourceRect.Width * resizeRatio)) * 0.5f);
destRect.Y = (float)(int)((screenHeight - (gameHeight * resizeRatio)) * 0.5f);
destRect.Width = (float)(int)(sourceRect.Width * resizeRatio);
destRect.Height = (float)(int)(gameHeight * resizeRatio);
}
private static void KeepWidthCentered(int screenWidth, int screenHeight, int gameWidth, int gameHeight, ref Rectangle sourceRect, ref Rectangle destRect)
{
float resizeRatio = ((float)screenWidth / (float)gameWidth);
sourceRect.X = 0.0f;
sourceRect.Y = 0.0f;
sourceRect.Width = (float)gameWidth;
sourceRect.Height = (float)(int)((float)screenHeight / resizeRatio);
destRect.X = (float)(int)((screenWidth - (gameWidth * resizeRatio)) * 0.5f);
destRect.Y = (float)(int)((screenHeight - (sourceRect.Height * resizeRatio)) * 0.5f);
destRect.Width = (float)(int)(gameWidth * resizeRatio);
destRect.Height = (float)(int)(sourceRect.Height * resizeRatio);
sourceRect.Height *= -1.0f;
}
private static void ResizeRenderSize(ViewportType viewportType, ref int screenWidth, ref int screenHeight, int gameWidth, int gameHeight, ref Rectangle sourceRect, ref Rectangle destRect, ref RenderTexture2D target)
{
screenWidth = GetScreenWidth();
screenHeight = GetScreenHeight();
switch (viewportType)
{
case ViewportType.KeepAspectInteger:
KeepAspectCenteredInteger(screenWidth, screenHeight, gameWidth, gameHeight, ref sourceRect, ref destRect);
break;
case ViewportType.KeepHeightInteger:
KeepHeightCenteredInteger(screenWidth, screenHeight, gameWidth, gameHeight, ref sourceRect, ref destRect);
break;
case ViewportType.KeepWidthInteger:
KeepWidthCenteredInteger(screenWidth, screenHeight, gameWidth, gameHeight, ref sourceRect, ref destRect);
break;
case ViewportType.KeepAspect:
KeepAspectCentered(screenWidth, screenHeight, gameWidth, gameHeight, ref sourceRect, ref destRect);
break;
case ViewportType.KeepHeight:
KeepHeightCentered(screenWidth, screenHeight, gameWidth, gameHeight, ref sourceRect, ref destRect);
break;
case ViewportType.KeepWidth:
KeepWidthCentered(screenWidth, screenHeight, gameWidth, gameHeight, ref sourceRect, ref destRect);
break;
default:
break;
}
UnloadRenderTexture(target);
target = LoadRenderTexture((int)sourceRect.Width, -(int)sourceRect.Height);
}
// Example how to calculate position on RenderTexture
private static Vector2 Screen2RenderTexturePosition(Vector2 point, Rectangle textureRect, Rectangle scaledRect)
{
Vector2 relativePosition = new Vector2(point.X - scaledRect.X, point.Y - scaledRect.Y);
Vector2 ratio = new Vector2(textureRect.Width / scaledRect.Width, -textureRect.Height / scaledRect.Height);
return new Vector2(relativePosition.X * ratio.X, relativePosition.Y * ratio.X);
}
public static int Main()
{
// Initialization
//---------------------------------------------------------
SetConfigFlags(ConfigFlags.ResizableWindow);
InitWindow(screenWidth, screenHeight, "raylib [core] example - viewport scaling");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//----------------------------------------------------------
var game = new ViewportScaling();
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,65 +1,76 @@
/*******************************************************************************************
*
* raylib [core] example - VR Simulator (Oculus Rift CV1 parameters)
* raylib [core] example - vr simulator
*
* 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: [★★★☆] 3/4
*
* Copyright (c) 2017 Ramon Santamaria (@raysan5)
* Example originally created with raylib 2.5, 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) 2017-2025 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
using System;
using System.Numerics;
using static Raylib_cs.Raylib;
namespace Examples.Core;
public class VrSimulator
public partial class VrSimulator : IExample
{
public 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 => "Core / VR Simulator";
public string Title => "raylib [core] example - vr simulator";
public bool CursorDisabled => true;
private VrStereoConfig config;
private Shader distortion;
private RenderTexture2D target;
private Rectangle sourceRec;
private Rectangle destRec;
private Camera3D camera;
private Vector3 cubePosition;
public unsafe void Init()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 1080;
const int screenHeight = 600;
SetConfigFlags(ConfigFlags.Msaa4xHint);
InitWindow(screenWidth, screenHeight, "raylib [core] example - vr simulator");
// VR device parameters definition
VrDeviceInfo device = new VrDeviceInfo
var device = new VrDeviceInfo
{
// Oculus Rift CV1 parameters for simulator
HResolution = 2160,
VResolution = 1200,
HScreenSize = 0.133793f,
VScreenSize = 0.0669f,
EyeToScreenDistance = 0.041f,
LensSeparationDistance = 0.07f,
InterpupillaryDistance = 0.07f,
HResolution = 2160, // Horizontal resolution in pixels
VResolution = 1200, // Vertical resolution in pixels
HScreenSize = 0.133793f, // Horizontal size in meters
VScreenSize = 0.0669f, // Vertical size in meters
EyeToScreenDistance = 0.041f, // Distance between eye and display in meters
LensSeparationDistance = 0.07f, // Lens separation distance in meters
InterpupillaryDistance = 0.07f, // IPD (distance between pupils) in meters
};
// NOTE: CV1 uses a Fresnel-hybrid-asymmetric lenses with specific distortion compute shaders.
// Following parameters are an approximation to distortion stereo rendering but results differ from actual
// device.
unsafe
{
device.LensDistortionValues[0] = 1.0f;
device.LensDistortionValues[1] = 0.22f;
device.LensDistortionValues[2] = 0.24f;
device.LensDistortionValues[3] = 0.0f;
device.ChromaAbCorrection[0] = 0.996f;
device.ChromaAbCorrection[1] = -0.004f;
device.ChromaAbCorrection[2] = 1.014f;
device.ChromaAbCorrection[3] = 0.0f;
}
// NOTE: CV1 uses fresnel-hybrid-asymmetric lenses with specific compute shaders
// Following parameters are just an approximation to CV1 distortion stereo rendering
device.LensDistortionValues[0] = 1.0f; // Lens distortion constant parameter 0
device.LensDistortionValues[1] = 0.22f; // Lens distortion constant parameter 1
device.LensDistortionValues[2] = 0.24f; // Lens distortion constant parameter 2
device.LensDistortionValues[3] = 0.0f; // Lens distortion constant parameter 3
device.ChromaAbCorrection[0] = 0.996f; // Chromatic aberration correction parameter 0
device.ChromaAbCorrection[1] = -0.004f; // Chromatic aberration correction parameter 1
device.ChromaAbCorrection[2] = 1.014f; // Chromatic aberration correction parameter 2
device.ChromaAbCorrection[3] = 0.0f; // Chromatic aberration correction parameter 3
// Load VR stereo config for VR device parameteres (Oculus Rift CV1 parameters)
VrStereoConfig config = LoadVrStereoConfig(device);
config = LoadVrStereoConfig(device);
// Distortion shader (uses device lens distortion and chroma)
Shader distortion = LoadShader(null, "resources/distortion330.fs");
distortion = LoadShader(null, $"resources/shaders/glsl{GlslVersion}/distortion.fs");
// Update distortion shader with lens and distortion-scale parameters
Raylib.SetShaderValue(
@ -100,88 +111,104 @@ public class VrSimulator
ShaderUniformDataType.Vec2
);
unsafe
{
SetShaderValue(
distortion,
GetShaderLocation(distortion, "deviceWarpParam"),
device.LensDistortionValues,
ShaderUniformDataType.Vec4
);
SetShaderValue(
distortion,
GetShaderLocation(distortion, "chromaAbParam"),
device.ChromaAbCorrection,
ShaderUniformDataType.Vec4
);
}
SetShaderValue(
distortion,
GetShaderLocation(distortion, "deviceWarpParam"),
device.LensDistortionValues,
ShaderUniformDataType.Vec4
);
SetShaderValue(
distortion,
GetShaderLocation(distortion, "chromaAbParam"),
device.ChromaAbCorrection,
ShaderUniformDataType.Vec4
);
// Initialize framebuffer for stereo rendering
// NOTE: Screen size should match HMD aspect ratio
RenderTexture2D target = LoadRenderTexture(GetScreenWidth(), GetScreenHeight());
target = LoadRenderTexture(device.HResolution, device.VResolution);
// The target's height is flipped (in the source Rectangle), due to OpenGL reasons
sourceRec = new(0.0f, 0.0f, (float)target.Texture.Width, -(float)target.Texture.Height);
destRec = new(0.0f, 0.0f, (float)GetScreenWidth(), (float)GetScreenHeight());
// Define the camera to look into our 3d world
Camera3D camera;
camera.Position = new Vector3(5.0f, 2.0f, 5.0f);
camera.Target = new Vector3(0.0f, 2.0f, 0.0f);
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
camera.FovY = 60.0f;
camera.Projection = CameraProjection.Perspective;
camera = new();
camera.Position = new Vector3(5.0f, 2.0f, 5.0f); // Camera position
camera.Target = new Vector3(0.0f, 2.0f, 0.0f); // Camera looking at point
camera.Up = new Vector3(0.0f, 1.0f, 0.0f); // Camera up vector
camera.FovY = 60.0f; // Camera field-of-view Y
camera.Projection = CameraProjection.Perspective; // Camera projection type
Vector3 cubePosition = new(0.0f, 0.0f, 0.0f);
cubePosition = new(0.0f, 0.0f, 0.0f);
}
SetTargetFPS(90); // Set our game to run at 90 frames-per-second
public void Update()
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.FirstPerson);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginTextureMode(target);
ClearBackground(Color.RayWhite);
BeginVrStereoMode(config);
BeginMode3D(camera);
DrawCube(cubePosition, 2.0f, 2.0f, 2.0f, Color.Red);
DrawCubeWires(cubePosition, 2.0f, 2.0f, 2.0f, Color.Maroon);
DrawGrid(40, 1.0f);
EndMode3D();
EndVrStereoMode();
EndTextureMode();
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginShaderMode(distortion);
DrawTexturePro(target.Texture, sourceRec, destRec, new Vector2(0.0f, 0.0f), 0.0f, Color.White);
EndShaderMode();
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadVrStereoConfig(config); // Unload stereo config
UnloadRenderTexture(target); // Unload stereo render fbo
UnloadShader(distortion); // Unload distortion shader
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
// NOTE: screenWidth/screenHeight should match VR device aspect ratio
InitWindow(screenWidth, screenHeight, "raylib [core] example - vr simulator");
DisableCursor(); // Limit cursor to relative movement inside the window
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new VrSimulator();
game.Init();
// Main game loop
while (!WindowShouldClose())
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.FirstPerson);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginTextureMode(target);
ClearBackground(Color.RayWhite);
BeginVrStereoMode(config);
BeginMode3D(camera);
DrawCube(cubePosition, 2.0f, 2.0f, 2.0f, Color.Red);
DrawCubeWires(cubePosition, 2.0f, 2.0f, 2.0f, Color.Maroon);
DrawGrid(40, 1.0f);
EndMode3D();
EndVrStereoMode();
EndTextureMode();
BeginShaderMode(distortion);
DrawTextureRec(
target.Texture,
new Rectangle(0, 0, (float)target.Texture.Width, (float)-target.Texture.Height),
new Vector2(0.0f, 0.0f),
Color.White
);
EndShaderMode();
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadVrStereoConfig(config);
UnloadRenderTexture(target);
UnloadShader(distortion);
CloseWindow();
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;

View file

@ -2,28 +2,254 @@
*
* raylib [core] example - window flags
*
* This example has been created using raylib 3.5 (www.raylib.com)
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
* Example complexity rating: [★★★☆] 3/4
*
* Copyright (c) 2020 Ramon Santamaria (@raysan5)
* Example originally created with raylib 3.5, 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) 2020-2025 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
using System.Numerics;
using static Raylib_cs.Raylib;
using static Raylib_cs.ConfigFlags;
namespace Examples.Core;
public class WindowFlags
[ExcludeFromBrowser("runtime window-state flags don't apply to the emscripten canvas")]
public partial class WindowFlags : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Core / Window Flags";
public string Title => "raylib [core] example - window flags";
private Vector2 ballPosition;
private Vector2 ballSpeed;
private float ballRadius;
private int framesCounter = 0;
public void Init()
{
ballPosition = new(GetScreenWidth() / 2.0f, GetScreenHeight() / 2.0f);
ballSpeed = new(5.0f, 4.0f);
ballRadius = 20;
framesCounter = 0;
}
public void Update()
{
// Update
//-----------------------------------------------------
if (IsKeyPressed(KeyboardKey.F))
{
// modifies window size when scaling!
ToggleFullscreen();
}
if (IsKeyPressed(KeyboardKey.R))
{
if (IsWindowState(ResizableWindow))
{
ClearWindowState(ResizableWindow);
}
else
{
SetWindowState(ResizableWindow);
}
}
if (IsKeyPressed(KeyboardKey.D))
{
if (IsWindowState(UndecoratedWindow))
{
ClearWindowState(UndecoratedWindow);
}
else
{
SetWindowState(UndecoratedWindow);
}
}
if (IsKeyPressed(KeyboardKey.H))
{
if (!IsWindowState(HiddenWindow))
{
SetWindowState(HiddenWindow);
}
framesCounter = 0;
}
if (IsWindowState(HiddenWindow))
{
framesCounter++;
if (framesCounter >= 240)
{
// Show window after 3 seconds
ClearWindowState(HiddenWindow);
}
}
if (IsKeyPressed(KeyboardKey.N))
{
if (!IsWindowState(MinimizedWindow))
{
MinimizeWindow();
}
framesCounter = 0;
}
if (IsWindowState(MinimizedWindow))
{
framesCounter++;
if (framesCounter >= 240)
{
// Restore window after 3 seconds
RestoreWindow();
}
}
if (IsKeyPressed(KeyboardKey.M))
{
// NOTE: Requires FLAG_WINDOW_RESIZABLE enabled!
if (IsWindowState(MaximizedWindow))
{
RestoreWindow();
}
else
{
MaximizeWindow();
}
}
if (IsKeyPressed(KeyboardKey.U))
{
if (IsWindowState(UnfocusedWindow))
{
ClearWindowState(UnfocusedWindow);
}
else
{
SetWindowState(UnfocusedWindow);
}
}
if (IsKeyPressed(KeyboardKey.T))
{
if (IsWindowState(TopmostWindow))
{
ClearWindowState(TopmostWindow);
}
else
{
SetWindowState(TopmostWindow);
}
}
if (IsKeyPressed(KeyboardKey.A))
{
if (IsWindowState(AlwaysRunWindow))
{
ClearWindowState(AlwaysRunWindow);
}
else
{
SetWindowState(AlwaysRunWindow);
}
}
if (IsKeyPressed(KeyboardKey.V))
{
if (IsWindowState(VSyncHint))
{
ClearWindowState(VSyncHint);
}
else
{
SetWindowState(VSyncHint);
}
}
if (IsKeyPressed(KeyboardKey.B))
{
ToggleBorderlessWindowed();
}
// Bouncing ball logic
ballPosition.X += ballSpeed.X;
ballPosition.Y += ballSpeed.Y;
if ((ballPosition.X >= (GetScreenWidth() - ballRadius)) || (ballPosition.X <= ballRadius))
{
ballSpeed.X *= -1.0f;
}
if ((ballPosition.Y >= (GetScreenHeight() - ballRadius)) || (ballPosition.Y <= ballRadius))
{
ballSpeed.Y *= -1.0f;
}
//-----------------------------------------------------
// Draw
//-----------------------------------------------------
BeginDrawing();
if (IsWindowState(TransparentWindow))
{
ClearBackground(Color.Blank);
}
else
{
ClearBackground(Color.RayWhite);
}
DrawCircleV(ballPosition, ballRadius, Color.Maroon);
DrawRectangleLinesEx(new Rectangle(0, 0, GetScreenWidth(), GetScreenHeight()), 4, Color.RayWhite);
DrawCircleV(GetMousePosition(), 10, Color.DarkBlue);
DrawFPS(10, 10);
DrawText($"Screen Size: [{GetScreenWidth()}, {GetScreenHeight()}]", 10, 40, 10, Color.Green);
// Draw window state info
DrawText("Following flags can be set after window creation:", 10, 60, 10, Color.Gray);
DrawWindowState(FullscreenMode, "[F] FLAG_FULLSCREEN_MODE: ", 10, 80, 10);
DrawWindowState(ResizableWindow, "[R] FLAG_WINDOW_RESIZABLE: ", 10, 100, 10);
DrawWindowState(UndecoratedWindow, "[D] FLAG_WINDOW_UNDECORATED: ", 10, 120, 10);
DrawWindowState(HiddenWindow, "[H] FLAG_WINDOW_HIDDEN: ", 10, 140, 10);
DrawWindowState(MinimizedWindow, "[N] FLAG_WINDOW_MINIMIZED: ", 10, 160, 10);
DrawWindowState(MaximizedWindow, "[M] FLAG_WINDOW_MAXIMIZED: ", 10, 180, 10);
DrawWindowState(UnfocusedWindow, "[G] FLAG_WINDOW_UNFOCUSED: ", 10, 200, 10);
DrawWindowState(TopmostWindow, "[T] FLAG_WINDOW_TOPMOST: ", 10, 220, 10);
DrawWindowState(AlwaysRunWindow, "[A] FLAG_WINDOW_ALWAYS_RUN: ", 10, 240, 10);
DrawWindowState(VSyncHint, "[V] FLAG_VSYNC_HINT: ", 10, 260, 10);
DrawWindowState(BorderlessWindowMode, "[B] FLAG_BORDERLESS_WINDOWED_MODE: ", 10, 280, 10);
DrawText("Following flags can only be set before window creation:", 10, 320, 10, Color.Gray);
DrawWindowState(HighDpiWindow, "FLAG_WINDOW_HIGHDPI: ", 10, 340, 10);
DrawWindowState(TransparentWindow, "FLAG_WINDOW_TRANSPARENT: ", 10, 360, 10);
DrawWindowState(Msaa4xHint, "FLAG_MSAA_4X_HINT: ", 10, 380, 10);
EndDrawing();
//-----------------------------------------------------
}
public void Unload()
{
}
public static int Main()
{
// Initialization
//---------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
// Possible window flags
/*
FLAG_VSYNC_HINT
@ -42,235 +268,43 @@ public class WindowFlags
*/
// Set configuration flags for window creation
SetConfigFlags(VSyncHint | Msaa4xHint);
//SetConfigFlags(VSyncHint | Msaa4xHint | HighDpiWindow);// | TransparentWindow);
InitWindow(screenWidth, screenHeight, "raylib [core] example - window flags");
Vector2 ballPosition = new(GetScreenWidth() / 2, GetScreenHeight() / 2);
Vector2 ballSpeed = new(5.0f, 4.0f);
int ballRadius = 20;
int framesCounter = 0;
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//----------------------------------------------------------
var game = new WindowFlags();
game.Init();
// Main game loop
while (!WindowShouldClose())
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//-----------------------------------------------------
if (IsKeyPressed(KeyboardKey.F))
{
// modifies window size when scaling!
ToggleFullscreen();
}
if (IsKeyPressed(KeyboardKey.R))
{
if (IsWindowState(ResizableWindow))
{
ClearWindowState(ResizableWindow);
}
else
{
SetWindowState(ResizableWindow);
}
}
if (IsKeyPressed(KeyboardKey.D))
{
if (IsWindowState(UndecoratedWindow))
{
ClearWindowState(UndecoratedWindow);
}
else
{
SetWindowState(UndecoratedWindow);
}
}
if (IsKeyPressed(KeyboardKey.H))
{
if (!IsWindowState(HiddenWindow))
{
SetWindowState(HiddenWindow);
}
framesCounter = 0;
}
if (IsWindowState(HiddenWindow))
{
framesCounter++;
if (framesCounter >= 240)
{
// Show window after 3 seconds
ClearWindowState(HiddenWindow);
}
}
if (IsKeyPressed(KeyboardKey.N))
{
if (!IsWindowState(MinimizedWindow))
{
MinimizeWindow();
}
framesCounter = 0;
}
if (IsWindowState(MinimizedWindow))
{
framesCounter++;
if (framesCounter >= 240)
{
// Restore window after 3 seconds
RestoreWindow();
}
}
if (IsKeyPressed(KeyboardKey.M))
{
// NOTE: Requires FLAG_WINDOW_RESIZABLE enabled!
if (IsWindowState(MaximizedWindow))
{
RestoreWindow();
}
else
{
MaximizeWindow();
}
}
if (IsKeyPressed(KeyboardKey.U))
{
if (IsWindowState(UnfocusedWindow))
{
ClearWindowState(UnfocusedWindow);
}
else
{
SetWindowState(UnfocusedWindow);
}
}
if (IsKeyPressed(KeyboardKey.T))
{
if (IsWindowState(TopmostWindow))
{
ClearWindowState(TopmostWindow);
}
else
{
SetWindowState(TopmostWindow);
}
}
if (IsKeyPressed(KeyboardKey.A))
{
if (IsWindowState(AlwaysRunWindow))
{
ClearWindowState(AlwaysRunWindow);
}
else
{
SetWindowState(AlwaysRunWindow);
}
}
if (IsKeyPressed(KeyboardKey.V))
{
if (IsWindowState(VSyncHint))
{
ClearWindowState(VSyncHint);
}
else
{
SetWindowState(VSyncHint);
}
}
// Bouncing ball logic
ballPosition.X += ballSpeed.X;
ballPosition.Y += ballSpeed.Y;
if ((ballPosition.X >= (GetScreenWidth() - ballRadius)) || (ballPosition.X <= ballRadius))
{
ballSpeed.X *= -1.0f;
}
if ((ballPosition.Y >= (GetScreenHeight() - ballRadius)) || (ballPosition.Y <= ballRadius))
{
ballSpeed.Y *= -1.0f;
}
//-----------------------------------------------------
// Draw
//-----------------------------------------------------
BeginDrawing();
if (IsWindowState(TransparentWindow))
{
ClearBackground(Color.Blank);
}
else
{
ClearBackground(Color.RayWhite);
}
DrawCircleV(ballPosition, ballRadius, Color.Maroon);
DrawRectangleLinesEx(new Rectangle(0, 0, GetScreenWidth(), GetScreenHeight()), 4, Color.RayWhite);
DrawCircleV(GetMousePosition(), 10, Color.DarkBlue);
DrawFPS(10, 10);
DrawText($"Screen Size: [{GetScreenWidth()}, {GetScreenHeight()}]", 10, 40, 10, Color.Green);
// Draw window state info
Color on = Color.Lime;
Color off = Color.Maroon;
DrawText("Following flags can be set after window creation:", 10, 60, 10, Color.Gray);
DrawWindowState(FullscreenMode, "[F] FLAG_FULLSCREEN_MODE: ", 10, 80, 10);
DrawWindowState(ResizableWindow, "[R] FLAG_WINDOW_RESIZABLE: ", 10, 100, 10);
DrawWindowState(UndecoratedWindow, "[D] FLAG_WINDOW_UNDECORATED: ", 10, 120, 10);
DrawWindowState(HiddenWindow, "[H] FLAG_WINDOW_HIDDEN: ", 10, 140, 10);
DrawWindowState(MinimizedWindow, "[N] FLAG_WINDOW_MINIMIZED: ", 10, 160, 10);
DrawWindowState(MaximizedWindow, "[M] FLAG_WINDOW_MAXIMIZED: ", 10, 180, 10);
DrawWindowState(UnfocusedWindow, "[G] FLAG_WINDOW_UNFOCUSED: ", 10, 200, 10);
DrawWindowState(TopmostWindow, "[T] FLAG_WINDOW_TOPMOST: ", 10, 220, 10);
DrawWindowState(AlwaysRunWindow, "[A] FLAG_WINDOW_ALWAYS_RUN: ", 10, 240, 10);
DrawWindowState(VSyncHint, "[V] FLAG_VSYNC_HINT: ", 10, 260, 10);
DrawText("Following flags can only be set before window creation:", 10, 300, 10, Color.Gray);
DrawWindowState(HighDpiWindow, "[F] FLAG_WINDOW_HIGHDPI: ", 10, 320, 10);
DrawWindowState(TransparentWindow, "[F] FLAG_WINDOW_TRANSPARENT: ", 10, 340, 10);
DrawWindowState(Msaa4xHint, "[F] FLAG_MSAA_4X_HINT: ", 10, 360, 10);
EndDrawing();
//-----------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//---------------------------------------------------------
CloseWindow();
CloseWindow(); // Close window and OpenGL context
//----------------------------------------------------------
return 0;
}
static void DrawWindowState(ConfigFlags flag, string text, int posX, int posY, int fontSize)
private static void DrawWindowState(ConfigFlags flag, string text, int posX, int posY, int fontSize)
{
Color onColor = Color.Lime;
Color offColor = Color.Maroon;
var onColor = Color.Lime;
var offColor = Color.Maroon;
if (Raylib.IsWindowState(flag))
{
DrawText($"{text} on", posX, posY, fontSize, onColor);
DrawText($"{text}on", posX, posY, fontSize, onColor);
}
else
{
DrawText($"{text} off", posX, posY, fontSize, offColor);
DrawText($"{text}off", posX, posY, fontSize, offColor);
}
}
}

View file

@ -1,139 +1,169 @@
/*******************************************************************************************
*
* raylib [core] example - window scale letterbox
* raylib [core] example - window letterbox
*
* 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: [★★☆☆] 2/4
*
* Example originally created with raylib 2.5, last time updated with raylib 4.0
*
* Example contributed by Anata (@anatagawa) and reviewed by Ramon Santamaria (@raysan5)
*
* Copyright (c) 2019 Anata (@anatagawa) 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 Anata (@anatagawa) and Ramon Santamaria (@raysan5)
*
********************************************************************************************/
using System;
using System.Numerics;
using static Raylib_cs.Raylib;
namespace Examples.Core;
public class WindowLetterbox
public partial class WindowLetterbox : IExample
{
public static int Main()
private const int windowWidth = 800;
private const int windowHeight = 450;
private const int gamescreenWidth = 640;
private const int gamescreenHeight = 480;
public string Name => "Core / Window Letterbox";
public string Title => "raylib [core] example - window letterbox";
public ConfigFlags ConfigFlags => ConfigFlags.ResizableWindow | ConfigFlags.VSyncHint;
private RenderTexture2D target;
private Color[] colors;
public void Init()
{
const int windowWidth = 800;
const int windowHeight = 450;
// Enable config flags for resizable window and vertical synchro
SetConfigFlags(ConfigFlags.ResizableWindow | ConfigFlags.VSyncHint);
InitWindow(windowWidth, windowHeight, "raylib [core] example - window scale letterbox");
SetWindowMinSize(320, 240);
int gameScreenWidth = 640;
int gameScreenHeight = 480;
// Render texture initialization, used to hold the rendering result so we can easily resize it
RenderTexture2D target = LoadRenderTexture(gameScreenWidth, gameScreenHeight);
SetTextureFilter(target.Texture, TextureFilter.Bilinear);
target = LoadRenderTexture(gamescreenWidth, gamescreenHeight);
SetTextureFilter(target.Texture, TextureFilter.Bilinear); // Texture scale filter to use
Color[] colors = new Color[10];
for (int i = 0; i < 10; i++)
colors = new Color[10];
for (var i = 0; i < 10; i++)
{
colors[i] = new Color(GetRandomValue(100, 250), GetRandomValue(50, 150), GetRandomValue(10, 100), 255);
}
}
SetTargetFPS(60);
public void Update()
{
// Update
//----------------------------------------------------------------------------------
// Compute required framebuffer scaling
var scale = MathF.Min(
(float)GetScreenWidth() / gamescreenWidth,
(float)GetScreenHeight() / gamescreenHeight
);
if (IsKeyPressed(KeyboardKey.Space))
{
// Recalculate random colors for the bars
for (var i = 0; i < 10; i++)
{
colors[i] = new Color(
GetRandomValue(100, 250),
GetRandomValue(50, 150),
GetRandomValue(10, 100),
255
);
}
}
// Update virtual mouse (clamped mouse value behind game screen)
var mouse = GetMousePosition();
var virtualMouse = Vector2.Zero;
virtualMouse.X = (mouse.X - (GetScreenWidth() - (gamescreenWidth * scale)) * 0.5f) / scale;
virtualMouse.Y = (mouse.Y - (GetScreenHeight() - (gamescreenHeight * scale)) * 0.5f) / scale;
Vector2 max = new((float)gamescreenWidth, (float)gamescreenHeight);
virtualMouse = Vector2.Clamp(virtualMouse, Vector2.Zero, max);
// Apply the same transformation as the virtual mouse to the real mouse (i.e. to work with raygui)
//SetMouseOffset(-(GetScreenWidth() - (gamescreenWidth*scale))*0.5f, -(GetScreenHeight() - (gamescreenHeight*scale))*0.5f);
//SetMouseScale(1/scale, 1/scale);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
// Draw everything in the render texture, note this will not be rendered on screen, yet
BeginTextureMode(target);
ClearBackground(Color.RayWhite); // Clear render texture background color
for (var i = 0; i < 10; i++)
{
DrawRectangle(0, (gamescreenHeight / 10) * i, gamescreenWidth, gamescreenHeight / 10, colors[i]);
}
DrawText(
"If executed inside a window,\nyou can resize the window,\nand see the screen scaling!",
10,
25,
20,
Color.White
);
DrawText($"Default Mouse: [{(int)mouse.X} , {(int)mouse.Y}]", 350, 25, 20, Color.Green);
DrawText($"Virtual Mouse: [{(int)virtualMouse.X} , {(int)virtualMouse.Y}]", 350, 55, 20, Color.Yellow);
EndTextureMode();
BeginDrawing();
ClearBackground(Color.Black); // Clear screen background
// Draw render texture to screen, properly scaled
Rectangle sourceRec = new(
0.0f,
0.0f,
(float)target.Texture.Width,
(float)-target.Texture.Height
);
Rectangle destRec = new(
(GetScreenWidth() - ((float)gamescreenWidth * scale)) * 0.5f,
(GetScreenHeight() - ((float)gamescreenHeight * scale)) * 0.5f,
(float)gamescreenWidth * scale,
(float)gamescreenHeight * scale
);
DrawTexturePro(target.Texture, sourceRec, destRec, new Vector2(0, 0), 0.0f, Color.White);
EndDrawing();
//--------------------------------------------------------------------------------------
}
public void Unload()
{
UnloadRenderTexture(target); // Unload render texture
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
// Enable config flags for resizable window and vertical synchro
SetConfigFlags(ConfigFlags.ResizableWindow | ConfigFlags.VSyncHint);
InitWindow(windowWidth, windowHeight, "raylib [core] example - window letterbox");
SetWindowMinSize(320, 240);
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new WindowLetterbox();
game.Init();
// Main game loop
while (!WindowShouldClose())
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
// Compute required framebuffer scaling
float scale = MathF.Min(
(float)GetScreenWidth() / gameScreenWidth,
(float)GetScreenHeight() / gameScreenHeight
);
if (IsKeyPressed(KeyboardKey.Space))
{
// Recalculate random colors for the bars
for (int i = 0; i < 10; i++)
{
colors[i] = new Color(
GetRandomValue(100, 250),
GetRandomValue(50, 150),
GetRandomValue(10, 100),
255
);
}
}
// Update virtual mouse (clamped mouse value behind game screen)
Vector2 mouse = GetMousePosition();
Vector2 virtualMouse = Vector2.Zero;
virtualMouse.X = (mouse.X - (GetScreenWidth() - (gameScreenWidth * scale)) * 0.5f) / scale;
virtualMouse.Y = (mouse.Y - (GetScreenHeight() - (gameScreenHeight * scale)) * 0.5f) / scale;
Vector2 max = new((float)gameScreenWidth, (float)gameScreenHeight);
virtualMouse = Vector2.Clamp(virtualMouse, Vector2.Zero, max);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.Black);
// Draw everything in the render texture, note this will not be rendered on screen, yet
BeginTextureMode(target);
ClearBackground(Color.RayWhite);
for (int i = 0; i < 10; i++)
{
DrawRectangle(0, (gameScreenHeight / 10) * i, gameScreenWidth, gameScreenHeight / 10, colors[i]);
}
DrawText(
"If executed inside a window,\nyou can resize the window,\nand see the screen scaling!",
10,
25,
20,
Color.White
);
DrawText($"Default Mouse: [{(int)mouse.X} {(int)mouse.Y}]", 350, 25, 20, Color.Green);
DrawText($"Virtual Mouse: [{(int)virtualMouse.X}, {(int)virtualMouse.Y}]", 350, 55, 20, Color.Yellow);
EndTextureMode();
// Draw RenderTexture2D to window, properly scaled
Rectangle sourceRec = new(
0.0f,
0.0f,
(float)target.Texture.Width,
(float)-target.Texture.Height
);
Rectangle destRec = new(
(GetScreenWidth() - ((float)gameScreenWidth * scale)) * 0.5f,
(GetScreenHeight() - ((float)gameScreenHeight * scale)) * 0.5f,
(float)gameScreenWidth * scale,
(float)gameScreenHeight * scale
);
DrawTexturePro(target.Texture, sourceRec, destRec, new Vector2(0, 0), 0.0f, Color.White);
EndDrawing();
//--------------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadRenderTexture(target);
CloseWindow();
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}
}

View file

@ -0,0 +1,126 @@
/*******************************************************************************************
*
* raylib [core] example - window should close
*
* Example complexity rating: [★☆☆☆] 1/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) 2013-2025 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
namespace Examples.Core;
public partial class WindowShouldClose : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Core / Window Should Close";
public string Title => "raylib [core] example - window should close";
// The runner drives its loop off this instead of WindowShouldClose(), so the exit
// confirmation (polled in Update) can intercept both the X-button and KEY_ESCAPE.
public bool ShouldClose => exitWindow;
private bool exitWindowRequested; // Flag to request window to exit
private bool exitWindow; // Flag to set window to exit
public void Init()
{
SetExitKey(KeyboardKey.Null); // Disable KEY_ESCAPE to close window, X-button still works
exitWindowRequested = false;
exitWindow = false;
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
// Detect if X-button or KEY_ESCAPE have been pressed to close window
bool closeRequested = IsKeyPressed(KeyboardKey.Escape);
#if !BROWSER
// WindowShouldClose() polls the OS window close button, but on the wasm target it calls
// emscripten_sleep (ASYNCIFY is not enabled) and the canvas has no window chrome anyway,
// so on web the confirmation is triggered by KEY_ESCAPE only.
closeRequested |= Raylib.WindowShouldClose();
#endif
if (closeRequested)
{
exitWindowRequested = true;
}
if (exitWindowRequested)
{
// A request for close window has been issued, we can save data before closing
// or just show a message asking for confirmation
if (IsKeyPressed(KeyboardKey.Y))
{
exitWindow = true;
}
else if (IsKeyPressed(KeyboardKey.N))
{
exitWindowRequested = false;
}
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
if (exitWindowRequested)
{
DrawRectangle(0, 100, screenWidth, 200, Color.Black);
DrawText("Are you sure you want to exit program? [Y/N]", 40, 180, 30, Color.White);
}
else
{
DrawText("Try to close the window to get confirmation message!", 120, 200, 20, Color.LightGray);
}
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [core] example - window should close");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new WindowShouldClose();
game.Init();
// Main game loop
while (!game.exitWindow)
{
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}
}

View file

@ -1,97 +1,127 @@
/*******************************************************************************************
*
* raylib [core] example - World to screen
* raylib [core] example - world screen
*
* This example has been created using raylib 1.3 (www.raylib.com)
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
* Example complexity rating: [★★☆☆] 2/4
*
* Copyright (c) 2015 Ramon Santamaria (@raysan5)
* Example originally created with raylib 1.3, 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) 2015-2025 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
using System.Numerics;
using static Raylib_cs.Raylib;
namespace Examples.Core;
public class WorldScreen
public partial class WorldScreen : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Core / World to Screen";
public string Title => "raylib [core] example - world screen";
public bool CursorDisabled => true;
private Camera3D camera;
private Vector3 cubePosition;
private Vector2 cubeScreenPosition = new(0.0f, 0.0f);
public void Init()
{
// Define the camera to look into our 3d world
camera = new();
camera.Position = new Vector3(10.0f, 10.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
cubePosition = new(0.0f, 0.0f, 0.0f);
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.ThirdPerson);
// Calculate cube screen space position (with a little offset to be in top)
cubeScreenPosition = GetWorldToScreen(
new Vector3(cubePosition.X, cubePosition.Y + 2.5f, cubePosition.Z),
camera
);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawCube(cubePosition, 2.0f, 2.0f, 2.0f, Color.Red);
DrawCubeWires(cubePosition, 2.0f, 2.0f, 2.0f, Color.Maroon);
DrawGrid(10, 1.0f);
EndMode3D();
DrawText(
"Enemy: 100/100",
(int)cubeScreenPosition.X - MeasureText("Enemy: 100/100", 20) / 2,
(int)cubeScreenPosition.Y,
20,
Color.Black
);
DrawText(
$"Cube position in screen space coordinates: [{(int)cubeScreenPosition.X}, {(int)cubeScreenPosition.Y}]",
10,
10,
20,
Color.Lime
);
DrawText("Text 2d should be always on top of the cube", 10, 40, 20, Color.Gray);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [core] example - world screen");
InitWindow(screenWidth, screenHeight, "raylib [core] example - 3d camera free");
DisableCursor(); // Limit cursor to relative movement inside the window
// Define the camera to look into our 3d world
Camera3D camera = new();
camera.Position = new Vector3(10.0f, 10.0f, 10.0f);
camera.Target = new Vector3(0.0f, 0.0f, 0.0f);
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
camera.FovY = 45.0f;
camera.Projection = CameraProjection.Perspective;
Vector3 cubePosition = new(0.0f, 0.0f, 0.0f);
Vector2 cubeScreenPosition;
SetTargetFPS(60);
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new WorldScreen();
game.Init();
// Main game loop
while (!WindowShouldClose())
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Free);
// Calculate cube screen space position (with a little offset to be in top)
cubeScreenPosition = GetWorldToScreen(
new Vector3(cubePosition.X, cubePosition.Y + 2.5f, cubePosition.Z),
camera
);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawCube(cubePosition, 2.0f, 2.0f, 2.0f, Color.Red);
DrawCubeWires(cubePosition, 2.0f, 2.0f, 2.0f, Color.Maroon);
DrawGrid(10, 1.0f);
EndMode3D();
DrawText(
"Enemy: 100 / 100",
(int)cubeScreenPosition.X - MeasureText("Enemy: 100 / 100", 20) / 2,
(int)cubeScreenPosition.Y,
20,
Color.Black
);
DrawText(
"Text is always on top of the cube",
(screenWidth - MeasureText("Text is always on top of the cube", 20)) / 2,
25,
20,
Color.Gray
);
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow();
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}
}

View file

@ -0,0 +1,53 @@
using System.Diagnostics.CodeAnalysis;
namespace Examples;
/// <summary>
/// Discovers every <see cref="IExample"/> implementation in this assembly via reflection:
/// <see cref="DesktopExamples"/> (Program.cs) is all of them, <see cref="BrowserExamples"/>
/// (Web/Host.cs) is everything not marked <see cref="ExcludeFromBrowserAttribute"/>.
/// Ordering is by category (browser dropdown grouping), then display name.
/// </summary>
public static class ExampleRegistry
{
/// <summary>Category order used for the browser dropdown and desktop run-all sequence.</summary>
private static readonly string[] CategoryOrder =
[
"Core",
"Shapes",
"Models",
"Textures",
"Text",
"Audio",
"Shaders",
];
private static readonly IExample[] AllExamples = DiscoverAll();
public static readonly IExample[] DesktopExamples = AllExamples;
public static readonly IExample[] BrowserExamples =
Array.FindAll(AllExamples, e => !e.GetType().IsDefined(typeof(ExcludeFromBrowserAttribute), inherit: false));
[UnconditionalSuppressMessage("Trimming", "IL2026",
Justification = "The Examples assembly is rooted via TrimmerRootAssembly in Examples.csproj.")]
[UnconditionalSuppressMessage("Trimming", "IL2067",
Justification = "Same as IL2026: all example types and their parameterless constructors are rooted.")]
private static IExample[] DiscoverAll()
{
return typeof(IExample).Assembly
.GetTypes()
.Where(t => typeof(IExample).IsAssignableFrom(t) && t.IsClass && !t.IsAbstract)
.Select(t => (IExample)Activator.CreateInstance(t))
.OrderBy(e => Array.IndexOf(CategoryOrder, Category(e)))
.ThenBy(e => e.Name, StringComparer.OrdinalIgnoreCase)
.ToArray();
}
// "Examples.Core" -> "Core"
private static string Category(IExample example)
{
var ns = example.GetType().Namespace ?? "";
return ns[(ns.LastIndexOf('.') + 1)..];
}
}

View file

@ -3,32 +3,75 @@
<OutputType>Exe</OutputType>
<TargetFrameworks>net8.0;net10.0</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<StartupObject>Examples.Program</StartupObject>
<StartupObject Condition="'$(RuntimeIdentifier)' != 'browser-wasm'">Examples.Program</StartupObject>
<StartupObject Condition="'$(RuntimeIdentifier)' == 'browser-wasm'">Examples.Web.Host</StartupObject>
<RunWorkingDirectory>$(MSBuildThisFileDirectory)</RunWorkingDirectory>
<LangVersion>12</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<IsPackable>false</IsPackable>
</PropertyGroup>
<PropertyGroup Condition="'$(RuntimeIdentifier)' == 'browser-wasm'">
<SelfContained>true</SelfContained>
<WasmMainJSPath>Web/main.js</WasmMainJSPath>
<Nullable>disable</Nullable>
<!-- [JSExport] is browser-only by design; these files only compile for browser-wasm. -->
<NoWarn>$(NoWarn);CA1416</NoWarn>
<DefineConstants>$(DefineConstants);BROWSER</DefineConstants>
<!-- Link the native raylib.a (shipped by the Raylib-cs package) into the wasm module. -->
<WasmBuildNative>true</WasmBuildNative>
<WasmAllowUndefinedSymbols>true</WasmAllowUndefinedSymbols>
<InvariantGlobalization>true</InvariantGlobalization>
<EmccFlags>$(EmccFlags) -sALLOW_MEMORY_GROWTH=1 -sINITIAL_MEMORY=64MB</EmccFlags>
<!-- Avoid wasm-opt on SDK/workload combinations where Binaryen rejects bulk-memory-opt. -->
<EmccLinkOptimizationFlag Condition="'$(EmccLinkOptimizationFlag)' == ''">-O0</EmccLinkOptimizationFlag>
<EmccCompileOptimizationFlag Condition="'$(EmccCompileOptimizationFlag)' == ''">-O0</EmccCompileOptimizationFlag>
<WasmNativeStrip Condition="'$(WasmNativeStrip)' == ''">false</WasmNativeStrip>
<WasmEmitSymbolMap Condition="'$(WasmEmitSymbolMap)' == ''">false</WasmEmitSymbolMap>
</PropertyGroup>
<ItemGroup>
<Compile Remove="Core/LoadingThread.cs"/>
<Compile Remove="Text/Unicode.cs"/>
</ItemGroup>
<ItemGroup Condition="'$(RuntimeIdentifier)' != 'browser-wasm'">
<Compile Remove="Web/**/*.cs"/>
</ItemGroup>
<ItemGroup Condition="'$(RuntimeIdentifier)' == 'browser-wasm'">
<Compile Remove="Program.cs"/>
<!-- Examples are discovered via reflection (ExampleRegistry);
therefore they must be excluded from trimming. -->
<TrimmerRootAssembly Include="Examples"/>
</ItemGroup>
<ItemGroup>
<Using Include="Raylib_cs"/>
<Using Include="Raylib_cs.Raylib" Static="true"/>
<Using Include="System.Numerics"/>
</ItemGroup>
<ItemGroup Condition="'$(UseRaylibCsPackage)' != 'true'">
<ProjectReference Include="..\Raylib-cs\Raylib-cs.csproj"/>
</ItemGroup>
<!-- NuGet imports this from buildTransitive/ for PackageReference; ProjectReference needs it imported from source
so the native raylib library is downloaded and copied to the output. -->
<Import Project="..\Raylib-cs\Raylib-cs.targets" Condition="'$(UseRaylibCsPackage)' != 'true'"/>
<ItemGroup Condition="'$(UseRaylibCsPackage)' == 'true'">
<PackageReference Include="Raylib-cs" Version="$(RaylibCsVersion)"/>
</ItemGroup>
<ItemGroup Condition="'$(RuntimeIdentifier)' == 'browser-wasm'">
<WasmExtraFilesToDeploy Include="Web/index.html" TargetPath="index.html"/>
<WasmExtraFilesToDeploy Include="Web/main.js" TargetPath="main.js"/>
<WasmExtraFilesToDeploy Include="Web/scaleUtils.js" TargetPath="scaleUtils.js"/>
<WasmExtraFilesToDeploy Include="..\Raylib-cs\logo\raylib-cs.ico" TargetPath="favicon.ico"/>
<!-- Bundle the example assets into the wasm virtual filesystem at /resources/...
so examples loading from "resources/..." work unchanged. -->
<WasmFilesToIncludeInFileSystem Include="resources\**\*.*"
TargetPath="resources\%(RecursiveDir)%(Filename)%(Extension)"/>
</ItemGroup>
</Project>

View file

@ -0,0 +1,20 @@
namespace Examples;
/// <summary>
/// Marks an <see cref="IExample"/> that is omitted from the browser host (Web/Host.cs)
/// because of a wasm/WebGL platform limitation. Desktop runs are unaffected.
/// </summary>
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public sealed class ExcludeFromBrowserAttribute : Attribute
{
/// <summary>Why the example cannot run in the browser.</summary>
public string Reason
{
get;
}
public ExcludeFromBrowserAttribute(string reason = null)
{
Reason = reason;
}
}

61
Examples/IExample.cs Normal file
View file

@ -0,0 +1,61 @@
namespace Examples;
/// <summary>
/// A runnable raylib example. Loop-spanning state lives in instance fields, (re)initialized in
/// <see cref="Init"/> so re-selecting an example resets it.
///
/// <para>
/// Desktop drives examples via <c>Program.cs</c> (each also keeps a thin standalone
/// <c>static Main()</c>); in the browser, <c>Web/Host.cs</c> owns the single window and calls
/// <see cref="Update"/> one frame at a time from JavaScript. Platform divergences are guarded
/// with <c>#if BROWSER</c>, preferably around a single constant.
/// </para>
/// </summary>
public interface IExample
{
/// <summary>Display name shown in the navigation dropdown.</summary>
string Name
{
get;
}
/// <summary>Window title, matching the example's standalone <c>Main()</c>.</summary>
string Title
{
get;
}
/// <summary>Desktop window size, matching the standalone <c>Main()</c>. The browser canvas is fixed at 800x450.</summary>
int Width => 800;
/// <inheritdoc cref="Width"/>
int Height => 450;
/// <summary>Config flags the desktop runner applies before window creation. Ignored in the browser.</summary>
ConfigFlags ConfigFlags => 0;
/// <summary>Target FPS. The desktop runner always applies it; the browser host paces its frame loop with it.</summary>
int TargetFps => 60;
/// <summary>Whether the desktop runner disables the cursor. Ignored in the browser (the pointer stays visible).</summary>
bool CursorDisabled => false;
/// <summary>Whether the desktop runner hides the cursor. Ignored in the browser.</summary>
bool CursorHidden => false;
/// <summary>
/// Whether the desktop runner should exit its frame loop; defaults to
/// <see cref="Raylib.WindowShouldClose"/>. Examples that intercept the close request
/// override this and poll WindowShouldClose in <see cref="Update"/> themselves.
/// </summary>
bool ShouldClose => WindowShouldClose();
/// <summary>Set up the Example and preload necessary resources.</summary>
void Init();
/// <summary>Render one frame, including BeginDrawing/EndDrawing.</summary>
void Update();
/// <summary>Free resources.</summary>
void Unload();
}

View file

@ -0,0 +1,387 @@
/*******************************************************************************************
*
* raylib [models] example - animation blend custom
*
* Example complexity rating: [★★★★] 4/4
*
* Example originally created with raylib 5.5, last time updated with raylib 6.0
*
* Example contributed by dmitrii-brand (@dmitrii-brand) and reviewed by Ramon Santamaria (@raysan5)
*
* DETAILS: Example demonstrates per-bone animation blending, allowing smooth transitions
* between two animations by interpolating bone transforms. This is useful for:
* - Blending movement animations (walk/run) with action animations (jump/attack)
* - Creating smooth animation transitions
* - Layering animations (e.g., upper body attack while lower body walks)
*
* WARNING: GPU skinning must be enabled in raylib with a compilation flag,
* if not enabled, CPU skinning will be used instead
*
* 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) 2026 dmitrii-brand (@dmitrii-brand)
*
********************************************************************************************/
using static Raylib_cs.Raymath;
namespace Examples.Models;
public partial class AnimationBlendCustom : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
#if BROWSER
private const int GlslVersion = 100; // WebGL1 needs GLSL ES 100
#else
private const int GlslVersion = 330;
#endif
public string Name => "Models / Animation Blend Custom";
public string Title => "raylib [models] example - animation blend custom";
private Camera3D camera;
private Model model;
private Vector3 position;
private Shader skinningShader;
private unsafe ModelAnimation* anims;
private int animCount;
private int animIndex0;
private int animIndex1;
private int animCurrentFrame0;
private int animCurrentFrame1;
private bool upperBodyBlend;
public unsafe void Init()
{
// Define the camera to look into our 3d world
camera = new();
camera.Position = new Vector3(4.0f, 4.0f, 4.0f); // Camera position
camera.Target = new Vector3(0.0f, 1.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
// Load gltf model
model = LoadModel("resources/models/gltf/greenman.glb");
position = new Vector3(0.0f, 0.0f, 0.0f); // Set model position
// Load skinning shader
// WARNING: GPU skinning must be enabled in raylib with a compilation flag,
// if not enabled, CPU skinning will be used instead
skinningShader = LoadShader(
$"resources/shaders/glsl{GlslVersion}/skinning.vs",
$"resources/shaders/glsl{GlslVersion}/skinning.fs"
);
model.Materials[1].Shader = skinningShader;
// Load gltf model animations
animCount = 0;
anims = LoadModelAnimations("resources/models/gltf/greenman.glb", ref animCount);
// Use specific animation indices: 2-walk/move, 3-attack
animIndex0 = 2; // Walk/Move animation (index 2)
animIndex1 = 3; // Attack animation (index 3)
animCurrentFrame0 = 0;
animCurrentFrame1 = 0;
// Validate indices
if (animIndex0 >= animCount)
{
animIndex0 = 0;
}
if (animIndex1 >= animCount)
{
animIndex1 = (animCount > 1) ? 1 : 0;
}
upperBodyBlend = true; // Toggle: true = upper/lower body blending, false = uniform blending (50/50)
}
public unsafe void Update()
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Orbital);
// Toggle upper/lower body blending mode (SPACE key)
if (IsKeyPressed(KeyboardKey.Space))
{
upperBodyBlend = !upperBodyBlend;
}
// Update animation frames
var anim0 = anims[animIndex0];
var anim1 = anims[animIndex1];
animCurrentFrame0 = (animCurrentFrame0 + 1) % anim0.KeyFrameCount;
animCurrentFrame1 = (animCurrentFrame1 + 1) % anim1.KeyFrameCount;
// Blend the two animations
// When upperBodyBlend is ON: upper body = attack (1.0), lower body = walk (0.0)
// When upperBodyBlend is OFF: uniform blend at 0.5 (50% walk, 50% attack)
var blendFactor = upperBodyBlend ? 1.0f : 0.5f;
UpdateModelAnimationBones(anim0, animCurrentFrame0, anim1, animCurrentFrame1, blendFactor, upperBodyBlend);
// raylib provided animation blending function
//UpdateModelAnimationEx(model, anim0, (float)animCurrentFrame0,
// anim1, (float)animCurrentFrame1, blendFactor);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawModel(model, position, 1.0f, Color.White);
DrawGrid(10, 1.0f);
EndMode3D();
// Draw UI
DrawText($"ANIM 0: {anim0.NameToString()}", 10, 10, 20, Color.Gray);
DrawText($"ANIM 1: {anim1.NameToString()}", 10, 40, 20, Color.Gray);
DrawText($"[SPACE] Toggle blending mode: {(upperBodyBlend ? "Upper/Lower Body Blending" : "Uniform Blending")}",
10, GetScreenHeight() - 30, 20, Color.DarkGray);
EndDrawing();
//----------------------------------------------------------------------------------
}
public unsafe void Unload()
{
UnloadModelAnimations(anims, animCount); // Unload model animation
UnloadModel(model); // Unload model and meshes/material
UnloadShader(skinningShader); // Unload GPU skinning shader
}
// Check if a bone is part of upper body (for selective blending)
private static bool IsUpperBodyBone(string boneName)
{
// Common upper body bone names (adjust based on your model)
if (boneName is "spine" or "spine1" or "spine2" or
"chest" or "upperChest" or
"neck" or "head" or
"shoulder" or "shoulder_L" or "shoulder_R" or
"upperArm" or "upperArm_L" or "upperArm_R" or
"lowerArm" or "lowerArm_L" or "lowerArm_R" or
"hand" or "hand_L" or "hand_R" or
"clavicle" or "clavicle_L" or "clavicle_R")
{
return true;
}
// Check if bone name contains upper body keywords
if (boneName.Contains("spine") || boneName.Contains("chest") ||
boneName.Contains("neck") || boneName.Contains("head") ||
boneName.Contains("shoulder") || boneName.Contains("arm") ||
boneName.Contains("hand") || boneName.Contains("clavicle"))
{
return true;
}
return false;
}
// Blend two animations per-bone with selective upper/lower body blending
private unsafe void UpdateModelAnimationBones(ModelAnimation anim0, int frame0,
ModelAnimation anim1, int frame1, float blend, bool upperBodyBlend)
{
// Validate inputs
if ((anim0.BoneCount != 0) && (anim0.KeyframePoses != null) &&
(anim1.BoneCount != 0) && (anim1.KeyframePoses != null) &&
(model.Skeleton.BoneCount != 0) && (model.Skeleton.BindPose != null))
{
// Clamp blend factor to [0, 1]
blend = MathF.Min(1.0f, MathF.Max(0.0f, blend));
// Ensure frame indices are valid
if (frame0 >= anim0.KeyFrameCount)
{
frame0 = anim0.KeyFrameCount - 1;
}
if (frame1 >= anim1.KeyFrameCount)
{
frame1 = anim1.KeyFrameCount - 1;
}
if (frame0 < 0)
{
frame0 = 0;
}
if (frame1 < 0)
{
frame1 = 0;
}
// Get bone count (use minimum of all to be safe)
var boneCount = model.Skeleton.BoneCount;
if (anim0.BoneCount < boneCount)
{
boneCount = anim0.BoneCount;
}
if (anim1.BoneCount < boneCount)
{
boneCount = anim1.BoneCount;
}
// Blend each bone
for (var boneIndex = 0; boneIndex < boneCount; boneIndex++)
{
// Determine blend factor for this bone
var boneBlendFactor = blend;
// If upper body blending is enabled, use different blend factors for upper vs lower body
if (upperBodyBlend)
{
var boneName = model.Skeleton.Bones[boneIndex].NameToString();
var isUpperBody = IsUpperBodyBone(boneName);
// Upper body: use anim1 (attack), Lower body: use anim0 (walk)
// blend = 0.0 means full anim0 (walk), 1.0 means full anim1 (attack)
if (isUpperBody)
{
boneBlendFactor = blend; // Upper body: blend towards anim1 (attack)
}
else
{
boneBlendFactor = 1.0f - blend; // Lower body: blend towards anim0 (walk) - invert the blend
}
}
// Get transforms from both animations
var bindTransform = model.Skeleton.BindPose[boneIndex];
var animTransform0 = anim0.KeyframePoses[frame0][boneIndex];
var animTransform1 = anim1.KeyframePoses[frame1][boneIndex];
// Blend the transforms
Transform blended = new();
blended.Translation = Vector3Lerp(animTransform0.Translation, animTransform1.Translation, boneBlendFactor);
blended.Rotation = QuaternionSlerp(animTransform0.Rotation, animTransform1.Rotation, boneBlendFactor);
blended.Scale = Vector3Lerp(animTransform0.Scale, animTransform1.Scale, boneBlendFactor);
// Convert bind pose to matrix
var bindMatrix = MatrixMultiply(MatrixMultiply(
MatrixScale(bindTransform.Scale.X, bindTransform.Scale.Y, bindTransform.Scale.Z),
QuaternionToMatrix(bindTransform.Rotation)),
MatrixTranslate(bindTransform.Translation.X, bindTransform.Translation.Y, bindTransform.Translation.Z));
// Convert blended transform to matrix
var blendedMatrix = MatrixMultiply(MatrixMultiply(
MatrixScale(blended.Scale.X, blended.Scale.Y, blended.Scale.Z),
QuaternionToMatrix(blended.Rotation)),
MatrixTranslate(blended.Translation.X, blended.Translation.Y, blended.Translation.Z));
// Calculate final bone matrix (similar to UpdateModelAnimationBones)
model.BoneMatrices[boneIndex] = MatrixMultiply(MatrixInvert(bindMatrix), blendedMatrix);
}
// CPU skinning, updates CPU buffers and uploads them to GPU (if available)
// NOTE: Fallback in case GPU skinning is not supported or enabled
for (var m = 0; m < model.MeshCount; m++)
{
var mesh = model.Meshes[m];
Vector3 animVertex;
Vector3 animNormal;
var vertexValuesCount = mesh.VertexCount * 3;
var boneCounter = 0;
var bufferUpdateRequired = false; // Flag to check when anim vertex information is updated
// Skip if missing bone data or missing anim buffers initialization
if ((mesh.BoneWeights == null) || (mesh.BoneIndices == null) ||
(mesh.AnimVertices == null) || (mesh.AnimNormals == null))
{
continue;
}
for (var vCounter = 0; vCounter < vertexValuesCount; vCounter += 3)
{
mesh.AnimVertices[vCounter] = 0;
mesh.AnimVertices[vCounter + 1] = 0;
mesh.AnimVertices[vCounter + 2] = 0;
if (mesh.AnimNormals != null)
{
mesh.AnimNormals[vCounter] = 0;
mesh.AnimNormals[vCounter + 1] = 0;
mesh.AnimNormals[vCounter + 2] = 0;
}
// Iterates over 4 bones per vertex
for (var j = 0; j < 4; j++, boneCounter++)
{
var boneWeight = mesh.BoneWeights[boneCounter];
var boneIndex = mesh.BoneIndices[boneCounter];
// Early stop when no transformation will be applied
if (boneWeight == 0.0f)
{
continue;
}
animVertex = new Vector3(mesh.Vertices[vCounter], mesh.Vertices[vCounter + 1], mesh.Vertices[vCounter + 2]);
animVertex = Vector3Transform(animVertex, model.BoneMatrices[boneIndex]);
mesh.AnimVertices[vCounter] += animVertex.X * boneWeight;
mesh.AnimVertices[vCounter + 1] += animVertex.Y * boneWeight;
mesh.AnimVertices[vCounter + 2] += animVertex.Z * boneWeight;
bufferUpdateRequired = true;
// Normals processing
// NOTE: We use meshes.baseNormals (default normal) to calculate meshes.normals (animated normals)
if ((mesh.Normals != null) && (mesh.AnimNormals != null))
{
animNormal = new Vector3(mesh.Normals[vCounter], mesh.Normals[vCounter + 1], mesh.Normals[vCounter + 2]);
animNormal = Vector3Transform(animNormal, MatrixTranspose(MatrixInvert(model.BoneMatrices[boneIndex])));
mesh.AnimNormals[vCounter] += animNormal.X * boneWeight;
mesh.AnimNormals[vCounter + 1] += animNormal.Y * boneWeight;
mesh.AnimNormals[vCounter + 2] += animNormal.Z * boneWeight;
}
}
}
if (bufferUpdateRequired)
{
// Update GPU vertex buffers with updated data (position + normals)
Rlgl.UpdateVertexBuffer(mesh.VboId[(int)ShaderLocationIndex.VertexPosition], mesh.AnimVertices, mesh.VertexCount * 3 * sizeof(float), 0);
if (mesh.Normals != null)
{
Rlgl.UpdateVertexBuffer(mesh.VboId[(int)ShaderLocationIndex.VertexNormal], mesh.AnimNormals, mesh.VertexCount * 3 * sizeof(float), 0);
}
}
}
}
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [models] example - animation blend custom");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new AnimationBlendCustom();
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,471 @@
/*******************************************************************************************
*
* raylib [models] example - animation blending
*
* Example complexity rating: [★★★★] 4/4
*
* Example originally created with raylib 5.5, last time updated with raylib 6.0
*
* Example contributed by Kirandeep (@Kirandeep-Singh-Khehra) and reviewed by Ramon Santamaria (@raysan5)
*
* WARNING: GPU skinning must be enabled in raylib with a compilation flag,
* if not enabled, CPU skinning will be used instead
*
* 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) 2024-2026 Kirandeep (@Kirandeep-Singh-Khehra) and Ramon Santamaria (@raysan5)
*
********************************************************************************************/
// NOTE: The upstream example uses raygui for its UI controls (dropdowns, sliders, progress bars).
// raygui is not part of raylib-cs, so the required controls are reimplemented here with
// basic raylib drawing primitives, preserving the original behaviour.
namespace Examples.Models;
public partial class AnimationBlending : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
#if BROWSER
private const int GlslVersion = 100; // WebGL1 needs GLSL ES 100
#else
private const int GlslVersion = 330;
#endif
public string Name => "Models / Animation Blending";
public string Title => "raylib [models] example - animation blending";
private Camera3D camera;
private Model model;
private Vector3 position;
private Shader skinningShader;
private unsafe ModelAnimation* anims;
private int animCount;
private int currentAnimPlaying;
private int nextAnimToPlay;
private bool animTransition;
private int animIndex0;
private float animCurrentFrame0;
private float animFrameSpeed0;
private int animIndex1;
private float animCurrentFrame1;
private float animFrameSpeed1;
private float animBlendFactor;
private float animBlendTime;
private float animBlendTimeCounter;
private bool animPause;
private string[] animNames;
private bool dropdownEditMode0;
private bool dropdownEditMode1;
private float animFrameProgress0;
private float animFrameProgress1;
private float animBlendProgress;
public unsafe void Init()
{
// Define the camera to look into our 3d world
camera = new();
camera.Position = new Vector3(6.0f, 6.0f, 6.0f); // Camera position
camera.Target = new Vector3(0.0f, 2.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
// Load model
model = LoadModel("resources/models/gltf/robot.glb"); // Load character model
position = new Vector3(0.0f, 0.0f, 0.0f); // Set model world position
// Load skinning shader
// NOTE: It must be a valid shader, following raylib attribs/uniform conventions for GPU skinning
skinningShader = LoadShader(
$"resources/shaders/glsl{GlslVersion}/skinning.vs",
$"resources/shaders/glsl{GlslVersion}/skinning.fs"
);
// Skinning shader could be required to be assigned to all materials shaders, just to make
// sure required uniforms are being updated for the mesh using that material (and shader)
for (var i = 0; i < model.MaterialCount; i++)
{
model.Materials[i].Shader = skinningShader;
}
// Load model animations
animCount = 0;
anims = LoadModelAnimations("resources/models/gltf/robot.glb", ref animCount);
// Animation playing variables
// NOTE: Two animations are played with a smooth transition between them
currentAnimPlaying = 0; // Current animation playing (0 o 1)
nextAnimToPlay = 1; // Next animation to play (to transition)
animTransition = false; // Flag to register anim transition state
animIndex0 = 10; // Current animation playing (walking)
animCurrentFrame0 = 0.0f; // Current animation frame (supporting interpolated frames)
animFrameSpeed0 = 0.5f; // Current animation play speed
animIndex1 = 6; // Next animation to play (running)
animCurrentFrame1 = 0.0f; // Next animation frame (supporting interpolated frames)
animFrameSpeed1 = 0.5f; // Next animation play speed
animBlendFactor = 0.0f; // Blend factor from anim0[frame0] --> anim1[frame1], [0.0f..1.0f]
animBlendTime = 2.0f; // Time to blend from one playing animation to another (in seconds)
animBlendTimeCounter = 0.0f; // Time counter (delta time)
animPause = false; // Pause animation
// UI required variables
animNames = new string[animCount];
for (var i = 0; i < animCount; i++)
{
animNames[i] = anims[i].NameToString();
}
dropdownEditMode0 = false;
dropdownEditMode1 = false;
animFrameProgress0 = 0.0f;
animFrameProgress1 = 0.0f;
animBlendProgress = 0.0f;
}
public unsafe void Update()
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Orbital);
if (IsKeyPressed(KeyboardKey.P))
{
animPause = !animPause;
}
if (!animPause)
{
// Start transition from anim0[] to anim1[]
if (IsKeyPressed(KeyboardKey.Space) && !animTransition)
{
if (currentAnimPlaying == 0)
{
// Transition anim0 --> anim1
nextAnimToPlay = 1;
animCurrentFrame1 = 0.0f;
}
else
{
// Transition anim1 --> anim0
nextAnimToPlay = 0;
animCurrentFrame0 = 0.0f;
}
// Set animation transition
animTransition = true;
animBlendTimeCounter = 0.0f;
animBlendFactor = 0.0f;
}
if (animTransition)
{
// Playing anim0 and anim1 at the same time
animCurrentFrame0 += animFrameSpeed0;
if (animCurrentFrame0 >= anims[animIndex0].KeyFrameCount)
{
animCurrentFrame0 = 0.0f;
}
animCurrentFrame1 += animFrameSpeed1;
if (animCurrentFrame1 >= anims[animIndex1].KeyFrameCount)
{
animCurrentFrame1 = 0.0f;
}
// Increment blend factor over time to transition from anim0 --> anim1 over time
// NOTE: Time blending could be other than linear, using some easing
animBlendFactor = animBlendTimeCounter / animBlendTime;
animBlendTimeCounter += GetFrameTime();
animBlendProgress = animBlendFactor;
// Update model with animations blending
if (nextAnimToPlay == 1)
{
// Blend anim0 --> anim1
UpdateModelAnimationEx(model, anims[animIndex0], animCurrentFrame0,
anims[animIndex1], animCurrentFrame1, animBlendFactor);
}
else
{
// Blend anim1 --> anim0
UpdateModelAnimationEx(model, anims[animIndex1], animCurrentFrame1,
anims[animIndex0], animCurrentFrame0, animBlendFactor);
}
// Check if transition completed
if (animBlendFactor > 1.0f)
{
// Reset frame states
if (currentAnimPlaying == 0)
{
animCurrentFrame0 = 0.0f;
}
else if (currentAnimPlaying == 1)
{
animCurrentFrame1 = 0.0f;
}
currentAnimPlaying = nextAnimToPlay; // Update current animation playing
animBlendFactor = 0.0f; // Reset blend factor
animTransition = false; // Exit transition mode
animBlendTimeCounter = 0.0f;
}
}
else
{
// Play only one anim, the current one
if (currentAnimPlaying == 0)
{
// Playing anim0 at defined speed
animCurrentFrame0 += animFrameSpeed0;
if (animCurrentFrame0 >= anims[animIndex0].KeyFrameCount)
{
animCurrentFrame0 = 0.0f;
}
UpdateModelAnimation(model, anims[animIndex0], animCurrentFrame0);
}
else if (currentAnimPlaying == 1)
{
// Playing anim1 at defined speed
animCurrentFrame1 += animFrameSpeed1;
if (animCurrentFrame1 >= anims[animIndex1].KeyFrameCount)
{
animCurrentFrame1 = 0.0f;
}
UpdateModelAnimation(model, anims[animIndex1], animCurrentFrame1);
}
}
}
// Update progress bars values with current frame for each animation
animFrameProgress0 = animCurrentFrame0;
animFrameProgress1 = animCurrentFrame1;
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawModel(model, position, 1.0f, Color.White); // Draw animated model
DrawGrid(10, 1.0f);
EndMode3D();
if (animTransition)
{
DrawText("ANIM TRANSITION BLENDING!", 170, 50, 30, Color.Blue);
}
// Draw UI elements
//---------------------------------------------------------------------------------------------
GuiSlider(new Rectangle(10, 38, 160, 12), null, $"x{animFrameSpeed0:0.0}", ref animFrameSpeed0, 0.1f, 2.0f);
GuiSlider(new Rectangle(GetScreenWidth() - 170.0f, 38, 160, 12), $"{animFrameSpeed1:0.0}x", null, ref animFrameSpeed1, 0.1f, 2.0f);
// Blending process progress bar
GuiProgressBar(new Rectangle(180, 14, 440, 16), null, null, animBlendProgress, 0.0f, 1.0f);
// Centered "PRESS SPACE" label
const string spaceLabel = "PRESS SPACE to START BLENDING";
var spaceLabelWidth = MeasureText(spaceLabel, 20);
DrawText(spaceLabel, (GetScreenWidth() - spaceLabelWidth) / 2, (int)(GetScreenHeight() - 100.0f + 10), 20, Color.DarkGray);
// Draw playing timeline with keyframes for anim0[]
GuiProgressBar(new Rectangle(60, GetScreenHeight() - 60.0f, GetScreenWidth() - 180.0f, 20), "ANIM 0",
$"FRAME: {animFrameProgress0:0.00} / {anims[animIndex0].KeyFrameCount}",
animFrameProgress0, 0.0f, anims[animIndex0].KeyFrameCount);
for (var i = 0; i < anims[animIndex0].KeyFrameCount; i++)
{
DrawRectangle(60 + (int)(((float)(GetScreenWidth() - 180) / anims[animIndex0].KeyFrameCount) * i),
GetScreenHeight() - 60, 1, 20, Color.Blue);
}
// Draw playing timeline with keyframes for anim1[]
GuiProgressBar(new Rectangle(60, GetScreenHeight() - 30.0f, GetScreenWidth() - 180.0f, 20), "ANIM 1",
$"FRAME: {animFrameProgress1:0.00} / {anims[animIndex1].KeyFrameCount}",
animFrameProgress1, 0.0f, anims[animIndex1].KeyFrameCount);
for (var i = 0; i < anims[animIndex1].KeyFrameCount; i++)
{
DrawRectangle(60 + (int)(((float)(GetScreenWidth() - 180) / anims[animIndex1].KeyFrameCount) * i),
GetScreenHeight() - 30, 1, 20, Color.Blue);
}
// Draw animation selectors for blending transition (drawn last so open lists render on top)
// NOTE: Transition does not start until requested
if (GuiDropdownBox(new Rectangle(10, 10, 160, 24), animNames, ref animIndex0, dropdownEditMode0))
{
dropdownEditMode0 = !dropdownEditMode0;
}
if (GuiDropdownBox(new Rectangle(GetScreenWidth() - 170.0f, 10, 160, 24), animNames, ref animIndex1, dropdownEditMode1))
{
dropdownEditMode1 = !dropdownEditMode1;
}
//---------------------------------------------------------------------------------------------
EndDrawing();
//----------------------------------------------------------------------------------
}
public unsafe void Unload()
{
UnloadModelAnimations(anims, animCount); // Unload model animation
UnloadModel(model); // Unload model and meshes/material
UnloadShader(skinningShader); // Unload GPU skinning shader
}
// Minimal immediate-mode slider (raygui replacement)
private static bool GuiSlider(Rectangle bounds, string textLeft, string textRight, ref float value, float minValue, float maxValue)
{
var mouse = GetMousePosition();
var dragging = false;
if (CheckCollisionPointRec(mouse, bounds) && IsMouseButtonDown(MouseButton.Left))
{
value = minValue + ((mouse.X - bounds.X) / bounds.Width) * (maxValue - minValue);
if (value < minValue)
{
value = minValue;
}
if (value > maxValue)
{
value = maxValue;
}
dragging = true;
}
DrawRectangleRec(bounds, Color.LightGray);
var pct = (value - minValue) / (maxValue - minValue);
DrawRectangleRec(new Rectangle(bounds.X, bounds.Y, bounds.Width * pct, bounds.Height), Color.SkyBlue);
DrawRectangleLinesEx(bounds, 1, Color.Gray);
if (!string.IsNullOrEmpty(textLeft))
{
DrawText(textLeft, (int)(bounds.X - MeasureText(textLeft, 10) - 5), (int)(bounds.Y + bounds.Height / 2 - 5), 10, Color.DarkGray);
}
if (!string.IsNullOrEmpty(textRight))
{
DrawText(textRight, (int)(bounds.X + bounds.Width + 5), (int)(bounds.Y + bounds.Height / 2 - 5), 10, Color.DarkGray);
}
return dragging;
}
// Minimal immediate-mode progress bar (raygui replacement)
private static void GuiProgressBar(Rectangle bounds, string textLeft, string textRight, float value, float minValue, float maxValue)
{
DrawRectangleRec(bounds, Color.LightGray);
var pct = maxValue > minValue ? (value - minValue) / (maxValue - minValue) : 0.0f;
if (pct < 0)
{
pct = 0;
}
if (pct > 1)
{
pct = 1;
}
DrawRectangleRec(new Rectangle(bounds.X, bounds.Y, bounds.Width * pct, bounds.Height), Color.SkyBlue);
DrawRectangleLinesEx(bounds, 1, Color.Gray);
if (!string.IsNullOrEmpty(textLeft))
{
DrawText(textLeft, (int)(bounds.X - MeasureText(textLeft, 10) - 5), (int)(bounds.Y + bounds.Height / 2 - 5), 10, Color.DarkGray);
}
if (!string.IsNullOrEmpty(textRight))
{
DrawText(textRight, (int)(bounds.X + bounds.Width + 5), (int)(bounds.Y + bounds.Height / 2 - 5), 10, Color.DarkGray);
}
}
// Minimal immediate-mode dropdown box (raygui replacement)
private static bool GuiDropdownBox(Rectangle bounds, string[] items, ref int active, bool editMode)
{
var result = false;
var mouse = GetMousePosition();
// Draw main box
DrawRectangleRec(bounds, Color.LightGray);
DrawRectangleLinesEx(bounds, 1, Color.Gray);
if (active >= 0 && active < items.Length)
{
DrawText(items[active], (int)bounds.X + 5, (int)(bounds.Y + bounds.Height / 2 - 5), 10, Color.DarkGray);
}
DrawText(editMode ? "^" : "v", (int)(bounds.X + bounds.Width - 12), (int)(bounds.Y + bounds.Height / 2 - 5), 10, Color.DarkGray);
// Draw items when open
if (editMode)
{
for (var i = 0; i < items.Length; i++)
{
Rectangle item = new(bounds.X, bounds.Y + bounds.Height * (i + 1), bounds.Width, bounds.Height);
var hover = CheckCollisionPointRec(mouse, item);
DrawRectangleRec(item, hover ? Color.SkyBlue : Color.LightGray);
DrawRectangleLinesEx(item, 1, Color.Gray);
DrawText(items[i], (int)item.X + 5, (int)(item.Y + item.Height / 2 - 5), 10, Color.DarkGray);
}
}
if (IsMouseButtonPressed(MouseButton.Left))
{
if (editMode)
{
for (var i = 0; i < items.Length; i++)
{
Rectangle item = new(bounds.X, bounds.Y + bounds.Height * (i + 1), bounds.Width, bounds.Height);
if (CheckCollisionPointRec(mouse, item))
{
active = i;
break;
}
}
result = true; // any click closes the dropdown
}
else if (CheckCollisionPointRec(mouse, bounds))
{
result = true; // open the dropdown
}
}
return result;
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [models] example - animation blending");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new AnimationBlending();
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,157 @@
/*******************************************************************************************
*
* raylib [models] example - animation gpu skinning
*
* Example complexity rating: [★★★☆] 3/4
*
* Example originally created with raylib 4.5, last time updated with raylib 4.5
*
* Example contributed by Daniel Holden (@orangeduck) and reviewed by Ramon Santamaria (@raysan5)
*
* WARNING: GPU skinning must be enabled in raylib with a compilation flag,
* if not enabled, CPU skinning will be used instead
*
* 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) 2024-2025 Daniel Holden (@orangeduck)
*
********************************************************************************************/
namespace Examples.Models;
public partial class AnimationGpuSkinning : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
#if BROWSER
private const int GlslVersion = 100; // WebGL1 needs GLSL ES 100
#else
private const int GlslVersion = 330;
#endif
public string Name => "Models / Animation GPU Skinning";
public string Title => "raylib [models] example - animation gpu skinning";
private Camera3D camera;
private Model model;
private Vector3 position;
private Shader skinningShader;
private unsafe ModelAnimation* anims;
private int animCount;
private int animIndex;
private int animCurrentFrame;
public unsafe void Init()
{
// Define the camera to look into our 3d world
camera = new();
camera.Position = new Vector3(5.0f, 5.0f, 5.0f); // Camera position
camera.Target = new Vector3(0.0f, 1.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
// Load gltf model
model = LoadModel("resources/models/gltf/greenman.glb"); // Load character model
position = new Vector3(0.0f, 0.0f, 0.0f); // Set model position
// Load skinning shader
// NOTE: It must be a valid shader, following raylib attribs/uniform conventions for GPU skinning
skinningShader = LoadShader(
$"resources/shaders/glsl{GlslVersion}/skinning.vs",
$"resources/shaders/glsl{GlslVersion}/skinning.fs"
);
// Skinning shader could be required to be assigned to all materials shaders, just to make
// sure required uniforms are being updated for the mesh using that material (and shader)
model.Materials[1].Shader = skinningShader; // Just assigning to materials[1] for this model
// Load gltf model animations
animCount = 0;
anims = LoadModelAnimations("resources/models/gltf/greenman.glb", ref animCount);
// Animation playing variables
animIndex = 0; // Current animation playing
animCurrentFrame = 0; // Current animation frame
}
public unsafe void Update()
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Orbital);
// Select current animation
if (IsKeyPressed(KeyboardKey.Right))
{
animIndex = (animIndex + 1) % animCount;
}
else if (IsKeyPressed(KeyboardKey.Left))
{
animIndex = (animIndex + animCount - 1) % animCount;
}
// Update model animation
animCurrentFrame = (animCurrentFrame + 1) % anims[animIndex].KeyFrameCount;
UpdateModelAnimation(model, anims[animIndex], animCurrentFrame);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawModel(model, position, 1.0f, Color.White);
DrawGrid(10, 1.0f);
EndMode3D();
DrawText($"Current animation: {anims[animIndex].NameToString()}", 10, 40, 20, Color.Maroon);
DrawText("Use the LEFT/RIGHT keys to switch animation", 10, 10, 20, Color.Gray);
EndDrawing();
//----------------------------------------------------------------------------------
}
public unsafe void Unload()
{
UnloadModelAnimations(anims, animCount); // Unload model animation
UnloadModel(model); // Unload model and meshes/material
UnloadShader(skinningShader); // Unload GPU skinning shader
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [models] example - animation gpu skinning");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new AnimationGpuSkinning();
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,289 @@
/*******************************************************************************************
*
* raylib [models] example - animation timing
*
* Example complexity rating: [★★★☆] 3/4
*
* Example originally created with raylib 6.0, last time updated with raylib 6.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) 2026 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
// NOTE: The upstream example uses raygui for its UI controls (dropdown, slider, progress bar).
// raygui is not part of raylib-cs, so the required controls are reimplemented here with
// basic raylib drawing primitives, preserving the original behaviour.
namespace Examples.Models;
public partial class AnimationTiming : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Models / Animation Timing";
public string Title => "raylib [models] example - animation timing";
private Camera3D camera;
private Model model;
private Vector3 position;
private unsafe ModelAnimation* anims;
private int animCount;
private int animIndex;
private float animCurrentFrame;
private float animFrameSpeed;
private bool animPause;
private string[] animNames;
private bool dropdownEditMode;
private float animFrameProgress;
public unsafe void Init()
{
// Define the camera to look into our 3d world
camera = new();
camera.Position = new Vector3(6.0f, 6.0f, 6.0f); // Camera position
camera.Target = new Vector3(0.0f, 2.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
// Load model
model = LoadModel("resources/models/gltf/robot.glb");
position = new Vector3(0.0f, 0.0f, 0.0f); // Set model world position
// Load model animations
animCount = 0;
anims = LoadModelAnimations("resources/models/gltf/robot.glb", ref animCount);
// Animation playing variables
animIndex = 10; // Current animation playing
animCurrentFrame = 0.0f; // Current animation frame (supporting interpolated frames)
animFrameSpeed = 0.5f; // Animation play speed
animPause = false; // Pause animation
// UI required variables
animNames = new string[animCount];
for (var i = 0; i < animCount; i++)
{
animNames[i] = anims[i].NameToString();
}
dropdownEditMode = false;
animFrameProgress = 0.0f;
}
public unsafe void Update()
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Orbital);
if (IsKeyPressed(KeyboardKey.P))
{
animPause = !animPause;
}
if (!animPause && (animIndex < animCount))
{
// Update model animation
animCurrentFrame += animFrameSpeed;
if (animCurrentFrame >= anims[animIndex].KeyFrameCount)
{
animCurrentFrame = 0.0f;
}
UpdateModelAnimation(model, anims[animIndex], animCurrentFrame);
}
// NOTE: Animation and playing speed selected through UI
// Update progressbar value with current frame
animFrameProgress = animCurrentFrame;
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawModel(model, position, 1.0f, Color.White);
DrawGrid(10, 1.0f);
EndMode3D();
// Draw UI, select anim and playing speed
GuiSlider(new Rectangle(260, 10, 500, 24), "FRAME SPEED: ", $"x{animFrameSpeed:0.0}", ref animFrameSpeed, 0.1f, 2.0f);
// Draw playing timeline with keyframes
DrawText($"CURRENT FRAME: {animFrameProgress:0.00} / {anims[animIndex].KeyFrameCount}",
10, (int)(GetScreenHeight() - 64.0f), 10, Color.DarkGray);
GuiProgressBar(new Rectangle(10, GetScreenHeight() - 40.0f, GetScreenWidth() - 20.0f, 24), null, null,
animFrameProgress, 0.0f, anims[animIndex].KeyFrameCount);
for (var i = 0; i < anims[animIndex].KeyFrameCount; i++)
{
DrawRectangle(10 + (int)(((float)(GetScreenWidth() - 20) / anims[animIndex].KeyFrameCount) * i),
GetScreenHeight() - 40, 1, 24, Color.Blue);
}
// NOTE: Dropdown drawn last so its open item list renders on top
if (GuiDropdownBox(new Rectangle(10, 10, 140, 24), animNames, ref animIndex, dropdownEditMode))
{
dropdownEditMode = !dropdownEditMode;
}
EndDrawing();
//----------------------------------------------------------------------------------
}
public unsafe void Unload()
{
UnloadModelAnimations(anims, animCount); // Unload model animation
UnloadModel(model); // Unload model and meshes/material
}
// Minimal immediate-mode slider (raygui replacement)
private static bool GuiSlider(Rectangle bounds, string textLeft, string textRight, ref float value, float minValue, float maxValue)
{
var mouse = GetMousePosition();
var dragging = false;
if (CheckCollisionPointRec(mouse, bounds) && IsMouseButtonDown(MouseButton.Left))
{
value = minValue + ((mouse.X - bounds.X) / bounds.Width) * (maxValue - minValue);
if (value < minValue)
{
value = minValue;
}
if (value > maxValue)
{
value = maxValue;
}
dragging = true;
}
DrawRectangleRec(bounds, Color.LightGray);
var pct = (value - minValue) / (maxValue - minValue);
DrawRectangleRec(new Rectangle(bounds.X, bounds.Y, bounds.Width * pct, bounds.Height), Color.SkyBlue);
DrawRectangleLinesEx(bounds, 1, Color.Gray);
if (!string.IsNullOrEmpty(textLeft))
{
DrawText(textLeft, (int)(bounds.X - MeasureText(textLeft, 10) - 5), (int)(bounds.Y + bounds.Height / 2 - 5), 10, Color.DarkGray);
}
if (!string.IsNullOrEmpty(textRight))
{
DrawText(textRight, (int)(bounds.X + bounds.Width + 5), (int)(bounds.Y + bounds.Height / 2 - 5), 10, Color.DarkGray);
}
return dragging;
}
// Minimal immediate-mode progress bar (raygui replacement)
private static void GuiProgressBar(Rectangle bounds, string textLeft, string textRight, float value, float minValue, float maxValue)
{
DrawRectangleRec(bounds, Color.LightGray);
var pct = maxValue > minValue ? (value - minValue) / (maxValue - minValue) : 0.0f;
if (pct < 0)
{
pct = 0;
}
if (pct > 1)
{
pct = 1;
}
DrawRectangleRec(new Rectangle(bounds.X, bounds.Y, bounds.Width * pct, bounds.Height), Color.SkyBlue);
DrawRectangleLinesEx(bounds, 1, Color.Gray);
if (!string.IsNullOrEmpty(textLeft))
{
DrawText(textLeft, (int)(bounds.X - MeasureText(textLeft, 10) - 5), (int)(bounds.Y + bounds.Height / 2 - 5), 10, Color.DarkGray);
}
if (!string.IsNullOrEmpty(textRight))
{
DrawText(textRight, (int)(bounds.X + bounds.Width + 5), (int)(bounds.Y + bounds.Height / 2 - 5), 10, Color.DarkGray);
}
}
// Minimal immediate-mode dropdown box (raygui replacement)
private static bool GuiDropdownBox(Rectangle bounds, string[] items, ref int active, bool editMode)
{
var result = false;
var mouse = GetMousePosition();
// Draw main box
DrawRectangleRec(bounds, Color.LightGray);
DrawRectangleLinesEx(bounds, 1, Color.Gray);
if (active >= 0 && active < items.Length)
{
DrawText(items[active], (int)bounds.X + 5, (int)(bounds.Y + bounds.Height / 2 - 5), 10, Color.DarkGray);
}
DrawText(editMode ? "^" : "v", (int)(bounds.X + bounds.Width - 12), (int)(bounds.Y + bounds.Height / 2 - 5), 10, Color.DarkGray);
// Draw items when open
if (editMode)
{
for (var i = 0; i < items.Length; i++)
{
Rectangle item = new(bounds.X, bounds.Y + bounds.Height * (i + 1), bounds.Width, bounds.Height);
var hover = CheckCollisionPointRec(mouse, item);
DrawRectangleRec(item, hover ? Color.SkyBlue : Color.LightGray);
DrawRectangleLinesEx(item, 1, Color.Gray);
DrawText(items[i], (int)item.X + 5, (int)(item.Y + item.Height / 2 - 5), 10, Color.DarkGray);
}
}
if (IsMouseButtonPressed(MouseButton.Left))
{
if (editMode)
{
for (var i = 0; i < items.Length; i++)
{
Rectangle item = new(bounds.X, bounds.Y + bounds.Height * (i + 1), bounds.Width, bounds.Height);
if (CheckCollisionPointRec(mouse, item))
{
active = i;
break;
}
}
result = true; // any click closes the dropdown
}
else if (CheckCollisionPointRec(mouse, bounds))
{
result = true; // open the dropdown
}
}
return result;
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [models] example - animation timing");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new AnimationTiming();
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,199 @@
/*******************************************************************************************
*
* raylib [models] example - basic voxel
*
* Example complexity rating: [★★☆☆] 2/4
*
* Example originally created with raylib 5.5, last time updated with raylib 5.5
*
* Example contributed by Tim Little (@timlittle) 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 Tim Little (@timlittle)
*
********************************************************************************************/
namespace Examples.Models;
public partial class BasicVoxel : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
private const int WorldSize = 8; // Size of our voxel world (8x8x8 cubes)
public string Name => "Models / Basic Voxel";
public string Title => "raylib [models] example - basic voxel";
public bool CursorDisabled => true;
private Camera3D camera;
private Model cubeModel;
private bool[,,] voxels;
public unsafe void Init()
{
// Define the camera to look into our 3d world (first person)
camera = new();
camera.Position = new Vector3(-2.0f, 0.0f, -2.0f); // Camera position at ground level
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
camera.FovY = 45.0f; // Camera field-of-view Y
camera.Projection = CameraProjection.Perspective; // Camera projection type
// Create a cube model
var cubeMesh = GenMeshCube(1.0f, 1.0f, 1.0f); // Create a unit cube mesh
cubeModel = LoadModelFromMesh(cubeMesh); // Convert mesh to a model
cubeModel.Materials[0].Maps[(int)MaterialMapIndex.Diffuse].Color = Color.Beige;
// Initialize voxel world - fill with voxels
voxels = new bool[WorldSize, WorldSize, WorldSize];
for (var x = 0; x < WorldSize; x++)
{
for (var y = 0; y < WorldSize; y++)
{
for (var z = 0; z < WorldSize; z++)
{
voxels[x, y, z] = true;
}
}
}
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.FirstPerson);
// Handle voxel removal with mouse click
// This method is quite inefficient. Ray marching through the voxel grid using DDA would be faster, but more complex.
if (IsMouseButtonPressed(MouseButton.Left))
{
// Cast a ray from the screen center (where crosshair would be)
Vector2 screenCenter = new(GetScreenWidth() / 2.0f, GetScreenHeight() / 2.0f);
var ray = GetScreenToWorldRay(screenCenter, camera);
// Check ray collision with all voxels
var closestDistance = 99999.0f;
Vector3 closestVoxelPosition = new(-1, -1, -1);
var voxelFound = false;
for (var x = 0; x < WorldSize; x++)
{
for (var y = 0; y < WorldSize; y++)
{
for (var z = 0; z < WorldSize; z++)
{
if (!voxels[x, y, z])
{
continue; // Skip empty voxels
}
// Build a bounding box for this voxel
Vector3 position = new(x, y, z);
BoundingBox box = new(
new Vector3(position.X - 0.5f, position.Y - 0.5f, position.Z - 0.5f),
new Vector3(position.X + 0.5f, position.Y + 0.5f, position.Z + 0.5f)
);
// Check ray-box collision
var collision = GetRayCollisionBox(ray, box);
if (collision.Hit && (collision.Distance < closestDistance))
{
closestDistance = collision.Distance;
closestVoxelPosition = new Vector3(x, y, z);
voxelFound = true;
}
}
}
}
// Remove the closest voxel if one was hit
if (voxelFound)
{
voxels[(int)closestVoxelPosition.X,
(int)closestVoxelPosition.Y,
(int)closestVoxelPosition.Z] = false;
}
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawGrid(10, 1.0f);
// Draw all voxels
for (var x = 0; x < WorldSize; x++)
{
for (var y = 0; y < WorldSize; y++)
{
for (var z = 0; z < WorldSize; z++)
{
if (!voxels[x, y, z])
{
continue;
}
Vector3 position = new(x, y, z);
DrawModel(cubeModel, position, 1.0f, Color.Beige);
DrawCubeWires(position, 1.0f, 1.0f, 1.0f, Color.Black);
}
}
}
EndMode3D();
// Draw reference point for raycasting to delete blocks
DrawCircle(GetScreenWidth() / 2, GetScreenHeight() / 2, 4, Color.Red);
DrawText("Left-click a voxel to remove it!", 10, 10, 20, Color.DarkGray);
DrawText("WASD to move, mouse to look around", 10, 35, 10, Color.Gray);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadModel(cubeModel);
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [models] example - basic voxel");
DisableCursor(); // Lock mouse to window center
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
var game = new BasicVoxel();
game.Init();
// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
{
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow();
//--------------------------------------------------------------------------------------
return 0;
}
}

View file

@ -1,134 +1,145 @@
/*******************************************************************************************
*
* raylib [models] example - Drawing billboards
* raylib [models] example - billboard rendering
*
* This example has been created using raylib 1.3 (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 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) 2015-2025 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
using System.Numerics;
using static Raylib_cs.Raylib;
namespace Examples.Models;
public class BillboardDemo
public partial class BillboardDemo : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Models / Billboard Demo";
public string Title => "raylib [models] example - billboard rendering";
private Camera3D camera;
private Texture2D bill;
private Vector3 billPositionStatic;
private Vector3 billPositionRotating;
private Rectangle source;
private Vector3 billUp;
private Vector2 size;
private Vector2 origin;
private float distanceStatic;
private float distanceRotating;
private float rotation;
public void Init()
{
// Define the camera to look into our 3d world
camera = new();
camera.Position = new Vector3(5.0f, 4.0f, 5.0f); // Camera position
camera.Target = new Vector3(0.0f, 2.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
bill = LoadTexture("resources/billboard.png"); // Our billboard texture
billPositionStatic = new(0.0f, 2.0f, 0.0f); // Position of static billboard
billPositionRotating = new(1.0f, 2.0f, 1.0f); // Position of rotating billboard
// Entire billboard texture, source is used to take a segment from a larger texture
source = new(0.0f, 0.0f, (float)bill.Width, (float)bill.Height);
// NOTE: Billboard locked on axis-Y
billUp = new(0.0f, 1.0f, 0.0f);
// Set the height of the rotating billboard to 1.0 with the aspect ratio fixed
size = new(source.Width / source.Height, 1.0f);
// Rotate around origin
// Here we choose to rotate around the image center
origin = size * 0.5f;
// Distance is needed for the correct billboard draw order
// Larger distance (further away from the camera) should be drawn prior to smaller distance
distanceStatic = 0.0f;
distanceRotating = 0.0f;
rotation = 0.0f;
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Orbital);
rotation += 0.4f;
distanceStatic = Vector3.Distance(camera.Position, billPositionStatic);
distanceRotating = Vector3.Distance(camera.Position, billPositionRotating);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawGrid(10, 1.0f); // Draw a grid
// Draw order matters!
if (distanceStatic > distanceRotating)
{
DrawBillboard(camera, bill, billPositionStatic, 2.0f, Color.White);
DrawBillboardPro(camera, bill, source, billPositionRotating, billUp, size, origin, rotation, Color.White);
}
else
{
DrawBillboardPro(camera, bill, source, billPositionRotating, billUp, size, origin, rotation, Color.White);
DrawBillboard(camera, bill, billPositionStatic, 2.0f, Color.White);
}
EndMode3D();
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadTexture(bill); // Unload texture
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [models] example - billboard rendering");
InitWindow(screenWidth, screenHeight, "raylib [models] example - drawing billboards");
// Define the camera to look into our 3d world
Camera3D camera = new();
camera.Position = new Vector3(5.0f, 4.0f, 5.0f);
camera.Target = new Vector3(0.0f, 2.0f, 0.0f);
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
camera.FovY = 45.0f;
camera.Projection = CameraProjection.Perspective;
// Our texture billboard
Texture2D bill = LoadTexture("resources/billboard.png");
// Position of billboard billboard
Vector3 billPositionStatic = new(0.0f, 2.0f, 0.0f);
Vector3 billPositionRotating = new(1.0f, 2.0f, 1.0f);
// Entire billboard texture, source is used to take a segment from a larger texture.
Rectangle source = new(0.0f, 0.0f, (float)bill.Width, (float)bill.Height);
// NOTE: Billboard locked on axis-Y
Vector3 billUp = new(0.0f, 1.0f, 0.0f);
// Rotate around origin
// Here we choose to rotate around the image center
// NOTE: (-1, 1) is the range where origin.X, origin.Y is inside the texture
Vector2 rotateOrigin = Vector2.Zero;
// Distance is needed for the correct billboard draw order
// Larger distance (further away from the camera) should be drawn prior to smaller distance.
float distanceStatic = 0.0f;
float distanceRotating = 0.0f;
float rotation = 0.0f;
SetTargetFPS(60);
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new BillboardDemo();
game.Init();
// Main game loop
while (!WindowShouldClose())
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Orbital);
rotation += 0.4f;
distanceStatic = Vector3.Distance(camera.Position, billPositionStatic);
distanceRotating = Vector3.Distance(camera.Position, billPositionRotating);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawGrid(10, 1.0f);
// Draw order matters!
if (distanceStatic > distanceRotating)
{
DrawBillboard(camera, bill, billPositionStatic, 2.0f, Color.White);
DrawBillboardPro(
camera,
bill,
source,
billPositionRotating,
billUp,
new Vector2(1.0f, 1.0f),
rotateOrigin,
rotation,
Color.White
);
}
else
{
DrawBillboardPro(
camera,
bill,
source,
billPositionRotating,
billUp,
new Vector2(1.0f, 1.0f),
rotateOrigin,
rotation,
Color.White
);
DrawBillboard(camera, bill, billPositionStatic, 2.0f, Color.White);
}
EndMode3D();
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadTexture(bill);
CloseWindow();
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}
}

View file

@ -0,0 +1,244 @@
/*******************************************************************************************
*
* raylib [models] example - bone socket
*
* Example complexity rating: [★★★★] 4/4
*
* Example originally created with raylib 4.5, last time updated with raylib 4.5
*
* Example contributed by iP (@ipzaur) 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) 2024-2025 iP (@ipzaur)
*
********************************************************************************************/
using static Raylib_cs.Raymath;
namespace Examples.Models;
public partial class BoneSocket : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
private const int BoneSockets = 3;
private const int BoneSocketHat = 0;
private const int BoneSocketHandR = 1;
private const int BoneSocketHandL = 2;
public string Name => "Models / Bone Socket";
public string Title => "raylib [models] example - bone socket";
public bool CursorDisabled => true;
private Camera3D camera;
private Model characterModel;
private Model[] equipModel;
private bool[] showEquip;
private int animsCount;
private int animIndex;
private int animCurrentFrame;
private unsafe ModelAnimation* modelAnimations;
private int[] boneSocketIndex;
private Vector3 position;
private int angle;
public unsafe void Init()
{
// Define the camera to look into our 3d world
camera = new();
camera.Position = new Vector3(5.0f, 5.0f, 5.0f); // Camera position
camera.Target = new Vector3(0.0f, 2.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
// Load gltf model
characterModel = LoadModel("resources/models/gltf/greenman.glb"); // Load character model
equipModel = new Model[BoneSockets]
{
LoadModel("resources/models/gltf/greenman_hat.glb"), // Index for the hat model is the same as BONE_SOCKET_HAT
LoadModel("resources/models/gltf/greenman_sword.glb"), // Index for the sword model is the same as BONE_SOCKET_HAND_R
LoadModel("resources/models/gltf/greenman_shield.glb") // Index for the shield model is the same as BONE_SOCKET_HAND_L
};
showEquip = new bool[3] { true, true, true }; // Toggle on/off equip
// Load gltf model animations
animsCount = 0;
animIndex = 0;
animCurrentFrame = 0;
modelAnimations = LoadModelAnimations("resources/models/gltf/greenman.glb", ref animsCount);
// Indices of bones for sockets
boneSocketIndex = new int[BoneSockets] { -1, -1, -1 };
// Search bones for sockets
for (var i = 0; i < characterModel.Skeleton.BoneCount; i++)
{
var boneName = characterModel.Skeleton.Bones[i].NameToString();
if (boneName == "socket_hat")
{
boneSocketIndex[BoneSocketHat] = i;
continue;
}
if (boneName == "socket_hand_R")
{
boneSocketIndex[BoneSocketHandR] = i;
continue;
}
if (boneName == "socket_hand_L")
{
boneSocketIndex[BoneSocketHandL] = i;
continue;
}
}
position = new Vector3(0.0f, 0.0f, 0.0f); // Set model position
angle = 0; // Set angle for rotate character
}
public unsafe void Update()
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.ThirdPerson);
// Rotate character
if (IsKeyDown(KeyboardKey.F))
{
angle = (angle + 1) % 360;
}
else if (IsKeyDown(KeyboardKey.H))
{
angle = (360 + angle - 1) % 360;
}
// Select current animation
if (IsKeyPressed(KeyboardKey.T))
{
animIndex = (animIndex + 1) % animsCount;
}
else if (IsKeyPressed(KeyboardKey.G))
{
animIndex = (animIndex + animsCount - 1) % animsCount;
}
// Toggle shown of equip
if (IsKeyPressed(KeyboardKey.One))
{
showEquip[BoneSocketHat] = !showEquip[BoneSocketHat];
}
if (IsKeyPressed(KeyboardKey.Two))
{
showEquip[BoneSocketHandR] = !showEquip[BoneSocketHandR];
}
if (IsKeyPressed(KeyboardKey.Three))
{
showEquip[BoneSocketHandL] = !showEquip[BoneSocketHandL];
}
// Update model animation
var anim = modelAnimations[animIndex];
animCurrentFrame = (animCurrentFrame + 1) % anim.KeyFrameCount;
UpdateModelAnimation(characterModel, anim, animCurrentFrame);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
// Draw character
var characterRotate = QuaternionFromAxisAngle(new Vector3(0.0f, 1.0f, 0.0f), angle * DEG2RAD);
characterModel.Transform = MatrixMultiply(QuaternionToMatrix(characterRotate), MatrixTranslate(position.X, position.Y, position.Z));
UpdateModelAnimation(characterModel, anim, animCurrentFrame);
DrawMesh(characterModel.Meshes[0], characterModel.Materials[1], characterModel.Transform);
// Draw equipments (hat, sword, shield)
for (var i = 0; i < BoneSockets; i++)
{
if (!showEquip[i])
{
continue;
}
var transform = &anim.KeyframePoses[animCurrentFrame][boneSocketIndex[i]];
var inRotation = characterModel.Skeleton.BindPose[boneSocketIndex[i]].Rotation;
var outRotation = transform->Rotation;
// Calculate socket rotation (angle between bone in initial pose and same bone in current animation frame)
var rotate = QuaternionMultiply(outRotation, QuaternionInvert(inRotation));
var matrixTransform = QuaternionToMatrix(rotate);
// Translate socket to its position in the current animation
matrixTransform = MatrixMultiply(matrixTransform, MatrixTranslate(transform->Translation.X, transform->Translation.Y, transform->Translation.Z));
// Transform the socket using the transform of the character (angle and translate)
matrixTransform = MatrixMultiply(matrixTransform, characterModel.Transform);
// Draw mesh at socket position with socket angle rotation
DrawMesh(equipModel[i].Meshes[0], equipModel[i].Materials[1], matrixTransform);
}
DrawGrid(10, 1.0f);
EndMode3D();
DrawText("Use the T/G to switch animation", 10, 10, 20, Color.Gray);
DrawText("Use the F/H to rotate character left/right", 10, 35, 20, Color.Gray);
DrawText("Use the 1,2,3 to toggle shown of hat, sword and shield", 10, 60, 20, Color.Gray);
EndDrawing();
//----------------------------------------------------------------------------------
}
public unsafe void Unload()
{
UnloadModelAnimations(modelAnimations, animsCount);
UnloadModel(characterModel); // Unload character model and meshes/material
// Unload equipment model and meshes/material
for (var i = 0; i < BoneSockets; i++)
{
UnloadModel(equipModel[i]);
}
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [models] example - bone socket");
DisableCursor(); // Limit cursor to relative movement inside the window
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new BoneSocket();
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,142 +1,179 @@
/*******************************************************************************************
*
* raylib [models] example - Detect basic 3d collisions (box vs sphere vs box)
* raylib [models] example - box collisions
*
* This example has been created using raylib 1.3 (www.raylib.com)
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
* Example complexity rating: [★☆☆☆] 1/4
*
* Copyright (c) 2015 Ramon Santamaria (@raysan5)
* Example originally created with raylib 1.3, 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) 2015-2025 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
using System.Numerics;
using static Raylib_cs.Raylib;
namespace Examples.Models;
public class BoxCollisions
public partial class BoxCollisions : IExample
{
public static int Main()
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Models / Box Collisions";
public string Title => "raylib [models] example - box collisions";
private Camera3D camera;
private Vector3 playerPosition;
private Vector3 playerSize;
private Color playerColor;
private Vector3 enemyBoxPos;
private Vector3 enemyBoxSize;
private Vector3 enemySpherePos;
private float enemySphereSize;
private bool collision;
public void Init()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [models] example - box collisions");
// Define the camera to look into our 3d world
Camera3D camera = new();
camera = new();
camera.Position = new Vector3(0.0f, 10.0f, 10.0f);
camera.Target = new Vector3(0.0f, 0.0f, 0.0f);
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
camera.FovY = 45.0f;
camera.Projection = CameraProjection.Perspective;
Vector3 playerPosition = new(0.0f, 1.0f, 2.0f);
Vector3 playerSize = new(1.0f, 2.0f, 1.0f);
Color playerColor = Color.Green;
playerPosition = new(0.0f, 1.0f, 2.0f);
playerSize = new(1.0f, 2.0f, 1.0f);
playerColor = Color.Green;
Vector3 enemyBoxPos = new(-4.0f, 1.0f, 0.0f);
Vector3 enemyBoxSize = new(2.0f, 2.0f, 2.0f);
enemyBoxPos = new(-4.0f, 1.0f, 0.0f);
enemyBoxSize = new(2.0f, 2.0f, 2.0f);
Vector3 enemySpherePos = new(4.0f, 0.0f, 0.0f);
float enemySphereSize = 1.5f;
enemySpherePos = new(4.0f, 0.0f, 0.0f);
enemySphereSize = 1.5f;
bool collision = false;
collision = false;
}
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
public void Update()
{
// Update
//----------------------------------------------------------------------------------
// Move player
if (IsKeyDown(KeyboardKey.Right))
{
playerPosition.X += 0.2f;
}
else if (IsKeyDown(KeyboardKey.Left))
{
playerPosition.X -= 0.2f;
}
else if (IsKeyDown(KeyboardKey.Down))
{
playerPosition.Z += 0.2f;
}
else if (IsKeyDown(KeyboardKey.Up))
{
playerPosition.Z -= 0.2f;
}
collision = false;
// Check collisions player vs enemy-box
BoundingBox box1 = new(
playerPosition - (playerSize / 2),
playerPosition + (playerSize / 2)
);
BoundingBox box2 = new(
enemyBoxPos - (enemyBoxSize / 2),
enemyBoxPos + (enemyBoxSize / 2)
);
if (CheckCollisionBoxes(box1, box2))
{
collision = true;
}
// Check collisions player vs enemy-sphere
if (CheckCollisionBoxSphere(box1, enemySpherePos, enemySphereSize))
{
collision = true;
}
if (collision)
{
playerColor = Color.Red;
}
else
{
playerColor = Color.Green;
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
// Draw enemy-box
DrawCube(enemyBoxPos, enemyBoxSize.X, enemyBoxSize.Y, enemyBoxSize.Z, Color.Gray);
DrawCubeWires(enemyBoxPos, enemyBoxSize.X, enemyBoxSize.Y, enemyBoxSize.Z, Color.DarkGray);
// Draw enemy-sphere
DrawSphere(enemySpherePos, enemySphereSize, Color.Gray);
DrawSphereWires(enemySpherePos, enemySphereSize, 16, 16, Color.DarkGray);
// Draw player
DrawCubeV(playerPosition, playerSize, playerColor);
DrawGrid(10, 1.0f); // Draw a grid
EndMode3D();
DrawText("Move player with arrow keys to collide", 220, 40, 20, Color.Gray);
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [models] example - box collisions");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new BoxCollisions();
game.Init();
// Main game loop
while (!WindowShouldClose())
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
// Move player
if (IsKeyDown(KeyboardKey.Right))
{
playerPosition.X += 0.2f;
}
else if (IsKeyDown(KeyboardKey.Left))
{
playerPosition.X -= 0.2f;
}
else if (IsKeyDown(KeyboardKey.Down))
{
playerPosition.Z += 0.2f;
}
else if (IsKeyDown(KeyboardKey.Up))
{
playerPosition.Z -= 0.2f;
}
collision = false;
// Check collisions player vs enemy-box
BoundingBox box1 = new(
playerPosition - (playerSize / 2),
playerPosition + (playerSize / 2)
);
BoundingBox box2 = new(
enemyBoxPos - (enemyBoxSize / 2),
enemyBoxPos + (enemyBoxSize / 2)
);
if (CheckCollisionBoxes(box1, box2))
{
collision = true;
}
// Check collisions player vs enemy-sphere
if (CheckCollisionBoxSphere(box1, enemySpherePos, enemySphereSize))
{
collision = true;
}
if (collision)
{
playerColor = Color.Red;
}
else
{
playerColor = Color.Green;
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
// Draw enemy-box
DrawCube(enemyBoxPos, enemyBoxSize.X, enemyBoxSize.Y, enemyBoxSize.Z, Color.Gray);
DrawCubeWires(enemyBoxPos, enemyBoxSize.X, enemyBoxSize.Y, enemyBoxSize.Z, Color.DarkGray);
// Draw enemy-sphere
DrawSphere(enemySpherePos, enemySphereSize, Color.Gray);
DrawSphereWires(enemySpherePos, enemySphereSize, 16, 16, Color.DarkGray);
// Draw player
DrawCubeV(playerPosition, playerSize, playerColor);
DrawGrid(10, 1.0f);
EndMode3D();
DrawText("Move player with cursors to collide", 220, 40, 20, Color.Gray);
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow();
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;

View file

@ -1,104 +1,142 @@
/*******************************************************************************************
*
* raylib [models] example - Cubicmap loading and drawing
* raylib [models] example - cubicmap rendering
*
* This example has been created using raylib 1.8 (www.raylib.com)
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
* Example complexity rating: [★★☆☆] 2/4
*
* Copyright (c) 2015 Ramon Santamaria (@raysan5)
* Example originally created with raylib 1.8, 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) 2015-2025 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
using System.Numerics;
using static Raylib_cs.Raylib;
namespace Examples.Models;
public class CubicmapDemo
public partial class CubicmapDemo : IExample
{
public static int Main()
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Models / Cubicmap Demo";
public string Title => "raylib [models] example - cubicmap rendering";
private Camera3D camera;
private Texture2D cubicmap;
private Texture2D texture;
private Model model;
private Vector3 mapPosition;
private bool pause;
public void Init()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [models] example - cubesmap loading and drawing");
// Define the camera to look into our 3d world
Camera3D camera = new();
camera.Position = new Vector3(16.0f, 14.0f, 16.0f);
camera.Target = new Vector3(0.0f, 0.0f, 0.0f);
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
camera.FovY = 45.0f;
camera.Projection = CameraProjection.Perspective;
camera = new();
camera.Position = new Vector3(16.0f, 14.0f, 16.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
Image image = LoadImage("resources/cubicmap.png");
Texture2D cubicmap = LoadTextureFromImage(image);
var image = LoadImage("resources/cubicmap.png"); // Load cubicmap image (RAM)
cubicmap = LoadTextureFromImage(image); // Convert image to texture to display (VRAM)
Mesh mesh = GenMeshCubicmap(image, new Vector3(1.0f, 1.0f, 1.0f));
Model model = LoadModelFromMesh(mesh);
var mesh = GenMeshCubicmap(image, new Vector3(1.0f, 1.0f, 1.0f));
model = LoadModelFromMesh(mesh);
// NOTE: By default each cube is mapped to one part of texture atlas
Texture2D texture = LoadTexture("resources/cubicmap_atlas.png");
texture = LoadTexture("resources/cubicmap_atlas.png"); // Load map texture
// Set map diffuse texture
Raylib.SetMaterialTexture(ref model, 0, MaterialMapIndex.Albedo, ref texture);
Vector3 mapPosition = new(-16.0f, 0.0f, -8.0f);
UnloadImage(image);
mapPosition = new(-16.0f, 0.0f, -8.0f); // Set model position
SetTargetFPS(60);
UnloadImage(image); // Unload cubesmap image from RAM, already uploaded to VRAM
pause = false; // Pause camera orbital rotation (and zoom)
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
if (IsKeyPressed(KeyboardKey.P))
{
pause = !pause;
}
if (!pause)
{
UpdateCamera(ref camera, CameraMode.Orbital);
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawModel(model, mapPosition, 1.0f, Color.White);
EndMode3D();
Vector2 position = new(screenWidth - cubicmap.Width * 4 - 20, 20);
DrawTextureEx(cubicmap, position, 0.0f, 4.0f, Color.White);
DrawRectangleLines(
screenWidth - cubicmap.Width * 4 - 20,
20,
cubicmap.Width * 4,
cubicmap.Height * 4,
Color.Green
);
DrawText("cubicmap image used to", 658, 90, 10, Color.Gray);
DrawText("generate map 3d model", 658, 104, 10, Color.Gray);
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadTexture(cubicmap); // Unload cubicmap texture
UnloadTexture(texture); // Unload map texture
UnloadModel(model); // Unload map model
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [models] example - cubicmap rendering");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new CubicmapDemo();
game.Init();
// Main game loop
while (!WindowShouldClose())
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Orbital);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawModel(model, mapPosition, 1.0f, Color.White);
EndMode3D();
Vector2 position = new(screenWidth - cubicmap.Width * 4 - 20, 20);
DrawTextureEx(cubicmap, position, 0.0f, 4.0f, Color.White);
DrawRectangleLines(
screenWidth - cubicmap.Width * 4 - 20,
20,
cubicmap.Width * 4,
cubicmap.Height * 4,
Color.Green
);
DrawText("cubicmap image used to", 658, 90, 10, Color.Gray);
DrawText("generate map 3d model", 658, 104, 10, Color.Gray);
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadTexture(cubicmap);
UnloadTexture(texture);
UnloadModel(model);
CloseWindow();
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}
}

602
Examples/Models/Decals.cs Normal file
View file

@ -0,0 +1,602 @@
/*******************************************************************************************
*
* raylib [models] example - decals
*
* Example complexity rating: [★★★★] 4/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)
* Based on previous work by @mrdoob
*
* 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) and Ramon Santamaria (@raysan5)
*
********************************************************************************************/
using static Raylib_cs.Raymath;
namespace Examples.Models;
public partial class Decals : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
private const int MaxDecals = 256;
public string Name => "Models / Decals";
public string Title => "raylib [models] example - decals";
public ConfigFlags ConfigFlags => ConfigFlags.Msaa4xHint;
private Camera3D camera;
private Model model;
private Texture2D modelTexture;
private BoundingBox modelBBox;
private float decalSize;
private float decalOffset;
private Model placementCube;
private Material decalMaterial;
private Texture2D decalTexture;
private bool showModel;
private readonly List<Model> decalModels = new();
public unsafe void Init()
{
// Define the camera to look into our 3d world
camera = new();
camera.Position = new Vector3(5.0f, 5.0f, 5.0f); // Camera position
camera.Target = new Vector3(0.0f, 1.0f, 0.0f); // Camera looking at point
camera.Up = new Vector3(0.0f, 1.6f, 0.0f); // Camera up vector (rotation towards target)
camera.FovY = 45.0f; // Camera field-of-view Y
camera.Projection = CameraProjection.Perspective; // Camera projection type
// Load character model
model = LoadModel("resources/models/obj/character.obj");
// Apply character skin
modelTexture = LoadTexture("resources/models/obj/character_diffuse.png");
SetTextureFilter(modelTexture, TextureFilter.Bilinear);
model.Materials[0].Maps[(int)MaterialMapIndex.Diffuse].Texture = modelTexture;
modelBBox = GetMeshBoundingBox(model.Meshes[0]); // Get mesh bounding box
camera.Target = Vector3Lerp(modelBBox.Min, modelBBox.Max, 0.5f);
camera.Position = modelBBox.Max * 1.0f;
camera.Position.X *= 0.1f;
var modelSize = MathF.Min(
MathF.Min(MathF.Abs(modelBBox.Max.X - modelBBox.Min.X), MathF.Abs(modelBBox.Max.Y - modelBBox.Min.Y)),
MathF.Abs(modelBBox.Max.Z - modelBBox.Min.Z));
camera.Position = new Vector3(0.0f, modelBBox.Max.Y * 1.2f, modelSize * 3.0f);
decalSize = modelSize * 0.25f;
decalOffset = 0.01f;
placementCube = LoadModelFromMesh(GenMeshCube(decalSize, decalSize, decalSize));
placementCube.Materials[0].Maps[0].Color = Color.Lime;
decalMaterial = LoadMaterialDefault();
decalMaterial.Maps[0].Color = Color.Yellow;
var decalImage = LoadImage("resources/raylib_logo.png");
ImageResizeNN(ref decalImage, decalImage.Width / 4, decalImage.Height / 4);
decalTexture = LoadTextureFromImage(decalImage);
UnloadImage(decalImage);
SetTextureFilter(decalTexture, TextureFilter.Bilinear);
decalMaterial.Maps[(int)MaterialMapIndex.Diffuse].Texture = decalTexture;
decalMaterial.Maps[(int)MaterialMapIndex.Diffuse].Color = Color.RayWhite;
showModel = true;
decalModels.Clear();
}
public unsafe void Update()
{
// Update
//----------------------------------------------------------------------------------
if (IsMouseButtonDown(MouseButton.Right))
{
UpdateCamera(ref camera, CameraMode.ThirdPerson);
}
// Display information about closest hit
RayCollision collision = new();
collision.Distance = float.MaxValue;
collision.Hit = false;
// Get mouse ray
var ray = GetScreenToWorldRay(GetMousePosition(), camera);
// Check ray collision against bounding box first, before trying the full ray-mesh test
var boxHitInfo = GetRayCollisionBox(ray, modelBBox);
RayCollision meshHitInfo = new();
if (boxHitInfo.Hit && (decalModels.Count < MaxDecals))
{
// Check ray collision against model meshes
for (var m = 0; m < model.MeshCount; m++)
{
// NOTE: We consider the model.transform for the collision check but
// it can be checked against any transform Matrix, used when checking against same
// model drawn multiple times with multiple transforms
meshHitInfo = GetRayCollisionMesh(ray, model.Meshes[m], model.Transform);
if (meshHitInfo.Hit)
{
// Save the closest hit mesh
if (!collision.Hit || (collision.Distance > meshHitInfo.Distance))
{
collision = meshHitInfo;
}
}
}
if (meshHitInfo.Hit)
{
collision = meshHitInfo;
}
}
// Add decal to mesh on hit point
if (collision.Hit && IsMouseButtonPressed(MouseButton.Left) && (decalModels.Count < MaxDecals))
{
// Create the transformation to project the decal
var origin = collision.Point + (collision.Normal * 1.0f);
var splat = MatrixLookAt(collision.Point, origin, new Vector3(0.0f, 1.0f, 0.0f));
// Spin the placement around a bit
splat = MatrixMultiply(splat, MatrixRotateZ(DEG2RAD * GetRandomValue(-180, 180)));
var decalMesh = GenMeshDecal(model, splat, decalSize, decalOffset);
if (decalMesh.VertexCount > 0)
{
var decalModel = LoadModelFromMesh(decalMesh);
decalModel.Materials[0].Maps[0] = decalMaterial.Maps[0];
decalModels.Add(decalModel);
}
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
// Draw the model at the origin and default scale
if (showModel)
{
DrawModel(model, new Vector3(0.0f, 0.0f, 0.0f), 1.0f, Color.White);
}
// Draw the decal models
for (var i = 0; i < decalModels.Count; i++)
{
DrawModel(decalModels[i], Vector3.Zero, 1.0f, Color.White);
}
// If we hit the mesh, draw the box for the decal
if (collision.Hit)
{
var origin = collision.Point + (collision.Normal * 1.0f);
var splat = MatrixLookAt(collision.Point, origin, new Vector3(0, 1, 0));
placementCube.Transform = MatrixInvert(splat);
DrawModel(placementCube, Vector3.Zero, 1.0f, Fade(Color.White, 0.5f));
}
DrawGrid(10, 10.0f);
EndMode3D();
float yPos = 10;
var x0 = GetScreenWidth() - 300.0f;
var x1 = x0 + 100;
var x2 = x1 + 100;
DrawText("Vertices", (int)x1, (int)yPos, 10, Color.Lime);
DrawText("Triangles", (int)x2, (int)yPos, 10, Color.Lime);
yPos += 15;
var vertexCount = 0;
var triangleCount = 0;
for (var i = 0; i < model.MeshCount; i++)
{
vertexCount += model.Meshes[i].VertexCount;
triangleCount += model.Meshes[i].TriangleCount;
}
DrawText("Main model", (int)x0, (int)yPos, 10, Color.Lime);
DrawText($"{vertexCount}", (int)x1, (int)yPos, 10, Color.Lime);
DrawText($"{triangleCount}", (int)x2, (int)yPos, 10, Color.Lime);
yPos += 15;
for (var i = 0; i < decalModels.Count; i++)
{
if (i == 20)
{
DrawText("...", (int)x0, (int)yPos, 10, Color.Lime);
yPos += 15;
}
if (i < 20)
{
DrawText($"Decal #{i + 1}", (int)x0, (int)yPos, 10, Color.Lime);
DrawText($"{decalModels[i].Meshes[0].VertexCount}", (int)x1, (int)yPos, 10, Color.Lime);
DrawText($"{decalModels[i].Meshes[0].TriangleCount}", (int)x2, (int)yPos, 10, Color.Lime);
yPos += 15;
}
vertexCount += decalModels[i].Meshes[0].VertexCount;
triangleCount += decalModels[i].Meshes[0].TriangleCount;
}
DrawText("TOTAL", (int)x0, (int)yPos, 10, Color.Lime);
DrawText($"{vertexCount}", (int)x1, (int)yPos, 10, Color.Lime);
DrawText($"{triangleCount}", (int)x2, (int)yPos, 10, Color.Lime);
yPos += 15;
DrawText("Hold RMB to move camera", 10, 430, 10, Color.Gray);
DrawText("(c) Character model and texture from kenney.nl", screenWidth - 260, screenHeight - 20, 10, Color.Gray);
// UI elements
if (GuiButton(new Rectangle(10, screenHeight - 1000.0f, 100, 60), showModel ? "Hide Model" : "Show Model"))
{
showModel = !showModel;
}
if (GuiButton(new Rectangle(10 + 110, screenHeight - 100.0f, 100, 60), "Clear Decals"))
{
// Clear decals, unload all decal models
for (var i = 0; i < decalModels.Count; i++)
{
UnloadModel(decalModels[i]);
}
decalModels.Clear();
}
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadModel(model);
UnloadTexture(modelTexture);
// Unload decal models
for (var i = 0; i < decalModels.Count; i++)
{
UnloadModel(decalModels[i]);
}
decalModels.Clear();
UnloadTexture(decalTexture);
UnloadModel(placementCube);
}
// Clip segment
private static Vector3 ClipSegment(Vector3 v0, Vector3 v1, Vector3 p, float s)
{
var d0 = Vector3.Dot(v0, p) - s;
var d1 = Vector3.Dot(v1, p) - s;
var s0 = d0 / (d0 - d1);
return Vector3.Lerp(v0, v1, s0);
}
// Generate mesh decals for provided model
private static unsafe Mesh GenMeshDecal(Model target, Matrix4x4 projection, float decalSize, float decalOffset)
{
// We're going to use these to build up our decal meshes
var meshBuilders = new List<Vector3>[2] { new(), new() };
// We're going to need the inverse matrix
var invProj = MatrixInvert(projection);
// We'll be flip-flopping between the two mesh builders
// Reading from one and writing to the other, then swapping
var mbIndex = 0;
// First pass, just get any triangle inside the bounding box (for each mesh of the model)
for (var meshIndex = 0; meshIndex < target.MeshCount; meshIndex++)
{
var mesh = target.Meshes[meshIndex];
for (var tri = 0; tri < mesh.TriangleCount; tri++)
{
var vertices = new Vector3[3];
// The way we calculate the vertices of the mesh triangle
// depend on whether the mesh vertices are indexed or not
if (mesh.Indices == null)
{
for (var v = 0; v < 3; v++)
{
vertices[v] = new Vector3(
mesh.Vertices[3 * 3 * tri + 3 * v + 0],
mesh.Vertices[3 * 3 * tri + 3 * v + 1],
mesh.Vertices[3 * 3 * tri + 3 * v + 2]
);
}
}
else
{
for (var v = 0; v < 3; v++)
{
vertices[v] = new Vector3(
mesh.Vertices[3 * mesh.Indices[3 * tri + 0] + v],
mesh.Vertices[3 * mesh.Indices[3 * tri + 1] + v],
mesh.Vertices[3 * mesh.Indices[3 * tri + 2] + v]
);
}
}
// Transform all 3 vertices of the triangle
// and check if they are inside our decal box
var insideCount = 0;
for (var i = 0; i < 3; i++)
{
// To projection space
var v = Vector3Transform(vertices[i], projection);
if ((MathF.Abs(v.X) < decalSize) || (MathF.Abs(v.Y) <= decalSize) || (MathF.Abs(v.Z) <= decalSize))
{
insideCount++;
}
// We need to keep the transformed vertex
vertices[i] = v;
}
// If any of them are inside, we add the triangle - we'll clip it later
if (insideCount > 0)
{
meshBuilders[mbIndex].Add(vertices[0]);
meshBuilders[mbIndex].Add(vertices[1]);
meshBuilders[mbIndex].Add(vertices[2]);
}
}
}
// Clipping time! We need to clip against all 6 directions
Vector3[] planes =
{
new(1, 0, 0),
new(-1, 0, 0),
new(0, 1, 0),
new(0, -1, 0),
new(0, 0, 1),
new(0, 0, -1)
};
for (var face = 0; face < 6; face++)
{
// Swap current model builder (so we read from the one we just wrote to)
mbIndex = 1 - mbIndex;
var inMesh = meshBuilders[1 - mbIndex];
var outMesh = meshBuilders[mbIndex];
// Reset write builder
outMesh.Clear();
var s = 0.5f * decalSize;
for (var i = 0; i < inMesh.Count; i += 3)
{
Vector3 nV1, nV2, nV3, nV4;
var d1 = Vector3.Dot(inMesh[i + 0], planes[face]) - s;
var d2 = Vector3.Dot(inMesh[i + 1], planes[face]) - s;
var d3 = Vector3.Dot(inMesh[i + 2], planes[face]) - s;
var v1Out = d1 > 0;
var v2Out = d2 > 0;
var v3Out = d3 > 0;
// Calculate, how many vertices of the face lie outside of the clipping plane
var total = (v1Out ? 1 : 0) + (v2Out ? 1 : 0) + (v3Out ? 1 : 0);
switch (total)
{
case 0:
// The entire face lies inside of the plane, no clipping needed
outMesh.Add(inMesh[i]);
outMesh.Add(inMesh[i + 1]);
outMesh.Add(inMesh[i + 2]);
break;
case 1:
// One vertex lies outside of the plane, perform clipping
if (v2Out)
{
nV1 = inMesh[i];
nV2 = inMesh[i + 2];
nV3 = ClipSegment(inMesh[i + 1], nV1, planes[face], s);
nV4 = ClipSegment(inMesh[i + 1], nV2, planes[face], s);
outMesh.Add(nV3);
outMesh.Add(nV2);
outMesh.Add(nV1);
outMesh.Add(nV2);
outMesh.Add(nV3);
outMesh.Add(nV4);
}
else
{
if (v1Out)
{
nV1 = inMesh[i + 1];
nV2 = inMesh[i + 2];
nV3 = ClipSegment(inMesh[i], nV1, planes[face], s);
nV4 = ClipSegment(inMesh[i], nV2, planes[face], s);
}
else // v3Out
{
nV1 = inMesh[i];
nV2 = inMesh[i + 1];
nV3 = ClipSegment(inMesh[i + 2], nV1, planes[face], s);
nV4 = ClipSegment(inMesh[i + 2], nV2, planes[face], s);
}
outMesh.Add(nV1);
outMesh.Add(nV2);
outMesh.Add(nV3);
outMesh.Add(nV4);
outMesh.Add(nV3);
outMesh.Add(nV2);
}
break;
case 2:
// Two vertices lies outside of the plane, perform clipping
if (!v1Out)
{
nV1 = inMesh[i];
nV2 = ClipSegment(nV1, inMesh[i + 1], planes[face], s);
nV3 = ClipSegment(nV1, inMesh[i + 2], planes[face], s);
outMesh.Add(nV1);
outMesh.Add(nV2);
outMesh.Add(nV3);
}
if (!v2Out)
{
nV1 = inMesh[i + 1];
nV2 = ClipSegment(nV1, inMesh[i + 2], planes[face], s);
nV3 = ClipSegment(nV1, inMesh[i], planes[face], s);
outMesh.Add(nV1);
outMesh.Add(nV2);
outMesh.Add(nV3);
}
if (!v3Out)
{
nV1 = inMesh[i + 2];
nV2 = ClipSegment(nV1, inMesh[i], planes[face], s);
nV3 = ClipSegment(nV1, inMesh[i + 1], planes[face], s);
outMesh.Add(nV1);
outMesh.Add(nV2);
outMesh.Add(nV3);
}
break;
default: // The entire face lies outside of the plane, so let's discard the corresponding vertices
break;
}
}
}
// Now we just need to re-transform the vertices
var theMesh = meshBuilders[mbIndex];
// Allocate room for UVs
if (theMesh.Count > 0)
{
var uvs = new Vector2[theMesh.Count];
for (var i = 0; i < theMesh.Count; i++)
{
var vert = theMesh[i];
// Calculate the UVs based on the projected coords
// They are clipped to (-decalSize .. decalSize) and we want them (0..1)
uvs[i] = new Vector2(vert.X / decalSize + 0.5f, vert.Y / decalSize + 0.5f);
// Tiny nudge in the normal direction so it renders properly over the mesh
vert.Z -= decalOffset;
// From projection space to world space
theMesh[i] = Vector3Transform(vert, invProj);
}
// Decal model data ready, create the mesh and return it
return BuildMesh(theMesh, uvs);
}
// Return a blank mesh as there's nothing to add
return new Mesh();
}
// Build a Mesh from builder data
private static unsafe Mesh BuildMesh(List<Vector3> builderVertices, Vector2[] uvs)
{
Mesh outMesh = new(builderVertices.Count, builderVertices.Count / 3);
outMesh.AllocVertices();
outMesh.AllocTexCoords();
var vertices = outMesh.VerticesAs<Vector3>();
var texcoords = outMesh.TexCoordsAs<Vector2>();
for (var i = 0; i < builderVertices.Count; i++)
{
vertices[i] = builderVertices[i];
texcoords[i] = uvs[i];
}
UploadMesh(ref outMesh, false);
return outMesh;
}
// Button UI element
private static bool GuiButton(Rectangle rec, string label)
{
var bgColor = Color.Gray;
var pressed = false;
if (CheckCollisionPointRec(GetMousePosition(), rec))
{
bgColor = Color.LightGray;
if (IsMouseButtonPressed(MouseButton.Left))
{
pressed = true;
}
}
DrawRectangleRec(rec, bgColor);
DrawRectangleLinesEx(rec, 2.0f, Color.DarkGray);
var fontSize = 10;
var textWidth = MeasureText(label, fontSize);
DrawText(label, (int)(rec.X + rec.Width * 0.5f - textWidth * 0.5f), (int)(rec.Y + rec.Height * 0.5f - fontSize * 0.5f), fontSize, Color.DarkGray);
return pressed;
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
SetConfigFlags(ConfigFlags.Msaa4xHint);
InitWindow(screenWidth, screenHeight, "raylib [models] example - decals");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new Decals();
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,145 @@
/*******************************************************************************************
*
* raylib [models] example - directional billboard
*
* Example complexity rating: [★★☆☆] 2/4
*
* Example originally created with raylib 6.0, last time updated with raylib 6.0
*
* Example contributed by Robin (@RobinsAviary) 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 Robin (@RobinsAviary)
* Killbot art by patvanmackelberg https://opengameart.org/content/killbot-8-directional under CC0
*
********************************************************************************************/
using static Raylib_cs.Raymath;
namespace Examples.Models;
public partial class DirectionalBillboard : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Models / Directional Billboard";
public string Title => "raylib [models] example - directional billboard";
private Camera3D camera;
private Texture2D skillbot;
private float animTimer;
private uint anim;
public void Init()
{
// Set up the camera
camera = new();
camera.Position = new Vector3(2.0f, 1.0f, 2.0f); // Starting position
camera.Target = new Vector3(0.0f, 0.5f, 0.0f); // Target position
camera.Up = new Vector3(0.0f, 1.0f, 0.0f); // Up vector
camera.FovY = 45.0f; // FOV
camera.Projection = CameraProjection.Perspective; // Projection type (Standard 3D perspective)
// Load billboard texture
skillbot = LoadTexture("resources/skillbot.png");
// Timer to update animation
animTimer = 0.0f;
// Animation frame
anim = 0;
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Orbital);
// Update timer with delta time
animTimer += GetFrameTime();
// Update frame index after a certain amount of time (half a second)
if (animTimer > 0.5f)
{
animTimer = 0.0f;
anim += 1;
}
// Reset frame index to zero on overflow
if (anim >= 4)
{
anim = 0;
}
// Find the current direction frame based on the camera position to the billboard object
var dir = (float)Math.Floor(((Vector2Angle(new Vector2(2.0f, 0.0f), new Vector2(camera.Position.X, camera.Position.Z)) / MathF.PI) * 4.0f) + 0.25f);
// Correct frame index if angle is negative
if (dir < 0.0f)
{
dir = 8.0f - Math.Abs((int)dir);
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawGrid(10, 1.0f);
// Draw billboard pointing straight up to the sky, rotated relative to the camera and offset from the bottom
DrawBillboardPro(camera, skillbot, new Rectangle(0.0f + (anim * 24.0f), 0.0f + (dir * 24.0f), 24.0f, 24.0f),
Vector3.Zero, new Vector3(0.0f, 1.0f, 0.0f), Vector2.One, new Vector2(0.5f, 0.0f), 0, Color.White);
EndMode3D();
// Render various variables for reference
DrawText($"animation: {anim}", 10, 10, 20, Color.DarkGray);
DrawText($"direction frame: {dir:0}", 10, 40, 20, Color.DarkGray);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
// Unload billboard texture
UnloadTexture(skillbot);
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [models] example - directional billboard");
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
var game = new DirectionalBillboard();
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,21 +1,27 @@
using System.Numerics;
using static Raylib_cs.Raylib;
namespace Examples.Models;
public class DynamicMesh
public partial class DynamicMesh : IExample
{
public unsafe static int Main()
private const int screenWidth = 800;
private const int screenHeight = 450;
private const int triangleRows = 48;
private const int vertexRows = triangleRows + 1;
public string Name => "Models / Dynamic Mesh";
public string Title => "raylib [models] example - dynamic mesh";
private Camera3D camera;
private Mesh dynamicMesh;
private Texture2D texture;
private Color[] pixels;
private Material material;
public void Init()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [models] example - dynamic mesh");
// Define the camera to look into our 3d world
Camera3D camera = new();
camera = new();
camera.Position = Vector3.One * 1.5f;
camera.Target = camera.Position + new Vector3(1f, -0.25f, 1f);
camera.Up = Vector3.UnitY;
@ -23,18 +29,14 @@ public class DynamicMesh
camera.Projection = CameraProjection.Perspective;
// Generate a dynamic mesh using utils to allocate/access mesh attribute data
const int triangleRows = 48;
const int vertexRows = triangleRows + 1;
Mesh dynamicMesh = new(vertexRows * vertexRows, triangleRows * triangleRows * 2);
dynamicMesh = new(vertexRows * vertexRows, triangleRows * triangleRows * 2);
dynamicMesh.AllocVertices();
dynamicMesh.AllocTexCoords();
dynamicMesh.AllocIndices();
Span<Vector3> vertices = dynamicMesh.VerticesAs<Vector3>();
Span<Vector2> texcoords = dynamicMesh.TexCoordsAs<Vector2>();
Span<ushort> indices = dynamicMesh.IndicesAs<ushort>();
var indices = dynamicMesh.IndicesAs<ushort>();
for (int z = 0, i = 0; z < triangleRows; z++)
{
for (int x = 0; x < triangleRows; x++, i += 6)
for (var x = 0; x < triangleRows; x++, i += 6)
{
indices[i + 0] = (ushort)(x + (z * vertexRows));
indices[i + 1] = (ushort)(indices[i] + vertexRows);
@ -47,73 +49,96 @@ public class DynamicMesh
UploadMesh(ref dynamicMesh, true);
// Allocate the texture
Image image = GenImageColor(triangleRows, triangleRows, Color.Blank);
Texture2D texture = LoadTextureFromImage(image);
Color[] pixels = new Color[texture.Width * texture.Height];
var image = GenImageColor(triangleRows, triangleRows, Color.Blank);
texture = LoadTextureFromImage(image);
pixels = new Color[texture.Width * texture.Height];
UnloadImage(image);
// Load the material
Material material = LoadMaterialDefault();
material = LoadMaterialDefault();
SetMaterialTexture(ref material, MaterialMapIndex.Diffuse, texture);
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
var time = (float)GetTime();
Random random = new(42);
var vertices = dynamicMesh.VerticesAs<Vector3>();
var texcoords = dynamicMesh.TexCoordsAs<Vector2>();
for (int z = 0, i = 0; z < vertexRows; z++)
{
for (var x = 0; x < vertexRows; x++, i++)
{
var noiseX = SmoothNoise(time + random.Next(10000));
var noiseZ = SmoothNoise(time + random.Next(10000));
vertices[i].X = x + noiseX - .5f;
vertices[i].Y = (noiseX + noiseZ) / 2;
vertices[i].Z = z + noiseZ - .5f;
texcoords[i].X = (x - noiseZ) / triangleRows;
texcoords[i].Y = (z - noiseX) / triangleRows;
}
}
UpdateMeshBuffer<Vector3>(dynamicMesh, Mesh.VboIdIndexVertices, vertices, 0);
UpdateMeshBuffer<Vector2>(dynamicMesh, Mesh.VboIdIndexTexCoords, texcoords, 0);
for (int y = 0, i = 0; y < texture.Height; y++)
{
for (var x = 0; x < texture.Width; x++, i++)
{
pixels[i] = new(32, 178, 170, 255);
pixels[i] = ColorBrightness(pixels[i], (SmoothNoise(time + random.Next(10000)) / 8) - (1 / 16f));
pixels[i] = ColorAlpha(pixels[i], (triangleRows - new Vector2(x, y).Length()) / triangleRows);
}
}
UpdateTexture(texture, pixels);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawMesh(dynamicMesh, material, Matrix4x4.Identity);
EndMode3D();
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadMaterial(material);
// Raylib.UnloadTexture(texture); <- No need to unload the texture. UnloadMaterial(Material) already unloaded it for us
UnloadMesh(dynamicMesh);
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [models] example - dynamic mesh");
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
var game = new DynamicMesh();
game.Init();
// Main game loop
while (!WindowShouldClose())
{
// Update
//----------------------------------------------------------------------------------
float time = (float)GetTime();
Random random = new(42);
for (int z = 0, i = 0; z < vertexRows; z++)
{
for (int x = 0; x < vertexRows; x++, i++)
{
float noiseX = SmoothNoise(time + random.Next(10000));
float noiseZ = SmoothNoise(time + random.Next(10000));
vertices[i].X = x + noiseX - .5f;
vertices[i].Y = (noiseX + noiseZ) / 2;
vertices[i].Z = z + noiseZ - .5f;
texcoords[i].X = (x - noiseZ) / triangleRows;
texcoords[i].Y = (z - noiseX) / triangleRows;
}
}
UpdateMeshBuffer<Vector3>(dynamicMesh, Mesh.VboIdIndexVertices, vertices, 0);
UpdateMeshBuffer<Vector2>(dynamicMesh, Mesh.VboIdIndexTexCoords, texcoords, 0);
for (int y = 0, i = 0; y < texture.Height; y++)
{
for (int x = 0; x < texture.Width; x++, i++)
{
pixels[i] = new(32, 178, 170, 255);
pixels[i] = ColorBrightness(pixels[i], (SmoothNoise(time + random.Next(10000)) / 8) - (1 / 16f));
pixels[i] = ColorAlpha(pixels[i], (triangleRows - new Vector2(x, y).Length()) / triangleRows);
}
}
UpdateTexture(texture, pixels);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawMesh(dynamicMesh, material, Matrix4x4.Identity);
EndMode3D();
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadMaterial(material);
// Raylib.UnloadTexture(texture); <- No need to unload the texture. UnloadMaterial(Material) already unloaded it for us
UnloadMesh(dynamicMesh);
CloseWindow();
//--------------------------------------------------------------------------------------

View file

@ -2,152 +2,176 @@
*
* raylib [models] example - first person maze
*
* 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: [★★☆☆] 2/4
*
* Copyright (c) 2019 Ramon Santamaria (@raysan5)
* Example originally created with raylib 2.5, 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) 2019-2025 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
using System.Numerics;
using static Raylib_cs.Raylib;
namespace Examples.Models;
public class FirstPersonMaze
public unsafe partial class FirstPersonMaze : IExample
{
public unsafe static int Main()
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Models / First Person Maze";
public string Title => "raylib [models] example - first person maze";
public bool CursorDisabled => true;
private Camera3D camera;
private Texture2D cubicmap;
private Texture2D texture;
private Model model;
private Color* mapPixels;
private Vector3 mapPosition;
public void Init()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [models] example - first person maze");
// Define the camera to look into our 3d world
Camera3D camera = new();
camera.Position = new Vector3(0.2f, 0.4f, 0.2f);
camera.Target = new Vector3(0.0f, 0.0f, 0.0f);
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
camera.FovY = 45.0f;
camera.Projection = CameraProjection.Perspective;
camera = new();
camera.Position = new Vector3(0.2f, 0.4f, 0.2f); // Camera position
camera.Target = new Vector3(0.185f, 0.4f, 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
Image imMap = LoadImage("resources/cubicmap.png");
Texture2D cubicmap = LoadTextureFromImage(imMap);
Mesh mesh = GenMeshCubicmap(imMap, new Vector3(1.0f, 1.0f, 1.0f));
Model model = LoadModelFromMesh(mesh);
var imMap = LoadImage("resources/cubicmap.png"); // Load cubicmap image (RAM)
cubicmap = LoadTextureFromImage(imMap); // Convert image to texture to display (VRAM)
var mesh = GenMeshCubicmap(imMap, new Vector3(1.0f, 1.0f, 1.0f));
model = LoadModelFromMesh(mesh);
// NOTE: By default each cube is mapped to one part of texture atlas
Texture2D texture = LoadTexture("resources/cubicmap_atlas.png");
texture = LoadTexture("resources/cubicmap_atlas.png"); // Load map texture
// Set map diffuse texture
Raylib.SetMaterialTexture(ref model, 0, MaterialMapIndex.Albedo, ref texture);
// Get map image data to be used for collision detection
Color* mapPixels = LoadImageColors(imMap);
UnloadImage(imMap);
mapPixels = LoadImageColors(imMap);
UnloadImage(imMap); // Unload image from RAM
Vector3 mapPosition = new(-16.0f, 0.0f, -8.0f);
Vector3 playerPosition = camera.Position;
mapPosition = new(-16.0f, 0.0f, -8.0f); // Set model position
}
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
public void Update()
{
// Update
//----------------------------------------------------------------------------------
var oldCamPos = camera.Position; // Store old camera position
// Main game loop
while (!WindowShouldClose())
UpdateCamera(ref camera, CameraMode.FirstPerson);
// Check player collision (we simplify to 2D collision detection)
Vector2 playerPos = new(camera.Position.X, camera.Position.Z);
var playerRadius = 0.1f; // Collision radius (player is modelled as a cilinder for collision)
var playerCellX = (int)(playerPos.X - mapPosition.X + 0.5f);
var playerCellY = (int)(playerPos.Y - mapPosition.Z + 0.5f);
// Out-of-limits security check
if (playerCellX < 0)
{
// Update
//----------------------------------------------------------------------------------
Vector3 oldCamPos = camera.Position;
playerCellX = 0;
}
else if (playerCellX >= cubicmap.Width)
{
playerCellX = cubicmap.Width - 1;
}
UpdateCamera(ref camera, CameraMode.FirstPerson);
if (playerCellY < 0)
{
playerCellY = 0;
}
else if (playerCellY >= cubicmap.Height)
{
playerCellY = cubicmap.Height - 1;
}
// Check player collision (we simplify to 2D collision detection)
Vector2 playerPos = new(camera.Position.X, camera.Position.Z);
// Collision radius (player is modelled as a cilinder for collision)
float playerRadius = 0.1f;
int playerCellX = (int)(playerPos.X - mapPosition.X + 0.5f);
int playerCellY = (int)(playerPos.Y - mapPosition.Z + 0.5f);
// Out-of-limits security check
if (playerCellX < 0)
// Check map collisions using image data and player position against surrounding cells only
for (var y = playerCellY - 1; y <= playerCellY + 1; y++)
{
// Avoid map accessing out of bounds
if ((y >= 0) && (y < cubicmap.Height))
{
playerCellX = 0;
}
else if (playerCellX >= cubicmap.Width)
{
playerCellX = cubicmap.Width - 1;
}
if (playerCellY < 0)
{
playerCellY = 0;
}
else if (playerCellY >= cubicmap.Height)
{
playerCellY = cubicmap.Height - 1;
}
// Check map collisions using image data and player position
// TODO: Improvement: Just check player surrounding cells for collision
for (int y = 0; y < cubicmap.Height; y++)
{
for (int x = 0; x < cubicmap.Width; x++)
for (var x = playerCellX - 1; x <= playerCellX + 1; x++)
{
Color* mapPixelsData = mapPixels;
// Collision: Color.white pixel, only check R channel
Rectangle rec = new(
mapPosition.X - 0.5f + x * 1.0f,
mapPosition.Z - 0.5f + y * 1.0f,
1.0f,
1.0f
);
bool collision = CheckCollisionCircleRec(playerPos, playerRadius, rec);
if ((mapPixelsData[y * cubicmap.Width + x].R == 255) && collision)
// NOTE: Collision: Only checking R channel for white pixel
if (((x >= 0) && (x < cubicmap.Width)) &&
(mapPixels[y * cubicmap.Width + x].R == 255) &&
(CheckCollisionCircleRec(playerPos, playerRadius,
new Rectangle(mapPosition.X - 0.5f + x * 1.0f, mapPosition.Z - 0.5f + y * 1.0f, 1.0f, 1.0f))))
{
// Collision detected, reset camera position
camera.Position = oldCamPos;
}
}
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
// Draw maze map
BeginMode3D(camera);
DrawModel(model, mapPosition, 1.0f, Color.White);
EndMode3D();
DrawTextureEx(cubicmap, new Vector2(GetScreenWidth() - cubicmap.Width * 4 - 20, 20), 0.0f, 4.0f, Color.White);
DrawRectangleLines(GetScreenWidth() - cubicmap.Width * 4 - 20, 20, cubicmap.Width * 4, cubicmap.Height * 4, Color.Green);
// Draw player position radar
DrawRectangle(GetScreenWidth() - cubicmap.Width * 4 - 20 + playerCellX * 4, 20 + playerCellY * 4, 4, 4, Color.Red);
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawModel(model, mapPosition, 1.0f, Color.White); // Draw maze map
EndMode3D();
DrawTextureEx(cubicmap, new Vector2(GetScreenWidth() - cubicmap.Width * 4 - 20, 20), 0.0f, 4.0f, Color.White);
DrawRectangleLines(GetScreenWidth() - cubicmap.Width * 4 - 20, 20, cubicmap.Width * 4, cubicmap.Height * 4, Color.Green);
// Draw player position radar
DrawRectangle(GetScreenWidth() - cubicmap.Width * 4 - 20 + playerCellX * 4, 20 + playerCellY * 4, 4, 4, Color.Red);
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadImageColors(mapPixels); // Unload color array
UnloadTexture(cubicmap); // Unload cubicmap texture
UnloadTexture(texture); // Unload map texture
UnloadModel(model); // Unload map model
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [models] example - first person maze");
DisableCursor(); // Limit cursor to relative movement inside the window
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new FirstPersonMaze();
game.Init();
// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
{
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadImageColors(mapPixels);
UnloadTexture(cubicmap);
UnloadTexture(texture);
UnloadModel(model);
CloseWindow();
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;

View file

@ -1,83 +1,105 @@
/*******************************************************************************************
*
* raylib [models] example - Draw some basic geometric shapes (cube, sphere, cylinder...)
* raylib [models] example - geometric shapes
*
* 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 complexity rating: [★☆☆☆] 1/4
*
* Copyright (c) 2014 Ramon Santamaria (@raysan5)
* Example originally created with raylib 1.0, 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) 2014-2025 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
using System.Numerics;
using static Raylib_cs.Raylib;
namespace Examples.Models;
public class GeometricShapes
public partial class GeometricShapes : IExample
{
public static int Main()
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Models / Geometric Shapes";
public string Title => "raylib [models] example - geometric shapes";
private Camera3D camera;
public void Init()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [models] example - geometric shapes");
// Define the camera to look into our 3d world
Camera3D camera = new();
camera = new();
camera.Position = new Vector3(0.0f, 10.0f, 10.0f);
camera.Target = new Vector3(0.0f, 0.0f, 0.0f);
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
camera.FovY = 45.0f;
camera.Projection = CameraProjection.Perspective;
}
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
public void Update()
{
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawCube(new Vector3(-4.0f, 0.0f, 2.0f), 2.0f, 5.0f, 2.0f, Color.Red);
DrawCubeWires(new Vector3(-4.0f, 0.0f, 2.0f), 2.0f, 5.0f, 2.0f, Color.Gold);
DrawCubeWires(new Vector3(-4.0f, 0.0f, -2.0f), 3.0f, 6.0f, 2.0f, Color.Maroon);
DrawSphere(new Vector3(-1.0f, 0.0f, -2.0f), 1.0f, Color.Green);
DrawSphereWires(new Vector3(1.0f, 0.0f, 2.0f), 2.0f, 16, 16, Color.Lime);
DrawCylinder(new Vector3(4.0f, 0.0f, -2.0f), 1.0f, 2.0f, 3.0f, 4, Color.SkyBlue);
DrawCylinderWires(new Vector3(4.0f, 0.0f, -2.0f), 1.0f, 2.0f, 3.0f, 4, Color.DarkBlue);
DrawCylinderWires(new Vector3(4.5f, -1.0f, 2.0f), 1.0f, 1.0f, 2.0f, 6, Color.Brown);
DrawCylinder(new Vector3(1.0f, 0.0f, -4.0f), 0.0f, 1.5f, 3.0f, 8, Color.Gold);
DrawCylinderWires(new Vector3(1.0f, 0.0f, -4.0f), 0.0f, 1.5f, 3.0f, 8, Color.Pink);
DrawCapsule(new Vector3(-3.0f, 1.5f, -4.0f), new Vector3(-4.0f, -1.0f, -4.0f), 1.2f, 8, 8, Color.Violet);
DrawCapsuleWires(new Vector3(-3.0f, 1.5f, -4.0f), new Vector3(-4.0f, -1.0f, -4.0f), 1.2f, 8, 8, Color.Purple);
DrawGrid(10, 1.0f); // Draw a grid
EndMode3D();
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [models] example - geometric shapes");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new GeometricShapes();
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);
BeginMode3D(camera);
DrawCube(new Vector3(-4.0f, 0.0f, 2.0f), 2.0f, 5.0f, 2.0f, Color.Red);
DrawCubeWires(new Vector3(-4.0f, 0.0f, 2.0f), 2.0f, 5.0f, 2.0f, Color.Gold);
DrawCubeWires(new Vector3(-4.0f, 0.0f, -2.0f), 3.0f, 6.0f, 2.0f, Color.Maroon);
DrawSphere(new Vector3(-1.0f, 0.0f, -2.0f), 1.0f, Color.Green);
DrawSphereWires(new Vector3(1.0f, 0.0f, 2.0f), 2.0f, 16, 16, Color.Lime);
DrawCylinder(new Vector3(4.0f, 0.0f, -2.0f), 1.0f, 2.0f, 3.0f, 4, Color.SkyBlue);
DrawCylinderWires(new Vector3(4.0f, 0.0f, -2.0f), 1.0f, 2.0f, 3.0f, 4, Color.DarkBlue);
DrawCylinderWires(new Vector3(4.5f, -1.0f, 2.0f), 1.0f, 1.0f, 2.0f, 6, Color.Brown);
DrawCylinder(new Vector3(1.0f, 0.0f, -4.0f), 0.0f, 1.5f, 3.0f, 8, Color.Gold);
DrawCylinderWires(new Vector3(1.0f, 0.0f, -4.0f), 0.0f, 1.5f, 3.0f, 8, Color.Pink);
DrawGrid(10, 1.0f);
EndMode3D();
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow();
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;

View file

@ -1,90 +1,116 @@
/*******************************************************************************************
*
* raylib [models] example - Heightmap loading and drawing
* raylib [models] example - heightmap rendering
*
* This example has been created using raylib 1.8 (www.raylib.com)
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
* Example complexity rating: [★☆☆☆] 1/4
*
* Copyright (c) 2015 Ramon Santamaria (@raysan5)
* Example originally created with raylib 1.8, 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) 2015-2025 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
using System.Numerics;
using static Raylib_cs.Raylib;
namespace Examples.Models;
public class HeightmapDemo
public partial class HeightmapDemo : IExample
{
public static int Main()
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Models / Heightmap Demo";
public string Title => "raylib [models] example - heightmap rendering";
private Camera3D camera;
private Texture2D texture;
private Model model;
private Vector3 mapPosition;
public void Init()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [models] example - heightmap loading and drawing");
// Define our custom camera to look into our 3d world
Camera3D camera = new();
camera.Position = new Vector3(18.0f, 16.0f, 18.0f);
camera.Target = new Vector3(0.0f, 0.0f, 0.0f);
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
camera.FovY = 45.0f;
camera.Projection = CameraProjection.Perspective;
camera = new();
camera.Position = new Vector3(18.0f, 21.0f, 18.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
Image image = LoadImage("resources/heightmap.png");
Texture2D texture = LoadTextureFromImage(image);
var image = LoadImage("resources/heightmap.png"); // Load heightmap image (RAM)
texture = LoadTextureFromImage(image); // Convert image to texture (VRAM)
Mesh mesh = GenMeshHeightmap(image, new Vector3(16, 8, 16));
Model model = LoadModelFromMesh(mesh);
var mesh = GenMeshHeightmap(image, new Vector3(16, 8, 16)); // Generate heightmap mesh (RAM and VRAM)
model = LoadModelFromMesh(mesh); // Load model from generated mesh
// Set map diffuse texture
Raylib.SetMaterialTexture(ref model, 0, MaterialMapIndex.Albedo, ref texture);
Vector3 mapPosition = new(-8.0f, 0.0f, -8.0f);
mapPosition = new(-8.0f, 0.0f, -8.0f); // Define model position
UnloadImage(image);
UnloadImage(image); // Unload heightmap image from RAM, already uploaded to VRAM
}
SetTargetFPS(60);
public void Update()
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Orbital);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawModel(model, mapPosition, 1.0f, Color.Red);
DrawGrid(20, 1.0f);
EndMode3D();
DrawTexture(texture, screenWidth - texture.Width - 20, 20, Color.White);
DrawRectangleLines(screenWidth - texture.Width - 20, 20, texture.Width, texture.Height, Color.Green);
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadTexture(texture); // Unload texture
UnloadModel(model); // Unload model
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [models] example - heightmap rendering");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new HeightmapDemo();
game.Init();
// Main game loop
while (!WindowShouldClose())
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Orbital);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawModel(model, mapPosition, 1.0f, Color.Red);
DrawGrid(20, 1.0f);
EndMode3D();
DrawTexture(texture, screenWidth - texture.Width - 20, 20, Color.White);
DrawRectangleLines(screenWidth - texture.Width - 20, 20, texture.Width, texture.Height, Color.Green);
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadTexture(texture);
UnloadModel(model);
CloseWindow();
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;

View file

@ -20,98 +20,124 @@
*
********************************************************************************************/
using System.Numerics;
using static Raylib_cs.Raylib;
namespace Examples.Models;
public class LoadingGltf
public partial class LoadingGltf : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Models / Loading GLTF";
public string Title => "raylib [models] example - loading gltf";
private Camera3D camera;
private Model model;
private Vector3 position;
private unsafe ModelAnimation* anims;
private int animCount;
private int animIndex;
private float animCurrentFrame;
public unsafe void Init()
{
// Define the camera to look into our 3d world
camera = new();
camera.Position = new Vector3(6.0f, 6.0f, 6.0f); // Camera position
camera.Target = new Vector3(0.0f, 2.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
// Load model
model = LoadModel("resources/models/gltf/robot.glb");
position = new(0.0f, 0.0f, 0.0f); // Set model world position
// Load model animations
anims = LoadModelAnimations("resources/models/gltf/robot.glb", ref animCount);
// Animation playing variables
animIndex = 0; // Current animation playing
animCurrentFrame = 0.0f; // Current animation frame
}
public unsafe void Update()
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Orbital);
// Select current animation
if (IsKeyPressed(KeyboardKey.Right))
{
animIndex = (animIndex + 1) % animCount;
}
else if (IsKeyPressed(KeyboardKey.Left))
{
animIndex = (animIndex + animCount - 1) % animCount;
}
// Update model animation
animCurrentFrame = (animCurrentFrame + 1) % anims[animIndex].KeyFrameCount;
UpdateModelAnimation(model, anims[animIndex], (float)animCurrentFrame);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawModel(
model,
position,
1f,
Color.White
);
DrawGrid(10, 1.0f);
EndMode3D();
DrawText($"Current animation: {anims[animIndex].NameToString()}", 10, 40, 20, Color.Maroon);
DrawText("Use the LEFT/RIGHT keys to switch animation", 10, 10, 20, Color.Gray);
EndDrawing();
//----------------------------------------------------------------------------------
}
public unsafe void Unload()
{
UnloadModelAnimations(anims, animCount); // Unload model animations data
UnloadModel(model); // Unload model
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [models] example - loading gltf");
// Define the camera to look into our 3d world
Camera3D camera = new();
camera.Position = new Vector3(6.0f, 6.0f, 6.0f);
camera.Target = new Vector3(0.0f, 2.0f, 0.0f);
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
camera.FovY = 45.0f;
camera.Projection = CameraProjection.Perspective;
Model model = LoadModel("resources/models/gltf/robot.glb");
Vector3 position = new(0.0f, 0.0f, 0.0f);
// Load animation data
var anims = LoadModelAnimations("resources/models/gltf/robot.glb");
// Animation playing variables
int animIndex = 0;
float animCurrentFrame = 0.0f;
SetTargetFPS(60);
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new LoadingGltf();
game.Init();
// Main game loop
while (!WindowShouldClose())
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Orbital);
if (IsKeyPressed(KeyboardKey.Right))
{
animIndex = (animIndex + 1) % anims.Length;
}
else if (IsKeyPressed(KeyboardKey.Left))
{
animIndex = (animIndex + anims.Length - 1) % anims.Length;
}
// Update model animation
animCurrentFrame = (animCurrentFrame + 1) % anims[animIndex].KeyFrameCount;
UpdateModelAnimation(model, anims[animIndex], (float)animCurrentFrame);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawModel(
model,
position,
1f,
Color.White
);
DrawGrid(10, 1.0f);
EndMode3D();
DrawText($"Current animation: {anims[animIndex].NameToString()}", 10, 40, 20, Color.Maroon);
DrawText("Use the LEFT/RIGHT keys to switch animation", 10, 10, 20, Color.Gray);
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadModelAnimations(anims);
UnloadModel(model);
CloseWindow();
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}
}

View file

@ -19,99 +19,123 @@
*
********************************************************************************************/
using System.Numerics;
using System.Runtime.InteropServices;
using static Raylib_cs.Raylib;
namespace Examples.Models;
public class LoadingIqm
public partial class LoadingIqm : IExample
{
public unsafe static int Main()
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Models / Loading IQM";
public string Title => "raylib [models] example - loading iqm";
private Camera3D camera;
private Model model;
private Texture2D texture;
private Vector3 position;
private unsafe ModelAnimation* anims;
private int animCount;
private int animIndex;
private float animCurrentFrame;
public unsafe void Init()
{
// Define the camera to look into our 3d world
camera = new();
camera.Position = new Vector3(10.0f, 10.0f, 10.0f); // Camera position
camera.Target = new Vector3(0.0f, 4.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
model = LoadModel("resources/models/iqm/guy.iqm"); // Load the animated model mesh and basic data
texture = LoadTexture("resources/models/iqm/guytex.png"); // Load model texture and set material
Raylib.SetMaterialTexture(ref model, 0, MaterialMapIndex.Diffuse, ref texture); // Set model material map texture
position = new(0.0f, 0.0f, 0.0f); // Set model position
// Load animation data
anims = LoadModelAnimations("resources/models/iqm/guyanim.iqm", ref animCount);
// Animation playing variables
animIndex = 0; // Current animation playing
animCurrentFrame = 0.0f; // Current animation frame (supporting interpolated frames)
}
public unsafe void Update()
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Orbital);
// Play animation when spacebar is held down
animCurrentFrame += 1.0f;
UpdateModelAnimation(model, anims[0], animCurrentFrame);
if (animCurrentFrame >= anims[0].KeyFrameCount)
{
animCurrentFrame = 0;
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawModelEx(
model,
position,
new Vector3(1.0f, 0.0f, 0.0f),
-90.0f,
new Vector3(1.0f, 1.0f, 1.0f),
Color.White
);
DrawGrid(10, 1.0f);
EndMode3D();
DrawText($"Current animation: {anims[animIndex].NameToString()}", 10, 10, 20, Color.Maroon);
DrawText("(c) Guy IQM 3D model by @culacant", screenWidth - 200, screenHeight - 20, 10, Color.Gray);
EndDrawing();
//----------------------------------------------------------------------------------
}
public unsafe void Unload()
{
UnloadTexture(texture); // Unload texture
UnloadModelAnimations(anims, animCount); // Unload model animations data
UnloadModel(model); // Unload model
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [models] example - loading iqm");
InitWindow(screenWidth, screenHeight, "raylib [models] example - model animation");
// Define the camera to look into our 3d world
Camera3D camera = new();
camera.Position = new Vector3(10.0f, 10.0f, 10.0f);
camera.Target = new Vector3(0.0f, 4.0f, 0.0f);
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
camera.FovY = 45.0f;
camera.Projection = CameraProjection.Perspective;
Model model = LoadModel("resources/models/iqm/guy.iqm");
Texture2D texture = LoadTexture("resources/models/iqm/guytex.png");
Raylib.SetMaterialTexture(ref model, 0, MaterialMapIndex.Diffuse, ref texture);
Vector3 position = new(0.0f, 0.0f, 0.0f);
// Load animation data
var anims = LoadModelAnimations("resources/models/iqm/guyanim.iqm");
// Animation playing variables
int animIndex = 0;
float animCurrentFrame = 0.0f;
SetTargetFPS(60);
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new LoadingIqm();
game.Init();
// Main game loop
while (!WindowShouldClose())
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Orbital);
// Play animation when spacebar is held down
animCurrentFrame += 1.0f;
UpdateModelAnimation(model, anims[0], animCurrentFrame);
if (animCurrentFrame >= anims[0].KeyFrameCount)
{
animCurrentFrame = 0;
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawModelEx(
model,
position,
new Vector3(1.0f, 0.0f, 0.0f),
-90.0f,
new Vector3(1.0f, 1.0f, 1.0f),
Color.White
);
DrawGrid(10, 1.0f);
EndMode3D();
DrawText($"Current animation: {anims[animIndex].NameToString()}", 10, 10, 20, Color.Maroon);
DrawText("(c) Guy IQM 3D model by @culacant", screenWidth - 200, screenHeight - 20, 10, Color.Gray);
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadTexture(texture);
UnloadModelAnimations(anims);
UnloadModel(model);
CloseWindow();
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}
}

View file

@ -0,0 +1,170 @@
/*******************************************************************************************
*
* raylib [models] example - loading m3d
*
* Example complexity rating: [★★☆☆] 2/4
*
* Example originally created with raylib 4.5, last time updated with raylib 4.5
*
* Example contributed by bzt (@bztsrc) and reviewed by Ramon Santamaria (@raysan5)
*
* NOTES:
* - Model3D (M3D) fileformat specs: https://gitlab.com/bztsrc/model3d
* - Bender M3D exported: https://gitlab.com/bztsrc/model3d/-/tree/master/blender
*
* 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-2025 bzt (@bztsrc)
*
********************************************************************************************/
namespace Examples.Models;
public partial class LoadingM3d : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Models / Loading M3D";
public string Title => "raylib [models] example - loading m3d";
private Camera3D camera;
private Model model;
private Vector3 position;
private unsafe ModelAnimation* anims;
private int animCount;
private int animIndex;
private float animCurrentFrame;
public unsafe void Init()
{
// Define the camera to look into our 3d world
camera = new();
camera.Position = new Vector3(1.5f, 1.5f, 1.5f); // Camera position
camera.Target = new Vector3(0.0f, 0.4f, 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
// Load model
model = LoadModel("resources/models/m3d/cesium_man.m3d"); // Load the animated model mesh and basic data
position = new Vector3(0.0f, 0.0f, 0.0f); // Set model position
// Load animation data
animCount = 0;
anims = LoadModelAnimations("resources/models/m3d/cesium_man.m3d", ref animCount);
// Animation playing variables
animIndex = 0; // Current animation playing
animCurrentFrame = 0.0f; // Current animation frame (supporting interpolated frames)
}
public unsafe void Update()
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Orbital);
// Select current animation
if (IsKeyPressed(KeyboardKey.Right))
{
animIndex = (animIndex + 1) % animCount;
}
else if (IsKeyPressed(KeyboardKey.Left))
{
animIndex = (animIndex + animCount - 1) % animCount;
}
// Update model animation
animCurrentFrame += 1.0f;
if (animCurrentFrame >= anims[animIndex].KeyFrameCount)
{
animCurrentFrame = 0.0f;
}
UpdateModelAnimation(model, anims[animIndex], animCurrentFrame);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
// Draw 3d model with texture
if (!IsKeyDown(KeyboardKey.Space))
{
DrawModel(model, position, 1.0f, Color.White);
}
else
{
// Draw the animated skeleton
DrawModelSkeleton(model.Skeleton, anims[animIndex].KeyframePoses[(int)animCurrentFrame], 1.0f, Color.Red);
}
DrawGrid(10, 1.0f);
EndMode3D();
DrawText($"Current animation: {anims[animIndex].NameToString()}", 10, 10, 20, Color.LightGray);
DrawText("Press SPACE to draw skeleton", 10, 40, 20, Color.Maroon);
DrawText("(c) CesiumMan model by KhronosGroup", GetScreenWidth() - 210, GetScreenHeight() - 20, 10, Color.Gray);
EndDrawing();
//----------------------------------------------------------------------------------
}
public unsafe void Unload()
{
UnloadModelAnimations(anims, animCount); // Unload model animations data
UnloadModel(model); // Unload model
}
// Draw model skeleton
private static unsafe void DrawModelSkeleton(ModelSkeleton skeleton, Transform* pose, float scale, Color color)
{
// Loop to (boneCount - 1) because the last one is a special "no bone" bone,
// needed to workaround buggy models without a -1, a cube is always drawn at the origin
for (var i = 0; i < skeleton.BoneCount - 1; i++)
{
// Display the frame-pose skeleton
DrawCube(pose[i].Translation, scale * 0.05f, scale * 0.05f, scale * 0.05f, color);
if (skeleton.Bones[i].Parent >= 0)
{
DrawLine3D(pose[i].Translation, pose[skeleton.Bones[i].Parent].Translation, color);
}
}
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [models] example - loading m3d");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new LoadingM3d();
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,244 @@
/*******************************************************************************************
*
* raylib [models] example - loading vox
*
* Example complexity rating: [★☆☆☆] 1/4
*
* Example originally created with raylib 4.0, last time updated with raylib 4.0
*
* Example contributed by Johann Nadalutti (@procfxgen) 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 Johann Nadalutti (@procfxgen) and Ramon Santamaria (@raysan5)
*
********************************************************************************************/
using static Raylib_cs.Raymath;
using Examples.Shared;
namespace Examples.Models;
public partial class LoadingVox : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
private const int MaxVoxFiles = 4;
private const int MaxLights = 4;
#if BROWSER
private const int GlslVersion = 100; // WebGL1 needs GLSL ES 100
#else
private const int GlslVersion = 330;
#endif
public string Name => "Models / Loading VOX";
public string Title => "raylib [models] example - loading vox";
private static readonly string[] VoxFileNames =
{
"resources/models/vox/chr_knight.vox",
"resources/models/vox/chr_sword.vox",
"resources/models/vox/monu9.vox",
"resources/models/vox/fez.vox"
};
private Camera3D camera;
private Model[] models;
private int currentModel;
private Vector3 modelpos;
private Vector3 camerarot;
private Shader shader;
private Light[] lights;
public unsafe void Init()
{
// Define the camera to look into our 3d world
camera = new();
camera.Position = new Vector3(10.0f, 10.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
// Load MagicaVoxel files
models = new Model[MaxVoxFiles];
for (var i = 0; i < MaxVoxFiles; i++)
{
// Load VOX file and measure time
var t0 = GetTime() * 1000.0;
models[i] = LoadModel(VoxFileNames[i]);
var t1 = GetTime() * 1000.0;
TraceLog(TraceLogLevel.Info, $"[{VoxFileNames[i]}] Model file loaded in {t1 - t0:0.000} ms");
// Compute model translation matrix to center model on draw position (0, 0 , 0)
var bb = GetModelBoundingBox(models[i]);
Vector3 center = new();
center.X = bb.Min.X + ((bb.Max.X - bb.Min.X) / 2);
center.Z = bb.Min.Z + ((bb.Max.Z - bb.Min.Z) / 2);
var matTranslate = MatrixTranslate(-center.X, 0, -center.Z);
models[i].Transform = matTranslate;
}
currentModel = 0;
modelpos = new Vector3(0, 0, 0);
camerarot = new Vector3(0, 0, 0);
// Load voxel shader
shader = LoadShader(
$"resources/shaders/glsl{GlslVersion}/voxel_lighting.vs",
$"resources/shaders/glsl{GlslVersion}/voxel_lighting.fs"
);
// Get some required shader locations
shader.Locs[(int)ShaderLocationIndex.VectorView] = GetShaderLocation(shader, "viewPos");
// NOTE: "matModel" location name is automatically assigned on shader loading,
// no need to get the location again if using that uniform name
//shader.Locs[(int)ShaderLocationIndex.MatrixModel] = GetShaderLocation(shader, "matModel");
// Ambient light level (some basic lighting)
var ambientLoc = GetShaderLocation(shader, "ambient");
Raylib.SetShaderValue(shader, ambientLoc, new[] { 0.1f, 0.1f, 0.1f, 1.0f }, ShaderUniformDataType.Vec4);
// Assign out lighting shader to model
for (var i = 0; i < MaxVoxFiles; i++)
{
for (var j = 0; j < models[i].MaterialCount; j++)
{
models[i].Materials[j].Shader = shader;
}
}
// Create lights
lights = new Light[MaxLights];
lights[0] = Rlights.CreateLight(0, LightType.Point, new Vector3(-20, 20, -20), Vector3.Zero, Color.Gray, shader);
lights[1] = Rlights.CreateLight(1, LightType.Point, new Vector3(20, -20, 20), Vector3.Zero, Color.Gray, shader);
lights[2] = Rlights.CreateLight(2, LightType.Point, new Vector3(-20, 20, 20), Vector3.Zero, Color.Gray, shader);
lights[3] = Rlights.CreateLight(3, LightType.Point, new Vector3(20, -20, -20), Vector3.Zero, Color.Gray, shader);
}
public unsafe void Update()
{
// Update
//----------------------------------------------------------------------------------
if (IsMouseButtonDown(MouseButton.Middle))
{
var mouseDelta = GetMouseDelta();
camerarot.X = mouseDelta.X * 0.05f;
camerarot.Y = mouseDelta.Y * 0.05f;
}
else
{
camerarot.X = 0;
camerarot.Y = 0;
}
// Update camere movement, custom controls
UpdateCameraPro(ref camera,
new Vector3(
(IsKeyDown(KeyboardKey.W) || IsKeyDown(KeyboardKey.Up) ? 0.1f : 0.0f) - (IsKeyDown(KeyboardKey.S) || IsKeyDown(KeyboardKey.Down) ? 0.1f : 0.0f), // Move forward-backward
(IsKeyDown(KeyboardKey.D) || IsKeyDown(KeyboardKey.Right) ? 0.1f : 0.0f) - (IsKeyDown(KeyboardKey.A) || IsKeyDown(KeyboardKey.Left) ? 0.1f : 0.0f), // Move right-left
0.0f), // Move up-down
camerarot, // Camera rotation
GetMouseWheelMove() * -2.0f); // Move to target (zoom)
// Cycle between models on mouse click
if (IsMouseButtonPressed(MouseButton.Left))
{
currentModel = (currentModel + 1) % MaxVoxFiles;
}
// Update the shader with the camera view vector (points towards { 0.0f, 0.0f, 0.0f })
Raylib.SetShaderValue(shader, shader.Locs[(int)ShaderLocationIndex.VectorView], camera.Position, ShaderUniformDataType.Vec3);
// Update light values (actually, only enable/disable them)
for (var i = 0; i < MaxLights; i++)
{
Rlights.UpdateLightValues(shader, lights[i]);
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
// Draw 3D model
BeginMode3D(camera);
DrawModel(models[currentModel], modelpos, 1.0f, Color.White);
DrawGrid(10, 1.0f);
// Draw spheres to show where the lights are
for (var i = 0; i < MaxLights; i++)
{
if (lights[i].Enabled)
{
DrawSphereEx(lights[i].Position, 0.2f, 8, 8, lights[i].Color);
}
else
{
DrawSphereWires(lights[i].Position, 0.2f, 8, 8, ColorAlpha(lights[i].Color, 0.3f));
}
}
EndMode3D();
// Display info
DrawRectangle(10, 40, 340, 70, Fade(Color.SkyBlue, 0.5f));
DrawRectangleLines(10, 40, 340, 70, Fade(Color.DarkBlue, 0.5f));
DrawText("- MOUSE LEFT BUTTON: CYCLE VOX MODELS", 20, 50, 10, Color.Blue);
DrawText("- MOUSE MIDDLE BUTTON: ZOOM OR ROTATE CAMERA", 20, 70, 10, Color.Blue);
DrawText("- UP-DOWN-LEFT-RIGHT KEYS: MOVE CAMERA", 20, 90, 10, Color.Blue);
DrawText($"VOX model file: {GetFileName(VoxFileNames[currentModel])}", 10, 10, 20, Color.Gray);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
// Unload models data (GPU VRAM)
for (var i = 0; i < MaxVoxFiles; i++)
{
UnloadModel(models[i]);
}
UnloadShader(shader);
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [models] example - loading vox");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new LoadingVox();
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,21 +1,23 @@
using System.Numerics;
using static Raylib_cs.Raylib;
namespace Examples.Models;
public class MeshDemo
public partial class MeshDemo : IExample
{
public unsafe static int Main()
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Models / Mesh Demo";
public string Title => "raylib [models] example - mesh demo";
private Camera3D camera;
private Model model;
private Texture2D texture;
private float rotationAngle;
public void Init()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [models] example - mesh demo");
// Define the camera to look into our 3d world
Camera3D camera = new();
camera = new();
camera.Position = Vector3.One * 1.5f;
camera.Target = Vector3.Zero;
camera.Up = Vector3.UnitY;
@ -28,10 +30,10 @@ public class MeshDemo
tetrahedron.AllocTexCoords();
tetrahedron.AllocColors();
tetrahedron.AllocIndices();
Span<Vector3> vertices = tetrahedron.VerticesAs<Vector3>();
Span<Vector2> texcoords = tetrahedron.TexCoordsAs<Vector2>();
Span<Color> colors = tetrahedron.ColorsAs<Color>();
Span<ushort> indices = tetrahedron.IndicesAs<ushort>();
var vertices = tetrahedron.VerticesAs<Vector3>();
var texcoords = tetrahedron.TexCoordsAs<Vector2>();
var colors = tetrahedron.ColorsAs<Color>();
var indices = tetrahedron.IndicesAs<ushort>();
// Coordinates for a regular tetrahedron
vertices[0] = new(MathF.Sqrt(8f / 9f), 0f, -1f / 3f);
@ -65,49 +67,69 @@ public class MeshDemo
indices[10] = 3;
indices[11] = 2;
float rotationAngle = 0f;
rotationAngle = 0f;
Raylib.UploadMesh(ref tetrahedron, false);
Model model = Raylib.LoadModelFromMesh(tetrahedron);
model = Raylib.LoadModelFromMesh(tetrahedron);
Image image = Raylib.GenImagePerlinNoise(16, 16, 0, 0, 1000f);
var image = Raylib.GenImagePerlinNoise(16, 16, 0, 0, 1000f);
Raylib.ImageBlurGaussian(ref image, 2);
Raylib.ImageColorBrightness(ref image, 100);
Raylib.ImageDither(ref image, 4, 4, 4, 4);
Texture2D texture = Raylib.LoadTextureFromImage(image);
texture = Raylib.LoadTextureFromImage(image);
Raylib.UnloadImage(image);
Raylib.SetMaterialTexture(ref model, 0, MaterialMapIndex.Diffuse, ref texture);
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Free);
rotationAngle = Raymath.Wrap(rotationAngle += 1f, 0f, 360f);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
Raylib.DrawModelEx(model, Vector3.Zero, Vector3.UnitX, rotationAngle, Vector3.One, Color.White);
EndMode3D();
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadTexture(texture);
UnloadModel(model);
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [models] example - mesh demo");
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
var game = new MeshDemo();
game.Init();
// Main game loop
while (!WindowShouldClose())
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Free);
rotationAngle = Raymath.Wrap(rotationAngle += 1f, 0f, 360f);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
Raylib.DrawModelEx(model, Vector3.Zero, Vector3.UnitX, rotationAngle, Vector3.One, Color.White);
EndMode3D();
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadTexture(texture);
UnloadModel(model);
CloseWindow();
//--------------------------------------------------------------------------------------

View file

@ -1,38 +1,45 @@
/*******************************************************************************************
*
* raylib example - procedural mesh generation
* raylib [models] example - mesh generation
*
* This example has been created using raylib 1.8 (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 (Ray San)
* Example originally created with raylib 1.8, 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) 2017-2025 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
using System.Numerics;
using static Raylib_cs.Raylib;
namespace Examples.Models;
public class MeshGeneration
public partial class MeshGeneration : IExample
{
public static int Main()
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Models / Mesh Generation";
public string Title => "raylib [models] example - mesh generation";
private Texture2D texture;
private Model[] models;
private Camera3D camera;
private Vector3 position;
private int currentModel;
public void Init()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [models] example - mesh generation");
// We generate a isChecked image for texturing
Image isChecked = GenImageChecked(2, 2, 1, 1, Color.Red, Color.Green);
Texture2D texture = LoadTextureFromImage(isChecked);
// We generate a checked image for texturing
var isChecked = GenImageChecked(2, 2, 1, 1, Color.Red, Color.Green);
texture = LoadTextureFromImage(isChecked);
UnloadImage(isChecked);
Model[] models = new Model[9];
models = new Model[9];
models[0] = LoadModelFromMesh(GenMeshPlane(2, 2, 5, 5));
models[0] = LoadModelFromMesh(GenMeshPlane(2, 2, 4, 3));
models[1] = LoadModelFromMesh(GenMeshCube(2.0f, 1.0f, 2.0f));
models[2] = LoadModelFromMesh(GenMeshSphere(2, 32, 32));
models[3] = LoadModelFromMesh(GenMeshHemiSphere(2, 16, 16));
@ -42,15 +49,17 @@ public class MeshGeneration
models[7] = LoadModelFromMesh(GenMeshPoly(5, 2.0f));
models[8] = LoadModelFromMesh(GenMeshCustom());
// Set isChecked texture as default diffuse component for all models material
for (int i = 0; i < models.Length; i++)
// NOTE: Generated meshes could be exported using ExportMesh()
// Set checked texture as default diffuse component for all models material
for (var i = 0; i < models.Length; i++)
{
// Set map diffuse texture
Raylib.SetMaterialTexture(ref models[i], 0, MaterialMapIndex.Albedo, ref texture);
}
// Define the camera to look into our 3d world
Camera3D camera = new();
camera = new();
camera.Position = new Vector3(5.0f, 5.0f, 5.0f);
camera.Target = new Vector3(0.0f, 0.0f, 0.0f);
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
@ -58,92 +67,102 @@ public class MeshGeneration
camera.Projection = CameraProjection.Perspective;
// Model drawing position
Vector3 position = new(0.0f, 0.0f, 0.0f);
position = new(0.0f, 0.0f, 0.0f);
int currentModel = 0;
currentModel = 0;
}
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
public void Update()
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Orbital);
// Main game loop
while (!WindowShouldClose())
if (IsMouseButtonPressed(MouseButton.Left))
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Orbital);
if (IsMouseButtonPressed(MouseButton.Left))
{
// Cycle between the textures
currentModel = (currentModel + 1) % models.Length;
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawModel(models[currentModel], position, 1.0f, Color.White);
DrawGrid(10, 1.0f);
EndMode3D();
DrawRectangle(30, 400, 310, 30, ColorAlpha(Color.SkyBlue, 0.5f));
DrawRectangleLines(30, 400, 310, 30, ColorAlpha(Color.DarkBlue, 0.5f));
DrawText("MOUSE LEFT BUTTON to CYCLE PROCEDURAL MODELS", 40, 410, 10, Color.Blue);
switch (currentModel)
{
case 0:
DrawText("PLANE", 680, 10, 20, Color.DarkBlue);
break;
case 1:
DrawText("CUBE", 680, 10, 20, Color.DarkBlue);
break;
case 2:
DrawText("SPHERE", 680, 10, 20, Color.DarkBlue);
break;
case 3:
DrawText("HEMISPHERE", 640, 10, 20, Color.DarkBlue);
break;
case 4:
DrawText("CYLINDER", 680, 10, 20, Color.DarkBlue);
break;
case 5:
DrawText("TORUS", 680, 10, 20, Color.DarkBlue);
break;
case 6:
DrawText("KNOT", 680, 10, 20, Color.DarkBlue);
break;
case 7:
DrawText("POLY", 680, 10, 20, Color.DarkBlue);
break;
case 8:
DrawText("Custom (triagnle)", 580, 10, 20, Color.DarkBlue);
break;
default:
break;
}
EndDrawing();
//----------------------------------------------------------------------------------
currentModel = (currentModel + 1) % models.Length; // Cycle between the textures
}
// De-Initialization
//--------------------------------------------------------------------------------------
for (int i = 0; i < models.Length; i++)
if (IsKeyPressed(KeyboardKey.Right))
{
currentModel++;
if (currentModel >= models.Length)
{
currentModel = 0;
}
}
else if (IsKeyPressed(KeyboardKey.Left))
{
currentModel--;
if (currentModel < 0)
{
currentModel = models.Length - 1;
}
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawModel(models[currentModel], position, 1.0f, Color.White);
DrawGrid(10, 1.0f);
EndMode3D();
DrawRectangle(30, 400, 310, 30, Fade(Color.SkyBlue, 0.5f));
DrawRectangleLines(30, 400, 310, 30, Fade(Color.DarkBlue, 0.5f));
DrawText("MOUSE LEFT BUTTON to CYCLE PROCEDURAL MODELS", 40, 410, 10, Color.Blue);
switch (currentModel)
{
case 0:
DrawText("PLANE", 680, 10, 20, Color.DarkBlue);
break;
case 1:
DrawText("CUBE", 680, 10, 20, Color.DarkBlue);
break;
case 2:
DrawText("SPHERE", 680, 10, 20, Color.DarkBlue);
break;
case 3:
DrawText("HEMISPHERE", 640, 10, 20, Color.DarkBlue);
break;
case 4:
DrawText("CYLINDER", 680, 10, 20, Color.DarkBlue);
break;
case 5:
DrawText("TORUS", 680, 10, 20, Color.DarkBlue);
break;
case 6:
DrawText("KNOT", 680, 10, 20, Color.DarkBlue);
break;
case 7:
DrawText("POLY", 680, 10, 20, Color.DarkBlue);
break;
case 8:
DrawText("Custom (triangle)", 580, 10, 20, Color.DarkBlue);
break;
default:
break;
}
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadTexture(texture); // Unload texture
// Unload models data (GPU VRAM)
for (var i = 0; i < models.Length; i++)
{
UnloadModel(models[i]);
}
CloseWindow();
//--------------------------------------------------------------------------------------
return 0;
}
// Generate a simple triangle mesh from code
@ -153,9 +172,9 @@ public class MeshGeneration
mesh.AllocVertices();
mesh.AllocTexCoords();
mesh.AllocNormals();
Span<Vector3> vertices = mesh.VerticesAs<Vector3>();
Span<Vector2> texcoords = mesh.TexCoordsAs<Vector2>();
Span<Vector3> normals = mesh.NormalsAs<Vector3>();
var vertices = mesh.VerticesAs<Vector3>();
var texcoords = mesh.TexCoordsAs<Vector2>();
var normals = mesh.NormalsAs<Vector3>();
// Vertex at (0, 0, 0)
vertices[0] = new(0, 0, 0);
@ -177,4 +196,32 @@ public class MeshGeneration
return mesh;
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [models] example - mesh generation");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new MeshGeneration();
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,34 +1,54 @@
/*******************************************************************************************
*
* raylib [models] example - Mesh picking in 3d mode, ground plane, triangle, mesh
* raylib [models] example - mesh picking
*
* 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: [★★★☆] 3/4
*
* Copyright (c) 2015 Ramon Santamaria (@raysan5)
* Example contributed by Joel Davis (@joeld42)
* Example originally created with raylib 1.7, last time updated with raylib 4.0
*
* Example contributed by Joel Davis (@joeld42) 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) 2017-2025 Joel Davis (@joeld42) and Ramon Santamaria (@raysan5)
*
********************************************************************************************/
using System.Numerics;
using static Raylib_cs.Raylib;
using static Raylib_cs.Raymath;
namespace Examples.Models;
public class MeshPicking
public partial class MeshPicking : IExample
{
public unsafe static int Main()
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Models / Mesh Picking";
public string Title => "raylib [models] example - mesh picking";
private Camera3D camera;
private Ray ray;
private Model tower;
private Texture2D texture;
private Vector3 towerPos;
private BoundingBox towerBBox;
private Vector3 g0;
private Vector3 g1;
private Vector3 g2;
private Vector3 g3;
private Vector3 ta;
private Vector3 tb;
private Vector3 tc;
private Vector3 bary;
private Vector3 sp;
private float sr;
public unsafe void Init()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [models] example - mesh picking");
// Define the camera to look into our 3d world
Camera3D camera;
camera = new();
camera.Position = new Vector3(20.0f, 20.0f, 20.0f);
camera.Target = new Vector3(0.0f, 8.0f, 0.0f);
camera.Up = new Vector3(0.0f, 1.6f, 0.0f);
@ -36,210 +56,232 @@ public class MeshPicking
camera.Projection = CameraProjection.Perspective;
// Picking ray
Ray ray = new();
ray = new();
Model tower = LoadModel("resources/models/obj/turret.obj");
Texture2D texture = LoadTexture("resources/models/obj/turret_diffuse.png");
tower = LoadModel("resources/models/obj/turret.obj");
texture = LoadTexture("resources/models/obj/turret_diffuse.png");
Raylib.SetMaterialTexture(ref tower, 0, MaterialMapIndex.Albedo, ref texture);
Vector3 towerPos = new(0.0f, 0.0f, 0.0f);
BoundingBox towerBBox = GetMeshBoundingBox(tower.Meshes[0]);
towerPos = new(0.0f, 0.0f, 0.0f);
towerBBox = GetMeshBoundingBox(tower.Meshes[0]);
// Ground quad
Vector3 g0 = new(-50.0f, 0.0f, -50.0f);
Vector3 g1 = new(-50.0f, 0.0f, 50.0f);
Vector3 g2 = new(50.0f, 0.0f, 50.0f);
Vector3 g3 = new(50.0f, 0.0f, -50.0f);
g0 = new(-50.0f, 0.0f, -50.0f);
g1 = new(-50.0f, 0.0f, 50.0f);
g2 = new(50.0f, 0.0f, 50.0f);
g3 = new(50.0f, 0.0f, -50.0f);
// Test triangle
Vector3 ta = new(-25.0f, 0.5f, 0.0f);
Vector3 tb = new(-4.0f, 2.5f, 1.0f);
Vector3 tc = new(-8.0f, 6.5f, 0.0f);
ta = new(-25.0f, 0.5f, 0.0f);
tb = new(-4.0f, 2.5f, 1.0f);
tc = new(-8.0f, 6.5f, 0.0f);
Vector3 bary = new(0.0f, 0.0f, 0.0f);
bary = new(0.0f, 0.0f, 0.0f);
// Test sphere
Vector3 sp = new(-30.0f, 5.0f, 5.0f);
float sr = 4.0f;
sp = new(-30.0f, 5.0f, 5.0f);
sr = 4.0f;
}
public unsafe void Update()
{
//----------------------------------------------------------------------------------
// Update
//----------------------------------------------------------------------------------
if (IsCursorHidden())
{
UpdateCamera(ref camera, CameraMode.FirstPerson);
}
// Toggle camera controls
if (IsMouseButtonPressed(MouseButton.Right))
{
if (IsCursorHidden())
{
EnableCursor();
}
else
{
DisableCursor();
}
}
// Display information about closest hit
RayCollision collision = new();
var hitObjectName = "None";
collision.Distance = float.MaxValue;
collision.Hit = false;
var cursorColor = Color.White;
// Get ray and test against objects
ray = GetScreenToWorldRay(GetMousePosition(), camera);
// Check ray collision against ground quad
var groundHitInfo = GetRayCollisionQuad(ray, g0, g1, g2, g3);
if (groundHitInfo.Hit && (groundHitInfo.Distance < collision.Distance))
{
collision = groundHitInfo;
cursorColor = Color.Green;
hitObjectName = "Ground";
}
// Check ray collision against test triangle
var triHitInfo = GetRayCollisionTriangle(ray, ta, tb, tc);
if (triHitInfo.Hit && (triHitInfo.Distance < collision.Distance))
{
collision = triHitInfo;
cursorColor = Color.Purple;
hitObjectName = "Triangle";
bary = Vector3Barycenter(collision.Point, ta, tb, tc);
}
// Check ray collision against test sphere
var sphereHitInfo = GetRayCollisionSphere(ray, sp, sr);
if ((sphereHitInfo.Hit) && (sphereHitInfo.Distance < collision.Distance))
{
collision = sphereHitInfo;
cursorColor = Color.Orange;
hitObjectName = "Sphere";
}
// Check ray collision against bounding box first, before trying the full ray-mesh test
var boxHitInfo = GetRayCollisionBox(ray, towerBBox);
if (boxHitInfo.Hit && boxHitInfo.Distance < collision.Distance)
{
collision = boxHitInfo;
cursorColor = Color.Orange;
hitObjectName = "Box";
// Check ray collision against model meshes
RayCollision meshHitInfo = new();
for (var m = 0; m < tower.MeshCount; m++)
{
// NOTE: We consider the model.Transform for the collision check but
// it can be checked against any transform matrix, used when checking against same
// model drawn multiple times with multiple transforms
meshHitInfo = GetRayCollisionMesh(ray, tower.Meshes[m], tower.Transform);
if (meshHitInfo.Hit)
{
// Save the closest hit mesh
if ((!collision.Hit) || (collision.Distance > meshHitInfo.Distance))
{
collision = meshHitInfo;
}
break;
}
}
if (meshHitInfo.Hit)
{
collision = meshHitInfo;
cursorColor = Color.Orange;
hitObjectName = "Mesh";
}
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
// Draw the tower
// WARNING: If scale is different than 1.0f,
// not considered by GetRayCollisionModel()
DrawModel(tower, towerPos, 1.0f, Color.White);
// Draw the test triangle
DrawLine3D(ta, tb, Color.Purple);
DrawLine3D(tb, tc, Color.Purple);
DrawLine3D(tc, ta, Color.Purple);
// Draw the test sphere
DrawSphereWires(sp, sr, 8, 8, Color.Purple);
// Draw the mesh bbox if we hit it
if (boxHitInfo.Hit)
{
DrawBoundingBox(towerBBox, Color.Lime);
}
// If we hit something, draw the cursor at the hit point
if (collision.Hit)
{
DrawCube(collision.Point, 0.3f, 0.3f, 0.3f, cursorColor);
DrawCubeWires(collision.Point, 0.3f, 0.3f, 0.3f, Color.Red);
var normalEnd = collision.Point + collision.Normal;
DrawLine3D(collision.Point, normalEnd, Color.Red);
}
DrawRay(ray, Color.Maroon);
DrawGrid(10, 10.0f);
EndMode3D();
// Draw some debug GUI text
DrawText($"Hit Object: {hitObjectName}", 10, 50, 10, Color.Black);
if (collision.Hit)
{
var ypos = 70;
DrawText($"Distance: {collision.Distance}", 10, ypos, 10, Color.Black);
DrawText($"Hit Pos: {collision.Point}", 10, ypos + 15, 10, Color.Black);
DrawText($"Hit Norm: {collision.Normal}", 10, ypos + 30, 10, Color.Black);
if (triHitInfo.Hit && hitObjectName == "Triangle")
{
DrawText($"Barycenter: {bary}", 10, ypos + 45, 10, Color.Black);
}
}
DrawText("Right click mouse to toggle camera controls", 10, 430, 10, Color.Gray);
DrawText("(c) Turret 3D model by Alberto Cano", screenWidth - 200, screenHeight - 20, 10, Color.Gray);
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadModel(tower);
UnloadTexture(texture);
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [models] example - mesh picking");
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
var game = new MeshPicking();
game.Init();
//----------------------------------------------------------------------------------
// Main game loop
//--------------------------------------------------------------------------------------
while (!WindowShouldClose())
{
//----------------------------------------------------------------------------------
// Update
//----------------------------------------------------------------------------------
if (IsCursorHidden())
{
UpdateCamera(ref camera, CameraMode.FirstPerson);
}
// Toggle camera controls
if (IsMouseButtonPressed(MouseButton.Right))
{
if (IsCursorHidden())
{
EnableCursor();
}
else
{
DisableCursor();
}
}
// Display information about closest hit
RayCollision collision = new();
string hitObjectName = "None";
collision.Distance = float.MaxValue;
collision.Hit = false;
Color cursorColor = Color.White;
// Get ray and test against objects
ray = GetScreenToWorldRay(GetMousePosition(), camera);
// Check ray collision aginst ground quad
RayCollision groundHitInfo = GetRayCollisionQuad(ray, g0, g1, g2, g3);
if (groundHitInfo.Hit && (groundHitInfo.Distance < collision.Distance))
{
collision = groundHitInfo;
cursorColor = Color.Green;
hitObjectName = "Ground";
}
// Check ray collision against test triangle
RayCollision triHitInfo = GetRayCollisionTriangle(ray, ta, tb, tc);
if (triHitInfo.Hit && (triHitInfo.Distance < collision.Distance))
{
collision = triHitInfo;
cursorColor = Color.Purple;
hitObjectName = "Triangle";
bary = Vector3Barycenter(collision.Point, ta, tb, tc);
}
// Check ray collision against test sphere
RayCollision sphereHitInfo = GetRayCollisionSphere(ray, sp, sr);
if ((sphereHitInfo.Hit) && (sphereHitInfo.Distance < collision.Distance))
{
collision = sphereHitInfo;
cursorColor = Color.Orange;
hitObjectName = "Sphere";
}
// Check ray collision against bounding box first, before trying the full ray-mesh test
RayCollision boxHitInfo = GetRayCollisionBox(ray, towerBBox);
if (boxHitInfo.Hit && boxHitInfo.Distance < collision.Distance)
{
collision = boxHitInfo;
cursorColor = Color.Orange;
hitObjectName = "Box";
// Check ray collision against model meshes
RayCollision meshHitInfo = new();
for (int m = 0; m < tower.MeshCount; m++)
{
// NOTE: We consider the model.Transform for the collision check but
// it can be checked against any transform matrix, used when checking against same
// model drawn multiple times with multiple transforms
meshHitInfo = GetRayCollisionMesh(ray, tower.Meshes[m], tower.Transform);
if (meshHitInfo.Hit)
{
// Save the closest hit mesh
if ((!collision.Hit) || (collision.Distance > meshHitInfo.Distance))
{
collision = meshHitInfo;
}
break;
}
}
if (meshHitInfo.Hit)
{
collision = meshHitInfo;
cursorColor = Color.Orange;
hitObjectName = "Mesh";
}
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
// Draw the tower
DrawModel(tower, towerPos, 1.0f, Color.White);
// Draw the test triangle
DrawLine3D(ta, tb, Color.Purple);
DrawLine3D(tb, tc, Color.Purple);
DrawLine3D(tc, ta, Color.Purple);
// Draw the test sphere
DrawSphereWires(sp, sr, 8, 8, Color.Purple);
// Draw the mesh bbox if we hit it
if (boxHitInfo.Hit)
{
DrawBoundingBox(towerBBox, Color.Lime);
}
// If we hit something, draw the cursor at the hit point
if (collision.Hit)
{
DrawCube(collision.Point, 0.3f, 0.3f, 0.3f, cursorColor);
DrawCubeWires(collision.Point, 0.3f, 0.3f, 0.3f, Color.Red);
Vector3 normalEnd = collision.Point + collision.Normal;
DrawLine3D(collision.Point, normalEnd, Color.Red);
}
DrawRay(ray, Color.Maroon);
DrawGrid(10, 10.0f);
EndMode3D();
// Draw some debug GUI text
DrawText($"Hit Object: {hitObjectName}", 10, 50, 10, Color.Black);
if (collision.Hit)
{
int ypos = 70;
DrawText($"Distance: {collision.Distance}", 10, ypos, 10, Color.Black);
DrawText($"Hit Pos: {collision.Point}", 10, ypos + 15, 10, Color.Black);
DrawText($"Hit Norm: {collision.Normal}", 10, ypos + 30, 10, Color.Black);
if (triHitInfo.Hit)
{
DrawText($"Barycenter: {bary}", 10, ypos + 45, 10, Color.Black);
}
}
DrawText("Right click mouse to toggle camera controls", 10, 430, 10, Color.Gray);
DrawText("(c) Turret 3D model by Alberto Cano", screenWidth - 200, screenHeight - 20, 10, Color.Gray);
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadModel(tower);
UnloadTexture(texture);
CloseWindow();
//--------------------------------------------------------------------------------------

View file

@ -1,34 +1,36 @@
/*******************************************************************************************
*
* raylib [models] example - Draw textured cube
* raylib [models] example - textured cube
*
* Example complexity rating: [★★☆☆] 2/4
*
* Example originally created with raylib 4.5, last time updated with raylib 4.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) 2022-2023 Ramon Santamaria (@raysan5)
* Copyright (c) 2022-2025 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
using System.Numerics;
using static Raylib_cs.Raylib;
namespace Examples.Models;
public class ModelCubeTexture
public partial class ModelCubeTexture : IExample
{
public static int Main()
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Models / Model Cube Texture";
public string Title => "raylib [models] example - textured cube";
private Camera3D camera;
private Texture2D texture;
public void Init()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [models] example - draw cube texture");
// Define the camera to look into our 3d world
Camera3D camera;
camera = new();
camera.Position = new Vector3(0.0f, 10.0f, 10.0f);
camera.Target = new Vector3(0.0f, 0.0f, 0.0f);
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
@ -36,60 +38,48 @@ public class ModelCubeTexture
camera.Projection = CameraProjection.Perspective;
// Load texture to be applied to the cubes sides
Texture2D texture = LoadTexture("resources/cubicmap_atlas.png");
texture = LoadTexture("resources/cubicmap_atlas.png");
}
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
public void Update()
{
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
// Main game loop
while (!WindowShouldClose())
{
// Update
//----------------------------------------------------------------------------------
//----------------------------------------------------------------------------------
BeginMode3D(camera);
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
// Draw cube with an applied texture
DrawCubeTexture(texture, new Vector3(-2.0f, 2.0f, 0.0f), 2.0f, 4.0f, 2.0f, Color.White);
BeginMode3D(camera);
// Draw cube with an applied texture, but only a defined rectangle piece of the texture
DrawCubeTextureRec(
texture,
new Rectangle(0, texture.Height / 2, texture.Width / 2, texture.Height / 2),
new Vector3(2.0f, 1.0f, 0.0f),
2.0f,
2.0f,
2.0f,
Color.White
);
// Draw cube with an applied texture
DrawCubeTexture(texture, new Vector3(-2.0f, 2.0f, 0.0f), 2.0f, 4.0f, 2.0f, Color.White);
DrawGrid(10, 1.0f);
// Draw cube with an applied texture, but only a defined rectangle piece of the texture
DrawCubeTextureRec(
texture,
new Rectangle(0, texture.Height / 2, texture.Width / 2, texture.Height / 2),
new Vector3(2.0f, 1.0f, 0.0f),
2.0f,
2.0f,
2.0f,
Color.White
);
EndMode3D();
DrawGrid(10, 1.0f);
EndDrawing();
//----------------------------------------------------------------------------------
}
EndMode3D();
EndDrawing();
//----------------------------------------------------------------------------------
}
// De-Initialization
//--------------------------------------------------------------------------------------
public void Unload()
{
UnloadTexture(texture);
CloseWindow();
//--------------------------------------------------------------------------------------
return 0;
}
// Draw cube textured
// NOTE: Cube position is the center position
static void DrawCubeTexture(
private static void DrawCubeTexture(
Texture2D texture,
Vector3 position,
float width,
@ -98,9 +88,9 @@ public class ModelCubeTexture
Color color
)
{
float x = position.X;
float y = position.Y;
float z = position.Z;
var x = position.X;
var y = position.Y;
var z = position.Z;
// Set desired texture to be enabled while drawing following vertex data
Rlgl.SetTexture(texture.Id);
@ -218,7 +208,7 @@ public class ModelCubeTexture
}
// Draw cube with texture piece applied to all faces
static void DrawCubeTextureRec(
private static void DrawCubeTextureRec(
Texture2D texture,
Rectangle source,
Vector3 position,
@ -228,11 +218,11 @@ public class ModelCubeTexture
Color color
)
{
float x = position.X;
float y = position.Y;
float z = position.Z;
float texWidth = (float)texture.Width;
float texHeight = (float)texture.Height;
var x = position.X;
var y = position.Y;
var z = position.Z;
var texWidth = (float)texture.Width;
var texHeight = (float)texture.Height;
// Set desired texture to be enabled while drawing following vertex data
Rlgl.SetTexture(texture.Id);
@ -312,5 +302,32 @@ public class ModelCubeTexture
Rlgl.SetTexture(0);
}
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [models] example - textured cube");
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
var game = new ModelCubeTexture();
game.Init();
// Main game loop
while (!WindowShouldClose())
{
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow();
//--------------------------------------------------------------------------------------
return 0;
}
}

View file

@ -1,157 +1,197 @@
/*******************************************************************************************
*
* raylib [models] example - Models loading
* raylib [models] example - loading
*
* raylib supports multiple models file formats:
* Example complexity rating: [★☆☆☆] 1/4
*
* - OBJ > Text file, must include vertex position-texcoords-normals information,
* if files references some .mtl materials file, it will be loaded (or try to)
* - GLTF > Modern text/binary file format, includes lot of information and it could
* also reference external files, raylib will try loading mesh and materials data
* - IQM > Binary file format including mesh vertex data but also animation data,
* raylib can load .iqm animations.
* NOTE: raylib supports multiple models file formats:
*
* 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)
* - OBJ > Text file format. Must include vertex position-texcoords-normals information,
* if .obj references some .mtl materials file, it will be tried to be loaded
* - GLTF/GLB > Text/binary file formats. Includes lot of information and it could
* also reference external files, mesh and materials data will be tried to be loaded
* - IQM > Binary file format. Includes mesh vertex data but also animation data,
* meshes and animation data can be loaded
* - VOX > Binary file format. MagikaVoxel mesh format:
* https://github.com/ephtracy/voxel-model/blob/master/MagicaVoxel-file-format-vox.txt
* - M3D > Binary file format. Model 3D format:
* https://bztsrc.gitlab.io/model3d
*
* Copyright (c) 2014-2019 Ramon Santamaria (@raysan5)
* Example originally created with raylib 2.0, 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) 2014-2025 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
using System.Numerics;
using static Raylib_cs.Raylib;
namespace Examples.Models;
public class ModelLoading
public partial class ModelLoading : IExample
{
public unsafe static int Main()
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Models / Model Loading";
public string Title => "raylib [models] example - loading";
private Camera3D camera;
private Model model;
private Texture2D texture;
private Vector3 position;
private BoundingBox bounds;
private bool selected;
public unsafe void Init()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [models] example - models loading");
// Define the camera to look into our 3d world
Camera3D camera = new();
camera.Position = new Vector3(50.0f, 50.0f, 50.0f);
camera.Target = new Vector3(0.0f, 10.0f, 0.0f);
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
camera.FovY = 45.0f;
camera.Projection = CameraProjection.Perspective;
camera = new();
camera.Position = new Vector3(50.0f, 50.0f, 50.0f); // Camera position
camera.Target = new Vector3(0.0f, 12.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
Model model = LoadModel("resources/models/obj/castle.obj");
Texture2D texture = LoadTexture("resources/models/obj/castle_diffuse.png");
model = LoadModel("resources/models/obj/castle.obj"); // Load model
texture = LoadTexture("resources/models/obj/castle_diffuse.png"); // Load model texture
// Set map diffuse texture
Raylib.SetMaterialTexture(ref model, 0, MaterialMapIndex.Albedo, ref texture);
Vector3 position = new(0.0f, 0.0f, 0.0f);
BoundingBox bounds = GetMeshBoundingBox(model.Meshes[0]);
position = new(0.0f, 0.0f, 0.0f); // Set model position
bounds = GetMeshBoundingBox(model.Meshes[0]); // Set model bounds
// NOTE: bounds are calculated from the original size of the model,
// if model is scaled on drawing, bounds must be also scaled
bool selected = false;
selected = false;
}
public unsafe void Update()
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Orbital);
#if BROWSER
// NOTE: drag-and-drop model loading is not supported in the browser host; default loaded model is kept.
#else
// Load new models/textures on drag&drop
if (IsFileDropped())
{
var droppedFiles = Raylib.GetDroppedFiles();
if (droppedFiles.Length == 1) // Only support one file dropped
{
if (IsFileExtension(droppedFiles[0], ".obj") ||
IsFileExtension(droppedFiles[0], ".gltf") ||
IsFileExtension(droppedFiles[0], ".glb") ||
IsFileExtension(droppedFiles[0], ".vox") ||
IsFileExtension(droppedFiles[0], ".iqm") ||
IsFileExtension(droppedFiles[0], ".m3d") // Model file formats supported
)
{
UnloadModel(model); // Unload previous model
model = LoadModel(droppedFiles[0]); // Load new model
// Set current map diffuse texture
Raylib.SetMaterialTexture(ref model, 0, MaterialMapIndex.Albedo, ref texture);
bounds = GetMeshBoundingBox(model.Meshes[0]);
// Move camera position from target enough distance to visualize model properly
camera.Position.X = bounds.Max.X + 10.0f;
camera.Position.Y = bounds.Max.Y + 10.0f;
camera.Position.Z = bounds.Max.Z + 10.0f;
}
else if (IsFileExtension(droppedFiles[0], ".png")) // Texture file formats supported
{
// Unload current model texture and load new one
UnloadTexture(texture);
texture = LoadTexture(droppedFiles[0]);
Raylib.SetMaterialTexture(ref model, 0, MaterialMapIndex.Albedo, ref texture);
}
}
}
#endif
// Select model on mouse click
if (IsMouseButtonPressed(MouseButton.Left))
{
// Check collision between ray and box
if (GetRayCollisionBox(GetScreenToWorldRay(GetMousePosition(), camera), bounds).Hit)
{
selected = !selected;
}
else
{
selected = false;
}
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawModel(model, position, 1.0f, Color.White); // Draw 3d model with texture
DrawGrid(20, 10.0f); // Draw a grid
if (selected)
{
DrawBoundingBox(bounds, Color.Green); // Draw selection box
}
EndMode3D();
DrawText("Drag & drop model to load mesh/texture.", 10, GetScreenHeight() - 20, 10, Color.DarkGray);
if (selected)
{
DrawText("MODEL SELECTED", GetScreenWidth() - 110, 10, 10, Color.Green);
}
DrawText("(c) Castle 3D model by Alberto Cano", screenWidth - 200, screenHeight - 20, 10, Color.Gray);
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadTexture(texture);
UnloadModel(model);
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [models] example - loading");
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
var game = new ModelLoading();
game.Init();
// Main game loop
while (!WindowShouldClose())
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Orbital);
if (IsFileDropped())
{
string[] droppedFiles = Raylib.GetDroppedFiles();
if (droppedFiles.Length == 1)
{
if (IsFileExtension(droppedFiles[0], ".obj") ||
IsFileExtension(droppedFiles[0], ".gltf") ||
IsFileExtension(droppedFiles[0], ".glb") ||
IsFileExtension(droppedFiles[0], ".vox") ||
IsFileExtension(droppedFiles[0], ".iqm") ||
IsFileExtension(droppedFiles[0], ".m3d")
)
{
UnloadModel(model);
model = LoadModel(droppedFiles[0]);
// Set current map diffuse texture
Raylib.SetMaterialTexture(ref model, 0, MaterialMapIndex.Albedo, ref texture);
bounds = GetMeshBoundingBox(model.Meshes[0]);
// TODO: Move camera position from target enough distance to visualize model properly
}
else if (IsFileExtension(droppedFiles[0], ".png"))
{
// Unload model texture and load new one
UnloadTexture(texture);
texture = LoadTexture(droppedFiles[0]);
Raylib.SetMaterialTexture(ref model, 0, MaterialMapIndex.Albedo, ref texture);
}
}
}
// Select model on mouse click
if (IsMouseButtonPressed(MouseButton.Left))
{
// Check collision between ray and box
if (GetRayCollisionBox(GetScreenToWorldRay(GetMousePosition(), camera), bounds).Hit)
{
selected = !selected;
}
else
{
selected = false;
}
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawModel(model, position, 1.0f, Color.White);
DrawGrid(20, 10.0f);
if (selected)
{
DrawBoundingBox(bounds, Color.Green);
}
EndMode3D();
DrawText("Drag & drop model to load mesh/texture.", 10, GetScreenHeight() - 20, 10, Color.DarkGray);
if (selected)
{
DrawText("MODEL SELECTED", GetScreenWidth() - 110, 10, 10, Color.Green);
}
DrawText("(c) Castle 3D model by Alberto Cano", screenWidth - 200, screenHeight - 20, 10, Color.Gray);
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadTexture(texture);
UnloadModel(model);
CloseWindow();
//--------------------------------------------------------------------------------------

View file

@ -1,111 +1,135 @@
/*******************************************************************************************
*
* raylib [models] example - Show the difference between perspective and orthographic projection
* raylib [models] example - orthographic projection
*
* This program is heavily based on the geometric objects example
* Example complexity rating: [★☆☆☆] 1/4
*
* This example has been created using raylib 1.9.7 (www.raylib.com)
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
* Example originally created with raylib 2.0, last time updated with raylib 3.7
*
* Copyright (c) 2018 Max Danielsson ref Ramon Santamaria (@raysan5)
* Example contributed by Max Danielsson (@autious) 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 Max Danielsson (@autious) and Ramon Santamaria (@raysan5)
*
********************************************************************************************/
using System.Numerics;
using static Raylib_cs.Raylib;
namespace Examples.Models;
public class OrthographicProjection
public partial class OrthographicProjection : IExample
{
public const float FOVY_PERSPECTIVE = 45.0f;
public const float WIDTH_ORTHOGRAPHIC = 10.0f;
public static int Main()
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Models / Orthographic Projection";
public string Title => "raylib [models] example - orthographic projection";
private Camera3D camera;
public void Init()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [models] example - geometric shapes");
// Define the camera to look into our 3d world
Camera3D camera = new();
camera = new();
camera.Position = new Vector3(0.0f, 10.0f, 10.0f);
camera.Target = new Vector3(0.0f, 0.0f, 0.0f);
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
camera.FovY = FOVY_PERSPECTIVE;
camera.Projection = CameraProjection.Perspective;
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
if (IsKeyPressed(KeyboardKey.Space))
{
if (camera.Projection == CameraProjection.Perspective)
{
camera.FovY = WIDTH_ORTHOGRAPHIC;
camera.Projection = CameraProjection.Orthographic;
}
else
{
camera.FovY = FOVY_PERSPECTIVE;
camera.Projection = CameraProjection.Perspective;
}
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawCube(new Vector3(-4.0f, 0.0f, 2.0f), 2.0f, 5.0f, 2.0f, Color.Red);
DrawCubeWires(new Vector3(-4.0f, 0.0f, 2.0f), 2.0f, 5.0f, 2.0f, Color.Gold);
DrawCubeWires(new Vector3(-4.0f, 0.0f, -2.0f), 3.0f, 6.0f, 2.0f, Color.Maroon);
DrawSphere(new Vector3(-1.0f, 0.0f, -2.0f), 1.0f, Color.Green);
DrawSphereWires(new Vector3(1.0f, 0.0f, 2.0f), 2.0f, 16, 16, Color.Lime);
DrawCylinder(new Vector3(4.0f, 0.0f, -2.0f), 1.0f, 2.0f, 3.0f, 4, Color.SkyBlue);
DrawCylinderWires(new Vector3(4.0f, 0.0f, -2.0f), 1.0f, 2.0f, 3.0f, 4, Color.DarkBlue);
DrawCylinderWires(new Vector3(4.5f, -1.0f, 2.0f), 1.0f, 1.0f, 2.0f, 6, Color.Brown);
DrawCylinder(new Vector3(1.0f, 0.0f, -4.0f), 0.0f, 1.5f, 3.0f, 8, Color.Gold);
DrawCylinderWires(new Vector3(1.0f, 0.0f, -4.0f), 0.0f, 1.5f, 3.0f, 8, Color.Pink);
DrawGrid(10, 1.0f); // Draw a grid
EndMode3D();
DrawText("Press Spacebar to switch camera type", 10, GetScreenHeight() - 30, 20, Color.DarkGray);
if (camera.Projection == CameraProjection.Orthographic)
{
DrawText("ORTHOGRAPHIC", 10, 40, 20, Color.Black);
}
else if (camera.Projection == CameraProjection.Perspective)
{
DrawText("PERSPECTIVE", 10, 40, 20, Color.Black);
}
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [models] example - orthographic projection");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new OrthographicProjection();
game.Init();
// Main game loop
while (!WindowShouldClose())
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
if (IsKeyPressed(KeyboardKey.Space))
{
if (camera.Projection == CameraProjection.Perspective)
{
camera.FovY = WIDTH_ORTHOGRAPHIC;
camera.Projection = CameraProjection.Orthographic;
}
else
{
camera.FovY = FOVY_PERSPECTIVE;
camera.Projection = CameraProjection.Perspective;
}
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawCube(new Vector3(-4.0f, 0.0f, 2.0f), 2.0f, 5.0f, 2.0f, Color.Red);
DrawCubeWires(new Vector3(-4.0f, 0.0f, 2.0f), 2.0f, 5.0f, 2.0f, Color.Gold);
DrawCubeWires(new Vector3(-4.0f, 0.0f, -2.0f), 3.0f, 6.0f, 2.0f, Color.Maroon);
DrawSphere(new Vector3(-1.0f, 0.0f, -2.0f), 1.0f, Color.Green);
DrawSphereWires(new Vector3(1.0f, 0.0f, 2.0f), 2.0f, 16, 16, Color.Lime);
DrawCylinder(new Vector3(4.0f, 0.0f, -2.0f), 1.0f, 2.0f, 3.0f, 4, Color.SkyBlue);
DrawCylinderWires(new Vector3(4.0f, 0.0f, -2.0f), 1.0f, 2.0f, 3.0f, 4, Color.DarkBlue);
DrawCylinderWires(new Vector3(4.5f, -1.0f, 2.0f), 1.0f, 1.0f, 2.0f, 6, Color.Brown);
DrawCylinder(new Vector3(1.0f, 0.0f, -4.0f), 0.0f, 1.5f, 3.0f, 8, Color.Gold);
DrawCylinderWires(new Vector3(1.0f, 0.0f, -4.0f), 0.0f, 1.5f, 3.0f, 8, Color.Pink);
DrawGrid(10, 1.0f);
EndMode3D();
DrawText("Press Spacebar to switch camera type", 10, GetScreenHeight() - 30, 20, Color.DarkGray);
if (camera.Projection == CameraProjection.Orthographic)
{
DrawText("ORTHOGRAPHIC", 10, 40, 20, Color.Black);
}
else if (camera.Projection == CameraProjection.Perspective)
{
DrawText("PERSPECTIVE", 10, 40, 20, Color.Black);
}
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow();
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;

View file

@ -0,0 +1,231 @@
/*******************************************************************************************
*
* raylib [models] example - point rendering
*
* Example complexity rating: [★★★☆] 3/4
*
* Example originally created with raylib 5.0, last time updated with raylib 5.0
*
* Example contributed by Reese Gallagher (@satchelfrost) 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) 2024-2025 Reese Gallagher (@satchelfrost)
*
********************************************************************************************/
namespace Examples.Models;
[ExcludeFromBrowser("rlEnablePointMode needs glPolygonMode, which OpenGL ES/WebGL lacks (renders as triangles)")]
public partial class PointRendering : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
private const int MaxPoints = 10000000; // 10 million
private const int MinPoints = 1000; // 1 thousand
public string Name => "Models / Point Rendering";
public string Title => "raylib [models] example - point rendering";
private static readonly Random Rand = new();
private Camera3D camera;
private Vector3 position;
private bool useDrawModelPoints;
private bool numPointsChanged;
private int numPoints;
private Mesh mesh;
private Model model;
public void Init()
{
camera = new()
{
Position = new Vector3(3.0f, 3.0f, 3.0f),
Target = new Vector3(0.0f, 0.0f, 0.0f),
Up = new Vector3(0.0f, 1.0f, 0.0f),
FovY = 45.0f,
Projection = CameraProjection.Perspective
};
position = new Vector3(0.0f, 0.0f, 0.0f);
useDrawModelPoints = true;
numPointsChanged = false;
numPoints = 1000;
mesh = GenMeshPoints(numPoints);
model = LoadModelFromMesh(mesh);
}
public unsafe void Update()
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Orbital);
if (IsKeyPressed(KeyboardKey.Space))
{
useDrawModelPoints = !useDrawModelPoints;
}
if (IsKeyPressed(KeyboardKey.Up))
{
numPoints = (numPoints * 10 > MaxPoints) ? MaxPoints : numPoints * 10;
numPointsChanged = true;
}
if (IsKeyPressed(KeyboardKey.Down))
{
numPoints = (numPoints / 10 < MinPoints) ? MinPoints : numPoints / 10;
numPointsChanged = true;
}
// Upload a different point cloud size
if (numPointsChanged)
{
UnloadModel(model);
mesh = GenMeshPoints(numPoints);
model = LoadModelFromMesh(mesh);
numPointsChanged = false;
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.Black);
BeginMode3D(camera);
// The new method only uploads the points once to the GPU
if (useDrawModelPoints)
{
DrawModelPoints(model, position, 1.0f, Color.White);
}
else
{
// The old method must continually draw the "points" (lines)
for (var i = 0; i < numPoints; i++)
{
Vector3 pos = new(
mesh.Vertices[i * 3 + 0],
mesh.Vertices[i * 3 + 1],
mesh.Vertices[i * 3 + 2]
);
Color color = new(
mesh.Colors[i * 4 + 0],
mesh.Colors[i * 4 + 1],
mesh.Colors[i * 4 + 2],
mesh.Colors[i * 4 + 3]
);
DrawPoint3D(pos, color);
}
}
// Draw a unit sphere for reference
DrawSphereWires(position, 1.0f, 10, 10, Color.Yellow);
EndMode3D();
// Draw UI text
DrawText($"Point Count: {numPoints}", 10, screenHeight - 50, 40, Color.White);
DrawText("UP - Increase points", 10, 40, 20, Color.White);
DrawText("DOWN - Decrease points", 10, 70, 20, Color.White);
DrawText("SPACE - Drawing function", 10, 100, 20, Color.White);
if (useDrawModelPoints)
{
DrawText("Using: DrawModelPoints()", 10, 130, 20, Color.Green);
}
else
{
DrawText("Using: DrawPoint3D()", 10, 130, 20, Color.Red);
}
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadModel(model);
}
// Generate a spherical point cloud
private static unsafe Mesh GenMeshPoints(int numPoints)
{
Mesh mesh = new(numPoints, 1);
mesh.AllocVertices();
mesh.AllocColors();
// REF: https://en.wikipedia.org/wiki/Spherical_coordinate_system
for (var i = 0; i < numPoints; i++)
{
var theta = MathF.PI * (float)Rand.NextDouble();
var phi = 2.0f * MathF.PI * (float)Rand.NextDouble();
var r = 10.0f * (float)Rand.NextDouble();
mesh.Vertices[i * 3 + 0] = r * MathF.Sin(theta) * MathF.Cos(phi);
mesh.Vertices[i * 3 + 1] = r * MathF.Sin(theta) * MathF.Sin(phi);
mesh.Vertices[i * 3 + 2] = r * MathF.Cos(theta);
var color = ColorFromHSV(r * 360.0f, 1.0f, 1.0f);
mesh.Colors[i * 4 + 0] = color.R;
mesh.Colors[i * 4 + 1] = color.G;
mesh.Colors[i * 4 + 2] = color.B;
mesh.Colors[i * 4 + 3] = color.A;
}
// Upload mesh data from CPU (RAM) to GPU (VRAM) memory
UploadMesh(ref mesh, false);
return mesh;
}
// Draw a model points
// WARNING: OpenGL ES 2.0 does not support point mode drawing
private static void DrawModelPoints(Model model, Vector3 position, float scale, Color tint)
{
Rlgl.EnablePointMode();
Rlgl.DisableBackfaceCulling();
DrawModel(model, position, scale, tint);
Rlgl.EnableBackfaceCulling();
Rlgl.DisablePointMode();
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [models] example - point rendering");
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
var game = new PointRendering();
game.Init();
// Main game loop
while (!WindowShouldClose())
{
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow();
//--------------------------------------------------------------------------------------
return 0;
}
}

View file

@ -0,0 +1,119 @@
/*******************************************************************************************
*
* raylib [models] example - rotating cube
*
* Example complexity rating: [★☆☆☆] 1/4
*
* Example originally created with raylib 6.0, last time updated with raylib 6.0
*
* Example contributed by Jopestpe (@jopestpe)
*
* 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 Jopestpe (@jopestpe)
*
********************************************************************************************/
namespace Examples.Models;
public partial class RotatingCube : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Models / Rotating Cube";
public string Title => "raylib [models] example - rotating cube";
private Camera3D camera;
private Model model;
private Texture2D texture;
private float rotation;
public unsafe void Init()
{
// Define the camera to look into our 3d world
camera = new();
camera.Position = new Vector3(0.0f, 3.0f, 3.0f);
camera.Target = new Vector3(0.0f, 0.0f, 0.0f);
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
camera.FovY = 45.0f;
camera.Projection = CameraProjection.Perspective;
// Load image to create texture for the cube
model = LoadModelFromMesh(GenMeshCube(1.0f, 1.0f, 1.0f));
var img = LoadImage("resources/cubicmap_atlas.png");
var crop = ImageFromImage(img, new Rectangle(0, img.Height / 2.0f, img.Width / 2.0f, img.Height / 2.0f));
texture = LoadTextureFromImage(crop);
UnloadImage(img);
UnloadImage(crop);
model.Materials[0].Maps[(int)MaterialMapIndex.Diffuse].Texture = texture;
rotation = 0.0f;
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
rotation += 1.0f;
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
// Draw model defining: position, size, rotation-axis, rotation (degrees), size, and tint-color
DrawModelEx(model, new Vector3(0.0f, 0.0f, 0.0f), new Vector3(0.5f, 1.0f, 0.0f),
rotation, new Vector3(1.0f, 1.0f, 1.0f), Color.White);
DrawGrid(10, 1.0f);
EndMode3D();
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadTexture(texture); // Unload texture
UnloadModel(model); // Unload model
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [models] example - rotating cube");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new RotatingCube();
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,32 +1,50 @@
/*******************************************************************************************
*
* raylib [models] example - Skybox loading and drawing
* raylib [models] example - skybox rendering
*
* This example has been created using raylib 1.8 (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.8, 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) 2017-2025 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
using System.Numerics;
using static Raylib_cs.Raylib;
namespace Examples.Models;
public class SkyboxDemo
[ExcludeFromBrowser("cubemap generation is too memory-heavy on web (upstream note)")]
public partial class SkyboxDemo : IExample
{
public static int Main()
private const int screenWidth = 800;
private const int screenHeight = 450;
// GLSL version used for shaders (330 desktop, 100 web/GLES)
#if BROWSER
public const int GlslVersion = 100;
#else
public const int GlslVersion = 330;
#endif
public string Name => "Models / Skybox Demo";
public string Title => "raylib [models] example - skybox rendering";
public bool CursorDisabled => true;
private Camera3D camera;
private Model skybox;
private bool useHdr;
private Shader shdrCubemap;
private string skyboxFileName;
private Texture2D panorama;
public void Init()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [models] example - skybox loading and drawing");
// Define the camera to look into our 3d world
Camera3D camera = new();
camera = new();
camera.Position = new Vector3(1.0f, 1.0f, 1.0f);
camera.Target = new Vector3(4.0f, 1.0f, 4.0f);
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
@ -34,14 +52,19 @@ public class SkyboxDemo
camera.Projection = CameraProjection.Perspective;
// Load skybox model
Mesh cube = GenMeshCube(1.0f, 1.0f, 1.0f);
Model skybox = LoadModelFromMesh(cube);
var cube = GenMeshCube(1.0f, 1.0f, 1.0f);
skybox = LoadModelFromMesh(cube);
bool useHdr = false;
// Set this to true to use an HDR Texture
// NOTE: raylib must be built with HDR Support for this to work: SUPPORT_FILEFORMAT_HDR
useHdr = false;
// Load skybox shader and set required locations
// NOTE: Some locations are automatically set at shader loading
Shader shdrSkybox = LoadShader("resources/shaders/glsl330/skybox.vs", "resources/shaders/glsl330/skybox.fs");
var shdrSkybox = LoadShader(
$"resources/shaders/glsl{GlslVersion}/skybox.vs",
$"resources/shaders/glsl{GlslVersion}/skybox.fs"
);
Raylib.SetShaderValue(
shdrSkybox,
@ -67,9 +90,9 @@ public class SkyboxDemo
Raylib.SetMaterialShader(ref skybox, 0, ref shdrSkybox);
// Load cubemap shader and setup required shader locations
Shader shdrCubemap = LoadShader(
"resources/shaders/glsl330/cubemap.vs",
"resources/shaders/glsl330/cubemap.fs"
shdrCubemap = LoadShader(
$"resources/shaders/glsl{GlslVersion}/cubemap.vs",
$"resources/shaders/glsl{GlslVersion}/cubemap.fs"
);
Raylib.SetShaderValue(
shdrCubemap,
@ -79,14 +102,12 @@ public class SkyboxDemo
);
// Load skybox
string skyboxFileName = "resources/dresden_square_2k.hdr";
Texture2D panorama;
skyboxFileName = "resources/dresden_square_2k.hdr";
if (useHdr)
{
panorama = LoadTexture(skyboxFileName);
Texture2D cubemap = GenTextureCubemap(
var cubemap = GenTextureCubemap(
shdrCubemap,
panorama,
1024,
@ -97,108 +118,130 @@ public class SkyboxDemo
}
else
{
Image img = LoadImage("resources/skybox.png");
Texture2D cubemap = LoadTextureCubemap(img, CubemapLayout.AutoDetect);
// TODO: WARNING: On PLATFORM_WEB it requires a big amount of memory to process input image
// and generate the required cubemap image to be passed to rlLoadTextureCubemap()
var img = LoadImage("resources/skybox.png");
var cubemap = LoadTextureCubemap(img, CubemapLayout.AutoDetect);
SetMaterialTexture(ref skybox, 0, MaterialMapIndex.Cubemap, ref cubemap);
UnloadImage(img);
}
}
DisableCursor();
public void Update()
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.FirstPerson);
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
// Main game loop
while (!WindowShouldClose())
// Load new cubemap texture on drag & drop
if (IsFileDropped())
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.FirstPerson);
var files = Raylib.GetDroppedFiles();
// Load new cubemap texture on drag & drop
if (IsFileDropped())
if (files.Length == 1)
{
string[] files = Raylib.GetDroppedFiles();
if (files.Length == 1)
if (IsFileExtension(files[0], ".png;.jpg;.hdr;.bmp;.tga"))
{
if (IsFileExtension(files[0], ".png;.jpg;.hdr;.bmp;.tga"))
// Unload cubemap texture and load new one
UnloadTexture(Raylib.GetMaterialTexture(ref skybox, 0, MaterialMapIndex.Cubemap));
if (useHdr)
{
// Unload cubemap texture and load new one
UnloadTexture(Raylib.GetMaterialTexture(ref skybox, 0, MaterialMapIndex.Cubemap));
if (useHdr)
{
panorama = LoadTexture(files[0]);
Texture2D cubemap = GenTextureCubemap(
shdrCubemap,
panorama,
1024,
PixelFormat.UncompressedR8G8B8A8
);
SetMaterialTexture(ref skybox, 0, MaterialMapIndex.Cubemap, ref cubemap);
UnloadTexture(panorama);
}
else
{
Image img = LoadImage(files[0]);
Texture2D cubemap = LoadTextureCubemap(img, CubemapLayout.AutoDetect);
SetMaterialTexture(ref skybox, 0, MaterialMapIndex.Cubemap, ref cubemap);
UnloadImage(img);
}
skyboxFileName = files[0];
panorama = LoadTexture(files[0]);
var cubemap = GenTextureCubemap(
shdrCubemap,
panorama,
1024,
PixelFormat.UncompressedR8G8B8A8
);
SetMaterialTexture(ref skybox, 0, MaterialMapIndex.Cubemap, ref cubemap);
UnloadTexture(panorama);
}
else
{
var img = LoadImage(files[0]);
var cubemap = LoadTextureCubemap(img, CubemapLayout.AutoDetect);
SetMaterialTexture(ref skybox, 0, MaterialMapIndex.Cubemap, ref cubemap);
UnloadImage(img);
}
skyboxFileName = files[0];
}
}
//----------------------------------------------------------------------------------
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
BeginMode3D(camera);
// We are inside the cube, we need to disable backface culling!
Rlgl.DisableBackfaceCulling();
Rlgl.DisableDepthMask();
DrawModel(skybox, Vector3.Zero, 1.0f, Color.White);
Rlgl.EnableBackfaceCulling();
Rlgl.EnableDepthMask();
// We are inside the cube, we need to disable backface culling!
Rlgl.DisableBackfaceCulling();
Rlgl.DisableDepthMask();
DrawModel(skybox, Vector3.Zero, 1.0f, Color.White);
Rlgl.EnableBackfaceCulling();
Rlgl.EnableDepthMask();
DrawGrid(10, 1.0f);
DrawGrid(10, 1.0f);
EndMode3D();
EndMode3D();
if (useHdr)
{
DrawText(
$"Panorama image from hdrihaven.com: {skyboxFileName}",
10,
GetScreenHeight() - 20,
10,
Color.Black
);
}
else
{
DrawText($": {skyboxFileName}", 10, GetScreenHeight() - 20, 10, Color.Black);
}
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
if (useHdr)
{
DrawText(
$"Panorama image from hdrihaven.com: {skyboxFileName}",
10,
GetScreenHeight() - 20,
10,
Color.Black
);
}
else
{
DrawText($": {skyboxFileName}", 10, GetScreenHeight() - 20, 10, Color.Black);
}
// De-Initialization
//--------------------------------------------------------------------------------------
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadShader(Raylib.GetMaterial(ref skybox, 0).Shader);
UnloadTexture(Raylib.GetMaterialTexture(ref skybox, 0, MaterialMapIndex.Cubemap));
UnloadModel(skybox);
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [models] example - skybox rendering");
DisableCursor(); // Limit cursor to relative movement inside the window
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
var game = new SkyboxDemo();
game.Init();
// Main game loop
while (!WindowShouldClose())
{
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow();
//--------------------------------------------------------------------------------------
@ -215,10 +258,10 @@ public class SkyboxDemo
// STEP 1: Setup framebuffer
//------------------------------------------------------------------------------------------
uint rbo = Rlgl.LoadTextureDepth(size, size, true);
var rbo = Rlgl.LoadTextureDepth(size, size, true);
cubemap.Id = Rlgl.LoadTextureCubemap(null, size, format, 1);
uint fbo = Rlgl.LoadFramebuffer();
var fbo = Rlgl.LoadFramebuffer();
Rlgl.FramebufferAttach(
fbo,
rbo,
@ -235,7 +278,7 @@ public class SkyboxDemo
);
// Check if framebuffer is complete with attachments (valid)
if (Rlgl.FramebufferComplete(fbo))
if (Rlgl.FramebufferComplete(fbo) != 0)
{
Console.WriteLine($"FBO: [ID {fbo}] Framebuffer object created successfully");
}
@ -247,7 +290,7 @@ public class SkyboxDemo
Rlgl.EnableShader(shader.Id);
// Define projection matrix and send it to shader
Matrix4x4 matFboProjection = Raymath.MatrixPerspective(
var matFboProjection = Raymath.MatrixPerspective(
90.0f * DEG2RAD,
1.0f,
Rlgl.CULL_DISTANCE_NEAR,
@ -256,14 +299,14 @@ public class SkyboxDemo
Rlgl.SetUniformMatrix(shader.Locs[(int)ShaderLocationIndex.MatrixProjection], matFboProjection);
// Define view matrix for every side of the cubemap
Matrix4x4[] fboViews = new[]
var fboViews = new[]
{
Raymath.MatrixLookAt(Vector3.Zero, new Vector3(-1.0f, 0.0f, 0.0f), new Vector3( 0.0f, -1.0f, 0.0f)),
Raymath.MatrixLookAt(Vector3.Zero, new Vector3( 1.0f, 0.0f, 0.0f), new Vector3( 0.0f, -1.0f, 0.0f)),
Raymath.MatrixLookAt(Vector3.Zero, new Vector3(-1.0f, 0.0f, 0.0f), new Vector3( 0.0f, -1.0f, 0.0f)),
Raymath.MatrixLookAt(Vector3.Zero, new Vector3( 0.0f, 1.0f, 0.0f), new Vector3( 0.0f, 0.0f, 1.0f)),
Raymath.MatrixLookAt(Vector3.Zero, new Vector3( 0.0f, -1.0f, 0.0f), new Vector3( 0.0f, 0.0f, -1.0f)),
Raymath.MatrixLookAt(Vector3.Zero, new Vector3( 0.0f, 0.0f, -1.0f), new Vector3( 0.0f, -1.0f, 0.0f)),
Raymath.MatrixLookAt(Vector3.Zero, new Vector3( 0.0f, 0.0f, 1.0f), new Vector3( 0.0f, -1.0f, 0.0f)),
Raymath.MatrixLookAt(Vector3.Zero, new Vector3( 0.0f, 0.0f, -1.0f), new Vector3( 0.0f, -1.0f, 0.0f)),
};
// Set viewport to current fbo dimensions
@ -273,7 +316,7 @@ public class SkyboxDemo
Rlgl.ActiveTextureSlot(0);
Rlgl.EnableTexture(panorama.Id);
for (int i = 0; i < 6; i++)
for (var i = 0; i < 6; i++)
{
// Set the view matrix for the current cube face
Rlgl.SetUniformMatrix(shader.Locs[(int)ShaderLocationIndex.MatrixView], fboViews[i]);

View file

@ -1,161 +1,153 @@
/*******************************************************************************************
*
* raylib [models] example - rlgl module usage with push/pop matrix transformations
* raylib [models] example - rlgl solar system
*
* This example uses [rlgl] module funtionality (pseudo-OpenGL 1.1 style coding)
* Example complexity rating: [★★★★] 4/4
*
* 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)
* NOTE: This example uses [rlgl] module functionality (pseudo-OpenGL 1.1 style coding)
*
* Copyright (c) 2018 Ramon Santamaria (@raysan5)
* Example originally created with raylib 2.5, 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) 2018-2025 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
using System;
using System.Numerics;
using static Raylib_cs.Raylib;
namespace Examples.Models;
public class SolarSystem
public partial class SolarSystem : IExample
{
public static int Main()
private const int screenWidth = 800;
private const int screenHeight = 450;
private const float sunRadius = 4.0f;
private const float earthRadius = 0.6f;
private const float earthOrbitRadius = 8.0f;
private const float moonRadius = 0.16f;
private const float moonOrbitRadius = 1.5f;
public string Name => "Models / Solar System";
public string Title => "raylib [models] example - rlgl solar system";
private Camera3D camera;
private float rotationSpeed;
private float earthRotation;
private float earthOrbitRotation;
private float moonRotation;
private float moonOrbitRotation;
public void Init()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
const float sunRadius = 4.0f;
const float earthRadius = 0.6f;
const float earthOrbitRadius = 8.0f;
const float moonRadius = 0.16f;
const float moonOrbitRadius = 1.5f;
InitWindow(screenWidth, screenHeight, "raylib [models] example - rlgl module usage with push/pop matrix transformations");
// Define the camera to look into our 3d world
Camera3D camera = new();
camera.Position = new Vector3(16.0f, 16.0f, 16.0f);
camera.Target = new Vector3(0.0f, 0.0f, 0.0f);
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
camera.FovY = 45.0f;
camera.Projection = CameraProjection.Perspective;
camera = new();
camera.Position = new Vector3(16.0f, 16.0f, 16.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
// General system rotation speed
float rotationSpeed = 0.2f;
// Rotation of earth around itself (days) in degrees
float earthRotation = 0.0f;
// Rotation of earth around the Sun (years) in degrees
float earthOrbitRotation = 0.0f;
// Rotation of moon around itself
float moonRotation = 0.0f;
// Rotation of moon around earth in degrees
float moonOrbitRotation = 0.0f;
rotationSpeed = 0.2f; // General system rotation speed
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
earthRotation = 0.0f; // Rotation of earth around itself (days) in degrees
earthOrbitRotation = 0.0f; // Rotation of earth around the Sun (years) in degrees
moonRotation = 0.0f; // Rotation of moon around itself
moonOrbitRotation = 0.0f; // Rotation of moon around earth in degrees
}
// Main game loop
while (!WindowShouldClose())
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Free);
public void Update()
{
// Update
//----------------------------------------------------------------------------------
earthRotation += (5.0f * rotationSpeed);
earthOrbitRotation += (365 / 360.0f * (5.0f * rotationSpeed) * rotationSpeed);
moonRotation += (2.0f * rotationSpeed);
moonOrbitRotation += (8.0f * rotationSpeed);
//----------------------------------------------------------------------------------
earthRotation += (5.0f * rotationSpeed);
earthOrbitRotation += (365 / 360.0f * (5.0f * rotationSpeed) * rotationSpeed);
moonRotation += (2.0f * rotationSpeed);
moonOrbitRotation += (8.0f * rotationSpeed);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
BeginMode3D(camera);
Rlgl.PushMatrix();
// Scale Sun
Rlgl.Scalef(sunRadius, sunRadius, sunRadius);
// Draw the Sun
DrawSphereBasic(Color.Gold);
Rlgl.PopMatrix();
Rlgl.PushMatrix();
// Scale Sun
Rlgl.Scalef(sunRadius, sunRadius, sunRadius);
// Draw the Sun
DrawSphereBasic(Color.Gold);
Rlgl.PopMatrix();
Rlgl.PushMatrix();
// Rotation for Earth orbit around Sun
Rlgl.Rotatef(earthOrbitRotation, 0.0f, 1.0f, 0.0f);
// Translation for Earth orbit
Rlgl.Translatef(earthOrbitRadius, 0.0f, 0.0f);
Rlgl.PushMatrix();
// Rotation for Earth orbit around Sun
Rlgl.Rotatef(earthOrbitRotation, 0.0f, 1.0f, 0.0f);
// Translation for Earth orbit
Rlgl.Translatef(earthOrbitRadius, 0.0f, 0.0f);
// Rotation for Earth orbit around Sun inverted
Rlgl.Rotatef(-earthOrbitRotation, 0.0f, 1.0f, 0.0f);
Rlgl.PushMatrix();
// Rotation for Earth itself
Rlgl.Rotatef(earthRotation, 0.25f, 1.0f, 0.0f);
// Scale Earth
Rlgl.Scalef(earthRadius, earthRadius, earthRadius);
Rlgl.PushMatrix();
// Rotation for Earth itself
Rlgl.Rotatef(earthRotation, 0.25f, 1.0f, 0.0f);
// Scale Earth
Rlgl.Scalef(earthRadius, earthRadius, earthRadius);
// Draw the Earth
DrawSphereBasic(Color.Blue);
Rlgl.PopMatrix();
// Draw the Earth
DrawSphereBasic(Color.Blue);
Rlgl.PopMatrix();
// Rotation for Moon orbit around Earth
Rlgl.Rotatef(moonOrbitRotation, 0.0f, 1.0f, 0.0f);
// Translation for Moon orbit
Rlgl.Translatef(moonOrbitRadius, 0.0f, 0.0f);
// Rotation for Moon itself
Rlgl.Rotatef(moonRotation, 0.0f, 1.0f, 0.0f);
// Scale Moon
Rlgl.Scalef(moonRadius, moonRadius, moonRadius);
// Rotation for Moon orbit around Earth
Rlgl.Rotatef(moonOrbitRotation, 0.0f, 1.0f, 0.0f);
// Translation for Moon orbit
Rlgl.Translatef(moonOrbitRadius, 0.0f, 0.0f);
// Rotation for Moon orbit around Earth inverted
Rlgl.Rotatef(-moonOrbitRotation, 0.0f, 1.0f, 0.0f);
// Rotation for Moon itself
Rlgl.Rotatef(moonRotation, 0.0f, 1.0f, 0.0f);
// Scale Moon
Rlgl.Scalef(moonRadius, moonRadius, moonRadius);
// Draw the Moon
DrawSphereBasic(Color.LightGray);
Rlgl.PopMatrix();
// Draw the Moon
DrawSphereBasic(Color.LightGray);
Rlgl.PopMatrix();
// Some reference elements (not affected by previous matrix transformations)
DrawCircle3D(
new Vector3(0.0f, 0.0f, 0.0f),
earthOrbitRadius,
new Vector3(1, 0, 0),
90.0f,
Fade(Color.Red, 0.5f)
);
DrawGrid(20, 1.0f);
// Some reference elements (not affected by previous matrix transformations)
DrawCircle3D(
new Vector3(0.0f, 0.0f, 0.0f),
earthOrbitRadius,
new Vector3(1, 0, 0),
90.0f,
ColorAlpha(Color.Red, 0.5f)
);
DrawGrid(20, 1.0f);
EndMode3D();
EndMode3D();
DrawText("EARTH ORBITING AROUND THE SUN!", 400, 10, 20, Color.Maroon);
DrawFPS(10, 10);
DrawText("EARTH ORBITING AROUND THE SUN!", 400, 10, 20, Color.Maroon);
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
}
EndDrawing();
//----------------------------------------------------------------------------------
}
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow();
//--------------------------------------------------------------------------------------
return 0;
public void Unload()
{
}
// Draw sphere without any matrix transformation
// NOTE: Sphere is drawn in world position ( 0, 0, 0 ) with radius 1.0f
static void DrawSphereBasic(Color color)
private static void DrawSphereBasic(Color color)
{
int rings = 16;
int slices = 16;
var rings = 16;
var slices = 16;
Rlgl.Begin(DrawMode.Triangles);
Rlgl.Color4ub(color.R, color.G, color.B, color.A);
for (int i = 0; i < (rings + 2); i++)
for (var i = 0; i < (rings + 2); i++)
{
for (int j = 0; j < slices; j++)
for (var j = 0; j < slices; j++)
{
Rlgl.Vertex3f(
MathF.Cos(DEG2RAD * (270 + (180 / (rings + 1)) * i)) * MathF.Sin(DEG2RAD * (j * 360 / slices)),
@ -192,5 +184,32 @@ public class SolarSystem
}
Rlgl.End();
}
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [models] example - rlgl solar system");
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
var game = new SolarSystem();
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,163 @@
/*******************************************************************************************
*
* raylib [models] example - tesseract view
*
* NOTE: This example only works on platforms that support drag & drop (Windows, Linux, OSX, Html5?)
*
* Example complexity rating: [★★☆☆] 2/4
*
* Example originally created with raylib 6.0, last time updated with raylib 6.0
*
* Example contributed by Timothy van der Valk (@arceryz) 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) 2024-2025 Timothy van der Valk (@arceryz) and Ramon Santamaria (@raysan5)
*
********************************************************************************************/
using static Raylib_cs.Raymath;
namespace Examples.Models;
public partial class TesseractView : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Models / Tesseract View";
public string Title => "raylib [models] example - tesseract view";
// Define the camera to look into our 3d world
private Camera3D camera;
// Find the coordinates by setting XYZW to +-1
private Vector4[] tesseract;
private float rotation;
private Vector3[] transformed;
private float[] wValues;
public void Init()
{
// Define the camera to look into our 3d world
camera = new Camera3D();
camera.Position = new Vector3(4.0f, 4.0f, 4.0f); // Camera position
camera.Target = new Vector3(0.0f, 0.0f, 0.0f); // Camera looking at point
camera.Up = new Vector3(0.0f, 0.0f, 1.0f); // Camera up vector (rotation towards target)
camera.FovY = 50.0f; // Camera field-of-view Y
camera.Projection = CameraProjection.Perspective; // Camera mode type
// Find the coordinates by setting XYZW to +-1
tesseract = new Vector4[16]
{
new( 1, 1, 1, 1 ), new( 1, 1, 1, -1 ),
new( 1, 1, -1, 1 ), new( 1, 1, -1, -1 ),
new( 1, -1, 1, 1 ), new( 1, -1, 1, -1 ),
new( 1, -1, -1, 1 ), new( 1, -1, -1, -1 ),
new(-1, 1, 1, 1 ), new(-1, 1, 1, -1 ),
new(-1, 1, -1, 1 ), new(-1, 1, -1, -1 ),
new(-1, -1, 1, 1 ), new(-1, -1, 1, -1 ),
new(-1, -1, -1, 1 ), new(-1, -1, -1, -1 ),
};
rotation = 0.0f;
transformed = new Vector3[16];
wValues = new float[16];
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
rotation = DEG2RAD * 45.0f * (float)GetTime();
for (int i = 0; i < 16; i++)
{
Vector4 p = tesseract[i];
// Rotate the XW part of the vector
Vector2 rotXW = Vector2Rotate(new Vector2(p.X, p.W), rotation);
p.X = rotXW.X;
p.W = rotXW.Y;
// Projection from XYZW to XYZ from perspective point (0, 0, 0, 3)
// NOTE: Trace a ray from (0, 0, 0, 3) > p and continue until W = 0
float c = 3.0f / (3.0f - p.W);
p.X = c * p.X;
p.Y = c * p.Y;
p.Z = c * p.Z;
// Split XYZ coordinate and W values later for drawing
transformed[i] = new Vector3(p.X, p.Y, p.Z);
wValues[i] = p.W;
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
for (int i = 0; i < 16; i++)
{
// Draw spheres to indicate the W value
DrawSphere(transformed[i], MathF.Abs(wValues[i] * 0.1f), Color.Red);
for (int j = 0; j < 16; j++)
{
// Two lines are connected if they differ by 1 coordinate
// This way we dont have to keep an edge list
Vector4 v1 = tesseract[i];
Vector4 v2 = tesseract[j];
int diff = (v1.X == v2.X ? 1 : 0) + (v1.Y == v2.Y ? 1 : 0) + (v1.Z == v2.Z ? 1 : 0) + (v1.W == v2.W ? 1 : 0);
// Draw only differing by 1 coordinate and the lower index only (duplicate lines)
if (diff == 3 && i < j)
{
DrawLine3D(transformed[i], transformed[j], Color.Maroon);
}
}
}
EndMode3D();
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [models] example - tesseract view");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new TesseractView();
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,117 +1,141 @@
/*******************************************************************************************
*
* raylib [models] example - Waving cubes
* raylib [models] example - waving cubes
*
* 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: [★★★☆] 3/4
*
* Example originally created with raylib 2.5, last time updated with raylib 3.7
*
* Example contributed by Codecat (@codecat) and reviewed by Ramon Santamaria (@raysan5)
*
* Copyright (c) 2019 Codecat (@codecat) 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 Codecat (@codecat) and Ramon Santamaria (@raysan5)
*
********************************************************************************************/
using System;
using System.Numerics;
using static Raylib_cs.Raylib;
namespace Examples.Models;
public class WavingCubes
public partial class WavingCubes : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
// Specify the amount of blocks in each direction
private const int numBlocks = 15;
public string Name => "Models / Waving Cubes";
public string Title => "raylib [models] example - waving cubes";
private Camera3D camera;
public void Init()
{
// Initialize the camera
camera = new();
camera.Position = new Vector3(30.0f, 20.0f, 30.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 = 70.0f; // Camera field-of-view Y
camera.Projection = CameraProjection.Perspective; // Camera projection type
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
var time = GetTime();
// Calculate time scale for cube position and size
var scale = (2.0f + (float)Math.Sin(time)) * 0.7f;
// Move camera around the scene
var cameraTime = time * 0.3;
camera.Position.X = (float)Math.Cos(cameraTime) * 40.0f;
camera.Position.Z = (float)Math.Sin(cameraTime) * 40.0f;
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawGrid(10, 5.0f);
for (var x = 0; x < numBlocks; x++)
{
for (var y = 0; y < numBlocks; y++)
{
for (var z = 0; z < numBlocks; z++)
{
// Scale of the blocks depends on x/y/z positions
var blockScale = (x + y + z) / 30.0f;
// Scatter makes the waving effect by adding blockScale over time
var scatter = (float)Math.Sin(blockScale * 20.0f + (float)(time * 4.0f));
// Calculate the cube position
Vector3 cubePos = new(
(float)(x - numBlocks / 2) * (scale * 3.0f) + scatter,
(float)(y - numBlocks / 2) * (scale * 2.0f) + scatter,
(float)(z - numBlocks / 2) * (scale * 3.0f) + scatter
);
// Pick a color with a hue depending on cube position for the rainbow color effect
// NOTE: This function is quite costly to be done per cube and frame,
// pre-catching the results into a separate array could improve performance
var cubeColor = ColorFromHSV((float)(((x + y + z) * 18) % 360), 0.75f, 0.9f);
// Calculate cube size
var cubeSize = (2.4f - scale) * blockScale;
// And finally, draw the cube!
DrawCube(cubePos, cubeSize, cubeSize, cubeSize, cubeColor);
}
}
}
EndMode3D();
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [models] example - waving cubes");
// Initialize the camera
Camera3D camera = new();
camera.Position = new Vector3(30.0f, 20.0f, 30.0f);
camera.Target = new Vector3(0.0f, 0.0f, 0.0f);
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
camera.FovY = 70.0f;
camera.Projection = CameraProjection.Perspective;
// Specify the amount of blocks in each direction
const int numBlocks = 15;
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
var game = new WavingCubes();
game.Init();
// Main game loop
while (!WindowShouldClose())
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
double time = GetTime();
// Calculate time scale for cube position and size
float scale = (2.0f + (float)Math.Sin(time)) * 0.7f;
// Move camera around the scene
double cameraTime = time * 0.3;
camera.Position.X = (float)Math.Cos(cameraTime) * 40.0f;
camera.Position.Z = (float)Math.Sin(cameraTime) * 40.0f;
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawGrid(10, 5.0f);
for (int x = 0; x < numBlocks; x++)
{
for (int y = 0; y < numBlocks; y++)
{
for (int z = 0; z < numBlocks; z++)
{
// Scale of the blocks depends on x/y/z positions
float blockScale = (x + y + z) / 30.0f;
// Scatter makes the waving effect by adding blockScale over time
float scatter = (float)Math.Sin(blockScale * 20.0f + (float)(time * 4.0f));
// Calculate the cube position
Vector3 cubePos = new(
(float)(x - numBlocks / 2) * (scale * 3.0f) + scatter,
(float)(y - numBlocks / 2) * (scale * 2.0f) + scatter,
(float)(z - numBlocks / 2) * (scale * 3.0f) + scatter
);
// Pick a color with a hue depending on cube position for the rainbow color effect
Color cubeColor = ColorFromHSV((float)(((x + y + z) * 18) % 360), 0.75f, 0.9f);
// Calculate cube size
float cubeSize = (2.4f - scale) * blockScale;
// And finally, draw the cube!
DrawCube(cubePos, cubeSize, cubeSize, cubeSize, cubeColor);
}
}
}
EndMode3D();
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow();
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}
}

View file

@ -1,162 +1,196 @@
/*******************************************************************************************
*
* raylib [models] example - Plane rotations (yaw, pitch, roll)
* raylib [models] example - yaw pitch roll
*
* This example has been created using raylib 1.8 (www.raylib.com)
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
* Example complexity rating: [★★☆☆] 2/4
*
* Example originally created with raylib 1.8, last time updated with raylib 4.0
*
* Example contributed by Berni (@Berni8k) and reviewed by Ramon Santamaria (@raysan5)
*
* Copyright (c) 2017-2021 Berni (@Berni8k) 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) 2017-2025 Berni (@Berni8k) and Ramon Santamaria (@raysan5)
*
********************************************************************************************/
using System.Numerics;
using static Raylib_cs.Raylib;
using static Raylib_cs.Raymath;
namespace Examples.Models;
public class YawPitchRoll
public partial class YawPitchRoll : IExample
{
public unsafe static int Main()
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Models / Yaw Pitch Roll";
public string Title => "raylib [models] example - yaw pitch roll";
private Camera3D camera;
private Model model;
private Texture2D texture;
private float pitch;
private float roll;
private float yaw;
public unsafe void Init()
{
camera = new();
camera.Position = new Vector3(0.0f, 50.0f, -120.0f);// Camera position perspective
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 = 30.0f; // Camera field-of-view Y
camera.Projection = CameraProjection.Perspective; // Camera type
model = LoadModel("resources/models/obj/plane.obj"); // Load model
texture = LoadTexture("resources/models/obj/plane_diffuse.png"); // Load model texture
SetTextureWrap(texture, TextureWrap.Repeat); // Force Repeat to avoid issue on Web version
model.Materials[0].Maps[(int)MaterialMapIndex.Diffuse].Texture = texture; // Set map diffuse texture
pitch = 0.0f;
roll = 0.0f;
yaw = 0.0f;
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
// Plane pitch (x-axis) controls
if (IsKeyDown(KeyboardKey.Down))
{
pitch += 0.6f;
}
else if (IsKeyDown(KeyboardKey.Up))
{
pitch -= 0.6f;
}
else
{
if (pitch > 0.3f)
{
pitch -= 0.3f;
}
else if (pitch < -0.3f)
{
pitch += 0.3f;
}
}
// Plane yaw (y-axis) controls
if (IsKeyDown(KeyboardKey.S))
{
yaw += 1.0f;
}
else if (IsKeyDown(KeyboardKey.A))
{
yaw -= 1.0f;
}
else
{
if (yaw > 0.0f)
{
yaw -= 0.5f;
}
else if (yaw < 0.0f)
{
yaw += 0.5f;
}
}
// Plane roll (z-axis) controls
if (IsKeyDown(KeyboardKey.Left))
{
roll += 1.0f;
}
else if (IsKeyDown(KeyboardKey.Right))
{
roll -= 1.0f;
}
else
{
if (roll > 0.0f)
{
roll -= 0.5f;
}
else if (roll < 0.0f)
{
roll += 0.5f;
}
}
// Tranformation matrix for rotations
model.Transform = MatrixRotateXYZ(new Vector3(DEG2RAD * pitch, DEG2RAD * yaw, DEG2RAD * roll));
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
// Draw 3D model (recomended to draw 3D always before 2D)
BeginMode3D(camera);
// Draw 3d model with texture
DrawModel(model, new Vector3(0.0f, -8.0f, 0.0f), 1.0f, Color.White);
DrawGrid(10, 10.0f);
EndMode3D();
// Draw controls info
DrawRectangle(30, 370, 260, 70, Fade(Color.Green, 0.5f));
DrawRectangleLines(30, 370, 260, 70, Fade(Color.DarkGreen, 0.5f));
DrawText("Pitch controlled with: KEY_UP / KEY_DOWN", 40, 380, 10, Color.DarkGray);
DrawText("Roll controlled with: KEY_LEFT / KEY_RIGHT", 40, 400, 10, Color.DarkGray);
DrawText("Yaw controlled with: KEY_A / KEY_S", 40, 420, 10, Color.DarkGray);
DrawText(
"(c) WWI Plane Model created by GiaHanLam",
screenWidth - 240,
screenHeight - 20,
10,
Color.DarkGray
);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadModel(model);
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [models] example - plane rotations (yaw, pitch, roll)");
Camera3D camera = new();
camera.Position = new Vector3(0.0f, 50.0f, -120.0f);
camera.Target = new Vector3(0.0f, 0.0f, 0.0f);
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
camera.FovY = 30.0f;
camera.Projection = CameraProjection.Perspective;
// Model loading
Model model = LoadModel("resources/models/obj/plane.obj");
Texture2D texture = LoadTexture("resources/models/obj/plane_diffuse.png");
model.Materials[0].Maps[(int)MaterialMapIndex.Diffuse].Texture = texture;
float pitch = 0.0f;
float roll = 0.0f;
float yaw = 0.0f;
InitWindow(screenWidth, screenHeight, "raylib [models] example - yaw pitch roll");
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
while (!WindowShouldClose())
var game = new YawPitchRoll();
game.Init();
// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
// Plane roll (x-axis) controls
if (IsKeyDown(KeyboardKey.Down))
{
pitch += 0.6f;
}
else if (IsKeyDown(KeyboardKey.Up))
{
pitch -= 0.6f;
}
else
{
if (pitch > 0.3f)
{
pitch -= 0.3f;
}
else if (pitch < -0.3f)
{
pitch += 0.3f;
}
}
// Plane yaw (y-axis) controls
if (IsKeyDown(KeyboardKey.S))
{
yaw += 1.0f;
}
else if (IsKeyDown(KeyboardKey.A))
{
yaw -= 1.0f;
}
else
{
if (yaw > 0.0f)
{
yaw -= 0.5f;
}
else if (yaw < 0.0f)
{
yaw += 0.5f;
}
}
// Plane pitch (z-axis) controls
if (IsKeyDown(KeyboardKey.Left))
{
roll += 1.0f;
}
else if (IsKeyDown(KeyboardKey.Right))
{
roll -= 1.0f;
}
else
{
if (roll > 0.0f)
{
roll -= 0.5f;
}
else if (roll < 0.0f)
{
roll += 0.5f;
}
}
// Tranformation matrix for rotations
model.Transform = MatrixRotateXYZ(new Vector3(DEG2RAD * pitch, DEG2RAD * yaw, DEG2RAD * roll));
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
// Draw 3D model (recomended to draw 3D always before 2D)
BeginMode3D(camera);
// Draw 3d model with texture
DrawModel(model, new Vector3(0.0f, -8.0f, 0.0f), 1.0f, Color.White);
DrawGrid(10, 10.0f);
EndMode3D();
// Draw controls info
DrawRectangle(30, 370, 260, 70, Fade(Color.Green, 0.5f));
DrawRectangleLines(30, 370, 260, 70, Fade(Color.DarkGreen, 0.5f));
DrawText("Pitch controlled with: KEY_UP / KEY_DOWN", 40, 380, 10, Color.DarkGray);
DrawText("Roll controlled with: KEY_LEFT / KEY_RIGHT", 40, 400, 10, Color.DarkGray);
DrawText("Yaw controlled with: KEY_A / KEY_S", 40, 420, 10, Color.DarkGray);
DrawText(
"(c) WWI Plane Model created by GiaHanLam",
screenWidth - 240,
screenHeight - 20,
10,
Color.DarkGray
);
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadModel(model);
CloseWindow();
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;

View file

@ -1,195 +1,88 @@
#nullable enable
using System.Diagnostics;
using Examples.Core;
using Examples.Shapes;
using Examples.Textures;
using Examples.Text;
using Examples.Models;
using Examples.Shaders;
using Examples.Audio;
namespace Examples;
public class ExampleInfo
internal static class Program
{
public ExampleInfo(string name, Func<int> main)
{
this.Name = name;
this.Main = main;
}
public string Name
{
get; set;
}
public Func<int> Main
{
get; set;
}
}
public class ExampleList
{
public static ExampleInfo[] AllExamples = new[]
{
// Core
new ExampleInfo("DeltaTime", DeltaTime.Main),
new ExampleInfo("InputGesturesTestBed", InputGesturesTestBed.Main),
new ExampleInfo("InputVirtualControls", InputVirtualControls.Main),
new ExampleInfo("Camera2dPlatformer", Camera2dPlatformer.Main),
new ExampleInfo("Camera2dDemo", Camera2dDemo.Main),
new ExampleInfo("Camera3dFirstPerson", Camera3dFirstPerson.Main),
new ExampleInfo("Camera3dFree", Camera3dFree.Main),
new ExampleInfo("Camera3dMode", Camera3dMode.Main),
new ExampleInfo("Picking3d", Picking3d.Main),
new ExampleInfo("BasicScreenManager", BasicScreenManager.Main),
new ExampleInfo("BasicWindow", BasicWindow.Main),
new ExampleInfo("CustomLogging", CustomLogging.Main),
new ExampleInfo("DropFiles", DropFiles.Main),
new ExampleInfo("InputGamepad", InputGamepad.Main),
new ExampleInfo("InputGestures", InputGestures.Main),
new ExampleInfo("InputKeys", InputKeys.Main),
new ExampleInfo("InputMouseWheel", InputMouseWheel.Main),
new ExampleInfo("InputMouse", InputMouse.Main),
new ExampleInfo("InputMultitouch", InputMultitouch.Main),
new ExampleInfo("RandomValues", RandomValues.Main),
new ExampleInfo("ScissorTest", ScissorTest.Main),
new ExampleInfo("SmoothPixelPerfect", SmoothPixelPerfect.Main),
new ExampleInfo("SplitScreen", SplitScreen.Main),
new ExampleInfo("StorageValues", StorageValues.Main),
new ExampleInfo("VrSimulator", VrSimulator.Main),
new ExampleInfo("WindowFlags", WindowFlags.Main),
new ExampleInfo("WindowLetterbox", WindowLetterbox.Main),
new ExampleInfo("WorldScreen", WorldScreen.Main),
// Shapes
new ExampleInfo("BasicShapes", BasicShapes.Main),
new ExampleInfo("BouncingBall", BouncingBall.Main),
new ExampleInfo("CollisionArea", CollisionArea.Main),
new ExampleInfo("ColorsPalette", ColorsPalette.Main),
new ExampleInfo("EasingsBallAnim", EasingsBallAnim.Main),
new ExampleInfo("EasingsBoxAnim", EasingsBoxAnim.Main),
new ExampleInfo("EasingsRectangleArray", EasingsRectangleArray.Main),
new ExampleInfo("FollowingEyes", FollowingEyes.Main),
new ExampleInfo("LinesBezier", LinesBezier.Main),
new ExampleInfo("LogoRaylibAnim", LogoRaylibAnim.Main),
new ExampleInfo("LogoRaylibShape", LogoRaylibShape.Main),
new ExampleInfo("RectangleScaling", RectangleScaling.Main),
// Textures
new ExampleInfo("BackgroundScrolling", BackgroundScrolling.Main),
new ExampleInfo("BlendModes", BlendModes.Main),
new ExampleInfo("Bunnymark", Bunnymark.Main),
new ExampleInfo("DrawTiled", DrawTiled.Main),
new ExampleInfo("ImageDrawing", ImageDrawing.Main),
new ExampleInfo("ImageGeneration", ImageGeneration.Main),
new ExampleInfo("ImageLoading", ImageLoading.Main),
new ExampleInfo("ImageProcessing", ImageProcessing.Main),
new ExampleInfo("ImageText", ImageText.Main),
new ExampleInfo("LogoRaylibTexture", LogoRaylibTexture.Main),
new ExampleInfo("MousePainting", MousePainting.Main),
new ExampleInfo("NpatchDrawing", NpatchDrawing.Main),
new ExampleInfo("ParticlesBlending", ParticlesBlending.Main),
new ExampleInfo("TexturedCurve", TexturedCurve.Main),
new ExampleInfo("Polygon", Polygon.Main),
new ExampleInfo("RawData", RawData.Main),
new ExampleInfo("SpriteAnim", SpriteAnim.Main),
new ExampleInfo("SpriteButton", SpriteButton.Main),
new ExampleInfo("SpriteExplosion", SpriteExplosion.Main),
new ExampleInfo("SrcRecDstRec", SrcRecDstRec.Main),
new ExampleInfo("ToImage", ToImage.Main),
// Text
new ExampleInfo("CodepointsLoading", CodepointsLoading.Main),
new ExampleInfo("FontFilters", FontFilters.Main),
new ExampleInfo("FontLoading", FontLoading.Main),
new ExampleInfo("FontSdf", FontSdf.Main),
new ExampleInfo("FontSpritefont", FontSpritefont.Main),
new ExampleInfo("FormatText", FormatText.Main),
new ExampleInfo("InputBox", InputBox.Main),
new ExampleInfo("RaylibFonts", RaylibFonts.Main),
new ExampleInfo("RectangleBounds", RectangleBounds.Main),
new ExampleInfo("WritingAnim", WritingAnim.Main),
// Models
new ExampleInfo("LoadingIqm", LoadingIqm.Main),
new ExampleInfo("LoadingGltf", LoadingGltf.Main),
new ExampleInfo("BillboardDemo", BillboardDemo.Main),
new ExampleInfo("BoxCollisions", BoxCollisions.Main),
new ExampleInfo("CubicmapDemo", CubicmapDemo.Main),
new ExampleInfo("ModelCubeTexture", ModelCubeTexture.Main),
new ExampleInfo("FirstPersonMaze", FirstPersonMaze.Main),
new ExampleInfo("GeometricShapes", GeometricShapes.Main),
new ExampleInfo("HeightmapDemo", HeightmapDemo.Main),
new ExampleInfo("MeshDemo", MeshDemo.Main),
new ExampleInfo("ModelLoading", ModelLoading.Main),
new ExampleInfo("MeshGeneration", MeshGeneration.Main),
new ExampleInfo("MeshPicking", MeshPicking.Main),
new ExampleInfo("OrthographicProjection", OrthographicProjection.Main),
new ExampleInfo("SolarSystem", SolarSystem.Main),
new ExampleInfo("SkyboxDemo", SkyboxDemo.Main),
new ExampleInfo("WavingCubes", WavingCubes.Main),
new ExampleInfo("YawPitchRoll", YawPitchRoll.Main),
new ExampleInfo("DynamicMesh", DynamicMesh.Main),
// Shaders
new ExampleInfo("BasicLighting", BasicLighting.Main),
new ExampleInfo("BasicPbr", BasicPbr.Main),
new ExampleInfo("CustomUniform", CustomUniform.Main),
new ExampleInfo("Eratosthenes", Eratosthenes.Main),
new ExampleInfo("Fog", Fog.Main),
new ExampleInfo("HotReloading", HotReloading.Main),
new ExampleInfo("HybridRender", HybridRender.Main),
new ExampleInfo("JuliaSet", JuliaSet.Main),
new ExampleInfo("ModelShader", ModelShader.Main),
new ExampleInfo("MultiSample2d", MultiSample2d.Main),
new ExampleInfo("PaletteSwitch", PaletteSwitch.Main),
new ExampleInfo("PostProcessing", PostProcessing.Main),
new ExampleInfo("Raymarching", Raymarching.Main),
new ExampleInfo("MeshInstancing", MeshInstancing.Main),
new ExampleInfo("ShapesTextures", ShapesTextures.Main),
new ExampleInfo("SimpleMask", SimpleMask.Main),
new ExampleInfo("Spotlight", Spotlight.Main),
new ExampleInfo("TextureDrawing", TextureDrawing.Main),
new ExampleInfo("TextureOutline", TextureOutline.Main),
new ExampleInfo("TextureWaves", TextureWaves.Main),
new ExampleInfo("WriteDepth", WriteDepth.Main),
// Audio
new ExampleInfo("ModulePlaying", ModulePlaying.Main),
new ExampleInfo("MusicStreamDemo", MusicStreamDemo.Main),
new ExampleInfo("SoundLoading", SoundLoading.Main),
};
public static ExampleInfo GetExample(string name)
{
var example = Array.Find(ExampleList.AllExamples, x =>
x.Name.Equals(name, StringComparison.OrdinalIgnoreCase)
);
return example;
}
}
class Program
{
static unsafe void Main(string[] args)
private static unsafe void Main(string[] args)
{
Raylib.SetTraceLogCallback(&Logging.LogConsole);
if (args.Length > 0)
{
var example = ExampleList.GetExample(args[0]);
example?.Main?.Invoke();
var example = GetExample(args[0]);
if (example == null)
{
Console.WriteLine($"Unknown example: {args[0]}");
return;
}
RunExample(example);
return;
}
foreach (var example in ExampleList.AllExamples)
foreach (var example in ExampleRegistry.DesktopExamples)
{
RunExampleProcess(Environment.ProcessPath, example.Name);
RunExampleProcess(Environment.ProcessPath!, example.GetType().Name);
}
}
static void RunExampleProcess(
private static IExample? GetExample(string name)
{
return Array.Find(ExampleRegistry.DesktopExamples, x =>
x.GetType().Name.Equals(name, StringComparison.OrdinalIgnoreCase) ||
x.Name.Equals(name, StringComparison.OrdinalIgnoreCase)
);
}
/// <summary>
/// Drives an example the same way its standalone Main() does: window setup from the
/// example's properties, then Init/Update/Unload around a blocking frame loop.
/// </summary>
private static void RunExample(IExample example)
{
if (example.ConfigFlags != 0)
{
SetConfigFlags(example.ConfigFlags);
}
InitWindow(example.Width, example.Height, example.Title);
if (example.CursorDisabled)
{
DisableCursor();
}
else if (example.CursorHidden)
{
HideCursor();
}
SetTargetFPS(example.TargetFps);
try
{
example.Init();
while (!example.ShouldClose)
{
example.Update();
}
}
finally
{
try
{
example.Unload();
}
finally
{
CloseWindow();
}
}
}
private static void RunExampleProcess(
string fileName,
string exampleName
)

View file

@ -0,0 +1,163 @@
/*******************************************************************************************
*
* raylib [shaders] example - ascii rendering
*
* Example complexity rating: [★★☆☆] 2/4
*
* Example originally created with raylib 5.5, last time updated with raylib 6.0
*
* Example contributed by Maicon Santana (@maiconpintoabreu) 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 Maicon Santana (@maiconpintoabreu)
*
********************************************************************************************/
namespace Examples.Shaders;
public partial class AsciiRendering : 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
public string Name => "Shaders / Ascii Rendering";
public string Title => "raylib [shaders] example - ascii rendering";
private Texture2D fudesumi;
private Texture2D raysan;
private Shader shader;
private int resolutionLoc;
private int fontSizeLoc;
private float fontSize;
private Vector2 circlePos;
private float circleSpeed;
private RenderTexture2D target;
public void Init()
{
// Texture to test static drawing
fudesumi = LoadTexture("resources/fudesumi.png");
// Texture to test moving drawing
raysan = LoadTexture("resources/raysan.png");
// Load shader to be used on postprocessing
shader = LoadShader(null, $"resources/shaders/glsl{GlslVersion}/ascii.fs");
// These locations are used to send data to the GPU
resolutionLoc = GetShaderLocation(shader, "resolution");
fontSizeLoc = GetShaderLocation(shader, "fontSize");
// Set the character size for the ASCII effect
// Fontsize should be 9 or more
fontSize = 9.0f;
// Send the updated values to the shader
var resolution = new[] { (float)screenWidth, (float)screenHeight };
Raylib.SetShaderValue(shader, resolutionLoc, resolution, ShaderUniformDataType.Vec2);
circlePos = new Vector2(40.0f, screenHeight * 0.5f);
circleSpeed = 1.0f;
// RenderTexture to apply the postprocessing later
target = LoadRenderTexture(screenWidth, screenHeight);
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
circlePos.X += circleSpeed;
if ((circlePos.X > 200.0f) || (circlePos.X < 40.0f))
{
circleSpeed *= -1; // Revert speed
}
if (IsKeyPressed(KeyboardKey.Left) && (fontSize > 9.0))
{
fontSize -= 1; // Reduce fontSize
}
if (IsKeyPressed(KeyboardKey.Right) && (fontSize < 15.0))
{
fontSize += 1; // Increase fontSize
}
// Set fontsize for the shader
Raylib.SetShaderValue(shader, fontSizeLoc, fontSize, ShaderUniformDataType.Float);
// Draw
//----------------------------------------------------------------------------------
BeginTextureMode(target);
ClearBackground(Color.White);
// Draw scene in our render texture
DrawTexture(fudesumi, 500, -30, Color.White);
DrawTextureV(raysan, circlePos, Color.White);
EndTextureMode();
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginShaderMode(shader);
// Draw the scene texture (that we rendered earlier) to the screen
// The shader will process every pixel of this texture
DrawTextureRec(
target.Texture,
new Rectangle(0, 0, target.Texture.Width, -target.Texture.Height),
new Vector2(0, 0),
Color.White
);
EndShaderMode();
DrawRectangle(0, 0, screenWidth, 40, Color.Black);
DrawText($"Ascii effect - FontSize:{fontSize,2:F0} - [Left] -1 [Right] +1 ", 120, 10, 20, Color.LightGray);
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadRenderTexture(target); // Unload render texture
UnloadShader(shader); // Unload shader
UnloadTexture(fudesumi); // Unload texture
UnloadTexture(raysan); // Unload texture
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [shaders] example - ascii rendering");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new AsciiRendering();
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

@ -2,81 +2,77 @@
*
* raylib [shaders] example - basic lighting
*
* Example complexity rating: [★★★★] 4/4
*
* NOTE: This example requires raylib OpenGL 3.3 or ES2 versions for shaders support,
* OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version.
* OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version
*
* NOTE: Shaders used in this example are #version 330 (OpenGL 3.3).
* NOTE: Shaders used in this example are #version 330 (OpenGL 3.3)
*
* 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 originally created with raylib 3.0, last time updated with raylib 4.2
*
* Example contributed by Chris Camacho (@codifies) and reviewed by Ramon Santamaria (@raysan5)
* Example contributed by Chris Camacho (@chriscamacho) and reviewed by Ramon Santamaria (@raysan5)
*
* Chris Camacho (@codifies - http://bedroomcoders.co.uk/) notes:
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
* BSD-like license that allows static linking with closed source software
*
* This is based on the PBR lighting example, but greatly simplified to aid learning...
* actually there is very little of the PBR example left!
* When I first looked at the bewildering complexity of the PBR example I feared
* I would never understand how I could do simple lighting with raylib however its
* a testement to the authors of raylib (including rlights.h) that the example
* came together fairly quickly.
*
* Copyright (c) 2019 Chris Camacho (@codifies) and Ramon Santamaria (@raysan5)
* Copyright (c) 2019-2025 Chris Camacho (@chriscamacho) and Ramon Santamaria (@raysan5)
*
********************************************************************************************/
using System.Numerics;
using static Raylib_cs.Raylib;
using Examples.Shared;
namespace Examples.Shaders;
public class BasicLighting
public class BasicLighting : IExample
{
const int GLSL_VERSION = 330;
private const int screenWidth = 800;
private const int screenHeight = 450;
public unsafe static int Main()
#if BROWSER
const int GlslVersion = 100; // WebGL1 needs GLSL ES 100
#else
private const int GlslVersion = 330;
#endif
public string Name => "Shaders / Basic Lighting";
public string Title => "raylib [shaders] example - basic lighting";
public ConfigFlags ConfigFlags => ConfigFlags.Msaa4xHint;
private Camera3D camera;
private Shader shader;
private Light[] lights;
public unsafe void Init()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
// Enable Multi Sampling Anti Aliasing 4x (if available)
SetConfigFlags(ConfigFlags.Msaa4xHint);
InitWindow(screenWidth, screenHeight, "raylib [shaders] example - basic lighting");
// Define the camera to look into our 3d world
Camera3D camera = new();
camera.Position = new Vector3(2.0f, 4.0f, 6.0f);
camera.Target = new Vector3(0.0f, 0.5f, 0.0f);
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
camera.FovY = 45.0f;
camera.Projection = CameraProjection.Perspective;
camera = new();
camera.Position = new Vector3(2.0f, 4.0f, 6.0f); // Camera position
camera.Target = new Vector3(0.0f, 0.5f, 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
// Load plane model from a generated mesh
Model model = LoadModelFromMesh(GenMeshPlane(10.0f, 10.0f, 3, 3));
Model cube = LoadModelFromMesh(GenMeshCube(2.0f, 4.0f, 2.0f));
Shader shader = LoadShader(
"resources/shaders/glsl330/lighting.vs",
"resources/shaders/glsl330/lighting.fs"
// Load basic lighting shader
shader = LoadShader(
$"resources/shaders/glsl{GlslVersion}/lighting.vs",
$"resources/shaders/glsl{GlslVersion}/lighting.fs"
);
// Get some required shader loactions
// Get some required shader locations
shader.Locs[(int)ShaderLocationIndex.VectorView] = GetShaderLocation(shader, "viewPos");
// NOTE: "matModel" location name is automatically assigned on shader loading,
// no need to get the location again if using that uniform name
//shader.Locs[(int)ShaderLocationIndex.MatrixModel] = GetShaderLocation(shader, "matModel");
// ambient light level
int ambientLoc = GetShaderLocation(shader, "ambient");
float[] ambient = new[] { 0.1f, 0.1f, 0.1f, 1.0f };
// Ambient light level (some basic lighting)
var ambientLoc = GetShaderLocation(shader, "ambient");
var ambient = new[] { 0.1f, 0.1f, 0.1f, 1.0f };
Raylib.SetShaderValue(shader, ambientLoc, ambient, ShaderUniformDataType.Vec4);
// Assign out lighting shader to model
model.Materials[0].Shader = shader;
cube.Materials[0].Shader = shader;
// Using 4 point lights: Color.gold, Color.red, Color.green and Color.blue
Light[] lights = new Light[4];
// Create lights
lights = new Light[4];
lights[0] = Rlights.CreateLight(
0,
LightType.Point,
@ -109,114 +105,116 @@ public class BasicLighting
Color.Blue,
shader
);
}
SetTargetFPS(60);
public unsafe void Update()
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Orbital);
// Update the shader with the camera view vector (points towards { 0.0f, 0.0f, 0.0f })
Raylib.SetShaderValue(
shader,
shader.Locs[(int)ShaderLocationIndex.VectorView],
camera.Position,
ShaderUniformDataType.Vec3
);
// Check key inputs to enable/disable lights
if (IsKeyPressed(KeyboardKey.Y))
{
lights[0].Enabled = !lights[0].Enabled;
}
if (IsKeyPressed(KeyboardKey.R))
{
lights[1].Enabled = !lights[1].Enabled;
}
if (IsKeyPressed(KeyboardKey.G))
{
lights[2].Enabled = !lights[2].Enabled;
}
if (IsKeyPressed(KeyboardKey.B))
{
lights[3].Enabled = !lights[3].Enabled;
}
// Update light values (actually, only enable/disable them)
for (var i = 0; i < 4; i++)
{
Rlights.UpdateLightValues(shader, lights[i]);
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
BeginShaderMode(shader);
DrawPlane(Vector3.Zero, new Vector2(10.0f, 10.0f), Color.White);
DrawCube(Vector3.Zero, 2.0f, 4.0f, 2.0f, Color.White);
EndShaderMode();
// Draw spheres to show where the lights are
for (var i = 0; i < 4; i++)
{
if (lights[i].Enabled)
{
DrawSphereEx(lights[i].Position, 0.2f, 8, 8, lights[i].Color);
}
else
{
DrawSphereWires(lights[i].Position, 0.2f, 8, 8, ColorAlpha(lights[i].Color, 0.3f));
}
}
DrawGrid(10, 1.0f);
EndMode3D();
DrawFPS(10, 10);
DrawText("Use keys [Y][R][G][B] to toggle lights", 10, 40, 20, Color.DarkGray);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadShader(shader); // Unload shader
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
SetConfigFlags(ConfigFlags.Msaa4xHint); // Enable Multi Sampling Anti Aliasing 4x (if available)
InitWindow(screenWidth, screenHeight, "raylib [shaders] example - basic lighting");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new BasicLighting();
game.Init();
// Main game loop
while (!WindowShouldClose())
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Orbital);
if (IsKeyPressed(KeyboardKey.Y))
{
lights[0].Enabled = !lights[0].Enabled;
}
if (IsKeyPressed(KeyboardKey.R))
{
lights[1].Enabled = !lights[1].Enabled;
}
if (IsKeyPressed(KeyboardKey.G))
{
lights[2].Enabled = !lights[2].Enabled;
}
if (IsKeyPressed(KeyboardKey.B))
{
lights[3].Enabled = !lights[3].Enabled;
}
// Update light values (actually, only enable/disable them)
Rlights.UpdateLightValues(shader, lights[0]);
Rlights.UpdateLightValues(shader, lights[1]);
Rlights.UpdateLightValues(shader, lights[2]);
Rlights.UpdateLightValues(shader, lights[3]);
// Update the light shader with the camera view position
Raylib.SetShaderValue(
shader,
shader.Locs[(int)ShaderLocationIndex.VectorView],
camera.Position,
ShaderUniformDataType.Vec3
);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawModel(model, Vector3.Zero, 1.0f, Color.White);
DrawModel(cube, Vector3.Zero, 1.0f, Color.White);
// Draw markers to show where the lights are
if (lights[0].Enabled)
{
DrawSphereEx(lights[0].Position, 0.2f, 8, 8, Color.Yellow);
}
else
{
DrawSphereWires(lights[0].Position, 0.2f, 8, 8, ColorAlpha(Color.Yellow, 0.3f));
}
if (lights[1].Enabled)
{
DrawSphereEx(lights[1].Position, 0.2f, 8, 8, Color.Red);
}
else
{
DrawSphereWires(lights[1].Position, 0.2f, 8, 8, ColorAlpha(Color.Red, 0.3f));
}
if (lights[2].Enabled)
{
DrawSphereEx(lights[2].Position, 0.2f, 8, 8, Color.Green);
}
else
{
DrawSphereWires(lights[2].Position, 0.2f, 8, 8, ColorAlpha(Color.Green, 0.3f));
}
if (lights[3].Enabled)
{
DrawSphereEx(lights[3].Position, 0.2f, 8, 8, Color.Blue);
}
else
{
DrawSphereWires(lights[3].Position, 0.2f, 8, 8, ColorAlpha(Color.Blue, 0.3f));
}
DrawGrid(10, 1.0f);
EndMode3D();
DrawFPS(10, 10);
DrawText("Use keys [Y][R][G][B] to toggle lights", 10, 40, 20, Color.DarkGray);
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadModel(model);
UnloadModel(cube);
UnloadShader(shader);
CloseWindow();
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;

View file

@ -1,53 +1,72 @@
/*******************************************************************************************
*
* raylib [shaders] example - Basic PBR
*
* Example originally created with raylib 5.0, last time updated with raylib 5.1-dev
*
* Example contributed by Afan OLOVCIC (@_DevDad) 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) 2023-2024 Afan OLOVCIC (@_DevDad)
*
* Model: "Old Rusty Car" (https://skfb.ly/LxRy) by Renafox,
* licensed under Creative Commons Attribution-NonCommercial
* (http://creativecommons.org/licenses/by-nc/4.0/)
*
********************************************************************************************/
*
* raylib [shaders] example - basic pbr
*
* Example complexity rating: [★★★★] 4/4
*
* Example originally created with raylib 5.0, last time updated with raylib 5.5
*
* Example contributed by Afan OLOVCIC (@_DevDad) 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) 2023-2025 Afan OLOVCIC (@_DevDad)
*
* Model: "Old Rusty Car" (https://skfb.ly/LxRy) by Renafox,
* licensed under Creative Commons Attribution-NonCommercial
* (http://creativecommons.org/licenses/by-nc/4.0/)
*
********************************************************************************************/
using System.Numerics;
using Examples.Shared;
using static Raylib_cs.Raylib;
namespace Examples.Shaders;
public class BasicPbr
public class BasicPbr : IExample
{
private const int GLSL_VERSION = 330;
#if BROWSER
const int GlslVersion = 100; // WebGL1 needs GLSL ES 100
#else
private const int GlslVersion = 330;
#endif
public static unsafe int Main()
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Shaders / Basic PBR";
public string Title => "raylib [shaders] example - basic pbr";
public ConfigFlags ConfigFlags => ConfigFlags.Msaa4xHint;
private Camera3D camera;
private Shader shader;
private Model car;
private Model floor;
private PbrLight[] lights;
private int metallicValueLoc;
private int roughnessValueLoc;
private int emissiveIntensityLoc;
private int emissiveColorLoc;
private int textureTilingLoc;
private Vector2 carTextureTiling;
private Vector2 floorTextureTiling;
public unsafe void Init()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
// Enable Multi Sampling Anti Aliasing 4x (if available)
SetConfigFlags(ConfigFlags.Msaa4xHint);
InitWindow(screenWidth, screenHeight, "raylib [shaders] example - basic pbr");
// Define the camera to look into our 3d world
Camera3D camera = new();
camera.Position = new Vector3(2.0f, 4.0f, 6.0f);
camera = new();
camera.Position = new Vector3(2.0f, 2.0f, 6.0f);
camera.Target = new Vector3(0.0f, 0.5f, 0.0f);
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
camera.FovY = 45.0f;
camera.Projection = CameraProjection.Perspective;
// Load PBR shader and setup all required locations
var shader = LoadShader("resources/shaders/glsl330/pbr.vs", "resources/shaders/glsl330/pbr.fs");
shader = LoadShader($"resources/shaders/glsl{GlslVersion}/pbr.vs", $"resources/shaders/glsl{GlslVersion}/pbr.fs");
shader.Locs[(int)ShaderLocationIndex.MapAlbedo] = GetShaderLocation(shader, "albedoMap");
// WARNING: Metalness, roughness, and ambient occlusion are all packed into a MRA texture
@ -75,23 +94,25 @@ public class BasicPbr
SetShaderValue(shader, GetShaderLocation(shader, "ambient"), &ambientIntensity, ShaderUniformDataType.Float);
// Get location for shader parameters that can be modified in real time
var emissiveIntensityLoc = GetShaderLocation(shader, "emissivePower");
var emissiveColorLoc = GetShaderLocation(shader, "emissiveColor");
var textureTilingLoc = GetShaderLocation(shader, "tiling");
metallicValueLoc = GetShaderLocation(shader, "metallicValue");
roughnessValueLoc = GetShaderLocation(shader, "roughnessValue");
emissiveIntensityLoc = GetShaderLocation(shader, "emissivePower");
emissiveColorLoc = GetShaderLocation(shader, "emissiveColor");
textureTilingLoc = GetShaderLocation(shader, "tiling");
// Load old car model using PBR maps and shader
// WARNING: We know this model consists of a single model.meshes[0] and
// that model.materials[0] is by default assigned to that mesh
// There could be more complex models consisting of multiple meshes and
// multiple materials defined for those meshes... but always 1 mesh = 1 material
var car = LoadModel("resources/models/gltf/old_car_new.glb");
car = LoadModel("resources/models/gltf/old_car_new.glb");
// Assign already setup PBR shader to model.materials[0], used by models.meshes[0]
car.Materials[0].Shader = shader;
// Setup materials[0].maps default parameters
car.Materials[0].Maps[(int)MaterialMapIndex.Albedo].Color = Color.White;
car.Materials[0].Maps[(int)MaterialMapIndex.Metalness].Value = 0.0f;
car.Materials[0].Maps[(int)MaterialMapIndex.Metalness].Value = 1.0f;
car.Materials[0].Maps[(int)MaterialMapIndex.Roughness].Value = 0.0f;
car.Materials[0].Maps[(int)MaterialMapIndex.Occlusion].Value = 1.0f;
car.Materials[0].Maps[(int)MaterialMapIndex.Emission].Color = new Color(255, 162, 0, 255);
@ -104,7 +125,7 @@ public class BasicPbr
// Load floor model mesh and assign material parameters
// NOTE: A basic plane shape can be generated instead of being loaded from a model file
var floor = LoadModel("resources/models/gltf/plane.glb");
floor = LoadModel("resources/models/gltf/plane.glb");
//Mesh floorMesh = GenMeshPlane(10, 10, 10, 10);
//GenMeshTangents(&floorMesh); // TODO: Review tangents generation
//Model floor = LoadModelFromMesh(floorMesh);
@ -113,8 +134,8 @@ public class BasicPbr
floor.Materials[0].Shader = shader;
floor.Materials[0].Maps[(int)MaterialMapIndex.Albedo].Color = Color.White;
floor.Materials[0].Maps[(int)MaterialMapIndex.Metalness].Value = 0.0f;
floor.Materials[0].Maps[(int)MaterialMapIndex.Roughness].Value = 0.0f;
floor.Materials[0].Maps[(int)MaterialMapIndex.Metalness].Value = 0.8f;
floor.Materials[0].Maps[(int)MaterialMapIndex.Roughness].Value = 0.1f;
floor.Materials[0].Maps[(int)MaterialMapIndex.Occlusion].Value = 1.0f;
floor.Materials[0].Maps[(int)MaterialMapIndex.Emission].Color = Color.Black;
@ -124,11 +145,11 @@ public class BasicPbr
// Models texture tiling parameter can be stored in the Material struct if required (CURRENTLY NOT USED)
// NOTE: Material.params[4] are available for generic parameters storage (float)
var carTextureTiling = new Vector2(0.5f, 0.5f);
var floorTextureTiling = new Vector2(0.5f, 0.5f);
carTextureTiling = new Vector2(0.5f, 0.5f);
floorTextureTiling = new Vector2(0.5f, 0.5f);
// Create some lights
var lights = new PbrLight[4];
lights = new PbrLight[4];
lights[0] = PbrLights.CreateLight(
0,
PbrLightType.Point,
@ -167,104 +188,113 @@ public class BasicPbr
SetShaderValue(shader, GetShaderLocation(shader, "useTexNormal"), &usage, ShaderUniformDataType.Int);
SetShaderValue(shader, GetShaderLocation(shader, "useTexMRA"), &usage, ShaderUniformDataType.Int);
SetShaderValue(shader, GetShaderLocation(shader, "useTexEmissive"), &usage, ShaderUniformDataType.Int);
}
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//---------------------------------------------------------------------------------------
public unsafe void Update()
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Orbital);
// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
// Update the shader with the camera view vector (points towards { 0.0f, 0.0f, 0.0f })
var cameraPos = camera.Position;
SetShaderValue(shader, shader.Locs[(int)ShaderLocationIndex.VectorView], cameraPos, ShaderUniformDataType.Vec3);
// Check key inputs to enable/disable lights
if (IsKeyPressed(KeyboardKey.One))
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(&camera, CameraMode.Orbital);
// Update the shader with the camera view vector (points towards { 0.0f, 0.0f, 0.0f })
var cameraPos = camera.Position;
SetShaderValue(shader, shader.Locs[(int)ShaderLocationIndex.VectorView], cameraPos, ShaderUniformDataType.Vec3);
// Check key inputs to enable/disable lights
if (IsKeyPressed(KeyboardKey.One))
{
lights[2].Enabled = !lights[2].Enabled;
}
if (IsKeyPressed(KeyboardKey.Two))
{
lights[1].Enabled = !lights[1].Enabled;
}
if (IsKeyPressed(KeyboardKey.Three))
{
lights[3].Enabled = !lights[3].Enabled;
}
if (IsKeyPressed(KeyboardKey.Four))
{
lights[0].Enabled = !lights[0].Enabled;
}
// Update light values on shader (actually, only enable/disable them)
for (var i = 0; i < 4; i++)
{
UpdateLight(shader, lights[i]);
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.Black);
BeginMode3D(camera);
// Set floor model texture tiling and emissive color parameters on shader
SetShaderValue(shader, textureTilingLoc, &floorTextureTiling, ShaderUniformDataType.Vec2);
var floorEmissiveColor = ColorNormalize(floor.Materials[0].Maps[(int)MaterialMapIndex.Emission].Color);
SetShaderValue(shader, emissiveColorLoc, &floorEmissiveColor, ShaderUniformDataType.Vec4);
DrawModel(floor, Vector3.Zero, 5.0f, Color.White); // Draw floor model
// Set old car model texture tiling, emissive color and emissive intensity parameters on shader
SetShaderValue(shader, textureTilingLoc, &carTextureTiling, ShaderUniformDataType.Vec2);
var carEmissiveColor = ColorNormalize(car.Materials[0].Maps[(int)MaterialMapIndex.Emission].Color);
SetShaderValue(shader, emissiveColorLoc, &carEmissiveColor, ShaderUniformDataType.Vec4);
var emissiveIntensity = 0.01f;
SetShaderValue(shader, emissiveIntensityLoc, &emissiveIntensity, ShaderUniformDataType.Float);
DrawModel(car, Vector3.Zero, 0.25f, Color.White); // Draw car model
// Draw spheres to show the lights positions
for (var i = 0; i < 4; i++)
{
var color = lights[i].Color;
var lightColor = new Color((byte)(color.X * 255), (byte)(color.Y * 255), (byte)(color.Z * 255),
(byte)(color.W * 255));
if (lights[i].Enabled)
{
DrawSphereEx(lights[i].Position, 0.2f, 8, 8, lightColor);
}
else
{
DrawSphereWires(lights[i].Position, 0.2f, 8, 8, ColorAlpha(lightColor, 0.3f));
}
}
EndMode3D();
DrawText("Toggle lights: [1][2][3][4]", 10, 40, 20, Color.LightGray);
DrawText("(c) Old Rusty Car model by Renafox (https://skfb.ly/LxRy)", screenWidth - 320, screenHeight - 20, 10, Color.LightGray);
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
lights[2].Enabled = !lights[2].Enabled;
}
// De-Initialization
//--------------------------------------------------------------------------------------
if (IsKeyPressed(KeyboardKey.Two))
{
lights[1].Enabled = !lights[1].Enabled;
}
if (IsKeyPressed(KeyboardKey.Three))
{
lights[3].Enabled = !lights[3].Enabled;
}
if (IsKeyPressed(KeyboardKey.Four))
{
lights[0].Enabled = !lights[0].Enabled;
}
// Update light values on shader (actually, only enable/disable them)
for (var i = 0; i < 4; i++)
{
UpdateLight(shader, lights[i]);
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.Black);
BeginMode3D(camera);
// Set floor model texture tiling and emissive color parameters on shader
SetShaderValue(shader, textureTilingLoc, floorTextureTiling, ShaderUniformDataType.Vec2);
var floorEmissiveColor = ColorNormalize(floor.Materials[0].Maps[(int)MaterialMapIndex.Emission].Color);
SetShaderValue(shader, emissiveColorLoc, &floorEmissiveColor, ShaderUniformDataType.Vec4);
// Set floor metallic and roughness values
var floorMetallicValue = floor.Materials[0].Maps[(int)MaterialMapIndex.Metalness].Value;
SetShaderValue(shader, metallicValueLoc, &floorMetallicValue, ShaderUniformDataType.Float);
var floorRoughnessValue = floor.Materials[0].Maps[(int)MaterialMapIndex.Roughness].Value;
SetShaderValue(shader, roughnessValueLoc, &floorRoughnessValue, ShaderUniformDataType.Float);
DrawModel(floor, Vector3.Zero, 5.0f, Color.White); // Draw floor model
// Set old car model texture tiling, emissive color and emissive intensity parameters on shader
SetShaderValue(shader, textureTilingLoc, carTextureTiling, ShaderUniformDataType.Vec2);
var carEmissiveColor = ColorNormalize(car.Materials[0].Maps[(int)MaterialMapIndex.Emission].Color);
SetShaderValue(shader, emissiveColorLoc, &carEmissiveColor, ShaderUniformDataType.Vec4);
var emissiveIntensity = 0.01f;
SetShaderValue(shader, emissiveIntensityLoc, &emissiveIntensity, ShaderUniformDataType.Float);
// Set old car metallic and roughness values
var carMetallicValue = car.Materials[0].Maps[(int)MaterialMapIndex.Metalness].Value;
SetShaderValue(shader, metallicValueLoc, &carMetallicValue, ShaderUniformDataType.Float);
var carRoughnessValue = car.Materials[0].Maps[(int)MaterialMapIndex.Roughness].Value;
SetShaderValue(shader, roughnessValueLoc, &carRoughnessValue, ShaderUniformDataType.Float);
DrawModel(car, Vector3.Zero, 0.25f, Color.White); // Draw car model
// Draw spheres to show the lights positions
for (var i = 0; i < 4; i++)
{
var color = lights[i].Color;
var lightColor = new Color((byte)(color.X * 255), (byte)(color.Y * 255), (byte)(color.Z * 255),
(byte)(color.W * 255));
if (lights[i].Enabled)
{
DrawSphereEx(lights[i].Position, 0.2f, 8, 8, lightColor);
}
else
{
DrawSphereWires(lights[i].Position, 0.2f, 8, 8, ColorAlpha(lightColor, 0.3f));
}
}
EndMode3D();
DrawText("Toggle lights: [1][2][3][4]", 10, 40, 20, Color.LightGray);
DrawText("(c) Old Rusty Car model by Renafox (https://skfb.ly/LxRy)", screenWidth - 320, screenHeight - 20, 10, Color.LightGray);
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
}
public unsafe void Unload()
{
// Unbind (disconnect) shader from car.material[0]
// to avoid UnloadMaterial() trying to unload it automatically
car.Materials[0].Shader = new();
@ -278,7 +308,32 @@ public class BasicPbr
UnloadModel(floor);
UnloadShader(shader);
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
// Enable Multi Sampling Anti Aliasing 4x (if available)
SetConfigFlags(ConfigFlags.Msaa4xHint);
InitWindow(screenWidth, screenHeight, "raylib [shaders] example - basic pbr");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//---------------------------------------------------------------------------------------
var game = new BasicPbr();
game.Init();
// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
{
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow();
//--------------------------------------------------------------------------------------
@ -287,8 +342,8 @@ public class BasicPbr
private static void UpdateLight(Shader shader, PbrLight light)
{
SetShaderValue(shader, light.EnabledLoc, light.Enabled, ShaderUniformDataType.Int);
SetShaderValue(shader, light.TypeLoc, light.Type, ShaderUniformDataType.Int);
SetShaderValue(shader, light.EnabledLoc, light.Enabled ? 1 : 0, ShaderUniformDataType.Int);
SetShaderValue(shader, light.TypeLoc, (int)light.Type, ShaderUniformDataType.Int);
// Send to shader light position values
SetShaderValue(shader, light.PositionLoc, light.Position, ShaderUniformDataType.Vec3);

View file

@ -0,0 +1,249 @@
/*******************************************************************************************
*
* raylib [shaders] example - cel shading
*
* Example complexity rating: [★★★☆] 3/4
*
* NOTE: This example requires raylib OpenGL 3.3 or ES2 versions for shaders support,
* OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version
*
* NOTE: Shaders used in this example are #version 330 (OpenGL 3.3)
*
* Example contributed by Gleb A (@ggrizzly) 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) 2026 Gleb A (@ggrizzly)
*
********************************************************************************************/
using static Raylib_cs.Raymath;
using static Raylib_cs.Rlgl;
using Examples.Shared;
namespace Examples.Shaders;
public partial class CelShading : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
private const int MaxLights = 4;
// rlgl cull face modes (rlgl.h: RL_CULL_FACE_FRONT = 0, RL_CULL_FACE_BACK = 1)
private const int CullFaceFront = 0;
private const int CullFaceBack = 1;
#if BROWSER
const int GlslVersion = 100; // WebGL1 needs GLSL ES 100
#else
private const int GlslVersion = 330;
#endif
public string Name => "Shaders / Cel Shading";
public string Title => "raylib [shaders] example - cel shading";
public ConfigFlags ConfigFlags => ConfigFlags.Msaa4xHint;
private Camera3D camera;
private Model model;
private Shader celShader;
private Shader defaultShader;
private Shader outlineShader;
private float numBands;
private int numBandsLoc;
private int outlineThicknessLoc;
private Light[] lights;
private bool celEnabled;
private bool outlineEnabled;
public unsafe void Init()
{
camera = new();
camera.Position = new Vector3(9.0f, 6.0f, 9.0f);
camera.Target = new Vector3(0.0f, 1.0f, 0.0f);
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
camera.FovY = 45.0f;
camera.Projection = CameraProjection.Perspective;
// Load model
model = LoadModel("resources/models/old_car_new.glb");
// Load cel shader
celShader = LoadShader(
$"resources/shaders/glsl{GlslVersion}/cel.vs",
$"resources/shaders/glsl{GlslVersion}/cel.fs"
);
celShader.Locs[(int)ShaderLocationIndex.VectorView] = GetShaderLocation(celShader, "viewPos");
// Apply cel shader to model, keep copy of default shader
defaultShader = model.Materials[0].Shader;
model.Materials[0].Shader = celShader;
// numBands: controls toon quantization steps (2 = hard binary, 20 = near-smooth)
numBands = 10.0f;
numBandsLoc = GetShaderLocation(celShader, "numBands");
Raylib.SetShaderValue(celShader, numBandsLoc, numBands, ShaderUniformDataType.Float);
// Inverted-hull outline shader: draws back faces extruded along normals
outlineShader = LoadShader(
$"resources/shaders/glsl{GlslVersion}/outline_hull.vs",
$"resources/shaders/glsl{GlslVersion}/outline_hull.fs"
);
outlineThicknessLoc = GetShaderLocation(outlineShader, "outlineThickness");
// Single directional white light, angled so toon bands are visible on the model sides.
// Spins opposite to CAMERA_ORBITAL (0.5 rad/s) so lighting changes as you watch.
lights = new Light[MaxLights];
lights[0] = Rlights.CreateLight(
0,
LightType.Directorional,
new Vector3(50.0f, 50.0f, 50.0f),
Vector3.Zero,
Color.White,
celShader
);
celEnabled = true;
outlineEnabled = true;
}
public unsafe void Update()
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Orbital);
Raylib.SetShaderValue(
celShader,
celShader.Locs[(int)ShaderLocationIndex.VectorView],
camera.Position,
ShaderUniformDataType.Vec3
);
// [Z] Toggle cel shading on/off
if (IsKeyPressed(KeyboardKey.Z))
{
celEnabled = !celEnabled;
if (celEnabled)
{
model.Materials[0].Shader = celShader; // Apply cel shader to model
}
else
{
model.Materials[0].Shader = defaultShader; // Apply default shader to model
}
}
// [C] Toggle outline on/off
if (IsKeyPressed(KeyboardKey.C))
{
outlineEnabled = !outlineEnabled;
}
// [Q/E] Decrease/increase toon band count (press or hold to repeat)
if (IsKeyPressed(KeyboardKey.E) || IsKeyPressedRepeat(KeyboardKey.E))
{
numBands = Clamp(numBands + 1.0f, 2.0f, 20.0f);
}
if (IsKeyPressed(KeyboardKey.Q) || IsKeyPressedRepeat(KeyboardKey.Q))
{
numBands = Clamp(numBands - 1.0f, 2.0f, 20.0f);
}
Raylib.SetShaderValue(celShader, numBandsLoc, numBands, ShaderUniformDataType.Float);
// Spin light opposite to CAMERA_ORBITAL (0.5 rad/s), angled 45 degrees off vertical
float t = (float)GetTime();
lights[0].Position = new Vector3(MathF.Sin(-t * 0.3f) * 5.0f, 5.0f, MathF.Cos(-t * 0.3f) * 5.0f);
for (var i = 0; i < MaxLights; i++)
{
Rlights.UpdateLightValues(celShader, lights[i]);
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
if (outlineEnabled)
{
// Outline pass: cull front faces, draw extruded back faces as silhouette
float thickness = 0.005f;
Raylib.SetShaderValue(outlineShader, outlineThicknessLoc, thickness, ShaderUniformDataType.Float);
SetCullFace(CullFaceFront);
model.Materials[0].Shader = outlineShader;
DrawModel(model, Vector3.Zero, 0.75f, Color.White);
if (celEnabled)
{
model.Materials[0].Shader = celShader; // Apply cel shader to model
}
else
{
model.Materials[0].Shader = defaultShader; // Apply default shader to model
}
SetCullFace(CullFaceBack);
}
DrawModel(model, Vector3.Zero, 0.75f, Color.White);
DrawSphereEx(lights[0].Position, 0.2f, 50, 50, Color.Yellow); // Light position indicator
DrawGrid(10, 10.0f);
EndMode3D();
DrawFPS(10, 10);
DrawText($"Cel: {(celEnabled ? "ON" : "OFF")} [Z]", 10, 65, 20, celEnabled ? Color.DarkGreen : Color.DarkGray);
DrawText($"Outline: {(outlineEnabled ? "ON" : "OFF")} [C]", 10, 90, 20, outlineEnabled ? Color.DarkGreen : Color.DarkGray);
DrawText($"Bands: {numBands:0} [Q/E]", 10, 115, 20, Color.DarkGray);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadModel(model);
UnloadShader(celShader);
UnloadShader(outlineShader);
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
SetConfigFlags(ConfigFlags.Msaa4xHint);
InitWindow(screenWidth, screenHeight, "raylib [shaders] example - cel shading");
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
var game = new CelShading();
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;
}
}

Some files were not shown because too many files have changed in this diff Show more