chore: clean recommit
This commit is contained in:
parent
8024c6ac40
commit
60ad2e7fb1
122 changed files with 23950 additions and 323 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -63,6 +63,7 @@ project.lock.json
|
||||||
project.fragment.lock.json
|
project.fragment.lock.json
|
||||||
artifacts/
|
artifacts/
|
||||||
**/Properties/launchSettings.json
|
**/Properties/launchSettings.json
|
||||||
|
dotnet-tools.json
|
||||||
|
|
||||||
*_i.c
|
*_i.c
|
||||||
*_p.c
|
*_p.c
|
||||||
|
|
|
||||||
263
Examples/Audio/AmpEnvelope.cs
Normal file
263
Examples/Audio/AmpEnvelope.cs
Normal file
|
|
@ -0,0 +1,263 @@
|
||||||
|
/*******************************************************************************************
|
||||||
|
*
|
||||||
|
* 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)
|
||||||
|
*
|
||||||
|
********************************************************************************************/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Numerics;
|
||||||
|
using static Raylib_cs.Raylib;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
159
Examples/Audio/MixedProcessor.cs
Normal file
159
Examples/Audio/MixedProcessor.cs
Normal file
|
|
@ -0,0 +1,159 @@
|
||||||
|
/*******************************************************************************************
|
||||||
|
*
|
||||||
|
* 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;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using static Raylib_cs.Raylib;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
182
Examples/Audio/RawStream.cs
Normal file
182
Examples/Audio/RawStream.cs
Normal file
|
|
@ -0,0 +1,182 @@
|
||||||
|
/*******************************************************************************************
|
||||||
|
*
|
||||||
|
* 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)
|
||||||
|
*
|
||||||
|
********************************************************************************************/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Numerics;
|
||||||
|
using static Raylib_cs.Raylib;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
114
Examples/Audio/SoundMulti.cs
Normal file
114
Examples/Audio/SoundMulti.cs
Normal file
|
|
@ -0,0 +1,114 @@
|
||||||
|
/*******************************************************************************************
|
||||||
|
*
|
||||||
|
* 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)
|
||||||
|
*
|
||||||
|
********************************************************************************************/
|
||||||
|
|
||||||
|
using static Raylib_cs.Raylib;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
154
Examples/Audio/SoundPositioning.cs
Normal file
154
Examples/Audio/SoundPositioning.cs
Normal file
|
|
@ -0,0 +1,154 @@
|
||||||
|
/*******************************************************************************************
|
||||||
|
*
|
||||||
|
* 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)
|
||||||
|
*
|
||||||
|
********************************************************************************************/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Numerics;
|
||||||
|
using static Raylib_cs.Raylib;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
306
Examples/Audio/StreamCallback.cs
Normal file
306
Examples/Audio/StreamCallback.cs
Normal file
|
|
@ -0,0 +1,306 @@
|
||||||
|
/*******************************************************************************************
|
||||||
|
*
|
||||||
|
* 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;
|
||||||
|
using System.Numerics;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using static Raylib_cs.Raylib;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
222
Examples/Audio/StreamEffects.cs
Normal file
222
Examples/Audio/StreamEffects.cs
Normal file
|
|
@ -0,0 +1,222 @@
|
||||||
|
/*******************************************************************************************
|
||||||
|
*
|
||||||
|
* 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;
|
||||||
|
using static Raylib_cs.Raylib;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
379
Examples/Core/AutomationEvents.cs
Normal file
379
Examples/Core/AutomationEvents.cs
Normal file
|
|
@ -0,0 +1,379 @@
|
||||||
|
/*******************************************************************************************
|
||||||
|
*
|
||||||
|
* 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)
|
||||||
|
*
|
||||||
|
********************************************************************************************/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Numerics;
|
||||||
|
using static Raylib_cs.Raylib;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
184
Examples/Core/Camera2dMouseZoom.cs
Normal file
184
Examples/Core/Camera2dMouseZoom.cs
Normal file
|
|
@ -0,0 +1,184 @@
|
||||||
|
/*******************************************************************************************
|
||||||
|
*
|
||||||
|
* 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 System;
|
||||||
|
using System.Numerics;
|
||||||
|
using static Raylib_cs.Raylib;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
348
Examples/Core/Camera3dFps.cs
Normal file
348
Examples/Core/Camera3dFps.cs
Normal file
|
|
@ -0,0 +1,348 @@
|
||||||
|
/*******************************************************************************************
|
||||||
|
*
|
||||||
|
* 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 System;
|
||||||
|
using System.Numerics;
|
||||||
|
using static Raylib_cs.Raylib;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
286
Examples/Core/ClipboardText.cs
Normal file
286
Examples/Core/ClipboardText.cs
Normal file
|
|
@ -0,0 +1,286 @@
|
||||||
|
/*******************************************************************************************
|
||||||
|
*
|
||||||
|
* 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;
|
||||||
|
using System.Numerics;
|
||||||
|
using System.Text;
|
||||||
|
using static Raylib_cs.Raylib;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
253
Examples/Core/ComputeHash.cs
Normal file
253
Examples/Core/ComputeHash.cs
Normal file
|
|
@ -0,0 +1,253 @@
|
||||||
|
/*******************************************************************************************
|
||||||
|
*
|
||||||
|
* 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;
|
||||||
|
using System.Numerics;
|
||||||
|
using System.Text;
|
||||||
|
using static Raylib_cs.Raylib;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
188
Examples/Core/CustomFrameControl.cs
Normal file
188
Examples/Core/CustomFrameControl.cs
Normal file
|
|
@ -0,0 +1,188 @@
|
||||||
|
/*******************************************************************************************
|
||||||
|
*
|
||||||
|
* 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)
|
||||||
|
*
|
||||||
|
********************************************************************************************/
|
||||||
|
|
||||||
|
using static Raylib_cs.Raylib;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
205
Examples/Core/DirectoryFiles.cs
Normal file
205
Examples/Core/DirectoryFiles.cs
Normal file
|
|
@ -0,0 +1,205 @@
|
||||||
|
/*******************************************************************************************
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Numerics;
|
||||||
|
using static Raylib_cs.Raylib;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -20,6 +20,7 @@ using static Raylib_cs.Raylib;
|
||||||
|
|
||||||
namespace Examples.Core;
|
namespace Examples.Core;
|
||||||
|
|
||||||
|
[ExcludeFromBrowser]
|
||||||
public partial class DropFiles : IExample
|
public partial class DropFiles : IExample
|
||||||
{
|
{
|
||||||
private const int screenWidth = 800;
|
private const int screenWidth = 800;
|
||||||
|
|
|
||||||
173
Examples/Core/HighDpiDemo.cs
Normal file
173
Examples/Core/HighDpiDemo.cs
Normal file
|
|
@ -0,0 +1,173 @@
|
||||||
|
/*******************************************************************************************
|
||||||
|
*
|
||||||
|
* 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)
|
||||||
|
*
|
||||||
|
********************************************************************************************/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Numerics;
|
||||||
|
using static Raylib_cs.Raylib;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
145
Examples/Core/HighDpiTestbed.cs
Normal file
145
Examples/Core/HighDpiTestbed.cs
Normal file
|
|
@ -0,0 +1,145 @@
|
||||||
|
/*******************************************************************************************
|
||||||
|
*
|
||||||
|
* 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)
|
||||||
|
*
|
||||||
|
********************************************************************************************/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Numerics;
|
||||||
|
using static Raylib_cs.Raylib;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
224
Examples/Core/InputActions.cs
Normal file
224
Examples/Core/InputActions.cs
Normal file
|
|
@ -0,0 +1,224 @@
|
||||||
|
/*******************************************************************************************
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
using System.Numerics;
|
||||||
|
using static Raylib_cs.Raylib;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
374
Examples/Core/KeyboardTestbed.cs
Normal file
374
Examples/Core/KeyboardTestbed.cs
Normal file
|
|
@ -0,0 +1,374 @@
|
||||||
|
/*******************************************************************************************
|
||||||
|
*
|
||||||
|
* 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)
|
||||||
|
*
|
||||||
|
********************************************************************************************/
|
||||||
|
|
||||||
|
using System.Numerics;
|
||||||
|
using static Raylib_cs.Raylib;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue