ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 최적화 - 4 (BallController 스크립트 제작#1)
    Galaxy Ball/5. 최적화 2024. 9. 20. 00:44
     

    최적화 - 3

    사실 나도 어떤 부분을 손대야 게임이 부드럽게 구현되는지는 알 수 없다.그러니 될 때까지 의심가는 부분들을 전부 하나씩 손을 봐가며 진행할 뿐...이번엔 구체를 직접 컨트롤하는 BallController

    sangeun00.tistory.com

     

    지난번 글에서 말했던대로 BallController 스크립트를 아예 처음부터 작성해보도록 하겠다

     

    ....라고 말은 했지만 너무 막막하다. 아예 백지 상태에서 할수도 없고 도대체 어디서부터 시작해야할까. 

    그래서 아예 새로운 연습용 씬을 만든뒤 코드를 써보기로 했다

     

     

    근데 또 씬을 새롭게 하나 만들자니 결국 실전에 쓰이는 씬을 그대로 만들려고 하는걸 볼 수 있었다

    이렇게 하면 결국 똑같은거 아닌가..? 달라지는게 뭐지라는 생각이 들었으나 

     

    내가 지금 개선하고 싶은 부분은 한가지.

    바로 update 안에서 1프레임마다 가동중인 Move와 expand 메서드이다

     

    이제 와서 이걸 수정하기엔 너무 코드가 꼬여버리니 아예 처음부터 만들어 이것들을 해결해보는것이 주 목적이다

    그러니 연습용 씬에서는 다른 요소들 전부 제외시키고,

    firezone에서 클릭하면 프리팹이 생성되고, 잡아당기면 날아가는것까지만 구현되도록 가져왔다

     

    한번 더 상기위해 적어두자면 내가 원하는건 update를 최대한 조금 이용하여 이동과 팽창을 구현하는것이다

     

    1. 공 발사 & 이동

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class ExBallController : MonoBehaviour
    {
        SPGameManager spgamemanager;
        Rigidbody2D rb;
        bool hasBeenLaunched = false; // 공이 이미 발사되었는지 확인
    
        void Start()
        {
            spgamemanager = FindAnyObjectByType<SPGameManager>();
            rb = GetComponent<Rigidbody2D>();
        }
    
        void Update()
        {
            // SPGameManager에서 공이 발사될 준비가 되었고, 아직 발사되지 않은 경우
            if (!hasBeenLaunched && !spgamemanager.isDragging)
            {
                LaunchBall();
            }
        }
    
        void LaunchBall()
        {
            
            Vector2 launchForce = spgamemanager.shotDirection * spgamemanager.shotDistance;
            rb.AddForce(launchForce, ForceMode2D.Impulse);
    
            // 공이 이미 발사되었음을 기록
            hasBeenLaunched = true;
        }
    }

     

    가장 먼저 공이 발사되는것.

    즉 드래그를 떼는 순간 SPGameManager에서 힘과 방향값을 받아와 발사하는 것만 구현해보았다

     

    기존 코드와 다른것이 있다면 LaunchBall 이라는 메서드를 따로 만들어

    rg.velocity 대신 AddForce로 공을 날려주었다

     

    2. 공 정지

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class ExBallController : MonoBehaviour
    {
        SPGameManager spgamemanager;
        Rigidbody2D rb;
        bool hasBeenLaunched = false;
        private float decelerationThreshold = 0.4f; // 속도가 이 값 이하일 때 공을 정지시킴
        private float dragAmount = 1.1f; // 감속을 위한 drag 값
    
        void Start()
        {
            spgamemanager = FindAnyObjectByType<SPGameManager>();
            rb = GetComponent<Rigidbody2D>();
    
            rb.drag = 0f;
        }
    
        void Update()
        {
            if (!hasBeenLaunched && !spgamemanager.isDragging)
            {
                LaunchBall();
            }
    
            if (hasBeenLaunched)
            {
                SlowDownBall();
            }
        }
    
        void LaunchBall()
        {
            Vector2 launchForce = SPGameManager.shotDirection * SPGameManager.shotDistance;
            rb.AddForce(launchForce, ForceMode2D.Impulse);
    
            rb.drag = dragAmount;
            hasBeenLaunched = true;
        }
    
        void SlowDownBall()
        {
            if (rb.velocity.magnitude <= decelerationThreshold)
            {
                rb.velocity = Vector2.zero; // 속도를 0으로 설정하여 공을 정지시킴
            }
        }
    }

     

    이것 역시 SlowDownBall이라는 메서드를 만들어주었고, 이번엔 rb.drag이라는 기능을 처음 사용해보았다

     

     

    • 물리 엔진 내장 기능 사용: Unity의 Rigidbody2D.drag는 물리 엔진에 최적화된 기능으로, 별도로 계산하는 것보다 효율적으로 속도를 줄입니다. Unity 엔진이 내부적으로 최적화된 방식을 사용하므로, 직접적인 수식이나 계산을 적용하는 것보다 성능에 부담이 적습니다.
    • 프레임에 미치는 영향 최소화: drag는 매 프레임마다 적용되지만, 그 연산은 매우 간단하며 물리 엔진의 다른 처리 과정에 통합되어 있기 때문에 추가적인 연산 부담이 거의 없습니다.
    • 간단한 코드: drag를 사용하면 별도의 감속 수식을 작성할 필요가 없으므로 코드가 간결해지고, 불필요한 연산을 줄여 성능에 유리합니다.
    • 일관된 감속: Unity는 drag를 통해 물리적으로 일관된 방식으로 감속을 처리합니다. 이것은 물리 법칙에 기반해 적용되기 때문에, 직접적인 수식 적용보다 자연스러운 결과를 제공합니다.

    이렇게 최적화에 좋다는데 사용하지 않을 이유가 없다

     

     

    3. 공 반사


    자 그 다음에 해볼것은 반사이다. 어떤 오브젝트던간에 부딫히는 순간 자연스럽게 반사시켜줘야 한다

    게임을 처음 만들때 이 부분을 어떻게 해야할지 몰라 굉장히 애먹었었던것 같은데 이번엔 아예 물리엔진을 사용하기로 했다

     

    public class ExBallController : MonoBehaviour
    {
        SPGameManager spgamemanager;
        Rigidbody2D rb;
        bool hasBeenLaunched = false;
        private float decelerationThreshold = 0.4f; 
        private float dragAmount = 1.1f;
        public PhysicsMaterial2D bouncyMaterial; // 공의 Collider2D에 적용할 물리 재질

     

     

    딱 한줄만 추가해주면 된다

     

     

    그리고 Asset창에서 Create -> 2D -> Physics Material 2D를 추가해준다

     

     

    그럼 이렇게 생긴 아이콘이 추가되고 Inspector창에서 2가지를 조절할 수 있는데

     

    Friction은 공의 마찰, Bounciness는 반사 강도를 의미한다

    사실 공의 마찰은 클수록 팅겨지는게 부자연스러워지기 때문에 최대한 적게,

    반사강도는 가장 기본인 1로 설정했다

     

     

    그리고 이렇게 설정한 Material을 스크립트 안에 부착해주기만 하면 끝! 한번 확인해보자

     

     

     

    오...진짜 자연스럽다. 100퍼 만족은 아니지만 이건 얼마든지 세세한 수치를 조율해주면 될 것 같다

     

     

    이게 기존 방식이다. 충돌이 일어날때마다 이걸 계산한다

    그래서 물리엔진과 코드작성중에 뭐가 더 최적화에 적합할까? 라고 묻는다면

     

    라고 한다... 그리고 애초에 물리엔진 자체가 고도로 최적화 되어있다고 하니 이걸 안쓸 이유가 없다

     

    5. 공 고정 & 팽창

    은근히 복잡하면서 개발 초반에 많이 애먹었던 고정과 팽창이다

    물론 고정과 팽창 자체는 따로 놓고 봤을때 어렵지 않다. 

     

    다만 정지를 멈추자마자 고정을 시키고, 팽창을 진행하다 다른 오브젝트와 충돌이 일어났을때 팽창을 멈추는것

    은 복잡하다...

     

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class ExBallController : MonoBehaviour
    {
        SPGameManager spgamemanager;
        Rigidbody2D rb;
        bool hasBeenLaunched = false;
        bool isExpanding = false; // 공이 팽창 중인지 여부를 추적합니다.
        bool isStopped = false; // 공이 완전히 멈췄는지 여부
        private float decelerationThreshold = 0.4f;
        private float dragAmount = 1.1f;
        private float expandSpeed = 0.5f; // 팽창 속도
        private Vector3 initialScale; // 초기 공 크기
        private Vector3 targetScale; // 목표 크기
        public PhysicsMaterial2D bouncyMaterial; // 공의 Collider2D에 적용할 물리 재질
    
        void Start()
        {
            spgamemanager = FindAnyObjectByType<SPGameManager>();
            rb = GetComponent<Rigidbody2D>();
    
            rb.drag = 0f;
            rb.collisionDetectionMode = CollisionDetectionMode2D.Continuous;
            rb.interpolation = RigidbodyInterpolation2D.Interpolate;
    
            // 물리 재질이 설정된 경우 Collider2D에 적용
            Collider2D collider = GetComponent<Collider2D>();
            if (collider != null && bouncyMaterial != null)
            {
                collider.sharedMaterial = bouncyMaterial;
            }
    
            initialScale = transform.localScale; // 초기 공 크기 저장
        }
    
        void Update()
        {
            if (!hasBeenLaunched && !spgamemanager.isDragging)
            {
                LaunchBall();
            }
    
            if (hasBeenLaunched)
            {
                SlowDownBall();
                if (isExpanding)
                {
                    // 부드럽게 팽창
                    transform.localScale = Vector3.Lerp(transform.localScale, targetScale, Time.deltaTime * expandSpeed);
    
                    // 팽창 목표 크기에 도달하면 멈춤
                    if (Vector3.Distance(transform.localScale, targetScale) < 0.01f)
                    {
                        transform.localScale = targetScale;
                        isExpanding = false;
                    }
                }
            }
        }
    
        void LaunchBall()
        {
            Vector2 launchForce = SPGameManager.shotDirection * SPGameManager.shotDistance;
            rb.AddForce(launchForce, ForceMode2D.Impulse);
    
            rb.drag = dragAmount;
            hasBeenLaunched = true;
        }
    
        void SlowDownBall()
        {
            if (rb == null) return;
            if (rb.velocity.magnitude <= decelerationThreshold && !isStopped)
            {
                rb.velocity = Vector2.zero; // 속도를 0으로 설정하여 공을 정지시킴
                isStopped = true;
    
                // 팽창 시작
                targetScale = initialScale * 10f; // 초기 크기의 10배로 설정
                isExpanding = true;
            }
        }
    
        private void OnCollisionEnter2D(Collision2D collision)
        {
            if (!collision.collider.isTrigger && isExpanding)
            {
                // 충돌 시 팽창을 멈추고 크기 고정
                isExpanding = false;
                transform.localScale = transform.localScale; // 현재 크기에서 멈춤
                Destroy(rb); // 팽창이 멈추는 순간 rb 삭제
            }
        }
    }

     


     void SlowDownBall()
        {
            if (rb == null) return;
            if (rb.velocity.magnitude <= decelerationThreshold && !isStopped)
            {
                rb.velocity = Vector2.zero; // 속도를 0으로 설정하여 공을 정지시킴
                isStopped = true;

                // 팽창 시작
                targetScale = initialScale * 10f; // 초기 크기의 10배로 설정
                isExpanding = true;
            }
        }

     

    우선 팽창이 되는 부분이다. 기약없이 계속해서 팽창하던 전과 달리 이번엔

    애초에 초기 크기의 10배로 맥시멈을 잡은뒤 팽창을 시작한다. 이 방식이 게임의 성능에 도움이 된다고 한다

     

     

    그리고 기존에는 속도가 0이 되는순간 rb를 삭제했다면

     

     

     private void OnCollisionEnter2D(Collision2D collision)
        {
            if (!collision.collider.isTrigger && isExpanding)
            {
                // 충돌 시 팽창을 멈추고 크기 고정
                isExpanding = false;
                transform.localScale = transform.localScale; // 현재 크기에서 멈춤
                Destroy(rb); // 팽창이 멈추는 순간 rb 삭제
            }

     

    이번엔 팽창도중 충돌이 일어나는 순간 rb를 삭제한다

     

     

     

    정상적으로 잘 작동하는것을 확인할 수 있다

     

     

     

     

     

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

    최적화 - 6  (1) 2024.09.24
    최적화 - 5 (BallController 스크립트 제작#2)  (0) 2024.09.20
    최적화 - 3  (1) 2024.09.19
    최적화 - 2  (1) 2024.09.10
    최적화 - 1  (0) 2024.09.10
Designed by Tistory.