Upload core-dump files

This commit is contained in:
Soneoy
2023-10-27 08:25:07 -07:00
parent 870a2b7b32
commit adc4d7ef56
30 changed files with 2206 additions and 46 deletions

View File

@@ -0,0 +1,98 @@
using System.Collections.Generic;
using AstroDX.Contexts.Gameplay.PlayerScope;
using AstroDX.Utilities;
using SimaiSharp.Structures;
using UnityEngine;
namespace AstroDX.Contexts.Gameplay.SlideGenerators
{
public sealed class CurveCcwGenerator : SlideGenerator
{
private const float CurveRadius = RenderManager.CenterRadius;
private const float RingRadius = RenderManager.PlayFieldRadius;
private readonly float _curveLength;
private readonly float _endForward;
private readonly Vector2 _endPoint;
private readonly float _startForward;
private readonly float _startLength;
private readonly Vector2 _startPoint;
private readonly Vector2 _tangentInPoint;
private readonly float _tangentInRotation;
private readonly Vector2 _tangentOutPoint;
private readonly float _totalLength;
public CurveCcwGenerator(IReadOnlyList<Location> vertices)
{
var startRotation = GetRotation(vertices[0]);
var endRotation = GetRotation(vertices[1]);
_tangentInRotation = startRotation +
Trigonometry.GetTangentAngleDelta(CurveRadius, RingRadius, false);
var tangentOutRotation = endRotation +
Trigonometry.GetTangentAngleDelta(CurveRadius, RingRadius, true);
_startPoint = GetPositionRadial(startRotation);
_tangentInPoint = GetPositionRadial(_tangentInRotation, CurveRadius);
_tangentOutPoint = GetPositionRadial(tangentOutRotation, CurveRadius);
_endPoint = GetPositionRadial(endRotation);
var startSegment = _tangentInPoint - _startPoint;
_startLength = startSegment.magnitude;
_startForward = Mathf.Atan2(startSegment.y, startSegment.x);
_curveLength = Trigonometry.GetAngleSpan(_tangentInRotation, tangentOutRotation,
false) * CurveRadius;
var endSegment = _endPoint - _tangentOutPoint;
var endLength = endSegment.magnitude;
_endForward = Mathf.Atan2(endSegment.y, endSegment.x);
_totalLength = _startLength + _curveLength + endLength;
}
public override float GetLength()
{
return _totalLength;
}
public override void GetPoint(float t, out Vector2 position, out float rotation)
{
var distanceFromStart = t * _totalLength;
if (distanceFromStart < _startLength)
{
position = Vector2.Lerp(_startPoint,
_tangentInPoint,
Mathf.InverseLerp(0,
_startLength,
distanceFromStart));
rotation = _startForward;
}
else if (distanceFromStart < _startLength + _curveLength)
{
var localT = Mathf.InverseLerp(_startLength, _startLength + _curveLength, distanceFromStart);
position = new Vector2(Mathf.Cos(_tangentInRotation + _curveLength / CurveRadius * localT) *
CurveRadius,
Mathf.Sin(_tangentInRotation + _curveLength / CurveRadius * localT) *
CurveRadius);
var forward = position.Rotate(Trigonometry.Tau / 4);
rotation = Mathf.Atan2(forward.y, forward.x);
}
else
{
position = Vector2.Lerp(_tangentOutPoint,
_endPoint,
Mathf.InverseLerp(_startLength + _curveLength,
_totalLength,
distanceFromStart));
rotation = _endForward;
}
}
}
}

View File

@@ -0,0 +1,98 @@
using System.Collections.Generic;
using AstroDX.Contexts.Gameplay.PlayerScope;
using AstroDX.Utilities;
using SimaiSharp.Structures;
using UnityEngine;
namespace AstroDX.Contexts.Gameplay.SlideGenerators
{
public sealed class CurveCwGenerator : SlideGenerator
{
private const float CurveRadius = RenderManager.CenterRadius;
private const float RingRadius = RenderManager.PlayFieldRadius;
private readonly float _curveLength;
private readonly float _endForward;
private readonly Vector2 _endPoint;
private readonly float _startForward;
private readonly float _startLength;
private readonly Vector2 _startPoint;
private readonly Vector2 _tangentInPoint;
private readonly float _tangentInRotation;
private readonly Vector2 _tangentOutPoint;
private readonly float _totalLength;
public CurveCwGenerator(IReadOnlyList<Location> vertices)
{
var startRotation = GetRotation(vertices[0]);
var endRotation = GetRotation(vertices[1]);
_tangentInRotation = startRotation +
Trigonometry.GetTangentAngleDelta(CurveRadius, RingRadius, true);
var tangentOutRotation = endRotation +
Trigonometry.GetTangentAngleDelta(CurveRadius, RingRadius, false);
_startPoint = GetPositionRadial(startRotation);
_tangentInPoint = GetPositionRadial(_tangentInRotation, CurveRadius);
_tangentOutPoint = GetPositionRadial(tangentOutRotation, CurveRadius);
_endPoint = GetPositionRadial(endRotation);
var startSegment = _tangentInPoint - _startPoint;
_startLength = startSegment.magnitude;
_startForward = Mathf.Atan2(startSegment.y, startSegment.x);
_curveLength = Trigonometry.GetAngleSpan(_tangentInRotation, tangentOutRotation,
true) * CurveRadius;
var endSegment = _endPoint - _tangentOutPoint;
var endLength = endSegment.magnitude;
_endForward = Mathf.Atan2(endSegment.y, endSegment.x);
_totalLength = _startLength + _curveLength + endLength;
}
public override float GetLength()
{
return _totalLength;
}
public override void GetPoint(float t, out Vector2 position, out float rotation)
{
var distanceFromStart = t * _totalLength;
if (distanceFromStart < _startLength)
{
position = Vector2.Lerp(_startPoint,
_tangentInPoint,
Mathf.InverseLerp(0,
_startLength,
distanceFromStart));
rotation = _startForward;
}
else if (distanceFromStart < _startLength + _curveLength)
{
var localT = Mathf.InverseLerp(_startLength, _startLength + _curveLength, distanceFromStart);
position = new Vector2(Mathf.Cos(_tangentInRotation - _curveLength / CurveRadius * localT) *
CurveRadius,
Mathf.Sin(_tangentInRotation - _curveLength / CurveRadius * localT) *
CurveRadius);
var forward = position.Rotate(-Trigonometry.Tau / 4);
rotation = Mathf.Atan2(forward.y, forward.x);
}
else
{
position = Vector2.Lerp(_tangentOutPoint,
_endPoint,
Mathf.InverseLerp(_startLength + _curveLength,
_totalLength,
distanceFromStart));
rotation = _endForward;
}
}
}
}

View File

