feat: basic MonoGame integration implementations added

This commit is contained in:
2025-06-01 15:02:25 +03:00
parent fe8bde855d
commit 2caa042317
14 changed files with 607 additions and 0 deletions

View File

@@ -0,0 +1,29 @@
using System.Collections.Generic;
using Syntriax.Engine.Core;
namespace Syntriax.Engine.Integration.MonoGame;
public class DrawableShapeBehaviour : Behaviour2D, IDrawableTriangle, IPreDraw
{
private readonly Shape2D shape = new([]);
private readonly List<Triangle> worldTriangles = [];
private readonly Shape2D worldShape = new([]);
protected ColorRGB color = new(255, 255, 255);
public void PreDraw() => UpdateWorldShape();
public void Draw(ITriangleBatch triangleBatch)
{
worldShape.ToTrianglesConvex(worldTriangles);
foreach (Triangle triangle in worldTriangles)
triangleBatch.Draw(new(triangle.C, triangle.B, triangle.A), color);
}
protected void UpdateWorldShape() => shape.Transform(Transform, worldShape);
public DrawableShapeBehaviour() => shape = Shape2D.Triangle;
public DrawableShapeBehaviour(Shape2D shape) => this.shape = shape;
public DrawableShapeBehaviour(Shape2D shape, ColorRGB color) { this.shape = shape; this.color = color; }
}

View File

@@ -0,0 +1,110 @@
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework.Input;
using Syntriax.Engine.Core;
using Syntriax.Engine.Systems.Input;
namespace Syntriax.Engine.Integration.MonoGame;
public class KeyboardInputsBehaviour : Behaviour, IButtonInputs<Keys>, IUpdate
{
public Event<IButtonInputs<Keys>, IButtonInputs<Keys>.ButtonCallbackArguments> OnAnyButtonPressed { get; } = new();
public Event<IButtonInputs<Keys>, IButtonInputs<Keys>.ButtonCallbackArguments> OnAnyButtonReleased { get; } = new();
private readonly Dictionary<Keys, Event<IButtonInputs<Keys>, IButtonInputs<Keys>.ButtonCallbackArguments>> OnPressed = new(256);
private readonly Dictionary<Keys, Event<IButtonInputs<Keys>, IButtonInputs<Keys>.ButtonCallbackArguments>> OnReleased = new(256);
private int cachePressedCurrentlyCount = 0;
private readonly Keys[] cachePressedCurrently = new Keys[256];
private int cachePressedPreviouslyCount = 0;
private readonly Keys[] cachePressedPreviously = new Keys[256];
public void RegisterOnPress(Keys key, Event<IButtonInputs<Keys>, IButtonInputs<Keys>.ButtonCallbackArguments>.EventHandler callback)
{
if (!OnPressed.TryGetValue(key, out Event<IButtonInputs<Keys>, IButtonInputs<Keys>.ButtonCallbackArguments>? delegateCallback))
{
delegateCallback = new();
OnPressed.Add(key, delegateCallback);
}
delegateCallback.AddListener(callback);
}
public void UnregisterOnPress(Keys key, Event<IButtonInputs<Keys>, IButtonInputs<Keys>.ButtonCallbackArguments>.EventHandler callback)
{
if (OnPressed.TryGetValue(key, out Event<IButtonInputs<Keys>, IButtonInputs<Keys>.ButtonCallbackArguments>? delegateCallback))
delegateCallback.RemoveListener(callback);
}
public void RegisterOnRelease(Keys key, Event<IButtonInputs<Keys>, IButtonInputs<Keys>.ButtonCallbackArguments>.EventHandler callback)
{
if (!OnReleased.TryGetValue(key, out Event<IButtonInputs<Keys>, IButtonInputs<Keys>.ButtonCallbackArguments>? delegateCallback))
{
delegateCallback = new();
OnReleased.Add(key, delegateCallback);
}
delegateCallback.AddListener(callback);
}
public void UnregisterOnRelease(Keys key, Event<IButtonInputs<Keys>, IButtonInputs<Keys>.ButtonCallbackArguments>.EventHandler callback)
{
if (OnReleased.TryGetValue(key, out Event<IButtonInputs<Keys>, IButtonInputs<Keys>.ButtonCallbackArguments>? delegateCallback))
delegateCallback.RemoveListener(callback);
}
public void Update()
{
KeyboardState keyboardState = Keyboard.GetState();
keyboardState.GetPressedKeys(cachePressedCurrently);
cachePressedCurrentlyCount = keyboardState.GetPressedKeyCount();
for (int i = 0; i < cachePressedCurrentlyCount; i++)
{
Keys currentlyPressedKey = cachePressedCurrently[i];
if (WasPressed(currentlyPressedKey))
continue;
if (OnPressed.TryGetValue(currentlyPressedKey, out Event<IButtonInputs<Keys>, IButtonInputs<Keys>.ButtonCallbackArguments>? callback))
callback?.Invoke(this, new(currentlyPressedKey));
OnAnyButtonPressed?.Invoke(this, new(currentlyPressedKey));
}
for (int i = 0; i < cachePressedPreviouslyCount; i++)
{
Keys previouslyPressedKey = cachePressedPreviously[i];
if (IsPressed(previouslyPressedKey))
continue;
if (OnReleased.TryGetValue(previouslyPressedKey, out Event<IButtonInputs<Keys>, IButtonInputs<Keys>.ButtonCallbackArguments>? callback))
callback?.Invoke(this, new(previouslyPressedKey));
OnAnyButtonReleased?.Invoke(this, new(previouslyPressedKey));
}
Array.Copy(cachePressedCurrently, cachePressedPreviously, cachePressedCurrentlyCount);
cachePressedPreviouslyCount = cachePressedCurrentlyCount;
}
public bool IsPressed(Keys key)
{
for (int i = 0; i < cachePressedCurrentlyCount; i++)
if (cachePressedCurrently[i] == key)
return true;
return false;
}
private bool WasPressed(Keys key)
{
for (int i = 0; i < cachePressedPreviouslyCount; i++)
if (cachePressedPreviously[i] == key)
return true;
return false;
}
}

