오늘은 아이템과 관련된 시스템을 만들어보자.

우선 아이템에 필요한 것을 생각해보자. 우선 아이템 이름, 설명, 식별자, 사진, 종류등이 필요할 것 같다. 

이를 클래스로 구현해보자 

이때 나중에 저장기능을 구현할 때

직렬화에 적합하고 데이터 관리와 로직을 분리하며 확장성을 높이기 위해 ItemData 객체를 만들어 이 곳에 정보를 넣고 

Item객체에는 필요한 정보만 가지고 있도록 구현했다.

Item.cs

using Controllers.Entity;
using UnityEngine;

public class Item : MonoBehaviour, IPickable
{
    public ItemData itemData; // ScriptableObject 참조
    public int quantity = 1;

    public void Pickup()
    {
        Managers.Inventory.AddItem(itemData, quantity); // ScriptableObject 기반 인벤토리 추가
        Destroy(gameObject);
    }
}

 

ItemData.cs

using UnityEngine;

[CreateAssetMenu(fileName = "NewItemData", menuName = "Inventory/Item Data")]
public class ItemData : ScriptableObject
{
    public string itemName;
    public int itemID;
    public Sprite icon;
    public string description;
    public ItemType itemType; // 예: 무기, 소비 아이템 등
}

 

ItemType.cs

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

public enum ItemType
{
    Weapon,
    Healing,
    ingredients,
    etc
}

 

이렇게 만든다음에 아이템을 습득할 수 있게 해야하는데 이것은 전에 npc와 상호작용에서 사용한 인터페이스과 레이케스틍을 통해 구현해보자.

IPickable.cs

namespace Controllers.Entity
{
    public interface IPickable
    {
        /// <summary>
        /// Pickable의 필수 구현 사항
        /// </summary>
        void Pickup();
    }
}

Player.cs

private void PickupSomeThing()
{
    RaycastHit2D hit = Physics2D.Raycast(transform.position, facingDirection, interactDistance, LayerMask.GetMask("Pickable"));

    if (hit.collider != null)
    {
        IPickable pickable = hit.collider.GetComponent<IPickable>();
        if (pickable != null)
        {
            pickable.Pickup();
        }
        else
        {
            Debug.LogWarning("주울수 있는 객체가 아닙니다.");
        }
    }
    else
    {
        Debug.LogWarning("주울수 있는 대상이 없습니다.");
    }
}

 

이렇게 해준다음 주웠을 때 플레이어의 인벤토리에 추가될 수 있도록 Inventory를 관리해줄 Manager를 만들어보자.

나는 인벤토리를 리스트로 구현해보았다. 그리고 아이템 추가, 제거, 정보가져오기와 같은 기능들이 들어가도록 구현해보았다.

저장할 때 직렬화에 적합하도록 ItemSlot을 통해 데이터를 조작하도록 구현하였다.

ItemSlot.cs

[System.Serializable]
public class ItemSlot
{
    public ItemData itemData; // ScriptableObject 참조
    public int quantity;      // 해당 아이템 개수
}

 

InventoryManager.cs

using System.Collections.Generic;
using UnityEngine;

public class InventoryManager : MonoBehaviour
{
    private List<ItemSlot> inventory; // 슬롯 기반 관리

    public void Init()
    {
        inventory = new List<ItemSlot>();
    }

    // 아이템 추가
    public void AddItem(Item item, int count = 1)
    {
        ItemSlot slot = inventory.Find(s => s.itemData.itemID == item.itemID);
        if (slot != null)
        {
            slot.quantity += count; // 이미 존재하면 개수 증가
            Debug.Log(slot.itemData.itemName + "의 아이템의 개수가 " + slot.quantity + "개로 늘어났습니다.");
        }
        else
        {
            inventory.Add(new ItemSlot { itemData = item, quantity = count }); // 새로운 슬롯 추가
            Debug.Log(item.itemName + "의 아이템의 개수가 " + count + "개로 늘어났습니다.");
        }
    }

    // 아이템 제거
    public void RemoveItem(Item item, int count = 1)
    {
        ItemSlot slot = inventory.Find(s => s.itemData.itemID == item.itemID);
        if (slot != null)
        {
            slot.quantity -= count;
            if (slot.quantity <= 0)
            {
                inventory.Remove(slot); // 개수가 0 이하일 경우 슬롯 제거
            }
        }
    }