@@ -0,0 +1,116 @@
using System.Collections.Generic;
using AstroDX.Contexts.Gameplay.PlayerScope;
using AstroDX.Utilities;
using SimaiSharp.Structures;
using UnityEngine;
namespace AstroDX.Contexts.Gameplay.SlideGenerators
{
public sealed class EdgeCurveCcwGenerator : SlideGenerator
{
private const float CurveRadius = RenderManager.CenterRadius * 1.2f;
private const float CenterAngularOffset = Trigonometry.Tau / 4 - Trigonometry.Tau / 16;
private const float CenterRadialOffset = RenderManager.PlayFieldRadius * 0.4662f;
private readonly Vector2 _centerPosition;
private readonly float _curveLength;
private readonly Vector2 _endPoint;
private readonly float _startRotation;
private readonly float _endRotation;
private readonly float _startLength;
private readonly Vector2 _startPoint;
private readonly Vector2 _tangentInPoint;
private readonly float _tangentInRotation;
private readonly Vector2 _tangentOutPoint;
private readonly float _totalLength;
public EdgeCurveCcwGenerator(IReadOnlyList<Location> vertices)
{
var startRotation = GetRotation(vertices[0]);
var endRotation = GetRotation(vertices[1]);
var centerAngle = startRotation - CenterAngularOffset;
_centerPosition = new Vector2(CenterRadialOffset * Mathf.Cos(centerAngle),
CenterRadialOffset * Mathf.Sin(centerAngle));
_startPoint = GetPositionRadial(startRotation);
var relativeStartRotation = Trigonometry.ToPolarAngle(_startPoint, _centerPosition);
var magnitude = (_centerPosition - _startPoint).magnitude;
var startDelta = Trigonometry.GetTangentAngleDelta(CurveRadius, magnitude, false);
_tangentInRotation = relativeStartRotation + startDelta;
_tangentInPoint = GetPositionRadial(_tangentInRotation, CurveRadius) +
_centerPosition;
_endPoint = GetPositionRadial(endRotation);
var relativeEndRotation = Trigonometry.ToPolarAngle(_endPoint, _centerPosition);
var endMagnitude = (_endPoint - _centerPosition).magnitude;
var endDelta = Trigonometry.GetTangentAngleDelta(CurveRadius, endMagnitude, true);
var tangentOutRotation = relativeEndRotation + endDelta;
_tangentOutPoint = GetPositionRadial(tangentOutRotation, CurveRadius) +
_centerPosition;
var startSegment = _tangentInPoint - _startPoint;
_startLength = startSegment.magnitude;
_startRotation = Mathf.Atan2(startSegment.y, startSegment.x);
_curveLength = Trigonometry.GetAngleSpan(_tangentInRotation, tangentOutRotation,
false, Trigonometry.Tau / 4f) * CurveRadius;
var endSegment = _endPoint - _tangentOutPoint;
var endLength = endSegment.magnitude;
_endRotation = Mathf.Atan2(endSegment.y, endSegment.x);
_totalLength = _startLength + _curveLength + endLength;
}
public override float GetLength()
{
return _totalLength;
}
public override void GetPoint(float t, out Vector2 position, out float rotation)
{
var distanceFromStart = t * _totalLength;
if (distanceFromStart < _startLength)
{
position = Vector2.Lerp(_startPoint,
_tangentInPoint,
Mathf.InverseLerp(0,
_startLength,
distanceFromStart));
rotation = _startRotation;
}
else if (distanceFromStart < _startLength + _curveLength)
{
var localT = Mathf.InverseLerp(_startLength, _startLength + _curveLength, distanceFromStart);
position = new Vector2(Mathf.Cos(_tangentInRotation + _curveLength / CurveRadius * localT) *
CurveRadius,
Mathf.Sin(_tangentInRotation + _curveLength / CurveRadius * localT) *
CurveRadius);
var forward = position.Rotate(Trigonometry.Tau / 4);
rotation = Mathf.Atan2(forward.y, forward.x);
position += _centerPosition;
}
else
{
position = Vector2.Lerp(_tangentOutPoint,
_endPoint,
Mathf.InverseLerp(_startLength + _curveLength,
_totalLength,
distanceFromStart));
rotation = _endRotation;
}
}
}
}

View File

@@ -0,0 +1,116 @@
using System.Collections.Generic;
using AstroDX.Contexts.Gameplay.PlayerScope;
using AstroDX.Utilities;
using SimaiSharp.Structures;
using UnityEngine;
namespace AstroDX.Contexts.Gameplay.SlideGenerators
{
public sealed class EdgeCurveCwGenerator : SlideGenerator
{
private const float CurveRadius = RenderManager.CenterRadius * 1.2f;
private const float CenterAngularOffset = Trigonometry.Tau / 4 - Trigonometry.Tau / 16;
private const float CenterRadialOffset = RenderManager.PlayFieldRadius * 0.4662f;
private readonly Vector2 _centerPosition;
private readonly float _curveLength;
private readonly Vector2 _endPoint;
private readonly float _startRotation;
private readonly float _endRotation;
private readonly float _startLength;
private readonly Vector2 _startPoint;
private readonly Vector2 _tangentInPoint;
private readonly float _tangentInRotation;
private readonly Vector2 _tangentOutPoint;
private readonly float _totalLength;
public EdgeCurveCwGenerator(IReadOnlyList<Location> vertices)
{
var startRotation = GetRotation(vertices[0]);
var endRotation = GetRotation(vertices[1]);
var centerAngle = startRotation + CenterAngularOffset;
_centerPosition = new Vector2(CenterRadialOffset * Mathf.Cos(centerAngle),
CenterRadialOffset * Mathf.Sin(centerAngle));
_startPoint = GetPositionRadial(startRotation);
var relativeStartRotation = Trigonometry.ToPolarAngle(_startPoint, _centerPosition);
var magnitude = (_centerPosition - _startPoint).magnitude;
var startDelta = Trigonometry.GetTangentAngleDelta(CurveRadius, magnitude, true);
_tangentInRotation = relativeStartRotation + startDelta;
_tangentInPoint = GetPositionRadial(_tangentInRotation, CurveRadius) +
_centerPosition;
_endPoint = GetPositionRadial(endRotation);
var relativeEndRotation = Trigonometry.ToPolarAngle(_endPoint, _centerPosition);
var endMagnitude = (_endPoint - _centerPosition).magnitude;
var endDelta = Trigonometry.GetTangentAngleDelta(CurveRadius, endMagnitude, false);
var tangentOutRotation = relativeEndRotation + endDelta;
_tangentOutPoint = GetPositionRadial(tangentOutRotation, CurveRadius) +
_centerPosition;
var startSegment = _tangentInPoint - _startPoint;
_startLength = startSegment.magnitude;
_startRotation = Mathf.Atan2(startSegment.y, startSegment.x);
_curveLength = Trigonometry.GetAngleSpan(_tangentInRotation, tangentOutRotation,
true, Trigonometry.Tau / 4f) * CurveRadius;
var endSegment = _endPoint - _tangentOutPoint;
var endLength = endSegment.magnitude;
_endRotation = Mathf.Atan2(endSegment.y, endSegment.x);
_totalLength = _startLength + _curveLength + endLength;
}
public override float GetLength()
{
return _totalLength;
}
public override void GetPoint(float t, out Vector2 position, out float rotation)
{
var distanceFromStart = t * _totalLength;
if (distanceFromStart < _startLength)
{
position = Vector2.Lerp(_startPoint,
_tangentInPoint,
Mathf.InverseLerp(0,
_startLength,
distanceFromStart));
rotation = _startRotation;
}
else if (distanceFromStart < _startLength + _curveLength)
{
var localT = Mathf.InverseLerp(_startLength, _startLength + _curveLength, distanceFromStart);
position = new Vector2(Mathf.Cos(_tangentInRotation - _curveLength / CurveRadius * localT) *
CurveRadius,
Mathf.Sin(_tangentInRotation - _curveLength / CurveRadius * localT) *
CurveRadius);
var forward = position.Rotate(-Trigonometry.Tau / 4);
rotation = Mathf.Atan2(forward.y, forward.x);
position += _centerPosition;
}
else
{
position = Vector2.Lerp(_tangentOutPoint,
_endPoint,
Mathf.InverseLerp(_startLength + _curveLength,
_totalLength,
distanceFromStart));
rotation = _endRotation;
}
}
}
}

View File