View File

@@ -0,0 +1,109 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Syntriax.Engine.Core;
namespace Syntriax.Engine.Integration.MonoGame;
public class MonoGameCamera2DBehaviour : BehaviourBase, ICamera2D, IFirstFrameUpdate, IPreDraw
{
public event MatrixTransformChangedArguments? OnMatrixTransformChanged = null;
public event ViewportChangedArguments? OnViewportChanged = null;
public event ZoomChangedArguments? OnZoomChanged = null;
private Matrix _matrixTransform = Matrix.Identity;
private Viewport _viewport = default;
private float _zoom = 1f;
public GraphicsDeviceManager Graphics { get; private set; } = null!;
public ITransform2D Transform { get; private set; } = null!;
public Matrix MatrixTransform
{
get => _matrixTransform;
set
{
if (_matrixTransform == value)
return;
_matrixTransform = value;
OnMatrixTransformChanged?.Invoke(this);
}
}
public Vector2D Position
{
get => Transform.Position;
set => Transform.Position = value;
}
public Viewport Viewport
{
get => _viewport;
set
{
if (_viewport.Equals(value))
return;
_viewport = value;
OnViewportChanged?.Invoke(this);
}
}
public float Zoom
{
get => _zoom;
set
{
float newValue = Syntriax.Engine.Core.Math.Max(0.1f, value);
if (_zoom == newValue)
return;
_zoom = newValue;
OnZoomChanged?.Invoke(this);
}
}
public float Rotation
{
get => Transform.Rotation;
set => Transform.Rotation = value;
}
// TODO This causes delay since OnPreDraw calls assuming this is called in in Update
public Vector2D ScreenToWorldPosition(Vector2D screenPosition)
{
Vector2D worldPosition = Vector2.Transform(screenPosition.ToVector2(), Matrix.Invert(MatrixTransform)).ToVector2D();
return worldPosition.Scale(EngineConverterExtensions.screenScale);
}
public Vector2D WorldToScreenPosition(Vector2D worldPosition)
{
Vector2D screenPosition = Vector2.Transform(worldPosition.ToVector2(), MatrixTransform).ToVector2D();
return screenPosition.Scale(EngineConverterExtensions.screenScale);
}
public void FirstActiveFrame()
{
Graphics = BehaviourController.UniverseObject.Universe.FindRequiredBehaviour<MonoGameWindowContainer>().Window.Graphics;
Viewport = Graphics.GraphicsDevice.Viewport;
}
public void PreDraw()
{
MatrixTransform =
Matrix.CreateTranslation(new Vector3(-Position.X, Position.Y, 0f)) *
Matrix.CreateRotationZ(Rotation * Syntriax.Engine.Core.Math.DegreeToRadian) *
Matrix.CreateScale(Transform.Scale.X.Max(Transform.Scale.Y)) *
Matrix.CreateScale(Zoom) *
Matrix.CreateTranslation(new Vector3(_viewport.Width * .5f, _viewport.Height * .5f, 0f));
}
protected sealed override void InitializeInternal() => Transform = BehaviourController.GetRequiredBehaviour<ITransform2D>();
protected sealed override void FinalizeInternal() => Transform = null!;
public delegate void MatrixTransformChangedArguments(MonoGameCamera2DBehaviour sender);
public delegate void ViewportChangedArguments(MonoGameCamera2DBehaviour sender);
public delegate void ZoomChangedArguments(MonoGameCamera2DBehaviour sender);
}

