구글 시트 연동 아키텍쳐 분리

This commit is contained in:
NTG_DESKTOP 2025-08-12 02:16:42 +09:00
parent 2228efe47d
commit b103ea6af0
123 changed files with 3606 additions and 13569 deletions

Binary file not shown.

View File

@ -9,7 +9,6 @@ GameObject:
serializedVersion: 6 serializedVersion: 6
m_Component: m_Component:
- component: {fileID: 4476251547817182662} - component: {fileID: 4476251547817182662}
- component: {fileID: 6289760680591803305}
m_Layer: 0 m_Layer: 0
m_Name: GoogleSheetManager m_Name: GoogleSheetManager
m_TagString: Untagged m_TagString: Untagged
@ -32,34 +31,3 @@ Transform:
m_Children: [] m_Children: []
m_Father: {fileID: 0} m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &6289760680591803305
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6237816563216546680}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 2631101f894592945a1c50aed7048e66, type: 3}
m_Name:
m_EditorClassIdentifier:
_persistent: 1
_isAccessGoogleSheet: 1
_googleSheetUrl: https://script.google.com/macros/s/AKfycbw8TRSl_OuY2S-RX0yvOJi1SqNqoflG0R3pWxk9GC9u_wvGQeuABZc0VH7YJ5lMrAl4/exec
_namespace: DDD
_autoCreateSheets:
- ItemData
- RecipeData
- FoodData
- DrinkData
- IngredientData
- CookwareData
- EnvironmentData
_soSyncSheets:
- TasteData
_generateFolderPath: /_DDD/_Scripts/GenerateGoogleSheet/AutoCreated
_currentVersion: "0 - 2025-08-08 19:40:27 by \uB0A8\uD0DC\uAC74"
_restoreIndex: 0
_editorName:
_refreshTrigger: 1

