✅ 기능
이전 글에서 아이템을 테스트로 획득하는 것을 확인해 보았습니다. 획득하는 기능을 연계하여 런타임 도중 씬 내에 있는 아이템을 획득하는 방법에 대해 다루겠습니다. 필자는 Raycast를 이용하여 바라볼 때 해당 아이템을 주울 수 있도록 구현하였습니다. Ray가 아닌 Trigger을 사용할 수도 있습니다.
🔨 로드맵
2. 유니티 인벤토리 시스템(2) - 아이템(ScriptableObject)
3. 유니티 인벤토리 시스템(3) - 아이템 슬롯(인벤토리 슬롯)
[📌현재 글] 4. 유니티 인벤토리 시스템(4) - 아이템 획득
5. 유니티 인벤토리 시스템(5) - 아이템 슬롯 관리
✅ 구현
1. ItemPickUp
using UnityEngine;
/// <summary>
/// Item을 넣는 공간 프리팹에 컴포넌트로 추가하고 인스펙터에 아이템을 할당한다.
/// </summary>
public class ItemPickUp : MonoBehaviour
{
[Header("해당 오브젝트에 할당되는 아이템")]
[SerializeField] private Item mItem;
/// <summary>
/// 상호작용 가능한 객체가 가지고 있는 아이템
/// /// </summary>
/// <value></value>
public Item Item
{
get
{
return mItem;
}
}
[Header("해당 오브젝트에 상호작용시, 보여줄 인디케이터의 높이")]
[SerializeField] private float mIndicatorHeight;
/// <summary>
/// 인디케이터의 높이
/// /// </summary>
/// <value></value>
public float IndicatorHeight
{
get
{
return mIndicatorHeight;
}
}
}
- ScriptableObject인 Item.cs를 담아둘 게임오브젝트에 컴포넌트를 추가하고, Item정보를 넘겨줄 클래스입니다.
- 단순히 Item에 대한 정보를 건네줄 목적으로 사용하는 클래스입니다.
[SerializeField] private Item mItem;
- 런타임 도중 상호작용 할 게임오브젝트에서 받아올 아이템 정보입니다.
- 해당 아이템을 줍거나, NONE Type인경우 줍지 않고, 특정 이벤트가 발생하도록 구현하였습니다.
[SerializeField] private float mIndicatorHeight;
- 해당 아이템에 대한 정보를 받아올 때, 보여줄 인디케이터(텍스트 등)에 대한 높이를 지정합니다.
- 아이템에 대한 크기가 모두 다르기에 아이템별로 설정을 하기위해 사용합니다.
2. ItemRaycast
using System.Collections;
using UnityEngine;
/// <summary>
/// 씬 내의 아이템(또는 정적 물체)에 다가가면 해당 아이템을 줍거나, 상호작용 할 수 있도록 해주는 스크립트
/// 플레이어의 오브젝트에 자식으로 넣은 EmptyObject에 Trigger Collider을 추가하여 사용
/// </summary>
public class ItemRaycast : MonoBehaviour
{
/// <summary>
/// 레이캐스트 된 아이템
/// </summary>
private RaycastHit mHit;
/// <summary>
/// 레이캐스트 거리
/// </summary>
[SerializeField] private float mRayDistance;
private bool mIsPickupActive = false; //아이템 습득이 가능한가?
private ItemPickUp mCurrentItem; //활성화시 현재 등록된 아이템
[Header("레이캐스트를 쏠 카메라")]
[SerializeField] private Camera mRayCamera; //레이를 쏠 카메라 (메인카메라)
[SerializeField] private InventoryMain mInventory; //인벤토리 메인
// [SerializeField] private ItemActionManager mItemActionCustomFunc; //아이템 상호작용 커스텀 함수 매니저 (이 글에서는 설명 X)
// [SerializeField] private ItemRaycastInfoText mItemRaycastInfoText; //아이템 상호작용 가능시 보여질 텍스트 매니저 (이 글에서는 설명 X)
private void Update()
{
CheckItem();
if (mIsPickupActive) { TryPickItem(); }
}
/// <summary>
/// 아이템을 주울 수 있는지 확인한다.
/// </summary>
private void TryPickItem()
{
if (Input.GetKeyDown(KeyCode.E))
{
//주울 수 있는 아이템이라면?
if (mCurrentItem.Item.Type > ItemType.NONE)
{
//현재 인벤토리 아이템 가져오기
InventorySlot[] allitems = mInventory.GetAllItems();
int count = 0;
for (; count < allitems.Length; ++count)
{
//현재 아이템 칸이 null이라면 주울 수 있는 상태
if (allitems[count].Item == null) { break; }
//현재 아이템칸이 null이 아니지만, 현재 아이템과 동일하면서 중첩이 가능한 아이템이라면 주울 수 있는 상태
if (allitems[count].Item.ItemID == mCurrentItem.Item.ItemID && allitems[count].Item.CanOverlap) { break; }
}
//모든 칸이 null이 아니고, 중첩이 불가능하면 주울 수 없음
if (count == allitems.Length) { return; }
//아이템 줍는 효과음 재생
SoundManager.Instance.PlaySound2D("GrabItem " + SoundManager.Range(1, 3));
}
TryPickUp();
ItemInfoDisappear();
}
}
/// <summary>
/// 레이캐스트를 이용하여 아이템을 확인한다.
/// </summary>
private void CheckItem()
{
if (Physics.Raycast(mRayCamera.transform.position, mRayCamera.transform.forward, out mHit, mRayDistance))
{
//레이캐스트 결과의 태그가 아이템이라면?
if (mHit.transform.tag == "Item" || mHit.transform.tag == "NPC")
{
//현재 레이캐스트된 아이템
ItemPickUp rayCastedItem = mHit.transform.GetComponent<ItemPickUp>();
//레이캐스트 결과가 현재 아이템과 같으면 무시 (중복호출 방지)
if(mCurrentItem == rayCastedItem) { return; }
//아이템 얻어오기 및 정보 호출
mCurrentItem = mHit.transform.GetComponent<ItemPickUp>();
// mItemRaycastInfoText.EnableText(mHit.transform.position + Vector3.up * rayCastedItem.IndicatorHeight, mCurrentItem.Item); (이 글에서는 설명 X)
Debug.LogFormat("아이템: {0} 획득 가능", mCurrentItem.Item.name);
mIsPickupActive = true;
return;
}
//레이캐스트 닿았을 때, 아이템이 아닌경우에는 비활성화
else
{
ItemInfoDisappear();
}
}
//레이캐스트 결과가 없으면 비활성화
else
{
ItemInfoDisappear();
}
}
/// <summary>
/// 아이템 정보 보여주기를 비활성화 한다.
/// </summary>
private void ItemInfoDisappear()
{
//픽업 비활성화
mIsPickupActive = false;
//텍스트 비활성화
// mItemRaycastInfoText.DisableText(); (이 글에서는 설명 X)
//현재 아이템은 null
mCurrentItem = null;
}
/// <summary>
/// 아이템을 습득한다.
/// </summary>
private void TryPickUp()
{
if (mIsPickupActive)
{
// mItemActionCustomFunc.InteractionItem(mCurrentItem.Item, mCurrentItem.gameObject); (이 글에서는 설명 X)
if (mCurrentItem.Item.Type != ItemType.NONE)
{
mInventory.AcquireItem(mCurrentItem.Item);
Destroy(mCurrentItem.gameObject);
}
ItemInfoDisappear();
}
}
}
- 카메라에서 Ray를 쏴 ItemPickUp 컴포넌트가 있는 게임오브젝트를 찾아 해당 아이템을 획득하거나, 상호작용 할 수 있도록 하는 클래스입니다.
[SerializeField] private float mRayDistance;
- 레이캐스트 거리입니다.
- 얼마나 멀리 있는 오브젝트까지 상호작용이 가능하게 할 지 설정합니다.
private bool mIsPickupActive = false; //아이템 습득이 가능한가?
- 현재 아이템 습득이 가능한지 확인하기 위한 플래그입니다.
- 매 프레임마다 불필요한 함수 호출을 방지하기 위해 사용합니다.
private ItemPickUp mCurrentItem; //활성화시 현재 등록된 아이템
- Ray에 맞은 게임오브젝트에 ItemPickUp 컴포넌트가 있을 경우, 해당 컴포넌트 정보를 가져옵니다.
- Item에 대한 정보를 가져와 사용할 수 있도록 합니다.
private void Update()
- 매 프레임마다 Ray를 쏘며 mIsPickupActive가 참일경우 아이템 획득에 대한 함수도 추가로 호출합니다.
private void TryPickItem()
- 아이템 획득을 시도합니다.
- E키를 누른경우 해당 아이템과 상호작용하거나, 줍는 행동을 합니다.
- 인벤토리가 꽉 찬 상태에서 중첩이 불가능하거나, 중첩가능하지만 해당아이템이 없는 경우 아이템을 주울 수 없는 상황이기에 리턴합니다.
private void CheckItem()
- Ray를 이용하여 플레이어가 바라보는 방향에대해 아이템이 있는지 확인합니다.
- Tag가 Item이나 NPC(추후에 설명)인 경우, 상호작용(또는 주움)할 수 있는 것으로 판단하여 ItemPickUp을 가져옵니다.
- 레이가 닿지 않거나, Item이 아닌경우 상호작용이 불가능하기에 ItemInfoDisappear()를 호출하여 정보를 가리며 아이템 상호작용에 대해서 비활성화합니다.
private void ItemInfoDisappear()
- 아이템을 주워서 해당 아이템이 사라지거나, Ray에 아무것도 맞지 않거나, Tag가 Item이나 NPC가 아닌 경우 아이템 습득에 대한 정보를 감추고, mIsPickupActive를 false로 설정합니다.
private void TryPickUp()
- 아이템을 주울 수 있는지 확인 후, 가능하다면 인벤토리에 해당 아이템을 등록시킵니다.
- mItemActionCustomFunc은 아이템을 습득하거나, 사용할 때 나타나는 이벤트를 관리하는 함수입니다. (이 글에서는 설명하지 않음)
✅ 사용(적용) 예시
- Ray를 쏘기 위해 구현한 ItemRaycast.cs를 사용하기위해 씬 내에 넣어줍니다.
- 컴포넌트의 위치는 상관 없습니다(종속 없음).
- 필자는 Player에 해당 스크립트를 컴포넌트로 넣었습니다.
- 획득할 게임오브젝트를 만들고, ItemPickUp.cs를 컴포넌트로 넣어줍니다.
- 태그를 확인하기에 태그를 Item으로 설정합니다.
- Ray에 대해 반응해야하기에 Collider을 넣어줍니다.
✅ 테스트
- Item에 Ray가 닿은경우 아이템을 획득할 수 있도록 구현하였으며, 제대로 동작하는지 확인해보겠습니다.
- Ray가 아이템에 닿은 경우, 콘솔창에서 해당 아이템을 획득가능하다는 디버그를 볼 수 있습니다.
- 인벤토리를 열면 해당 아이템이 들어와있는것을 볼 수 있습니다.
- 해당 글에서 아이템 데이터를 받아와 글을 출력하는것은 다루지 않습니다.
'unity game modules' 카테고리의 다른 글
[유니티] 인벤토리 시스템(6) - 아이템 사용 (0) | 2022.12.27 |
---|---|
[유니티] 인벤토리 시스템(5) - 아이템 슬롯 관리 (0) | 2022.12.26 |
[유니티] 인벤토리 시스템(3) - 아이템 슬롯(인벤토리 슬롯) (0) | 2022.12.26 |
[유니티] 인벤토리 시스템(2) - 아이템(ScriptableObject) (0) | 2022.12.26 |
[유니티] 인벤토리 시스템(1) - 인벤토리 관리자 (0) | 2022.12.26 |