added crude camera movement controls
This commit is contained in:
@@ -8,7 +8,7 @@ namespace EngineSharp.Core.ECS;
|
||||
// https://github.com/friflo/Friflo.Engine.ECS/tree/main
|
||||
// OR: https://github.com/genaray/Arch
|
||||
|
||||
public class Entity
|
||||
public abstract class Entity
|
||||
{
|
||||
// TODO: Maybe use a Hashset and implement a comparer or something? Should be faster than the current .OfType<>().Any() implementation
|
||||
private readonly List<LogicComponent> _logicComponents = [];
|
||||
@@ -49,6 +49,11 @@ public class Entity
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets called once the entity gets loaded but before the components get executed for the first time.
|
||||
/// </summary>
|
||||
public abstract void OnLoad();
|
||||
|
||||
internal void InitialiseComponents()
|
||||
{
|
||||
foreach (var components in _logicComponents)
|
||||
|
||||
@@ -25,6 +25,7 @@ public class Scene
|
||||
{
|
||||
foreach (var entity in _entities.Values)
|
||||
{
|
||||
entity.OnLoad();
|
||||
entity.InitialiseComponents();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ public interface IEngine
|
||||
void Stop();
|
||||
|
||||
Shader GetShader(string vertexShaderPath);
|
||||
|
||||
InputService GetInputService();
|
||||
}
|
||||
|
||||
public enum GraphicsAPI
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Silk.NET.Input;
|
||||
using Silk.NET.Maths;
|
||||
|
||||
namespace EngineSharp.Core;
|
||||
|
||||
public class Input
|
||||
public class InputService
|
||||
{
|
||||
private readonly IInputContext _context;
|
||||
private readonly IDictionary<IKeyboard, HashSet<Key>> _pressedKeys = new Dictionary<IKeyboard, HashSet<Key>>();
|
||||
private readonly Dictionary<IKeyboard, HashSet<Key>> _pressedKeys = new();
|
||||
private Vector2D<float> _currentMouseDelta = Vector2D<float>.Zero;
|
||||
private Vector2? _currentMousePosition;
|
||||
|
||||
// TODO: Add mouse
|
||||
internal Input(IInputContext inputContext)
|
||||
internal InputService(IInputContext inputContext)
|
||||
{
|
||||
_context = inputContext;
|
||||
foreach (var keyboard in _context.Keyboards)
|
||||
@@ -18,6 +22,12 @@ public class Input
|
||||
keyboard.KeyDown += OnKeyDown;
|
||||
keyboard.KeyUp += OnKeyUp;
|
||||
}
|
||||
|
||||
foreach (var mouse in _context.Mice)
|
||||
{
|
||||
mouse.MouseMove += OnMouseMove;
|
||||
}
|
||||
|
||||
_context.ConnectionChanged += OnDeviceConnectionChanges;
|
||||
}
|
||||
|
||||
@@ -37,6 +47,15 @@ public class Input
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public Vector2D<float> GetMouseDelta()
|
||||
{
|
||||
var tempDelta = _currentMouseDelta;
|
||||
_currentMouseDelta = Vector2D<float>.Zero;
|
||||
return tempDelta;
|
||||
}
|
||||
|
||||
public Vector2D<float> GetMouseDelta(IMouse mouse) => GetMouseDelta();
|
||||
|
||||
private void OnKeyDown(IKeyboard keyboard, Key key, int keyCode)
|
||||
{
|
||||
@@ -58,6 +77,25 @@ public class Input
|
||||
}
|
||||
}
|
||||
|
||||
private void OnMouseMove(IMouse mouse, Vector2 newPosition)
|
||||
{
|
||||
if (!_currentMousePosition.HasValue)
|
||||
{
|
||||
_currentMousePosition = newPosition;
|
||||
return;
|
||||
}
|
||||
|
||||
var delta = _currentMousePosition.Value - newPosition;
|
||||
|
||||
// TODO: Very very bad solution! I need to find a good way to determine if the cursor has left the window and now has re-entered it
|
||||
if (Math.Abs(delta.X) <= 100 || Math.Abs(delta.Y) <= 100)
|
||||
{
|
||||
_currentMouseDelta = new Vector2D<float>(-delta.X, delta.Y);
|
||||
}
|
||||
|
||||
_currentMousePosition = newPosition;
|
||||
}
|
||||
|
||||
private void OnDeviceConnectionChanges(IInputDevice device, bool connected)
|
||||
{
|
||||
// TODO: See how to best utilise this. probably type pattern matching for IKeyboard etc.
|
||||
@@ -14,7 +14,7 @@ internal class OpenGLEngine : Engine
|
||||
private readonly IWindow _window;
|
||||
private readonly Dictionary<string, Scene> _scenes;
|
||||
private GL _gl = null!; // because between constructing the engine and OnLoad being called nothing should call these fields. Therefore, we do "!" to get rid of warnings
|
||||
private Input _input = null!;
|
||||
private InputService _inputService = null!;
|
||||
private PerspectiveCamera _camera = null!;
|
||||
private Scene? _currentScene;
|
||||
|
||||
@@ -47,6 +47,8 @@ internal class OpenGLEngine : Engine
|
||||
_window.Close();
|
||||
}
|
||||
|
||||
public override InputService GetInputService() => _inputService;
|
||||
|
||||
private void InitialiseShaders(IEnumerable<string> vertexShaderFiles)
|
||||
{
|
||||
foreach (var vertexShaderFile in vertexShaderFiles)
|
||||
@@ -91,7 +93,7 @@ internal class OpenGLEngine : Engine
|
||||
}
|
||||
|
||||
_gl = _window.CreateOpenGL();
|
||||
_input = new Input(_window.CreateInput());
|
||||
_inputService = new InputService(_window.CreateInput());
|
||||
|
||||
_gl.ClearColor(Color.Black);
|
||||
_gl.Enable(EnableCap.DepthTest);
|
||||
@@ -104,10 +106,13 @@ internal class OpenGLEngine : Engine
|
||||
|
||||
private void OnUpdate(double deltaTime)
|
||||
{
|
||||
if (_input.IsKeyPressed(Key.Escape))
|
||||
if (_inputService.IsKeyPressed(Key.Escape))
|
||||
{
|
||||
_window.Close();
|
||||
}
|
||||
|
||||
TemporaryMovementProcessing((float)deltaTime);
|
||||
TemporaryMouseProcessing();
|
||||
|
||||
_currentScene?.UpdateComponents(deltaTime);
|
||||
}
|
||||
@@ -129,4 +134,43 @@ internal class OpenGLEngine : Engine
|
||||
_camera.UpdateAspect(newDimensions);
|
||||
_gl.Viewport(newDimensions);
|
||||
}
|
||||
|
||||
private void TemporaryMovementProcessing(float deltaTime)
|
||||
{
|
||||
var moveDirection = Vector3D<float>.Zero;
|
||||
if (_inputService.IsKeyPressed(Key.A))
|
||||
{
|
||||
moveDirection.X -= 1;
|
||||
}
|
||||
else if (_inputService.IsKeyPressed(Key.D))
|
||||
{
|
||||
moveDirection.X += 1;
|
||||
}
|
||||
|
||||
if (_inputService.IsKeyPressed(Key.W))
|
||||
{
|
||||
moveDirection.Z += 1;
|
||||
}
|
||||
else if (_inputService.IsKeyPressed(Key.S))
|
||||
{
|
||||
moveDirection.Z -= 1;
|
||||
}
|
||||
|
||||
if (_inputService.IsKeyPressed(Key.Space))
|
||||
{
|
||||
moveDirection.Y += 1;
|
||||
}
|
||||
else if (_inputService.IsKeyPressed(Key.ControlLeft))
|
||||
{
|
||||
moveDirection.Y -= 1;
|
||||
}
|
||||
|
||||
_camera.ProcessMovement(deltaTime, moveDirection);
|
||||
}
|
||||
|
||||
private void TemporaryMouseProcessing()
|
||||
{
|
||||
var offset = _inputService.GetMouseDelta() * 0.1f;
|
||||
_camera.ProcessMouse(offset);
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ using Silk.NET.Maths;
|
||||
|
||||
namespace EngineSharp.Core;
|
||||
|
||||
// Camera should be an entity just like anything else (property IsDefault so the engine knows which camera to use for drawing to the screen)
|
||||
public class PerspectiveCamera
|
||||
{
|
||||
private readonly Vector3D<float> _worldUp;
|
||||
|
||||
@@ -19,6 +19,8 @@ internal abstract class Engine : IEngine
|
||||
|
||||
public abstract void Start();
|
||||
public abstract void Stop();
|
||||
public abstract InputService GetInputService();
|
||||
|
||||
internal abstract Scene CreateScene(string name);
|
||||
internal abstract void SetCurrentScene(string sceneName);
|
||||
|
||||
|
||||
33
src/EngineSharp/InputEntity.cs
Normal file
33
src/EngineSharp/InputEntity.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using EngineSharp.Core;
|
||||
using EngineSharp.Core.ECS;
|
||||
|
||||
namespace EngineSharp;
|
||||
|
||||
public class InputEntity : Entity
|
||||
{
|
||||
public InputService? InputService { get; set; }
|
||||
public PerspectiveCamera? Camera { get; set; }
|
||||
|
||||
private readonly InputManager _inputComponent;
|
||||
|
||||
public InputEntity()
|
||||
{
|
||||
_inputComponent = new InputManager();
|
||||
AddComponent(_inputComponent);
|
||||
}
|
||||
|
||||
public override void OnLoad()
|
||||
{
|
||||
if (InputService is null)
|
||||
{
|
||||
throw new InvalidOperationException($"Input service is not initialized. Cannot load Entity '{nameof(InputEntity)}'.");
|
||||
}
|
||||
if (Camera is null)
|
||||
{
|
||||
throw new InvalidOperationException($"Camera is not initialized. Cannot load Entity '{nameof(InputEntity)}'.");
|
||||
}
|
||||
|
||||
_inputComponent.InputService = InputService;
|
||||
_inputComponent.Camera = Camera;
|
||||
}
|
||||
}
|
||||
64
src/EngineSharp/InputManager.cs
Normal file
64
src/EngineSharp/InputManager.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
using EngineSharp.Core;
|
||||
using EngineSharp.Core.ECS;
|
||||
using Silk.NET.Input;
|
||||
using Silk.NET.Maths;
|
||||
|
||||
namespace EngineSharp;
|
||||
|
||||
public class InputManager : LogicComponent
|
||||
{
|
||||
public InputService? InputService { get; set; }
|
||||
|
||||
public PerspectiveCamera? Camera { get; set; }
|
||||
|
||||
public override void Initialise()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public override void OnUpdate(double deltaTime)
|
||||
{
|
||||
// TODO: update camera position and rotation (currently without transform component)
|
||||
if (InputService is null)
|
||||
{
|
||||
Console.WriteLine("ERROR: No InputService given. Skipping this iteration.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (Camera is null)
|
||||
{
|
||||
Console.WriteLine("ERROR: No Camera given. Skipping this iteration.");
|
||||
return;
|
||||
}
|
||||
|
||||
var moveDirection = Vector3D<float>.Zero;
|
||||
if (InputService.IsKeyPressed(Key.A))
|
||||
{
|
||||
moveDirection.X -= 1;
|
||||
}
|
||||
else if (InputService.IsKeyPressed(Key.D))
|
||||
{
|
||||
moveDirection.X += 1;
|
||||
}
|
||||
|
||||
if (InputService.IsKeyPressed(Key.W))
|
||||
{
|
||||
moveDirection.Z -= 1;
|
||||
}
|
||||
else if (InputService.IsKeyPressed(Key.S))
|
||||
{
|
||||
moveDirection.Z += 1;
|
||||
}
|
||||
|
||||
if (InputService.IsKeyPressed(Key.Space))
|
||||
{
|
||||
moveDirection.Y += 1;
|
||||
}
|
||||
else if (InputService.IsKeyPressed(Key.ControlLeft))
|
||||
{
|
||||
moveDirection.Y -= 1;
|
||||
}
|
||||
|
||||
Camera.ProcessMovement((float)deltaTime, moveDirection);
|
||||
}
|
||||
}
|
||||
@@ -8,4 +8,9 @@ public class Planet : Core.ECS.Entity
|
||||
{
|
||||
AddComponent(new PlanetGenerator());
|
||||
}
|
||||
|
||||
public override void OnLoad()
|
||||
{
|
||||
// Nothing to do
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,11 @@ internal static class Program
|
||||
var mainScene = SceneManager.CreateDefaultScene("default");
|
||||
var planet = mainScene.CreateEntity<Planet>("planet");
|
||||
|
||||
// I probably need some sort of dependency injection. It is just terrible to work with an uinitialised engine etc.
|
||||
// var input = mainScene.CreateEntity<InputEntity>("inputEntity");
|
||||
// input.InputService = engine.GetInputService();
|
||||
// input.Camera = engine.GetCamera();
|
||||
|
||||
engine.Start();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user