Chapter 5-1. 인벤토리 : 아이템 획득, ScriptableObject
카테고리: Unity Lesson 3
태그: Unity Game Engine
인프런에 있는 케이디님의 [유니티 3D] 실전! 생존게임 만들기 - Advanced 강의를 듣고 정리한 필기입니다. 😀
🌜 강의 들으러 가기 Click
Chapter 5. 인벤토리
아이템 획득
🚖 바위 아이템 프리팹
바위, 나무 같은 오브젝트를 파괴하면 생성되는 아이템을 만들자. 나무는 통나무를 추후 아이템으로 삼으면 되지만 바위는 파괴 후 생성되는게 없기 때문에 바위의 경우 아이템으로 주울 수 있는 프리팹을 생성하자.
바위 아이템 프리팹 만들기
이름은
Rock_Item
- 바위가 깨지면 인벤토리에 담아 주울 수 있는 아이템으로서 생성할 것이다.
- 기존에 섰던 바위 3D 모델을 가져와 크기만 둘여서 만듬
- 크기는 당연히 일반 바위 오브젝트보다 작아야 하므로 Scale을 (30, 30, 30) 정도로 해주었다.
- 태그는 Item 으로, 레이어 또한 Item 으로 해주었다.
- 태그 레이어 둘다 Item 추가
- 레이어로 불, 건물, 나무, 바위 등등 게임 내에서 상호작용 할 수 있는 레이어인지를 인수로서
LayerMask
를 설정하여 종류에 따라 다르게 처리할 것이고 - 태그 또한 레이어로 충돌 한 대상이 이런 태그를 갖고 있다면 처리를 다르게 할 것이다.
- Rigidbody 붙이기
- 중력이나 여러 마찰력 등등 물리적 효과 받기.
- 질량은 0.01로 가볍게 해줌.
- Box Collider 붙이기
- 중력에 의해 바닥을 뚫고 떨어지지 않도록
- 📜ItemPickUp.cs 붙이기
- 설명 밑에 참고
- 기존에 섰던 바위 3D 모델을 가져와 크기만 둘여서 만듬
📜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
데이터들을 저장하는데 사용할 수 있는 데이터 컨테이너 에셋
각각의 좀비 오브젝트들은 체력, 시야, 속도 값이 모두 같다고 가정해보자. 좀비 프리팹으로 좀비 오브젝트 3개를 찍어내면 동일한 이 체력, 시야, 속도 값이 3 개나 사본으로 생성되는 셈이다. 어차피 동일한 데이터를 사용하니까 체력, 시야, 속도 값을 한군데 만들어두고 이를 인스턴스들이 이를 참조하게 만들면 메모리를 더 아낄 수 있다.
모든 좀비들이 가지는 동일한 데이터들을 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 를 상속받는다.- 다른 스크립트와 달리 오브젝트에 컴포넌트로서 붙일 수가 없다.
- 아이템들이 가지는 기본적인 데이터들을 관리한다.
- 에셋으로서 만들어 둘 수 있다.
에셋에서 우클 - Create 에 New Item/item
이 생긴 것을 볼 수 있다. 이제 New Item/item
메뉴가 생겨 이것으로 📜Item.cs을 ScriptableObject 타입의 에셋으로서 생성할 수 있게 됨.
에셋으로 생성해주면 fileName
으로 설정했었던 “New Item” 이름을 기본적으로 하여 생성 된다.
📜Item.cs 에서 정의한 데이터 속성들을 확인할 수 있다.
모든 바위 아이템들이 공통적으로 같은 값을 가지는 데이터 에셋으로서 사용하기 위하여 이름을 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
에 할당해줄 것이다.
item
에 Rock
ScriptableObject 에셋을 할당 해준다. 모든 바위 아이템 오브젝트들이 공통적으로 참조하게 될 데이터 에셋.
🚖 아이템 주울 수 있는 텍스트 띄우기 + 아이템 습득
텍스트 UI
ShowText
👉 빈 오브젝트actionText
👉 텍스트 UI
아이템을 주울 수 있는 상태가 되면 자동으로 위와 같은 텍스트 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 태그도 쓸 수 있나보다..!
- 아이템을 주울 수 있는 상태라는 것을 표시하는
- ItemInfoAppear() 👉 아이템 주울 수 있는 상태. 아이템 주우라는 텍스트 띄우기
- 그 오브젝트의 태그가 “Item” 이라면
- 사정 거리 내에
layerMask
레이어를 가진 오브젝트가 없다면- ItemInfoDisappear() 👉 아이템 주울 수 없는 상태. 아이템 주우라는 텍스트 비활성화
- 아이템 못 줍도록
pickupActivated
False 설정. - 텍스트 비활성화
- 아이템 못 줍도록
- ItemInfoDisappear() 👉 아이템 주울 수 없는 상태. 아이템 주우라는 텍스트 비활성화
- 사정 거리 내에
- 메인카메라의 위치
- TryAction()
E
키 입력이 들어오면- 위의 CheckItem() 과정을 또 한다. 아이템 주울 수 있는 상태인지를 검사
- CanPickUp() 👉 실제로 아이템을 줍는 처리를 한다.
pickupActivated
가 True일 때만 다음 과정을 실행한다.- 충돌 오브젝트가 존재할 때만 처리한다. (혹시 모를 오류 방지)
- 인벤토리에 아이템 추가하는 처리 (아직 구현 X)
- 월드에 배치된 해당 아이템은 파괴. (주워서 인벤토리에 넣었으니까)
- ItemInfoDisappear() 실행으로 아이템 주울 수 없는 상태로 만듬. 아이템 주우라는 텍스트 비활성화.
- 충돌 오브젝트가 존재할 때만 처리한다. (혹시 모를 오류 방지)
Main Camera 설정
- 사정 거리는 3
layerMask
에 Item 설정- 활성화/비활성화 할 텍스트는
actionText
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
갯수만큼 생성한다.
go_rock_item_prefab
, count
5 할당
바위가 깨지니 5 개의 아이템이 생성되고 아이템 주우라는 메세지가 뜨는 것을 확인할 수 있다. 아이템을 E
키로 주우면 아이템이 파괴된다.
🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄
댓글 남기기