Chapter 3-4. 파괴 가능한 환경들 : 나무 벌목하기

Date:     Updated:

카테고리:

태그:

인프런에 있는 케이디님의 [유니티 3D] 실전! 생존게임 만들기 - Advanced 강의를 듣고 정리한 필기입니다. 😀
🌜 강의 들으러 가기 Click

Chapter 3. 파괴 가능한 환경들


벌목 하기

  • 나무에 여러 군데 흠집을 내고
  • 나무의 모든 면에 흠집이 나있는 상태에서 나무를 타격하면
  • 나무가 쓰러지고
  • 통나무 3 개로 변하게 구현


🚖 나무 구성 오브젝트들 설정

나무 구조

image

  • Tree 👉 빈 오브젝트
    • Stump
      • 나무의 아랫 부분.
      • 나무를 벌목 하고 나면 이 부분만 남는다.
    • Tree
      • 나무의 윗 부분
      • 나무를 벌복 하고 나면 이 Tree는 땅에 쓰러진다.
    • 5 개의 TreePiece
      • 나무의 이 5 개의 TreePiece 부분을 때릴 때 마다 각각 파괴 됨.
    • 1 개의 TreePieceMiddle
      • 5 개 조각을 다 때리고 마지막 가운데 피스까지 때려서 모든 조각을 때리게 되면 나무의 윗부분이 베여 쓰러지게 할 것이다.


컴포넌트 설정

  • Tree
    • Capsule Collider 붙어있는 상태
      • 평소에 활성화 해두되 나무가 쓰러질 땐 비활성화 해주어야 한다.
        • 아래에 후술할 나무 윗부분의 Capsule Collider 가 나무가 쓰러질 떄 활성화 되는데, 그럼 두 콜라이더가 맞닿게 되서 엄청난 충돌이 일어난다.
        • 따라서 나무가 쓰러지면 전체적인 나무 형태에 붙어있는 Capsule Collider를 꺼주어야 한다.
    • “Tree” 태그 달아주기
    • Stump
      • Box Collider
    • Tree
      • Rigidbody
        • Gravity 꺼두기 (쓰러졌을 때 활성화)
        • 나무 베기가 완료 되면 중력에 의하여 쓰러져야 하니까
      • Capsule Collider
        • 비활성화 해두기 (쓰러졌을 때 활성화)
        • 쓰러졌을 때 땅에 충돌되어 땅을 뚫지 않도록
        • 나무 윗부분 크기에 맞게 크기 조절해주기

전체적인 나무에 하나, 쓰러질 나무 윗 부분에 하나 이렇게 Capsule Collider가 2 개 붙어있는 상태.


🚖 나무 때릴 때 생성할 이펙트 효과

image

  • 나무 때릴 때 생성시킬 이펙트로 쓸 Tree_Hit_Effect_Prefab 프리팹
    • 여러 조각들로 구성되어 있다.
    • 나무를 때릴 때마다 이 조각들의 생성 시키자.
      • 생성되자마자 Collider 와 중력에 의해 조각들이 각각 떨어질 것이다.
  • Tree_Hit_Effect_Prefab의 조각인 자식 오브젝트들 하나 하나
    • Box Collider 붙이기
    • Rigidbody 붙이기
      • 질량은 아주 가볍게
  • 🟦프리팹으로 만들자.


🚖 나무 때릴 때 도끼질할 때 애니메이션

AxeController 애니메이션 컨트롤러 수정

image

  • Chop 트리거 파라미터를 추가하였다.
  • Any State 👉 Axe_Tree
    • Chop 트리거가 발동될 때 전이
  • Axe_Tree 👉 Axe_Idle
    • Has Exit Time 체크
    • Exit Time 1 끝까지 재생한 후 전이


📜CloseWeapon.cs

근접 무기

    public float attackDelay;  // 공격 딜레이. 마우스 클릭하는 순간 마다 공격할 순 없으므로.
    public float attackDelayA;  // 공격 활성화 시점. 공격 애니메이션 중에서 주먹이 다 뻗어졌을 때 부터 공격 데미지가 들어가야 한다.
    public float attackDelayB;  // 공격 비활성화 시점. 이제 다 때리고 주먹을 빼는 애니메이션이 시작되면 공격 데미지가 들어가면 안된다.

    public float workDelay;  // 작업 딜레이
    public float workDelayA;  // 작업 활성화 시점.
    public float workDelayB;  // 작업 비활성화 시점. 

