Files
bansonic_beta_main/Assets/scripts/gamePlay_gameplay/HoldNoteController.cs
T

130 lines
3.9 KiB
C#

using UnityEngine;
using System;
public class HoldNoteController : MonoBehaviour
{
private bool isInsideJudgeZone = false;
public event Action<bool> OnJudgeZoneChanged;
private bool isMoving = false;
private float speed = 0f;
private float activationTime;
// Absolute positioning for hold segments (keeps segments connected under frame spikes)
private bool useAbsolutePositioning = false;
private Vector3 spawnPosition;
private float baseSpawnYOffset = 0f; // can be negative to offset upward for delayed segments
private float visualOffset = 0f; // additional Y offset for visual compensation
private void Update()
{
if (useAbsolutePositioning)
{
if (!isMoving) return;
ApplyAbsolutePosition(GameplayClock.NowSongTime);
return;
}
// Start moving after activation time in legacy mode
if (!isMoving && GameplayClock.NowSongTime >= activationTime)
{
isMoving = true;
}
if (isMoving)
{
transform.Translate(Vector3.down * speed * Time.deltaTime);
}
}
/// <summary>
/// Configure legacy timing (per-segment activation).
/// </summary>
public void ConfigureTiming(float baseHitTime, float segmentDelay, float travelTime)
{
float hitTime = baseHitTime + segmentDelay;
activationTime = hitTime - travelTime;
isMoving = false;
useAbsolutePositioning = false;
visualOffset = 0f;
}
/// <summary>
/// Legacy delay setter (not recommended).
/// </summary>
public void SetSegmentDelay(float segmentDelay)
{
activationTime = GameplayClock.NowSongTime + segmentDelay;
isMoving = false;
useAbsolutePositioning = false;
visualOffset = 0f;
}
public void SetSpeed(float newSpeed)
{
speed = newSpeed;
}
/// <summary>
/// Configure absolute positioning for hold segments so they stay connected.
/// spawnPoint should be the original spawn point for the lane.
/// baseSpawnYOffset is applied to keep delayed segments offset upward (can be negative).
/// </summary>
public void ConfigureAbsolutePositioning(Vector3 spawnPoint, float newActivationTime, float spawnYOffset, float newVisualOffset = 0f)
{
spawnPosition = spawnPoint;
activationTime = newActivationTime;
baseSpawnYOffset = spawnYOffset;
visualOffset = newVisualOffset;
useAbsolutePositioning = true;
isMoving = true;
// Snap immediately to the expected position to avoid a 1-frame pop.
ApplyAbsolutePosition(GameplayClock.NowSongTime);
}
public void StopMovement()
{
isMoving = false;
visualOffset = 0f;
}
private void OnTriggerEnter2D(Collider2D collision)
{
if (!collision.CompareTag("JudgmentLine")) return;
isInsideJudgeZone = true;
OnJudgeZoneChanged?.Invoke(true);
}
private void OnTriggerExit2D(Collider2D collision)
{
if (!collision.CompareTag("JudgmentLine")) return;
isInsideJudgeZone = false;
OnJudgeZoneChanged?.Invoke(false);
}
public bool IsInsideJudgArea()
{
return isInsideJudgeZone;
}
public float ActivationTime => activationTime;
public float CurrentSpeed => speed;
public bool UsesAbsolutePositioning => useAbsolutePositioning;
public float BaseSpawnYOffset => baseSpawnYOffset;
public Vector3 SpawnPosition => spawnPosition;
public float VisualOffset => visualOffset;
private void ApplyAbsolutePosition(float time)
{
float elapsed = time - activationTime;
float travelDistance = speed * elapsed;
Vector3 newPos = spawnPosition;
// Apply visual offset (positive moves the note down its path earlier)
newPos.y -= (baseSpawnYOffset + travelDistance + visualOffset);
transform.position = newPos;
}
}