View File

@@ -0,0 +1,31 @@
using System.Collections.Generic;
using Microsoft.Xna.Framework.Graphics;
using Syntriax.Engine.Core;
namespace Syntriax.Engine.Integration.MonoGame;
public class SpriteBatcher : BehaviourBase, IFirstFrameUpdate, IDraw
{
private static Comparer<IBehaviour> SortByPriority() => Comparer<IBehaviour>.Create((x, y) => y.Priority.CompareTo(x.Priority));
private SpriteBatch spriteBatch = null!;
private MonoGameCamera2DBehaviour camera2D = null!;
private readonly ActiveBehaviourCollectorSorted<IDrawableSprite> drawableSprites = new() { SortBy = SortByPriority() };
public void FirstActiveFrame()
{
MonoGameWindowContainer windowContainer = BehaviourController.UniverseObject.Universe.FindRequiredBehaviour<MonoGameWindowContainer>();
camera2D = BehaviourController.UniverseObject.Universe.FindRequiredBehaviour<MonoGameCamera2DBehaviour>();
spriteBatch = new(windowContainer.Window.GraphicsDevice);
}
public void Draw()
{
spriteBatch.Begin(transformMatrix: camera2D.MatrixTransform);
for (int i = 0; i < drawableSprites.Count; i++)
drawableSprites[i].Draw(spriteBatch);
spriteBatch.End();
}
}

View File

@@ -0,0 +1,38 @@
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Syntriax.Engine.Core;
namespace Syntriax.Engine.Integration.MonoGame;
public class TriangleBatcher : BehaviourBase, ITriangleBatch, IFirstFrameUpdate, IDraw
{
private static Comparer<IBehaviour> SortByAscendingPriority() => Comparer<IBehaviour>.Create((x, y) => x.Priority.CompareTo(y.Priority));
private TriangleBatch triangleBatch = null!;
private MonoGameCamera2DBehaviour camera2D = null!;
private readonly ActiveBehaviourCollectorSorted<IDrawableTriangle> drawableShapes = new() { SortBy = SortByAscendingPriority() };
public void FirstActiveFrame()
{
MonoGameWindowContainer windowContainer = BehaviourController.UniverseObject.Universe.FindRequiredBehaviour<MonoGameWindowContainer>();
camera2D = BehaviourController.UniverseObject.Universe.FindRequiredBehaviour<MonoGameCamera2DBehaviour>();
triangleBatch = new(windowContainer.Window.GraphicsDevice);
drawableShapes.Unassign();
drawableShapes.Assign(BehaviourController.UniverseObject.Universe);
}
public void Draw()
{
triangleBatch.Begin(camera2D.MatrixTransform);
for (int i = 0; i < drawableShapes.Count; i++)
drawableShapes[i].Draw(triangleBatch);
triangleBatch.End();
}
public void Begin(Matrix? view = null, Matrix? projection = null) => triangleBatch.Begin(view, projection);
public void Draw(Triangle triangle, ColorRGBA colorRGBA) => triangleBatch.Draw(triangle, colorRGBA);
public void End() => triangleBatch.End();
}