ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 최적화 - 7
    Galaxy Ball/5. 최적화 2024. 9. 25. 19:34

    드디어 공을 최대한 부드럽게 이동시키는데 성공했다. 이제 남은건 게임에 등장하는 모든 공들을

    전부 각자의 상황에 맞춰 똑같이 최적화를 해주는것이다

     

    using System.Collections;
    using System.Collections.Generic;
    using TMPro;
    using UnityEngine;
    
    public class ExBallController : MonoBehaviour
    {
        SPGameManager spgamemanager;
        Rigidbody2D rb;
        BGMControl bGMControl;
        bool hasBeenLaunched = false;
        public bool isExpanding = false; // 공이 팽창 중인지 여부
        bool isStopped = false; // 공이 완전히 멈췄는지 여부
        private float decelerationThreshold = 0.4f;
        private float dragAmount = 1.1f;
        private float expandSpeed = 1f; // 팽창 속도
        private Vector3 initialScale; // 초기 공 크기
        private Vector3 targetScale; // 목표 크기
        private int durability; // 공의 내구도
        private const string SPTwiceFName = "SPTwiceF(Clone)";
        private const string TwiceBulletName = "TwiceBullet(Clone)";
        public PhysicsMaterial2D bouncyMaterial;
        private TextMeshPro textMesh;
        private const string GojungTag = "Gojung";
        private const string WallTag = "Wall";
    
    
        void Start()
        {
            bGMControl = FindAnyObjectByType<BGMControl>();
            spgamemanager = FindAnyObjectByType<SPGameManager>();
            rb = GetComponent<Rigidbody2D>();
    
            GameObject textObject = new GameObject("TextMeshPro");
            textObject.transform.parent = transform; // 구체의 자식으로 설정
            durability = Random.Range(1, 6);
    
            textMesh = textObject.AddComponent<TextMeshPro>();
            textMesh.text = durability.ToString();
            textMesh.fontSize = 4;
            textMesh.alignment = TextAlignmentOptions.Center;
            textMesh.autoSizeTextContainer = true;
            textMesh.rectTransform.localPosition = Vector3.zero; // 구체 중심에 텍스트 배치
            textMesh.sortingOrder = 1; // 레이어 순서를 조정하여 구체 위에 배치
    
            rb.drag = 0f;
            rb.collisionDetectionMode = CollisionDetectionMode2D.Continuous;
            rb.interpolation = RigidbodyInterpolation2D.Interpolate;
    
            Collider2D collider = GetComponent<Collider2D>();
            if (collider != null && bouncyMaterial != null)
            {
                collider.sharedMaterial = bouncyMaterial;
            }
    
            initialScale = transform.localScale; 
        }
    
        void Update()
        {
            if (!hasBeenLaunched && !spgamemanager.isDragging)
            {
                LaunchBall();
            }
    
            if (hasBeenLaunched && !isStopped)
            {
                SlowDownBall();
            }
    
            if (isExpanding)
            {
                ExpandBall(); 
            }
        }
    
        void LaunchBall()
        {
            Vector2 launchForce = SPGameManager.shotDirection * (SPGameManager.shotDistance*1.4f);
            rb.AddForce(launchForce, ForceMode2D.Impulse);
    
            rb.drag = dragAmount;
            hasBeenLaunched = true;
        }
    
        void SlowDownBall()
        {
            if (rb == null) return;
    
            if (rb.velocity.magnitude <= decelerationThreshold)
            {
                rb.velocity = Vector2.zero; 
                isStopped = true;
                StartExpansion();
            }
        }
    
        void StartExpansion()
        {
            bGMControl.SoundEffectPlay(1);
            targetScale = initialScale * 10f; 
            isExpanding = true;
        }
    
        void ExpandBall()
        {
            if (Vector3.Distance(transform.localScale, targetScale) > 0.01f)
            {
                transform.localScale = Vector3.Lerp(transform.localScale, targetScale, Time.deltaTime * expandSpeed);
            }
            else
            {
                transform.localScale = targetScale; // 목표 크기에 도달하면 팽창 완료
                isExpanding = false; // 팽창 중단
            }
        }
    
        private void OnCollisionEnter2D(Collision2D collision)
        {
            if(!isExpanding)
            {
                bGMControl.SoundEffectPlay(0);
            }
            if (!collision.collider.isTrigger && isExpanding)
            {
                isExpanding = false; // 팽창 중단
                transform.localScale = transform.localScale; // 현재 크기에서 멈춤
                DestroyRigidbody(); // Rigidbody 제거
            }
    
            if ((collision.collider.name != SPTwiceFName || collision.collider.name != TwiceBulletName) && rb == null)
            {
                if (collision.collider.CompareTag(GojungTag)) return;
                if (collision.collider.CompareTag(WallTag)) return;
    
                TakeDamage(1);
                textMesh.text = durability.ToString();
            }
            if ((collision.collider.name == SPTwiceFName || collision.collider.name == TwiceBulletName) && rb == null)
            {
                TakeDamage(2);
                textMesh.text = durability.ToString();
            }
        }
    
        void TakeDamage(int damage)
        {
            durability -= damage;
            if (durability <= 0)
            {
                spgamemanager.RemoveBall();
                Destroy(gameObject); 
            }
        }
    
        void DestroyRigidbody()
        {
            if (rb != null)
            {
                Destroy(rb);
                rb = null; 
            }
        }
    }

     

    최종 최적화가 된 코드이다. 이제 이걸 기준으로 다른 코드들도 이 코드에 맞춰 수정해주도록 하겠다

     

    1. 적 총알

    지금까지 내가 날릴 공에 대한 최적화였다면 이번엔 적 유닛이 발사할 총알이다

     

    사실상 이 게임은 내가 날리는 공, 적이 날리는 공 크게 이 두가지뿐이니 이것까지 끝내면 대부분 완료되는거나 다름없다

     

    가장 먼저 기존에 짠 코드를 기준으로 변수이름부터 수정해주었다

    예를들어 기존에 쓰던 rigid는 rb로, randomNumber를 durability로 통일하여 보기 좋도록 수정하였다

     

    PhysicsMaterial2D bouncyMaterial; 도 추가해준뒤 플레이어 공과 똑같은 메터리얼도 넣어주었다

     

    ...아니 근데 이것까지만 했는데도 벌써 공이 똑같이 부드럽게 나온다....

    뭐지? 정말 이거 하나만 고쳐주면 끝나는거였나...??

     

    아무튼 계속 이어가보자. Bouncy Material로 반사 기능은 필요없으니

    반사기능을 담당하는 코드들은 전부 지워버려도 된다

     

       private void OnCollisionEnter2D(Collision2D coll)
        {
            if (!hasExpanded)
            {
                bGMControl.SoundEffectPlay(0);
            }
            if (coll.gameObject.name == "SPInvincibleF(Clone)")
            {
                ChallengeGameManager chmanager = FindObjectOfType<ChallengeGameManager>();
                chmanager.scorenum++;
                Destroy(gameObject);
            }
    
            if ((coll.collider.name != SPTwiceFName || coll.collider.name != TwiceBulletName) && rb == null)
            {
                if (coll.collider.CompareTag(GojungTag)) return;
                if (coll.collider.CompareTag(WallTag)) return;
    
                TakeDamage(1);
                textMesh.text = durability.ToString();
            }
            if ((coll.collider.name == SPTwiceFName || coll.collider.name == TwiceBulletName) && rb == null)
            {
                TakeDamage(2);
                textMesh.text = durability.ToString();
            }
            this.iscolliding = true;
        }

     

    데미지를 받는 부분들도 전부 최적화에 맞춰 수정해주었다

    한가지 다른 점이 있다면 챌린지 모드를 고려하여 함께 있는 코드들인데

     

     if (coll.gameObject.name == "SPInvincibleF(Clone)")
            {
                ChallengeGameManager chmanager = FindObjectOfType<ChallengeGameManager>();
                chmanager.scorenum++;
                Destroy(gameObject);
            }

     

    사실 저게 보기 그렇다면 챌린지모드용 총알 프리팹과 코드를 새로 하나 만들어주면 되긴하다

    그리고 여태까지 전부 그렇게 해오긴 했지만 챌린지 모드 자체의 볼륨이 작기도 하고

    그렇게 추가되는 코드들이 많질 않아 이것만큼은 그냥 같이 병행시켜주기로 했다

     

     void TakeDamage(int damage)
        {
            durability -= damage;
            if (durability <= 0)
            {
                ChallengeGameManager chmanager = FindObjectOfType<ChallengeGameManager>();
                if (SceneManager.GetActiveScene().name == "ChallengeScene")
                {
                    chmanager.scorenum++;
                }
                spGameManager.RemoveBall();
                Destroy(gameObject);
            }
        }

     

    물론 이 데미지 시스템을 위한 TakeDamage 메서드도 추가해주었다!

     

    그리고 중요한 부분은 Rigidbody 파괴 부분. 동일한 기능을 하는 메서드이지만

     

    위는 최적화용 메서드, 밑은 최적화가 되기전 메서드이다. 이제 이것도 최적화용으로 맞추기 위해 코드를 수정해보겠다

     

    사실 이걸 넣기 위해서는 다른 변수들이 이것저것 추가되어 대거 수정에 들어가려 했으나..

    그럼 또다시 한번 코드를 싹 다 갈아엎어야 하기에 기존에 있던 hasExpanded 변수를 이용하여

    코드를 완성해주었다. 사실 이미 부드럽게 이동하고 팽창하는데 성공했기에 이런 방법으로 해도 큰 지장은 없다

     

    using System.Collections;
    using System.Collections.Generic;
    using TMPro;
    using UnityEngine;
    using UnityEngine.SceneManagement;
    
    public class EnemyBulletControl : MonoBehaviour
    {
        SPGameManager spGameManager;
        Rigidbody2D rb;
        BGMControl bGMControl;
        Vector2 lastVelocity;
        float deceleration = 2f;
        public float increase = 4f;
        private bool iscolliding = false;
        public bool hasExpanded = false;
        private bool isStopped = false;
        private int durability;
        private TextMeshPro textMesh;
        private bool hasBeenReleased = false;
        private float rotationAngle = 0f; // 회전 각도를 저장할 변수
        public float fontsize;
        public int BallMinHP = 1;
        public int BallMaxHP = 6;
        public PhysicsMaterial2D bouncyMaterial;
        private Vector3 initialScale; // 초기 공 크기
        private Vector3 targetScale; // 목표 크기
        private const string SPTwiceFName = "SPTwiceF(Clone)";
        private const string TwiceBulletName = "TwiceBullet(Clone)";
        private const string GojungTag = "Gojung";
        private const string WallTag = "Wall";
    
        private void Start()
        {
            spGameManager = FindObjectOfType<SPGameManager>();
            bGMControl = FindObjectOfType<BGMControl>();
            rb = GetComponent<Rigidbody2D>();
            GameObject textObject = new GameObject("TextMeshPro");
    
            textObject.transform.parent = transform;
            textMesh = textObject.AddComponent<TextMeshPro>();
            durability = Random.Range(BallMinHP, BallMaxHP);
            textMesh.text = durability.ToString();
            textMesh.fontSize = fontsize;
            textMesh.alignment = TextAlignmentOptions.Center;
            textMesh.autoSizeTextContainer = true;
            textMesh.rectTransform.localPosition = Vector3.zero;
            textMesh.sortingOrder = 1;
    
            rb.drag = 0f;
            rb.collisionDetectionMode = CollisionDetectionMode2D.Continuous;
            rb.interpolation = RigidbodyInterpolation2D.Interpolate;
    
            Collider2D collider = GetComponent<Collider2D>();
            if (collider != null && bouncyMaterial != null)
            {
                collider.sharedMaterial = bouncyMaterial;
            }
    
            initialScale = transform.localScale;
        }
    
        private void Update()
        {
            Move();
            expand();
        }
    
        void Move()
        {
            if (rb == null || isStopped) return;
    
            lastVelocity = rb.velocity;
            rb.velocity -= rb.velocity.normalized * deceleration * Time.deltaTime;
    
            if (rb.velocity.magnitude <= 0.01f && hasExpanded)
            {
                isStopped = true;
            }
        }
    
        void expand()
        {
            if (rb == null || iscolliding) return;
            if (rb.velocity.magnitude > 0.1f) return;
            if (Input.GetMouseButton(0)) return;
    
            if (!hasExpanded)
            {
                bGMControl.SoundEffectPlay(1);
            }
            transform.localScale += Vector3.one * increase * Time.deltaTime;
            hasExpanded = true;
        }
    
        private void OnCollisionEnter2D(Collision2D coll)
        {
            if (!hasExpanded)
            {
                bGMControl.SoundEffectPlay(0);
            }
            if (!coll.collider.isTrigger && hasExpanded)
            {
                hasExpanded = true; // 팽창 중단
                transform.localScale = transform.localScale; // 현재 크기에서 멈춤
                DestroyRigidbody(); // Rigidbody 제거
            }
            if (coll.gameObject.name == "SPInvincibleF(Clone)")
            {
                ChallengeGameManager chmanager = FindObjectOfType<ChallengeGameManager>();
                chmanager.scorenum++;
                Destroy(gameObject);
            }
    
            if ((coll.collider.name != SPTwiceFName || coll.collider.name != TwiceBulletName) && rb == null)
            {
                if (coll.collider.CompareTag(GojungTag)) return;
                if (coll.collider.CompareTag(WallTag)) return;
    
                TakeDamage(1);
                textMesh.text = durability.ToString();
            }
            if ((coll.collider.name == SPTwiceFName || coll.collider.name == TwiceBulletName) && rb == null)
            {
                TakeDamage(2);
                textMesh.text = durability.ToString();
            }
            this.iscolliding = true;
        }
    
        private void OnCollisionExit2D(Collision2D collision)
        {
            this.iscolliding = false;
        }
        void TakeDamage(int damage)
        {
            durability -= damage;
            if (durability <= 0)
            {
                ChallengeGameManager chmanager = FindObjectOfType<ChallengeGameManager>();
                if (SceneManager.GetActiveScene().name == "ChallengeScene")
                {
                    chmanager.scorenum++;
                }
                spGameManager.RemoveBall();
                Destroy(gameObject);
            }
        }
    
        void DestroyRigidbody()
        {
            if (rb != null)
            {
                Destroy(rb);
                rb = null;
            }
        }
    }

     

    그렇게 적 총알을 컨트롤 하는 EnemyBulletControl도 최적화를 완료해주었다

     

     

    그렇게 최적화도 완료되었으니 해당 아이템, 총알 중 BallController와 EnemyBallControl를 사용하는 프리팹들에게

    최적화된 새로운 코드들을 붙여주었다. 

     

    모두 예상대로 부드럽게 움직이며 구현되지만 하지만 한가지 문제가 있었으니...

     

     

    바로 가장 작은 사이즈의 총알을 담당하는 SmallBullet이 다른 총알들에 비해 팽창크기, 폰트사이즈가 많이 동떨어져

    오른쪽 사진같은 오류가 일어나버리고 만것...

     

    그래서 원래는 SmallBullet용 코드를 하나 새로 만들까 생각했었는데 굳이 그럴필요없다

    지금 이런 일이 벌어지는 결정적인 변수가 2개 있는데

     

    하나는 공의 글자크기를 지정하는 textMesh.fontSize

    다른 하나는 공의 팽창 한계를 지정하는  targetScale = initialScale * 10f; 이다

     

    즉, 굳이 스크립트를 하나 만들게 아니라 저 변수 두개만 내가 컨트롤 할 수 있으면 된다는 것

     

    public class ExBallController : MonoBehaviour
    {
        ...
        
        public int fontsize;
        public int PlusScale; // public으로 변수 2개 추가
    
    
        void Start()
        {
           
            textMesh.fontSize = fontsize; //앞으로 폰트 사이즈는 직접 설정
           
        }
        
          .........
          
        void StartExpansion()
        {
            bGMControl.SoundEffectPlay(1);
            targetScale = initialScale * PlusScale; // 팽창 한계도 직접 설정
            isExpanding = true;
        }

     

    그래서 위 코드에 보이는것처럼 public으로 직접 설정할 변수 2개를 추가해준 다음

    그것에 맞춰 적절하게 수정하여 주었다

     

    왼쪽이 일반 적 총알 설정, 오른쪽이 SmallBullet 설정

     

    이제 정상적으로 나오는것을 확인할 수 있다

     

    그리고 계속 게임을 돌려보니 한가지 문제점을 더 발견할 수 있었다. 

    바로 적이 날린 공이 팽창중에 적과 충돌시 내구도가 줄어드는것. 

     

        private const string EnemyCenterTag = "EnemyCenter";
    
        .....
    
    
    if ((coll.collider.name != SPTwiceFName || coll.collider.name != TwiceBulletName) && rb == null)
            {
                if (coll.collider.CompareTag(GojungTag)) return; // 추가
                if (coll.collider.CompareTag(WallTag)) return;
                if (coll.collider.CompareTag(EnemyCenterTag)) return;
    
                TakeDamage(1);
                textMesh.text = durability.ToString();
            }

     

    그도 그럴것이 SPTwiceName, TwiceBulletName이 아니면 무조건 내구도가 깎이게 설계되었으니 그럴수밖에..

    그래서 EnemyCenterTag라는 데미지를 받지 않는 조건을 한가지 추가해주었다

     

    그리고 모든 적 유닛의 실질적인 몸통 부분에 전부 EnemyCenter 태그를 추가해주었다

     

    이러면 이제 본인이 날린 총알이 본인을 맞고 내구도가 깎이는 일도 사라지게 된다

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

    최적화 - 9  (1) 2024.10.09
    최적화 - 8  (1) 2024.10.01
    최적화 - 6  (1) 2024.09.24
    최적화 - 5 (BallController 스크립트 제작#2)  (0) 2024.09.20
    최적화 - 4 (BallController 스크립트 제작#1)  (1) 2024.09.20
Designed by Tistory.