도끼나 곡괭이 같은 CloseWeapon 근접무기들은 공격 뿐만하니라 나무나 풀을 베는, 즉 일하는 작업도 있기 때문에 각각의 작업을 하는 애니메이션이 따로 있다.(도끼의 경우 나무 벨 때 애니메이션이 따로 존재) 따라서 공격 애니메이션 기준의 딜레이 시간을 사용하면 잘 안맞을 수 있다. 따라서 📜CloseWeapon.cs에 작업 애니메이션의 총 지연 시간 workDelay, 애니메이션 중 작업이 처리되는 활성화되는 시점 workDelayA, 애니메이션 중 작업이 비활성화 처리되는 시점 workDelayB.

image


📜CloseWeaponController.cs

근접 무기 컨트롤러

    protected void TryAttack()
    {
        if (Input.GetButton("Fire1"))
        {
            if (!isAttack)
            {
                if(CheckObject())
                {
                    if (currentCloseWeapon.isAxe && hitInfo.transform.tag == "Tree")
                    {
                        StartCoroutine(AttackCoroutine("Chop", currentCloseWeapon.workDelayA, currentCloseWeapon.workDelayB, currentCloseWeapon.workDelay));
                        return;
                    }
                }
                
                StartCoroutine(AttackCoroutine("Attack", currentCloseWeapon.attackDelayA, currentCloseWeapon.attackDelayB, currentCloseWeapon.attackDelay));
            }
        }
    }

    protected IEnumerator AttackCoroutine(string swingType, float _delayA, float _delayB, float _delay)
    {
        isAttack = true;
        currentCloseWeapon.anim.SetTrigger(swingType);

        yield return new WaitForSeconds(_delayA);
        isSwing = true;

        StartCoroutine(HitCoroutine());

        yield return new WaitForSeconds(_delayB);
        isSwing = false;

        yield return new WaitForSeconds(_delay - _delayA - _delayB);
        isAttack = false;
    }
  • AttackCoroutine 함수에 매개 변수 추가
    • 수정하기 전
      • 애니메이션 재생과 코루틴 지연 시간을 가지는데에 있어서 공격 애니메이션을 재생하는데에만 기능하였다.
    • 수정한 후
      • 👉 매개 변수들을 추가해줌으로서 공격 뿐만 아니라 얼마든지 다른 작업에 대해서도 그에 맞는 애니메이션을 재생할 수 있도록 애니메이션 파라미터 문자열과 코루틴 지연시간을 인수로 받았다.
        • 이렇게 인수를 추가하지 않았다면 WorkCorouine() 이렇게 따로 또 함수를 만들어야 했을지도 모른다!

    /* 수정 전 */
    protected IEnumerator AttackCoroutine()
    {
        isAttack = true;
        currentCloseWeapon.anim.SetTrigger("Attack");

        yield return new WaitForSeconds(currentCloseWeapon.attackDelayA);
        isSwing = true;

        StartCoroutine(HitCoroutine());

        yield return new WaitForSeconds(currentCloseWeapon.attackDelayB);
        isSwing = false;

        yield return new WaitForSeconds(currentCloseWeapon.attackDelay - currentCloseWeapon.attackDelayA - currentCloseWeapon.attackDelayB);
        isAttack = false;
    }


    /* 수정 후 */
    protected IEnumerator AttackCoroutine(string swingType, float _delayA, float _delayB, float _delay)
    {
        isAttack = true;
        currentCloseWeapon.anim.SetTrigger(swingType);

        yield return new WaitForSeconds(_delayA);
        isSwing = true;

        StartCoroutine(HitCoroutine());

        yield return new WaitForSeconds(_delayB);
        isSwing = false;

        yield return new WaitForSeconds(_delay - _delayA - _delayB);
        isAttack = false;
    }
  • TryAttack() 마우스 좌클 입력으로 공격하려는데 CheckObject() 충돌된 대상이 있다면
    • 그 때 현재의 무기가 도끼이며 충돌된 대상이 나무라면
      • AttackCoroutine 함수에 인수로 “Chop” 과 작업과 관련된 딜레이 시간들을 넘겨 벌목하는 작업에 맞는 애니메이션을 재생하도록 한다.
        StartCoroutine(AttackCoroutine("Chop", currentCloseWeapon.workDelayA, currentCloseWeapon.workDelayB, currentCloseWeapon.workDelay));
        
      • 리턴하고 종료시킨다.
    • 리턴되지 않았다면 그냥 공격하기 위해 좌클 눌렀던 것일테니 공격에 맞는 애니메이션을 재생하도록 한다.
    if (!isAttack)
    {
        if(CheckObject())
        {
            if (currentCloseWeapon.isAxe && hitInfo.transform.tag == "Tree")
            {
                StartCoroutine(AttackCoroutine("Chop", currentCloseWeapon.workDelayA, currentCloseWeapon.workDelayB, currentCloseWeapon.workDelay));
                return;
            }
        }
                
        StartCoroutine(AttackCoroutine("Attack", currentCloseWeapon.attackDelayA, currentCloseWeapon.attackDelayB, currentCloseWeapon.attackDelay));
    }