@@ -0,0 +1,63 @@
using System.Collections.Generic;
using SimaiSharp.Structures;
using UnityEngine;
namespace AstroDX.Contexts.Gameplay.SlideGenerators
{
public sealed class EdgeFoldGenerator : SlideGenerator
{
private readonly Vector2 _endPoint;
private readonly Vector2 _midPoint;
private readonly Vector2 _startPoint;
private readonly float _startRotation;
private readonly float _endRotation;
private readonly float _startSegmentLength;
private readonly float _totalLength;
public EdgeFoldGenerator(IReadOnlyList<Location> vertices)
{
_startPoint = GetPosition(vertices[0]);
_midPoint = GetPosition(vertices[1]);
_endPoint = GetPosition(vertices[2]);
var startSegment = _midPoint - _startPoint;
_startSegmentLength = startSegment.magnitude;
_startRotation = Mathf.Atan2(startSegment.y, startSegment.x);
var endSegment = _endPoint - _midPoint;
var endSegmentSpan = endSegment.magnitude;
_endRotation = Mathf.Atan2(endSegment.y, endSegment.x);
_totalLength = _startSegmentLength + endSegmentSpan;
}
public override float GetLength()
{
return _totalLength;
}
public override void GetPoint(float t, out Vector2 position, out float rotation)
{
var distanceFromStart = t * _totalLength;
if (distanceFromStart < _startSegmentLength)
{
position = Vector2.Lerp(_startPoint, _midPoint,
Mathf.InverseLerp(0,
_startSegmentLength / _totalLength,
t));
rotation = _startRotation;
}
else
{
position = Vector2.Lerp(_midPoint, _endPoint,
Mathf.InverseLerp(_startSegmentLength / _totalLength,
1,
t));
rotation = _endRotation;
}
}
}
}

View File

@@ -0,0 +1,327 @@
using System.Collections.Generic;
using System.Linq;
using AstroDX.Contexts.Gameplay.Behaviours.Slide.SlideMarkers;
using AstroDX.Contexts.Gameplay.Interactions;
using AstroDX.Contexts.Gameplay.PlayerScope;
using AstroDX.Contexts.Gameplay.SlideGenerators;
using AstroDX.Globals;
using Medicine;
using SimaiSharp.Structures;
using UnityEngine;
namespace AstroDX.Contexts.Gameplay.Behaviours.Slide.Handlers
{
public class FanSlideSegmentHandler : SlideSegmentHandler
{
private const int Margin = 2;
private const int ArrowCount = 11;
private readonly List<FanSlideArrow> _arrows = new();
private readonly Vector2[] _endPositions;
private readonly List<List<TouchInteractable>> _interactableGroups = new();
private readonly Vector2 _startPosition;
private readonly Vector2[] _ups;
private int _interactionReceivedState;
private SlideStarBehaviour[] _stars;
public FanSlideSegmentHandler(SlideBehaviour slideBehaviour,
SlideSegment segment,
Location startLocation) :
base(slideBehaviour)
{
var vertices = segment.vertices.ToList();
vertices.Insert(0, startLocation);
_startPosition = SlideGenerator.GetPosition(vertices[0]);
var ccwEndLocation = startLocation;
ccwEndLocation.index += 3;
var centerEndLocation = startLocation;
centerEndLocation.index += 4;
var cwEndLocation = startLocation;
cwEndLocation.index += 5;
_endPositions = new[]
{
SlideGenerator.GetPosition(ccwEndLocation),
SlideGenerator.GetPosition(centerEndLocation),
SlideGenerator.GetPosition(cwEndLocation)
};
_ups = new[]
{
_endPositions[0] - _startPosition,
_endPositions[1] - _startPosition,
_endPositions[2] - _startPosition
};
_stars = new[]
{
SlideManager.slideStars.Get(),
SlideManager.slideStars.Get(),
SlideManager.slideStars.Get()
};
foreach (var star in _stars) star.Initialize(ParentPath, ParentNote.parentCollection);
GenerateSensors();
GenerateArrows();
}
[Inject.Single]
private SlideManager SlideManager { get; }
[Inject.Single]
private TouchManager TouchManager { get; }
public override float GetLength()
{
return RenderManager.PlayFieldRadius * 2;
}
public override void OnUpdate(float segmentT)
{
if (IsJudgementTarget)
CheckInteraction();
UpdateSlideStarPosition(segmentT);
UpdateArrowOpacity();
}
private void UpdateArrowOpacity()
{
var timeSinceNoteStart = TimeSinceSlideStart + ParentPath.delay;
var alpha = 0.8f;
if (timeSinceNoteStart < 0)
{
var colorPoint = Mathf.InverseLerp(-Persistent.Settings.Gameplay.slideFadeInDuration,
0,
(float)timeSinceNoteStart);
alpha = Mathf.Lerp(0, 0.8f, colorPoint);
}
foreach (var arrow in _arrows.Select(a => a.spriteRenderer))
{
var color = arrow.color;
color.a = alpha;
arrow.color = color;
}
}
private void CheckInteraction()
{
if (_interactionReceivedState == 0)
{
if (!_interactableGroups[0][0].GetSlideJudgement())
return;
_interactionReceivedState = 1;
foreach (var slideArrow in _arrows
.Where(a => a.interactionGroupIndex == 0)
.ToList())
{
_arrows.Remove(slideArrow);
slideArrow.ExitSequence();
}
}
if (_interactionReceivedState >= 3)
{
Cleared = true;
return;
}
for (var groupIndex = _interactionReceivedState; groupIndex < 3; groupIndex++)
{
foreach (var interactable in
_interactableGroups[groupIndex]
.Where(interactable => interactable.GetSlideJudgement())
.ToList())
_interactableGroups[groupIndex].Remove(interactable);
if (_interactableGroups[groupIndex].Count > 0 ||
_interactionReceivedState < groupIndex)
continue;
foreach (var slideArrow in _arrows
.Where(a => a.interactionGroupIndex == _interactionReceivedState)
.ToList())
{
_arrows.Remove(slideArrow);
slideArrow.ExitSequence();
}
_interactionReceivedState++;
}
}
public override float GetRemainingLength()
{
return (3 - _interactionReceivedState) / 3f * GetLength();
}
public override void OnDestroy()
{
base.OnDestroy();
foreach (var slideArrow in _arrows)
SlideManager.fanSlideArrows.Release(slideArrow);
foreach (var star in _stars)
SlideManager.slideStars.Release(star);
}
private void UpdateSlideStarPosition(float segmentT)
{
if (disposed || _stars is null)
return;
for (var index = 0; index < _stars.Length; index++)
{
var star = _stars[index];
if (star == null)
continue;
var transform = star.transform;
if (segmentT >= 1 && indexInSlide != ParentPath.segments.Count - 1)
{
transform.localScale = Vector3.one * Persistent.Settings.Gameplay.noteScale;
star.Appearance.Color = new Color(1, 1, 1, 0);
continue;
}
var position = Vector3.Lerp(_startPosition,
_endPositions[index],
segmentT);
transform.localPosition = position;
transform.up = _ups[index];
if (TimeSinceSlideStart < 0 &&
indexInSlide == 0 &&
ParentNote.slideMorph != SlideMorph.SuddenIn)
{
var interpolation =
Mathf.InverseLerp(-ParentPath.delay, 0,
(float)TimeSinceSlideStart);
transform.localScale = new Vector3(interpolation, interpolation) *
Persistent.Settings.Gameplay.noteScale;
star.Appearance.Color = new Color(1, 1, 1, interpolation);
return;
}
if (segmentT < 0)
return;
transform.localScale = Vector3.one * Persistent.Settings.Gameplay.noteScale;
star.Appearance.Color = Color.white;
}
}
public override void GetJudgementVector(out Vector2 position, out float rotation)
{
position = _endPositions[1];
rotation = Mathf.Atan2(_ups[1].y, _ups[1].x);
}
private void GenerateSensors()
{
_interactableGroups.AddRange(new List<TouchInteractable>[]
{
new()
{
TouchManager.GetCollider(new Location
{
group = NoteGroup.ASensor,
index = ParentNote.location.index
})
},
new()
{
TouchManager.GetCollider(new Location
{
group = NoteGroup.BSensor,
index = (ParentNote.location.index + 2) %
8
}),
TouchManager.GetCollider(new Location
{
group = NoteGroup.BSensor,
index = (ParentNote.location.index + 6) %
8
})
},
new()
{
TouchManager.GetCollider(new Location
{
group = NoteGroup.ASensor,
index = (ParentNote.location.index + 5) %
8
}),
TouchManager.GetCollider(new Location
{
group = NoteGroup.ASensor,
index = (ParentNote.location.index + 3) %
8
}),
TouchManager.GetCollider(new Location
{
group = NoteGroup.ASensor,
index = (ParentNote.location.index + 4) %
8
})
}
}
);
}
private void GenerateArrows()
{
var halfMargin = Mathf.RoundToInt(Margin / 2f);
for (var i = 0;
i < ArrowCount;
i++)
{
var position = Vector3.Lerp(_startPosition,
_endPositions[1],
(float)(i + halfMargin) / (ArrowCount + Margin));
var arrow = SlideManager.fanSlideArrows.Get();
arrow.Initialize(position,
_ups[1],
ArrowCount,
i,
isBreak,
isEach);
AssignArrows(arrow, i);
}
}
private void AssignArrows(FanSlideArrow arrow, int index)
{
_arrows.Add(arrow);
if (index < ArrowCount / 3f)
arrow.interactionGroupIndex = 0;
else if (index < ArrowCount * 2 / 3f)
arrow.interactionGroupIndex = 1;
else
arrow.interactionGroupIndex = 2;
}
}
}

