From 44b5f9ebb18f41af6b7eada11dc0a77f6ce70510 Mon Sep 17 00:00:00 2001 From: Prown0 <100746335+Prown0@users.noreply.github.com> Date: Fri, 20 Dec 2024 02:24:20 +0900 Subject: [PATCH] Refactor `AnomalyManager` --- 302/Assets/Scripts/AnomalyManager.cs | 280 ++++++++++++++++++--------- 302/Assets/Scripts/GameManager.cs | 46 ++--- 302/Assets/Scripts/SCH_Random.cs | 33 +++- 302/Assets/Scripts/SlideManager.cs | 1 + 4 files changed, 245 insertions(+), 115 deletions(-) diff --git a/302/Assets/Scripts/AnomalyManager.cs b/302/Assets/Scripts/AnomalyManager.cs index 4e0e015..4cbb9a2 100644 --- a/302/Assets/Scripts/AnomalyManager.cs +++ b/302/Assets/Scripts/AnomalyManager.cs @@ -1,120 +1,216 @@ using UnityEngine; -using System.Collections; -using System.Collections.Generic; -using UnityEngine.SceneManagement; -public class AnomalyManager : MonoBehaviour +public class AnomalyManager : AbstractStageObserver { + /********** + * fields * + **********/ + + // 이상현상 컨트롤러 프리팹 + public GameObject[] prefabs; + + // 개수 + public int numStage; + public int numAnomaly; + + // 테스트용 수치 + public int testMode; + public int testAnomalyID; + + // 난수 + private SCH_Random _random; + + // 이상현상 색인 배열 + private int[] _anomalyList; + + /************** + * properties * + **************/ + + // 클래스 이름 + public override string Name { get; } = "AnomalyManager"; + + // 클래스 인스턴스 public static AnomalyManager Instance { get; private set; } - public GameObject[] anomalyPrefabs; // Anomaly1Manager ~ Anomaly31Manager 프리팹 보관용 리스트 - private List anomalyList = new List(); // 이상현상 리스트 - private const int AnomalyCount = 8; // 사이즈 8 - private System.Random random = new System.Random(); - public bool checkSpecificAnomaly; - public bool checkIntersect; - public int SpecificAnomalyNum; - public GameObject currentAnomalyInstance; // 현재 활성화된 이상현상 인스턴스 - // 하나의 AnomalyManager만 보장 - private void Awake() + + /************************************* + * implementation: AbstractBehaviour * + *************************************/ + + // `Awake` 메시지 용 메서드 + protected override bool Awake_() { - if (Instance == null) - { + bool res = false; + + if (Instance == null) { + Log($"`Instance` has not been set => set `Instance` as `{Name}`"); Instance = this; DontDestroyOnLoad(gameObject); - GenerateAnomalyList(); - } - else - { + res = base.Awake_(); + } else { + Log($"`Instance` has already been set => destroy `{gameObject.name}`"); Destroy(gameObject); } - // 프리팹이 설정되어 있는지 확인 - if (anomalyPrefabs == null || anomalyPrefabs.Length == 0) - { - Debug.LogError("Anomaly prefabs are missing! Please assign prefabs in the Inspector."); + + return res; + } + + // 필드를 초기화하는 메서드 + protected override bool InitFields() + { + bool res = base.InitFields(); + + _random = new SCH_Random(); + + _anomalyList = new int[numStage]; + + return res; + } + + /***************************************** + * implementation: AbstractStageObserver * + *****************************************/ + + // 단계 변경 시 불리는 메서드 + public override bool UpdateStage() + { + bool res = base.UpdateStage(); + + if (GameManager.Instance.Stage == 1) { + Log("Call `GenerateList` begin"); + if (GenerateList()) { + Log("Call `GenerateList` sucess"); + } else { + Log("Call `GenerateList` failed", mode: 1); + res = false; + } } + + return res; } - // 이상현상 리스트 생성 - private void GenerateAnomalyList() + + /*************** + * new methods * + ***************/ + + // 이상현상 컨트롤러를 생성 후 반환하는 메서드 + public AbstractAnomalyObject GetAnomalyController() { - anomalyList.Clear(); - HashSet uniqueNumbers = new HashSet(); - bool hasHighAnomaly = false; + GameObject obj; + AbstractAnomalyObject controller = null; + int stage = GameManager.Instance.Stage; - for (int i = 0; i < AnomalyCount; i++) - { - int anomaly; - if (!checkSpecificAnomaly) - { - do - { - anomaly = GenerateRandomAnomaly(); - } while (i > 0 && anomaly == anomalyList[i - 1] // 연속 방지 - || (anomaly != 0 && !uniqueNumbers.Add(anomaly))); // 중복 방지 - - if (anomaly >= 21) - { - hasHighAnomaly = true; - } + if (stage == 0) { + obj = Instantiate(prefabs[0]); + if (obj != null) { + controller = obj.GetComponent(); + } else { + Log($"Find `AbstractAnomalyObject` for {obj.name} failed", mode: 2); } - else - { - if(checkIntersect && i%2==1) anomaly = 0; - else anomaly = SpecificAnomalyNum; + } else if (stage > 0 && stage <= numStage) { + obj = Instantiate(prefabs[_anomalyList[stage - 1]]); + if (obj != null) { + controller = obj.GetComponent(); + } else { + Log($"Find `AbstractAnomalyObject` for {obj.name} failed", mode: 2); } - anomalyList.Add(anomaly); + } else { + Log($"Invalid stage: {stage}", mode: 2); } - if(!hasHighAnomaly) - { - int randomIndex = random.Next(0, AnomalyCount); - int highAnomaly = random.Next(21, 32); - anomalyList[randomIndex] = highAnomaly; - } - Debug.Log($"[AnomalyManager] Generated Anomaly List: {string.Join(", ", anomalyList)}"); + return controller; } - // 20% 확률로 0, 나머지 확률로 1~31의 이상현상을 생성 - private int GenerateRandomAnomaly() + + // 모든 이상현상 컨트롤러가 `AbstractAnomalyObject`의 서브클래스이기 전에 쓸 메서드 + public void StartAnomalyController() { - return random.Next(0, 100) < 20 ? 0 : random.Next(1, 31); + int stage = GameManager.Instance.Stage; + + if (stage == 0) { + Instantiate(prefabs[0]); + } else if (stage > 0 && stage <= numStage) { + Instantiate(prefabs[_anomalyList[stage - 1]]); + } else { + Log($"Invalid stage: {stage}", mode: 2); + } } - // 현재 스테이지에 맞는 이상현상을 로드 - public void CheckAndInstantiateAnomaly() + + // 이상현상 색인 리스트를 생성하는 메서드 + private bool GenerateList() { - int stageIndex = GameManager.Instance.GetCurrentStage() - 1; - if (stageIndex >= anomalyList.Count) - { - Debug.LogError("Invalid stage index in anomaly list."); - return; - } - else if (stageIndex == -1) - { - Debug.Log($"[AnomalyManager] Current Stage : {GameManager.Instance.GetCurrentStage()}"); - currentAnomalyInstance = Instantiate(anomalyPrefabs[0]); - return; + bool res = true; + + switch (testMode) { + case 0: + GenerateAnomalyListNormal(); + break; + case 1: + GenerateAnomalyListTest1(); + break; + case 2: + GenerateAnomalyListTest2(); + break; + default: + Log("Generate `_anomalyList` failed", mode: 1); + res = false; + break; } - int anomaly = anomalyList[stageIndex]; - Debug.Log($"[AnomalyManager] Current Stage: {GameManager.Instance.GetCurrentStage()}, Anomaly Number: {anomaly}"); - // 특정 인덱스에 해당하는 이상현상 프리팹만 인스턴스화 - if (anomaly >= 0 && anomaly < anomalyPrefabs.Length && anomalyPrefabs[anomaly] != null) - { - currentAnomalyInstance = Instantiate(anomalyPrefabs[anomaly]); - if (currentAnomalyInstance == null) - { - Debug.LogError("Failed to instantiate anomaly prefab."); - } - else - { - Debug.Log("Anomaly instantiated successfully."); + + return res; + } + + // 일반 이상현상 색인 리스트를 생성하는 메서드 + private void GenerateAnomalyListNormal() + { + bool hasHighAnomaly = false; + + while (!hasHighAnomaly) { + int idxLastZero = -2; + + _anomalyList = _random.Permutation(numAnomaly, numStage - 1); + for (int idx = 0; idx < numStage; idx++) { + if (idx - idxLastZero > 1 && _random.UniformDist(0.0, 1.0) < 0.2) { + // 직전 단계가 제0번이 아닌 경우 20 %의 확률로 제0번 생성 + _anomalyList[idx] = 0; + idxLastZero = idx; + } else { + // 0번을 위한 색인 조정 + _anomalyList[idx] += 1; + + // 적대적 이상현상 포함 확인 + if (_anomalyList[idx] > 20) { + hasHighAnomaly = true; + } + } } } - else - { - Debug.LogError($"Anomaly prefab for anomaly {anomaly} is missing or not assigned in the prefab list."); + + Log($"Generate `_anomalyList` success: [{string.Join(", ", _anomalyList)}]"); + } + + // 테스트용 이상현상 색인 리스트를 생성하는 메서드 1 + private void GenerateAnomalyListTest1() + { + for (int idx = 0; idx < numStage; idx++) { + // 원하는 이상현상이 계속 나오도록 + _anomalyList[idx] = testAnomalyID; } + + Log($"Generate `_anomalyList` success: [{string.Join(", ", _anomalyList)}]"); } - // 스테이지 실패 시 이상현상 리스트 재 생성 - public void ResetAnomaliesOnFailure() + + // 테스트용 이상현상 색인 리스트를 생성하는 메서드 2 + private void GenerateAnomalyListTest2() { - GenerateAnomalyList(); + for (int idx = 0; idx < numStage; idx++) { + // 원하는 이상현상과 제0번이 번갈아 나오도록 + if (idx % 2 == 0) { + _anomalyList[idx] = testAnomalyID; + } else { + _anomalyList[idx] = 0; + } + } + + Log($"Generate `_anomalyList` success: [{string.Join(", ", _anomalyList)}]"); } -} \ No newline at end of file +} diff --git a/302/Assets/Scripts/GameManager.cs b/302/Assets/Scripts/GameManager.cs index 3ac7f11..b6fed04 100644 --- a/302/Assets/Scripts/GameManager.cs +++ b/302/Assets/Scripts/GameManager.cs @@ -20,17 +20,20 @@ public enum GameState { Playing, Ending, + Paused, // deprecated states Sleeping, GameOver, - Paused } /********** * fields * **********/ + // 단계 수 + public int numStage; + // 이상현상 컨트롤러 /* private AbstractAnomalyObject _anomalyController; */ @@ -136,7 +139,7 @@ public bool Sleep() Stage = 1; } - if (Stage <= 8) { + if (Stage <= numStage) { Log("Call `StartStage` asynchronously"); StartCoroutine(StartStage()); } else { @@ -191,13 +194,10 @@ private IEnumerator StartStage() for (int idx = 0; idx < observers.Length; idx++) { observers[idx].UpdateStage(); } - if (Stage == 1) { - AnomalyManager.Instance.ResetAnomaliesOnFailure(); - } /* _anomalyController = AnomalyManager.Instance.GetAnomalyController(); _anomalyController.StartAnomaly(); */ - AnomalyManager.Instance.CheckAndInstantiateAnomaly(); + AnomalyManager.Instance.StartAnomalyController(); /* Log("Call `PlayerManager.WakeUp` begin"); if (PlayerManager.Instance.WakeUp()) { @@ -220,6 +220,24 @@ private bool StartEnding() return res; } + // 일시정지하는 메서드 + public void PauseGame() + { + if (State == GameState.Playing) { + State = GameState.Paused; + Time.timeScale = 0.0f; + } + } + + // 재개하는 메서드 + public void ResumeGame() + { + if (State == GameState.Paused) { + State = GameState.Playing; + Time.timeScale = 1.0f; + } + } + /********************** * deprecated methods * **********************/ @@ -259,20 +277,4 @@ public void RestartGame() Log("Call `StartStage` asynchronously"); StartCoroutine(StartStage()); } - - public void PauseGame() - { - if (State == GameState.Playing) { - State = GameState.Paused; - Time.timeScale = 0; - } - } - - public void ResumeGame() - { - if (State == GameState.Paused) { - State = GameState.Playing; - Time.timeScale = 1; - } - } } diff --git a/302/Assets/Scripts/SCH_Random.cs b/302/Assets/Scripts/SCH_Random.cs index 2e1e883..2d551a5 100644 --- a/302/Assets/Scripts/SCH_Random.cs +++ b/302/Assets/Scripts/SCH_Random.cs @@ -49,7 +49,7 @@ public double LogNormalDist(double mu, double sigma) } // 정규 분포 - public double NormalDist(double mu = 0.0, double sigma = 0.0) + public double NormalDist(double mu = 0.0, double sigma = 1.0) { double u1, u2, z, zz; @@ -66,6 +66,31 @@ public double NormalDist(double mu = 0.0, double sigma = 0.0) return mu + z * sigma; } + // 순열 + public int[] Permutation(int n, int r) + { + int[] candidates = new int[n]; + int[] result = new int[r]; + + for (int i = 0; i < n; i++) { + candidates[i] = i; + } + + for (int i = 0; i < r; i++) { + int index = Next(i, n); + + result[i] = candidates[index]; + if (index != i) { + int tmp = candidates[i]; + + candidates[i] = candidates[index]; + candidates[index] = tmp; + } + } + + return result; + } + // 삼각 분포 public double TriangularDist(double low = 0.0, double high = 1.0, double mode = 0.5) { @@ -89,4 +114,10 @@ public double TriangularDist(double low = 0.0, double high = 1.0, double mode = return low + (high - low) * Math.Sqrt(u * c); } + + // 균등 분포 + public double UniformDist(double a, double b) + { + return a + (b - a) * Sample(); + } } diff --git a/302/Assets/Scripts/SlideManager.cs b/302/Assets/Scripts/SlideManager.cs index 78c2afa..4504102 100644 --- a/302/Assets/Scripts/SlideManager.cs +++ b/302/Assets/Scripts/SlideManager.cs @@ -84,6 +84,7 @@ protected override bool InitFields() * implementation: AbstractStageObserver * *****************************************/ + // 단계 변경 시 불리는 메서드 public override bool UpdateStage() { int stage = GameManager.Instance.GetCurrentStage();