🚖 작은 통나무 프리팹

image

  • Log 프리팹 👉 큰 나무가 쓰러지고 파괴되면 3 개의 Log 통나무 오브젝트를 생성.
    • Rigidbody 붙이기
    • Capsule Collider 붙이기
      • 통나무 크기에 맞춰 크기 조절하기
        • Direction으로 축을 변경할 수 있다. 크기의 기준이 되는 축.
          • Z-Axis 앞뒤 크기
          • X-Axis 좌우 크기
          • Y-Axis 위아래 크기
    • Material 을 Mesh 에 추가
      • 텍스처 파일을 Material의 Albedo 에 입힐 수 있다.


🚖 나무 벌목 하기

📜TreeComponent.cs

Tree 부모 오브젝트에 붙여주기

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TreeComponent : MonoBehaviour
{
    [SerializeField]
    private GameObject[] go_treePieces;  // 가운데 조각을 제외한 깎일 5 개의 나무 테두리 조각들.
    [SerializeField]
    private GameObject go_treeCenter;  // 가운데 조각. 이 조각이 마지막으로 파괴되면 나무가 쓰러져야 한다.

    [SerializeField]
    private GameObject go_Log_Prefabs;  // 통나무. 나무가 쓰러진 이후 생성할.
         
    [SerializeField]
    private float force;  // 나무가 땅에 쓰러지도록 밀어줄 힘의 세기(랜덤으로 정할 것) 
    [SerializeField]
    private GameObject go_ChildTree;  // 쓰러질 나무 윗부분. 쓰러지고 난 다음에 지연 시간 후 파괴 되야 해서 필요함.

    [SerializeField]
    private CapsuleCollider parentCol;  // 전체적인 나무에 붙어있는 캡슐 콜라이더. 나무가 쓰러지면 이걸 비활성화 해주어야 함.
    [SerializeField]
    private CapsuleCollider childCol;  // 쓰러질 나무인 나무 윗부분에 붙어있는 캡슐 콜라이더. 나무가 쓰러지면 이걸 활성화 해주어야 함.
    [SerializeField]
    private Rigidbody childRigid; // 쓰러질 나무인 나무 윗부분에 붙어있는 Rigidbody를 통해 나무가 쓰러지면 중력을 활성화 해주어야 함.

    [SerializeField]
    private GameObject go_hit_effect_prefab;  // 나무 도끼질 할 때마다 이펙트 효과(나무 파편)
    [SerializeField]
    private float debrisDestroyTime;  // 파편 제거 시간. 나무 도끼질 이펙트(나무 파편) 파괴될 시간

    [SerializeField]
    private float destroyTime;  // 나무 제거 시간. 나무 윗 부분이 땅에 쓰러지고 나서 파괴될 시간.

    [SerializeField]
    private string chop_sound;  // 나무 도끼질시 재생시킬 사운드 이름 
    [SerializeField]
    private string falldown_sound;  // 나무 쓰러질 때 재생시킬 사운드 이름 
    [SerializeField]
    private string logChange_sound;  // 나무 쓰러져서 통나무로 바뀔 때 재생시킬 사운드 이름


    // 도끼질 한 위치를 알아야 그 곳에서 이펙트 효과 재생함
    // 플레이어가 도끼질 한 Y 방향 회전값을 알아야 어느 조각이 맞았는지를 알 수 있음.
    public void Chop(Vector3 _pos, float angleY)   
    {
        Hit(_pos);

        AngleCalc(angleY);

        if (CheckTreePieces())
            return;

        FallDownTree();
    }

    private void Hit(Vector3 _pos)
    {
        SoundManager.instance.PlaySE(chop_sound);

        GameObject clone = Instantiate(go_hit_effect_prefab, _pos, Quaternion.Euler(Vector3.zero));
        Destroy(clone, debrisDestroyTime);
    }

    private void AngleCalc(float _angleY)
    {
        if (_angleY >= 0 && _angleY <= 70)
            DestroyPiece(2);
        else if (_angleY >= 70 && _angleY <= 140)
            DestroyPiece(3);
        else if (_angleY >= 140 && _angleY <= 210)
            DestroyPiece(4);
        else if (_angleY >= 210 && _angleY <= 280)
            DestroyPiece(0);
        else if (_angleY >= 280 && _angleY <= 360)
            DestroyPiece(1);
    }

    private void DestroyPiece(int _num)
    {
        if(go_treePieces[_num].gameObject != null)
        {
            GameObject clone = Instantiate(go_hit_effect_prefab, go_treePieces[_num].transform.position, Quaternion.Euler(Vector3.zero));
            Destroy(clone, debrisDestroyTime);
            Destroy(go_treePieces[_num].gameObject);
        }
    }

    private bool CheckTreePieces()
    {
        for (int i = 0; i < go_treePieces.Length; i++)
        {
            if(go_treePieces[i].gameObject != null)
            {
                return true;
            }
        }
        return false;
    }

    private void FallDownTree()
    {
        SoundManager.instance.PlaySE(falldown_sound);

        Destroy(go_treeCenter);
        
        parentCol.enabled = false;
        childCol.enabled = true;
        childRigid.useGravity = true;

        childRigid.AddForce(Random.Range(-force, force), 0f, Random.Range(-force, force));

        StartCoroutine(LogCoroutine());
    }

    IEnumerator LogCoroutine()
    {
        yield return new WaitForSeconds(destroyTime);

        SoundManager.instance.PlaySE(logChange_sound);

        Instantiate(go_Log_Prefabs, go_ChildTree.transform.position + (go_ChildTree.transform.up * 3f), Quaternion.LookRotation(go_ChildTree.transform.up));
        Instantiate(go_Log_Prefabs, go_ChildTree.transform.position + (go_ChildTree.transform.up * 6f), Quaternion.LookRotation(go_ChildTree.transform.up));
        Instantiate(go_Log_Prefabs, go_ChildTree.transform.position + (go_ChildTree.transform.up * 9f), Quaternion.LookRotation(go_ChildTree.transform.up));

        Destroy(go_ChildTree.gameObject);
    }
}

  • Chop(Vector3 _pos, float angleY) 👉 도끼로 나무 때릴 때마다 실행 됨.
    • 1️⃣ Hit 👉 도끼 치자 마자 할 처리
      • 도끼 치는 사운드 재생
      • 도끼 이펙트 효과 프리팹 생성 후 일정 시간 뒤 파괴
        • 생성 위치
          • _pos엔 도끼와 나무가 충돌한 위치가 들어온다.
        • 생성 방향
          • 회전값 원점
    • 2️⃣ AngleCalc(float _angleY) 👉 도끼로 때린 곳이 어느 쪽 나무 조각인지
      • _angleY엔 플레이어의 y 회전 값이 들어온다. 즉, 플레이어가 향하는 방향으로 어느 쪽 나무 조각을 때린 것인지를 파악할 것이다.
        • 플레이어가 앞뒤양옆 구분이 없는 머테리얼도 없는 캡슐 모양이라서 이렇게 하는게 가능한 것 같다. 보통의 상황에선 지난 포스트처럼 플레이어로부터 도끼로 나무 피격한 지점을 향하는 방향의 y값을 따져야 할 것같다.
      • 0 ~ 70 도 사이라면 3 번째 조각이 맞았다고 보고 210 ~ 280 도 사이라면 첫 번째 조각이 맞았다고 보고 이런식이다.
      • 맞았다고 파악되는 조각을 파괴시킨다.
        • DestroyPiece(int _num) 👉 인수로 들어온 인덱스에 해당하는 나무 조각 오브젝트를 파괴
          • 체크해 줘야 런타임 에러가 안나겟JO
            if(go_treePieces[_num].gameObject != null)
            
          • 피격 이펙트 효과 프리팹을 한 번 더 생성한다. (진짜 나무를 친 경우에는 이펙트가 두 개 생성되는 셈이다.)
          • 이펙트 효과 파괴시키고
          • 해당 나무 조각 오브젝트를 파괴
    • 3️⃣ CheckTreePieces() 👉 아직 남아 있는 나무 조각이 있는지 체크
      • 나무 조각 배열 검사
        • 남아 있는 나무 조각이 하나라도 있다면 true 리턴
        • 남아 있는 나무 조각이 아예 없다면 false 리턴
      • 남아 있다면 FallDownTree() 실행 못하게 리턴 종료
    • 4️⃣ FallDownTree() 👉 짱 큰 나무 쓰러 뜨리기
      • 남아 있는 나무 조각이 아예 없는데 때린 경우에만 실행 됨. 즉 가운데 나무 조각만 남아 있는 상태일 때 때린 경우
        • 나무 쓰러지는 소리 재생
        • 나무 가운데 조각 파괴
        • 두 콜라이더가 맞닿아 충돌을 일으키지 않도록 기존의 부모 나무 Collider 비활성화
        • 쓰러질 나무 윗부분 Collider 활성화
        • 쓰러질 나무 윗부분 중력 활성화
        • 밀어주지 않으면 너무 정직하게 위 나무가 떨어질 때 아랫 나뭇 위에 딱 서있게 된다.그래서 자연스럽게 넘어 지도록 X,Z 앞뒤 좌우로 랜덤한 크기의 밀어주는 힘을 준다.
        • 통나무 생성 LogCoroutine()
          • LogCoroutine() 👉 쓰러진 나무 윗부분(go_ChildTree) 삭제하고 통나무 3 개 생성
            • 쓰러진 나무 윗 부분 다 넘어질 때까지 기다린 후에 실행될 수 있도록 destroyTime만큼 기다림
            • 통나무 생성 소리 재생
            • 3 개의 통나무 생성
              • 생성 위치
                • go_ChildTree 위치로부터 go_ChildTree 기준 조금 윗 부분 (쓰러졌으니까 y축도 가로로 누운 상태라는 것을 생각해보자)
              • 생성 방향
                • go_ChildTree 기준 조금 윗 부분을 바라보는 방향. (쓰러졌으니까 y축도 가로로 누운 상태이므로 즉 나무가 쓰러진 방향을 바라보도록 생성한다.)
          • go_ChildTree 삭제