    // 특정 아이템 개수 가져오기
    public int GetItemCount(Item item)
    {
        ItemSlot slot = inventory.Find(s => s.itemData.itemID == item.itemID);
        return slot != null ? slot.quantity : 0;
    }

    private Item FindItemDataByID(int itemID)
    {
        return Resources.Load<Item>($"Items/{itemID}"); // Resources에서 아이템 데이터 검색
    }
}

 

이렇게 해서 테스트해보면 잘 작동하는 것을 볼 수 있다.

 

https://www.acmicpc.net/problem/3273

 

수열을 입력받고 정렬한 다음에 제일 작은 쪽의 인덱스를 가리키는 수 하나와 제일 큰 쪽의 인덱스를 가리키는 수를 통해 문제를 해결하면 된다. 만약 목표로 하는 수보다 크다면 큰 쪽의 인덱스를 줄여주고 목표로 하는 수보다 작다면 작은 쪽의 인덱스를 증가시켜주고 만약 같다면 작은 쪽은 증가시켜주고 큰 쪽은 감소시키고 횟수 카운트를 증가시켜주면 된다.

 

 

정답코드

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

int main()
{
    int n;
    cin >> n; // 수열 크기
    vector<int> v(n);

    for (int i = 0; i < n; i++) {
        cin >> v[i];
    }

    sort(v.begin(), v.end()); // 수열 정렬

    int target, cnt = 0;
    cin >> target; // 목표 합

    int p = 0, q = n - 1; // 투 포인터 초기화

    while (p < q) {
        int sum = v[p] + v[q];
        if (sum == target) { // 목표 합과 같으면
            cnt++;
            p++;    
            q--;    
        }
        else if (sum < target) {
            p++;    // 합이 작으면 작은 값을 증가
        }
        else {
            q--;    // 합이 크면 큰 값을 감소
        }
    }

    cout << cnt << endl; // 쌍의 개수 출력

    return 0;
}

'코딩테스트 > 백준' 카테고리의 다른 글

[백준][C++]2110번. 공유기 설치  (1) 2024.12.02
[백준][C++]9663번. N-Queen  (1) 2024.11.29
[백준][C++]11279번. 최대 힙  (1) 2024.11.28
[백준][C++]7569번. 토마토  (1) 2024.11.27
[백준][C++]7576번. 토마토  (1) 2024.11.26

https://www.acmicpc.net/problem/2110

 

n개 만큼의 좌표를 입력받고 이를 정렬한 다음, 이분 탐색을 활용하여 해결한다. 

1. 공유기 사이의 거리(간격)를 기준으로 최소 거리 low와 최대 거리 high를 설정한다.

2. mid 값을 기준으로 mid 이상인 위치에 공유기를 배치해보고, 설치 가능한 공유기의 개수보다 많다면 거리를 늘려보고, 그렇지 않으면 줄인다.

 

정답코드

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

int main() {
    int n, c;
    cin >> n >> c;

    vector<int> house(n);
    for (int i = 0; i < n; i++) {
        cin >> house[i];
    }

    // 집 좌표 정렬
    sort(house.begin(), house.end());

    int low = 1; // 최소 거리
    int high = house[n - 1] - house[0]; // 최대 거리
    int result = 0;

    while (low <= high) {
        int mid = (low + high) / 2; // 현재 거리
        int prev = house[0]; // 첫 번째 집에 공유기를 설치
        int count = 1; // 설치된 공유기 개수

        // 공유기 설치
        for (int i = 1; i < n; i++) {
            //현재거리보다 멀리떨어진 집에 공유기 설치
            if (house[i] - prev >= mid) {
                count++;
                prev = house[i];
            }
        }

        // 공유기 개수가 많으면 거리 증가
        if (count >= c) {
            result = mid; // 현재 거리 저장
            low = mid + 1;
        }

        // 공유기 개수가 부족하면 거리 감소
        else {
            high = mid - 1;
        }
    }

    cout << result << endl;

    return 0;
}

'코딩테스트 > 백준' 카테고리의 다른 글