View File

@@ -0,0 +1,63 @@
using System.Collections.Generic;
using SimaiSharp.Structures;
using UnityEngine;
namespace AstroDX.Contexts.Gameplay.SlideGenerators
{
public sealed class FoldGenerator : SlideGenerator
{
private readonly Vector2 _endPoint;
private readonly float _endRotation;
private readonly Vector2 _midPoint;
private readonly Vector2 _startPoint;
private readonly float _startRotation;
private readonly float _startSegmentLength;
private readonly float _totalLength;
public FoldGenerator(IReadOnlyList<Location> vertices)
{
_startPoint = GetPosition(vertices[0]);
_endPoint = GetPosition(vertices[1]);
_midPoint = Vector2.zero;
var startSegment = _midPoint - _startPoint;
_startSegmentLength = startSegment.magnitude;
_startRotation = Mathf.Atan2(startSegment.y, startSegment.x);
var endSegment = _endPoint - _midPoint;
var endSegmentSpan = endSegment.magnitude;
_endRotation = Mathf.Atan2(endSegment.y, endSegment.x);
_totalLength = _startSegmentLength + endSegmentSpan;
}
public override float GetLength()
{
return _totalLength;
}
public override void GetPoint(float t, out Vector2 position, out float rotation)
{
var distanceFromStart = t * _totalLength;
if (distanceFromStart < _startSegmentLength)
{
position = Vector2.Lerp(_startPoint, _midPoint,
Mathf.InverseLerp(0,
_startSegmentLength / _totalLength,
t));
rotation = _startRotation;
}
else
{
position = Vector2.Lerp(_midPoint, _endPoint,
Mathf.InverseLerp(_startSegmentLength / _totalLength,
1,
t));
rotation = _endRotation;
}
}
}
}

View File

@@ -0,0 +1,69 @@
using Unity.Mathematics;
using UnityEngine;
namespace Timing
{
public class GameTime : MonoBehaviour
{
private double _lastCapturedDspTime;
public static bool Active { get; private set; }
public static double StartTime { get; private set; }
public static double TimeSinceClipStart { get; private set; }
private static NativeLinearRegression TimePrediction { get; set; }
private void Update()
{
if (!Active) return;
#region Smoothen DSP buffer time
if (AudioSettings.dspTime > _lastCapturedDspTime)
{
TimePrediction.Sample(new double2(Time.realtimeSinceStartupAsDouble, AudioSettings.dspTime));
_lastCapturedDspTime = AudioSettings.dspTime;
}
var smoothDspTime = TimePrediction.SampleCount < 2
? AudioSettings.dspTime
: TimePrediction.Predict(Time.realtimeSinceStartupAsDouble);
#endregion
TimeSinceClipStart = smoothDspTime - StartTime;
}
private void OnDestroy()
{
TimePrediction.Dispose();
}
/// <summary>
/// Tells the time manager to start counting time
/// </summary>
public static void StartFrom(double startTime)
{
StartTime = startTime;
TimeSinceClipStart = AudioSettings.dspTime - startTime;
Active = true;
TimePrediction ??= new NativeLinearRegression();
TimePrediction.Clear();
}
public static void Pause()
{
Active = false;
}
public static void UnPause(double timeSinceClipStart)
{
TimePrediction.Clear();
StartTime = AudioSettings.dspTime - timeSinceClipStart;
TimeSinceClipStart = timeSinceClipStart;
Active = true;
}
}
}

View File

@@ -0,0 +1,102 @@
using System;
using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
namespace Timing
{
public sealed class NativeLinearRegression : IDisposable
{
public int SampleCount { get; private set; }
public int MaxSampleCount { get; }
private NativeArray<double2> _samples;
public NativeLinearRegression(int maxSampleCount = 12)
{
MaxSampleCount = maxSampleCount;
_samples = new NativeArray<double2>(maxSampleCount, Allocator.Persistent);
}
public void Sample(double2 plot)
{
if (SampleCount < MaxSampleCount)
SampleCount++;
else
for (var i = 1; i < SampleCount; i++)
_samples[i - 1] = _samples[i];
_samples[SampleCount - 1] = plot;
}
public double Predict(in double x)
{
using var result = new NativeArray<double>(2, Allocator.TempJob);
var jobData = new LinearRegressionJob
{
samples = _samples.Slice(0, SampleCount),
yInterceptAndSlope = result
};
jobData.Schedule().Complete();
var yIntercept = result[0];
var slope = result[1];
return x * slope + yIntercept;
}
public void Clear()
{
SampleCount = 0;
}
public void Dispose()
{
_samples.Dispose();
}
}
[BurstCompile]
public struct LinearRegressionJob : IJob
{
[ReadOnly]
public NativeSlice<double2> samples;
[WriteOnly]
public NativeArray<double> yInterceptAndSlope;
public void Execute()
{
double sumOfX = 0;
double sumOfY = 0;
double sumOfXSq = 0;
double sumCoDeviates = 0;
var sampleCount = samples.Length;
for (var i = 0; i < sampleCount; i++)
{
var plot = samples[i];
sumCoDeviates += plot.x * plot.y;
sumOfX += plot.x;
sumOfY += plot.y;
sumOfXSq += plot.x * plot.x;
}
var ssX = sumOfXSq - sumOfX * sumOfX / sampleCount;
var sCo = sumCoDeviates - sumOfX * sumOfY / sampleCount;
var meanX = sumOfX / sampleCount;
var meanY = sumOfY / sampleCount;
// y-intercept
yInterceptAndSlope[0] = meanY - sCo / ssX * meanX;
// slope
yInterceptAndSlope[1] = sCo / ssX;
}
}
}

View File