image

image

📜SoundManager 에도 나무와 관련된 3 개의 효과음과 그의 이름을 추가해주었다.


📜AxeController.cs

도끼 컨트롤러

    protected override IEnumerator HitCoroutine()
    {
        while (isSwing)
        {
            if (CheckObject())
            {
                if(hitInfo.transform.tag == "Grass")
                {
                    hitInfo.transform.GetComponent<Grass>().Damage();
                }

                if (hitInfo.transform.tag == "Tree")
                {
                    hitInfo.transform.GetComponent<TreeComponent>().Chop(hitInfo.point, transform.eulerAngles.y);
                }

                isSwing = false;
                Debug.Log(hitInfo.transform.name);
            }
            yield return null;
        }
    }

도끼와 충돌한 대상이 “Tree” 태그가 붙어있다면 나뭇 가지이므로 📜TreeComponent.cs 의 Chop 함수를 호출한다. 나무 피격 이펙트 프리팹을 생성할 위치는 도끼와 대상이 충돌한 위치인 hitInfo.point 이며, 어떤 나무 조각이 파괴될 지는 플레이어의 y회전값으로 결정하므로 transform.eulerAngles.y을 넘겨 준다.


🚖 벌목 시선 처리

나무의 위를 보고 벌목하더라도 나무 조각 오브젝트는 아래에 있으므로 아래 조각이 파괴되며 벌목이 된다. 따라서 자연스럽게 어느 곳을 바라보든 일단 나무를 때리면 플레이어의 시선을 강제로 가운데 나무 조각이 있는 아래로 내려주도록 구현할 것이다.


