Chapter 8-4. 모닥불 : 불에 고기 익히기(퀵슬롯 자동 장착)

Date:     Updated:

카테고리:

태그:

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

불에 고기 익히기

🚘 익힌 고기, 탄 고기 아이템 만들기

Prefab

image

기존에 있던 생고기 프리팹의 Material 만 바꿔서 익힌 고기, 탄 고기 프리팹을 만들어주었다.

  • Meat_Cooked
  • Meat_Burnt

Item

image

익힌 고기, 탄 고기 Item도 만들고 위에서 만든 프리팹도 할당해주었다. 프리팹들의 📜ItemPickUp.cs 컴포넌트에도 마찬가지로 이 아이템들을 각각 할당.


🚘 고기 굽기

📜FoodOnFire.cs

이 스크립트를 구울 수 있는 생고기, 익힌고기 프리팹에 붙여준다. (탄고기는 구울 수 없으므로 X)

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

public class FoodOnFire : MonoBehaviour

    /* 익힌 고기와 탄 고기가 이 스크립트를 공유할 것이다. */
{
    [SerializeField]
    private float time; // 익히거나 타는데 걸리는 시간
    private float currentTime; // 업데이트해 나갈 시간. time 에 도달시킬 것.

    private bool done; // 끝났으면 더 이상 불에 있어도 계산 무시할 수 있게끔

    [SerializeField]
    private GameObject go_CookedItem_Prefab; // 익혀진 혹은 탄 고기 아이템 교체


    private void OnTriggerStay(Collider other)
    {
        if(other.transform.tag == "Fire" && !done)
        {
            currentTime += Time.deltaTime;

            if (currentTime >= time)
            {
                done = true;
                Instantiate(go_CookedItem_Prefab, transform.position, Quaternion.Euler(transform.eulerAngles));
                Destroy(gameObject); // 날고기인 자기 자신은 파괴
            }
        }
    }
}

image

  • 익힌 고기, 생 고기 두 프리팹에 위 스크립트를 붙여준다.
    • 두 고기 모두 5 초 뒤에 [생 고기👉익힌 고기], [익힌 고기 👉 탄고기]가 되도록 할 것이다.
      • 5초가 지나면 자기 자신은 파괴하고 go_CookedItem_Prefab에 할당된 다음 단계의 고기 프리팹을 생성
    • 익힌 고기 불에서 안빼면 알아서 또 다시 5초 후에 탄 고기가 된다. 즉, 생고기 넣고 10초동안 안빼면 탄 고기가 됨.
      • 생고기의 📜FoodOnFire 👉 5 초 후에 익힌 고기 오브젝트 생성 (익힌 고기의 📜FoodOnFire 새롭게 호출)
      • 익힌 고기의 📜FoodOnFire 👉 5 초 후에 탄 고기 오브젝트 생성
  • 생 고기의 go_CookedItem_Prefab에는 익힌 고기 프리팹을, 익힌 고기의 go_CookedItem_Prefab에는 탄 고기 프리팹을 할당.


불 설정

빨간 불 파티클 시스템인 Flame 오브젝트의 태그와 레이어를 “Fire”로 설정해준다. 레이어까지 설정해주는 이유는 밑에 📜ActionController 에서 플레이어(메인카메라)가 불을 바라보면(레이캐스팅 필요) 불에 고기를 익힐 수 있다는 메세지를 띄울 것이라서! 📢주의할 점 👉 “Fire” 레이어를 붙여줬으면 Main Camera의 Culling Mask 에도 “Fire” 레이어를 추가로 설정해주어야 렌더링 되게 된다.

image

불에 고기를 던져 구울 때 고기가 불에 파묻히면 보이지 않으므로 불의 윗부분에 고기를 구울 수 있도록 Sphere Collider를 하나 더 깔아주었다. Is Trigger가 체크 해제 되어야 충돌 처리되어 Sphere Collider에 관통되지 않고 위에 있을 수 있게 된다.


🚘 고기 굽는 메세지 및 고기 아이템 처리

플레이어가 고기를 직접 불에 떨구려면 불에 들어가야 해서 데미지를 입으므로 불에 가까이 가서 불을 바라보면 자동으로 메세지를 띄우고 특정 키를 누르면 자동으로 고기가 불에 들어가 구워지게끔 할 것이다.

  • 📜ActionController.cs
    • 레이어(1인칭 메인카메라)가 불을 가까이서 불을 바라 보면 불에 고기를 구울 수 있다는 메세지 출력
    • 불 굽기 입력 처리
    • 실제 불을 굽는 처리
      • 퀵슬롯에 있던 고기 아이템은 제거 (구워졌으니)

📜Fire.cs

    public bool GetIsFire()
    {
        return isFire;
    }
  • 불이 활성화 되있는 상태에서만 고기를 구울 수 있으므로 불의 활성화 상태를 가져오는 접근 함수를 만들었다. isFire가 private 이라서.

