feat: camera following player

This commit is contained in:
2024-04-18 22:54:09 +07:00
parent 36af656e0d
commit bcd0650aed
10 changed files with 216 additions and 97 deletions

View File

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

View File

@@ -28,19 +28,7 @@ public class BlockFactory
return block;
}
public IEnumerable<Entity> 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;
}

View File

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

20
Entities/RoomBuilder.cs Normal file
View File

@@ -0,0 +1,20 @@
using Scellecs.Morpeh;
namespace BakeryGame.Entities;
public class RoomBuilder
{
public static int RoomSize = 16;
public static IEnumerable<Entity> 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);
}
}
}
}
}

56
Models/CameraRef.cs Normal file
View File

@@ -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);
}
/// <summary>
/// Camera position
/// </summary>
public void SetPosition(Vector3 position)
{
_cam.Position = position;
}
/// <summary>
/// Camera target it looks-at
/// </summary>
public void SetTarget(Vector3 target)
{
_cam.Target = target;
}
/// <summary>
/// Camera up vector (rotation over its axis)
/// </summary>
public void SetUp(Vector3 up)
{
_cam.Up = up;
}
/// <summary>
/// Camera field-of-view apperture in Y (degrees) in perspective, used as near plane width in orthographic
/// </summary>
public void FovY(float fovy)
{
_cam.FovY = fovy;
}
/// <summary>
/// Camera type, defines projection type: CAMERA_PERSPECTIVE or CAMERA_ORTHOGRAPHIC
/// </summary>
public void SetProjection(CameraProjection projection)
{
_cam.Projection = projection;
}
public Camera3D GetCamera3D() => _cam;
}

View File

@@ -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();

View File

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

View File

@@ -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<CameraComponent>().Build();
}
public void OnUpdate(float deltaTime)
{
foreach (var entity in _filter)
{
var position = entity.GetComponent<PositionComponent>().Position;
ref var cameraComponent = ref entity.GetComponent<CameraComponent>();
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()
{
}
}

View File

@@ -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<PlayerComponent>().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<CameraComponent>();
var camera = cameraComponent.Camera.GetCamera3D();
var playerPosition = player.GetComponent<PositionComponent>().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);
}
}

View File

@@ -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<PlayerComponent>().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<CameraComponent>();
// 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;
}
}