@@ -0,0 +1,248 @@
using System.Collections.Generic;
using System.Linq;
using AstroDX.Contexts.Gameplay.Behaviours.Slide.SlideMarkers;
using AstroDX.Contexts.Gameplay.Interactions;
using AstroDX.Contexts.Gameplay.PlayerScope;
using AstroDX.Contexts.Gameplay.SlideGenerators;
using AstroDX.Globals;
using Medicine;
using SimaiSharp.Structures;
using UnityEngine;
namespace AstroDX.Contexts.Gameplay.Behaviours.Slide.Handlers
{
public class RegularSlideSegmentHandler : SlideSegmentHandler
{
private readonly SlideGenerator _generator;
private readonly
List<(TouchInteractable relevantSensor,
List<RegularSlideArrow> relevantArrows)> _interactionPath = new();
/// <summary>
/// Used in auto judgement
/// </summary>
private int _initialSensorCount;
/// <summary>
/// Used in auto judgement
/// </summary>
private int _lastCheckedClearIndex;
private readonly SlideStarBehaviour _slideStar;
public RegularSlideSegmentHandler(SlideBehaviour slideBehaviour,
SlideSegment segment,
Location startLocation) :
base(slideBehaviour)
{
var vertices = segment.vertices.ToList();
vertices.Insert(0, startLocation);
_generator = SlideManager.GetGenerator(segment.slideType, vertices);
_slideStar = SlideManager.slideStars.Get();
_slideStar.Initialize(ParentPath, ParentNote.parentCollection);
GenerateArrows();
}
[Inject.Single]
private SlideManager SlideManager { get; }
public override float GetLength()
{
return _generator.GetLength();
}
public override void OnUpdate(float segmentT)
{
if (IsJudgementTarget)
CheckInteraction(segmentT);
UpdateSlideStarPosition(segmentT);
UpdateArrowOpacity();
}
private float _arrowOpacity;
private void UpdateArrowOpacity()
{
var timeSinceNoteStart = TimeSinceSlideStart + ParentPath.delay;
if (timeSinceNoteStart < 0)
{
_arrowOpacity = Mathf.InverseLerp(-Persistent.Settings.Gameplay.slideFadeInDuration,
0,
(float)timeSinceNoteStart);
_arrowOpacity = Mathf.Lerp(0, 0.8f, _arrowOpacity);
}
else
{
if (_arrowOpacity >= 1)
return;
_arrowOpacity = 1;
}
foreach (var (_, relevantArrows) in _interactionPath)
{
foreach (var arrow in relevantArrows)
{
arrow.SetAlpha(_arrowOpacity);
}
}
}
private void CheckInteraction(float segmentT)
{
var clearCount = GetClearCount(segmentT);
for (; clearCount > 0 && _interactionPath.Count > 0; clearCount--)
{
foreach (var slideArrow in _interactionPath[0].relevantArrows)
slideArrow.ExitSequence();
_interactionPath.RemoveAt(0);
}
Cleared = _interactionPath.Count == 0;
}
public override float GetRemainingLength()
{
return (float)_interactionPath.Count / _initialSensorCount * GetLength();
}
public override void OnDestroy()
{
base.OnDestroy();
foreach (var (_, arrows) in _interactionPath)
foreach (var slideArrow in arrows)
SlideManager.regularSlideArrows.Release(slideArrow);
SlideManager.slideStars.Release(_slideStar);
}
public override void GetJudgementVector(out Vector2 position, out float rotation)
{
_generator.GetPoint(1, out position, out rotation);
}
private void UpdateSlideStarPosition(float segmentT)
{
if (disposed)
return;
var starTransform = _slideStar.transform;
if (segmentT >= 1 && IsLastSegment)
{
starTransform.localScale = Vector3.one * Persistent.Settings.Gameplay.noteScale;
_slideStar.Appearance.Color = new Color(1, 1, 1, 0);
return;
}
_generator.GetPoint(Mathf.Clamp01(segmentT), out var position, out var rotation);
starTransform.SetLocalPositionAndRotation(position, Quaternion.Euler(0, 0, rotation * Mathf.Rad2Deg - 90));
if (TimeSinceSlideStart < 0 &&
indexInSlide == 0 &&
ParentNote.slideMorph != SlideMorph.SuddenIn)
{
var interpolation =
Mathf.InverseLerp(-ParentPath.delay, 0,
(float)TimeSinceSlideStart);
starTransform.localScale = new Vector3(interpolation, interpolation) *
Persistent.Settings.Gameplay.noteScale;
_slideStar.Appearance.Color = new Color(1, 1, 1, interpolation);
}
if (segmentT < 0)
return;
starTransform.localScale = Vector3.one * Persistent.Settings.Gameplay.noteScale;
_slideStar.Appearance.Color = Color.white;
}
private void GenerateArrows()
{
var relevantArrows = new List<RegularSlideArrow>();
TouchInteractable relevantSensor = null;
var totalLength = GetLength();
var arrowCount = Mathf.FloorToInt(totalLength / SlideManager.ArrowDistance);
for (var (distance, index) = (SlideManager.ArrowDistance, 0);
distance <= totalLength;
distance += SlideManager.ArrowDistance)
{
_generator.GetPoint(distance / totalLength, out var position, out var rotation);
var slideArrow = SlideManager.regularSlideArrows.Get();
slideArrow.Initialize(position, rotation,
arrowCount, index, isBreak, isEach);
var sensor = slideArrow.GetClosestSensor();
if (relevantSensor != sensor)
{
if (relevantSensor != null)
_interactionPath.Add((relevantSensor, relevantArrows));
relevantArrows = new List<RegularSlideArrow>();
relevantSensor = sensor;
}
relevantArrows.Add(slideArrow);
index++;
}
if (relevantArrows.Count > 0) _interactionPath.Add((relevantSensor, relevantArrows));
_initialSensorCount = _interactionPath.Count;
}
private int GetClearCount(double segmentT)
{
if (Persistent.Settings.Mods.Auto && segmentT >= 0)
{
var clearCount = Mathf.FloorToInt((float)(segmentT * _initialSensorCount));
if (_lastCheckedClearIndex >= clearCount)
return 0;
var lastCheckedClearIndex = _lastCheckedClearIndex;
_lastCheckedClearIndex = clearCount;
return clearCount - lastCheckedClearIndex;
}
else
{
var clearCount = 0;
var scanIndex = 0;
var tolerance = Persistent.Settings.Judgement.slideTolerance;
while (tolerance >= 0 && scanIndex < _interactionPath.Count)
{
if (_interactionPath[scanIndex].relevantSensor.GetSlideJudgement())
{
tolerance = Persistent.Settings.Judgement.slideTolerance;
clearCount = scanIndex + 1;
}
else
{
tolerance--;
}
scanIndex++;
}
return clearCount;
}
}
}
}

View File

@@ -0,0 +1,52 @@
using System.Collections.Generic;
using AstroDX.Utilities;
using SimaiSharp.Structures;
using UnityEngine;
namespace AstroDX.Contexts.Gameplay.SlideGenerators
{
public sealed class RingCcwGenerator : SlideGenerator
{
private readonly float _angleSpan;
private readonly float _endRadius;
private readonly float _length;
private readonly float _startRadius;
private readonly float _startRotation;
public RingCcwGenerator(IReadOnlyList<Location> vertices)
{
var inPosition = GetPosition(vertices[0]);
var inRadians = Trigonometry.ToPolarAngle(inPosition);
var outPosition = GetPosition(vertices[1]);
var outRadians = Trigonometry.ToPolarAngle(outPosition);
_startRotation = inRadians;
_angleSpan = Trigonometry.GetAngleSpan(inRadians, outRadians, false);
_startRadius = GetRadiusFromCenter(vertices[0]);
_endRadius = GetRadiusFromCenter(vertices[1]);
_length = _angleSpan * (_startRadius + _endRadius) / 2;
}
public override float GetLength()
{
return _length;
}
public override void GetPoint(float t,
out Vector2 position,
out float rotation)
{
var radiusAtT = Mathf.Lerp(_startRadius, _endRadius, t);
var rotationAtT = _startRotation + _angleSpan * t;
position = new Vector2(Mathf.Cos(rotationAtT) * radiusAtT,
Mathf.Sin(rotationAtT) * radiusAtT);
rotation = rotationAtT + Trigonometry.Tau / 4;
}
}
}

View File

@@ -0,0 +1,52 @@
using System.Collections.Generic;
using AstroDX.Utilities;
using SimaiSharp.Structures;
using UnityEngine;
namespace AstroDX.Contexts.Gameplay.SlideGenerators
{
public sealed class RingCwGenerator : SlideGenerator
{
private readonly float _angleSpan;
private readonly float _endRadius;
private readonly float _length;
private readonly float _startRadius;
private readonly float _startRotation;
public RingCwGenerator(IReadOnlyList<Location> vertices)
{
var inPosition = GetPosition(vertices[0]);
var inRadians = Trigonometry.ToPolarAngle(inPosition);
var outPosition = GetPosition(vertices[1]);
var outRadians = Trigonometry.ToPolarAngle(outPosition);
_startRotation = inRadians;
_angleSpan = Trigonometry.GetAngleSpan(_startRotation, outRadians, true);
_startRadius = GetRadiusFromCenter(vertices[0]);
_endRadius = GetRadiusFromCenter(vertices[1]);
_length = _angleSpan * (_startRadius + _endRadius) / 2;
}
public override float GetLength()
{
return _length;
}
public override void GetPoint(float t,
out Vector2 position,
out float rotation)
{
var radiusAtT = Mathf.Lerp(_startRadius, _endRadius, t);
var rotationAtT = _startRotation - _angleSpan * t;
position = new Vector2(Mathf.Cos(rotationAtT) * radiusAtT,
Mathf.Sin(rotationAtT) * radiusAtT);
rotation = rotationAtT - Trigonometry.Tau / 4;
}
}
}

View File

@@ -0,0 +1,190 @@
using System;
using System.Collections.Generic;
using AstroDX.Contexts.Gameplay.Behaviours.Slide.Handlers;
using AstroDX.Contexts.Gameplay.PlayerScope;
using AstroDX.Contexts.Gameplay.SceneScope;
using AstroDX.Globals;
using Medicine;
using SimaiSharp.Structures;
using UnityEngine;
using TimeSpan = AstroDX.Models.Scoring.TimeSpan;
namespace AstroDX.Contexts.Gameplay.Behaviours.Slide
{
public sealed class SlideBehaviour : MonoBehaviour, IDisposable
{
private readonly List<(float distance, SlideSegmentHandler handler)> _segments = new();
private float _endRotation;
private Vector2 _endPosition;
private SlideType _endSegmentType;
private bool _judged;
private int _judgedSegmentCount;
private float _length;
private float _slideStartTime;
[Inject.Single]
private StatisticsManager StatisticsManager { get; }
[Inject.Single]
private MusicManager MusicManager { get; }
[Inject.Single]
private SlideManager SlideManager { get; }
public SlidePath Path { get; private set; }
public Note ParentNote { get; private set; }
public double TimeSinceStart => MusicManager.Time - _slideStartTime;
private void Update()
{
UpdateSegments();
if (TimeSinceStart >= 0)
CheckInteraction();
if (Persistent.Settings.Mods.Auto &&
TimeSinceStart > Path.duration)
Judge();
else if (TimeSinceStart > Path.duration +
TimeSpan.SlideFinish.TimingWindows[^1].lateSpan)
Judge();
}
public void Init(in SlidePath path, in Note parent)
{
_judgedSegmentCount = 0;
_judged = false;
Path = path;
ParentNote = parent;
_slideStartTime = ParentNote.parentCollection.time + Path.delay;
GenerateHandlers(path);
}
private void UpdateSegments()
{
var t = (float)TimeSinceStart / Path.duration * _length;
var isJudgementTarget = TimeSinceStart + Path.delay >= 0;
for (var i = 0; i < _segments.Count; i++)
{
var (segmentStartT, handler) = _segments[i];
var segmentEndT = i + 1 < _segments.Count ? _segments[i + 1].distance : _length;
var segmentT = (t - segmentStartT) / (segmentEndT - segmentStartT);
handler.IsJudgementTarget = isJudgementTarget;
handler.OnUpdate(segmentT);
if (!handler.Cleared)
isJudgementTarget = false;
}
}
private void CheckInteraction()
{
while (_judgedSegmentCount < _segments.Count)
{
var currentHandler = _segments[_judgedSegmentCount].handler;
var segmentCleared = currentHandler.Cleared;
if (!segmentCleared)
return;
_judgedSegmentCount++;
}
Judge();
}
private void GenerateHandlers(in SlidePath slidePath)
{
if (_segments.Count > 0)
_segments.Clear();
var totalDistance = 0f;
var handlers = new List<SlideSegmentHandler>();
for (var i = 0; i < slidePath.segments.Count; i++)
{
var segment = slidePath.segments[i];
var startLocation = i > 0 ? slidePath.segments[i - 1].vertices[^1] : ParentNote.location;
var handler = SlideSegmentHandler.Recommend(this, segment, startLocation);
handlers.Add(handler);
}
var index = 0;
foreach (var segmentHandler in handlers)
{
segmentHandler.SetIndex(index);
_segments.Add((totalDistance, segmentHandler));
totalDistance += segmentHandler.GetLength();
index++;
}
_segments[^1].handler.GetJudgementVector(out _endPosition, out _endRotation);
_endSegmentType = Path.segments[^1].slideType;
_length = totalDistance;
}
private void Judge()
{
if (_judged)
return;
_judged = true;
var incomplete = _judgedSegmentCount < _segments.Count;
var startTime = ParentNote.parentCollection.time +
Path.delay;
var timeFromEnd = MusicManager.Time - (startTime + Path.duration);
var distanceToEnd = incomplete
? _segments[_judgedSegmentCount].handler.GetRemainingLength()
: _length - Mathf.InverseLerp(startTime,
startTime + Path.duration,
(float)MusicManager.Time) * _length;
if (incomplete)
{
var multipleSegmentsRemaining = _judgedSegmentCount + 1 < _segments.Count;
if (multipleSegmentsRemaining)
distanceToEnd += _length - _segments[_judgedSegmentCount + 1].distance;
}
StatisticsManager.TallySlide(Path,
distanceToEnd,
timeFromEnd,
_endPosition,
_endRotation,
_endSegmentType,
incomplete);
Dispose();
}
public void Dispose()
{
foreach (var segment in _segments)
segment.handler.OnDestroy();
SlideManager.slides.Release(this);
}
}
}

View File

@@ -0,0 +1,54 @@
using System;
using AstroDX.Contexts.Gameplay.PlayerScope;
using AstroDX.Utilities;
using SimaiSharp.Structures;
using UnityEngine;
namespace AstroDX.Contexts.Gameplay.SlideGenerators
{
public abstract class SlideGenerator
{
public abstract float GetLength();
public abstract void GetPoint(float t, out Vector2 position, out float rotation);
protected static float GetRotation(in Location location)
{
const float initialAngle = Trigonometry.Tau / 4f - Trigonometry.Tau / 16f;
var angle = initialAngle - Trigonometry.Tau / 8f * location.index;
if (location.group is NoteGroup.DSensor or NoteGroup.ESensor) angle += Trigonometry.Tau / 16f;
return angle;
}
public static Vector2 GetPosition(in Location location)
{
var radius = GetRadiusFromCenter(location);
return GetPositionRadial(GetRotation(location), radius);
}
protected static Vector2 GetPositionRadial(in float rotationRadians,
in float radius = RenderManager.PlayFieldRadius)
{
return new Vector2(Mathf.Cos(rotationRadians) * radius,
Mathf.Sin(rotationRadians) * radius);
}
protected static float GetRadiusFromCenter(Location location)
{
return location.group switch
{
NoteGroup.Tap => RenderManager.PlayFieldRadius,
NoteGroup.ASensor => RenderManager.AreaARadius,
NoteGroup.BSensor => RenderManager.AreaBRadius,
NoteGroup.CSensor => 0,
NoteGroup.DSensor => RenderManager.AreaDRadius,
NoteGroup.ESensor => RenderManager.AreaERadius,
_ => throw new ArgumentOutOfRangeException()
};
}
}
}

View File

