XR플밍 - 11. 사전 합반 프로젝트 6일차(6/26)

이형원·2025년 6월 26일
0

XR플밍

목록 보기
116/215

0.들어가기에 앞서

문제의 JSON 오류, 결국 해결책을 찾아왔다.
오늘 작업으로 모인 당일날, 팀장님을 제외한 모든 팀원에게서 해당 문제가 발생했고 그 해결 과정에 대해 알아보고자 한다.

1. 금일 한 업무 정리, 구현 내용

오전부터 터진 병합 오류 때문에 1시간을 날리고 시작했다 보니 작업량이 많지는 않았다.

  • 아이템 생성기

  • 아이템 크래프팅 UI 구성

기획서에 기재된 UI 디자인에 맞춰 제작하였다.

  • 아이템 레시피 생성기

2. 문제의 발생과 해결과정

오늘은 아무래도 생성기 관련해서 문제를 많이 겪었다.

특히나 이미지와 프리팹을 생성하는 과정을 적용하기 위해서 시도한 방식에서 시행착오를 제법 겪었었다.

위 1번에서의 결과로도 볼 수 있지만 CSV파일로 가져오기 힘든 이미지나 프리팹을 가져온 방식은 다음과 같다.

CSV파일에서 직접적으로 해당 정보를 넣는 것은 어렵다. 따라서 프로젝트에 아이템과 프리팹을 전부 한 폴더에 모아 놓고, 해당 경로를 참조하는 방식을 사용해보자.

이미지와 프리팹은 지정된 경로에서 생성하고, 오탈자 없이 매칭되게 생성한다. 그 다음으로 해당 경로를 통해 이미지와 프리팹을 각각 가져오는 방식으로 구현했다.

  • ItemSO
    구성요소가 하나 추가 되었다.(아이템 코드)
using UnityEngine;

public enum ItemType { Material, Usable, Equip }

/// <summary>
/// Scriptable Object that contains Item Data.
/// </summary>
[CreateAssetMenu(fileName = "New Item", menuName = "Assets/New Item")]
public class ItemSO : ScriptableObject
{
    public int ItemId;
    public string Name;
    public string Description;
    public float Weight;
    public ItemType Type;

    // if is stackable, input number larger than 1.
    // if is unStackable, input 1.
    public int MaxStackSize;
    public int Energy;
    public Sprite Icon;
    public GameObject Prefab;
}
  • TableItemToSO
using UnityEngine;
using UnityEditor;
using System.IO;
using System;

/// <summary>
/// Create Item base on Table Data.(Will be fixed when table is created)
/// How to use : check the editor bar - Utilities - Generate Item
/// Should not be contained in the build file.
/// </summary>
public class TableItemToSO
{
    private static string _itemCSVPath = "/LHW/Scripts/ItemCreator/Editor/CSV/TestItem.csv";
    [MenuItem("Utilities/Generate Item")]
    public static void GenerateItems()
    {
        string[] allLines = File.ReadAllLines(Application.dataPath + _itemCSVPath);

        foreach(string s  in allLines)
        {
            string[] splitData = s.Split(",");

            if(splitData.Length != 9 )
            {
                Debug.LogWarning($"{s} could not be imported.");
                return;
            }

            ItemSO item = ScriptableObject.CreateInstance<ItemSO>();
            int.TryParse(splitData[0], out item.ItemId);
            item.Name = splitData[1];
            item.Description = splitData[2];
            float.TryParse(splitData[3], out item.Weight);
            Enum.TryParse<ItemType>(splitData[4], true, out item.Type);
            int.TryParse(splitData[5], out item.MaxStackSize);
            int.TryParse(splitData[6], out item.Energy);

            string spritePath = splitData[7];
            string prefabPath = splitData[8];

            // If Item Path is selected, path will be edited.
            item.Icon = AssetDatabase.LoadAssetAtPath<Sprite>(spritePath);
            item.Prefab = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);

            if (item.Icon == null)
                Debug.LogWarning($"Sprite not found at path: {spritePath}");
            if (item.Prefab == null)
                Debug.LogWarning($"Prefab not found at path: {prefabPath}");

            AssetDatabase.CreateAsset(item, $"Assets/LHW/ItemData/{item.Name}.asset");
        }

        AssetDatabase.SaveAssets();
    }
}

이와 같이 만들어 테스트 시에 모든 변수가 참조될 수 있도록 수정하였다.
여기서, 이 경로를 전부 테이블 데이터로 받아오기 힘들 것 같아서, 어떻게 하면 좀 더 편하게 받아올 수 있을까 고민을 했다. 그러던 중에 떠오른 아이디어가 있었는데 해당 아이디어를 이미지로 정리했다.

아이템 이미지와 프리팹을 생성하는 경로가 확정되면 경로에 해당하는 부분을 최대한 문자열 보간으로 처리하는 방식이다. 이와 같이 경로를 전부 써 놓으면 아마 기획자가 작성해야 할건 이미지의 이름 정도만 정해주면 될 것이다. (이미지는 형식까지는 요구할지 고민이다.)

우선은 이와 같이 아이템을 만들었으니, 다음으로 크래프팅에 대한 작업을 할 차례이다.