[백준][C++]3273번. 두 수의 합  (1) 2024.12.12
[백준][C++]9663번. N-Queen  (1) 2024.11.29
[백준][C++]11279번. 최대 힙  (1) 2024.11.28
[백준][C++]7569번. 토마토  (1) 2024.11.27
[백준][C++]7576번. 토마토  (1) 2024.11.26

https://www.acmicpc.net/problem/9663

 

백트래킹을 사용하여 퀸의 위치를 탐색해야하는 문제이다. 한 행에서 열마다 퀸을 배치해보면서 현재 위치에 퀸을 놓을 수 있는지 검사하고 퀸을 놓고 놓을 수 없다면 skip하는 방식으로 진행한다 이 과정에서 만약 끝 행에 도달했다면 경우의 수를 하나 추가해준다. 

 

정답코드

#include <iostream>
#include <vector>

using namespace std;

int n;
int result = 0;
vector<int> board;

bool isSafe(int row, int col)
{
	//한행씩 탐색
	for (int i = 0; i < row; i++)
	{
		//같은열 | 대각선
		if (board[i] == col || abs(board[i] - col) == row - i)
			return false;
	}

	return true;
}

void solve(int row) {
    if (row == n) {     //만약 끝에 도달했다면
        result++;       //경우의 수 추가
        return;
    }

    for (int col = 0; col < n; col++) {
        if (isSafe(row, col)) { // 현재 위치에 퀸을 놓을 수 있다면
            board[row] = col;  // 퀸을 놓음
			solve(row + 1); // 다음 행으로 진행
        }
    }
}

int main()
{
	cin >> n;
	board.resize(n);
	solve(0);

	cout << result << "\n";

	return 0;
}

'코딩테스트 > 백준' 카테고리의 다른 글

[백준][C++]3273번. 두 수의 합  (1) 2024.12.12
[백준][C++]2110번. 공유기 설치  (1) 2024.12.02
[백준][C++]11279번. 최대 힙  (1) 2024.11.28
[백준][C++]7569번. 토마토  (1) 2024.11.27
[백준][C++]7576번. 토마토  (1) 2024.11.26

 

오늘은 대화창의 선택에 따라 퀘스트가 진행되도록 만들어보자 

우선 퀘스트를 하나 만들어줘야한다. 퀘스트는 Scriptable Object로 만들어서 관리해주도록 하자. 

클래스위에 Create를 쉽게 할 수 있도록 메뉴에서 선택가능하도록 메뉴에 추가해주는 코드를 추가해줬다.

QuestData.cs

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

[CreateAssetMenu(fileName = "NewQuest", menuName = "Quest/QuestData")]
public class QuestData : ScriptableObject
{
    [Header("Quest Info")]
    public string questName;
    public int questId;             //퀘스트번호
    public QuestState questState;   //퀘스트 상태
    public int QuestIndex;          //퀘스트 필요인덱스
    public string location;

    [Header("Reward Info")]
    public int goldReward;
}

 

이 QuestData를 관리해주는 Manager를 만들어주자. 퀘스트를 Resource에서 가져오고 Quest의 상태를 Update하고 QuestData를 반환해주는 함수도 추가해주자.

QuestManager.cs

using System.Collections.Generic;
using UnityEngine;

public class QuestManager : MonoBehaviour
{
    [SerializeField] private Dictionary<int, QuestData> Quests;

    public void Init()
    {
        LoadQuestsFromResources();
        InitializeQuest();
    }

    // Resources 폴더에서 모든 ScriptableObject를 로드하고 Dictionary로 초기화
    private void LoadQuestsFromResources()
    {
        QuestData[] loadedQuests = Resources.LoadAll<QuestData>("Quest");
        Quests = new Dictionary<int, QuestData>();
        if (loadedQuests.Length == 0)
        {
            Debug.LogError("Resources/Quest 폴더에 퀘스트 데이터가 없습니다.");
            return;
        }

        foreach (QuestData questData in loadedQuests)
        {
            if (!Quests.ContainsKey(questData.questId))
            {
                Quests.Add(questData.questId, questData);
                Debug.Log($"퀘스트 Id '{questData.questId}' 인 '{questData.questName}'가 추가되었습니다.");
            }
            else
            {
                Debug.LogWarning($"중복된 questId({questData.questId})가 발견되었습니다. QuestName: {questData.questName}");
            }
        }

        Debug.Log($"{Quests.Count}개의 퀘스트가 로드되었습니다.");
    }

