ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 최적화 - 10 (설정 수정★)
    Galaxy Ball/5. 최적화 2024. 10. 9. 23:13

    안좋은 소식이 있다.. 최근에 핸드폰에다 다시 한번 빌드를 해본 결과....달라진게 없다...

    확실히 컴 유니티에서는 부드러워진게 체감이 되길래 진짜 끝난줄 알았는데 다시 원점이다

     

    하지만 낙심하지말고 다시 방법을 찾을때까지 해보자!

     

    지금부터는 순서 같은것 없이 최적화에 조금이라도 도움이 될 것 같으면 실행에 옮겨보겠다

     

    그러고보니 최적화를 하고 나서 만든 실행파일은 찍어둔 로그들을 하나도 제거하지 않았었다

     

     

    상황에 따라 Debug.Log 제거하는 법

    이미 어느정도 예상은 하고 있었지만 스크립트안에 찍힌 로그가 많을시 모바일 환경안에서는 CPU 사용량이 증가하여 성능에 영향을 준다고 한다 그리고 웬만한 로직보다 이 로그 연산이 더 무

    sangeun00.tistory.com

     

    위에 첨부한 방법대로 찍은 로그들부터 제거해주었다

     

     

    그리고 인게임화면에서 배경(흰색)을 담당하고 있는 BackGround에 부착된 Rigidbody2D도 빼주었다

    게임에 아무런 지장이 없는데 이걸 왜 여태 달고 있었던건가 싶다

     

     

    그동안은 코드를 많이 손봤었는데 이번엔 유니티 설정 부분에 답이 있지 않을까 싶어 만져보았다

     

     

    • Quality Settings:
      • Unity의 메뉴에서 Edit > Project Settings > Quality로 가서 모바일 플랫폼에 맞는 품질 설정을 낮추세요. 샘플링 레이트, 그림자 품질, 안티앨리어싱 등을 줄이면 성능이 개선될 수 있습니다.
    • Graphics Settings:
      • Edit > Project Settings > Graphics에서 모바일에 적합한 그래픽 설정을 조정하세요. 사용하지 않는 쉐이더나 그래픽 기능을 비활성화하면 도움이 됩니다.
    • Fixed Timestep:
      • Edit > Project Settings > Time에서 Fixed Timestep 값을 조정하세요. 이 값이 너무 낮으면 물리 계산이 자주 발생하여 성능 저하를 초래할 수 있습니다.
    • Rigidbody Interpolation:
      • Rigidbody2D의 Interpolation을 Interpolate 또는 Extrapolate로 설정하여 물리 기반 오브젝트의 움직임을 더 부드럽게 만들 수 있습니다.
    • Dynamic Resolution:
      • 모바일 플랫폼에서는 동적 해상도(Dynamic Resolution)를 활용하여 프레임 수를 유지하면서 그래픽 품질을 조정할 수 있습니다.
    • Object Pooling:
      • 많은 게임 오브젝트를 생성하고 파괴하는 대신 오브젝트 풀링을 사용하면 메모리 할당 및 해제를 줄여 성능을 향상시킬 수 있습니다.
    • Profiler 사용:
      • Unity Profiler를 사용하여 어떤 부분에서 성능이 저하되고 있는지 분석해보세요. CPU, GPU, 메모리 사용량을 확인할 수 있습니다.
    • Update 호출 최적화:
      • Update 메서드 내의 연산을 최소화하고, 필요하지 않은 경우에는 Coroutine이나 이벤트를 사용하여 성능을 개선하세요.
    • UI 최적화:
      • Canvas의 설정을 확인하고, 불필요한 UI 요소를 줄이는 것도 좋습니다. UI 요소가 많을 경우 성능 저하를 일으킬 수 있습니다.

     

     

    챗지피티에서는 총 9가지의 가능성을 제시했다

    첫번째부터 한번 해보자. 

     

    • Quality Settings:
      • Unity의 메뉴에서 Edit > Project Settings > Quality로 가서 모바일 플랫폼에 맞는 품질 설정을 낮추세요. 샘플링 레이트, 그림자 품질, 안티앨리어싱 등을 줄이면 성능이 개선될 수 있습니다.

    퀄리티 세팅에서 품질을 조절할 수 있다고 한다. 

    크게 Quality라고 써진곳 밑을 보면 각각의 플랫폼별 퀄리티 레벨이 정해져있다(안드로이드는 Medium)

    나는 굳이 있던 레벨을 건들지 않고 Add Quality Level로 'For Mobile'이라는 임의의 레벨을 추가해주었다

     

    Anti Aliasing - Disabled

    VSync Count - Don't Sync

    Anisotropic Texture - Disabled

    Resolution Scaling Fixed DPI Factor - 0.5~0.75

     

    이렇게 설정해주면 된다. 셰도우 부분이 있긴하지만 2D 게임에다 이 게임은 그림자가 아예 없기 때문에 넘어가주겠다

     

    • Graphics Settings:
      • Edit > Project Settings > Graphics에서 모바일에 적합한 그래픽 설정을 조정하세요. 사용하지 않는 쉐이더나 그래픽 기능을 비활성화하면 도움이 됩니다.

    그 다음은 그래픽 세팅. 하지만 이 게임은 라이트도, 쉐이더도, 대부분의 3D게임에 사용되는것들이

    사용되지 않기에 크게 최적화와 관련없는것들이라 넘어가겠다

     

    그나마 있다면 이정도인데 사실 이미 디폴트 값으로 정해져있는 것들이다

     

    하지만 한가지 알게 된 사실이 있다

     

    Project Setting > Player > Resolution and Presentation 을 타고 들어가면

     > Default Orientation을 선택할 수 있다. 이게 모바일에서 구동될 때 가로모드로 갈지 세로모드로 갈지 

    결정해주는 역할을 한다. 둘다 가능하다면 Auto Rotation이겠지만, 나처럼 세로모드로만 가능한 경우에는

    Potrait으로 설정해주도록 하겠다

     

     

    • Fixed Timestep:
      • Edit > Project Settings > Time에서 Fixed Timestep 값을 조정하세요. 이 값이 너무 낮으면 물리 계산이 자주 발생하여 성능 저하를 초래할 수 있습니다.

     

    Fixed Timestep은 Unity의 물리 시뮬레이션과 관련된 설정으로, 물리 계산의 주기를 정의한다

     

     

     

    Fixed Timestep의 기본값은 보통 0.02로 설정되어 있다. 이는 물리 계산이 초당 50회(1 / 0.02) 수행된다는 뜻

    값을 높이면 물리 계산 빈도가 줄어들어 CPU 부담이 감소하지만, 물리 시뮬레이션의 정확성이 떨어질 수 있다.

    예를 들어, 값을 0.03으로 변경하면 물리 계산이 초당 약 33.3회로 줄어들어 성능이 향상될 수 있다.

    그러나 너무 높이 설정하면 물리 동작이 부자연스러워질 수 있으므로, 테스트를 통해 적절한 값을 찾아야 한다.

     

    값을 조금씩 올려가면서 결과를 확인해보니 진짜 숫자를 올리면 올릴수록 초반 프레임이 끊기고

    물리계산이 이상해져가는것을 확인할 수 있었다. 그래서 0.1만 올리려고 했는데...

     

     

    이쪽씬에서 너무 심각하게 끊겨버린다... 그냥 저 움직이는 구체들은 전부 빼버릴까도 생각해봤지만

    스토리상 절대 뺄 수 없는 부분이라 스케일을 대폭 줄여보기로 했다

     

     

     

    공 개수도 줄이고, 전체적인 사이즈도 줄여주었다

     

    그리고 스스로 움직이는 공들이 밖으로 빠져나가지 못하게 해주었던 데드라인

    이걸 제어한답시고 모든 공마다 이렇게 길게 코드를 짜두었었는데

    생각해보니까 모든공에 스크립트를 붙여둘게 아니라 걍 데드존에만 붙여주면 되는거 아니었나..? 

    이렇게 이제와서 보면 정말 이해가 안가는 부분들이 하나둘씩 보인다.

    물론 개발을 배우고 있었던것도 있지만 최적화라는걸 정말 1도 신경쓰지 않고 만들었으니 그럴만도 하다

     

     

    사방으로 데드라인은 총 두개이다. 플레이어가 아닌 공들의 이탈을 제어하는 데드라인과

    플레이어의 이탈을 제어하는 데드라인. 이것도 지금도 굳이 두개로 나눴다 싶으니 두가지를 수정해주겠다.

     

    1. 데드라인 하나로 통일

    2. 플레이어와 공들에 붙어있는 이탈방지 코드를 데드라인에 통일하여 붙여주기

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class StageDeadZone : MonoBehaviour
    {
        private void OnTriggerEnter2D(Collider2D collision)
        {
            Rigidbody2D rb = collision.GetComponent<Rigidbody2D>();
    
            if (rb != null)
            {
                switch (gameObject.name)
                {
                    case "B":
                        rb.position += new Vector2(0, 210);
                        break;
                    case "T":
                        rb.position += new Vector2(0, -210);
                        break;
                    case "L":
                        rb.position += new Vector2(200, 0);
                        break;
                    case "R":
                        rb.position += new Vector2(-200, 0);
                        break;
                }
            }
        }
    }

     

    공에 부착된 위치조절 코드를 전부 지워주고 데드라인 용 스크립트를 새로 만들어 간단하게 짜주었다

    물론 공이 아닌 플레이어도 예외는 아니다. 

     

    이로써 그 많던 공에 전부 붙어있는 위치조절용 코드를 없애주었다. 단순히 위치조절용이 아니라

    충돌계산을 없애줬다는것에 의의를 둔다

     

    그 다음은 프레임을 조절해주겠다

    using UnityEngine;
    
    public class DynamicFrameRate : MonoBehaviour
    {
        void Awake()
        {
            DontDestroyOnLoad(gameObject); 
        }
    
        void Start()
        {
            if (SystemInfo.processorCount > 4) 
            {
                Application.targetFrameRate = 60; 
            }
            else
            {
                Application.targetFrameRate = 30; 
            }
        }
    }

     

    우선 Awake문에 오브젝트가 게임내내 지워지지 않도록 해주었고

     

    만약

     

    if (SystemInfo.processorCount > 4) 

    프로세스 코어가 4개 이상이라면 고성능 기기로 간주하고
            {
                Application.targetFrameRate = 60; // 60프레임으로 고정
            }
            else
            {
                Application.targetFrameRate = 30; // 그게 아니라면 30프레임으로
            }

     

    Unity는 기본적으로 최대 프레임레이트에 따라 게임을 실행하게 되고 최대 프레임은 다양한 기준으로 결정된다고 한다

    그러니 확실하게 프레임을 고정시켜주는게 많은 면에서 유용하다고 한다!

     

    이제 이 스크립트를 Start씬 삭제되지 않는 오브젝트에 부착해주면 된다

     

    그리고 DontDestroyOnLoad를 사용하면서 느낀게 상식적으로 모든 씬에서 없어지지 않는 오브젝트라는건데...

    너무 남발하면 분명히 프레임에 영향을 주지 않을까라는 생각이 들었다. 생각이 아니라 사실 무거울 수 밖에 없다

    어디를 가던 늘 들고가야하는 짐 같은 느낌인데...

     

    using UnityEngine.SceneManagement;
    using UnityEngine;
    
    public class BGMControl : MonoBehaviour
    {
        public AudioSource[] SoundEffect;
        public bool BGMSwitch = true;
        public bool SoundEffectSwitch = true;
    
        private void Awake()
        {
            // 게임 시작 시 저장된 상태를 불러오기
            LoadAudioSettings();
        }
    
        void Update()
        {
            UpdateAudioSources();
        }
    
        void UpdateAudioSources()
        {
            ToggleAudioSources(SoundEffect, SoundEffectSwitch);
        }
    
        void ToggleAudioSources(AudioSource[] sources, bool isEnabled)
        {
            foreach (var source in sources)
            {
                if (!isEnabled && source.isPlaying)
                {
                    source.Stop();
                }
                source.enabled = isEnabled;
            }
        }
        private void StopAllAudio(AudioSource[] sources)
        {
            foreach (var source in sources)
            {
                if (source.isPlaying)
                {
                    source.Stop();
                }
            }
        }
    
        // 게임 시작 시 저장된 상태를 불러오기
        private void LoadAudioSettings()
        {
            BGMSwitch = PlayerPrefs.GetInt("BGMSwitch", 1) == 1; // 기본값은 true
            SoundEffectSwitch = PlayerPrefs.GetInt("SoundEffectSwitch", 1) == 1; // 기본값은 true
        }
    
        // 상태가 변경될 때 저장하기
        public void SaveAudioSettings()
        {
            PlayerPrefs.SetInt("BGMSwitch", BGMSwitch ? 1 : 0);
            PlayerPrefs.SetInt("SoundEffectSwitch", SoundEffectSwitch ? 1 : 0);
            PlayerPrefs.Save();
        }
    
        // 새로운 메서드 추가
        public void SoundEffectPlay(int index)
        {
            if (index >= 0 && index < SoundEffect.Length)
            {
                SoundEffect[index].Play();
            }
            else
            {
                Debug.LogWarning("SoundEffectPlay: Index out of range.");
            }
        }
    }
    using System;
    using System.Collections.Generic;
    using Unity.VisualScripting;
    using UnityEngine;
    using UnityEngine.EventSystems;
    using UnityEngine.SceneManagement;
    
    public class StageGameManager : MonoBehaviour
    {
        public static StageGameManager instance = null;
        public float StageClearID;
        public bool isending = false;
    
        private void Awake()
        {
            if (instance == null)
            {
                instance = this;
                DontDestroyOnLoad(gameObject);
                LoadStageClearID();
                LoadIsEnding();
            }
            else
            {
                if (instance != this)
                    Destroy(this.gameObject);
            }
        }
    
        public void PauseGame()
        {
            Time.timeScale = 0f; // 게임 일시정지
        }
    
        public void ResumeGame()
        {
            Time.timeScale = 1f; // 게임 일시정지 해제
        }
    
        public void SaveStageClearID()
        {
            PlayerPrefs.SetFloat("StageClearID", StageClearID);
            PlayerPrefs.Save();
        }
    
        public void SaveIsEnding()
        {
            PlayerPrefs.SetInt("IsEnding", isending ? 1 : 0);
            PlayerPrefs.Save();
        }
    
        private void LoadStageClearID()
        {
            if (PlayerPrefs.HasKey("StageClearID"))
            {
                StageClearID = PlayerPrefs.GetFloat("StageClearID");
            }
        }
    
        private void LoadIsEnding()
        {
            if (PlayerPrefs.HasKey("IsEnding"))
            {
                isending = PlayerPrefs.GetInt("IsEnding") == 1;
            }
        }
    }

     

    그래서 뒤늦게 보니 이 두개의 묵직한 코드가 모든 씬에 따라다니고 있었다

    물론 이게 얼마나 영향을 끼칠지는 알 수 없다.

    지금까지의 최적화가 그랬듯 항상 몰랐지만 조금이라도 도움이 될 것 같으면 할 수 있는걸 모두 해봐야 한다

     

    하나는 게임내내 BGM과 사운드 효과 재생을 담당할 BGMControl과

    다른 하나는 게임의 스테이지 변경을 담당해줄 StageGameManager이다

     

    using UnityEngine;
    
    public class StageGameManager : MonoBehaviour
    {
        public static StageGameManager instance = null;
        public float StageClearID;
        public bool isending = false;
    
        private float stageClearIDCache; // PlayerPrefs 값을 캐싱
        private bool isEndingCache;
    
        private void Awake()
        {
            if (instance == null)
            {
                instance = this;
                DontDestroyOnLoad(gameObject);
                // PlayerPrefs에서 값 불러오고 캐시에 저장
                stageClearIDCache = PlayerPrefs.GetFloat("StageClearID", 0f);
                isEndingCache = PlayerPrefs.GetInt("IsEnding", 0) == 1;
                StageClearID = stageClearIDCache;
                isending = isEndingCache;
            }
            else
            {
                Destroy(gameObject); // 중복 방지
            }
        }
    
        public void PauseGame()
        {
            Time.timeScale = 0f; // 게임 일시정지
        }
    
        public void ResumeGame()
        {
            Time.timeScale = 1f; // 게임 일시정지 해제
        }
    
        public void SaveStageClearID()
        {
            if (stageClearIDCache != StageClearID)  // 값이 변경된 경우에만 저장
            {
                PlayerPrefs.SetFloat("StageClearID", StageClearID);
                PlayerPrefs.Save();
                stageClearIDCache = StageClearID;  // 캐시 업데이트
            }
        }
    
        public void SaveIsEnding()
        {
            if (isEndingCache != isending)  // 값이 변경된 경우에만 저장
            {
                PlayerPrefs.SetInt("IsEnding", isending ? 1 : 0);
                PlayerPrefs.Save();
                isEndingCache = isending;  // 캐시 업데이트
            }
        }
    }

     

    그래서 StageGameManager 스크립트를 더 최적화해주었다

    이제부터는 PlayerPrefs 데이터를 불러올 때 캐시에 저장하고, 이후 데이터변경이 있을때만 저장하도록 최적화 해주었다

     

     

    그리고 중간에 한가지 알게 된 사실이 있는데, 게임에 쓰이지 않는 에셋들을 가지고 있으면 최적화에 문제가 된다는 것

    물론 프레임에 직접적인 영향을 주지는 않지만..

     

     

    • 메모리 사용량: 불필요한 오디오 리소스가 빌드 파일에 포함되면 메모리 사용량이 증가할 수 있습니다. 특히 모바일 기기에서 메모리 자원이 제한적인 경우, 리소스 관리가 중요합니다. 메모리가 과도하게 사용되면 앱이 느려지거나, 심각할 경우 충돌(crash)이 발생할 수 있습니다.
    • 로딩 시간: 안 쓰이는 오디오 파일이 포함되면 초기 로딩 시간에 영향을 줄 수 있습니다. 유니티는 빌드된 리소스들을 메모리에 로드할 때, 모든 리소스를 한 번에 로드하려 하거나 필요한 부분만 로드하는 전략을 사용할 수 있지만, 불필요한 리소스가 많으면 이 로딩 시간이 길어질 수 있습니다.
    • 스토리지와 다운로드 크기: 불필요한 오디오 파일은 앱의 크기를 증가시키며, 이는 사용자에게 더 큰 저장 공간을 요구하게 되고 다운로드 시간을 늘리게 됩니다. 이는 최종 사용자의 경험에 부정적인 영향을 미칠 수 있습니다.

    이러한 문제들이 있다고 한다

     

     

     

    그래서 안쓰는 리소스들을 전부 제거해주었다. 조금이라도 최적화에 도움이 됐길..

     

     

    'Galaxy Ball > 5. 최적화' 카테고리의 다른 글

    최적화 - 12 (코드수정 #Canvas Group, #DOTween)  (2) 2024.10.30
    최적화 - 11 (설정수정★)  (1) 2024.10.17
    최적화 - 9  (1) 2024.10.09
    최적화 - 8  (1) 2024.10.01
    최적화 - 7  (0) 2024.09.25
Designed by Tistory.