-
최적화 - 14 (코드수정 #InvokeRepeating)Galaxy Ball/5. 최적화 2024. 11. 7. 20:25
이번에 해볼것은 싱글플레이 패배창을 관리하는 SPFailSceneManager코드이다
using System.Collections; using UnityEngine; using UnityEngine.SceneManagement; public class SPFailSceneManager : MonoBehaviour { void Update() { StageGameManager stageGameManager = FindObjectOfType<StageGameManager>(); int randomnumber = Random.Range(1, 6); if (Input.GetMouseButtonDown(0)) { RaycastHit2D hit = Physics2D.Raycast(Camera.main.ScreenToWorldPoint(Input.mousePosition), Vector2.zero); if (hit.collider != null && hit.collider.gameObject.name == "GoStage") { if (stageGameManager.StageClearID <= 5) { SceneManager.LoadScene("Stage"); } else if (stageGameManager.StageClearID >= 6) { SceneManager.LoadScene("Main Stage"); } } if (hit.collider != null && hit.collider.gameObject.name == "GoMenu") { SceneManager.LoadScene("Start Scene"); } if (hit.collider != null && hit.collider.gameObject.name == "Retry") { SceneManager.LoadScene("Story-InGame"); } if (hit.collider != null && hit.collider.gameObject.name == "Random") { switch(randomnumber) { case 1: SceneManager.LoadScene("Example Scene"); break; case 2: SceneManager.LoadScene("Design Scene"); break; case 3: SceneManager.LoadScene("1-1 Intro Scene"); break; case 4: SceneManager.LoadScene("Credit Scene"); break; case 5: SceneManager.LoadScene("ChallengeScene"); break; } } } } }
업데이트 안에 모든걸 때려넣은 엉망진창 코드...
using UnityEngine; using UnityEngine.SceneManagement; public class SPFailSceneManager : MonoBehaviour { private StageGameManager stageGameManager; void Start() { // Start에서 StageGameManager를 한 번만 찾도록 변경 stageGameManager = FindObjectOfType<StageGameManager>(); } void Update() { if (Input.GetMouseButtonDown(0)) { // 마우스 클릭 위치를 기준으로 RaycastHit2D 실행 RaycastHit2D hit = Physics2D.Raycast(Camera.main.ScreenToWorldPoint(Input.mousePosition), Vector2.zero); if (hit.collider == null) return; // 히트된 오브젝트의 이름에 따라 처리 string hitName = hit.collider.gameObject.name; if (hitName == "GoStage") { LoadStage(); } else if (hitName == "GoMenu") { SceneManager.LoadScene("Start Scene"); } else if (hitName == "Retry") { SceneManager.LoadScene("Story-InGame"); } else if (hitName == "Random") { LoadRandomScene(); } } } // 스테이지를 로드하는 함수 private void LoadStage() { if (stageGameManager != null) { if (stageGameManager.StageClearID <= 5) { SceneManager.LoadScene("Stage"); } else if (stageGameManager.StageClearID >= 6) { SceneManager.LoadScene("Main Stage"); } } else { Debug.LogWarning("StageGameManager를 찾을 수 없습니다."); } } // 랜덤한 씬을 로드하는 함수 private void LoadRandomScene() { int randomnumber = Random.Range(1, 6); switch (randomnumber) { case 1: SceneManager.LoadScene("Example Scene"); break; case 2: SceneManager.LoadScene("Design Scene"); break; case 3: SceneManager.LoadScene("1-1 Intro Scene"); break; case 4: SceneManager.LoadScene("Credit Scene"); break; case 5: SceneManager.LoadScene("ChallengeScene"); break; } } }
보기 좋게, 그리고 update문안에 모든걸 쓸데없이 다 때려넣은 것들을 줄였다
그 다음은 싱글플레이 안에서 아이템을 생성하는 스크립트이다
using UnityEngine; public class SPRandomGenerate : MonoBehaviour { BGMControl bGMControl; public GameObject[] spherePrefabs; public GameObject background; public float minSpawnTime = 7f; public float maxSpawnTime = 12f; private float nextSpawnTime; private Collider2D backgroundCollider; private StageGameManager stageGameManager; void Start() { bGMControl = FindObjectOfType<BGMControl>(); nextSpawnTime = Time.time + Random.Range(minSpawnTime, maxSpawnTime); backgroundCollider = background.GetComponent<Collider2D>(); stageGameManager = FindObjectOfType<StageGameManager>(); if (stageGameManager == null) { Debug.LogError("StageGameManager�� ã�� �� �����ϴ�."); } } void Update() { if (Time.time >= nextSpawnTime) { SpawnSphere(); nextSpawnTime = Time.time + Random.Range(minSpawnTime, maxSpawnTime); } } void SpawnSphere() { Vector2 min = backgroundCollider.bounds.min; Vector2 max = backgroundCollider.bounds.max; Vector3 randomPosition = new Vector3(Random.Range(min.x, max.x), Random.Range(min.y, max.y), 0f); int maxIndex; if (stageGameManager.StageClearID <= 6) { return; } else if (stageGameManager.StageClearID <= 10) { maxIndex = 0; } else if (stageGameManager.StageClearID <= 17) { maxIndex = 1; } else if (stageGameManager.StageClearID <= 25) { maxIndex = 2; } else if (stageGameManager.StageClearID <= 32) { maxIndex = 3; } else if (stageGameManager.StageClearID <=44) { maxIndex = 4; } else if(stageGameManager.StageClearID == 45) { maxIndex = 3; } else if(stageGameManager.StageClearID == 65) { maxIndex = 3; } else { maxIndex = 4; } int prefabIndex = Random.Range(0, maxIndex); bGMControl.SoundEffectPlay(2); Instantiate(spherePrefabs[prefabIndex], randomPosition, Quaternion.identity); } }
우선 update에서 매 프레임마다 SpawnSphere 메서드를 실행중이다. 이러면 성능에 지장이 간다
기왕이면 update문 없이 코드를 짜고 싶은데 7~12초 사이에 매번 아이템을 랜덤으로 생성해야하기에
이걸 어떻게하면 update없이 짤 수 있을까 싶지만 그럴때 사용하는 기능이 있다
using UnityEngine; public class SPRandomGenerate : MonoBehaviour { private BGMControl bGMControl; public GameObject[] spherePrefabs; public GameObject background; public float minSpawnTime = 7f; public float maxSpawnTime = 12f; private Collider2D backgroundCollider; private StageGameManager stageGameManager; private int maxIndex = 0; void Start() { // 필요한 오브젝트 캐싱 bGMControl = FindObjectOfType<BGMControl>(); stageGameManager = FindObjectOfType<StageGameManager>(); backgroundCollider = background.GetComponent<Collider2D>(); if (stageGameManager == null) { Debug.LogError("StageGameManager를 찾을 수 없습니다."); return; } SetMaxIndex(); // 지정된 간격으로 SpawnSphere를 호출 float initialSpawnTime = Random.Range(minSpawnTime, maxSpawnTime); InvokeRepeating("SpawnSphere", initialSpawnTime, initialSpawnTime); } // StageClearID에 따른 maxIndex 설정 private void SetMaxIndex() { float stageID = stageGameManager.StageClearID; if (stageID <= 6) { CancelInvoke("SpawnSphere"); // StageClearID가 6 이하일 때는 스폰을 중지 } else if (stageID <= 10) { maxIndex = 0; } else if (stageID <= 17) { maxIndex = 1; } else if (stageID <= 21) { maxIndex = 2; } else if (stageID <= 34) { maxIndex = 3; } else if (stageID <= 44) { maxIndex = 4; } else if (stageID == 45 || stageID == 64 || stageID == 65) { maxIndex = 3; } else { maxIndex = 4; } } // 구체 생성 private void SpawnSphere() { if (maxIndex < 0) return; // 스폰 조건을 충족하지 않으면 종료 // 배경 내의 무작위 위치 계산 Vector2 min = backgroundCollider.bounds.min; Vector2 max = backgroundCollider.bounds.max; Vector3 randomPosition = new Vector3(Random.Range(min.x, max.x), Random.Range(min.y, max.y), 0f); // 구체 프리팹 중 하나를 무작위로 선택하여 인스턴스화 int prefabIndex = Random.Range(0, maxIndex + 1); if (bGMControl.SoundEffectSwitch) { bGMControl.SoundEffectPlay(2); } Instantiate(spherePrefabs[prefabIndex], randomPosition, Quaternion.identity); // 다음 스폰 간격을 랜덤 설정 float nextSpawnTime = Random.Range(minSpawnTime, maxSpawnTime); CancelInvoke("SpawnSphere"); InvokeRepeating("SpawnSphere", nextSpawnTime, nextSpawnTime); Debug.Log("MaxIndex : " + maxIndex); } }
InvokeRepeating("SpawnSphere", initialSpawnTime, initialSpawnTime);
바로 InvokeRepeating이라는 기능이다.
더보기더보기1. Update vs InvokeRepeating
- Update: Update는 프레임마다 실행됩니다. 즉, 매 프레임마다 Time.time과 nextSpawnTime을 비교하게 되어, 스폰 조건을 확인하지 않아도 무조건 실행되므로 반복적이고 불필요한 계산이 발생할 수 있습니다.
- InvokeRepeating: 특정 주기마다 메서드를 호출하므로, 필요한 시점에만 스폰 로직을 실행하게 됩니다. InvokeRepeating은 지정된 시간 간격마다 실행되기 때문에 불필요한 호출을 줄일 수 있고, 성능을 더 효율적으로 관리할 수 있습니다.
2. 메모리 관리 및 CPU 부담
- Update는 게임 오브젝트가 활성 상태인 한 지속적으로 실행되므로, CPU 연산 부담이 누적될 수 있습니다.
- InvokeRepeating은 지정된 간격 동안 CPU를 활용하지 않고도 로직을 실행할 수 있어, 최적화에 유리합니다.
3. 간단한 스폰 로직에 최적
- 스폰처럼 반복 호출이 필요한 작업이라면, InvokeRepeating이 적합합니다. 특히 주기가 명확하게 정해진 경우, InvokeRepeating을 사용하면 단순 반복 연산이 줄어들어 더 효율적입니다.
즉, 간격 기반의 호출이 필요하거나 주기적인 처리가 필요하다면, InvokeRepeating을 사용하는 것이 훨씬 효율적이다
이럴때 사용하면 가장 좋은 기능이며 세트로 알아둬야할게
CancelInvoke("SpawnSphere");
조건이나 주기에 따라 캔슬도 가능하다.
추가로 게임의 밸런스를 위해 StageID가 45, 64, 65일땐 Invincible 아이템은 생성하지 않도록 해주었다
참고로 저 3개의 스테이지는 중간보스 & 최종보스가 등장하는 스테이지이다.
그 다음은 스테이지맵에서 네비게이션 역할을 하는 스크립트이다
void FixedUpdate() { // Clearhere 오브젝트를 찾지 못했을 경우 한 번만 찾기 시도 if (clearhereObject == null) { clearhereObject = GameObject.Find("Clearhere(Clone)"); if (clearhereObject != null) { lastClearherePosition = clearhereObject.transform.position; UpdateActivation(); } }
간략하게 핵심만 보고 넘어가겠다
1. 우선 update > fixedupdate로 바꾸어주었다. 근데 이렇게 바꿔도 기능이 복잡하질 않으니 큰 변화는 없다
2. 원래는 매 프레임마다 찾았던 다음 목적지 Clearhere를 FixedUpdate문 안에서 한번만 찾도록 해주었다
그 다음은 스토리에 관련된 코드들을 수정해보려고 한다
누가 말해준건 아니지만 스토리와 관련된 코드들은 중요할 것 같다.
우선 스테이지맵을 돌아다니는 내내 계속해서 활성화 되어있기 때문에 특히 update같이 매 프레임마다
검사하는 방식은 굉장히 최적화에 치명적이다
void Update() { if (showText != null && stageGameManager.StageClearID == 1) { if (showText.logTextIndex == 17) { stageBallController.enabled = true; } if (showText.logTextIndex < 41) { Stage.SetActive(false); } if (showText.logTextIndex >= 42) { Stage.SetActive(true); Clearhere.gameObject.SetActive(true); } } if (showText != null && stageGameManager.StageClearID == 5.5) { if (showText.logTextIndex == 4) { StartCoroutine(IncreaseCameraSize(mainCamera, 112, 5.5f)); } if (showText.logTextIndex == 8) { ContinuousRandomMovement[] randomMovements = FindObjectsOfType<ContinuousRandomMovement>(); foreach (ContinuousRandomMovement randomMovement in randomMovements) { randomMovement.enabled = false; } } if (showText.logTextIndex == 23) { ContinuousRandomMovement[] randomMovements = FindObjectsOfType<ContinuousRandomMovement>(); foreach (ContinuousRandomMovement randomMovement in randomMovements) { randomMovement.enabled = true; } StartCoroutine(HandleCameraAndFadeIn(mainCamera, 15, 7f)); } } }
이게 Ch1story update메서드안에 들어간 코드들이다
매 프레임마다 이렇게나 많은 조건문을 실행하고 있었으니 충분히 지장이 갈만하다
switch (stageGameManager.StageClearID) { case 1: stageBallController.enabled = false; textManager.GiveMeTextId(1); showText = FindAnyObjectByType<ShowText>(); StartCoroutine(HandleStage1()); break; case 2: textManager.GiveMeTextId(2); break; case 5.5f: Destroy(Stage); textManager.GiveMeTextId(3); showText = FindAnyObjectByType<ShowText>(); StartCoroutine(HandleStage5_5()); break; case 6: Destroy(Stage); RemainTime.SetActive(true); break; }
그래서 update 메서드를 아예 없애주었다..!
대신 switch문으로 조건에 만족할때만 코루틴을 돌리도록 해주었다.
이렇게 해주면 매 프레임마다 수많은 조건문들을 검사하지 않아도 될것이다
근데 문제는 다른 스토리 관련 스크립트에서도 다 이런 식이라는것...
void Update() { showText = FindObjectOfType<ShowText>(); if (showText != null && stageGameManager.StageClearID == 65) { if (showText.logTextIndex == 8 && !isZooming) { StartCoroutine(SmoothZoom(5f, 1700f)); } if (showText.logTextIndex == 12) { StartCoroutine(ExecuteAfterDelay(4f)); } if (showText.logTextIndex == 28) { StartCoroutine(LoadSceneAfterDelay(4f, "Story-InGame")); } } }
업데이트로 매 프레임마다 FindobjectofType을 실행중이었다.
이건 정말 모바일 환경에서 프레임에 지장을 줄 수 있는 위험한 코드로 전부 수정해주었다
'Galaxy Ball > 5. 최적화' 카테고리의 다른 글
최적화 - 16 (최종 마무리 #텍스쳐 낮추기) (0) 2024.11.19 최적화 - 15 (코드 수정 마무리) (7) 2024.11.13 최적화 - 13 (코드 수정 #SmoothDamp, #LateUpdate, #Awake) (0) 2024.11.05 최적화 - 12 (코드수정 #Canvas Group, #DOTween) (2) 2024.10.30 최적화 - 11 (설정수정★) (1) 2024.10.17