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:
parent
b207e633ae
commit
8c22e68c2a
236 changed files with 40405 additions and 10896 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -63,6 +63,7 @@ project.lock.json
|
||||||
project.fragment.lock.json
|
project.fragment.lock.json
|
||||||
artifacts/
|
artifacts/
|
||||||
**/Properties/launchSettings.json
|
**/Properties/launchSettings.json
|
||||||
|
dotnet-tools.json
|
||||||
|
|
||||||
*_i.c
|
*_i.c
|
||||||
*_p.c
|
*_p.c
|
||||||
|
|
|
||||||
273
Examples/Audio/AmpEnvelope.cs
Normal file
273
Examples/Audio/AmpEnvelope.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
177
Examples/Audio/MixedProcessor.cs
Normal file
177
Examples/Audio/MixedProcessor.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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
192
Examples/Audio/RawStream.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
122
Examples/Audio/SoundMulti.cs
Normal file
122
Examples/Audio/SoundMulti.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
156
Examples/Audio/SoundPositioning.cs
Normal file
156
Examples/Audio/SoundPositioning.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
369
Examples/Audio/StreamCallback.cs
Normal file
369
Examples/Audio/StreamCallback.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
248
Examples/Audio/StreamEffects.cs
Normal file
248
Examples/Audio/StreamEffects.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
419
Examples/Core/AutomationEvents.cs
Normal file
419
Examples/Core/AutomationEvents.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
181
Examples/Core/Camera2dMouseZoom.cs
Normal file
181
Examples/Core/Camera2dMouseZoom.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
360
Examples/Core/Camera3dFps.cs
Normal file
360
Examples/Core/Camera3dFps.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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
Loading…
Reference in a new issue