📜PlayerController.cs

    private bool pauseCameraRotation = false;

    private void CameraRotation() 
    {
        if (!pauseCameraRotation)
        {
            float _xRotation = Input.GetAxisRaw("Mouse Y");
            float _cameraRotationX = _xRotation * lookSensitivity;

            currentCameraRotationX -= _cameraRotationX;
            currentCameraRotationX = Mathf.Clamp(currentCameraRotationX, -cameraRotationLimit, cameraRotationLimit);

            theCamera.transform.localEulerAngles = new Vector3(currentCameraRotationX, 0f, 0f);
        }
    }

    public IEnumerator TreeLookCoroutine(Vector3 _target)
    {
        pauseCameraRotation = true;

        Quaternion direction = Quaternion.LookRotation(_target - theCamera.transform.position);
        Vector3 eulerValue = direction.eulerAngles;
        float destinationX = eulerValue.x;

        while(Mathf.Abs(destinationX - currentCameraRotationX) >= 0.5f)
        {
            eulerValue = Quaternion.Lerp(theCamera.transform.localRotation, direction, 0.3f).eulerAngles;  // 쿼터니언 👉 벡터
            theCamera.transform.localRotation = Quaternion.Euler(eulerValue.x, 0f, 0f); // 벡터 👉 쿼터니언 (X축으로만 회전하면 됨)
            currentCameraRotationX = theCamera.transform.localEulerAngles.x; 
            yield return null;
        }
        pauseCameraRotation = false;
    }
  • pauseCameraRotation
    • 마우스 위 아래 움직임에 따른 카메라 회전을 멈춰야 할 때인지를 알릴 Boolean 타입 변수
      • 멈춰야 한다면 True, 아니라면 False.
  • CameraRotation()
    • 기존에 구현 했었던, 마우스가 위 아래로 움직이면 고개를 위 아래로 움직이는 효과를 주기 위해 카메라를 X 축을 중심으로 위 아래 회전시키는 함수다.
    • pauseCameraRotation가 False 상태일 때만 동작하도록 한다.
      • 도끼로 나무 때릴 땐 나무 가운데 조각에 카메라 회전을 강제로 맞추기 위하여 이럴 땐 *CameraRotation()* 함수에서 카메라 회전하는 부분을 실행하지 않기 위해서!
  • TreeLookCoroutine(Vector3 _target)
    • 인수인 _target은 카메라가 바라봐야 하는 위치가 될 것이다. 즉, 나무의 가운데 조각의 위치가 들어올 것.
    • 나무를 때릴 땐 강제로 나무의 가운데 조각을 바라 보도록 카메라 회전값을 설정한다.

      • pauseCameraRotation을 True로 설정하여 CameraRotation() 함수 내용 실행 못 하도록.
      • direction
        • 카메라 위치로부터 나무 가운데 조각 위치로 향하는 방향을 바라보는 회전 값 쿼터니언
      • eulerValue
        • 카메라 위치로부터 나무 가운데 조각 위치로 향하는 방향을 바라보는 회전 값 Vector3
      • destinationX
        • (카메라 위치로부터 나무 가운데 조각 위치로 향하는 방향을 바라보는 회전 값 쿼터니언)을 Vector3 로 변환한 것의 x
          • 카메라는 x축 중심으로 회전할 것이니 (위로든 아래로든 나무 가운데 조각을 바라보도록 x 축 중심 회전해야 하니까)
        • 이 값은 카메라가 나무 가운데 조각을 바라보기 위하여 x축을 중심으로 얼만큼 회전해야 하는지를 나타내는 값이다.
      • Lerp와 코루틴 대기시간을 사용하여 카메라의 현재 회전값을 나무 가운데 조각을 바라보는 회전값이 될 때까지 부드럽게 변화시키자
        • currentCameraRotationXdestinationX가 될 때까지 반복 진행
          • 둘의 차이가 0.5 미만이면 같아졌다고 보고 루프문 빠져 나오도록 한다. Lerp 라서 정확하게 둘이 같아지기는 힘드므로 무한 루프에 빠질 가능성 배제
        • Lerp 는 Vector3, 즉 eulerValue로 보간하여 업데이트 하고
        • 카메라 현재 회전값 업데이트는 보간한 eulerValue를 쿼터니언으로 변환한 것으로 업뎃
        • currentCameraRotationX 업데이트
      • 이 과정이 다 끝나면 pauseCameraRotation을 False로 설정하여 CameraRotation() 함수가 다시 잘 실행되어 마우스 위 아래 움직임에 따라 카메라가 위 아래로 회전할 수 있도록 한다.


