From 106fe81c8895f0a3112848d275e2df4d27aa3170 Mon Sep 17 00:00:00 2001 From: dal4segno Date: Mon, 2 Feb 2026 16:14:01 +0900 Subject: [PATCH] =?UTF-8?q?=EC=9E=90=EC=9B=90=20=EC=83=9D=EC=84=B1?= =?UTF-8?q?=EA=B8=B0=20=EC=A0=9C=EC=9E=91=20=EB=B0=8F=20=EB=A7=B5=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=EA=B8=B0=EC=97=90=20=ED=86=B5=ED=95=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 및 씬 내 오브젝트 구조 정리 --- Assembly-CSharp.csproj | 1 + Assets/Scenes/GameMain.unity | 479 ++++++++++------- Assets/Scripts/MapGenerator.cs | 774 ++++++++++++++++++++++++++++ Assets/Scripts/MapGenerator.cs.meta | 2 + Assets/Scripts/Resource.cs | 102 +++- 5 files changed, 1163 insertions(+), 195 deletions(-) create mode 100644 Assets/Scripts/MapGenerator.cs create mode 100644 Assets/Scripts/MapGenerator.cs.meta diff --git a/Assembly-CSharp.csproj b/Assembly-CSharp.csproj index 39a2197..de4d5c1 100644 --- a/Assembly-CSharp.csproj +++ b/Assembly-CSharp.csproj @@ -58,6 +58,7 @@ + diff --git a/Assets/Scenes/GameMain.unity b/Assets/Scenes/GameMain.unity index b71e09c..58aa196 100644 --- a/Assets/Scenes/GameMain.unity +++ b/Assets/Scenes/GameMain.unity @@ -143,15 +143,20 @@ Transform: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 61373298} serializedVersion: 2 - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 16.14601, y: -0.99998, z: -532.7231} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: - {fileID: 519420032} - {fileID: 1290143990} - m_Father: {fileID: 0} + m_Father: {fileID: 576429380} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &109813795 stripped +Transform: + m_CorrespondingSourceObject: {fileID: 228462577495887354, guid: 11e3760dda2c0164abf759c18d918893, type: 3} + m_PrefabInstance: {fileID: 1061936651} + m_PrefabAsset: {fileID: 0} --- !u!1 &306979656 GameObject: m_ObjectHideFlags: 0 @@ -329,8 +334,8 @@ Transform: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 447015514} serializedVersion: 2 - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: -10, y: 1, z: -80} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 6.1460094, y: 0.00002002716, z: -612.7231} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: @@ -338,7 +343,7 @@ Transform: - {fileID: 1536695130} - {fileID: 629469724} - {fileID: 2098115308} - m_Father: {fileID: 0} + m_Father: {fileID: 576429380} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &457600242 GameObject: @@ -661,6 +666,45 @@ MonoBehaviour: m_PostInfinity: 2 m_RotationOrder: 4 CustomBlends: {fileID: 0} +--- !u!1 &576429379 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 576429380} + m_Layer: 0 + m_Name: Defaults + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &576429380 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 576429379} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: -16.14601, y: 0.99998, z: 532.7231} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 61373299} + - {fileID: 1135136907} + - {fileID: 782070683} + - {fileID: 8940572951313384071} + - {fileID: 447015515} + - {fileID: 1138174508} + - {fileID: 109813795} + - {fileID: 639696858} + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1001 &629469723 PrefabInstance: m_ObjectHideFlags: 0 @@ -735,6 +779,173 @@ Transform: m_CorrespondingSourceObject: {fileID: 922888705413710451, guid: 5662d0b0d0eb5f54290edd8dd0980b57, type: 3} m_PrefabInstance: {fileID: 629469723} m_PrefabAsset: {fileID: 0} +--- !u!4 &639696858 stripped +Transform: + m_CorrespondingSourceObject: {fileID: 6727958582834582256, guid: ace4a11b046dac9498e6897c2b5eb245, type: 3} + m_PrefabInstance: {fileID: 463088717151812744} + m_PrefabAsset: {fileID: 0} +--- !u!1 &640318136 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 640318137} + m_Layer: 0 + m_Name: System + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &640318137 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 640318136} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: -16.14601, y: 0.99998, z: 532.7231} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1442785555} + - {fileID: 672563223} + - {fileID: 1142746334} + - {fileID: 1199559224} + - {fileID: 955933985} + - {fileID: 946527919} + - {fileID: 1701756768} + - {fileID: 1166878644} + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &672563220 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 672563223} + - component: {fileID: 672563221} + - component: {fileID: 672563222} + m_Layer: 0 + m_Name: MapGenerator + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &672563221 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 672563220} + 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: 3027998365 + 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!114 &672563222 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 672563220} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: f0dde802bf94bf6448eb7d4838d1c42a, type: 3} + m_Name: + m_EditorClassIdentifier: Assembly-CSharp::Northbound.MapGenerator + ShowTopMostFoldoutHeaderGroup: 1 + generateOnSpawn: 1 + groupUnderParent: 1 + playableAreaWidth: 80 + startZ: -75 + endZ: 700 + resourcePrefab: {fileID: 5585059388146411250, guid: f395fcc064a3a834ba957327f1387c19, type: 3} + minResourceCount: 8 + maxResourceCount: 12 + minDistanceBetweenResources: 80 + minDistanceFromCore: 50 + minDistanceFromBarracks: 50 + initialResourceProduction: 50 + additionalResourceBaseProduction: 25 + targetTotalProduction: 300 + targetProductionTolerance: 0.05 + minQualityModifier: -30 + maxQualityModifier: 30 + maxResourceGenerationAttempts: 10 + obstacles: + - prefab: {fileID: 7867287811520877109, guid: 915c4daebc2463840b83193366491ba1, type: 3} + spawnWeight: 5 + minCount: 0 + maxCount: 99999 + - prefab: {fileID: 7245241003860522441, guid: 2c33c67d086286d4a929d533b4e26863, type: 3} + spawnWeight: 20 + minCount: 0 + maxCount: 99999 + - prefab: {fileID: 7773749726967315046, guid: b49d9f4b44d4c7d4b92498e95c847667, type: 3} + spawnWeight: 5 + minCount: 0 + maxCount: 99999 + - prefab: {fileID: 1559926009631082787, guid: 4ade932e47f340647ac833f7188a6dd0, type: 3} + spawnWeight: 30 + minCount: 0 + maxCount: 99999 + - prefab: {fileID: 4345146930867537194, guid: d754e5f1f9047ce428c55a0ae1975b4e, type: 3} + spawnWeight: 30 + minCount: 0 + maxCount: 99999 + obstacleDensity: 0.01 + maxTotalObstacles: 10000 + minDistanceBetweenObstacles: 2 + checkCollision: 1 + collisionLayers: + serializedVersion: 2 + m_Bits: 1152 + collisionCheckRadius: 1 + alignToTerrain: 1 + randomRotation: 1 + scaleVariation: 0.2 + maxObstacleSpawnAttempts: 50 + generateResourcesFirst: 1 +--- !u!4 &672563223 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 672563220} + serializedVersion: 2 + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 16.14601, y: -0.99998, z: -532.7231} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 640318137} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &768754084 GameObject: m_ObjectHideFlags: 0 @@ -932,11 +1143,11 @@ Transform: m_GameObject: {fileID: 782070680} serializedVersion: 2 m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261} - m_LocalPosition: {x: -0.76944, y: 1, z: 7.59836} + m_LocalPosition: {x: 15.37657, y: 0.00002002716, z: -525.1247} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] - m_Father: {fileID: 0} + m_Father: {fileID: 576429380} m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} --- !u!1 &908127335 GameObject: @@ -1154,12 +1365,12 @@ Transform: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 946527916} serializedVersion: 2 - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: 0, y: 100, z: -50} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 16.14601, y: 99.00002, z: -582.7231} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] - m_Father: {fileID: 0} + m_Father: {fileID: 640318137} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &955933983 GameObject: @@ -1199,12 +1410,12 @@ Transform: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 955933983} serializedVersion: 2 - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: -29.48045, y: -0, z: -38.91273} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -13.33444, y: -0.99998, z: -571.6358} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] - m_Father: {fileID: 0} + m_Father: {fileID: 640318137} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &985764481 stripped GameObject: @@ -1348,19 +1559,19 @@ PrefabInstance: serializedVersion: 2 m_Modification: serializedVersion: 3 - m_TransformParent: {fileID: 0} + m_TransformParent: {fileID: 576429380} m_Modifications: - target: {fileID: 228462577495887354, guid: 11e3760dda2c0164abf759c18d918893, type: 3} propertyPath: m_LocalPosition.x - value: 20 + value: 36.14601 objectReference: {fileID: 0} - target: {fileID: 228462577495887354, guid: 11e3760dda2c0164abf759c18d918893, type: 3} propertyPath: m_LocalPosition.y - value: 1 + value: 0.00002002716 objectReference: {fileID: 0} - target: {fileID: 228462577495887354, guid: 11e3760dda2c0164abf759c18d918893, type: 3} propertyPath: m_LocalPosition.z - value: 650 + value: 117.27692 objectReference: {fileID: 0} - target: {fileID: 228462577495887354, guid: 11e3760dda2c0164abf759c18d918893, type: 3} propertyPath: m_LocalRotation.w @@ -1368,15 +1579,15 @@ PrefabInstance: objectReference: {fileID: 0} - target: {fileID: 228462577495887354, guid: 11e3760dda2c0164abf759c18d918893, type: 3} propertyPath: m_LocalRotation.x - value: 0 + value: -0 objectReference: {fileID: 0} - target: {fileID: 228462577495887354, guid: 11e3760dda2c0164abf759c18d918893, type: 3} propertyPath: m_LocalRotation.y - value: 0 + value: -0 objectReference: {fileID: 0} - target: {fileID: 228462577495887354, guid: 11e3760dda2c0164abf759c18d918893, type: 3} propertyPath: m_LocalRotation.z - value: 0 + value: -0 objectReference: {fileID: 0} - target: {fileID: 228462577495887354, guid: 11e3760dda2c0164abf759c18d918893, type: 3} propertyPath: m_LocalEulerAnglesHint.x @@ -1431,6 +1642,16 @@ PrefabInstance: m_AddedGameObjects: [] m_AddedComponents: [] m_SourcePrefab: {fileID: 100100000, guid: 11e3760dda2c0164abf759c18d918893, type: 3} +--- !u!4 &1135136907 stripped +Transform: + m_CorrespondingSourceObject: {fileID: 3247786716306397435, guid: f395fcc064a3a834ba957327f1387c19, type: 3} + m_PrefabInstance: {fileID: 4875211098963642791} + m_PrefabAsset: {fileID: 0} +--- !u!4 &1138174508 stripped +Transform: + m_CorrespondingSourceObject: {fileID: 228462577495887354, guid: 11e3760dda2c0164abf759c18d918893, type: 3} + m_PrefabInstance: {fileID: 1440648431994998967} + m_PrefabAsset: {fileID: 0} --- !u!1 &1142746332 GameObject: m_ObjectHideFlags: 0 @@ -1468,12 +1689,12 @@ Transform: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1142746332} serializedVersion: 2 - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: 0.64446, y: 5.17816, z: -1.23575} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 16.79047, y: 4.17818, z: -533.95886} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] - m_Father: {fileID: 0} + m_Father: {fileID: 640318137} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1166878641 GameObject: @@ -1547,12 +1768,12 @@ Transform: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1166878641} serializedVersion: 2 - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 16.14601, y: -0.99998, z: -532.7231} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] - m_Father: {fileID: 0} + m_Father: {fileID: 640318137} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1199559221 GameObject: @@ -1626,12 +1847,12 @@ Transform: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1199559221} serializedVersion: 2 - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: -0.48349, y: 1, z: 0.31137} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 15.662519, y: 0.00002002716, z: -532.41174} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] - m_Father: {fileID: 0} + m_Father: {fileID: 640318137} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1290143989 GameObject: @@ -1828,7 +2049,7 @@ MonoBehaviour: m_DisconnectTimeoutMS: 30000 ConnectionData: Address: 127.0.0.1 - Port: 7843 + Port: 7914 ServerListenAddress: 127.0.0.1 ClientBindPort: 0 DebugSimulator: @@ -1919,12 +2140,12 @@ Transform: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1442785552} serializedVersion: 2 - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: -20.15257, y: 1.00001, z: 4.73882} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -4.0065613, y: 0.000030040741, z: -527.98425} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] - m_Father: {fileID: 0} + m_Father: {fileID: 640318137} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1001 &1536695129 PrefabInstance: @@ -2000,115 +2221,6 @@ Transform: m_CorrespondingSourceObject: {fileID: 922888705413710451, guid: 5662d0b0d0eb5f54290edd8dd0980b57, type: 3} m_PrefabInstance: {fileID: 1536695129} m_PrefabAsset: {fileID: 0} ---- !u!1 &1572384097 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 1572384099} - - component: {fileID: 1572384100} - - component: {fileID: 1572384098} - m_Layer: 0 - m_Name: ObstacleSpawner - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!114 &1572384098 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1572384097} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: a889d8fab7aeaa24dbbffaee2f02ba54, type: 3} - m_Name: - m_EditorClassIdentifier: Assembly-CSharp::Northbound.ObstacleSpawner - ShowTopMostFoldoutHeaderGroup: 1 - spawnCenter: {fileID: 0} - spawnAreaSize: {x: 80, y: 725} - areaShape: 0 - obstacles: - - prefab: {fileID: 1559926009631082787, guid: 4ade932e47f340647ac833f7188a6dd0, type: 3} - spawnWeight: 40 - minCount: 0 - maxCount: 290 - - prefab: {fileID: 4345146930867537194, guid: d754e5f1f9047ce428c55a0ae1975b4e, type: 3} - spawnWeight: 40 - minCount: 0 - maxCount: 307 - - prefab: {fileID: 7867287811520877109, guid: 915c4daebc2463840b83193366491ba1, type: 3} - spawnWeight: 5 - minCount: 0 - maxCount: 247 - - prefab: {fileID: 7245241003860522441, guid: 2c33c67d086286d4a929d533b4e26863, type: 3} - spawnWeight: 30 - minCount: 0 - maxCount: 274 - - prefab: {fileID: 7773749726967315046, guid: b49d9f4b44d4c7d4b92498e95c847667, type: 3} - spawnWeight: 10 - minCount: 0 - maxCount: 228 - density: 0.02 - maxTotalObstacles: 6400 - minDistanceBetweenObstacles: 4 - alignToTerrain: 1 - randomRotation: 1 - scaleVariation: 0.197 - maxAttempts: 50 - checkCollision: 1 - collisionLayers: - serializedVersion: 2 - m_Bits: 896 - collisionCheckRadius: 1 - spawnOnStart: 1 - groupUnderParent: 1 ---- !u!4 &1572384099 -Transform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1572384097} - serializedVersion: 2 - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: 0, y: 1, z: 290} - 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 &1572384100 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1572384097} - 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: 2866586184 - 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!1 &1701756764 GameObject: m_ObjectHideFlags: 0 @@ -2213,12 +2325,12 @@ Transform: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1701756764} serializedVersion: 2 - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: -0.16227, y: 1.00001, z: -48.21116} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 15.98374, y: 0.000030040741, z: -580.93427} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] - m_Father: {fileID: 0} + m_Father: {fileID: 640318137} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1859567749 GameObject: @@ -2445,7 +2557,7 @@ PrefabInstance: serializedVersion: 2 m_Modification: serializedVersion: 3 - m_TransformParent: {fileID: 0} + m_TransformParent: {fileID: 576429380} m_Modifications: - target: {fileID: 2255421732537623704, guid: ace4a11b046dac9498e6897c2b5eb245, type: 3} propertyPath: m_Name @@ -2457,15 +2569,15 @@ PrefabInstance: objectReference: {fileID: 0} - target: {fileID: 6727958582834582256, guid: ace4a11b046dac9498e6897c2b5eb245, type: 3} propertyPath: m_LocalPosition.x - value: 10 + value: 26.14601 objectReference: {fileID: 0} - target: {fileID: 6727958582834582256, guid: ace4a11b046dac9498e6897c2b5eb245, type: 3} propertyPath: m_LocalPosition.y - value: 1 + value: 0.00002002716 objectReference: {fileID: 0} - target: {fileID: 6727958582834582256, guid: ace4a11b046dac9498e6897c2b5eb245, type: 3} propertyPath: m_LocalPosition.z - value: -90 + value: -622.7231 objectReference: {fileID: 0} - target: {fileID: 6727958582834582256, guid: ace4a11b046dac9498e6897c2b5eb245, type: 3} propertyPath: m_LocalRotation.w @@ -2473,7 +2585,7 @@ PrefabInstance: objectReference: {fileID: 0} - target: {fileID: 6727958582834582256, guid: ace4a11b046dac9498e6897c2b5eb245, type: 3} propertyPath: m_LocalRotation.x - value: 0 + value: -0 objectReference: {fileID: 0} - target: {fileID: 6727958582834582256, guid: ace4a11b046dac9498e6897c2b5eb245, type: 3} propertyPath: m_LocalRotation.y @@ -2481,7 +2593,7 @@ PrefabInstance: objectReference: {fileID: 0} - target: {fileID: 6727958582834582256, guid: ace4a11b046dac9498e6897c2b5eb245, type: 3} propertyPath: m_LocalRotation.z - value: 0 + value: -0 objectReference: {fileID: 0} - target: {fileID: 6727958582834582256, guid: ace4a11b046dac9498e6897c2b5eb245, type: 3} propertyPath: m_LocalEulerAnglesHint.x @@ -2506,19 +2618,19 @@ PrefabInstance: serializedVersion: 2 m_Modification: serializedVersion: 3 - m_TransformParent: {fileID: 0} + m_TransformParent: {fileID: 576429380} m_Modifications: - target: {fileID: 228462577495887354, guid: 11e3760dda2c0164abf759c18d918893, type: 3} propertyPath: m_LocalPosition.x - value: -20 + value: -3.8539906 objectReference: {fileID: 0} - target: {fileID: 228462577495887354, guid: 11e3760dda2c0164abf759c18d918893, type: 3} propertyPath: m_LocalPosition.y - value: 1 + value: 0.00002002716 objectReference: {fileID: 0} - target: {fileID: 228462577495887354, guid: 11e3760dda2c0164abf759c18d918893, type: 3} propertyPath: m_LocalPosition.z - value: 650 + value: 117.27692 objectReference: {fileID: 0} - target: {fileID: 228462577495887354, guid: 11e3760dda2c0164abf759c18d918893, type: 3} propertyPath: m_LocalRotation.w @@ -2526,15 +2638,15 @@ PrefabInstance: objectReference: {fileID: 0} - target: {fileID: 228462577495887354, guid: 11e3760dda2c0164abf759c18d918893, type: 3} propertyPath: m_LocalRotation.x - value: 0 + value: -0 objectReference: {fileID: 0} - target: {fileID: 228462577495887354, guid: 11e3760dda2c0164abf759c18d918893, type: 3} propertyPath: m_LocalRotation.y - value: 0 + value: -0 objectReference: {fileID: 0} - target: {fileID: 228462577495887354, guid: 11e3760dda2c0164abf759c18d918893, type: 3} propertyPath: m_LocalRotation.z - value: 0 + value: -0 objectReference: {fileID: 0} - target: {fileID: 228462577495887354, guid: 11e3760dda2c0164abf759c18d918893, type: 3} propertyPath: m_LocalEulerAnglesHint.x @@ -2660,7 +2772,7 @@ PrefabInstance: serializedVersion: 2 m_Modification: serializedVersion: 3 - m_TransformParent: {fileID: 0} + m_TransformParent: {fileID: 576429380} m_Modifications: - target: {fileID: 1287070985890992582, guid: e56926eda34629f4fbf3e4c53f0f8bd4, type: 3} propertyPath: equipmentData.socketName @@ -2700,15 +2812,15 @@ PrefabInstance: objectReference: {fileID: 0} - target: {fileID: 8064559726283331702, guid: e56926eda34629f4fbf3e4c53f0f8bd4, type: 3} propertyPath: m_LocalPosition.x - value: 0 + value: 16.14601 objectReference: {fileID: 0} - target: {fileID: 8064559726283331702, guid: e56926eda34629f4fbf3e4c53f0f8bd4, type: 3} propertyPath: m_LocalPosition.y - value: 0 + value: -0.99998 objectReference: {fileID: 0} - target: {fileID: 8064559726283331702, guid: e56926eda34629f4fbf3e4c53f0f8bd4, type: 3} propertyPath: m_LocalPosition.z - value: -90 + value: -622.7231 objectReference: {fileID: 0} - target: {fileID: 8064559726283331702, guid: e56926eda34629f4fbf3e4c53f0f8bd4, type: 3} propertyPath: m_LocalRotation.w @@ -2716,15 +2828,15 @@ PrefabInstance: objectReference: {fileID: 0} - target: {fileID: 8064559726283331702, guid: e56926eda34629f4fbf3e4c53f0f8bd4, type: 3} propertyPath: m_LocalRotation.x - value: 0 + value: -0 objectReference: {fileID: 0} - target: {fileID: 8064559726283331702, guid: e56926eda34629f4fbf3e4c53f0f8bd4, type: 3} propertyPath: m_LocalRotation.y - value: 0 + value: -0 objectReference: {fileID: 0} - target: {fileID: 8064559726283331702, guid: e56926eda34629f4fbf3e4c53f0f8bd4, type: 3} propertyPath: m_LocalRotation.z - value: 0 + value: -0 objectReference: {fileID: 0} - target: {fileID: 8064559726283331702, guid: e56926eda34629f4fbf3e4c53f0f8bd4, type: 3} propertyPath: m_LocalEulerAnglesHint.x @@ -2753,19 +2865,19 @@ PrefabInstance: serializedVersion: 2 m_Modification: serializedVersion: 3 - m_TransformParent: {fileID: 0} + m_TransformParent: {fileID: 576429380} m_Modifications: - target: {fileID: 3247786716306397435, guid: f395fcc064a3a834ba957327f1387c19, type: 3} propertyPath: m_LocalPosition.x - value: -30 + value: -13.853991 objectReference: {fileID: 0} - target: {fileID: 3247786716306397435, guid: f395fcc064a3a834ba957327f1387c19, type: 3} propertyPath: m_LocalPosition.y - value: 1 + value: 0.00002002716 objectReference: {fileID: 0} - target: {fileID: 3247786716306397435, guid: f395fcc064a3a834ba957327f1387c19, type: 3} propertyPath: m_LocalPosition.z - value: -90 + value: -622.7231 objectReference: {fileID: 0} - target: {fileID: 3247786716306397435, guid: f395fcc064a3a834ba957327f1387c19, type: 3} propertyPath: m_LocalRotation.w @@ -2773,7 +2885,7 @@ PrefabInstance: objectReference: {fileID: 0} - target: {fileID: 3247786716306397435, guid: f395fcc064a3a834ba957327f1387c19, type: 3} propertyPath: m_LocalRotation.x - value: 0 + value: -0 objectReference: {fileID: 0} - target: {fileID: 3247786716306397435, guid: f395fcc064a3a834ba957327f1387c19, type: 3} propertyPath: m_LocalRotation.y @@ -2781,7 +2893,7 @@ PrefabInstance: objectReference: {fileID: 0} - target: {fileID: 3247786716306397435, guid: f395fcc064a3a834ba957327f1387c19, type: 3} propertyPath: m_LocalRotation.z - value: 0 + value: -0 objectReference: {fileID: 0} - target: {fileID: 3247786716306397435, guid: f395fcc064a3a834ba957327f1387c19, type: 3} propertyPath: m_LocalEulerAnglesHint.x @@ -2819,26 +2931,17 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 7c94274e2af2c8d4f827fe52b26c4410, type: 3} m_Name: m_EditorClassIdentifier: Assembly-CSharp::Northbound.Core +--- !u!4 &8940572951313384071 stripped +Transform: + m_CorrespondingSourceObject: {fileID: 8064559726283331702, guid: e56926eda34629f4fbf3e4c53f0f8bd4, type: 3} + m_PrefabInstance: {fileID: 4786254629656932894} + m_PrefabAsset: {fileID: 0} --- !u!1660057539 &9223372036854775807 SceneRoots: m_ObjectHideFlags: 0 m_Roots: - {fileID: 1975225896} - {fileID: 1433142232} - - {fileID: 61373299} - - {fileID: 1142746334} - - {fileID: 1199559224} - - {fileID: 4875211098963642791} - - {fileID: 782070683} - - {fileID: 4786254629656932894} - - {fileID: 447015515} - - {fileID: 955933985} - {fileID: 457600247} - - {fileID: 1166878644} - - {fileID: 946527919} - - {fileID: 1701756768} - - {fileID: 1572384099} - - {fileID: 1442785555} - - {fileID: 1440648431994998967} - - {fileID: 1061936651} - - {fileID: 463088717151812744} + - {fileID: 640318137} + - {fileID: 576429380} diff --git a/Assets/Scripts/MapGenerator.cs b/Assets/Scripts/MapGenerator.cs new file mode 100644 index 0000000..71c7596 --- /dev/null +++ b/Assets/Scripts/MapGenerator.cs @@ -0,0 +1,774 @@ +using Unity.Netcode; +using UnityEngine; +using System.Collections.Generic; +#if UNITY_EDITOR +using UnityEditor; +#endif + +namespace Northbound +{ + public class MapGenerator : NetworkBehaviour + { + public static MapGenerator Instance { get; private set; } + + [Header("Common Settings")] + [Tooltip("맵 생성 시작 시 자동 생성")] + [SerializeField] private bool generateOnSpawn = true; + [Tooltip("생성된 오브젝트를 부모로 그룹화")] + [SerializeField] private bool groupUnderParent = true; + + [Header("Map Boundaries")] + [Tooltip("X 범위 (-width/2 ~ +width/2)")] + public float playableAreaWidth = 200f; + [Tooltip("Z 시작 위치")] + public float startZ = 25f; + [Tooltip("Z 끝 위치")] + public float endZ = 700f; + + private Vector2 _initialResourcePosition; + private Vector2 _corePosition; + private Vector2 _barracksPosition; + + [Header("Resource Generation")] + [Tooltip("자원 프리팹")] + public GameObject resourcePrefab; + [Tooltip("최소 자원 개수 (초기 자원 제외)")] + [Range(8, 12)] + public int minResourceCount = 8; + [Tooltip("최대 자원 개수 (초기 자원 제외)")] + [Range(8, 12)] + public int maxResourceCount = 12; + [Tooltip("자원 간 최소 거리")] + public float minDistanceBetweenResources = 80f; + [Tooltip("코어와의 최소 거리")] + public float minDistanceFromCore = 50f; + [Tooltip("막사와의 최소 거리")] + public float minDistanceFromBarracks = 50f; + [Tooltip("초기 자원 생산량 (분당)")] + public float initialResourceProduction = 50f; + [Tooltip("추가 자원 기본 생산량 (분당)")] + public float additionalResourceBaseProduction = 25f; + [Tooltip("목표 총 생산량 (분당)")] + public float targetTotalProduction = 300f; + [Tooltip("생산량 허용 오차")] + [Range(0f, 0.2f)] + public float targetProductionTolerance = 0.05f; + [Tooltip("최소 품질 보정")] + public float minQualityModifier = -30f; + [Tooltip("최대 품질 보정")] + public float maxQualityModifier = 30f; + [Tooltip("최대 생성 시도 횟수")] + public int maxResourceGenerationAttempts = 10; + + [Header("Obstacle Settings")] + [Tooltip("배치할 장애물 목록")] + [SerializeField] private List obstacles = new List(); + [Tooltip("장애물 밀도")] + [Range(0f, 1f)] + [SerializeField] private float obstacleDensity = 0.5f; + [Tooltip("최대 장애물 개수")] + [SerializeField] private int maxTotalObstacles = 100; + [Tooltip("장애물 간 최소 거리")] + [SerializeField] private float minDistanceBetweenObstacles = 2f; + [Tooltip("배치 전 충돌 체크")] + [SerializeField] private bool checkCollision = true; + [Tooltip("충돌 체크 레이어")] + [SerializeField] private LayerMask collisionLayers = -1; + [Tooltip("충돌 체크 반경")] + [SerializeField] private float collisionCheckRadius = 1f; + [Tooltip("지형에 맞춰 배치")] + [SerializeField] private bool alignToTerrain = true; + [Tooltip("Y축 랜덤 회전 적용")] + [SerializeField] private bool randomRotation = true; + [Tooltip("크기 랜덤 변화 범위")] + [Range(0f, 0.5f)] + [SerializeField] private float scaleVariation = 0.1f; + [Tooltip("배치 시도 최대 횟수")] + [SerializeField] private int maxObstacleSpawnAttempts = 50; + + [Header("Generation Order")] + [Tooltip("자원 먼저 생성 후 장애물 생성 (true) 또는 그 반대 (false)")] + [SerializeField] private bool generateResourcesFirst = true; + + private ResourceData[] _generatedResources; + private float _globalProductionMultiplier = 1f; + private List _spawnedPositions = new List(); + private Transform _objectsParent; + + [System.Serializable] + public class ObstacleEntry + { + [Tooltip("배치할 장애물 프리팹")] + public GameObject prefab; + + [Tooltip("이 장애물의 스폰 가중치")] + [Range(1, 100)] + public int spawnWeight = 50; + + [Tooltip("최소 스폰 개수")] + public int minCount = 0; + + [Tooltip("최대 스폰 개수")] + public int maxCount = 10; + } + + public override void OnNetworkSpawn() + { + if (IsServer && generateOnSpawn) + { + Instance = this; + FindImportantPositions(); + GenerateMap(); + } + } + + private void FindImportantPositions() + { + Core core = FindObjectOfType(); + if (core != null) + { + _corePosition = new Vector2(core.transform.position.x, core.transform.position.z); + Debug.Log($"[MapGenerator] Found Core at {_corePosition}"); + } + else + { + Debug.LogWarning("[MapGenerator] Core not found in scene!"); + _corePosition = Vector2.zero; + } + + Resource[] resources = FindObjectsOfType(); + if (resources.Length > 0) + { + _initialResourcePosition = new Vector2(resources[0].transform.position.x, resources[0].transform.position.z); + Debug.Log($"[MapGenerator] Found initial Resource at {_initialResourcePosition}"); + } + else + { + Debug.LogWarning("[MapGenerator] No Resource found in scene!"); + _initialResourcePosition = Vector2.zero; + } + + GameObject barracks = GameObject.Find("Worker Hall"); + if (barracks != null) + { + _barracksPosition = new Vector2(barracks.transform.position.x, barracks.transform.position.z); + Debug.Log($"[MapGenerator] Found Worker Hall at {_barracksPosition}"); + } + else + { + Debug.LogWarning("[MapGenerator] Worker Hall not found in scene!"); + _barracksPosition = Vector2.zero; + } + } + + private void GenerateMap() + { + if (groupUnderParent) + { + _objectsParent = new GameObject("Generated Map Objects").transform; + _objectsParent.SetParent(transform); + + NetworkObject parentNetworkObj = _objectsParent.gameObject.GetComponent(); + if (parentNetworkObj == null) + { + parentNetworkObj = _objectsParent.gameObject.AddComponent(); + } + parentNetworkObj.Spawn(); + } + + _spawnedPositions.Clear(); + + if (generateResourcesFirst) + { + GenerateResources(); + GenerateObstacles(); + } + else + { + GenerateObstacles(); + GenerateResources(); + } + + Debug.Log($"[MapGenerator] Map generation complete!"); + } + + #region Resource Generation + + private void GenerateResources() + { + if (resourcePrefab == null) + { + Debug.LogError("[MapGenerator] Resource prefab not assigned!"); + return; + } + + bool success = false; + + for (int attempt = 0; attempt < maxResourceGenerationAttempts; attempt++) + { + if (TryGenerateResources(out var resources)) + { + _generatedResources = resources; + success = true; + Debug.Log($"[MapGenerator] Successfully generated resources on attempt {attempt + 1}"); + break; + } + else + { + Debug.LogWarning($"[MapGenerator] Resource generation attempt {attempt + 1} failed validation"); + } + } + + if (!success) + { + Debug.LogWarning("[MapGenerator] All resource generation attempts failed, using fallback layout"); + GenerateResourceFallbackLayout(); + } + + SpawnResources(); + } + + private bool TryGenerateResources(out ResourceData[] resources) + { + resources = null; + int additionalResourceCount = Random.Range(minResourceCount, maxResourceCount + 1); + ResourceData[] tempResources = new ResourceData[additionalResourceCount]; + + for (int i = 0; i < additionalResourceCount; i++) + { + Vector2 position; + float qualityModifier; + + if (!TryFindValidResourcePosition(tempResources, i, out position)) + { + return false; + } + + qualityModifier = Random.Range(minQualityModifier, maxQualityModifier); + float qualityMultiplier = 1f + (qualityModifier / 100f); + float finalProduction = additionalResourceBaseProduction * qualityMultiplier; + + tempResources[i] = new ResourceData + { + position = position, + baseProduction = additionalResourceBaseProduction, + qualityModifier = qualityModifier, + finalProduction = finalProduction + }; + } + + if (!ValidateProductionRate(tempResources, out float globalMultiplier)) + { + return false; + } + + _globalProductionMultiplier = globalMultiplier; + + for (int i = 0; i < tempResources.Length; i++) + { + tempResources[i].finalProduction *= _globalProductionMultiplier; + } + + resources = tempResources; + return true; + } + + private bool TryFindValidResourcePosition(ResourceData[] existingResources, int currentIndex, out Vector2 position) + { + position = Vector2.zero; + int maxAttempts = 100; + + for (int attempt = 0; attempt < maxAttempts; attempt++) + { + float x = Random.Range(-playableAreaWidth / 2f, playableAreaWidth / 2f); + float y = Random.Range(startZ, endZ); + Vector2 candidatePosition = new Vector2(x, y); + + if (IsValidResourcePosition(candidatePosition, existingResources, currentIndex)) + { + position = candidatePosition; + return true; + } + } + + return false; + } + + private bool IsValidResourcePosition(Vector2 position, ResourceData[] existingResources, int currentIndex) + { + if (Vector2.Distance(position, _initialResourcePosition) < minDistanceBetweenResources) + { + return false; + } + + if (Vector2.Distance(position, _corePosition) < minDistanceFromCore) + { + return false; + } + + if (Vector2.Distance(position, _barracksPosition) < minDistanceFromBarracks) + { + return false; + } + + for (int i = 0; i < currentIndex; i++) + { + if (Vector2.Distance(position, existingResources[i].position) < minDistanceBetweenResources) + { + return false; + } + } + + return true; + } + + private bool ValidateProductionRate(ResourceData[] additionalResources, out float globalMultiplier) + { + globalMultiplier = 1f; + float totalProduction = initialResourceProduction; + + for (int i = 0; i < additionalResources.Length; i++) + { + totalProduction += additionalResources[i].finalProduction; + } + + float minTarget = targetTotalProduction * (1f - targetProductionTolerance); + float maxTarget = targetTotalProduction * (1f + targetProductionTolerance); + + if (totalProduction >= minTarget && totalProduction <= maxTarget) + { + globalMultiplier = 1f; + return true; + } + + globalMultiplier = targetTotalProduction / totalProduction; + float adjustedTotal = totalProduction * globalMultiplier; + + if (adjustedTotal >= minTarget && adjustedTotal <= maxTarget) + { + return true; + } + + return false; + } + + private void GenerateResourceFallbackLayout() + { + _generatedResources = new ResourceData[] + { + new ResourceData { position = new Vector2(30, 100), baseProduction = additionalResourceBaseProduction, qualityModifier = 0f, finalProduction = additionalResourceBaseProduction }, + new ResourceData { position = new Vector2(50, 200), baseProduction = additionalResourceBaseProduction, qualityModifier = 0f, finalProduction = additionalResourceBaseProduction }, + new ResourceData { position = new Vector2(20, 300), baseProduction = additionalResourceBaseProduction, qualityModifier = 0f, finalProduction = additionalResourceBaseProduction }, + new ResourceData { position = new Vector2(60, 400), baseProduction = additionalResourceBaseProduction, qualityModifier = 0f, finalProduction = additionalResourceBaseProduction }, + new ResourceData { position = new Vector2(35, 500), baseProduction = additionalResourceBaseProduction, qualityModifier = 0f, finalProduction = additionalResourceBaseProduction }, + new ResourceData { position = new Vector2(55, 600), baseProduction = additionalResourceBaseProduction, qualityModifier = 0f, finalProduction = additionalResourceBaseProduction }, + new ResourceData { position = new Vector2(25, 700), baseProduction = additionalResourceBaseProduction, qualityModifier = 0f, finalProduction = additionalResourceBaseProduction }, + new ResourceData { position = new Vector2(45, 150), baseProduction = additionalResourceBaseProduction, qualityModifier = 0f, finalProduction = additionalResourceBaseProduction }, + new ResourceData { position = new Vector2(65, 250), baseProduction = additionalResourceBaseProduction, qualityModifier = 0f, finalProduction = additionalResourceBaseProduction } + }; + + float totalProduction = initialResourceProduction; + for (int i = 0; i < _generatedResources.Length; i++) + { + totalProduction += _generatedResources[i].finalProduction; + } + + _globalProductionMultiplier = targetTotalProduction / totalProduction; + for (int i = 0; i < _generatedResources.Length; i++) + { + _generatedResources[i].finalProduction *= _globalProductionMultiplier; + } + + Debug.Log($"[MapGenerator] Resource fallback layout generated. Global multiplier: {_globalProductionMultiplier:F2}"); + } + + private void SpawnResources() + { + float totalProduction = initialResourceProduction; + + for (int i = 0; i < _generatedResources.Length; i++) + { + GameObject resourceObj = Instantiate(resourcePrefab, new Vector3(_generatedResources[i].position.x, 1f, _generatedResources[i].position.y), Quaternion.identity); + + Resource resource = resourceObj.GetComponent(); + if (resource == null) + { + Debug.LogError($"[MapGenerator] Resource prefab at index {i} doesn't have Resource component!"); + Destroy(resourceObj); + continue; + } + + resource.InitializeQuality(_generatedResources[i].qualityModifier); + Debug.Log($"[MapGenerator] Spawned resource at {_generatedResources[i].position} with quality: {_generatedResources[i].qualityModifier:F1}% (Actual Max: {resource.ActualMaxResources}, Actual Recharge: {resource.ActualRechargeAmount})"); + + NetworkObject networkObj = resourceObj.GetComponent(); + if (networkObj != null) + { + networkObj.Spawn(); + if (groupUnderParent && _objectsParent != null) + { + resourceObj.transform.SetParent(_objectsParent); + } + } + else + { + Debug.LogError($"[MapGenerator] Resource prefab at index {i} doesn't have NetworkObject component!"); + Destroy(resourceObj); + } + + _spawnedPositions.Add(new Vector3(_generatedResources[i].position.x, 1f, _generatedResources[i].position.y)); + totalProduction += _generatedResources[i].finalProduction; + } + + Debug.Log($"[MapGenerator] Spawned {_generatedResources.Length} additional resources (plus initial at {_initialResourcePosition}). Total production: {totalProduction:F2} mana/min. Global multiplier: {_globalProductionMultiplier:F2}"); + } + + #endregion + + #region Obstacle Generation + + private void GenerateObstacles() + { + if (obstacles.Count == 0) + { + Debug.Log("[MapGenerator] No obstacles configured, skipping obstacle generation."); + return; + } + + int totalSpawned = 0; + int targetCount = Mathf.RoundToInt(maxTotalObstacles * obstacleDensity); + + Debug.Log($"[MapGenerator] Starting obstacle generation. Target: {targetCount}, Density: {obstacleDensity}"); + + foreach (var obstacle in obstacles) + { + if (obstacle.prefab == null) + { + Debug.LogWarning($"[MapGenerator] Obstacle prefab is null!"); + continue; + } + + int minRequired = obstacle.minCount; + for (int i = 0; i < minRequired && totalSpawned < maxTotalObstacles; i++) + { + if (TrySpawnObstacle(obstacle)) + { + totalSpawned++; + Debug.Log($"[MapGenerator] Spawned min required obstacle: {obstacle.prefab.name}"); + } + } + } + + int consecutiveFailures = 0; + int maxConsecutiveFailures = 50; + + while (totalSpawned < targetCount && consecutiveFailures < maxConsecutiveFailures) + { + ObstacleEntry selectedObstacle = SelectRandomObstacle(); + if (selectedObstacle == null || selectedObstacle.prefab == null) + { + consecutiveFailures++; + continue; + } + + int currentCount = CountObstacleType(selectedObstacle.prefab); + if (currentCount >= selectedObstacle.maxCount) + { + consecutiveFailures++; + if (AllObstaclesAtMax()) + { + break; + } + continue; + } + + if (TrySpawnObstacle(selectedObstacle)) + { + totalSpawned++; + consecutiveFailures = 0; + } + else + { + consecutiveFailures++; + } + } + + Debug.Log($"[MapGenerator] Spawned {totalSpawned} obstacles (target: {targetCount}). Consecutive failures: {consecutiveFailures}"); + } + + private bool TrySpawnObstacle(ObstacleEntry obstacleEntry) + { + for (int attempt = 0; attempt < maxObstacleSpawnAttempts; attempt++) + { + Vector3 randomPos = GetRandomPositionInPlayableArea(); + + if (!IsValidObstaclePosition(randomPos)) + { + continue; + } + + if (checkCollision && Physics.CheckSphere(randomPos, collisionCheckRadius, collisionLayers)) + { + continue; + } + + if (alignToTerrain) + { + if (Physics.Raycast(randomPos + Vector3.up * 100f, Vector3.down, out RaycastHit hit, 200f)) + { + randomPos = hit.point; + } + } + + randomPos.y = 1f; + + Quaternion rotation = randomRotation + ? Quaternion.Euler(0, Random.Range(0f, 360f), 0) + : Quaternion.identity; + + GameObject obstacle = Instantiate(obstacleEntry.prefab, randomPos, rotation); + + if (scaleVariation > 0) + { + float scale = 1f + Random.Range(-scaleVariation, scaleVariation); + obstacle.transform.localScale *= scale; + } + + NetworkObject networkObj = obstacle.GetComponent(); + if (networkObj == null) + { + networkObj = obstacle.AddComponent(); + } + + networkObj.Spawn(); + + if (groupUnderParent && _objectsParent != null) + { + obstacle.transform.SetParent(_objectsParent); + } + + if (obstacle.GetComponent() == null) + { + var visibility = obstacle.AddComponent(); + visibility.showInExploredAreas = false; + visibility.updateInterval = 0.2f; + } + + _spawnedPositions.Add(randomPos); + return true; + } + + return false; + } + + private Vector3 GetRandomPositionInPlayableArea() + { + Vector3 center = transform.position; + float x = Random.Range(-playableAreaWidth / 2f, playableAreaWidth / 2f); + float z = Random.Range(startZ, endZ); + + return new Vector3(x, center.y, z); + } + + private bool IsValidObstaclePosition(Vector3 position) + { + foreach (var spawnedPos in _spawnedPositions) + { + if (Vector3.Distance(position, spawnedPos) < minDistanceBetweenObstacles) + { + return false; + } + } + + if (Vector2.Distance(new Vector2(position.x, position.z), _corePosition) < minDistanceFromCore) + { + return false; + } + + if (Vector2.Distance(new Vector2(position.x, position.z), _barracksPosition) < minDistanceFromBarracks) + { + return false; + } + + return true; + } + + private ObstacleEntry SelectRandomObstacle() + { + if (obstacles.Count == 0) return null; + + int totalWeight = 0; + foreach (var obstacle in obstacles) + { + if (obstacle.prefab != null) + { + totalWeight += obstacle.spawnWeight; + } + } + + if (totalWeight == 0) return null; + + int randomValue = Random.Range(0, totalWeight); + int currentWeight = 0; + + foreach (var obstacle in obstacles) + { + if (obstacle.prefab == null) continue; + + currentWeight += obstacle.spawnWeight; + if (randomValue < currentWeight) + { + return obstacle; + } + } + + return obstacles[0]; + } + + private bool AllObstaclesAtMax() + { + foreach (var obstacle in obstacles) + { + if (obstacle.prefab == null) continue; + + int currentCount = CountObstacleType(obstacle.prefab); + if (currentCount < obstacle.maxCount) + { + return false; + } + } + return true; + } + + private int CountObstacleType(GameObject prefab) + { + int count = 0; + if (_objectsParent != null) + { + foreach (Transform child in _objectsParent) + { + if (child.name.StartsWith(prefab.name)) + { + count++; + } + } + } + return count; + } + + #endregion + + #region Public Methods + + public float GetTotalProduction() + { + if (_generatedResources == null) + return initialResourceProduction; + + float total = initialResourceProduction; + for (int i = 0; i < _generatedResources.Length; i++) + { + total += _generatedResources[i].finalProduction; + } + return total; + } + + public void ClearGeneratedObjects() + { + if (_objectsParent != null) + { + NetworkObject parentNetworkObj = _objectsParent.GetComponent(); + if (parentNetworkObj != null && parentNetworkObj.IsSpawned) + { + parentNetworkObj.Despawn(true); + } + + foreach (Transform child in _objectsParent) + { + NetworkObject networkObj = child.GetComponent(); + if (networkObj != null && networkObj.IsSpawned) + { + networkObj.Despawn(true); + } + } + + if (Application.isPlaying) + { + Destroy(_objectsParent.gameObject); + } + else + { + DestroyImmediate(_objectsParent.gameObject); + } + _objectsParent = null; + } + + _spawnedPositions.Clear(); + } + + public void RegenerateMap() + { + ClearGeneratedObjects(); + GenerateMap(); + } + + #endregion + + #region Editor + + private void OnDrawGizmos() + { + Vector3 center = transform.position; + center.x = 0f; + center.z = (startZ + endZ) / 2f; + float height = endZ - startZ; + + Gizmos.color = new Color(0, 1, 0, 0.3f); + Gizmos.DrawCube(center, new Vector3(playableAreaWidth, 0.1f, height)); + + Gizmos.color = Color.green; + Gizmos.DrawWireCube(center, new Vector3(playableAreaWidth, 0.1f, height)); + + Gizmos.color = Color.cyan; + Gizmos.DrawWireSphere(new Vector3(_corePosition.x, 0, _corePosition.y), minDistanceFromCore); + Gizmos.color = Color.magenta; + Gizmos.DrawWireSphere(new Vector3(_barracksPosition.x, 0, _barracksPosition.y), minDistanceFromBarracks); + } + + private void OnDrawGizmosSelected() + { + #if UNITY_EDITOR + Gizmos.color = Color.yellow; + foreach (var pos in _spawnedPositions) + { + Gizmos.DrawWireSphere(pos, minDistanceBetweenObstacles / 2f); + } + + if (_generatedResources != null) + { + Gizmos.color = Color.blue; + foreach (var resource in _generatedResources) + { + Gizmos.DrawWireSphere(new Vector3(resource.position.x, 0, resource.position.y), 5f); + } + } + + if (resourcePrefab != null) + { + Gizmos.color = Color.blue; + Gizmos.DrawWireSphere(new Vector3(_initialResourcePosition.x, 0, _initialResourcePosition.y), 5f); + } + #endif + } + + #endregion + } + + public struct ResourceData + { + public Vector2 position; + public float baseProduction; + public float qualityModifier; + public float finalProduction; + } +} diff --git a/Assets/Scripts/MapGenerator.cs.meta b/Assets/Scripts/MapGenerator.cs.meta new file mode 100644 index 0000000..6008966 --- /dev/null +++ b/Assets/Scripts/MapGenerator.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: f0dde802bf94bf6448eb7d4838d1c42a \ No newline at end of file diff --git a/Assets/Scripts/Resource.cs b/Assets/Scripts/Resource.cs index 83ccfe3..390e9e3 100644 --- a/Assets/Scripts/Resource.cs +++ b/Assets/Scripts/Resource.cs @@ -19,6 +19,24 @@ namespace Northbound public float rechargeInterval = 5f; // 충전 주기 (초) public int rechargeAmount = 10; // 주기당 충전량 + [Header("Quality (Runtime)")] + [SerializeField] private NetworkVariable _qualityPercentage = new NetworkVariable( + 0f, + NetworkVariableReadPermission.Everyone, + NetworkVariableWritePermission.Server + ); + + [Tooltip("품질 보정율 (-30% ~ +30%)")] + [SerializeField] private float _displayQuality = 0f; + + [Tooltip("품질 적용 후 최대 자원량")] + [SerializeField] private int _displayMaxResources = 100; + + [Tooltip("품질 적용 후 충전량")] + [SerializeField] private int _displayRechargeAmount = 10; + + private bool _isQualityInitialized = false; + [Header("Animation")] public string interactionAnimationTrigger = "Mining"; // 플레이어 애니메이션 트리거 @@ -107,13 +125,83 @@ namespace Northbound private float _lastGatheringTime; private float _lastRechargeTime; + public float QualityPercentage => _qualityPercentage.Value; + + public int ActualMaxResources + { + get + { + float multiplier = 1f + (_qualityPercentage.Value / 100f); + return Mathf.RoundToInt(maxResources * multiplier); + } + } + + public int ActualRechargeAmount + { + get + { + float multiplier = 1f + (_qualityPercentage.Value / 100f); + return Mathf.RoundToInt(rechargeAmount * multiplier); + } + } + public override void OnNetworkSpawn() + { + _qualityPercentage.OnValueChanged += OnQualityChanged; + + if (IsServer) + { + if (!_isQualityInitialized) + { + _qualityPercentage.Value = Random.Range(-30f, 30f); + } + + _currentResources.Value = ActualMaxResources; + _lastRechargeTime = Time.time; + _lastGatheringTime = Time.time - gatheringCooldown; + } + + _displayQuality = _qualityPercentage.Value; + UpdateDisplayValues(); + } + + public void InitializeQuality(float qualityPercentage) { if (IsServer) { - _currentResources.Value = maxResources; - _lastRechargeTime = Time.time; - _lastGatheringTime = Time.time - gatheringCooldown; + _qualityPercentage.Value = qualityPercentage; + _displayQuality = qualityPercentage; + _isQualityInitialized = true; + } + else if (IsClient) + { + _displayQuality = qualityPercentage; + UpdateDisplayValues(); + } + } + + public override void OnNetworkDespawn() + { + _qualityPercentage.OnValueChanged -= OnQualityChanged; + } + + private void OnQualityChanged(float previous, float current) + { + _displayQuality = current; + UpdateDisplayValues(); + } + + private void UpdateDisplayValues() + { + if (IsClient || IsServer) + { + _displayMaxResources = ActualMaxResources; + _displayRechargeAmount = ActualRechargeAmount; + } + else + { + _displayMaxResources = maxResources; + _displayRechargeAmount = rechargeAmount; } } @@ -125,12 +213,12 @@ namespace Northbound // 자원 충전 로직 if (Time.time - _lastRechargeTime >= rechargeInterval) { - if (_currentResources.Value < maxResources) + if (_currentResources.Value < ActualMaxResources) { - int rechargeAmountToAdd = Mathf.Min(rechargeAmount, maxResources - _currentResources.Value); + int rechargeAmountToAdd = Mathf.Min(ActualRechargeAmount, ActualMaxResources - _currentResources.Value); _currentResources.Value += rechargeAmountToAdd; - // Debug.Log($"{resourceName} {rechargeAmountToAdd} 충전됨. 현재: {_currentResources.Value}/{maxResources}"); + // Debug.Log($"{resourceName} {rechargeAmountToAdd} 충전됨. 현재: {_currentResources.Value}/{ActualMaxResources}"); } _lastRechargeTime = Time.time; @@ -266,7 +354,7 @@ namespace Northbound if (_currentResources.Value <= 0) return "자원 충전 중..."; - return $"[E] {resourceName} 채집 ({_currentResources.Value}/{maxResources})"; + return $"[E] {resourceName} 채집 ({_currentResources.Value}/{ActualMaxResources})"; } public string GetInteractionAnimation()