    // Dictionary를 사용하여 퀘스트를 초기화
    private void InitializeQuest()
    {
        if (Quests == null || Quests.Count == 0)
        {
            Debug.LogError("Quests가 null이거나 비어 있습니다. LoadQuestsFromResources를 확인하세요.");
            return;
        }

        foreach (var questData in Quests.Values)
        {
            if (questData.QuestIndex >= Managers.Game.GetPlayer().GetComponent<Player>().index &&
                questData.questState == QuestState.REQUIREMENTS_NOT_MET)
            {
                // 진행 가능한 상태로 변경
                questData.questState = QuestState.CAN_START;
                Debug.Log($"퀘스트 Id '{questData.questId}' 인 '{questData.questName}'가 CAN_START로 초기화되었습니다.");
            }
        }
    }

    // 퀘스트 상태를 업데이트
    public void UpdateQuest(int questId)
    {
        if (Quests.TryGetValue(questId, out QuestData questData))
        {
            questData.questState++;

            if (questData.questState == QuestState.CAN_START)
            {
                Debug.Log($"퀘스트 시작! QuestName: {questData.questName}");
            }
            else if (questData.questState == QuestState.CAN_FINISH)
            {
                Debug.Log($"퀘스트 완료 가능! QuestName: {questData.questName}");
            }
            else if (questData.questState == QuestState.FINISHED)
            {
                Debug.Log($"퀘스트 완료! QuestName: {questData.questName}");
                Managers.Game.GetPlayer().GetComponent<Player>().index++;
                InitializeQuest();
            }
        }
        else
        {
            Debug.LogError($"questId({questId})에 해당하는 퀘스트가 없습니다.");
        }
    }

    // questId로 퀘스트 데이터를 가져옴
    public QuestData GetQuestData(int questId)
    {
        foreach (var questData in Quests.Values)
        {
            Debug.Log(questData.name);
            if (questData.questId == questId) return questData;
        }

        return null;
    }
}

 

이렇게 해주고 간단한 퀘스트를 추가해서 이 매니저가 작동하는지 확인해주자

우선 퀘스트를 가져오는 것은 성공했다.

이제 대화에서 선택했을 때 퀘스트의 진행상황이 바뀌는지 확인하자. 이를 위해 DialogueManager의 Tag관리 코드를 수정해주자.

DialogueManager.cs

private const string QuestTag = "Quest"; //테그값들 테그값 : 변수
private const string ChooseTag = "Start";
private const string EndTag = "End";

public void ContinueStory()
{
    if (currentStory == null) // Null 체크 추가
    {
        Debug.LogError("currentStory가 null입니다!");
        return;
    }

    if (currentStory.canContinue) // 더 보여줄 이야기가 있다면
    {
        popup.displayNameText.text = npcdata.getName();
        popup.portraitImage.sprite = npcdata.getPortrait();
        popup.dialogueText.text = currentStory.Continue(); // 한줄 출력
        DisplayChoices(); // 선택이 있으면 선택 출력
        HandleTags(currentStory.currentTags);
    }
    else
    {
        ExitDialogueMode();
    }
}
 
 private void HandleTags(List<string> currentTags)
 {
     foreach (string tag in currentTags)
     {
         string[] splitTag = tag.Split(':');
         if (splitTag.Length != 2)
         {
             Debug.LogError("Tag parsed error : " + tag + splitTag);
         }
         string tagkey = splitTag[0].Trim();
         string tagvalue = splitTag[1].Trim();


         switch (tagkey)
         {
             case QuestTag:
                 currentQuestIndex = int.Parse(tagvalue);
                 break;
             case ChooseTag:
                 if (tagvalue =="1")
                 {
                     Managers.Quest.UpdateQuest(currentQuestIndex);
                     Debug.Log("Update!"+Managers.Quest.GetQuestData(currentQuestIndex).questState);
                 }
                 break;
             case EndTag:
                 if(tagvalue =="1")
                 {
                     Managers.Quest.UpdateQuest(currentQuestIndex);
                     Debug.Log("Update!" + Managers.Quest.GetQuestData(currentQuestIndex).questState);
                 }
                 break;
             default:
                 Debug.LogWarning("Tag exists but not handled");
                 break;
         }

     }
 }

 

