ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 최적화 - 8
    Galaxy Ball/5. 최적화 2024. 10. 1. 18:35
     

    최적화 - 7

    드디어 공을 최대한 부드럽게 이동시키는데 성공했다. 이제 남은건 게임에 등장하는 모든 공들을전부 각자의 상황에 맞춰 똑같이 최적화를 해주는것이다 using System.Collections;using System.Collections.

    sangeun00.tistory.com

    지난 글에서 일부 적 총알과 아이템들을 최적화 해주는데 성공했으니 이번글에서는

    게임에서 사용될 모든 아이템, 공들을 최적화 해주도록 하겠다

     

    왼쪽이 싱글플레이 오른쪽이 멀티플레이

     

    그전에 한가지 문제가 있다. 아무래도 싱글플레이와 멀티플레이를 둘다 제공하는 게임이기에

    싱글플레이용 코드와 멀티플레이용 코드가 나뉘는것...

    큰 차이는 없다 다만 싱글플레이는 SPGameManager에서 공을 날릴 힘과 방향을 받아오고

    멀티플레이는 GameManager에서 받아오는것뿐

     

    그리고 하나 더 있다...!

    플레이어가 날리는 공과 적 유닛이 발사하는 공은 또 다른 원리로 구현된다는것...

     

    즉 똑같은 공이라도 싱글플레이, 멀티플레이, 적 유닛

    이렇게 3가지 버전의 스크립트를 준비해야 한다는 것이다

    ...내가 왜 3가지나 챙기려한건지 후회가 되지만 그래도 힘내서 해보자

     

    원래는 현재씬의 이름에 따라 어디서 받아오는지를 다르게 하려고 했는데 그것 자체가 연산이 들어가기도 하고

    후에 있을 코드 관리의 용의성을 위해 각자의 코드를 분리하도록 하겠다

     

    가장 먼저 게임 패배시 보게될 Fail씬에서 발사되는 공부터 수정해주겠다

    여기 나오는 공을 최적화 해줄 예정

    using System.Collections;
    using System.Collections.Generic;
    using TMPro;
    using UnityEngine;
    using UnityEngine.SceneManagement;
    
    public class FEnemyBulletControl : MonoBehaviour
    {
        Rigidbody2D rb;
        BGMControl bGMControl;
        Vector2 lastVelocity;
        float deceleration = 2f;
        public float increase;
        private bool iscolliding = false;
        public bool hasExpanded = false;
        private bool isStopped = false;
        private int durability;
        private TextMeshPro textMesh;
        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 const string EnemyCenterTag = "EnemyCenter";
    
    
    
        private void Start()
        {
            bGMControl = FindAnyObjectByType<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;
                if (coll.collider.CompareTag(EnemyCenterTag)) 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)
            {
                Destroy(gameObject);
            }
        }
    
        void DestroyRigidbody()
        {
            if (rb != null)
            {
                Destroy(rb);
                rb = null;
            }
        }
    }

     

    적 총알 스크립트와 대부분 유사하다

     

     

     

    그 다음엔 아이템을 수정해보자 기본 구체를 이동시키는 BallController와 달리

    아이템들 중 고유의 능력이 있는 아이템들은 코드의 일부분이 다르기때문에 맞춰 수정해주어야 한다

    using System.Collections;
    using System.Collections.Generic;
    using TMPro;
    using UnityEngine;
    
    public class BlackHole_Skill : MonoBehaviour
    {
        GameManager gamemanager;
        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 bool hasBeenReleased = false;
        public PhysicsMaterial2D bouncyMaterial;
    
        bool hasBeenLaunched = false;
        public bool isExpanding = false; // 공이 팽창 중인지 여부
        private float decelerationThreshold = 0.4f;
        private float dragAmount = 1.1f;
        private float expandSpeed = 1f; // 팽창 속도
        private Vector3 initialScale; // 초기 공 크기
        private Vector3 targetScale; // 목표 크기
    
        private const string GojungTag = "Gojung";
        private const string WallTag = "Wall";
        private void Start()
        {
            gamemanager = FindObjectOfType<GameManager>();
            bGMControl = FindObjectOfType<BGMControl>();
            rb = GetComponent<Rigidbody2D>();
            StartCoroutine(DestroyObjectDelayed(Random.Range(15f, 25f)));
    
            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()
        {
            if (!hasBeenLaunched && !gamemanager.isDragging)
            {
                LaunchBall();
            }
            if (hasBeenLaunched && !isStopped)
            {
                SlowDownBall();
            }
            if (isExpanding)
            {
                ExpandBall();
            }
        }
        void LaunchBall()
        {
            Debug.Log("공을 발사합니다");
            Vector2 launchForce = GameManager.shotDirection * (GameManager.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 * 10;
            isExpanding = true;
        }
        void ExpandBall()
        {
            if (Vector3.Distance(transform.localScale, targetScale) > 0.01f)
            {
                hasExpanded = true;
                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.CompareTag(GojungTag) && !collision.collider.CompareTag(WallTag)) && rb == null)
            {
                Destroy(collision.gameObject);
            }
            this.iscolliding = true;
    
        }
        private void OnCollisionExit2D(Collision2D collision)
        {
            this.iscolliding = false;
        }
        void DestroyRigidbody()
        {
            if (rb != null)
            {
                Destroy(rb);
                rb = null;
            }
        }
        IEnumerator DestroyObjectDelayed(float delayTime)
        {
            yield return new WaitForSeconds(delayTime);
            Destroy(gameObject);
        }
    }

     

    BlackHole 아이템을 제어하는 최적화 된 코드이다

    여기서 싱글플레이용 스크립트는 GameManager를 SPGameManager로 바꿔준뒤

    파괴되기전 spgamemanager.Removeball() 만 넣어주면 된다

     

    using System.Collections;
    using System.Collections.Generic;
    using TMPro;
    using UnityEngine;
    using UnityEngine.SceneManagement;
    
    public class BlackHole_Bullet : MonoBehaviour
    {
        SPGameManager spGameManager;
        Rigidbody2D rb;
        BGMControl bGMControl;
        Vector2 lastVelocity;
        float deceleration = 2f;
        public float increase;
        private bool iscolliding = false;
        public bool hasExpanded = false;
        private bool isStopped = false;
        public PhysicsMaterial2D bouncyMaterial;
        private Vector3 initialScale; // 초기 공 크기
        private Vector3 targetScale; // 목표 크기
        private const string GojungTag = "Gojung";
        private const string WallTag = "Wall";
        private const string EnemyCenterTag = "EnemyCenter";
    
    
    
        private void Start()
        {
            spGameManager = FindAnyObjectByType<SPGameManager>();
            bGMControl = FindAnyObjectByType<BGMControl>();
            rb = GetComponent<Rigidbody2D>();
            GameObject textObject = new GameObject("TextMeshPro");
    
            textObject.transform.parent = transform;
    
            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.CompareTag(GojungTag) || !coll.collider.CompareTag(WallTag)) && rb == null)
            {
                spGameManager.RemoveBall();
                if (coll.collider.CompareTag(WallTag)) return;
                Destroy(coll.gameObject);
            }
            this.iscolliding = true;
        }
        private void OnCollisionExit2D(Collision2D collision)
        {
            this.iscolliding = false;
        }
    
        void DestroyRigidbody()
        {
            if (rb != null)
            {
                Destroy(rb);
                rb = null;
            }
        }
    }

     

    적 유닛이 발사할 BlackHole 총알을 제어하는 스크립트이다

    그래봤자 최종보스 때 딱 한번 나오긴 하지만.. 그래도 필요하다

     

    이렇게 매번 코드를 풀로 다 첨부하는건 너무 가독성이 떨어지니 적어도 이 글안에서 만큼은

    이제부터 코드를 전부 붙여넣지 않겠다

     

    이번엔 Invincible 아이템이다

     

    BlackHole 아이템과 거의 비슷한 원리지만 딱 한가지 다른게 있다면

    BlackHole은 정지후 팽창이 되고나서 부딫히는 모든 구체들은 파괴시키는 특성이 있지면(벽, 고정구체, 적유닛 제외)

    Invincible 아이템의 경우 팽창 상태건 아니건 중요하지 않다. 그리고 파괴하는 대상도 벽을 제외하고는 가리지 않는다

    그리고 이번엔 상대 오브젝트를 파괴하며 본인도 함께 파괴된다는 특성도 있다

     

     if (!collision.collider.CompareTag(WallTag))
            {
                Destroy(collision.gameObject);
                spgamemanager.RemoveBall();
                Destroy(gameObject);
                spgamemanager.RemoveBall();
            }

     

    그래서 태그가 Wall(벽)만 아니라면 둘다 함께 파괴되도록 구현해주었다

     

    Invincible_Bullet도 비슷한 원리기 때문에 설명은 생략하도록 하겠다

     

    마지막 아이템은 바로 Endless 아이템이다

     

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class SEndless_Skill : MonoBehaviour
    {
        BGMControl bGMControl;
        private Rigidbody2D rb;
        private Vector2 lastVelocity;
    
        void Start()
        {
            bGMControl = FindAnyObjectByType<BGMControl>();
            rb = GetComponent<Rigidbody2D>();
            rb.gravityScale = 0;
            rb.velocity = SPGameManager.shotDirection * 3.5f;
    
            // Rigidbody2D 보간(interpolation) 설정을 통해 더 부드러운 이동 적용
            rb.interpolation = RigidbodyInterpolation2D.Interpolate;
    
            // 물리 업데이트를 60FPS로 설정하여 물리 연산의 빈도를 줄임
            Time.fixedDeltaTime = 1f / 60f;
        }
    
        void FixedUpdate()
        {
            lastVelocity = rb.velocity;
        }
    
        void OnCollisionEnter2D(Collision2D collision)
        {
            bGMControl.SoundEffectPlay(0);
            Vector2 normal = collision.contacts[0].normal;
            Vector2 reflectDirection = Vector2.Reflect(lastVelocity.normalized, normal);
    
            rb.velocity = reflectDirection * 3.5f;
        }
    }

     

    Endless 아이템의 경우는 공이 멈추지도 않고 BlackHole 아이템이 아닌이상 파괴되지도 않으며

    내구도 기능도 없다. 정말 말그대로 멈추지 않고 부드럽게 움직이도록만 구현하면 된다

     

    하지만 기존 공의 스크립트와 구조가 많이 다르기 때문에 이번엔 아예 다른 방법을 사용해주었다

    위 스크립트에서 주석을 걸어둔 두 줄을 보자

     

     

    • Rigidbody Interpolation은 물리 시뮬레이션을 부드럽게 만드는 방법 중 하나로 Unity에서 물리 연산(FixedUpdate)은 프레임별로 실행되는 것이 아니라 일정한 주기로 실행되는데, 이때 게임의 프레임률과 물리 엔진의 업데이트 속도가 다를 수 있다.
    • 보간(Interpolate) 옵션은 물리 프레임 간의 객체 이동을 부드럽게 하기 위한 기법이다.
    • Rigidbody2D가 움직일 때 물리 업데이트 주기 사이의 움직임을 보간해서 그래픽 프레임의 부드러움을 향상보통, Interpolate는 물체가 느리게 움직이거나 반복적인 충돌 상황에서 매끄러운 움직임을 구현할 때 사용된다. 게임에서 공이 이동할 때, 화면에서 더 자연스럽고 부드럽게 보이도록 해주는 효과가 있다

    ------------------------------------------------------------------------

     

    • **fixedDeltaTime**은 Unity의 FixedUpdate가 호출되는 주기를 제어해. Unity는 프레임마다 물리 계산을 하지 않고, 일정 주기로만 물리 연산을 실행해 성능을 최적화한다. 그 주기가 바로 fixedDeltaTime에 의해 결정된다
      • 60FPS로 설정하면 물리 계산이 더 자주 일어나기 때문에, 빠르게 움직이는 물체나 충돌이 잦은 게임에서 더 부드럽게 처리된다
      • 이 설정을 통해 물리 연산 주기를 그래픽 프레임 속도와 비슷하게 맞춰, 물리적인 객체의 움직임이 더 자연스럽게 보일 수 있다

    이 두가지 방식을 추가하면 더 부드럽게 움직이는 아이템을 확인할 수 있다

     

    이렇게 BlackHole, Invincible, Endless 총 3가지의 아이템을 최적화보았다

     

     

    추가로 기본 BallController 스크립트를 사용하는 3개의 아이템들도 전부 최적화 된 스크립트로 넣어주었다

     

    물론 폰트 사이즈와 팽창 배수는 각각 다르게 설정해주었다

    이로써 싱글플레이에 나오는 모든 아이템, 기본 구체, 적 총알을 최적화 해주었다

     

     

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

    최적화 - 10 (설정 수정★)  (0) 2024.10.09
    최적화 - 9  (1) 2024.10.09
    최적화 - 7  (0) 2024.09.25
    최적화 - 6  (1) 2024.09.24
    최적화 - 5 (BallController 스크립트 제작#2)  (0) 2024.09.20
Designed by Tistory.