feat: Transform Hierarchy System
This commit is contained in:
parent
b931abb735
commit
6e4c9b0ef8
|
@ -1,11 +1,12 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core.Abstract;
|
namespace Syntriax.Engine.Core.Abstract;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the transformation properties of an object such as position, scale, and rotation.
|
/// Represents the transformation properties of an object such as position, scale, and rotation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface ITransform
|
public interface ITransform : IEnumerable<ITransform>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event triggered when the <see cref="Position"/> of the <see cref="ITransform"/> changes.
|
/// Event triggered when the <see cref="Position"/> of the <see cref="ITransform"/> changes.
|
||||||
|
@ -23,17 +24,61 @@ public interface ITransform
|
||||||
Action<ITransform>? OnRotationChanged { get; set; }
|
Action<ITransform>? OnRotationChanged { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The position of the <see cref="ITransform"/> in 2D space.
|
/// Event triggered when the <see cref="Parent"/> of the <see cref="ITransform"/> changes. The second parameter is the old <see cref="ITransform"/>.
|
||||||
|
/// </summary>
|
||||||
|
Action<ITransform, ITransform?>? OnParentChanged { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event triggered when a new <see cref="ITransform"/> is added to the <see cref="Children"/>.
|
||||||
|
/// </summary>
|
||||||
|
Action<ITransform, ITransform>? OnChildrenAdded { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event triggered when an <see cref="ITransform"/> is removed from the <see cref="Children"/>.
|
||||||
|
/// </summary>
|
||||||
|
Action<ITransform, ITransform>? OnChildrenRemoved { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The world position of the <see cref="ITransform"/> in 2D space.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Vector2D Position { get; set; }
|
Vector2D Position { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The scale of the <see cref="ITransform"/>.
|
/// The world scale of the <see cref="ITransform"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Vector2D Scale { get; set; }
|
Vector2D Scale { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The rotation of the <see cref="ITransform"/> in degrees.
|
/// The world rotation of the <see cref="ITransform"/> in degrees.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
float Rotation { get; set; }
|
float Rotation { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The local position of the <see cref="ITransform"/> in 2D space.
|
||||||
|
/// </summary>
|
||||||
|
Vector2D LocalPosition { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The local scale of the <see cref="ITransform"/>.
|
||||||
|
/// </summary>
|
||||||
|
Vector2D LocalScale { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The local rotation of the <see cref="ITransform"/> in degrees.
|
||||||
|
/// </summary>
|
||||||
|
float LocalRotation { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The parent <see cref="ITransform"/> of the <see cref="ITransform"/>.
|
||||||
|
/// </summary>
|
||||||
|
ITransform? Parent { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="ITransform"/>s that have this <see cref="ITransform"/> as their <see cref="Parent"/>.
|
||||||
|
/// </summary>
|
||||||
|
IReadOnlyList<ITransform> Children { get; }
|
||||||
|
|
||||||
|
void SetParent(ITransform? transform);
|
||||||
|
void AddChild(ITransform transform);
|
||||||
|
void RemoveChild(ITransform transform);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,11 +4,16 @@ namespace Syntriax.Engine.Core;
|
||||||
|
|
||||||
public static class TransformExtensions
|
public static class TransformExtensions
|
||||||
{
|
{
|
||||||
public static ITransform SetTransform(this ITransform transform, Vector2D? position = null, float? rotation = null, Vector2D? scale = null)
|
public static ITransform SetTransform(this ITransform transform,
|
||||||
|
Vector2D? position = null, float? rotation = null, Vector2D? scale = null,
|
||||||
|
Vector2D? localPosition = null, float? localRotation = null, Vector2D? localScale = null)
|
||||||
{
|
{
|
||||||
if (position.HasValue) transform.Position = position.Value;
|
if (position.HasValue) transform.Position = position.Value;
|
||||||
if (rotation.HasValue) transform.Rotation = rotation.Value;
|
if (rotation.HasValue) transform.Rotation = rotation.Value;
|
||||||
if (scale.HasValue) transform.Scale = scale.Value;
|
if (scale.HasValue) transform.Scale = scale.Value;
|
||||||
|
if (localPosition.HasValue) transform.LocalPosition = localPosition.Value;
|
||||||
|
if (localRotation.HasValue) transform.LocalRotation = localRotation.Value;
|
||||||
|
if (localScale.HasValue) transform.LocalScale = localScale.Value;
|
||||||
return transform;
|
return transform;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
using Syntriax.Engine.Core.Abstract;
|
using Syntriax.Engine.Core.Abstract;
|
||||||
|
|
||||||
|
@ -11,10 +13,25 @@ public class Transform : ITransform
|
||||||
public Action<ITransform>? OnScaleChanged { get; set; } = null;
|
public Action<ITransform>? OnScaleChanged { get; set; } = null;
|
||||||
public Action<ITransform>? OnRotationChanged { get; set; } = null;
|
public Action<ITransform>? OnRotationChanged { get; set; } = null;
|
||||||
|
|
||||||
|
public Action<ITransform, ITransform?>? OnParentChanged { get; set; } = null;
|
||||||
|
public Action<ITransform, ITransform>? OnChildrenAdded { get; set; } = null;
|
||||||
|
public Action<ITransform, ITransform>? OnChildrenRemoved { get; set; } = null;
|
||||||
|
|
||||||
|
|
||||||
private Vector2D _position = Vector2D.Zero;
|
private Vector2D _position = Vector2D.Zero;
|
||||||
private Vector2D _scale = Vector2D.One;
|
private Vector2D _scale = Vector2D.One;
|
||||||
private float _rotation = 0f;
|
private float _rotation = 0f;
|
||||||
|
|
||||||
|
private Vector2D _localPosition = Vector2D.Zero;
|
||||||
|
private Vector2D _localScale = Vector2D.One;
|
||||||
|
private float _localRotation = 0f;
|
||||||
|
|
||||||
|
private readonly List<ITransform> _children = [];
|
||||||
|
|
||||||
|
public ITransform? Parent { get; private set; } = null;
|
||||||
|
|
||||||
|
public IReadOnlyList<ITransform> Children => _children;
|
||||||
|
|
||||||
public Vector2D Position
|
public Vector2D Position
|
||||||
{
|
{
|
||||||
get => _position;
|
get => _position;
|
||||||
|
@ -24,6 +41,7 @@ public class Transform : ITransform
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_position = value;
|
_position = value;
|
||||||
|
UpdateLocalPosition();
|
||||||
OnPositionChanged?.Invoke(this);
|
OnPositionChanged?.Invoke(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,6 +55,7 @@ public class Transform : ITransform
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_scale = value;
|
_scale = value;
|
||||||
|
UpdateLocalScale();
|
||||||
OnScaleChanged?.Invoke(this);
|
OnScaleChanged?.Invoke(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,7 +69,180 @@ public class Transform : ITransform
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_rotation = value;
|
_rotation = value;
|
||||||
|
UpdateLocalPosition();
|
||||||
OnRotationChanged?.Invoke(this);
|
OnRotationChanged?.Invoke(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Vector2D LocalPosition
|
||||||
|
{
|
||||||
|
get => _localPosition;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value == _localPosition)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_localPosition = value;
|
||||||
|
UpdatePosition();
|
||||||
|
OnPositionChanged?.Invoke(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector2D LocalScale
|
||||||
|
{
|
||||||
|
get => _localScale;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value == _localScale)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_localScale = value;
|
||||||
|
UpdateScale();
|
||||||
|
OnScaleChanged?.Invoke(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public float LocalRotation
|
||||||
|
{
|
||||||
|
get => _localRotation;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value == _localRotation)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_localRotation = value;
|
||||||
|
UpdateRotation();
|
||||||
|
OnRotationChanged?.Invoke(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetParent(ITransform? transform)
|
||||||
|
{
|
||||||
|
if (transform == this || Parent == transform)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ITransform? previousParent = Parent;
|
||||||
|
if (Parent is not null)
|
||||||
|
{
|
||||||
|
Parent.RemoveChild(this);
|
||||||
|
Parent.OnPositionChanged -= RecalculatePosition;
|
||||||
|
Parent.OnScaleChanged -= RecalculateScale;
|
||||||
|
Parent.OnRotationChanged -= RecalculateRotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
Parent = transform;
|
||||||
|
|
||||||
|
if (transform is not null)
|
||||||
|
{
|
||||||
|
transform.AddChild(this);
|
||||||
|
transform.OnPositionChanged += RecalculatePosition;
|
||||||
|
transform.OnScaleChanged += RecalculateScale;
|
||||||
|
transform.OnRotationChanged += RecalculateRotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateLocalPosition();
|
||||||
|
UpdateLocalScale();
|
||||||
|
UpdateLocalRotation();
|
||||||
|
|
||||||
|
OnParentChanged?.Invoke(this, previousParent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddChild(ITransform transform)
|
||||||
|
{
|
||||||
|
if (_children.Contains(transform))
|
||||||
|
return;
|
||||||
|
|
||||||
|
_children.Add(transform);
|
||||||
|
transform.SetParent(this);
|
||||||
|
OnChildrenAdded?.Invoke(this, transform);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveChild(ITransform transform)
|
||||||
|
{
|
||||||
|
if (!_children.Remove(transform))
|
||||||
|
return;
|
||||||
|
|
||||||
|
transform.SetParent(null);
|
||||||
|
OnChildrenRemoved?.Invoke(this, transform);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerator<ITransform> GetEnumerator() => _children.GetEnumerator();
|
||||||
|
IEnumerator IEnumerable.GetEnumerator() => _children.GetEnumerator();
|
||||||
|
|
||||||
|
private void RecalculatePosition(ITransform _)
|
||||||
|
{
|
||||||
|
if (Parent is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
UpdatePosition();
|
||||||
|
OnPositionChanged?.Invoke(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RecalculateScale(ITransform _)
|
||||||
|
{
|
||||||
|
if (Parent is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
UpdateScale();
|
||||||
|
OnScaleChanged?.Invoke(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RecalculateRotation(ITransform _)
|
||||||
|
{
|
||||||
|
if (Parent is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
UpdatePosition();
|
||||||
|
UpdateRotation();
|
||||||
|
OnPositionChanged?.Invoke(this);
|
||||||
|
OnRotationChanged?.Invoke(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateLocalPosition()
|
||||||
|
{
|
||||||
|
if (Parent is null)
|
||||||
|
_localPosition = Position;
|
||||||
|
else
|
||||||
|
_localPosition = Parent.Position.FromTo(Position).Scale(Parent.Scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateLocalScale()
|
||||||
|
{
|
||||||
|
if (Parent is null)
|
||||||
|
_localScale = Scale;
|
||||||
|
else
|
||||||
|
_localScale = Scale.Scale(new(1f / Parent.Scale.X, 1f / Parent.Scale.Y));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateLocalRotation()
|
||||||
|
{
|
||||||
|
if (Parent is null)
|
||||||
|
_localRotation = Rotation;
|
||||||
|
else
|
||||||
|
_localRotation = Rotation - Parent.Rotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdatePosition()
|
||||||
|
{
|
||||||
|
if (Parent is null)
|
||||||
|
_position = LocalPosition.Scale(Vector2D.One).Rotate(0f * Math.DegreeToRadian);
|
||||||
|
else
|
||||||
|
_position = Parent.Position + LocalPosition.Scale(new(1f / Parent.Scale.X, 1f / Parent.Scale.Y)).Rotate(Parent.Rotation * Math.DegreeToRadian);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateScale()
|
||||||
|
{
|
||||||
|
if (Parent is null)
|
||||||
|
_scale = LocalScale;
|
||||||
|
else
|
||||||
|
_scale = Vector2D.Scale(Parent.Scale, LocalScale);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateRotation()
|
||||||
|
{
|
||||||
|
if (Parent is null)
|
||||||
|
_rotation = LocalRotation;
|
||||||
|
else
|
||||||
|
_rotation = Parent.Rotation + LocalRotation;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue