Skip to content

Commit

Permalink
Gameplay: Modify the lane notes judgement mechanism to be similar to …
Browse files Browse the repository at this point in the history
…the NS version

- Add extra keys in InputManager.asset.
- Extract the joystickSensibility threshold variable.
- Correct the deviation calculation function `GetAngleDeviation`.
- Straight arcs with same start and end position will be accepts now.
- Narrow the `arcJudgementThreshold` to 45.0 (to be similar to the NS version).
  • Loading branch information
freeze-dolphin committed Jun 22, 2024
1 parent ca89c47 commit ae915e5
Show file tree
Hide file tree
Showing 2 changed files with 195 additions and 52 deletions.
215 changes: 163 additions & 52 deletions Assets/Scripts/Gameplay/Judgement/Input/ControllerInputHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,17 @@ namespace ArcCreate.Gameplay.Judgement.Input
{
public class ControllerInputHandler : IInputHandler
{
private static void DebugKeyPressed()
{
foreach (KeyCode keyCode in Enum.GetValues(typeof(KeyCode)))
{
if (UnityEngine.Input.GetKeyDown(keyCode))
{
Debug.Log("Pressed: " + keyCode);
}
}
}

public record JoyconArcInputState
{
public record JoyconSingleArcInputState
Expand Down Expand Up @@ -39,17 +50,98 @@ public void Reset()
}
}

public static bool AddIfNotExists<T>(List<T> list, T value)
{
if (!list.Contains(value))
{
list.Add(value);
return true;
}

return false;
}

public record DPadInputState
{
public record DPadSingleInputState
{
public bool JustClicked { get; set; }
public float PreviousValue { get; set; }

public DPadSingleInputState(bool justClicked, float prev)
{
JustClicked = justClicked;
PreviousValue = prev;
}
}

public DPadSingleInputState Lane1 { get; }
public DPadSingleInputState Lane2 { get; }
public DPadSingleInputState Lane3 { get; }
public DPadSingleInputState Lane4 { get; }

public DPadInputState()
{
Lane1 = new DPadSingleInputState(false, 0);
Lane2 = new DPadSingleInputState(false, 0);
Lane3 = new DPadSingleInputState(false, 0);
Lane4 = new DPadSingleInputState(false, 0);
}

public void Update(DPadSingleInputState lane, float dPadInput)
{
lane.JustClicked = dPadInput != lane.PreviousValue;
lane.PreviousValue = dPadInput;
}
}

public enum TapSide
{
Left,
Right,
Undefined
}

public static TapSide FromLaneIndex(int lane)
{
return lane switch
{
>= 0 and <= 2 => TapSide.Left,
<= 5 => TapSide.Right,
_ => TapSide.Undefined
};
}

public static void LaneFeedback(TapSide side, int timing)
{
var enwidened =
Services.Scenecontrol.Scene.Track.ExtraL.ColorA.ValueAt(timing) >= 200 &&
Services.Scenecontrol.Scene.Track.ExtraR.ColorA.ValueAt(timing) >= 200;
switch (side)
{
case TapSide.Left:
Services.InputFeedback.LaneFeedback(1);
Services.InputFeedback.LaneFeedback(2);
if (enwidened) Services.InputFeedback.LaneFeedback(0);
break;
case TapSide.Right:
Services.InputFeedback.LaneFeedback(3);
Services.InputFeedback.LaneFeedback(4);
if (enwidened) Services.InputFeedback.LaneFeedback(5);
break;
default:
throw new ArgumentOutOfRangeException(nameof(side), side, null);
}
}

protected List<int> CurrentLaneDownInputs = new(4);
protected List<int> CurrentLaneContinualInputs = new(4);
protected List<bool> CurrentArctapInputs = new(2);
protected JoyconArcInputState CurrentArcInputs = new(0, 0, 0, 0);
protected DPadInputState CurrentDPadInputs = new();

private double arcJudgementThreshold = 90;

private bool lane1JustClicked;
private float prevLane1;
private bool lane2JustClicked;
private float prevLane2;
private static double arcJudgementThreshold = 45.0;
private static double joystickSensibility = 0.125;

public void PollInput()
{
Expand All @@ -58,37 +150,55 @@ public void PollInput()
CurrentArctapInputs.Clear();
CurrentArcInputs.Reset();

// Arc Input from Joysticks
CurrentArcInputs.Left.Horizontal = UnityEngine.Input.GetAxis("Left Horizontal");
CurrentArcInputs.Left.Vertical = UnityEngine.Input.GetAxis("Left Vertical");
CurrentArcInputs.Right.Horizontal = UnityEngine.Input.GetAxis("Right Horizontal");
CurrentArcInputs.Right.Vertical = UnityEngine.Input.GetAxis("Right Vertical");

float lane1 = UnityEngine.Input.GetAxis("Lane 1");
float lane2 = UnityEngine.Input.GetAxis("Lane 2");

lane1JustClicked = lane1 != prevLane1;
lane2JustClicked = lane2 != prevLane2;

if (lane1JustClicked && lane1 < 0) CurrentLaneDownInputs.Add(1);
if (lane2JustClicked && lane2 > 0) CurrentLaneDownInputs.Add(2);
if (UnityEngine.Input.GetButtonDown("Lane 3")) CurrentLaneDownInputs.Add(3);
if (UnityEngine.Input.GetButtonDown("Lane 4")) CurrentLaneDownInputs.Add(4);

if (lane1 < 0) CurrentLaneContinualInputs.Add(1);
if (lane2 > 0) CurrentLaneContinualInputs.Add(2);
if (UnityEngine.Input.GetButton("Lane 3")) CurrentLaneContinualInputs.Add(3);
if (UnityEngine.Input.GetButton("Lane 4")) CurrentLaneContinualInputs.Add(4);

// Update D-Pad Input
float dPadLane1Input = UnityEngine.Input.GetAxis("Lane 1");
float dPadLane2Input = UnityEngine.Input.GetAxis("Lane 2");
float dPadLane3AlternateInput = -dPadLane1Input;
float dPadLane4AlternateInput = -dPadLane2Input;

CurrentDPadInputs.Update(CurrentDPadInputs.Lane1, dPadLane1Input);
CurrentDPadInputs.Update(CurrentDPadInputs.Lane2, dPadLane2Input);
CurrentDPadInputs.Update(CurrentDPadInputs.Lane3, dPadLane3AlternateInput);
CurrentDPadInputs.Update(CurrentDPadInputs.Lane4, dPadLane4AlternateInput);

// Normal Button
if (CurrentDPadInputs.Lane1.JustClicked && dPadLane1Input < 0) AddIfNotExists(CurrentLaneDownInputs, 1);
if (CurrentDPadInputs.Lane2.JustClicked && dPadLane2Input > 0) AddIfNotExists(CurrentLaneDownInputs, 2);
if (UnityEngine.Input.GetButtonDown("Lane 3")) AddIfNotExists(CurrentLaneDownInputs, 3);
if (UnityEngine.Input.GetButtonDown("Lane 4")) AddIfNotExists(CurrentLaneDownInputs, 4);

if (dPadLane1Input < 0) AddIfNotExists(CurrentLaneContinualInputs, 1);
if (dPadLane2Input > 0) AddIfNotExists(CurrentLaneContinualInputs, 2);
if (UnityEngine.Input.GetButton("Lane 3")) AddIfNotExists(CurrentLaneContinualInputs, 3);
if (UnityEngine.Input.GetButton("Lane 4")) AddIfNotExists(CurrentLaneContinualInputs, 4);

// Alternate Button
if (CurrentDPadInputs.Lane3.JustClicked && dPadLane3AlternateInput < 0) AddIfNotExists(CurrentLaneDownInputs, 3);
if (CurrentDPadInputs.Lane4.JustClicked && dPadLane4AlternateInput > 0) AddIfNotExists(CurrentLaneDownInputs, 4);
if (UnityEngine.Input.GetButtonDown("Lane 1 Alternate")) AddIfNotExists(CurrentLaneDownInputs, 1);
if (UnityEngine.Input.GetButtonDown("Lane 2 Alternate")) AddIfNotExists(CurrentLaneDownInputs, 2);

if (dPadLane3AlternateInput < 0) AddIfNotExists(CurrentLaneContinualInputs, 3);
if (dPadLane4AlternateInput > 0) AddIfNotExists(CurrentLaneContinualInputs, 4);
if (UnityEngine.Input.GetButton("Lane 1 Alternate")) AddIfNotExists(CurrentLaneContinualInputs, 1);
if (UnityEngine.Input.GetButton("Lane 2 Alternate")) AddIfNotExists(CurrentLaneContinualInputs, 2);

// Arctap
CurrentArctapInputs.Add(UnityEngine.Input.GetButtonDown("Left Arctap"));
CurrentArctapInputs.Add(UnityEngine.Input.GetButtonDown("Right Arctap"));

for (int i = 0; i < CurrentLaneContinualInputs.Count; i++)
foreach (var laneInput in CurrentLaneContinualInputs)
{
Services.InputFeedback.LaneFeedback(CurrentLaneContinualInputs[i]);
LaneFeedback(FromLaneIndex(laneInput), Services.Audio.AudioTiming);
}

prevLane1 = lane1;
prevLane2 = lane2;
// DebugKeyPressed();
}

public enum JoyconJudgementSide
Expand Down Expand Up @@ -118,7 +228,9 @@ public void HandleTapRequests(
{
for (int inpIndex = 0; inpIndex < CurrentLaneDownInputs.Count; inpIndex++)
{
int lane = CurrentLaneDownInputs[inpIndex];
int laneIndex = CurrentLaneDownInputs[inpIndex];
TapSide side = FromLaneIndex(laneIndex);
if (side == TapSide.Undefined) continue;

int minTimingDifference = int.MaxValue;

Expand All @@ -135,7 +247,7 @@ public void HandleTapRequests(
continue;
}

if (req.Lane == lane && timingDifference < minTimingDifference)
if (FromLaneIndex(req.Lane) == side && timingDifference < minTimingDifference)
{
minTimingDifference = timingDifference;
applicableLaneRequestExists = true;
Expand Down Expand Up @@ -205,7 +317,9 @@ public void HandleLaneHoldRequests(int currentTiming, UnorderedList<LaneHoldJudg
{
for (int inpIndex = 0; inpIndex < CurrentLaneContinualInputs.Count; inpIndex++)
{
int lane = CurrentLaneContinualInputs[inpIndex];
int laneIndex = CurrentLaneContinualInputs[inpIndex];
TapSide side = FromLaneIndex(laneIndex);
if (side == TapSide.Undefined) continue;

for (int i = requests.Count - 1; i >= 0; i--)
{
Expand All @@ -216,7 +330,7 @@ public void HandleLaneHoldRequests(int currentTiming, UnorderedList<LaneHoldJudg
continue;
}

if (req.Lane == lane)
if (FromLaneIndex(req.Lane) == side)
{
req.Receiver.ProcessLaneHoldJudgement(currentTiming >= req.ExpireAtTiming, req.IsJudgement, req.Properties);
requests.RemoveAt(i);
Expand Down Expand Up @@ -246,17 +360,15 @@ private float MatrixToEuler(Matrix4x4 matrix)

private static double GetAngleDeviation(double input, double judge)
{
return Math.Min(
Math.Abs(input - judge),
Math.Abs(judge - input)
);
var d = Math.Abs(judge - input);
return Math.Min(d, 360 - d);
}

private static double CalculateEulerFromJoystickInput(JoyconArcInputState.JoyconSingleArcInputState input)
{
float h = input.Horizontal;
float v = input.Vertical;
if (Math.Sqrt(Math.Pow(h, 2) + Math.Pow(v, 2)) < 0.125) return -1;
if (Math.Sqrt(Math.Pow(h, 2) + Math.Pow(v, 2)) < joystickSensibility) return -1;
var radians = Math.PI / 2 - Math.Atan(v / h);
if (h < 0) radians += Math.PI;
return 180 / Math.PI * radians;
Expand All @@ -274,29 +386,28 @@ public void HandleArcRequests(int currentTiming, UnorderedList<ArcJudgementReque
continue;
}

var matrix = req.Arc.ArcCapMatrix;
var eulerAngles = MatrixToEuler(matrix);
bool accepted;

double currentAngle;

switch (req.Arc.Color)
if (req.Arc.XStart == req.Arc.XEnd && req.Arc.YStart == req.Arc.YEnd)
{
accepted = true;
}
else
{
case 0:
currentAngle = CalculateEulerFromJoystickInput(input.Left);
break;
var matrix = req.Arc.ArcCapMatrix;
var eulerAngles = MatrixToEuler(matrix);

case 1:
currentAngle = CalculateEulerFromJoystickInput(input.Right);
break;
double currentAngle = req.Arc.Color switch
{
0 => CalculateEulerFromJoystickInput(input.Left),
1 => CalculateEulerFromJoystickInput(input.Right),
_ => -1.0
};

default:
currentAngle = -1;
break;
double deviation = GetAngleDeviation(currentAngle, eulerAngles);
accepted = currentAngle <= 0 && deviation < arcJudgementThreshold;
}

double deviation = GetAngleDeviation(currentAngle, eulerAngles);
bool accepted = currentAngle >= 0 && deviation < arcJudgementThreshold;

if (accepted)
{
req.Receiver.ProcessArcJudgement(currentTiming >= req.ExpireAtTiming, req.IsJudgement, req.Properties);
Expand Down
32 changes: 32 additions & 0 deletions ProjectSettings/InputManager.asset
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,38 @@ InputManager:
type: 0
axis: 3
joyNum: 0
- serializedVersion: 3
m_Name: Lane 1 Alternate
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton: joystick button 2
altNegativeButton:
altPositiveButton:
gravity: 1000
dead: 0.001
sensitivity: 1000
snap: 0
invert: 0
type: 0
axis: 3
joyNum: 0
- serializedVersion: 3
m_Name: Lane 2 Alternate
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton: joystick button 0
altNegativeButton:
altPositiveButton:
gravity: 1000
dead: 0.001
sensitivity: 1000
snap: 0
invert: 0
type: 0
axis: 3
joyNum: 0
- serializedVersion: 3
m_Name: Left Arctap
descriptiveName:
Expand Down

0 comments on commit ae915e5

Please sign in to comment.