@@ -0,0 +1,70 @@
using System.Linq;
using SimaiSharp.Structures;
using UnityEngine;
namespace AstroDX.Contexts.Gameplay.Behaviours.Slide.Handlers
{
public abstract class SlideSegmentHandler
{
protected readonly bool isBreak;
protected readonly bool isEach;
protected bool disposed;
protected int indexInSlide = -1;
protected SlideSegmentHandler(SlideBehaviour slideBehaviour)
{
disposed = false;
ParentSlide = slideBehaviour;
isEach = ParentSlide.ParentNote.parentCollection
.Sum(n => n.slidePaths.Count) > 1;
isBreak = ParentSlide.Path.type == NoteType.Break;
}
public bool Cleared { get; protected set; }
protected SlideBehaviour ParentSlide { get; }
protected SlidePath ParentPath => ParentSlide.Path;
protected Note ParentNote => ParentSlide.ParentNote;
protected double TimeSinceSlideStart => ParentSlide.TimeSinceStart;
protected bool IsLastSegment => indexInSlide != ParentPath.segments.Count - 1;
public bool IsJudgementTarget { get; set; }
public void SetIndex(int index)
{
indexInSlide = index;
}
/// <summary>
/// Describes the position and the up vector for the judgement text.
/// </summary>
public abstract void GetJudgementVector(out Vector2 position, out float rotation);
public abstract void OnUpdate(float segmentT);
public virtual void OnDestroy()
{
disposed = true;
}
public abstract float GetLength();
public abstract float GetRemainingLength();
public static SlideSegmentHandler Recommend(SlideBehaviour slideBehaviour,
SlideSegment segment,
Location startLocation)
{
return segment.slideType switch
{
SlideType.Fan => new FanSlideSegmentHandler(slideBehaviour, segment, startLocation),
_ => new RegularSlideSegmentHandler(slideBehaviour, segment, startLocation)
};
}
}
}

View File

@@ -0,0 +1,35 @@
using System.Collections.Generic;
using SimaiSharp.Structures;
using UnityEngine;
namespace AstroDX.Contexts.Gameplay.SlideGenerators
{
public sealed class StraightGenerator : SlideGenerator
{
private readonly Vector2 _endPoint;
private readonly float _rotation;
private readonly float _length;
private readonly Vector2 _startPoint;
public StraightGenerator(IReadOnlyList<Location> vertices)
{
_startPoint = GetPosition(vertices[0]);
_endPoint = GetPosition(vertices[1]);
var segment = _endPoint - _startPoint;
_length = segment.magnitude;
_rotation = Mathf.Atan2(segment.y, segment.x);
}
public override float GetLength()
{
return _length;
}
public override void GetPoint(float t, out Vector2 position, out float rotation)
{
position = Vector2.Lerp(_startPoint, _endPoint, t);
rotation = _rotation;
}
}
}

View File

@@ -0,0 +1,45 @@
using System.Collections;
using System.Collections.Generic;
using Helpers;
using UnityEngine;
using UnityEngine.UI;
namespace Title
{
public class TitleManager : MonoBehaviour
{
public Text anyKeyPressed;
public List<string> textList;
private int _textIndex;
private void Start()
{
textList = new List<string>
{
Localization.ParseAuto($"TITLE_ANYKEY"),
Localization.ParseAuto($"TITLE_THANKS"),
$"{Localization.ParseAuto($"TITLE_BUILDVERSION")} {Application.version}"
};
StartCoroutine(CycleThroughText());
}
private IEnumerator CycleThroughText()
{
while (true)
{
_textIndex = (_textIndex + 1) % textList.Count;
anyKeyPressed.text = textList[_textIndex];
yield return new WaitForSeconds(3);
}
// ReSharper disable once IteratorNeverReturns
}
public void ChangeScene()
{
SceneSwapper.LoadScene("ModeSelection");
}
}
}

View File

@@ -0,0 +1,84 @@
using UnityEngine;
namespace AstroDX.Utilities
{
public static class Trigonometry
{
public const float Tau = Mathf.PI * 2;
public static Vector2 Rotate(in this Vector2 v, in float degreesRad)
{
var magnitude = v.magnitude;
var originalDegrees = Mathf.Atan2(v.y, v.x);
var newDegrees = originalDegrees + degreesRad;
var newX = Mathf.Cos(newDegrees) * magnitude;
var newY = Mathf.Sin(newDegrees) * magnitude;
return new Vector2(newX, newY);
}
/// <summary>
/// Calculates a point's angle relative to a center point.
/// </summary>
/// <param name="position">The absolute position of a point.</param>
/// <param name="offset">The offset of the calculation's center point.</param>
/// <returns>The relative angle of a point from the given center point.</returns>
internal static float ToPolarAngle(in Vector2 position, in Vector2? offset = null)
{
if (!offset.HasValue)
return Mathf.Atan2(position.y, position.x);
var difference = position - offset.Value;
return Mathf.Atan2(difference.y, difference.x);
}
/// <summary>
/// Calculates the angle between a line to a point on a ring
/// and another line perpendicular from a tangent line of that point.
/// </summary>
/// <param name="adjacent"></param>
/// <param name="hypotenuse"></param>
/// <param name="clockwise"></param>
/// <returns></returns>
internal static float GetTangentAngleDelta(in float adjacent,
in float hypotenuse,
in bool clockwise)
{
var angleDiff = Mathf.Acos(adjacent / hypotenuse);
return clockwise ? -angleDiff : angleDiff;
}
/// <summary>
/// <para>
/// Calculates the angle between <c>startRotation</c> and <c>endRotation</c>,
/// given its traversing direction.
/// </para>
/// </summary>
/// <param name="startRotation">The starting rotation.</param>
/// <param name="endRotation">The ending rotation.</param>
/// <param name="clockwise">Traversing direction.</param>
/// <param name="wrapThreshold">
/// <para>Wraps to full circle for spans smaller than this value.</para>
/// <para><code>Tau / 4f</code> is recommended for offset circles</para>
/// </param>
/// <returns>
/// The span between the starting rotation and the ending rotation on a unit circle,
/// in radians.
/// </returns>
public static float GetAngleSpan(in float startRotation,
in float endRotation,
bool clockwise,
float wrapThreshold = Tau / 32f)
{
var span = clockwise
? (startRotation - endRotation + 2 * Tau) % Tau
: (endRotation - startRotation + 2 * Tau) % Tau;
if (span <= wrapThreshold)
span += Tau;
return span;
}
}
}

View File

@@ -0,0 +1,95 @@
using System.Collections.Generic;
using AstroDX.Contexts.Gameplay.PlayerScope;
using AstroDX.Utilities;
using SimaiSharp.Structures;
using UnityEngine;
namespace AstroDX.Contexts.Gameplay.SlideGenerators
{
public sealed class ZigZagSGenerator : SlideGenerator
{
private readonly Vector2 _endPoint;
private readonly Vector2 _endZagPoint;
private readonly float _midSegmentLength;
private readonly Vector2 _startPoint;
private readonly float _startRotation;
private readonly float _midRotation;
private readonly float _endRotation;
private readonly float _startSegmentLength;
private readonly Vector2 _startZagPoint;
private readonly float _totalLength;
public ZigZagSGenerator(IReadOnlyList<Location> vertices)
{
const float distance = RenderManager.PlayFieldRadius;
const float inner = RenderManager.CenterRadius;
var startRotation = GetRotation(vertices[0]);
var endRotation = GetRotation(vertices[1]);
var startZag = startRotation + Trigonometry.Tau / 4f;
var endZag = endRotation + Trigonometry.Tau / 4f;
_startPoint = new Vector2(distance * Mathf.Cos(startRotation),
distance * Mathf.Sin(startRotation));
_startZagPoint = new Vector2(inner * Mathf.Cos(startZag),
inner * Mathf.Sin(startZag));
var startSegment = _startZagPoint - _startPoint;
_startSegmentLength = startSegment.magnitude;
_startRotation = Mathf.Atan2(startSegment.y, startSegment.x);
_endZagPoint = new Vector2(inner * Mathf.Cos(endZag),
inner * Mathf.Sin(endZag));
var midSegment = _endZagPoint - _startZagPoint;
_midSegmentLength = midSegment.magnitude;
_midRotation = Mathf.Atan2(midSegment.y, midSegment.x);
_endPoint = new Vector2(distance * Mathf.Cos(endRotation),
distance * Mathf.Sin(endRotation));
var endSegment = _endPoint - _endZagPoint;
var endSegmentLength = endSegment.magnitude;
_endRotation = Mathf.Atan2(endSegment.y, endSegment.x);
_totalLength = _startSegmentLength + _midSegmentLength + endSegmentLength;
}
public override float GetLength()
{
return _totalLength;
}
public override void GetPoint(float t, out Vector2 position, out float rotation)
{
var distanceFromStart = t * _totalLength;
if (distanceFromStart < _startSegmentLength)
{
position = Vector2.Lerp(_startPoint, _startZagPoint,
Mathf.InverseLerp(0, _startSegmentLength, distanceFromStart));
rotation = _startRotation;
}
else if (distanceFromStart < _startSegmentLength + _midSegmentLength)
{
var midLength = _startSegmentLength + _midSegmentLength;
position = Vector2.Lerp(_startZagPoint, _endZagPoint,
Mathf.InverseLerp(_startSegmentLength, midLength, distanceFromStart));
rotation = _midRotation;
}
else
{
var midLength = _startSegmentLength + _midSegmentLength;
position = Vector2.Lerp(_endZagPoint, _endPoint,
Mathf.InverseLerp(midLength, _totalLength, distanceFromStart));
rotation = _endRotation;
}
}
}
}