이렇게 해주고 대화를 하나 만들어주자.

Sori1.ink

이렇게 해주고 테스트해보자.

정상적으로 작동하는 것을 볼 수 있다.

https://www.acmicpc.net/problem/11279

 

priority_queue를 사용해서 입력받는 수에 따라 push와 pop을 진행해주면 된다. 

 

 

정답코드

#include <iostream>
#include <queue>
using namespace std;

int main()
{

	ios::sync_with_stdio(false);  // C와 C++의 I/O 동기화 비활성화
	cin.tie(NULL);  // 입출력 독립 처리

	int n;
	cin >> n;
	priority_queue<int> pq;
	for (int i = 0; i < n; i++)
	{
		int x;
		cin >> x;
		if (x > 0)
		{
			pq.push(x);
		}
		else
		{
			if (!pq.empty())
			{
				cout << pq.top() << "\n";
				pq.pop();
			}
			else
			{
				cout << 0 << "\n";
			}
		}
	}

	return 0;
}

 

'코딩테스트 > 백준' 카테고리의 다른 글

[백준][C++]2110번. 공유기 설치  (1) 2024.12.02
[백준][C++]9663번. N-Queen  (1) 2024.11.29
[백준][C++]7569번. 토마토  (1) 2024.11.27
[백준][C++]7576번. 토마토  (1) 2024.11.26
[백준][C++]1697번. 숨바꼭질  (0) 2024.11.25

https://www.acmicpc.net/problem/7569

 

m,n,h값을 통해 3차원 벡터를 초기화 해주고 1인 부분을 queue에 저장하고 이를 BFS방식으로 순회하면서 1인 부분을 추가하고 이 추가한 값을 다시 queue에 넣어서 날마다 반복해주면 된다. 전체 개수와 익은 개수를 비교하기 위해 빈것도 포함해서 개수를 추가해줬다. 

 

정답코드

#include <iostream>
#include <vector>
#include <queue>

using namespace std;

// 6방향 이동 (위, 아래, 좌, 우, 앞, 뒤)
int dx[6] = { 0, 0, 0, 0, -1, 1 };
int dy[6] = { 0, 0, -1, 1, 0, 0 };
int dz[6] = { -1, 1, 0, 0, 0, 0 };

int main() {
    int m, n, h;
    cin >> m >> n >> h;

    vector<vector<vector<int>>> tomato(h, vector<vector<int>>(n, vector<int>(m)));
    queue<pair<int, pair<int, int>>> q;  // 큐에 (z, x, y) 좌표 저장

    int empty_count = 0;  // 비어 있는 칸 개수

    // 입력 받기
    for (int z = 0; z < h; z++) {
        for (int x = 0; x < n; x++) {
            for (int y = 0; y < m; y++) {
                cin >> tomato[z][x][y];
                if (tomato[z][x][y] == 1) {
                    q.push({ z, {x, y} });  // 익은 토마토 위치 큐에 삽입
                }
                else if (tomato[z][x][y] == -1) {
                    empty_count++;  // 비어 있는 칸 카운트
                }
            }
        }
    }

    int total_cells = n * m * h - empty_count;  // 전체 칸에서 비어 있는 칸 제외
    int ripe_count = q.size();  // 초기 익은 토마토 개수
    int days = -1;

    // 이미 모든 토마토가 익어 있는 경우
    if (ripe_count == total_cells) {
        cout << 0 << '\n';
        return 0;
    }

    // BFS 수행
    while (!q.empty()) {
        int size = q.size();
        for (int i = 0; i < size; i++) {
            int z = q.front().first;
            int x = q.front().second.first;
            int y = q.front().second.second;
            q.pop();

            for (int d = 0; d < 6; d++) {
                int nz = z + dz[d];
                int nx = x + dx[d];
                int ny = y + dy[d];

                // 배열 범위 체크 및 익지 않은 토마토 찾아서 익히기
                if (nz >= 0 && nz < h && nx >= 0 && nx < n && ny >= 0 && ny < m && tomato[nz][nx][ny] == 0) {
                    tomato[nz][nx][ny] = 1;  // 익은 토마토로 상태 변경
                    q.push({ nz, {nx, ny} });  // 큐에 추가
                    ripe_count++;  // 익은 토마토 수 증가
                }
            }
        }
        days++;  // 하루가 지나면
    }

    // 모든 칸이 익었는지 확인
    if (ripe_count == total_cells) {
        cout << days << '\n';  // 모든 토마토가 익었으면 소요된 일수 출력
    }
    else {
        cout << -1 << '\n';  // 익지 않은 토마토가 남았으면 -1 출력
    }

    return 0;
}

