From bcd0650aed4b55d76a3cd16144e615173ab5781a Mon Sep 17 00:00:00 2001 From: HiveBeats Date: Thu, 18 Apr 2024 22:54:09 +0700 Subject: [PATCH] feat: camera following player --- Components/Player/CameraComponent.cs | 3 +- Entities/BlockFactory.cs | 14 +----- Entities/PlayerFactory.cs | 3 +- Entities/RoomBuilder.cs | 20 ++++++++ Models/CameraRef.cs | 56 +++++++++++++++++++++ Program.cs | 28 ++++------- SystemRegistrations.cs | 6 ++- Systems/Player/CameraSystem.cs | 62 ----------------------- Systems/Rendering/CameraSystem.cs | 46 +++++++++++++++++ Systems/Rendering/SliderSystem.cs | 75 ++++++++++++++++++++++++++++ 10 files changed, 216 insertions(+), 97 deletions(-) create mode 100644 Entities/RoomBuilder.cs create mode 100644 Models/CameraRef.cs delete mode 100644 Systems/Player/CameraSystem.cs create mode 100644 Systems/Rendering/CameraSystem.cs create mode 100644 Systems/Rendering/SliderSystem.cs diff --git a/Components/Player/CameraComponent.cs b/Components/Player/CameraComponent.cs index de98267..6bbe5e0 100644 --- a/Components/Player/CameraComponent.cs +++ b/Components/Player/CameraComponent.cs @@ -1,3 +1,4 @@ +using BakeryGame.Models; using Raylib_cs; using Scellecs.Morpeh; @@ -5,5 +6,5 @@ namespace BakeryGame.Components.Player; public struct CameraComponent : IComponent { - public Camera3D Camera; + public CameraRef Camera; } \ No newline at end of file diff --git a/Entities/BlockFactory.cs b/Entities/BlockFactory.cs index 46bd329..d91ba9d 100644 --- a/Entities/BlockFactory.cs +++ b/Entities/BlockFactory.cs @@ -28,19 +28,7 @@ public class BlockFactory return block; } - public IEnumerable GenerateMapOfBlocks() - { - for (int x = -16 / 2; x <= 16 / 2; x++) { - for (int z = -16 / 2; z <= 16 / 2; z++) { - if (x == -16 / 2 || x == 16 / 2 || z == -16 / 2 || z == 16 / 2) - { - yield return CreateBlock(x, z); - } - } - } - - - } + public const float BlockSize = 1; } \ No newline at end of file diff --git a/Entities/PlayerFactory.cs b/Entities/PlayerFactory.cs index 7c24b40..5167e5a 100644 --- a/Entities/PlayerFactory.cs +++ b/Entities/PlayerFactory.cs @@ -1,6 +1,7 @@ using System.Numerics; using BakeryGame.Components.Common; using BakeryGame.Components.Player; +using BakeryGame.Models; using Raylib_cs; using Scellecs.Morpeh; @@ -23,7 +24,7 @@ public class PlayerFactory player.SetComponent(new MovementComponent() { Speed = 0.1f }); camera = new CameraComponent() { - Camera = new Camera3D(new(0.0f, 10.0f, 10.0f), new(0.0f, 0.0f, 0.0f), new(0.0f, 1.0f, 0.0f), 45.0f, 0) + Camera = new CameraRef(new(0.0f, 20.0f, 10.0f), new(0.0f, 0.0f, 0.0f), new(0.0f, 1.0f, 0.0f), 60.0f) }; player.SetComponent(camera); diff --git a/Entities/RoomBuilder.cs b/Entities/RoomBuilder.cs new file mode 100644 index 0000000..c13e938 --- /dev/null +++ b/Entities/RoomBuilder.cs @@ -0,0 +1,20 @@ +using Scellecs.Morpeh; + +namespace BakeryGame.Entities; + +public class RoomBuilder +{ + public static int RoomSize = 16; + + public static IEnumerable GenerateMapOfBlocks(BlockFactory blockFactory) + { + for (int x = -RoomSize / 2; x <= RoomSize / 2; x++) { + for (int z = -RoomSize / 2; z <= RoomSize / 2; z++) { + if (x == -RoomSize / 2 || x == RoomSize / 2 || z == -RoomSize / 2 || z == RoomSize / 2) + { + yield return blockFactory.CreateBlock(x, z); + } + } + } + } +} \ No newline at end of file diff --git a/Models/CameraRef.cs b/Models/CameraRef.cs new file mode 100644 index 0000000..3a1263e --- /dev/null +++ b/Models/CameraRef.cs @@ -0,0 +1,56 @@ +using System.Numerics; +using Raylib_cs; + +namespace BakeryGame.Models; + +public class CameraRef +{ + private Camera3D _cam; + + public CameraRef(Vector3 position, Vector3 target, Vector3 up, float fovy) + { + _cam = new Camera3D(position, target, up, fovy, 0); + } + + /// + /// Camera position + /// + public void SetPosition(Vector3 position) + { + _cam.Position = position; + } + + /// + /// Camera target it looks-at + /// + public void SetTarget(Vector3 target) + { + _cam.Target = target; + } + + /// + /// Camera up vector (rotation over its axis) + /// + public void SetUp(Vector3 up) + { + _cam.Up = up; + } + + /// + /// Camera field-of-view apperture in Y (degrees) in perspective, used as near plane width in orthographic + /// + public void FovY(float fovy) + { + _cam.FovY = fovy; + } + + /// + /// Camera type, defines projection type: CAMERA_PERSPECTIVE or CAMERA_ORTHOGRAPHIC + /// + public void SetProjection(CameraProjection projection) + { + _cam.Projection = projection; + } + + public Camera3D GetCamera3D() => _cam; +} \ No newline at end of file diff --git a/Program.cs b/Program.cs index 129b909..76a1dc5 100644 --- a/Program.cs +++ b/Program.cs @@ -18,17 +18,8 @@ namespace BakeryGame; internal class Program { private static World _world; - - private static readonly EnvItem[] _envItems = - { - new EnvItem(new Rectangle(0, 0, 1000, 400), Color.LightGray, false), - new EnvItem(new Rectangle(0, 400, 1000, 200), Color.Gray, true), - new EnvItem(new Rectangle(300, 200, 400, 100), Color.Gray, true), - new EnvItem(new Rectangle(250, 300, 100, 10), Color.Gray, true), - new EnvItem(new Rectangle(650, 300, 100, 10), Color.Gray, true) - }; - - + private static int WindowWidth = 800; + private static int WindowHeight = 480; private static void Main(string[] args) { @@ -36,10 +27,11 @@ internal class Program var playerFactory = new PlayerFactory(_world); var blockFactory = new BlockFactory(_world); - Raylib.InitWindow(800, 480, "Hello World"); + Raylib.InitWindow(WindowWidth, WindowHeight, "Hello World"); - var player = playerFactory.CreatePlayer(out var camera); - var block = blockFactory.GenerateMapOfBlocks().ToList(); + CameraComponent camera; + var player = playerFactory.CreatePlayer(out camera); + var block = RoomBuilder.GenerateMapOfBlocks(blockFactory).ToList(); SystemRegistrations.RegisterLogicGroup(_world); SystemRegistrations.RegisterGraphicsGroup(_world); @@ -48,15 +40,15 @@ internal class Program while (!Raylib.WindowShouldClose()) { var deltaTime = Raylib.GetFrameTime(); - //Raylib.UpdateCamera(ref camera.Camera, CameraMode.Free); + //Raylib.UpdateCamera(ref camera.Camera, CameraMode.Custom); Raylib.BeginDrawing(); Raylib.ClearBackground(Color.White); - Raylib.BeginMode3D(camera.Camera); + Raylib.BeginMode3D(camera.Camera.GetCamera3D()); _world.Update(deltaTime); - Raylib.DrawGrid(10, 1.0f); + Raylib.DrawGrid(RoomBuilder.RoomSize, 1.0f); _world.CleanupUpdate(deltaTime); Raylib.EndMode3D(); - Raylib.DrawFPS(100, 10); + Raylib.DrawFPS(WindowWidth - 100, 12); _world.LateUpdate(deltaTime); _world.Commit(); Raylib.EndDrawing(); diff --git a/SystemRegistrations.cs b/SystemRegistrations.cs index acd54d2..d7e9d21 100644 --- a/SystemRegistrations.cs +++ b/SystemRegistrations.cs @@ -12,11 +12,13 @@ public static class SystemRegistrations var healthSystem = new HealthSystem { World = world }; var movementSystem = new MovementSystem { World = world }; var collisionSystem = new CollisionSystem(world); + var systemsGroup = world.CreateSystemsGroup(); systemsGroup.AddSystem(healthSystem); systemsGroup.AddSystem(movementSystem); systemsGroup.AddSystem(collisionSystem); - + + systemsGroup.EnableSystem(movementSystem); systemsGroup.EnableSystem(healthSystem); systemsGroup.EnableSystem(collisionSystem); @@ -28,7 +30,7 @@ public static class SystemRegistrations var renderSystemsGroup = world.CreateSystemsGroup(); var hpRenreSystem = new HPRenderSystem { World = world }; var ballRenderSystem = new PlayerRenderSystem { World = world }; - var cameraSystem = new CameraSystem(world); + var cameraSystem = new CameraSystem() {World = world }; var blockRenderSystem = new BlockRenderSystem(world); renderSystemsGroup.AddSystem(blockRenderSystem); diff --git a/Systems/Player/CameraSystem.cs b/Systems/Player/CameraSystem.cs deleted file mode 100644 index e950528..0000000 --- a/Systems/Player/CameraSystem.cs +++ /dev/null @@ -1,62 +0,0 @@ -using BakeryGame.Components.Common; -using BakeryGame.Components.Player; -using Scellecs.Morpeh; - -namespace BakeryGame.Systems.Player; - -public class CameraSystem : ISystem -{ - private Filter _filter; - - public CameraSystem(World world) - { - World = world; - } - - public World World { get; set; } - - - public void OnAwake() - { - _filter = World.Filter.With().Build(); - } - - public void OnUpdate(float deltaTime) - { - foreach (var entity in _filter) - { - var position = entity.GetComponent().Position; - ref var cameraComponent = ref entity.GetComponent(); - - ref var camera = ref cameraComponent.Camera; - // Camera target follows player - //camera.Target.X = position.X; - //camera.Target.Z = position.Z; - - // // Camera rotation controls - // if (IsKeyDown(KEY_A)) camera.rotation--; - // else if (IsKeyDown(KEY_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 - // camera.Zoom += Raylib.GetMouseWheelMove() * 0.05f; - // - // 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(KEY_R)) - // { - // camera.zoom = 1.0f; - // camera.rotation = 0.0f; - // } - } - } - - public void Dispose() - { - } -} \ No newline at end of file diff --git a/Systems/Rendering/CameraSystem.cs b/Systems/Rendering/CameraSystem.cs new file mode 100644 index 0000000..0bc4e93 --- /dev/null +++ b/Systems/Rendering/CameraSystem.cs @@ -0,0 +1,46 @@ +using BakeryGame.Components.Common; +using BakeryGame.Components.Player; +using Raylib_cs; +using Scellecs.Morpeh; + +namespace BakeryGame.Systems.Rendering; + +public class CameraSystem: ILateSystem +{ + private Filter _player; + float sliderValue = 50.0f; // Initial value of the slider + float minValue = -20.0f; // Minimum value of the slider + float maxValue = 20.0f; // Maximum value of the slider + Rectangle sliderRect = new Rectangle( 100, 50, 200, 20 ); // Rectangle defining the slider's position and size + bool isSliderActive = false; + private const float CameraSpeed = 0.1f; + private const float DistanceToPlayer = 10.0f; + + public void Dispose() + { + } + + public void OnAwake() + { + _player = World.Filter.With().Build(); + } + + public World World { get; set; } + + private float Lerp(float a, float b, float t) + { + return a + (b - a) * t; + } + + public void OnUpdate(float deltaTime) + { + var player = _player.First(); + ref var cameraComponent = ref player.GetComponent(); + var camera = cameraComponent.Camera.GetCamera3D(); + var playerPosition = player.GetComponent().Position; + // Update camera position to follow the player + cameraComponent.Camera.SetPosition(camera.Position with {X = Lerp(camera.Position.X, playerPosition.X, CameraSpeed), Z = Lerp(camera.Position.Z, playerPosition.Z + DistanceToPlayer, CameraSpeed)}); + // Update camera target to look at the player + cameraComponent.Camera.SetTarget(playerPosition); + } +} \ No newline at end of file diff --git a/Systems/Rendering/SliderSystem.cs b/Systems/Rendering/SliderSystem.cs new file mode 100644 index 0000000..0d24d1a --- /dev/null +++ b/Systems/Rendering/SliderSystem.cs @@ -0,0 +1,75 @@ +using BakeryGame.Components.Common; +using BakeryGame.Components.Player; +using Raylib_cs; +using Scellecs.Morpeh; + +namespace BakeryGame.Systems.Rendering; + +public class SliderSystem:ILateSystem +{ + private Filter _player; + float sliderValue = 50.0f; // Initial value of the slider + float minValue = -20.0f; // Minimum value of the slider + float maxValue = 20.0f; // Maximum value of the slider + Rectangle sliderRect = new Rectangle( 100, 50, 200, 20 ); // Rectangle defining the slider's position and size + bool isSliderActive = false; + private const float CameraSpeed = 0.1f; + private const float DistanceToPlayer = 10.0f; + + public void Dispose() + { + + } + + public void OnAwake() + { + _player = World.Filter.With().Build(); + } + + public World World { get; set; } + + + private float Lerp(float a, float b, float t) + { + return a + (b - a) * t; + } + + public void OnUpdate(float deltaTime) + { + + if (Raylib.IsMouseButtonPressed(MouseButton.Left)) { + // Check if mouse is clicked within the slider rectangle + if (Raylib.CheckCollisionPointRec(Raylib.GetMousePosition(), sliderRect)) { + isSliderActive = true; // Slider is being interacted with + } + } + + if (Raylib.IsMouseButtonReleased(MouseButton.Left)) { + isSliderActive = false; // Stop slider interaction + } + + // Update slider value when slider is active + if (isSliderActive) { + // // Calculate new slider value based on mouse position + float mousePosX = Raylib.GetMouseX(); + sliderValue = minValue + (mousePosX - sliderRect.X) / sliderRect.Width * (maxValue - minValue); + + // Clamp slider value within the valid range + if (sliderValue < minValue) sliderValue = minValue; + if (sliderValue > maxValue) sliderValue = maxValue; + + // ref var cameraComponent = ref player.GetComponent(); + // var camera = cameraComponent.Camera.GetCamera3D(); + // cameraComponent.Camera.SetTarget(camera.Target with {X = sliderValue}); + } + + // Draw slider background + Raylib.DrawRectangleRec(sliderRect, Color.LightGray); + + // Draw slider handle + float sliderHandlePosX = sliderRect.X + (sliderValue - minValue) / (maxValue - minValue) * sliderRect.Width; + var sliderHandleRect = new Rectangle( sliderHandlePosX - 5, sliderRect.Y - 5, 10, sliderRect.Height + 10 ); + Raylib.DrawRectangleRec(sliderHandleRect, Color.Blue); + isSliderActive = false; + } +} \ No newline at end of file