diff --git a/Assembly-CSharp-Editor.csproj b/Assembly-CSharp-Editor.csproj index 6d7e972..7b1079b 100644 --- a/Assembly-CSharp-Editor.csproj +++ b/Assembly-CSharp-Editor.csproj @@ -1178,30 +1178,18 @@ C:\Program Files\Unity\Hub\Editor\6000.3.3f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Xml.XPath.XDocument.dll False - - Library\ScriptAssemblies\UnityEditor.TestRunner.dll - False - Library\ScriptAssemblies\UnityEngine.TestRunner.dll False - - Library\ScriptAssemblies\Unity.Multiplayer.Tools.MetricEvents.dll - False - - - Library\ScriptAssemblies\Unity.RenderPipelines.Core.Editor.dll + + Library\ScriptAssemblies\UnityEditor.TestRunner.dll False Library\ScriptAssemblies\Unity.2D.Common.Path.Editor.dll False - - Library\ScriptAssemblies\Unity.Netcode.Editor.dll - False - Library\ScriptAssemblies\Unity.2D.Sprite.Editor.dll False @@ -1226,14 +1214,6 @@ Library\ScriptAssemblies\Unity.Multiplayer.Tools.MetricTestData.dll False - - Library\ScriptAssemblies\Unity.Multiplayer.Tools.NetworkSolutionInterface.dll - False - - - Library\ScriptAssemblies\Unity.Multiplayer.Tools.NetStats.dll - False - Library\ScriptAssemblies\Unity.Rendering.LightTransport.Editor.dll False @@ -1246,22 +1226,10 @@ Library\ScriptAssemblies\Unity.RenderPipelines.Core.ShaderLibrary.dll False - - Library\ScriptAssemblies\Unity.2D.IK.Runtime.dll - False - - - Library\ScriptAssemblies\Unity.Multiplayer.Tools.NetworkSimulator.Runtime.dll - False - Library\ScriptAssemblies\Unity.VisualScripting.SettingsProvider.Editor.dll False - - Library\ScriptAssemblies\Unity.Multiplayer.Tools.DependencyInjection.dll - False - Library\ScriptAssemblies\Unity.Networking.Editor.dll False @@ -1274,20 +1242,8 @@ Library\ScriptAssemblies\Unity.Splines.Editor.dll False - - Library\ScriptAssemblies\Unity.RenderPipelines.Universal.2D.Editor.Overrides.dll - False - - - Library\ScriptAssemblies\Unity.RenderPipelines.Universal.Editor.dll - False - - - Library\ScriptAssemblies\Unity.Multiplayer.Tools.NetworkProfiler.Runtime.dll - False - - - Library\ScriptAssemblies\Unity.Timeline.dll + + Library\ScriptAssemblies\PPv2URPConverters.dll False @@ -1298,12 +1254,12 @@ Library\ScriptAssemblies\Unity.Multiplayer.Tools.NetVis.Configuration.dll False - - Library\ScriptAssemblies\Unity.2D.Tooling.Editor.dll + + Library\ScriptAssemblies\Unity.AI.Navigation.Editor.dll False - - Library\ScriptAssemblies\PPv2URPConverters.dll + + Library\ScriptAssemblies\Unity.AI.Navigation.Updater.dll False @@ -1322,6 +1278,170 @@ Library\ScriptAssemblies\Unity.RenderPipelines.Universal.2D.Runtime.dll False + + Library\ScriptAssemblies\Unity.Multiplayer.Tools.NetStatsMonitor.Configuration.dll + False + + + Library\ScriptAssemblies\Unity.UnifiedRayTracing.Runtime.dll + False + + + Library\ScriptAssemblies\Unity.2D.Aseprite.Common.dll + False + + + Library\ScriptAssemblies\Unity.Rider.Editor.dll + False + + + Library\ScriptAssemblies\Unity.2D.Aseprite.Editor.dll + False + + + Library\ScriptAssemblies\Unity.AI.Navigation.Editor.ConversionSystem.dll + False + + + Library\ScriptAssemblies\Unity.RenderPipelines.ShaderGraph.ShaderGraphLibrary.dll + False + + + Library\ScriptAssemblies\Unity.VisualScripting.Flow.dll + False + + + Library\ScriptAssemblies\Unity.TextMeshPro.Editor.dll + False + + + Library\ScriptAssemblies\Unity.2D.Tilemap.Extras.Editor.dll + False + + + Library\ScriptAssemblies\Unity.VisualStudio.Editor.dll + False + + + Library\ScriptAssemblies\Unity.2D.Tilemap.Editor.dll + False + + + Library\ScriptAssemblies\Unity.InputSystem.ForUI.dll + False + + + Library\ScriptAssemblies\Unity.Profiling.Core.dll + False + + + Library\ScriptAssemblies\Unity.RenderPipelines.GPUDriven.Runtime.dll + False + + + Library\ScriptAssemblies\Unity.VisualScripting.State.Editor.dll + False + + + Library\ScriptAssemblies\Unity.RenderPipelines.Universal.Shaders.dll + False + + + Library\ScriptAssemblies\UnityEngine.UI.dll + False + + + Library\ScriptAssemblies\com.unity.multiplayer.tools.window.dll + False + + + Library\ScriptAssemblies\Unity.RenderPipelines.Universal.Config.Runtime.dll + False + + + Library\ScriptAssemblies\Unity.Multiplayer.Tools.Common.dll + False + + + Library\ScriptAssemblies\Unity.2D.Sprite.AIIntegration.Editor.dll + False + + + Library\ScriptAssemblies\Unity.2D.SpriteShape.Editor.dll + False + + + Library\ScriptAssemblies\Unity.RenderPipelines.Core.Editor.Shared.dll + False + + + Library\ScriptAssemblies\Unity.InputSystem.dll + False + + + Library\ScriptAssemblies\Unity.Splines.dll + False + + + Library\ScriptAssemblies\Unity.Multiplayer.Tools.MetricEvents.dll + False + + + Library\ScriptAssemblies\Unity.RenderPipelines.Core.Editor.dll + False + + + Library\ScriptAssemblies\Unity.Netcode.Editor.dll + False + + + Library\ScriptAssemblies\Unity.Multiplayer.Tools.NetworkSolutionInterface.dll + False + + + Library\ScriptAssemblies\Unity.Multiplayer.Tools.NetStats.dll + False + + + Library\ScriptAssemblies\Unity.AI.Navigation.dll + False + + + Library\ScriptAssemblies\Unity.2D.IK.Runtime.dll + False + + + Library\ScriptAssemblies\Unity.Multiplayer.Tools.NetworkSimulator.Runtime.dll + False + + + Library\ScriptAssemblies\Unity.Multiplayer.Tools.DependencyInjection.dll + False + + + Library\ScriptAssemblies\Unity.2D.Animation.Editor.dll + False + + + Library\ScriptAssemblies\Unity.RenderPipelines.Universal.2D.Editor.Overrides.dll + False + + + Library\ScriptAssemblies\Unity.RenderPipelines.Universal.Editor.dll + False + + + Library\ScriptAssemblies\Unity.Multiplayer.Tools.NetworkProfiler.Runtime.dll + False + + + Library\ScriptAssemblies\Unity.Timeline.dll + False + + + Library\ScriptAssemblies\Unity.2D.Tooling.Editor.dll + False + Library\ScriptAssemblies\Unity.VisualScripting.Shared.Editor.dll False @@ -1334,10 +1454,6 @@ Library\ScriptAssemblies\Unity.PlasticSCM.Editor.dll False - - Library\ScriptAssemblies\Unity.2D.Animation.Editor.dll - False - Library\ScriptAssemblies\Unity.Burst.Editor.dll False @@ -1354,18 +1470,10 @@ Library\ScriptAssemblies\Unity.Mathematics.Editor.dll False - - Library\ScriptAssemblies\Unity.Multiplayer.Tools.NetStatsMonitor.Configuration.dll - False - Library\ScriptAssemblies\Unity.Cinemachine.Editor.dll False - - Library\ScriptAssemblies\Unity.UnifiedRayTracing.Runtime.dll - False - Library\ScriptAssemblies\Unity.Cinemachine.dll False @@ -1374,26 +1482,10 @@ Library\ScriptAssemblies\Unity.Multiplayer.Tools.NetVis.Editor.Visualization.dll False - - Library\ScriptAssemblies\Unity.2D.Aseprite.Common.dll - False - - - Library\ScriptAssemblies\Unity.Rider.Editor.dll - False - Library\ScriptAssemblies\Unity.Multiplayer.Tools.NetStatsMonitor.Editor.dll False - - Library\ScriptAssemblies\Unity.2D.Aseprite.Editor.dll - False - - - Library\ScriptAssemblies\Unity.2D.SpriteShape.Editor.dll - False - Library\ScriptAssemblies\Unity.Multiplayer.Tools.DependencyInjection.UIElements.dll False @@ -1406,22 +1498,6 @@ Library\ScriptAssemblies\Unity.RenderPipelines.Core.Runtime.dll False - - Library\ScriptAssemblies\Unity.VisualScripting.Flow.dll - False - - - Library\ScriptAssemblies\Unity.RenderPipelines.ShaderGraph.ShaderGraphLibrary.dll - False - - - Library\ScriptAssemblies\Unity.TextMeshPro.Editor.dll - False - - - Library\ScriptAssemblies\Unity.2D.Tilemap.Extras.Editor.dll - False - Library\ScriptAssemblies\Unity.Netcode.PackageChecker.Editor.dll False @@ -1446,14 +1522,6 @@ Library\ScriptAssemblies\Unity.2D.SpriteShape.Runtime.dll False - - Library\ScriptAssemblies\Unity.VisualStudio.Editor.dll - False - - - Library\ScriptAssemblies\Unity.2D.Tilemap.Editor.dll - False - Library\ScriptAssemblies\Unity.2D.Common.Runtime.dll False @@ -1462,30 +1530,14 @@ Library\ScriptAssemblies\Unity.Collections.dll False - - Library\ScriptAssemblies\Unity.Profiling.Core.dll - False - Library\ScriptAssemblies\Unity.Networking.Transport.dll False - - Library\ScriptAssemblies\Unity.InputSystem.ForUI.dll - False - Library\ScriptAssemblies\Unity.Timeline.Editor.dll False - - Library\ScriptAssemblies\Unity.VisualScripting.State.Editor.dll - False - - - Library\ScriptAssemblies\Unity.RenderPipelines.GPUDriven.Runtime.dll - False - Library\ScriptAssemblies\Unity.Mathematics.dll False @@ -1494,14 +1546,6 @@ Library\ScriptAssemblies\Unity.Multiplayer.Tools.NetworkProfiler.Editor.dll False - - Library\ScriptAssemblies\Unity.RenderPipelines.Universal.Shaders.dll - False - - - Library\ScriptAssemblies\UnityEngine.UI.dll - False - Library\ScriptAssemblies\Unity.Multiplayer.Center.Common.dll False @@ -1522,22 +1566,10 @@ Library\ScriptAssemblies\Unity.Multiplayer.Center.Editor.dll False - - Library\ScriptAssemblies\com.unity.multiplayer.tools.window.dll - False - Library\ScriptAssemblies\Unity.VisualScripting.State.dll False - - Library\ScriptAssemblies\Unity.RenderPipelines.Universal.Config.Runtime.dll - False - - - Library\ScriptAssemblies\Unity.Multiplayer.Tools.Common.dll - False - Library\ScriptAssemblies\Unity.RenderPipeline.Universal.ShaderLibrary.dll False @@ -1554,38 +1586,22 @@ Library\ScriptAssemblies\Unity.Multiplayer.Tools.NetStatsMonitor.Implementation.dll False - - Library\ScriptAssemblies\Unity.2D.Sprite.AIIntegration.Editor.dll - False - - - Library\ScriptAssemblies\Unity.RenderPipelines.Core.Editor.Shared.dll + + Library\ScriptAssemblies\Unity.Multiplayer.Tools.NetworkSimulator.Editor.dll False Library\ScriptAssemblies\Unity.Multiplayer.Tools.Initialization.dll False - - Library\ScriptAssemblies\Unity.InputSystem.dll - False - Library\ScriptAssemblies\Unity.Multiplayer.Tools.NetStatsMonitor.Component.dll False - - Library\ScriptAssemblies\Unity.Multiplayer.Tools.NetworkSimulator.Editor.dll - False - Library\ScriptAssemblies\Unity.Settings.Editor.dll False - - Library\ScriptAssemblies\Unity.Splines.dll - False - Library\ScriptAssemblies\Unity.Multiplayer.Tools.NetVis.Editor.UI.dll False @@ -1597,9 +1613,9 @@ - + diff --git a/Assembly-CSharp.csproj b/Assembly-CSharp.csproj index c99ac11..73efd78 100644 --- a/Assembly-CSharp.csproj +++ b/Assembly-CSharp.csproj @@ -51,7 +51,10 @@ + + + @@ -60,9 +63,12 @@ + + + @@ -87,6 +93,7 @@ + @@ -1186,22 +1193,10 @@ C:\Program Files\Unity\Hub\Editor\6000.3.3f1\Editor\Data\NetStandard\compat\2.1.0\shims\netfx\System.Xml.Serialization.dll False - - Library\ScriptAssemblies\Unity.Multiplayer.Tools.MetricEvents.dll - False - - - Library\ScriptAssemblies\Unity.RenderPipelines.Core.Editor.dll - False - Library\ScriptAssemblies\Unity.2D.Common.Path.Editor.dll False - - Library\ScriptAssemblies\Unity.Netcode.Editor.dll - False - Library\ScriptAssemblies\Unity.2D.Sprite.Editor.dll False @@ -1226,14 +1221,6 @@ Library\ScriptAssemblies\Unity.Multiplayer.Tools.MetricTestData.dll False - - Library\ScriptAssemblies\Unity.Multiplayer.Tools.NetworkSolutionInterface.dll - False - - - Library\ScriptAssemblies\Unity.Multiplayer.Tools.NetStats.dll - False - Library\ScriptAssemblies\Unity.Rendering.LightTransport.Editor.dll False @@ -1246,22 +1233,10 @@ Library\ScriptAssemblies\Unity.RenderPipelines.Core.ShaderLibrary.dll False - - Library\ScriptAssemblies\Unity.2D.IK.Runtime.dll - False - - - Library\ScriptAssemblies\Unity.Multiplayer.Tools.NetworkSimulator.Runtime.dll - False - Library\ScriptAssemblies\Unity.VisualScripting.SettingsProvider.Editor.dll False - - Library\ScriptAssemblies\Unity.Multiplayer.Tools.DependencyInjection.dll - False - Library\ScriptAssemblies\Unity.Networking.Editor.dll False @@ -1274,20 +1249,8 @@ Library\ScriptAssemblies\Unity.Splines.Editor.dll False - - Library\ScriptAssemblies\Unity.RenderPipelines.Universal.2D.Editor.Overrides.dll - False - - - Library\ScriptAssemblies\Unity.RenderPipelines.Universal.Editor.dll - False - - - Library\ScriptAssemblies\Unity.Multiplayer.Tools.NetworkProfiler.Runtime.dll - False - - - Library\ScriptAssemblies\Unity.Timeline.dll + + Library\ScriptAssemblies\PPv2URPConverters.dll False @@ -1298,12 +1261,12 @@ Library\ScriptAssemblies\Unity.Multiplayer.Tools.NetVis.Configuration.dll False - - Library\ScriptAssemblies\Unity.2D.Tooling.Editor.dll + + Library\ScriptAssemblies\Unity.AI.Navigation.Editor.dll False - - Library\ScriptAssemblies\PPv2URPConverters.dll + + Library\ScriptAssemblies\Unity.AI.Navigation.Updater.dll False @@ -1322,6 +1285,170 @@ Library\ScriptAssemblies\Unity.RenderPipelines.Universal.2D.Runtime.dll False + + Library\ScriptAssemblies\Unity.Multiplayer.Tools.NetStatsMonitor.Configuration.dll + False + + + Library\ScriptAssemblies\Unity.UnifiedRayTracing.Runtime.dll + False + + + Library\ScriptAssemblies\Unity.2D.Aseprite.Common.dll + False + + + Library\ScriptAssemblies\Unity.Rider.Editor.dll + False + + + Library\ScriptAssemblies\Unity.2D.Aseprite.Editor.dll + False + + + Library\ScriptAssemblies\Unity.AI.Navigation.Editor.ConversionSystem.dll + False + + + Library\ScriptAssemblies\Unity.RenderPipelines.ShaderGraph.ShaderGraphLibrary.dll + False + + + Library\ScriptAssemblies\Unity.VisualScripting.Flow.dll + False + + + Library\ScriptAssemblies\Unity.TextMeshPro.Editor.dll + False + + + Library\ScriptAssemblies\Unity.2D.Tilemap.Extras.Editor.dll + False + + + Library\ScriptAssemblies\Unity.VisualStudio.Editor.dll + False + + + Library\ScriptAssemblies\Unity.2D.Tilemap.Editor.dll + False + + + Library\ScriptAssemblies\Unity.InputSystem.ForUI.dll + False + + + Library\ScriptAssemblies\Unity.Profiling.Core.dll + False + + + Library\ScriptAssemblies\Unity.RenderPipelines.GPUDriven.Runtime.dll + False + + + Library\ScriptAssemblies\Unity.VisualScripting.State.Editor.dll + False + + + Library\ScriptAssemblies\Unity.RenderPipelines.Universal.Shaders.dll + False + + + Library\ScriptAssemblies\UnityEngine.UI.dll + False + + + Library\ScriptAssemblies\com.unity.multiplayer.tools.window.dll + False + + + Library\ScriptAssemblies\Unity.RenderPipelines.Universal.Config.Runtime.dll + False + + + Library\ScriptAssemblies\Unity.Multiplayer.Tools.Common.dll + False + + + Library\ScriptAssemblies\Unity.2D.Sprite.AIIntegration.Editor.dll + False + + + Library\ScriptAssemblies\Unity.2D.SpriteShape.Editor.dll + False + + + Library\ScriptAssemblies\Unity.RenderPipelines.Core.Editor.Shared.dll + False + + + Library\ScriptAssemblies\Unity.InputSystem.dll + False + + + Library\ScriptAssemblies\Unity.Splines.dll + False + + + Library\ScriptAssemblies\Unity.Multiplayer.Tools.MetricEvents.dll + False + + + Library\ScriptAssemblies\Unity.RenderPipelines.Core.Editor.dll + False + + + Library\ScriptAssemblies\Unity.Netcode.Editor.dll + False + + + Library\ScriptAssemblies\Unity.Multiplayer.Tools.NetworkSolutionInterface.dll + False + + + Library\ScriptAssemblies\Unity.Multiplayer.Tools.NetStats.dll + False + + + Library\ScriptAssemblies\Unity.AI.Navigation.dll + False + + + Library\ScriptAssemblies\Unity.2D.IK.Runtime.dll + False + + + Library\ScriptAssemblies\Unity.Multiplayer.Tools.NetworkSimulator.Runtime.dll + False + + + Library\ScriptAssemblies\Unity.Multiplayer.Tools.DependencyInjection.dll + False + + + Library\ScriptAssemblies\Unity.2D.Animation.Editor.dll + False + + + Library\ScriptAssemblies\Unity.RenderPipelines.Universal.2D.Editor.Overrides.dll + False + + + Library\ScriptAssemblies\Unity.RenderPipelines.Universal.Editor.dll + False + + + Library\ScriptAssemblies\Unity.Multiplayer.Tools.NetworkProfiler.Runtime.dll + False + + + Library\ScriptAssemblies\Unity.Timeline.dll + False + + + Library\ScriptAssemblies\Unity.2D.Tooling.Editor.dll + False + Library\ScriptAssemblies\Unity.VisualScripting.Shared.Editor.dll False @@ -1334,10 +1461,6 @@ Library\ScriptAssemblies\Unity.PlasticSCM.Editor.dll False - - Library\ScriptAssemblies\Unity.2D.Animation.Editor.dll - False - Library\ScriptAssemblies\Unity.Burst.Editor.dll False @@ -1354,18 +1477,10 @@ Library\ScriptAssemblies\Unity.Mathematics.Editor.dll False - - Library\ScriptAssemblies\Unity.Multiplayer.Tools.NetStatsMonitor.Configuration.dll - False - Library\ScriptAssemblies\Unity.Cinemachine.Editor.dll False - - Library\ScriptAssemblies\Unity.UnifiedRayTracing.Runtime.dll - False - Library\ScriptAssemblies\Unity.Cinemachine.dll False @@ -1374,26 +1489,10 @@ Library\ScriptAssemblies\Unity.Multiplayer.Tools.NetVis.Editor.Visualization.dll False - - Library\ScriptAssemblies\Unity.2D.Aseprite.Common.dll - False - - - Library\ScriptAssemblies\Unity.Rider.Editor.dll - False - Library\ScriptAssemblies\Unity.Multiplayer.Tools.NetStatsMonitor.Editor.dll False - - Library\ScriptAssemblies\Unity.2D.Aseprite.Editor.dll - False - - - Library\ScriptAssemblies\Unity.2D.SpriteShape.Editor.dll - False - Library\ScriptAssemblies\Unity.Multiplayer.Tools.DependencyInjection.UIElements.dll False @@ -1406,22 +1505,6 @@ Library\ScriptAssemblies\Unity.RenderPipelines.Core.Runtime.dll False - - Library\ScriptAssemblies\Unity.VisualScripting.Flow.dll - False - - - Library\ScriptAssemblies\Unity.RenderPipelines.ShaderGraph.ShaderGraphLibrary.dll - False - - - Library\ScriptAssemblies\Unity.TextMeshPro.Editor.dll - False - - - Library\ScriptAssemblies\Unity.2D.Tilemap.Extras.Editor.dll - False - Library\ScriptAssemblies\Unity.Netcode.PackageChecker.Editor.dll False @@ -1446,14 +1529,6 @@ Library\ScriptAssemblies\Unity.2D.SpriteShape.Runtime.dll False - - Library\ScriptAssemblies\Unity.VisualStudio.Editor.dll - False - - - Library\ScriptAssemblies\Unity.2D.Tilemap.Editor.dll - False - Library\ScriptAssemblies\Unity.2D.Common.Runtime.dll False @@ -1462,30 +1537,14 @@ Library\ScriptAssemblies\Unity.Collections.dll False - - Library\ScriptAssemblies\Unity.Profiling.Core.dll - False - Library\ScriptAssemblies\Unity.Networking.Transport.dll False - - Library\ScriptAssemblies\Unity.InputSystem.ForUI.dll - False - Library\ScriptAssemblies\Unity.Timeline.Editor.dll False - - Library\ScriptAssemblies\Unity.VisualScripting.State.Editor.dll - False - - - Library\ScriptAssemblies\Unity.RenderPipelines.GPUDriven.Runtime.dll - False - Library\ScriptAssemblies\Unity.Mathematics.dll False @@ -1494,14 +1553,6 @@ Library\ScriptAssemblies\Unity.Multiplayer.Tools.NetworkProfiler.Editor.dll False - - Library\ScriptAssemblies\Unity.RenderPipelines.Universal.Shaders.dll - False - - - Library\ScriptAssemblies\UnityEngine.UI.dll - False - Library\ScriptAssemblies\Unity.Multiplayer.Center.Common.dll False @@ -1522,22 +1573,10 @@ Library\ScriptAssemblies\Unity.Multiplayer.Center.Editor.dll False - - Library\ScriptAssemblies\com.unity.multiplayer.tools.window.dll - False - Library\ScriptAssemblies\Unity.VisualScripting.State.dll False - - Library\ScriptAssemblies\Unity.RenderPipelines.Universal.Config.Runtime.dll - False - - - Library\ScriptAssemblies\Unity.Multiplayer.Tools.Common.dll - False - Library\ScriptAssemblies\Unity.RenderPipeline.Universal.ShaderLibrary.dll False @@ -1554,38 +1593,22 @@ Library\ScriptAssemblies\Unity.Multiplayer.Tools.NetStatsMonitor.Implementation.dll False - - Library\ScriptAssemblies\Unity.2D.Sprite.AIIntegration.Editor.dll - False - - - Library\ScriptAssemblies\Unity.RenderPipelines.Core.Editor.Shared.dll + + Library\ScriptAssemblies\Unity.Multiplayer.Tools.NetworkSimulator.Editor.dll False Library\ScriptAssemblies\Unity.Multiplayer.Tools.Initialization.dll False - - Library\ScriptAssemblies\Unity.InputSystem.dll - False - Library\ScriptAssemblies\Unity.Multiplayer.Tools.NetStatsMonitor.Component.dll False - - Library\ScriptAssemblies\Unity.Multiplayer.Tools.NetworkSimulator.Editor.dll - False - Library\ScriptAssemblies\Unity.Settings.Editor.dll False - - Library\ScriptAssemblies\Unity.Splines.dll - False - Library\ScriptAssemblies\Unity.Multiplayer.Tools.NetVis.Editor.UI.dll False @@ -1596,9 +1619,9 @@ - + diff --git a/Assets/Data/BuildingData_Core.asset b/Assets/Data/BuildingData_Core.asset new file mode 100644 index 0000000..231247a --- /dev/null +++ b/Assets/Data/BuildingData_Core.asset @@ -0,0 +1,27 @@ +%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: 937e64980d44d6b46acb35b8046adf34, type: 3} + m_Name: BuildingData_Core + m_EditorClassIdentifier: Assembly-CSharp::Northbound.BuildingData + buildingName: BaseTower + prefab: {fileID: 3733880183385667081, guid: 1979909431408184b9bc587877c5b4b4, type: 3} + width: 5 + length: 5 + height: 5 + placementOffset: {x: 0, y: 0, z: 0} + allowRotation: 1 + maxHealth: 1000 + isIndestructible: 0 + autoRegenerate: 0 + regenPerSecond: 1 + providesVision: 1 + visionRange: 40 diff --git a/Assets/Data/BuildingData_Core.asset.meta b/Assets/Data/BuildingData_Core.asset.meta new file mode 100644 index 0000000..098d3ca --- /dev/null +++ b/Assets/Data/BuildingData_Core.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0e495d169ee3bce449f4b1aea83d6818 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Prefabs/Core.prefab b/Assets/Prefabs/Core.prefab index 8e00086..d68e9b8 100644 --- a/Assets/Prefabs/Core.prefab +++ b/Assets/Prefabs/Core.prefab @@ -11,7 +11,7 @@ GameObject: - component: {fileID: 8064559726283331702} - component: {fileID: 5173262576415873253} - component: {fileID: 1287070985890992582} - - component: {fileID: 945062474581833766} + - component: {fileID: 2964705630284685173} m_Layer: 0 m_Name: Core m_TagString: Untagged @@ -47,7 +47,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3} m_Name: m_EditorClassIdentifier: Unity.Netcode.Runtime::Unity.Netcode.NetworkObject - GlobalObjectIdHash: 1288120633 + GlobalObjectIdHash: 615747208 InScenePlacedSourceGlobalObjectIdHash: 0 DeferredDespawnTick: 0 Ownership: 1 @@ -85,7 +85,7 @@ MonoBehaviour: detachOnEnd: 1 depositEffectPrefab: {fileID: 0} effectSpawnPoint: {fileID: 0} ---- !u!114 &945062474581833766 +--- !u!114 &2964705630284685173 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -94,11 +94,22 @@ MonoBehaviour: m_GameObject: {fileID: 8124290768227340041} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: b59ae4328ce49c846b20d7a6d7ce7e47, type: 3} + m_Script: {fileID: 11500000, guid: 0ceedb9b012d848478813136b65738ae, type: 3} m_Name: - m_EditorClassIdentifier: Assembly-CSharp::Northbound.BuildingVisionProvider + m_EditorClassIdentifier: Assembly-CSharp::Northbound.Building ShowTopMostFoldoutHeaderGroup: 1 - visionRange: 15 + buildingData: {fileID: 11400000, guid: 0e495d169ee3bce449f4b1aea83d6818, type: 2} + gridPosition: {x: 0, y: 0, z: 0} + rotation: 0 + 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!1001 &1876730568674182127 PrefabInstance: m_ObjectHideFlags: 0 diff --git a/Assets/Prefabs/Player.prefab b/Assets/Prefabs/Player.prefab index 5ce18da..c831156 100644 --- a/Assets/Prefabs/Player.prefab +++ b/Assets/Prefabs/Player.prefab @@ -20,7 +20,7 @@ GameObject: - component: {fileID: 6066313428661204362} - component: {fileID: 2443072964133329520} - component: {fileID: 2148255267416253297} - m_Layer: 0 + m_Layer: 9 m_Name: Player m_TagString: Untagged m_Icon: {fileID: 0} @@ -83,6 +83,11 @@ MonoBehaviour: ShowTopMostFoldoutHeaderGroup: 1 moveSpeed: 5 rotationSpeed: 10 + initialTeam: 1 + maxHealth: 100 + showHealthBar: 1 + damageEffectPrefab: {fileID: 0} + deathEffectPrefab: {fileID: 0} --- !u!95 &1698609800605343773 Animator: serializedVersion: 7 @@ -265,6 +270,14 @@ PrefabInstance: serializedVersion: 3 m_TransformParent: {fileID: 5887522270574905679} m_Modifications: + - target: {fileID: -9217289772674400175, guid: 4652a9058e767d142b3e889c2983fa9a, type: 3} + propertyPath: m_Layer + value: 9 + objectReference: {fileID: 0} + - target: {fileID: -8749280752073934791, guid: 4652a9058e767d142b3e889c2983fa9a, type: 3} + propertyPath: m_Layer + value: 9 + objectReference: {fileID: 0} - target: {fileID: -8679921383154817045, guid: 4652a9058e767d142b3e889c2983fa9a, type: 3} propertyPath: m_LocalPosition.x value: 0 @@ -309,6 +322,18 @@ PrefabInstance: propertyPath: 'm_Materials.Array.data[0]' value: objectReference: {fileID: 2100000, guid: d64c307f1b4197c44970c29f9845c245, type: 2} + - target: {fileID: -8629495297202134608, guid: 4652a9058e767d142b3e889c2983fa9a, type: 3} + propertyPath: m_Layer + value: 9 + objectReference: {fileID: 0} + - target: {fileID: -8343894014087287100, guid: 4652a9058e767d142b3e889c2983fa9a, type: 3} + propertyPath: m_Layer + value: 9 + objectReference: {fileID: 0} + - target: {fileID: -7440837640338354081, guid: 4652a9058e767d142b3e889c2983fa9a, type: 3} + propertyPath: m_Layer + value: 9 + objectReference: {fileID: 0} - target: {fileID: -7164137249434462698, guid: 4652a9058e767d142b3e889c2983fa9a, type: 3} propertyPath: 'm_Materials.Array.data[0]' value: @@ -317,26 +342,138 @@ PrefabInstance: propertyPath: 'm_Materials.Array.data[0]' value: objectReference: {fileID: 2100000, guid: d64c307f1b4197c44970c29f9845c245, type: 2} + - target: {fileID: -6645333679261498596, guid: 4652a9058e767d142b3e889c2983fa9a, type: 3} + propertyPath: m_Layer + value: 9 + objectReference: {fileID: 0} + - target: {fileID: -6415790494268509736, guid: 4652a9058e767d142b3e889c2983fa9a, type: 3} + propertyPath: m_Layer + value: 9 + objectReference: {fileID: 0} + - target: {fileID: -5821640607724269708, guid: 4652a9058e767d142b3e889c2983fa9a, type: 3} + propertyPath: m_Layer + value: 9 + objectReference: {fileID: 0} + - target: {fileID: -5489203338784653783, guid: 4652a9058e767d142b3e889c2983fa9a, type: 3} + propertyPath: m_Layer + value: 9 + objectReference: {fileID: 0} + - target: {fileID: -5235451391474362517, guid: 4652a9058e767d142b3e889c2983fa9a, type: 3} + propertyPath: m_Layer + value: 9 + objectReference: {fileID: 0} + - target: {fileID: -3771971891951071861, guid: 4652a9058e767d142b3e889c2983fa9a, type: 3} + propertyPath: m_Layer + value: 9 + objectReference: {fileID: 0} + - target: {fileID: -3230676936149971385, guid: 4652a9058e767d142b3e889c2983fa9a, type: 3} + propertyPath: m_Layer + value: 9 + objectReference: {fileID: 0} + - target: {fileID: -3087093466631822622, guid: 4652a9058e767d142b3e889c2983fa9a, type: 3} + propertyPath: m_Layer + value: 9 + objectReference: {fileID: 0} + - target: {fileID: -2565563256467093774, guid: 4652a9058e767d142b3e889c2983fa9a, type: 3} + propertyPath: m_Layer + value: 9 + objectReference: {fileID: 0} + - target: {fileID: -2268532608001192311, guid: 4652a9058e767d142b3e889c2983fa9a, type: 3} + propertyPath: m_Layer + value: 9 + objectReference: {fileID: 0} - target: {fileID: -1697328484770790153, guid: 4652a9058e767d142b3e889c2983fa9a, type: 3} propertyPath: 'm_Materials.Array.data[0]' value: objectReference: {fileID: 2100000, guid: d64c307f1b4197c44970c29f9845c245, type: 2} + - target: {fileID: -787705004876472881, guid: 4652a9058e767d142b3e889c2983fa9a, type: 3} + propertyPath: m_Layer + value: 9 + objectReference: {fileID: 0} - target: {fileID: -540077757996713287, guid: 4652a9058e767d142b3e889c2983fa9a, type: 3} propertyPath: 'm_Materials.Array.data[0]' value: objectReference: {fileID: 2100000, guid: d64c307f1b4197c44970c29f9845c245, type: 2} + - target: {fileID: -129758803659467788, guid: 4652a9058e767d142b3e889c2983fa9a, type: 3} + propertyPath: m_Layer + value: 9 + objectReference: {fileID: 0} + - target: {fileID: 58325350010635884, guid: 4652a9058e767d142b3e889c2983fa9a, type: 3} + propertyPath: m_Layer + value: 9 + objectReference: {fileID: 0} + - target: {fileID: 316027028415609013, guid: 4652a9058e767d142b3e889c2983fa9a, type: 3} + propertyPath: m_Layer + value: 9 + objectReference: {fileID: 0} - target: {fileID: 919132149155446097, guid: 4652a9058e767d142b3e889c2983fa9a, type: 3} propertyPath: m_Name value: Dummy objectReference: {fileID: 0} + - target: {fileID: 919132149155446097, guid: 4652a9058e767d142b3e889c2983fa9a, type: 3} + propertyPath: m_Layer + value: 9 + objectReference: {fileID: 0} + - target: {fileID: 2071513765411332684, guid: 4652a9058e767d142b3e889c2983fa9a, type: 3} + propertyPath: m_Layer + value: 9 + objectReference: {fileID: 0} + - target: {fileID: 2870523794981691266, guid: 4652a9058e767d142b3e889c2983fa9a, type: 3} + propertyPath: m_Layer + value: 9 + objectReference: {fileID: 0} + - target: {fileID: 3424958915229829536, guid: 4652a9058e767d142b3e889c2983fa9a, type: 3} + propertyPath: m_Layer + value: 9 + objectReference: {fileID: 0} + - target: {fileID: 4260319478689324092, guid: 4652a9058e767d142b3e889c2983fa9a, type: 3} + propertyPath: m_Layer + value: 9 + objectReference: {fileID: 0} + - target: {fileID: 4700718891997078985, guid: 4652a9058e767d142b3e889c2983fa9a, type: 3} + propertyPath: m_Layer + value: 9 + objectReference: {fileID: 0} + - target: {fileID: 4902866164562761394, guid: 4652a9058e767d142b3e889c2983fa9a, type: 3} + propertyPath: m_Layer + value: 9 + objectReference: {fileID: 0} - target: {fileID: 5737771032674114347, guid: 4652a9058e767d142b3e889c2983fa9a, type: 3} propertyPath: 'm_Materials.Array.data[0]' value: objectReference: {fileID: 2100000, guid: d64c307f1b4197c44970c29f9845c245, type: 2} + - target: {fileID: 6026115103549348504, guid: 4652a9058e767d142b3e889c2983fa9a, type: 3} + propertyPath: m_Layer + value: 9 + objectReference: {fileID: 0} + - target: {fileID: 6041330210177057777, guid: 4652a9058e767d142b3e889c2983fa9a, type: 3} + propertyPath: m_Layer + value: 9 + objectReference: {fileID: 0} + - target: {fileID: 7116785954168059035, guid: 4652a9058e767d142b3e889c2983fa9a, type: 3} + propertyPath: m_Layer + value: 9 + objectReference: {fileID: 0} + - target: {fileID: 7354008096416770832, guid: 4652a9058e767d142b3e889c2983fa9a, type: 3} + propertyPath: m_Layer + value: 9 + objectReference: {fileID: 0} + - target: {fileID: 8498165350272959449, guid: 4652a9058e767d142b3e889c2983fa9a, type: 3} + propertyPath: m_Layer + value: 9 + objectReference: {fileID: 0} - target: {fileID: 8594629792745241546, guid: 4652a9058e767d142b3e889c2983fa9a, type: 3} propertyPath: 'm_Materials.Array.data[0]' value: objectReference: {fileID: 2100000, guid: d64c307f1b4197c44970c29f9845c245, type: 2} + - target: {fileID: 8674666463418728774, guid: 4652a9058e767d142b3e889c2983fa9a, type: 3} + propertyPath: m_Layer + value: 9 + objectReference: {fileID: 0} + - target: {fileID: 9179509530285530577, guid: 4652a9058e767d142b3e889c2983fa9a, type: 3} + propertyPath: m_Layer + value: 9 + objectReference: {fileID: 0} m_RemovedComponents: [] m_RemovedGameObjects: [] m_AddedGameObjects: [] diff --git a/Assets/Prefabs/Wall.prefab b/Assets/Prefabs/Wall.prefab index 3b92bb4..77b7db9 100644 --- a/Assets/Prefabs/Wall.prefab +++ b/Assets/Prefabs/Wall.prefab @@ -75,9 +75,6 @@ PrefabInstance: - targetCorrespondingSourceObject: {fileID: 919132149155446097, guid: 244a8d70d41b1a948beb2221c7c0efa9, type: 3} insertIndex: -1 addedObject: {fileID: 1591641544412467547} - - targetCorrespondingSourceObject: {fileID: 919132149155446097, guid: 244a8d70d41b1a948beb2221c7c0efa9, type: 3} - insertIndex: -1 - addedObject: {fileID: 1638952835164862066} m_SourcePrefab: {fileID: 100100000, guid: 244a8d70d41b1a948beb2221c7c0efa9, type: 3} --- !u!1 &2938167817760513538 stripped GameObject: @@ -96,7 +93,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3} m_Name: m_EditorClassIdentifier: Unity.Netcode.Runtime::Unity.Netcode.NetworkObject - GlobalObjectIdHash: 3026494903 + GlobalObjectIdHash: 2718147317 InScenePlacedSourceGlobalObjectIdHash: 0 DeferredDespawnTick: 0 Ownership: 1 @@ -109,17 +106,3 @@ MonoBehaviour: AutoObjectParentSync: 1 SyncOwnerTransformWhenParented: 1 AllowOwnerToParent: 0 ---- !u!114 &1638952835164862066 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 2938167817760513538} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: b59ae4328ce49c846b20d7a6d7ce7e47, type: 3} - m_Name: - m_EditorClassIdentifier: Assembly-CSharp::Northbound.BuildingVisionProvider - ShowTopMostFoldoutHeaderGroup: 1 - visionRange: 15 diff --git a/Assets/Scenes/GameMain.meta b/Assets/Scenes/GameMain.meta new file mode 100644 index 0000000..54b6370 --- /dev/null +++ b/Assets/Scenes/GameMain.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e8051163255a7b946abfc30e245d51c3 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scenes/GameMain.unity b/Assets/Scenes/GameMain.unity index 602e725..66496ff 100644 --- a/Assets/Scenes/GameMain.unity +++ b/Assets/Scenes/GameMain.unity @@ -543,6 +543,141 @@ Transform: m_CorrespondingSourceObject: {fileID: 922888705413710451, guid: 5662d0b0d0eb5f54290edd8dd0980b57, type: 3} m_PrefabInstance: {fileID: 2588157855179843872} m_PrefabAsset: {fileID: 0} +--- !u!1 &513701714 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 513701719} + - component: {fileID: 513701718} + - component: {fileID: 513701717} + - component: {fileID: 513701716} + - component: {fileID: 513701715} + m_Layer: 0 + m_Name: EnemyTest + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &513701715 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 513701714} + 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: 1957460564 + 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 &513701716 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 513701714} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 345fc6e7d4f06314f8b548129700eccb, type: 3} + m_Name: + m_EditorClassIdentifier: Assembly-CSharp::Northbound.EnemyUnit + ShowTopMostFoldoutHeaderGroup: 1 + enemyTeam: 2 + maxHealth: 100 + visionRange: 10 + damageEffectPrefab: {fileID: 0} + destroyEffectPrefab: {fileID: 0} +--- !u!23 &513701717 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 513701714} + 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: 2100000, guid: e831e374758eb4a019b3031699e35407, type: 2} + 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!33 &513701718 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 513701714} + m_Mesh: {fileID: 5840309848095958252, guid: a2ea40155b7314a559bca224f68394d6, type: 3} +--- !u!4 &513701719 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 513701714} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 6, 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!1 &519420028 GameObject: m_ObjectHideFlags: 0 @@ -2338,7 +2473,10 @@ PrefabInstance: m_RemovedComponents: [] m_RemovedGameObjects: [] m_AddedGameObjects: [] - m_AddedComponents: [] + m_AddedComponents: + - targetCorrespondingSourceObject: {fileID: 8124290768227340041, guid: e56926eda34629f4fbf3e4c53f0f8bd4, type: 3} + insertIndex: -1 + addedObject: {fileID: 8940572951313384068} m_SourcePrefab: {fileID: 100100000, guid: e56926eda34629f4fbf3e4c53f0f8bd4, type: 3} --- !u!1001 &4875211098963642791 PrefabInstance: @@ -2466,17 +2604,48 @@ PrefabInstance: m_AddedGameObjects: [] m_AddedComponents: [] m_SourcePrefab: {fileID: 100100000, guid: 04e95700704d92248b63ce5674bd9638, type: 3} +--- !u!1 &8940572951313384064 stripped +GameObject: + m_CorrespondingSourceObject: {fileID: 8124290768227340041, guid: e56926eda34629f4fbf3e4c53f0f8bd4, type: 3} + m_PrefabInstance: {fileID: 4786254629656932894} + m_PrefabAsset: {fileID: 0} --- !u!114 &8940572951313384066 stripped MonoBehaviour: m_CorrespondingSourceObject: {fileID: 1287070985890992582, guid: e56926eda34629f4fbf3e4c53f0f8bd4, type: 3} m_PrefabInstance: {fileID: 4786254629656932894} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 0} + m_GameObject: {fileID: 8940572951313384064} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 7c94274e2af2c8d4f827fe52b26c4410, type: 3} m_Name: m_EditorClassIdentifier: Assembly-CSharp::Northbound.Core +--- !u!114 &8940572951313384068 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8940572951313384064} + 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: 11400000, guid: 23c12a82ea534b34299700b86fffd524, type: 2} + gridPosition: {x: 0, y: 0, z: 0} + rotation: 0 + initialTeam: 1 + initialOwnerId: 0 + useInitialOwner: 0 + showHealthBar: 0 + healthBarPrefab: {fileID: 0} + destroyEffectPrefab: {fileID: 0} + damageEffectPrefab: {fileID: 0} + effectSpawnPoint: {fileID: 0} + showGridBounds: 1 + gridBoundsColor: {r: 0, g: 1, b: 1, a: 1} --- !u!1660057539 &9223372036854775807 SceneRoots: m_ObjectHideFlags: 0 @@ -2496,3 +2665,4 @@ SceneRoots: - {fileID: 1166878644} - {fileID: 946527919} - {fileID: 1701756768} + - {fileID: 513701719} diff --git a/Assets/Scenes/GameMain/NavMesh-Primitive_Floor.asset b/Assets/Scenes/GameMain/NavMesh-Primitive_Floor.asset new file mode 100644 index 0000000..35461e0 Binary files /dev/null and b/Assets/Scenes/GameMain/NavMesh-Primitive_Floor.asset differ diff --git a/Assets/Scenes/GameMain/NavMesh-Primitive_Floor.asset.meta b/Assets/Scenes/GameMain/NavMesh-Primitive_Floor.asset.meta new file mode 100644 index 0000000..a6706df --- /dev/null +++ b/Assets/Scenes/GameMain/NavMesh-Primitive_Floor.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a847cf63b54abee4cbd5b6c92c8ad5e6 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 23800000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/AttackAction.cs b/Assets/Scripts/AttackAction.cs index c6d112c..10fdce2 100644 --- a/Assets/Scripts/AttackAction.cs +++ b/Assets/Scripts/AttackAction.cs @@ -4,7 +4,7 @@ using UnityEngine; namespace Northbound { /// - /// 액션 - 공격 (대상 없이도 실행 가능) + /// 액션 - 공격 (팀 시스템 적용) /// public class AttackAction : NetworkBehaviour, IAction { @@ -15,7 +15,7 @@ namespace Northbound public LayerMask attackableLayer = ~0; [Header("Animation")] - public string attackAnimationTrigger = "Attack"; // 공격 애니메이션 트리거 + public string attackAnimationTrigger = "Attack"; [Header("Visual")] public GameObject attackEffectPrefab; @@ -23,10 +23,12 @@ namespace Northbound private float _lastAttackTime; private Animator _animator; + private ITeamMember _teamMember; private void Awake() { _animator = GetComponent(); + _teamMember = GetComponent(); } public bool CanExecute(ulong playerId) @@ -44,7 +46,7 @@ namespace Northbound // 애니메이션 재생 PlayAttackAnimation(); - // 범위 내 적이 있으면 데미지 + // 범위 내 적 검색 Vector3 attackOrigin = attackPoint != null ? attackPoint.position : transform.position; Collider[] hits = Physics.OverlapSphere(attackOrigin, attackRange, attackableLayer); @@ -54,10 +56,22 @@ namespace Northbound if (hit.transform.root == transform.root) continue; - // 적에게 데미지 - var enemy = hit.GetComponent(); - if (enemy != null) + // 대상 확인 + var targetDamageable = hit.GetComponent(); + var targetTeamMember = hit.GetComponent(); + + if (targetDamageable != null) { + // 팀 확인 - 적대 관계인 경우에만 공격 + if (_teamMember != null && targetTeamMember != null) + { + if (!TeamManager.CanAttack(_teamMember, targetTeamMember)) + { + Debug.Log($"[AttackAction] {TeamManager.GetTeamName(_teamMember.GetTeam())} 팀은 {TeamManager.GetTeamName(targetTeamMember.GetTeam())} 팀을 공격할 수 없습니다."); + continue; + } + } + var netObj = hit.GetComponent(); if (netObj != null) { @@ -66,7 +80,7 @@ namespace Northbound } } - Debug.Log($"플레이어 {playerId} 공격! (적중: {hits.Length}개)"); + Debug.Log($"[AttackAction] 플레이어 {playerId} ({TeamManager.GetTeamName(_teamMember?.GetTeam() ?? TeamType.Neutral)}) 공격!"); } [Rpc(SendTo.Server, InvokePermission = RpcInvokePermission.Everyone)] diff --git a/Assets/Scripts/AutoTargetSystem.cs b/Assets/Scripts/AutoTargetSystem.cs new file mode 100644 index 0000000..dccb706 --- /dev/null +++ b/Assets/Scripts/AutoTargetSystem.cs @@ -0,0 +1,177 @@ +using Unity.Netcode; +using UnityEngine; + +namespace Northbound +{ + /// + /// 자동으로 적을 탐지하고 공격하는 시스템 + /// + public class AutoTargetSystem : NetworkBehaviour + { + [Header("Targeting")] + [Tooltip("적을 감지하는 범위")] + public float detectionRange = 15f; + + [Tooltip("공격 가능한 범위")] + public float attackRange = 10f; + + [Tooltip("공격 간격 (초)")] + public float attackInterval = 1f; + + [Tooltip("탐지할 레이어")] + public LayerMask targetLayer = ~0; + + [Header("Combat")] + [Tooltip("공격 데미지")] + public int attackDamage = 10; + + [Header("Debug")] + [Tooltip("디버그 정보 표시")] + public bool showDebugInfo = true; + + private ITeamMember _teamMember; + private float _lastAttackTime; + + private void Awake() + { + _teamMember = GetComponent(); + + if (_teamMember == null) + { + Debug.LogError($"[AutoTargetSystem] {gameObject.name}에 ITeamMember 컴포넌트가 없습니다!"); + } + } + + private void Update() + { + if (!IsServer) return; + if (_teamMember == null) return; + + if (Time.time - _lastAttackTime >= attackInterval) + { + FindAndAttackEnemy(); + } + } + + private void FindAndAttackEnemy() + { + // 범위 내 모든 콜라이더 탐지 + Collider[] colliders = Physics.OverlapSphere(transform.position, detectionRange, targetLayer); + + if (showDebugInfo && colliders.Length > 0) + { + Debug.Log($"[AutoTarget] {gameObject.name}이(가) {colliders.Length}개의 오브젝트를 감지했습니다."); + } + + GameObject closestEnemy = null; + float closestDistance = float.MaxValue; + + foreach (Collider col in colliders) + { + // 자기 자신 제외 + if (col.transform.root == transform.root) + continue; + + // 팀 확인 + ITeamMember targetTeam = col.GetComponent(); + + if (targetTeam == null) + { + // 부모나 자식에서 찾기 + targetTeam = col.GetComponentInParent(); + if (targetTeam == null) + { + targetTeam = col.GetComponentInChildren(); + } + } + + if (targetTeam == null) + { + if (showDebugInfo) + { + Debug.Log($"[AutoTarget] {col.gameObject.name}에 ITeamMember가 없습니다."); + } + continue; + } + + // 적대 관계 확인 + bool canAttack = TeamManager.CanAttack(_teamMember, targetTeam); + + if (showDebugInfo) + { + Debug.Log($"[AutoTarget] {gameObject.name} ({TeamManager.GetTeamName(_teamMember.GetTeam())}) → {col.gameObject.name} ({TeamManager.GetTeamName(targetTeam.GetTeam())}): 공격가능={canAttack}"); + } + + if (!canAttack) + continue; + + // 가장 가까운 적 찾기 + float distance = Vector3.Distance(transform.position, col.transform.position); + if (distance < closestDistance && distance <= attackRange) + { + closestDistance = distance; + closestEnemy = col.gameObject; + } + } + + // 공격 + if (closestEnemy != null) + { + IDamageable damageable = closestEnemy.GetComponent(); + + if (damageable == null) + { + damageable = closestEnemy.GetComponentInParent(); + if (damageable == null) + { + damageable = closestEnemy.GetComponentInChildren(); + } + } + + if (damageable != null) + { + damageable.TakeDamage(attackDamage, NetworkObjectId); + _lastAttackTime = Time.time; + + var targetTeam = closestEnemy.GetComponent() ?? + closestEnemy.GetComponentInParent() ?? + closestEnemy.GetComponentInChildren(); + + Debug.Log($"[AutoTarget] {gameObject.name} ({TeamManager.GetTeamName(_teamMember.GetTeam())})이(가) {closestEnemy.name} ({TeamManager.GetTeamName(targetTeam?.GetTeam() ?? TeamType.Neutral)})을(를) 공격! (거리: {closestDistance:F2}m, 데미지: {attackDamage})"); + } + else + { + Debug.LogWarning($"[AutoTarget] {closestEnemy.name}에 IDamageable이 없습니다."); + } + } + else if (showDebugInfo && colliders.Length > 0) + { + Debug.Log($"[AutoTarget] {gameObject.name}이(가) 공격 가능한 적을 찾지 못했습니다."); + } + } + + private void OnDrawGizmos() + { + // 탐지 범위 (노란색) + Gizmos.color = Color.yellow; + Gizmos.DrawWireSphere(transform.position, detectionRange); + + // 공격 범위 (빨간색) + Gizmos.color = Color.red; + Gizmos.DrawWireSphere(transform.position, attackRange); + } + + private void OnDrawGizmosSelected() + { + OnDrawGizmos(); + + #if UNITY_EDITOR + if (_teamMember != null && Application.isPlaying) + { + UnityEditor.Handles.Label(transform.position + Vector3.up * 3f, + $"Auto Target\nTeam: {TeamManager.GetTeamName(_teamMember.GetTeam())}\nDetection: {detectionRange}m\nAttack: {attackRange}m"); + } + #endif + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/AutoTargetSystem.cs.meta b/Assets/Scripts/AutoTargetSystem.cs.meta new file mode 100644 index 0000000..563887a --- /dev/null +++ b/Assets/Scripts/AutoTargetSystem.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 8dd1c341faa09554aa2bc35164888453 \ No newline at end of file diff --git a/Assets/Scripts/Building.cs b/Assets/Scripts/Building.cs index a39db3f..0d7f814 100644 --- a/Assets/Scripts/Building.cs +++ b/Assets/Scripts/Building.cs @@ -4,7 +4,7 @@ using UnityEngine; namespace Northbound { - public class Building : NetworkBehaviour, IDamageable, IVisionProvider + public class Building : NetworkBehaviour, IDamageable, IVisionProvider, ITeamMember { [Header("References")] public BuildingData buildingData; @@ -13,6 +13,10 @@ namespace Northbound public Vector3Int gridPosition; public int rotation; // 0-3 (0=0°, 1=90°, 2=180°, 3=270°) + [Header("Team")] + [Tooltip("건물의 팀 (플레이어/적대세력/몬스터/중립)")] + public TeamType initialTeam = TeamType.Player; + [Header("Ownership (for pre-placed buildings)")] [Tooltip("씬에 미리 배치된 건물의 경우 여기서 소유자 설정 (0 = 중립, 1+ = 플레이어 ID)")] public ulong initialOwnerId = 0; @@ -46,9 +50,17 @@ namespace Northbound NetworkVariableWritePermission.Server ); + // 건물 팀 + private NetworkVariable _team = new NetworkVariable( + TeamType.Neutral, + NetworkVariableReadPermission.Everyone, + NetworkVariableWritePermission.Server + ); + // 이벤트 public event Action OnHealthChanged; // (current, max) public event Action OnDestroyed; + public event Action OnTeamChanged; private BuildingHealthBar _healthBar; private float _lastRegenTime; @@ -66,11 +78,17 @@ namespace Northbound _currentHealth.Value = buildingData != null ? buildingData.maxHealth : 100; } + // 팀 초기화 + if (_team.Value == TeamType.Neutral) + { + _team.Value = initialTeam; + } + // 소유자 초기화 (사전 배치 건물 체크) if (useInitialOwner && _ownerId.Value == 0) { _ownerId.Value = initialOwnerId; - Debug.Log($"[Building] 사전 배치 건물 '{buildingData?.buildingName ?? gameObject.name}' 소유자: {initialOwnerId}"); + Debug.Log($"[Building] 사전 배치 건물 '{buildingData?.buildingName ?? gameObject.name}' 소유자: {initialOwnerId}, 팀: {_team.Value}"); } else if (!useInitialOwner && _ownerId.Value == 0) { @@ -87,8 +105,9 @@ namespace Northbound } } - // 체력 변경 이벤트 구독 + // 이벤트 구독 _currentHealth.OnValueChanged += OnHealthValueChanged; + _team.OnValueChanged += OnTeamValueChanged; // 체력바 생성 if (showHealthBar && healthBarPrefab != null) @@ -98,11 +117,13 @@ namespace Northbound // 초기 체력 UI 업데이트 UpdateHealthUI(); + UpdateTeamVisuals(); } public override void OnNetworkDespawn() { _currentHealth.OnValueChanged -= OnHealthValueChanged; + _team.OnValueChanged -= OnTeamValueChanged; // FogOfWar 시스템에서 제거 if (IsServer && buildingData != null && buildingData.providesVision) @@ -131,7 +152,7 @@ namespace Northbound /// /// 건물 초기화 (BuildingManager가 동적 생성 시 호출) /// - public void Initialize(BuildingData data, Vector3Int gridPos, int rot, ulong ownerId) + public void Initialize(BuildingData data, Vector3Int gridPos, int rot, ulong ownerId, TeamType team = TeamType.Player) { buildingData = data; gridPosition = gridPos; @@ -142,6 +163,7 @@ namespace Northbound { _currentHealth.Value = data.maxHealth; _ownerId.Value = ownerId; + _team.Value = team; // 시야 제공자 등록 if (data.providesVision) @@ -156,14 +178,17 @@ namespace Northbound /// /// 건물 소유권 변경 (점령 등) /// - public void SetOwner(ulong newOwnerId) + public void SetOwner(ulong newOwnerId, TeamType newTeam) { if (!IsServer) return; ulong previousOwner = _ownerId.Value; + TeamType previousTeam = _team.Value; + _ownerId.Value = newOwnerId; + _team.Value = newTeam; - Debug.Log($"[Building] {buildingData?.buildingName ?? "건물"} 소유권 변경: {previousOwner} → {newOwnerId}"); + Debug.Log($"[Building] {buildingData?.buildingName ?? "건물"} 소유권 변경: {previousOwner} → {newOwnerId}, 팀: {TeamManager.GetTeamName(previousTeam)} → {TeamManager.GetTeamName(newTeam)}"); // 시야 제공자 재등록 (소유자가 바뀌었으므로) if (buildingData != null && buildingData.providesVision) @@ -173,6 +198,35 @@ namespace Northbound } } + #region ITeamMember Implementation + + public TeamType GetTeam() => _team.Value; + + public void SetTeam(TeamType team) + { + if (!IsServer) return; + _team.Value = team; + } + + private void OnTeamValueChanged(TeamType previousValue, TeamType newValue) + { + OnTeamChanged?.Invoke(newValue); + UpdateTeamVisuals(); + Debug.Log($"[Building] {buildingData?.buildingName ?? "건물"} 팀 변경: {TeamManager.GetTeamName(previousValue)} → {TeamManager.GetTeamName(newValue)}"); + } + + private void UpdateTeamVisuals() + { + // 팀 색상으로 건물 외곽선이나 이펙트 변경 가능 + // 예: Renderer의 emission 색상 변경 + Color teamColor = TeamManager.GetTeamColor(_team.Value); + + // 여기에 실제 비주얼 업데이트 로직 추가 + // 예: outline shader, emission, particle system 색상 등 + } + + #endregion + #region IVisionProvider Implementation public ulong GetOwnerId() => _ownerId.Value; @@ -214,11 +268,24 @@ namespace Northbound if (_currentHealth.Value <= 0) return; + // 공격자의 팀 확인 (팀 공격 방지) + var attackerObj = NetworkManager.Singleton.SpawnManager.SpawnedObjects[attackerId]; + var attackerTeamMember = attackerObj?.GetComponent(); + + if (attackerTeamMember != null) + { + if (!TeamManager.CanAttack(attackerTeamMember, this)) + { + Debug.Log($"[Building] {TeamManager.GetTeamName(attackerTeamMember.GetTeam())} 팀은 {TeamManager.GetTeamName(_team.Value)} 팀을 공격할 수 없습니다."); + return; + } + } + // 데미지 적용 int actualDamage = Mathf.Min(damage, _currentHealth.Value); _currentHealth.Value -= actualDamage; - Debug.Log($"[Building] {buildingData?.buildingName ?? "건물"}이(가) {actualDamage} 데미지를 받았습니다. 남은 체력: {_currentHealth.Value}/{buildingData?.maxHealth ?? 100}"); + Debug.Log($"[Building] {buildingData?.buildingName ?? "건물"} ({TeamManager.GetTeamName(_team.Value)})이(가) {actualDamage} 데미지를 받았습니다. 남은 체력: {_currentHealth.Value}/{buildingData?.maxHealth ?? 100}"); // 데미지 이펙트 ShowDamageEffectClientRpc(); @@ -248,7 +315,7 @@ namespace Northbound if (!IsServer) return; - Debug.Log($"[Building] {buildingData?.buildingName ?? "건물"}이(가) 파괴되었습니다! (공격자: {attackerId})"); + Debug.Log($"[Building] {buildingData?.buildingName ?? "건물"} ({TeamManager.GetTeamName(_team.Value)})이(가) 파괴되었습니다! (공격자: {attackerId})"); // 파괴 이벤트 발생 OnDestroyed?.Invoke(); @@ -427,7 +494,10 @@ namespace Northbound if (!showGridBounds || buildingData == null) return; Bounds bounds = GetGridBounds(); - Gizmos.color = gridBoundsColor; + + // 팀 색상으로 표시 + Color teamColor = Application.isPlaying ? TeamManager.GetTeamColor(_team.Value) : TeamManager.GetTeamColor(initialTeam); + Gizmos.color = new Color(teamColor.r, teamColor.g, teamColor.b, 0.3f); Gizmos.DrawWireCube(bounds.center, bounds.size); } @@ -454,17 +524,19 @@ namespace Northbound Gizmos.DrawWireSphere(transform.position, buildingData.visionRange); } - // Draw owner ID label + // Draw team info label #if UNITY_EDITOR if (Application.isPlaying) { + string teamName = TeamManager.GetTeamName(_team.Value); UnityEditor.Handles.Label(transform.position + Vector3.up * 3f, - $"Owner: {_ownerId.Value}"); + $"Owner: {_ownerId.Value}\nTeam: {teamName}"); } else if (useInitialOwner) { + string teamName = TeamManager.GetTeamName(initialTeam); UnityEditor.Handles.Label(transform.position + Vector3.up * 3f, - $"Initial Owner: {initialOwnerId}"); + $"Initial Owner: {initialOwnerId}\nTeam: {teamName}"); } #endif } diff --git a/Assets/Scripts/Core.cs b/Assets/Scripts/Core.cs index 25ef456..c1160ca 100644 --- a/Assets/Scripts/Core.cs +++ b/Assets/Scripts/Core.cs @@ -6,12 +6,17 @@ namespace Northbound /// /// 플레이어가 자원을 건내받아 게임의 전역 자원으로 관리하는 중앙 허브 /// - public class Core : NetworkBehaviour, IInteractable + public class Core : NetworkBehaviour, IInteractable, IDamageable, ITeamMember { [Header("Core Settings")] public int maxStorageCapacity = 1000; // 코어의 최대 저장 용량 public bool unlimitedStorage = false; // 무제한 저장소 + [Header("Health")] + public int maxHealth = 1000; + public GameObject damageEffectPrefab; + public GameObject destroyEffectPrefab; + [Header("Deposit Settings")] public bool depositAll = true; // true: 전부 건네기, false: 일부만 건네기 public int depositAmountPerInteraction = 10; // depositAll이 false일 때 한 번에 건네는 양 @@ -32,17 +37,163 @@ namespace Northbound NetworkVariableWritePermission.Server ); + private NetworkVariable _currentHealth = new NetworkVariable( + 0, + NetworkVariableReadPermission.Everyone, + NetworkVariableWritePermission.Server + ); + public int TotalResources => _totalResources.Value; public int MaxStorageCapacity => maxStorageCapacity; + public int CurrentHealth => _currentHealth.Value; + public int MaxHealth => maxHealth; public override void OnNetworkSpawn() { if (IsServer) { _totalResources.Value = 0; + _currentHealth.Value = maxHealth; + } + + _currentHealth.OnValueChanged += OnHealthChanged; + } + + public override void OnNetworkDespawn() + { + _currentHealth.OnValueChanged -= OnHealthChanged; + } + + private void OnHealthChanged(int previousValue, int newValue) + { + Debug.Log($"[Core] 코어 체력 변경: {previousValue} → {newValue} ({newValue}/{maxHealth})"); + } + + #region ITeamMember Implementation + + public TeamType GetTeam() + { + return TeamType.Player; // 코어는 플레이어 팀 + } + + public void SetTeam(TeamType team) + { + // 코어의 팀은 변경할 수 없음 (항상 플레이어 팀) + Debug.LogWarning("[Core] 코어의 팀은 변경할 수 없습니다."); + } + + #endregion + + #region IDamageable Implementation + + public void TakeDamage(int damage, ulong attackerId) + { + if (!IsServer) return; + if (_currentHealth.Value <= 0) return; + + int actualDamage = Mathf.Min(damage, _currentHealth.Value); + _currentHealth.Value -= actualDamage; + + Debug.Log($"[Core] 코어가 {actualDamage} 데미지를 받았습니다! 남은 체력: {_currentHealth.Value}/{maxHealth}"); + + // 데미지 이펙트 + ShowDamageEffectClientRpc(); + + // 체력이 0이 되면 게임 오버 + if (_currentHealth.Value <= 0) + { + OnCoreDestroyed(); } } + private void OnCoreDestroyed() + { + if (!IsServer) return; + + Debug.Log($"[Core] 코어가 파괴되었습니다! 게임 오버!"); + + // 파괴 이펙트 + ShowDestroyEffectClientRpc(); + + // 게임 오버 로직 (추후 구현) + // GameManager.Instance?.OnGameOver(); + } + + [Rpc(SendTo.ClientsAndHost)] + private void ShowDamageEffectClientRpc() + { + if (damageEffectPrefab != null) + { + GameObject effect = Instantiate(damageEffectPrefab, transform.position + Vector3.up * 2f, Quaternion.identity); + Destroy(effect, 2f); + } + } + + [Rpc(SendTo.ClientsAndHost)] + private void ShowDestroyEffectClientRpc() + { + if (destroyEffectPrefab != null) + { + GameObject effect = Instantiate(destroyEffectPrefab, transform.position, Quaternion.identity); + Destroy(effect, 5f); + } + } + + #endregion + + #region Resource Management + + /// + /// 자원을 소비할 수 있는지 확인 + /// + public bool CanConsumeResource(int amount) + { + return _totalResources.Value >= amount; + } + + /// + /// 자원 소비 (서버에서만) + /// + [Rpc(SendTo.Server, InvokePermission = RpcInvokePermission.Everyone)] + public void ConsumeResourceServerRpc(int amount) + { + if (!CanConsumeResource(amount)) + { + Debug.LogWarning($"[Core] 자원이 부족합니다. 필요: {amount}, 보유: {_totalResources.Value}"); + return; + } + + int previousAmount = _totalResources.Value; + _totalResources.Value -= amount; + + Debug.Log($"[Core] {amount} 자원 소비. 남은 자원: {_totalResources.Value}/{maxStorageCapacity}"); + } + + /// + /// 자원 추가 (서버에서만) + /// + public void AddResource(int amount) + { + if (!IsServer) return; + + if (!unlimitedStorage) + { + int availableSpace = maxStorageCapacity - _totalResources.Value; + amount = Mathf.Min(amount, availableSpace); + } + + if (amount > 0) + { + _totalResources.Value += amount; + Debug.Log($"[Core] {amount} 자원 추가. 총 자원: {_totalResources.Value}" + + (unlimitedStorage ? "" : $"/{maxStorageCapacity}") + ""); + } + } + + #endregion + + #region IInteractable Implementation + public bool CanInteract(ulong playerId) { // 저장소가 가득 찼는지 확인 (무제한이 아닐 때) @@ -75,6 +226,33 @@ namespace Northbound DepositResourceServerRpc(playerId); } + public string GetInteractionPrompt() + { + if (unlimitedStorage) + { + return "자원 보관 (무제한)"; + } + else + { + return $"자원 보관 ({_totalResources.Value}/{maxStorageCapacity})"; + } + } + + public string GetInteractionAnimation() + { + return interactionAnimationTrigger; + } + + public InteractionEquipmentData GetEquipmentData() + { + return equipmentData; + } + + public Transform GetTransform() + { + return transform; + } + [Rpc(SendTo.Server, InvokePermission = RpcInvokePermission.Everyone)] private void DepositResourceServerRpc(ulong playerId) { @@ -132,8 +310,8 @@ namespace Northbound // 코어에 자원 추가 _totalResources.Value += depositAmount; - Debug.Log($"플레이어 {playerId}가 {depositAmount} 자원을 코어에 건넸습니다. 코어 총 자원: {_totalResources.Value}" + - (unlimitedStorage ? "" : $"/{maxStorageCapacity}")); + Debug.Log($"[Core] 플레이어 {playerId}가 {depositAmount} 자원을 건넸습니다. 코어 총 자원: {_totalResources.Value}" + + (unlimitedStorage ? "" : $"/{maxStorageCapacity}") + ""); ShowDepositEffectClientRpc(); } @@ -148,76 +326,6 @@ namespace Northbound } } - /// - /// 게임 시스템이 코어의 자원을 사용 (건물 건설 등) - /// - [Rpc(SendTo.Server, InvokePermission = RpcInvokePermission.Everyone)] - public void ConsumeResourceServerRpc(int amount) - { - if (amount <= 0) return; - - int actualAmount = Mathf.Min(amount, _totalResources.Value); - _totalResources.Value -= actualAmount; - - Debug.Log($"코어에서 {actualAmount} 자원을 사용했습니다. 남은 자원: {_totalResources.Value}"); - } - - /// - /// 자원을 사용할 수 있는지 확인 - /// - public bool CanConsumeResource(int amount) - { - return _totalResources.Value >= amount; - } - - /// - /// 코어에 자원 추가 (디버그/관리자 기능) - /// - [Rpc(SendTo.Server, InvokePermission = RpcInvokePermission.Everyone)] - public void AddResourceServerRpc(int amount) - { - if (amount <= 0) return; - - if (!unlimitedStorage) - { - int availableSpace = maxStorageCapacity - _totalResources.Value; - amount = Mathf.Min(amount, availableSpace); - } - - _totalResources.Value += amount; - Debug.Log($"코어에 {amount} 자원이 추가되었습니다. 현재: {_totalResources.Value}"); - } - - public string GetInteractionPrompt() - { - if (unlimitedStorage) - { - return depositAll ? - $"[E] 자원 모두 건네기" : - $"[E] 자원 건네기 ({depositAmountPerInteraction}개씩)"; - } - - if (_totalResources.Value >= maxStorageCapacity) - return "코어 저장소 가득 찼음"; - - return depositAll ? - $"[E] 자원 모두 건네기 ({_totalResources.Value}/{maxStorageCapacity})" : - $"[E] 자원 건네기 ({_totalResources.Value}/{maxStorageCapacity})"; - } - - public string GetInteractionAnimation() - { - return interactionAnimationTrigger; - } - - public InteractionEquipmentData GetEquipmentData() - { - return equipmentData; - } - - public Transform GetTransform() - { - return transform; - } + #endregion } } \ No newline at end of file diff --git a/Assets/Scripts/EnemyAIController.cs b/Assets/Scripts/EnemyAIController.cs new file mode 100644 index 0000000..fa23c37 --- /dev/null +++ b/Assets/Scripts/EnemyAIController.cs @@ -0,0 +1,714 @@ +using Unity.Netcode; +using UnityEngine; +using UnityEngine.AI; + +namespace Northbound +{ + /// + /// 몬스터와 적대 세력의 AI 컨트롤러 + /// + [RequireComponent(typeof(NavMeshAgent))] + [RequireComponent(typeof(EnemyUnit))] + public class EnemyAIController : NetworkBehaviour + { + [Header("AI Type")] + [Tooltip("Monster: 코어로 이동, Hostile: 제자리에서 대기")] + public TeamType aiType = TeamType.Monster; + + [Header("Detection")] + [Tooltip("플레이어 감지 범위")] + public float detectionRange = 15f; + + [Tooltip("시야 각도 (0-360, 360=전방향)")] + [Range(0, 360)] + public float detectionAngle = 120f; + + [Tooltip("탐지할 레이어")] + public LayerMask playerLayer = ~0; + + [Tooltip("시야 체크 장애물 레이어")] + public LayerMask obstacleLayer = ~0; + + [Header("Chase Settings")] + [Tooltip("추적 최대 거리 (이 거리 이상 추적하면 중단)")] + public float maxChaseDistance = 30f; + + [Tooltip("추적 포기 거리 (플레이어와 이 거리 이상 멀어지면 추적 중단)")] + public float chaseGiveUpDistance = 25f; + + [Header("Combat")] + [Tooltip("공격 범위")] + public float attackRange = 2f; + + [Tooltip("공격 간격 (초)")] + public float attackInterval = 1.5f; + + [Tooltip("공격 데미지")] + public int attackDamage = 10; + + [Header("Movement")] + [Tooltip("이동 속도")] + public float moveSpeed = 3.5f; + + [Tooltip("추적 중 속도 배율")] + public float chaseSpeedMultiplier = 1.5f; + + [Header("Debug")] + [Tooltip("디버그 정보 표시")] + public bool showDebugInfo = true; + + private NavMeshAgent _agent; + private EnemyUnit _enemyUnit; + private Transform _coreTransform; + private Collider _coreCollider; + private Vector3 _originPosition; + private Vector3 _chaseStartPosition; + private float _lastAttackTime; + private bool _hasSetCoreDestination; + private float _lastDetectionLogTime; + + private NetworkVariable _currentState = new NetworkVariable( + EnemyAIState.Idle, + NetworkVariableReadPermission.Everyone, + NetworkVariableWritePermission.Server + ); + + private NetworkVariable _targetPlayerId = new NetworkVariable( + 0, + NetworkVariableReadPermission.Everyone, + NetworkVariableWritePermission.Server + ); + + private GameObject _cachedTargetPlayer; + + public override void OnNetworkSpawn() + { + base.OnNetworkSpawn(); + + _agent = GetComponent(); + _enemyUnit = GetComponent(); + _originPosition = transform.position; + + if (IsServer) + { + // NavMeshAgent 초기 설정 + _agent.speed = moveSpeed; + _agent.acceleration = 8f; + _agent.angularSpeed = 120f; + _agent.stoppingDistance = attackRange * 0.7f; + _agent.autoBraking = true; + _agent.updateRotation = true; + _agent.updateUpAxis = false; + + // NavMesh 위에 있는지 확인 + if (!_agent.isOnNavMesh) + { + Debug.LogWarning($"[EnemyAI] {gameObject.name}이(가) NavMesh 위에 있지 않습니다!"); + } + else + { + Debug.Log($"[EnemyAI] {gameObject.name} NavMeshAgent 초기화 완료"); + } + + // AI 타입에 따라 초기 상태 설정 + if (aiType == TeamType.Monster) + { + FindCore(); + TransitionToState(EnemyAIState.MoveToCore); + } + else if (aiType == TeamType.Hostile) + { + TransitionToState(EnemyAIState.Idle); + } + } + } + + private void Update() + { + if (!IsServer) return; + if (!_agent.isOnNavMesh) return; + + switch (_currentState.Value) + { + case EnemyAIState.Idle: + UpdateIdle(); + break; + case EnemyAIState.MoveToCore: + UpdateMoveToCore(); + break; + case EnemyAIState.ChasePlayer: + UpdateChasePlayer(); + break; + case EnemyAIState.Attack: + UpdateAttack(); + break; + case EnemyAIState.ReturnToOrigin: + UpdateReturnToOrigin(); + break; + } + } + + #region State Updates + + private void UpdateIdle() + { + GameObject player = DetectPlayer(); + if (player != null) + { + SetTargetPlayer(player); + TransitionToState(EnemyAIState.ChasePlayer); + } + } + + private void UpdateMoveToCore() + { + // 플레이어 감지 + GameObject player = DetectPlayer(); + if (player != null) + { + SetTargetPlayer(player); + TransitionToState(EnemyAIState.ChasePlayer); + return; + } + + // 코어가 없으면 찾기 + if (_coreTransform == null) + { + FindCore(); + _hasSetCoreDestination = false; + return; + } + + // 코어 표면까지의 실제 거리 계산 + float distanceToCore = GetDistanceToCoreSurface(); + + // 공격 범위 안에 있으면 공격 상태로 전환 + if (distanceToCore <= attackRange) + { + TransitionToState(EnemyAIState.Attack); + return; + } + + // 경로가 설정되지 않았거나 무효화된 경우에만 설정 + if (!_hasSetCoreDestination || !_agent.hasPath || _agent.pathStatus == NavMeshPathStatus.PathInvalid) + { + if (_agent.SetDestination(_coreTransform.position)) + { + _hasSetCoreDestination = true; + + if (showDebugInfo) + { + Debug.Log($"[EnemyAI] {gameObject.name} 코어로 경로 설정 (표면 거리: {distanceToCore:F2}m, 공격범위: {attackRange:F2}m)"); + } + } + else + { + Debug.LogWarning($"[EnemyAI] {gameObject.name}이(가) 코어로 가는 경로를 찾을 수 없습니다!"); + _hasSetCoreDestination = false; + } + } + } + + private void UpdateChasePlayer() + { + GameObject targetPlayer = GetTargetPlayer(); + + if (targetPlayer == null) + { + OnLostTarget(); + return; + } + + float distanceToPlayer = Vector3.Distance(transform.position, targetPlayer.transform.position); + + // 추적 기준점 설정 + Vector3 chaseReferencePoint = (aiType == TeamType.Monster) ? _chaseStartPosition : _originPosition; + float distanceFromReference = Vector3.Distance(transform.position, chaseReferencePoint); + + // 추적 중단 조건 확인 + if (distanceToPlayer > chaseGiveUpDistance || distanceFromReference > maxChaseDistance) + { + if (showDebugInfo) + { + string referenceType = (aiType == TeamType.Monster) ? "추적 시작" : "원점"; + Debug.Log($"[EnemyAI] {gameObject.name}이(가) 추적을 중단합니다. (플레이어 거리: {distanceToPlayer:F2}m, {referenceType} 거리: {distanceFromReference:F2}m)"); + } + OnLostTarget(); + return; + } + + // 공격 범위 확인 + if (distanceToPlayer <= attackRange) + { + TransitionToState(EnemyAIState.Attack); + return; + } + + // 플레이어 추적 - 매 프레임 업데이트 + if (_agent.isOnNavMesh && !_agent.isStopped) + { + _agent.SetDestination(targetPlayer.transform.position); + } + } + + private void UpdateAttack() + { + // 코어 공격 중인지 확인 + bool attackingCore = _coreTransform != null && + GetDistanceToCoreSurface() <= attackRange * 1.2f; + + if (attackingCore) + { + float distanceToCore = GetDistanceToCoreSurface(); + + if (distanceToCore > attackRange * 1.2f) + { + TransitionToState(EnemyAIState.MoveToCore); + return; + } + + // 코어를 바라보기 + Vector3 directionToCore = (_coreTransform.position - transform.position).normalized; + directionToCore.y = 0; + if (directionToCore != Vector3.zero) + { + Quaternion targetRotation = Quaternion.LookRotation(directionToCore); + transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, Time.deltaTime * 5f); + } + + // 코어 공격 + IDamageable coreHealth = _coreTransform.GetComponent(); + if (coreHealth != null && Time.time - _lastAttackTime >= attackInterval) + { + coreHealth.TakeDamage(attackDamage, NetworkObjectId); + _lastAttackTime = Time.time; + Debug.Log($"[EnemyAI] {gameObject.name}이(가) 코어를 공격! (데미지: {attackDamage}, 표면 거리: {distanceToCore:F2}m)"); + } + } + else + { + // 플레이어 공격 + GameObject targetPlayer = GetTargetPlayer(); + + if (targetPlayer == null) + { + OnLostTarget(); + return; + } + + float distanceToPlayer = Vector3.Distance(transform.position, targetPlayer.transform.position); + + if (distanceToPlayer > attackRange * 1.2f) + { + TransitionToState(EnemyAIState.ChasePlayer); + return; + } + + // 플레이어를 바라보기 + Vector3 directionToPlayer = (targetPlayer.transform.position - transform.position).normalized; + directionToPlayer.y = 0; + if (directionToPlayer != Vector3.zero) + { + Quaternion targetRotation = Quaternion.LookRotation(directionToPlayer); + transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, Time.deltaTime * 5f); + } + + // 공격 + if (Time.time - _lastAttackTime >= attackInterval) + { + AttackPlayer(targetPlayer); + } + } + } + + private void UpdateReturnToOrigin() + { + GameObject player = DetectPlayer(); + if (player != null) + { + SetTargetPlayer(player); + TransitionToState(EnemyAIState.ChasePlayer); + return; + } + + if (!_agent.pathPending && _agent.remainingDistance <= _agent.stoppingDistance) + { + if (!_agent.hasPath || _agent.velocity.sqrMagnitude == 0f) + { + if (showDebugInfo) + { + Debug.Log($"[EnemyAI] {gameObject.name}이(가) 원래 위치로 복귀했습니다."); + } + TransitionToState(EnemyAIState.Idle); + } + } + } + + #endregion + + #region Detection + + private GameObject DetectPlayer() + { + Collider[] colliders = Physics.OverlapSphere(transform.position, detectionRange, playerLayer); + + GameObject closestPlayer = null; + float closestDistance = float.MaxValue; + + foreach (Collider col in colliders) + { + // 자기 자신 제외 + if (col.transform.root == transform.root) + continue; + + // 플레이어 팀 확인 (부모에서 찾기) + ITeamMember teamMember = col.GetComponentInParent(); + if (teamMember == null || teamMember.GetTeam() != TeamType.Player) + continue; + + // 플레이어 위치 (루트 오브젝트 사용) + Transform playerRoot = col.transform.root; + Vector3 playerPosition = playerRoot.position; + + // 거리 체크 + float distance = Vector3.Distance(transform.position, playerPosition); + if (distance > detectionRange) + continue; + + // 시야각 확인 (360도면 모든 방향 감지) + if (detectionAngle < 360f) + { + Vector3 directionToTarget = (playerPosition - transform.position).normalized; + float angleToTarget = Vector3.Angle(transform.forward, directionToTarget); + + if (angleToTarget > detectionAngle / 2f) + continue; + } + + // 시야 체크 (레이캐스트) - 플레이어 중심으로 + Vector3 rayStart = transform.position + Vector3.up * 1f; // 적의 눈 높이 + Vector3 rayTarget = playerPosition + Vector3.up * 1f; // 플레이어 중심 + Vector3 rayDirection = (rayTarget - rayStart).normalized; + float rayDistance = Vector3.Distance(rayStart, rayTarget); + + bool lineOfSight = true; + + // 장애물 체크 (옵션) + if (Physics.Raycast(rayStart, rayDirection, out RaycastHit hit, rayDistance, obstacleLayer)) + { + // 맞은 오브젝트가 플레이어의 루트나 자식인지 확인 + if (hit.transform.root != playerRoot) + { + lineOfSight = false; + } + } + + if (lineOfSight) + { + // 가장 가까운 플레이어 찾기 + if (distance < closestDistance) + { + closestDistance = distance; + closestPlayer = playerRoot.gameObject; + } + } + } + + // 감지 성공 시 로그 (1초에 한 번만) + if (closestPlayer != null && showDebugInfo && Time.time - _lastDetectionLogTime >= 1f) + { + string angleInfo = detectionAngle >= 360f ? "전방향" : $"{Vector3.Angle(transform.forward, (closestPlayer.transform.position - transform.position).normalized):F1}°"; + Debug.Log($"[EnemyAI] {gameObject.name}이(가) {closestPlayer.name}을(를) 감지! (거리: {closestDistance:F2}m, 각도: {angleInfo})"); + _lastDetectionLogTime = Time.time; + } + + return closestPlayer; + } + + #endregion + + #region Combat + + private void AttackPlayer(GameObject player) + { + IDamageable damageable = player.GetComponentInParent(); + if (damageable != null) + { + damageable.TakeDamage(attackDamage, NetworkObjectId); + _lastAttackTime = Time.time; + + if (showDebugInfo) + { + Debug.Log($"[EnemyAI] {gameObject.name}이(가) {player.name}을(를) 공격! (데미지: {attackDamage})"); + } + } + } + + #endregion + + #region Distance Calculation + + private float GetDistanceToCoreSurface() + { + if (_coreTransform == null) + return float.MaxValue; + + if (_coreCollider != null) + { + Vector3 closestPoint = _coreCollider.ClosestPoint(transform.position); + float distanceToSurface = Vector3.Distance(transform.position, closestPoint); + return distanceToSurface; + } + else + { + return Vector3.Distance(transform.position, _coreTransform.position); + } + } + + #endregion + + #region State Management + + private void TransitionToState(EnemyAIState newState) + { + if (_currentState.Value == newState) return; + + if (showDebugInfo) + { + Debug.Log($"[EnemyAI] {gameObject.name} 상태 변경: {_currentState.Value} → {newState}"); + } + + OnExitState(_currentState.Value); + _currentState.Value = newState; + OnEnterState(newState); + } + + private void OnEnterState(EnemyAIState state) + { + switch (state) + { + case EnemyAIState.Idle: + _agent.isStopped = true; + _agent.speed = moveSpeed; + _agent.ResetPath(); + break; + + case EnemyAIState.MoveToCore: + _agent.isStopped = false; + _agent.speed = moveSpeed; + _hasSetCoreDestination = false; + + if (showDebugInfo) + { + Debug.Log($"[EnemyAI] {gameObject.name}이(가) 코어로 이동 시작"); + } + break; + + case EnemyAIState.ChasePlayer: + _agent.isStopped = false; + _agent.speed = moveSpeed * chaseSpeedMultiplier; + _chaseStartPosition = transform.position; + + if (showDebugInfo) + { + Debug.Log($"[EnemyAI] {gameObject.name}이(가) 추적 시작! (시작 위치: {_chaseStartPosition})"); + } + break; + + case EnemyAIState.Attack: + _agent.isStopped = true; + _agent.ResetPath(); + break; + + case EnemyAIState.ReturnToOrigin: + _agent.isStopped = false; + _agent.speed = moveSpeed; + _agent.stoppingDistance = 1f; + _agent.SetDestination(_originPosition); + ClearTargetPlayer(); + break; + } + } + + private void OnExitState(EnemyAIState state) + { + if (state == EnemyAIState.ReturnToOrigin) + { + _agent.stoppingDistance = attackRange * 0.7f; + } + } + + private void OnLostTarget() + { + if (aiType == TeamType.Hostile) + { + TransitionToState(EnemyAIState.ReturnToOrigin); + } + else if (aiType == TeamType.Monster) + { + ClearTargetPlayer(); + _hasSetCoreDestination = false; + TransitionToState(EnemyAIState.MoveToCore); + } + } + + #endregion + + #region Target Management + + private void SetTargetPlayer(GameObject player) + { + var networkObject = player.GetComponentInParent(); + if (networkObject != null) + { + _targetPlayerId.Value = networkObject.NetworkObjectId; + _cachedTargetPlayer = player; + } + } + + private void ClearTargetPlayer() + { + _targetPlayerId.Value = 0; + _cachedTargetPlayer = null; + } + + private GameObject GetTargetPlayer() + { + if (_targetPlayerId.Value == 0) return null; + + if (_cachedTargetPlayer != null && _cachedTargetPlayer.activeSelf) + { + return _cachedTargetPlayer; + } + + if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(_targetPlayerId.Value, out NetworkObject networkObject)) + { + _cachedTargetPlayer = networkObject.gameObject; + return _cachedTargetPlayer; + } + + return null; + } + + #endregion + + #region Utilities + + private void FindCore() + { + Core core = FindFirstObjectByType(); + if (core != null) + { + _coreTransform = core.transform; + + _coreCollider = core.GetComponent(); + if (_coreCollider == null) + { + _coreCollider = core.GetComponentInChildren(); + } + + if (_coreCollider != null) + { + Debug.Log($"[EnemyAI] {gameObject.name}이(가) 코어를 찾았습니다! (위치: {_coreTransform.position}, Collider: {_coreCollider.GetType().Name})"); + } + else + { + Debug.LogWarning($"[EnemyAI] {gameObject.name}이(가) 코어를 찾았지만 Collider가 없습니다."); + } + } + else + { + Debug.LogWarning($"[EnemyAI] {gameObject.name}이(가) 코어를 찾을 수 없습니다!"); + } + } + + #endregion + + #region Gizmos + + private void OnDrawGizmos() + { + if (!showDebugInfo) return; + + // 감지 범위 + Gizmos.color = Color.yellow; + Gizmos.DrawWireSphere(transform.position, detectionRange); + + // 공격 범위 + Gizmos.color = Color.red; + Gizmos.DrawWireSphere(transform.position, attackRange); + + // 시야각 (360도가 아닐 때만 표시) + if (detectionAngle < 360f) + { + Vector3 forward = transform.forward * detectionRange; + Vector3 leftBoundary = Quaternion.Euler(0, -detectionAngle / 2f, 0) * forward; + Vector3 rightBoundary = Quaternion.Euler(0, detectionAngle / 2f, 0) * forward; + + Gizmos.color = Color.blue; + Gizmos.DrawLine(transform.position, transform.position + leftBoundary); + Gizmos.DrawLine(transform.position, transform.position + rightBoundary); + } + + // 원점 표시 (적대 세력만) + if (aiType == TeamType.Hostile && Application.isPlaying) + { + Gizmos.color = Color.green; + Gizmos.DrawWireSphere(_originPosition, 1f); + Gizmos.DrawLine(transform.position, _originPosition); + } + + // 추적 시작 위치 표시 + if (Application.isPlaying && (_currentState.Value == EnemyAIState.ChasePlayer || _currentState.Value == EnemyAIState.Attack)) + { + Gizmos.color = Color.cyan; + Gizmos.DrawWireSphere(_chaseStartPosition, 1.5f); + Gizmos.DrawLine(transform.position, _chaseStartPosition); + } + + // 코어 방향 및 표면까지의 거리 표시 + if (Application.isPlaying && _currentState.Value == EnemyAIState.MoveToCore && _coreTransform != null) + { + Gizmos.color = Color.magenta; + Gizmos.DrawLine(transform.position, _coreTransform.position); + Gizmos.DrawWireSphere(_coreTransform.position, 2f); + + if (_coreCollider != null) + { + Vector3 closestPoint = _coreCollider.ClosestPoint(transform.position); + Gizmos.color = Color.green; + Gizmos.DrawLine(transform.position, closestPoint); + Gizmos.DrawWireSphere(closestPoint, 0.5f); + } + } + } + + private void OnDrawGizmosSelected() + { + OnDrawGizmos(); + + #if UNITY_EDITOR + if (Application.isPlaying && _agent != null) + { + string pathInfo = _agent.hasPath ? $"Path: {_agent.path.status}" : "No Path"; + string navMeshInfo = _agent.isOnNavMesh ? "On NavMesh" : "OFF NAVMESH!"; + string velocityInfo = $"Velocity: {_agent.velocity.magnitude:F2}"; + + string distanceInfo = ""; + if (_coreTransform != null && _currentState.Value == EnemyAIState.MoveToCore) + { + float surfaceDistance = GetDistanceToCoreSurface(); + distanceInfo = $"\nCore Surface Dist: {surfaceDistance:F2}m"; + } + + string angleInfo = detectionAngle >= 360f ? "\nDetection: 360° (전방향)" : $"\nDetection: {detectionAngle}°"; + + UnityEditor.Handles.Label(transform.position + Vector3.up * 3f, + $"Enemy AI\nState: {_currentState.Value}\nType: {aiType}\n{navMeshInfo}\n{pathInfo}\n{velocityInfo}{angleInfo}\nRange: {detectionRange}m\nAttack: {attackRange}m{distanceInfo}"); + } + #endif + } + + #endregion + } +} \ No newline at end of file diff --git a/Assets/Scripts/EnemyAIController.cs.meta b/Assets/Scripts/EnemyAIController.cs.meta new file mode 100644 index 0000000..202bdc6 --- /dev/null +++ b/Assets/Scripts/EnemyAIController.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 453e726e48d16214f84c6d5737edd7df \ No newline at end of file diff --git a/Assets/Scripts/EnemyAIState.cs b/Assets/Scripts/EnemyAIState.cs new file mode 100644 index 0000000..9c346f2 --- /dev/null +++ b/Assets/Scripts/EnemyAIState.cs @@ -0,0 +1,14 @@ +namespace Northbound +{ + /// + /// 적 AI의 상태 + /// + public enum EnemyAIState + { + Idle, // 대기 (적대 세력 기본 상태) + MoveToCore, // 코어로 이동 (몬스터 기본 상태) + ChasePlayer, // 플레이어 추적 + Attack, // 공격 + ReturnToOrigin // 원래 위치로 복귀 (적대 세력) + } +} \ No newline at end of file diff --git a/Assets/Scripts/EnemyAIState.cs.meta b/Assets/Scripts/EnemyAIState.cs.meta new file mode 100644 index 0000000..9d8ee64 --- /dev/null +++ b/Assets/Scripts/EnemyAIState.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: f34add9794647d043ae26b47fac8e429 \ No newline at end of file diff --git a/Assets/Scripts/EnemyUnit.cs b/Assets/Scripts/EnemyUnit.cs new file mode 100644 index 0000000..634bc64 --- /dev/null +++ b/Assets/Scripts/EnemyUnit.cs @@ -0,0 +1,191 @@ +using Unity.Netcode; +using UnityEngine; + +namespace Northbound +{ + /// + /// 적대 유닛 (적대세력 또는 몬스터) + /// + public class EnemyUnit : NetworkBehaviour, IDamageable, ITeamMember, IVisionProvider + { + [Header("Team Settings")] + [Tooltip("이 유닛의 팀 (Hostile = 적대세력, Monster = 몬스터)")] + public TeamType enemyTeam = TeamType.Hostile; + + [Header("Combat")] + public int maxHealth = 100; + public float visionRange = 10f; + + [Header("Visual")] + public GameObject damageEffectPrefab; + public GameObject destroyEffectPrefab; + + private NetworkVariable _currentHealth = new NetworkVariable( + 0, + NetworkVariableReadPermission.Everyone, + NetworkVariableWritePermission.Server + ); + + private NetworkVariable _team = new NetworkVariable( + TeamType.Neutral, + NetworkVariableReadPermission.Everyone, + NetworkVariableWritePermission.Server + ); + + public override void OnNetworkSpawn() + { + base.OnNetworkSpawn(); + + if (IsServer) + { + _currentHealth.Value = maxHealth; + _team.Value = enemyTeam; + + // FogOfWar 시스템에 등록 + FogOfWarSystem.Instance?.RegisterVisionProvider(this); + + Debug.Log($"[EnemyUnit] {gameObject.name} 스폰됨 (팀: {TeamManager.GetTeamName(_team.Value)})"); + } + } + + public override void OnNetworkDespawn() + { + base.OnNetworkDespawn(); + + if (IsServer) + { + FogOfWarSystem.Instance?.UnregisterVisionProvider(this); + } + } + + #region IDamageable Implementation + + public void TakeDamage(int damage, ulong attackerId) + { + if (!IsServer) return; + if (_currentHealth.Value <= 0) return; + + // 공격자의 팀 확인 + if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(attackerId, out NetworkObject attackerObj)) + { + var attackerTeamMember = attackerObj.GetComponent(); + if (attackerTeamMember != null) + { + if (!TeamManager.CanAttack(attackerTeamMember, this)) + { + Debug.Log($"[EnemyUnit] {TeamManager.GetTeamName(attackerTeamMember.GetTeam())} 팀은 {TeamManager.GetTeamName(_team.Value)} 팀을 공격할 수 없습니다."); + return; + } + } + } + + int actualDamage = Mathf.Min(damage, _currentHealth.Value); + _currentHealth.Value -= actualDamage; + + Debug.Log($"[EnemyUnit] {gameObject.name} ({TeamManager.GetTeamName(_team.Value)})이(가) {actualDamage} 데미지를 받았습니다. 남은 체력: {_currentHealth.Value}/{maxHealth}"); + + // 데미지 이펙트 + ShowDamageEffectClientRpc(); + + // 체력이 0이 되면 파괴 + if (_currentHealth.Value <= 0) + { + DestroyUnit(attackerId); + } + } + + private void DestroyUnit(ulong attackerId) + { + if (!IsServer) return; + + Debug.Log($"[EnemyUnit] {gameObject.name} ({TeamManager.GetTeamName(_team.Value)})이(가) 파괴되었습니다! (공격자: {attackerId})"); + + // 파괴 이펙트 + ShowDestroyEffectClientRpc(); + + // FogOfWar 시스템에서 제거 + FogOfWarSystem.Instance?.UnregisterVisionProvider(this); + + // 네트워크 오브젝트 파괴 + Invoke(nameof(DespawnUnit), 0.5f); + } + + private void DespawnUnit() + { + if (IsServer && NetworkObject != null) + { + NetworkObject.Despawn(true); + } + } + + [ClientRpc] + private void ShowDamageEffectClientRpc() + { + if (damageEffectPrefab != null) + { + GameObject effect = Instantiate(damageEffectPrefab, transform.position, Quaternion.identity); + Destroy(effect, 2f); + } + } + + [ClientRpc] + private void ShowDestroyEffectClientRpc() + { + if (destroyEffectPrefab != null) + { + GameObject effect = Instantiate(destroyEffectPrefab, transform.position, Quaternion.identity); + Destroy(effect, 3f); + } + } + + #endregion + + #region ITeamMember Implementation + + public TeamType GetTeam() => _team.Value; + + public void SetTeam(TeamType team) + { + if (!IsServer) return; + _team.Value = team; + } + + #endregion + + #region IVisionProvider Implementation + + public ulong GetOwnerId() => OwnerClientId; + + public float GetVisionRange() => visionRange; + + public Transform GetTransform() => transform; + + public bool IsActive() => IsSpawned && _currentHealth.Value > 0; + + #endregion + + private void OnDrawGizmosSelected() + { + // 팀 색상으로 시야 범위 표시 + Color teamColor = Application.isPlaying + ? TeamManager.GetTeamColor(_team.Value) + : TeamManager.GetTeamColor(enemyTeam); + + Gizmos.color = new Color(teamColor.r, teamColor.g, teamColor.b, 0.3f); + Gizmos.DrawWireSphere(transform.position, visionRange); + + #if UNITY_EDITOR + if (Application.isPlaying) + { + UnityEditor.Handles.Label(transform.position + Vector3.up * 2f, + $"Team: {TeamManager.GetTeamName(_team.Value)}\nHP: {_currentHealth.Value}/{maxHealth}"); + } + else + { + UnityEditor.Handles.Label(transform.position + Vector3.up * 2f, + $"Team: {TeamManager.GetTeamName(enemyTeam)}"); + } + #endif + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/EnemyUnit.cs.meta b/Assets/Scripts/EnemyUnit.cs.meta new file mode 100644 index 0000000..6561170 --- /dev/null +++ b/Assets/Scripts/EnemyUnit.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 345fc6e7d4f06314f8b548129700eccb \ No newline at end of file diff --git a/Assets/Scripts/ITeamMember.cs b/Assets/Scripts/ITeamMember.cs new file mode 100644 index 0000000..0e9c127 --- /dev/null +++ b/Assets/Scripts/ITeamMember.cs @@ -0,0 +1,11 @@ +namespace Northbound +{ + /// + /// 팀에 속한 엔티티 + /// + public interface ITeamMember + { + TeamType GetTeam(); + void SetTeam(TeamType team); + } +} \ No newline at end of file diff --git a/Assets/Scripts/ITeamMember.cs.meta b/Assets/Scripts/ITeamMember.cs.meta new file mode 100644 index 0000000..366ac40 --- /dev/null +++ b/Assets/Scripts/ITeamMember.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 5e343729a9b720e438ce09faa7886ab0 \ No newline at end of file diff --git a/Assets/Scripts/NetworkPlayerController.cs b/Assets/Scripts/NetworkPlayerController.cs index 4f52afa..347b60d 100644 --- a/Assets/Scripts/NetworkPlayerController.cs +++ b/Assets/Scripts/NetworkPlayerController.cs @@ -2,13 +2,37 @@ using Unity.Netcode; using UnityEngine; using UnityEngine.InputSystem; using Unity.Cinemachine; +using Northbound; -public class NetworkPlayerController : NetworkBehaviour +public class NetworkPlayerController : NetworkBehaviour, ITeamMember, IDamageable { [Header("Movement Settings")] public float moveSpeed = 5f; public float rotationSpeed = 10f; + [Header("Team Settings")] + [SerializeField] private TeamType initialTeam = TeamType.Player; + + [Header("Health Settings")] + [SerializeField] private int maxHealth = 100; + [SerializeField] private bool showHealthBar = true; + + [Header("Visual Effects")] + [SerializeField] private GameObject damageEffectPrefab; + [SerializeField] private GameObject deathEffectPrefab; + + private NetworkVariable _team = new NetworkVariable( + TeamType.Player, + NetworkVariableReadPermission.Everyone, + NetworkVariableWritePermission.Server + ); + + private NetworkVariable _currentHealth = new NetworkVariable( + 100, + NetworkVariableReadPermission.Everyone, + NetworkVariableWritePermission.Server + ); + private Vector2 _moveInput; private CharacterController _controller; private PlayerInputActions _inputActions; @@ -22,6 +46,27 @@ public class NetworkPlayerController : NetworkBehaviour public override void OnNetworkSpawn() { + base.OnNetworkSpawn(); + + // 서버에서 초기화 + if (IsServer) + { + if (_team.Value == TeamType.Neutral) + { + _team.Value = initialTeam; + } + + if (_currentHealth.Value == 0) + { + _currentHealth.Value = maxHealth; + } + + Debug.Log($"[Player] {gameObject.name} 스폰됨 (팀: {TeamManager.GetTeamName(_team.Value)}, 체력: {_currentHealth.Value}/{maxHealth})"); + } + + // 체력 변경 이벤트 구독 + _currentHealth.OnValueChanged += OnHealthChanged; + if (!IsOwner) return; var vcam = GameObject.FindFirstObjectByType(); @@ -39,16 +84,23 @@ public class NetworkPlayerController : NetworkBehaviour public override void OnNetworkDespawn() { + _currentHealth.OnValueChanged -= OnHealthChanged; + if (IsOwner && _inputActions != null) { _inputActions.Disable(); } + + base.OnNetworkDespawn(); } void Update() { if (!IsOwner) return; + // 죽었으면 이동 불가 + if (_currentHealth.Value <= 0) return; + _moveInput = _inputActions.Player.Move.ReadValue(); Vector3 move = new Vector3(_moveInput.x, 0, _moveInput.y).normalized; @@ -68,4 +120,201 @@ public class NetworkPlayerController : NetworkBehaviour _animator.SetFloat("MoveSpeed", move.magnitude); } } + + #region ITeamMember Implementation + + public TeamType GetTeam() => _team.Value; + + public void SetTeam(TeamType team) + { + if (!IsServer) return; + + TeamType previousTeam = _team.Value; + _team.Value = team; + Debug.Log($"[Player] 팀 변경: {TeamManager.GetTeamName(previousTeam)} → {TeamManager.GetTeamName(team)}"); + } + + #endregion + + #region IDamageable Implementation + + public void TakeDamage(int damage, ulong attackerId) + { + if (!IsServer) return; + + // 이미 죽었으면 무시 + if (_currentHealth.Value <= 0) return; + + // 공격자의 팀 확인 + if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(attackerId, out NetworkObject attackerObj)) + { + var attackerTeamMember = attackerObj.GetComponent(); + if (attackerTeamMember != null) + { + if (!TeamManager.CanAttack(attackerTeamMember, this)) + { + Debug.Log($"[Player] {TeamManager.GetTeamName(attackerTeamMember.GetTeam())} 팀은 {TeamManager.GetTeamName(_team.Value)} 팀을 공격할 수 없습니다."); + return; + } + } + } + + // 데미지 적용 + int actualDamage = Mathf.Min(damage, _currentHealth.Value); + _currentHealth.Value -= actualDamage; + + Debug.Log($"[Player] {gameObject.name} ({TeamManager.GetTeamName(_team.Value)})이(가) {actualDamage} 데미지를 받았습니다. 남은 체력: {_currentHealth.Value}/{maxHealth}"); + + // 데미지 이펙트 + ShowDamageEffectClientRpc(); + + // 체력이 0이 되면 사망 + if (_currentHealth.Value <= 0) + { + Die(attackerId); + } + } + + private void Die(ulong killerId) + { + if (!IsServer) return; + + Debug.Log($"[Player] {gameObject.name} ({TeamManager.GetTeamName(_team.Value)})이(가) 사망했습니다! (킬러: {killerId})"); + + // 사망 이펙트 + ShowDeathEffectClientRpc(); + + // 애니메이션 (있는 경우) + if (_animator != null) + { + _animator.SetTrigger("Die"); + } + + // 일정 시간 후 리스폰 또는 디스폰 + Invoke(nameof(HandleDeath), 3f); + } + + private void HandleDeath() + { + if (!IsServer) return; + + // 여기서 리스폰 로직을 추가하거나 게임 오버 처리 + // 예: 리스폰 위치로 이동 및 체력 회복 + Respawn(); + } + + private void Respawn() + { + if (!IsServer) return; + + // 체력 회복 + _currentHealth.Value = maxHealth; + + // 스폰 포인트로 이동 (PlayerSpawnPoint 활용) + var spawnPoints = FindObjectsByType(FindObjectsSortMode.None); + if (spawnPoints.Length > 0) + { + var spawnPoint = spawnPoints[Random.Range(0, spawnPoints.Length)]; + transform.position = spawnPoint.transform.position; + transform.rotation = spawnPoint.transform.rotation; + } + + Debug.Log($"[Player] {gameObject.name} 리스폰!"); + } + + [ClientRpc] + private void ShowDamageEffectClientRpc() + { + if (damageEffectPrefab != null) + { + GameObject effect = Instantiate(damageEffectPrefab, transform.position + Vector3.up, Quaternion.identity); + Destroy(effect, 2f); + } + } + + [ClientRpc] + private void ShowDeathEffectClientRpc() + { + if (deathEffectPrefab != null) + { + GameObject effect = Instantiate(deathEffectPrefab, transform.position, Quaternion.identity); + Destroy(effect, 3f); + } + } + + #endregion + + #region Health Management + + /// + /// 현재 체력 + /// + public int GetCurrentHealth() => _currentHealth.Value; + + /// + /// 최대 체력 + /// + public int GetMaxHealth() => maxHealth; + + /// + /// 체력 비율 (0.0 ~ 1.0) + /// + public float GetHealthPercentage() + { + return maxHealth > 0 ? (float)_currentHealth.Value / maxHealth : 0f; + } + + /// + /// 죽었는지 여부 + /// + public bool IsDead() => _currentHealth.Value <= 0; + + /// + /// 체력 회복 + /// + public void Heal(int amount) + { + if (!IsServer) return; + + int healAmount = Mathf.Min(amount, maxHealth - _currentHealth.Value); + _currentHealth.Value += healAmount; + + Debug.Log($"[Player] {gameObject.name}이(가) {healAmount} 회복되었습니다. 현재 체력: {_currentHealth.Value}/{maxHealth}"); + } + + private void OnHealthChanged(int previousValue, int newValue) + { + // 체력바 UI 업데이트 또는 체력 변경 시각 효과 + Debug.Log($"[Player] 체력 변경: {previousValue} → {newValue}"); + + // 클라이언트에서도 체력 변경 인지 가능 + if (IsOwner) + { + // UI 업데이트 등 + } + } + + #endregion + + #region Gizmos + + private void OnDrawGizmosSelected() + { + #if UNITY_EDITOR + if (Application.isPlaying) + { + string teamName = TeamManager.GetTeamName(_team.Value); + UnityEditor.Handles.Label(transform.position + Vector3.up * 3f, + $"Player: {gameObject.name}\nTeam: {teamName}\nHP: {_currentHealth.Value}/{maxHealth}"); + } + else + { + string teamName = TeamManager.GetTeamName(initialTeam); + UnityEditor.Handles.Label(transform.position + Vector3.up * 3f, + $"Player: {gameObject.name}\nTeam: {teamName}\nHP: {maxHealth}/{maxHealth}"); + } + #endif + } + + #endregion } diff --git a/Assets/Scripts/TeamManager.cs b/Assets/Scripts/TeamManager.cs new file mode 100644 index 0000000..1101af0 --- /dev/null +++ b/Assets/Scripts/TeamManager.cs @@ -0,0 +1,96 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace Northbound +{ + /// + /// 팀 간의 관계 및 적대 관계를 관리 + /// + public static class TeamManager + { + // 팀 간 적대 관계 테이블 + private static readonly Dictionary<(TeamType, TeamType), bool> _hostilityTable = new Dictionary<(TeamType, TeamType), bool> + { + // 플레이어 vs 적대 세력 + { (TeamType.Player, TeamType.Hostile), true }, + { (TeamType.Hostile, TeamType.Player), true }, + + // 플레이어 vs 몬스터 + { (TeamType.Player, TeamType.Monster), true }, + { (TeamType.Monster, TeamType.Player), true }, + + // 적대 세력 vs 몬스터 (서로 공격하지 않음) + { (TeamType.Hostile, TeamType.Monster), false }, + { (TeamType.Monster, TeamType.Hostile), false }, + + // 같은 팀끼리는 공격하지 않음 + { (TeamType.Player, TeamType.Player), false }, + { (TeamType.Hostile, TeamType.Hostile), false }, + { (TeamType.Monster, TeamType.Monster), false }, + + // 중립은 공격받지 않음 + { (TeamType.Neutral, TeamType.Player), false }, + { (TeamType.Neutral, TeamType.Hostile), false }, + { (TeamType.Neutral, TeamType.Monster), false }, + { (TeamType.Player, TeamType.Neutral), false }, + { (TeamType.Hostile, TeamType.Neutral), false }, + { (TeamType.Monster, TeamType.Neutral), false }, + { (TeamType.Neutral, TeamType.Neutral), false } + }; + + /// + /// 두 팀이 적대 관계인지 확인 + /// + public static bool AreHostile(TeamType team1, TeamType team2) + { + if (_hostilityTable.TryGetValue((team1, team2), out bool isHostile)) + { + return isHostile; + } + + // 기본적으로 다른 팀이면 적대 + return team1 != team2 && team1 != TeamType.Neutral && team2 != TeamType.Neutral; + } + + /// + /// 공격 가능한 대상인지 확인 + /// + public static bool CanAttack(ITeamMember attacker, ITeamMember target) + { + if (attacker == null || target == null) + return false; + + return AreHostile(attacker.GetTeam(), target.GetTeam()); + } + + /// + /// 팀의 색상 가져오기 (UI 표시용) + /// + public static Color GetTeamColor(TeamType team) + { + return team switch + { + TeamType.Player => Color.blue, + TeamType.Hostile => Color.red, + TeamType.Monster => new Color(0.8f, 0f, 0.8f), // 보라색 + TeamType.Neutral => Color.gray, + _ => Color.white + }; + } + + /// + /// 팀 이름 가져오기 (한글) + /// + public static string GetTeamName(TeamType team) + { + return team switch + { + TeamType.Player => "플레이어", + TeamType.Hostile => "적대 세력", + TeamType.Monster => "몬스터", + TeamType.Neutral => "중립", + _ => "알 수 없음" + }; + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/TeamManager.cs.meta b/Assets/Scripts/TeamManager.cs.meta new file mode 100644 index 0000000..0f66789 --- /dev/null +++ b/Assets/Scripts/TeamManager.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 95252f6c80f5e2a40b0d4f95c23b2039 \ No newline at end of file diff --git a/Assets/Scripts/TeamType.cs b/Assets/Scripts/TeamType.cs new file mode 100644 index 0000000..2972275 --- /dev/null +++ b/Assets/Scripts/TeamType.cs @@ -0,0 +1,13 @@ +namespace Northbound +{ + /// + /// 게임 내 팀 타입 + /// + public enum TeamType + { + Neutral = 0, // 중립 (공격받지 않음) + Player = 1, // 플레이어 팀 + Hostile = 2, // 적대 세력 (플레이어 공격) + Monster = 3 // 몬스터 (플레이어 공격) + } +} \ No newline at end of file diff --git a/Assets/Scripts/TeamType.cs.meta b/Assets/Scripts/TeamType.cs.meta new file mode 100644 index 0000000..16312e3 --- /dev/null +++ b/Assets/Scripts/TeamType.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: fa67976f76e9a5f4fac1e55e7bfedf52 \ No newline at end of file diff --git a/ExternAttributes.Editor.csproj b/ExternAttributes.Editor.csproj index 74feaaf..6f4b026 100644 --- a/ExternAttributes.Editor.csproj +++ b/ExternAttributes.Editor.csproj @@ -1198,14 +1198,14 @@ Library\ScriptAssemblies\UnityEngine.UI.dll False - - Library\ScriptAssemblies\UnityEditor.TestRunner.dll - False - Library\ScriptAssemblies\UnityEngine.TestRunner.dll False + + Library\ScriptAssemblies\UnityEditor.TestRunner.dll + False + diff --git a/FlatKit.Utils.Editor.csproj b/FlatKit.Utils.Editor.csproj index f4dd6f4..d2b30be 100644 --- a/FlatKit.Utils.Editor.csproj +++ b/FlatKit.Utils.Editor.csproj @@ -1181,14 +1181,14 @@ Library\ScriptAssemblies\UnityEngine.UI.dll False - - Library\ScriptAssemblies\UnityEditor.TestRunner.dll - False - Library\ScriptAssemblies\UnityEngine.TestRunner.dll False + + Library\ScriptAssemblies\UnityEditor.TestRunner.dll + False + diff --git a/Packages/manifest.json b/Packages/manifest.json index c6add5b..efe669f 100644 --- a/Packages/manifest.json +++ b/Packages/manifest.json @@ -8,6 +8,7 @@ "com.unity.2d.tilemap": "1.0.0", "com.unity.2d.tilemap.extras": "6.0.1", "com.unity.2d.tooling": "1.0.0", + "com.unity.ai.navigation": "2.0.9", "com.unity.cinemachine": "3.1.5", "com.unity.collab-proxy": "2.10.2", "com.unity.ide.rider": "3.0.38", diff --git a/Packages/packages-lock.json b/Packages/packages-lock.json index 318bc10..4b0f52d 100644 --- a/Packages/packages-lock.json +++ b/Packages/packages-lock.json @@ -99,6 +99,15 @@ }, "url": "https://packages.unity.com" }, + "com.unity.ai.navigation": { + "version": "2.0.9", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.modules.ai": "1.0.0" + }, + "url": "https://packages.unity.com" + }, "com.unity.burst": { "version": "1.8.27", "depth": 1,