ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • (4) 구체 팽창 & 팽창 후 고정
    Galaxy Ball/1. 멀티플레이 - 대전모드 2024. 3. 25. 18:13

    이번엔 구체가 정지한뒤 다른 콜라이더와 충돌할때까지 팽창하는것을 구현해보겠다

     

    using System.Collections;
    using TMPro;
    using UnityEngine;
    
    public class BallController : MonoBehaviour
    {
        Rigidbody2D rigid;
        Vector2 lastVelocity;
        float deceleration = 2f;
        public float increase = 4f;
        private bool iscolliding = false;
        private bool isStopped = false;
    
        private void Start()
        {
            rigid = GetComponent<Rigidbody2D>();
            rigid.velocity = transform.up * 300 * Time.deltaTime;
        }
    
        private void Update()
        {
            Move();
            expand();
        }
    
        void Move()
        {
            if (rigid == null || isStopped) return;
    
            lastVelocity = rigid.velocity;
            rigid.velocity -= rigid.velocity.normalized * deceleration * Time.deltaTime;
            
            if (rigid.velocity.magnitude <= 0.01f && hasExpanded)
            {
                isStopped = true;
            }
        }
    
        void expand()
        {
            if (rigid == null || iscolliding) return;
            if (rigid.velocity.magnitude > 0.01f) return;
            transform.localScale += Vector3.one * increase * Time.deltaTime;
        }
    
        private void OnCollisionEnter2D(Collision2D coll)
        {
            if (coll.contacts != null && coll.contacts.Length > 0)
            {
                // 충돌 지점 정보가 있을 때만 처리
                Vector2 dir = Vector2.Reflect(lastVelocity.normalized, coll.contacts[0].normal);
                if (rigid != null)
                    rigid.velocity = dir * Mathf.Max(lastVelocity.magnitude, 0f); // 감속하지 않고 반사만 진행
            }
            this.iscolliding = true;
        }
        private void OnCollisionExit2D(Collision2D collision)
        {
            this.iscolliding = false;
        }
    }

     

    전역변수로 다른 콜라이더와 충돌여부를 확인할수 있는 iscolliding, 구체의 정지여부를 확인할 수 있는 isStopped 추가

     

    Update문에는 확장을 수행하는 expand 메서드를 추가하고 만든다

     

    void expand()
        {

            확장을 하기전,
            if (rigid == null || iscolliding) return;

            만약 rigid값이 없거나 이미 다른 콜라이더와 부딫힌 상태라면 확장안함
            if (rigid.velocity.magnitude > 0.01f) return;

            또 만약 구체의 속도가 0.01이상이면 확장안함(사실상 정지상태가 아니면 확장안한다는뜻)
            transform.localScale += Vector3.one * increase * Time.deltaTime;

            만약 이 모든걸 뚫고 내려왔다면 그때 확장해줌
        }

     

    이제 맨 처음 조건에 나온 iscolliding의 상태를 상황에 따라 변화를 줘야한다

     private void OnCollisionEnter2D(Collision2D coll)
        {
            .......
            .......
            this.iscolliding = true;
        }
        private void OnCollisionExit2D(Collision2D collision)
        {
            this.iscolliding = false;
        }

    간단하다. 충돌했다면 true, 떨어졌다면 false를 주면 된다

     

    이렇게 스크립트를 작성한다면

    "구체가 멈추지않고, 다른 콜라이더와 충돌하지도 않았다면 충돌할때까지 팽창한다"를 시전한다

     

     

     

    확인해보면 잘 확장하는것을 볼 수 있다

     

    자 그리고 지금까지 프로젝트를 해오며 가장 오랜 시간이 걸렸고 정말 애를 많이 먹었던 부분이다

     

    바로 구체가 확장이 끝나면 완벽하게 그 자리에 고정되어 다른 충돌이 들어오더라도 본인은 움직이지 않는것이다

    이것 역시 말로만 들으면 정말 별게 아닌것같다.

    근데 문제는 고정을 시키면 확장이 멈추질않고, 확장을 멈추게 하면 고정이 되질 않는다는것이다

     

    처음 생각했던건 구체가 멈추고, 팽창이 끝나는순간 iskinematic 혹은 Static으로 변경하는것이었다

    하지만 이렇게 하니 다른 콜라이더와 충돌이 허용되어 무한팽창이 되어버리고 만다

     

    두번째로 생각했던건 expand 메서드를 불러오는데 조건문을 걸어 특정상황에만 팽창을 하고 멈추게 시키는것이었다

    하지만 이것도 실패...

     

    이미 다 해결한 시점에서 글을 적는거라 이렇게 간단하게 적어내리지만 정말 별의별 시도를 다 해본것같다

    그리고 결국 찾아낸 방법은 바로 코루틴과 rigidbody2D를 직접 파괴하는것

     

    애초에 나는 rigidbody를 파괴할수 있는건지 몰랐다. 하지만 만약 파괴할수 있다면, 더이상 충돌에도 움직이지않고 고정될것이고, Circle Collider는 남아있으니 충돌판정은 그대로 일어날것이다

     

    using System.Collections;
    using UnityEngine;
    
    public class BallController : MonoBehaviour
    {
        Rigidbody2D rigid;
        Vector2 lastVelocity;
        float deceleration = 2f;
        public float increase = 4f;
        private bool iscolliding = false;
        public bool hasExpanded = false;
        private bool isStopped = false;
    
        private void Start()
        {
            rigid = GetComponent<Rigidbody2D>();
            rigid.velocity = transform.up * 300 * Time.deltaTime;
        }
    
        private void Update()
        {
            Move();
            expand();
        }
    
        void Move()
        {
            if (rigid == null || isStopped) return;
    
            lastVelocity = rigid.velocity;
            rigid.velocity -= rigid.velocity.normalized * deceleration * Time.deltaTime;
    
            if (rigid.velocity.magnitude <= 0.01f && hasExpanded)
            {
                isStopped = true;
                StartCoroutine(DestroyRigidbodyDelayed());
            }
        }
    
        void expand()
        {
            if (rigid == null || iscolliding) return;
            if (rigid.velocity.magnitude > 0.01f) return;
            transform.localScale += Vector3.one * increase * Time.deltaTime;
            hasExpanded = true;
        }
    
        private void OnCollisionEnter2D(Collision2D coll)
        {
            if (coll.contacts != null && coll.contacts.Length > 0)
            {
                // 충돌 지점 정보가 있을 때만 처리
                Vector2 dir = Vector2.Reflect(lastVelocity.normalized, coll.contacts[0].normal);
                if (rigid != null)
                    rigid.velocity = dir * Mathf.Max(lastVelocity.magnitude, 0f); // 감속하지 않고 반사만 진행
            }
            this.iscolliding = true;
        }
        private void OnCollisionExit2D(Collision2D collision)
        {
            this.iscolliding = false;
        }
    
        IEnumerator DestroyRigidbodyDelayed()
        {
            yield return new WaitForSeconds(0.8f);
            if (rigid != null)
                Destroy(rigid);
        }
    }

     

    Move 메서드에서 만약 구체가 멈추고 팽창이 끝났다면(hasExpanded = true)

    DestroyRigidbodyDelayed() 코루틴을 실행한다

     

    DestroyRigidbodyDelayed() 코루틴이란, 0.8초를 기다린뒤 해당 오브젝트의 rigidbody를 제거해버리는 코루틴이다

    왜 하필이면 0.8초냐. 충분히 팽창할 시간은 줘야하고, 그렇다고 너무 시간을 오래줘도 다음턴에

    상대가 날린 구체에 맞아 이동해버릴수도 있으니 그 사이에 적당한 시간을 내가 임의로 정한것이다

     

     

    잠깐의 여유만 준다면 성공적으로 구체가 고정된것을 확인할 수 있다

     

    만약 한창 이걸로 머리 아파하고 있을때 글을 썼다면 분량이 배가 되었을텐데 다행이다ㅎ

Designed by Tistory.