크래프팅에는 기본적으로 크래프팅 레시피라는 데이터도 필요하다는 사실이 알게 되었다.

이에 따라 크래프팅 레시피도 스크립터블 오브젝트로 변환하는 기능이 필요하겠다 생각했다.

  • Crafting Recipe
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// Scriptable Object that contains Recipe.
/// </summary>
[CreateAssetMenu(fileName = "Recipe", menuName = "Assets/New CraftingRecipe")]
public class CraftingRecipe : ScriptableObject
{
    [SerializeField] public float craftingTime;
    [SerializeField] public List<ItemSO> reqItem = new();
    [SerializeField] public ItemSO resultItem;
}

내용 자체는 간단하다. 어차피 레시피란 것은 결국 아이템의 데이터를 받아와야 하는 것이고, 아이템 데이터에 이미지 정보라든지 데이터를 가져올 수 있기 때문에, 재료 개수를 다변화할 수 있으면서 제작 시간까지 설정할 수 있게 했다.
이제 문제는 이 스크립터블 오브젝트를 어떻게 가져오느냐다.
마찬가지로 경로로 가져오는 것은 맞는데, 아무래도 테스트하는 데에 어려움이 있었다.

  • TableRecipeToSO
using UnityEditor;
using UnityEngine;
using System.IO;

/// <summary>
/// Create Recipe base on Table Data.(Will be fixed when table is created)
/// How to use : check the editor bar - Utilities - Generate Item
/// Should not be contained in the build file.
/// </summary>
public class TableRecipeToSO : MonoBehaviour
{
    private static string _itemCSVPath = "/LHW/Scripts/Crafting/Editor/CSV/TestRecipe.csv";
    [MenuItem("Utilities/Generate Recipe")]
    public static void GenerateRecipe()
    {
        string[] allLines = File.ReadAllLines(Application.dataPath + _itemCSVPath);

        foreach (string s in allLines)
        {
            string[] splitData = s.Split(",");

            CraftingRecipe recipe = ScriptableObject.CreateInstance<CraftingRecipe>();
            float.TryParse(splitData[0], out recipe.craftingTime);

            Debug.Log("Sucess1");
            
            for(int i = 1; i < splitData.Length - 1; i++)
            {
                string itemDataPath = splitData[i];
                recipe.reqItem.Add(AssetDatabase.LoadAssetAtPath<ItemSO>(itemDataPath));

                if (recipe.reqItem == null)
                    Debug.LogWarning($"Sprite not found at path: {itemDataPath}");

                else
                {
                    Debug.Log("Success2");
                }
            }            

            string lastItemDataPath = splitData[splitData.Length - 1];
            recipe.resultItem = AssetDatabase.LoadAssetAtPath<ItemSO>(lastItemDataPath);
                        
            if (recipe.resultItem == null)
                Debug.LogWarning($"Prefab not found at path: {lastItemDataPath}");

            AssetDatabase.CreateAsset(recipe, $"Assets/LHW/RecipeData/{recipe.resultItem.Name}.asset");
        }

        AssetDatabase.SaveAssets();
    }
}

리스트로 데이터를 가져와야 하고, 재료의 개수를 한정짓지 않았기 때문에 for문으로 돌리는 방식으로 했다.
특히나 쩔쩔맸던 게 각각의 구성요소마다 경로와 함께 형식을 적어야 하는 게 어려웠다.

예를 들어 스프라이트면 .png나 .jpeg, 프리팹은 .prefab, 그리고 스크립터블 오브젝트는 .asset 으로 가져와야 한다.
이걸 일일히 알아보는 것은 어렵지 않을까? 라는 생각이 들었던 잠시, 아주 쉽게 경로를 확인할 수 있는 방법을 놓쳤다는 걸 깨달았다.

...그냥 참조하고 싶은 걸 누르면 맨 아래에 경로와 확장자명까지 표시되고 있었다.
언젠가 한 번쯤 들었던 설명이었던 것 같기도 하고 아닌 것 같기도 한데, 절망적 시야로 보지 못했던 것이다.

어쨌든 결과적으로 이와 같이 아이템은 물론이고 아이템을 생성하고 나서 레시피까지 생성하는 기능을 구현했다.

3. 개선점 및 과제

  • 아이템 생성기와 레시피 생성기

프로토타입 기능은 완성됐다. 이제 테이블 데이터 생성기만 팀원이 준비해주면 그에 맞춰 데이터 읽어오는 방식을 변환해야 한다.

  • 여전히 구현하지 못한 기능들

정작 크래프팅과 분해 관련 기능은 오늘 공부는 했지만 하나도 구현하지 못했다. 아무래도 아직 상호작용 관련 작업을 다른 팀원이 하고 있는데 작동하지 않는 문제도 있고, 그것 때문에 기능 테스트 및 구현 등이 늦어지고 있는 상황이다.

해당 기능 구현이 어떻게 되어가고 있는지 정확히 알지 못하는 이상 한 번 여쭤보거나 임시 테스트용 상호작용 기능을 만들어서 테스트를 진행해야 할 것으로 보인다.

profile
게임 만들러 코딩 공부중

0개의 댓글