'코딩테스트 > 백준' 카테고리의 다른 글

[백준][C++]9663번. N-Queen  (1) 2024.11.29
[백준][C++]11279번. 최대 힙  (1) 2024.11.28
[백준][C++]7576번. 토마토  (1) 2024.11.26
[백준][C++]1697번. 숨바꼭질  (0) 2024.11.25
[백준][C++]7562번. 나이트의 이동  (0) 2024.11.24

https://www.acmicpc.net/problem/7576

 

 

2차원 벡터를 통해 토마토가 있는 곳을 입력받고 출발점이 되는 1을 모두 큐에 저장하고 이 q를 BFS로 순회하면서 토마토가 있는 곳을 익게하고 이 익은 토마토가 다시 출발점이 되어서 이 큐가 빌때까지 계속하면 된다. 

전체가 다 익었는지 확인하기 위하여 비어있는 토마토의 개수도 같이 세어준다. 

 

정답코드

#include <iostream>
#include <vector>
#include <queue>

using namespace std;


int dx[4] = { 0,0,-1,1 };
int dy[4] = { -1,1,0,0 };

int main()
{
	int m, n;
	cin >> m >> n;
	vector<vector<int>> tomato(n, vector<int>(m));
	queue<pair<int, int>> q;		//출발점 큐
	int empty_count = 0;

	for (int i = 0; i < n; i++) {
		for (int j = 0; j < m; j++) {
			cin >> tomato[i][j];
			if (tomato[i][j] == 1) {
				q.push({ i, j }); // 익은 토마토의 위치 저장
			}
			else if (tomato[i][j] == -1) {
				empty_count++; // 비어있는 칸 개수 카운트
			}
		}
	}

	int days = -1;
	int total_cells = n * m;
	int ripe_count = q.size() + empty_count; // 초기 익은 토마토와 비어있는 칸
	

	while (!q.empty())
	{
		int size = q.size();
		for (int i = 0; i < size; i++)
		{
			int x = q.front().first;
			int y = q.front().second;
			q.pop();

			for (int d = 0; d < 4; d++)
			{
				int nx = x + dx[d];
				int ny = y + dy[d];

				if (nx >= 0 && nx < n && ny >= 0 && ny < m && tomato[nx][ny] == 0)
				{
					tomato[nx][ny] = 1;
					q.push({ nx,ny });
					ripe_count++;
				}
			}
		}
		days++;
	}

	// 모든 칸이 익었는지 확인
	if (ripe_count == total_cells) {
		cout << days << '\n';
	}
	else {
		cout << -1 << '\n';
	} return 0;

	return 0;
}

 

이전에 만들었던 대화 출력은 올바르게 되었으니 이제 대화창에서 선택지가 출력되고 이를 선택할 수 있게 만들어주자.

일단 저번에 만들었던 대화를 출력해주는 DialogueManager의 ContinueStory 함수를 수정해주자. 

DialogueManager.cs

public void ContinueStory()
{
    if (currentStory == null) // Null 체크 추가
    {
        Debug.LogError("currentStory가 null입니다!");
        return;
    }

    if (currentStory.canContinue) // 더 보여줄 이야기가 있다면
    {
        popup.displayNameText.text = npcdata.getName();
        popup.portraitImage.sprite = npcdata.getPortrait();
        popup.dialogueText.text = currentStory.Continue(); // 한줄 출력
        DisplayChoices(); // 선택이 있으면 선택 출력
    }
    else
    {
        ExitDialogueMode();
    }
}

 

