Files
WhaleAdventure_VR/Assets/My project/Fishing Scripts/UI/FishingGameManager.cs
2026-06-26 18:05:00 +09:00

964 lines
30 KiB
C#

using System;
using System.Collections;
using UnityEngine;
using UnityEngine.InputSystem;
public class FishingGameManager : MonoBehaviour
{
public enum ResultType
{
Perfect,
Good,
Miss
}
public enum FishingControlMode
{
TimingGauge,
SimpleReel
}
[Header("Auto Bind")]
[Tooltip("현재 만든 Prefab 구조 기준으로 비어 있는 참조를 자동 연결합니다.")]
[SerializeField] private bool autoBindMissingReferences = true;
[Header("References")]
[Tooltip("낚시 UI 전체 루트입니다. UI를 자동으로 켜고 끄고 싶을 때만 연결하세요.")]
[SerializeField] private GameObject uiRoot;
[SerializeField] private FishingGaugeUI ui;
[SerializeField] private FishingRewardSystem rewardSystem;
[SerializeField] private FishingHapticManager haptic;
[SerializeField] private RotateUI centerIconRotate;
[Header("Simple Reel Fishing")]
[Tooltip("TimingGauge는 기존 원형 타이밍 게이지 방식, SimpleReel은 릴을 감아 줄 길이를 줄이면 성공하는 방식입니다.")]
[SerializeField] private FishingControlMode fishingControlMode = FishingControlMode.SimpleReel;
[Tooltip("낚싯대 / 찌 / 릴 제어 스크립트입니다. SimpleReel 모드에서 줄 길이를 읽습니다.")]
[SerializeField] private FishingRodVRController rodController;
[Tooltip("현재 줄 길이가 이 값 이하가 되면 낚기 성공으로 처리합니다.")]
[SerializeField] private float catchCompleteLineLength = 0.7f;
[Tooltip("릴 낚시 시작 시 줄 길이가 성공 길이보다 너무 짧으면 이 거리만큼 최소 시작 길이를 보정합니다.")]
[SerializeField] private float simpleReelMinimumPullDistance = 0.35f;
[Tooltip("릴 낚기 성공 후 찌를 낚싯대 쪽 대기 위치로 회수합니다.")]
[SerializeField] private bool returnBobberAfterSimpleReelCatch = true;
[Tooltip("SimpleReel 모드에서 기존 타이밍 게이지 UI를 숨기고 진행도/줄 길이 중심으로 표시합니다.")]
[SerializeField] private bool hideTimingGaugeInSimpleReelMode = true;
[Tooltip("SimpleReel 모드에서는 StartFishing이 여러 번 호출됩니다. 켜면 첫 시작 때만 연못 상태를 초기화합니다.")]
[SerializeField] private bool resetPondOnlyOnFirstSimpleReelStart = true;
[Header("XR Controller Input")]
[Tooltip("낚시 판정을 실행할 컨트롤러 입력입니다. 예: XRI Right Interaction / Select")]
[SerializeField] private InputActionReference submitAction;
[Tooltip("낚시를 다시 시작할 컨트롤러 입력입니다. 필요 없으면 비워둬도 됩니다.")]
[SerializeField] private InputActionReference resetAction;
[Tooltip("씬에 Input Action Manager가 없거나 액션이 자동 활성화되지 않으면 켜두세요.")]
[SerializeField] private bool enableInputActionsManually = true;
[Header("Pointer")]
[SerializeField] private float pointerAngle;
[SerializeField] private float pointerSpeed = 180f;
[SerializeField] private float minSpeed = 180f;
[SerializeField] private float maxSpeed = 450f;
[Header("Zone")]
[SerializeField] private float startZoneSize = 90f;
[SerializeField] private float zoneSize = 90f;
[SerializeField] private float minZoneSize = 30f;
[SerializeField] private float maxZoneSize = 120f;
[SerializeField] private float zoneCenter;
[Header("Catch Rules")]
[Tooltip("아이템 1개를 낚기 위해 필요한 성공 횟수입니다.")]
[SerializeField] private int requiredSuccesses = 3;
[Tooltip("아이템 1개를 낚는 동안 허용되는 실패 횟수입니다. 초과하면 낚싯줄이 끊어진 것으로 처리합니다.")]
[SerializeField] private int allowedFails = 3;
[Header("Pond Cleanup Rules")]
[Tooltip("연못을 맑게 만들기 위해 필요한 쓰레기성 아이템 수입니다. 쓰레기, 병, 봉지가 여기에 포함됩니다.")]
[SerializeField] private int requiredCleanupItems = 3;
[Tooltip("연못이 맑아진 뒤 다음 성공 낚시에서 기억의 조각을 확정으로 낚습니다.")]
[SerializeField] private bool guaranteeMemoryPieceAfterCleaned = true;
[Tooltip("StartFishing을 호출할 때 쓰레기 수거/연못 상태를 초기화합니다.")]
[SerializeField] private bool resetPondStateOnStart = true;
[Tooltip("낚시 세션이 진행 중일 때 StartFishing이 중복 호출되는 것을 막습니다.")]
[SerializeField] private bool preventRestartWhileSessionRunning = true;
[Header("Round Settings")]
[SerializeField] private float nextRoundDelay = 0.35f;
[SerializeField] private float nextCatchDelay = 2.0f;
[SerializeField] private bool randomDirectionEachRound = true;
[SerializeField] private bool resetDifficultyEachCatch = true;
[SerializeField] private bool startOnAwake = true;
[Header("Final Result Settings")]
[Tooltip("최종 결과를 잠깐 보여준 뒤 uiRoot를 자동으로 끕니다. uiRoot가 비어 있으면 동작하지 않습니다.")]
[SerializeField] private bool hideUIRootAfterFinalResult = false;
[SerializeField] private float finalResultShowTime = 1.5f;
[Header("Debug")]
[SerializeField] private bool showDebugLog = true;
private int successCount;
private int failCount;
private int cleanupItemCount;
private int totalCaughtItems;
// 임시 낚시 세션 카운트입니다. 나중에 공용 인벤토리와 연결할 때는 저장용으로 쓰지 말고 UI 표시용으로만 쓰세요.
private int sessionFishCount;
private int sessionTrashCount;
private int sessionMemoryPieceCount;
private int sessionCompassCount;
private bool clockwise = true;
private bool activeGame;
private bool inputLocked;
private bool fishingSessionRunning;
private bool pondCleaned;
private bool memoryPieceCollected;
private bool simpleReelCatchActive;
private bool simpleReelPondInitialized;
private float simpleReelStartLineLength;
private float simpleReelProgress;
private bool submitActionWasEnabled;
private bool resetActionWasEnabled;
private Coroutine nextRoundRoutine;
private Coroutine nextCatchRoutine;
private Coroutine finalResultRoutine;
public bool IsActiveGame => activeGame;
public bool IsFishingSessionRunning => fishingSessionRunning;
public bool IsPondCleaned => pondCleaned;
public bool IsMemoryPieceCollected => memoryPieceCollected;
public int SuccessCount => successCount;
public int FailCount => failCount;
public int CleanupItemCount => cleanupItemCount;
public int RequiredCleanupItems => requiredCleanupItems;
public int TotalCaughtItems => totalCaughtItems;
public int SessionFishCount => sessionFishCount;
public int SessionTrashCount => sessionTrashCount;
public int SessionMemoryPieceCount => sessionMemoryPieceCount;
public int SessionCompassCount => sessionCompassCount;
public FishingControlMode ControlMode => fishingControlMode;
public bool IsSimpleReelCatchActive => simpleReelCatchActive;
public float SimpleReelProgress => simpleReelProgress;
public event Action<FishingItemType, int> ItemCaught;
private void OnEnable()
{
RegisterInputAction(submitAction, OnSubmitAction, ref submitActionWasEnabled);
RegisterInputAction(resetAction, OnResetAction, ref resetActionWasEnabled);
}
private void OnDisable()
{
UnregisterInputAction(submitAction, OnSubmitAction, submitActionWasEnabled);
UnregisterInputAction(resetAction, OnResetAction, resetActionWasEnabled);
}
private void Start()
{
if (autoBindMissingReferences)
AutoBindMissingReferences();
ValidateRuntimeSettings();
if (startOnAwake)
{
StartFishing();
}
else
{
InitializeIdleUI();
}
}
private void Update()
{
if (!activeGame)
return;
if (fishingControlMode == FishingControlMode.SimpleReel)
{
UpdateSimpleReelCatch();
return;
}
if (inputLocked)
return;
RotatePointer();
}
private void OnValidate()
{
ValidateRuntimeSettings();
}
[ContextMenu("Auto Bind Fishing References")]
public void AutoBindMissingReferences()
{
Transform searchRoot = GetSearchRoot();
if (uiRoot == null)
{
Transform canvasTransform = FindTransformRecursive(searchRoot, "FishingCanvas");
if (canvasTransform != null)
uiRoot = canvasTransform.gameObject;
}
if (ui == null)
ui = searchRoot != null ? searchRoot.GetComponentInChildren<FishingGaugeUI>(true) : GetComponentInChildren<FishingGaugeUI>(true);
if (rewardSystem == null)
rewardSystem = searchRoot != null ? searchRoot.GetComponentInChildren<FishingRewardSystem>(true) : GetComponentInChildren<FishingRewardSystem>(true);
if (haptic == null)
haptic = searchRoot != null ? searchRoot.GetComponentInChildren<FishingHapticManager>(true) : GetComponentInChildren<FishingHapticManager>(true);
if (rodController == null)
rodController = searchRoot != null ? searchRoot.GetComponentInChildren<FishingRodVRController>(true) : GetComponentInChildren<FishingRodVRController>(true);
if (centerIconRotate == null)
{
Transform centerIconTransform = FindTransformRecursive(searchRoot, "CenterIcon");
if (centerIconTransform != null)
centerIconRotate = centerIconTransform.GetComponent<RotateUI>();
if (centerIconRotate == null && searchRoot != null)
centerIconRotate = searchRoot.GetComponentInChildren<RotateUI>(true);
}
}
private Transform GetSearchRoot()
{
Transform current = transform;
while (current.parent != null)
current = current.parent;
return current;
}
private Transform FindTransformRecursive(Transform current, string targetName)
{
if (current == null || string.IsNullOrEmpty(targetName))
return null;
if (NormalizeName(current.name) == NormalizeName(targetName))
return current;
for (int i = 0; i < current.childCount; i++)
{
Transform found = FindTransformRecursive(current.GetChild(i), targetName);
if (found != null)
return found;
}
return null;
}
private string NormalizeName(string value)
{
return value.Replace(" ", string.Empty)
.Replace("_", string.Empty)
.Replace("-", string.Empty)
.ToLowerInvariant();
}
private void RegisterInputAction(InputActionReference actionReference, System.Action<InputAction.CallbackContext> callback, ref bool wasEnabled)
{
if (actionReference == null || actionReference.action == null)
return;
wasEnabled = actionReference.action.enabled;
actionReference.action.performed += callback;
if (enableInputActionsManually && !wasEnabled)
actionReference.action.Enable();
}
private void UnregisterInputAction(InputActionReference actionReference, System.Action<InputAction.CallbackContext> callback, bool wasEnabled)
{
if (actionReference == null || actionReference.action == null)
return;
actionReference.action.performed -= callback;
if (enableInputActionsManually && !wasEnabled)
actionReference.action.Disable();
}
private void ValidateRuntimeSettings()
{
requiredSuccesses = Mathf.Max(1, requiredSuccesses);
allowedFails = Mathf.Max(1, allowedFails);
requiredCleanupItems = Mathf.Max(1, requiredCleanupItems);
minSpeed = Mathf.Max(1f, minSpeed);
maxSpeed = Mathf.Max(minSpeed, maxSpeed);
pointerSpeed = Mathf.Clamp(pointerSpeed, minSpeed, maxSpeed);
minZoneSize = Mathf.Clamp(minZoneSize, 1f, 360f);
maxZoneSize = Mathf.Clamp(maxZoneSize, minZoneSize, 360f);
startZoneSize = Mathf.Clamp(startZoneSize, minZoneSize, maxZoneSize);
zoneSize = Mathf.Clamp(zoneSize, minZoneSize, maxZoneSize);
nextRoundDelay = Mathf.Max(0f, nextRoundDelay);
nextCatchDelay = Mathf.Max(0f, nextCatchDelay);
finalResultShowTime = Mathf.Max(0f, finalResultShowTime);
catchCompleteLineLength = Mathf.Max(0.05f, catchCompleteLineLength);
simpleReelMinimumPullDistance = Mathf.Max(0.01f, simpleReelMinimumPullDistance);
pointerAngle = Normalize(pointerAngle);
zoneCenter = Normalize(zoneCenter);
cleanupItemCount = Mathf.Max(0, cleanupItemCount);
totalCaughtItems = Mathf.Max(0, totalCaughtItems);
}
private void InitializeIdleUI()
{
if (ui != null)
{
ui.InitializeFishingUI();
ui.UpdateCounter(0, requiredSuccesses, 0, allowedFails);
ui.UpdateFishingProgress(cleanupItemCount, requiredCleanupItems, pondCleaned, memoryPieceCollected, totalCaughtItems);
ui.UpdateInventoryUI(sessionFishCount, sessionTrashCount, sessionMemoryPieceCount, sessionCompassCount);
ui.SetPointerRotation(0f);
ui.SetZone(0f, startZoneSize);
ui.SetSimpleReelUIVisible(false);
ui.SetTimingGaugeVisible(fishingControlMode == FishingControlMode.TimingGauge);
}
if (centerIconRotate != null)
centerIconRotate.StopRotate();
}
public void StartFishing()
{
StartFishing(false);
}
public void StartFishing(bool forceRestart)
{
ValidateRuntimeSettings();
if (preventRestartWhileSessionRunning && fishingSessionRunning && !forceRestart)
{
if (showDebugLog)
Debug.Log("낚시 세션이 이미 진행 중이라 StartFishing 호출을 무시했습니다.");
return;
}
StopRunningRoutines();
if (uiRoot != null)
uiRoot.SetActive(true);
bool shouldResetPondState = forceRestart || resetPondStateOnStart;
if (fishingControlMode == FishingControlMode.SimpleReel && resetPondOnlyOnFirstSimpleReelStart)
shouldResetPondState = forceRestart || (resetPondStateOnStart && !simpleReelPondInitialized);
if (shouldResetPondState)
{
ResetPondState();
if (fishingControlMode == FishingControlMode.SimpleReel)
simpleReelPondInitialized = true;
}
fishingSessionRunning = true;
if (ui != null)
ui.InitializeFishingUI();
if (fishingControlMode == FishingControlMode.SimpleReel)
StartSimpleReelCatch();
else
StartNewCatchAttempt();
if (showDebugLog)
Debug.Log(fishingControlMode == FishingControlMode.SimpleReel ? "릴 감기 낚시 시작" : "기묘한 낚시터 시작");
}
public void StopFishing(bool hideUI = false)
{
StopRunningRoutines();
activeGame = false;
inputLocked = true;
fishingSessionRunning = false;
simpleReelCatchActive = false;
if (centerIconRotate != null)
centerIconRotate.StopRotate();
if (hideUI && uiRoot != null)
uiRoot.SetActive(false);
}
public void ResetFishing()
{
fishingSessionRunning = false;
simpleReelCatchActive = false;
simpleReelPondInitialized = false;
StartFishing(true);
}
public void ResetPondState()
{
successCount = 0;
failCount = 0;
cleanupItemCount = 0;
totalCaughtItems = 0;
sessionFishCount = 0;
sessionTrashCount = 0;
sessionMemoryPieceCount = 0;
sessionCompassCount = 0;
pondCleaned = false;
memoryPieceCollected = false;
simpleReelCatchActive = false;
simpleReelProgress = 0f;
simpleReelStartLineLength = 0f;
}
private void StartSimpleReelCatch()
{
if (rodController == null && autoBindMissingReferences)
AutoBindMissingReferences();
if (rodController == null)
{
Debug.LogWarning("[FishingGameManager] SimpleReel 모드에는 FishingRodVRController 참조가 필요합니다.", this);
activeGame = false;
inputLocked = true;
fishingSessionRunning = false;
return;
}
successCount = 0;
failCount = 0;
inputLocked = false;
activeGame = true;
simpleReelCatchActive = true;
float currentLineLength = Mathf.Max(rodController.CurrentLineLength, catchCompleteLineLength + simpleReelMinimumPullDistance);
simpleReelStartLineLength = currentLineLength;
simpleReelProgress = 0f;
if (centerIconRotate != null)
{
centerIconRotate.ResetRotation();
centerIconRotate.StopRotate();
}
if (ui != null)
{
ui.SetSimpleReelUIVisible(true);
if (hideTimingGaugeInSimpleReelMode)
ui.SetTimingGaugeVisible(false);
ui.UpdateSimpleReelUI(simpleReelProgress, rodController.CurrentLineLength, rodController.IsReeling);
ui.ShowPersistentResult("릴을 감아 끌어올려라!");
}
UpdateUI();
}
private void UpdateSimpleReelCatch()
{
if (!simpleReelCatchActive || rodController == null)
return;
float currentLineLength = rodController.CurrentLineLength;
float totalPullDistance = Mathf.Max(0.001f, simpleReelStartLineLength - catchCompleteLineLength);
float pulledDistance = simpleReelStartLineLength - currentLineLength;
simpleReelProgress = Mathf.Clamp01(pulledDistance / totalPullDistance);
if (ui != null)
ui.UpdateSimpleReelUI(simpleReelProgress, currentLineLength, rodController.IsReeling);
if (currentLineLength <= catchCompleteLineLength || simpleReelProgress >= 1f)
{
simpleReelCatchActive = false;
inputLocked = true;
if (haptic != null)
haptic.Perfect();
if (ui != null)
{
ui.UpdateSimpleReelUI(1f, currentLineLength, false);
ui.ShowPersistentResult("낚았다!");
}
CatchItemSuccess();
}
}
private void StartNewCatchAttempt()
{
successCount = 0;
failCount = 0;
inputLocked = false;
activeGame = true;
if (resetDifficultyEachCatch)
{
pointerAngle = 0f;
pointerSpeed = minSpeed;
zoneSize = startZoneSize;
}
if (randomDirectionEachRound)
clockwise = UnityEngine.Random.value > 0.5f;
if (centerIconRotate != null)
{
centerIconRotate.ResetRotation();
centerIconRotate.StartRotate();
}
RandomizeZone();
UpdateUI();
}
private void StopRunningRoutines()
{
if (nextRoundRoutine != null)
{
StopCoroutine(nextRoundRoutine);
nextRoundRoutine = null;
}
if (nextCatchRoutine != null)
{
StopCoroutine(nextCatchRoutine);
nextCatchRoutine = null;
}
if (finalResultRoutine != null)
{
StopCoroutine(finalResultRoutine);
finalResultRoutine = null;
}
}
private void RotatePointer()
{
float dir = clockwise ? 1f : -1f;
pointerAngle += dir * pointerSpeed * Time.deltaTime;
pointerAngle = Normalize(pointerAngle);
if (ui != null)
ui.SetPointerRotation(pointerAngle);
}
public void SubmitAttempt()
{
if (fishingControlMode == FishingControlMode.SimpleReel)
return;
if (!activeGame)
return;
if (inputLocked)
return;
inputLocked = true;
if (ui != null)
ui.HideControllerGuide();
ResultType result = EvaluateResult();
if (ui != null)
{
ui.ShowResultForType(result);
ui.FlashFeedbackForType(result);
}
switch (result)
{
case ResultType.Perfect:
OnPerfect();
break;
case ResultType.Good:
OnGood();
break;
case ResultType.Miss:
OnMiss();
break;
}
if (successCount >= requiredSuccesses)
{
CatchItemSuccess();
return;
}
if (failCount >= allowedFails)
{
FishingFail();
return;
}
nextRoundRoutine = StartCoroutine(NextRoundRoutine());
}
private ResultType EvaluateResult()
{
float delta = Mathf.Abs(Mathf.DeltaAngle(pointerAngle, zoneCenter));
float perfectRange = zoneSize * 0.2f;
float goodRange = zoneSize * 0.5f;
if (delta <= perfectRange)
return ResultType.Perfect;
if (delta <= goodRange)
return ResultType.Good;
return ResultType.Miss;
}
private void OnPerfect()
{
successCount++;
zoneSize *= 0.9f;
pointerSpeed *= 1.05f;
if (haptic != null)
haptic.Perfect();
if (showDebugLog)
Debug.Log("낚시 판정: Perfect");
ClampDifficulty();
UpdateUI();
}
private void OnGood()
{
successCount++;
zoneSize *= 0.95f;
pointerSpeed *= 1.02f;
if (haptic != null)
haptic.Good();
if (showDebugLog)
Debug.Log("낚시 판정: Good");
ClampDifficulty();
UpdateUI();
}
private void OnMiss()
{
failCount++;
zoneSize *= 1.05f;
pointerSpeed *= 0.95f;
if (haptic != null)
haptic.Miss();
if (showDebugLog)
Debug.Log("낚시 판정: Miss");
ClampDifficulty();
UpdateUI();
}
private IEnumerator NextRoundRoutine()
{
yield return new WaitForSeconds(nextRoundDelay);
if (!activeGame)
{
nextRoundRoutine = null;
yield break;
}
if (randomDirectionEachRound)
clockwise = UnityEngine.Random.value > 0.5f;
RandomizeZone();
UpdateUI();
inputLocked = false;
nextRoundRoutine = null;
}
private void CatchItemSuccess()
{
activeGame = false;
inputLocked = true;
if (centerIconRotate != null)
centerIconRotate.StopRotate();
bool forceMemoryPiece = pondCleaned && guaranteeMemoryPieceAfterCleaned && !memoryPieceCollected;
FishingRewardSystem.CatchResult catchResult;
if (rewardSystem != null)
catchResult = rewardSystem.RollCatch(pondCleaned, forceMemoryPiece);
else
catchResult = new FishingRewardSystem.CatchResult(FishingItemType.Fish, "생선", false, false);
ApplyCatchResult(catchResult);
}
private void ApplyCatchResult(FishingRewardSystem.CatchResult catchResult)
{
totalCaughtItems++;
AddToSessionCounts(catchResult.ItemType);
ItemCaught?.Invoke(catchResult.ItemType, 1);
bool pondJustCleaned = false;
string extraHint = string.Empty;
if (catchResult.CountsAsCleanupItem)
{
cleanupItemCount = Mathf.Min(cleanupItemCount + 1, requiredCleanupItems);
extraHint = $"연못 정화 {cleanupItemCount}/{requiredCleanupItems}";
if (!pondCleaned && cleanupItemCount >= requiredCleanupItems)
{
pondCleaned = true;
pondJustCleaned = true;
extraHint = "연못이 맑아졌다. 기억의 조각이 모습을 드러낸다.";
}
}
if (catchResult.IsMemoryPiece)
{
memoryPieceCollected = true;
extraHint = "잃어버린 기억의 일부다.";
}
UpdateUI();
if (ui != null)
{
ui.HighlightSlotForItem(catchResult.ItemType);
if (!catchResult.IsMemoryPiece)
ui.ShowCaughtItem(catchResult.ItemType, catchResult.DisplayName, catchResult.CountsAsCleanupItem, extraHint);
if (pondJustCleaned)
ui.ShowNotice("연못이 맑아졌다!\n기억의 조각이 모습을 드러냈다!");
// 기억의 조각 획득은 FinalResultPanel에서 크게 보여줍니다.
// NoticePanel까지 동시에 띄우면 ShowFinalResult에서 바로 숨겨져 연출이 겹칠 수 있습니다.
}
if (showDebugLog)
Debug.Log($"{catchResult.DisplayName}을(를) 낚았다!");
if (catchResult.IsMemoryPiece)
{
FishingSuccess();
return;
}
if (fishingControlMode == FishingControlMode.SimpleReel)
{
fishingSessionRunning = false;
simpleReelCatchActive = false;
if (returnBobberAfterSimpleReelCatch && rodController != null)
rodController.ReturnBobberToRest();
return;
}
nextCatchRoutine = StartCoroutine(NextCatchRoutine());
}
private IEnumerator NextCatchRoutine()
{
yield return new WaitForSeconds(nextCatchDelay);
if (memoryPieceCollected)
{
nextCatchRoutine = null;
yield break;
}
StartNewCatchAttempt();
nextCatchRoutine = null;
}
private void AddToSessionCounts(FishingItemType itemType)
{
switch (itemType)
{
case FishingItemType.Fish:
sessionFishCount++;
break;
case FishingItemType.Trash:
case FishingItemType.Bottle:
case FishingItemType.PlasticBag:
sessionTrashCount++;
break;
case FishingItemType.MemoryPiece:
sessionMemoryPieceCount++;
break;
case FishingItemType.OldCompass:
sessionCompassCount++;
break;
}
}
private void ClampDifficulty()
{
zoneSize = Mathf.Clamp(zoneSize, minZoneSize, maxZoneSize);
pointerSpeed = Mathf.Clamp(pointerSpeed, minSpeed, maxSpeed);
}
private void RandomizeZone()
{
zoneCenter = UnityEngine.Random.Range(0f, 360f);
}
private void UpdateUI()
{
if (ui == null)
return;
if (fishingControlMode == FishingControlMode.SimpleReel)
{
ui.SetSimpleReelUIVisible(activeGame || simpleReelCatchActive);
if (hideTimingGaugeInSimpleReelMode)
ui.SetTimingGaugeVisible(false);
ui.UpdateFishingProgress(cleanupItemCount, requiredCleanupItems, pondCleaned, memoryPieceCollected, totalCaughtItems);
ui.UpdateInventoryUI(sessionFishCount, sessionTrashCount, sessionMemoryPieceCount, sessionCompassCount);
float currentLineLength = rodController != null ? rodController.CurrentLineLength : 0f;
bool isReeling = rodController != null && rodController.IsReeling;
ui.UpdateSimpleReelUI(simpleReelProgress, currentLineLength, isReeling);
return;
}
ui.SetSimpleReelUIVisible(false);
ui.SetTimingGaugeVisible(true);
ui.SetZone(zoneCenter, zoneSize);
ui.UpdateCounter(successCount, requiredSuccesses, failCount, allowedFails);
ui.UpdateFishingProgress(cleanupItemCount, requiredCleanupItems, pondCleaned, memoryPieceCollected, totalCaughtItems);
ui.UpdateInventoryUI(sessionFishCount, sessionTrashCount, sessionMemoryPieceCount, sessionCompassCount);
}
private void FishingSuccess()
{
activeGame = false;
inputLocked = true;
fishingSessionRunning = false;
simpleReelCatchActive = false;
if (centerIconRotate != null)
centerIconRotate.StopRotate();
UpdateUI();
if (ui != null)
ui.ShowFinalResult("기억의 조각 획득!\n기묘한 낚시터 클리어!");
if (showDebugLog)
Debug.Log("기묘한 낚시터 클리어");
BeginFinalResultRoutineIfNeeded();
}
private void FishingFail()
{
activeGame = false;
inputLocked = true;
fishingSessionRunning = false;
simpleReelCatchActive = false;
if (centerIconRotate != null)
centerIconRotate.StopRotate();
if (ui != null)
ui.ShowFinalResult("낚싯줄이 끊어졌다!\n다시 천천히 낚아보자.");
if (showDebugLog)
Debug.Log("낚시 실패: 낚싯줄 끊어짐");
BeginFinalResultRoutineIfNeeded();
}
private void BeginFinalResultRoutineIfNeeded()
{
if (!hideUIRootAfterFinalResult || uiRoot == null)
return;
if (finalResultRoutine != null)
StopCoroutine(finalResultRoutine);
finalResultRoutine = StartCoroutine(HideUIRootAfterFinalResultRoutine());
}
private IEnumerator HideUIRootAfterFinalResultRoutine()
{
yield return new WaitForSeconds(finalResultShowTime);
if (uiRoot != null)
uiRoot.SetActive(false);
finalResultRoutine = null;
}
private float Normalize(float angle)
{
angle %= 360f;
if (angle < 0f)
angle += 360f;
return angle;
}
private void OnSubmitAction(InputAction.CallbackContext context)
{
SubmitAttempt();
}
private void OnResetAction(InputAction.CallbackContext context)
{
ResetFishing();
}
}