본문 바로가기
Development/C#

멋쟁이사자처럼부트캠프 Unity 게임 개발 3기 26일차

by Mobics 2024. 12. 26.

 

목차


    앵그리버드 만들기

    24.12.26

    발사 궤적 구현

    : Slingshot.cs에 코드 추가

    >> 발사 궤적 구현을 위한 변수 선언

    public GameObject trajectory;
    private List<GameObject> trajectoryObjects = new List<GameObject>();
    public int maxStep = 20; // 궤적의 최대 점 수 --> 작을수록 궤적이 짧아진다
    public float timeStep = 0.1f; // 시간 간격 --> 작을수록 점이 촘촘해진다

     

    >> 발사 궤적 구현

    void PredictTrajectory(Vector3 force)
    {
        ClearTrajectory();
    
        Vector3 position = center.position;
        Vector3 velocity = force / bird.mass; // 물체의 초기 속도 (F = ma --> v = F/m)
    
        for (int i = 0; i < maxStep; i++)
        {
            float timeElapsed = timeStep * i; // timeElapsed : 현재까지 경과 시간
            Vector3 gravity = Physics.gravity;
            // 등가속도 운동 --> 각 시간 간격마다 계산된 물체의 위치를 리스트에 추가
            Vector3 trajectoryPoint = position +
                                      velocity * timeElapsed +
                                      gravity * (0.5f * timeElapsed * timeElapsed);
            
            var trajectoryObject = Instantiate(trajectory, trajectoryPoint, Quaternion.identity);
            trajectoryObjects.Add(trajectoryObject);
        }
    }
    
    private void ClearTrajectory()
    {
        foreach (var obj in trajectoryObjects)
        {
            Destroy(obj);
        }
        trajectoryObjects.Clear();
    }

    ※ 등가속도 운동

     

    >> 마우스를 눌렀을 때 발사 궤적 표시

    void Update()
    {
        if (isMouseDown && MainCamera != null)
        {
            Vector3 mousePosition = Input.mousePosition;
            mousePosition.z = 10;
            currentPosition = MainCamera.ScreenToWorldPoint(mousePosition);
            currentPosition = center.position + Vector3.ClampMagnitude(currentPosition - center.position, maxDistance);
    
            currentPosition = ClampBoundary(currentPosition);
            
            SetStrips(currentPosition);
    
            if (birdCollider) // 안정성 --> Shoot()에서 null로 설정했기 때문에
            {
                birdCollider.enabled = true; // bird를 마우스로 당길 때, Collider가 필요
            }
    
            Vector3 birdForce = (currentPosition - center.position) * (force * -1);
            PredictTrajectory(birdForce);
        }
        else
        {
            ResetStrips();
            ClearTrajectory();
        }
    }

    └ 표시되는 궤적과 실제 발사되는 궤적이 다른 문제(보완 완료)

    : 표시되는 궤적은 새총의 중심을 출발점으로 계산하고 있지만 실제 발사되는 궤적은 새를 끌어당긴 지점을 출발점으로 계산하고 있기 때문에 차이가 발생

    >> Bird.cs

    void PredictTrajectory(Vector3 force)
    {
        ClearTrajectory();
    
        Vector3 position = currentPosition; // center.position --> currentPosition으로 수정
        Vector3 velocity = force / bird.mass;
    
        for (int i = 0; i < maxStep; i++)
        {
            float timeElapsed = timeStep * i;
            Vector3 gravity = Physics.gravity;
            Vector3 trajectoryPoint = position +
                                      velocity * timeElapsed +
                                      gravity * (0.5f * timeElapsed * timeElapsed);
            
            var trajectoryObject = Instantiate(trajectory, trajectoryPoint, Quaternion.identity);
            trajectoryObjects.Add(trajectoryObject);
        }
    }

     

    카메라 이동 구현

    Bird.cs에 코드 추가

    >> 카메라 추가 및 새의 위치에 따른 카메라 이동

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using Unity.VisualScripting;
    using UnityEngine;
    
    public class Bird : MonoBehaviour
    {
        private Rigidbody2D _rigidbody;
    
        private Camera mainCamera;
    
        private void Awake()
        {
            _rigidbody = GetComponent<Rigidbody2D>();
            mainCamera = Camera.main;
        }
    
        private void OnCollisionEnter2D(Collision2D other)
        {
            if (_rigidbody != null)
            {
                _rigidbody.drag = 1.0f;
            }
            Destroy(gameObject, 3.0f);
        }
    
        private void LateUpdate() // Bird가 날아갈 때 카메라 이동
        {
            if (transform.position.x > mainCamera.transform.position.x)
            {
                var vector3 = mainCamera.transform.position;
                vector3.x = transform.position.x;
                mainCamera.transform.position = vector3;
            }
        }
    
        private void OnDestroy() // Bird가 사라지면 다시 카메라 원위치
        {
            if (!mainCamera.IsUnityNull())
                mainCamera.GetComponent<ReturnToPosition>().StartReturnToPosition();
            // Destroy할 때, UnityEngine에서는 null이지만 System에서는 null이 아니게 되는 Fake null 현상을 대비
        }
    }

     

    >> 카메라 이동 구현

    : ReturnToPosition.cs 를 만들어서 MainCamera에 추가

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using Unity.VisualScripting;
    using UnityEngine;
    
    public class ReturnToPosition : MonoBehaviour
    {
        [SerializeField] private float returnTime;
        private Vector3 startPosition;
        
        void Start()
        {
            startPosition = transform.position; // 처음 카메라 위치 저장
        }
    
        public void StartReturnToPosition()
        {
            StartCoroutine(ReturnToPositionCoroutine());
        }
    
        IEnumerator ReturnToPositionCoroutine()
        {
            float duration = 0.0f;
            Vector3 initPosition = transform.position; // 이동한 카메라 위치 저장
    
            while (true)
            {
                float lerp = Mathf.Min(duration, returnTime) / returnTime; // duration 값을 0~1로 보간
                // Mathf.Min(a, b) : a와 b 중 더 작은 값 반환
                Vector3 newPos = Vector3.Slerp(initPosition, startPosition, lerp); // Slerp : 구면 선형 보간
                // 이동한 카메라 위치로부터 처음 카메라 위치까지 부드럽게 보간
                transform.position = newPos; // 보간한 위치로 이동
    
                if (lerp >= 1.0f) // lerp가 1이면 처음 위치로 이동을 완료한 것이니 반복 탈출
                    break;
    
                duration += Time.deltaTime;
                
                yield return new LateUpdate(); // LateUpdate까지 정지 후 재개
            }
    
            transform.position = startPosition;
            // Slerp를 하면 미세한 오차(ex. 1.00001)가 발생할 수 있으므로 다시 startPosition으로 위치 재설정
        }
    }