View File

@@ -0,0 +1,95 @@
using System.Collections.Generic;
using AstroDX.Contexts.Gameplay.PlayerScope;
using AstroDX.Utilities;
using SimaiSharp.Structures;
using UnityEngine;
namespace AstroDX.Contexts.Gameplay.SlideGenerators
{
public sealed class ZigZagZGenerator : SlideGenerator
{
private readonly Vector2 _endPoint;
private readonly Vector2 _endZagPoint;
private readonly float _midSegmentLength;
private readonly Vector2 _startPoint;
private readonly float _startRotation;
private readonly float _midRotation;
private readonly float _endRotation;
private readonly float _startSegmentLength;
private readonly Vector2 _startZagPoint;
private readonly float _totalLength;
public ZigZagZGenerator(IReadOnlyList<Location> vertices)
{
const float distance = RenderManager.PlayFieldRadius;
const float inner = RenderManager.CenterRadius;
var startRotation = GetRotation(vertices[0]);
var endRotation = GetRotation(vertices[1]);
var startZag = startRotation - Trigonometry.Tau / 4f;
var endZag = endRotation - Trigonometry.Tau / 4f;
_startPoint = new Vector2(distance * Mathf.Cos(startRotation),
distance * Mathf.Sin(startRotation));
_startZagPoint = new Vector2(inner * Mathf.Cos(startZag),
inner * Mathf.Sin(startZag));
var startSegment = _startZagPoint - _startPoint;
_startSegmentLength = startSegment.magnitude;
_startRotation = Mathf.Atan2(startSegment.y, startSegment.x);
_endZagPoint = new Vector2(inner * Mathf.Cos(endZag),
inner * Mathf.Sin(endZag));
var midSegment = _endZagPoint - _startZagPoint;
_midSegmentLength = midSegment.magnitude;
_midRotation = Mathf.Atan2(midSegment.y, midSegment.x);
_endPoint = new Vector2(distance * Mathf.Cos(endRotation),
distance * Mathf.Sin(endRotation));
var endSegment = _endPoint - _endZagPoint;
var endSegmentLength = endSegment.magnitude;
_endRotation = Mathf.Atan2(endSegment.y, endSegment.x);
_totalLength = _startSegmentLength + _midSegmentLength + endSegmentLength;
}
public override float GetLength()
{
return _totalLength;
}
public override void GetPoint(float t, out Vector2 position, out float rotation)
{
var distanceFromStart = t * _totalLength;
if (distanceFromStart < _startSegmentLength)
{
position = Vector2.Lerp(_startPoint, _startZagPoint,
Mathf.InverseLerp(0, _startSegmentLength, distanceFromStart));
rotation = _startRotation;
}
else if (distanceFromStart < _startSegmentLength + _midSegmentLength)
{
var midLength = _startSegmentLength + _midSegmentLength;
position = Vector2.Lerp(_startZagPoint, _endZagPoint,
Mathf.InverseLerp(_startSegmentLength, midLength, distanceFromStart));
rotation = _midRotation;
}
else
{
var midLength = _startSegmentLength + _midSegmentLength;
position = Vector2.Lerp(_endZagPoint, _endPoint,
Mathf.InverseLerp(midLength, _totalLength, distanceFromStart));
rotation = _endRotation;
}
}
}
}

View File

@@ -0,0 +1,122 @@
Shader "AstroDX/Note"
{
Properties
{
[PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
_GradientTex ("Gradient Texture", 2D) = "white" {}
_Color ("Tint", Color) = (1,1,1,1)
_ShadowColor ("Shadow Tint", Color) = (0,0,0,1)
_Grayscale("Grayscale", Range(0, 1)) = 0
[MaterialToggle] _Shine ("Shine", Float) = 0
[MaterialToggle] PixelSnap ("Pixel snap", Float) = 0
}
SubShader
{
Tags
{
"Queue"="Transparent"
"IgnoreProjector"="True"
"RenderType"="Transparent"
"PreviewType"="Plane"
"CanUseSpriteAtlas"="True"
}
Cull Off
Lighting Off
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile _ PIXELSNAP_ON
#include "UnityCG.cginc"
struct appdata_t
{
fixed4 vertex : POSITION;
fixed4 color : COLOR;
fixed2 texcoord : TEXCOORD0;
};
struct v2f
{
fixed4 vertex : SV_POSITION;
fixed4 color : COLOR;
fixed2 texcoord : TEXCOORD0;
};
fixed4 _Color;
fixed _Shine;
v2f vert(appdata_t IN)
{
v2f OUT;
OUT.vertex = UnityObjectToClipPos(IN.vertex);
OUT.texcoord = IN.texcoord;
OUT.color = IN.color * _Color;
#ifdef PIXELSNAP_ON
OUT.vertex = UnityPixelSnap (OUT.vertex);
#endif
return OUT;
}
sampler2D _MainTex;
sampler2D _GradientTex;
sampler2D _AlphaTex;
fixed4 _ShadowColor;
fixed _AlphaSplitEnabled;
fixed _Grayscale;
fixed4 sample_sprite_texture(float2 uv)
{
const fixed4 color = tex2D(_MainTex, uv);
// setting to 0.004 and 0.996 prevents getting wrong texture edge colors
const fixed gradient_position = lerp(0.005, 0.995, saturate(color.r * 2 - 1));
const fixed4 gradient_map = tex2D(_GradientTex, fixed2(gradient_position, 0));
#if UNITY_TEXTURE_ALPHASPLIT_ALLOWED
if (_AlphaSplitEnabled)
{
color.a = tex2D (_AlphaTex, uv).r;
}
#endif //UNITY_TEXTURE_ALPHASPLIT_ALLOWED
// choke starts at 0.1 higher than the gradient map threshold
// to allow a smoother color blending (see touch)
const fixed red_choke = smoothstep(0, 0.5, color.r);
const fixed3 shadow_color = fixed3(color.b * _ShadowColor.rgb);
fixed3 sum = gradient_map * red_choke + shadow_color * _ShadowColor.a * color.b;
sum = color.g.xxxx + sum * (1 - color.g);
return fixed4(sum.rgb, color.a);
}
fixed4 frag(v2f IN) : SV_Target
{
fixed4 c = sample_sprite_texture(IN.texcoord);
//Rough human eye adjusted grayscale computation
const fixed mono_rgb = 0.299 * c.r + 0.587 * c.g + 0.114 * c.b;
c.rgb *= IN.color.rgb;
const fixed shine_multiplier = (sin(_Time.y * 16) * 0.5 + 0.5) * _Shine;
c.rgb += shine_multiplier.rrr * 0.2;
c.rgb *= 1 + shine_multiplier.rrr * 0.3;
const fixed3 out_color = lerp(c.rgb, mono_rgb.rrr, _Grayscale);
return fixed4(out_color, c.a * IN.color.a);
}
ENDCG
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB