유니티 3D 아이템 획득 - yuniti 3D aitem hoegdeug

Date: 2020.10.03    Updated: 2020.10.03

카테고리: Unity Lesson 3

태그: Unity Game Engine

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

Chapter 5. 인벤토리

아이템 획득

🚖 바위 아이템 프리팹

바위, 나무 같은 오브젝트를 파괴하면 생성되는 아이템을 만들자. 나무는 통나무를 추후 아이템으로 삼으면 되지만 바위는 파괴 후 생성되는게 없기 때문에 바위의 경우 아이템으로 주울 수 있는 프리팹을 생성하자.

바위 아이템 프리팹 만들기

유니티 3D 아이템 획득 - yuniti 3D aitem hoegdeug

이름은 Rock_Item

  • 바위가 깨지면 인벤토리에 담아 주울 수 있는 아이템으로서 생성할 것이다.
    • 기존에 섰던 바위 3D 모델을 가져와 크기만 둘여서 만듬
      • 크기는 당연히 일반 바위 오브젝트보다 작아야 하므로 Scale을 (30, 30, 30) 정도로 해주었다.
    • 태그는 Item 으로, 레이어 또한 Item 으로 해주었다.
      • 태그 레이어 둘다 Item 추가
      • 레이어로 불, 건물, 나무, 바위 등등 게임 내에서 상호작용 할 수 있는 레이어인지를 인수로서 LayerMask를 설정하여 종류에 따라 다르게 처리할 것이고
      • 태그 또한 레이어로 충돌 한 대상이 이런 태그를 갖고 있다면 처리를 다르게 할 것이다.
    • Rigidbody 붙이기
      • 중력이나 여러 마찰력 등등 물리적 효과 받기.
      • 질량은 0.01로 가볍게 해줌.
    • Box Collider 붙이기
      • 중력에 의해 바닥을 뚫고 떨어지지 않도록
    • 📜ItemPickUp.cs 붙이기
      • 설명 밑에 참고

📜Item.cs

오브젝트 어디에도 붙일 수 없다.

using System.Collections; using System.Collections.Generic; using UnityEngine; [CreateAssetMenu(fileName = "New Item", menuName = "New Item/item")] public class Item : ScriptableObject // 게임 오브젝트에 붙일 필요 X { public enum ItemType // 아이템 유형 { Equipment, Used, Ingredient, ETC, } public string itemName; // 아이템의 이름 public ItemType itemType; // 아이템 유형 public Sprite itemImage; // 아이템의 이미지(인벤 토리 안에서 띄울) public GameObject itemPrefab; // 아이템의 프리팹 (아이템 생성시 프리팹으로 찍어냄) public string weaponType; // 무기 유형 }

여러 아이템의 공통적인 데이터를 담는다.

  • 아이템 기본 정보
    • 아이템 이름
    • 아이템 유형
      • 무기/장비
      • 소모품
      • 재료
        • 바위 깨면 나오는 아이템은 재료 유형에 해당할 것
      • 기타
    • 아이템 이미지
      • Sprite다. 월드 어디서든 배치될 수 있도록
        • '인벤토리'에서 아이템을 이미지로서 보기 위한, 아이템을 대표하는 이미지
          • 마인크래프트 인벤토리처럼!
          • 인벤토리에서 사용되기 위해 필요
    • 아이템 프리팹
      • 이 프리팹으로 아이템을 생성
    • 무기 유형
      • 아이템을 주울 때 들고 있는 현재 무기에 따라 아이템 줍는 애니메이션을 재생해야 하므로 필요한 정보
  • Sprite 와 Image의 차이
    • Image 👉 Canvas 위 에서만 이미지를 띄울 수 있다.
    • Sprite 👉 Canvas와 상관없이 월드 어디에서든 이미지를 띄울 수 있다.

ScriptableObject

데이터들을 저장하는데 사용할 수 있는 데이터 컨테이너 에셋

유니티 3D 아이템 획득 - yuniti 3D aitem hoegdeug

