diff --git a/Assembly-CSharp-Editor.csproj b/Assembly-CSharp-Editor.csproj
index 33273b5..4901f29 100644
--- a/Assembly-CSharp-Editor.csproj
+++ b/Assembly-CSharp-Editor.csproj
@@ -43,6 +43,7 @@
6000.3.5f2
+
@@ -54,9 +55,12 @@
+
+
+
@@ -1245,14 +1249,14 @@
Library\ScriptAssemblies\PPv2URPConverters.dll
False
-
- Library\ScriptAssemblies\Unity.AI.Navigation.Updater.dll
- False
-
Library\ScriptAssemblies\Unity.2D.Common.Editor.dll
False
+
+ Library\ScriptAssemblies\Unity.AI.Navigation.Updater.dll
+ False
+
Library\ScriptAssemblies\Unity.AI.Navigation.Editor.dll
False
@@ -1289,6 +1293,10 @@
Library\ScriptAssemblies\Unity.2D.Aseprite.Common.dll
False
+
+ Library\ScriptAssemblies\Unity.Rider.Editor.dll
+ False
+
Library\ScriptAssemblies\Unity.AI.Navigation.Editor.ConversionSystem.dll
False
@@ -1297,10 +1305,6 @@
Library\ScriptAssemblies\Unity.2D.Aseprite.Editor.dll
False
-
- Library\ScriptAssemblies\Unity.Rider.Editor.dll
- False
-
Library\ScriptAssemblies\Unity.RenderPipelines.ShaderGraph.ShaderGraphLibrary.dll
False
diff --git a/Assembly-CSharp.csproj b/Assembly-CSharp.csproj
index 2006850..99a8509 100644
--- a/Assembly-CSharp.csproj
+++ b/Assembly-CSharp.csproj
@@ -43,6 +43,7 @@
6000.3.5f2
+
@@ -71,6 +72,7 @@
+
@@ -1266,14 +1268,14 @@
Library\ScriptAssemblies\PPv2URPConverters.dll
False
-
- Library\ScriptAssemblies\Unity.AI.Navigation.Updater.dll
- False
-
Library\ScriptAssemblies\Unity.2D.Common.Editor.dll
False
+
+ Library\ScriptAssemblies\Unity.AI.Navigation.Updater.dll
+ False
+
Library\ScriptAssemblies\Unity.AI.Navigation.Editor.dll
False
@@ -1310,6 +1312,10 @@
Library\ScriptAssemblies\Unity.2D.Aseprite.Common.dll
False
+
+ Library\ScriptAssemblies\Unity.Rider.Editor.dll
+ False
+
Library\ScriptAssemblies\Unity.AI.Navigation.Editor.ConversionSystem.dll
False
@@ -1318,10 +1324,6 @@
Library\ScriptAssemblies\Unity.2D.Aseprite.Editor.dll
False
-
- Library\ScriptAssemblies\Unity.Rider.Editor.dll
- False
-
Library\ScriptAssemblies\Unity.RenderPipelines.ShaderGraph.ShaderGraphLibrary.dll
False
diff --git a/Assets/Data/ScriptableObjects/Tower/Tower1.asset b/Assets/Data/ScriptableObjects/Tower/Tower1.asset
new file mode 100644
index 0000000..30f963d
--- /dev/null
+++ b/Assets/Data/ScriptableObjects/Tower/Tower1.asset
@@ -0,0 +1,52 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!114 &11400000
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 0}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 8c40fef5ebc37b743a3f225c1ca57c32, type: 3}
+ m_Name: Tower1
+ m_EditorClassIdentifier: Assembly-CSharp::Northbound.Data.TowerData
+ buildingName: "\uD0C0\uC6CC"
+ prefab: {fileID: 8512676738329978770, guid: 3f7838db2c2fc424d9bd9a0d243b43be, type: 3}
+ icon: {fileID: 0}
+ width: 3
+ length: 3
+ height: 4
+ placementOffset: {x: 0, y: 0, z: 0}
+ allowRotation: 1
+ requiredWorkAmount: 10
+ workPerInteraction: 10
+ interactionCooldown: 1
+ constructionAnimationTrigger: Build
+ constructionEquipment:
+ socketName: RightHand
+ equipmentPrefab: {fileID: 0}
+ attachOnStart: 1
+ detachOnEnd: 1
+ keepEquipped: 0
+ attachDelay: 0
+ detachDelay: 0
+ maxHealth: 50
+ isIndestructible: 0
+ autoRegenerate: 0
+ regenPerSecond: 1
+ providesVision: 1
+ visionRange: 10
+ id: 1
+ memo: "\uD0C0\uC6CC"
+ mana: 25
+ manpower: 10
+ sizeX: 3
+ sizeY: 3
+ sizeZ: 3
+ maxHp: 50
+ atkRange: 10
+ atkDamage: 5
+ atkIntervalSec: 2
+ modelPath: Assets/Models/building_tower_B_blue.fbx
diff --git a/Assets/Data/ScriptableObjects/Tower/Tower1.asset.meta b/Assets/Data/ScriptableObjects/Tower/Tower1.asset.meta
new file mode 100644
index 0000000..64011f4
--- /dev/null
+++ b/Assets/Data/ScriptableObjects/Tower/Tower1.asset.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 3e2e145df85a3ee4eb615f87efba4554
+NativeFormatImporter:
+ externalObjects: {}
+ mainObjectFileID: 11400000
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Data/ScriptableObjects/Tower/Tower2.asset b/Assets/Data/ScriptableObjects/Tower/Tower2.asset
new file mode 100644
index 0000000..015e69a
--- /dev/null
+++ b/Assets/Data/ScriptableObjects/Tower/Tower2.asset
@@ -0,0 +1,52 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!114 &11400000
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 0}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 8c40fef5ebc37b743a3f225c1ca57c32, type: 3}
+ m_Name: Tower2
+ m_EditorClassIdentifier: Assembly-CSharp::Northbound.Data.TowerData
+ buildingName: "\uBCBD"
+ prefab: {fileID: 3671057791414486316, guid: ae9a9b515e1792a45887f0d967b943d6, type: 3}
+ icon: {fileID: 0}
+ width: 2
+ length: 1
+ height: 4
+ placementOffset: {x: 0, y: 0, z: 0}
+ allowRotation: 1
+ requiredWorkAmount: 5
+ workPerInteraction: 10
+ interactionCooldown: 1
+ constructionAnimationTrigger: Build
+ constructionEquipment:
+ socketName: RightHand
+ equipmentPrefab: {fileID: 0}
+ attachOnStart: 1
+ detachOnEnd: 1
+ keepEquipped: 0
+ attachDelay: 0
+ detachDelay: 0
+ maxHealth: 30
+ isIndestructible: 0
+ autoRegenerate: 0
+ regenPerSecond: 1
+ providesVision: 1
+ visionRange: 0
+ id: 2
+ memo: "\uBCBD"
+ mana: 5
+ manpower: 5
+ sizeX: 2
+ sizeY: 2
+ sizeZ: 1
+ maxHp: 30
+ atkRange: 0
+ atkDamage: 0
+ atkIntervalSec: 0
+ modelPath: Assets/Models/wall_straight.fbx
diff --git a/Assets/Data/ScriptableObjects/Tower/Tower2.asset.meta b/Assets/Data/ScriptableObjects/Tower/Tower2.asset.meta
new file mode 100644
index 0000000..3099776
--- /dev/null
+++ b/Assets/Data/ScriptableObjects/Tower/Tower2.asset.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 03a521eb1160745439ba2d0efeb12f3c
+NativeFormatImporter:
+ externalObjects: {}
+ mainObjectFileID: 11400000
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Data/Scripts/DataClasses/TowerData.cs b/Assets/Data/Scripts/DataClasses/TowerData.cs
index 59b9751..138d7fb 100644
--- a/Assets/Data/Scripts/DataClasses/TowerData.cs
+++ b/Assets/Data/Scripts/DataClasses/TowerData.cs
@@ -3,11 +3,12 @@
using UnityEngine;
using System.Collections.Generic; // 리스트 지원을 위해 추가
+using Northbound;
namespace Northbound.Data
{
[CreateAssetMenu(fileName = "TowerData", menuName = "Northbound/Tower Data")]
- public class TowerData : ScriptableObject
+ public class TowerData : BuildingData
{
[Header("기본 정보")]
/// 고유 ID
@@ -22,6 +23,8 @@ namespace Northbound.Data
public int sizeX;
/// Y 그리드 차지 공간
public int sizeY;
+ /// Z 차지 공간
+ public int sizeZ;
/// 체력
public int maxHp;
/// 사정거리
@@ -30,8 +33,40 @@ namespace Northbound.Data
public int atkDamage;
/// 공격 주기
public float atkIntervalSec;
- /// 프리팹/리소스 경로
- public string prefabPath;
+ /// 모델 경로
+ public string modelPath;
+ private bool fieldsSynced = false;
+
+ private void Awake()
+ {
+ SyncFields();
+ }
+
+ private void SyncFields()
+ {
+ if (fieldsSynced) return;
+ fieldsSynced = true;
+
+ // Map TowerData fields to BuildingData fields
+ if (string.IsNullOrEmpty(base.buildingName))
+ {
+ base.buildingName = memo;
+ }
+ base.width = sizeX;
+ base.length = sizeY;
+ base.height = sizeZ;
+ base.maxHealth = maxHp;
+ base.visionRange = atkRange;
+ base.requiredWorkAmount = manpower;
+ base.workPerInteraction = 10f;
+ base.interactionCooldown =1f;
+ base.providesVision = true;
+ }
+
+ public void EnsureSynced()
+ {
+ SyncFields();
+ }
}
}
\ No newline at end of file
diff --git a/Assets/Data/Templates/TowerTemplate.prefab b/Assets/Data/Templates/TowerTemplate.prefab
new file mode 100644
index 0000000..5c079e2
--- /dev/null
+++ b/Assets/Data/Templates/TowerTemplate.prefab
@@ -0,0 +1,146 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1 &7169488976654481418
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 4800397361066136527}
+ - component: {fileID: 4995981673923607444}
+ - component: {fileID: -5559005308629590716}
+ - component: {fileID: 8546144977850478548}
+ - component: {fileID: -4086363217010332088}
+ - component: {fileID: 4822792797652874450}
+ m_Layer: 0
+ m_Name: TowerTemplate
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &4800397361066136527
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 7169488976654481418}
+ serializedVersion: 2
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 0}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &4995981673923607444
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 7169488976654481418}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3}
+ m_Name:
+ m_EditorClassIdentifier: Unity.Netcode.Runtime::Unity.Netcode.NetworkObject
+ GlobalObjectIdHash: 4259369348
+ InScenePlacedSourceGlobalObjectIdHash: 0
+ DeferredDespawnTick: 0
+ Ownership: 1
+ AlwaysReplicateAsRoot: 0
+ SynchronizeTransform: 1
+ ActiveSceneSynchronization: 0
+ SceneMigrationSynchronization: 0
+ SpawnWithObservers: 1
+ DontDestroyWithOwner: 0
+ AutoObjectParentSync: 1
+ SyncOwnerTransformWhenParented: 1
+ AllowOwnerToParent: 0
+--- !u!65 &-5559005308629590716
+BoxCollider:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 7169488976654481418}
+ m_Material: {fileID: 0}
+ m_IncludeLayers:
+ serializedVersion: 2
+ m_Bits: 0
+ m_ExcludeLayers:
+ serializedVersion: 2
+ m_Bits: 0
+ m_LayerOverridePriority: 0
+ m_IsTrigger: 0
+ m_ProvidesContacts: 0
+ m_Enabled: 1
+ serializedVersion: 3
+ m_Size: {x: 1, y: 2, z: 1}
+ m_Center: {x: 0, y: 1, z: 0}
+--- !u!208 &8546144977850478548
+NavMeshObstacle:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 7169488976654481418}
+ m_Enabled: 1
+ serializedVersion: 3
+ m_Shape: 1
+ m_Extents: {x: 0.5, y: 1, z: 0.5}
+ m_MoveThreshold: 0.1
+ m_Carve: 0
+ m_CarveOnlyStationary: 1
+ m_Center: {x: 0, y: 1, z: 0}
+ m_TimeToStationary: 0.5
+--- !u!114 &-4086363217010332088
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 7169488976654481418}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 0ceedb9b012d848478813136b65738ae, type: 3}
+ m_Name:
+ m_EditorClassIdentifier: Assembly-CSharp::Northbound.Building
+ ShowTopMostFoldoutHeaderGroup: 1
+ buildingData: {fileID: 0}
+ gridPosition: {x: 0, y: 0, z: 0}
+ rotation: 0
+ initialTeam: 1
+ initialOwnerId: 0
+ useInitialOwner: 0
+ showHealthBar: 1
+ healthBarPrefab: {fileID: 0}
+ destroyEffectPrefab: {fileID: 0}
+ damageEffectPrefab: {fileID: 0}
+ effectSpawnPoint: {fileID: 0}
+ showGridBounds: 1
+ gridBoundsColor: {r: 0, g: 1, b: 1, a: 1}
+--- !u!114 &4822792797652874450
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 7169488976654481418}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 760137a2fd0da7f458ac4b0ee7f485d6, type: 3}
+ m_Name:
+ m_EditorClassIdentifier: Assembly-CSharp::Northbound.FogOfWarVisibility
+ showInExploredAreas: 0
+ updateInterval: 0.2
+ renderers: []
+ enableDistantVisibility: 1
+ heightVisibilityMultiplier: 2
+ minHeightForDistantVisibility: 3
+ useExploredMaterial: 0
+ exploredMaterial: {fileID: 0}
diff --git a/Assets/Data/Templates/TowerTemplate.prefab.meta b/Assets/Data/Templates/TowerTemplate.prefab.meta
new file mode 100644
index 0000000..eb72a97
--- /dev/null
+++ b/Assets/Data/Templates/TowerTemplate.prefab.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 5cb8ca5552f975e4c88d418d11d28dbc
+PrefabImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/DefaultNetworkPrefabs.asset b/Assets/DefaultNetworkPrefabs.asset
index eca1e29..ea8eed0 100644
--- a/Assets/DefaultNetworkPrefabs.asset
+++ b/Assets/DefaultNetworkPrefabs.asset
@@ -99,3 +99,18 @@ MonoBehaviour:
SourcePrefabToOverride: {fileID: 0}
SourceHashToOverride: 0
OverridingTargetPrefab: {fileID: 0}
+ - Override: 0
+ Prefab: {fileID: 7169488976654481418, guid: 5cb8ca5552f975e4c88d418d11d28dbc, type: 3}
+ SourcePrefabToOverride: {fileID: 0}
+ SourceHashToOverride: 0
+ OverridingTargetPrefab: {fileID: 0}
+ - Override: 0
+ Prefab: {fileID: 8512676738329978770, guid: 3f7838db2c2fc424d9bd9a0d243b43be, type: 3}
+ SourcePrefabToOverride: {fileID: 0}
+ SourceHashToOverride: 0
+ OverridingTargetPrefab: {fileID: 0}
+ - Override: 0
+ Prefab: {fileID: 3671057791414486316, guid: ae9a9b515e1792a45887f0d967b943d6, type: 3}
+ SourcePrefabToOverride: {fileID: 0}
+ SourceHashToOverride: 0
+ OverridingTargetPrefab: {fileID: 0}
diff --git a/Assets/Models/building_tower_B_blue.fbx b/Assets/Models/building_tower_B_blue.fbx
new file mode 100644
index 0000000..f067591
Binary files /dev/null and b/Assets/Models/building_tower_B_blue.fbx differ
diff --git a/Assets/Models/building_tower_B_blue.fbx.meta b/Assets/Models/building_tower_B_blue.fbx.meta
new file mode 100644
index 0000000..7b7b2c9
--- /dev/null
+++ b/Assets/Models/building_tower_B_blue.fbx.meta
@@ -0,0 +1,110 @@
+fileFormatVersion: 2
+guid: 3dbb43609bf79104da27aad0a7704ed3
+ModelImporter:
+ serializedVersion: 24200
+ internalIDToNameTable: []
+ externalObjects: {}
+ materials:
+ materialImportMode: 2
+ materialName: 0
+ materialSearch: 1
+ materialLocation: 1
+ animations:
+ legacyGenerateAnimations: 4
+ bakeSimulation: 0
+ resampleCurves: 1
+ optimizeGameObjects: 0
+ removeConstantScaleCurves: 0
+ motionNodeName:
+ animationImportErrors:
+ animationImportWarnings:
+ animationRetargetingWarnings:
+ animationDoRetargetingWarnings: 0
+ importAnimatedCustomProperties: 0
+ importConstraints: 0
+ animationCompression: 1
+ animationRotationError: 0.5
+ animationPositionError: 0.5
+ animationScaleError: 0.5
+ animationWrapMode: 0
+ extraExposedTransformPaths: []
+ extraUserProperties: []
+ clipAnimations: []
+ isReadable: 0
+ meshes:
+ lODScreenPercentages: []
+ globalScale: 1
+ meshCompression: 0
+ addColliders: 0
+ useSRGBMaterialColor: 1
+ sortHierarchyByName: 1
+ importPhysicalCameras: 1
+ importVisibility: 1
+ importBlendShapes: 1
+ importCameras: 1
+ importLights: 1
+ nodeNameCollisionStrategy: 1
+ fileIdsGeneration: 2
+ swapUVChannels: 0
+ generateSecondaryUV: 0
+ useFileUnits: 1
+ keepQuads: 0
+ weldVertices: 1
+ bakeAxisConversion: 0
+ preserveHierarchy: 0
+ skinWeightsMode: 0
+ maxBonesPerVertex: 4
+ minBoneWeight: 0.001
+ optimizeBones: 1
+ generateMeshLods: 0
+ meshLodGenerationFlags: 0
+ maximumMeshLod: -1
+ meshOptimizationFlags: -1
+ indexFormat: 0
+ secondaryUVAngleDistortion: 8
+ secondaryUVAreaDistortion: 15.000001
+ secondaryUVHardAngle: 88
+ secondaryUVMarginMethod: 1
+ secondaryUVMinLightmapResolution: 40
+ secondaryUVMinObjectScale: 1
+ secondaryUVPackMargin: 4
+ useFileScale: 1
+ strictVertexDataChecks: 0
+ tangentSpace:
+ normalSmoothAngle: 60
+ normalImportMode: 0
+ tangentImportMode: 3
+ normalCalculationMode: 4
+ legacyComputeAllNormalsFromSmoothingGroupsWhenMeshHasBlendShapes: 0
+ blendShapeNormalImportMode: 1
+ normalSmoothingSource: 0
+ referencedClips: []
+ importAnimation: 1
+ humanDescription:
+ serializedVersion: 3
+ human: []
+ skeleton: []
+ armTwist: 0.5
+ foreArmTwist: 0.5
+ upperLegTwist: 0.5
+ legTwist: 0.5
+ armStretch: 0.05
+ legStretch: 0.05
+ feetSpacing: 0
+ globalScale: 1
+ rootMotionBoneName:
+ hasTranslationDoF: 0
+ hasExtraRoot: 0
+ skeletonHasParents: 1
+ lastHumanDescriptionAvatarSource: {instanceID: 0}
+ autoGenerateAvatarMappingIfUnspecified: 1
+ animationType: 2
+ humanoidOversampling: 1
+ avatarSetup: 0
+ addHumanoidExtraRootOnlyWhenUsingAvatar: 1
+ importBlendShapeDeformPercent: 1
+ remapMaterialsIfMaterialImportModeIsNone: 0
+ additionalBone: 0
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Models/wall_straight.fbx b/Assets/Models/wall_straight.fbx
new file mode 100644
index 0000000..dee0508
Binary files /dev/null and b/Assets/Models/wall_straight.fbx differ
diff --git a/Assets/Models/wall_straight.fbx.meta b/Assets/Models/wall_straight.fbx.meta
new file mode 100644
index 0000000..31cf921
--- /dev/null
+++ b/Assets/Models/wall_straight.fbx.meta
@@ -0,0 +1,110 @@
+fileFormatVersion: 2
+guid: 0204078ba040180418c65c7f09f3bdb2
+ModelImporter:
+ serializedVersion: 24200
+ internalIDToNameTable: []
+ externalObjects: {}
+ materials:
+ materialImportMode: 2
+ materialName: 0
+ materialSearch: 1
+ materialLocation: 1
+ animations:
+ legacyGenerateAnimations: 4
+ bakeSimulation: 0
+ resampleCurves: 1
+ optimizeGameObjects: 0
+ removeConstantScaleCurves: 0
+ motionNodeName:
+ animationImportErrors:
+ animationImportWarnings:
+ animationRetargetingWarnings:
+ animationDoRetargetingWarnings: 0
+ importAnimatedCustomProperties: 0
+ importConstraints: 0
+ animationCompression: 1
+ animationRotationError: 0.5
+ animationPositionError: 0.5
+ animationScaleError: 0.5
+ animationWrapMode: 0
+ extraExposedTransformPaths: []
+ extraUserProperties: []
+ clipAnimations: []
+ isReadable: 0
+ meshes:
+ lODScreenPercentages: []
+ globalScale: 1
+ meshCompression: 0
+ addColliders: 0
+ useSRGBMaterialColor: 1
+ sortHierarchyByName: 1
+ importPhysicalCameras: 1
+ importVisibility: 1
+ importBlendShapes: 1
+ importCameras: 1
+ importLights: 1
+ nodeNameCollisionStrategy: 1
+ fileIdsGeneration: 2
+ swapUVChannels: 0
+ generateSecondaryUV: 0
+ useFileUnits: 1
+ keepQuads: 0
+ weldVertices: 1
+ bakeAxisConversion: 0
+ preserveHierarchy: 0
+ skinWeightsMode: 0
+ maxBonesPerVertex: 4
+ minBoneWeight: 0.001
+ optimizeBones: 1
+ generateMeshLods: 0
+ meshLodGenerationFlags: 0
+ maximumMeshLod: -1
+ meshOptimizationFlags: -1
+ indexFormat: 0
+ secondaryUVAngleDistortion: 8
+ secondaryUVAreaDistortion: 15.000001
+ secondaryUVHardAngle: 88
+ secondaryUVMarginMethod: 1
+ secondaryUVMinLightmapResolution: 40
+ secondaryUVMinObjectScale: 1
+ secondaryUVPackMargin: 4
+ useFileScale: 1
+ strictVertexDataChecks: 0
+ tangentSpace:
+ normalSmoothAngle: 60
+ normalImportMode: 0
+ tangentImportMode: 3
+ normalCalculationMode: 4
+ legacyComputeAllNormalsFromSmoothingGroupsWhenMeshHasBlendShapes: 0
+ blendShapeNormalImportMode: 1
+ normalSmoothingSource: 0
+ referencedClips: []
+ importAnimation: 1
+ humanDescription:
+ serializedVersion: 3
+ human: []
+ skeleton: []
+ armTwist: 0.5
+ foreArmTwist: 0.5
+ upperLegTwist: 0.5
+ legTwist: 0.5
+ armStretch: 0.05
+ legStretch: 0.05
+ feetSpacing: 0
+ globalScale: 1
+ rootMotionBoneName:
+ hasTranslationDoF: 0
+ hasExtraRoot: 0
+ skeletonHasParents: 1
+ lastHumanDescriptionAvatarSource: {instanceID: 0}
+ autoGenerateAvatarMappingIfUnspecified: 1
+ animationType: 2
+ humanoidOversampling: 1
+ avatarSetup: 0
+ addHumanoidExtraRootOnlyWhenUsingAvatar: 1
+ importBlendShapeDeformPercent: 1
+ remapMaterialsIfMaterialImportModeIsNone: 0
+ additionalBone: 0
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Prefabs/Monster/Monster101.prefab b/Assets/Prefabs/Monster/Monster101.prefab
index 4227abb..e34c056 100644
--- a/Assets/Prefabs/Monster/Monster101.prefab
+++ b/Assets/Prefabs/Monster/Monster101.prefab
@@ -1285,7 +1285,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3}
m_Name:
m_EditorClassIdentifier: Unity.Netcode.Runtime::Unity.Netcode.NetworkObject
- GlobalObjectIdHash: 2991918417
+ GlobalObjectIdHash: 209349728
InScenePlacedSourceGlobalObjectIdHash: 0
DeferredDespawnTick: 0
Ownership: 1
diff --git a/Assets/Prefabs/Monster/Monster105.prefab b/Assets/Prefabs/Monster/Monster105.prefab
index cc44787..77039a5 100644
--- a/Assets/Prefabs/Monster/Monster105.prefab
+++ b/Assets/Prefabs/Monster/Monster105.prefab
@@ -964,8 +964,8 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3}
m_Name:
m_EditorClassIdentifier: Unity.Netcode.Runtime::Unity.Netcode.NetworkObject
- GlobalObjectIdHash: 3377967349
- InScenePlacedSourceGlobalObjectIdHash: 2825901034
+ GlobalObjectIdHash: 209349728
+ InScenePlacedSourceGlobalObjectIdHash: 0
DeferredDespawnTick: 0
Ownership: 1
AlwaysReplicateAsRoot: 0
diff --git a/Assets/Prefabs/Player.prefab b/Assets/Prefabs/Player.prefab
index 4f292c3..b6ee481 100644
--- a/Assets/Prefabs/Player.prefab
+++ b/Assets/Prefabs/Player.prefab
@@ -55,7 +55,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3}
m_Name:
m_EditorClassIdentifier: Unity.Netcode.Runtime::Unity.Netcode.NetworkObject
- GlobalObjectIdHash: 4211758632
+ GlobalObjectIdHash: 1360081626
InScenePlacedSourceGlobalObjectIdHash: 4211758632
DeferredDespawnTick: 0
Ownership: 1
diff --git a/Assets/Prefabs/Tower/Tower1.prefab b/Assets/Prefabs/Tower/Tower1.prefab
new file mode 100644
index 0000000..4275411
--- /dev/null
+++ b/Assets/Prefabs/Tower/Tower1.prefab
@@ -0,0 +1,342 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1 &1289760089446993883
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 6062607789191353382}
+ - component: {fileID: 7470654452104141725}
+ - component: {fileID: 6347255101590433954}
+ m_Layer: 0
+ m_Name: building_tower_B_top_blue
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &6062607789191353382
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1289760089446993883}
+ serializedVersion: 2
+ m_LocalRotation: {x: 0, y: -0, z: -0, w: 1}
+ m_LocalPosition: {x: -0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 1872842023463012809}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &7470654452104141725
+MeshFilter:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1289760089446993883}
+ m_Mesh: {fileID: -2975848345017946836, guid: 3dbb43609bf79104da27aad0a7704ed3, type: 3}
+--- !u!23 &6347255101590433954
+MeshRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1289760089446993883}
+ m_Enabled: 1
+ m_CastShadows: 1
+ m_ReceiveShadows: 1
+ m_DynamicOccludee: 1
+ m_StaticShadowCaster: 0
+ m_MotionVectors: 1
+ m_LightProbeUsage: 1
+ m_ReflectionProbeUsage: 1
+ m_RayTracingMode: 2
+ m_RayTraceProcedural: 0
+ m_RayTracingAccelStructBuildFlagsOverride: 0
+ m_RayTracingAccelStructBuildFlags: 1
+ m_SmallMeshCulling: 1
+ m_ForceMeshLod: -1
+ m_MeshLodSelectionBias: 0
+ m_RenderingLayerMask: 1
+ m_RendererPriority: 0
+ m_Materials:
+ - {fileID: -6545801570897300931, guid: 3dbb43609bf79104da27aad0a7704ed3, type: 3}
+ m_StaticBatchInfo:
+ firstSubMesh: 0
+ subMeshCount: 0
+ m_StaticBatchRoot: {fileID: 0}
+ m_ProbeAnchor: {fileID: 0}
+ m_LightProbeVolumeOverride: {fileID: 0}
+ m_ScaleInLightmap: 1
+ m_ReceiveGI: 1
+ m_PreserveUVs: 0
+ m_IgnoreNormalsForChartDetection: 0
+ m_ImportantGI: 0
+ m_StitchLightmapSeams: 1
+ m_SelectedEditorRenderState: 3
+ m_MinimumChartSize: 4
+ m_AutoUVMaxDistance: 0.5
+ m_AutoUVMaxAngle: 89
+ m_LightmapParameters: {fileID: 0}
+ m_GlobalIlluminationMeshLod: 0
+ m_SortingLayerID: 0
+ m_SortingLayer: 0
+ m_SortingOrder: 0
+ m_MaskInteraction: 0
+ m_AdditionalVertexStreams: {fileID: 0}
+--- !u!1 &7757785777080043176
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 1872842023463012809}
+ - component: {fileID: 7668707206409267505}
+ - component: {fileID: 2906673092929627781}
+ m_Layer: 0
+ m_Name: Model
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &1872842023463012809
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 7757785777080043176}
+ serializedVersion: 2
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 3, y: 3, z: 3}
+ m_ConstrainProportionsScale: 0
+ m_Children:
+ - {fileID: 6062607789191353382}
+ m_Father: {fileID: 5446357005769341236}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &7668707206409267505
+MeshFilter:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 7757785777080043176}
+ m_Mesh: {fileID: -1867165846391908481, guid: 3dbb43609bf79104da27aad0a7704ed3, type: 3}
+--- !u!23 &2906673092929627781
+MeshRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 7757785777080043176}
+ m_Enabled: 1
+ m_CastShadows: 1
+ m_ReceiveShadows: 1
+ m_DynamicOccludee: 1
+ m_StaticShadowCaster: 0
+ m_MotionVectors: 1
+ m_LightProbeUsage: 1
+ m_ReflectionProbeUsage: 1
+ m_RayTracingMode: 2
+ m_RayTraceProcedural: 0
+ m_RayTracingAccelStructBuildFlagsOverride: 0
+ m_RayTracingAccelStructBuildFlags: 1
+ m_SmallMeshCulling: 1
+ m_ForceMeshLod: -1
+ m_MeshLodSelectionBias: 0
+ m_RenderingLayerMask: 1
+ m_RendererPriority: 0
+ m_Materials:
+ - {fileID: -6545801570897300931, guid: 3dbb43609bf79104da27aad0a7704ed3, type: 3}
+ m_StaticBatchInfo:
+ firstSubMesh: 0
+ subMeshCount: 0
+ m_StaticBatchRoot: {fileID: 0}
+ m_ProbeAnchor: {fileID: 0}
+ m_LightProbeVolumeOverride: {fileID: 0}
+ m_ScaleInLightmap: 1
+ m_ReceiveGI: 1
+ m_PreserveUVs: 0
+ m_IgnoreNormalsForChartDetection: 0
+ m_ImportantGI: 0
+ m_StitchLightmapSeams: 1
+ m_SelectedEditorRenderState: 3
+ m_MinimumChartSize: 4
+ m_AutoUVMaxDistance: 0.5
+ m_AutoUVMaxAngle: 89
+ m_LightmapParameters: {fileID: 0}
+ m_GlobalIlluminationMeshLod: 0
+ m_SortingLayerID: 0
+ m_SortingLayer: 0
+ m_SortingOrder: 0
+ m_MaskInteraction: 0
+ m_AdditionalVertexStreams: {fileID: 0}
+--- !u!1 &8512676738329978770
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 5446357005769341236}
+ - component: {fileID: 6253727954874418700}
+ - component: {fileID: 1804512144580361968}
+ - component: {fileID: 100877884298911200}
+ - component: {fileID: 8485093670801034058}
+ - component: {fileID: 7262612124217315611}
+ - component: {fileID: 3089566480349729}
+ m_Layer: 0
+ m_Name: Tower1
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &5446357005769341236
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 8512676738329978770}
+ serializedVersion: 2
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children:
+ - {fileID: 1872842023463012809}
+ m_Father: {fileID: 0}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &6253727954874418700
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 8512676738329978770}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3}
+ m_Name:
+ m_EditorClassIdentifier: Unity.Netcode.Runtime::Unity.Netcode.NetworkObject
+ GlobalObjectIdHash: 4259369348
+ InScenePlacedSourceGlobalObjectIdHash: 0
+ DeferredDespawnTick: 0
+ Ownership: 1
+ AlwaysReplicateAsRoot: 0
+ SynchronizeTransform: 1
+ ActiveSceneSynchronization: 0
+ SceneMigrationSynchronization: 0
+ SpawnWithObservers: 1
+ DontDestroyWithOwner: 0
+ AutoObjectParentSync: 1
+ SyncOwnerTransformWhenParented: 1
+ AllowOwnerToParent: 0
+--- !u!65 &1804512144580361968
+BoxCollider:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 8512676738329978770}
+ m_Material: {fileID: 0}
+ m_IncludeLayers:
+ serializedVersion: 2
+ m_Bits: 0
+ m_ExcludeLayers:
+ serializedVersion: 2
+ m_Bits: 0
+ m_LayerOverridePriority: 0
+ m_IsTrigger: 0
+ m_ProvidesContacts: 0
+ m_Enabled: 1
+ serializedVersion: 3
+ m_Size: {x: 3, y: 3, z: 3}
+ m_Center: {x: 0, y: 1.5, z: 0}
+--- !u!208 &100877884298911200
+NavMeshObstacle:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 8512676738329978770}
+ m_Enabled: 1
+ serializedVersion: 3
+ m_Shape: 1
+ m_Extents: {x: 1.5, y: 1.5, z: 1.5}
+ m_MoveThreshold: 0.1
+ m_Carve: 0
+ m_CarveOnlyStationary: 1
+ m_Center: {x: 0, y: 1.5, z: 0}
+ m_TimeToStationary: 0.5
+--- !u!114 &8485093670801034058
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 8512676738329978770}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 0ceedb9b012d848478813136b65738ae, type: 3}
+ m_Name:
+ m_EditorClassIdentifier: Assembly-CSharp::Northbound.Building
+ ShowTopMostFoldoutHeaderGroup: 1
+ buildingData: {fileID: 0}
+ gridPosition: {x: 0, y: 0, z: 0}
+ rotation: 0
+ initialTeam: 1
+ initialOwnerId: 0
+ useInitialOwner: 0
+ showHealthBar: 1
+ healthBarPrefab: {fileID: 0}
+ destroyEffectPrefab: {fileID: 0}
+ damageEffectPrefab: {fileID: 0}
+ effectSpawnPoint: {fileID: 0}
+ showGridBounds: 1
+ gridBoundsColor: {r: 0, g: 1, b: 1, a: 1}
+--- !u!114 &7262612124217315611
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 8512676738329978770}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 760137a2fd0da7f458ac4b0ee7f485d6, type: 3}
+ m_Name:
+ m_EditorClassIdentifier: Assembly-CSharp::Northbound.FogOfWarVisibility
+ showInExploredAreas: 0
+ updateInterval: 0.2
+ renderers: []
+ enableDistantVisibility: 1
+ heightVisibilityMultiplier: 2
+ minHeightForDistantVisibility: 3
+ useExploredMaterial: 0
+ exploredMaterial: {fileID: 0}
+--- !u!114 &3089566480349729
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 8512676738329978770}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 56c4536effc49fe47af593bf9d17e979, type: 3}
+ m_Name:
+ m_EditorClassIdentifier: Assembly-CSharp::Northbound.TowerDataComponent
+ towerData: {fileID: 11400000, guid: 3e2e145df85a3ee4eb615f87efba4554, type: 2}
diff --git a/Assets/Prefabs/Tower/Tower1.prefab.meta b/Assets/Prefabs/Tower/Tower1.prefab.meta
new file mode 100644
index 0000000..1abb589
--- /dev/null
+++ b/Assets/Prefabs/Tower/Tower1.prefab.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 3f7838db2c2fc424d9bd9a0d243b43be
+PrefabImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Prefabs/Tower/Tower2.prefab b/Assets/Prefabs/Tower/Tower2.prefab
new file mode 100644
index 0000000..965619e
--- /dev/null
+++ b/Assets/Prefabs/Tower/Tower2.prefab
@@ -0,0 +1,251 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1 &3671057791414486316
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 1657799771882240}
+ - component: {fileID: 3850164934046790335}
+ - component: {fileID: 7724449748843190139}
+ - component: {fileID: 2615519446934682856}
+ - component: {fileID: 3203720634638459019}
+ - component: {fileID: 3906797260079127802}
+ - component: {fileID: 3692219876976097854}
+ m_Layer: 0
+ m_Name: Tower2
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &1657799771882240
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 3671057791414486316}
+ serializedVersion: 2
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children:
+ - {fileID: 8553397989698296005}
+ m_Father: {fileID: 0}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &3850164934046790335
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 3671057791414486316}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3}
+ m_Name:
+ m_EditorClassIdentifier: Unity.Netcode.Runtime::Unity.Netcode.NetworkObject
+ GlobalObjectIdHash: 4259369348
+ InScenePlacedSourceGlobalObjectIdHash: 0
+ DeferredDespawnTick: 0
+ Ownership: 1
+ AlwaysReplicateAsRoot: 0
+ SynchronizeTransform: 1
+ ActiveSceneSynchronization: 0
+ SceneMigrationSynchronization: 0
+ SpawnWithObservers: 1
+ DontDestroyWithOwner: 0
+ AutoObjectParentSync: 1
+ SyncOwnerTransformWhenParented: 1
+ AllowOwnerToParent: 0
+--- !u!65 &7724449748843190139
+BoxCollider:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 3671057791414486316}
+ m_Material: {fileID: 0}
+ m_IncludeLayers:
+ serializedVersion: 2
+ m_Bits: 0
+ m_ExcludeLayers:
+ serializedVersion: 2
+ m_Bits: 0
+ m_LayerOverridePriority: 0
+ m_IsTrigger: 0
+ m_ProvidesContacts: 0
+ m_Enabled: 1
+ serializedVersion: 3
+ m_Size: {x: 2, y: 1, z: 2}
+ m_Center: {x: 0, y: 0.5, z: 0}
+--- !u!208 &2615519446934682856
+NavMeshObstacle:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 3671057791414486316}
+ m_Enabled: 1
+ serializedVersion: 3
+ m_Shape: 1
+ m_Extents: {x: 1, y: 0.5, z: 1}
+ m_MoveThreshold: 0.1
+ m_Carve: 0
+ m_CarveOnlyStationary: 1
+ m_Center: {x: 0, y: 0.5, z: 0}
+ m_TimeToStationary: 0.5
+--- !u!114 &3203720634638459019
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 3671057791414486316}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 0ceedb9b012d848478813136b65738ae, type: 3}
+ m_Name:
+ m_EditorClassIdentifier: Assembly-CSharp::Northbound.Building
+ ShowTopMostFoldoutHeaderGroup: 1
+ buildingData: {fileID: 0}
+ gridPosition: {x: 0, y: 0, z: 0}
+ rotation: 0
+ initialTeam: 1
+ initialOwnerId: 0
+ useInitialOwner: 0
+ showHealthBar: 1
+ healthBarPrefab: {fileID: 0}
+ destroyEffectPrefab: {fileID: 0}
+ damageEffectPrefab: {fileID: 0}
+ effectSpawnPoint: {fileID: 0}
+ showGridBounds: 1
+ gridBoundsColor: {r: 0, g: 1, b: 1, a: 1}
+--- !u!114 &3906797260079127802
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 3671057791414486316}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 760137a2fd0da7f458ac4b0ee7f485d6, type: 3}
+ m_Name:
+ m_EditorClassIdentifier: Assembly-CSharp::Northbound.FogOfWarVisibility
+ showInExploredAreas: 0
+ updateInterval: 0.2
+ renderers: []
+ enableDistantVisibility: 1
+ heightVisibilityMultiplier: 2
+ minHeightForDistantVisibility: 3
+ useExploredMaterial: 0
+ exploredMaterial: {fileID: 0}
+--- !u!114 &3692219876976097854
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 3671057791414486316}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 56c4536effc49fe47af593bf9d17e979, type: 3}
+ m_Name:
+ m_EditorClassIdentifier: Assembly-CSharp::Northbound.TowerDataComponent
+ towerData: {fileID: 11400000, guid: 03a521eb1160745439ba2d0efeb12f3c, type: 2}
+--- !u!1 &8947776510381915047
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 8553397989698296005}
+ - component: {fileID: 5020570213282043347}
+ - component: {fileID: 3242347197691888434}
+ m_Layer: 0
+ m_Name: Model
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &8553397989698296005
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 8947776510381915047}
+ serializedVersion: 2
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 2, y: 1, z: 2}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 1657799771882240}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &5020570213282043347
+MeshFilter:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 8947776510381915047}
+ m_Mesh: {fileID: -4423419886561025764, guid: 0204078ba040180418c65c7f09f3bdb2, type: 3}
+--- !u!23 &3242347197691888434
+MeshRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 8947776510381915047}
+ m_Enabled: 1
+ m_CastShadows: 1
+ m_ReceiveShadows: 1
+ m_DynamicOccludee: 1
+ m_StaticShadowCaster: 0
+ m_MotionVectors: 1
+ m_LightProbeUsage: 1
+ m_ReflectionProbeUsage: 1
+ m_RayTracingMode: 2
+ m_RayTraceProcedural: 0
+ m_RayTracingAccelStructBuildFlagsOverride: 0
+ m_RayTracingAccelStructBuildFlags: 1
+ m_SmallMeshCulling: 1
+ m_ForceMeshLod: -1
+ m_MeshLodSelectionBias: 0
+ m_RenderingLayerMask: 1
+ m_RendererPriority: 0
+ m_Materials:
+ - {fileID: -6545801570897300931, guid: 0204078ba040180418c65c7f09f3bdb2, type: 3}
+ m_StaticBatchInfo:
+ firstSubMesh: 0
+ subMeshCount: 0
+ m_StaticBatchRoot: {fileID: 0}
+ m_ProbeAnchor: {fileID: 0}
+ m_LightProbeVolumeOverride: {fileID: 0}
+ m_ScaleInLightmap: 1
+ m_ReceiveGI: 1
+ m_PreserveUVs: 0
+ m_IgnoreNormalsForChartDetection: 0
+ m_ImportantGI: 0
+ m_StitchLightmapSeams: 1
+ m_SelectedEditorRenderState: 3
+ m_MinimumChartSize: 4
+ m_AutoUVMaxDistance: 0.5
+ m_AutoUVMaxAngle: 89
+ m_LightmapParameters: {fileID: 0}
+ m_GlobalIlluminationMeshLod: 0
+ m_SortingLayerID: 0
+ m_SortingLayer: 0
+ m_SortingOrder: 0
+ m_MaskInteraction: 0
+ m_AdditionalVertexStreams: {fileID: 0}
diff --git a/Assets/Prefabs/Tower/Tower2.prefab.meta b/Assets/Prefabs/Tower/Tower2.prefab.meta
new file mode 100644
index 0000000..56bc1b7
--- /dev/null
+++ b/Assets/Prefabs/Tower/Tower2.prefab.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: ae9a9b515e1792a45887f0d967b943d6
+PrefabImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scenes/GameMain.unity b/Assets/Scenes/GameMain.unity
index ccf40cd..4a60462 100644
--- a/Assets/Scenes/GameMain.unity
+++ b/Assets/Scenes/GameMain.unity
@@ -1712,6 +1712,7 @@ MonoBehaviour:
enableHeightBlocking: 1
viewerEyeHeight: 1.5
minBlockingHeight: 2
+ disableInEditor: 1
--- !u!4 &946527919
Transform:
m_ObjectHideFlags: 0
@@ -1962,16 +1963,28 @@ PrefabInstance:
objectReference: {fileID: 0}
- target: {fileID: 6859825394817103090, guid: 11e3760dda2c0164abf759c18d918893, type: 3}
propertyPath: monsterEntries.Array.size
- value: 0
+ value: 5
objectReference: {fileID: 0}
- target: {fileID: 6859825394817103090, guid: 11e3760dda2c0164abf759c18d918893, type: 3}
propertyPath: monsterEntries.Array.data[0].prefab
value:
- objectReference: {fileID: 8774623643640324048, guid: c92c9f1bf0ae6364f85409ecdc4aeaf3, type: 3}
+ objectReference: {fileID: 7146441246397375875, guid: eccdace7a7b21a446891da5739b1549f, type: 3}
- target: {fileID: 6859825394817103090, guid: 11e3760dda2c0164abf759c18d918893, type: 3}
propertyPath: monsterEntries.Array.data[1].prefab
value:
- objectReference: {fileID: 5176576196123937037, guid: 951b69041a2ace947afd721fefb3eaba, type: 3}
+ objectReference: {fileID: 9065146123892903723, guid: 4ee9b364dfe9d0949a23374e660d3e13, type: 3}
+ - target: {fileID: 6859825394817103090, guid: 11e3760dda2c0164abf759c18d918893, type: 3}
+ propertyPath: monsterEntries.Array.data[2].prefab
+ value:
+ objectReference: {fileID: 2347008446492047424, guid: 9cac250ebe8d420469bd4da3ab8cfd86, type: 3}
+ - target: {fileID: 6859825394817103090, guid: 11e3760dda2c0164abf759c18d918893, type: 3}
+ propertyPath: monsterEntries.Array.data[3].prefab
+ value:
+ objectReference: {fileID: 4973514444862586119, guid: ff2924ffa397a8e44925625790831d25, type: 3}
+ - target: {fileID: 6859825394817103090, guid: 11e3760dda2c0164abf759c18d918893, type: 3}
+ propertyPath: monsterEntries.Array.data[4].prefab
+ value:
+ objectReference: {fileID: 5710477988932221578, guid: e8d09b814275ad745a427433c28dd53f, type: 3}
- target: {fileID: 6859825394817103090, guid: 11e3760dda2c0164abf759c18d918893, type: 3}
propertyPath: monsterEntries.Array.data[0].monsterData
value:
@@ -3315,16 +3328,28 @@ PrefabInstance:
objectReference: {fileID: 0}
- target: {fileID: 6859825394817103090, guid: 11e3760dda2c0164abf759c18d918893, type: 3}
propertyPath: monsterEntries.Array.size
- value: 0
+ value: 5
objectReference: {fileID: 0}
- target: {fileID: 6859825394817103090, guid: 11e3760dda2c0164abf759c18d918893, type: 3}
propertyPath: monsterEntries.Array.data[0].prefab
value:
- objectReference: {fileID: 8774623643640324048, guid: c92c9f1bf0ae6364f85409ecdc4aeaf3, type: 3}
+ objectReference: {fileID: 7146441246397375875, guid: eccdace7a7b21a446891da5739b1549f, type: 3}
- target: {fileID: 6859825394817103090, guid: 11e3760dda2c0164abf759c18d918893, type: 3}
propertyPath: monsterEntries.Array.data[1].prefab
value:
- objectReference: {fileID: 5176576196123937037, guid: 951b69041a2ace947afd721fefb3eaba, type: 3}
+ objectReference: {fileID: 9065146123892903723, guid: 4ee9b364dfe9d0949a23374e660d3e13, type: 3}
+ - target: {fileID: 6859825394817103090, guid: 11e3760dda2c0164abf759c18d918893, type: 3}
+ propertyPath: monsterEntries.Array.data[2].prefab
+ value:
+ objectReference: {fileID: 2347008446492047424, guid: 9cac250ebe8d420469bd4da3ab8cfd86, type: 3}
+ - target: {fileID: 6859825394817103090, guid: 11e3760dda2c0164abf759c18d918893, type: 3}
+ propertyPath: monsterEntries.Array.data[3].prefab
+ value:
+ objectReference: {fileID: 4973514444862586119, guid: ff2924ffa397a8e44925625790831d25, type: 3}
+ - target: {fileID: 6859825394817103090, guid: 11e3760dda2c0164abf759c18d918893, type: 3}
+ propertyPath: monsterEntries.Array.data[4].prefab
+ value:
+ objectReference: {fileID: 5710477988932221578, guid: e8d09b814275ad745a427433c28dd53f, type: 3}
- target: {fileID: 6859825394817103090, guid: 11e3760dda2c0164abf759c18d918893, type: 3}
propertyPath: monsterEntries.Array.data[0].monsterData
value:
diff --git a/Assets/ScriptableObjects.meta b/Assets/ScriptableObjects.meta
new file mode 100644
index 0000000..1160584
--- /dev/null
+++ b/Assets/ScriptableObjects.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: b5e2a173b42c98746a251ef1dcb4f1a4
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ScriptableObjects/Monster.meta b/Assets/ScriptableObjects/Monster.meta
new file mode 100644
index 0000000..6e990d9
--- /dev/null
+++ b/Assets/ScriptableObjects/Monster.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: def17a694f320ed4a80ff136df2b01b7
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ScriptableObjects/Tower.meta b/Assets/ScriptableObjects/Tower.meta
new file mode 100644
index 0000000..8d82dc8
--- /dev/null
+++ b/Assets/ScriptableObjects/Tower.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 32bffeae31a205549b9fb32d3674b199
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/BuildingPlacement.cs b/Assets/Scripts/BuildingPlacement.cs
index 8022b63..7d6ba65 100644
--- a/Assets/Scripts/BuildingPlacement.cs
+++ b/Assets/Scripts/BuildingPlacement.cs
@@ -189,6 +189,13 @@ namespace Northbound
}
Debug.Log($"[BuildingPlacement] 건설 모드 {(isBuildModeActive ? "활성화" : "비활성화")}");
+ Debug.Log($"[BuildingPlacement] Preview object: {(previewObject != null ? "CREATED" : "NULL")}");
+ if (previewObject != null)
+ {
+ Debug.Log($"[BuildingPlacement] Preview position: {previewObject.transform.position}");
+ Debug.Log($"[BuildingPlacement] Preview scale: {previewObject.transform.localScale}");
+ Debug.Log($"[BuildingPlacement] Preview active: {previewObject.activeSelf}");
+ }
}
///
@@ -231,6 +238,10 @@ namespace Northbound
return;
}
+ Debug.Log($"[BuildingPlacement] Creating preview...");
+ Debug.Log($"[BuildingPlacement] BuildingManager.availableBuildings.Count: {BuildingManager.Instance.availableBuildings.Count}");
+ Debug.Log($"[BuildingPlacement] selectedBuildingIndex: {selectedBuildingIndex}");
+
if (selectedBuildingIndex < 0 || selectedBuildingIndex >= BuildingManager.Instance.availableBuildings.Count)
{
Debug.LogWarning($"[BuildingPlacement] 유효하지 않은 건물 인덱스: {selectedBuildingIndex}");
@@ -238,12 +249,21 @@ namespace Northbound
}
BuildingData data = BuildingManager.Instance.availableBuildings[selectedBuildingIndex];
- if (data == null || data.prefab == null)
+ if (data == null)
{
- Debug.LogWarning("[BuildingPlacement] BuildingData 또는 Prefab이 없습니다.");
+ Debug.LogError($"[BuildingPlacement] BuildingData is NULL at index {selectedBuildingIndex}");
return;
}
+ if (data.prefab == null)
+ {
+ Debug.LogError($"[BuildingPlacement] BuildingData.prefab is NULL at index {selectedBuildingIndex}. Run 'Northbound > Diagnose Tower System'");
+ return;
+ }
+
+ Debug.Log($"[BuildingPlacement] BuildingData: {data.buildingName}, Prefab: {data.prefab.name}");
+ Debug.Log($"[BuildingPlacement] Prefab scale: {data.prefab.transform.localScale}");
+
// 완성 건물 프리팹으로 프리뷰 생성 (사용자가 완성 모습을 볼 수 있도록)
previewObject = Instantiate(data.prefab);
@@ -296,11 +316,17 @@ namespace Northbound
if (previewObject == null || BuildingManager.Instance == null)
return;
+ if (selectedBuildingIndex < 0 || selectedBuildingIndex >= BuildingManager.Instance.availableBuildings.Count)
+ return;
+
+ BuildingData data = BuildingManager.Instance.availableBuildings[selectedBuildingIndex];
+ if (data == null || data.prefab == null)
+ return;
+
Ray ray = Camera.main.ScreenPointToRay(Mouse.current.position.ReadValue());
if (Physics.Raycast(ray, out RaycastHit hit, maxPlacementDistance, groundLayer))
{
- BuildingData data = BuildingManager.Instance.availableBuildings[selectedBuildingIndex];
// Check if placement is valid
bool isValid = BuildingManager.Instance.IsValidPlacement(data, hit.point, currentRotation, out Vector3 snappedPosition);
@@ -412,13 +438,24 @@ namespace Northbound
{
if (BuildingManager.Instance == null) return;
+ if (selectedBuildingIndex < 0 || selectedBuildingIndex >= BuildingManager.Instance.availableBuildings.Count)
+ {
+ Debug.LogWarning($"[BuildingPlacement] Invalid building index: {selectedBuildingIndex}");
+ return;
+ }
+
+ BuildingData data = BuildingManager.Instance.availableBuildings[selectedBuildingIndex];
+ if (data == null || data.prefab == null)
+ {
+ Debug.LogError($"[BuildingPlacement] BuildingData or prefab is null at index {selectedBuildingIndex}. Please run 'Northbound > Populate Towers from Prefabs' and update BuildingManager.");
+ return;
+ }
+
Ray ray = Camera.main.ScreenPointToRay(Mouse.current.position.ReadValue());
if (!Physics.Raycast(ray, out RaycastHit hit, maxPlacementDistance, groundLayer))
{
return;
}
-
- BuildingData data = BuildingManager.Instance.availableBuildings[selectedBuildingIndex];
// 드래그 영역 계산
Vector3 dragEndPosition = hit.point;
diff --git a/Assets/Scripts/BuildingQuickslotUI.cs b/Assets/Scripts/BuildingQuickslotUI.cs
index e4211e8..7658d39 100644
--- a/Assets/Scripts/BuildingQuickslotUI.cs
+++ b/Assets/Scripts/BuildingQuickslotUI.cs
@@ -99,6 +99,8 @@ namespace Northbound
Debug.LogWarning($"[BuildingQuickslotUI] QuickSlot{i + 1} 액션이 null입니다. Input Actions 에셋을 확인하세요.");
}
}
+
+ Debug.Log($"[BuildingQuickslotUI] {_quickslotActions.Length}개의 퀵슬롯 액션 초기화됨 (최대 8개 키 바인딩)");
}
///
@@ -155,6 +157,10 @@ namespace Northbound
{
SelectBuilding(slotIndex);
}
+ else if (slotIndex >= slotButtons.Count && quickslotPanel != null && quickslotPanel.activeSelf)
+ {
+ Debug.LogWarning($"[BuildingQuickslotUI] 슬롯 {slotIndex + 1}은 존재하지 않습니다. 마우스로 클릭하여 선택하세요.");
+ }
}
///
@@ -176,11 +182,10 @@ namespace Northbound
}
slotButtons.Clear();
- // 건물 목록으로 슬롯 생성 (최대 maxSlots개)
+ // 모든 건물 슬롯 생성 (제한 없음)
var buildings = BuildingManager.Instance.availableBuildings;
- int slotCount = Mathf.Min(buildings.Count, maxSlots);
- for (int i = 0; i < slotCount; i++)
+ for (int i = 0; i < buildings.Count; i++)
{
CreateSlot(buildings[i], i);
}
diff --git a/Assets/Scripts/Editor/CSVToSOImporter.cs b/Assets/Scripts/Editor/CSVToSOImporter.cs
new file mode 100644
index 0000000..4bd8d83
--- /dev/null
+++ b/Assets/Scripts/Editor/CSVToSOImporter.cs
@@ -0,0 +1,353 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using UnityEditor;
+using UnityEditor.SceneManagement;
+using UnityEngine;
+using Northbound.Data;
+
+namespace Northbound.Editor
+{
+ public class CSVToSOImporter : EditorWindow
+ {
+ private static Dictionary prefabSetups;
+
+ [MenuItem("Tools/Data/Import All CSV")]
+ public static void ImportAllCSV()
+ {
+ InitializePrefabSetups();
+ string dataPath = Path.Combine(Application.dataPath, "../GameData");
+
+ if (!Directory.Exists(dataPath))
+ {
+ Debug.LogError("[CSVToSOImporter] GameData folder not found!");
+ return;
+ }
+
+ string[] csvFiles = Directory.GetFiles(dataPath, "*.csv");
+ int successCount = 0;
+ int failCount = 0;
+ bool towerImported = false;
+
+ foreach (string csvFile in csvFiles)
+ {
+ string fileName = Path.GetFileNameWithoutExtension(csvFile);
+ if (ImportCSV(fileName))
+ {
+ successCount++;
+ if (fileName == "Tower")
+ towerImported = true;
+ }
+ else
+ {
+ failCount++;
+ }
+ }
+
+ Debug.Log($"[CSVToSOImporter] Import complete: {successCount} succeeded, {failCount} failed");
+
+ if (towerImported)
+ {
+ AutoConfigureBuildingManager();
+ }
+
+ AssetDatabase.Refresh();
+ }
+
+ private static void InitializePrefabSetups()
+ {
+ prefabSetups = new Dictionary
+ {
+ { "Monster", new MonsterPrefabSetup() },
+ { "Tower", new TowerPrefabSetup() }
+ };
+ }
+
+ private static bool ImportCSV(string typeName)
+ {
+ string csvPath = Path.Combine(Application.dataPath, "../GameData", $"{typeName}.csv");
+
+ if (!File.Exists(csvPath))
+ {
+ Debug.LogWarning($"[CSVToSOImporter] CSV file not found: {csvPath}");
+ return false;
+ }
+
+ if (!prefabSetups.ContainsKey(typeName))
+ {
+ Debug.LogWarning($"[CSVToSOImporter] No prefab setup found for type: {typeName}");
+ return false;
+ }
+
+ IPrefabSetup prefabSetup = prefabSetups[typeName];
+ string templateName = prefabSetup.GetTemplateName();
+ string templatePath = $"Assets/Data/Templates/{templateName}.prefab";
+
+ GameObject template = AssetDatabase.LoadAssetAtPath(templatePath);
+ if (template == null)
+ {
+ Debug.LogError($"[CSVToSOImporter] Template not found: {templatePath}");
+ return false;
+ }
+
+ string[] csvLines = File.ReadAllLines(csvPath);
+ if (csvLines.Length < 2)
+ {
+ Debug.LogWarning($"[CSVToSOImporter] CSV file is empty or has no data: {csvPath}");
+ return false;
+ }
+
+ string[] headers = ParseCSVLine(csvLines[0]);
+ int successCount = 0;
+
+ for (int i = 1; i < csvLines.Length; i++)
+ {
+ if (string.IsNullOrWhiteSpace(csvLines[i]))
+ continue;
+
+ string[] values = ParseCSVLine(csvLines[i]);
+ if (CreatePrefabFromRow(typeName, headers, values, template, prefabSetup))
+ {
+ successCount++;
+ }
+ }
+
+ Debug.Log($"[CSVToSOImporter] {typeName}: {successCount} prefabs created/updated");
+
+ // If towers were imported, auto-configure BuildingManager
+ // TowerData now extends BuildingData, so it can be used directly!
+ if (typeName == "Tower")
+ {
+ Debug.Log($"[CSVToSOImporter] Tower import complete, TowerData extends BuildingData now!");
+ }
+
+ return true;
+ }
+
+ private static bool CreatePrefabFromRow(string typeName, string[] headers, string[] values, GameObject template, IPrefabSetup prefabSetup)
+ {
+ string soPath = $"Assets/Data/ScriptableObjects/{typeName}";
+ Directory.CreateDirectory(Path.Combine(Application.dataPath, $"ScriptableObjects/{typeName}"));
+
+ int id = 0;
+ for (int i = 0; i < headers.Length && i < values.Length; i++)
+ {
+ if (headers[i].ToLower() == "id")
+ {
+ int.TryParse(values[i], out id);
+ break;
+ }
+ }
+
+ if (id == 0)
+ {
+ Debug.LogWarning($"[CSVToSOImporter] No valid ID found in row");
+ return false;
+ }
+
+ ScriptableObject data;
+ string soAssetPath = $"{soPath}/{typeName}{id}.asset";
+ data = AssetDatabase.LoadAssetAtPath(soAssetPath);
+
+ bool isNew = false;
+ if (data == null)
+ {
+ data = CreateInstance(typeName + "Data");
+ isNew = true;
+ }
+
+ for (int i = 0; i < headers.Length && i < values.Length; i++)
+ {
+ string fieldName = CSVToCamelCase(headers[i]);
+ var field = data.GetType().GetField(fieldName);
+ if (field != null)
+ {
+ try
+ {
+ object value = ParseValue(values[i], field.FieldType);
+ field.SetValue(data, value);
+ }
+ catch (System.Exception e)
+ {
+ Debug.LogWarning($"[CSVToSOImporter] Failed to set {fieldName}: {e.Message}");
+ }
+ }
+ }
+
+ string prefabPath = $"Assets/Prefabs/{typeName}/{typeName}{id}.prefab";
+ Directory.CreateDirectory(Path.Combine(Application.dataPath, $"Prefabs/{typeName}"));
+
+ GameObject prefabInstance = GameObject.Instantiate(template);
+ prefabInstance.name = $"{typeName}{id}";
+
+ prefabSetup.SetupPrefab(prefabInstance, data);
+
+ GameObject prefabObj = PrefabUtility.SaveAsPrefabAsset(prefabInstance, prefabPath);
+ GameObject.DestroyImmediate(prefabInstance);
+
+ // Now set the prefab reference on data
+ if (data is BuildingData buildingData)
+ {
+ buildingData.prefab = prefabObj;
+ Debug.Log($"[CSVToSOImporter] Set prefab reference: {buildingData.name} -> {prefabObj.name}");
+ }
+
+ // Save data asset
+ if (isNew)
+ {
+ AssetDatabase.CreateAsset(data, soAssetPath);
+ }
+ else
+ {
+ EditorUtility.SetDirty(data);
+ }
+
+ // Force save assets to disk
+ AssetDatabase.SaveAssets();
+ AssetDatabase.Refresh();
+
+ return true;
+ }
+
+ private static void AutoConfigureBuildingManager()
+ {
+ BuildingManager buildingManager = GameObject.FindObjectOfType();
+
+ if (buildingManager == null)
+ {
+ Debug.LogError("[CSVToSOImporter] BuildingManager not found in scene! Please add a BuildingManager component to a GameObject in your scene.");
+ return;
+ }
+
+ // Load TowerData (which extends BuildingData)
+ string[] towerDataGuids = AssetDatabase.FindAssets("t:TowerData", new[] { "Assets/Data/ScriptableObjects" });
+ List allTowers = new List();
+
+ Debug.Log($"[CSVToSOImporter] Found {towerDataGuids.Length} TowerData assets");
+
+ foreach (string guid in towerDataGuids)
+ {
+ string assetPath = AssetDatabase.GUIDToAssetPath(guid);
+ TowerData towerData = AssetDatabase.LoadAssetAtPath(assetPath);
+
+ if (towerData == null)
+ {
+ Debug.LogWarning($"[CSVToSOImporter] Failed to load TowerData: {assetPath}");
+ continue;
+ }
+
+ if (towerData.prefab == null)
+ {
+ Debug.LogWarning($"[CSVToSOImporter] TowerData {towerData.name} has no prefab reference - skipping");
+ continue;
+ }
+
+ allTowers.Add(towerData);
+ Debug.Log($"[CSVToSOImporter] Added tower: {towerData.buildingName}");
+ }
+
+ if (allTowers.Count == 0)
+ {
+ Debug.LogWarning("[CSVToSOImporter] No TowerData with valid prefabs found!");
+ Debug.LogWarning("Run 'Northbound > Diagnose Tower System' to see what's wrong");
+ return;
+ }
+
+ allTowers.Sort((a, b) => string.Compare(a.buildingName, b.buildingName));
+
+ buildingManager.availableBuildings.Clear();
+ foreach (var towerData in allTowers)
+ {
+ buildingManager.availableBuildings.Add(towerData);
+ }
+
+ EditorUtility.SetDirty(buildingManager);
+ EditorSceneManager.MarkSceneDirty(EditorSceneManager.GetActiveScene());
+
+ Debug.Log($"========================================");
+ Debug.Log($"🏗️ TOWER IMPORT COMPLETE!");
+ Debug.Log($"========================================");
+ Debug.Log($"✓ BuildingManager automatically configured!");
+ Debug.Log($"✓ Added {allTowers.Count} TowerData to availableBuildings list");
+ Debug.Log($"✓ Ready to play - all towers will appear in quickslot!");
+ Debug.Log($"========================================");
+ foreach (var towerData in allTowers)
+ {
+ Debug.Log($" - {towerData.buildingName}");
+ }
+ Debug.Log($"========================================");
+ }
+
+ private static object ParseValue(string value, System.Type type)
+ {
+ if (string.IsNullOrEmpty(value))
+ {
+ return type.IsValueType ? System.Activator.CreateInstance(type) : null;
+ }
+
+ if (type == typeof(int))
+ {
+ int result;
+ if (int.TryParse(value, out result)) return result;
+ }
+ else if (type == typeof(float))
+ {
+ float result;
+ if (float.TryParse(value, out result)) return result;
+ }
+ else if (type == typeof(bool))
+ {
+ bool result;
+ if (bool.TryParse(value, out result)) return result;
+ }
+ else if (type == typeof(string))
+ {
+ return value;
+ }
+
+ return type.IsValueType ? System.Activator.CreateInstance(type) : null;
+ }
+
+ private static string CSVToCamelCase(string csvName)
+ {
+ if (string.IsNullOrEmpty(csvName))
+ return csvName;
+
+ string[] parts = csvName.Split('_');
+ for (int i = 1; i < parts.Length; i++)
+ {
+ if (!string.IsNullOrEmpty(parts[i]))
+ {
+ parts[i] = char.ToUpper(parts[i][0]) + parts[i].Substring(1);
+ }
+ }
+ return string.Join("", parts);
+ }
+
+ private static string[] ParseCSVLine(string line)
+ {
+ List result = new List();
+ bool inQuotes = false;
+ string current = "";
+
+ foreach (char c in line)
+ {
+ if (c == '"')
+ {
+ inQuotes = !inQuotes;
+ }
+ else if (c == ',' && !inQuotes)
+ {
+ result.Add(current);
+ current = "";
+ }
+ else
+ {
+ current += c;
+ }
+ }
+ result.Add(current);
+ return result.ToArray();
+ }
+ }
+}
diff --git a/Assets/Scripts/Editor/CSVToSOImporter.cs.meta b/Assets/Scripts/Editor/CSVToSOImporter.cs.meta
new file mode 100644
index 0000000..275f321
--- /dev/null
+++ b/Assets/Scripts/Editor/CSVToSOImporter.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: c00f900c9a822184dbb4dc85440d40dd
\ No newline at end of file
diff --git a/Assets/Scripts/Editor/TemplateCreator.cs b/Assets/Scripts/Editor/TemplateCreator.cs
index 63cb655..0b66088 100644
--- a/Assets/Scripts/Editor/TemplateCreator.cs
+++ b/Assets/Scripts/Editor/TemplateCreator.cs
@@ -2,6 +2,7 @@ using Unity.Netcode;
using UnityEditor;
using UnityEngine;
using UnityEngine.AI;
+using Northbound;
namespace Northbound.Editor
{
@@ -147,9 +148,35 @@ namespace Northbound.Editor
private static void SetupTowerComponents(GameObject go)
{
+ Transform t = go.transform;
+ t.localPosition = Vector3.zero;
+ t.localRotation = Quaternion.identity;
+ t.localScale = Vector3.one;
+
if (go.GetComponent() == null)
go.AddComponent();
+ if (go.GetComponent() == null)
+ go.AddComponent();
+
+ if (go.GetComponent() == null)
+ go.AddComponent();
+
+ if (go.GetComponent() == null)
+ {
+ BoxCollider collider = go.AddComponent();
+ collider.size = new Vector3(1f, 2f, 1f);
+ collider.center = new Vector3(0f, 1f, 0f);
+ }
+
+ if (go.GetComponent() == null)
+ {
+ NavMeshObstacle obstacle = go.AddComponent();
+ obstacle.shape = NavMeshObstacleShape.Box;
+ obstacle.size = new Vector3(1f, 1f, 1f);
+ obstacle.center = new Vector3(0f, 0.5f, 0f);
+ }
+
int defaultLayer = LayerMask.NameToLayer("Default");
if (defaultLayer >= 0)
{
diff --git a/Assets/Scripts/Editor/TowerPopulator.cs b/Assets/Scripts/Editor/TowerPopulator.cs
new file mode 100644
index 0000000..d4772b1
--- /dev/null
+++ b/Assets/Scripts/Editor/TowerPopulator.cs
@@ -0,0 +1,80 @@
+using System.Collections.Generic;
+using UnityEditor;
+using UnityEngine;
+using Northbound;
+using Northbound.Data;
+
+namespace Northbound.Editor
+{
+ public class TowerPopulator
+ {
+ private const string TOWER_PREFAB_PATH = "Assets/Prefabs/Tower";
+ private const string TOWER_DATA_PATH = "Assets/Data/ScriptableObjects";
+
+ [MenuItem("Northbound/Diagnose Tower System")]
+ public static void DiagnoseTowerSystem()
+ {
+ Debug.Log($"========================================");
+ Debug.Log($"[TowerPopulator] DIAGNOSING TOWER SYSTEM");
+ Debug.Log($"========================================");
+
+ string[] prefabGuids = AssetDatabase.FindAssets("t:Tower", new[] { TOWER_PREFAB_PATH });
+ Debug.Log($"Tower Prefabs in Assets/Prefabs/Tower/:");
+ if (prefabGuids.Length == 0)
+ {
+ Debug.Log($"✗ No tower prefabs found!");
+ }
+ else
+ {
+ foreach (string guid in prefabGuids)
+ {
+ string assetPath = AssetDatabase.GUIDToAssetPath(guid);
+ GameObject prefab = AssetDatabase.LoadAssetAtPath(assetPath);
+ TowerDataComponent tower = prefab?.GetComponent();
+ string towerStatus = tower != null && tower.towerData != null ? "✓" : "✗";
+ string towerDataName = tower?.towerData?.name ?? "MISSING";
+ Debug.Log($" {towerStatus} {prefab.name} - TowerDataComponent: {tower != null}, TowerData: {towerDataName}");
+ }
+ }
+
+ string[] towerDataGuids = AssetDatabase.FindAssets("t:TowerData", new[] { TOWER_DATA_PATH });
+ Debug.Log($"TowerData assets in Assets/Data/ScriptableObjects/:");
+ if (towerDataGuids.Length == 0)
+ {
+ Debug.Log($"⚠ No TowerData assets found - Run 'Tools > Data > Import All CSV' first!");
+ }
+ else
+ {
+ foreach (string guid in towerDataGuids)
+ {
+ string assetPath = AssetDatabase.GUIDToAssetPath(guid);
+ TowerData data = AssetDatabase.LoadAssetAtPath(assetPath);
+ string prefabStatus = data?.prefab != null ? "✓" : "✗";
+ Debug.Log($" {prefabStatus} {data.name} - Prefab: {data?.prefab?.name ?? "MISSING"}, BuildingName: {data?.buildingName}");
+ }
+ }
+
+ BuildingManager buildingManager = GameObject.FindObjectOfType();
+ Debug.Log($"BuildingManager in Scene:");
+ if (buildingManager == null)
+ {
+ Debug.Log($"✗ BuildingManager not found in scene!");
+ }
+ else
+ {
+ Debug.Log($"✓ BuildingManager found: {buildingManager.gameObject.name}");
+ Debug.Log($" Available Buildings: {buildingManager.availableBuildings.Count}");
+ foreach (var building in buildingManager.availableBuildings)
+ {
+ string status = building?.prefab != null ? "✓" : "✗";
+ string isTower = building is TowerData ? "[Tower]" : "yellow>[Building]";
+ Debug.Log($" {status} {isTower} {building?.name ?? "MISSING"} - {building?.buildingName}");
+ }
+ }
+
+ Debug.Log($"========================================");
+ Debug.Log($"[TowerPopulator] DIAGNOSIS COMPLETE");
+ Debug.Log($"========================================");
+ }
+ }
+}
diff --git a/Assets/Scripts/Editor/TowerPopulator.cs.meta b/Assets/Scripts/Editor/TowerPopulator.cs.meta
new file mode 100644
index 0000000..d4dd3ed
--- /dev/null
+++ b/Assets/Scripts/Editor/TowerPopulator.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 069a105319ca9cf4a8323e1d5357960f
\ No newline at end of file
diff --git a/Assets/Scripts/Editor/TowerPrefabSetup.cs b/Assets/Scripts/Editor/TowerPrefabSetup.cs
new file mode 100644
index 0000000..005a462
--- /dev/null
+++ b/Assets/Scripts/Editor/TowerPrefabSetup.cs
@@ -0,0 +1,128 @@
+using Northbound.Data;
+using UnityEditor;
+using UnityEngine;
+using UnityEngine.AI;
+
+namespace Northbound.Editor
+{
+ public class TowerPrefabSetup : IPrefabSetup
+ {
+ public string GetTemplateName()
+ {
+ return "TowerTemplate";
+ }
+
+ public void SetupPrefab(GameObject prefab, ScriptableObject data)
+ {
+ if (!(data is TowerData towerData))
+ {
+ Debug.LogWarning($"[TowerPrefabSetup] Expected TowerData, got {data.GetType().Name}");
+ return;
+ }
+
+ var towerDataComponent = prefab.GetComponent();
+ if (towerDataComponent == null)
+ {
+ towerDataComponent = prefab.AddComponent();
+ Debug.Log($"[TowerPrefabSetup] Added TowerDataComponent component");
+ }
+
+ if (towerDataComponent != null)
+ {
+ towerDataComponent.towerData = towerData;
+ }
+
+ // TowerData now extends BuildingData, so set prefab reference
+ towerData.prefab = prefab;
+
+ // Ensure TowerData fields are synced to BuildingData
+ towerData.EnsureSynced();
+
+ Transform modelTransform = null;
+
+ if (!string.IsNullOrEmpty(towerData.modelPath))
+ {
+ RemoveOldModel(prefab);
+
+ if (towerData.modelPath.ToLower().EndsWith(".fbx"))
+ {
+ GameObject fbxModel = AssetDatabase.LoadAssetAtPath(towerData.modelPath);
+ if (fbxModel != null)
+ {
+ GameObject fbxInstance = GameObject.Instantiate(fbxModel);
+ fbxInstance.name = "Model";
+ fbxInstance.transform.SetParent(prefab.transform, false);
+ fbxInstance.transform.localPosition = Vector3.zero;
+ fbxInstance.transform.localRotation = Quaternion.identity;
+
+ // Set model scale based on sizeX/sizeY/sizeZ
+ fbxInstance.transform.localScale = new Vector3(towerData.sizeX, towerData.sizeZ, towerData.sizeY);
+
+ modelTransform = fbxInstance.transform;
+
+ Debug.Log($"[TowerPrefabSetup] Applied FBX model: {towerData.modelPath} with scale {towerData.sizeX}x{towerData.sizeZ}x{towerData.sizeY}");
+ }
+ else
+ {
+ Debug.LogWarning($"[TowerPrefabSetup] Could not load FBX model: {towerData.modelPath}");
+ }
+ }
+ else
+ {
+ var meshFilter = prefab.GetComponent();
+ if (meshFilter == null)
+ {
+ meshFilter = prefab.AddComponent();
+ }
+
+ var renderer = prefab.GetComponent();
+ if (renderer == null)
+ {
+ renderer = prefab.AddComponent();
+ }
+
+ Mesh mesh = AssetDatabase.LoadAssetAtPath(towerData.modelPath);
+ if (mesh != null)
+ {
+ meshFilter.sharedMesh = mesh;
+
+ modelTransform = renderer.transform;
+ modelTransform.localScale = new Vector3(towerData.sizeX, towerData.sizeZ, towerData.sizeY);
+
+ Debug.Log($"[TowerPrefabSetup] Applied mesh: {towerData.modelPath} with scale {towerData.sizeX}x{towerData.sizeZ}x{towerData.sizeY}");
+ }
+ else
+ {
+ Debug.LogWarning($"[TowerPrefabSetup] Could not load mesh: {towerData.modelPath}");
+ }
+ }
+ }
+
+ var collider = prefab.GetComponent();
+ if (collider == null)
+ {
+ collider = prefab.AddComponent();
+ }
+ collider.size = new Vector3(towerData.sizeX, towerData.sizeZ, towerData.sizeY);
+ collider.center = new Vector3(0f, towerData.sizeZ / 2f, 0f);
+
+ var navObstacle = prefab.GetComponent();
+ if (navObstacle == null)
+ {
+ navObstacle = prefab.AddComponent();
+ navObstacle.shape = NavMeshObstacleShape.Box;
+ }
+ navObstacle.size = new Vector3(towerData.sizeX, towerData.sizeZ, towerData.sizeY);
+ navObstacle.center = new Vector3(0f, towerData.sizeZ / 2f, 0f);
+ }
+
+ private void RemoveOldModel(GameObject prefab)
+ {
+ Transform oldModel = prefab.transform.Find("Model");
+ if (oldModel != null)
+ {
+ GameObject.DestroyImmediate(oldModel.gameObject);
+ }
+ }
+ }
+}
diff --git a/Assets/Scripts/Editor/TowerPrefabSetup.cs.meta b/Assets/Scripts/Editor/TowerPrefabSetup.cs.meta
new file mode 100644
index 0000000..026c9ce
--- /dev/null
+++ b/Assets/Scripts/Editor/TowerPrefabSetup.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: ea17487cde4842d45baf5eb92943142a
\ No newline at end of file
diff --git a/Assets/Scripts/TowerDataComponent.cs b/Assets/Scripts/TowerDataComponent.cs
new file mode 100644
index 0000000..35525e8
--- /dev/null
+++ b/Assets/Scripts/TowerDataComponent.cs
@@ -0,0 +1,26 @@
+using Northbound.Data;
+using Unity.Netcode;
+using UnityEngine;
+
+namespace Northbound
+{
+ [RequireComponent(typeof(Building))]
+ [RequireComponent(typeof(NetworkObject))]
+ public class TowerDataComponent : MonoBehaviour
+ {
+ [Header("Data Reference")]
+ [Tooltip("ScriptableObject containing tower data")]
+ public TowerData towerData;
+
+ private void Awake()
+ {
+ // TowerData now extends BuildingData, so just pass it directly
+ Building building = GetComponent();
+ if (building != null && towerData != null)
+ {
+ building.buildingData = towerData;
+ building.initialTeam = TeamType.Player;
+ }
+ }
+ }
+}
diff --git a/Assets/Scripts/TowerDataComponent.cs.meta b/Assets/Scripts/TowerDataComponent.cs.meta
new file mode 100644
index 0000000..f7f5fa3
--- /dev/null
+++ b/Assets/Scripts/TowerDataComponent.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 56c4536effc49fe47af593bf9d17e979
\ No newline at end of file
diff --git a/ExternAttributes.Editor.csproj b/ExternAttributes.Editor.csproj
index 35f03b0..a9c702c 100644
--- a/ExternAttributes.Editor.csproj
+++ b/ExternAttributes.Editor.csproj
@@ -43,6 +43,7 @@
6000.3.5f2
+
diff --git a/FlatKit.Utils.Editor.csproj b/FlatKit.Utils.Editor.csproj
index 9988b14..850b945 100644
--- a/FlatKit.Utils.Editor.csproj
+++ b/FlatKit.Utils.Editor.csproj
@@ -43,6 +43,7 @@
6000.3.5f2
+
diff --git a/GameData/.Tower_schema.json b/GameData/.Tower_schema.json
index bd677e4..878b8b4 100644
--- a/GameData/.Tower_schema.json
+++ b/GameData/.Tower_schema.json
@@ -35,6 +35,12 @@
"condition": null,
"description": "Y 그리드 차지 공간"
},
+ {
+ "name": "size_z",
+ "type": "int",
+ "condition": null,
+ "description": "Z 차지 공간"
+ },
{
"name": "max_hp",
"type": "int",
@@ -60,9 +66,9 @@
"description": "공격 주기"
},
{
- "name": "prefab_path",
+ "name": "model_path",
"type": "string",
"condition": null,
- "description": "프리팹/리소스 경로"
+ "description": "모델 경로"
}
]
\ No newline at end of file
diff --git a/GameData/Tower.csv b/GameData/Tower.csv
index ffbde1e..6dd9fb9 100644
--- a/GameData/Tower.csv
+++ b/GameData/Tower.csv
@@ -1,3 +1,3 @@
-id,memo,mana,manpower,size_x,size_y,max_hp,atk_range,atk_damage,atk_interval_sec,prefab_path
-1,타워,25,10,3,3,50,10,5,2,Assets/Prefabs/TowerArrow
-2,벽,5,5,1,1,30,0,0,0,Assets/Prefabs/Wall
+id,memo,mana,manpower,size_x,size_y,size_z,max_hp,atk_range,atk_damage,atk_interval_sec,model_path
+1,타워,25,10,3,3,3,50,10,5,2,Assets/Models/building_tower_B_blue.fbx
+2,벽,5,5,2,2,1,30,0,0,0,Assets/Models/wall_straight.fbx
diff --git a/TOWER_CSV_IMPORTER.md b/TOWER_CSV_IMPORTER.md
new file mode 100644
index 0000000..b49e3bf
--- /dev/null
+++ b/TOWER_CSV_IMPORTER.md
@@ -0,0 +1,138 @@
+# Tower CSV Importer Guide
+
+## Overview
+The tower CSV importer allows you to create tower prefabs from CSV data in one step, similar to the monster importer.
+
+## Files Created
+1. **TowerDataComponent.cs** - Component that applies TowerData to a GameObject
+2. **TowerPrefabSetup.cs** - Implementation of IPrefabSetup for tower prefab generation
+3. **CSVToSOImporter.cs** - Generic CSV importer that works with any data type
+
+## How to Use
+
+### Step 1: Ensure Tower Template Exists
+If you haven't created the TowerTemplate yet:
+1. In Unity, go to `Tools > Data > Create Tower Template`
+2. This creates `Assets/Data/Templates/TowerTemplate.prefab` with all required components:
+ - NetworkObject
+ - Building
+ - TowerDataComponent
+ - MeshFilter & MeshRenderer
+ - BoxCollider
+ - NavMeshObstacle
+
+### Step 2: Update Tower.csv
+Edit `GameData/Tower.csv` with your tower data. The CSV uses camelCase field names matching TowerData class:
+
+```csv
+id,memo,mana,manpower,sizeX,sizeY,maxHp,atkRange,atkDamage,atkIntervalSec,modelPath
+1,타워,25,10,3,3,50,10,5,2,Assets/Meshes/building_tower_B_blue.fbx
+2,벽,5,5,1,1,30,0,0,0,Assets/Meshes/building_tower_B_blue.fbx
+```
+
+**Field Descriptions:**
+- `id`: Unique tower ID
+- `memo`: Tower name/description
+- `mana`: Mana cost to build
+- `manpower`: Construction work required
+- `sizeX`: Grid width
+- `sizeY`: Grid length
+- `maxHp`: Maximum health
+- `atkRange`: Attack range
+- `atkDamage`: Attack damage
+- `atkIntervalSec`: Attack interval in seconds
+- `modelPath`: Path to FBX model or mesh asset
+
+### Step 3: Import CSV
+In Unity, go to `Tools > Data > Import All CSV`
+
+The importer will:
+1. Create ScriptableObject files in `Assets/Data/ScriptableObjects/Tower/`
+2. Create/Update prefabs in `Assets/Prefabs/Tower/`
+3. Apply models from `modelPath` column
+4. Configure components based on CSV data
+5. Link TowerData to TowerDataComponent
+
+### Step 4: Use Tower Prefabs
+Your generated tower prefabs are ready to use in the BuildingManager system!
+
+## File Structure After Import
+```
+Assets/
+├── Data/
+│ ├── ScriptableObjects/
+│ │ └── Tower/ # SO files (generated from CSV)
+│ │ ├── Tower1.asset
+│ │ └── Tower2.asset
+│ └── Templates/ # Template prefabs (created once)
+│ └── TowerTemplate.prefab
+├── Prefabs/
+│ └── Tower/ # Generated tower prefabs
+│ ├── Tower1.prefab
+│ └── Tower2.prefab
+└── GameData/
+ └── Tower.csv # Source data (editable)
+```
+
+## How It Works
+
+### CSV Import Pipeline
+```
+Tower.csv (CSV data)
+ ↓ CSVToSOImporter
+TowerData (ScriptableObject)
+ ↓ TowerPrefabSetup
+Tower.prefab (GameObject with components)
+```
+
+### TowerPrefabSetup Logic
+1. **TowerDataComponent**: Links TowerData SO to the prefab
+2. **Model Application**: Loads and applies FBX model from modelPath
+3. **Collider Setup**: Creates BoxCollider sized to tower dimensions
+4. **Building Integration**: Converts TowerData to BuildingData for Building component
+
+## Benefits
+- ✅ **One-click import**: Import all towers from CSV at once
+- ✅ **Consistent structure**: All towers have the same components
+- ✅ **Easy updates**: Edit CSV and re-import to update all towers
+- ✅ **Designer-friendly**: Non-programmers can add new towers
+- ✅ **Type-safe**: TowerData class ensures data integrity
+
+## Troubleshooting
+
+**"No prefab setup found for Tower"**
+- Make sure TowerPrefabSetup.cs is in the Assets/Scripts/Editor folder
+- Restart Unity Editor to ensure the script is compiled
+
+**"Template not found"**
+- Run `Tools > Data > Create Tower Template` first
+- Verify TowerTemplate.prefab exists in Assets/Data/Templates/
+
+**Prefabs not created**
+- Check the Console for error messages
+- Verify Tower.csv exists in GameData folder
+- Ensure modelPath in CSV points to valid assets
+
+**Model not showing**
+- Verify modelPath in CSV is correct
+- Check that the FBX file exists at the specified path
+- Ensure the model has valid materials assigned
+
+## Customization
+
+### Adding New Components to All Towers
+1. Open `Assets/Data/Templates/TowerTemplate.prefab`
+2. Add the component
+3. Configure defaults
+4. Save template
+5. Re-import CSV to apply changes to new towers
+
+### Modifying Existing Towers
+- Edit the prefab directly (changes persist on next import)
+- OR modify Tower.csv and re-import (will update SO link and model)
+
+## Notes
+- TowerDataComponent converts TowerData to BuildingData for compatibility with the existing Building system
+- The importer creates prefabs if they don't exist, or updates existing ones
+- Prefab edits (other than SO and model) are preserved on re-import
+- CSV field names must match TowerData class property names exactly (case-sensitive, camelCase)
diff --git a/TOWER_QUICKSLOT_SETUP.md b/TOWER_QUICKSLOT_SETUP.md
new file mode 100644
index 0000000..060925c
--- /dev/null
+++ b/TOWER_QUICKSLOT_SETUP.md
@@ -0,0 +1,120 @@
+# Tower Quickslot Setup Guide
+
+## 🎯 ULTRA-SIMPLE 1-CLICK SETUP (For Non-Programmers!)
+
+### Just ONE Step!
+
+Edit your `GameData/Tower.csv` file with tower data, then in Unity:
+
+```
+Tools > Data > Import All CSV
+```
+
+**THAT'S IT!** This automatically does everything:
+- ✅ Creates TowerData from CSV (TowerData now extends BuildingData!)
+- ✅ Creates Tower prefabs with TowerDataComponent
+- ✅ **Auto-configures BuildingManager with all TowerData!**
+
+### Testing
+Press **B** in-game → All towers appear in quickslot! 🎉
+
+---
+
+## 📝 Adding New Towers
+
+1. Add row to `GameData/Tower.csv`
+2. Run `Tools > Data > Import All CSV`
+3. Done! 🚀
+
+Everything is automatic!
+
+---
+
+## 🔧 Troubleshooting
+
+### Error: "BuildingManager not found in scene"
+
+**Cause:** Your scene doesn't have a GameObject with BuildingManager component.
+
+**Fix:**
+1. Create a new GameObject (e.g., "BuildingManager")
+2. Add BuildingManager component to it
+3. Run `Tools > Data > Import All CSV` again
+4. Done!
+
+### Error: "MissingReferenceException: prefab doesn't exist"
+
+**Cause:** TowerData doesn't have prefab reference.
+
+**Fix:**
+1. Run `Northbound > Diagnose Tower System`
+2. Check which TowerData shows `✗ MISSING PREFAB`
+3. Run `Tools > Data > Import All CSV` to fix
+4. Done!
+
+### No towers appear in quickslot
+
+**Cause:** BuildingManager wasn't found or configured.
+
+**Fix:**
+1. Run `Northbound > Diagnose Tower System`
+2. Check if BuildingManager is found and has TowerData in list
+3. If not, add BuildingManager to your scene
+4. Run `Tools > Data > Import All CSV`
+
+---
+
+## 🎮 Menu Commands
+
+| Command | What it does | When to use |
+|---------|--------------|--------------|
+| `Tools > Data > Import All CSV` | **ONE-CLICK SETUP** - Import all CSV files, create prefabs, auto-configure BuildingManager with TowerData | After editing Tower.csv or anytime you want to update towers |
+| `Northbound > Diagnose Tower System` | Check tower system health and find issues | When something doesn't work |
+
+---
+
+## ✅ Why This is Awesome
+
+- ✅ **ONE CLICK** - Edit CSV, run importer, done!
+- ✅ **One folder for towers** - `Assets/Prefabs/Tower/` only
+- ✅ **No duplicates** - No manual copying anywhere
+- ✅ **Fully automatic** - BuildingManager configured automatically
+- ✅ **Non-programmer friendly** - Just edit CSV and click one button!
+- ✅ **Works in builds** - Everything is pre-populated
+- ✅ **Instant testing** - Press B and see all towers!
+- ✅ **Unified data structure** - TowerData extends BuildingData, no redundant data!
+
+---
+
+## 🎉 Complete Workflow for New Teammates
+
+1. Open Unity project
+2. Add BuildingManager GameObject to scene (once)
+3. Edit `GameData/Tower.csv` with tower data
+4. Run `Tools > Data > Import All CSV`
+5. Play game, press **B**, build towers! 🏗️
+
+**That's literally it! No manual configuration needed!**
+
+---
+
+## 🏗️ Technical Details
+
+### Unified Data Structure
+
+**Before:** Two separate data systems
+- TowerData (CSV, tower-specific stats)
+- BuildingData (auto-generated, building stats)
+
+**Now:** Single unified system
+- TowerData extends BuildingData
+- TowerData has all tower-specific fields (atkRange, atkDamage, etc.)
+- TowerData automatically maps to BuildingData fields (width, length, maxHealth, etc.)
+- BuildingManager uses TowerData directly (no conversion needed!)
+
+### Benefits of Unified Structure
+- ✅ No duplicate data
+- ✅ Single source of truth
+- ✅ Cleaner code
+- ✅ Easier to maintain
+- ✅ Tower prefabs can be used directly without intermediate BuildingData files
diff --git a/Unity.RenderPipelines.Universal.Runtime.csproj b/Unity.RenderPipelines.Universal.Runtime.csproj
index c30acff..faa692e 100644
--- a/Unity.RenderPipelines.Universal.Runtime.csproj
+++ b/Unity.RenderPipelines.Universal.Runtime.csproj
@@ -43,14 +43,15 @@
6000.3.5f2
+
-
+
@@ -142,8 +143,8 @@
-
+
@@ -246,8 +247,8 @@
-
+