📜QuickSlotController.cs

    public Slot GetSelectedSlot()
    {
        return quickSlots[selectedSlot];
    }

    public void DecreaseSelectedItem()
  • 마찬가지로 현재 활성화된 슬롯이 어떤건지도 알 수 있도록 이를 리턴하는 접근 함수를 만들었다.
  • 기존 EatItem() 함수를 DecreaseSelectedItem()로 이름 변경.

📜Inventory.cs

    [SerializeField]
    private QuickSlotController theQuickSlotController;
    private int slotNumber;

    public void AcquireItem(Item _item, int _count = 1)
    {
        PutSlot(quickSlots, _item, _count);

        if (!isNotPut)  /// 🧡🧡🧡 이거 추가
            theQuickSlotController.IsActivatedQuickSlot(slotNumber);

        if (isNotPut)
            PutSlot(slots, _item, _count);

        if (isNotPut)
        {
            isFull = true;
            StartCoroutine(theActionController.WhenInventoryIsFull());
        }      
    }

    private void PutSlot(Slot[] _slots, Item _item, int _count)
    {
        if (Item.ItemType.Equipment != _item.itemType)
        {
            for (int i = 0; i < _slots.Length; i++)
            {
                if (_slots[i].item != null)
                {
                    if (_slots[i].item.itemName == _item.itemName)
                    {
                        slotNumber = i;  /// 🧡🧡🧡 이거 추가
                        _slots[i].SetSlotCount(_count);
                        isNotPut = false;
                        return;
                    }
                }
            }
        }

        for (int i = 0; i < _slots.Length; i++)
        {
            if (_slots[i].item == null)
            {
                _slots[i].AddItem(_item, _count);
                isNotPut = false;
                return;
            }
        }

        isNotPut = true;
    }
  • 변수 추가
    • slotNumber 👉 방금 아이템 넣는데 성공한, 아이템 넣은 그 슬롯의 넘버 저장
    • theQuickSlotController 👉 에디터에서 📜QuickSlotController 할당
  • 퀵슬롯에 아이템을 넣었다면 그 슬롯을 활성화 퀵슬롯으로 한다.
    • PutSlot(quickSlots, _item, _count)을 통해 퀵슬롯에 성공적으로 아이템을 넣었다면 isNotPut는 False 상태가 된다. 또한 PutSlot 함수를 실행하면서 아이템 넣은 퀵슬롯 넘버인 slotNumber에 저장하기 때문에 slotNumber를 📜QuickSlotController의 IsActivatedQuickSlot 함수에 넘겨 활성화 퀵슬롯으로 지정하고 이에 대해 실행한다.(손에 들고 있게 한다던지 Excute()를 실행함)
          PutSlot(quickSlots, _item, _count);
      
          if (!isNotPut)  /// 🧡🧡🧡 이거 추가
              theQuickSlotController.IsActivatedQuickSlot(slotNumber);
      


📜ActionController.cs