각각의 좀비 오브젝트들은 체력, 시야, 속도 값이 모두 같다고 가정해보자. 좀비 프리팹으로 좀비 오브젝트 3개를 찍어내면 동일한 이 체력, 시야, 속도 값이 3 개나 사본으로 생성되는 셈이다. 어차피 동일한 데이터를 사용하니까 체력, 시야, 속도 값을 한군데 만들어두고 이를 인스턴스들이 이를 참조하게 만들면 메모리를 더 아낄 수 있다.

유니티 3D 아이템 획득 - yuniti 3D aitem hoegdeug

모든 좀비들이 가지는 동일한 데이터들을 ScriptableObject 로서 한 군데에 에셋으로서 모아 관리하고 생성되는 오브젝트들이 이 동일한 에셋을 참조하면 메모리를 효율적으로 쓸 수 있게 된다. 예를 들어 ScriptableObject 을 상속 받는 ZombieData 클래스를 만들고 이 곳에 좀비가 가지는 데이터들을 모아 두고 이를 에셋으로 생성하여서 좀비 오브젝트들이 이 곳을 참조하게 하면 된다!

  • 메모리 사용을 줄인다. 👉 여러 사본들이 생성되는 것을 방지
    • 이 ScriptableObject을 참조하게 된다.
  • MonoBehavior 대신 ScriptableObject를 상속 받는다면
    • 다른 스크립트와 달리 오브젝트에 컴포넌트로서 붙일 수 없다.⭐ MonoBehavior를 상속 받지 못 했으니까!
    • 이벤트는 OnEnable, OnDisable, OnDestroy 만 받을 수 있다.
  • 스크립트는 아닌 에셋이다. 어떤 고유한 파일로서.
    • 클래스이름.CreateInstance 와 클래스이름.CreateAsset 으로, 이 클래스의 인스턴스를 만들고 이를 하나의 에셋으로서 생성할 수 있다.
    • 그냥 스크립트나 폴더 추가하듯이 [CreateAssetMenu]로 에셋 생성 메뉴에 쉽게 추가할 수 있도록 할 수도 있다.

      [CreateAssetMenu](filename, menuName, order) // filename 이 에셋을 생성하게 되면 기본적으로 지어질 이름 // menuName 유니티 에셋-우클-Create- 메뉴에 보일 이름 // order 메뉴에 보일 순서 (기본적으론 첫 번째)

[CreateAssetMenu(fileName = "New Item", menuName = "New Item/item")] public class Item : ScriptableObject { }

  • Item 클래스는 ScriptableObject 를 상속받는다.
    • 다른 스크립트와 달리 오브젝트에 컴포넌트로서 붙일 수가 없다.
    • 아이템들이 가지는 기본적인 데이터들을 관리한다.
    • 에셋으로서 만들어 둘 수 있다.

유니티 3D 아이템 획득 - yuniti 3D aitem hoegdeug

에셋에서 우클 - Create 에 New Item/item이 생긴 것을 볼 수 있다. 이제 New Item/item 메뉴가 생겨 이것으로 📜Item.cs을 ScriptableObject 타입의 에셋으로서 생성할 수 있게 됨.

유니티 3D 아이템 획득 - yuniti 3D aitem hoegdeug

에셋으로 생성해주면 fileName으로 설정했었던 “New Item” 이름을 기본적으로 하여 생성 된다.

유니티 3D 아이템 획득 - yuniti 3D aitem hoegdeug

📜Item.cs 에서 정의한 데이터 속성들을 확인할 수 있다.

유니티 3D 아이템 획득 - yuniti 3D aitem hoegdeug

모든 바위 아이템들이 공통적으로 같은 값을 가지는 데이터 에셋으로서 사용하기 위하여 이름을 Rock 으로 바꾸고 바위 아이템 값에 맞는 데이터들을 할당해주었다. 이제 이 Rock ScriptableObject 에셋을 참조하는 모든 오브젝트는 동일한 이 Rock ScriptableObject 에셋 한 군데를 참조하게 되는 것이고, 이에 따라 모두 동일한 데이터 값을 가지게 된다. 모든 바위 아이템들은 Item Name이 Rock 이며 Item Type이 Ingredient 가 될 것이다.

  • itemImage에 할당할 이미지는 모두 Textrue Type을 Sprite(2D and UI)로 하여 넣어준다.
  • itemPrefab에는 위에서 만든 Rock_Item 바위 아이템 프리팹을 넣어준다.

📜ItemPickUp.cs

Rock_Item 에 붙여 준다. 바위 아이템 프리팹.

using System.Collections; using System.Collections.Generic; using UnityEngine; public class ItemPickUp : MonoBehaviour { public Item item; }

📜Item.cs ScriptableObject 에셋을 참조할 수 있는 용도의 스크립트를 만들었다. 📜Item.cs 자체로는 오브젝트에 컴포넌트로서 붙일 수가 없기 때문에! 이렇게 📜Item.cs 을 할당 받을 수 있는 컴포넌트를 만들었다. 앞으로 아이템들마다 📜ItemPickUp.cs을 붙여 알맞는 📜Item.cs 타입의 데이터 에셋을 📜ItemPickUp.cs의 item에 할당해줄 것이다.

유니티 3D 아이템 획득 - yuniti 3D aitem hoegdeug

item에 Rock ScriptableObject 에셋을 할당 해준다. 모든 바위 아이템 오브젝트들이 공통적으로 참조하게 될 데이터 에셋.

🚖 아이템 주울 수 있는 텍스트 띄우기 + 아이템 습득

텍스트 UI

유니티 3D 아이템 획득 - yuniti 3D aitem hoegdeug

  • ShowText 👉 빈 오브젝트
  • actionText 👉 텍스트 UI

유니티 3D 아이템 획득 - yuniti 3D aitem hoegdeug

아이템을 주울 수 있는 상태가 되면 자동으로 위와 같은 텍스트 actionText가 활성화 되도록 한다. 위치 잡고 색상 흰색 가운데 정렬.

📜ActionController.cs

Player의 자식인 Main Camera에 붙인다.

  • 카메라와의 일정 사정거리 안에 아이템이 들어오면 (Raycast)
    • actionText 텍스트를 활성화 시키고
    • 아이템을 주울 수 있는 상태가 되도록 한다.