오류체크와 선택이 있다면 선택을 출력해주는 함수를 추가해줬다.

이 Display함수는 만약 선택이 있다면 선택의 개수만큼만 버튼을 활성화 시켜주고 나머지는 비활성화시켜준다. 그리고 마지막에 코루틴을 사용하여 첫 항목이 선택되게 한다. 이 코루틴이 있어야 선택이 정상적으로 작동한다.

DialogueManager.cs

 private void DisplayChoices()
 {
     if (popup == null) // Null 체크 추가
     {
         Debug.LogError("팝업 UI가 null입니다!");
         return;
     }

     List<Choice> currentChoices = currentStory.currentChoices;
     if (currentChoices.Count > popup.choices.Length) // 현재 선택지의 개수가 버튼의 개수보다 많으면 오류
     {
         Debug.LogError("More choices than ever");
     }

     int index = 0;
     foreach (Choice choice in currentChoices)
     {
         popup.choices[index].gameObject.SetActive(true);
         popup.choicesText[index].text = choice.text;
         index++;
     }

     for (int i = index; i < popup.choices.Length; i++)
     {
         popup.choices[i].gameObject.SetActive(false);
     }
     popup.choicep.SetActive(true);

     StartCoroutine(SelectFirstChoice());
 }
  private IEnumerator SelectFirstChoice()
  {
      EventSystem.current.SetSelectedGameObject(null);
      yield return new WaitForEndOfFrame();
      EventSystem.current.SetSelectedGameObject(popup.choices[0].gameObject);
  }

 

그리고 버튼에 OnclickEvent를 추가해주기 위해 MakeChoice함수를 추가하고 이를 Popup을 관리해주는 객체에서 사용하도록 하자.

DialogueManager.cs

public void makeChoice(int choice)
{
    if (currentStory == null) // Null 체크 추가
    {
        Debug.LogError("currentStory가 null입니다!");
        return;
    }

    currentStory.ChooseChoiceIndex(choice);
    ContinueStory();
}

 

UI_DialioguePopup.cs

private void Start()
{
    for (int i = 0; i < choices.Length; i++)
    {
        int id = i;
        choiceButton[i].onClick.AddListener(() => Managers.Dialogue.makeChoice(id));
    }
}

 

이렇게 해주면 이제 대화를 진행하면서 선택지가 있으면 선택지를 선택할 수 있게 된다.

 

이제 다음에는 이 선택지에 따라 퀘스트가 진행되게 만들어볼 것이다.

 

※매니저에서 코루틴을 사용하는 상황에서 만약 각 매니저가 통합매니저에서 싱글톤으로 관리되고 있다고 하면 사용할 때 New 키워드를 사용하면 nullreferenceexception 에러가 뜬다. 이를 방지하기 위해 GameObject에서 Getcomponent를 통해 해당 매니저를 가져와서 사용해야한다.

https://www.acmicpc.net/problem/1697

 

최대 범위인 100,000으로 벡터를 초기화해주고 BFS를 통해 탐색을 해주면서 제일 먼저 동생의 위치에 도달하면 그 값을 반환해준다. 이때 현재값에서 -1 +1 *2를  해줘야하기 때문에 이 값을 내부 반복문에 넣고 이를 순회하면서 탐색하도록 해주었다.

 

정답코드

#include <iostream>
#include <queue>
#include <vector>

using namespace std;

const int MAX = 100001;

vector<int> v(MAX, 0);

int BFS(int start, int end)
{
	queue<int> q;

	q.push(start);
	v[start] = 1;

	while (!q.empty())
	{
		int curx = q.front();
		q.pop();

		if (curx == end) return v[end] - 1;

		for (int nextx : {curx - 1, curx + 1, 2 * curx})
		{
			//범위검사
			if (nextx >= 0 && nextx < MAX && v[nextx] == 0)
			{
				v[nextx] = v[curx] + 1;
				q.push(nextx);
			}
		}
	}

	return -1;
}


int main()
{
	int n, k;
	cin >> n >> k;


	cout << BFS(n, k) << "\n";

	return 0;
	
}

 

+ Recent posts