All delegate events are refactored to use the Event<TSender> and Event<TSender, TArgument> for performance issues regarding delegate events creating garbage, also this gives us better control on event invocation since C# Delegates did also create unnecessary garbage during Delegate.DynamicInvoke
268 lines
7.8 KiB
C#
268 lines
7.8 KiB
C#
using Syntriax.Engine.Core.Serialization;
|
|
|
|
namespace Syntriax.Engine.Core;
|
|
|
|
[System.Diagnostics.DebuggerDisplay("Name: {UniverseObject.Name, nq} Position: {Position.ToString(), nq}, Scale: {Scale.ToString(), nq}, Rotation: {Rotation}")]
|
|
public class Transform2D : Behaviour, ITransform2D
|
|
{
|
|
public Event<ITransform2D, ITransform2D.PositionChangedArguments> OnPositionChanged { get; } = new();
|
|
public Event<ITransform2D, ITransform2D.ScaleChangedArguments> OnScaleChanged { get; } = new();
|
|
public Event<ITransform2D, ITransform2D.RotationChangedArguments> OnRotationChanged { get; } = new();
|
|
|
|
private Vector2D _position = Vector2D.Zero;
|
|
private Vector2D _scale = Vector2D.One;
|
|
private float _rotation = 0f;
|
|
|
|
[Serialize] private Vector2D _localPosition = Vector2D.Zero;
|
|
[Serialize] private Vector2D _localScale = Vector2D.One;
|
|
[Serialize] private float _localRotation = 0f;
|
|
|
|
private ITransform2D? parentTransform = null;
|
|
|
|
public Vector2D Position
|
|
{
|
|
get => _position;
|
|
set
|
|
{
|
|
if (value == _position)
|
|
return;
|
|
|
|
Vector2D previousPosition = _position;
|
|
_position = value;
|
|
|
|
UpdateLocalPosition();
|
|
OnPositionChanged?.Invoke(this, new(previousPosition));
|
|
}
|
|
}
|
|
|
|
public Vector2D Scale
|
|
{
|
|
get => _scale;
|
|
set
|
|
{
|
|
if (value == _scale)
|
|
return;
|
|
|
|
Vector2D previousScale = _scale;
|
|
_scale = value;
|
|
|
|
UpdateLocalScale();
|
|
OnScaleChanged?.Invoke(this, new(previousScale));
|
|
}
|
|
}
|
|
|
|
public float Rotation
|
|
{
|
|
get => _rotation;
|
|
set
|
|
{
|
|
if (value == _rotation)
|
|
return;
|
|
|
|
float previousRotation = _rotation;
|
|
_rotation = value;
|
|
|
|
UpdateLocalRotation();
|
|
OnRotationChanged?.Invoke(this, new(previousRotation));
|
|
}
|
|
}
|
|
|
|
public Vector2D LocalPosition
|
|
{
|
|
get => _localPosition;
|
|
set
|
|
{
|
|
if (value == _localPosition)
|
|
return;
|
|
|
|
Vector2D previousPosition = _position;
|
|
_localPosition = value;
|
|
|
|
UpdatePosition();
|
|
OnPositionChanged?.Invoke(this, new(previousPosition));
|
|
}
|
|
}
|
|
|
|
public Vector2D LocalScale
|
|
{
|
|
get => _localScale;
|
|
set
|
|
{
|
|
if (value == _localScale)
|
|
return;
|
|
|
|
Vector2D previousScale = _scale;
|
|
Vector2D previousPosition = _position;
|
|
_localScale = value;
|
|
|
|
UpdateScale();
|
|
UpdatePosition();
|
|
OnScaleChanged?.Invoke(this, new(previousScale));
|
|
OnPositionChanged?.Invoke(this, new(previousPosition));
|
|
}
|
|
}
|
|
|
|
public float LocalRotation
|
|
{
|
|
get => _localRotation;
|
|
set
|
|
{
|
|
if (value == _localRotation)
|
|
return;
|
|
|
|
float previousRotation = _rotation;
|
|
_localRotation = value;
|
|
|
|
UpdateRotation();
|
|
OnRotationChanged?.Invoke(this, new(previousRotation));
|
|
}
|
|
}
|
|
|
|
private void RecalculatePosition(ITransform2D _, ITransform2D.PositionChangedArguments args)
|
|
{
|
|
if (parentTransform is null)
|
|
return;
|
|
|
|
UpdatePosition();
|
|
|
|
OnPositionChanged?.Invoke(this, args);
|
|
}
|
|
|
|
private void RecalculateScale(ITransform2D _, ITransform2D.ScaleChangedArguments args)
|
|
{
|
|
if (parentTransform is null)
|
|
return;
|
|
|
|
Vector2D previousPosition = _position;
|
|
|
|
UpdateScale();
|
|
UpdatePosition();
|
|
|
|
OnScaleChanged?.Invoke(this, args);
|
|
OnPositionChanged?.Invoke(this, new(previousPosition));
|
|
}
|
|
|
|
private void RecalculateRotation(ITransform2D _, ITransform2D.RotationChangedArguments args)
|
|
{
|
|
if (parentTransform is null)
|
|
return;
|
|
|
|
Vector2D previousPosition = Position;
|
|
|
|
UpdateRotation();
|
|
UpdatePosition();
|
|
|
|
OnRotationChanged?.Invoke(this, args);
|
|
OnPositionChanged?.Invoke(this, new(previousPosition));
|
|
}
|
|
|
|
private void UpdateLocalPosition()
|
|
{
|
|
if (parentTransform is null)
|
|
_localPosition = Position;
|
|
else
|
|
_localPosition = parentTransform.Position.FromTo(Position).Scale(parentTransform.Scale);
|
|
}
|
|
|
|
private void UpdateLocalScale()
|
|
{
|
|
if (parentTransform is null)
|
|
_localScale = Scale;
|
|
else
|
|
_localScale = Scale.Scale(new(1f / parentTransform.Scale.X, 1f / parentTransform.Scale.Y));
|
|
}
|
|
|
|
private void UpdateLocalRotation()
|
|
{
|
|
if (parentTransform is null)
|
|
_localRotation = Rotation;
|
|
else
|
|
_localRotation = Rotation - parentTransform.Rotation;
|
|
}
|
|
|
|
private void UpdatePosition()
|
|
{
|
|
if (parentTransform is null)
|
|
_position = LocalPosition.Rotate(0f * Math.DegreeToRadian);
|
|
else
|
|
_position = parentTransform.Position + LocalPosition.Scale(new(parentTransform.Scale.X, parentTransform.Scale.Y)).Rotate(parentTransform.Rotation * Math.DegreeToRadian);
|
|
}
|
|
|
|
private void UpdateScale()
|
|
{
|
|
if (parentTransform is null)
|
|
_scale = LocalScale;
|
|
else
|
|
_scale = (parentTransform?.Scale ?? Vector2D.One).Scale(_localScale);
|
|
}
|
|
|
|
private void UpdateRotation()
|
|
{
|
|
if (parentTransform is null)
|
|
_rotation = LocalRotation;
|
|
else
|
|
_rotation = parentTransform.Rotation + LocalRotation;
|
|
}
|
|
|
|
protected override void InitializeInternal()
|
|
{
|
|
UpdateReferences(UniverseObject.Parent);
|
|
UniverseObject.OnParentChanged.AddListener(OnParentChanged);
|
|
}
|
|
|
|
protected override void FinalizeInternal()
|
|
{
|
|
UniverseObject.OnParentChanged.RemoveListener(OnParentChanged);
|
|
}
|
|
|
|
private void UpdateReferences(IUniverseObject? parent)
|
|
{
|
|
ITransform2D? previousParent = parentTransform;
|
|
if (previousParent is not null)
|
|
{
|
|
previousParent.OnPositionChanged.RemoveListener(RecalculatePosition);
|
|
previousParent.OnScaleChanged.RemoveListener(RecalculateScale);
|
|
previousParent.OnRotationChanged.RemoveListener(RecalculateRotation);
|
|
previousParent.BehaviourController.UniverseObject.OnParentChanged.RemoveListener(OnParentChanged);
|
|
previousParent.BehaviourController.OnBehaviourAdded.RemoveListener(LookForTransform2D);
|
|
}
|
|
|
|
parentTransform = parent?.BehaviourController.GetBehaviour<ITransform2D>();
|
|
|
|
if (parentTransform is not null)
|
|
{
|
|
parentTransform.OnPositionChanged.AddListener(RecalculatePosition);
|
|
parentTransform.OnScaleChanged.AddListener(RecalculateScale);
|
|
parentTransform.OnRotationChanged.AddListener(RecalculateRotation);
|
|
parentTransform.BehaviourController.UniverseObject.OnParentChanged.AddListener(OnParentChanged);
|
|
|
|
UpdatePosition();
|
|
UpdateScale();
|
|
UpdateRotation();
|
|
}
|
|
else if (UniverseObject.Parent is not null)
|
|
UniverseObject.Parent.BehaviourController.OnBehaviourAdded.AddListener(LookForTransform2D);
|
|
|
|
UpdateLocalPosition();
|
|
UpdateLocalScale();
|
|
UpdateLocalRotation();
|
|
|
|
OnPositionChanged?.Invoke(this, new(Position));
|
|
OnScaleChanged?.Invoke(this, new(Scale));
|
|
OnRotationChanged?.Invoke(this, new(Rotation));
|
|
}
|
|
|
|
private void LookForTransform2D(IBehaviourController sender, IBehaviourController.BehaviourAddedArguments args)
|
|
{
|
|
if (args.BehaviourAdded is not ITransform2D)
|
|
return;
|
|
|
|
UpdateReferences(UniverseObject.Parent);
|
|
}
|
|
|
|
private void OnParentChanged(IUniverseObject sender, IUniverseObject.ParentChangedArguments args)
|
|
{
|
|
UpdateReferences(args.CurrentParent);
|
|
}
|
|
}
|