using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class ActionController : MonoBehaviour { [SerializeField] private float range; // 아이템 습득이 가능한 최대 거리 private bool pickupActivated = false; // 아이템 습득 가능할시 True private RaycastHit hitInfo; // 충돌체 정보 저장 [SerializeField] private LayerMask layerMask; // 특정 레이어를 가진 오브젝트에 대해서만 습득할 수 있어야 한다. [SerializeField] private Text actionText; // 행동을 보여 줄 텍스트 void Update() { CheckItem(); TryAction(); } private void TryAction() { if(Input.GetKeyDown(KeyCode.E)) { CheckItem(); CanPickUp(); } } private void CheckItem() { if (Physics.Raycast(transform.position, transform.forward, out hitInfo, range, layerMask)) { if (hitInfo.transform.tag == "Item") { ItemInfoAppear(); } } else ItemInfoDisappear(); } private void ItemInfoAppear() { pickupActivated = true; actionText.gameObject.SetActive(true); actionText.text = hitInfo.transform.GetComponent<ItemPickUp>().item.itemName + " 획득 " + "<color=yellow>" + "(E)" + "</color>"; } private void ItemInfoDisappear() { pickupActivated = false; actionText.gameObject.SetActive(false); } private void CanPickUp() { if(pickupActivated) { if(hitInfo.transform != null) { Debug.Log(hitInfo.transform.GetComponent<ItemPickUp>().item.itemName + " 획득 했습니다."); // 인벤토리 넣기 Destroy(hitInfo.transform.gameObject); ItemInfoDisappear(); } } } }

  • 매 프레임마다
    • CheckItem() 👉 항상 아이템이 사정 거리 안에 있는지를 체크한다. (E키 누르는게 아니더라도)
    • TryAction() 👉 E 키 입력이 들어왔는지를 검사한다. E 키가 들어 온다면
      • CheckItem() 👉 아이템이 사정 거리 안에 있는지를 체크한다.
      • CanPickUp() 👉 아이템을 줍는 처리.
  • CheckItem()
    • 메인카메라의 위치 transform.position로부터, 앞 방향으로, 사정 거리 range에 달하는 광선을 쐈을 때 layerMask 레이어 마스크에 해당 되는 충돌된 오브젝트가 있다면 이 충돌 정보를 hitInfo에 담고 True를 리턴한다.
      • 사정 거리 내에 layerMask 레이어를 가진 오브젝트가 있다면
        • 그 오브젝트의 태그가 “Item” 이라면
          • ItemInfoAppear() 👉 아이템 주울 수 있는 상태. 아이템 주우라는 텍스트 띄우기
            • 아이템을 주울 수 있는 상태라는 것을 표시하는 pickupActivated True 설정.
              • CanPickUp() 에서 이 값이 True 면 아이템 줍는 처리를 하기 때문에
            • 텍스트 활성화
            • 텍스트 (아이템 이름 + ` 획득 ` + (E)) 형식의 텍스트 출력
              • (E)는 노란색으로 표시
                • 텍스트에 이렇게 HTML 태그도 쓸 수 있나보다..!
      • 사정 거리 내에 layerMask 레이어를 가진 오브젝트가 없다면
        • ItemInfoDisappear() 👉 아이템 주울 수 없는 상태. 아이템 주우라는 텍스트 비활성화
          • 아이템 못 줍도록 pickupActivated False 설정.
          • 텍스트 비활성화
  • TryAction()
    • E 키 입력이 들어오면
      • 위의 CheckItem() 과정을 또 한다. 아이템 주울 수 있는 상태인지를 검사
      • CanPickUp() 👉 실제로 아이템을 줍는 처리를 한다.
        • pickupActivated가 True일 때만 다음 과정을 실행한다.
          • 충돌 오브젝트가 존재할 때만 처리한다. (혹시 모를 오류 방지)
            • 인벤토리에 아이템 추가하는 처리 (아직 구현 X)
            • 월드에 배치된 해당 아이템은 파괴. (주워서 인벤토리에 넣었으니까)
            • ItemInfoDisappear() 실행으로 아이템 주울 수 없는 상태로 만듬. 아이템 주우라는 텍스트 비활성화.

Main Camera 설정

유니티 3D 아이템 획득 - yuniti 3D aitem hoegdeug

  • 사정 거리는 3
  • layerMask에 Item 설정
  • 활성화/비활성화 할 텍스트는 actionText

유니티 3D 아이템 획득 - yuniti 3D aitem hoegdeug

Culling Mask 에 Item 레이어를 추가로 등록해주어야 Item 레이어를 가진 아이템 오브젝트들도 렌더링 되어 게임 화면에 그려질 수 있다!! 해주지 않으면 게임에 나오지 않는다..

🚖 바위 깨면 아이템 생성

📜Rock.cs

[SerializeField] private GameObject go_rock_item_prefab; // 바위 파괴시 생성할 아이템 프리팹 [SerializeField] private int count; // 바위 파괴시 생성할 아이템 갯수 //... private void Destruction() { SoundManager.instance.PlaySE(destroy_Sound); col.enabled = false; for (int i = 0; i < count; i++) { Instantiate(go_rock_item_prefab, go_rock.transform.position, Quaternion.identity); } Destroy(go_rock); go_debris.SetActive(true); Destroy(go_debris, destroyTime); }

바위가 파괴되면 바위 아이템 프리팹을 count 갯수만큼 생성한다.

유니티 3D 아이템 획득 - yuniti 3D aitem hoegdeug

go_rock_item_prefab, count 5 할당

유니티 3D 아이템 획득 - yuniti 3D aitem hoegdeug

바위가 깨지니 5 개의 아이템이 생성되고 아이템 주우라는 메세지가 뜨는 것을 확인할 수 있다. 아이템을 E키로 주우면 아이템이 파괴된다.


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

맨 위로 이동하기

Unity Lesson 3 카테고리 내 다른 글 보러가기