private bool firedLookActivated = false; // 불에 접근해서 불을 바라볼 시 True
[SerializeField]
private QuickSlotController theQuickSlotController; // 📜QuickSlotController.cs


    private void TryAction()
    {
        if(Input.GetKeyDown(KeyCode.E))
        {
            CheckAction();
            CanPickUp();
            CanMeat();
            CanDropFire();
        }
    }
    
    private void CheckAction()
    {
        if (Physics.Raycast(transform.position, transform.forward, out hitInfo, range, layerMask))
        {
            if (hitInfo.transform.tag == "Item")
                ItemInfoAppear();
            else if (hitInfo.transform.tag == "Weak_Animal")
                MeatInfoAppear();
            else if (hitInfo.transform.tag == "Fire")
                FireInfoAppear();
            else
                InfoDisappear();
        }
        else
            InfoDisappear();
    }

    private void Reset()
    {
        pickupActivated = false;
        dissolveActivated = false;
        firedLookActivated = false;
    }

    private void ItemInfoAppear()
    {
        Reset();
        pickupActivated = true;
        actionText.gameObject.SetActive(true);
        actionText.text = hitInfo.transform.GetComponent<ItemPickUp>().item.itemName + " 획득 " + "<color=yellow>" + "(E)" + "</color>";
    }

    private void MeatInfoAppear()
    {
        // 📜Animal.cs의 animalName, isDead 을 public 으로 바꿨다.
        if (hitInfo.transform.GetComponent<Animal>().isDead) // 죽은 돼지를 바라봤을 경우에만 (카메라에서 쏜 Raycast가 죽은 돼지와 충돌)
        {
            Reset();
            dissolveActivated = true;
            actionText.gameObject.SetActive(true);
            actionText.text = hitInfo.transform.GetComponent<Animal>().animalName + " 해체하기 " + "<color=yellow>" + "(E)" + "</color>";
        }
    }

    private void FireInfoAppear()
    {
        Reset();
        firedLookActivated = true;
        
        if (hitInfo.transform.GetComponent<Fire>().GetIsFire())
        {
            actionText.gameObject.SetActive(true);
            actionText.text = "선택된 아이템 불에 넣기" + "<color=yellow>" + "(E)" + "</color>";
        }
    }

    private void InfoDisappear()
    {
        pickupActivated = false;
        dissolveActivated = false;
        firedLookActivated = false;
        actionText.gameObject.SetActive(false);
    }

    private void CanDropFire()
    {
        if (firedLookActivated)
        {
            if (hitInfo.transform.tag == "Fire" && hitInfo.transform.GetComponent<Fire>().GetIsFire())
            {
                // 손에 들고 있는 아이템을 불에 넣음 <- 선택된 퀵슬롯의 아이템을 넣음 (Null 빈슬롯일 수도 있음. 판별이 필요)
                Slot _selectedSlot = theQuickSlotController.GetSelectedSlot();

                if (_selectedSlot.item != null)
                    DropAnItem(_selectedSlot);
            }
        }
    }

    private void DropAnItem(Slot _selectedSlot)
    {
        switch(_selectedSlot.item.itemType)
        {
            case Item.ItemType.Used:
                if (_selectedSlot.item.itemName.Contains("고기"))
                {
                    Instantiate(_selectedSlot.item.itemPrefab, hitInfo.transform.position + Vector3.up, Quaternion.identity);
                    theQuickSlotController.DecreaseSelectedItem();
                }
                break;
            case Item.ItemType.Ingredient:
                break;
        }
    }
  • 1️⃣ 고기 바라볼 시 메세지 띄우기
    • 매 프레임마다 실행되는 CheckAction() 에서 이 스크립트가 붙어있는 메인카메라(플레이어의 시점)에서 레이캐스팅을 할 때 충돌되는 레이어가 “Fire”라면 👉 즉 플레이어가 rnage 사정거리 내에 바라보고 있는게 불이라면
      • FireInfoAppear() 👉 고기 구울 수 있다는 액션 텍스트 띄우기
        • Reset() : 해당 작업이 이제 가능하다는 flag 변수들을 모두 false로 초기화 한다.
          • Reset()InfoDisappear() 에서 액션 텍스트를 비활성화 하는 작업만 빠져있는 형태다. flag 변수들만 초기화 함.
          • 이 초기화 작업이 필요한 이유는, 불에 굽고 있는 고기 아이템을 바라보면 pickupActivated인 상태가 될 수도 있고 동시에 firedLookActivated인 상태가 될 수도 있기 때문이다. 이렇게 되면 CanPickUp(), CanDropFire() 두 함수 모두 다 실행할 수 있는 상태가 되므로 이 상태에서 E를 누르면 두 함수가 실행되어 충돌이 일어난다. 아이템을 불에 버려야하는데 아이템을 주우라니 충돌이 일어날 수 밖에 없다. 그래서 런타임 에러가 발생한다. 따라서 Reset() 함수를 만들어 초기화 하는 작업을 먼저 함. 두 flag 변수가 동시에 true가 되는 일 없고 하나만 작업될 수 있도록.
          • 다른 ItemInfoAppear(), MeatInfoAppear() 함수에 대해서도 똑같이 작업전 Reset()을 실행하도록 하였다.
        • firedLookActivated를 True로 하여 E 키 입력이 들어오면 CanDropFire() 함수가 실행될 수 있는 상태로 만들어주준다.
        • 불이 활활 타고 있는 상태라면 액션텍스트를 활성화한다. “선택된 아이템 불에 넣기”
  • 2️⃣ 고기 굽는 처리
    • TryAction()
      • E키 입력을 받으면 CanDropFire() 함수 실행
    • CanDropFire()
      • 플레이어가 고기를 가까이서 바라보아서 firedLookActivated를 True로 갱신된 상태라면 다음과 같이 고기를 불에 드롭하는 처리를 해준다.
        • 손에 들고 있는(활성화 퀵슬롯) 아이템을 불에 넣는다. DropAnItem(_selectedSlot) 다만 빈슬롯이 아닐 때만!!
          • DropAnItem(Slot _selectedSlot)
            • 활성화 퀵슬롯 아이템이(즉 손에 들고 있는 아이템이) 고기 종류의 소모(Used) 아이템이라면 (아이템 이름에 “고기”가 들어감) 해당 아이템을 불 위에 생성한다.
            • 그리고 DecreaseSelectedItem 함수를 호출하여 해당 아이템은 퀵슬롯에서 감소 혹은 제거한다.

image

  • layerMask에 “Fire” 레이어를 추가한다.
  • 📜QuickSlotController 할당


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

맨 위로 이동하기

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

댓글 남기기