📜CloseWeaponController.cs

추상클래스임을 명심

protected PlayerController thePlayerController;


    protected void TryAttack()
    {
        if (Input.GetButton("Fire1"))
        {
            if (!isAttack)
            {
                if(CheckObject())
                {
                    if (currentCloseWeapon.isAxe && hitInfo.transform.tag == "Tree")
                    {
                        StartCoroutine(thePlayerController.TreeLookCoroutine(hitInfo.transform.GetComponent<TreeComponent>().GetTreeCenterPosition()));
                        StartCoroutine(AttackCoroutine("Chop", currentCloseWeapon.workDelayA, currentCloseWeapon.workDelayB, currentCloseWeapon.workDelay));
                        return;
                    }
                }
                
                StartCoroutine(AttackCoroutine("Attack", currentCloseWeapon.attackDelayA, currentCloseWeapon.attackDelayB, currentCloseWeapon.attackDelay));
            }
        }
    }
StartCoroutine(thePlayerController.TreeLookCoroutine(hitInfo.transform.GetComponent<TreeComponent>().GetTreeCenterPosition()));
  • thePlayerController
    • 📜PlayerController.cs의 TreeLookCoroutine 함수 실행이 필요하여 선언함.
    • 보호 수준을 protected 로 하여 자식인 📜AxeController.cs 에서 상속받을 수 있도록 한다.
    • Player 객체 받아오는건 📜AxeController.cs의 Start() 함수에서 하자.
      • 📜CloseWeaponController.cs 는 추상클래스이기 때문에 📜CloseWeaponController.cs의 Start() 에서 Player 객체를 받아 오면.. 그럴 순 있을 것 같긴 한데.. 자식 근접무기 컨트롤러들은 이미 자신만의 Start 를 가지고 있기 때문에 그냥 Player 객체를 받아 오는 일은 📜AxeController.cs의 Start() 함수에서 해주기로 했다.
        • 물론 이것이 이루어지려면 thePlayerController는 private이 아닌 protected 이상의 보호 수준이어야 한다.
      • 강의자 분께서는 thePlayerController를 private으로 선언하시고 📜CloseWeaponController.cs에 Start()를 만들어 이 곳에서 Player 객체를 받아 오셨다. 강의 보면 별 문제 없으시던데 난 런타임 에러가 발생했다. 📜CloseWeaponController.cs는 추상클래스니까 Start()가 이 곳에서 실행되는게 아니라 자식들에게 상속되어 실행이 되는데 thePlayerController는 private이니까 상속이 될리가 없다. 그래서 thePlayerController가 null 이여서 계속해서 런타임 에러가 발생했었다. 한참 헤매다 protected로 바꾸고 객체 받아와 초기화 해주는 일을 📜AxeController.cs의 Start() 함수에서 해주니 런타임 에러는 해결 되었다.
        • 밑에 참고
  • 현재 근접 무기가 도끼인데다가 때린 대상이 나무라면
    • 📜PlayerController.cs의 TreeLookCoroutine 함수를 실행시키되 이 함수의 인수는 hitInfo.transform.GetComponent<TreeComponent>().GetTreeCenterPosition() 로 넘긴다.
      • 충돌한 나무의 📜TreeComponent.cs의 GetTreeCenterPosition() 함수 리턴값
        • 가운데 나무 조각의 위치를 리턴해주는 함수다. 밑에 참고.


📜AxeController.cs

    private void Start()
    {
        WeaponManager.currentWeapon = currentCloseWeapon.GetComponent<Transform>();
        WeaponManager.currentWeaponAnim = currentCloseWeapon.anim;
        thePlayerController = FindObjectOfType<PlayerController>();
    }

📜CloseWeaponController.cs 로부터 상속 받은 thePlayerControllerPlayer 객체 받아와 초기화 해주는 일은 📜AxeController.cs의 Start() 함수에서 해주었다.


📜TreeComponent.cs

    public Vector3 GetTreeCenterPosition()
    {
        return go_treeCenter.transform.position;
    }

나무 가운데 조각 오브젝트의 위치 리턴.




🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우 
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄

맨 위로 이동하기

댓글남기기