네트워크 멀티플레이 대응
This commit is contained in:
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Bash(tree:*)"
|
||||
]
|
||||
}
|
||||
}
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -8,4 +8,8 @@
|
||||
[Uu]ser[Ss]ettings/
|
||||
|
||||
.vs
|
||||
.vs/*
|
||||
.vs/*
|
||||
.vscode
|
||||
.claude
|
||||
Assets/_Recovery
|
||||
Assets/_Recovery.meta
|
||||
|
||||
@@ -43,7 +43,6 @@
|
||||
<UnityVersion>6000.3.5f2</UnityVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Analyzer Include="C:\Program Files\Microsoft Visual Studio\18\Community\Common7\IDE\Extensions\Microsoft\Visual Studio Tools for Unity\Analyzers\Microsoft.Unity.Analyzers.dll" />
|
||||
<Analyzer Include="C:\Program Files\Unity\Hub\Editor\6000.3.5f2\Editor\Data\Tools\BuildPipeline\Unity.SourceGenerators\Unity.SourceGenerators.dll" />
|
||||
<Analyzer Include="C:\Program Files\Unity\Hub\Editor\6000.3.5f2\Editor\Data\Tools\BuildPipeline\Unity.SourceGenerators\Unity.Properties.SourceGenerator.dll" />
|
||||
<Analyzer Include="C:\Program Files\Unity\Hub\Editor\6000.3.5f2\Editor\Data\Tools\BuildPipeline\Unity.SourceGenerators\Unity.UIToolkit.SourceGenerator.dll" />
|
||||
@@ -621,6 +620,10 @@
|
||||
<HintPath>C:\Program Files\Unity\Hub\Editor\6000.3.5f2\Editor\Data\PlaybackEngines\WindowsStandaloneSupport\UnityEditor.WindowsStandalone.Extensions.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="websocket-sharp">
|
||||
<HintPath>Library\PackageCache\com.unity.services.wire@9a73acde80cc\Plugins\websocket-sharp.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.VisualScripting.YamlDotNet">
|
||||
<HintPath>Library\PackageCache\com.unity.visualscripting@191c0d7e3b69\Editor\VisualScripting.Core\Dependencies\YamlDotNet\Unity.VisualScripting.YamlDotNet.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
@@ -1213,10 +1216,22 @@
|
||||
<HintPath>Library\ScriptAssemblies\Unity.Multiplayer.Tools.MetricTestData.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Services.QoS">
|
||||
<HintPath>Library\ScriptAssemblies\Unity.Services.QoS.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Services.Multiplayer.Editor.Shared">
|
||||
<HintPath>Library\ScriptAssemblies\Unity.Services.Multiplayer.Editor.Shared.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Rendering.LightTransport.Editor">
|
||||
<HintPath>Library\ScriptAssemblies\Unity.Rendering.LightTransport.Editor.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Services.Core">
|
||||
<HintPath>Library\ScriptAssemblies\Unity.Services.Core.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Netcode.Runtime">
|
||||
<HintPath>Library\ScriptAssemblies\Unity.Netcode.Runtime.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
@@ -1245,14 +1260,14 @@
|
||||
<HintPath>Library\ScriptAssemblies\PPv2URPConverters.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.2D.Common.Editor">
|
||||
<HintPath>Library\ScriptAssemblies\Unity.2D.Common.Editor.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.AI.Navigation.Updater">
|
||||
<HintPath>Library\ScriptAssemblies\Unity.AI.Navigation.Updater.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.2D.Common.Editor">
|
||||
<HintPath>Library\ScriptAssemblies\Unity.2D.Common.Editor.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.AI.Navigation.Editor">
|
||||
<HintPath>Library\ScriptAssemblies\Unity.AI.Navigation.Editor.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
@@ -1277,6 +1292,10 @@
|
||||
<HintPath>Library\ScriptAssemblies\Unity.RenderPipelines.Universal.2D.Runtime.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Services.Core.Components">
|
||||
<HintPath>Library\ScriptAssemblies\Unity.Services.Core.Components.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Multiplayer.Tools.NetStatsMonitor.Configuration">
|
||||
<HintPath>Library\ScriptAssemblies\Unity.Multiplayer.Tools.NetStatsMonitor.Configuration.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
@@ -1289,8 +1308,12 @@
|
||||
<HintPath>Library\ScriptAssemblies\Unity.2D.Aseprite.Common.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Rider.Editor">
|
||||
<HintPath>Library\ScriptAssemblies\Unity.Rider.Editor.dll</HintPath>
|
||||
<Reference Include="Unity.Cursor.Editor">
|
||||
<HintPath>Library\ScriptAssemblies\Unity.Cursor.Editor.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Services.Authentication">
|
||||
<HintPath>Library\ScriptAssemblies\Unity.Services.Authentication.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.AI.Navigation.Editor.ConversionSystem">
|
||||
@@ -1301,10 +1324,22 @@
|
||||
<HintPath>Library\ScriptAssemblies\Unity.2D.Aseprite.Editor.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Rider.Editor">
|
||||
<HintPath>Library\ScriptAssemblies\Unity.Rider.Editor.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.RenderPipelines.ShaderGraph.ShaderGraphLibrary">
|
||||
<HintPath>Library\ScriptAssemblies\Unity.RenderPipelines.ShaderGraph.ShaderGraphLibrary.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Services.Authentication.Editor.Shared">
|
||||
<HintPath>Library\ScriptAssemblies\Unity.Services.Authentication.Editor.Shared.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Services.Multiplayer.Editor.Matchmaker.Authoring">
|
||||
<HintPath>Library\ScriptAssemblies\Unity.Services.Multiplayer.Editor.Matchmaker.Authoring.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.VisualScripting.Flow">
|
||||
<HintPath>Library\ScriptAssemblies\Unity.VisualScripting.Flow.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
@@ -1317,6 +1352,10 @@
|
||||
<HintPath>Library\ScriptAssemblies\Unity.2D.Tilemap.Extras.Editor.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Services.Authentication.PlayerAccounts">
|
||||
<HintPath>Library\ScriptAssemblies\Unity.Services.Authentication.PlayerAccounts.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.VisualStudio.Editor">
|
||||
<HintPath>Library\ScriptAssemblies\Unity.VisualStudio.Editor.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
@@ -1349,6 +1388,14 @@
|
||||
<HintPath>Library\ScriptAssemblies\UnityEngine.UI.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Services.Core.Environments">
|
||||
<HintPath>Library\ScriptAssemblies\Unity.Services.Core.Environments.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Services.Multiplayer">
|
||||
<HintPath>Library\ScriptAssemblies\Unity.Services.Multiplayer.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="com.unity.multiplayer.tools.window">
|
||||
<HintPath>Library\ScriptAssemblies\com.unity.multiplayer.tools.window.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
@@ -1485,10 +1532,18 @@
|
||||
<HintPath>Library\ScriptAssemblies\Unity.Multiplayer.Tools.NetVis.Editor.Visualization.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Services.Core.Analytics">
|
||||
<HintPath>Library\ScriptAssemblies\Unity.Services.Core.Analytics.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Multiplayer.Tools.NetStatsMonitor.Editor">
|
||||
<HintPath>Library\ScriptAssemblies\Unity.Multiplayer.Tools.NetStatsMonitor.Editor.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Multiplayer.Center.NetcodeForGameObjectsExample">
|
||||
<HintPath>Library\ScriptAssemblies\Unity.Multiplayer.Center.NetcodeForGameObjectsExample.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Multiplayer.Tools.DependencyInjection.UIElements">
|
||||
<HintPath>Library\ScriptAssemblies\Unity.Multiplayer.Tools.DependencyInjection.UIElements.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
@@ -1553,6 +1608,10 @@
|
||||
<HintPath>Library\ScriptAssemblies\Unity.Multiplayer.Center.Common.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Multiplayer.Center.Integrations">
|
||||
<HintPath>Library\ScriptAssemblies\Unity.Multiplayer.Center.Integrations.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Burst">
|
||||
<HintPath>Library\ScriptAssemblies\Unity.Burst.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
|
||||
@@ -43,16 +43,13 @@
|
||||
<UnityVersion>6000.3.5f2</UnityVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Analyzer Include="C:\Program Files\Microsoft Visual Studio\18\Community\Common7\IDE\Extensions\Microsoft\Visual Studio Tools for Unity\Analyzers\Microsoft.Unity.Analyzers.dll" />
|
||||
<Analyzer Include="C:\Program Files\Unity\Hub\Editor\6000.3.5f2\Editor\Data\Tools\BuildPipeline\Unity.SourceGenerators\Unity.SourceGenerators.dll" />
|
||||
<Analyzer Include="C:\Program Files\Unity\Hub\Editor\6000.3.5f2\Editor\Data\Tools\BuildPipeline\Unity.SourceGenerators\Unity.Properties.SourceGenerator.dll" />
|
||||
<Analyzer Include="C:\Program Files\Unity\Hub\Editor\6000.3.5f2\Editor\Data\Tools\BuildPipeline\Unity.SourceGenerators\Unity.UIToolkit.SourceGenerator.dll" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Assets\Scripts\NetworkManagerUI.cs" />
|
||||
<Compile Include="Assets\Data\Scripts\DataClasses\WaveMasterData.cs" />
|
||||
<Compile Include="Assets\FlatKit\Demos\[Demo] Desert\Scripts\FloatingMotion.cs" />
|
||||
<Compile Include="Assets\Scripts\PlayerResourceInventory.cs" />
|
||||
<Compile Include="Assets\Scripts\ITeamMember.cs" />
|
||||
<Compile Include="Assets\Scripts\EnemyUnit.cs" />
|
||||
<Compile Include="Assets\Scripts\PlayerActionSystem.cs" />
|
||||
@@ -64,10 +61,12 @@
|
||||
<Compile Include="Assets\Scripts\FogOfWarRenderer.cs" />
|
||||
<Compile Include="Assets\Scripts\ResourcePickup.cs" />
|
||||
<Compile Include="Assets\Scripts\PlayerVisionProvider.cs" />
|
||||
<Compile Include="Assets\Scripts\InputActionManager.cs" />
|
||||
<Compile Include="Assets\Scripts\PlayerController.cs" />
|
||||
<Compile Include="Assets\FlatKit\Demos\Common\Scripts\Motion\OrbitMotion.cs" />
|
||||
<Compile Include="Assets\Scripts\BuildingDamageTest.cs" />
|
||||
<Compile Include="Assets\Scripts\EnemyAIController.cs" />
|
||||
<Compile Include="Assets\Scripts\GameConstants.cs" />
|
||||
<Compile Include="Assets\Scripts\EquipmentSocket.cs" />
|
||||
<Compile Include="Assets\Scripts\TeamManager.cs" />
|
||||
<Compile Include="Assets\Scripts\TeamGate.cs" />
|
||||
@@ -81,9 +80,12 @@
|
||||
<Compile Include="Assets\Scripts\AttackAction.cs" />
|
||||
<Compile Include="Assets\Scripts\IInteractable.cs" />
|
||||
<Compile Include="Assets\FlatKit\Demos\Common\Scripts\UvScroller.cs" />
|
||||
<Compile Include="Assets\Scripts\TeamMemberNetworkBehaviour.cs" />
|
||||
<Compile Include="Assets\Scripts\PlayerInventory.cs" />
|
||||
<Compile Include="Assets\Scripts\NetworkSpawnHelper.cs" />
|
||||
<Compile Include="Assets\Scripts\GlobalTimer.cs" />
|
||||
<Compile Include="Assets\Scripts\SmartAutoHost.cs" />
|
||||
<Compile Include="Assets\InputSystem_Actions.cs" />
|
||||
<Compile Include="Assets\Data\Scripts\DataClasses\MonsterMasterData.cs" />
|
||||
<Compile Include="Assets\FlatKit\Demos\[Demo] Desert\Scripts\BillboardLineRendererCircle.cs" />
|
||||
<Compile Include="Assets\Scripts\BuildingHealthBar.cs" />
|
||||
<Compile Include="Assets\Scripts\PlayerSpawnPoint.cs" />
|
||||
@@ -93,15 +95,18 @@
|
||||
<Compile Include="Assets\Scripts\BuildingManager.cs" />
|
||||
<Compile Include="Assets\Scripts\GhostMaterialTest.cs" />
|
||||
<Compile Include="Assets\Scripts\BuildingPlacement.cs" />
|
||||
<Compile Include="Assets\Scripts\PlayerSpawnPositionSetter.cs" />
|
||||
<Compile Include="Assets\Scripts\Core.cs" />
|
||||
<Compile Include="Assets\Scripts\EnemyPortal.cs" />
|
||||
<Compile Include="Assets\FlatKit\Demos\Common\Scripts\Motion\LinearMotion.cs" />
|
||||
<Compile Include="Assets\Scripts\IDamageable.cs" />
|
||||
<Compile Include="Assets\Scripts\NetworkSpawnManager.cs" />
|
||||
<Compile Include="Assets\Scripts\FogOfWarSystem.cs" />
|
||||
<Compile Include="Assets\Data\Scripts\DataClasses\MonsterData.cs" />
|
||||
<Compile Include="Assets\Scripts\IAction.cs" />
|
||||
<Compile Include="Assets\Scripts\NetworkPlayerController.cs" />
|
||||
<Compile Include="Assets\Scripts\PlayerInteraction.cs" />
|
||||
<Compile Include="Assets\Scripts\DamageableNetworkBehaviour.cs" />
|
||||
<Compile Include="Assets\Scripts\FogOfWarVisibility.cs" />
|
||||
<Compile Include="Assets\Scripts\EnemyAIState.cs" />
|
||||
<Compile Include="Assets\Scripts\EquipmentData.cs" />
|
||||
@@ -687,6 +692,10 @@
|
||||
<HintPath>C:\Program Files\Unity\Hub\Editor\6000.3.5f2\Editor\Data\Managed\UnityEngine\UnityEditor.XRModule.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="websocket-sharp">
|
||||
<HintPath>Library\PackageCache\com.unity.services.wire@9a73acde80cc\Plugins\websocket-sharp.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Collections.LowLevel.ILSupport">
|
||||
<HintPath>Library\PackageCache\com.unity.collections@aea9d3bd5e19\Unity.Collections.LowLevel.ILSupport\Unity.Collections.LowLevel.ILSupport.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
@@ -1231,10 +1240,22 @@
|
||||
<HintPath>Library\ScriptAssemblies\Unity.Multiplayer.Tools.MetricTestData.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Services.QoS">
|
||||
<HintPath>Library\ScriptAssemblies\Unity.Services.QoS.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Services.Multiplayer.Editor.Shared">
|
||||
<HintPath>Library\ScriptAssemblies\Unity.Services.Multiplayer.Editor.Shared.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Rendering.LightTransport.Editor">
|
||||
<HintPath>Library\ScriptAssemblies\Unity.Rendering.LightTransport.Editor.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Services.Core">
|
||||
<HintPath>Library\ScriptAssemblies\Unity.Services.Core.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Netcode.Runtime">
|
||||
<HintPath>Library\ScriptAssemblies\Unity.Netcode.Runtime.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
@@ -1263,14 +1284,14 @@
|
||||
<HintPath>Library\ScriptAssemblies\PPv2URPConverters.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.2D.Common.Editor">
|
||||
<HintPath>Library\ScriptAssemblies\Unity.2D.Common.Editor.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.AI.Navigation.Updater">
|
||||
<HintPath>Library\ScriptAssemblies\Unity.AI.Navigation.Updater.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.2D.Common.Editor">
|
||||
<HintPath>Library\ScriptAssemblies\Unity.2D.Common.Editor.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.AI.Navigation.Editor">
|
||||
<HintPath>Library\ScriptAssemblies\Unity.AI.Navigation.Editor.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
@@ -1295,6 +1316,10 @@
|
||||
<HintPath>Library\ScriptAssemblies\Unity.RenderPipelines.Universal.2D.Runtime.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Services.Core.Components">
|
||||
<HintPath>Library\ScriptAssemblies\Unity.Services.Core.Components.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Multiplayer.Tools.NetStatsMonitor.Configuration">
|
||||
<HintPath>Library\ScriptAssemblies\Unity.Multiplayer.Tools.NetStatsMonitor.Configuration.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
@@ -1307,8 +1332,12 @@
|
||||
<HintPath>Library\ScriptAssemblies\Unity.2D.Aseprite.Common.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Rider.Editor">
|
||||
<HintPath>Library\ScriptAssemblies\Unity.Rider.Editor.dll</HintPath>
|
||||
<Reference Include="Unity.Cursor.Editor">
|
||||
<HintPath>Library\ScriptAssemblies\Unity.Cursor.Editor.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Services.Authentication">
|
||||
<HintPath>Library\ScriptAssemblies\Unity.Services.Authentication.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.AI.Navigation.Editor.ConversionSystem">
|
||||
@@ -1319,10 +1348,22 @@
|
||||
<HintPath>Library\ScriptAssemblies\Unity.2D.Aseprite.Editor.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Rider.Editor">
|
||||
<HintPath>Library\ScriptAssemblies\Unity.Rider.Editor.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.RenderPipelines.ShaderGraph.ShaderGraphLibrary">
|
||||
<HintPath>Library\ScriptAssemblies\Unity.RenderPipelines.ShaderGraph.ShaderGraphLibrary.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Services.Authentication.Editor.Shared">
|
||||
<HintPath>Library\ScriptAssemblies\Unity.Services.Authentication.Editor.Shared.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Services.Multiplayer.Editor.Matchmaker.Authoring">
|
||||
<HintPath>Library\ScriptAssemblies\Unity.Services.Multiplayer.Editor.Matchmaker.Authoring.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.VisualScripting.Flow">
|
||||
<HintPath>Library\ScriptAssemblies\Unity.VisualScripting.Flow.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
@@ -1335,6 +1376,10 @@
|
||||
<HintPath>Library\ScriptAssemblies\Unity.2D.Tilemap.Extras.Editor.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Services.Authentication.PlayerAccounts">
|
||||
<HintPath>Library\ScriptAssemblies\Unity.Services.Authentication.PlayerAccounts.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.VisualStudio.Editor">
|
||||
<HintPath>Library\ScriptAssemblies\Unity.VisualStudio.Editor.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
@@ -1367,6 +1412,14 @@
|
||||
<HintPath>Library\ScriptAssemblies\UnityEngine.UI.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Services.Core.Environments">
|
||||
<HintPath>Library\ScriptAssemblies\Unity.Services.Core.Environments.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Services.Multiplayer">
|
||||
<HintPath>Library\ScriptAssemblies\Unity.Services.Multiplayer.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="com.unity.multiplayer.tools.window">
|
||||
<HintPath>Library\ScriptAssemblies\com.unity.multiplayer.tools.window.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
@@ -1503,10 +1556,18 @@
|
||||
<HintPath>Library\ScriptAssemblies\Unity.Multiplayer.Tools.NetVis.Editor.Visualization.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Services.Core.Analytics">
|
||||
<HintPath>Library\ScriptAssemblies\Unity.Services.Core.Analytics.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Multiplayer.Tools.NetStatsMonitor.Editor">
|
||||
<HintPath>Library\ScriptAssemblies\Unity.Multiplayer.Tools.NetStatsMonitor.Editor.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Multiplayer.Center.NetcodeForGameObjectsExample">
|
||||
<HintPath>Library\ScriptAssemblies\Unity.Multiplayer.Center.NetcodeForGameObjectsExample.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Multiplayer.Tools.DependencyInjection.UIElements">
|
||||
<HintPath>Library\ScriptAssemblies\Unity.Multiplayer.Tools.DependencyInjection.UIElements.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
@@ -1571,6 +1632,10 @@
|
||||
<HintPath>Library\ScriptAssemblies\Unity.Multiplayer.Center.Common.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Multiplayer.Center.Integrations">
|
||||
<HintPath>Library\ScriptAssemblies\Unity.Multiplayer.Center.Integrations.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.Burst">
|
||||
<HintPath>Library\ScriptAssemblies\Unity.Burst.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
|
||||
@@ -27137,13 +27137,20 @@ AnimationClip:
|
||||
floatParameter: 0
|
||||
intParameter: 0
|
||||
messageOptions: 0
|
||||
- time: 1.0333333
|
||||
- time: 1
|
||||
functionName: OnUnequipWeapon
|
||||
data: handslot.r
|
||||
objectReferenceParameter: {fileID: 0}
|
||||
floatParameter: 0
|
||||
intParameter: 0
|
||||
messageOptions: 0
|
||||
- time: 1.0333333
|
||||
functionName: OnAttackHit
|
||||
data:
|
||||
objectReferenceParameter: {fileID: 0}
|
||||
floatParameter: 0
|
||||
intParameter: 0
|
||||
messageOptions: 0
|
||||
- time: 1.0666667
|
||||
functionName: OnAttackComplete
|
||||
data:
|
||||
|
||||
124
Assets/Prefabs/Bat.prefab
Normal file
124
Assets/Prefabs/Bat.prefab
Normal file
@@ -0,0 +1,124 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!1 &6430526095172430446
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 5247455493693100088}
|
||||
- component: {fileID: 1386822053491278300}
|
||||
- component: {fileID: 337360336842274253}
|
||||
m_Layer: 0
|
||||
m_Name: GameObject
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!4 &5247455493693100088
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6430526095172430446}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 6825883197719469110}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!33 &1386822053491278300
|
||||
MeshFilter:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6430526095172430446}
|
||||
m_Mesh: {fileID: 716655249603938094, guid: 1261145a64d4f3e43bee728a02c1b5e3, type: 3}
|
||||
--- !u!23 &337360336842274253
|
||||
MeshRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6430526095172430446}
|
||||
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: d64c307f1b4197c44970c29f9845c245, 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!1 &9113353226622644240
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 6825883197719469110}
|
||||
m_Layer: 0
|
||||
m_Name: Bat
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!4 &6825883197719469110
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 9113353226622644240}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: -5.09173, y: 2, z: -37.867}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 5247455493693100088}
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
7
Assets/Prefabs/Bat.prefab.meta
Normal file
7
Assets/Prefabs/Bat.prefab.meta
Normal file
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 66fa238e7512e1547bf2ef4b62d76afd
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -10,16 +10,18 @@ GameObject:
|
||||
m_Component:
|
||||
- component: {fileID: 5887522270574905679}
|
||||
- component: {fileID: 2636831972010436653}
|
||||
- component: {fileID: 3792365921352178844}
|
||||
- component: {fileID: 5989504606015899400}
|
||||
- component: {fileID: 1698609800605343773}
|
||||
- component: {fileID: 3007098678582223509}
|
||||
- component: {fileID: 1883169379180791275}
|
||||
- component: {fileID: 8729870597719024730}
|
||||
- component: {fileID: 5217638038410020423}
|
||||
- component: {fileID: 9062880697264624749}
|
||||
- component: {fileID: 2753625045547947614}
|
||||
- component: {fileID: 6066313428661204362}
|
||||
- component: {fileID: 2443072964133329520}
|
||||
- component: {fileID: 2148255267416253297}
|
||||
- component: {fileID: 7256821725794344131}
|
||||
- component: {fileID: 5045194683846919184}
|
||||
- component: {fileID: 7246316046087380146}
|
||||
m_Layer: 9
|
||||
m_Name: Player
|
||||
m_TagString: Untagged
|
||||
@@ -55,7 +57,7 @@ MonoBehaviour:
|
||||
m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Unity.Netcode.Runtime::Unity.Netcode.NetworkObject
|
||||
GlobalObjectIdHash: 4211758632
|
||||
GlobalObjectIdHash: 1360081626
|
||||
InScenePlacedSourceGlobalObjectIdHash: 4211758632
|
||||
DeferredDespawnTick: 0
|
||||
Ownership: 1
|
||||
@@ -68,7 +70,7 @@ MonoBehaviour:
|
||||
AutoObjectParentSync: 1
|
||||
SyncOwnerTransformWhenParented: 1
|
||||
AllowOwnerToParent: 0
|
||||
--- !u!114 &3792365921352178844
|
||||
--- !u!114 &5989504606015899400
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
@@ -77,15 +79,15 @@ MonoBehaviour:
|
||||
m_GameObject: {fileID: 1314983689436087486}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 9e8f7d6c5b4a3d2e1f0a9b8c7d6e5f4a, type: 3}
|
||||
m_Script: {fileID: 11500000, guid: 19b1385c0dcb77240b2cfb3c6b10f717, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Assembly-CSharp::NetworkPlayerController
|
||||
m_EditorClassIdentifier: Assembly-CSharp::Northbound.NetworkPlayerController
|
||||
ShowTopMostFoldoutHeaderGroup: 1
|
||||
moveSpeed: 5
|
||||
rotationSpeed: 10
|
||||
initialTeam: 1
|
||||
maxHealth: 100
|
||||
showHealthBar: 1
|
||||
showHealthBar: 0
|
||||
damageEffectPrefab: {fileID: 0}
|
||||
deathEffectPrefab: {fileID: 0}
|
||||
--- !u!95 &1698609800605343773
|
||||
@@ -175,7 +177,7 @@ MonoBehaviour:
|
||||
interactableLayer:
|
||||
serializedVersion: 2
|
||||
m_Bits: 128
|
||||
rayOrigin: {fileID: 0}
|
||||
rayOrigin: {fileID: 6366268612314379957}
|
||||
useForwardDirection: 1
|
||||
playAnimations: 1
|
||||
useAnimationEvents: 1
|
||||
@@ -196,9 +198,9 @@ MonoBehaviour:
|
||||
m_EditorClassIdentifier: Assembly-CSharp::Northbound.PlayerActionSystem
|
||||
ShowTopMostFoldoutHeaderGroup: 1
|
||||
actionComponents:
|
||||
- {fileID: 9062880697264624749}
|
||||
- {fileID: 2753625045547947614}
|
||||
playAnimations: 1
|
||||
--- !u!114 &9062880697264624749
|
||||
--- !u!114 &2753625045547947614
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
@@ -207,23 +209,23 @@ MonoBehaviour:
|
||||
m_GameObject: {fileID: 1314983689436087486}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 66ec3984614d8a64b8eae821376d038d, type: 3}
|
||||
m_Script: {fileID: 11500000, guid: c467f66382052bc429affc5cb01d17db, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Assembly-CSharp::Northbound.AttackAction
|
||||
ShowTopMostFoldoutHeaderGroup: 1
|
||||
attackRange: 3
|
||||
attackDamage: 100
|
||||
attackRange: 2
|
||||
attackDamage: 10
|
||||
attackCooldown: 0.5
|
||||
attackableLayer:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_Bits: 256
|
||||
attackAnimationTrigger: Attack
|
||||
useAnimationEvents: 1
|
||||
blockDuringAnimation: 1
|
||||
useEquipment: 1
|
||||
equipmentData:
|
||||
socketName: handslot.r
|
||||
equipmentPrefab: {fileID: 919132149155446097, guid: 1261145a64d4f3e43bee728a02c1b5e3, type: 3}
|
||||
equipmentPrefab: {fileID: 9113353226622644240, guid: 66fa238e7512e1547bf2ef4b62d76afd, type: 3}
|
||||
attachOnStart: 1
|
||||
detachOnEnd: 1
|
||||
keepEquipped: 0
|
||||
@@ -243,24 +245,14 @@ MonoBehaviour:
|
||||
m_Script: {fileID: 11500000, guid: ac908541bf903c745a1794d409a5f048, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Assembly-CSharp::Northbound.EquipmentSocket
|
||||
ShowTopMostFoldoutHeaderGroup: 1
|
||||
sockets:
|
||||
- socketName: handslot.r
|
||||
socketTransform: {fileID: 2844947653216056832}
|
||||
currentEquipment: {fileID: 0}
|
||||
--- !u!114 &2443072964133329520
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1314983689436087486}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 3c64072402b0a3f46a674eb73c5541ac, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Assembly-CSharp::Northbound.PlayerResourceInventory
|
||||
ShowTopMostFoldoutHeaderGroup: 1
|
||||
maxResourceCapacity: 50
|
||||
equipmentPrefabs:
|
||||
- {fileID: 9113353226622644240, guid: 66fa238e7512e1547bf2ef4b62d76afd, type: 3}
|
||||
- {fileID: 7492862814627084760, guid: 4e920eca75dd2f44685f2be6a29e8b43, type: 3}
|
||||
--- !u!114 &2148255267416253297
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -275,6 +267,98 @@ MonoBehaviour:
|
||||
m_EditorClassIdentifier: Assembly-CSharp::Northbound.PlayerVisionProvider
|
||||
ShowTopMostFoldoutHeaderGroup: 1
|
||||
visionRange: 10
|
||||
--- !u!114 &7256821725794344131
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1314983689436087486}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: e96cb6065543e43c4a752faaa1468eb1, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Unity.Netcode.Runtime::Unity.Netcode.Components.NetworkTransform
|
||||
ShowTopMostFoldoutHeaderGroup: 1
|
||||
NetworkTransformExpanded: 0
|
||||
AutoOwnerAuthorityTickOffset: 1
|
||||
PositionInterpolationType: 0
|
||||
RotationInterpolationType: 0
|
||||
ScaleInterpolationType: 0
|
||||
PositionLerpSmoothing: 1
|
||||
PositionMaxInterpolationTime: 0.1
|
||||
RotationLerpSmoothing: 1
|
||||
RotationMaxInterpolationTime: 0.1
|
||||
ScaleLerpSmoothing: 1
|
||||
ScaleMaxInterpolationTime: 0.1
|
||||
AuthorityMode: 0
|
||||
TickSyncChildren: 0
|
||||
UseUnreliableDeltas: 0
|
||||
SyncPositionX: 1
|
||||
SyncPositionY: 1
|
||||
SyncPositionZ: 1
|
||||
SyncRotAngleX: 1
|
||||
SyncRotAngleY: 1
|
||||
SyncRotAngleZ: 1
|
||||
SyncScaleX: 1
|
||||
SyncScaleY: 1
|
||||
SyncScaleZ: 1
|
||||
PositionThreshold: 0.001
|
||||
RotAngleThreshold: 0.01
|
||||
ScaleThreshold: 0.01
|
||||
UseQuaternionSynchronization: 0
|
||||
UseQuaternionCompression: 0
|
||||
UseHalfFloatPrecision: 0
|
||||
InLocalSpace: 0
|
||||
SwitchTransformSpaceWhenParented: 0
|
||||
Interpolate: 1
|
||||
SlerpPosition: 0
|
||||
--- !u!114 &5045194683846919184
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1314983689436087486}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: e8d0727d5ae3244e3b569694d3912374, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Unity.Netcode.Runtime::Unity.Netcode.Components.NetworkAnimator
|
||||
ShowTopMostFoldoutHeaderGroup: 1
|
||||
NetworkAnimatorExpanded: 0
|
||||
AuthorityMode: 0
|
||||
m_Animator: {fileID: 1698609800605343773}
|
||||
TransitionStateInfoList: []
|
||||
AnimatorParameterEntries:
|
||||
ParameterEntries:
|
||||
- name: MoveSpeed
|
||||
NameHash: 526065662
|
||||
Synchronize: 1
|
||||
ParameterType: 1
|
||||
- name: Mining
|
||||
NameHash: 577859424
|
||||
Synchronize: 1
|
||||
ParameterType: 9
|
||||
- name: Attack
|
||||
NameHash: 1080829965
|
||||
Synchronize: 1
|
||||
ParameterType: 9
|
||||
AnimatorParametersExpanded: 0
|
||||
--- !u!114 &7246316046087380146
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1314983689436087486}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 90231c209cbef84469511de397004be9, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Assembly-CSharp::Northbound.PlayerInventory
|
||||
ShowTopMostFoldoutHeaderGroup: 1
|
||||
maxResourceCapacity: 100
|
||||
--- !u!1001 &1445453803682481668
|
||||
PrefabInstance:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -502,3 +586,8 @@ Transform:
|
||||
m_CorrespondingSourceObject: {fileID: -5515783359193845756, guid: 4652a9058e767d142b3e889c2983fa9a, type: 3}
|
||||
m_PrefabInstance: {fileID: 1445453803682481668}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
--- !u!4 &6366268612314379957 stripped
|
||||
Transform:
|
||||
m_CorrespondingSourceObject: {fileID: 5500811601672728753, guid: 4652a9058e767d142b3e889c2983fa9a, type: 3}
|
||||
m_PrefabInstance: {fileID: 1445453803682481668}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
|
||||
@@ -86,7 +86,7 @@ MonoBehaviour:
|
||||
m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Unity.Netcode.Runtime::Unity.Netcode.NetworkObject
|
||||
GlobalObjectIdHash: 1061286994
|
||||
GlobalObjectIdHash: 1574233340
|
||||
InScenePlacedSourceGlobalObjectIdHash: 1061286994
|
||||
DeferredDespawnTick: 0
|
||||
Ownership: 1
|
||||
@@ -112,17 +112,18 @@ MonoBehaviour:
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Assembly-CSharp::Northbound.Building
|
||||
ShowTopMostFoldoutHeaderGroup: 1
|
||||
maxHealth: 100
|
||||
showHealthBar: 1
|
||||
damageEffectPrefab: {fileID: 0}
|
||||
destroyEffectPrefab: {fileID: 0}
|
||||
effectSpawnPoint: {fileID: 0}
|
||||
buildingData: {fileID: 11400000, guid: 0e495d169ee3bce449f4b1aea83d6818, type: 2}
|
||||
gridPosition: {x: 0, y: 0, z: 0}
|
||||
rotation: 0
|
||||
initialTeam: 1
|
||||
initialOwnerId: 1
|
||||
useInitialOwner: 1
|
||||
showHealthBar: 1
|
||||
healthBarPrefab: {fileID: 0}
|
||||
destroyEffectPrefab: {fileID: 0}
|
||||
damageEffectPrefab: {fileID: 0}
|
||||
effectSpawnPoint: {fileID: 0}
|
||||
showGridBounds: 1
|
||||
gridBoundsColor: {r: 0, g: 1, b: 1, a: 1}
|
||||
--- !u!114 &9023294375343742146
|
||||
@@ -140,6 +141,9 @@ MonoBehaviour:
|
||||
showInExploredAreas: 1
|
||||
updateInterval: 0.2
|
||||
renderers: []
|
||||
enableDistantVisibility: 1
|
||||
heightVisibilityMultiplier: 2
|
||||
minHeightForDistantVisibility: 3
|
||||
useExploredMaterial: 0
|
||||
exploredMaterial: {fileID: 0}
|
||||
--- !u!1001 &8926581783111832504
|
||||
|
||||
8
Assets/Resources.meta
Normal file
8
Assets/Resources.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0aef2ea1f5e3aed4d839d26090fcb09c
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Resources/Prefabs.meta
Normal file
8
Assets/Resources/Prefabs.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3d69632731200bd4a86a369c95ee485b
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
124
Assets/Resources/Prefabs/Bat.prefab
Normal file
124
Assets/Resources/Prefabs/Bat.prefab
Normal file
@@ -0,0 +1,124 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!1 &6430526095172430446
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 5247455493693100088}
|
||||
- component: {fileID: 1386822053491278300}
|
||||
- component: {fileID: 337360336842274253}
|
||||
m_Layer: 0
|
||||
m_Name: GameObject
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!4 &5247455493693100088
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6430526095172430446}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 6825883197719469110}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!33 &1386822053491278300
|
||||
MeshFilter:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6430526095172430446}
|
||||
m_Mesh: {fileID: 716655249603938094, guid: 1261145a64d4f3e43bee728a02c1b5e3, type: 3}
|
||||
--- !u!23 &337360336842274253
|
||||
MeshRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6430526095172430446}
|
||||
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: 726c1e9087356f74594664341c681f12, 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!1 &9113353226622644240
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 6825883197719469110}
|
||||
m_Layer: 0
|
||||
m_Name: Bat
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!4 &6825883197719469110
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 9113353226622644240}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: -5.09173, y: 2, z: -37.867}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 5247455493693100088}
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
7
Assets/Resources/Prefabs/Bat.prefab.meta
Normal file
7
Assets/Resources/Prefabs/Bat.prefab.meta
Normal file
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 63b758cca5ef327449b0debcea47b006
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
124
Assets/Resources/Prefabs/Pickaxe.prefab
Normal file
124
Assets/Resources/Prefabs/Pickaxe.prefab
Normal file
@@ -0,0 +1,124 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!1 &7492862814627084760
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 3311562868863992538}
|
||||
m_Layer: 0
|
||||
m_Name: Pickaxe
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!4 &3311562868863992538
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 7492862814627084760}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: -4.36068, y: 2, z: -36.9}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 3026504303214829544}
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!1 &8843044757232415404
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 3026504303214829544}
|
||||
- component: {fileID: 5528544188432120844}
|
||||
- component: {fileID: 8380855244601599694}
|
||||
m_Layer: 0
|
||||
m_Name: GameObject
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!4 &3026504303214829544
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 8843044757232415404}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 3311562868863992538}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!33 &5528544188432120844
|
||||
MeshFilter:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 8843044757232415404}
|
||||
m_Mesh: {fileID: 1616382711435183797, guid: 804d477fc7f114c498aa6f95452be893, type: 3}
|
||||
--- !u!23 &8380855244601599694
|
||||
MeshRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 8843044757232415404}
|
||||
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: d64c307f1b4197c44970c29f9845c245, 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}
|
||||
7
Assets/Resources/Prefabs/Pickaxe.prefab.meta
Normal file
7
Assets/Resources/Prefabs/Pickaxe.prefab.meta
Normal file
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4e920eca75dd2f44685f2be6a29e8b43
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -119,6 +119,80 @@ NavMeshSettings:
|
||||
debug:
|
||||
m_Flags: 0
|
||||
m_NavMeshData: {fileID: 0}
|
||||
--- !u!1 &48181808
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 48181811}
|
||||
- component: {fileID: 48181809}
|
||||
- component: {fileID: 48181810}
|
||||
m_Layer: 0
|
||||
m_Name: PlayerSpawnPositionSetter
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!114 &48181809
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 48181808}
|
||||
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: 2243665744
|
||||
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 &48181810
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 48181808}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 49cd9c4e7c611b04c8740c9e049129b9, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Assembly-CSharp::Northbound.PlayerSpawnPositionSetter
|
||||
ShowTopMostFoldoutHeaderGroup: 1
|
||||
spawnPoints: []
|
||||
useRandomSpawn: 0
|
||||
findSpawnPointsAutomatically: 1
|
||||
--- !u!4 &48181811
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 48181808}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: -0.03658, y: 1.00002, z: -4.65261}
|
||||
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 &61373298
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -2102,7 +2176,7 @@ MonoBehaviour:
|
||||
LoadSceneTimeOut: 120
|
||||
SpawnTimeout: 10
|
||||
EnableNetworkLogs: 1
|
||||
NetworkTopology: 0
|
||||
NetworkTopology: 1
|
||||
UseCMBService: 0
|
||||
AutoSpawnPlayerPrefabClientSide: 1
|
||||
NetworkMessageMetrics: 1
|
||||
@@ -2440,12 +2514,12 @@ MonoBehaviour:
|
||||
spawnWeight: 10
|
||||
minCount: 0
|
||||
maxCount: 228
|
||||
density: 0.02
|
||||
density: 0.04
|
||||
maxTotalObstacles: 6400
|
||||
minDistanceBetweenObstacles: 4
|
||||
alignToTerrain: 1
|
||||
randomRotation: 1
|
||||
scaleVariation: 0.197
|
||||
scaleVariation: 0.2
|
||||
maxAttempts: 50
|
||||
checkCollision: 1
|
||||
collisionLayers:
|
||||
@@ -3042,6 +3116,63 @@ Transform:
|
||||
m_CorrespondingSourceObject: {fileID: 922888705413710451, guid: 5662d0b0d0eb5f54290edd8dd0980b57, type: 3}
|
||||
m_PrefabInstance: {fileID: 2098115307}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
--- !u!1001 &957391555319539118
|
||||
PrefabInstance:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 2
|
||||
m_Modification:
|
||||
serializedVersion: 3
|
||||
m_TransformParent: {fileID: 0}
|
||||
m_Modifications:
|
||||
- target: {fileID: 6825883197719469110, guid: 66fa238e7512e1547bf2ef4b62d76afd, type: 3}
|
||||
propertyPath: m_LocalPosition.x
|
||||
value: -5.09173
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 6825883197719469110, guid: 66fa238e7512e1547bf2ef4b62d76afd, type: 3}
|
||||
propertyPath: m_LocalPosition.y
|
||||
value: 2
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 6825883197719469110, guid: 66fa238e7512e1547bf2ef4b62d76afd, type: 3}
|
||||
propertyPath: m_LocalPosition.z
|
||||
value: -37.867
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 6825883197719469110, guid: 66fa238e7512e1547bf2ef4b62d76afd, type: 3}
|
||||
propertyPath: m_LocalRotation.w
|
||||
value: 1
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 6825883197719469110, guid: 66fa238e7512e1547bf2ef4b62d76afd, type: 3}
|
||||
propertyPath: m_LocalRotation.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 6825883197719469110, guid: 66fa238e7512e1547bf2ef4b62d76afd, type: 3}
|
||||
propertyPath: m_LocalRotation.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 6825883197719469110, guid: 66fa238e7512e1547bf2ef4b62d76afd, type: 3}
|
||||
propertyPath: m_LocalRotation.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 6825883197719469110, guid: 66fa238e7512e1547bf2ef4b62d76afd, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 6825883197719469110, guid: 66fa238e7512e1547bf2ef4b62d76afd, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 6825883197719469110, guid: 66fa238e7512e1547bf2ef4b62d76afd, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 9113353226622644240, guid: 66fa238e7512e1547bf2ef4b62d76afd, type: 3}
|
||||
propertyPath: m_Name
|
||||
value: Bat
|
||||
objectReference: {fileID: 0}
|
||||
m_RemovedComponents: []
|
||||
m_RemovedGameObjects: []
|
||||
m_AddedGameObjects: []
|
||||
m_AddedComponents: []
|
||||
m_SourcePrefab: {fileID: 100100000, guid: 66fa238e7512e1547bf2ef4b62d76afd, type: 3}
|
||||
--- !u!1001 &1440648431994998967
|
||||
PrefabInstance:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -3383,6 +3514,10 @@ PrefabInstance:
|
||||
serializedVersion: 3
|
||||
m_TransformParent: {fileID: 0}
|
||||
m_Modifications:
|
||||
- target: {fileID: -7869551286978933574, guid: f395fcc064a3a834ba957327f1387c19, type: 3}
|
||||
propertyPath: equipmentData.equipmentPrefab
|
||||
value:
|
||||
objectReference: {fileID: 7492862814627084760, guid: 4e920eca75dd2f44685f2be6a29e8b43, type: 3}
|
||||
- target: {fileID: 3247786716306397435, guid: f395fcc064a3a834ba957327f1387c19, type: 3}
|
||||
propertyPath: m_LocalPosition.x
|
||||
value: -30
|
||||
@@ -3502,6 +3637,63 @@ Transform:
|
||||
m_CorrespondingSourceObject: {fileID: 5749230937810543840, guid: 88f7f1e8a019b674498ab5fd494c1d34, type: 3}
|
||||
m_PrefabInstance: {fileID: 6204924723497734287}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
--- !u!1001 &6296397022839506560
|
||||
PrefabInstance:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 2
|
||||
m_Modification:
|
||||
serializedVersion: 3
|
||||
m_TransformParent: {fileID: 0}
|
||||
m_Modifications:
|
||||
- target: {fileID: 3311562868863992538, guid: 4e920eca75dd2f44685f2be6a29e8b43, type: 3}
|
||||
propertyPath: m_LocalPosition.x
|
||||
value: -4.36068
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 3311562868863992538, guid: 4e920eca75dd2f44685f2be6a29e8b43, type: 3}
|
||||
propertyPath: m_LocalPosition.y
|
||||
value: 2
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 3311562868863992538, guid: 4e920eca75dd2f44685f2be6a29e8b43, type: 3}
|
||||
propertyPath: m_LocalPosition.z
|
||||
value: -36.9
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 3311562868863992538, guid: 4e920eca75dd2f44685f2be6a29e8b43, type: 3}
|
||||
propertyPath: m_LocalRotation.w
|
||||
value: 1
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 3311562868863992538, guid: 4e920eca75dd2f44685f2be6a29e8b43, type: 3}
|
||||
propertyPath: m_LocalRotation.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 3311562868863992538, guid: 4e920eca75dd2f44685f2be6a29e8b43, type: 3}
|
||||
propertyPath: m_LocalRotation.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 3311562868863992538, guid: 4e920eca75dd2f44685f2be6a29e8b43, type: 3}
|
||||
propertyPath: m_LocalRotation.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 3311562868863992538, guid: 4e920eca75dd2f44685f2be6a29e8b43, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 3311562868863992538, guid: 4e920eca75dd2f44685f2be6a29e8b43, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 3311562868863992538, guid: 4e920eca75dd2f44685f2be6a29e8b43, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 7492862814627084760, guid: 4e920eca75dd2f44685f2be6a29e8b43, type: 3}
|
||||
propertyPath: m_Name
|
||||
value: Pickaxe
|
||||
objectReference: {fileID: 0}
|
||||
m_RemovedComponents: []
|
||||
m_RemovedGameObjects: []
|
||||
m_AddedGameObjects: []
|
||||
m_AddedComponents: []
|
||||
m_SourcePrefab: {fileID: 100100000, guid: 4e920eca75dd2f44685f2be6a29e8b43, type: 3}
|
||||
--- !u!114 &8940572951313384066 stripped
|
||||
MonoBehaviour:
|
||||
m_CorrespondingSourceObject: {fileID: 1287070985890992582, guid: e56926eda34629f4fbf3e4c53f0f8bd4, type: 3}
|
||||
@@ -3603,3 +3795,6 @@ SceneRoots:
|
||||
- {fileID: 1442785555}
|
||||
- {fileID: 1440648431994998967}
|
||||
- {fileID: 1061936651}
|
||||
- {fileID: 48181811}
|
||||
- {fileID: 957391555319539118}
|
||||
- {fileID: 6296397022839506560}
|
||||
|
||||
@@ -31,8 +31,14 @@ namespace Northbound
|
||||
private Animator _animator;
|
||||
private ITeamMember _teamMember;
|
||||
private EquipmentSocket _equipmentSocket;
|
||||
private bool _isAttacking = false;
|
||||
private NetworkVariable<bool> _isAttacking = new NetworkVariable<bool>(
|
||||
false,
|
||||
NetworkVariableReadPermission.Everyone,
|
||||
NetworkVariableWritePermission.Owner
|
||||
);
|
||||
private bool _isWeaponEquipped = false;
|
||||
private float _attackStartTime;
|
||||
private const float ATTACK_TIMEOUT = 2f; // Auto-reset if animation event doesn't fire
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
@@ -43,7 +49,7 @@ namespace Northbound
|
||||
|
||||
public bool CanExecute(ulong playerId)
|
||||
{
|
||||
if (blockDuringAnimation && _isAttacking)
|
||||
if (blockDuringAnimation && _isAttacking.Value)
|
||||
return false;
|
||||
|
||||
return Time.time - _lastAttackTime >= attackCooldown;
|
||||
@@ -55,7 +61,10 @@ namespace Northbound
|
||||
return;
|
||||
|
||||
_lastAttackTime = Time.time;
|
||||
_isAttacking = true;
|
||||
_attackStartTime = Time.time;
|
||||
|
||||
// Owner writes directly (Owner permission on _isAttacking)
|
||||
_isAttacking.Value = true;
|
||||
|
||||
// 장비 장착 (애니메이션 이벤트 사용 안 할 경우)
|
||||
if (!useAnimationEvents && useEquipment && !_isWeaponEquipped)
|
||||
@@ -73,7 +82,7 @@ namespace Northbound
|
||||
if (_animator == null || string.IsNullOrEmpty(attackAnimationTrigger))
|
||||
{
|
||||
PerformAttack();
|
||||
_isAttacking = false;
|
||||
_isAttacking.Value = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,7 +177,10 @@ namespace Northbound
|
||||
|
||||
public void OnAttackComplete()
|
||||
{
|
||||
_isAttacking = false;
|
||||
if (IsOwner)
|
||||
{
|
||||
_isAttacking.Value = false;
|
||||
}
|
||||
|
||||
if (useEquipment && equipmentData != null && equipmentData.detachOnEnd && !equipmentData.keepEquipped)
|
||||
{
|
||||
@@ -183,16 +195,10 @@ namespace Northbound
|
||||
private void AttachWeapon(string socketName = null)
|
||||
{
|
||||
if (_equipmentSocket == null || equipmentData == null)
|
||||
{
|
||||
Debug.LogWarning("[AttackAction] EquipmentSocket 또는 EquipmentData가 없습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (equipmentData.equipmentPrefab == null)
|
||||
{
|
||||
Debug.LogWarning("[AttackAction] 무기 프리팹이 설정되지 않았습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
string socket = socketName ?? equipmentData.socketName;
|
||||
_equipmentSocket.AttachToSocket(socket, equipmentData.equipmentPrefab);
|
||||
@@ -251,6 +257,34 @@ namespace Northbound
|
||||
Gizmos.DrawWireSphere(attackOrigin, attackRange);
|
||||
}
|
||||
|
||||
public override void OnNetworkSpawn()
|
||||
{
|
||||
base.OnNetworkSpawn();
|
||||
|
||||
// Subscribe to attack state changes
|
||||
_isAttacking.OnValueChanged += OnAttackStateChanged;
|
||||
}
|
||||
|
||||
public override void OnNetworkDespawn()
|
||||
{
|
||||
_isAttacking.OnValueChanged -= OnAttackStateChanged;
|
||||
base.OnNetworkDespawn();
|
||||
}
|
||||
|
||||
private void OnAttackStateChanged(bool previousValue, bool newValue)
|
||||
{
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
if (!IsOwner) return;
|
||||
|
||||
if (_isAttacking.Value && Time.time - _attackStartTime > ATTACK_TIMEOUT)
|
||||
{
|
||||
_isAttacking.Value = false;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnDestroy()
|
||||
{
|
||||
// 무기 정리
|
||||
@@ -262,6 +296,6 @@ namespace Northbound
|
||||
base.OnDestroy();
|
||||
}
|
||||
|
||||
public bool IsAttacking => _isAttacking;
|
||||
public bool IsAttacking => _isAttacking.Value;
|
||||
}
|
||||
}
|
||||
@@ -1,2 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 66ec3984614d8a64b8eae821376d038d
|
||||
guid: c467f66382052bc429affc5cb01d17db
|
||||
@@ -3,24 +3,63 @@ using Unity.Netcode;
|
||||
|
||||
public class AutoHost : MonoBehaviour
|
||||
{
|
||||
// 에디터에서만 작동하도록 설정
|
||||
#if UNITY_EDITOR
|
||||
private bool _hasStarted = false;
|
||||
|
||||
void Start()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
// 1. NetworkManager가 씬에 존재하는지 확인
|
||||
if (NetworkManager.Singleton != null)
|
||||
{
|
||||
// 2. 이미 서버나 클라이언트가 실행 중이 아닐 때만 실행
|
||||
if (!NetworkManager.Singleton.IsServer && !NetworkManager.Singleton.IsClient)
|
||||
{
|
||||
NetworkManager.Singleton.StartHost();
|
||||
Debug.Log("<color=yellow><b>[AutoHost]</b> 에디터 전용 호스트 자동 시작됨</color>");
|
||||
}
|
||||
}
|
||||
else
|
||||
if (NetworkManager.Singleton == null)
|
||||
{
|
||||
Debug.LogError("[AutoHost] NetworkManager를 찾을 수 없습니다!");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (NetworkManager.Singleton.IsServer || NetworkManager.Singleton.IsClient)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
TryStartAsHost();
|
||||
}
|
||||
|
||||
private void TryStartAsHost()
|
||||
{
|
||||
try
|
||||
{
|
||||
NetworkManager.Singleton.StartHost();
|
||||
_hasStarted = true;
|
||||
Debug.Log("<color=yellow><b>[AutoHost]</b> 호스트로 시작됨 (MAIN EDITOR)</color>");
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
Debug.Log($"<color=orange><b>[AutoHost]</b> 호스트 시작 실패: {e.Message}</color>");
|
||||
Debug.Log("<color=blue><b>[AutoHost]</b> 클라이언트 모드로 전환...</color>");
|
||||
|
||||
NetworkManager.Singleton.Shutdown();
|
||||
TryStartAsClient();
|
||||
}
|
||||
}
|
||||
|
||||
private void TryStartAsClient()
|
||||
{
|
||||
try
|
||||
{
|
||||
NetworkManager.Singleton.StartClient();
|
||||
_hasStarted = true;
|
||||
Debug.Log("<color=blue><b>[AutoHost]</b> 클라이언트로 연결됨 (SECONDARY EDITOR)</color>");
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
Debug.LogError($"<color=red><b>[AutoHost]</b> 클라이언트 연결 실패: {e.Message}</color>");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (NetworkManager.Singleton != null)
|
||||
{
|
||||
NetworkManager.Singleton.Shutdown();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -44,7 +44,7 @@ namespace Northbound
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (!IsServer) return;
|
||||
if (!IsOwner) return;
|
||||
if (_teamMember == null) return;
|
||||
|
||||
if (Time.time - _lastAttackTime >= attackInterval)
|
||||
|
||||
@@ -4,9 +4,9 @@ using UnityEngine;
|
||||
|
||||
namespace Northbound
|
||||
{
|
||||
public class Building : NetworkBehaviour, IDamageable, IVisionProvider, ITeamMember
|
||||
public class Building : DamageableNetworkBehaviour, IVisionProvider, ITeamMember
|
||||
{
|
||||
[Header("References")]
|
||||
[Header("Building Data")]
|
||||
public BuildingData buildingData;
|
||||
|
||||
[Header("Runtime Info")]
|
||||
@@ -14,52 +14,34 @@ namespace Northbound
|
||||
public int rotation; // 0-3 (0=0°, 1=90°, 2=180°, 3=270°)
|
||||
|
||||
[Header("Team")]
|
||||
[Tooltip("건물의 팀 (플레이어/적대세력/몬스터/중립)")]
|
||||
[Tooltip("Building team (Player/Enemy/Monster/Neutral)")]
|
||||
public TeamType initialTeam = TeamType.Player;
|
||||
|
||||
[Header("Ownership (for pre-placed buildings)")]
|
||||
[Tooltip("씬에 미리 배치된 건물의 경우 여기서 소유자 설정 (0 = 중립, 1+ = 플레이어 ID)")]
|
||||
[Tooltip("For pre-placed buildings, set owner here (0 = neutral, 1+ = player ID)")]
|
||||
public ulong initialOwnerId = 0;
|
||||
[Tooltip("사전 배치 건물인가요? 체크하면 initialOwnerId를 사용합니다")]
|
||||
[Tooltip("Is this a pre-placed building? If checked, uses initialOwnerId")]
|
||||
public bool useInitialOwner = false;
|
||||
|
||||
[Header("Health")]
|
||||
public bool showHealthBar = true;
|
||||
[Header("Health Bar")]
|
||||
public GameObject healthBarPrefab;
|
||||
|
||||
[Header("Visual Effects")]
|
||||
public GameObject destroyEffectPrefab;
|
||||
public GameObject damageEffectPrefab;
|
||||
public Transform effectSpawnPoint;
|
||||
|
||||
[Header("Debug")]
|
||||
public bool showGridBounds = true;
|
||||
public Color gridBoundsColor = Color.cyan;
|
||||
|
||||
// 현재 체력
|
||||
private NetworkVariable<int> _currentHealth = new NetworkVariable<int>(
|
||||
0,
|
||||
NetworkVariableReadPermission.Everyone,
|
||||
NetworkVariableWritePermission.Server
|
||||
);
|
||||
|
||||
// 건물 소유자 (사전 배치 건물 또는 동적 건물 모두 지원)
|
||||
private NetworkVariable<ulong> _ownerId = new NetworkVariable<ulong>(
|
||||
0,
|
||||
NetworkVariableReadPermission.Everyone,
|
||||
NetworkVariableWritePermission.Server
|
||||
);
|
||||
|
||||
// 건물 팀
|
||||
private NetworkVariable<TeamType> _team = new NetworkVariable<TeamType>(
|
||||
TeamType.Neutral,
|
||||
NetworkVariableReadPermission.Everyone,
|
||||
NetworkVariableWritePermission.Server
|
||||
);
|
||||
|
||||
// 이벤트
|
||||
public event Action<int, int> OnHealthChanged; // (current, max)
|
||||
public event Action OnDestroyed;
|
||||
public event Action<TeamType> OnTeamChanged;
|
||||
|
||||
private BuildingHealthBar _healthBar;
|
||||
@@ -70,63 +52,54 @@ namespace Northbound
|
||||
{
|
||||
base.OnNetworkSpawn();
|
||||
|
||||
if (IsServer)
|
||||
if (IsOwner)
|
||||
{
|
||||
// 체력 초기화
|
||||
if (_currentHealth.Value == 0)
|
||||
if (maxHealth == 0)
|
||||
{
|
||||
_currentHealth.Value = buildingData != null ? buildingData.maxHealth : 100;
|
||||
maxHealth = buildingData != null ? buildingData.maxHealth : 100;
|
||||
_currentHealth.Value = maxHealth;
|
||||
}
|
||||
|
||||
// 팀 초기화
|
||||
if (_team.Value == TeamType.Neutral)
|
||||
{
|
||||
_team.Value = initialTeam;
|
||||
}
|
||||
|
||||
// 소유자 초기화 (사전 배치 건물 체크)
|
||||
if (useInitialOwner && _ownerId.Value == 0)
|
||||
{
|
||||
_ownerId.Value = initialOwnerId;
|
||||
// Debug.Log($"<color=cyan>[Building] 사전 배치 건물 '{buildingData?.buildingName ?? gameObject.name}' 소유자: {initialOwnerId}, 팀: {_team.Value}</color>");
|
||||
}
|
||||
else if (!useInitialOwner && _ownerId.Value == 0)
|
||||
{
|
||||
// 동적 생성 건물은 NetworkObject의 Owner 사용
|
||||
_ownerId.Value = OwnerClientId;
|
||||
}
|
||||
|
||||
|
||||
_lastRegenTime = Time.time;
|
||||
|
||||
// FogOfWar 시스템에 시야 제공자로 등록
|
||||
if (buildingData != null && buildingData.providesVision)
|
||||
{
|
||||
FogOfWarSystem.Instance?.RegisterVisionProvider(this);
|
||||
}
|
||||
}
|
||||
|
||||
// 이벤트 구독
|
||||
_currentHealth.OnValueChanged += OnHealthValueChanged;
|
||||
_team.OnValueChanged += OnTeamValueChanged;
|
||||
|
||||
// 체력바 생성
|
||||
if (showHealthBar && healthBarPrefab != null)
|
||||
{
|
||||
CreateHealthBar();
|
||||
base.InitializeHealthBar();
|
||||
}
|
||||
|
||||
// 초기 체력 UI 업데이트
|
||||
UpdateHealthUI();
|
||||
UpdateTeamVisuals();
|
||||
}
|
||||
|
||||
public override void OnNetworkDespawn()
|
||||
{
|
||||
_currentHealth.OnValueChanged -= OnHealthValueChanged;
|
||||
base.OnNetworkDespawn();
|
||||
|
||||
_team.OnValueChanged -= OnTeamValueChanged;
|
||||
|
||||
// FogOfWar 시스템에서 제거
|
||||
if (IsServer && buildingData != null && buildingData.providesVision)
|
||||
if (IsOwner && buildingData != null && buildingData.providesVision)
|
||||
{
|
||||
FogOfWarSystem.Instance?.UnregisterVisionProvider(this);
|
||||
}
|
||||
@@ -134,38 +107,33 @@ namespace Northbound
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (!IsServer || buildingData == null)
|
||||
if (!IsOwner || buildingData == null)
|
||||
return;
|
||||
|
||||
// 자동 체력 회복
|
||||
if (buildingData.autoRegenerate && _currentHealth.Value < buildingData.maxHealth)
|
||||
if (buildingData.autoRegenerate && _currentHealth.Value < maxHealth)
|
||||
{
|
||||
if (Time.time - _lastRegenTime >= 1f)
|
||||
{
|
||||
int regenAmount = Mathf.Min(buildingData.regenPerSecond, buildingData.maxHealth - _currentHealth.Value);
|
||||
int regenAmount = Mathf.Min(buildingData.regenPerSecond, maxHealth - _currentHealth.Value);
|
||||
_currentHealth.Value += regenAmount;
|
||||
_lastRegenTime = Time.time;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 건물 초기화 (BuildingManager가 동적 생성 시 호출)
|
||||
/// </summary>
|
||||
public void Initialize(BuildingData data, Vector3Int gridPos, int rot, ulong ownerId, TeamType team = TeamType.Player)
|
||||
{
|
||||
buildingData = data;
|
||||
gridPosition = gridPos;
|
||||
rotation = rot;
|
||||
|
||||
// 이미 스폰된 경우
|
||||
if (IsServer && IsSpawned)
|
||||
if (IsOwner && IsSpawned)
|
||||
{
|
||||
_currentHealth.Value = data.maxHealth;
|
||||
maxHealth = data.maxHealth;
|
||||
_currentHealth.Value = maxHealth;
|
||||
_ownerId.Value = ownerId;
|
||||
_team.Value = team;
|
||||
|
||||
// 시야 제공자 등록
|
||||
|
||||
if (data.providesVision)
|
||||
{
|
||||
FogOfWarSystem.Instance?.RegisterVisionProvider(this);
|
||||
@@ -175,22 +143,28 @@ namespace Northbound
|
||||
_isInitialized = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 건물 소유권 변경 (점령 등)
|
||||
/// </summary>
|
||||
public void SetOwner(ulong newOwnerId, TeamType newTeam)
|
||||
{
|
||||
if (!IsServer) return;
|
||||
if (!IsOwner)
|
||||
{
|
||||
SetOwnerServerRpc(newOwnerId, newTeam);
|
||||
return;
|
||||
}
|
||||
|
||||
SetOwnerServerRpc(newOwnerId, newTeam);
|
||||
}
|
||||
|
||||
[ServerRpc]
|
||||
private void SetOwnerServerRpc(ulong newOwnerId, TeamType newTeam)
|
||||
{
|
||||
ulong previousOwner = _ownerId.Value;
|
||||
TeamType previousTeam = _team.Value;
|
||||
|
||||
|
||||
_ownerId.Value = newOwnerId;
|
||||
_team.Value = newTeam;
|
||||
|
||||
Debug.Log($"<color=yellow>[Building] {buildingData?.buildingName ?? "건물"} 소유권 변경: {previousOwner} → {newOwnerId}, 팀: {TeamManager.GetTeamName(previousTeam)} → {TeamManager.GetTeamName(newTeam)}</color>");
|
||||
Debug.Log($"<color=yellow>[Building] {buildingData?.buildingName ?? "Building"} ownership changed: {previousOwner} → {newOwnerId}, team: {TeamManager.GetTeamName(previousTeam)} → {TeamManager.GetTeamName(newTeam)}</color>");
|
||||
|
||||
// 시야 제공자 재등록 (소유자가 바뀌었으므로)
|
||||
if (buildingData != null && buildingData.providesVision)
|
||||
{
|
||||
FogOfWarSystem.Instance?.UnregisterVisionProvider(this);
|
||||
@@ -204,7 +178,18 @@ namespace Northbound
|
||||
|
||||
public void SetTeam(TeamType team)
|
||||
{
|
||||
if (!IsServer) return;
|
||||
if (!IsOwner)
|
||||
{
|
||||
SetTeamServerRpc(team);
|
||||
return;
|
||||
}
|
||||
|
||||
SetTeamServerRpc(team);
|
||||
}
|
||||
|
||||
[ServerRpc]
|
||||
private void SetTeamServerRpc(TeamType team)
|
||||
{
|
||||
_team.Value = team;
|
||||
}
|
||||
|
||||
@@ -212,17 +197,12 @@ namespace Northbound
|
||||
{
|
||||
OnTeamChanged?.Invoke(newValue);
|
||||
UpdateTeamVisuals();
|
||||
Debug.Log($"<color=cyan>[Building] {buildingData?.buildingName ?? "건물"} 팀 변경: {TeamManager.GetTeamName(previousValue)} → {TeamManager.GetTeamName(newValue)}</color>");
|
||||
Debug.Log($"<color=cyan>[Building] {buildingData?.buildingName ?? "Building"} team changed: {TeamManager.GetTeamName(previousValue)} → {TeamManager.GetTeamName(newValue)}</color>");
|
||||
}
|
||||
|
||||
private void UpdateTeamVisuals()
|
||||
{
|
||||
// 팀 색상으로 건물 외곽선이나 이펙트 변경 가능
|
||||
// 예: Renderer의 emission 색상 변경
|
||||
Color teamColor = TeamManager.GetTeamColor(_team.Value);
|
||||
|
||||
// 여기에 실제 비주얼 업데이트 로직 추가
|
||||
// 예: outline shader, emission, particle system 색상 등
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -240,65 +220,85 @@ namespace Northbound
|
||||
|
||||
public bool IsActive()
|
||||
{
|
||||
// 건물이 스폰되어 있고, 파괴되지 않았으며, 시야 제공 설정이 켜져있어야 함
|
||||
return IsSpawned && !IsDestroyed() && buildingData != null && buildingData.providesVision;
|
||||
return IsSpawned && !IsDead() && buildingData != null && buildingData.providesVision;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IDamageable Implementation
|
||||
#region IDamageable Overrides
|
||||
|
||||
public void TakeDamage(int damage, ulong attackerId)
|
||||
protected override void Die(ulong killerId)
|
||||
{
|
||||
if (!IsServer)
|
||||
base.Die(killerId);
|
||||
|
||||
if (!IsOwner) return;
|
||||
|
||||
Debug.Log($"<color=red>[Building] {buildingData?.buildingName ?? "Building"} ({TeamManager.GetTeamName(_team.Value)}) destroyed! Attacker: {killerId}</color>");
|
||||
|
||||
InvokeOnDestroyed();
|
||||
NotifyDestroyedClientRpc();
|
||||
|
||||
if (buildingData != null && buildingData.providesVision)
|
||||
{
|
||||
// 클라이언트는 서버에 요청
|
||||
TakeDamageServerRpc(damage, attackerId);
|
||||
FogOfWarSystem.Instance?.UnregisterVisionProvider(this);
|
||||
}
|
||||
|
||||
if (BuildingManager.Instance != null)
|
||||
{
|
||||
BuildingManager.Instance.RemoveBuilding(this);
|
||||
}
|
||||
|
||||
Invoke(nameof(DespawnBuilding), 0.5f);
|
||||
}
|
||||
|
||||
private void DespawnBuilding()
|
||||
{
|
||||
if (IsOwner && NetworkObject != null)
|
||||
{
|
||||
NetworkObject.Despawn(true);
|
||||
}
|
||||
}
|
||||
|
||||
[ClientRpc]
|
||||
private void NotifyDestroyedClientRpc()
|
||||
{
|
||||
if (!IsOwner)
|
||||
{
|
||||
InvokeOnDestroyed();
|
||||
}
|
||||
}
|
||||
|
||||
public override void TakeDamage(int damage, ulong attackerId)
|
||||
{
|
||||
if (!IsOwner)
|
||||
{
|
||||
TakeDamageOwnerRpc(damage, attackerId);
|
||||
return;
|
||||
}
|
||||
|
||||
// 무적 건물
|
||||
if (buildingData != null && buildingData.isIndestructible)
|
||||
{
|
||||
Debug.Log($"<color=yellow>[Building] {buildingData.buildingName}은(는) 무적입니다.</color>");
|
||||
Debug.Log($"<color=yellow>[Building] {buildingData.buildingName} is indestructible.</color>");
|
||||
return;
|
||||
}
|
||||
|
||||
// 이미 파괴됨
|
||||
if (_currentHealth.Value <= 0)
|
||||
return;
|
||||
|
||||
// 공격자의 팀 확인 (팀 공격 방지)
|
||||
var attackerObj = NetworkManager.Singleton.SpawnManager.SpawnedObjects[attackerId];
|
||||
var attackerTeamMember = attackerObj?.GetComponent<ITeamMember>();
|
||||
|
||||
|
||||
if (attackerTeamMember != null)
|
||||
{
|
||||
if (!TeamManager.CanAttack(attackerTeamMember, this))
|
||||
{
|
||||
Debug.Log($"<color=yellow>[Building] {TeamManager.GetTeamName(attackerTeamMember.GetTeam())} 팀은 {TeamManager.GetTeamName(_team.Value)} 팀을 공격할 수 없습니다.</color>");
|
||||
Debug.Log($"<color=yellow>[Building] {TeamManager.GetTeamName(attackerTeamMember.GetTeam())} team cannot attack {TeamManager.GetTeamName(_team.Value)} team.</color>");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 데미지 적용
|
||||
int actualDamage = Mathf.Min(damage, _currentHealth.Value);
|
||||
_currentHealth.Value -= actualDamage;
|
||||
|
||||
Debug.Log($"<color=red>[Building] {buildingData?.buildingName ?? "건물"} ({TeamManager.GetTeamName(_team.Value)})이(가) {actualDamage} 데미지를 받았습니다. 남은 체력: {_currentHealth.Value}/{buildingData?.maxHealth ?? 100}</color>");
|
||||
|
||||
// 데미지 이펙트
|
||||
ShowDamageEffectClientRpc();
|
||||
|
||||
// 체력이 0이 되면 파괴
|
||||
if (_currentHealth.Value <= 0)
|
||||
{
|
||||
DestroyBuilding(attackerId);
|
||||
}
|
||||
base.TakeDamage(damage, attackerId);
|
||||
}
|
||||
|
||||
[Rpc(SendTo.Server)]
|
||||
private void TakeDamageServerRpc(int damage, ulong attackerId)
|
||||
[Rpc(SendTo.Owner)]
|
||||
private void TakeDamageOwnerRpc(int damage, ulong attackerId)
|
||||
{
|
||||
TakeDamage(damage, attackerId);
|
||||
}
|
||||
@@ -307,179 +307,63 @@ namespace Northbound
|
||||
|
||||
#region Health Management
|
||||
|
||||
/// <summary>
|
||||
/// 건물 파괴
|
||||
/// </summary>
|
||||
private void DestroyBuilding(ulong attackerId)
|
||||
public new int GetMaxHealth()
|
||||
{
|
||||
if (!IsServer)
|
||||
return;
|
||||
|
||||
Debug.Log($"<color=red>[Building] {buildingData?.buildingName ?? "건물"} ({TeamManager.GetTeamName(_team.Value)})이(가) 파괴되었습니다! (공격자: {attackerId})</color>");
|
||||
|
||||
// 파괴 이벤트 발생
|
||||
OnDestroyed?.Invoke();
|
||||
NotifyDestroyedClientRpc();
|
||||
|
||||
// 파괴 이펙트
|
||||
ShowDestroyEffectClientRpc();
|
||||
|
||||
// FogOfWar 시스템에서 제거
|
||||
if (buildingData != null && buildingData.providesVision)
|
||||
{
|
||||
FogOfWarSystem.Instance?.UnregisterVisionProvider(this);
|
||||
}
|
||||
|
||||
// BuildingManager에서 제거
|
||||
if (BuildingManager.Instance != null)
|
||||
{
|
||||
BuildingManager.Instance.RemoveBuilding(this);
|
||||
}
|
||||
|
||||
// 네트워크 오브젝트 파괴 (약간의 딜레이)
|
||||
Invoke(nameof(DespawnBuilding), 0.5f);
|
||||
return buildingData != null ? buildingData.maxHealth : 100;
|
||||
}
|
||||
|
||||
private void DespawnBuilding()
|
||||
public new float GetHealthPercentage()
|
||||
{
|
||||
if (IsServer && NetworkObject != null)
|
||||
{
|
||||
NetworkObject.Despawn(true);
|
||||
}
|
||||
int maxHp = GetMaxHealth();
|
||||
return maxHp > 0 ? (float)_currentHealth.Value / maxHp : 0f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 체력 회복
|
||||
/// </summary>
|
||||
public void Heal(int amount)
|
||||
{
|
||||
if (!IsServer)
|
||||
return;
|
||||
|
||||
if (buildingData == null)
|
||||
return;
|
||||
|
||||
int healAmount = Mathf.Min(amount, buildingData.maxHealth - _currentHealth.Value);
|
||||
_currentHealth.Value += healAmount;
|
||||
|
||||
Debug.Log($"<color=green>[Building] {buildingData.buildingName}이(가) {healAmount} 회복되었습니다. 현재 체력: {_currentHealth.Value}/{buildingData.maxHealth}</color>");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 현재 체력
|
||||
/// </summary>
|
||||
public int GetCurrentHealth() => _currentHealth.Value;
|
||||
|
||||
/// <summary>
|
||||
/// 최대 체력
|
||||
/// </summary>
|
||||
public int GetMaxHealth() => buildingData != null ? buildingData.maxHealth : 100;
|
||||
|
||||
/// <summary>
|
||||
/// 체력 비율 (0.0 ~ 1.0)
|
||||
/// </summary>
|
||||
public float GetHealthPercentage()
|
||||
{
|
||||
int maxHealth = GetMaxHealth();
|
||||
return maxHealth > 0 ? (float)_currentHealth.Value / maxHealth : 0f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 파괴되었는지 여부
|
||||
/// </summary>
|
||||
public bool IsDestroyed() => _currentHealth.Value <= 0;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Health UI
|
||||
|
||||
private void CreateHealthBar()
|
||||
protected override void InitializeHealthBar()
|
||||
{
|
||||
if (_healthBar != null)
|
||||
return;
|
||||
|
||||
GameObject healthBarObj = Instantiate(healthBarPrefab, transform);
|
||||
_healthBar = healthBarObj.GetComponent<BuildingHealthBar>();
|
||||
|
||||
if (_healthBar != null)
|
||||
if (healthBarPrefab != null)
|
||||
{
|
||||
_healthBar.Initialize(this);
|
||||
GameObject healthBarObj = Instantiate(healthBarPrefab, transform);
|
||||
_healthBar = healthBarObj.GetComponent<BuildingHealthBar>();
|
||||
|
||||
if (_healthBar != null)
|
||||
{
|
||||
_healthBar.Initialize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateHealthUI()
|
||||
protected override void UpdateHealthUI()
|
||||
{
|
||||
if (_healthBar != null)
|
||||
{
|
||||
_healthBar.UpdateHealth(_currentHealth.Value, GetMaxHealth());
|
||||
}
|
||||
|
||||
OnHealthChanged?.Invoke(_currentHealth.Value, GetMaxHealth());
|
||||
}
|
||||
|
||||
private void OnHealthValueChanged(int previousValue, int newValue)
|
||||
{
|
||||
UpdateHealthUI();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Visual Effects
|
||||
|
||||
[ClientRpc]
|
||||
private void ShowDamageEffectClientRpc()
|
||||
{
|
||||
if (damageEffectPrefab != null)
|
||||
{
|
||||
Transform spawnPoint = effectSpawnPoint != null ? effectSpawnPoint : transform;
|
||||
GameObject effect = Instantiate(damageEffectPrefab, spawnPoint.position, spawnPoint.rotation);
|
||||
Destroy(effect, 2f);
|
||||
}
|
||||
}
|
||||
|
||||
[ClientRpc]
|
||||
private void ShowDestroyEffectClientRpc()
|
||||
{
|
||||
if (destroyEffectPrefab != null)
|
||||
{
|
||||
Transform spawnPoint = effectSpawnPoint != null ? effectSpawnPoint : transform;
|
||||
GameObject effect = Instantiate(destroyEffectPrefab, spawnPoint.position, spawnPoint.rotation);
|
||||
Destroy(effect, 3f);
|
||||
}
|
||||
}
|
||||
|
||||
[ClientRpc]
|
||||
private void NotifyDestroyedClientRpc()
|
||||
{
|
||||
if (!IsServer)
|
||||
{
|
||||
OnDestroyed?.Invoke();
|
||||
}
|
||||
InvokeOnHealthChanged(_currentHealth.Value, GetMaxHealth());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Grid Bounds
|
||||
|
||||
/// <summary>
|
||||
/// Gets the grid-based bounds (from BuildingData width/length/height)
|
||||
/// This is used for placement validation, NOT the actual collider bounds
|
||||
/// Bounds are slightly shrunk to allow adjacent buildings to touch
|
||||
/// </summary>
|
||||
public Bounds GetGridBounds()
|
||||
{
|
||||
if (buildingData == null) return new Bounds(transform.position, Vector3.one);
|
||||
|
||||
Vector3 gridSize = buildingData.GetSize(rotation);
|
||||
|
||||
// Shrink slightly to allow buildings to be adjacent without Intersects() returning true
|
||||
Vector3 shrunkSize = gridSize - Vector3.one * 0.01f;
|
||||
return new Bounds(transform.position + Vector3.up * gridSize.y * 0.5f, shrunkSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Legacy method, use GetGridBounds() instead
|
||||
/// </summary>
|
||||
public Bounds GetBounds()
|
||||
{
|
||||
return GetGridBounds();
|
||||
@@ -495,50 +379,15 @@ namespace Northbound
|
||||
|
||||
Bounds bounds = GetGridBounds();
|
||||
|
||||
// 팀 색상으로 표시
|
||||
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);
|
||||
}
|
||||
|
||||
private void OnDrawGizmosSelected()
|
||||
{
|
||||
if (buildingData == null) return;
|
||||
Gizmos.color = teamColor;
|
||||
Gizmos.DrawWireSphere(transform.position + Vector3.up * 2f, 0.5f);
|
||||
|
||||
Bounds bounds = GetGridBounds();
|
||||
Gizmos.color = Color.yellow;
|
||||
Gizmos.DrawWireCube(bounds.center, bounds.size);
|
||||
|
||||
// Draw grid position
|
||||
if (BuildingManager.Instance != null)
|
||||
{
|
||||
Vector3 worldPos = BuildingManager.Instance.GridToWorld(gridPosition);
|
||||
Gizmos.color = Color.magenta;
|
||||
Gizmos.DrawSphere(worldPos, 0.2f);
|
||||
}
|
||||
|
||||
// Draw vision range (if provides vision)
|
||||
if (buildingData.providesVision)
|
||||
{
|
||||
Gizmos.color = Color.cyan;
|
||||
Gizmos.DrawWireSphere(transform.position, buildingData.visionRange);
|
||||
}
|
||||
|
||||
// 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}\nTeam: {teamName}");
|
||||
}
|
||||
else if (useInitialOwner)
|
||||
{
|
||||
string teamName = TeamManager.GetTeamName(initialTeam);
|
||||
UnityEditor.Handles.Label(transform.position + Vector3.up * 3f,
|
||||
$"Initial Owner: {initialOwnerId}\nTeam: {teamName}");
|
||||
}
|
||||
#endif
|
||||
UnityEditor.Handles.Label(transform.position + Vector3.up * 3f,
|
||||
$"{buildingData.buildingName}\nHP: {_currentHealth.Value}/{maxHealth}");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -75,22 +75,32 @@ namespace Northbound
|
||||
/// </summary>
|
||||
public void Initialize(BuildingData data, Vector3Int pos, int rot, ulong ownerId, TeamType team)
|
||||
{
|
||||
if (!IsServer) return;
|
||||
if (!IsOwner)
|
||||
{
|
||||
InitializeServerRpc(data != null ? data.buildingName : "", pos.x, pos.y, pos.z, rot, ownerId, team);
|
||||
return;
|
||||
}
|
||||
|
||||
buildingData = data;
|
||||
gridPosition = pos;
|
||||
InitializeServerRpc(data != null ? data.buildingName : "", pos.x, pos.y, pos.z, rot, ownerId, team);
|
||||
}
|
||||
|
||||
[ServerRpc]
|
||||
private void InitializeServerRpc(string buildingName, int posX, int posY, int posZ, int rot, ulong ownerId, TeamType team)
|
||||
{
|
||||
buildingData = BuildingManager.Instance?.availableBuildings.Find(b => b != null && b.buildingName == buildingName);
|
||||
gridPosition = new Vector3Int(posX, posY, posZ);
|
||||
rotation = rot;
|
||||
_ownerId.Value = ownerId;
|
||||
_team.Value = team;
|
||||
_currentProgress.Value = 0f;
|
||||
|
||||
// BuildingData의 크기를 기반으로 스케일 설정
|
||||
Vector3 size = data.GetSize(rot);
|
||||
|
||||
Vector3 size = buildingData != null ? buildingData.GetSize(rot) : Vector3.one;
|
||||
|
||||
// foundationVisual의 스케일만 조정 (토대 자체의 pivot은 중앙에 유지)
|
||||
if (foundationVisual != null)
|
||||
{
|
||||
// 토대 비주얼을 건물 크기에 맞게 조정 (높이는 얇게)
|
||||
// 토대 높이를 건물 크기에 맞게 조정 (높이는 얇게)
|
||||
foundationVisual.transform.localScale = new Vector3(size.x, 0.2f, size.z);
|
||||
foundationVisual.transform.localPosition = new Vector3(0, 0.1f, 0); // 바닥에서 약간 위
|
||||
}
|
||||
@@ -102,12 +112,31 @@ namespace Northbound
|
||||
_collider = gameObject.AddComponent<BoxCollider>();
|
||||
}
|
||||
|
||||
// 상호작용 가능한 크기로 설정 (전체 건물 높이가 아닌 접근 가능한 크기)
|
||||
// 상호작용 가능한 크기로 설정 (전체 건물 높이가 아니라 접근 가능한 크기)
|
||||
_collider.size = new Vector3(size.x, 2f, size.z); // 높이를 2m로 설정하여 상호작용 가능
|
||||
_collider.center = new Vector3(0, 1f, 0); // 중심을 1m 높이에 배치
|
||||
_collider.isTrigger = false; // Trigger가 아닌 일반 Collider로 설정
|
||||
|
||||
Debug.Log($"<color=yellow>[BuildingFoundation] 토대 생성: {data.buildingName}, 크기: {size}, 위치: {transform.position}, Collider: {_collider.size}, 소유자: {ownerId}, 팀: {team}</color>");
|
||||
Debug.Log($"<color=yellow>[BuildingFoundation] 토대 생성: {buildingData?.buildingName ?? "Building"}, 크기: {size}, 위치: {transform.position}, Collider: {_collider.size}, 소유자: {ownerId}, 팀: {TeamManager.GetTeamName(team)}</color>");
|
||||
}
|
||||
|
||||
private void UpdateProgressBar()
|
||||
{
|
||||
if (buildingData == null || _progressBarInstance == null)
|
||||
return;
|
||||
|
||||
float progress = buildingData.requiredWorkAmount > 0 ? _currentProgress.Value / buildingData.requiredWorkAmount : 1f;
|
||||
|
||||
// 간단한 progress bar update - 필요한 경우 BuildingProgressBar 컴포넌트 사용
|
||||
var progressBarTransform = _progressBarInstance.transform;
|
||||
progressBarTransform.localScale = new Vector3(progress, 1f, 1f);
|
||||
}
|
||||
|
||||
private void OnProgressValueChanged(float previousValue, float newValue)
|
||||
{
|
||||
UpdateProgressBar();
|
||||
float max = buildingData != null ? buildingData.requiredWorkAmount : 100f;
|
||||
OnProgressChanged?.Invoke(newValue, max);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -123,6 +152,11 @@ namespace Northbound
|
||||
return new Bounds(transform.position + Vector3.up * size.y * 0.5f, size);
|
||||
}
|
||||
|
||||
public Bounds GetBounds()
|
||||
{
|
||||
return GetGridBounds();
|
||||
}
|
||||
|
||||
#region IInteractable Implementation
|
||||
|
||||
public bool CanInteract(ulong playerId)
|
||||
@@ -139,7 +173,7 @@ namespace Northbound
|
||||
Debug.Log($"[BuildingFoundation] Already completed");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// 같은 팀만 건설 가능 - 플레이어의 팀을 가져와서 비교
|
||||
TeamType playerTeam = GetPlayerTeam(playerId);
|
||||
if (playerTeam != _team.Value)
|
||||
@@ -153,7 +187,8 @@ namespace Northbound
|
||||
|
||||
public void Interact(ulong playerId)
|
||||
{
|
||||
if (!IsServer || buildingData == null) return;
|
||||
if (!IsServer || buildingData == null)
|
||||
return;
|
||||
|
||||
if (!CanInteract(playerId))
|
||||
return;
|
||||
@@ -163,45 +198,71 @@ namespace Northbound
|
||||
// 건설 진행
|
||||
_currentProgress.Value += buildingData.workPerInteraction;
|
||||
|
||||
Debug.Log($"<color=green>[BuildingFoundation] 건설 진행: {_currentProgress.Value}/{buildingData.requiredWorkAmount} ({(_currentProgress.Value / buildingData.requiredWorkAmount * 100f):F1}%)</color>");
|
||||
Debug.Log($"<color=yellow>[BuildingFoundation] {buildingData.buildingName} 건설 진행: {_currentProgress.Value}/{buildingData.requiredWorkAmount}</color>");
|
||||
|
||||
// 완성 체크
|
||||
// 완료 체크
|
||||
if (_currentProgress.Value >= buildingData.requiredWorkAmount)
|
||||
{
|
||||
CompleteConstruction();
|
||||
CompleteConstruction(playerId);
|
||||
}
|
||||
}
|
||||
|
||||
private void CompleteConstruction(ulong playerId)
|
||||
{
|
||||
Debug.Log($"<color=green>[BuildingFoundation] {buildingData.buildingName} 건설 완료! 완성자: {playerId}</color>");
|
||||
|
||||
OnConstructionComplete?.Invoke();
|
||||
|
||||
// 토대 디스폰
|
||||
if (IsServer && NetworkObject != null)
|
||||
{
|
||||
NetworkObject.Despawn(true);
|
||||
}
|
||||
|
||||
// 상호작용 UI 제거
|
||||
if (_progressBarInstance != null)
|
||||
{
|
||||
Destroy(_progressBarInstance);
|
||||
_progressBarInstance = null;
|
||||
}
|
||||
}
|
||||
|
||||
private TeamType GetPlayerTeam(ulong playerId)
|
||||
{
|
||||
if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(playerId, out NetworkObject playerObj))
|
||||
{
|
||||
var teamMember = playerObj.GetComponent<ITeamMember>();
|
||||
if (teamMember != null)
|
||||
{
|
||||
return teamMember.GetTeam();
|
||||
}
|
||||
}
|
||||
return TeamType.Neutral;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IInteractable Implementation - Getters
|
||||
|
||||
public string GetInteractionPrompt()
|
||||
{
|
||||
if (buildingData == null)
|
||||
return "[E] 건설하기";
|
||||
|
||||
float percentage = (_currentProgress.Value / buildingData.requiredWorkAmount) * 100f;
|
||||
return $"[E] {buildingData.buildingName} 건설 ({percentage:F0}%)";
|
||||
return "건설하기";
|
||||
float workNeeded = buildingData.requiredWorkAmount - _currentProgress.Value;
|
||||
float interactionsNeeded = Mathf.Ceil(workNeeded / buildingData.workPerInteraction);
|
||||
return $"[{interactionsNeeded}] 건설하기";
|
||||
}
|
||||
|
||||
public string GetInteractionAnimation()
|
||||
{
|
||||
// BuildingData에서 애니메이션 트리거 가져오기
|
||||
if (buildingData != null && !string.IsNullOrEmpty(buildingData.constructionAnimationTrigger))
|
||||
{
|
||||
if (buildingData != null && buildingData.constructionAnimationTrigger != null)
|
||||
return buildingData.constructionAnimationTrigger;
|
||||
}
|
||||
|
||||
// 기본값: 빈 문자열 (애니메이션 없음)
|
||||
return "";
|
||||
return null;
|
||||
}
|
||||
|
||||
public EquipmentData GetEquipmentData()
|
||||
{
|
||||
// BuildingData에 건설 도구가 정의되어 있으면 반환
|
||||
if (buildingData != null && buildingData.constructionEquipment != null)
|
||||
{
|
||||
return buildingData.constructionEquipment;
|
||||
}
|
||||
|
||||
return null; // 특별한 도구 불필요
|
||||
return buildingData != null ? buildingData.constructionEquipment : null;
|
||||
}
|
||||
|
||||
public Transform GetTransform()
|
||||
@@ -213,136 +274,25 @@ namespace Northbound
|
||||
|
||||
#region ITeamMember Implementation
|
||||
|
||||
public TeamType GetTeam()
|
||||
{
|
||||
return _team.Value;
|
||||
}
|
||||
public TeamType GetTeam() => _team.Value;
|
||||
|
||||
public void SetTeam(TeamType team)
|
||||
{
|
||||
if (!IsServer) return;
|
||||
if (!IsOwner)
|
||||
{
|
||||
SetTeamServerRpc(team);
|
||||
return;
|
||||
}
|
||||
|
||||
SetTeamServerRpc(team);
|
||||
}
|
||||
|
||||
[ServerRpc]
|
||||
private void SetTeamServerRpc(TeamType team)
|
||||
{
|
||||
_team.Value = team;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// 플레이어의 팀 가져오기
|
||||
/// </summary>
|
||||
private TeamType GetPlayerTeam(ulong playerId)
|
||||
{
|
||||
// 플레이어의 NetworkObject 찾기
|
||||
if (NetworkManager.Singleton != null && NetworkManager.Singleton.SpawnManager != null)
|
||||
{
|
||||
if (NetworkManager.Singleton.ConnectedClients.TryGetValue(playerId, out var client))
|
||||
{
|
||||
if (client.PlayerObject != null)
|
||||
{
|
||||
var teamMember = client.PlayerObject.GetComponent<ITeamMember>();
|
||||
if (teamMember != null)
|
||||
{
|
||||
return teamMember.GetTeam();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 기본값: 플레이어 팀
|
||||
return TeamType.Player;
|
||||
}
|
||||
|
||||
private void CompleteConstruction()
|
||||
{
|
||||
if (!IsServer) return;
|
||||
|
||||
Debug.Log($"<color=cyan>[BuildingFoundation] 건물 완성! {buildingData.buildingName}</color>");
|
||||
|
||||
OnConstructionComplete?.Invoke();
|
||||
|
||||
// BuildingManager에서 토대 제거
|
||||
var buildingManager = BuildingManager.Instance;
|
||||
if (buildingManager != null)
|
||||
{
|
||||
buildingManager.RemoveFoundation(this);
|
||||
}
|
||||
|
||||
// 완성된 건물 생성
|
||||
SpawnCompletedBuilding();
|
||||
|
||||
// 토대 제거
|
||||
if (NetworkObject != null)
|
||||
{
|
||||
NetworkObject.Despawn(true);
|
||||
}
|
||||
}
|
||||
|
||||
private void SpawnCompletedBuilding()
|
||||
{
|
||||
if (!IsServer || buildingData == null || buildingData.prefab == null)
|
||||
return;
|
||||
|
||||
// BuildingManager를 통해 건물 생성
|
||||
var buildingManager = BuildingManager.Instance;
|
||||
if (buildingManager != null)
|
||||
{
|
||||
buildingManager.SpawnCompletedBuildingServerRpc(
|
||||
buildingData.name,
|
||||
gridPosition,
|
||||
rotation,
|
||||
_ownerId.Value,
|
||||
_team.Value
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("[BuildingFoundation] BuildingManager를 찾을 수 없습니다!");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnProgressValueChanged(float oldValue, float newValue)
|
||||
{
|
||||
if (buildingData != null)
|
||||
{
|
||||
OnProgressChanged?.Invoke(newValue, buildingData.requiredWorkAmount);
|
||||
}
|
||||
UpdateProgressBar();
|
||||
}
|
||||
|
||||
private void UpdateProgressBar()
|
||||
{
|
||||
if (_progressBarInstance == null || buildingData == null) return;
|
||||
|
||||
// 진행바 UI 업데이트 (BuildingHealthBar와 유사한 구조 사용 가능)
|
||||
var progressBar = _progressBarInstance.GetComponent<BuildingHealthBar>();
|
||||
if (progressBar != null)
|
||||
{
|
||||
// BuildingHealthBar를 재사용하여 진행도 표시
|
||||
progressBar.UpdateHealth((int)_currentProgress.Value, (int)buildingData.requiredWorkAmount);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDrawGizmos()
|
||||
{
|
||||
if (buildingData == null) return;
|
||||
|
||||
// 건물 경계 표시 (노란색)
|
||||
Gizmos.color = Color.yellow;
|
||||
Vector3 size = buildingData.GetSize(rotation);
|
||||
Gizmos.DrawWireCube(transform.position + Vector3.up * size.y * 0.5f, size);
|
||||
|
||||
// Collider 경계 표시 (초록색)
|
||||
if (_collider != null)
|
||||
{
|
||||
Gizmos.color = Color.green;
|
||||
Gizmos.DrawWireCube(transform.position + _collider.center, _collider.size);
|
||||
}
|
||||
|
||||
// 상호작용 가능 여부 표시
|
||||
if (_currentProgress.Value < (buildingData?.requiredWorkAmount ?? 100f))
|
||||
{
|
||||
Gizmos.color = Color.cyan;
|
||||
Gizmos.DrawSphere(transform.position + Vector3.up, 0.3f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace Northbound
|
||||
public List<BuildingData> availableBuildings = new List<BuildingData>();
|
||||
|
||||
[Header("Foundation Settings")]
|
||||
public GameObject foundationPrefab; // 토대 프리팹 (Inspector에서 할당)
|
||||
public GameObject foundationPrefab;
|
||||
|
||||
private List<Building> placedBuildings = new List<Building>();
|
||||
private List<BuildingFoundation> placedFoundations = new List<BuildingFoundation>();
|
||||
@@ -58,6 +58,53 @@ namespace Northbound
|
||||
);
|
||||
}
|
||||
|
||||
private bool ValidateClient(ulong clientId)
|
||||
{
|
||||
if (!NetworkManager.Singleton.ConnectedClients.ContainsKey(clientId))
|
||||
{
|
||||
Debug.LogWarning($"<color=red>[BuildingManager] 유효하지 않은 클라이언트 ID: {clientId}</color>");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool ValidateBuildingIndex(int buildingIndex)
|
||||
{
|
||||
if (buildingIndex < 0 || buildingIndex >= availableBuildings.Count)
|
||||
{
|
||||
Debug.LogWarning($"<color=red>[BuildingManager] 유효하지 않은 건물 인덱스: {buildingIndex}</color>");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private BuildingData GetBuildingData(int buildingIndex)
|
||||
{
|
||||
BuildingData data = availableBuildings[buildingIndex];
|
||||
if (data == null || data.prefab == null)
|
||||
{
|
||||
Debug.LogWarning($"<color=red>[BuildingManager] 건물 데이터가 유효하지 않습니다.</color>");
|
||||
return null;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
private void SetupSpawnedObject(GameObject obj, ulong ownerId)
|
||||
{
|
||||
if (obj.GetComponent<FogOfWarVisibility>() == null)
|
||||
{
|
||||
var visibility = obj.AddComponent<FogOfWarVisibility>();
|
||||
visibility.showInExploredAreas = true;
|
||||
visibility.updateInterval = 0.2f;
|
||||
}
|
||||
|
||||
NetworkObject netObj = obj.GetComponent<NetworkObject>();
|
||||
if (netObj != null)
|
||||
{
|
||||
netObj.SpawnWithOwnership(ownerId);
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsValidPlacement(BuildingData data, Vector3 position, int rotation, out Vector3 groundPosition)
|
||||
{
|
||||
groundPosition = position;
|
||||
@@ -141,8 +188,10 @@ namespace Northbound
|
||||
{
|
||||
groundPosition = position;
|
||||
|
||||
// Raycast down to find ground
|
||||
if (Physics.Raycast(position + Vector3.up * 10f, Vector3.down, out RaycastHit hit, 20f, groundLayer))
|
||||
float originHeight = 10f;
|
||||
float rayDistance = 20f;
|
||||
Vector3 rayOrigin = position + Vector3.up * originHeight;
|
||||
if (Physics.Raycast(rayOrigin, Vector3.down, out RaycastHit hit, rayDistance, groundLayer))
|
||||
{
|
||||
groundPosition = hit.point;
|
||||
return true;
|
||||
@@ -169,30 +218,22 @@ namespace Northbound
|
||||
[Rpc(SendTo.Server, InvokePermission = RpcInvokePermission.Everyone)]
|
||||
private void PlaceBuildingServerRpc(int buildingIndex, Vector3 position, int rotation, ulong requestingClientId)
|
||||
{
|
||||
// 보안 검증 1: 유효한 클라이언트인지 확인
|
||||
if (!NetworkManager.Singleton.ConnectedClients.ContainsKey(requestingClientId))
|
||||
if (!ValidateClient(requestingClientId))
|
||||
{
|
||||
Debug.LogWarning($"<color=red>[BuildingManager] 유효하지 않은 클라이언트 ID: {requestingClientId}</color>");
|
||||
return;
|
||||
}
|
||||
|
||||
// 보안 검증 2: 건물 인덱스 유효성 확인
|
||||
if (buildingIndex < 0 || buildingIndex >= availableBuildings.Count)
|
||||
if (!ValidateBuildingIndex(buildingIndex))
|
||||
{
|
||||
Debug.LogWarning($"<color=red>[BuildingManager] 유효하지 않은 건물 인덱스: {buildingIndex} (클라이언트: {requestingClientId})</color>");
|
||||
return;
|
||||
}
|
||||
|
||||
BuildingData data = availableBuildings[buildingIndex];
|
||||
|
||||
// 보안 검증 3: 건물 데이터 유효성 확인
|
||||
if (data == null || data.prefab == null)
|
||||
BuildingData data = GetBuildingData(buildingIndex);
|
||||
if (data == null)
|
||||
{
|
||||
Debug.LogWarning($"<color=red>[BuildingManager] 건물 데이터가 유효하지 않습니다. (클라이언트: {requestingClientId})</color>");
|
||||
return;
|
||||
}
|
||||
|
||||
// 배치 가능 여부 확인
|
||||
if (!IsValidPlacement(data, position, rotation, out Vector3 snappedPosition))
|
||||
{
|
||||
Debug.LogWarning($"<color=yellow>[BuildingManager] 건물 배치 불가능 위치 (클라이언트: {requestingClientId})</color>");
|
||||
@@ -200,41 +241,23 @@ namespace Northbound
|
||||
}
|
||||
|
||||
Vector3Int gridPosition = WorldToGrid(snappedPosition);
|
||||
|
||||
// 건물 생성
|
||||
GameObject buildingObj = Instantiate(data.prefab, snappedPosition + data.placementOffset, Quaternion.Euler(0, rotation * 90f, 0));
|
||||
NetworkObject netObj = buildingObj.GetComponent<NetworkObject>();
|
||||
|
||||
// Add FogOfWarVisibility component to hide buildings in unexplored areas
|
||||
if (buildingObj.GetComponent<FogOfWarVisibility>() == null)
|
||||
SetupSpawnedObject(buildingObj, requestingClientId);
|
||||
|
||||
Building building = buildingObj.GetComponent<Building>();
|
||||
if (building == null)
|
||||
{
|
||||
var visibility = buildingObj.AddComponent<FogOfWarVisibility>();
|
||||
visibility.showInExploredAreas = true; // Buildings remain visible in explored areas
|
||||
visibility.updateInterval = 0.2f;
|
||||
}
|
||||
|
||||
if (netObj != null)
|
||||
{
|
||||
// 건물의 소유자를 설정
|
||||
netObj.SpawnWithOwnership(requestingClientId);
|
||||
|
||||
Building building = buildingObj.GetComponent<Building>();
|
||||
if (building == null)
|
||||
{
|
||||
building = buildingObj.AddComponent<Building>();
|
||||
}
|
||||
|
||||
// 건물 초기화
|
||||
building.Initialize(data, gridPosition, rotation, requestingClientId);
|
||||
placedBuildings.Add(building);
|
||||
|
||||
Debug.Log($"<color=green>[BuildingManager] {data.buildingName} 건설 완료 (소유자: {requestingClientId}, 위치: {gridPosition})</color>");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"<color=red>[BuildingManager] NetworkObject 컴포넌트가 없습니다! (Prefab: {data.prefab.name})</color>");
|
||||
Debug.LogError($"<color=red>[BuildingManager] Building prefab must have Building component! (Prefab: {data.prefab.name})</color>");
|
||||
buildingObj.GetComponent<NetworkObject>()?.Despawn(true);
|
||||
Destroy(buildingObj);
|
||||
return;
|
||||
}
|
||||
|
||||
building.Initialize(data, gridPosition, rotation, requestingClientId);
|
||||
placedBuildings.Add(building);
|
||||
|
||||
Debug.Log($"<color=green>[BuildingManager] {data.buildingName} 건설 완료 (소유자: {requestingClientId}, 위치: {gridPosition})</color>");
|
||||
}
|
||||
|
||||
public void RemoveBuilding(Building building)
|
||||
@@ -321,37 +344,28 @@ namespace Northbound
|
||||
[Rpc(SendTo.Server, InvokePermission = RpcInvokePermission.Everyone)]
|
||||
private void PlaceFoundationServerRpc(int buildingIndex, Vector3 position, int rotation, ulong requestingClientId)
|
||||
{
|
||||
// 보안 검증 1: 유효한 클라이언트인지 확인
|
||||
if (!NetworkManager.Singleton.ConnectedClients.ContainsKey(requestingClientId))
|
||||
if (!ValidateClient(requestingClientId))
|
||||
{
|
||||
Debug.LogWarning($"<color=red>[BuildingManager] 유효하지 않은 클라이언트 ID: {requestingClientId}</color>");
|
||||
return;
|
||||
}
|
||||
|
||||
// 보안 검증 2: 건물 인덱스 유효성 확인
|
||||
if (buildingIndex < 0 || buildingIndex >= availableBuildings.Count)
|
||||
if (!ValidateBuildingIndex(buildingIndex))
|
||||
{
|
||||
Debug.LogWarning($"<color=red>[BuildingManager] 유효하지 않은 건물 인덱스: {buildingIndex}</color>");
|
||||
return;
|
||||
}
|
||||
|
||||
BuildingData data = availableBuildings[buildingIndex];
|
||||
|
||||
// 보안 검증 3: 건물 데이터 유효성 확인
|
||||
BuildingData data = GetBuildingData(buildingIndex);
|
||||
if (data == null)
|
||||
{
|
||||
Debug.LogWarning($"<color=red>[BuildingManager] 건물 데이터가 유효하지 않습니다.</color>");
|
||||
return;
|
||||
}
|
||||
|
||||
// 토대 프리팹 확인
|
||||
if (foundationPrefab == null)
|
||||
{
|
||||
Debug.LogError("<color=red>[BuildingManager] foundationPrefab이 설정되지 않았습니다!</color>");
|
||||
return;
|
||||
}
|
||||
|
||||
// 배치 가능 여부 확인
|
||||
if (!IsValidPlacement(data, position, rotation, out Vector3 snappedPosition))
|
||||
{
|
||||
Debug.LogWarning($"<color=yellow>[BuildingManager] 토대 배치 불가능 위치</color>");
|
||||
@@ -359,43 +373,23 @@ namespace Northbound
|
||||
}
|
||||
|
||||
Vector3Int gridPosition = WorldToGrid(snappedPosition);
|
||||
|
||||
// 플레이어 팀 가져오기
|
||||
TeamType playerTeam = GetPlayerTeam(requestingClientId);
|
||||
|
||||
// 토대 생성
|
||||
GameObject foundationObj = Instantiate(foundationPrefab, snappedPosition + data.placementOffset, Quaternion.Euler(0, rotation * 90f, 0));
|
||||
NetworkObject netObj = foundationObj.GetComponent<NetworkObject>();
|
||||
|
||||
// Add FogOfWarVisibility component to hide foundations in unexplored areas
|
||||
if (foundationObj.GetComponent<FogOfWarVisibility>() == null)
|
||||
SetupSpawnedObject(foundationObj, requestingClientId);
|
||||
|
||||
BuildingFoundation foundation = foundationObj.GetComponent<BuildingFoundation>();
|
||||
if (foundation != null)
|
||||
{
|
||||
var visibility = foundationObj.AddComponent<FogOfWarVisibility>();
|
||||
visibility.showInExploredAreas = true; // Foundations remain visible in explored areas
|
||||
visibility.updateInterval = 0.2f;
|
||||
}
|
||||
|
||||
if (netObj != null)
|
||||
{
|
||||
netObj.SpawnWithOwnership(requestingClientId);
|
||||
|
||||
BuildingFoundation foundation = foundationObj.GetComponent<BuildingFoundation>();
|
||||
if (foundation != null)
|
||||
{
|
||||
foundation.Initialize(data, gridPosition, rotation, requestingClientId, playerTeam);
|
||||
placedFoundations.Add(foundation); // 토대 목록에 추가
|
||||
Debug.Log($"<color=yellow>[BuildingManager] {data.buildingName} 토대 생성 (소유자: {requestingClientId}, 위치: {gridPosition})</color>");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("<color=red>[BuildingManager] BuildingFoundation 컴포넌트가 없습니다!</color>");
|
||||
netObj.Despawn(true);
|
||||
}
|
||||
foundation.Initialize(data, gridPosition, rotation, requestingClientId, playerTeam);
|
||||
placedFoundations.Add(foundation);
|
||||
Debug.Log($"<color=yellow>[BuildingManager] {data.buildingName} 토대 생성 (소유자: {requestingClientId}, 위치: {gridPosition})</color>");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("<color=red>[BuildingManager] NetworkObject 컴포넌트가 없습니다!</color>");
|
||||
Destroy(foundationObj);
|
||||
Debug.LogError("<color=red>[BuildingManager] BuildingFoundation 컴포넌트가 없습니다!</color>");
|
||||
foundationObj.GetComponent<NetworkObject>()?.Despawn(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -427,7 +421,6 @@ namespace Northbound
|
||||
[Rpc(SendTo.Server, InvokePermission = RpcInvokePermission.Everyone)]
|
||||
public void SpawnCompletedBuildingServerRpc(string buildingDataName, Vector3Int gridPosition, int rotation, ulong ownerId, TeamType team)
|
||||
{
|
||||
// BuildingData 찾기
|
||||
BuildingData data = availableBuildings.Find(b => b.name == buildingDataName);
|
||||
if (data == null || data.prefab == null)
|
||||
{
|
||||
@@ -436,40 +429,23 @@ namespace Northbound
|
||||
}
|
||||
|
||||
Vector3 worldPosition = GridToWorld(gridPosition);
|
||||
|
||||
// 완성된 건물 생성
|
||||
GameObject buildingObj = Instantiate(data.prefab, worldPosition + data.placementOffset, Quaternion.Euler(0, rotation * 90f, 0));
|
||||
NetworkObject netObj = buildingObj.GetComponent<NetworkObject>();
|
||||
|
||||
// Add FogOfWarVisibility component to hide buildings in unexplored areas
|
||||
if (buildingObj.GetComponent<FogOfWarVisibility>() == null)
|
||||
SetupSpawnedObject(buildingObj, ownerId);
|
||||
|
||||
Building building = buildingObj.GetComponent<Building>();
|
||||
if (building == null)
|
||||
{
|
||||
var visibility = buildingObj.AddComponent<FogOfWarVisibility>();
|
||||
visibility.showInExploredAreas = true; // Buildings remain visible in explored areas
|
||||
visibility.updateInterval = 0.2f;
|
||||
}
|
||||
|
||||
if (netObj != null)
|
||||
{
|
||||
netObj.SpawnWithOwnership(ownerId);
|
||||
|
||||
Building building = buildingObj.GetComponent<Building>();
|
||||
if (building == null)
|
||||
{
|
||||
building = buildingObj.AddComponent<Building>();
|
||||
}
|
||||
|
||||
// 건물 초기화
|
||||
building.Initialize(data, gridPosition, rotation, ownerId);
|
||||
placedBuildings.Add(building);
|
||||
|
||||
Debug.Log($"<color=green>[BuildingManager] {data.buildingName} 건설 완료! (소유자: {ownerId}, 위치: {gridPosition}, 팀: {team})</color>");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"<color=red>[BuildingManager] NetworkObject 컴포넌트가 없습니다!</color>");
|
||||
Debug.LogError($"<color=red>[BuildingManager] Building prefab must have Building component! (Prefab: {data.prefab.name})</color>");
|
||||
buildingObj.GetComponent<NetworkObject>()?.Despawn(true);
|
||||
Destroy(buildingObj);
|
||||
return;
|
||||
}
|
||||
|
||||
building.Initialize(data, gridPosition, rotation, ownerId);
|
||||
placedBuildings.Add(building);
|
||||
|
||||
Debug.Log($"<color=green>[BuildingManager] {data.buildingName} 건설 완료! (소유자: {ownerId}, 위치: {gridPosition}, 팀: {team})</color>");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,61 +225,71 @@ namespace Northbound
|
||||
|
||||
private void CreatePreview()
|
||||
{
|
||||
if (!ValidateBuildingData(selectedBuildingIndex, out BuildingData data))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
previewObject = Instantiate(data.prefab);
|
||||
SetupPreviewObject(previewObject, validMaterial);
|
||||
Debug.Log($"[BuildingPlacement] 프리뷰 생성됨: {data.buildingName}");
|
||||
}
|
||||
|
||||
private bool ValidateBuildingData(int index, out BuildingData data)
|
||||
{
|
||||
data = null;
|
||||
|
||||
if (BuildingManager.Instance == null)
|
||||
{
|
||||
Debug.LogWarning("[BuildingPlacement] BuildingManager가 없습니다.");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (selectedBuildingIndex < 0 || selectedBuildingIndex >= BuildingManager.Instance.availableBuildings.Count)
|
||||
if (index < 0 || index >= BuildingManager.Instance.availableBuildings.Count)
|
||||
{
|
||||
Debug.LogWarning($"[BuildingPlacement] 유효하지 않은 건물 인덱스: {selectedBuildingIndex}");
|
||||
return;
|
||||
Debug.LogWarning($"[BuildingPlacement] 유효하지 않은 건물 인덱스: {index}");
|
||||
return false;
|
||||
}
|
||||
|
||||
BuildingData data = BuildingManager.Instance.availableBuildings[selectedBuildingIndex];
|
||||
data = BuildingManager.Instance.availableBuildings[index];
|
||||
if (data == null || data.prefab == null)
|
||||
{
|
||||
Debug.LogWarning("[BuildingPlacement] BuildingData 또는 Prefab이 없습니다.");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
// 완성 건물 프리팹으로 프리뷰 생성 (사용자가 완성 모습을 볼 수 있도록)
|
||||
previewObject = Instantiate(data.prefab);
|
||||
|
||||
// Remove NetworkObject component from preview
|
||||
NetworkObject netObj = previewObject.GetComponent<NetworkObject>();
|
||||
return true;
|
||||
}
|
||||
|
||||
private void SetupPreviewObject(GameObject previewObj, Material material)
|
||||
{
|
||||
NetworkObject netObj = previewObj.GetComponent<NetworkObject>();
|
||||
if (netObj != null)
|
||||
{
|
||||
Destroy(netObj);
|
||||
}
|
||||
|
||||
// Remove Building component from preview
|
||||
Building building = previewObject.GetComponent<Building>();
|
||||
Building building = previewObj.GetComponent<Building>();
|
||||
if (building != null)
|
||||
{
|
||||
Destroy(building);
|
||||
}
|
||||
|
||||
// Apply ghost materials
|
||||
previewRenderers = previewObject.GetComponentsInChildren<Renderer>();
|
||||
foreach (var renderer in previewRenderers)
|
||||
Renderer[] renderers = previewObj.GetComponentsInChildren<Renderer>();
|
||||
foreach (var renderer in renderers)
|
||||
{
|
||||
Material[] mats = new Material[renderer.materials.Length];
|
||||
for (int i = 0; i < mats.Length; i++)
|
||||
{
|
||||
mats[i] = validMaterial;
|
||||
mats[i] = material;
|
||||
}
|
||||
renderer.materials = mats;
|
||||
}
|
||||
|
||||
// Disable colliders in preview
|
||||
foreach (var collider in previewObject.GetComponentsInChildren<Collider>())
|
||||
foreach (var collider in previewObj.GetComponentsInChildren<Collider>())
|
||||
{
|
||||
collider.enabled = false;
|
||||
}
|
||||
|
||||
Debug.Log($"[BuildingPlacement] 프리뷰 생성됨: {data.buildingName}");
|
||||
}
|
||||
|
||||
private void DestroyPreview()
|
||||
@@ -297,29 +307,33 @@ namespace Northbound
|
||||
return;
|
||||
|
||||
Ray ray = Camera.main.ScreenPointToRay(Mouse.current.position.ReadValue());
|
||||
|
||||
|
||||
if (Physics.Raycast(ray, out RaycastHit hit, maxPlacementDistance, groundLayer))
|
||||
{
|
||||
BuildingData data = BuildingManager.Instance.availableBuildings[selectedBuildingIndex];
|
||||
|
||||
// Check if placement is valid
|
||||
bool isValid = BuildingManager.Instance.IsValidPlacement(data, hit.point, currentRotation, out Vector3 snappedPosition);
|
||||
|
||||
// Update preview position (placementOffset 적용)
|
||||
previewObject.transform.position = snappedPosition + data.placementOffset;
|
||||
previewObject.transform.rotation = Quaternion.Euler(0, currentRotation * 90f, 0);
|
||||
|
||||
// Update material based on validity
|
||||
Material targetMat = isValid ? validMaterial : invalidMaterial;
|
||||
foreach (var renderer in previewRenderers)
|
||||
UpdatePreviewMaterials(previewRenderers, targetMat);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdatePreviewMaterials(Renderer[] renderers, Material material)
|
||||
{
|
||||
if (renderers == null) return;
|
||||
|
||||
foreach (var renderer in renderers)
|
||||
{
|
||||
Material[] mats = new Material[renderer.materials.Length];
|
||||
for (int i = 0; i < mats.Length; i++)
|
||||
{
|
||||
Material[] mats = new Material[renderer.materials.Length];
|
||||
for (int i = 0; i < mats.Length; i++)
|
||||
{
|
||||
mats[i] = targetMat;
|
||||
}
|
||||
renderer.materials = mats;
|
||||
mats[i] = material;
|
||||
}
|
||||
renderer.materials = mats;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -419,16 +433,13 @@ namespace Northbound
|
||||
}
|
||||
|
||||
BuildingData data = BuildingManager.Instance.availableBuildings[selectedBuildingIndex];
|
||||
|
||||
// 드래그 영역 계산
|
||||
|
||||
Vector3 dragEndPosition = hit.point;
|
||||
List<Vector3> positions = CalculateDragBuildingPositions(dragStartPosition, dragEndPosition, data);
|
||||
|
||||
// 기존 프리뷰 정리
|
||||
ClearDragPreviews();
|
||||
|
||||
// 새로운 프리뷰 생성
|
||||
dragBuildingPositions.Clear();
|
||||
|
||||
foreach (var pos in positions)
|
||||
{
|
||||
if (dragPreviewObjects.Count >= maxDragBuildingCount)
|
||||
@@ -440,38 +451,14 @@ namespace Northbound
|
||||
bool isValid = BuildingManager.Instance.IsValidPlacement(data, pos, currentRotation, out Vector3 snappedPosition);
|
||||
|
||||
GameObject preview = Instantiate(data.prefab);
|
||||
|
||||
// Remove NetworkObject and Building components
|
||||
if (preview.GetComponent<NetworkObject>() != null)
|
||||
Destroy(preview.GetComponent<NetworkObject>());
|
||||
if (preview.GetComponent<Building>() != null)
|
||||
Destroy(preview.GetComponent<Building>());
|
||||
Material targetMat = isValid ? validMaterial : invalidMaterial;
|
||||
SetupPreviewObject(preview, targetMat);
|
||||
|
||||
// Set position and rotation
|
||||
preview.transform.position = snappedPosition + data.placementOffset;
|
||||
preview.transform.rotation = Quaternion.Euler(0, currentRotation * 90f, 0);
|
||||
|
||||
// Apply materials
|
||||
Material targetMat = isValid ? validMaterial : invalidMaterial;
|
||||
Renderer[] renderers = preview.GetComponentsInChildren<Renderer>();
|
||||
foreach (var renderer in renderers)
|
||||
{
|
||||
Material[] mats = new Material[renderer.materials.Length];
|
||||
for (int i = 0; i < mats.Length; i++)
|
||||
{
|
||||
mats[i] = targetMat;
|
||||
}
|
||||
renderer.materials = mats;
|
||||
}
|
||||
|
||||
// Disable colliders
|
||||
foreach (var collider in preview.GetComponentsInChildren<Collider>())
|
||||
{
|
||||
collider.enabled = false;
|
||||
}
|
||||
|
||||
dragPreviewObjects.Add(preview);
|
||||
|
||||
|
||||
if (isValid)
|
||||
{
|
||||
dragBuildingPositions.Add(snappedPosition);
|
||||
|
||||
@@ -50,7 +50,7 @@ namespace Northbound
|
||||
|
||||
public override void OnNetworkSpawn()
|
||||
{
|
||||
if (IsServer)
|
||||
if (IsOwner)
|
||||
{
|
||||
_totalResources.Value = 0;
|
||||
_currentHealth.Value = maxHealth;
|
||||
@@ -88,7 +88,7 @@ namespace Northbound
|
||||
|
||||
public void TakeDamage(int damage, ulong attackerId)
|
||||
{
|
||||
if (!IsServer) return;
|
||||
if (!IsOwner) return;
|
||||
if (_currentHealth.Value <= 0) return;
|
||||
|
||||
int actualDamage = Mathf.Min(damage, _currentHealth.Value);
|
||||
@@ -108,7 +108,7 @@ namespace Northbound
|
||||
|
||||
private void OnCoreDestroyed()
|
||||
{
|
||||
if (!IsServer) return;
|
||||
if (!IsOwner) return;
|
||||
|
||||
Debug.Log($"<color=red>[Core] 코어가 파괴되었습니다! 게임 오버!</color>");
|
||||
|
||||
@@ -174,7 +174,7 @@ namespace Northbound
|
||||
/// </summary>
|
||||
public void AddResource(int amount)
|
||||
{
|
||||
if (!IsServer) return;
|
||||
if (!IsOwner) return;
|
||||
|
||||
if (!unlimitedStorage)
|
||||
{
|
||||
@@ -206,7 +206,7 @@ namespace Northbound
|
||||
{
|
||||
if (client.PlayerObject != null)
|
||||
{
|
||||
var playerInventory = client.PlayerObject.GetComponent<PlayerResourceInventory>();
|
||||
var playerInventory = client.PlayerObject.GetComponent<PlayerInventory>();
|
||||
if (playerInventory != null)
|
||||
{
|
||||
// 플레이어가 자원을 가지고 있어야 함
|
||||
@@ -264,10 +264,10 @@ namespace Northbound
|
||||
if (playerObject == null)
|
||||
return;
|
||||
|
||||
var playerInventory = playerObject.GetComponent<PlayerResourceInventory>();
|
||||
var playerInventory = playerObject.GetComponent<PlayerInventory>();
|
||||
if (playerInventory == null)
|
||||
{
|
||||
Debug.LogWarning($"플레이어 {playerId}에게 PlayerResourceInventory 컴포넌트가 없습니다.");
|
||||
Debug.LogWarning($"플레이어 {playerId}에게 PlayerInventory 컴포넌트가 없습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -304,8 +304,8 @@ namespace Northbound
|
||||
return;
|
||||
}
|
||||
|
||||
// 플레이어로부터 자원 차감
|
||||
playerInventory.RemoveResourceServerRpc(depositAmount);
|
||||
// 플레이어로부터 자원 차감 (RPC로 owner에게 요청)
|
||||
playerInventory.RemoveResourcesRpc(depositAmount);
|
||||
|
||||
// 코어에 자원 추가
|
||||
_totalResources.Value += depositAmount;
|
||||
|
||||
162
Assets/Scripts/DamageableNetworkBehaviour.cs
Normal file
162
Assets/Scripts/DamageableNetworkBehaviour.cs
Normal file
@@ -0,0 +1,162 @@
|
||||
using System;
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Northbound
|
||||
{
|
||||
public abstract class DamageableNetworkBehaviour : NetworkBehaviour, IDamageable
|
||||
{
|
||||
[Header("Health Settings")]
|
||||
[SerializeField] protected int maxHealth = 100;
|
||||
[SerializeField] protected bool showHealthBar = true;
|
||||
|
||||
[Header("Visual Effects")]
|
||||
[SerializeField] protected GameObject damageEffectPrefab;
|
||||
[SerializeField] protected GameObject destroyEffectPrefab;
|
||||
[SerializeField] protected Transform effectSpawnPoint;
|
||||
|
||||
protected NetworkVariable<int> _currentHealth = new NetworkVariable<int>(
|
||||
0,
|
||||
NetworkVariableReadPermission.Everyone,
|
||||
NetworkVariableWritePermission.Server
|
||||
);
|
||||
|
||||
public event Action<int, int> OnHealthChanged;
|
||||
public event Action OnDestroyed;
|
||||
|
||||
protected void InvokeOnHealthChanged(int currentHealth, int maxHealth)
|
||||
{
|
||||
OnHealthChanged?.Invoke(currentHealth, maxHealth);
|
||||
}
|
||||
|
||||
protected void InvokeOnDestroyed()
|
||||
{
|
||||
OnDestroyed?.Invoke();
|
||||
}
|
||||
|
||||
public override void OnNetworkSpawn()
|
||||
{
|
||||
base.OnNetworkSpawn();
|
||||
|
||||
if (IsOwner)
|
||||
{
|
||||
InitializeHealthServerRpc(maxHealth);
|
||||
}
|
||||
|
||||
_currentHealth.OnValueChanged += OnHealthValueChanged;
|
||||
InitializeHealthBar();
|
||||
UpdateHealthUI();
|
||||
}
|
||||
|
||||
[ServerRpc]
|
||||
private void InitializeHealthServerRpc(int health)
|
||||
{
|
||||
if (_currentHealth.Value == 0)
|
||||
_currentHealth.Value = health;
|
||||
}
|
||||
|
||||
public override void OnNetworkDespawn()
|
||||
{
|
||||
_currentHealth.OnValueChanged -= OnHealthValueChanged;
|
||||
base.OnNetworkDespawn();
|
||||
}
|
||||
|
||||
protected virtual void InitializeHealthBar()
|
||||
{
|
||||
}
|
||||
|
||||
protected virtual void UpdateHealthUI()
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void TakeDamage(int damage, ulong attackerId)
|
||||
{
|
||||
if (!IsOwner)
|
||||
{
|
||||
TakeDamageServerRpc(damage, attackerId);
|
||||
return;
|
||||
}
|
||||
|
||||
TakeDamageServerRpc(damage, attackerId);
|
||||
}
|
||||
|
||||
[ServerRpc]
|
||||
private void TakeDamageServerRpc(int damage, ulong attackerId)
|
||||
{
|
||||
if (_currentHealth.Value <= 0)
|
||||
return;
|
||||
|
||||
int actualDamage = Mathf.Min(damage, _currentHealth.Value);
|
||||
_currentHealth.Value -= actualDamage;
|
||||
|
||||
Debug.Log($"<color=red>[{GetType().Name}] {gameObject.name} received {actualDamage} damage. Health: {_currentHealth.Value}/{maxHealth}</color>");
|
||||
|
||||
ShowDamageEffectClientRpc();
|
||||
|
||||
if (_currentHealth.Value <= 0)
|
||||
{
|
||||
Die(attackerId);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void Die(ulong killerId)
|
||||
{
|
||||
Debug.Log($"<color=red>[{GetType().Name}] {gameObject.name} destroyed! Killer: {killerId}</color>");
|
||||
OnDestroyed?.Invoke();
|
||||
ShowDeathEffectClientRpc();
|
||||
}
|
||||
|
||||
[ClientRpc]
|
||||
protected void ShowDamageEffectClientRpc()
|
||||
{
|
||||
if (damageEffectPrefab != null)
|
||||
{
|
||||
Transform spawnPoint = effectSpawnPoint != null ? effectSpawnPoint : transform;
|
||||
GameObject effect = Instantiate(damageEffectPrefab, spawnPoint.position + Vector3.up, Quaternion.identity);
|
||||
Destroy(effect, 2f);
|
||||
}
|
||||
}
|
||||
|
||||
[ClientRpc]
|
||||
protected void ShowDeathEffectClientRpc()
|
||||
{
|
||||
if (destroyEffectPrefab != null)
|
||||
{
|
||||
Transform spawnPoint = effectSpawnPoint != null ? effectSpawnPoint : transform;
|
||||
GameObject effect = Instantiate(destroyEffectPrefab, spawnPoint.position, Quaternion.identity);
|
||||
Destroy(effect, 3f);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnHealthValueChanged(int previousValue, int newValue)
|
||||
{
|
||||
OnHealthChanged?.Invoke(newValue, maxHealth);
|
||||
UpdateHealthUI();
|
||||
}
|
||||
|
||||
public int GetCurrentHealth() => _currentHealth.Value;
|
||||
public int GetMaxHealth() => maxHealth;
|
||||
public float GetHealthPercentage() => maxHealth > 0 ? (float)_currentHealth.Value / maxHealth : 0f;
|
||||
public bool IsDead() => _currentHealth.Value <= 0;
|
||||
|
||||
public virtual void Heal(int amount)
|
||||
{
|
||||
if (!IsOwner)
|
||||
{
|
||||
HealServerRpc(amount);
|
||||
return;
|
||||
}
|
||||
|
||||
HealServerRpc(amount);
|
||||
}
|
||||
|
||||
[ServerRpc]
|
||||
private void HealServerRpc(int amount)
|
||||
{
|
||||
int healAmount = Mathf.Min(amount, maxHealth - _currentHealth.Value);
|
||||
_currentHealth.Value += healAmount;
|
||||
|
||||
Debug.Log($"<color=green>[{GetType().Name}] {gameObject.name} healed {healAmount}. Health: {_currentHealth.Value}/{maxHealth}</color>");
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/DamageableNetworkBehaviour.cs.meta
Normal file
2
Assets/Scripts/DamageableNetworkBehaviour.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b7aeb2eee85ba3c42ab4d53ef28f96fc
|
||||
@@ -72,13 +72,13 @@ namespace Northbound
|
||||
private NetworkVariable<EnemyAIState> _currentState = new NetworkVariable<EnemyAIState>(
|
||||
EnemyAIState.Idle,
|
||||
NetworkVariableReadPermission.Everyone,
|
||||
NetworkVariableWritePermission.Server
|
||||
NetworkVariableWritePermission.Owner
|
||||
);
|
||||
|
||||
private NetworkVariable<ulong> _targetPlayerId = new NetworkVariable<ulong>(
|
||||
0,
|
||||
NetworkVariableReadPermission.Everyone,
|
||||
NetworkVariableWritePermission.Server
|
||||
NetworkVariableWritePermission.Owner
|
||||
);
|
||||
|
||||
private GameObject _cachedTargetPlayer;
|
||||
@@ -91,7 +91,7 @@ namespace Northbound
|
||||
_enemyUnit = GetComponent<EnemyUnit>();
|
||||
_originPosition = transform.position;
|
||||
|
||||
if (IsServer)
|
||||
if (IsOwner)
|
||||
{
|
||||
// NavMeshAgent 초기 설정
|
||||
_agent.speed = moveSpeed;
|
||||
@@ -121,7 +121,7 @@ namespace Northbound
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (!IsServer) return;
|
||||
if (!IsOwner) return;
|
||||
if (!_agent.isOnNavMesh) return;
|
||||
|
||||
switch (_currentState.Value)
|
||||
|
||||
@@ -36,13 +36,21 @@ namespace Northbound
|
||||
{
|
||||
base.OnNetworkSpawn();
|
||||
|
||||
if (IsServer)
|
||||
if (IsOwner)
|
||||
{
|
||||
_currentHealth.Value = maxHealth;
|
||||
_team.Value = enemyTeam;
|
||||
InitializeServerRpc(maxHealth, enemyTeam);
|
||||
}
|
||||
}
|
||||
|
||||
[ServerRpc]
|
||||
private void InitializeServerRpc(int health, TeamType team)
|
||||
{
|
||||
if (_currentHealth.Value == 0)
|
||||
_currentHealth.Value = health;
|
||||
if (_team.Value == TeamType.Neutral)
|
||||
_team.Value = team;
|
||||
}
|
||||
|
||||
public override void OnNetworkDespawn()
|
||||
{
|
||||
base.OnNetworkDespawn();
|
||||
@@ -52,7 +60,18 @@ namespace Northbound
|
||||
|
||||
public void TakeDamage(int damage, ulong attackerId)
|
||||
{
|
||||
if (!IsServer) return;
|
||||
if (!IsOwner)
|
||||
{
|
||||
TakeDamageServerRpc(damage, attackerId);
|
||||
return;
|
||||
}
|
||||
|
||||
TakeDamageServerRpc(damage, attackerId);
|
||||
}
|
||||
|
||||
[ServerRpc]
|
||||
private void TakeDamageServerRpc(int damage, ulong attackerId)
|
||||
{
|
||||
if (_currentHealth.Value <= 0) return;
|
||||
|
||||
// 공격자의 팀 확인
|
||||
@@ -128,7 +147,18 @@ namespace Northbound
|
||||
|
||||
public void SetTeam(TeamType team)
|
||||
{
|
||||
if (!IsServer) return;
|
||||
if (!IsOwner)
|
||||
{
|
||||
SetTeamServerRpc(team);
|
||||
return;
|
||||
}
|
||||
|
||||
SetTeamServerRpc(team);
|
||||
}
|
||||
|
||||
[ServerRpc]
|
||||
private void SetTeamServerRpc(TeamType team)
|
||||
{
|
||||
_team.Value = team;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,29 +1,31 @@
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
using Unity.Netcode;
|
||||
|
||||
namespace Northbound
|
||||
{
|
||||
/// <summary>
|
||||
/// 플레이어의 장비 소켓 관리 (손, 등, 허리 등)
|
||||
/// </summary>
|
||||
public class EquipmentSocket : MonoBehaviour
|
||||
public class EquipmentSocket : NetworkBehaviour
|
||||
{
|
||||
[System.Serializable]
|
||||
public class Socket
|
||||
{
|
||||
public string socketName; // "RightHand", "LeftHand", "Back" 등
|
||||
public Transform socketTransform; // 실제 본 Transform
|
||||
[HideInInspector] public GameObject currentEquipment; // 현재 장착된 장비
|
||||
public string socketName;
|
||||
public Transform socketTransform;
|
||||
[HideInInspector] public GameObject currentEquipment;
|
||||
}
|
||||
|
||||
[Header("Available Sockets")]
|
||||
public List<Socket> sockets = new List<Socket>();
|
||||
|
||||
[Header("Equipment Prefabs")]
|
||||
public GameObject[] equipmentPrefabs;
|
||||
|
||||
private Dictionary<string, Socket> _socketDict = new Dictionary<string, Socket>();
|
||||
private Dictionary<string, GameObject> _prefabDict = new Dictionary<string, GameObject>();
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
// 빠른 검색을 위한 딕셔너리 생성
|
||||
_socketDict.Clear();
|
||||
foreach (var socket in sockets)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(socket.socketName))
|
||||
@@ -31,82 +33,124 @@ namespace Northbound
|
||||
_socketDict[socket.socketName] = socket;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 소켓에 장비 부착
|
||||
/// </summary>
|
||||
public GameObject AttachToSocket(string socketName, GameObject equipmentPrefab)
|
||||
{
|
||||
if (!_socketDict.TryGetValue(socketName, out Socket socket))
|
||||
_prefabDict.Clear();
|
||||
if (equipmentPrefabs != null)
|
||||
{
|
||||
Debug.LogWarning($"소켓을 찾을 수 없습니다: {socketName}");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (socket.socketTransform == null)
|
||||
{
|
||||
Debug.LogWarning($"소켓 Transform이 없습니다: {socketName}");
|
||||
return null;
|
||||
}
|
||||
|
||||
// 기존 장비 제거
|
||||
DetachFromSocket(socketName);
|
||||
|
||||
// 새 장비 생성
|
||||
if (equipmentPrefab != null)
|
||||
{
|
||||
GameObject equipment = Instantiate(equipmentPrefab, socket.socketTransform);
|
||||
equipment.transform.localPosition = Vector3.zero;
|
||||
equipment.transform.localRotation = Quaternion.identity;
|
||||
socket.currentEquipment = equipment;
|
||||
|
||||
return equipment;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 소켓에서 장비 제거
|
||||
/// </summary>
|
||||
public void DetachFromSocket(string socketName)
|
||||
{
|
||||
if (!_socketDict.TryGetValue(socketName, out Socket socket))
|
||||
return;
|
||||
|
||||
if (socket.currentEquipment != null)
|
||||
{
|
||||
Destroy(socket.currentEquipment);
|
||||
socket.currentEquipment = null;
|
||||
foreach (var prefab in equipmentPrefabs)
|
||||
{
|
||||
if (prefab != null)
|
||||
{
|
||||
_prefabDict[prefab.name] = prefab;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 모든 소켓에서 장비 제거
|
||||
/// </summary>
|
||||
public void DetachAll()
|
||||
public override void OnNetworkDespawn()
|
||||
{
|
||||
foreach (var socket in sockets)
|
||||
{
|
||||
if (socket.currentEquipment != null)
|
||||
{
|
||||
Destroy(socket.currentEquipment);
|
||||
Object.Destroy(socket.currentEquipment);
|
||||
socket.currentEquipment = null;
|
||||
}
|
||||
}
|
||||
base.OnNetworkDespawn();
|
||||
}
|
||||
|
||||
public GameObject AttachToSocket(string socketName, GameObject equipmentPrefab)
|
||||
{
|
||||
if (equipmentPrefab != null)
|
||||
{
|
||||
AttachToSocketServerRpc(socketName, equipmentPrefab.name);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
[Rpc(SendTo.Server)]
|
||||
private void AttachToSocketServerRpc(string socketName, string prefabName)
|
||||
{
|
||||
AttachToSocketClientRpc(socketName, prefabName);
|
||||
}
|
||||
|
||||
[Rpc(SendTo.ClientsAndHost)]
|
||||
private void AttachToSocketClientRpc(string socketName, string prefabName)
|
||||
{
|
||||
if (!_socketDict.ContainsKey(socketName))
|
||||
return;
|
||||
|
||||
var socket = sockets.Find(s => s.socketName == socketName);
|
||||
if (socket == null || socket.socketTransform == null)
|
||||
return;
|
||||
|
||||
DetachFromSocketInternal(socketName);
|
||||
|
||||
GameObject prefab = FindPrefab(prefabName);
|
||||
if (prefab != null)
|
||||
{
|
||||
GameObject equipment = Object.Instantiate(prefab, socket.socketTransform);
|
||||
equipment.transform.localPosition = Vector3.zero;
|
||||
equipment.transform.localRotation = Quaternion.identity;
|
||||
socket.currentEquipment = equipment;
|
||||
}
|
||||
}
|
||||
|
||||
public void DetachFromSocket(string socketName)
|
||||
{
|
||||
DetachFromSocketServerRpc(socketName);
|
||||
}
|
||||
|
||||
[Rpc(SendTo.Server)]
|
||||
private void DetachFromSocketServerRpc(string socketName)
|
||||
{
|
||||
DetachFromSocketClientRpc(socketName);
|
||||
}
|
||||
|
||||
[Rpc(SendTo.ClientsAndHost)]
|
||||
private void DetachFromSocketClientRpc(string socketName)
|
||||
{
|
||||
DetachFromSocketInternal(socketName);
|
||||
}
|
||||
|
||||
private void DetachFromSocketInternal(string socketName)
|
||||
{
|
||||
var socket = sockets.Find(s => s.socketName == socketName);
|
||||
if (socket == null) return;
|
||||
|
||||
if (socket.currentEquipment != null)
|
||||
{
|
||||
Object.Destroy(socket.currentEquipment);
|
||||
socket.currentEquipment = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 특정 소켓에 장비가 있는지 확인
|
||||
/// </summary>
|
||||
public bool HasEquipment(string socketName)
|
||||
{
|
||||
if (_socketDict.TryGetValue(socketName, out Socket socket))
|
||||
var socket = sockets.Find(s => s.socketName == socketName);
|
||||
return socket != null && socket.currentEquipment != null;
|
||||
}
|
||||
|
||||
public GameObject GetEquipment(string socketName)
|
||||
{
|
||||
var socket = sockets.Find(s => s.socketName == socketName);
|
||||
return socket != null ? socket.currentEquipment : null;
|
||||
}
|
||||
|
||||
private GameObject FindPrefab(string name)
|
||||
{
|
||||
if (_prefabDict.TryGetValue(name, out var prefab))
|
||||
{
|
||||
return socket.currentEquipment != null;
|
||||
return prefab;
|
||||
}
|
||||
return false;
|
||||
|
||||
prefab = Resources.Load<GameObject>($"Prefabs/{name}");
|
||||
if (prefab != null)
|
||||
{
|
||||
return prefab;
|
||||
}
|
||||
return Resources.Load<GameObject>(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -258,7 +258,7 @@ namespace Northbound
|
||||
|
||||
private void OnClientConnected(ulong clientId)
|
||||
{
|
||||
if (!IsServer) return;
|
||||
if (!IsOwner) return;
|
||||
|
||||
// Ensure fog data exists for this client
|
||||
if (!_serverFogData.ContainsKey(clientId))
|
||||
@@ -280,7 +280,7 @@ namespace Northbound
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (!IsServer) return;
|
||||
if (!IsOwner) return;
|
||||
|
||||
_updateTimer += Time.deltaTime;
|
||||
if (_updateTimer >= updateInterval)
|
||||
|
||||
34
Assets/Scripts/GameConstants.cs
Normal file
34
Assets/Scripts/GameConstants.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
namespace Northbound
|
||||
{
|
||||
public static class GameConstants
|
||||
{
|
||||
public static class Building
|
||||
{
|
||||
public const float BoundsShrinkAmount = 0.01f;
|
||||
public const float MaxPlacementDistance = 100f;
|
||||
public const int MaxDragBuildingCount = 50;
|
||||
}
|
||||
|
||||
public static class Physics
|
||||
{
|
||||
public const float GroundRaycastOriginHeight = 10f;
|
||||
public const float GroundRaycastDistance = 20f;
|
||||
}
|
||||
|
||||
public static class FogOfWar
|
||||
{
|
||||
public const float DefaultUpdateInterval = 0.2f;
|
||||
}
|
||||
|
||||
public static class Player
|
||||
{
|
||||
public const float RespawnDelay = 3f;
|
||||
public const int DefaultMaxHealth = 100;
|
||||
}
|
||||
|
||||
public static class Material
|
||||
{
|
||||
public const int TransparentRenderQueue = 3000;
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/GameConstants.cs.meta
Normal file
2
Assets/Scripts/GameConstants.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 55b873d6b94628148b3c61c4f5d435fc
|
||||
@@ -133,7 +133,7 @@ namespace Northbound
|
||||
return;
|
||||
}
|
||||
|
||||
var inventory = localPlayer.GetComponent<PlayerResourceInventory>();
|
||||
var inventory = localPlayer.GetComponent<PlayerInventory>();
|
||||
if (inventory == null)
|
||||
{
|
||||
playerResourceText.text = playerPrefix + "---";
|
||||
|
||||
@@ -67,7 +67,7 @@ namespace Northbound
|
||||
{
|
||||
base.OnNetworkSpawn();
|
||||
|
||||
if (IsServer && autoStart)
|
||||
if (IsOwner && autoStart)
|
||||
{
|
||||
StartTimer();
|
||||
}
|
||||
@@ -83,7 +83,7 @@ namespace Northbound
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (!IsServer || !_isRunning.Value)
|
||||
if (!IsOwner || !_isRunning.Value)
|
||||
return;
|
||||
|
||||
_currentTime.Value -= Time.deltaTime;
|
||||
@@ -149,7 +149,7 @@ namespace Northbound
|
||||
private void OnCurrentTimeChanged(float previousValue, float newValue)
|
||||
{
|
||||
// 클라이언트에서도 Tick 이벤트 발생
|
||||
if (!IsServer)
|
||||
if (!IsOwner)
|
||||
{
|
||||
OnTimerTick?.Invoke(newValue);
|
||||
}
|
||||
@@ -162,7 +162,7 @@ namespace Northbound
|
||||
/// </summary>
|
||||
public void StartTimer()
|
||||
{
|
||||
if (!IsServer) return;
|
||||
if (!IsOwner) return;
|
||||
|
||||
_currentTime.Value = cycleLength;
|
||||
_isRunning.Value = true;
|
||||
@@ -181,7 +181,7 @@ namespace Northbound
|
||||
/// </summary>
|
||||
public void PauseTimer()
|
||||
{
|
||||
if (!IsServer) return;
|
||||
if (!IsOwner) return;
|
||||
|
||||
_isRunning.Value = false;
|
||||
|
||||
@@ -194,7 +194,7 @@ namespace Northbound
|
||||
/// </summary>
|
||||
public void ResumeTimer()
|
||||
{
|
||||
if (!IsServer) return;
|
||||
if (!IsOwner) return;
|
||||
|
||||
_isRunning.Value = true;
|
||||
|
||||
@@ -207,7 +207,7 @@ namespace Northbound
|
||||
/// </summary>
|
||||
public void ResetTimer()
|
||||
{
|
||||
if (!IsServer) return;
|
||||
if (!IsOwner) return;
|
||||
|
||||
_currentTime.Value = cycleLength;
|
||||
_cycleCount.Value = 0;
|
||||
@@ -255,7 +255,7 @@ namespace Northbound
|
||||
[ClientRpc]
|
||||
private void NotifyCycleCompleteClientRpc()
|
||||
{
|
||||
if (!IsServer)
|
||||
if (!IsOwner)
|
||||
{
|
||||
OnCycleComplete?.Invoke();
|
||||
}
|
||||
@@ -264,7 +264,7 @@ namespace Northbound
|
||||
[ClientRpc]
|
||||
private void NotifyCycleStartClientRpc(int cycleNumber)
|
||||
{
|
||||
if (!IsServer)
|
||||
if (!IsOwner)
|
||||
{
|
||||
OnCycleStart?.Invoke(cycleNumber);
|
||||
}
|
||||
@@ -273,7 +273,7 @@ namespace Northbound
|
||||
[ClientRpc]
|
||||
private void NotifyHalfwayClientRpc()
|
||||
{
|
||||
if (!IsServer)
|
||||
if (!IsOwner)
|
||||
{
|
||||
OnHalfwayPoint?.Invoke(cycleLength / 2f);
|
||||
}
|
||||
|
||||
54
Assets/Scripts/InputActionManager.cs
Normal file
54
Assets/Scripts/InputActionManager.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
|
||||
namespace Northbound
|
||||
{
|
||||
public abstract class InputActionManager : NetworkBehaviour
|
||||
{
|
||||
protected PlayerInputActions _inputActions;
|
||||
|
||||
public override void OnNetworkSpawn()
|
||||
{
|
||||
base.OnNetworkSpawn();
|
||||
|
||||
if (!IsOwner) return;
|
||||
|
||||
InitializeInputActions();
|
||||
}
|
||||
|
||||
public override void OnNetworkDespawn()
|
||||
{
|
||||
if (IsOwner && _inputActions != null)
|
||||
{
|
||||
UnbindInputActions();
|
||||
_inputActions.Disable();
|
||||
_inputActions.Dispose();
|
||||
_inputActions = null;
|
||||
}
|
||||
|
||||
base.OnNetworkDespawn();
|
||||
}
|
||||
|
||||
public override void OnDestroy()
|
||||
{
|
||||
if (_inputActions != null)
|
||||
{
|
||||
_inputActions.Dispose();
|
||||
_inputActions = null;
|
||||
}
|
||||
base.OnDestroy();
|
||||
}
|
||||
|
||||
protected virtual void InitializeInputActions()
|
||||
{
|
||||
_inputActions = new PlayerInputActions();
|
||||
_inputActions.Enable();
|
||||
BindInputActions();
|
||||
}
|
||||
|
||||
protected abstract void BindInputActions();
|
||||
|
||||
protected abstract void UnbindInputActions();
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/InputActionManager.cs.meta
Normal file
2
Assets/Scripts/InputActionManager.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 06c90984b0af66542bd5011748fe67c1
|
||||
@@ -68,16 +68,11 @@ namespace Northbound
|
||||
NetworkManager.ConnectionApprovalRequest request,
|
||||
NetworkManager.ConnectionApprovalResponse response)
|
||||
{
|
||||
// 🔍 디버깅: 스폰 포인트 상태 확인
|
||||
if (spawnPoints.Count == 0)
|
||||
{
|
||||
Debug.LogError($"<color=red>[Connection] 스폰 포인트가 없습니다! 씬에 PlayerSpawnPoint가 있는지 확인하세요.</color>");
|
||||
}
|
||||
|
||||
response.Approved = true;
|
||||
response.CreatePlayerObject = true;
|
||||
|
||||
// 스폰 위치 설정
|
||||
|
||||
// AutoSpawnPlayerPrefabClientSide를 false로 설정해야 서버에서 스폰 위치가 적용됩니다
|
||||
// 임시로 스폰 위치 설정
|
||||
response.Position = GetSpawnPosition(request.ClientNetworkId);
|
||||
response.Rotation = GetSpawnRotation(request.ClientNetworkId);
|
||||
|
||||
|
||||
@@ -1,11 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8d7e6f5c4b3a2d1e0f9a8b7c6d5e4f3a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
guid: 8d7e6f5c4b3a2d1e0f9a8b7c6d5e4f3a
|
||||
@@ -1,337 +1,343 @@
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
using Unity.Cinemachine;
|
||||
using Northbound;
|
||||
|
||||
public class NetworkPlayerController : NetworkBehaviour, ITeamMember, IDamageable
|
||||
namespace Northbound
|
||||
{
|
||||
[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<TeamType> _team = new NetworkVariable<TeamType>(
|
||||
TeamType.Player,
|
||||
NetworkVariableReadPermission.Everyone,
|
||||
NetworkVariableWritePermission.Server
|
||||
);
|
||||
|
||||
private NetworkVariable<int> _currentHealth = new NetworkVariable<int>(
|
||||
100,
|
||||
NetworkVariableReadPermission.Everyone,
|
||||
NetworkVariableWritePermission.Server
|
||||
);
|
||||
|
||||
private Vector2 _moveInput;
|
||||
private CharacterController _controller;
|
||||
private PlayerInputActions _inputActions;
|
||||
private Animator _animator;
|
||||
|
||||
void Awake()
|
||||
public class NetworkPlayerController : NetworkBehaviour, ITeamMember, IDamageable
|
||||
{
|
||||
_controller = GetComponent<CharacterController>();
|
||||
_animator = GetComponent<Animator>();
|
||||
}
|
||||
[Header("Movement Settings")]
|
||||
public float moveSpeed = 5f;
|
||||
public float rotationSpeed = 10f;
|
||||
|
||||
public override void OnNetworkSpawn()
|
||||
{
|
||||
base.OnNetworkSpawn();
|
||||
[Header("Team Settings")]
|
||||
[SerializeField] private TeamType initialTeam = TeamType.Player;
|
||||
|
||||
// 서버에서 초기화
|
||||
if (IsServer)
|
||||
[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<TeamType> _team = new NetworkVariable<TeamType>(
|
||||
TeamType.Player,
|
||||
NetworkVariableReadPermission.Everyone,
|
||||
NetworkVariableWritePermission.Server
|
||||
);
|
||||
|
||||
private NetworkVariable<int> _currentHealth = new NetworkVariable<int>(
|
||||
100,
|
||||
NetworkVariableReadPermission.Everyone,
|
||||
NetworkVariableWritePermission.Server
|
||||
);
|
||||
|
||||
private Vector2 _moveInput;
|
||||
private CharacterController _controller;
|
||||
private PlayerInputActions _inputActions;
|
||||
private Animator _animator;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
_controller = GetComponent<CharacterController>();
|
||||
_animator = GetComponent<Animator>();
|
||||
}
|
||||
|
||||
public override void OnNetworkSpawn()
|
||||
{
|
||||
base.OnNetworkSpawn();
|
||||
|
||||
Debug.Log($"<color=cyan>[Player] {gameObject.name} spawned. OwnerId: {OwnerClientId}, LocalClientId: {NetworkManager.Singleton.LocalClientId}, IsOwner: {IsOwner}, IsServer: {IsServer}</color>");
|
||||
|
||||
if (IsOwner)
|
||||
{
|
||||
SetSpawnPosition();
|
||||
InitializePlayerServerRpc(initialTeam, maxHealth);
|
||||
}
|
||||
|
||||
_currentHealth.OnValueChanged += OnHealthChanged;
|
||||
|
||||
if (!IsOwner) return;
|
||||
|
||||
var vcam = GameObject.FindFirstObjectByType<CinemachineCamera>();
|
||||
|
||||
if (vcam != null)
|
||||
{
|
||||
vcam.Follow = transform;
|
||||
vcam.LookAt = transform;
|
||||
Debug.Log("<color=green>[Camera] Camera attached to local player.</color>");
|
||||
}
|
||||
|
||||
_inputActions = new PlayerInputActions();
|
||||
_inputActions.Enable();
|
||||
Debug.Log("<color=green>[Player] Input actions enabled for local player.</color>");
|
||||
}
|
||||
|
||||
[ServerRpc]
|
||||
private void InitializePlayerServerRpc(TeamType team, int health)
|
||||
{
|
||||
if (_team.Value == TeamType.Neutral)
|
||||
{
|
||||
_team.Value = initialTeam;
|
||||
}
|
||||
|
||||
_team.Value = team;
|
||||
if (_currentHealth.Value == 0)
|
||||
_currentHealth.Value = health;
|
||||
|
||||
Debug.Log($"<color=cyan>[Player] {gameObject.name} initialized (Team: {TeamManager.GetTeamName(_team.Value)}, HP: {_currentHealth.Value}/{maxHealth})</color>");
|
||||
}
|
||||
|
||||
private void SetSpawnPosition()
|
||||
{
|
||||
if (PlayerSpawnPositionSetter.Instance == null)
|
||||
{
|
||||
_currentHealth.Value = maxHealth;
|
||||
Debug.LogWarning("[Player] PlayerSpawnPositionSetter not found. Using default spawn position.");
|
||||
return;
|
||||
}
|
||||
|
||||
Debug.Log($"<color=cyan>[Player] {gameObject.name} 스폰됨 (팀: {TeamManager.GetTeamName(_team.Value)}, 체력: {_currentHealth.Value}/{maxHealth})</color>");
|
||||
Vector3 spawnPos = PlayerSpawnPositionSetter.Instance.GetSpawnPosition(OwnerClientId);
|
||||
Quaternion spawnRot = PlayerSpawnPositionSetter.Instance.GetSpawnRotation(OwnerClientId);
|
||||
|
||||
transform.position = spawnPos;
|
||||
transform.rotation = spawnRot;
|
||||
|
||||
Debug.Log($"<color=yellow>[Player] Spawn position set: {spawnPos}</color>");
|
||||
}
|
||||
|
||||
// 체력 변경 이벤트 구독
|
||||
_currentHealth.OnValueChanged += OnHealthChanged;
|
||||
|
||||
if (!IsOwner) return;
|
||||
|
||||
var vcam = GameObject.FindFirstObjectByType<CinemachineCamera>();
|
||||
|
||||
if (vcam != null)
|
||||
public override void OnNetworkDespawn()
|
||||
{
|
||||
vcam.Follow = transform;
|
||||
vcam.LookAt = transform;
|
||||
Debug.Log("<color=green>[Camera] 로컬 플레이어에게 카메라가 연결되었습니다.</color>");
|
||||
}
|
||||
_currentHealth.OnValueChanged -= OnHealthChanged;
|
||||
|
||||
_inputActions = new PlayerInputActions();
|
||||
_inputActions.Enable();
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
// 액션/상호작용 중이면 이동 불가
|
||||
var attackAction = GetComponent<AttackAction>();
|
||||
var playerInteraction = GetComponent<PlayerInteraction>();
|
||||
|
||||
bool isActionBlocked = (attackAction != null && attackAction.IsAttacking) ||
|
||||
(playerInteraction != null && playerInteraction.IsInteracting);
|
||||
|
||||
if (isActionBlocked)
|
||||
{
|
||||
// 이동 불가 시 애니메이션 속도를 0으로
|
||||
if (_animator != null)
|
||||
if (IsOwner && _inputActions != null)
|
||||
{
|
||||
_animator.SetFloat("MoveSpeed", 0f);
|
||||
_inputActions.Disable();
|
||||
_inputActions.Dispose();
|
||||
}
|
||||
return;
|
||||
|
||||
base.OnNetworkDespawn();
|
||||
}
|
||||
|
||||
_moveInput = _inputActions.Player.Move.ReadValue<Vector2>();
|
||||
Vector3 move = new Vector3(_moveInput.x, 0, _moveInput.y).normalized;
|
||||
|
||||
if (move.magnitude >= 0.1f)
|
||||
void Update()
|
||||
{
|
||||
Quaternion targetRotation = Quaternion.LookRotation(move);
|
||||
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, rotationSpeed * Time.deltaTime);
|
||||
if (!IsOwner) return;
|
||||
|
||||
if (_controller != null)
|
||||
if (_currentHealth.Value <= 0) return;
|
||||
|
||||
var attackAction = GetComponent<AttackAction>();
|
||||
var playerInteraction = GetComponent<PlayerInteraction>();
|
||||
|
||||
bool isActionBlocked = (attackAction != null && attackAction.IsAttacking) ||
|
||||
(playerInteraction != null && playerInteraction.IsInteracting);
|
||||
|
||||
if (isActionBlocked)
|
||||
{
|
||||
_controller.Move(move * moveSpeed * Time.deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
if (_animator != null)
|
||||
{
|
||||
_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($"<color=cyan>[Player] 팀 변경: {TeamManager.GetTeamName(previousTeam)} → {TeamManager.GetTeamName(team)}</color>");
|
||||
}
|
||||
|
||||
#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<ITeamMember>();
|
||||
if (attackerTeamMember != null)
|
||||
{
|
||||
if (!TeamManager.CanAttack(attackerTeamMember, this))
|
||||
if (_animator != null)
|
||||
{
|
||||
Debug.Log($"<color=yellow>[Player] {TeamManager.GetTeamName(attackerTeamMember.GetTeam())} 팀은 {TeamManager.GetTeamName(_team.Value)} 팀을 공격할 수 없습니다.</color>");
|
||||
return;
|
||||
_animator.SetFloat("MoveSpeed", 0f);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
_moveInput = _inputActions.Player.Move.ReadValue<Vector2>();
|
||||
Vector3 move = new Vector3(_moveInput.x, 0, _moveInput.y).normalized;
|
||||
|
||||
if (move.magnitude >= 0.1f)
|
||||
{
|
||||
Quaternion targetRotation = Quaternion.LookRotation(move);
|
||||
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, rotationSpeed * Time.deltaTime);
|
||||
|
||||
if (_controller != null)
|
||||
{
|
||||
_controller.Move(move * moveSpeed * Time.deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
if (_animator != null)
|
||||
{
|
||||
_animator.SetFloat("MoveSpeed", move.magnitude);
|
||||
}
|
||||
}
|
||||
|
||||
// 데미지 적용
|
||||
int actualDamage = Mathf.Min(damage, _currentHealth.Value);
|
||||
_currentHealth.Value -= actualDamage;
|
||||
#region ITeamMember Implementation
|
||||
|
||||
Debug.Log($"<color=red>[Player] {gameObject.name} ({TeamManager.GetTeamName(_team.Value)})이(가) {actualDamage} 데미지를 받았습니다. 남은 체력: {_currentHealth.Value}/{maxHealth}</color>");
|
||||
public TeamType GetTeam() => _team.Value;
|
||||
|
||||
// 데미지 이펙트
|
||||
ShowDamageEffectClientRpc();
|
||||
|
||||
// 체력이 0이 되면 사망
|
||||
if (_currentHealth.Value <= 0)
|
||||
public void SetTeam(TeamType team)
|
||||
{
|
||||
Die(attackerId);
|
||||
if (!IsOwner) return;
|
||||
|
||||
SetTeamServerRpc(team);
|
||||
}
|
||||
}
|
||||
|
||||
private void Die(ulong killerId)
|
||||
{
|
||||
if (!IsServer) return;
|
||||
|
||||
Debug.Log($"<color=red>[Player] {gameObject.name} ({TeamManager.GetTeamName(_team.Value)})이(가) 사망했습니다! (킬러: {killerId})</color>");
|
||||
|
||||
// 사망 이펙트
|
||||
ShowDeathEffectClientRpc();
|
||||
|
||||
// 애니메이션 (있는 경우)
|
||||
if (_animator != null)
|
||||
[ServerRpc]
|
||||
private void SetTeamServerRpc(TeamType team)
|
||||
{
|
||||
_animator.SetTrigger("Die");
|
||||
TeamType previousTeam = _team.Value;
|
||||
_team.Value = team;
|
||||
Debug.Log($"<color=cyan>[Player] 팀 변경: {TeamManager.GetTeamName(previousTeam)} → {TeamManager.GetTeamName(team)}</color>");
|
||||
}
|
||||
|
||||
// 일정 시간 후 리스폰 또는 디스폰
|
||||
Invoke(nameof(HandleDeath), 3f);
|
||||
}
|
||||
#endregion
|
||||
|
||||
private void HandleDeath()
|
||||
{
|
||||
if (!IsServer) return;
|
||||
#region IDamageable Implementation
|
||||
|
||||
// 여기서 리스폰 로직을 추가하거나 게임 오버 처리
|
||||
// 예: 리스폰 위치로 이동 및 체력 회복
|
||||
Respawn();
|
||||
}
|
||||
|
||||
private void Respawn()
|
||||
{
|
||||
if (!IsServer) return;
|
||||
|
||||
// 체력 회복
|
||||
_currentHealth.Value = maxHealth;
|
||||
|
||||
// 스폰 포인트로 이동 (PlayerSpawnPoint 활용)
|
||||
var spawnPoints = FindObjectsByType<PlayerSpawnPoint>(FindObjectsSortMode.None);
|
||||
if (spawnPoints.Length > 0)
|
||||
public void TakeDamage(int damage, ulong attackerId)
|
||||
{
|
||||
var spawnPoint = spawnPoints[Random.Range(0, spawnPoints.Length)];
|
||||
transform.position = spawnPoint.transform.position;
|
||||
transform.rotation = spawnPoint.transform.rotation;
|
||||
if (!IsOwner)
|
||||
{
|
||||
TakeDamageServerRpc(damage, attackerId);
|
||||
return;
|
||||
}
|
||||
|
||||
TakeDamageServerRpc(damage, attackerId);
|
||||
}
|
||||
|
||||
Debug.Log($"<color=green>[Player] {gameObject.name} 리스폰!</color>");
|
||||
}
|
||||
|
||||
[ClientRpc]
|
||||
private void ShowDamageEffectClientRpc()
|
||||
{
|
||||
if (damageEffectPrefab != null)
|
||||
[ServerRpc]
|
||||
private void TakeDamageServerRpc(int damage, ulong attackerId)
|
||||
{
|
||||
GameObject effect = Instantiate(damageEffectPrefab, transform.position + Vector3.up, Quaternion.identity);
|
||||
Destroy(effect, 2f);
|
||||
}
|
||||
}
|
||||
if (_currentHealth.Value <= 0) return;
|
||||
|
||||
[ClientRpc]
|
||||
private void ShowDeathEffectClientRpc()
|
||||
{
|
||||
if (deathEffectPrefab != null)
|
||||
if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(attackerId, out NetworkObject attackerObj))
|
||||
{
|
||||
var attackerTeamMember = attackerObj.GetComponent<ITeamMember>();
|
||||
if (attackerTeamMember != null)
|
||||
{
|
||||
if (!TeamManager.CanAttack(attackerTeamMember, this))
|
||||
{
|
||||
Debug.Log($"<color=yellow>[Player] {TeamManager.GetTeamName(attackerTeamMember.GetTeam())} 팀은 {TeamManager.GetTeamName(_team.Value)} 팀을 공격할 수 없습니다.</color>");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int actualDamage = Mathf.Min(damage, _currentHealth.Value);
|
||||
_currentHealth.Value -= actualDamage;
|
||||
|
||||
Debug.Log($"<color=red>[Player] {gameObject.name} ({TeamManager.GetTeamName(_team.Value)})이(가) {actualDamage} 데미지를 받았습니다. 남은 체력: {_currentHealth.Value}/{maxHealth}</color>");
|
||||
|
||||
ShowDamageEffectClientRpc();
|
||||
|
||||
if (_currentHealth.Value <= 0)
|
||||
{
|
||||
Die(attackerId);
|
||||
}
|
||||
}
|
||||
|
||||
private void Die(ulong killerId)
|
||||
{
|
||||
GameObject effect = Instantiate(deathEffectPrefab, transform.position, Quaternion.identity);
|
||||
Destroy(effect, 3f);
|
||||
Debug.Log($"<color=red>[Player] {gameObject.name} ({TeamManager.GetTeamName(_team.Value)})이(가) 사망했습니다! (킬러: {killerId})</color>");
|
||||
|
||||
ShowDeathEffectClientRpc();
|
||||
|
||||
if (_animator != null)
|
||||
{
|
||||
_animator.SetTrigger("Die");
|
||||
}
|
||||
|
||||
Invoke(nameof(HandleDeath), 3f);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Health Management
|
||||
|
||||
/// <summary>
|
||||
/// 현재 체력
|
||||
/// </summary>
|
||||
public int GetCurrentHealth() => _currentHealth.Value;
|
||||
|
||||
/// <summary>
|
||||
/// 최대 체력
|
||||
/// </summary>
|
||||
public int GetMaxHealth() => maxHealth;
|
||||
|
||||
/// <summary>
|
||||
/// 체력 비율 (0.0 ~ 1.0)
|
||||
/// </summary>
|
||||
public float GetHealthPercentage()
|
||||
{
|
||||
return maxHealth > 0 ? (float)_currentHealth.Value / maxHealth : 0f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 죽었는지 여부
|
||||
/// </summary>
|
||||
public bool IsDead() => _currentHealth.Value <= 0;
|
||||
|
||||
/// <summary>
|
||||
/// 체력 회복
|
||||
/// </summary>
|
||||
public void Heal(int amount)
|
||||
{
|
||||
if (!IsServer) return;
|
||||
|
||||
int healAmount = Mathf.Min(amount, maxHealth - _currentHealth.Value);
|
||||
_currentHealth.Value += healAmount;
|
||||
|
||||
Debug.Log($"<color=green>[Player] {gameObject.name}이(가) {healAmount} 회복되었습니다. 현재 체력: {_currentHealth.Value}/{maxHealth}</color>");
|
||||
}
|
||||
|
||||
private void OnHealthChanged(int previousValue, int newValue)
|
||||
{
|
||||
// 체력바 UI 업데이트 또는 체력 변경 시각 효과
|
||||
Debug.Log($"<color=yellow>[Player] 체력 변경: {previousValue} → {newValue}</color>");
|
||||
|
||||
// 클라이언트에서도 체력 변경 인지 가능
|
||||
if (IsOwner)
|
||||
private void HandleDeath()
|
||||
{
|
||||
// UI 업데이트 등
|
||||
RespawnServerRpc();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Gizmos
|
||||
|
||||
private void OnDrawGizmosSelected()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (Application.isPlaying)
|
||||
[ServerRpc]
|
||||
private void RespawnServerRpc()
|
||||
{
|
||||
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
|
||||
}
|
||||
_currentHealth.Value = maxHealth;
|
||||
|
||||
#endregion
|
||||
var spawnPoints = FindObjectsByType<PlayerSpawnPoint>(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($"<color=green>[Player] {gameObject.name} 리스폰!</color>");
|
||||
}
|
||||
|
||||
[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;
|
||||
|
||||
public float GetHealthPercentage()
|
||||
{
|
||||
return maxHealth > 0 ? (float)_currentHealth.Value / maxHealth : 0f;
|
||||
}
|
||||
|
||||
public bool IsDead() => _currentHealth.Value <= 0;
|
||||
|
||||
public void Heal(int amount)
|
||||
{
|
||||
if (!IsOwner)
|
||||
{
|
||||
HealServerRpc(amount);
|
||||
return;
|
||||
}
|
||||
|
||||
HealServerRpc(amount);
|
||||
}
|
||||
|
||||
[ServerRpc]
|
||||
private void HealServerRpc(int amount)
|
||||
{
|
||||
int healAmount = Mathf.Min(amount, maxHealth - _currentHealth.Value);
|
||||
_currentHealth.Value += healAmount;
|
||||
|
||||
Debug.Log($"<color=green>[Player] {gameObject.name}이(가) {healAmount} 회복되었습니다. 현재 체력: {_currentHealth.Value}/{maxHealth}</color>");
|
||||
}
|
||||
|
||||
private void OnHealthChanged(int previousValue, int newValue)
|
||||
{
|
||||
Debug.Log($"<color=yellow>[Player] 체력 변경: {previousValue} → {newValue}</color>");
|
||||
}
|
||||
|
||||
#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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9e8f7d6c5b4a3d2e1f0a9b8c7d6e5f4a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
guid: 19b1385c0dcb77240b2cfb3c6b10f717
|
||||
55
Assets/Scripts/NetworkSpawnHelper.cs
Normal file
55
Assets/Scripts/NetworkSpawnHelper.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Northbound
|
||||
{
|
||||
public static class NetworkSpawnHelper
|
||||
{
|
||||
public static void AddFogOfWarVisibility(GameObject obj, bool showInExploredAreas = true, float updateInterval = 0.2f)
|
||||
{
|
||||
if (obj.GetComponent<FogOfWarVisibility>() == null)
|
||||
{
|
||||
var visibility = obj.AddComponent<FogOfWarVisibility>();
|
||||
visibility.showInExploredAreas = showInExploredAreas;
|
||||
visibility.updateInterval = updateInterval;
|
||||
}
|
||||
}
|
||||
|
||||
public static void PreparePreviewForRendering(GameObject preview)
|
||||
{
|
||||
NetworkObject netObj = preview.GetComponent<NetworkObject>();
|
||||
if (netObj != null)
|
||||
{
|
||||
Object.DestroyImmediate(netObj);
|
||||
}
|
||||
|
||||
Building building = preview.GetComponent<Building>();
|
||||
if (building != null)
|
||||
{
|
||||
Object.DestroyImmediate(building);
|
||||
}
|
||||
}
|
||||
|
||||
public static void ApplyMaterialToPreview(GameObject preview, Material material)
|
||||
{
|
||||
Renderer[] renderers = preview.GetComponentsInChildren<Renderer>();
|
||||
foreach (var renderer in renderers)
|
||||
{
|
||||
Material[] mats = new Material[renderer.materials.Length];
|
||||
for (int i = 0; i < mats.Length; i++)
|
||||
{
|
||||
mats[i] = material;
|
||||
}
|
||||
renderer.materials = mats;
|
||||
}
|
||||
}
|
||||
|
||||
public static void DisableColliders(GameObject obj)
|
||||
{
|
||||
foreach (var collider in obj.GetComponentsInChildren<Collider>())
|
||||
{
|
||||
collider.enabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/NetworkSpawnHelper.cs.meta
Normal file
2
Assets/Scripts/NetworkSpawnHelper.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9b6c0b0cbea70604fac8231195b6b7b4
|
||||
@@ -16,9 +16,9 @@ namespace Northbound
|
||||
public List<Transform> spawnPoints = new List<Transform>();
|
||||
public bool useRandomSpawn = false;
|
||||
public bool findSpawnPointsAutomatically = true;
|
||||
|
||||
|
||||
private Dictionary<ulong, int> _clientSpawnIndices = new Dictionary<ulong, int>();
|
||||
private int _nextSpawnIndex = 0;
|
||||
private NetworkVariable<int> networkNextSpawnIndex = new NetworkVariable<int>(0);
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
@@ -100,8 +100,8 @@ namespace Northbound
|
||||
{
|
||||
if (!_clientSpawnIndices.ContainsKey(clientId))
|
||||
{
|
||||
_clientSpawnIndices[clientId] = _nextSpawnIndex;
|
||||
_nextSpawnIndex = (_nextSpawnIndex + 1) % spawnPoints.Count;
|
||||
_clientSpawnIndices[clientId] = networkNextSpawnIndex.Value;
|
||||
networkNextSpawnIndex.Value = (networkNextSpawnIndex.Value + 1) % spawnPoints.Count;
|
||||
}
|
||||
spawnIndex = _clientSpawnIndices[clientId];
|
||||
}
|
||||
|
||||
@@ -5,10 +5,7 @@ using System.Collections.Generic;
|
||||
|
||||
namespace Northbound
|
||||
{
|
||||
/// <summary>
|
||||
/// 상호작용 대상 없이 실행 가능한 액션들을 관리
|
||||
/// </summary>
|
||||
public class PlayerActionSystem : NetworkBehaviour
|
||||
public class PlayerActionSystem : InputActionManager
|
||||
{
|
||||
[Header("Actions")]
|
||||
public List<MonoBehaviour> actionComponents = new List<MonoBehaviour>();
|
||||
@@ -16,7 +13,6 @@ namespace Northbound
|
||||
[Header("Animation")]
|
||||
public bool playAnimations = true;
|
||||
|
||||
private PlayerInputActions _inputActions;
|
||||
private Dictionary<string, IAction> _actions = new Dictionary<string, IAction>();
|
||||
private Animator _animator;
|
||||
|
||||
@@ -27,9 +23,10 @@ namespace Northbound
|
||||
|
||||
public override void OnNetworkSpawn()
|
||||
{
|
||||
base.OnNetworkSpawn();
|
||||
|
||||
if (!IsOwner) return;
|
||||
|
||||
// 액션 컴포넌트들을 딕셔너리에 등록
|
||||
foreach (var component in actionComponents)
|
||||
{
|
||||
if (component is IAction action)
|
||||
@@ -37,21 +34,16 @@ namespace Northbound
|
||||
_actions[action.GetActionName()] = action;
|
||||
}
|
||||
}
|
||||
|
||||
_inputActions = new PlayerInputActions();
|
||||
_inputActions.Player.Attack.performed += OnAttack;
|
||||
// 다른 액션들도 여기에 바인딩
|
||||
_inputActions.Enable();
|
||||
}
|
||||
|
||||
public override void OnNetworkDespawn()
|
||||
protected override void BindInputActions()
|
||||
{
|
||||
if (IsOwner && _inputActions != null)
|
||||
{
|
||||
_inputActions.Player.Attack.performed -= OnAttack;
|
||||
_inputActions.Disable();
|
||||
_inputActions.Dispose();
|
||||
}
|
||||
_inputActions.Player.Attack.performed += OnAttack;
|
||||
}
|
||||
|
||||
protected override void UnbindInputActions()
|
||||
{
|
||||
_inputActions.Player.Attack.performed -= OnAttack;
|
||||
}
|
||||
|
||||
private void OnAttack(InputAction.CallbackContext context)
|
||||
@@ -65,7 +57,6 @@ namespace Northbound
|
||||
{
|
||||
if (action.CanExecute(OwnerClientId))
|
||||
{
|
||||
// 애니메이션 재생 (액션 실행 전)
|
||||
if (playAnimations && _animator != null)
|
||||
{
|
||||
string animTrigger = action.GetActionAnimation();
|
||||
@@ -79,15 +70,5 @@ namespace Northbound
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override public void OnDestroy()
|
||||
{
|
||||
if (_inputActions != null)
|
||||
{
|
||||
_inputActions.Dispose();
|
||||
}
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7a3e5b8c4d2f1a9e6b0c3d7e8f1a2b3c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
guid: 7a3e5b8c4d2f1a9e6b0c3d7e8f1a2b3c
|
||||
@@ -27,231 +27,170 @@ namespace Northbound
|
||||
|
||||
[Header("Debug")]
|
||||
public bool showDebugRay = true;
|
||||
|
||||
|
||||
private PlayerInputActions _inputActions;
|
||||
private IInteractable _currentInteractable;
|
||||
private Camera _mainCamera;
|
||||
private Animator _animator;
|
||||
private EquipmentSocket _equipmentSocket;
|
||||
|
||||
private EquipmentData _pendingEquipmentData;
|
||||
private string _currentEquipmentSocket;
|
||||
private bool _isInteracting = false;
|
||||
private bool _isInteracting;
|
||||
private float _interactionStartTime;
|
||||
private const float INTERACTION_TIMEOUT = 3f;
|
||||
|
||||
// 다른 컴포넌트가 이동 차단 여부를 확인할 수 있도록 public 프로퍼티 제공
|
||||
public bool IsInteracting => _isInteracting;
|
||||
|
||||
public override void OnNetworkSpawn()
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (!IsOwner) return;
|
||||
|
||||
_mainCamera = Camera.main;
|
||||
_animator = GetComponent<Animator>();
|
||||
_equipmentSocket = GetComponent<EquipmentSocket>();
|
||||
}
|
||||
|
||||
public override void OnNetworkSpawn()
|
||||
{
|
||||
base.OnNetworkSpawn();
|
||||
|
||||
if (rayOrigin == null)
|
||||
rayOrigin = transform;
|
||||
|
||||
|
||||
_inputActions = new PlayerInputActions();
|
||||
_inputActions.Player.Interact.performed += OnInteract;
|
||||
_inputActions.Enable();
|
||||
}
|
||||
|
||||
|
||||
public override void OnNetworkDespawn()
|
||||
{
|
||||
if (IsOwner && _inputActions != null)
|
||||
if (_inputActions != null)
|
||||
{
|
||||
_inputActions.Player.Interact.performed -= OnInteract;
|
||||
_inputActions.Disable();
|
||||
_inputActions.Dispose();
|
||||
}
|
||||
|
||||
base.OnNetworkDespawn();
|
||||
}
|
||||
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (!IsOwner) return;
|
||||
|
||||
if (_isInteracting && Time.time - _interactionStartTime > INTERACTION_TIMEOUT)
|
||||
{
|
||||
_isInteracting = false;
|
||||
}
|
||||
|
||||
DetectInteractable();
|
||||
}
|
||||
|
||||
|
||||
private void DetectInteractable()
|
||||
{
|
||||
Vector3 origin = rayOrigin.position;
|
||||
Vector3 direction = useForwardDirection ? transform.forward : _mainCamera.transform.forward;
|
||||
|
||||
Ray ray = new Ray(origin, direction);
|
||||
|
||||
|
||||
if (showDebugRay)
|
||||
{
|
||||
Debug.DrawRay(ray.origin, ray.direction * interactionRange,
|
||||
_currentInteractable != null ? Color.green : Color.yellow);
|
||||
}
|
||||
|
||||
|
||||
if (Physics.Raycast(ray, out RaycastHit hit, interactionRange, interactableLayer))
|
||||
{
|
||||
IInteractable interactable = hit.collider.GetComponent<IInteractable>();
|
||||
|
||||
if (interactable == null)
|
||||
{
|
||||
interactable = hit.collider.GetComponentInParent<IInteractable>();
|
||||
|
||||
}
|
||||
|
||||
if (interactable != null && interactable.CanInteract(OwnerClientId))
|
||||
{
|
||||
_currentInteractable = interactable;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
_currentInteractable = null;
|
||||
}
|
||||
|
||||
|
||||
private void OnInteract(InputAction.CallbackContext context)
|
||||
{
|
||||
if (blockDuringAnimation && _isInteracting)
|
||||
if (blockDuringAnimation && IsInteracting)
|
||||
return;
|
||||
|
||||
if (_currentInteractable == null)
|
||||
return;
|
||||
|
||||
if (_currentInteractable != null)
|
||||
var equipmentData = _currentInteractable.GetEquipmentData();
|
||||
string animTrigger = _currentInteractable.GetInteractionAnimation();
|
||||
bool hasAnimation = !string.IsNullOrEmpty(animTrigger);
|
||||
|
||||
if (playAnimations && _animator != null && hasAnimation)
|
||||
{
|
||||
_isInteracting = true;
|
||||
_pendingEquipmentData = _currentInteractable.GetEquipmentData();
|
||||
_interactionStartTime = Time.time;
|
||||
|
||||
string animTrigger = _currentInteractable.GetInteractionAnimation();
|
||||
bool hasAnimation = !string.IsNullOrEmpty(animTrigger);
|
||||
|
||||
// 장비 장착 (애니메이션 이벤트 사용 안 할 경우)
|
||||
if (!useAnimationEvents && useEquipment && _equipmentSocket != null && _pendingEquipmentData != null)
|
||||
{
|
||||
if (_pendingEquipmentData.attachOnStart && _pendingEquipmentData.equipmentPrefab != null)
|
||||
{
|
||||
AttachEquipment();
|
||||
|
||||
if (_pendingEquipmentData.detachOnEnd)
|
||||
{
|
||||
StartCoroutine(DetachEquipmentAfterDelay(2f));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 애니메이션 재생
|
||||
if (playAnimations && _animator != null && hasAnimation)
|
||||
if (useAnimationEvents)
|
||||
{
|
||||
_animator.SetTrigger(animTrigger);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 애니메이션이 없으면 즉시 상호작용 완료
|
||||
_animator.SetTrigger(animTrigger);
|
||||
_currentInteractable.Interact(OwnerClientId);
|
||||
_isInteracting = false;
|
||||
}
|
||||
|
||||
// 상호작용 실행 (서버에서 처리)
|
||||
}
|
||||
else
|
||||
{
|
||||
_currentInteractable.Interact(OwnerClientId);
|
||||
}
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// Animation Event 함수들
|
||||
// ========================================
|
||||
|
||||
public void OnEquipTool()
|
||||
|
||||
public void AttachEquipment()
|
||||
{
|
||||
if (!useAnimationEvents || !useEquipment) return;
|
||||
AttachEquipment();
|
||||
}
|
||||
if (!IsOwner) return;
|
||||
if (_currentInteractable == null || !useEquipment) return;
|
||||
|
||||
public void OnEquipTool(string socketName)
|
||||
var equipmentData = _currentInteractable.GetEquipmentData();
|
||||
if (equipmentData == null || !equipmentData.attachOnStart) return;
|
||||
|
||||
if (_equipmentSocket == null)
|
||||
{
|
||||
Debug.LogWarning("[PlayerInteraction] EquipmentSocket component not found on player");
|
||||
return;
|
||||
}
|
||||
|
||||
if (equipmentData.equipmentPrefab == null)
|
||||
{
|
||||
Debug.LogWarning("[PlayerInteraction] Equipment prefab is null. Assign a prefab in the Resource's EquipmentData in Inspector");
|
||||
return;
|
||||
}
|
||||
|
||||
_equipmentSocket.AttachToSocket(equipmentData.socketName, equipmentData.equipmentPrefab);
|
||||
}
|
||||
|
||||
public void DetachEquipment()
|
||||
{
|
||||
if (!useAnimationEvents || !useEquipment) return;
|
||||
AttachEquipment(socketName);
|
||||
}
|
||||
if (!IsOwner) return;
|
||||
if (_currentInteractable == null || !useEquipment) return;
|
||||
|
||||
public void OnUnequipTool()
|
||||
{
|
||||
if (!useAnimationEvents || !useEquipment) return;
|
||||
DetachEquipment();
|
||||
}
|
||||
var equipmentData = _currentInteractable.GetEquipmentData();
|
||||
if (equipmentData == null || !equipmentData.detachOnEnd) return;
|
||||
|
||||
public void OnUnequipTool(string socketName)
|
||||
{
|
||||
if (!useAnimationEvents || !useEquipment) return;
|
||||
DetachEquipment(socketName);
|
||||
}
|
||||
if (_equipmentSocket == null) return;
|
||||
|
||||
_equipmentSocket.DetachFromSocket(equipmentData.socketName);
|
||||
}
|
||||
|
||||
public void OnInteractionComplete()
|
||||
{
|
||||
_isInteracting = false;
|
||||
Debug.Log("[PlayerInteraction] 상호작용 완료");
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// 내부 헬퍼 함수들
|
||||
// ========================================
|
||||
|
||||
private void AttachEquipment(string socketName = null)
|
||||
{
|
||||
if (_equipmentSocket == null || _pendingEquipmentData == null)
|
||||
return;
|
||||
|
||||
if (_pendingEquipmentData.equipmentPrefab == null)
|
||||
return;
|
||||
|
||||
string socket = socketName ?? _pendingEquipmentData.socketName;
|
||||
_equipmentSocket.AttachToSocket(socket, _pendingEquipmentData.equipmentPrefab);
|
||||
_currentEquipmentSocket = socket;
|
||||
}
|
||||
|
||||
private void DetachEquipment(string socketName = null)
|
||||
{
|
||||
if (_equipmentSocket == null)
|
||||
return;
|
||||
|
||||
string socket = socketName ?? _currentEquipmentSocket;
|
||||
|
||||
if (!string.IsNullOrEmpty(socket))
|
||||
if (_currentInteractable != null)
|
||||
{
|
||||
_equipmentSocket.DetachFromSocket(socket);
|
||||
|
||||
if (socket == _currentEquipmentSocket)
|
||||
_currentEquipmentSocket = null;
|
||||
}
|
||||
}
|
||||
|
||||
private System.Collections.IEnumerator DetachEquipmentAfterDelay(float delay)
|
||||
{
|
||||
yield return new WaitForSeconds(delay);
|
||||
DetachEquipment();
|
||||
|
||||
if (!useAnimationEvents)
|
||||
{
|
||||
_isInteracting = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
if (!IsOwner || _currentInteractable == null) return;
|
||||
|
||||
GUIStyle style = new GUIStyle(GUI.skin.label)
|
||||
{
|
||||
fontSize = 24,
|
||||
alignment = TextAnchor.MiddleCenter
|
||||
};
|
||||
style.normal.textColor = Color.white;
|
||||
|
||||
string prompt = _currentInteractable.GetInteractionPrompt();
|
||||
|
||||
if (_isInteracting)
|
||||
{
|
||||
prompt += " (진행 중...)";
|
||||
style.normal.textColor = Color.yellow;
|
||||
}
|
||||
|
||||
GUI.Label(new Rect(Screen.width / 2 - 200, Screen.height - 100, 400, 50), prompt, style);
|
||||
}
|
||||
|
||||
public override void OnDestroy()
|
||||
{
|
||||
if (_inputActions != null)
|
||||
{
|
||||
_inputActions.Dispose();
|
||||
_currentInteractable.Interact(OwnerClientId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
73
Assets/Scripts/PlayerInventory.cs
Normal file
73
Assets/Scripts/PlayerInventory.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Northbound
|
||||
{
|
||||
public class PlayerInventory : NetworkBehaviour
|
||||
{
|
||||
[Header("Inventory Settings")]
|
||||
public int maxResourceCapacity = 100;
|
||||
|
||||
private NetworkVariable<int> resourceCount = new NetworkVariable<int>(
|
||||
0,
|
||||
NetworkVariableReadPermission.Everyone,
|
||||
NetworkVariableWritePermission.Owner
|
||||
);
|
||||
|
||||
public int CurrentResourceAmount => resourceCount.Value;
|
||||
public int MaxResourceCapacity => maxResourceCapacity;
|
||||
|
||||
public bool CanAddResource(int amount)
|
||||
{
|
||||
return resourceCount.Value + amount <= maxResourceCapacity;
|
||||
}
|
||||
|
||||
public int GetAvailableSpace()
|
||||
{
|
||||
return maxResourceCapacity - resourceCount.Value;
|
||||
}
|
||||
|
||||
public void AddResources(int amount)
|
||||
{
|
||||
if (amount <= 0) return;
|
||||
|
||||
int actualAmount = Mathf.Min(amount, maxResourceCapacity - resourceCount.Value);
|
||||
resourceCount.Value += actualAmount;
|
||||
|
||||
Debug.Log($"Player {OwnerClientId} added resources: +{actualAmount}, total: {resourceCount.Value}/{maxResourceCapacity}");
|
||||
}
|
||||
|
||||
public void RemoveResources(int amount)
|
||||
{
|
||||
if (amount <= 0) return;
|
||||
|
||||
int actualAmount = Mathf.Min(amount, resourceCount.Value);
|
||||
resourceCount.Value -= actualAmount;
|
||||
|
||||
Debug.Log($"Player {OwnerClientId} used resources: -{actualAmount}, total: {resourceCount.Value}/{maxResourceCapacity}");
|
||||
}
|
||||
|
||||
public void SetResources(int amount)
|
||||
{
|
||||
resourceCount.Value = Mathf.Clamp(amount, 0, maxResourceCapacity);
|
||||
}
|
||||
|
||||
[Rpc(SendTo.Owner)]
|
||||
public void AddResourcesRpc(int amount)
|
||||
{
|
||||
AddResources(amount);
|
||||
}
|
||||
|
||||
[Rpc(SendTo.Owner)]
|
||||
public void RemoveResourcesRpc(int amount)
|
||||
{
|
||||
RemoveResources(amount);
|
||||
}
|
||||
|
||||
[Rpc(SendTo.Owner)]
|
||||
public void SetResourcesRpc(int amount)
|
||||
{
|
||||
SetResources(amount);
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/PlayerInventory.cs.meta
Normal file
2
Assets/Scripts/PlayerInventory.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 90231c209cbef84469511de397004be9
|
||||
@@ -1,76 +0,0 @@
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Northbound
|
||||
{
|
||||
/// <summary>
|
||||
/// 플레이어의 자원 인벤토리 관리
|
||||
/// </summary>
|
||||
public class PlayerResourceInventory : NetworkBehaviour
|
||||
{
|
||||
[Header("Inventory Settings")]
|
||||
public int maxResourceCapacity = 100; // 최대 자원 보유량
|
||||
|
||||
private NetworkVariable<int> _currentResourceAmount = new NetworkVariable<int>(
|
||||
0,
|
||||
NetworkVariableReadPermission.Everyone,
|
||||
NetworkVariableWritePermission.Server
|
||||
);
|
||||
|
||||
public int CurrentResourceAmount => _currentResourceAmount.Value;
|
||||
public int MaxResourceCapacity => maxResourceCapacity;
|
||||
|
||||
/// <summary>
|
||||
/// 자원을 추가할 수 있는지 확인
|
||||
/// </summary>
|
||||
public bool CanAddResource(int amount)
|
||||
{
|
||||
return _currentResourceAmount.Value + amount <= maxResourceCapacity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 추가 가능한 최대 자원량 계산
|
||||
/// </summary>
|
||||
public int GetAvailableSpace()
|
||||
{
|
||||
return maxResourceCapacity - _currentResourceAmount.Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 자원 추가 (서버에서만 호출)
|
||||
/// </summary>
|
||||
[Rpc(SendTo.Server, InvokePermission = RpcInvokePermission.Everyone)]
|
||||
public void AddResourceServerRpc(int amount)
|
||||
{
|
||||
if (amount <= 0) return;
|
||||
|
||||
int actualAmount = Mathf.Min(amount, maxResourceCapacity - _currentResourceAmount.Value);
|
||||
_currentResourceAmount.Value += actualAmount;
|
||||
|
||||
Debug.Log($"플레이어 {OwnerClientId} - 자원 추가: +{actualAmount}, 현재: {_currentResourceAmount.Value}/{maxResourceCapacity}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 자원 제거 (서버에서만 호출)
|
||||
/// </summary>
|
||||
[Rpc(SendTo.Server, InvokePermission = RpcInvokePermission.Everyone)]
|
||||
public void RemoveResourceServerRpc(int amount)
|
||||
{
|
||||
if (amount <= 0) return;
|
||||
|
||||
int actualAmount = Mathf.Min(amount, _currentResourceAmount.Value);
|
||||
_currentResourceAmount.Value -= actualAmount;
|
||||
|
||||
Debug.Log($"플레이어 {OwnerClientId} - 자원 사용: -{actualAmount}, 현재: {_currentResourceAmount.Value}/{maxResourceCapacity}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 자원 설정 (서버에서만 호출)
|
||||
/// </summary>
|
||||
[Rpc(SendTo.Server, InvokePermission = RpcInvokePermission.Everyone)]
|
||||
public void SetResourceServerRpc(int amount)
|
||||
{
|
||||
_currentResourceAmount.Value = Mathf.Clamp(amount, 0, maxResourceCapacity);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3c64072402b0a3f46a674eb73c5541ac
|
||||
103
Assets/Scripts/PlayerSpawnPositionSetter.cs
Normal file
103
Assets/Scripts/PlayerSpawnPositionSetter.cs
Normal file
@@ -0,0 +1,103 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Northbound
|
||||
{
|
||||
public class PlayerSpawnPositionSetter : NetworkBehaviour
|
||||
{
|
||||
public static PlayerSpawnPositionSetter Instance { get; private set; }
|
||||
|
||||
[Header("Spawn Settings")]
|
||||
public List<Transform> spawnPoints = new List<Transform>();
|
||||
public bool useRandomSpawn = false;
|
||||
public bool findSpawnPointsAutomatically = true;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (Instance != null && Instance != this)
|
||||
{
|
||||
Destroy(gameObject);
|
||||
return;
|
||||
}
|
||||
Instance = this;
|
||||
|
||||
if (findSpawnPointsAutomatically)
|
||||
{
|
||||
FindSpawnPoints();
|
||||
}
|
||||
}
|
||||
|
||||
private void FindSpawnPoints()
|
||||
{
|
||||
PlayerSpawnPoint[] points = FindObjectsByType<PlayerSpawnPoint>(FindObjectsSortMode.None);
|
||||
var sortedPoints = points.OrderBy(p => p.spawnIndex == -1 ? int.MaxValue : p.spawnIndex);
|
||||
|
||||
spawnPoints.Clear();
|
||||
foreach (var point in sortedPoints)
|
||||
{
|
||||
if (point.isAvailable)
|
||||
{
|
||||
spawnPoints.Add(point.transform);
|
||||
}
|
||||
}
|
||||
|
||||
Debug.Log($"<color=cyan>[SpawnPositionSetter] {spawnPoints.Count}개의 스폰 포인트를 찾았습니다.</color>");
|
||||
}
|
||||
|
||||
public Vector3 GetSpawnPosition(ulong clientId)
|
||||
{
|
||||
if (spawnPoints.Count == 0)
|
||||
{
|
||||
Debug.LogWarning("[SpawnPositionSetter] 스폰 포인트가 없습니다. 기본 위치 반환.");
|
||||
return Vector3.zero;
|
||||
}
|
||||
|
||||
int spawnIndex = GetSpawnIndexForClient(clientId);
|
||||
|
||||
Debug.Log($"<color=yellow>[SpawnPositionSetter] 클라이언트 {clientId}에게 스폰 인덱스 {spawnIndex} 할당</color>");
|
||||
return spawnPoints[spawnIndex].position;
|
||||
}
|
||||
|
||||
private int GetSpawnIndexForClient(ulong clientId)
|
||||
{
|
||||
if (useRandomSpawn)
|
||||
{
|
||||
return Random.Range(0, spawnPoints.Count);
|
||||
}
|
||||
|
||||
int spawnIndex;
|
||||
if (IsServer)
|
||||
{
|
||||
spawnIndex = GetAssignedSpawnIndexServer(clientId);
|
||||
}
|
||||
else
|
||||
{
|
||||
spawnIndex = (int)(clientId % (ulong)spawnPoints.Count);
|
||||
}
|
||||
|
||||
return spawnIndex;
|
||||
}
|
||||
|
||||
private int GetAssignedSpawnIndexServer(ulong clientId)
|
||||
{
|
||||
List<ulong> connectedClientIds = new List<ulong>(NetworkManager.Singleton.ConnectedClientsIds);
|
||||
connectedClientIds.Sort();
|
||||
|
||||
int index = connectedClientIds.IndexOf(clientId);
|
||||
if (index < 0) index = 0;
|
||||
|
||||
return index % spawnPoints.Count;
|
||||
}
|
||||
|
||||
public Quaternion GetSpawnRotation(ulong clientId)
|
||||
{
|
||||
if (spawnPoints.Count == 0)
|
||||
return Quaternion.identity;
|
||||
|
||||
int spawnIndex = GetSpawnIndexForClient(clientId);
|
||||
return spawnPoints[spawnIndex].rotation;
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/PlayerSpawnPositionSetter.cs.meta
Normal file
2
Assets/Scripts/PlayerSpawnPositionSetter.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 49cd9c4e7c611b04c8740c9e049129b9
|
||||
@@ -45,7 +45,7 @@ namespace Northbound
|
||||
|
||||
public override void OnNetworkSpawn()
|
||||
{
|
||||
if (IsServer)
|
||||
if (IsOwner)
|
||||
{
|
||||
_currentResources.Value = maxResources;
|
||||
_lastRechargeTime = Time.time;
|
||||
@@ -54,7 +54,7 @@ namespace Northbound
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (!IsServer)
|
||||
if (!IsOwner)
|
||||
return;
|
||||
|
||||
// 자원 충전 로직
|
||||
@@ -88,7 +88,7 @@ namespace Northbound
|
||||
{
|
||||
if (client.PlayerObject != null)
|
||||
{
|
||||
var playerInventory = client.PlayerObject.GetComponent<PlayerResourceInventory>();
|
||||
var playerInventory = client.PlayerObject.GetComponent<PlayerInventory>();
|
||||
if (playerInventory != null)
|
||||
{
|
||||
// 플레이어가 받을 수 있는 공간이 없으면 상호작용 불가
|
||||
@@ -120,10 +120,10 @@ namespace Northbound
|
||||
if (playerObject == null)
|
||||
return;
|
||||
|
||||
var playerInventory = playerObject.GetComponent<PlayerResourceInventory>();
|
||||
var playerInventory = playerObject.GetComponent<PlayerInventory>();
|
||||
if (playerInventory == null)
|
||||
{
|
||||
Debug.LogWarning($"플레이어 {playerId}에게 PlayerResourceInventory 컴포넌트가 없습니다.");
|
||||
Debug.LogWarning($"플레이어 {playerId}에게 PlayerInventory 컴포넌트가 없습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -147,8 +147,8 @@ namespace Northbound
|
||||
_currentResources.Value -= gatheredAmount;
|
||||
_lastGatheringTime = Time.time;
|
||||
|
||||
// 플레이어에게 자원 추가
|
||||
playerInventory.AddResourceServerRpc(gatheredAmount);
|
||||
// 플레이어에게 자원 추가 (RPC로 owner에게 요청)
|
||||
playerInventory.AddResourcesRpc(gatheredAmount);
|
||||
|
||||
Debug.Log($"플레이어 {playerId}가 {gatheredAmount} {resourceName}을(를) 채집했습니다. 남은 자원: {_currentResources.Value}");
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace Northbound
|
||||
{
|
||||
if (client.PlayerObject != null)
|
||||
{
|
||||
var playerInventory = client.PlayerObject.GetComponent<PlayerResourceInventory>();
|
||||
var playerInventory = client.PlayerObject.GetComponent<PlayerInventory>();
|
||||
if (playerInventory != null)
|
||||
{
|
||||
// 플레이어가 받을 수 있는 공간이 없으면 상호작용 불가
|
||||
@@ -80,10 +80,10 @@ namespace Northbound
|
||||
if (playerObject == null)
|
||||
return;
|
||||
|
||||
var playerInventory = playerObject.GetComponent<PlayerResourceInventory>();
|
||||
var playerInventory = playerObject.GetComponent<PlayerInventory>();
|
||||
if (playerInventory == null)
|
||||
{
|
||||
Debug.LogWarning($"플레이어 {playerId}에게 PlayerResourceInventory 컴포넌트가 없습니다.");
|
||||
Debug.LogWarning($"플레이어 {playerId}에게 PlayerInventory 컴포넌트가 없습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -100,8 +100,8 @@ namespace Northbound
|
||||
return;
|
||||
}
|
||||
|
||||
// 플레이어에게 자원 추가
|
||||
playerInventory.AddResourceServerRpc(collectedAmount);
|
||||
// 플레이어에게 자원 추가 (RPC로 owner에게 요청)
|
||||
playerInventory.AddResourcesRpc(collectedAmount);
|
||||
|
||||
Debug.Log($"플레이어 {playerId}가 {collectedAmount} {resourceName}을(를) 획득했습니다.");
|
||||
|
||||
|
||||
52
Assets/Scripts/SmartAutoHost.cs
Normal file
52
Assets/Scripts/SmartAutoHost.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
using UnityEngine;
|
||||
using Unity.Netcode;
|
||||
|
||||
public class SmartAutoHost : MonoBehaviour
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
private void Start()
|
||||
{
|
||||
if (NetworkManager.Singleton == null)
|
||||
{
|
||||
Debug.LogError("[SmartAutoHost] NetworkManager not found!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (NetworkManager.Singleton.IsServer || NetworkManager.Singleton.IsClient)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool isMainEditor = IsMainEditor();
|
||||
|
||||
if (isMainEditor)
|
||||
{
|
||||
NetworkManager.Singleton.StartHost();
|
||||
Debug.Log("<color=yellow><b>[SmartAutoHost]</b> MAIN EDITOR → Starting as HOST</color>");
|
||||
}
|
||||
else
|
||||
{
|
||||
NetworkManager.Singleton.StartClient();
|
||||
Debug.Log("<color=blue><b>[SmartAutoHost]</b> SECONDARY EDITOR → Connecting as CLIENT</color>");
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsMainEditor()
|
||||
{
|
||||
string[] args = System.Environment.GetCommandLineArgs();
|
||||
return System.Array.Exists(args, arg => arg == "-mainEditor");
|
||||
}
|
||||
#else
|
||||
private void Start()
|
||||
{
|
||||
// 빌드된 버전은 항상 클라이언트
|
||||
if (NetworkManager.Singleton != null &&
|
||||
!NetworkManager.Singleton.IsServer &&
|
||||
!NetworkManager.Singleton.IsClient)
|
||||
{
|
||||
NetworkManager.Singleton.StartClient();
|
||||
Debug.Log("<color=blue>[SmartAutoHost] Build → Connecting as CLIENT</color>");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
2
Assets/Scripts/SmartAutoHost.cs.meta
Normal file
2
Assets/Scripts/SmartAutoHost.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 13b6e5c1ebfd6fb4d939d2e99b25fffc
|
||||
@@ -97,7 +97,7 @@ namespace Northbound
|
||||
private void OnTriggerEnter(Collider other)
|
||||
{
|
||||
// 서버에서만 텔레포트 처리
|
||||
if (!IsServer) return;
|
||||
if (!IsOwner) return;
|
||||
|
||||
// 어느 트리거에 진입했는지 확인
|
||||
Collider triggeredCollider = null;
|
||||
|
||||
62
Assets/Scripts/TeamMemberNetworkBehaviour.cs
Normal file
62
Assets/Scripts/TeamMemberNetworkBehaviour.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Northbound
|
||||
{
|
||||
public abstract class TeamMemberNetworkBehaviour : NetworkBehaviour, ITeamMember
|
||||
{
|
||||
[SerializeField] protected TeamType initialTeam = TeamType.Player;
|
||||
|
||||
protected NetworkVariable<TeamType> _team = new NetworkVariable<TeamType>(
|
||||
TeamType.Neutral,
|
||||
NetworkVariableReadPermission.Everyone,
|
||||
NetworkVariableWritePermission.Server
|
||||
);
|
||||
|
||||
public event System.Action<TeamType> OnTeamChanged;
|
||||
|
||||
public override void OnNetworkSpawn()
|
||||
{
|
||||
base.OnNetworkSpawn();
|
||||
|
||||
if (IsOwner)
|
||||
{
|
||||
if (_team.Value == TeamType.Neutral)
|
||||
{
|
||||
_team.Value = initialTeam;
|
||||
}
|
||||
}
|
||||
|
||||
_team.OnValueChanged += OnTeamValueChanged;
|
||||
UpdateTeamVisuals();
|
||||
}
|
||||
|
||||
public override void OnNetworkDespawn()
|
||||
{
|
||||
_team.OnValueChanged -= OnTeamValueChanged;
|
||||
base.OnNetworkDespawn();
|
||||
}
|
||||
|
||||
public TeamType GetTeam() => _team.Value;
|
||||
|
||||
public void SetTeam(TeamType team)
|
||||
{
|
||||
if (!IsOwner) return;
|
||||
|
||||
TeamType previousTeam = _team.Value;
|
||||
_team.Value = team;
|
||||
|
||||
Debug.Log($"<color=cyan>[{GetType().Name}] {gameObject.name} team changed: {TeamManager.GetTeamName(previousTeam)} → {TeamManager.GetTeamName(team)}</color>");
|
||||
}
|
||||
|
||||
protected virtual void OnTeamValueChanged(TeamType previousValue, TeamType newValue)
|
||||
{
|
||||
OnTeamChanged?.Invoke(newValue);
|
||||
UpdateTeamVisuals();
|
||||
}
|
||||
|
||||
protected virtual void UpdateTeamVisuals()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/TeamMemberNetworkBehaviour.cs.meta
Normal file
2
Assets/Scripts/TeamMemberNetworkBehaviour.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 25aae83d83e1df146ad1b22af79848bb
|
||||
36
Assets/UserChoices.choices
Normal file
36
Assets/UserChoices.choices
Normal file
@@ -0,0 +1,36 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &1
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 53
|
||||
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: 18cde282a8d045bf9d245fdcfaa7271b, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Unity.Multiplayer.Center.Editor::Unity.Multiplayer.Center.Questionnaire.UserChoicesObject
|
||||
QuestionnaireVersion: 1.3
|
||||
UserAnswers:
|
||||
Answers:
|
||||
- QuestionId: Pace
|
||||
Answers:
|
||||
- Fast
|
||||
- QuestionId: Cheating
|
||||
Answers:
|
||||
- CheatingNotImportant
|
||||
- QuestionId: CostSensitivity
|
||||
Answers:
|
||||
- BestMargin
|
||||
- QuestionId: NetcodeArchitecture
|
||||
Answers:
|
||||
- ClientServer
|
||||
- QuestionId: PlayerCount
|
||||
Answers:
|
||||
- 4
|
||||
Preset: 11
|
||||
SelectedSolutions:
|
||||
SelectedHostingModel: 4
|
||||
SelectedNetcodeSolution: 1
|
||||
7
Assets/UserChoices.choices.meta
Normal file
7
Assets/UserChoices.choices.meta
Normal file
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 624b6b15c0f5b4c4d912ee2e880c8581
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -43,7 +43,6 @@
|
||||
<UnityVersion>6000.3.5f2</UnityVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Analyzer Include="C:\Program Files\Microsoft Visual Studio\18\Community\Common7\IDE\Extensions\Microsoft\Visual Studio Tools for Unity\Analyzers\Microsoft.Unity.Analyzers.dll" />
|
||||
<Analyzer Include="C:\Program Files\Unity\Hub\Editor\6000.3.5f2\Editor\Data\Tools\BuildPipeline\Unity.SourceGenerators\Unity.SourceGenerators.dll" />
|
||||
<Analyzer Include="C:\Program Files\Unity\Hub\Editor\6000.3.5f2\Editor\Data\Tools\BuildPipeline\Unity.SourceGenerators\Unity.Properties.SourceGenerator.dll" />
|
||||
<Analyzer Include="C:\Program Files\Unity\Hub\Editor\6000.3.5f2\Editor\Data\Tools\BuildPipeline\Unity.SourceGenerators\Unity.UIToolkit.SourceGenerator.dll" />
|
||||
@@ -631,6 +630,10 @@
|
||||
<HintPath>C:\Program Files\Unity\Hub\Editor\6000.3.5f2\Editor\Data\PlaybackEngines\WindowsStandaloneSupport\UnityEditor.WindowsStandalone.Extensions.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="websocket-sharp">
|
||||
<HintPath>Library\PackageCache\com.unity.services.wire@9a73acde80cc\Plugins\websocket-sharp.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.VisualScripting.YamlDotNet">
|
||||
<HintPath>Library\PackageCache\com.unity.visualscripting@191c0d7e3b69\Editor\VisualScripting.Core\Dependencies\YamlDotNet\Unity.VisualScripting.YamlDotNet.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
|
||||
@@ -43,7 +43,6 @@
|
||||
<UnityVersion>6000.3.5f2</UnityVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Analyzer Include="C:\Program Files\Microsoft Visual Studio\18\Community\Common7\IDE\Extensions\Microsoft\Visual Studio Tools for Unity\Analyzers\Microsoft.Unity.Analyzers.dll" />
|
||||
<Analyzer Include="C:\Program Files\Unity\Hub\Editor\6000.3.5f2\Editor\Data\Tools\BuildPipeline\Unity.SourceGenerators\Unity.SourceGenerators.dll" />
|
||||
<Analyzer Include="C:\Program Files\Unity\Hub\Editor\6000.3.5f2\Editor\Data\Tools\BuildPipeline\Unity.SourceGenerators\Unity.Properties.SourceGenerator.dll" />
|
||||
<Analyzer Include="C:\Program Files\Unity\Hub\Editor\6000.3.5f2\Editor\Data\Tools\BuildPipeline\Unity.SourceGenerators\Unity.UIToolkit.SourceGenerator.dll" />
|
||||
@@ -614,6 +613,10 @@
|
||||
<HintPath>C:\Program Files\Unity\Hub\Editor\6000.3.5f2\Editor\Data\PlaybackEngines\WindowsStandaloneSupport\UnityEditor.WindowsStandalone.Extensions.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="websocket-sharp">
|
||||
<HintPath>Library\PackageCache\com.unity.services.wire@9a73acde80cc\Plugins\websocket-sharp.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.VisualScripting.YamlDotNet">
|
||||
<HintPath>Library\PackageCache\com.unity.visualscripting@191c0d7e3b69\Editor\VisualScripting.Core\Dependencies\YamlDotNet\Unity.VisualScripting.YamlDotNet.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
|
||||
44
Northbound.sln
Normal file
44
Northbound.sln
Normal file
@@ -0,0 +1,44 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Assembly-CSharp", "Assembly-CSharp.csproj", "{5C483496-7666-CA70-B23E-AE514115EE94}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Unity.RenderPipelines.Universal.Runtime", "Unity.RenderPipelines.Universal.Runtime.csproj", "{7D4DAA2B-AB80-6E9C-56F6-9BBE627B864B}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FlatKit.Utils.Editor", "FlatKit.Utils.Editor.csproj", "{9FFF03DB-13D2-F002-5CE2-72F68DD2B255}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExternAttributes.Editor", "ExternAttributes.Editor.csproj", "{2EE5E6D5-274E-E52C-7FC1-8ABD29E99E2F}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Assembly-CSharp-Editor", "Assembly-CSharp-Editor.csproj", "{EF726A86-D8F8-62E9-98E4-841FF7F5D94D}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{5C483496-7666-CA70-B23E-AE514115EE94}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{5C483496-7666-CA70-B23E-AE514115EE94}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{5C483496-7666-CA70-B23E-AE514115EE94}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{5C483496-7666-CA70-B23E-AE514115EE94}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{7D4DAA2B-AB80-6E9C-56F6-9BBE627B864B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{7D4DAA2B-AB80-6E9C-56F6-9BBE627B864B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7D4DAA2B-AB80-6E9C-56F6-9BBE627B864B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7D4DAA2B-AB80-6E9C-56F6-9BBE627B864B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{9FFF03DB-13D2-F002-5CE2-72F68DD2B255}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9FFF03DB-13D2-F002-5CE2-72F68DD2B255}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9FFF03DB-13D2-F002-5CE2-72F68DD2B255}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{9FFF03DB-13D2-F002-5CE2-72F68DD2B255}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{2EE5E6D5-274E-E52C-7FC1-8ABD29E99E2F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{2EE5E6D5-274E-E52C-7FC1-8ABD29E99E2F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{2EE5E6D5-274E-E52C-7FC1-8ABD29E99E2F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{2EE5E6D5-274E-E52C-7FC1-8ABD29E99E2F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{EF726A86-D8F8-62E9-98E4-841FF7F5D94D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{EF726A86-D8F8-62E9-98E4-841FF7F5D94D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{EF726A86-D8F8-62E9-98E4-841FF7F5D94D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{EF726A86-D8F8-62E9-98E4-841FF7F5D94D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"com.boxqkrtm.ide.cursor": "https://github.com/boxqkrtm/com.unity.ide.cursor.git",
|
||||
"com.unity.2d.animation": "13.0.4",
|
||||
"com.unity.2d.aseprite": "3.0.1",
|
||||
"com.unity.2d.psdimporter": "12.0.1",
|
||||
@@ -15,9 +16,12 @@
|
||||
"com.unity.ide.visualstudio": "2.0.26",
|
||||
"com.unity.inputsystem": "1.17.0",
|
||||
"com.unity.multiplayer.center": "1.0.1",
|
||||
"com.unity.multiplayer.center.quickstart": "1.1.1",
|
||||
"com.unity.multiplayer.playmode": "2.0.1",
|
||||
"com.unity.multiplayer.tools": "2.2.7",
|
||||
"com.unity.netcode.gameobjects": "2.8.0",
|
||||
"com.unity.netcode.gameobjects": "2.8.1",
|
||||
"com.unity.render-pipelines.universal": "17.3.0",
|
||||
"com.unity.services.multiplayer": "2.1.0",
|
||||
"com.unity.test-framework": "1.6.0",
|
||||
"com.unity.timeline": "1.8.10",
|
||||
"com.unity.ugui": "2.0.0",
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"com.boxqkrtm.ide.cursor": {
|
||||
"version": "https://github.com/boxqkrtm/com.unity.ide.cursor.git",
|
||||
"depth": 0,
|
||||
"source": "git",
|
||||
"dependencies": {
|
||||
"com.unity.test-framework": "1.1.9"
|
||||
},
|
||||
"hash": "53e1baac3cde90c46934f82ee1d61802d2bd6651"
|
||||
},
|
||||
"com.unity.2d.animation": {
|
||||
"version": "13.0.4",
|
||||
"depth": 0,
|
||||
@@ -196,6 +205,25 @@
|
||||
"com.unity.modules.uielements": "1.0.0"
|
||||
}
|
||||
},
|
||||
"com.unity.multiplayer.center.quickstart": {
|
||||
"version": "1.1.1",
|
||||
"depth": 0,
|
||||
"source": "registry",
|
||||
"dependencies": {
|
||||
"com.unity.modules.uielements": "1.0.0",
|
||||
"com.unity.multiplayer.center": "1.0.0"
|
||||
},
|
||||
"url": "https://packages.unity.com"
|
||||
},
|
||||
"com.unity.multiplayer.playmode": {
|
||||
"version": "2.0.1",
|
||||
"depth": 0,
|
||||
"source": "registry",
|
||||
"dependencies": {
|
||||
"com.unity.nuget.newtonsoft-json": "2.0.2"
|
||||
},
|
||||
"url": "https://packages.unity.com"
|
||||
},
|
||||
"com.unity.multiplayer.tools": {
|
||||
"version": "2.2.7",
|
||||
"depth": 0,
|
||||
@@ -212,7 +240,7 @@
|
||||
"url": "https://packages.unity.com"
|
||||
},
|
||||
"com.unity.netcode.gameobjects": {
|
||||
"version": "2.8.0",
|
||||
"version": "2.8.1",
|
||||
"depth": 0,
|
||||
"source": "registry",
|
||||
"dependencies": {
|
||||
@@ -281,6 +309,87 @@
|
||||
"dependencies": {},
|
||||
"url": "https://packages.unity.com"
|
||||
},
|
||||
"com.unity.services.authentication": {
|
||||
"version": "3.6.0",
|
||||
"depth": 1,
|
||||
"source": "registry",
|
||||
"dependencies": {
|
||||
"com.unity.ugui": "1.0.0",
|
||||
"com.unity.services.core": "1.15.1",
|
||||
"com.unity.nuget.newtonsoft-json": "3.2.1",
|
||||
"com.unity.modules.unitywebrequest": "1.0.0"
|
||||
},
|
||||
"url": "https://packages.unity.com"
|
||||
},
|
||||
"com.unity.services.core": {
|
||||
"version": "1.16.0",
|
||||
"depth": 1,
|
||||
"source": "registry",
|
||||
"dependencies": {
|
||||
"com.unity.modules.androidjni": "1.0.0",
|
||||
"com.unity.nuget.newtonsoft-json": "3.2.1",
|
||||
"com.unity.modules.unitywebrequest": "1.0.0"
|
||||
},
|
||||
"url": "https://packages.unity.com"
|
||||
},
|
||||
"com.unity.services.deployment": {
|
||||
"version": "1.6.2",
|
||||
"depth": 1,
|
||||
"source": "registry",
|
||||
"dependencies": {
|
||||
"com.unity.services.core": "1.15.1",
|
||||
"com.unity.services.deployment.api": "1.1.2"
|
||||
},
|
||||
"url": "https://packages.unity.com"
|
||||
},
|
||||
"com.unity.services.deployment.api": {
|
||||
"version": "1.1.3",
|
||||
"depth": 2,
|
||||
"source": "registry",
|
||||
"dependencies": {},
|
||||
"url": "https://packages.unity.com"
|
||||
},
|
||||
"com.unity.services.multiplayer": {
|
||||
"version": "2.1.0",
|
||||
"depth": 0,
|
||||
"source": "registry",
|
||||
"dependencies": {
|
||||
"com.unity.transport": "2.5.0",
|
||||
"com.unity.collections": "2.2.1",
|
||||
"com.unity.services.qos": "1.3.0",
|
||||
"com.unity.services.core": "1.15.1",
|
||||
"com.unity.services.wire": "1.4.0",
|
||||
"com.unity.services.deployment": "1.6.2",
|
||||
"com.unity.nuget.newtonsoft-json": "3.2.1",
|
||||
"com.unity.modules.unitywebrequest": "1.0.0",
|
||||
"com.unity.services.authentication": "3.6.0"
|
||||
},
|
||||
"url": "https://packages.unity.com"
|
||||
},
|
||||
"com.unity.services.qos": {
|
||||
"version": "1.4.1",
|
||||
"depth": 1,
|
||||
"source": "registry",
|
||||
"dependencies": {
|
||||
"com.unity.collections": "1.2.4",
|
||||
"com.unity.services.core": "1.12.5",
|
||||
"com.unity.nuget.newtonsoft-json": "3.0.2",
|
||||
"com.unity.modules.unitywebrequest": "1.0.0",
|
||||
"com.unity.services.authentication": "3.5.2"
|
||||
},
|
||||
"url": "https://packages.unity.com"
|
||||
},
|
||||
"com.unity.services.wire": {
|
||||
"version": "1.4.1",
|
||||
"depth": 1,
|
||||
"source": "registry",
|
||||
"dependencies": {
|
||||
"com.unity.services.core": "1.12.5",
|
||||
"com.unity.nuget.newtonsoft-json": "3.2.1",
|
||||
"com.unity.services.authentication": "2.7.4"
|
||||
},
|
||||
"url": "https://packages.unity.com"
|
||||
},
|
||||
"com.unity.settings-manager": {
|
||||
"version": "2.1.1",
|
||||
"depth": 2,
|
||||
|
||||
4
ProjectSettings/VirtualProjectsConfig.json
Normal file
4
ProjectSettings/VirtualProjectsConfig.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"PlayerTags": [],
|
||||
"version": "6000.3.5f2"
|
||||
}
|
||||
@@ -43,15 +43,14 @@
|
||||
<UnityVersion>6000.3.5f2</UnityVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Analyzer Include="C:\Program Files\Microsoft Visual Studio\18\Community\Common7\IDE\Extensions\Microsoft\Visual Studio Tools for Unity\Analyzers\Microsoft.Unity.Analyzers.dll" />
|
||||
<Analyzer Include="C:\Program Files\Unity\Hub\Editor\6000.3.5f2\Editor\Data\Tools\BuildPipeline\Unity.SourceGenerators\Unity.SourceGenerators.dll" />
|
||||
<Analyzer Include="C:\Program Files\Unity\Hub\Editor\6000.3.5f2\Editor\Data\Tools\BuildPipeline\Unity.SourceGenerators\Unity.Properties.SourceGenerator.dll" />
|
||||
<Analyzer Include="C:\Program Files\Unity\Hub\Editor\6000.3.5f2\Editor\Data\Tools\BuildPipeline\Unity.SourceGenerators\Unity.UIToolkit.SourceGenerator.dll" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Library\PackageCache\com.unity.render-pipelines.universal@1e87cf1dccb8\Runtime\Overrides\ColorAdjustments.cs" />
|
||||
<Compile Include="Library\PackageCache\com.unity.render-pipelines.universal@1e87cf1dccb8\Runtime\Passes\ScreenSpaceAmbientOcclusionPass.cs" />
|
||||
<Compile Include="Library\PackageCache\com.unity.render-pipelines.universal@1e87cf1dccb8\Runtime\Decal\Entities\DecalEntityManager.cs" />
|
||||
<Compile Include="Library\PackageCache\com.unity.render-pipelines.universal@1e87cf1dccb8\Runtime\Passes\ScreenSpaceAmbientOcclusionPass.cs" />
|
||||
<Compile Include="Library\PackageCache\com.unity.render-pipelines.universal@1e87cf1dccb8\Runtime\Overrides\ScreenSpaceLensFlare.cs" />
|
||||
<Compile Include="Library\PackageCache\com.unity.render-pipelines.universal@1e87cf1dccb8\Runtime\History\RawColorHistory.cs" />
|
||||
<Compile Include="Assets\FlatKit\[Render Pipeline] URP\EditorAttributes\Core\DrawerAttributes\InfoBoxAttribute.cs" />
|
||||
@@ -143,8 +142,8 @@
|
||||
<Compile Include="Assets\FlatKit\[Render Pipeline] URP\RenderFeatures\Outline\FlatKitOutline.cs" />
|
||||
<Compile Include="Library\PackageCache\com.unity.render-pipelines.universal@1e87cf1dccb8\Runtime\Passes\AdditionalLightsShadowAtlasLayout.cs" />
|
||||
<Compile Include="Library\PackageCache\com.unity.render-pipelines.universal@1e87cf1dccb8\Runtime\Deprecated.cs" />
|
||||
<Compile Include="Library\PackageCache\com.unity.render-pipelines.universal@1e87cf1dccb8\Runtime\Passes\ColorGradingLutPass.cs" />
|
||||
<Compile Include="Assets\FlatKit\[Render Pipeline] URP\RenderFeatures\Common\ScreenRenderPass.cs" />
|
||||
<Compile Include="Library\PackageCache\com.unity.render-pipelines.universal@1e87cf1dccb8\Runtime\Passes\ColorGradingLutPass.cs" />
|
||||
<Compile Include="Library\PackageCache\com.unity.render-pipelines.universal@1e87cf1dccb8\Runtime\Tiling\TileRangeExpansionJob.cs" />
|
||||
<Compile Include="Library\PackageCache\com.unity.render-pipelines.universal@1e87cf1dccb8\Runtime\TemporalAA.cs" />
|
||||
<Compile Include="Library\PackageCache\com.unity.render-pipelines.universal@1e87cf1dccb8\Runtime\VFXGraph\Utility\PropertyBinders\URPCameraBinder.cs" />
|
||||
@@ -247,8 +246,8 @@
|
||||
<Compile Include="Library\PackageCache\com.unity.render-pipelines.universal@1e87cf1dccb8\Runtime\Data\UniversalRenderPipelineAsset.DefaultResources.cs" />
|
||||
<Compile Include="Library\PackageCache\com.unity.render-pipelines.universal@1e87cf1dccb8\Runtime\Data\RenderStateData.cs" />
|
||||
<Compile Include="Assets\FlatKit\[Render Pipeline] URP\EditorAttributes\Core\DrawerAttributes\DrawerAttribute.cs" />
|
||||
<Compile Include="Library\PackageCache\com.unity.render-pipelines.universal@1e87cf1dccb8\Runtime\FrameData\UniversalLightData.cs" />
|
||||
<Compile Include="Library\PackageCache\com.unity.render-pipelines.universal@1e87cf1dccb8\Runtime\Debug\DebugHandler.cs" />
|
||||
<Compile Include="Library\PackageCache\com.unity.render-pipelines.universal@1e87cf1dccb8\Runtime\FrameData\UniversalLightData.cs" />
|
||||
<Compile Include="Library\PackageCache\com.unity.render-pipelines.universal@1e87cf1dccb8\Runtime\Decal\Entities\DecalDrawSystem.cs" />
|
||||
<Compile Include="Library\PackageCache\com.unity.render-pipelines.universal@1e87cf1dccb8\Runtime\Passes\PostProcessPass.cs" />
|
||||
<Compile Include="Library\PackageCache\com.unity.render-pipelines.universal@1e87cf1dccb8\Runtime\Passes\CapturePass.cs" />
|
||||
@@ -832,6 +831,10 @@
|
||||
<HintPath>C:\Program Files\Unity\Hub\Editor\6000.3.5f2\Editor\Data\PlaybackEngines\WindowsStandaloneSupport\UnityEditor.WindowsStandalone.Extensions.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="websocket-sharp">
|
||||
<HintPath>Library\PackageCache\com.unity.services.wire@9a73acde80cc\Plugins\websocket-sharp.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Unity.VisualScripting.YamlDotNet">
|
||||
<HintPath>Library\PackageCache\com.unity.visualscripting@191c0d7e3b69\Editor\VisualScripting.Core\Dependencies\YamlDotNet\Unity.VisualScripting.YamlDotNet.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
|
||||
Reference in New Issue
Block a user