BIN
Assets/_DDD/_ScriptAssets/So/GoogleSheetManager.asset (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: dd926e8709e44f347b04909219c78a6a
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

BIN
Assets/_DDD/_ScriptAssets/So/GoogleSheetSettingsSo.asset (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: bafacdb9dade16f4790e4400e9ce5879
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,43 @@
// <auto-generated>
using System;
using UnityEngine;
namespace DDD
{
public enum TasteType
{
None = 0,
Sweet = 1,
Warm = 2,
Vegetable = 3,
Bitter = 4,
Salty = 5,
Mild = 6,
Addictive = 7,
Sour = 8,
Savory = 9,
Meat = 10,
SuperHot = 11,
Fish = 12,
NoneDegree = 13,
Soda = 14,
LowDegree = 15,
WoodFlavor = 16,
MiddleDegree = 17,
FruitFlavor = 18,
}
[Serializable]
public class TasteData : IId
{
/// <summary>식별ID</summary>
[Tooltip("식별ID")]
[field: SerializeField]
public string Id { get; set; }
/// <summary>맛 종류</summary>
[Tooltip("맛 종류")]
public TasteType TasteType;
}
}

View File

@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: fa2ac723705c9624dadd61b238ad2819
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: 52d686087dc8f2b45afa9f4e757e66a1
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: be71db161987564429bd0e62591815eb
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: 582a711af5879c942b17b9936b8871e0
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -58,10 +58,419 @@
"": "FruitFlavor" "": "FruitFlavor"
} }
], ],
"Test": [
{
"Id:string": "식별번호",
"CustomerLimitCount": "손님 최대 수",
"Customers:string": "등장 손님",
"Test": 2352,
"Tests": ""
},
{
"Id:string": "customer_pool_001",
"CustomerLimitCount": 4,
"Customers:string": "customer_001",
"Test": 0,
"Tests": "테스트, 테스트2"
},
{
"Id:string": "customer_pool_002",
"CustomerLimitCount": 7,
"Customers:string": "customer_001 | customer_002",
"Test": 3,
"Tests": "1 , 2"
},
{
"Id:string": "customer_pool_003",
"CustomerLimitCount": 9,
"Customers:string": "customer_001 | customer_002 | customer_003",
"Test": 2,
"Tests": "5, 23,253"
},
{
"Id:string": "customer_pool_004",
"CustomerLimitCount": 3,
"Customers:string": "customer_001",
"Test": "",
"Tests": ""
},
{
"Id:string": "customer_pool_005",
"CustomerLimitCount": 3,
"Customers:string": "customer_001",
"Test": "",
"Tests": "435, 25"
},
{
"Id:string": "customer_pool_006",
"CustomerLimitCount": 3,
"Customers:string": "customer_001",
"Test": "",
"Tests": ""
},
{
"Id:string": "special_customer_pool_001",
"CustomerLimitCount": 1,
"Customers:string": "special_customer_001",
"Test": "",
"Tests": "111|234"
}
],
"Test_view": [
{
"Id:string": "식별번호",
"CustomerLimitCount": "손님 최대 수",
"Customers1": "등장 손님1",
"Customers2": "등장 손님2",
"Customers3": "등장 손님3",
"Customers4": "등장 손님4",
"Customers5": "등장 손님5",
"Customers6": "등장 손님6",
"Test": 2352,
"Tests1": "Tests1",
"Tests2": "Tests2",
"Tests3": "Tests3"
},
{
"Id:string": "customer_pool_001",
"CustomerLimitCount": 4,
"Customers1": "customer_001",
"Customers2": "",
"Customers3": "",
"Customers4": "",
"Customers5": "",
"Customers6": "",
"Test": 0,
"Tests1": "테스트",
"Tests2": "테스트2",
"Tests3": ""
},
{
"Id:string": "customer_pool_002",
"CustomerLimitCount": 7,
"Customers1": "customer_001",
"Customers2": "customer_002",
"Customers3": "",
"Customers4": "",
"Customers5": "",
"Customers6": "",
"Test": 3,
"Tests1": 1,
"Tests2": 2,
"Tests3": ""
},
{
"Id:string": "customer_pool_003",
"CustomerLimitCount": 9,
"Customers1": "customer_001",
"Customers2": "customer_002",
"Customers3": "customer_003",
"Customers4": "",
"Customers5": "",
"Customers6": "",
"Test": 2,
"Tests1": 5,
"Tests2": 23,
"Tests3": 253
},
{
"Id:string": "customer_pool_004",
"CustomerLimitCount": 3,
"Customers1": "customer_001",
"Customers2": "",
"Customers3": "",
"Customers4": "",
"Customers5": "",
"Customers6": "",
"Test": "",
"Tests1": "",
"Tests2": "",
"Tests3": ""
},
{
"Id:string": "customer_pool_005",
"CustomerLimitCount": 3,
"Customers1": "customer_001",
"Customers2": "",
"Customers3": "",
"Customers4": "",
"Customers5": "",
"Customers6": "",
"Test": "",
"Tests1": 435,
"Tests2": 25,
"Tests3": ""
},
{
"Id:string": "customer_pool_006",
"CustomerLimitCount": 3,
"Customers1": "customer_001",
"Customers2": "",
"Customers3": "",
"Customers4": "",
"Customers5": "",
"Customers6": "",
"Test": "",
"Tests1": "",
"Tests2": "",
"Tests3": ""
},
{
"Id:string": "special_customer_pool_001",
"CustomerLimitCount": 1,
"Customers1": "special_customer_001",
"Customers2": "",
"Customers3": "",
"Customers4": "",
"Customers5": "",
"Customers6": "",
"Test": "",
"Tests1": 111,
"Tests2": 234,
"Tests3": ""
}
],
"CustomerPoolData": [
{
"Id:string": "식별번호",
"CustomerLimitCount": "손님 최대 수",
"Customer1": "등장 손님1",
"Customer2": "등장 손님2",
"Customer3": "등장 손님3",
"Customer4": "등장 손님4",
"Customer5": "등장 손님5",
"Customer6": "등장 손님6"
},
{
"Id:string": "customer_pool_001",
"CustomerLimitCount": 4,
"Customer1": "customer_001",
"Customer2": "",
"Customer3": "",
"Customer4": "",
"Customer5": "",
"Customer6": ""
},
{
"Id:string": "customer_pool_002",
"CustomerLimitCount": 7,
"Customer1": "customer_001",
"Customer2": "customer_002",
"Customer3": "",
"Customer4": "",
"Customer5": "",
"Customer6": ""
},
{
"Id:string": "customer_pool_003",
"CustomerLimitCount": 9,
"Customer1": "customer_001",
"Customer2": "customer_002",
"Customer3": " customer_003",
"Customer4": "",
"Customer5": "",
"Customer6": ""
},
{
"Id:string": "customer_pool_004",
"CustomerLimitCount": 3,
"Customer1": "customer_001",
"Customer2": "",
"Customer3": "",
"Customer4": "",
"Customer5": "",
"Customer6": ""
},
{
"Id:string": "customer_pool_005",
"CustomerLimitCount": 3,
"Customer1": "customer_001",
"Customer2": "",
"Customer3": "",
"Customer4": "",
"Customer5": "",
"Customer6": ""
},
{
"Id:string": "customer_pool_006",
"CustomerLimitCount": 3,
"Customer1": "customer_001",
"Customer2": "",
"Customer3": "",
"Customer4": "",
"Customer5": "",
"Customer6": ""
},
{
"Id:string": "special_customer_pool_001",
"CustomerLimitCount": 1,
"Customer1": "special_customer_001",
"Customer2": "",
"Customer3": "",
"Customer4": "",
"Customer5": "",
"Customer6": ""
}
],
"LevelData": [
{
"Id:string": "식별번호",
"Area": "등장 지역",
"CustomerPool": "일반 손님 풀",
"SpecialCustomerPool": "스페셜 손님 풀",
"FixSpecialCustomerCount": "확정 스페셜 손님 수",
"AddSpecialCustomerCount": "추가 스페셜 손님 확률",
"CustomerRespawnTime": "손님 리스폰 시간",
"Exp": "손님 1명당 경험치",
"OrderTime": "주문 대기 인내심 카운트 시간",
"WaitTime": "주문 인내심 카운트 시간",
"EatingTime": "식사 소요시간"
},
{
"Id:string": 1,
"Area": "Area1",
"CustomerPool": "customer_pool_001",
"SpecialCustomerPool": "",
"FixSpecialCustomerCount": 0,
"AddSpecialCustomerCount": 0,
"CustomerRespawnTime": 6,
"Exp": 13,
"OrderTime": 30,
"WaitTime": 60,
"EatingTime": 6
},
{
"Id:string": 2,
"Area": "Area1",
"CustomerPool": "customer_pool_002",
"SpecialCustomerPool": "special_customer_pool_001",
"FixSpecialCustomerCount": 1,
"AddSpecialCustomerCount": 10,
"CustomerRespawnTime": 6,
"Exp": 13,
"OrderTime": 30,
"WaitTime": 60,
"EatingTime": 6
},
{
"Id:string": 3,
"Area": "Area1",
"CustomerPool": "customer_pool_003",
"SpecialCustomerPool": "special_customer_pool_001",
"FixSpecialCustomerCount": 2,
"AddSpecialCustomerCount": 30,
"CustomerRespawnTime": 6,
"Exp": 13,
"OrderTime": 30,
"WaitTime": 60,
"EatingTime": 6
},
{
"Id:string": 4,
"Area": "Area2",
"CustomerPool": "customer_pool_004",
"SpecialCustomerPool": "special_customer_pool_001",
"FixSpecialCustomerCount": 3,
"AddSpecialCustomerCount": 50,
"CustomerRespawnTime": 6,
"Exp": 13,
"OrderTime": 30,
"WaitTime": 60,
"EatingTime": 6
},
{
"Id:string": 5,
"Area": "Area2",
"CustomerPool": "customer_pool_005",
"SpecialCustomerPool": "special_customer_pool_001",
"FixSpecialCustomerCount": 3,
"AddSpecialCustomerCount": 50,
"CustomerRespawnTime": 6,
"Exp": 13,
"OrderTime": 30,
"WaitTime": 60,
"EatingTime": 6
},
{
"Id:string": 6,
"Area": "Area2",
"CustomerPool": "customer_pool_006",
"SpecialCustomerPool": "special_customer_pool_001",
"FixSpecialCustomerCount": 3,
"AddSpecialCustomerCount": 50,
"CustomerRespawnTime": 6,
"Exp": 13,
"OrderTime": 30,
"WaitTime": 60,
"EatingTime": 6
}
],
"CustomerData": [
{
"Id": "식별번호",
"CustomerType": "손님 타입",
"#Name": "이름",
"SpineSkinKey": "스파인 스킨 키 값",
"FavoriteTaste1": "선호 맛1",
"FavoriteTaste2": "선호 맛2",
"FavoriteTaste3": "선호 맛3",
"FavoriteTaste4": "선호 맛4",
"FavoriteTaste5": "선호 맛5",
"FavoriteTaste6": "선호 맛6"
},
{
"Id": "customer_001",
"CustomerType": "Normal",
"#Name": "유령1",
"SpineSkinKey": "",
"FavoriteTaste1": "",
"FavoriteTaste2": "",
"FavoriteTaste3": "",
"FavoriteTaste4": "",
"FavoriteTaste5": "",
"FavoriteTaste6": ""
},
{
"Id": "customer_002",
"CustomerType": "Normal",
"#Name": "유령2",
"SpineSkinKey": "",
"FavoriteTaste1": "",
"FavoriteTaste2": "",
"FavoriteTaste3": "",
"FavoriteTaste4": "",
"FavoriteTaste5": "",
"FavoriteTaste6": ""
},
{
"Id": "customer_003",
"CustomerType": "Normal",
"#Name": "유령3",
"SpineSkinKey": "",
"FavoriteTaste1": "",
"FavoriteTaste2": "",
"FavoriteTaste3": "",
"FavoriteTaste4": "",
"FavoriteTaste5": "",
"FavoriteTaste6": ""
},
{
"Id": "special_customer_001",
"CustomerType": "Special",
"#Name": "차이",
"SpineSkinKey": "",
"FavoriteTaste1": "",
"FavoriteTaste2": "",
"FavoriteTaste3": "",
"FavoriteTaste4": "",
"FavoriteTaste5": "",
"FavoriteTaste6": ""
}
],
"ItemData": [ "ItemData": [
{ {
"Id": "식별번호", "Id": "식별번호",
"ItemType:Enum": "아이템타입" "ItemType:Enum": "아이템타입1"
}, },
{ {
"Id": "item_food_001", "Id": "item_food_001",
@ -1125,7 +1534,7 @@
}, },
{ {
"Id:string": "item_ingredient_005", "Id:string": "item_ingredient_005",
"#Name": "치킨닭 고기" "#Name": "미정"
}, },
{ {
"Id:string": "item_ingredient_006", "Id:string": "item_ingredient_006",

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: d686bc895b261af419a5bdb06a243551 guid: e83477b2d57d3e247bf2b5ec240a731c
TextScriptImporter: TextScriptImporter:
externalObjects: {} externalObjects: {}
userData: userData:

View File

@ -58,10 +58,419 @@
"": "FruitFlavor" "": "FruitFlavor"
} }
], ],
"Test": [
{
"Id:string": "식별번호",
"CustomerLimitCount": "손님 최대 수",
"Customers:string": "등장 손님",
"Test": 2352,
"Tests": ""
},
{
"Id:string": "customer_pool_001",
"CustomerLimitCount": 4,
"Customers:string": "customer_001",
"Test": 0,
"Tests": "테스트, 테스트2"
},
{
"Id:string": "customer_pool_002",
"CustomerLimitCount": 7,
"Customers:string": "customer_001 | customer_002",
"Test": 3,
"Tests": "1 , 2"
},
{
"Id:string": "customer_pool_003",
"CustomerLimitCount": 9,
"Customers:string": "customer_001 | customer_002 | customer_003",
"Test": 2,
"Tests": "5, 23,253"
},
{
"Id:string": "customer_pool_004",
"CustomerLimitCount": 3,
"Customers:string": "customer_001",
"Test": "",
"Tests": ""
},
{
"Id:string": "customer_pool_005",
"CustomerLimitCount": 3,
"Customers:string": "customer_001",
"Test": "",
"Tests": "435, 25"
},
{
"Id:string": "customer_pool_006",
"CustomerLimitCount": 3,
"Customers:string": "customer_001",
"Test": "",
"Tests": ""
},
{
"Id:string": "special_customer_pool_001",
"CustomerLimitCount": 1,
"Customers:string": "special_customer_001",
"Test": "",
"Tests": "111|234"
}
],
"Test_view": [
{
"Id:string": "식별번호",
"CustomerLimitCount": "손님 최대 수",
"Customers1": "등장 손님1",
"Customers2": "등장 손님2",
"Customers3": "등장 손님3",
"Customers4": "등장 손님4",
"Customers5": "등장 손님5",
"Customers6": "등장 손님6",
"Test": 2352,
"Tests1": "Tests1",
"Tests2": "Tests2",
"Tests3": "Tests3"
},
{
"Id:string": "customer_pool_001",
"CustomerLimitCount": 4,
"Customers1": "customer_001",
"Customers2": "",
"Customers3": "",
"Customers4": "",
"Customers5": "",
"Customers6": "",
"Test": 0,
"Tests1": "테스트",
"Tests2": "테스트2",
"Tests3": ""
},
{
"Id:string": "customer_pool_002",
"CustomerLimitCount": 7,
"Customers1": "customer_001",
"Customers2": "customer_002",
"Customers3": "",
"Customers4": "",
"Customers5": "",
"Customers6": "",
"Test": 3,
"Tests1": 1,
"Tests2": 2,
"Tests3": ""
},
{
"Id:string": "customer_pool_003",
"CustomerLimitCount": 9,
"Customers1": "customer_001",
"Customers2": "customer_002",
"Customers3": "customer_003",
"Customers4": "",
"Customers5": "",
"Customers6": "",
"Test": 2,
"Tests1": 5,
"Tests2": 23,
"Tests3": 253
},
{
"Id:string": "customer_pool_004",
"CustomerLimitCount": 3,
"Customers1": "customer_001",
"Customers2": "",
"Customers3": "",
"Customers4": "",
"Customers5": "",
"Customers6": "",
"Test": "",
"Tests1": "",
"Tests2": "",
"Tests3": ""
},
{
"Id:string": "customer_pool_005",
"CustomerLimitCount": 3,
"Customers1": "customer_001",
"Customers2": "",
"Customers3": "",
"Customers4": "",
"Customers5": "",
"Customers6": "",
"Test": "",
"Tests1": 435,
"Tests2": 25,
"Tests3": ""
},
{
"Id:string": "customer_pool_006",
"CustomerLimitCount": 3,
"Customers1": "customer_001",
"Customers2": "",
"Customers3": "",
"Customers4": "",
"Customers5": "",
"Customers6": "",
"Test": "",
"Tests1": "",
"Tests2": "",
"Tests3": ""
},
{
"Id:string": "special_customer_pool_001",
"CustomerLimitCount": 1,
"Customers1": "special_customer_001",
"Customers2": "",
"Customers3": "",
"Customers4": "",
"Customers5": "",
"Customers6": "",
"Test": "",
"Tests1": 111,
"Tests2": 234,
"Tests3": ""
}
],
"CustomerPoolData": [
{
"Id:string": "식별번호",
"CustomerLimitCount": "손님 최대 수",
"Customer1": "등장 손님1",
"Customer2": "등장 손님2",
"Customer3": "등장 손님3",
"Customer4": "등장 손님4",
"Customer5": "등장 손님5",
"Customer6": "등장 손님6"
},
{
"Id:string": "customer_pool_001",
"CustomerLimitCount": 4,
"Customer1": "customer_001",
"Customer2": "",
"Customer3": "",
"Customer4": "",
"Customer5": "",
"Customer6": ""
},
{
"Id:string": "customer_pool_002",
"CustomerLimitCount": 7,
"Customer1": "customer_001",
"Customer2": "customer_002",
"Customer3": "",
"Customer4": "",
"Customer5": "",
"Customer6": ""
},
{
"Id:string": "customer_pool_003",
"CustomerLimitCount": 9,
"Customer1": "customer_001",
"Customer2": "customer_002",
"Customer3": " customer_003",
"Customer4": "",
"Customer5": "",
"Customer6": ""
},
{
"Id:string": "customer_pool_004",
"CustomerLimitCount": 3,
"Customer1": "customer_001",
"Customer2": "",
"Customer3": "",
"Customer4": "",
"Customer5": "",
"Customer6": ""
},
{
"Id:string": "customer_pool_005",
"CustomerLimitCount": 3,
"Customer1": "customer_001",
"Customer2": "",
"Customer3": "",
"Customer4": "",
"Customer5": "",
"Customer6": ""
},
{
"Id:string": "customer_pool_006",
"CustomerLimitCount": 3,
"Customer1": "customer_001",
"Customer2": "",
"Customer3": "",
"Customer4": "",
"Customer5": "",
"Customer6": ""
},
{
"Id:string": "special_customer_pool_001",
"CustomerLimitCount": 1,
"Customer1": "special_customer_001",
"Customer2": "",
"Customer3": "",
"Customer4": "",
"Customer5": "",
"Customer6": ""
}
],
"LevelData": [
{
"Id:string": "식별번호",
"Area": "등장 지역",
"CustomerPool": "일반 손님 풀",
"SpecialCustomerPool": "스페셜 손님 풀",
"FixSpecialCustomerCount": "확정 스페셜 손님 수",
"AddSpecialCustomerCount": "추가 스페셜 손님 확률",
"CustomerRespawnTime": "손님 리스폰 시간",
"Exp": "손님 1명당 경험치",
"OrderTime": "주문 대기 인내심 카운트 시간",
"WaitTime": "주문 인내심 카운트 시간",
"EatingTime": "식사 소요시간"
},
{
"Id:string": 1,
"Area": "Area1",
"CustomerPool": "customer_pool_001",
"SpecialCustomerPool": "",
"FixSpecialCustomerCount": 0,
"AddSpecialCustomerCount": 0,
"CustomerRespawnTime": 6,
"Exp": 13,
"OrderTime": 30,
"WaitTime": 60,
"EatingTime": 6
},
{
"Id:string": 2,
"Area": "Area1",
"CustomerPool": "customer_pool_002",
"SpecialCustomerPool": "special_customer_pool_001",
"FixSpecialCustomerCount": 1,
"AddSpecialCustomerCount": 10,
"CustomerRespawnTime": 6,
"Exp": 13,
"OrderTime": 30,
"WaitTime": 60,
"EatingTime": 6
},
{
"Id:string": 3,
"Area": "Area1",
"CustomerPool": "customer_pool_003",
"SpecialCustomerPool": "special_customer_pool_001",
"FixSpecialCustomerCount": 2,
"AddSpecialCustomerCount": 30,
"CustomerRespawnTime": 6,
"Exp": 13,
"OrderTime": 30,
"WaitTime": 60,
"EatingTime": 6
},
{
"Id:string": 4,
"Area": "Area2",
"CustomerPool": "customer_pool_004",
"SpecialCustomerPool": "special_customer_pool_001",
"FixSpecialCustomerCount": 3,
"AddSpecialCustomerCount": 50,
"CustomerRespawnTime": 6,
"Exp": 13,
"OrderTime": 30,
"WaitTime": 60,
"EatingTime": 6
},
{
"Id:string": 5,
"Area": "Area2",
"CustomerPool": "customer_pool_005",
"SpecialCustomerPool": "special_customer_pool_001",
"FixSpecialCustomerCount": 3,
"AddSpecialCustomerCount": 50,
"CustomerRespawnTime": 6,
"Exp": 13,
"OrderTime": 30,
"WaitTime": 60,
"EatingTime": 6
},
{
"Id:string": 6,
"Area": "Area2",
"CustomerPool": "customer_pool_006",
"SpecialCustomerPool": "special_customer_pool_001",
"FixSpecialCustomerCount": 3,
"AddSpecialCustomerCount": 50,
"CustomerRespawnTime": 6,
"Exp": 13,
"OrderTime": 30,
"WaitTime": 60,
"EatingTime": 6
}
],
"CustomerData": [
{
"Id": "식별번호",
"CustomerType": "손님 타입",
"#Name": "이름",
"SpineSkinKey": "스파인 스킨 키 값",
"FavoriteTaste1": "선호 맛1",
"FavoriteTaste2": "선호 맛2",
"FavoriteTaste3": "선호 맛3",
"FavoriteTaste4": "선호 맛4",
"FavoriteTaste5": "선호 맛5",
"FavoriteTaste6": "선호 맛6"
},
{
"Id": "customer_001",
"CustomerType": "Normal",
"#Name": "유령1",
"SpineSkinKey": "",
"FavoriteTaste1": "",
"FavoriteTaste2": "",
"FavoriteTaste3": "",
"FavoriteTaste4": "",
"FavoriteTaste5": "",
"FavoriteTaste6": ""
},
{
"Id": "customer_002",
"CustomerType": "Normal",
"#Name": "유령2",
"SpineSkinKey": "",
"FavoriteTaste1": "",
"FavoriteTaste2": "",
"FavoriteTaste3": "",
"FavoriteTaste4": "",
"FavoriteTaste5": "",
"FavoriteTaste6": ""
},
{
"Id": "customer_003",
"CustomerType": "Normal",
"#Name": "유령3",
"SpineSkinKey": "",
"FavoriteTaste1": "",
"FavoriteTaste2": "",
"FavoriteTaste3": "",
"FavoriteTaste4": "",
"FavoriteTaste5": "",
"FavoriteTaste6": ""
},
{
"Id": "special_customer_001",
"CustomerType": "Special",
"#Name": "차이",
"SpineSkinKey": "",
"FavoriteTaste1": "",
"FavoriteTaste2": "",
"FavoriteTaste3": "",
"FavoriteTaste4": "",
"FavoriteTaste5": "",
"FavoriteTaste6": ""
}
],
"ItemData": [ "ItemData": [
{ {
"Id": "식별번호", "Id": "식별번호",
"ItemType:Enum": "아이템타입" "ItemType:Enum": "아이템타입1"
}, },
{ {
"Id": "item_food_001", "Id": "item_food_001",
@ -279,6 +688,14 @@
"Id": "item_environment_cookware_006", "Id": "item_environment_cookware_006",
"ItemType:Enum": "Environment" "ItemType:Enum": "Environment"
}, },
{
"Id": "item_environment_cookware_007",
"ItemType:Enum": "Environment"
},
{
"Id": "item_environment_cookware_008",
"ItemType:Enum": "Environment"
},
{ {
"Id": "item_drink_001", "Id": "item_drink_001",
"ItemType:Enum": "Drink" "ItemType:Enum": "Drink"
@ -548,7 +965,7 @@
{ {
"Id:string": "식별ID", "Id:string": "식별ID",
"#Name": "이름", "#Name": "이름",
"CookwareKey:string": "요리도구 타입", "CookwareKey:string": "요리도구 키 값",
"CookTime:int": "요리시간", "CookTime:int": "요리시간",
"Price:int": "요리가격", "Price:int": "요리가격",
"IngredientKey1:string": "재료1", "IngredientKey1:string": "재료1",
@ -991,7 +1408,7 @@
{ {
"Id:string": "식별ID", "Id:string": "식별ID",
"#Name": "이름", "#Name": "이름",
"CookwareType:Enum": "요리도구", "CookwareKey:string": "요리도구 키 값",
"CookTime:int": "요리시간", "CookTime:int": "요리시간",
"Price:int": "요리가격", "Price:int": "요리가격",
"IngredientKey1:string": "재료1", "IngredientKey1:string": "재료1",
@ -1012,7 +1429,7 @@
{ {
"Id:string": "item_drink_001", "Id:string": "item_drink_001",
"#Name": "토마토 주스", "#Name": "토마토 주스",
"CookwareType:Enum": "JuiceMachine", "CookwareKey:string": "item_environment_cookware_007",
"CookTime:int": 1, "CookTime:int": 1,
"Price:int": 10, "Price:int": 10,
"IngredientKey1:string": "item_ingredient_006", "IngredientKey1:string": "item_ingredient_006",
@ -1033,7 +1450,7 @@
{ {
"Id:string": "item_drink_002", "Id:string": "item_drink_002",
"#Name": "오렌지 주스", "#Name": "오렌지 주스",
"CookwareType:Enum": "JuiceMachine", "CookwareKey:string": "item_environment_cookware_007",
"CookTime:int": 1, "CookTime:int": 1,
"Price:int": 10, "Price:int": 10,
"IngredientKey1:string": "item_ingredient_023", "IngredientKey1:string": "item_ingredient_023",
@ -1054,7 +1471,7 @@
{ {
"Id:string": "item_drink_003", "Id:string": "item_drink_003",
"#Name": "여신의눈물", "#Name": "여신의눈물",
"CookwareType:Enum": "Barrel", "CookwareKey:string": "item_environment_cookware_008",
"CookTime:int": 3, "CookTime:int": 3,
"Price:int": 10, "Price:int": 10,
"IngredientKey1:string": "item_ingredient_008", "IngredientKey1:string": "item_ingredient_008",
@ -1075,7 +1492,7 @@
{ {
"Id:string": "item_drink_004", "Id:string": "item_drink_004",
"#Name": "라벨블루", "#Name": "라벨블루",
"CookwareType:Enum": "Barrel", "CookwareKey:string": "item_environment_cookware_008",
"CookTime:int": 3, "CookTime:int": 3,
"Price:int": 10, "Price:int": 10,
"IngredientKey1:string": "item_ingredient_002", "IngredientKey1:string": "item_ingredient_002",
@ -1117,7 +1534,7 @@
}, },
{ {
"Id:string": "item_ingredient_005", "Id:string": "item_ingredient_005",
"#Name": "치킨닭 고기" "#Name": "미정"
}, },
{ {
"Id:string": "item_ingredient_006", "Id:string": "item_ingredient_006",
@ -1227,6 +1644,16 @@
"Id:string": "item_environment_cookware_006", "Id:string": "item_environment_cookware_006",
"#Name": "", "#Name": "",
"CookwareType:Enum": "MagicOven" "CookwareType:Enum": "MagicOven"
},
{
"Id:string": "item_environment_cookware_007",
"#Name": "",
"CookwareType:Enum": "JuiceMachine"
},
{
"Id:string": "item_environment_cookware_008",
"#Name": "",
"CookwareType:Enum": "Barrel"
} }
], ],
"TasteData": [ "TasteData": [

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 56d87f6c872900b41beecc99b88b67af guid: 0a7e264dbeab2004d972d028db1ed457
TextScriptImporter: TextScriptImporter:
externalObjects: {} externalObjects: {}
userData: userData:

View File

@ -58,10 +58,419 @@
"": "FruitFlavor" "": "FruitFlavor"
} }
], ],
"Test": [
{
"Id:string": "식별번호",
"CustomerLimitCount": "손님 최대 수",
"Customers:string": "등장 손님",
"Test": 2352,
"Tests": ""
},
{
"Id:string": "customer_pool_001",
"CustomerLimitCount": 4,
"Customers:string": "customer_001",
"Test": 0,
"Tests": "테스트, 테스트2"
},
{
"Id:string": "customer_pool_002",
"CustomerLimitCount": 7,
"Customers:string": "customer_001 | customer_002",
"Test": 3,
"Tests": "1 , 2"
},
{
"Id:string": "customer_pool_003",
"CustomerLimitCount": 9,
"Customers:string": "customer_001 | customer_002 | customer_003",
"Test": 2,
"Tests": "5, 23,253"
},
{
"Id:string": "customer_pool_004",
"CustomerLimitCount": 3,
"Customers:string": "customer_001",
"Test": "",
"Tests": ""
},
{
"Id:string": "customer_pool_005",
"CustomerLimitCount": 3,
"Customers:string": "customer_001",
"Test": "",
"Tests": "435, 25"
},
{
"Id:string": "customer_pool_006",
"CustomerLimitCount": 3,
"Customers:string": "customer_001",
"Test": "",
"Tests": ""
},
{
"Id:string": "special_customer_pool_001",
"CustomerLimitCount": 1,
"Customers:string": "special_customer_001",
"Test": "",
"Tests": "111|234"
}
],
"Test_view": [
{
"Id:string": "식별번호",
"CustomerLimitCount": "손님 최대 수",
"Customers1": "등장 손님1",
"Customers2": "등장 손님2",
"Customers3": "등장 손님3",
"Customers4": "등장 손님4",
"Customers5": "등장 손님5",
"Customers6": "등장 손님6",
"Test": 2352,
"Tests1": "Tests1",
"Tests2": "Tests2",
"Tests3": "Tests3"
},
{
"Id:string": "customer_pool_001",
"CustomerLimitCount": 4,
"Customers1": "customer_001",
"Customers2": "",
"Customers3": "",
"Customers4": "",
"Customers5": "",
"Customers6": "",
"Test": 0,
"Tests1": "테스트",
"Tests2": "테스트2",
"Tests3": ""
},
{
"Id:string": "customer_pool_002",
"CustomerLimitCount": 7,
"Customers1": "customer_001",
"Customers2": "customer_002",
"Customers3": "",
"Customers4": "",
"Customers5": "",
"Customers6": "",
"Test": 3,
"Tests1": 1,
"Tests2": 2,
"Tests3": ""
},
{
"Id:string": "customer_pool_003",
"CustomerLimitCount": 9,
"Customers1": "customer_001",
"Customers2": "customer_002",
"Customers3": "customer_003",
"Customers4": "",
"Customers5": "",
"Customers6": "",
"Test": 2,
"Tests1": 5,
"Tests2": 23,
"Tests3": 253
},
{
"Id:string": "customer_pool_004",
"CustomerLimitCount": 3,
"Customers1": "customer_001",
"Customers2": "",
"Customers3": "",
"Customers4": "",
"Customers5": "",
"Customers6": "",
"Test": "",
"Tests1": "",
"Tests2": "",
"Tests3": ""
},
{
"Id:string": "customer_pool_005",
"CustomerLimitCount": 3,
"Customers1": "customer_001",
"Customers2": "",
"Customers3": "",
"Customers4": "",
"Customers5": "",
"Customers6": "",
"Test": "",
"Tests1": 435,
"Tests2": 25,
"Tests3": ""
},
{
"Id:string": "customer_pool_006",
"CustomerLimitCount": 3,
"Customers1": "customer_001",
"Customers2": "",
"Customers3": "",
"Customers4": "",
"Customers5": "",
"Customers6": "",
"Test": "",
"Tests1": "",
"Tests2": "",
"Tests3": ""
},
{
"Id:string": "special_customer_pool_001",
"CustomerLimitCount": 1,
"Customers1": "special_customer_001",
"Customers2": "",
"Customers3": "",
"Customers4": "",
"Customers5": "",
"Customers6": "",
"Test": "",
"Tests1": 111,
"Tests2": 234,
"Tests3": ""
}
],
"CustomerPoolData": [
{
"Id:string": "식별번호",
"CustomerLimitCount": "손님 최대 수",
"Customer1": "등장 손님1",
"Customer2": "등장 손님2",
"Customer3": "등장 손님3",
"Customer4": "등장 손님4",
"Customer5": "등장 손님5",
"Customer6": "등장 손님6"
},
{
"Id:string": "customer_pool_001",
"CustomerLimitCount": 4,
"Customer1": "customer_001",
"Customer2": "",
"Customer3": "",
"Customer4": "",
"Customer5": "",
"Customer6": ""
},
{
"Id:string": "customer_pool_002",
"CustomerLimitCount": 7,
"Customer1": "customer_001",
"Customer2": "customer_002",
"Customer3": "",
"Customer4": "",
"Customer5": "",
"Customer6": ""
},
{
"Id:string": "customer_pool_003",
"CustomerLimitCount": 9,
"Customer1": "customer_001",
"Customer2": "customer_002",
"Customer3": " customer_003",
"Customer4": "",
"Customer5": "",
"Customer6": ""
},
{
"Id:string": "customer_pool_004",
"CustomerLimitCount": 3,
"Customer1": "customer_001",
"Customer2": "",
"Customer3": "",
"Customer4": "",
"Customer5": "",
"Customer6": ""
},
{
"Id:string": "customer_pool_005",
"CustomerLimitCount": 3,
"Customer1": "customer_001",
"Customer2": "",
"Customer3": "",
"Customer4": "",
"Customer5": "",
"Customer6": ""
},
{
"Id:string": "customer_pool_006",
"CustomerLimitCount": 3,
"Customer1": "customer_001",
"Customer2": "",
"Customer3": "",
"Customer4": "",
"Customer5": "",
"Customer6": ""
},
{
"Id:string": "special_customer_pool_001",
"CustomerLimitCount": 1,
"Customer1": "special_customer_001",
"Customer2": "",
"Customer3": "",
"Customer4": "",
"Customer5": "",
"Customer6": ""
}
],
"LevelData": [
{
"Id:string": "식별번호",
"Area": "등장 지역",
"CustomerPool": "일반 손님 풀",
"SpecialCustomerPool": "스페셜 손님 풀",
"FixSpecialCustomerCount": "확정 스페셜 손님 수",
"AddSpecialCustomerCount": "추가 스페셜 손님 확률",
"CustomerRespawnTime": "손님 리스폰 시간",
"Exp": "손님 1명당 경험치",
"OrderTime": "주문 대기 인내심 카운트 시간",
"WaitTime": "주문 인내심 카운트 시간",
"EatingTime": "식사 소요시간"
},
{
"Id:string": 1,
"Area": "Area1",
"CustomerPool": "customer_pool_001",
"SpecialCustomerPool": "",
"FixSpecialCustomerCount": 0,
"AddSpecialCustomerCount": 0,
"CustomerRespawnTime": 6,
"Exp": 13,
"OrderTime": 30,
"WaitTime": 60,
"EatingTime": 6
},
{
"Id:string": 2,
"Area": "Area1",
"CustomerPool": "customer_pool_002",
"SpecialCustomerPool": "special_customer_pool_001",
"FixSpecialCustomerCount": 1,
"AddSpecialCustomerCount": 10,
"CustomerRespawnTime": 6,
"Exp": 13,
"OrderTime": 30,
"WaitTime": 60,
"EatingTime": 6
},
{
"Id:string": 3,
"Area": "Area1",
"CustomerPool": "customer_pool_003",
"SpecialCustomerPool": "special_customer_pool_001",
"FixSpecialCustomerCount": 2,
"AddSpecialCustomerCount": 30,
"CustomerRespawnTime": 6,
"Exp": 13,
"OrderTime": 30,
"WaitTime": 60,
"EatingTime": 6
},
{
"Id:string": 4,
"Area": "Area2",
"CustomerPool": "customer_pool_004",
"SpecialCustomerPool": "special_customer_pool_001",
"FixSpecialCustomerCount": 3,
"AddSpecialCustomerCount": 50,
"CustomerRespawnTime": 6,
"Exp": 13,
"OrderTime": 30,
"WaitTime": 60,
"EatingTime": 6
},
{
"Id:string": 5,
"Area": "Area2",
"CustomerPool": "customer_pool_005",
"SpecialCustomerPool": "special_customer_pool_001",
"FixSpecialCustomerCount": 3,
"AddSpecialCustomerCount": 50,
"CustomerRespawnTime": 6,
"Exp": 13,
"OrderTime": 30,
"WaitTime": 60,
"EatingTime": 6
},
{
"Id:string": 6,
"Area": "Area2",
"CustomerPool": "customer_pool_006",
"SpecialCustomerPool": "special_customer_pool_001",
"FixSpecialCustomerCount": 3,
"AddSpecialCustomerCount": 50,
"CustomerRespawnTime": 6,
"Exp": 13,
"OrderTime": 30,
"WaitTime": 60,
"EatingTime": 6
}
],
"CustomerData": [
{
"Id": "식별번호",
"CustomerType": "손님 타입",
"#Name": "이름",
"SpineSkinKey": "스파인 스킨 키 값",
"FavoriteTaste1": "선호 맛1",
"FavoriteTaste2": "선호 맛2",
"FavoriteTaste3": "선호 맛3",
"FavoriteTaste4": "선호 맛4",
"FavoriteTaste5": "선호 맛5",
"FavoriteTaste6": "선호 맛6"
},
{
"Id": "customer_001",
"CustomerType": "Normal",
"#Name": "유령1",
"SpineSkinKey": "",
"FavoriteTaste1": "",
"FavoriteTaste2": "",
"FavoriteTaste3": "",
"FavoriteTaste4": "",
"FavoriteTaste5": "",
"FavoriteTaste6": ""
},
{
"Id": "customer_002",
"CustomerType": "Normal",
"#Name": "유령2",
"SpineSkinKey": "",
"FavoriteTaste1": "",
"FavoriteTaste2": "",
"FavoriteTaste3": "",
"FavoriteTaste4": "",
"FavoriteTaste5": "",
"FavoriteTaste6": ""
},
{
"Id": "customer_003",
"CustomerType": "Normal",
"#Name": "유령3",
"SpineSkinKey": "",
"FavoriteTaste1": "",
"FavoriteTaste2": "",
"FavoriteTaste3": "",
"FavoriteTaste4": "",
"FavoriteTaste5": "",
"FavoriteTaste6": ""
},
{
"Id": "special_customer_001",
"CustomerType": "Special",
"#Name": "차이",
"SpineSkinKey": "",
"FavoriteTaste1": "",
"FavoriteTaste2": "",
"FavoriteTaste3": "",
"FavoriteTaste4": "",
"FavoriteTaste5": "",
"FavoriteTaste6": ""
}
],
"ItemData": [ "ItemData": [
{ {
"Id": "식별번호", "Id": "식별번호",
"ItemType:Enum": "아이템타입" "ItemType:Enum": "아이템타입1"
}, },
{ {
"Id": "item_food_001", "Id": "item_food_001",
@ -255,6 +664,38 @@
"Id": "item_environment_005", "Id": "item_environment_005",
"ItemType:Enum": "Environment" "ItemType:Enum": "Environment"
}, },
{
"Id": "item_environment_cookware_001",
"ItemType:Enum": "Environment"
},
{
"Id": "item_environment_cookware_002",
"ItemType:Enum": "Environment"
},
{
"Id": "item_environment_cookware_003",
"ItemType:Enum": "Environment"
},
{
"Id": "item_environment_cookware_004",
"ItemType:Enum": "Environment"
},
{
"Id": "item_environment_cookware_005",
"ItemType:Enum": "Environment"
},
{
"Id": "item_environment_cookware_006",
"ItemType:Enum": "Environment"
},
{
"Id": "item_environment_cookware_007",
"ItemType:Enum": "Environment"
},
{
"Id": "item_environment_cookware_008",
"ItemType:Enum": "Environment"
},
{ {
"Id": "item_drink_001", "Id": "item_drink_001",
"ItemType:Enum": "Drink" "ItemType:Enum": "Drink"
@ -524,7 +965,7 @@
{ {
"Id:string": "식별ID", "Id:string": "식별ID",
"#Name": "이름", "#Name": "이름",
"CookwareType:Enum": "요리도구 타입", "CookwareKey:string": "요리도구 키 값",
"CookTime:int": "요리시간", "CookTime:int": "요리시간",
"Price:int": "요리가격", "Price:int": "요리가격",
"IngredientKey1:string": "재료1", "IngredientKey1:string": "재료1",
@ -545,7 +986,7 @@
{ {
"Id:string": "item_food_001", "Id:string": "item_food_001",
"#Name": "블루 스튜", "#Name": "블루 스튜",
"CookwareType:Enum": "Pot", "CookwareKey:string": "item_environment_cookware_001",
"CookTime:int": 7, "CookTime:int": 7,
"Price:int": 10, "Price:int": 10,
"IngredientKey1:string": "item_ingredient_001", "IngredientKey1:string": "item_ingredient_001",
@ -566,7 +1007,7 @@
{ {
"Id:string": "item_food_002", "Id:string": "item_food_002",
"#Name": "햇빛 스튜", "#Name": "햇빛 스튜",
"CookwareType:Enum": "Pot", "CookwareKey:string": "item_environment_cookware_001",
"CookTime:int": 7, "CookTime:int": 7,
"Price:int": 20, "Price:int": 20,
"IngredientKey1:string": "item_ingredient_002", "IngredientKey1:string": "item_ingredient_002",
@ -587,7 +1028,7 @@
{ {
"Id:string": "item_food_003", "Id:string": "item_food_003",
"#Name": "심해기억 스튜", "#Name": "심해기억 스튜",
"CookwareType:Enum": "Pot", "CookwareKey:string": "item_environment_cookware_001",
"CookTime:int": 7, "CookTime:int": 7,
"Price:int": 15, "Price:int": 15,
"IngredientKey1:string": "item_ingredient_002", "IngredientKey1:string": "item_ingredient_002",
@ -608,7 +1049,7 @@
{ {
"Id:string": "item_food_004", "Id:string": "item_food_004",
"#Name": "치킨 굴라쉬", "#Name": "치킨 굴라쉬",
"CookwareType:Enum": "Pot", "CookwareKey:string": "item_environment_cookware_001",
"CookTime:int": 10, "CookTime:int": 10,
"Price:int": 30, "Price:int": 30,
"IngredientKey1:string": "item_ingredient_005", "IngredientKey1:string": "item_ingredient_005",
@ -629,7 +1070,7 @@
{ {
"Id:string": "item_food_005", "Id:string": "item_food_005",
"#Name": "꿈해초 크로켓", "#Name": "꿈해초 크로켓",
"CookwareType:Enum": "Fryer", "CookwareKey:string": "item_environment_cookware_002",
"CookTime:int": 5, "CookTime:int": 5,
"Price:int": 20, "Price:int": 20,
"IngredientKey1:string": "item_ingredient_007", "IngredientKey1:string": "item_ingredient_007",
@ -650,7 +1091,7 @@
{ {
"Id:string": "item_food_006", "Id:string": "item_food_006",
"#Name": "선라이즈 당근카츠", "#Name": "선라이즈 당근카츠",
"CookwareType:Enum": "Fryer", "CookwareKey:string": "item_environment_cookware_002",
"CookTime:int": 5, "CookTime:int": 5,
"Price:int": 30, "Price:int": 30,
"IngredientKey1:string": "item_ingredient_003", "IngredientKey1:string": "item_ingredient_003",
@ -671,7 +1112,7 @@
{ {
"Id:string": "item_food_007", "Id:string": "item_food_007",
"#Name": "미정", "#Name": "미정",
"CookwareType:Enum": "Fryer", "CookwareKey:string": "item_environment_cookware_002",
"CookTime:int": 5, "CookTime:int": 5,
"Price:int": 30, "Price:int": 30,
"IngredientKey1:string": "item_ingredient_011", "IngredientKey1:string": "item_ingredient_011",
@ -692,7 +1133,7 @@
{ {
"Id:string": "item_food_008", "Id:string": "item_food_008",
"#Name": "비늘치킨덕", "#Name": "비늘치킨덕",
"CookwareType:Enum": "FirePit", "CookwareKey:string": "item_environment_cookware_003",
"CookTime:int": 6, "CookTime:int": 6,
"Price:int": 30, "Price:int": 30,
"IngredientKey1:string": "item_ingredient_012", "IngredientKey1:string": "item_ingredient_012",
@ -713,7 +1154,7 @@
{ {
"Id:string": "item_food_009", "Id:string": "item_food_009",
"#Name": "화룡장어 구이", "#Name": "화룡장어 구이",
"CookwareType:Enum": "FirePit", "CookwareKey:string": "item_environment_cookware_003",
"CookTime:int": 6, "CookTime:int": 6,
"Price:int": 15, "Price:int": 15,
"IngredientKey1:string": "item_ingredient_014", "IngredientKey1:string": "item_ingredient_014",
@ -734,7 +1175,7 @@
{ {
"Id:string": "item_food_010", "Id:string": "item_food_010",
"#Name": "미정", "#Name": "미정",
"CookwareType:Enum": "FirePit", "CookwareKey:string": "item_environment_cookware_003",
"CookTime:int": 6, "CookTime:int": 6,
"Price:int": 30, "Price:int": 30,
"IngredientKey1:string": "item_ingredient_016", "IngredientKey1:string": "item_ingredient_016",
@ -755,7 +1196,7 @@
{ {
"Id:string": "item_food_011", "Id:string": "item_food_011",
"#Name": "바다의선물", "#Name": "바다의선물",
"CookwareType:Enum": "CuttingBoard", "CookwareKey:string": "item_environment_cookware_004",
"CookTime:int": 5, "CookTime:int": 5,
"Price:int": 10, "Price:int": 10,
"IngredientKey1:string": "item_ingredient_002", "IngredientKey1:string": "item_ingredient_002",
@ -776,7 +1217,7 @@
{ {
"Id:string": "item_food_012", "Id:string": "item_food_012",
"#Name": "유령새우회", "#Name": "유령새우회",
"CookwareType:Enum": "CuttingBoard", "CookwareKey:string": "item_environment_cookware_004",
"CookTime:int": 5, "CookTime:int": 5,
"Price:int": 25, "Price:int": 25,
"IngredientKey1:string": "item_ingredient_017", "IngredientKey1:string": "item_ingredient_017",
@ -797,7 +1238,7 @@
{ {
"Id:string": "item_food_013", "Id:string": "item_food_013",
"#Name": "비늘초무침", "#Name": "비늘초무침",
"CookwareType:Enum": "CuttingBoard", "CookwareKey:string": "item_environment_cookware_004",
"CookTime:int": 5, "CookTime:int": 5,
"Price:int": 45, "Price:int": 45,
"IngredientKey1:string": "item_ingredient_018", "IngredientKey1:string": "item_ingredient_018",
@ -818,7 +1259,7 @@
{ {
"Id:string": "item_food_014", "Id:string": "item_food_014",
"#Name": "쫀징어 냉채", "#Name": "쫀징어 냉채",
"CookwareType:Enum": "CuttingBoard", "CookwareKey:string": "item_environment_cookware_004",
"CookTime:int": 5, "CookTime:int": 5,
"Price:int": 15, "Price:int": 15,
"IngredientKey1:string": "item_ingredient_011", "IngredientKey1:string": "item_ingredient_011",
@ -839,7 +1280,7 @@
{ {
"Id:string": "item_food_015", "Id:string": "item_food_015",
"#Name": "쫀징어링", "#Name": "쫀징어링",
"CookwareType:Enum": "Griddle", "CookwareKey:string": "item_environment_cookware_005",
"CookTime:int": 8, "CookTime:int": 8,
"Price:int": 25, "Price:int": 25,
"IngredientKey1:string": "item_ingredient_011", "IngredientKey1:string": "item_ingredient_011",
@ -860,7 +1301,7 @@
{ {
"Id:string": "item_food_016", "Id:string": "item_food_016",
"#Name": "꿈해파리 볶음", "#Name": "꿈해파리 볶음",
"CookwareType:Enum": "Griddle", "CookwareKey:string": "item_environment_cookware_005",
"CookTime:int": 6, "CookTime:int": 6,
"Price:int": 15, "Price:int": 15,
"IngredientKey1:string": "item_ingredient_019", "IngredientKey1:string": "item_ingredient_019",
@ -881,7 +1322,7 @@
{ {
"Id:string": "item_food_017", "Id:string": "item_food_017",
"#Name": "페퍼포칼립스", "#Name": "페퍼포칼립스",
"CookwareType:Enum": "Griddle", "CookwareKey:string": "item_environment_cookware_005",
"CookTime:int": 6, "CookTime:int": 6,
"Price:int": 25, "Price:int": 25,
"IngredientKey1:string": "item_ingredient_012", "IngredientKey1:string": "item_ingredient_012",
@ -902,7 +1343,7 @@
{ {
"Id:string": "item_food_018", "Id:string": "item_food_018",
"#Name": "미정", "#Name": "미정",
"CookwareType:Enum": "MagicOven", "CookwareKey:string": "item_environment_cookware_006",
"CookTime:int": 0, "CookTime:int": 0,
"Price:int": 0, "Price:int": 0,
"IngredientKey1:string": "", "IngredientKey1:string": "",
@ -923,7 +1364,7 @@
{ {
"Id:string": "item_food_019", "Id:string": "item_food_019",
"#Name": "미정", "#Name": "미정",
"CookwareType:Enum": "MagicOven", "CookwareKey:string": "item_environment_cookware_006",
"CookTime:int": 0, "CookTime:int": 0,
"Price:int": 0, "Price:int": 0,
"IngredientKey1:string": "", "IngredientKey1:string": "",
@ -944,7 +1385,7 @@
{ {
"Id:string": "item_food_020", "Id:string": "item_food_020",
"#Name": "미정", "#Name": "미정",
"CookwareType:Enum": "MagicOven", "CookwareKey:string": "item_environment_cookware_006",
"CookTime:int": 0, "CookTime:int": 0,
"Price:int": 0, "Price:int": 0,
"IngredientKey1:string": "", "IngredientKey1:string": "",
@ -967,7 +1408,7 @@
{ {
"Id:string": "식별ID", "Id:string": "식별ID",
"#Name": "이름", "#Name": "이름",
"CookwareType:Enum": "요리도구", "CookwareKey:string": "요리도구 키 값",
"CookTime:int": "요리시간", "CookTime:int": "요리시간",
"Price:int": "요리가격", "Price:int": "요리가격",
"IngredientKey1:string": "재료1", "IngredientKey1:string": "재료1",
@ -988,7 +1429,7 @@
{ {
"Id:string": "item_drink_001", "Id:string": "item_drink_001",
"#Name": "토마토 주스", "#Name": "토마토 주스",
"CookwareType:Enum": "JuiceMachine", "CookwareKey:string": "item_environment_cookware_007",
"CookTime:int": 1, "CookTime:int": 1,
"Price:int": 10, "Price:int": 10,
"IngredientKey1:string": "item_ingredient_006", "IngredientKey1:string": "item_ingredient_006",
@ -1009,7 +1450,7 @@
{ {
"Id:string": "item_drink_002", "Id:string": "item_drink_002",
"#Name": "오렌지 주스", "#Name": "오렌지 주스",
"CookwareType:Enum": "JuiceMachine", "CookwareKey:string": "item_environment_cookware_007",
"CookTime:int": 1, "CookTime:int": 1,
"Price:int": 10, "Price:int": 10,
"IngredientKey1:string": "item_ingredient_023", "IngredientKey1:string": "item_ingredient_023",
@ -1030,7 +1471,7 @@
{ {
"Id:string": "item_drink_003", "Id:string": "item_drink_003",
"#Name": "여신의눈물", "#Name": "여신의눈물",
"CookwareType:Enum": "Barrel", "CookwareKey:string": "item_environment_cookware_008",
"CookTime:int": 3, "CookTime:int": 3,
"Price:int": 10, "Price:int": 10,
"IngredientKey1:string": "item_ingredient_008", "IngredientKey1:string": "item_ingredient_008",
@ -1051,7 +1492,7 @@
{ {
"Id:string": "item_drink_004", "Id:string": "item_drink_004",
"#Name": "라벨블루", "#Name": "라벨블루",
"CookwareType:Enum": "Barrel", "CookwareKey:string": "item_environment_cookware_008",
"CookTime:int": 3, "CookTime:int": 3,
"Price:int": 10, "Price:int": 10,
"IngredientKey1:string": "item_ingredient_002", "IngredientKey1:string": "item_ingredient_002",
@ -1093,7 +1534,7 @@
}, },
{ {
"Id:string": "item_ingredient_005", "Id:string": "item_ingredient_005",
"#Name": "치킨닭 고기" "#Name": "미정"
}, },
{ {
"Id:string": "item_ingredient_006", "Id:string": "item_ingredient_006",
@ -1168,6 +1609,53 @@
"#Name": "오렌지" "#Name": "오렌지"
} }
], ],
"CookwareData": [
{
"Id:string": "식별ID",
"#Name": "이름",
"CookwareType:Enum": "요리도구 타입"
},
{
"Id:string": "item_environment_cookware_001",
"#Name": "냄비",
"CookwareType:Enum": "Pot"
},
{
"Id:string": "item_environment_cookware_002",
"#Name": "튀김",
"CookwareType:Enum": "Fryer"
},
{
"Id:string": "item_environment_cookware_003",
"#Name": "화로",
"CookwareType:Enum": "FirePit"
},
{
"Id:string": "item_environment_cookware_004",
"#Name": "",
"CookwareType:Enum": "CuttingBoard"
},
{
"Id:string": "item_environment_cookware_005",
"#Name": "",
"CookwareType:Enum": "Griddle"
},
{
"Id:string": "item_environment_cookware_006",
"#Name": "",
"CookwareType:Enum": "MagicOven"
},
{
"Id:string": "item_environment_cookware_007",
"#Name": "",
"CookwareType:Enum": "JuiceMachine"
},
{
"Id:string": "item_environment_cookware_008",
"#Name": "",
"CookwareType:Enum": "Barrel"
}
],
"TasteData": [ "TasteData": [
{ {
"Id:string": "식별ID", "Id:string": "식별ID",
@ -1176,92 +1664,92 @@
}, },
{ {
"Id:string": "Sweet", "Id:string": "Sweet",
"#Name": "", "#Name": "단맛",
"TasteType:Enum": "Sweet" "TasteType:Enum": "Sweet"
}, },
{ {
"Id:string": "Warm", "Id:string": "Warm",
"#Name": "", "#Name": "따뜻한",
"TasteType:Enum": "Warm" "TasteType:Enum": "Warm"
}, },
{ {
"Id:string": "Vegetable", "Id:string": "Vegetable",
"#Name": "", "#Name": "채류",
"TasteType:Enum": "Vegetable" "TasteType:Enum": "Vegetable"
}, },
{ {
"Id:string": "Bitter", "Id:string": "Bitter",
"#Name": "", "#Name": "쓴맛",
"TasteType:Enum": "Bitter" "TasteType:Enum": "Bitter"
}, },
{ {
"Id:string": "Salty", "Id:string": "Salty",
"#Name": "", "#Name": "짠맛",
"TasteType:Enum": "Salty" "TasteType:Enum": "Salty"
}, },
{ {
"Id:string": "Mild", "Id:string": "Mild",
"#Name": "", "#Name": "담백한",
"TasteType:Enum": "Mild" "TasteType:Enum": "Mild"
}, },
{ {
"Id:string": "Addictive", "Id:string": "Addictive",
"#Name": "", "#Name": "중독적인",
"TasteType:Enum": "Addictive" "TasteType:Enum": "Addictive"
}, },
{ {
"Id:string": "Sour", "Id:string": "Sour",
"#Name": "", "#Name": "신맛",
"TasteType:Enum": "Sour" "TasteType:Enum": "Sour"
}, },
{ {
"Id:string": "Savory", "Id:string": "Savory",
"#Name": "", "#Name": "고소한",
"TasteType:Enum": "Savory" "TasteType:Enum": "Savory"
}, },
{ {
"Id:string": "Meat", "Id:string": "Meat",
"#Name": "", "#Name": "육류",
"TasteType:Enum": "Meat" "TasteType:Enum": "Meat"
}, },
{ {
"Id:string": "SuperHot", "Id:string": "SuperHot",
"#Name": "", "#Name": "핵불닭맛",
"TasteType:Enum": "SuperHot" "TasteType:Enum": "SuperHot"
}, },
{ {
"Id:string": "Fish", "Id:string": "Fish",
"#Name": "", "#Name": "어류",
"TasteType:Enum": "Fish" "TasteType:Enum": "Fish"
}, },
{ {
"Id:string": "NoneDegree", "Id:string": "NoneDegree",
"#Name": "", "#Name": "도수가없는",
"TasteType:Enum": "NoneDegree" "TasteType:Enum": "NoneDegree"
}, },
{ {
"Id:string": "Soda", "Id:string": "Soda",
"#Name": "", "#Name": "탄산",
"TasteType:Enum": "Soda" "TasteType:Enum": "Soda"
}, },
{ {
"Id:string": "LowDegree", "Id:string": "LowDegree",
"#Name": "", "#Name": "도수가낮은",
"TasteType:Enum": "LowDegree" "TasteType:Enum": "LowDegree"
}, },
{ {
"Id:string": "WoodFlavor", "Id:string": "WoodFlavor",
"#Name": "", "#Name": "나무향",
"TasteType:Enum": "WoodFlavor" "TasteType:Enum": "WoodFlavor"
}, },
{ {
"Id:string": "MiddleDegree", "Id:string": "MiddleDegree",
"#Name": "", "#Name": "도수가보통인",
"TasteType:Enum": "MiddleDegree" "TasteType:Enum": "MiddleDegree"
}, },
{ {
"Id:string": "FruitFlavor", "Id:string": "FruitFlavor",
"#Name": "", "#Name": "과일향",
"TasteType:Enum": "FruitFlavor" "TasteType:Enum": "FruitFlavor"
} }
], ],

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 61a1a3168841b2843bdc42a2d7affc2f guid: 26d67eeb92542fa4bb54e438ab5d3628
TextScriptImporter: TextScriptImporter:
externalObjects: {} externalObjects: {}
userData: userData:

View File

@ -1,5 +1,4 @@
// <auto-generated> File: IngredientDataSo.cs // <auto-generated>
using System.Collections.Generic;
using UnityEngine; using UnityEngine;
namespace DDD namespace DDD

View File

@ -0,0 +1,56 @@
// <auto-generated>
using System;
using UnityEngine;
namespace DDD
{
[Serializable]
public class LevelData : IId
{
/// <summary>식별번호</summary>
[Tooltip("식별번호")]
[field: SerializeField]
public string Id { get; set; }
/// <summary>등장 지역</summary>
[Tooltip("등장 지역")]
public string Area;
/// <summary>일반 손님 풀</summary>
[Tooltip("일반 손님 풀")]
public string CustomerPool;
/// <summary>스페셜 손님 풀</summary>
[Tooltip("스페셜 손님 풀")]
public string SpecialCustomerPool;
/// <summary>확정 스페셜 손님 수</summary>
[Tooltip("확정 스페셜 손님 수")]
public int FixSpecialCustomerCount;
/// <summary>추가 스페셜 손님 확률</summary>
[Tooltip("추가 스페셜 손님 확률")]
public int AddSpecialCustomerCount;
/// <summary>손님 리스폰 시간</summary>
[Tooltip("손님 리스폰 시간")]
public int CustomerRespawnTime;
/// <summary>손님 1명당 경험치</summary>
[Tooltip("손님 1명당 경험치")]
public int Exp;
/// <summary>주문 대기 인내심 카운트 시간</summary>
[Tooltip("주문 대기 인내심 카운트 시간")]
public int OrderTime;
/// <summary>주문 인내심 카운트 시간</summary>
[Tooltip("주문 인내심 카운트 시간")]
public int WaitTime;
/// <summary>식사 소요시간</summary>
[Tooltip("식사 소요시간")]
public int EatingTime;
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 56f9c016bfc63a04280b30c71254c556

View File

@ -0,0 +1,8 @@
// <auto-generated>
using UnityEngine;
namespace DDD
{
[CreateAssetMenu(fileName = "LevelDataSo", menuName = "GoogleSheet/LevelDataSo")]
public class LevelDataSo : DataSo<LevelData> { }
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 4205a3f22a79fa74ba1cb5786c1bc13b

View File

@ -1,20 +0,0 @@
// <auto-generated>
using System;
using UnityEngine;
namespace DDD
{
[Serializable]
public class TasteData : IId
{
/// <summary>식별ID</summary>
[Tooltip("식별ID")]
[field: SerializeField]
public string Id { get; set; }
/// <summary>맛 종류</summary>
[Tooltip("맛 종류")]
public TasteType TasteType;
}
}

View File

@ -3,64 +3,41 @@
namespace DDD namespace DDD
{ {
public enum ItemType public enum CookwareType
{ {
None = 0, None = 0,
Food = 1, Barrel = 1,
Ingredient = 2, CuttingBoard = 2,
Environment = 3, FirePit = 3,
Drink = 4, Fryer = 4,
Griddle = 5,
JuiceMachine = 6,
MagicOven = 7,
Pot = 8,
}
public enum ItemType
{
None = 0,
Drink = 1,
Environment = 2,
Food = 3,
Ingredient = 4,
Recipe = 5, Recipe = 5,
} }
public enum RecipeType public enum RecipeType
{ {
None = 0, None = 0,
FoodRecipe = 1, DrinkRecipe = 1,
DrinkRecipe = 2, FoodRecipe = 2,
} }
public enum CookwareType public enum RendererType
{ {
None = 0, None = 0,
Pot = 1, Spine = 1,
Fryer = 2, Sprite = 2,
FirePit = 3,
CuttingBoard = 4,
Griddle = 5,
MagicOven = 6,
JuiceMachine = 7,
Barrel = 8,
}
public enum TasteType
{
None = 0,
Sweet = 1,
Warm = 2,
Vegetable = 3,
Bitter = 4,
Salty = 5,
Mild = 6,
Addictive = 7,
Sour = 8,
Savory = 9,
Meat = 10,
SuperHot = 11,
Fish = 12,
NoneDegree = 13,
Soda = 14,
LowDegree = 15,
WoodFlavor = 16,
MiddleDegree = 17,
FruitFlavor = 18,
}
public enum RendererType
{
None = 0,
Sprite = 1,
Spine = 2,
} }
} }

View File

@ -58,10 +58,419 @@
"": "FruitFlavor" "": "FruitFlavor"
} }
], ],
"Test": [
{
"Id:string": "식별번호",
"CustomerLimitCount": "손님 최대 수",
"Customers:string": "등장 손님",
"Test": 2352,
"Tests": ""
},
{
"Id:string": "customer_pool_001",
"CustomerLimitCount": 4,
"Customers:string": "customer_001",
"Test": 0,
"Tests": "테스트, 테스트2"
},
{
"Id:string": "customer_pool_002",
"CustomerLimitCount": 7,
"Customers:string": "customer_001 | customer_002",
"Test": 3,
"Tests": "1 , 2"
},
{
"Id:string": "customer_pool_003",
"CustomerLimitCount": 9,
"Customers:string": "customer_001 | customer_002 | customer_003",
"Test": 2,
"Tests": "5, 23,253"
},
{
"Id:string": "customer_pool_004",
"CustomerLimitCount": 3,
"Customers:string": "customer_001",
"Test": "",
"Tests": ""
},
{
"Id:string": "customer_pool_005",
"CustomerLimitCount": 3,
"Customers:string": "customer_001",
"Test": "",
"Tests": "435, 25"
},
{
"Id:string": "customer_pool_006",
"CustomerLimitCount": 3,
"Customers:string": "customer_001",
"Test": "",
"Tests": ""
},
{
"Id:string": "special_customer_pool_001",
"CustomerLimitCount": 1,
"Customers:string": "special_customer_001",
"Test": "",
"Tests": "111|234"
}
],
"Test_view": [
{
"Id:string": "식별번호",
"CustomerLimitCount": "손님 최대 수",
"Customers1": "등장 손님1",
"Customers2": "등장 손님2",
"Customers3": "등장 손님3",
"Customers4": "등장 손님4",
"Customers5": "등장 손님5",
"Customers6": "등장 손님6",
"Test": 2352,
"Tests1": "Tests1",
"Tests2": "Tests2",
"Tests3": "Tests3"
},
{
"Id:string": "customer_pool_001",
"CustomerLimitCount": 4,
"Customers1": "customer_001",
"Customers2": "",
"Customers3": "",
"Customers4": "",
"Customers5": "",
"Customers6": "",
"Test": 0,
"Tests1": "테스트",
"Tests2": "테스트2",
"Tests3": ""
},
{
"Id:string": "customer_pool_002",
"CustomerLimitCount": 7,
"Customers1": "customer_001",
"Customers2": "customer_002",
"Customers3": "",
"Customers4": "",
"Customers5": "",
"Customers6": "",
"Test": 3,
"Tests1": 1,
"Tests2": 2,
"Tests3": ""
},
{
"Id:string": "customer_pool_003",
"CustomerLimitCount": 9,
"Customers1": "customer_001",
"Customers2": "customer_002",
"Customers3": "customer_003",
"Customers4": "",
"Customers5": "",
"Customers6": "",
"Test": 2,
"Tests1": 5,
"Tests2": 23,
"Tests3": 253
},
{
"Id:string": "customer_pool_004",
"CustomerLimitCount": 3,
"Customers1": "customer_001",
"Customers2": "",
"Customers3": "",
"Customers4": "",
"Customers5": "",
"Customers6": "",
"Test": "",
"Tests1": "",
"Tests2": "",
"Tests3": ""
},
{
"Id:string": "customer_pool_005",
"CustomerLimitCount": 3,
"Customers1": "customer_001",
"Customers2": "",
"Customers3": "",
"Customers4": "",
"Customers5": "",
"Customers6": "",
"Test": "",
"Tests1": 435,
"Tests2": 25,
"Tests3": ""
},
{
"Id:string": "customer_pool_006",
"CustomerLimitCount": 3,
"Customers1": "customer_001",
"Customers2": "",
"Customers3": "",
"Customers4": "",
"Customers5": "",
"Customers6": "",
"Test": "",
"Tests1": "",
"Tests2": "",
"Tests3": ""
},
{
"Id:string": "special_customer_pool_001",
"CustomerLimitCount": 1,
"Customers1": "special_customer_001",
"Customers2": "",
"Customers3": "",
"Customers4": "",
"Customers5": "",
"Customers6": "",
"Test": "",
"Tests1": 111,
"Tests2": 234,
"Tests3": ""
}
],
"CustomerPoolData": [
{
"Id:string": "식별번호",
"CustomerLimitCount": "손님 최대 수",
"Customer1": "등장 손님1",
"Customer2": "등장 손님2",
"Customer3": "등장 손님3",
"Customer4": "등장 손님4",
"Customer5": "등장 손님5",
"Customer6": "등장 손님6"
},
{
"Id:string": "customer_pool_001",
"CustomerLimitCount": 4,
"Customer1": "customer_001",
"Customer2": "",
"Customer3": "",
"Customer4": "",
"Customer5": "",
"Customer6": ""
},
{
"Id:string": "customer_pool_002",
"CustomerLimitCount": 7,
"Customer1": "customer_001",
"Customer2": "customer_002",
"Customer3": "",
"Customer4": "",
"Customer5": "",
"Customer6": ""
},
{
"Id:string": "customer_pool_003",
"CustomerLimitCount": 9,
"Customer1": "customer_001",
"Customer2": "customer_002",
"Customer3": " customer_003",
"Customer4": "",
"Customer5": "",
"Customer6": ""
},
{
"Id:string": "customer_pool_004",
"CustomerLimitCount": 3,
"Customer1": "customer_001",
"Customer2": "",
"Customer3": "",
"Customer4": "",
"Customer5": "",
"Customer6": ""
},
{
"Id:string": "customer_pool_005",
"CustomerLimitCount": 3,
"Customer1": "customer_001",
"Customer2": "",
"Customer3": "",
"Customer4": "",
"Customer5": "",
"Customer6": ""
},
{
"Id:string": "customer_pool_006",
"CustomerLimitCount": 3,
"Customer1": "customer_001",
"Customer2": "",
"Customer3": "",
"Customer4": "",
"Customer5": "",
"Customer6": ""
},
{
"Id:string": "special_customer_pool_001",
"CustomerLimitCount": 1,
"Customer1": "special_customer_001",
"Customer2": "",
"Customer3": "",
"Customer4": "",
"Customer5": "",
"Customer6": ""
}
],
"LevelData": [
{
"Id:string": "식별번호",
"Area": "등장 지역",
"CustomerPool": "일반 손님 풀",
"SpecialCustomerPool": "스페셜 손님 풀",
"FixSpecialCustomerCount": "확정 스페셜 손님 수",
"AddSpecialCustomerCount": "추가 스페셜 손님 확률",
"CustomerRespawnTime": "손님 리스폰 시간",
"Exp": "손님 1명당 경험치",
"OrderTime": "주문 대기 인내심 카운트 시간",
"WaitTime": "주문 인내심 카운트 시간",
"EatingTime": "식사 소요시간"
},
{
"Id:string": 1,
"Area": "Area1",
"CustomerPool": "customer_pool_001",
"SpecialCustomerPool": "",
"FixSpecialCustomerCount": 0,
"AddSpecialCustomerCount": 0,
"CustomerRespawnTime": 6,
"Exp": 13,
"OrderTime": 30,
"WaitTime": 60,
"EatingTime": 6
},
{
"Id:string": 2,
"Area": "Area1",
"CustomerPool": "customer_pool_002",
"SpecialCustomerPool": "special_customer_pool_001",
"FixSpecialCustomerCount": 1,
"AddSpecialCustomerCount": 10,
"CustomerRespawnTime": 6,
"Exp": 13,
"OrderTime": 30,
"WaitTime": 60,
"EatingTime": 6
},
{
"Id:string": 3,
"Area": "Area1",
"CustomerPool": "customer_pool_003",
"SpecialCustomerPool": "special_customer_pool_001",
"FixSpecialCustomerCount": 2,
"AddSpecialCustomerCount": 30,
"CustomerRespawnTime": 6,
"Exp": 13,
"OrderTime": 30,
"WaitTime": 60,
"EatingTime": 6
},
{
"Id:string": 4,
"Area": "Area2",
"CustomerPool": "customer_pool_004",
"SpecialCustomerPool": "special_customer_pool_001",
"FixSpecialCustomerCount": 3,
"AddSpecialCustomerCount": 50,
"CustomerRespawnTime": 6,
"Exp": 13,
"OrderTime": 30,
"WaitTime": 60,
"EatingTime": 6
},
{
"Id:string": 5,
"Area": "Area2",
"CustomerPool": "customer_pool_005",
"SpecialCustomerPool": "special_customer_pool_001",
"FixSpecialCustomerCount": 3,
"AddSpecialCustomerCount": 50,
"CustomerRespawnTime": 6,
"Exp": 13,
"OrderTime": 30,
"WaitTime": 60,
"EatingTime": 6
},
{
"Id:string": 6,
"Area": "Area2",
"CustomerPool": "customer_pool_006",
"SpecialCustomerPool": "special_customer_pool_001",
"FixSpecialCustomerCount": 3,
"AddSpecialCustomerCount": 50,
"CustomerRespawnTime": 6,
"Exp": 13,
"OrderTime": 30,
"WaitTime": 60,
"EatingTime": 6
}
],
"CustomerData": [
{
"Id": "식별번호",
"CustomerType": "손님 타입",
"#Name": "이름",
"SpineSkinKey": "스파인 스킨 키 값",
"FavoriteTaste1": "선호 맛1",
"FavoriteTaste2": "선호 맛2",
"FavoriteTaste3": "선호 맛3",
"FavoriteTaste4": "선호 맛4",
"FavoriteTaste5": "선호 맛5",
"FavoriteTaste6": "선호 맛6"
},
{
"Id": "customer_001",
"CustomerType": "Normal",
"#Name": "유령1",
"SpineSkinKey": "",
"FavoriteTaste1": "",
"FavoriteTaste2": "",
"FavoriteTaste3": "",
"FavoriteTaste4": "",
"FavoriteTaste5": "",
"FavoriteTaste6": ""
},
{
"Id": "customer_002",
"CustomerType": "Normal",
"#Name": "유령2",
"SpineSkinKey": "",
"FavoriteTaste1": "",
"FavoriteTaste2": "",
"FavoriteTaste3": "",
"FavoriteTaste4": "",
"FavoriteTaste5": "",
"FavoriteTaste6": ""
},
{
"Id": "customer_003",
"CustomerType": "Normal",
"#Name": "유령3",
"SpineSkinKey": "",
"FavoriteTaste1": "",
"FavoriteTaste2": "",
"FavoriteTaste3": "",
"FavoriteTaste4": "",
"FavoriteTaste5": "",
"FavoriteTaste6": ""
},
{
"Id": "special_customer_001",
"CustomerType": "Special",
"#Name": "차이",
"SpineSkinKey": "",
"FavoriteTaste1": "",
"FavoriteTaste2": "",
"FavoriteTaste3": "",
"FavoriteTaste4": "",
"FavoriteTaste5": "",
"FavoriteTaste6": ""
}
],
"ItemData": [ "ItemData": [
{ {
"Id": "식별번호", "Id": "식별번호",
"ItemType:Enum": "아이템타입" "ItemType:Enum": "아이템타입1"
}, },
{ {
"Id": "item_food_001", "Id": "item_food_001",
@ -1125,7 +1534,7 @@
}, },
{ {
"Id:string": "item_ingredient_005", "Id:string": "item_ingredient_005",
"#Name": "치킨닭 고기" "#Name": "미정"
}, },
{ {
"Id:string": "item_ingredient_006", "Id:string": "item_ingredient_006",

Binary file not shown.

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 3392661ff91138a4991ec360e86907cb
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,66 +0,0 @@
#if UNITY_EDITOR
using System.IO;
using UnityEditor;
using UnityEditor.AddressableAssets;
using UnityEditor.AddressableAssets.Settings;
using UnityEditor.AddressableAssets.Settings.GroupSchemas;
using UnityEngine;
public static class GoogleSheetAddressableAutoSetup
{
private const string TargetGroupName = "GoogleSheetSo_Group";
private const string TargetLabel = "GoogleSheetSo";
public static void AutoRegisterSo(string assetPath)
{
var settings = AddressableAssetSettingsDefaultObject.Settings;
if (settings == null)
{
Debug.LogError("Addressable 설정을 찾을 수 없습니다.");
return;
}
string guid = AssetDatabase.AssetPathToGUID(assetPath);
if (string.IsNullOrEmpty(guid)) return;
// 그룹 가져오기 또는 생성
var group = settings.FindGroup(TargetGroupName);
if (group == null)
{
group = settings.CreateGroup(TargetGroupName, false, false, false, null,
typeof(BundledAssetGroupSchema), typeof(ContentUpdateGroupSchema));
var bundledSchema = group.GetSchema<BundledAssetGroupSchema>();
bundledSchema.BundleMode = BundledAssetGroupSchema.BundlePackingMode.PackTogether;
}
// 엔트리 추가
var entry = settings.FindAssetEntry(guid);
if (entry == null)
{
entry = settings.CreateOrMoveEntry(guid, group);
}
else if (entry.parentGroup != group)
{
settings.MoveEntry(entry, group);
}
// Address 설정
string name = Path.GetFileNameWithoutExtension(assetPath);
entry.address = name;
// Label 설정
if (!entry.labels.Contains(TargetLabel))
{
entry.SetLabel(TargetLabel, true, true);
}
// 저장
EditorUtility.SetDirty(settings);
settings.SetDirty(AddressableAssetSettings.ModificationEvent.EntryMoved, entry, true);
AssetDatabase.SaveAssets();
Debug.Log($"✅ Addressables에 등록 완료: {name}, 그룹: {TargetGroupName}, 라벨: {TargetLabel}");
}
}
#endif

View File

@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 91161e8df06b9734ca03d579aceaa508

View File

@ -1,30 +0,0 @@
// GoogleSheetChangeLog.cs
using System;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(fileName = "GoogleSheetChangeLog", menuName = "GoogleSheet/ChangeLog", order = 0)]
public class GoogleSheetChangeLog : ScriptableObject
{
[Serializable]
public class LogEntry
{
public string Editor;
public string Timestamp;
[TextArea(5, 20)] public string JsonSnapshot;
}
[SerializeField] private List<LogEntry> _logs = new();
public List<LogEntry> Logs => _logs;
public int MaxLogs = 100;
public void AddEntry(LogEntry entry)
{
if (_logs.Count >= MaxLogs)
{
_logs.RemoveAt(0);
}
_logs.Add(entry);
}
}

View File

@ -1,11 +0,0 @@
using System;
[Serializable]
public class GoogleSheetDiff
{
public string Sheet;
public string Field;
public int RowIndex;
public string OldValue;
public string NewValue;
}

View File

@ -1,73 +0,0 @@
using Newtonsoft.Json.Linq;
using System.Collections.Generic;
public static class GoogleSheetDiffHelper
{
public static List<GoogleSheetDiff> CompareJsonDiff(string oldJson, string newJson)
{
var diffs = new List<GoogleSheetDiff>();
if (string.IsNullOrEmpty(oldJson) || string.IsNullOrEmpty(newJson))
return diffs;
var oldObj = JObject.Parse(oldJson);
var newObj = JObject.Parse(newJson);
foreach (var sheet in newObj)
{
var sheetName = sheet.Key;
if (!oldObj.TryGetValue(sheetName, out var oldSheetToken))
continue;
var oldArray = oldSheetToken as JArray;
var newArray = sheet.Value as JArray;
for (int i = 1; i < newArray.Count; i++)
{
if (i >= oldArray.Count)
break;
var newRow = (JObject)newArray[i];
var oldRow = (JObject)oldArray[i];
foreach (var prop in newRow.Properties())
{
var field = prop.Name;
string newValue = prop.Value.ToString();
string oldValue = oldRow.TryGetValue(field, out var oldVal) ? oldVal.ToString() : "";
if (oldValue != newValue)
{
diffs.Add(new GoogleSheetDiff
{
Sheet = sheetName,
Field = field,
RowIndex = i,
OldValue = oldValue,
NewValue = newValue
});
}
}
}
}
return diffs;
}
public static string GenerateDiff(string oldJson, string newJson)
{
var diffs = CompareJsonDiff(oldJson, newJson);
if (diffs.Count == 0)
return "No differences found.";
var sb = new System.Text.StringBuilder();
sb.AppendLine("[GoogleSheetManager] 변경된 필드들:");
foreach (var diff in diffs)
{
sb.AppendLine($"{diff.Sheet} / Row {diff.RowIndex} / {diff.Field} : '{diff.OldValue}' → '{diff.NewValue}'");
}
return sb.ToString();
}
}

View File

@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: b1cb07de998538442b14e113ff8da86e

View File

@ -1,69 +0,0 @@
#if UNITY_EDITOR
using UnityEditor;
using UnityEngine;
using System.Collections.Generic;
public class GoogleSheetDiffViewer : EditorWindow
{
private List<GoogleSheetDiff> _diffs;
private bool _isReversed; // true: 현재 → 선택버전, false: 변경 전 → 변경 후
private Vector2 _scroll;
public static void ShowWindow(List<GoogleSheetDiff> diffs, bool isReversed = false)
{
var window = GetWindow<GoogleSheetDiffViewer>("Google Sheet 변경점");
window._diffs = diffs;
window._isReversed = isReversed;
window.Show();
}
private void OnGUI()
{
EditorGUILayout.LabelField("변경된 항목", EditorStyles.boldLabel);
EditorGUILayout.Space();
if (_diffs == null || _diffs.Count == 0)
{
EditorGUILayout.HelpBox("변경 사항이 없습니다.", MessageType.Info);
return;
}
// 헤더
EditorGUILayout.BeginHorizontal("box");
EditorGUILayout.LabelField("시트", EditorStyles.boldLabel, GUILayout.Width(80));
EditorGUILayout.LabelField("행", EditorStyles.boldLabel, GUILayout.Width(40));
EditorGUILayout.LabelField("필드", EditorStyles.boldLabel, GUILayout.Width(140));
GUIStyle oldStyle = new GUIStyle(EditorStyles.label);
oldStyle.normal.textColor = Color.green;
EditorGUILayout.LabelField(_isReversed ? "현재 버전" : "변경 전", oldStyle, GUILayout.Width(100));
GUIStyle newStyle = new GUIStyle(EditorStyles.label);
newStyle.normal.textColor = new Color(0.3f, 0.75f, 1.0f);
EditorGUILayout.LabelField(_isReversed ? "선택한 버전" : "변경 후", newStyle, GUILayout.Width(100));
EditorGUILayout.EndHorizontal();
_scroll = EditorGUILayout.BeginScrollView(_scroll);
foreach (var diff in _diffs)
{
EditorGUILayout.BeginHorizontal("box");
EditorGUILayout.LabelField(diff.Sheet, GUILayout.Width(80));
EditorGUILayout.LabelField(diff.RowIndex.ToString(), GUILayout.Width(40));
EditorGUILayout.LabelField(diff.Field, GUILayout.Width(140));
string left = _isReversed ? diff.NewValue : diff.OldValue;
string right = _isReversed ? diff.OldValue : diff.NewValue;
EditorGUILayout.LabelField(left, oldStyle, GUILayout.Width(100));
EditorGUILayout.LabelField(right, newStyle, GUILayout.Width(100));
EditorGUILayout.EndHorizontal();
}
EditorGUILayout.EndScrollView();
}
}
#endif

View File

@ -1,53 +0,0 @@
using System.Collections.Generic;
using Newtonsoft.Json.Linq;
using UnityEngine;
public static class GoogleSheetFetchHelper
{
public static List<GoogleSheetDiff> CompareJsonDiff(string oldJson, string newJson)
{
var result = new List<GoogleSheetDiff>();
if (string.IsNullOrEmpty(oldJson) || string.IsNullOrEmpty(newJson))
return result;
var oldObj = JObject.Parse(oldJson);
var newObj = JObject.Parse(newJson);
foreach (var sheet in newObj)
{
if (!oldObj.TryGetValue(sheet.Key, out var oldSheetData))
continue;
var newSheetData = (JArray)sheet.Value;
var oldSheetArray = (JArray)oldSheetData;
int minCount = Mathf.Min(oldSheetArray.Count, newSheetData.Count);
for (int i = 1; i < minCount; i++)
{
var oldRow = (JObject)oldSheetArray[i];
var newRow = (JObject)newSheetData[i];
foreach (var prop in newRow.Properties())
{
string oldVal = oldRow.TryGetValue(prop.Name, out var val) ? val.ToString() : "";
string newVal = prop.Value.ToString();
if (oldVal != newVal)
{
result.Add(new GoogleSheetDiff
{
Sheet = sheet.Key,
Field = prop.Name,
RowIndex = i + 1,
OldValue = oldVal,
NewValue = newVal
});
}
}
}
}
return result;
}
}

View File

@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: e3062282d3af306458153938166b72ce

View File

@ -1,906 +0,0 @@
#if UNITY_EDITOR
using System;
using System.IO;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;
using System.Collections.Generic;
using System.Collections;
using UnityEngine;
using Newtonsoft.Json.Linq;
using System.Linq;
using System.Runtime.CompilerServices;
using JetBrains.Annotations;
using Sirenix.OdinInspector;
using UnityEditor;
using UnityEngine.AddressableAssets;
using ColorUtility = UnityEngine.ColorUtility;
public class GoogleSheetManager : Singleton<GoogleSheetManager>
{
[BoxGroup("기본 설정")]
[SerializeField, Tooltip("true: google sheet, false: local json")]
private bool _isAccessGoogleSheet = true;
[BoxGroup("기본 설정")]
[SerializeField, Tooltip("구글 시트 -> 확장 프로그램 -> Apps Script -> 새 배포(웹 앱) or 배포 관리 -> 웹 앱 URL(~~~/exec)")]
private string _googleSheetUrl;
[BoxGroup("기본 설정")]
[SerializeField, Tooltip("기본 네임 스페이스")]
private string _namespace = "DDD";
[BoxGroup("기본 설정")]
[SerializeField, Tooltip("코드/Enum/So 자동 생성 + 데이터 반영 시트")]
private List<string> _autoCreateSheets = new();
[BoxGroup("기본 설정")]
[SerializeField, Tooltip("기존 Data/So 유지, SO 데이터만 동기화 시트")]
private List<string> _soSyncSheets = new();
[BoxGroup("기본 설정")]
[SerializeField, Tooltip("Class, Json, So 생성 위치 \"/GenerateGoogleSheet\"")]
private string _generateFolderPath = "/_DDD/_Scripts/GenerateGoogleSheet/AutoCreated";
[BoxGroup("버전 복구")]
[SerializeField, Tooltip("현재 사용중인 버전"), ReadOnly, UsedImplicitly]
private string _currentVersion;
[BoxGroup("버전 복구")]
[SerializeField, ValueDropdown(nameof(GetVersionOptions))]
private int _restoreIndex;
[BoxGroup("데이터 변경"), LabelText("수정자 이름")]
[SerializeField, Required("반드시 수정자 이름을 입력해야 합니다\n이력을 남길 때 표시될 사용자 이름입니다.")]
private string _editorName;
private string BaseFullPath => $"{Application.dataPath}{_generateFolderPath}";
private string BaseAssetPath => $"Assets{_generateFolderPath}";
private string JsonFullPath => $"{BaseFullPath}/GoogleSheetJson.json";
private string ChangeLogAssetPath => $"{BaseAssetPath}/Logs/GoogleSheetChangeLog.asset";
private string BackupFullPath => $"{BaseFullPath}/BackUps";
private string ClassedFullPath => $"{BaseAssetPath}/Classes";
private string[] _availSheetArray;
private string _json;
private bool _isCreatingSo;
[SerializeField, ReadOnly]
private bool _refreshTrigger;
private bool _alreadyCreatedSo;
private bool IsAuto(string name) => _autoCreateSheets.Contains(name);
private bool IsSync(string name) => _soSyncSheets.Contains(name);
private bool IsSelected(string name) => IsAuto(name) || IsSync(name);
[BoxGroup("데이터 변경")]
[Button("데이터 최신화"), EnableIf(nameof(CanFetchData))]
private async Task FetchGoogleSheet()
{
// 0) 이전 원본 JSON
var prevLog = AssetDatabase.LoadAssetAtPath<GoogleSheetChangeLog>(ChangeLogAssetPath);
string previousJson = prevLog?.Logs.LastOrDefault()?.JsonSnapshot ?? "";
// 1) 최신 원본 JSON 로드 (전체 시트)
if (_isAccessGoogleSheet)
{
if (!IsValidGoogleSheetUrl(_googleSheetUrl))
{
Debug.LogError("Google Sheet URL이 유효하지 않습니다.");
return;
}
Debug.Log("구글 시트 데이터 읽는 중...");
_json = await LoadDataGoogleSheet(_googleSheetUrl);
}
else
{
Debug.Log("Local Json 파일 읽는 중...");
_json = LoadDataLocalJson();
}
if (string.IsNullOrEmpty(_json))
{
Debug.LogWarning("Json is null/empty. 최신화 실패");
return;
}
// 2) Diff/로그는 '전체 JSON' 기준
var diffs = GoogleSheetFetchHelper.CompareJsonDiff(previousJson, _json);
if (diffs.Count > 0)
GoogleSheetDiffViewer.ShowWindow(diffs);
// 3) 워크 파일로 전체 JSON 저장(변경시만)
bool savedJson = SaveFileOrSkip(JsonFullPath, _json);
// 4) 클래스 생성 (auto만, 파일 없을 때만 생성)
bool createdScripts = false;
try
{
createdScripts = GenerateClassFilesPerSheet(_json);
}
catch (Exception e)
{
Debug.LogError($"[GoogleSheetManager] 클래스 파일 생성 중 오류: {e.Message}");
return;
}
// 5) 로그/백업은 전체 JSON 기준으로 변경시에만 기록
if (!string.IsNullOrEmpty(previousJson))
{
if (!previousJson.Equals(_json))
SaveChangeLog(_json);
}
else
{
// 첫 기록
SaveChangeLog(_json);
}
// 6) 새 스크립트가 생겼다면 컴파일 완료 대기
if (createdScripts)
{
await WaitUntilScriptsReady();
await Task.Delay(100); // 도메인 리로드 직후 안정화 여유
}
// 7) SO 동기화는 항상 수행 (변경 없어도)
bool ok = await CreateGoogleSheetSoAsync();
if (!ok) Debug.LogWarning("SO 동기화 중 일부 실패가 있었습니다.");
// 8) 필요시 마무리 리프레시
if (savedJson || createdScripts || diffs.Count > 0)
AssetDatabase.Refresh();
}
private async Task WaitUntilScriptsReady()
{
// 스크립트 생성 직후 컴파일이 끝날 때까지 대기
while (EditorApplication.isCompiling)
await Task.Delay(150);
AssetDatabase.Refresh();
}
private bool CanFetchData()
{
return !string.IsNullOrWhiteSpace(_editorName);
}
/// <summary>
/// Json 로그 저장
/// </summary>
private void SaveChangeLog(string json)
{
string logsDirectory = Path.GetDirectoryName(ChangeLogAssetPath);
if (!Directory.Exists(logsDirectory))
{
if (logsDirectory != null)
{
Directory.CreateDirectory(logsDirectory);
AssetDatabase.ImportAsset(logsDirectory);
}
}
var log = AssetDatabase.LoadAssetAtPath<GoogleSheetChangeLog>(ChangeLogAssetPath);
if (log == null)
{
log = ScriptableObject.CreateInstance<GoogleSheetChangeLog>();
AssetDatabase.CreateAsset(log, ChangeLogAssetPath);
}
string previousJson = log.Logs.Count > 0 ? log.Logs[^1].JsonSnapshot : null;
// 차이 비교
if (!string.IsNullOrEmpty(previousJson))
{
string diffResult = GoogleSheetDiffHelper.GenerateDiff(previousJson, json);
Debug.Log(diffResult);
}
string saveTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
string versionLabel = $"{log.Logs.Count} - {saveTime} by {_editorName}";
_currentVersion = versionLabel;
log.Logs.Add(new GoogleSheetChangeLog.LogEntry
{
Editor = _editorName,
Timestamp = saveTime,
JsonSnapshot = json
});
EditorUtility.SetDirty(log);
AssetDatabase.SaveAssets();
SaveJsonBackup(json, saveTime);
_editorName = null;
}
/// <summary>
/// Json 백업
/// </summary>
private void SaveJsonBackup(string json, string saveTime)
{
string safeSaveTime = saveTime.Replace(":", "-"); // 윈도우 파일 이름 안전 처리
if (!Directory.Exists(BackupFullPath))
Directory.CreateDirectory(BackupFullPath);
string fileName = $"{safeSaveTime} by {_editorName}.json";
string filePath = Path.Combine(BackupFullPath, fileName);
File.WriteAllText(filePath, json);
}
[BoxGroup("버전 복구")]
[Button("선택한 버전과 현재 비교")]
private void CompareWithSelectedVersion()
{
var log = AssetDatabase.LoadAssetAtPath<GoogleSheetChangeLog>(ChangeLogAssetPath);
if (log == null || _restoreIndex < 0 || _restoreIndex >= log.Logs.Count)
{
Debug.LogWarning("비교할 수 있는 로그가 없습니다.");
return;
}
string restoreJson = log.Logs[_restoreIndex].JsonSnapshot;
string currentJson = File.Exists(JsonFullPath) ? File.ReadAllText(JsonFullPath) : "";
List<GoogleSheetDiff> diffs = GoogleSheetFetchHelper.CompareJsonDiff(currentJson, restoreJson);
if (diffs.Count > 0)
{
GoogleSheetDiffViewer.ShowWindow(diffs, true); // 현재 → 선택 버전
Debug.Log("[GoogleSheetManager] 선택한 버전과 현재 버전 간의 변경점을 표시합니다.");
}
else
{
Debug.Log("[GoogleSheetManager] 변경점 없음.");
}
}
[BoxGroup("버전 복구")]
[Button("선택한 버전으로 복구")]
private async Task RestoreSelectedVersion()
{
var log = AssetDatabase.LoadAssetAtPath<GoogleSheetChangeLog>(ChangeLogAssetPath);
if (log == null || _restoreIndex < 0 || _restoreIndex >= log.Logs.Count)
{
Debug.LogWarning("복원할 수 있는 로그가 없습니다.");
return;
}
string restoreJson = log.Logs[_restoreIndex].JsonSnapshot;
string currentJson = File.Exists(JsonFullPath) ? await File.ReadAllTextAsync(JsonFullPath) : "";
var diffs = GoogleSheetFetchHelper.CompareJsonDiff(currentJson, restoreJson);
if (diffs.Count > 0)
GoogleSheetDiffViewer.ShowWindow(diffs);
_json = restoreJson;
SaveFileOrSkip(JsonFullPath, _json);
await CreateGoogleSheetSoAsync();
_currentVersion = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
Debug.Log($"[{log.Logs[_restoreIndex].Editor}]의 버전으로 복원 완료");
}
/// <summary>
/// 버전 로그 드롭다운 함수
/// </summary>
private IEnumerable<ValueDropdownItem<int>> GetVersionOptions()
{
var log = AssetDatabase.LoadAssetAtPath<GoogleSheetChangeLog>(ChangeLogAssetPath);
if (log == null)
yield break;
for (int i = 0; i < log.Logs.Count; i++)
{
yield return new ValueDropdownItem<int>($"{i} - {log.Logs[i].Timestamp} by {log.Logs[i].Editor}", i);
}
}
/// <summary>
/// 구글 시트 데이터 읽어오기
/// </summary>
private async Task<string> LoadDataGoogleSheet(string url)
{
// 네트워크가 느리거나 프록시/방화벽 영향 받는 환경에서 멈춤 방지
var handler = new HttpClientHandler
{
// 사내 프록시/보안 툴 때문에 멈출 수 있으면 필요에 따라 끄기
UseProxy = true, // 필요하면 false 로 바꿔 테스트
};
using var client = new HttpClient(handler)
{
Timeout = TimeSpan.FromSeconds(20) // ← 핵심: 타임아웃
};
try
{
Debug.Log("[GSM] HTTP GET start");
var resp = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false);
Debug.Log($"[GSM] HTTP status: {(int)resp.StatusCode} {resp.ReasonPhrase}");
resp.EnsureSuccessStatusCode(); // 2xx 아니면 예외
var text = await resp.Content.ReadAsStringAsync().ConfigureAwait(false);
Debug.Log($"[GSM] HTTP OK, length={text?.Length ?? 0}");
return text;
}
catch (TaskCanceledException)
{
Debug.LogError("[GSM] 요청이 타임아웃되었습니다. URL이 열리는지 브라우저에서 먼저 확인해보세요.");
return null;
}
catch (HttpRequestException e)
{
Debug.LogError($"[GSM] HTTP 오류: {e.Message}");
return null;
}
catch (Exception e)
{
Debug.LogError($"[GSM] 예기치 못한 오류: {e}");
return null;
}
}
/// <summary>
/// jSON 데이터 파일 읽어오기
/// </summary>
private string LoadDataLocalJson()
{
if (File.Exists(JsonFullPath))
{
return File.ReadAllText(JsonFullPath);
}
Debug.Log($"Json 파일이 존재하지 않습니다\n{JsonFullPath}");
return null;
}
/// <summary>
/// 파일 생성 및 비교
/// </summary>
private bool SaveFileOrSkip(string path, string contents)
{
string directoryPath = Path.GetDirectoryName(path);
if (!Directory.Exists(directoryPath))
{
if (directoryPath != null) Directory.CreateDirectory(directoryPath);
}
if (File.Exists(path) && File.ReadAllText(path).Equals(contents))
return false;
File.WriteAllText(path, contents);
return true;
}
/// <summary>
/// 유효한 구글 웹 앱 URL인지 확인
/// </summary>
private bool IsValidGoogleSheetUrl(string url)
{
return !string.IsNullOrEmpty(url)
&& url.StartsWith("https://script.google.com/macros/")
&& url.EndsWith("/exec");
}
private bool GenerateClassFilesPerSheet(string jsonInput)
{
try
{
AssetDatabase.StartAssetEditing();
EditorApplication.LockReloadAssemblies();
AssetDatabase.DisallowAutoRefresh();
bool createdAny = false;
var root = JObject.Parse(jsonInput);
// 1) Enum 후보 수집 (auto 시트만)
var enumCandidates = new Dictionary<string, HashSet<string>>(StringComparer.Ordinal);
foreach (var pair in root)
{
string className = pair.Key;
if (!IsAuto(className)) continue;
var items = pair.Value as JArray;
if (items == null || items.Count < 2) continue;
for (int i = 1; i < items.Count; i++)
{
foreach (var prop in ((JObject)items[i]).Properties())
{
string raw = prop.Name;
if (raw.StartsWith("#")) continue;
string enumType = null;
if (raw.Contains(":Enum"))
{
// 단일 필드 Enum: Cookware:Enum -> enumType = "Cookware"
enumType = raw.Split(':')[0];
}
else if (raw.Contains(":") && raw.EndsWith("_Enum"))
{
// 공통 Enum: Taste1:Taste_Enum -> enumType = "Taste"
enumType = raw.Split(':')[1].Replace("_Enum", "");
}
else if (raw.Contains(":NativeEnum"))
{
continue; // 네이티브 enum은 자동 생성 대상 아님
}
if (string.IsNullOrEmpty(enumType)) continue;
string enumValue = NormalizeEnumKey(prop.Value?.ToString() ?? "");
if (!enumCandidates.TryGetValue(enumType, out var set))
{
set = new HashSet<string>(StringComparer.Ordinal);
enumCandidates.Add(enumType, set);
}
set.Add(enumValue);
}
}
}
// 2) EnumTypes.cs 생성/갱신 (자동 파일이므로 덮어써도 안전)
var enumPath = $"{BaseAssetPath}/EnumTypes.cs";
File.WriteAllText(enumPath, BuildEnumCode(enumCandidates));
AssetDatabase.ImportAsset(enumPath);
// 3) 클래스/So 생성 (auto만, 파일이 없을 때만)
if (!Directory.Exists(ClassedFullPath))
{
Directory.CreateDirectory(ClassedFullPath);
AssetDatabase.ImportAsset(ClassedFullPath);
}
foreach (var pair in root)
{
string className = pair.Key;
if (!IsAuto(className)) continue;
var items = pair.Value as JArray;
if (items == null || items.Count < 2) continue;
string dataPath = $"{ClassedFullPath}/{className}.cs";
string soPath = $"{ClassedFullPath}/{className}So.cs";
if (!File.Exists(dataPath))
{
File.WriteAllText(dataPath, GenerateDataClassCode(className, items));
AssetDatabase.ImportAsset(dataPath);
createdAny = true;
}
if (!File.Exists(soPath))
{
File.WriteAllText(soPath, GenerateSoClassCode(className));
AssetDatabase.ImportAsset(soPath);
createdAny = true;
}
}
return createdAny;
}
finally
{
AssetDatabase.StopAssetEditing();
EditorApplication.UnlockReloadAssemblies();
AssetDatabase.AllowAutoRefresh();
AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport); // 마지막에 1회만
}
}
private string BuildEnumCode(Dictionary<string, HashSet<string>> enums)
{
var sb = new StringBuilder();
sb.AppendLine("// <auto-generated>");
sb.AppendLine("using System;");
sb.AppendLine();
sb.AppendLine($"namespace {_namespace}");
sb.AppendLine("{");
foreach (var kv in enums)
{
sb.AppendLine($" public enum {kv.Key}");
sb.AppendLine(" {");
sb.AppendLine(" None = 0,");
int i = 1;
foreach (var val in kv.Value)
{
if (!string.IsNullOrWhiteSpace(val) && val != "None")
sb.AppendLine($" {val} = {i++},");
}
sb.AppendLine(" }");
sb.AppendLine();
}
sb.AppendLine("}");
return sb.ToString();
}
private string GenerateSoClassCode(string className)
{
return
$"// <auto-generated> File: {className}So.cs\n" +
"using System.Collections.Generic;\n" +
"using UnityEngine;\n\n" +
$"namespace {_namespace}\n" +
$"{{\n" +
$" [CreateAssetMenu(fileName = \"{className}So\", menuName = \"GoogleSheet/{className}So\")]\n" +
$" public class {className}So : DataSo<{className}> {{ }}\n" +
$"}}";
}
private string GenerateDataClassCode(string className, JArray items)
{
var commentRow = (JObject)items[0];
var sampleRow = (JObject)items[1];
StringBuilder sb = new();
sb.AppendLine("// <auto-generated>");
sb.AppendLine("using System;");
sb.AppendLine("using UnityEngine;");
sb.AppendLine();
sb.AppendLine($"namespace {_namespace}");
sb.AppendLine("{");
sb.AppendLine(" [Serializable]");
sb.AppendLine($" public class {className} : IId");
sb.AppendLine(" {");
List<string> types = new();
List<string> names = new();
List<string> tooltips = new();
foreach (var prop in sampleRow.Properties())
{
string rawName = prop.Name;
// 무시할 컬럼이면 continue
if (rawName.StartsWith("#")) continue;
string fieldName = rawName;
string explicitType = null;
if (rawName.Contains(":Enum"))
{
fieldName = rawName.Split(':')[0];
explicitType = fieldName;
}
else if (rawName.Contains(":") && rawName.EndsWith("_Enum"))
{
var parts = rawName.Split(':');
fieldName = parts[0];
explicitType = parts[1].Replace("_Enum", "");
}
else if (rawName.Contains(":NativeEnum"))
{
fieldName = rawName.Split(':')[0];
explicitType = fieldName;
}
else if (rawName.Contains(":"))
{
var parts = rawName.Split(':');
fieldName = parts[0];
explicitType = parts[1];
}
types.Add(explicitType ?? GetCSharpType(prop.Value.Type));
names.Add(fieldName);
tooltips.Add(commentRow.TryGetValue(rawName, out var tip) ? tip.ToString() : "");
}
for (int i = 0; i < names.Count; i++)
{
if (!string.IsNullOrWhiteSpace(tooltips[i]))
{
sb.AppendLine($" /// <summary>{tooltips[i]}</summary>");
sb.AppendLine($" [Tooltip(\"{tooltips[i]}\")]");
}
if (names[i] == "Id" && types[i] == "string")
{
sb.AppendLine(" [field: SerializeField]");
sb.AppendLine(" public string Id { get; set; }\n");
}
else
{
sb.AppendLine($" public {types[i]} {names[i]};\n");
}
}
sb.AppendLine(" }");
sb.AppendLine("}");
return sb.ToString();
}
private string GetCSharpType(JTokenType jsonType)
{
switch (jsonType)
{
case JTokenType.Integer:
return "int";
case JTokenType.Float:
return "float";
case JTokenType.Boolean:
return "bool";
default:
return "string";
}
}
private async Task<bool> CreateGoogleSheetSoAsync()
{
await Addressables.InitializeAsync().Task;
if (_isCreatingSo)
{
Debug.LogWarning("[GoogleSheetManager] 이미 SO 생성 중입니다. 중복 호출 방지");
return false;
}
_isCreatingSo = true;
bool result = false;
try
{
// 🔁 카탈로그 업데이트 제거 (로컬 모드에서는 불필요)
Debug.Log("[GoogleSheetManager] 로컬 모드 - 카탈로그 업데이트 스킵");
result = InternalCreateGoogleSheetSoAsync();
}
catch (Exception e)
{
Debug.LogError($"[GoogleSheetManager] SO 생성 중 예외 발생: {e}");
}
finally
{
_isCreatingSo = false;
}
if (result) // 성공적으로 SO 생성된 경우에만 빌드 수행
{
Debug.Log("[GoogleSheetManager] Addressables BuildPlayerContent 실행");
UnityEditor.AddressableAssets.Settings.AddressableAssetSettings.BuildPlayerContent();
}
return result;
}
private static IEnumerable<Type> GetAllTypesSafe()
{
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
{
Type[] types;
try
{
types = asm.GetTypes();
}
catch (ReflectionTypeLoadException ex)
{
types = ex.Types.Where(t => t != null).ToArray();
}
foreach (var t in types)
if (t != null)
yield return t;
}
}
private bool InternalCreateGoogleSheetSoAsync()
{
if (string.IsNullOrEmpty(_json))
{
Debug.LogError("[GoogleSheetManager] JSON 비어있음");
return false;
}
var root = JObject.Parse(_json);
bool allSuccess = true;
// 타입 캐시
var typeCache = GetAllTypesSafe()
.Where(t =>
t.IsClass &&
t.Namespace == _namespace &&
!t.Name.StartsWith("<") && // <>c 같은 컴파일러 생성 타입 제외
!Attribute.IsDefined(t, typeof(CompilerGeneratedAttribute))
)
.GroupBy(t => t.Name) // 같은 이름 여러 개면 첫 것만
.ToDictionary(g => g.Key, g => g.First());
string soDir = $"Assets{_generateFolderPath}/So";
if (!Directory.Exists(soDir))
{
Directory.CreateDirectory(soDir);
AssetDatabase.ImportAsset(soDir);
}
foreach (var pair in root)
{
string sheet = pair.Key;
if (!IsSelected(sheet))
{
Debug.Log($"[GSM] Skip (not selected): {sheet}");
continue;
}
typeCache.TryGetValue(sheet, out var dataType);
typeCache.TryGetValue($"{sheet}So", out var soType);
if (dataType == null || soType == null)
{
Debug.LogWarning($"[GSM] Type missing for '{sheet}'. ns='{_namespace}' " +
$"dataType={(dataType == null ? "null" : dataType.FullName)} " +
$"soType={(soType == null ? "null" : soType.FullName)}");
allSuccess = false;
continue;
}
string soPath = $"{soDir}/{sheet}So.asset";
var so = AssetDatabase.LoadAssetAtPath<ScriptableObject>(soPath);
if (so == null)
{
Debug.Log($"[GSM] Creating SO asset: {soPath}");
so = ScriptableObject.CreateInstance(soType);
AssetDatabase.CreateAsset(so, soPath);
AssetDatabase.SaveAssets();
AssetDatabase.ImportAsset(soPath, ImportAssetOptions.ForceSynchronousImport);
// 존재 확인
var check = AssetDatabase.LoadAssetAtPath<ScriptableObject>(soPath);
Debug.Log(check ? $"[GSM] SO created OK: {soPath}" : $"[GSM] SO create FAILED: {soPath}");
}
else
{
Debug.Log($"[GSM] SO asset exists: {soPath}");
}
// 데이터 파싱 → 리스트
var list = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(dataType));
var rows = (JArray)pair.Value;
for (int i = 1; i < rows.Count; i++)
{
var row = (JObject)rows[i];
var inst = Activator.CreateInstance(dataType);
foreach (var prop in row.Properties())
{
var raw = prop.Name;
if (raw.StartsWith("#")) continue;
string field = raw.Contains(":") ? raw.Split(':')[0] : raw;
var f = dataType.GetField(field,
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
var p = dataType.GetProperty(field,
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (f == null && p == null) continue;
try
{
var target = f?.FieldType ?? p?.PropertyType;
object value;
if (target.IsEnum)
{
var k = NormalizeEnumKey(prop.Value.ToString());
value = Enum.TryParse(target, k, out var parsed)
? parsed
: Activator.CreateInstance(target);
}
else if (target == typeof(Color))
{
value = ColorUtility.TryParseHtmlString(prop.Value.ToString(), out var c) ? c : Color.white;
}
else if (target == typeof(string))
{
value = prop.Value.ToString();
}
else
{
value = Convert.ChangeType(prop.Value.ToString(), target);
}
if (f != null) f.SetValue(inst, value);
else if (p is { CanWrite: true }) p.SetValue(inst, value);
}
catch (Exception e)
{
Debug.LogWarning($"[{sheet}] 값 할당 실패 {raw} → {e.Message}");
}
}
list.Add(inst);
}
// SetDataList 호출
var setMethod = soType.GetMethod("SetDataList",
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (setMethod == null)
{
Debug.LogError($"{soType.Name}에 SetDataList가 없습니다.");
allSuccess = false;
continue;
}
setMethod.Invoke(so, new object[] { list });
EditorUtility.SetDirty(so);
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
// Addressables 자동 등록은 auto만 (팀별 환경오염 방지)
if (IsAuto(sheet))
GoogleSheetAddressableAutoSetup.AutoRegisterSo(soPath);
}
Debug.Log("✅ SO 동기화 완료(선택 시트만, 변경 없어도 항상 반영)");
return allSuccess;
}
private string NormalizeEnumKey(string input)
{
if (string.IsNullOrEmpty(input))
return "None";
// 특수문자 및 공백을 밑줄(_)로 치환
string validName = System.Text.RegularExpressions.Regex.Replace(input, @"[^a-zA-Z0-9_]+", "_");
// 숫자로 시작하는 경우 밑줄 추가
if (char.IsDigit(validName[0]))
validName = "_" + validName;
// 첫 글자 대문자화
return char.ToUpper(validName[0]) + validName.Substring(1);
}
private void OnValidate()
{
if (_refreshTrigger && !_alreadyCreatedSo && EditorPrefs.GetBool("GoogleSheetManager_ShouldCreateSO"))
{
_refreshTrigger = false;
_alreadyCreatedSo = true;
EditorPrefs.SetBool("GoogleSheetManager_ShouldCreateSO", false); // 재실행 방지
EditorApplication.delayCall += () =>
{
_ = DelayAndCreateSo(); // 무시 가능한 비동기
};
}
}
private async Task DelayAndCreateSo()
{
await Task.Delay(300); // 300ms 지연 (너무 짧으면 반영 안됨)
if (!string.IsNullOrEmpty(_json))
{
bool success = await CreateGoogleSheetSoAsync();
Debug.Log(success ? "Fetch done. SO 업데이트 완료" : "SO 생성 실패. 수동으로 Fetch를 다시 시도하세요.");
}
}
public async Task CreateSoAfterScriptReload()
{
if (_json != null)
{
Debug.Log("[GoogleSheetManager] Script Reload 이후 SO 생성 실행");
await CreateGoogleSheetSoAsync();
}
}
}
#endif

View File

@ -1,18 +0,0 @@
#if UNITY_EDITOR
using UnityEditor;
using UnityEditor.Callbacks;
using UnityEngine;
public static class GoogleSheetPostProcessor
{
[DidReloadScripts]
private static void OnScriptsReloaded()
{
if (EditorPrefs.GetBool("GoogleSheetManager_ShouldCreateSO", false))
{
EditorPrefs.DeleteKey("GoogleSheetManager_ShouldCreateSO");
_ = GoogleSheetManager.Instance.CreateSoAfterScriptReload();
}
}
}
#endif

View File

@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: acef684d9f9959e4dbbf34cd1c9dfb37

View File

@ -1,6 +1,7 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 99e82f09adb03584fba0e4366be017c9 guid: 7e514e02415867140926993a92bcf816
TextScriptImporter: folderAsset: yes
DefaultImporter:
externalObjects: {} externalObjects: {}
userData: userData:
assetBundleName: assetBundleName:

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 176b356799a34a2f9e9a212ebb8f586d
timeCreated: 1754925910

View File

@ -0,0 +1,68 @@
#if UNITY_EDITOR
using UnityEditor;
using UnityEditor.AddressableAssets;
using UnityEditor.AddressableAssets.Settings;
using UnityEditor.AddressableAssets.Settings.GroupSchemas;
using UnityEngine;
namespace DDD
{
public sealed class AddressablesRegistrar : IAddressablesRegistrar
{
private AddressableAssetSettings _settings;
private bool _dirty;
public AddressablesRegistrar()
{
_settings = AddressableAssetSettingsDefaultObject.Settings;
if (_settings == null)
Debug.LogError("[AddressablesRegistrar] Addressables Settings를 찾지 못했습니다.");
}
public void RegisterOrMoveEntry(string assetPath, string groupName, string label, string addressOverride = null)
{
if (_settings == null) return;
string guid = AssetDatabase.AssetPathToGUID(assetPath);
if (string.IsNullOrEmpty(guid)) return;
var group = _settings.FindGroup(groupName);
if (group == null)
{
group = _settings.CreateGroup(groupName, false, false, false, null,
typeof(BundledAssetGroupSchema), typeof(ContentUpdateGroupSchema));
var bundled = group.GetSchema<BundledAssetGroupSchema>();
bundled.BundleMode = BundledAssetGroupSchema.BundlePackingMode.PackTogether;
}
var entry = _settings.FindAssetEntry(guid);
if (entry == null) entry = _settings.CreateOrMoveEntry(guid, group);
else if (entry.parentGroup != group) _settings.MoveEntry(entry, group);
entry.address = string.IsNullOrEmpty(addressOverride)
? System.IO.Path.GetFileNameWithoutExtension(assetPath)
: addressOverride;
if (!entry.labels.Contains(label))
entry.SetLabel(label, true, true);
_settings.SetDirty(AddressableAssetSettings.ModificationEvent.EntryMoved, entry, true);
_dirty = true;
}
public void SaveAll()
{
if (!_dirty) return;
EditorUtility.SetDirty(_settings);
AssetDatabase.SaveAssets();
_dirty = false;
}
public void BuildIfNeeded(bool shouldBuild)
{
if (!shouldBuild || _settings == null) return;
AddressableAssetSettings.BuildPlayerContent();
}
}
}
#endif

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 794fd8fb1485449f86dd73037cad2abc
timeCreated: 1754926004

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ae0646d42ada4352a2a089bda93fc49e
timeCreated: 1754925896

View File

@ -0,0 +1,241 @@
#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Newtonsoft.Json.Linq;
using UnityEditor;
namespace DDD
{
public sealed class CSharpCodeGenerator : ICodeGenerator
{
private readonly ITypeNameResolver _resolver;
public CSharpCodeGenerator(ITypeNameResolver resolver) => _resolver = resolver;
public CodeGenerationResult Generate(string fullJson, IReadOnlyCollection<string> autoCreateSheets,
string rootNamespace, string generateAssetBasePath,
string enumTypesAssetPath, string classesFolderAssetPath)
{
var result = new CodeGenerationResult();
if (string.IsNullOrEmpty(fullJson)) return result;
JObject root;
try
{
root = JObject.Parse(fullJson);
}
catch
{
return result;
}
// Enum 후보 수집
var enumCandidates = new Dictionary<string, HashSet<string>>(StringComparer.Ordinal);
foreach (var pair in root)
{
if (!autoCreateSheets.Contains(pair.Key)) continue;
if (pair.Value is not JArray items || items.Count < 2) continue;
for (int i = 1; i < items.Count; i++)
{
foreach (var prop in ((JObject)items[i]).Properties())
{
var raw = prop.Name;
if (raw.StartsWith("#")) continue;
string enumType = null;
if (raw.Contains(":Enum")) enumType = raw.Split(':')[0];
else if (raw.Contains(":") && raw.EndsWith("_Enum"))
enumType = raw.Split(':')[1].Replace("_Enum", "");
else if (raw.Contains(":NativeEnum")) continue;
if (string.IsNullOrEmpty(enumType)) continue;
string enumValue = NormalizeEnumKey(prop.Value?.ToString() ?? "");
if (!enumCandidates.TryGetValue(enumType, out var set))
{
set = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
enumCandidates.Add(enumType, set);
}
set.Add(enumValue);
}
}
}
// EnumTypes.cs (결정성: 알파벳 정렬)
EnsureDirectory(enumTypesAssetPath);
File.WriteAllText(GoogleSheetSettingsSo.AssetToFull(enumTypesAssetPath),
BuildEnumCode(rootNamespace, enumCandidates));
AssetDatabase.ImportAsset(enumTypesAssetPath);
result.CreatedAssetPaths.Add(enumTypesAssetPath);
result.AnyFileCreated = true;
// Classes 폴더
if (!AssetDatabase.IsValidFolder(classesFolderAssetPath))
CreateFolderRecursive(classesFolderAssetPath);
foreach (var pair in root)
{
string className = pair.Key;
if (!autoCreateSheets.Contains(className)) continue;
if (pair.Value is not JArray items || items.Count < 2) continue;
string dataAssetPath = $"{classesFolderAssetPath}/{className}.cs";
string soAssetPath = $"{classesFolderAssetPath}/{className}So.cs";
if (!File.Exists(GoogleSheetSettingsSo.AssetToFull(dataAssetPath)))
{
EnsureDirectory(dataAssetPath);
File.WriteAllText(GoogleSheetSettingsSo.AssetToFull(dataAssetPath),
GenerateDataClass(rootNamespace, className, items));
AssetDatabase.ImportAsset(dataAssetPath);
result.CreatedAssetPaths.Add(dataAssetPath);
result.AnyFileCreated = true;
}
if (!File.Exists(GoogleSheetSettingsSo.AssetToFull(soAssetPath)))
{
EnsureDirectory(soAssetPath);
File.WriteAllText(GoogleSheetSettingsSo.AssetToFull(soAssetPath),
GenerateSoClass(rootNamespace, className));
AssetDatabase.ImportAsset(soAssetPath);
result.CreatedAssetPaths.Add(soAssetPath);
result.AnyFileCreated = true;
}
}
return result;
}
private static void CreateFolderRecursive(string assetPath)
{
var parts = assetPath.Split('/');
string acc = parts[0]; // "Assets"
for (int i = 1; i < parts.Length; i++)
{
string next = $"{acc}/{parts[i]}";
if (!AssetDatabase.IsValidFolder(next))
AssetDatabase.CreateFolder(acc, parts[i]);
acc = next;
}
}
private static void EnsureDirectory(string assetPath)
{
var full = GoogleSheetSettingsSo.AssetToFull(assetPath);
var dir = Path.GetDirectoryName(full);
if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir))
Directory.CreateDirectory(dir);
}
private static string BuildEnumCode(string rootNamespace, Dictionary<string, HashSet<string>> enums)
{
var sb = new StringBuilder();
sb.AppendLine("// <auto-generated>");
sb.AppendLine("using System;");
sb.AppendLine();
sb.AppendLine($"namespace {rootNamespace}");
sb.AppendLine("{");
foreach (var kv in enums.OrderBy(k => k.Key, StringComparer.Ordinal))
{
var enumName = IdentifierSanitizer.Sanitize(kv.Key, true);
var values = kv.Value
.Where(v => !string.IsNullOrWhiteSpace(v) &&
!string.Equals(v, "None", StringComparison.OrdinalIgnoreCase))
.Select(v => IdentifierSanitizer.Sanitize(v, true))
.Distinct(StringComparer.OrdinalIgnoreCase)
.OrderBy(v => v, StringComparer.Ordinal)
.ToList();
sb.AppendLine($" public enum {enumName}");
sb.AppendLine(" {");
sb.AppendLine(" None = 0,");
for (int i = 0; i < values.Count; i++)
sb.AppendLine($" {values[i]} = {i + 1},");
sb.AppendLine(" }");
sb.AppendLine();
}
sb.AppendLine("}");
return sb.ToString();
}
private string GenerateSoClass(string rootNamespace, string className)
{
var safe = IdentifierSanitizer.Sanitize(className, true);
return
$@"// <auto-generated>
using UnityEngine;
namespace {rootNamespace}
{{
[CreateAssetMenu(fileName = ""{safe}So"", menuName = ""GoogleSheet/{safe}So"")]
public class {safe}So : DataSo<{safe}> {{ }}
}}";
}
private string GenerateDataClass(string rootNamespace, string className, JArray items)
{
var commentRow = (JObject)items[0];
var sampleRow = (JObject)items[1];
var safeClass = IdentifierSanitizer.Sanitize(className, true);
var sb = new StringBuilder();
sb.AppendLine("// <auto-generated>");
sb.AppendLine("using System;");
sb.AppendLine("using UnityEngine;");
sb.AppendLine();
sb.AppendLine($"namespace {rootNamespace}");
sb.AppendLine("{");
sb.AppendLine(" [Serializable]");
sb.AppendLine($" public class {safeClass} : IId");
sb.AppendLine(" {");
foreach (var prop in sampleRow.Properties())
{
string rawHeader = prop.Name;
if (rawHeader.StartsWith("#")) continue;
string fieldName = rawHeader.Contains(":") ? rawHeader.Split(':')[0] : rawHeader;
fieldName = IdentifierSanitizer.Sanitize(fieldName, true);
string typeName = _resolver.ResolveFieldTypeName(rawHeader, prop.Value);
string tooltip = commentRow.TryGetValue(rawHeader, out var tip) ? tip.ToString() : "";
if (!string.IsNullOrWhiteSpace(tooltip))
{
sb.AppendLine($" /// <summary>{tooltip}</summary>");
sb.AppendLine($" [Tooltip(\"{tooltip}\")]");
}
if (fieldName == "Id" && typeName == "string")
{
sb.AppendLine(" [field: SerializeField]");
sb.AppendLine(" public string Id { get; set; }\n");
}
else
{
sb.AppendLine($" public {typeName} {fieldName};\n");
}
}
sb.AppendLine(" }");
sb.AppendLine("}");
return sb.ToString();
}
private static string NormalizeEnumKey(string input)
{
if (string.IsNullOrEmpty(input)) return "None";
var valid = System.Text.RegularExpressions.Regex.Replace(input, @"[^a-zA-Z0-9_]+", "_");
if (char.IsDigit(valid[0])) valid = "_" + valid;
return char.ToUpperInvariant(valid[0]) + valid.Substring(1);
}
}
}
#endif

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 9b30078a6640456ea2bda893f12b40e1
timeCreated: 1754925971

View File

@ -0,0 +1,28 @@
#if UNITY_EDITOR
using Newtonsoft.Json.Linq;
namespace DDD
{
public sealed class DefaultTypeNameResolver : ITypeNameResolver
{
public string ResolveFieldTypeName(string rawHeader, JToken sampleValue)
{
if (string.IsNullOrEmpty(rawHeader)) return "string";
if (rawHeader.Contains(":Enum")) return rawHeader.Split(':')[0];
if (rawHeader.Contains(":") && rawHeader.EndsWith("_Enum"))
return rawHeader.Split(':')[1].Replace("_Enum", "");
if (rawHeader.Contains(":NativeEnum")) return rawHeader.Split(':')[0];
if (rawHeader.Contains(":")) return rawHeader.Split(':')[1];
return sampleValue?.Type switch
{
JTokenType.Integer => "int",
JTokenType.Float => "float",
JTokenType.Boolean => "bool",
_ => "string"
};
}
}
}
#endif

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 615215c08bd844f6a7a5625148b4df8a
timeCreated: 1754925977

View File

@ -0,0 +1,25 @@
using System.Collections.Generic;
namespace DDD
{
public static class IdentifierSanitizer
{
private static readonly HashSet<string> CSharpKeywords = new()
{
"class","struct","enum","namespace","public","private","protected","internal",
"static","void","int","float","double","decimal","string","bool",
"new","return","if","else","for","while","switch","case","default",
"break","continue","this","base","null","true","false","using"
};
public static string Sanitize(string raw, bool pascalCase = false)
{
if (string.IsNullOrWhiteSpace(raw)) return "_";
string s = System.Text.RegularExpressions.Regex.Replace(raw, @"[^\w]", "_");
if (char.IsDigit(s[0])) s = "_" + s;
if (pascalCase) s = char.ToUpperInvariant(s[0]) + s.Substring(1);
if (CSharpKeywords.Contains(s)) s = "_" + s;
return s;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 22119c040e354af5bbf8777f9430016b
timeCreated: 1754925982

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3f537f96e27749dd9d4ddfcdc0870ecc
timeCreated: 1754925901

View File

@ -0,0 +1,75 @@
using System;
using System.Globalization;
using UnityEngine;
using Color = System.Drawing.Color;
namespace DDD
{
public sealed class DefaultValueConverterRegistry : IValueConverterRegistry
{
public bool TryConvert(string source, Type targetType, out object value)
{
value = null;
if (targetType == typeof(string)) { value = source ?? ""; return true; }
if (targetType == typeof(bool))
{
var s = (source ?? "").Trim();
if (bool.TryParse(s, out var b)) { value = b; return true; }
if (s == "1" || s.Equals("yes", StringComparison.OrdinalIgnoreCase)) { value = true; return true; }
if (s == "0" || s.Equals("no", StringComparison.OrdinalIgnoreCase)) { value = false; return true; }
return false;
}
if (targetType.IsEnum)
{
var key = NormalizeEnumKey(source);
if (Enum.TryParse(targetType, key, true, out var parsed)) { value = parsed; return true; }
value = Activator.CreateInstance(targetType);
return true;
}
if (targetType == typeof(int))
{
if (int.TryParse(source, NumberStyles.Integer, CultureInfo.InvariantCulture, out var i)) { value = i; return true; }
return false;
}
if (targetType == typeof(float))
{
if (float.TryParse(source, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out var f)) { value = f; return true; }
return false;
}
if (targetType == typeof(double))
{
if (double.TryParse(source, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out var d)) { value = d; return true; }
return false;
}
if (targetType == typeof(decimal))
{
if (decimal.TryParse(source, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out var m)) { value = m; return true; }
return false;
}
if (targetType == typeof(Color))
{
if (ColorUtility.TryParseHtmlString(source, out var c)) { value = c; return true; }
return false;
}
try
{
value = Convert.ChangeType(source, targetType, CultureInfo.InvariantCulture);
return true;
}
catch { return false; }
}
private static string NormalizeEnumKey(string input)
{
if (string.IsNullOrEmpty(input)) return "None";
string valid = System.Text.RegularExpressions.Regex.Replace(input, @"[^a-zA-Z0-9_]+", "_");
if (char.IsDigit(valid[0])) valid = "_" + valid;
return char.ToUpperInvariant(valid[0]) + valid.Substring(1);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 38fb58c2a107435997dc9dfef500a644
timeCreated: 1754925991

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3e17817ee69f4a3eb23ba2132644e157
timeCreated: 1754925891

View File

@ -0,0 +1,68 @@
#if UNITY_EDITOR
using UnityEditor;
using UnityEngine;
using System.Collections.Generic;
namespace DDD
{
public class GoogleSheetDiffViewer : EditorWindow
{
private List<GoogleSheetDiff> _diffs;
private bool _isReversed;
private Vector2 _scroll;
public static void ShowWindow(List<GoogleSheetDiff> diffs, bool isReversed = false)
{
var window = GetWindow<GoogleSheetDiffViewer>("Google Sheet 변경점");
window._diffs = diffs;
window._isReversed = isReversed;
window.Show();
}
private void OnGUI()
{
EditorGUILayout.LabelField("변경된 항목", EditorStyles.boldLabel);
EditorGUILayout.Space();
if (_diffs == null || _diffs.Count == 0)
{
EditorGUILayout.HelpBox("변경 사항이 없습니다.", MessageType.Info);
return;
}
EditorGUILayout.BeginHorizontal("box");
EditorGUILayout.LabelField("시트", EditorStyles.boldLabel, GUILayout.Width(80));
EditorGUILayout.LabelField("행", EditorStyles.boldLabel, GUILayout.Width(40));
EditorGUILayout.LabelField("필드", EditorStyles.boldLabel, GUILayout.Width(140));
GUIStyle oldStyle = new GUIStyle(EditorStyles.label) { normal = { textColor = Color.green } };
EditorGUILayout.LabelField(_isReversed ? "현재 버전" : "변경 전", oldStyle, GUILayout.Width(100));
GUIStyle newStyle = new GUIStyle(EditorStyles.label) { normal = { textColor = new Color(0.3f, 0.75f, 1.0f) } };
EditorGUILayout.LabelField(_isReversed ? "선택한 버전" : "변경 후", newStyle, GUILayout.Width(100));
EditorGUILayout.EndHorizontal();
_scroll = EditorGUILayout.BeginScrollView(_scroll);
foreach (var diff in _diffs)
{
EditorGUILayout.BeginHorizontal("box");
EditorGUILayout.LabelField(diff.Sheet, GUILayout.Width(80));
EditorGUILayout.LabelField(diff.RowIndex.ToString(), GUILayout.Width(40));
EditorGUILayout.LabelField(diff.Field, GUILayout.Width(140));
string left = _isReversed ? diff.NewValue : diff.OldValue;
string right = _isReversed ? diff.OldValue : diff.NewValue;
EditorGUILayout.LabelField(left, oldStyle, GUILayout.Width(100));
EditorGUILayout.LabelField(right, newStyle, GUILayout.Width(100));
EditorGUILayout.EndHorizontal();
}
EditorGUILayout.EndScrollView();
}
}
}
#endif

View File

@ -0,0 +1,60 @@
#if UNITY_EDITOR
using System.Collections.Generic;
using UnityEngine;
using Newtonsoft.Json.Linq;
namespace DDD
{
public sealed class NewtonsoftJsonDiffer : IJsonDiffer
{
public List<GoogleSheetDiff> Compare(string oldJson, string newJson)
{
var diffs = new List<GoogleSheetDiff>();
if (string.IsNullOrEmpty(oldJson) || string.IsNullOrEmpty(newJson)) return diffs;
try
{
var oldObj = JObject.Parse(oldJson);
var newObj = JObject.Parse(newJson);
foreach (var sheet in newObj)
{
if (!oldObj.TryGetValue(sheet.Key, out var oldSheetToken)) continue;
var newArr = sheet.Value as JArray;
var oldArr = oldSheetToken as JArray;
if (newArr == null || oldArr == null) continue;
int min = Mathf.Min(newArr.Count, oldArr.Count);
for (int i = 1; i < min; i++)
{
var newRow = (JObject)newArr[i];
var oldRow = (JObject)oldArr[i];
foreach (var prop in newRow.Properties())
{
string n = prop.Value?.ToString() ?? "";
string o = oldRow.TryGetValue(prop.Name, out var ov) ? ov?.ToString() ?? "" : "";
if (n != o)
{
diffs.Add(new GoogleSheetDiff
{
Sheet = sheet.Key,
Field = prop.Name,
RowIndex = i,
OldValue = o,
NewValue = n
});
}
}
}
}
}
catch
{
// ignore
}
return diffs;
}
}
}
#endif

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 7046da15bf734ab49bcada6844f87ade
timeCreated: 1754925964

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d2dec2cbe46d49d5aee5c5438d4da327
timeCreated: 1754925917

View File

@ -0,0 +1,95 @@
#if UNITY_EDITOR
using System;
using System.Linq;
using System.Threading.Tasks;
using UnityEditor;
using UnityEditor.Callbacks;
using UnityEngine;
namespace DDD
{
public sealed class SessionStateReloadCoordinator : IReloadCoordinator
{
private const string KeyShouldRun = "DDD_GSM_ShouldRun";
private const string KeyManagerPath = "DDD_GSM_ManagerPath";
public void RequestPostCompileSoSync(GoogleSheetManager manager)
{
var path = AssetDatabase.GetAssetPath(manager);
if (string.IsNullOrEmpty(path))
{
Debug.LogWarning("[ReloadCoordinator] GoogleSheetManager 경로를 찾을 수 없습니다.");
return;
}
// 리로드 후 이어서 실행하라는 플래그 + 매니저 경로 저장
SessionState.SetBool(KeyShouldRun, true);
SessionState.SetString(KeyManagerPath, path);
// 혹시 이미 컴파일이 끝나 있었다면 바로 한 번 더 체크
EditorApplication.delayCall += TryRun;
}
// 에디터가 켜질 때도, 리로드 직후도 여기로 들어오게 한다
[InitializeOnLoadMethod]
private static void InitOnLoad() => EditorApplication.delayCall += TryRun;
[DidReloadScripts]
private static void OnScriptsReloaded() => EditorApplication.delayCall += TryRun;
private static async void TryRun()
{
if (!SessionState.GetBool(KeyShouldRun, false))
return;
// 아직 컴파일 중이면 다음 프레임에 재시도
if (EditorApplication.isCompiling)
{
EditorApplication.delayCall += TryRun;
return;
}
// 플래그 회수
SessionState.EraseBool(KeyShouldRun);
// 매니저 에셋 로드
var path = SessionState.GetString(KeyManagerPath, "");
SessionState.EraseString(KeyManagerPath);
GoogleSheetManager manager = null;
if (!string.IsNullOrEmpty(path))
manager = AssetDatabase.LoadAssetAtPath<GoogleSheetManager>(path);
if (manager == null)
{
// 경로 저장이 실패했을 경우 타입으로 검색(폴백)
var guid = AssetDatabase.FindAssets("t:GoogleSheetManager").FirstOrDefault();
if (!string.IsNullOrEmpty(guid))
{
var p = AssetDatabase.GUIDToAssetPath(guid);
manager = AssetDatabase.LoadAssetAtPath<GoogleSheetManager>(p);
}
}
if (manager == null)
{
Debug.LogWarning("[ReloadCoordinator] GoogleSheetManager 에셋을 찾지 못했습니다.");
return;
}
// 리로드 후 후속 SO 동기화 실행
try
{
// 짧은 안정화 대기 (도메인 리로드 직후 임포트 마무리용)
await Task.Delay(100);
await manager.ContinueSoSyncAfterReload_Public(); // ← 아래 3) 참고(퍼블릭 메서드)
}
catch (System.Exception e)
{
Debug.LogError($"[ReloadCoordinator] 후속 실행 중 예외: {e}");
}
}
}
}
#endif

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 06e205705bf641e9bef9d0ffd54ff2a6
timeCreated: 1754926015

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 154be435047b48c09ff4301ceba2057d
timeCreated: 1754925883

View File

@ -0,0 +1,29 @@
#if UNITY_EDITOR
using System.IO;
using System.Threading.Tasks;
using UnityEditor;
using UnityEngine;
namespace DDD
{
public sealed class LocalAssetJsonSource : IGoogleSheetSource
{
private readonly GoogleSheetSettingsSo _settings;
public LocalAssetJsonSource(GoogleSheetSettingsSo settings) => _settings = settings;
public Task<string> FetchAllJsonAsync()
{
// 프로젝트 내부 AssetPath의 작업 JSON을 읽음
var text = AssetDatabase.LoadAssetAtPath<TextAsset>(_settings.JsonWorkFileAssetPath);
if (text != null) return Task.FromResult(text.text);
// 파일이 TextAsset으로 임포트되지 않았다면 파일로 읽기
var full = GoogleSheetSettingsSo.AssetToFull(_settings.JsonWorkFileAssetPath);
if (File.Exists(full)) return Task.FromResult(File.ReadAllText(full));
Debug.LogWarning($"[LocalAssetJsonSource] 작업 JSON이 없습니다: {_settings.JsonWorkFileAssetPath}");
return Task.FromResult<string>(null);
}
}
}
#endif

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d1cc71a02cce46dcbe05b15dc211448d
timeCreated: 1754925958

View File

@ -0,0 +1,50 @@
#if UNITY_EDITOR
using System;
using System.IO;
using System.Net.Http;
using System.Threading.Tasks;
using UnityEditor;
using UnityEngine;
namespace DDD
{
public sealed class WebAppGoogleSheetSource : IGoogleSheetSource
{
private readonly GoogleSheetSettingsSo _settings;
public WebAppGoogleSheetSource(GoogleSheetSettingsSo settings) => _settings = settings;
public async Task<string> FetchAllJsonAsync()
{
if (string.IsNullOrWhiteSpace(_settings.WebAppUrl))
{
Debug.LogError("[WebAppGoogleSheetSource] WebAppUrl 비어있음");
return null;
}
var handler = new HttpClientHandler { UseProxy = _settings.UseSystemProxy };
using var client = new HttpClient(handler) { Timeout = TimeSpan.FromSeconds(_settings.HttpTimeoutSeconds) };
try
{
var resp = await client.GetAsync(_settings.WebAppUrl, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false);
resp.EnsureSuccessStatusCode();
var json = await resp.Content.ReadAsStringAsync().ConfigureAwait(false);
// 작업용 JSON을 AssetPath에 저장해 팀과 공유
var fullPath = GoogleSheetSettingsSo.AssetToFull(_settings.JsonWorkFileAssetPath);
var dir = Path.GetDirectoryName(fullPath);
if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir)) Directory.CreateDirectory(dir);
File.WriteAllText(fullPath, json);
// AssetDatabase.ImportAsset(_settings.JsonWorkFileAssetPath);
Debug.Log($"[WebAppGoogleSheetSource] Fetch OK, length={json?.Length ?? 0}");
return json;
}
catch (Exception e)
{
Debug.LogError($"[WebAppGoogleSheetSource] 오류: {e}");
return null;
}
}
}
}
#endif

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ca5370e3b0114cdd9d7a5db8f4f709c7
timeCreated: 1754925953

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 457c112c40574497804a44bee76d29dd
timeCreated: 1754925906

View File

@ -0,0 +1,150 @@
#if UNITY_EDITOR
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Newtonsoft.Json.Linq;
using UnityEditor;
using UnityEngine;
namespace DDD
{
public sealed class SoSynchronizer : ISoSynchronizer
{
private readonly IValueConverterRegistry _converter;
private readonly List<string> _updatedPaths = new();
public IReadOnlyList<string> UpdatedSoAssetPaths => _updatedPaths;
public SoSynchronizer(IValueConverterRegistry converter) => _converter = converter;
public bool SynchronizeAll(string fullJson, IReadOnlyCollection<string> selectedSheets, string rootNamespace, string soFolderAssetPath)
{
_updatedPaths.Clear();
if (string.IsNullOrEmpty(fullJson)) return false;
var root = JObject.Parse(fullJson);
bool allSuccess = true;
// 폴더 보장
if (!AssetDatabase.IsValidFolder(soFolderAssetPath))
CreateFolderRecursive(soFolderAssetPath);
// 타입 캐시
var types = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(a => {
try { return a.GetTypes(); }
catch (ReflectionTypeLoadException ex) { return ex.Types.Where(t => t != null); }
})
.Where(t => t != null && t.IsClass && t.Namespace == rootNamespace && !t.Name.StartsWith("<"))
.GroupBy(t => t.Name)
.ToDictionary(g => g.Key, g => g.First());
Debug.Log($"[SoSync] soFolder={soFolderAssetPath}");
Debug.Log($"[SoSync] selected: {string.Join(", ", selectedSheets)}");
foreach (var pair in root)
{
string sheet = pair.Key;
if (!selectedSheets.Contains(sheet))
{
Debug.Log($"[SoSync] SKIP(not selected): {sheet}");
continue;
}
types.TryGetValue(sheet, out var dataType);
types.TryGetValue($"{sheet}So", out var soType);
if (dataType == null || soType == null)
{
Debug.LogWarning($"[SoSynchronizer] 타입 누락: {sheet} (data={dataType}, so={soType})");
allSuccess = false;
continue;
}
string soAssetPath = $"{soFolderAssetPath}/{sheet}So.asset";
var so = AssetDatabase.LoadAssetAtPath<ScriptableObject>(soAssetPath);
if (so == null)
{
Debug.Log($"[SoSync] CREATE SO: {soAssetPath}");
so = ScriptableObject.CreateInstance(soType);
AssetDatabase.CreateAsset(so, soAssetPath);
}
else
{
Debug.Log($"[SoSync] FOUND SO: {soAssetPath}");
}
var list = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(dataType));
var rows = (JArray)pair.Value;
for (int i = 1; i < rows.Count; i++)
{
var row = (JObject)rows[i];
var inst = Activator.CreateInstance(dataType);
foreach (var prop in row.Properties())
{
var raw = prop.Name;
if (raw.StartsWith("#")) continue;
string field = raw.Contains(":") ? raw.Split(':')[0] : raw;
var f = dataType.GetField(field, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
var p = dataType.GetProperty(field, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (f == null && p == null) continue;
var targetType = f?.FieldType ?? p?.PropertyType;
try
{
if (_converter.TryConvert(prop.Value?.ToString(), targetType, out var converted))
{
if (f != null) f.SetValue(inst, converted);
else if (p is { CanWrite: true }) p.SetValue(inst, converted);
}
else
{
Debug.LogWarning($"[SoSynchronizer] 변환 실패: {sheet}.{field} -> {targetType?.Name} (원본:'{prop.Value}')");
}
}
catch (Exception e)
{
Debug.LogWarning($"[SoSynchronizer] 값 할당 예외: {sheet}.{field} -> {e.Message}");
}
}
list.Add(inst);
}
var setMethod = soType.GetMethod("SetDataList", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (setMethod == null)
{
Debug.LogError($"{soType.Name}에 SetDataList 메서드가 없습니다.");
allSuccess = false;
continue;
}
Debug.Log($"[SoSync] INVOKE SetDataList for {sheet} (rows={(pair.Value as JArray)?.Count ?? 0})");
setMethod.Invoke(so, new object[] { list });
EditorUtility.SetDirty(so);
_updatedPaths.Add(soAssetPath);
}
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
return allSuccess;
}
private static void CreateFolderRecursive(string assetPath)
{
var parts = assetPath.Split('/');
string acc = parts[0]; // Assets
for (int i = 1; i < parts.Length; i++)
{
string next = $"{acc}/{parts[i]}";
if (!AssetDatabase.IsValidFolder(next))
AssetDatabase.CreateFolder(acc, parts[i]);
acc = next;
}
}
}
}
#endif

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6a55e8e97a9a4549b980ba97cef9e45c
timeCreated: 1754925998

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 8779f010104a4df5a30a7d75261b9b1c
timeCreated: 1754925914

View File

@ -0,0 +1,92 @@
#if UNITY_EDITOR
using System.IO;
using UnityEditor;
using UnityEngine;
namespace DDD
{
public sealed class GoogleSheetChangeLogVersionStore : IVersionStore
{
private readonly string _changeLogAssetPath;
public GoogleSheetChangeLogVersionStore(string changeLogAssetPath) => _changeLogAssetPath = changeLogAssetPath;
public string GetLatestJsonOrEmpty()
{
var log = AssetDatabase.LoadAssetAtPath<GoogleSheetChangeLog>(_changeLogAssetPath);
if (log == null || log.Logs.Count == 0) return "";
return log.Logs[^1].JsonSnapshot ?? "";
}
public void SaveSnapshot(string json, string editorName, string backupsFolderAssetPath)
{
// 1) 체인지로그 에셋이 없으면 폴더부터 보장
var dir = Path.GetDirectoryName(_changeLogAssetPath)?.Replace("\\", "/");
EnsureAssetFolder(dir);
var log = AssetDatabase.LoadAssetAtPath<GoogleSheetChangeLog>(_changeLogAssetPath);
if (log == null)
{
log = ScriptableObject.CreateInstance<GoogleSheetChangeLog>();
AssetDatabase.CreateAsset(log, _changeLogAssetPath);
AssetDatabase.ImportAsset(_changeLogAssetPath);
}
// 2) 로그 추가
string timestamp = System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
log.AddEntry(new GoogleSheetChangeLog.LogEntry { Editor = editorName, Timestamp = timestamp, JsonSnapshot = json });
EditorUtility.SetDirty(log);
AssetDatabase.SaveAssets();
// 3) 백업 파일도 AssetPath 하위에 생성 (폴더 보장 + 임포트)
if (!string.IsNullOrEmpty(backupsFolderAssetPath))
{
EnsureAssetFolder(backupsFolderAssetPath);
string safe = timestamp.Replace(":", "-");
string fileAssetPath = $"{backupsFolderAssetPath}/{safe} by {editorName}.json";
File.WriteAllText(GoogleSheetSettingsSo.AssetToFull(fileAssetPath), json);
AssetDatabase.ImportAsset(fileAssetPath);
}
}
public string GetSnapshotAt(int index)
{
var log = AssetDatabase.LoadAssetAtPath<GoogleSheetChangeLog>(_changeLogAssetPath);
if (log == null) return "";
if (index < 0 || index >= log.Logs.Count) return "";
return log.Logs[index].JsonSnapshot ?? "";
}
public int SnapshotCount
{
get
{
var log = AssetDatabase.LoadAssetAtPath<GoogleSheetChangeLog>(_changeLogAssetPath);
return log == null ? 0 : log.Logs.Count;
}
}
// ── 중요한 보조 함수: AssetPath 폴더 보장 (슬래시 정규화 + 단계별 생성)
private static void EnsureAssetFolder(string assetDir)
{
if (string.IsNullOrEmpty(assetDir)) return;
var path = assetDir.Replace("\\", "/").TrimEnd('/');
if (AssetDatabase.IsValidFolder(path)) return;
var parts = path.Split('/');
if (parts.Length == 0 || parts[0] != "Assets")
throw new System.Exception($"Asset path must start with 'Assets/': {path}");
string acc = "Assets";
for (int i = 1; i < parts.Length; i++)
{
string next = $"{acc}/{parts[i]}";
if (!AssetDatabase.IsValidFolder(next))
AssetDatabase.CreateFolder(acc, parts[i]);
acc = next;
}
}
}
}
#endif

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 7fed1695995346aab1abdf8c8e7c0399
timeCreated: 1754926010

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 397392f2c0f514f4ca13c51c0af8e2f0
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,9 @@
namespace DDD
{
public interface IAddressablesRegistrar
{
void RegisterOrMoveEntry(string assetPath, string groupName, string label, string addressOverride = null);
void SaveAll();
void BuildIfNeeded(bool shouldBuild);
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4965067595f849f8b9a5c0083a181de9
timeCreated: 1754925853

View File

@ -0,0 +1,16 @@
using System.Collections.Generic;
namespace DDD
{
public sealed class CodeGenerationResult
{
public bool AnyFileCreated;
public List<string> CreatedAssetPaths = new();
}
public interface ICodeGenerator
{
CodeGenerationResult Generate(string fullJson, IReadOnlyCollection<string> autoCreateSheets, string rootNamespace, string generateAssetBasePath,
string enumTypesAssetPath, string classesFolderAssetPath);
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 2c57d237cbac4907b4f1e1b9047019f5
timeCreated: 1754925825

View File

@ -0,0 +1,9 @@
using System.Threading.Tasks;
namespace DDD
{
public interface IGoogleSheetSource
{
Task<string> FetchAllJsonAsync(); // WebApp에서 가져오거나, AssetPath의 작업 JSON을 읽음
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e5d6c83a531f4017adac059edef2e9cb
timeCreated: 1754925804

Some files were not shown because too many files have changed in this diff Show More