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

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

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

* chg: Modernize CI workflow, enable SourceLink

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

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

* chore: updated readme

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

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

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

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

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

* chore: readme updated

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

* fix: Wrong signature of FrameBufferComplete

* chore: readme update

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

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

* chore: readme, gitignore, and targets backport.

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

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

* chore: readme, gitignore, and targets backport.

* chore: clean up linter warnings

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

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

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

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

1
.gitignore vendored
View file

@ -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

View file

@ -0,0 +1,273 @@
/*******************************************************************************************
*
* raylib [audio] example - amp envelope
*
* Example complexity rating: [★☆☆☆] 1/4
*
* Example originally created with raylib 6.0, last time updated with raylib 6.0
*
* Example contributed by Arbinda Rizki Muhammad (@arbipink) and reviewed by Ramon Santamaria (@raysan5)
*
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
* BSD-like license that allows static linking with closed source software
*
* Copyright (c) 2026 Arbinda Rizki Muhammad (@arbipink)
*
********************************************************************************************/
namespace Examples.Audio;
public unsafe partial class AmpEnvelope : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
private const int BUFFER_SIZE = 4096;
private const int SAMPLE_RATE = 44100;
// Wave state
private enum ADSRState
{
Idle,
Attack,
Decay,
Sustain,
Release
}
// Grouping all ADSR parameters and state into a struct
private struct Envelope
{
public float AttackTime;
public float DecayTime;
public float SustainLevel;
public float ReleaseTime;
public float CurrentValue;
public ADSRState State;
}
public string Name => "Audio / Amp Envelope";
public string Title => "raylib [audio] example - amp envelope";
private float[] buffer;
private AudioStream stream;
private float audioTime;
private Envelope env;
public void Init()
{
InitAudioDevice();
// Set the number of samples the stream will keep in memory at a time to BUFFER_SIZE
SetAudioStreamBufferSizeDefault(BUFFER_SIZE);
buffer = new float[BUFFER_SIZE];
// Init raw audio stream (sample rate: 44100, sample size: 32bit-float, channels: 1-mono)
stream = LoadAudioStream(SAMPLE_RATE, 32, 1);
// Init Phase
audioTime = 0.0f;
// Initialize the struct
env = new Envelope
{
AttackTime = 1.0f,
DecayTime = 1.0f,
SustainLevel = 0.5f,
ReleaseTime = 1.0f,
CurrentValue = 0.0f,
State = ADSRState.Idle
};
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
if (IsKeyPressed(KeyboardKey.Space))
{
env.State = ADSRState.Attack;
}
if (IsKeyReleased(KeyboardKey.Space) && (env.State != ADSRState.Idle))
{
env.State = ADSRState.Release;
}
if (IsAudioStreamProcessed(stream))
{
if ((env.State != ADSRState.Idle) || (env.CurrentValue > 0.0f))
{
for (int i = 0; i < BUFFER_SIZE; i++)
{
UpdateEnvelope(ref env);
FillAudioBuffer(i, buffer, env.CurrentValue, ref audioTime);
}
}
else
{
// Clear buffer if silent to avoid looping noise
for (int i = 0; i < BUFFER_SIZE; i++)
{
buffer[i] = 0;
}
audioTime = 0.0f;
}
fixed (float* bufferPtr = buffer)
{
UpdateAudioStream(stream, bufferPtr, BUFFER_SIZE);
}
}
if (!IsAudioStreamPlaying(stream))
{
PlayAudioStream(stream);
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
// NOTE: raygui is not bound in raylib-cs, so the sliders below are left as reference.
// The envelope keeps its default parameters (Attack/Decay/Release = 1.0s, Sustain = 0.5).
//GuiSliderBar(new Rectangle( 100, 60, 400, 30 ), "Attack (s)", TextFormat("%2.2fs", env.AttackTime), ref env.AttackTime, 0.1f, 3.0f);
//GuiSliderBar(new Rectangle( 100, 100, 400, 30 ), "Decay (s)", TextFormat("%2.2fs", env.DecayTime), ref env.DecayTime, 0.1f, 3.0f);
//GuiSliderBar(new Rectangle( 100, 140, 400, 30 ), "Sustain", TextFormat("%2.2f", env.SustainLevel), ref env.SustainLevel, 0.0f, 1.0f);
//GuiSliderBar(new Rectangle( 100, 180, 400, 30 ), "Release (s)", TextFormat("%2.2fs", env.ReleaseTime), ref env.ReleaseTime, 0.1f, 3.0f);
DrawADSRGraph(ref env, new Rectangle(100, 250, 400, 100));
DrawCircleV(new Vector2(520, 350 - (env.CurrentValue * 100)), 5, Color.Maroon);
DrawText($"Current Gain: {env.CurrentValue:F2}", 535, (int)(345 - (env.CurrentValue * 100)), 10, Color.Maroon);
DrawText("Press SPACE to PLAY the sound!", 200, 400, 20, Color.LightGray);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadAudioStream(stream);
CloseAudioDevice();
}
//------------------------------------------------------------------------------------
// Module Functions Definition
//------------------------------------------------------------------------------------
private static void FillAudioBuffer(int i, float[] buffer, float envelopeValue, ref float audioTime)
{
int frequency = 440;
buffer[i] = envelopeValue * MathF.Sin(2.0f * MathF.PI * frequency * audioTime);
audioTime += (1.0f / SAMPLE_RATE);
}
private static void UpdateEnvelope(ref Envelope env)
{
// Calculate the time delta for ONE sample (1/44100)
float sampleTime = 1.0f / SAMPLE_RATE;
switch (env.State)
{
case ADSRState.Attack:
{
env.CurrentValue += (1.0f / env.AttackTime) * sampleTime;
if (env.CurrentValue >= 1.0f)
{
env.CurrentValue = 1.0f;
env.State = ADSRState.Decay;
}
}
break;
case ADSRState.Decay:
{
env.CurrentValue -= ((1.0f - env.SustainLevel) / env.DecayTime) * sampleTime;
if (env.CurrentValue <= env.SustainLevel)
{
env.CurrentValue = env.SustainLevel;
env.State = ADSRState.Sustain;
}
}
break;
case ADSRState.Sustain:
{
env.CurrentValue = env.SustainLevel;
}
break;
case ADSRState.Release:
{
env.CurrentValue -= (env.SustainLevel / env.ReleaseTime) * sampleTime;
if (env.CurrentValue <= 0.001f) // Use a small threshold to avoid infinite tail
{
env.CurrentValue = 0.0f;
env.State = ADSRState.Idle;
}
}
break;
default:
break;
}
}
private static void DrawADSRGraph(ref Envelope env, Rectangle bounds)
{
DrawRectangleRec(bounds, Fade(Color.LightGray, 0.3f));
DrawRectangleLinesEx(bounds, 1, Color.Gray);
// Fixed visual width for sustain stage since it's an amplitude not a time value
float sustainWidth = 1.0f;
// Total time to visualize (sum of A, D, R + a padding for Sustain)
float totalTime = env.AttackTime + env.DecayTime + sustainWidth + env.ReleaseTime;
float scaleX = bounds.Width / totalTime;
float scaleY = bounds.Height;
Vector2 start = new Vector2(bounds.X, bounds.Y + bounds.Height);
Vector2 peak = new Vector2(start.X + (env.AttackTime * scaleX), bounds.Y);
Vector2 sustain = new Vector2(peak.X + (env.DecayTime * scaleX), bounds.Y + (1.0f - env.SustainLevel) * scaleY);
Vector2 rel = new Vector2(sustain.X + (sustainWidth * scaleX), sustain.Y);
Vector2 end = new Vector2(rel.X + (env.ReleaseTime * scaleX), bounds.Y + bounds.Height);
DrawLineV(start, peak, Color.SkyBlue);
DrawLineV(peak, sustain, Color.Blue);
DrawLineV(sustain, rel, Color.DarkBlue);
DrawLineV(rel, end, Color.Orange);
DrawText("ADSR Visualizer", (int)bounds.X, (int)bounds.Y - 20, 10, Color.DarkGray);
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [audio] example - amp envelope");
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
var game = new AmpEnvelope();
game.Init();
// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
{
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}
}

View file

@ -0,0 +1,177 @@
/*******************************************************************************************
*
* raylib [audio] example - mixed processor
*
* Example complexity rating: [★★★★] 4/4
*
* Example originally created with raylib 4.2, last time updated with raylib 4.2
*
* Example contributed by hkc (@hatkidchan) and reviewed by Ramon Santamaria (@raysan5)
*
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
* BSD-like license that allows static linking with closed source software
*
* Copyright (c) 2023-2025 hkc (@hatkidchan)
*
********************************************************************************************/
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace Examples.Audio;
[ExcludeFromBrowser("AttachAudioMixedProcessor callback is unreliable on the wasm audio backend")]
public unsafe partial class MixedProcessor : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Audio / Mixed Processor";
public string Title => "raylib [audio] example - mixed processor";
private static float exponent = 1.0f; // Audio exponentiation value
private static readonly float[] averageVolume = new float[400]; // Average volume history
private Music music;
private Sound sound;
//------------------------------------------------------------------------------------
// Audio processing function
//------------------------------------------------------------------------------------
[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
private static void ProcessAudio(void* buffer, uint frames)
{
float* samples = (float*)buffer; // Samples internally stored as <float>s
float average = 0.0f; // Temporary average volume
for (uint frame = 0; frame < frames; frame++)
{
float* left = &samples[frame * 2 + 0];
float* right = &samples[frame * 2 + 1];
*left = MathF.Pow(MathF.Abs(*left), exponent) * ((*left < 0.0f) ? -1.0f : 1.0f);
*right = MathF.Pow(MathF.Abs(*right), exponent) * ((*right < 0.0f) ? -1.0f : 1.0f);
average += MathF.Abs(*left) / frames; // accumulating average volume
average += MathF.Abs(*right) / frames;
}
// Moving history to the left
for (int i = 0; i < 399; i++)
{
averageVolume[i] = averageVolume[i + 1];
}
averageVolume[399] = average; // Adding last average value
}
public void Init()
{
InitAudioDevice(); // Initialize audio device
exponent = 1.0f;
Array.Clear(averageVolume, 0, averageVolume.Length);
AttachAudioMixedProcessor(&ProcessAudio);
music = LoadMusicStream("resources/audio/country.mp3");
sound = LoadSound("resources/audio/coin.wav");
PlayMusicStream(music);
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
UpdateMusicStream(music); // Update music buffer with new stream data
// Modify processing variables
//----------------------------------------------------------------------------------
if (IsKeyPressed(KeyboardKey.Left))
{
exponent -= 0.05f;
}
if (IsKeyPressed(KeyboardKey.Right))
{
exponent += 0.05f;
}
if (exponent <= 0.5f)
{
exponent = 0.5f;
}
if (exponent >= 3.0f)
{
exponent = 3.0f;
}
if (IsKeyPressed(KeyboardKey.Space))
{
PlaySound(sound);
}
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
DrawText("MUSIC SHOULD BE PLAYING!", 255, 150, 20, Color.LightGray);
DrawText($"EXPONENT = {exponent:F2}", 215, 180, 20, Color.LightGray);
DrawRectangle(199, 199, 402, 34, Color.LightGray);
for (int i = 0; i < 400; i++)
{
DrawLine(201 + i, 232 - (int)(averageVolume[i] * 32), 201 + i, 232, Color.Maroon);
}
DrawRectangleLines(199, 199, 402, 34, Color.Gray);
DrawText("PRESS SPACE TO PLAY OTHER SOUND", 200, 250, 20, Color.LightGray);
DrawText("USE LEFT AND RIGHT ARROWS TO ALTER DISTORTION", 140, 280, 20, Color.LightGray);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadMusicStream(music); // Unload music stream buffers from RAM
DetachAudioMixedProcessor(&ProcessAudio); // Disconnect audio processor
CloseAudioDevice(); // Close audio device (music streaming is automatically stopped)
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [audio] example - mixed processor");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new MixedProcessor();
game.Init();
// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
{
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}
}

View file

@ -1,26 +1,34 @@
/******************************************************************************************* /*******************************************************************************************
* *
* raylib [audio] example - Module playing (streaming) * raylib [audio] example - module playing
* *
* NOTE: This example requires OpenAL Soft library installed * Example complexity rating: [★☆☆☆] 1/4
* *
* This example has been created using raylib 1.5 (www.raylib.com) * Example originally created with raylib 1.5, last time updated with raylib 3.5
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
* *
* Copyright (c) 2016 Ramon Santamaria (@raysan5) * Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
* BSD-like license that allows static linking with closed source software
*
* Copyright (c) 2016-2025 Ramon Santamaria (@raysan5)
* *
********************************************************************************************/ ********************************************************************************************/
using System.Numerics;
using static Raylib_cs.Raylib;
namespace Examples.Audio; namespace Examples.Audio;
public class ModulePlaying public partial class ModulePlaying : IExample
{ {
const int MaxCircles = 64; private const int screenWidth = 800;
private const int screenHeight = 450;
struct CircleWave private const int MaxCircles = 64;
public string Name => "Audio / Module Playing";
public string Title => "raylib [audio] example - module playing";
public ConfigFlags ConfigFlags => ConfigFlags.Msaa4xHint;
private struct CircleWave
{ {
public Vector2 Position; public Vector2 Position;
public float Radius; public float Radius;
@ -29,20 +37,18 @@ public class ModulePlaying
public Color Color; public Color Color;
} }
public static int Main() private Color[] colors;
private CircleWave[] circles;
private Music music;
private float pitch;
private float timePlayed;
private bool pause;
public void Init()
{ {
// Initialization InitAudioDevice(); // Initialize audio device
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
SetConfigFlags(ConfigFlags.Msaa4xHint); // NOTE: Try to enable MSAA 4X colors = new Color[14] {
InitWindow(screenWidth, screenHeight, "raylib [audio] example - module playing (streaming)");
InitAudioDevice();
Color[] colors = new Color[14] {
Color.Orange, Color.Orange,
Color.Red, Color.Red,
Color.Gold, Color.Gold,
@ -59,133 +65,163 @@ public class ModulePlaying
Color.Beige Color.Beige
}; };
// Creates ome circles for visual effect // Creates some circles for visual effect
CircleWave[] circles = new CircleWave[MaxCircles]; circles = new CircleWave[MaxCircles];
for (int i = MaxCircles - 1; i >= 0; i--) for (var i = MaxCircles - 1; i >= 0; i--)
{ {
circles[i].Alpha = 0.0f; circles[i].Alpha = 0.0f;
circles[i].Radius = GetRandomValue(10, 40); circles[i].Radius = GetRandomValue(10, 40);
circles[i].Position.X = GetRandomValue((int)circles[i].Radius, screenWidth - (int)circles[i].Radius); circles[i].Position.X = GetRandomValue((int)circles[i].Radius, screenWidth - (int)circles[i].Radius);
circles[i].Position.Y = GetRandomValue((int)circles[i].Radius, screenHeight - (int)circles[i].Radius); circles[i].Position.Y = GetRandomValue((int)circles[i].Radius, screenHeight - (int)circles[i].Radius);
circles[i].Speed = (float)GetRandomValue(1, 100) / 20000.0f; circles[i].Speed = (float)GetRandomValue(1, 100) / 2000.0f;
circles[i].Color = colors[GetRandomValue(0, 13)]; circles[i].Color = colors[GetRandomValue(0, 13)];
} }
Music music = LoadMusicStream("resources/audio/mini1111.xm"); music = LoadMusicStream("resources/audio/mini1111.xm");
music.Looping = false; music.Looping = false;
float pitch = 1.0f; pitch = 1.0f;
PlayMusicStream(music); PlayMusicStream(music);
float timePlayed = 0.0f; timePlayed = 0.0f;
bool pause = false; pause = false;
}
SetTargetFPS(60); public void Update()
{
// Update
//----------------------------------------------------------------------------------
UpdateMusicStream(music); // Update music buffer with new stream data
// Restart music playing (stop and play)
if (IsKeyPressed(KeyboardKey.Space))
{
StopMusicStream(music);
PlayMusicStream(music);
}
// Pause/Resume music playing
if (IsKeyPressed(KeyboardKey.P))
{
pause = !pause;
if (pause)
{
PauseMusicStream(music);
}
else
{
ResumeMusicStream(music);
}
}
if (IsKeyDown(KeyboardKey.Down))
{
pitch -= 0.01f;
}
else if (IsKeyDown(KeyboardKey.Up))
{
pitch += 0.01f;
}
SetMusicPitch(music, pitch);
// Get timePlayed scaled to bar dimensions
timePlayed = GetMusicTimePlayed(music) / GetMusicTimeLength(music) * (screenWidth - 40);
// Color circles animation
for (var i = MaxCircles - 1; (i >= 0) && !pause; i--)
{
circles[i].Alpha += circles[i].Speed;
circles[i].Radius += circles[i].Speed * 10.0f;
if (circles[i].Alpha > 1.0f)
{
circles[i].Speed *= -1;
}
if (circles[i].Alpha <= 0.0f)
{
circles[i].Alpha = 0.0f;
circles[i].Radius = GetRandomValue(10, 40);
circles[i].Position.X = GetRandomValue(
(int)circles[i].Radius,
screenWidth - (int)circles[i].Radius
);
circles[i].Position.Y = GetRandomValue(
(int)circles[i].Radius,
screenHeight - (int)circles[i].Radius
);
circles[i].Color = colors[GetRandomValue(0, 13)];
circles[i].Speed = (float)GetRandomValue(1, 100) / 2000.0f;
}
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
for (var i = MaxCircles - 1; i >= 0; i--)
{
DrawCircleV(
circles[i].Position,
circles[i].Radius,
Fade(circles[i].Color, circles[i].Alpha)
);
}
// Draw time bar
DrawRectangle(20, screenHeight - 20 - 12, screenWidth - 40, 12, Color.LightGray);
DrawRectangle(20, screenHeight - 20 - 12, (int)timePlayed, 12, Color.Maroon);
DrawRectangleLines(20, screenHeight - 20 - 12, screenWidth - 40, 12, Color.Gray);
// Draw help instructions
DrawRectangle(20, 20, 425, 145, Color.White);
DrawRectangleLines(20, 20, 425, 145, Color.Gray);
DrawText("PRESS SPACE TO RESTART MUSIC", 40, 40, 20, Color.Black);
DrawText("PRESS P TO PAUSE/RESUME", 40, 70, 20, Color.Black);
DrawText("PRESS UP/DOWN TO CHANGE SPEED", 40, 100, 20, Color.Black);
DrawText($"SPEED: {pitch:F6}", 40, 130, 20, Color.Maroon);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadMusicStream(music); // Unload music stream buffers from RAM
CloseAudioDevice(); // Close audio device (music streaming is automatically stopped)
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
SetConfigFlags(ConfigFlags.Msaa4xHint); // NOTE: Try to enable MSAA 4X
InitWindow(screenWidth, screenHeight, "raylib [audio] example - module playing");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//-------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------
var game = new ModulePlaying();
game.Init();
// Main game loop // Main game loop
while (!WindowShouldClose()) while (!WindowShouldClose()) // Detect window close button or ESC key
{ {
// Update game.Update();
//----------------------------------------------------------------------------------
UpdateMusicStream(music); // Update music buffer with new stream data
// Restart music playing (stop and play)
if (IsKeyPressed(KeyboardKey.Space))
{
StopMusicStream(music);
PlayMusicStream(music);
}
// Pause/Resume music playing
if (IsKeyPressed(KeyboardKey.P))
{
pause = !pause;
if (pause)
{
PauseMusicStream(music);
}
else
{
ResumeMusicStream(music);
}
}
if (IsKeyDown(KeyboardKey.Down))
{
pitch -= 0.01f;
}
else if (IsKeyDown(KeyboardKey.Up))
{
pitch += 0.01f;
}
SetMusicPitch(music, pitch);
// Get timePlayed scaled to bar dimensions
timePlayed = GetMusicTimePlayed(music) / GetMusicTimeLength(music) * (screenWidth - 40);
// Color circles animation
for (int i = MaxCircles - 1; (i >= 0) && !pause; i--)
{
circles[i].Alpha += circles[i].Speed;
circles[i].Radius += circles[i].Speed * 10.0f;
if (circles[i].Alpha > 1.0f)
{
circles[i].Speed *= -1;
}
if (circles[i].Alpha <= 0.0f)
{
circles[i].Alpha = 0.0f;
circles[i].Radius = GetRandomValue(10, 40);
circles[i].Position.X = GetRandomValue(
(int)circles[i].Radius,
screenWidth - (int)circles[i].Radius
);
circles[i].Position.Y = GetRandomValue(
(int)circles[i].Radius,
screenHeight - (int)circles[i].Radius
);
circles[i].Color = colors[GetRandomValue(0, 13)];
circles[i].Speed = (float)GetRandomValue(1, 100) / 2000.0f;
}
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
for (int i = MaxCircles - 1; i >= 0; i--)
{
DrawCircleV(
circles[i].Position,
circles[i].Radius,
ColorAlpha(circles[i].Color, circles[i].Alpha)
);
}
// Draw time bar
DrawRectangle(20, screenHeight - 20 - 12, screenWidth - 40, 12, Color.LightGray);
DrawRectangle(20, screenHeight - 20 - 12, (int)timePlayed, 12, Color.Maroon);
DrawRectangleLines(20, screenHeight - 20 - 12, screenWidth - 40, 12, Color.Gray);
EndDrawing();
//----------------------------------------------------------------------------------
} }
game.Unload();
// De-Initialization // De-Initialization
//-------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------
UnloadMusicStream(music); CloseWindow(); // Close window and OpenGL context
CloseAudioDevice();
CloseWindow();
//-------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------
return 0; return 0;

View file

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

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

@ -0,0 +1,192 @@
/*******************************************************************************************
*
* raylib [audio] example - raw stream
*
* Example complexity rating: [★★★☆] 3/4
*
* Example originally created with raylib 1.6, last time updated with raylib 6.0
*
* Example created by Ramon Santamaria (@raysan5) and reviewed by James Hofmann (@triplefox)
*
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
* BSD-like license that allows static linking with closed source software
*
* Copyright (c) 2015-2026 Ramon Santamaria (@raysan5) and James Hofmann (@triplefox)
*
********************************************************************************************/
namespace Examples.Audio;
public unsafe partial class RawStream : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
private const int BUFFER_SIZE = 4096;
private const int SAMPLE_RATE = 44100;
public string Name => "Audio / Raw Stream";
public string Title => "raylib [audio] example - raw stream";
public int TargetFps => 30;
private float[] buffer;
private AudioStream stream;
private float pan;
private int sineFrequency;
private int newSineFrequency;
private int sineIndex;
private double sineStartTime;
public void Init()
{
InitAudioDevice();
// Set the number of samples the stream will keep in memory at a time to BUFFER_SIZE
SetAudioStreamBufferSizeDefault(BUFFER_SIZE);
buffer = new float[BUFFER_SIZE];
// Init raw audio stream (sample rate: 44100, sample size: 32bit-float, channels: 1-mono)
stream = LoadAudioStream(SAMPLE_RATE, 32, 1);
pan = 0.0f;
SetAudioStreamPan(stream, pan);
PlayAudioStream(stream);
sineFrequency = 440;
newSineFrequency = 440;
sineIndex = 0;
sineStartTime = 0.0;
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
if (IsKeyDown(KeyboardKey.Up))
{
newSineFrequency += 10;
if (newSineFrequency > 12500)
{
newSineFrequency = 12500;
}
}
if (IsKeyDown(KeyboardKey.Down))
{
newSineFrequency -= 10;
if (newSineFrequency < 20)
{
newSineFrequency = 20;
}
}
if (IsKeyDown(KeyboardKey.Left))
{
pan -= 0.01f;
if (pan < -1.0f)
{
pan = -1.0f;
}
SetAudioStreamPan(stream, pan);
}
if (IsKeyDown(KeyboardKey.Right))
{
pan += 0.01f;
if (pan > 1.0f)
{
pan = 1.0f;
}
SetAudioStreamPan(stream, pan);
}
if (IsAudioStreamProcessed(stream))
{
for (int i = 0; i < BUFFER_SIZE; i++)
{
int wl = SAMPLE_RATE / sineFrequency;
buffer[i] = MathF.Sin(2 * MathF.PI * sineIndex / wl);
sineIndex++;
if (sineIndex >= wl)
{
sineFrequency = newSineFrequency;
sineIndex = 0;
sineStartTime = GetTime();
}
}
fixed (float* bufferPtr = buffer)
{
UpdateAudioStream(stream, bufferPtr, BUFFER_SIZE);
}
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
DrawText($"sine frequency: {sineFrequency}", screenWidth - 220, 10, 20, Color.Red);
DrawText($"pan: {pan:F2}", screenWidth - 220, 30, 20, Color.Red);
DrawText("Up/down to change frequency", 10, 10, 20, Color.DarkGray);
DrawText("Left/right to pan", 10, 30, 20, Color.DarkGray);
int windowStart = (int)((GetTime() - sineStartTime) * SAMPLE_RATE);
int windowSize = (int)(0.1f * SAMPLE_RATE);
int wavelength = SAMPLE_RATE / sineFrequency;
// Draw a sine wave with the same frequency as the one being sent to the audio stream
for (int i = 0; i < screenWidth; i++)
{
int t0 = windowStart + i * windowSize / screenWidth;
int t1 = windowStart + (i + 1) * windowSize / screenWidth;
Vector2 startPos = new Vector2(i, 250 + 50 * MathF.Sin(2 * MathF.PI * t0 / wavelength));
Vector2 endPos = new Vector2(i + 1, 250 + 50 * MathF.Sin(2 * MathF.PI * t1 / wavelength));
DrawLineV(startPos, endPos, Color.Red);
}
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadAudioStream(stream); // Close raw audio stream and delete buffers from RAM
CloseAudioDevice(); // Close audio device (music streaming is automatically stopped)
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [audio] example - raw stream");
SetTargetFPS(30);
//--------------------------------------------------------------------------------------
var game = new RawStream();
game.Init();
// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
{
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}
}

View file

@ -1,74 +1,98 @@
/******************************************************************************************* /*******************************************************************************************
* *
* raylib [audio] example - Sound loading and playing * raylib [audio] example - sound loading
* *
* NOTE: This example requires OpenAL Soft library installed * Example complexity rating: [★☆☆☆] 1/4
* *
* This example has been created using raylib 1.0 (www.raylib.com) * Example originally created with raylib 1.1, last time updated with raylib 3.5
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
* *
* Copyright (c) 2014 Ramon Santamaria (@raysan5) * Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
* BSD-like license that allows static linking with closed source software
*
* Copyright (c) 2014-2025 Ramon Santamaria (@raysan5)
* *
********************************************************************************************/ ********************************************************************************************/
using static Raylib_cs.Raylib;
namespace Examples.Audio; namespace Examples.Audio;
public class SoundLoading public partial class SoundLoading : IExample
{ {
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Audio / Sound Loading";
public string Title => "raylib [audio] example - sound loading";
private Sound fxWav;
private Sound fxOgg;
public void Init()
{
InitAudioDevice(); // Initialize audio device
fxWav = LoadSound("resources/audio/sound.wav"); // Load WAV audio file
fxOgg = LoadSound("resources/audio/target.ogg"); // Load OGG audio file
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
if (IsKeyPressed(KeyboardKey.Space))
{
PlaySound(fxWav); // Play WAV sound
}
if (IsKeyPressed(KeyboardKey.Enter))
{
PlaySound(fxOgg); // Play OGG sound
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
DrawText("Press SPACE to PLAY the WAV sound!", 200, 180, 20, Color.LightGray);
DrawText("Press ENTER to PLAY the OGG sound!", 200, 220, 20, Color.LightGray);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadSound(fxWav); // Unload sound data
UnloadSound(fxOgg); // Unload sound data
CloseAudioDevice(); // Close audio device
}
public static int Main() public static int Main()
{ {
// Initialization // Initialization
//-------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------
const int screenWidth = 800; InitWindow(screenWidth, screenHeight, "raylib [audio] example - sound loading");
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [audio] example - sound loading and playing"); SetTargetFPS(60); // Set our game to run at 60 frames-per-second
InitAudioDevice();
Sound fxWav = LoadSound("resources/audio/sound.wav");
Sound fxOgg = LoadSound("resources/audio/target.ogg");
SetTargetFPS(60);
//-------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------
var game = new SoundLoading();
game.Init();
// Main game loop // Main game loop
while (!WindowShouldClose()) while (!WindowShouldClose()) // Detect window close button or ESC key
{ {
// Update game.Update();
//----------------------------------------------------------------------------------
if (IsKeyPressed(KeyboardKey.Space))
{
PlaySound(fxWav);
}
if (IsKeyPressed(KeyboardKey.Enter))
{
PlaySound(fxOgg);
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
DrawText("Press SPACE to PLAY the WAV sound!", 200, 180, 20, Color.LightGray);
DrawText("Press ENTER to PLAY the OGG sound!", 200, 220, 20, Color.LightGray);
EndDrawing();
//----------------------------------------------------------------------------------
} }
game.Unload();
// De-Initialization // De-Initialization
//-------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------
UnloadSound(fxWav); CloseWindow(); // Close window and OpenGL context
UnloadSound(fxOgg);
CloseAudioDevice();
CloseWindow();
//-------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------
return 0; return 0;

View file

@ -0,0 +1,122 @@
/*******************************************************************************************
*
* raylib [audio] example - sound multi
*
* Example complexity rating: [★★☆☆] 2/4
*
* Example originally created with raylib 5.0, last time updated with raylib 5.0
*
* Example contributed by Jeffery Myers (@JeffM2501) and reviewed by Ramon Santamaria (@raysan5)
*
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
* BSD-like license that allows static linking with closed source software
*
* Copyright (c) 2023-2025 Jeffery Myers (@JeffM2501)
*
********************************************************************************************/
namespace Examples.Audio;
public partial class SoundMulti : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
private const int MAX_SOUNDS = 10;
public string Name => "Audio / Sound Multi";
public string Title => "raylib [audio] example - sound multi";
private Sound[] soundArray = new Sound[MAX_SOUNDS];
private int currentSound;
public void Init()
{
InitAudioDevice(); // Initialize audio device
// Load audio file into the first slot as the 'source' sound,
// this sound owns the sample data
soundArray[0] = LoadSound("resources/audio/sound.wav");
// Load an alias of the sound into slots 1-9. These do not own the sound data, but can be played
for (int i = 1; i < MAX_SOUNDS; i++)
{
soundArray[i] = LoadSoundAlias(soundArray[0]);
}
currentSound = 0; // Set the sound list to the start
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
if (IsKeyPressed(KeyboardKey.Space))
{
PlaySound(soundArray[currentSound]); // Play the next open sound slot
currentSound++; // Increment the sound slot
// If the sound slot is out of bounds, go back to 0
if (currentSound >= MAX_SOUNDS)
{
currentSound = 0;
}
// NOTE: Another approach would be to look at the list for the first sound
// that is not playing and use that slot
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
DrawText("Press SPACE to PLAY a WAV sound!", 200, 180, 20, Color.LightGray);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
for (int i = 1; i < MAX_SOUNDS; i++)
{
UnloadSoundAlias(soundArray[i]); // Unload sound aliases
}
UnloadSound(soundArray[0]); // Unload source sound data
CloseAudioDevice(); // Close audio device
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [audio] example - sound multi");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new SoundMulti();
game.Init();
// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
{
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}
}

View file

@ -0,0 +1,156 @@
/*******************************************************************************************
*
* raylib [audio] example - sound positioning
*
* Example complexity rating: [★★☆☆] 2/4
*
* Example originally created with raylib 5.5, last time updated with raylib 5.5
*
* Example contributed by Le Juez Victor (@Bigfoot71) and reviewed by Ramon Santamaria (@raysan5)
*
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
* BSD-like license that allows static linking with closed source software
*
* Copyright (c) 2025 Le Juez Victor (@Bigfoot71)
*
********************************************************************************************/
namespace Examples.Audio;
public partial class SoundPositioning : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Audio / Sound Positioning";
public string Title => "raylib [audio] example - sound positioning";
public bool CursorDisabled => true;
private Sound sound;
private Camera3D camera;
public void Init()
{
InitAudioDevice();
sound = LoadSound("resources/audio/coin.wav");
camera = new Camera3D
{
Position = new Vector3(0, 5, 5),
Target = new Vector3(0, 0, 0),
Up = new Vector3(0, 1, 0),
FovY = 60,
Projection = CameraProjection.Perspective
};
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Free);
float th = (float)GetTime();
Vector3 spherePos = new Vector3(
5.0f * MathF.Cos(th),
0.0f,
5.0f * MathF.Sin(th)
);
SetSoundPosition(camera, sound, spherePos, 1.0f);
if (!IsSoundPlaying(sound))
{
PlaySound(sound);
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawGrid(10, 2);
DrawSphere(spherePos, 0.5f, Color.Red);
EndMode3D();
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadSound(sound);
CloseAudioDevice(); // Close audio device
}
//------------------------------------------------------------------------------------
// Module Functions Definition
//------------------------------------------------------------------------------------
// Set sound 3d position
private static void SetSoundPosition(Camera3D listener, Sound sound, Vector3 position, float maxDist)
{
// Calculate direction vector and distance between listener and sound source
Vector3 direction = Vector3.Subtract(position, listener.Position);
float distance = direction.Length();
// Apply logarithmic distance attenuation and clamp between 0-1
float attenuation = 1.0f / (1.0f + (distance / maxDist));
attenuation = Math.Clamp(attenuation, 0.0f, 1.0f);
// Calculate normalized vectors for spatial positioning
Vector3 normalizedDirection = Vector3.Normalize(direction);
Vector3 forward = Vector3.Normalize(Vector3.Subtract(listener.Target, listener.Position));
Vector3 right = Vector3.Normalize(Vector3.Cross(listener.Up, forward));
// Reduce volume for sounds behind the listener
float dotProduct = Vector3.Dot(forward, normalizedDirection);
if (dotProduct < 0.0f)
{
attenuation *= (1.0f + dotProduct * 0.5f);
}
// Set stereo panning based on sound position relative to listener
float pan = 0.5f + 0.5f * Vector3.Dot(normalizedDirection, right);
// Apply final sound properties
SetSoundVolume(sound, attenuation);
SetSoundPan(sound, pan);
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [audio] example - sound positioning");
DisableCursor();
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
var game = new SoundPositioning();
game.Init();
// Main game loop
while (!WindowShouldClose())
{
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}
}

View file

@ -0,0 +1,369 @@
/*******************************************************************************************
*
* raylib [audio] example - stream callback
*
* Example complexity rating: [★★★☆] 3/4
*
* Example originally created with raylib 6.0, last time updated with raylib 6.0
*
* Example created by Dan Hoang (@dan-hoang) and reviewed by Ramon Santamaria (@raysan5)
*
* NOTE: Example sends a wave to the audio device,
* user gets the choice of four waves: sine, square, triangle, and sawtooth
* A stream is set up to play to the audio device; stream is hooked to a callback that
* generates a wave, that is determined by user choice
*
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
* BSD-like license that allows static linking with closed source software
*
* Copyright (c) 2026 Dan Hoang (@dan-hoang)
*
********************************************************************************************/
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace Examples.Audio;
[ExcludeFromBrowser("SetAudioStreamCallback is unreliable on the wasm audio backend")]
public unsafe partial class StreamCallback : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
private const int BUFFER_SIZE = 4096;
private const int SAMPLE_RATE = 44100;
// Wave type
private enum WaveType
{
Sine,
Square,
Triangle,
Sawtooth
}
public string Name => "Audio / Stream Callback";
public string Title => "raylib [audio] example - stream callback";
public int TargetFps => 30;
private static int waveFrequency = 440;
private static int newWaveFrequency = 440;
private static int waveIndex = 0;
// Buffer to keep the last second of uploaded audio,
// part of which will be drawn on the screen
private static readonly float[] buffer = new float[SAMPLE_RATE];
private static readonly string[] waveTypesAsString = { "sine", "square", "triangle", "sawtooth" };
private AudioStream stream;
private WaveType waveType;
public void Init()
{
InitAudioDevice();
waveFrequency = 440;
newWaveFrequency = 440;
waveIndex = 0;
Array.Clear(buffer, 0, buffer.Length);
// Set the number of samples the stream will keep in memory at a time to BUFFER_SIZE
SetAudioStreamBufferSizeDefault(BUFFER_SIZE);
// Init raw audio stream (sample rate: 44100, sample size: 32bit-float, channels: 1-mono)
stream = LoadAudioStream(SAMPLE_RATE, 32, 1);
PlayAudioStream(stream);
// Configure it so that the callback for waveType is called whenever stream is out of samples
waveType = WaveType.Sine;
SetWaveCallback();
}
// Attach the callback matching the current waveType to the stream
private void SetWaveCallback()
{
switch (waveType)
{
case WaveType.Sine:
SetAudioStreamCallback(stream, &SineCallback);
break;
case WaveType.Square:
SetAudioStreamCallback(stream, &SquareCallback);
break;
case WaveType.Triangle:
SetAudioStreamCallback(stream, &TriangleCallback);
break;
case WaveType.Sawtooth:
SetAudioStreamCallback(stream, &SawtoothCallback);
break;
}
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
if (IsKeyDown(KeyboardKey.Up))
{
newWaveFrequency += 10;
if (newWaveFrequency > 12500)
{
newWaveFrequency = 12500;
}
}
if (IsKeyDown(KeyboardKey.Down))
{
newWaveFrequency -= 10;
if (newWaveFrequency < 20)
{
newWaveFrequency = 20;
}
}
if (IsKeyPressed(KeyboardKey.Left))
{
if (waveType == WaveType.Sine)
{
waveType = WaveType.Sawtooth;
}
else if (waveType == WaveType.Square)
{
waveType = WaveType.Sine;
}
else if (waveType == WaveType.Triangle)
{
waveType = WaveType.Square;
}
else
{
waveType = WaveType.Triangle;
}
SetWaveCallback();
}
if (IsKeyPressed(KeyboardKey.Right))
{
if (waveType == WaveType.Sine)
{
waveType = WaveType.Square;
}
else if (waveType == WaveType.Square)
{
waveType = WaveType.Triangle;
}
else if (waveType == WaveType.Triangle)
{
waveType = WaveType.Sawtooth;
}
else
{
waveType = WaveType.Sine;
}
SetWaveCallback();
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
DrawText($"frequency: {newWaveFrequency}", screenWidth - 220, 10, 20, Color.Red);
DrawText($"wave type: {waveTypesAsString[(int)waveType]}", screenWidth - 220, 30, 20, Color.Red);
DrawText("Up/down to change frequency", 10, 10, 20, Color.DarkGray);
DrawText("Left/right to change wave type", 10, 30, 20, Color.DarkGray);
// Draw the last 10 ms of uploaded audio
for (int i = 0; i < screenWidth; i++)
{
Vector2 startPos = new Vector2(i, 250 - 50 * buffer[WaveSampleIndex(i)]);
Vector2 endPos = new Vector2(i + 1, 250 - 50 * buffer[WaveSampleIndex(i + 1)]);
DrawLineV(startPos, endPos, Color.Red);
}
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadAudioStream(stream); // Close raw audio stream and delete buffers from RAM
CloseAudioDevice(); // Close audio device (music streaming is automatically stopped)
}
// Maps a screen column to a sample index in the last 10 ms of the buffer. The final column
// maps one sample past the end of the buffer; upstream C reads out of bounds there, so we
// clamp to the last valid sample (visually identical, but safe under C# bounds checking).
private static int WaveSampleIndex(int column)
{
int index = SAMPLE_RATE - SAMPLE_RATE / 100 + column * SAMPLE_RATE / 100 / screenWidth;
return index < SAMPLE_RATE ? index : SAMPLE_RATE - 1;
}
//------------------------------------------------------------------------------------
// Module Functions Definition
//------------------------------------------------------------------------------------
[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
private static void SineCallback(void* framesOut, uint frameCount)
{
int fc = (int)frameCount;
int wavelength = SAMPLE_RATE / waveFrequency;
float* frames = (float*)framesOut;
// Synthesize the sine wave
for (int i = 0; i < fc; i++)
{
frames[i] = MathF.Sin(2 * MathF.PI * waveIndex / wavelength);
waveIndex++;
if (waveIndex >= wavelength)
{
waveFrequency = newWaveFrequency;
waveIndex = 0;
}
}
// Save the synthesized samples for later drawing
for (int i = 0; i < SAMPLE_RATE - fc; i++)
{
buffer[i] = buffer[i + fc];
}
for (int i = 0; i < fc; i++)
{
buffer[SAMPLE_RATE - fc + i] = frames[i];
}
}
[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
private static void SquareCallback(void* framesOut, uint frameCount)
{
int fc = (int)frameCount;
int wavelength = SAMPLE_RATE / waveFrequency;
float* frames = (float*)framesOut;
// Synthesize the square wave
for (int i = 0; i < fc; i++)
{
frames[i] = (waveIndex < wavelength / 2) ? 1 : -1;
waveIndex++;
if (waveIndex >= wavelength)
{
waveFrequency = newWaveFrequency;
waveIndex = 0;
}
}
// Save the synthesized samples for later drawing
for (int i = 0; i < SAMPLE_RATE - fc; i++)
{
buffer[i] = buffer[i + fc];
}
for (int i = 0; i < fc; i++)
{
buffer[SAMPLE_RATE - fc + i] = frames[i];
}
}
[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
private static void TriangleCallback(void* framesOut, uint frameCount)
{
int fc = (int)frameCount;
int wavelength = SAMPLE_RATE / waveFrequency;
float* frames = (float*)framesOut;
// Synthesize the triangle wave
for (int i = 0; i < fc; i++)
{
frames[i] = (waveIndex < wavelength / 2) ? (-1 + 2.0f * waveIndex / (wavelength / 2)) : (1 - 2.0f * (waveIndex - wavelength / 2) / (wavelength / 2));
waveIndex++;
if (waveIndex >= wavelength)
{
waveFrequency = newWaveFrequency;
waveIndex = 0;
}
}
// Save the synthesized samples for later drawing
for (int i = 0; i < SAMPLE_RATE - fc; i++)
{
buffer[i] = buffer[i + fc];
}
for (int i = 0; i < fc; i++)
{
buffer[SAMPLE_RATE - fc + i] = frames[i];
}
}
[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
private static void SawtoothCallback(void* framesOut, uint frameCount)
{
int fc = (int)frameCount;
int wavelength = SAMPLE_RATE / waveFrequency;
float* frames = (float*)framesOut;
// Synthesize the sawtooth wave
for (int i = 0; i < fc; i++)
{
frames[i] = -1 + 2.0f * waveIndex / wavelength;
waveIndex++;
if (waveIndex >= wavelength)
{
waveFrequency = newWaveFrequency;
waveIndex = 0;
}
}
// Save the synthesized samples for later drawing
for (int i = 0; i < SAMPLE_RATE - fc; i++)
{
buffer[i] = buffer[i + fc];
}
for (int i = 0; i < fc; i++)
{
buffer[SAMPLE_RATE - fc + i] = frames[i];
}
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [audio] example - stream callback");
SetTargetFPS(30);
//--------------------------------------------------------------------------------------
var game = new StreamCallback();
game.Init();
// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
{
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}
}

View file

@ -0,0 +1,248 @@
/*******************************************************************************************
*
* raylib [audio] example - stream effects
*
* Example complexity rating: [★★★★] 4/4
*
* Example originally created with raylib 4.2, last time updated with raylib 5.0
*
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
* BSD-like license that allows static linking with closed source software
*
* Copyright (c) 2022-2025 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace Examples.Audio;
[ExcludeFromBrowser("AttachAudioStreamProcessor is unreliable on the wasm audio backend")]
public unsafe partial class StreamEffects : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Audio / Stream Effects";
public string Title => "raylib [audio] example - stream effects";
//----------------------------------------------------------------------------------
// Global Variables Definition
//----------------------------------------------------------------------------------
private static float[] delayBuffer = null;
private static uint delayBufferSize = 0;
private static uint delayReadIndex = 2;
private static uint delayWriteIndex = 0;
// Low-pass filter state (was a function-local static in the C example)
private static readonly float[] low = { 0.0f, 0.0f };
private Music music;
private float timePlayed;
private bool pause;
private bool enableEffectLPF;
private bool enableEffectDelay;
public void Init()
{
InitAudioDevice(); // Initialize audio device
music = LoadMusicStream("resources/audio/country.mp3");
// Allocate buffer for the delay effect
delayBufferSize = 48000 * 2; // 1 second delay (device sampleRate*channels)
delayBuffer = new float[delayBufferSize];
delayReadIndex = 2;
delayWriteIndex = 0;
low[0] = 0.0f;
low[1] = 0.0f;
PlayMusicStream(music);
timePlayed = 0.0f; // Time played normalized [0.0f..1.0f]
pause = false; // Music playing paused
enableEffectLPF = false; // Enable effect low-pass-filter
enableEffectDelay = false; // Enable effect delay (1 second)
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
UpdateMusicStream(music); // Update music buffer with new stream data
// Restart music playing (stop and play)
if (IsKeyPressed(KeyboardKey.Space))
{
StopMusicStream(music);
PlayMusicStream(music);
}
// Pause/Resume music playing
if (IsKeyPressed(KeyboardKey.P))
{
pause = !pause;
if (pause)
{
PauseMusicStream(music);
}
else
{
ResumeMusicStream(music);
}
}
// Add/Remove effect: lowpass filter
if (IsKeyPressed(KeyboardKey.F))
{
enableEffectLPF = !enableEffectLPF;
if (enableEffectLPF)
{
AttachAudioStreamProcessor(music.Stream, &AudioProcessEffectLPF);
}
else
{
DetachAudioStreamProcessor(music.Stream, &AudioProcessEffectLPF);
}
}
// Add/Remove effect: delay
if (IsKeyPressed(KeyboardKey.D))
{
enableEffectDelay = !enableEffectDelay;
if (enableEffectDelay)
{
AttachAudioStreamProcessor(music.Stream, &AudioProcessEffectDelay);
}
else
{
DetachAudioStreamProcessor(music.Stream, &AudioProcessEffectDelay);
}
}
// Get normalized time played for current music stream
timePlayed = GetMusicTimePlayed(music) / GetMusicTimeLength(music);
if (timePlayed > 1.0f)
{
timePlayed = 1.0f; // Make sure time played is no longer than music
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
DrawText("MUSIC SHOULD BE PLAYING!", 245, 150, 20, Color.LightGray);
DrawRectangle(200, 180, 400, 12, Color.LightGray);
DrawRectangle(200, 180, (int)(timePlayed * 400.0f), 12, Color.Maroon);
DrawRectangleLines(200, 180, 400, 12, Color.Gray);
DrawText("PRESS SPACE TO RESTART MUSIC", 215, 230, 20, Color.LightGray);
DrawText("PRESS P TO PAUSE/RESUME MUSIC", 208, 260, 20, Color.LightGray);
DrawText($"PRESS F TO TOGGLE LPF EFFECT: {(enableEffectLPF ? "ON" : "OFF")}", 200, 320, 20, Color.Gray);
DrawText($"PRESS D TO TOGGLE DELAY EFFECT: {(enableEffectDelay ? "ON" : "OFF")}", 180, 350, 20, Color.Gray);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadMusicStream(music); // Unload music stream buffers from RAM
CloseAudioDevice(); // Close audio device (music streaming is automatically stopped)
delayBuffer = null; // Free delay buffer
}
//------------------------------------------------------------------------------------
// Module Functions Definition
//------------------------------------------------------------------------------------
// Audio effect: lowpass filter
[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
private static void AudioProcessEffectLPF(void* buffer, uint frames)
{
const float cutoff = 70.0f / 44100.0f; // 70 Hz lowpass filter
const float k = cutoff / (cutoff + 0.1591549431f); // RC filter formula
// Converts the buffer data before using it
float* bufferData = (float*)buffer;
for (uint i = 0; i < frames * 2; i += 2)
{
float l = bufferData[i];
float r = bufferData[i + 1];
low[0] += k * (l - low[0]);
low[1] += k * (r - low[1]);
bufferData[i] = low[0];
bufferData[i + 1] = low[1];
}
}
// Audio effect: delay
[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
private static void AudioProcessEffectDelay(void* buffer, uint frames)
{
float* bufferData = (float*)buffer;
fixed (float* delay = delayBuffer)
{
for (uint i = 0; i < frames * 2; i += 2)
{
float leftDelay = delay[delayReadIndex++]; // ERROR: Reading buffer -> WHY??? Maybe thread related???
float rightDelay = delay[delayReadIndex++];
if (delayReadIndex == delayBufferSize)
{
delayReadIndex = 0;
}
bufferData[i] = 0.5f * bufferData[i] + 0.5f * leftDelay;
bufferData[i + 1] = 0.5f * bufferData[i + 1] + 0.5f * rightDelay;
delay[delayWriteIndex++] = bufferData[i];
delay[delayWriteIndex++] = bufferData[i + 1];
if (delayWriteIndex == delayBufferSize)
{
delayWriteIndex = 0;
}
}
}
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [audio] example - stream effects");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new StreamEffects();
game.Init();
// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
{
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}
}

View file

@ -0,0 +1,419 @@
/*******************************************************************************************
*
* raylib [core] example - automation events
*
* Example complexity rating: [★★★☆] 3/4
*
* Example originally created with raylib 5.0, last time updated with raylib 5.0
*
* Example based on 2d_camera_platformer example by arvyy (@arvyy)
*
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
* BSD-like license that allows static linking with closed source software
*
* Copyright (c) 2023-2025 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
namespace Examples.Core;
public partial class AutomationEvents : IExample
{
private const int GRAVITY = 400;
private const float PLAYER_JUMP_SPD = 350.0f;
private const float PLAYER_HOR_SPD = 200.0f;
private const int MAX_ENVIRONMENT_ELEMENTS = 5;
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Core / Automation Events";
public string Title => "raylib [core] example - automation events";
private struct Player
{
public Vector2 Position;
public float Speed;
public bool CanJump;
}
private struct EnvElement
{
public Rectangle Rect;
public int Blocking;
public Color Color;
}
private Player player;
private EnvElement[] envElements;
private Camera2D camera;
private AutomationEventList aelist;
private bool eventRecording;
private bool eventPlaying;
private uint frameCounter;
private uint playFrameCounter;
private uint currentPlayFrame;
public unsafe void Init()
{
// Define player
player = new Player();
player.Position = new Vector2(400, 280);
player.Speed = 0;
player.CanJump = false;
// Define environment elements (platforms)
envElements = new EnvElement[MAX_ENVIRONMENT_ELEMENTS]
{
new EnvElement { Rect = new Rectangle(0, 0, 1000, 400), Blocking = 0, Color = Color.LightGray },
new EnvElement { Rect = new Rectangle(0, 400, 1000, 200), Blocking = 1, Color = Color.Gray },
new EnvElement { Rect = new Rectangle(300, 200, 400, 10), Blocking = 1, Color = Color.Gray },
new EnvElement { Rect = new Rectangle(250, 300, 100, 10), Blocking = 1, Color = Color.Gray },
new EnvElement { Rect = new Rectangle(650, 300, 100, 10), Blocking = 1, Color = Color.Gray }
};
// Define camera
camera = new Camera2D();
camera.Target = player.Position;
camera.Offset = new Vector2(screenWidth / 2.0f, screenHeight / 2.0f);
camera.Rotation = 0.0f;
camera.Zoom = 1.0f;
// Automation events
aelist = LoadAutomationEventList((sbyte*)null); // Initialize list of automation events to record new events
SetAutomationEventList(ref aelist);
eventRecording = false;
eventPlaying = false;
frameCounter = 0;
playFrameCounter = 0;
currentPlayFrame = 0;
}
public unsafe void Update()
{
// Update
//----------------------------------------------------------------------------------
float deltaTime = 0.015f;//GetFrameTime();
// Dropped files logic
//----------------------------------------------------------------------------------
#if BROWSER
// NOTE: drag-and-drop event-list loading (.txt/.rae) is not supported in the browser host.
#else
if (IsFileDropped())
{
FilePathList droppedFiles = LoadDroppedFiles();
// Supports loading .rgs style files (text or binary) and .png style palette images
if (IsFileExtension(droppedFiles[0], ".txt;.rae"))
{
UnloadAutomationEventList(aelist);
aelist = LoadAutomationEventList(droppedFiles[0]);
eventRecording = false;
// Reset scene state to play
eventPlaying = true;
playFrameCounter = 0;
currentPlayFrame = 0;
player.Position = new Vector2(400, 280);
player.Speed = 0;
player.CanJump = false;
camera.Target = player.Position;
camera.Offset = new Vector2(screenWidth / 2.0f, screenHeight / 2.0f);
camera.Rotation = 0.0f;
camera.Zoom = 1.0f;
}
UnloadDroppedFiles(droppedFiles); // Unload filepaths from memory
}
#endif
//----------------------------------------------------------------------------------
// Update player
//----------------------------------------------------------------------------------
if (IsKeyDown(KeyboardKey.Left))
{
player.Position.X -= PLAYER_HOR_SPD * deltaTime;
}
if (IsKeyDown(KeyboardKey.Right))
{
player.Position.X += PLAYER_HOR_SPD * deltaTime;
}
if (IsKeyDown(KeyboardKey.Space) && player.CanJump)
{
player.Speed = -PLAYER_JUMP_SPD;
player.CanJump = false;
}
int hitObstacle = 0;
for (int i = 0; i < MAX_ENVIRONMENT_ELEMENTS; i++)
{
EnvElement element = envElements[i];
if (element.Blocking != 0 &&
element.Rect.X <= player.Position.X &&
element.Rect.X + element.Rect.Width >= player.Position.X &&
element.Rect.Y >= player.Position.Y &&
element.Rect.Y <= player.Position.Y + player.Speed * deltaTime)
{
hitObstacle = 1;
player.Speed = 0.0f;
player.Position.Y = element.Rect.Y;
}
}
if (hitObstacle == 0)
{
player.Position.Y += player.Speed * deltaTime;
player.Speed += GRAVITY * deltaTime;
player.CanJump = false;
}
else
{
player.CanJump = true;
}
if (IsKeyPressed(KeyboardKey.R))
{
// Reset game state
player.Position = new Vector2(400, 280);
player.Speed = 0;
player.CanJump = false;
camera.Target = player.Position;
camera.Offset = new Vector2(screenWidth / 2.0f, screenHeight / 2.0f);
camera.Rotation = 0.0f;
camera.Zoom = 1.0f;
}
//----------------------------------------------------------------------------------
// Events playing
// NOTE: Logic must be before Camera update because it depends on mouse-wheel value,
// that can be set by the played event... but some other inputs could be affected
//----------------------------------------------------------------------------------
if (eventPlaying)
{
// NOTE: Multiple events could be executed in a single frame
while (playFrameCounter == aelist.Events[currentPlayFrame].Frame)
{
PlayAutomationEvent(aelist.Events[currentPlayFrame]);
currentPlayFrame++;
if (currentPlayFrame == aelist.Count)
{
eventPlaying = false;
currentPlayFrame = 0;
playFrameCounter = 0;
TraceLog(TraceLogLevel.Info, "FINISH PLAYING!");
break;
}
}
playFrameCounter++;
}
//----------------------------------------------------------------------------------
// Update camera
//----------------------------------------------------------------------------------
camera.Target = player.Position;
camera.Offset = new Vector2(screenWidth / 2.0f, screenHeight / 2.0f);
float minX = 1000, minY = 1000, maxX = -1000, maxY = -1000;
// WARNING: On event replay, mouse-wheel internal value is set
camera.Zoom += ((float)GetMouseWheelMove() * 0.05f);
if (camera.Zoom > 3.0f)
{
camera.Zoom = 3.0f;
}
else if (camera.Zoom < 0.25f)
{
camera.Zoom = 0.25f;
}
for (int i = 0; i < MAX_ENVIRONMENT_ELEMENTS; i++)
{
EnvElement element = envElements[i];
minX = MathF.Min(element.Rect.X, minX);
maxX = MathF.Max(element.Rect.X + element.Rect.Width, maxX);
minY = MathF.Min(element.Rect.Y, minY);
maxY = MathF.Max(element.Rect.Y + element.Rect.Height, maxY);
}
Vector2 max = GetWorldToScreen2D(new Vector2(maxX, maxY), camera);
Vector2 min = GetWorldToScreen2D(new Vector2(minX, minY), camera);
if (max.X < screenWidth)
{
camera.Offset.X = screenWidth - (max.X - (float)screenWidth / 2);
}
if (max.Y < screenHeight)
{
camera.Offset.Y = screenHeight - (max.Y - (float)screenHeight / 2);
}
if (min.X > 0)
{
camera.Offset.X = (float)screenWidth / 2 - min.X;
}
if (min.Y > 0)
{
camera.Offset.Y = (float)screenHeight / 2 - min.Y;
}
//----------------------------------------------------------------------------------
// Events management
if (IsKeyPressed(KeyboardKey.S)) // Toggle events recording
{
if (!eventPlaying)
{
if (eventRecording)
{
StopAutomationEventRecording();
eventRecording = false;
ExportAutomationEventList(aelist, "automation.rae");
TraceLog(TraceLogLevel.Info, $"RECORDED FRAMES: {aelist.Count}");
}
else
{
SetAutomationEventBaseFrame(180);
StartAutomationEventRecording();
eventRecording = true;
}
}
}
else if (IsKeyPressed(KeyboardKey.A)) // Toggle events playing (WARNING: Starts next frame)
{
if (!eventRecording && (aelist.Count > 0))
{
// Reset scene state to play
eventPlaying = true;
playFrameCounter = 0;
currentPlayFrame = 0;
player.Position = new Vector2(400, 280);
player.Speed = 0;
player.CanJump = false;
camera.Target = player.Position;
camera.Offset = new Vector2(screenWidth / 2.0f, screenHeight / 2.0f);
camera.Rotation = 0.0f;
camera.Zoom = 1.0f;
}
}
if (eventRecording || eventPlaying)
{
frameCounter++;
}
else
{
frameCounter = 0;
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.LightGray);
BeginMode2D(camera);
// Draw environment elements
for (int i = 0; i < MAX_ENVIRONMENT_ELEMENTS; i++)
{
DrawRectangleRec(envElements[i].Rect, envElements[i].Color);
}
// Draw player rectangle
DrawRectangleRec(new Rectangle(player.Position.X - 20, player.Position.Y - 40, 40, 40), Color.Red);
EndMode2D();
// Draw game controls
DrawRectangle(10, 10, 290, 145, Fade(Color.SkyBlue, 0.5f));
DrawRectangleLines(10, 10, 290, 145, Fade(Color.Blue, 0.8f));
DrawText("Controls:", 20, 20, 10, Color.Black);
DrawText("- RIGHT | LEFT: Player movement", 30, 40, 10, Color.DarkGray);
DrawText("- SPACE: Player jump", 30, 60, 10, Color.DarkGray);
DrawText("- R: Reset game state", 30, 80, 10, Color.DarkGray);
DrawText("- S: START/STOP RECORDING INPUT EVENTS", 30, 110, 10, Color.Black);
DrawText("- A: REPLAY LAST RECORDED INPUT EVENTS", 30, 130, 10, Color.Black);
// Draw automation events recording indicator
if (eventRecording)
{
DrawRectangle(10, 160, 290, 30, Fade(Color.Red, 0.3f));
DrawRectangleLines(10, 160, 290, 30, Fade(Color.Maroon, 0.8f));
DrawCircle(30, 175, 10, Color.Maroon);
if (((frameCounter / 15) % 2) == 1)
{
DrawText($"RECORDING EVENTS... [{aelist.Count}]", 50, 170, 10, Color.Maroon);
}
}
else if (eventPlaying)
{
DrawRectangle(10, 160, 290, 30, Fade(Color.Lime, 0.3f));
DrawRectangleLines(10, 160, 290, 30, Fade(Color.DarkGreen, 0.8f));
DrawTriangle(new Vector2(20, 155 + 10), new Vector2(20, 155 + 30), new Vector2(40, 155 + 20), Color.DarkGreen);
if (((frameCounter / 15) % 2) == 1)
{
DrawText($"PLAYING RECORDED EVENTS... [{currentPlayFrame}]", 50, 170, 10, Color.DarkGreen);
}
}
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadAutomationEventList(aelist);
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [core] example - automation events");
var game = new AutomationEvents();
game.Init();
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
// Main game loop
while (!WindowShouldClose())
{
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,87 +1,114 @@
/******************************************************************************************* /*******************************************************************************************
* *
* raylib [core] example - Initialize 3d camera free * raylib [core] example - 3d camera free
* *
* This example has been created using raylib 1.3 (www.raylib.com) * Example complexity rating: [★☆☆☆] 1/4
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
* *
* Copyright (c) 2015 Ramon Santamaria (@raysan5) * Example originally created with raylib 1.3, last time updated with raylib 1.3
*
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
* BSD-like license that allows static linking with closed source software
*
* Copyright (c) 2015-2025 Ramon Santamaria (@raysan5)
* *
********************************************************************************************/ ********************************************************************************************/
using System.Numerics;
using static Raylib_cs.Raylib;
namespace Examples.Core; namespace Examples.Core;
public class Camera3dFree public partial class Camera3dFree : IExample
{ {
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Core / Camera 3D Free";
public string Title => "raylib [core] example - 3d camera free";
public bool CursorDisabled => true;
private Camera3D camera;
private Vector3 cubePosition;
public void Init()
{
// Define the camera to look into our 3d world
camera = new();
camera.Position = new Vector3(10.0f, 10.0f, 10.0f); // Camera position
camera.Target = new Vector3(0.0f, 0.0f, 0.0f); // Camera looking at point
camera.Up = new Vector3(0.0f, 1.0f, 0.0f); // Camera up vector (rotation towards target)
camera.FovY = 45.0f; // Camera field-of-view Y
camera.Projection = CameraProjection.Perspective; // Camera projection type
cubePosition = new(0.0f, 0.0f, 0.0f);
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Free);
if (IsKeyPressed(KeyboardKey.Z))
{
camera.Target = new Vector3(0.0f, 0.0f, 0.0f);
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawCube(cubePosition, 2.0f, 2.0f, 2.0f, Color.Red);
DrawCubeWires(cubePosition, 2.0f, 2.0f, 2.0f, Color.Maroon);
DrawGrid(10, 1.0f);
EndMode3D();
DrawRectangle(10, 10, 320, 93, Fade(Color.SkyBlue, 0.5f));
DrawRectangleLines(10, 10, 320, 93, Color.Blue);
DrawText("Free camera default controls:", 20, 20, 10, Color.Black);
DrawText("- Mouse Wheel to Zoom in-out", 40, 40, 10, Color.DarkGray);
DrawText("- Mouse Wheel Pressed to Pan", 40, 60, 10, Color.DarkGray);
DrawText("- Z to zoom to (0, 0, 0)", 40, 80, 10, Color.DarkGray);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
}
public static int Main() public static int Main()
{ {
// Initialization // Initialization
//-------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [core] example - 3d camera free"); InitWindow(screenWidth, screenHeight, "raylib [core] example - 3d camera free");
// Define the camera to look into our 3d world DisableCursor(); // Limit cursor to relative movement inside the window
Camera3D camera;
camera.Position = new Vector3(10.0f, 10.0f, 10.0f);
camera.Target = new Vector3(0.0f, 0.0f, 0.0f);
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
camera.FovY = 45.0f;
camera.Projection = CameraProjection.Perspective;
Vector3 cubePosition = new(0.0f, 0.0f, 0.0f); SetTargetFPS(60); // Set our game to run at 60 frames-per-second
SetTargetFPS(60);
//-------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------
var game = new Camera3dFree();
game.Init();
// Main game loop // Main game loop
while (!WindowShouldClose()) while (!WindowShouldClose()) // Detect window close button or ESC key
{ {
// Update game.Update();
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Free);
if (IsKeyDown(KeyboardKey.Z))
{
camera.Target = new Vector3(0.0f, 0.0f, 0.0f);
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawCube(cubePosition, 2.0f, 2.0f, 2.0f, Color.Red);
DrawCubeWires(cubePosition, 2.0f, 2.0f, 2.0f, Color.Maroon);
DrawGrid(10, 1.0f);
EndMode3D();
DrawRectangle(10, 10, 320, 133, ColorAlpha(Color.SkyBlue, 0.5f));
DrawRectangleLines(10, 10, 320, 133, Color.Blue);
DrawText("Free camera default controls:", 20, 20, 10, Color.Black);
DrawText("- Mouse Wheel to Zoom in-out", 40, 40, 10, Color.DarkGray);
DrawText("- Mouse Wheel Pressed to Pan", 40, 60, 10, Color.DarkGray);
DrawText("- Alt + Mouse Wheel Pressed to Rotate", 40, 80, 10, Color.DarkGray);
DrawText("- Alt + Ctrl + Mouse Wheel Pressed for Smooth Zoom", 40, 100, 10, Color.DarkGray);
DrawText("- Z to zoom to (0, 0, 0)", 40, 120, 10, Color.DarkGray);
EndDrawing();
//----------------------------------------------------------------------------------
} }
game.Unload();
// De-Initialization // De-Initialization
//-------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------
CloseWindow(); CloseWindow(); // Close window and OpenGL context
//-------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------
return 0; return 0;

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