지하 생성 및 터널 구조 개선
This commit is contained in:
@@ -69,13 +69,13 @@ MonoBehaviour:
|
|||||||
SourcePrefabToOverride: {fileID: 0}
|
SourcePrefabToOverride: {fileID: 0}
|
||||||
SourceHashToOverride: 0
|
SourceHashToOverride: 0
|
||||||
OverridingTargetPrefab: {fileID: 0}
|
OverridingTargetPrefab: {fileID: 0}
|
||||||
- Override: 0
|
|
||||||
Prefab: {fileID: 1126366023313988, guid: a4e5400b711cb57478dcf41916935a7e, type: 3}
|
|
||||||
SourcePrefabToOverride: {fileID: 0}
|
|
||||||
SourceHashToOverride: 0
|
|
||||||
OverridingTargetPrefab: {fileID: 0}
|
|
||||||
- Override: 0
|
- Override: 0
|
||||||
Prefab: {fileID: 989066657509100432, guid: dbb1cfcb3d9e3844e8d9cdf09b0a1660, type: 3}
|
Prefab: {fileID: 989066657509100432, guid: dbb1cfcb3d9e3844e8d9cdf09b0a1660, type: 3}
|
||||||
SourcePrefabToOverride: {fileID: 0}
|
SourcePrefabToOverride: {fileID: 0}
|
||||||
SourceHashToOverride: 0
|
SourceHashToOverride: 0
|
||||||
OverridingTargetPrefab: {fileID: 0}
|
OverridingTargetPrefab: {fileID: 0}
|
||||||
|
- Override: 0
|
||||||
|
Prefab: {fileID: 989066657509100432, guid: 17532917e1ada23469c573abf64905f0, type: 3}
|
||||||
|
SourcePrefabToOverride: {fileID: 0}
|
||||||
|
SourceHashToOverride: 0
|
||||||
|
OverridingTargetPrefab: {fileID: 0}
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ MonoBehaviour:
|
|||||||
m_Script: {fileID: 11500000, guid: d71ce06d133743140877345b807f33ad, type: 3}
|
m_Script: {fileID: 11500000, guid: d71ce06d133743140877345b807f33ad, type: 3}
|
||||||
m_Name:
|
m_Name:
|
||||||
m_EditorClassIdentifier: Assembly-CSharp::TunnelTraveler
|
m_EditorClassIdentifier: Assembly-CSharp::TunnelTraveler
|
||||||
travelSpeed: 20
|
travelSpeed: 2
|
||||||
--- !u!95 &5870045807328036684
|
--- !u!95 &5870045807328036684
|
||||||
Animator:
|
Animator:
|
||||||
serializedVersion: 7
|
serializedVersion: 7
|
||||||
@@ -207,7 +207,7 @@ MonoBehaviour:
|
|||||||
interactRange: 3
|
interactRange: 3
|
||||||
interactableLayer:
|
interactableLayer:
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
m_Bits: 1024
|
m_Bits: 9216
|
||||||
constructionLayer:
|
constructionLayer:
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
m_Bits: 256
|
m_Bits: 256
|
||||||
|
|||||||
157
Assets/Prefabs/ResourceBlock.prefab
Normal file
157
Assets/Prefabs/ResourceBlock.prefab
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
%YAML 1.1
|
||||||
|
%TAG !u! tag:unity3d.com,2011:
|
||||||
|
--- !u!1 &989066657509100432
|
||||||
|
GameObject:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
serializedVersion: 6
|
||||||
|
m_Component:
|
||||||
|
- component: {fileID: 5902598773338262147}
|
||||||
|
- component: {fileID: 7167019900531103381}
|
||||||
|
- component: {fileID: 384943650775659982}
|
||||||
|
- component: {fileID: 3086293200253757409}
|
||||||
|
- component: {fileID: 7528764990365051674}
|
||||||
|
- component: {fileID: 3421159559893464927}
|
||||||
|
m_Layer: 12
|
||||||
|
m_Name: ResourceBlock
|
||||||
|
m_TagString: Untagged
|
||||||
|
m_Icon: {fileID: 0}
|
||||||
|
m_NavMeshLayer: 0
|
||||||
|
m_StaticEditorFlags: 0
|
||||||
|
m_IsActive: 1
|
||||||
|
--- !u!4 &5902598773338262147
|
||||||
|
Transform:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 989066657509100432}
|
||||||
|
serializedVersion: 2
|
||||||
|
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||||
|
m_LocalPosition: {x: 0.5, y: 0.5, z: 0.5}
|
||||||
|
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!33 &7167019900531103381
|
||||||
|
MeshFilter:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 989066657509100432}
|
||||||
|
m_Mesh: {fileID: 4300000, guid: 09d8bdb2477b8c04f87c3f5d1d04a55f, type: 3}
|
||||||
|
--- !u!23 &384943650775659982
|
||||||
|
MeshRenderer:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 989066657509100432}
|
||||||
|
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: 4930c6ffa5b9b9f4098249a43abf8506, 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!64 &3086293200253757409
|
||||||
|
MeshCollider:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 989066657509100432}
|
||||||
|
m_Material: {fileID: 0}
|
||||||
|
m_IncludeLayers:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_Bits: 0
|
||||||
|
m_ExcludeLayers:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_Bits: 0
|
||||||
|
m_LayerOverridePriority: 0
|
||||||
|
m_IsTrigger: 0
|
||||||
|
m_ProvidesContacts: 0
|
||||||
|
m_Enabled: 1
|
||||||
|
serializedVersion: 5
|
||||||
|
m_Convex: 0
|
||||||
|
m_CookingOptions: 30
|
||||||
|
m_Mesh: {fileID: 4300000, guid: 10d475a0f15b31443aa032c25648465b, type: 3}
|
||||||
|
--- !u!114 &7528764990365051674
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 989066657509100432}
|
||||||
|
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: 1191681468
|
||||||
|
InScenePlacedSourceGlobalObjectIdHash: 3862515551
|
||||||
|
DeferredDespawnTick: 0
|
||||||
|
Ownership: 1
|
||||||
|
AlwaysReplicateAsRoot: 0
|
||||||
|
SynchronizeTransform: 1
|
||||||
|
ActiveSceneSynchronization: 0
|
||||||
|
SceneMigrationSynchronization: 0
|
||||||
|
SpawnWithObservers: 1
|
||||||
|
DontDestroyWithOwner: 0
|
||||||
|
AutoObjectParentSync: 1
|
||||||
|
SyncOwnerTransformWhenParented: 1
|
||||||
|
AllowOwnerToParent: 0
|
||||||
|
--- !u!114 &3421159559893464927
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 989066657509100432}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: e8e42f98781f84c42855fa4e989d3c1b, type: 3}
|
||||||
|
m_Name:
|
||||||
|
m_EditorClassIdentifier: Assembly-CSharp::MineableBlock
|
||||||
|
ShowTopMostFoldoutHeaderGroup: 1
|
||||||
|
maxHp: 100
|
||||||
|
breakEffectPrefab: {fileID: 0}
|
||||||
7
Assets/Prefabs/ResourceBlock.prefab.meta
Normal file
7
Assets/Prefabs/ResourceBlock.prefab.meta
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 17532917e1ada23469c573abf64905f0
|
||||||
|
PrefabImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -11,7 +11,7 @@ GameObject:
|
|||||||
- component: {fileID: 4703568612144557396}
|
- component: {fileID: 4703568612144557396}
|
||||||
- component: {fileID: 2320767985316498782}
|
- component: {fileID: 2320767985316498782}
|
||||||
- component: {fileID: 2292926820231089492}
|
- component: {fileID: 2292926820231089492}
|
||||||
m_Layer: 10
|
m_Layer: 13
|
||||||
m_Name: TunnelNodeA
|
m_Name: TunnelNodeA
|
||||||
m_TagString: Untagged
|
m_TagString: Untagged
|
||||||
m_Icon: {fileID: 0}
|
m_Icon: {fileID: 0}
|
||||||
@@ -45,9 +45,8 @@ MonoBehaviour:
|
|||||||
m_Script: {fileID: 11500000, guid: 275306f282948c343bf20ee884ad22b8, type: 3}
|
m_Script: {fileID: 11500000, guid: 275306f282948c343bf20ee884ad22b8, type: 3}
|
||||||
m_Name:
|
m_Name:
|
||||||
m_EditorClassIdentifier: Assembly-CSharp::TunnelNode
|
m_EditorClassIdentifier: Assembly-CSharp::TunnelNode
|
||||||
ShowTopMostFoldoutHeaderGroup: 1
|
isTop: 1
|
||||||
aboveNode: {fileID: 0}
|
partnerNode: {fileID: 3033105595038429359}
|
||||||
belowNode: {fileID: 0}
|
|
||||||
--- !u!65 &2292926820231089492
|
--- !u!65 &2292926820231089492
|
||||||
BoxCollider:
|
BoxCollider:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
@@ -100,8 +99,77 @@ Transform:
|
|||||||
m_Children:
|
m_Children:
|
||||||
- {fileID: 4229549338725785498}
|
- {fileID: 4229549338725785498}
|
||||||
- {fileID: 4703568612144557396}
|
- {fileID: 4703568612144557396}
|
||||||
|
- {fileID: 6728412950896342264}
|
||||||
m_Father: {fileID: 7892781535212668078}
|
m_Father: {fileID: 7892781535212668078}
|
||||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
|
--- !u!1 &6875506762777844854
|
||||||
|
GameObject:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
serializedVersion: 6
|
||||||
|
m_Component:
|
||||||
|
- component: {fileID: 6728412950896342264}
|
||||||
|
- component: {fileID: 3033105595038429359}
|
||||||
|
- component: {fileID: 3152835292698073290}
|
||||||
|
m_Layer: 13
|
||||||
|
m_Name: TunnelNodeB
|
||||||
|
m_TagString: Untagged
|
||||||
|
m_Icon: {fileID: 0}
|
||||||
|
m_NavMeshLayer: 0
|
||||||
|
m_StaticEditorFlags: 0
|
||||||
|
m_IsActive: 1
|
||||||
|
--- !u!4 &6728412950896342264
|
||||||
|
Transform:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 6875506762777844854}
|
||||||
|
serializedVersion: 2
|
||||||
|
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||||
|
m_LocalPosition: {x: 0, y: -3, z: 0}
|
||||||
|
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||||
|
m_ConstrainProportionsScale: 0
|
||||||
|
m_Children: []
|
||||||
|
m_Father: {fileID: 5411003075815891614}
|
||||||
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
|
--- !u!114 &3033105595038429359
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 6875506762777844854}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: 275306f282948c343bf20ee884ad22b8, type: 3}
|
||||||
|
m_Name:
|
||||||
|
m_EditorClassIdentifier: Assembly-CSharp::TunnelNode
|
||||||
|
isTop: 0
|
||||||
|
partnerNode: {fileID: 2320767985316498782}
|
||||||
|
--- !u!65 &3152835292698073290
|
||||||
|
BoxCollider:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 6875506762777844854}
|
||||||
|
m_Material: {fileID: 0}
|
||||||
|
m_IncludeLayers:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_Bits: 0
|
||||||
|
m_ExcludeLayers:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_Bits: 0
|
||||||
|
m_LayerOverridePriority: 0
|
||||||
|
m_IsTrigger: 1
|
||||||
|
m_ProvidesContacts: 0
|
||||||
|
m_Enabled: 1
|
||||||
|
serializedVersion: 3
|
||||||
|
m_Size: {x: 1, y: 1, z: 1}
|
||||||
|
m_Center: {x: 0, y: 0, z: 0}
|
||||||
--- !u!1 &7297238503126003997
|
--- !u!1 &7297238503126003997
|
||||||
GameObject:
|
GameObject:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
@@ -225,6 +293,7 @@ GameObject:
|
|||||||
m_Component:
|
m_Component:
|
||||||
- component: {fileID: 7892781535212668078}
|
- component: {fileID: 7892781535212668078}
|
||||||
- component: {fileID: 5637751854208875435}
|
- component: {fileID: 5637751854208875435}
|
||||||
|
- component: {fileID: 5398902060671424421}
|
||||||
m_Layer: 11
|
m_Layer: 11
|
||||||
m_Name: Tunnel
|
m_Name: Tunnel
|
||||||
m_TagString: Untagged
|
m_TagString: Untagged
|
||||||
@@ -273,3 +342,30 @@ MonoBehaviour:
|
|||||||
AutoObjectParentSync: 1
|
AutoObjectParentSync: 1
|
||||||
SyncOwnerTransformWhenParented: 1
|
SyncOwnerTransformWhenParented: 1
|
||||||
AllowOwnerToParent: 0
|
AllowOwnerToParent: 0
|
||||||
|
--- !u!54 &5398902060671424421
|
||||||
|
Rigidbody:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 7817822323996939414}
|
||||||
|
serializedVersion: 5
|
||||||
|
m_Mass: 1
|
||||||
|
m_LinearDamping: 0
|
||||||
|
m_AngularDamping: 0.05
|
||||||
|
m_CenterOfMass: {x: 0, y: 0, z: 0}
|
||||||
|
m_InertiaTensor: {x: 1, y: 1, z: 1}
|
||||||
|
m_InertiaRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||||
|
m_IncludeLayers:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_Bits: 0
|
||||||
|
m_ExcludeLayers:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_Bits: 0
|
||||||
|
m_ImplicitCom: 1
|
||||||
|
m_ImplicitTensor: 1
|
||||||
|
m_UseGravity: 0
|
||||||
|
m_IsKinematic: 1
|
||||||
|
m_Interpolate: 0
|
||||||
|
m_Constraints: 0
|
||||||
|
m_CollisionDetection: 0
|
||||||
|
|||||||
@@ -678,152 +678,6 @@ MonoBehaviour:
|
|||||||
m_MinRegionArea: 2
|
m_MinRegionArea: 2
|
||||||
m_NavMeshData: {fileID: 23800000, guid: 2d822e23a760f3943a3174a0d27b4254, type: 2}
|
m_NavMeshData: {fileID: 23800000, guid: 2d822e23a760f3943a3174a0d27b4254, type: 2}
|
||||||
m_BuildHeightMesh: 0
|
m_BuildHeightMesh: 0
|
||||||
--- !u!1 &342345820
|
|
||||||
GameObject:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
serializedVersion: 6
|
|
||||||
m_Component:
|
|
||||||
- component: {fileID: 342345821}
|
|
||||||
- component: {fileID: 342345825}
|
|
||||||
- component: {fileID: 342345824}
|
|
||||||
- component: {fileID: 342345823}
|
|
||||||
- component: {fileID: 342345822}
|
|
||||||
m_Layer: 7
|
|
||||||
m_Name: DungeonBase
|
|
||||||
m_TagString: Untagged
|
|
||||||
m_Icon: {fileID: 0}
|
|
||||||
m_NavMeshLayer: 0
|
|
||||||
m_StaticEditorFlags: 2147483647
|
|
||||||
m_IsActive: 1
|
|
||||||
--- !u!4 &342345821
|
|
||||||
Transform:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 342345820}
|
|
||||||
serializedVersion: 2
|
|
||||||
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
|
|
||||||
m_LocalPosition: {x: -9, y: -5, z: 0}
|
|
||||||
m_LocalScale: {x: 10, y: 2, z: 6}
|
|
||||||
m_ConstrainProportionsScale: 0
|
|
||||||
m_Children: []
|
|
||||||
m_Father: {fileID: 743367988}
|
|
||||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
|
||||||
--- !u!114 &342345822
|
|
||||||
MonoBehaviour:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 342345820}
|
|
||||||
m_Enabled: 1
|
|
||||||
m_EditorHideFlags: 0
|
|
||||||
m_Script: {fileID: 11500000, guid: 7a5ac11cc976e418e8d13136b07e1f52, type: 3}
|
|
||||||
m_Name:
|
|
||||||
m_EditorClassIdentifier: Unity.AI.Navigation::Unity.AI.Navigation.NavMeshSurface
|
|
||||||
m_SerializedVersion: 0
|
|
||||||
m_AgentTypeID: 0
|
|
||||||
m_CollectObjects: 0
|
|
||||||
m_Size: {x: 10, y: 10, z: 10}
|
|
||||||
m_Center: {x: 0, y: 2, z: 0}
|
|
||||||
m_LayerMask:
|
|
||||||
serializedVersion: 2
|
|
||||||
m_Bits: 4294967295
|
|
||||||
m_UseGeometry: 0
|
|
||||||
m_DefaultArea: 0
|
|
||||||
m_GenerateLinks: 0
|
|
||||||
m_IgnoreNavMeshAgent: 1
|
|
||||||
m_IgnoreNavMeshObstacle: 1
|
|
||||||
m_OverrideTileSize: 0
|
|
||||||
m_TileSize: 256
|
|
||||||
m_OverrideVoxelSize: 0
|
|
||||||
m_VoxelSize: 0.016666668
|
|
||||||
m_MinRegionArea: 2
|
|
||||||
m_NavMeshData: {fileID: 0}
|
|
||||||
m_BuildHeightMesh: 0
|
|
||||||
--- !u!64 &342345823
|
|
||||||
MeshCollider:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 342345820}
|
|
||||||
m_Material: {fileID: 0}
|
|
||||||
m_IncludeLayers:
|
|
||||||
serializedVersion: 2
|
|
||||||
m_Bits: 0
|
|
||||||
m_ExcludeLayers:
|
|
||||||
serializedVersion: 2
|
|
||||||
m_Bits: 0
|
|
||||||
m_LayerOverridePriority: 0
|
|
||||||
m_IsTrigger: 0
|
|
||||||
m_ProvidesContacts: 0
|
|
||||||
m_Enabled: 1
|
|
||||||
serializedVersion: 5
|
|
||||||
m_Convex: 0
|
|
||||||
m_CookingOptions: 30
|
|
||||||
m_Mesh: {fileID: 10209, guid: 0000000000000000e000000000000000, type: 0}
|
|
||||||
--- !u!23 &342345824
|
|
||||||
MeshRenderer:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 342345820}
|
|
||||||
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: a3cd22eceea5e8548b10cdb5d9b49a25, type: 2}
|
|
||||||
m_StaticBatchInfo:
|
|
||||||
firstSubMesh: 0
|
|
||||||
subMeshCount: 0
|
|
||||||
m_StaticBatchRoot: {fileID: 0}
|
|
||||||
m_ProbeAnchor: {fileID: 0}
|
|
||||||
m_LightProbeVolumeOverride: {fileID: 0}
|
|
||||||
m_ScaleInLightmap: 1
|
|
||||||
m_ReceiveGI: 1
|
|
||||||
m_PreserveUVs: 0
|
|
||||||
m_IgnoreNormalsForChartDetection: 0
|
|
||||||
m_ImportantGI: 0
|
|
||||||
m_StitchLightmapSeams: 1
|
|
||||||
m_SelectedEditorRenderState: 3
|
|
||||||
m_MinimumChartSize: 4
|
|
||||||
m_AutoUVMaxDistance: 0.5
|
|
||||||
m_AutoUVMaxAngle: 89
|
|
||||||
m_LightmapParameters: {fileID: 0}
|
|
||||||
m_GlobalIlluminationMeshLod: 0
|
|
||||||
m_SortingLayerID: 0
|
|
||||||
m_SortingLayer: 0
|
|
||||||
m_SortingOrder: 0
|
|
||||||
m_MaskInteraction: 0
|
|
||||||
m_AdditionalVertexStreams: {fileID: 0}
|
|
||||||
--- !u!33 &342345825
|
|
||||||
MeshFilter:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 342345820}
|
|
||||||
m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
|
|
||||||
--- !u!4 &372588680 stripped
|
--- !u!4 &372588680 stripped
|
||||||
Transform:
|
Transform:
|
||||||
m_CorrespondingSourceObject: {fileID: 6438027004378685690, guid: 2bb7e098e271eb44a873c856dbf59c7c, type: 3}
|
m_CorrespondingSourceObject: {fileID: 6438027004378685690, guid: 2bb7e098e271eb44a873c856dbf59c7c, type: 3}
|
||||||
@@ -838,8 +692,8 @@ GameObject:
|
|||||||
serializedVersion: 6
|
serializedVersion: 6
|
||||||
m_Component:
|
m_Component:
|
||||||
- component: {fileID: 412220987}
|
- component: {fileID: 412220987}
|
||||||
- component: {fileID: 412220988}
|
|
||||||
- component: {fileID: 412220989}
|
- component: {fileID: 412220989}
|
||||||
|
- component: {fileID: 412220988}
|
||||||
m_Layer: 0
|
m_Layer: 0
|
||||||
m_Name: BuildManager
|
m_Name: BuildManager
|
||||||
m_TagString: Untagged
|
m_TagString: Untagged
|
||||||
@@ -876,7 +730,8 @@ MonoBehaviour:
|
|||||||
m_EditorClassIdentifier: Assembly-CSharp::BuildManager
|
m_EditorClassIdentifier: Assembly-CSharp::BuildManager
|
||||||
ShowTopMostFoldoutHeaderGroup: 1
|
ShowTopMostFoldoutHeaderGroup: 1
|
||||||
cellSize: 1
|
cellSize: 1
|
||||||
tunnelHeight: 3
|
pivotOffset: 0.5
|
||||||
|
tunnelLengthInBlocks: 3
|
||||||
constructionSitePrefab: {fileID: 7327242023390354019, guid: 7d362c5c1b34c2b4e901294618e6c3e8, type: 3}
|
constructionSitePrefab: {fileID: 7327242023390354019, guid: 7d362c5c1b34c2b4e901294618e6c3e8, type: 3}
|
||||||
turretLibrary:
|
turretLibrary:
|
||||||
- turretName: Arrow Tower
|
- turretName: Arrow Tower
|
||||||
@@ -895,6 +750,12 @@ MonoBehaviour:
|
|||||||
finalPrefab: {fileID: 7817822323996939414, guid: 48bf40d31e903d34f9469451d7de06dd, type: 3}
|
finalPrefab: {fileID: 7817822323996939414, guid: 48bf40d31e903d34f9469451d7de06dd, type: 3}
|
||||||
ghostPrefab: {fileID: 7817822323996939414, guid: 532bea89bbc1d2245b65a9c22749711a, type: 3}
|
ghostPrefab: {fileID: 7817822323996939414, guid: 532bea89bbc1d2245b65a9c22749711a, type: 3}
|
||||||
buildTime: 10
|
buildTime: 10
|
||||||
|
groundLayer:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_Bits: 128
|
||||||
|
tunnelLayer:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_Bits: 2048
|
||||||
--- !u!114 &412220989
|
--- !u!114 &412220989
|
||||||
MonoBehaviour:
|
MonoBehaviour:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
@@ -1111,7 +972,7 @@ MonoBehaviour:
|
|||||||
m_DisconnectTimeoutMS: 30000
|
m_DisconnectTimeoutMS: 30000
|
||||||
ConnectionData:
|
ConnectionData:
|
||||||
Address: 127.0.0.1
|
Address: 127.0.0.1
|
||||||
Port: 7777
|
Port: 7774
|
||||||
ServerListenAddress: 127.0.0.1
|
ServerListenAddress: 127.0.0.1
|
||||||
ClientBindPort: 0
|
ClientBindPort: 0
|
||||||
DebugSimulator:
|
DebugSimulator:
|
||||||
@@ -1564,7 +1425,6 @@ Transform:
|
|||||||
m_ConstrainProportionsScale: 0
|
m_ConstrainProportionsScale: 0
|
||||||
m_Children:
|
m_Children:
|
||||||
- {fileID: 290482132}
|
- {fileID: 290482132}
|
||||||
- {fileID: 342345821}
|
|
||||||
- {fileID: 445606026}
|
- {fileID: 445606026}
|
||||||
- {fileID: 372588680}
|
- {fileID: 372588680}
|
||||||
- {fileID: 2084947170}
|
- {fileID: 2084947170}
|
||||||
@@ -2699,6 +2559,86 @@ CanvasRenderer:
|
|||||||
m_PrefabAsset: {fileID: 0}
|
m_PrefabAsset: {fileID: 0}
|
||||||
m_GameObject: {fileID: 1600458457}
|
m_GameObject: {fileID: 1600458457}
|
||||||
m_CullTransparentMesh: 1
|
m_CullTransparentMesh: 1
|
||||||
|
--- !u!1 &1634635642
|
||||||
|
GameObject:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
serializedVersion: 6
|
||||||
|
m_Component:
|
||||||
|
- component: {fileID: 1634635645}
|
||||||
|
- component: {fileID: 1634635643}
|
||||||
|
- component: {fileID: 1634635644}
|
||||||
|
m_Layer: 0
|
||||||
|
m_Name: UndergroundGenerator
|
||||||
|
m_TagString: Untagged
|
||||||
|
m_Icon: {fileID: 0}
|
||||||
|
m_NavMeshLayer: 0
|
||||||
|
m_StaticEditorFlags: 0
|
||||||
|
m_IsActive: 1
|
||||||
|
--- !u!114 &1634635643
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 1634635642}
|
||||||
|
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: 1220698054
|
||||||
|
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 &1634635644
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 1634635642}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: 7fc80ebb61fb70d4fa694d3a1f81d2ab, type: 3}
|
||||||
|
m_Name:
|
||||||
|
m_EditorClassIdentifier: Assembly-CSharp::UndergroundGenerator
|
||||||
|
ShowTopMostFoldoutHeaderGroup: 1
|
||||||
|
generationRange: {x: 10, y: 3, z: 6}
|
||||||
|
noiseScale: 0.14
|
||||||
|
hollowThreshold: 0.4
|
||||||
|
baseResourceThreshold: 0.6
|
||||||
|
increaseResourceWithDepth: 1
|
||||||
|
depthFactor: 0.005
|
||||||
|
normalBlockPrefab: {fileID: 989066657509100432, guid: dbb1cfcb3d9e3844e8d9cdf09b0a1660, type: 3}
|
||||||
|
resourceBlockPrefab: {fileID: 989066657509100432, guid: 17532917e1ada23469c573abf64905f0, type: 3}
|
||||||
|
containerName: UndergroundBlocks
|
||||||
|
--- !u!4 &1634635645
|
||||||
|
Transform:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 1634635642}
|
||||||
|
serializedVersion: 2
|
||||||
|
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||||
|
m_LocalPosition: {x: -14, y: -8, z: -3}
|
||||||
|
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!20 &1648146738 stripped
|
--- !u!20 &1648146738 stripped
|
||||||
Camera:
|
Camera:
|
||||||
m_CorrespondingSourceObject: {fileID: 5650099317679730308, guid: 2b08dd32e48ef5e4aa65a6122099152e, type: 3}
|
m_CorrespondingSourceObject: {fileID: 5650099317679730308, guid: 2b08dd32e48ef5e4aa65a6122099152e, type: 3}
|
||||||
@@ -3346,3 +3286,4 @@ SceneRoots:
|
|||||||
- {fileID: 1409253547}
|
- {fileID: 1409253547}
|
||||||
- {fileID: 14847856}
|
- {fileID: 14847856}
|
||||||
- {fileID: 556982644}
|
- {fileID: 556982644}
|
||||||
|
- {fileID: 1634635645}
|
||||||
|
|||||||
@@ -78,7 +78,6 @@ public class EnemyAttack : MonoBehaviour
|
|||||||
_targetDamageable.TakeDamage(damage);
|
_targetDamageable.TakeDamage(damage);
|
||||||
_nextAttackTime = Time.time + attackCooldown;
|
_nextAttackTime = Time.time + attackCooldown;
|
||||||
StartCoroutine(FlashEmissionRoutine());
|
StartCoroutine(FlashEmissionRoutine());
|
||||||
Debug.Log($"[공격!] 대상: {_target.name}");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,21 +12,23 @@ public class BuildManager : NetworkBehaviour
|
|||||||
public struct TurretData
|
public struct TurretData
|
||||||
{
|
{
|
||||||
public string turretName;
|
public string turretName;
|
||||||
public GameObject finalPrefab; // NetworkObject 필수
|
public GameObject finalPrefab;
|
||||||
public GameObject ghostPrefab; // 로컬 프리뷰용
|
public GameObject ghostPrefab;
|
||||||
public float buildTime;
|
public float buildTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Header("Grid Settings")]
|
[Header("Uniform Grid Settings")]
|
||||||
public float cellSize = 1f;
|
public float cellSize = 1f;
|
||||||
public float tunnelHeight = 3f;
|
[SerializeField] private float pivotOffset = 0.5f; // 블록/터널 에셋의 중심점
|
||||||
[SerializeField] private float yOffset = 0.5f; // 터널 중심점 오프셋
|
|
||||||
|
[Header("Tunnel Settings")]
|
||||||
|
public int tunnelLengthInBlocks = 3;
|
||||||
|
|
||||||
[Header("Prefabs & Layers")]
|
[Header("Prefabs & Layers")]
|
||||||
[SerializeField] private GameObject constructionSitePrefab;
|
[SerializeField] private GameObject constructionSitePrefab;
|
||||||
[SerializeField] private List<TurretData> turretLibrary = new List<TurretData>();
|
[SerializeField] private List<TurretData> turretLibrary = new List<TurretData>();
|
||||||
[SerializeField] private LayerMask groundLayer;
|
[SerializeField] private LayerMask groundLayer;
|
||||||
[SerializeField] private LayerMask playerLayer;
|
[SerializeField] private LayerMask tunnelLayer;
|
||||||
|
|
||||||
private int _selectedTurretIndex = 0;
|
private int _selectedTurretIndex = 0;
|
||||||
private bool _isBuildMode = false;
|
private bool _isBuildMode = false;
|
||||||
@@ -34,7 +36,6 @@ public class BuildManager : NetworkBehaviour
|
|||||||
private Vector3Int _currentGridPos;
|
private Vector3Int _currentGridPos;
|
||||||
private PlayerInputActions _inputActions;
|
private PlayerInputActions _inputActions;
|
||||||
|
|
||||||
// 데이터 레지스트리 (다른 코드에서 참조)
|
|
||||||
private Dictionary<Vector3Int, TunnelNode> _tunnelRegistry = new Dictionary<Vector3Int, TunnelNode>();
|
private Dictionary<Vector3Int, TunnelNode> _tunnelRegistry = new Dictionary<Vector3Int, TunnelNode>();
|
||||||
private HashSet<Vector3Int> _occupiedNodes = new HashSet<Vector3Int>();
|
private HashSet<Vector3Int> _occupiedNodes = new HashSet<Vector3Int>();
|
||||||
|
|
||||||
@@ -42,26 +43,20 @@ public class BuildManager : NetworkBehaviour
|
|||||||
{
|
{
|
||||||
if (Instance == null) Instance = this;
|
if (Instance == null) Instance = this;
|
||||||
else Destroy(gameObject);
|
else Destroy(gameObject);
|
||||||
|
|
||||||
_inputActions = new PlayerInputActions();
|
_inputActions = new PlayerInputActions();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnNetworkSpawn()
|
public override void OnNetworkSpawn()
|
||||||
{
|
{
|
||||||
// 최신 Action-based 이벤트 바인딩
|
|
||||||
_inputActions.Player.ToggleBuild.performed += ctx => ToggleBuildMode();
|
_inputActions.Player.ToggleBuild.performed += ctx => ToggleBuildMode();
|
||||||
_inputActions.Player.Build.performed += ctx => OnBuildRequested();
|
_inputActions.Player.Build.performed += ctx => OnBuildRequested();
|
||||||
_inputActions.Player.Cancel.performed += ctx => ExitBuildMode();
|
_inputActions.Player.Cancel.performed += ctx => ExitBuildMode();
|
||||||
|
|
||||||
// 숫자키 슬롯 선택 (Input Action 에셋 설정에 따라 Select1, Select2... 등 사용)
|
|
||||||
_inputActions.Player.Select1.performed += ctx => SelectTurret(0);
|
_inputActions.Player.Select1.performed += ctx => SelectTurret(0);
|
||||||
_inputActions.Player.Select2.performed += ctx => SelectTurret(1);
|
_inputActions.Player.Select2.performed += ctx => SelectTurret(1);
|
||||||
_inputActions.Player.Select3.performed += ctx => SelectTurret(2);
|
_inputActions.Player.Select3.performed += ctx => SelectTurret(2);
|
||||||
|
|
||||||
_inputActions.Enable();
|
_inputActions.Enable();
|
||||||
|
|
||||||
// 시작 시 씬에 배치된 터널 자동 등록
|
|
||||||
RegisterAllExistingTunnels();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Update()
|
void Update()
|
||||||
@@ -70,144 +65,115 @@ public class BuildManager : NetworkBehaviour
|
|||||||
UpdateGhostPosition();
|
UpdateGhostPosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Selection & Build Logic
|
|
||||||
|
|
||||||
public void SelectTurret(int index)
|
|
||||||
{
|
|
||||||
if (index < 0 || index >= turretLibrary.Count) return;
|
|
||||||
|
|
||||||
_selectedTurretIndex = index;
|
|
||||||
Debug.Log($"타워 선택: {turretLibrary[_selectedTurretIndex].turretName}");
|
|
||||||
|
|
||||||
if (_isBuildMode)
|
|
||||||
{
|
|
||||||
if (_ghostInstance != null) Destroy(_ghostInstance);
|
|
||||||
_ghostInstance = Instantiate(turretLibrary[_selectedTurretIndex].ghostPrefab);
|
|
||||||
_ghostInstance.transform.position = GridToWorld(_currentGridPos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateGhostPosition()
|
private void UpdateGhostPosition()
|
||||||
{
|
{
|
||||||
Ray ray = Camera.main.ScreenPointToRay(Mouse.current.position.ReadValue());
|
Ray ray = Camera.main.ScreenPointToRay(Mouse.current.position.ReadValue());
|
||||||
int tunnelMask = LayerMask.GetMask("Tunnel");
|
|
||||||
|
|
||||||
if (Physics.Raycast(ray, out RaycastHit hit, Mathf.Infinity, groundLayer | tunnelMask))
|
if (Physics.Raycast(ray, out RaycastHit hit, Mathf.Infinity, groundLayer | tunnelLayer))
|
||||||
{
|
{
|
||||||
Vector3Int gridPos = WorldToGrid3D(hit.point);
|
Vector3 targetPos;
|
||||||
|
|
||||||
// 터널 조준 시 해당 터널의 한 칸 아래로 스냅
|
// 터널 조준 시: 복잡한 계산 없이 X, Z는 유지, Y만 3 낮춤
|
||||||
if (((1 << hit.collider.gameObject.layer) & tunnelMask) != 0)
|
if (((1 << hit.collider.gameObject.layer) & tunnelLayer) != 0)
|
||||||
{
|
{
|
||||||
gridPos = WorldToGrid3D(hit.collider.transform.position) + Vector3Int.down;
|
Vector3 parentPos = hit.collider.transform.position;
|
||||||
|
targetPos = new Vector3(parentPos.x, parentPos.y - (tunnelLengthInBlocks * cellSize), parentPos.z);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 지형 조준 시: 격자에 맞춤
|
||||||
|
_currentGridPos = WorldToGrid3D(hit.point + hit.normal * 0.01f);
|
||||||
|
targetPos = GridToWorld(_currentGridPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
_currentGridPos = gridPos;
|
_currentGridPos = WorldToGrid3D(targetPos);
|
||||||
_ghostInstance.transform.position = GridToWorld(gridPos);
|
_ghostInstance.transform.position = targetPos;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 모든 좌표를 반올림하여 0.5 단위 오차를 제거
|
||||||
|
public Vector3Int WorldToGrid3D(Vector3 worldPos)
|
||||||
|
{
|
||||||
|
return new Vector3Int(
|
||||||
|
Mathf.RoundToInt((worldPos.x - pivotOffset) / cellSize),
|
||||||
|
Mathf.RoundToInt((worldPos.y - pivotOffset) / cellSize),
|
||||||
|
Mathf.RoundToInt((worldPos.z - pivotOffset) / cellSize)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3 GridToWorld(Vector3Int gridPos)
|
||||||
|
{
|
||||||
|
return new Vector3(
|
||||||
|
(gridPos.x * cellSize) + pivotOffset,
|
||||||
|
(gridPos.y * cellSize) + pivotOffset,
|
||||||
|
(gridPos.z * cellSize) + pivotOffset
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// [에러 해결] ConstructionSite.cs 참조용
|
||||||
|
public TurretData GetTurretData(int index)
|
||||||
|
{
|
||||||
|
if (index < 0 || index >= turretLibrary.Count) return default;
|
||||||
|
return turretLibrary[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuildManager.cs 내 핵심 수정 부분
|
||||||
|
// [수정] 서버와 클라이언트 모두에서 터널 위치를 기록하도록 함
|
||||||
|
// BuildManager.cs 내부
|
||||||
|
public void RegisterTunnel(Vector3Int topPos, TunnelNode node)
|
||||||
|
{
|
||||||
|
// 터널이 점유하는 Y, Y-1, Y-2 세 칸 모두에 노드를 등록합니다.
|
||||||
|
for (int i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
Vector3Int cellPos = topPos + (Vector3Int.down * i);
|
||||||
|
if (!_tunnelRegistry.ContainsKey(cellPos))
|
||||||
|
{
|
||||||
|
_tunnelRegistry.Add(cellPos, node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// [수정] 특정 좌표의 터널을 찾는 공용 함수
|
||||||
|
public TunnelNode GetTunnelAt(Vector3Int pos)
|
||||||
|
{
|
||||||
|
return _tunnelRegistry.GetValueOrDefault(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. 건설 요청 시 실제 계산된 worldPos를 넘겨줍니다.
|
||||||
private void OnBuildRequested()
|
private void OnBuildRequested()
|
||||||
{
|
{
|
||||||
if (!_isBuildMode || EventSystem.current.IsPointerOverGameObject()) return;
|
if (!_isBuildMode || EventSystem.current.IsPointerOverGameObject()) return;
|
||||||
|
|
||||||
// 서버에 건설 가능 여부 확인 및 생성 요청
|
// 고스트가 현재 위치한 '그 좌표'를 그대로 보냅니다.
|
||||||
RequestBuildRpc(_selectedTurretIndex, _currentGridPos);
|
RequestBuildRpc(_selectedTurretIndex, _currentGridPos, _ghostInstance.transform.position);
|
||||||
|
|
||||||
ExitBuildMode();
|
ExitBuildMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 2. RPC에서 넘겨받은 worldPos를 사용하여 토대를 생성합니다.
|
||||||
[Rpc(SendTo.Server, InvokePermission = RpcInvokePermission.Everyone)]
|
[Rpc(SendTo.Server, InvokePermission = RpcInvokePermission.Everyone)]
|
||||||
private void RequestBuildRpc(int index, Vector3Int gridPos)
|
private void RequestBuildRpc(int index, Vector3Int gridPos, Vector3 worldPos)
|
||||||
{
|
{
|
||||||
// 서버 측 중복 점유 확인
|
// GridToWorld를 다시 계산하지 않고 전달받은 worldPos를 그대로 사용합니다.
|
||||||
if (_occupiedNodes.Contains(gridPos)) return;
|
GameObject siteObj = Instantiate(constructionSitePrefab, worldPos, Quaternion.identity);
|
||||||
|
|
||||||
Vector3 spawnPos = GridToWorld(gridPos);
|
|
||||||
|
|
||||||
// 1. 토대 생성 및 네트워크 스폰
|
|
||||||
GameObject siteObj = Instantiate(constructionSitePrefab, spawnPos, Quaternion.identity);
|
|
||||||
siteObj.GetComponent<NetworkObject>().Spawn();
|
siteObj.GetComponent<NetworkObject>().Spawn();
|
||||||
|
|
||||||
// 2. 토대 데이터 초기화
|
|
||||||
ConstructionSite site = siteObj.GetComponent<ConstructionSite>();
|
ConstructionSite site = siteObj.GetComponent<ConstructionSite>();
|
||||||
if (site != null)
|
if (site != null) site.Initialize(index, gridPos);
|
||||||
{
|
|
||||||
site.Initialize(index, gridPos);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_occupiedNodes.Add(gridPos);
|
public void SelectTurret(int index)
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Utility Functions (External References)
|
|
||||||
|
|
||||||
// ConstructionSite에서 프리팹 정보를 가져갈 때 사용
|
|
||||||
public TurretData GetTurretData(int index) => turretLibrary[index];
|
|
||||||
|
|
||||||
// TunnelNode가 스폰될 때 자신을 등록하기 위해 사용
|
|
||||||
public void RegisterTunnel(Vector3Int pos, TunnelNode node)
|
|
||||||
{
|
{
|
||||||
if (!_tunnelRegistry.ContainsKey(pos))
|
if (index < 0 || index >= turretLibrary.Count) return;
|
||||||
|
_selectedTurretIndex = index;
|
||||||
|
if (_isBuildMode && _ghostInstance != null)
|
||||||
{
|
{
|
||||||
_tunnelRegistry.Add(pos, node);
|
Destroy(_ghostInstance);
|
||||||
_occupiedNodes.Add(pos); // 건설된 구역으로 마킹
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TunnelNode가 위아래 노드를 찾기 위해 사용
|
|
||||||
public TunnelNode GetTunnelAt(Vector3Int pos) => _tunnelRegistry.GetValueOrDefault(pos);
|
|
||||||
|
|
||||||
// 좌표 변환: 월드 -> 격자 인덱스 (0.5 오프셋 반영)
|
|
||||||
public Vector3Int WorldToGrid3D(Vector3 worldPos)
|
|
||||||
{
|
|
||||||
return new Vector3Int(
|
|
||||||
Mathf.FloorToInt(worldPos.x / cellSize),
|
|
||||||
Mathf.RoundToInt((worldPos.y - yOffset) / tunnelHeight),
|
|
||||||
Mathf.FloorToInt(worldPos.z / cellSize)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 좌표 변환: 격자 인덱스 -> 월드 (0.5 오프셋 반영)
|
|
||||||
public Vector3 GridToWorld(Vector3Int gridPos)
|
|
||||||
{
|
|
||||||
return new Vector3(
|
|
||||||
gridPos.x * cellSize,
|
|
||||||
(gridPos.y * tunnelHeight) + yOffset,
|
|
||||||
gridPos.z * cellSize
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 씬에 미리 배치된 터널들을 한꺼번에 등록
|
|
||||||
public void RegisterAllExistingTunnels()
|
|
||||||
{
|
|
||||||
TunnelNode[] nodes = FindObjectsByType<TunnelNode>(FindObjectsSortMode.None);
|
|
||||||
foreach (var node in nodes)
|
|
||||||
{
|
|
||||||
Vector3Int pos = WorldToGrid3D(node.transform.position);
|
|
||||||
RegisterTunnel(pos, node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Mode Switching
|
|
||||||
|
|
||||||
public void ToggleBuildMode() { if (_isBuildMode) ExitBuildMode(); else EnterBuildMode(); }
|
|
||||||
private void EnterBuildMode()
|
|
||||||
{
|
|
||||||
if (turretLibrary.Count == 0) return;
|
|
||||||
_isBuildMode = true;
|
|
||||||
_ghostInstance = Instantiate(turretLibrary[_selectedTurretIndex].ghostPrefab);
|
_ghostInstance = Instantiate(turretLibrary[_selectedTurretIndex].ghostPrefab);
|
||||||
}
|
}
|
||||||
private void ExitBuildMode()
|
|
||||||
{
|
|
||||||
_isBuildMode = false;
|
|
||||||
if (_ghostInstance) Destroy(_ghostInstance);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
private void ToggleBuildMode() { if (_isBuildMode) ExitBuildMode(); else EnterBuildMode(); }
|
||||||
|
private void EnterBuildMode() { _isBuildMode = true; if (turretLibrary.Count > 0) _ghostInstance = Instantiate(turretLibrary[_selectedTurretIndex].ghostPrefab); }
|
||||||
|
private void ExitBuildMode() { _isBuildMode = false; if (_ghostInstance) Destroy(_ghostInstance); }
|
||||||
public override void OnNetworkDespawn() => _inputActions.Disable();
|
public override void OnNetworkDespawn() => _inputActions.Disable();
|
||||||
}
|
}
|
||||||
120
Assets/Scripts/GameBase/UndergroundGenerator.cs
Normal file
120
Assets/Scripts/GameBase/UndergroundGenerator.cs
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
using Unity.Netcode;
|
||||||
|
|
||||||
|
public class UndergroundGenerator : NetworkBehaviour
|
||||||
|
{
|
||||||
|
[Header("Generation Range")]
|
||||||
|
[SerializeField] private Vector3Int generationRange = new Vector3Int(20, 30, 10);
|
||||||
|
[SerializeField] private float noiseScale = 0.12f;
|
||||||
|
|
||||||
|
[Header("Thresholds (0 to 1)")]
|
||||||
|
[SerializeField, Range(0, 1)] private float hollowThreshold = 0.35f;
|
||||||
|
[SerializeField, Range(0, 1)] private float baseResourceThreshold = 0.8f;
|
||||||
|
|
||||||
|
[Header("Depth Settings")]
|
||||||
|
[SerializeField] private bool increaseResourceWithDepth = true;
|
||||||
|
[SerializeField] private float depthFactor = 0.005f; // 깊어질수록 임계값 감소 (자원 증가)
|
||||||
|
|
||||||
|
[Header("Prefabs")]
|
||||||
|
[SerializeField] private GameObject normalBlockPrefab;
|
||||||
|
[SerializeField] private GameObject resourceBlockPrefab;
|
||||||
|
|
||||||
|
[Header("Organization")]
|
||||||
|
[SerializeField] private string containerName = "UndergroundBlocks";
|
||||||
|
private Transform _blockContainer; // 블록들을 담을 부모 오브젝트
|
||||||
|
|
||||||
|
private float _seedX, _seedY, _seedZ;
|
||||||
|
|
||||||
|
private void Awake()
|
||||||
|
{
|
||||||
|
_seedX = Random.Range(0f, 99999f);
|
||||||
|
_seedY = Random.Range(0f, 99999f);
|
||||||
|
_seedZ = Random.Range(0f, 99999f);
|
||||||
|
|
||||||
|
// Hierarchy 정리를 위한 컨테이너 생성
|
||||||
|
_blockContainer = new GameObject(containerName).transform;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnNetworkSpawn()
|
||||||
|
{
|
||||||
|
if (IsServer) GenerateFromGeneratorPivot();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GenerateFromGeneratorPivot()
|
||||||
|
{
|
||||||
|
Vector3Int originGrid = BuildManager.Instance.WorldToGrid3D(transform.position);
|
||||||
|
|
||||||
|
for (int x = 0; x < generationRange.x; x++)
|
||||||
|
{
|
||||||
|
for (int y = 0; y > -generationRange.y; y--)
|
||||||
|
{
|
||||||
|
for (int z = 0; z < generationRange.z; z++)
|
||||||
|
{
|
||||||
|
Vector3Int targetGridPos = originGrid + new Vector3Int(x, y, z);
|
||||||
|
float noise = Get3DNoise(targetGridPos.x, targetGridPos.y, targetGridPos.z);
|
||||||
|
|
||||||
|
if (noise < hollowThreshold) continue;
|
||||||
|
|
||||||
|
float currentThreshold = baseResourceThreshold + (y * depthFactor);
|
||||||
|
GameObject prefab = (noise > currentThreshold) ? resourceBlockPrefab : normalBlockPrefab;
|
||||||
|
|
||||||
|
SpawnBlock(prefab, targetGridPos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SpawnBlock(GameObject prefab, Vector3Int gridPos)
|
||||||
|
{
|
||||||
|
if (prefab == null) return;
|
||||||
|
|
||||||
|
Vector3 worldPos = BuildManager.Instance.GridToWorld(gridPos);
|
||||||
|
|
||||||
|
// 1. 생성 시 부모(Container)를 지정하여 Hierarchy 정리
|
||||||
|
GameObject block = Instantiate(prefab, worldPos, Quaternion.identity);
|
||||||
|
|
||||||
|
// 2. 네트워크 스폰 (Netcode for GameObjects)
|
||||||
|
block.GetComponent<NetworkObject>().Spawn();
|
||||||
|
}
|
||||||
|
|
||||||
|
private float Get3DNoise(int x, int y, int z)
|
||||||
|
{
|
||||||
|
// 대칭 방지를 위한 10000 오프셋 및 시드 적용
|
||||||
|
float xCoord = (x + _seedX + 10000f) * noiseScale;
|
||||||
|
float yCoord = (y + _seedY + 10000f) * noiseScale;
|
||||||
|
float zCoord = (z + _seedZ + 10000f) * noiseScale;
|
||||||
|
|
||||||
|
float ab = Mathf.PerlinNoise(xCoord, yCoord);
|
||||||
|
float bc = Mathf.PerlinNoise(yCoord, zCoord);
|
||||||
|
float ac = Mathf.PerlinNoise(xCoord, zCoord);
|
||||||
|
|
||||||
|
return (ab + bc + ac) / 3f;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDrawGizmosSelected()
|
||||||
|
{
|
||||||
|
BuildManager bm = BuildManager.Instance;
|
||||||
|
if (bm == null) bm = FindFirstObjectByType<BuildManager>(); // 에러 방지 안전장치
|
||||||
|
if (bm == null) return;
|
||||||
|
|
||||||
|
Vector3Int originGrid = bm.WorldToGrid3D(transform.position);
|
||||||
|
|
||||||
|
for (int x = 0; x < generationRange.x; x += 2)
|
||||||
|
{
|
||||||
|
for (int y = -generationRange.y; y < 0; y += 2)
|
||||||
|
{
|
||||||
|
for (int z = 0; z < generationRange.z; z += 2)
|
||||||
|
{
|
||||||
|
Vector3Int targetGridPos = originGrid + new Vector3Int(x, y, z);
|
||||||
|
float noise = Get3DNoise(targetGridPos.x, targetGridPos.y, targetGridPos.z);
|
||||||
|
|
||||||
|
if (noise < hollowThreshold) continue;
|
||||||
|
|
||||||
|
Gizmos.color = (noise > baseResourceThreshold) ? Color.yellow : new Color(0.5f, 0.5f, 0.5f, 0.2f);
|
||||||
|
Vector3 pos = bm.GridToWorld(targetGridPos);
|
||||||
|
Gizmos.DrawSphere(pos, 0.2f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
2
Assets/Scripts/GameBase/UndergroundGenerator.cs.meta
Normal file
2
Assets/Scripts/GameBase/UndergroundGenerator.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 7fc80ebb61fb70d4fa694d3a1f81d2ab
|
||||||
@@ -4,7 +4,4 @@ public interface IInteractable
|
|||||||
{
|
{
|
||||||
// 누가 상호작용을 시도했는지 알려주기 위해 GameObject를 인자로 받습니다.
|
// 누가 상호작용을 시도했는지 알려주기 위해 GameObject를 인자로 받습니다.
|
||||||
void Interact(GameObject user);
|
void Interact(GameObject user);
|
||||||
|
|
||||||
// 선택 사항: 화면에 띄울 메시지를 반환하는 기능 (예: "통로 이용하기 [E]")
|
|
||||||
string GetInteractionText();
|
|
||||||
}
|
}
|
||||||
@@ -26,7 +26,6 @@ public class MineableBlock : NetworkBehaviour
|
|||||||
if (_currentHp.Value <= 0) return;
|
if (_currentHp.Value <= 0) return;
|
||||||
|
|
||||||
_currentHp.Value -= damageAmount;
|
_currentHp.Value -= damageAmount;
|
||||||
Debug.Log($"블록 대미지! 남은 체력: {_currentHp.Value}");
|
|
||||||
|
|
||||||
if (_currentHp.Value <= 0)
|
if (_currentHp.Value <= 0)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.InputSystem;
|
using UnityEngine.InputSystem;
|
||||||
using Unity.Netcode; // NGO 필수 네임스페이스
|
using Unity.Netcode;
|
||||||
using Unity.Netcode.Components;
|
using Unity.Netcode.Components;
|
||||||
|
|
||||||
[RequireComponent(typeof(NetworkObject))]
|
[RequireComponent(typeof(NetworkObject))]
|
||||||
public class PlayerNetworkController : NetworkBehaviour
|
public class PlayerNetworkController : NetworkBehaviour
|
||||||
{
|
{
|
||||||
|
// ... 기존 변수들 유지 ...
|
||||||
[Header("Movement Settings")]
|
[Header("Movement Settings")]
|
||||||
public float moveSpeed = 5f;
|
public float moveSpeed = 5f;
|
||||||
public float rotationSpeed = 10f;
|
public float rotationSpeed = 10f;
|
||||||
@@ -21,7 +22,7 @@ public class PlayerNetworkController : NetworkBehaviour
|
|||||||
[Header("Mining Settings")]
|
[Header("Mining Settings")]
|
||||||
[SerializeField] private float attackRange = 1.5f;
|
[SerializeField] private float attackRange = 1.5f;
|
||||||
[SerializeField] private int miningDamage = 25;
|
[SerializeField] private int miningDamage = 25;
|
||||||
[SerializeField] private LayerMask mineableLayer; // 'Mineable' 레이어 설정 필요
|
[SerializeField] private LayerMask mineableLayer;
|
||||||
|
|
||||||
private CharacterController _controller;
|
private CharacterController _controller;
|
||||||
private PlayerInputActions _inputActions;
|
private PlayerInputActions _inputActions;
|
||||||
@@ -34,16 +35,15 @@ public class PlayerNetworkController : NetworkBehaviour
|
|||||||
private bool _isGrounded;
|
private bool _isGrounded;
|
||||||
private bool _isHoldingInteract = false;
|
private bool _isHoldingInteract = false;
|
||||||
|
|
||||||
// NGO에서 Start 대신 사용하는 네트워크 초기화 메서드
|
// NGO 초기화
|
||||||
public override void OnNetworkSpawn()
|
public override void OnNetworkSpawn()
|
||||||
{
|
{
|
||||||
// 내 캐릭터가 아니라면 입력을 활성화하지 않습니다.
|
|
||||||
if (!IsOwner) return;
|
if (!IsOwner) return;
|
||||||
|
|
||||||
_inputActions = new PlayerInputActions();
|
_inputActions = new PlayerInputActions();
|
||||||
_inputActions.Player.Jump.performed += ctx => OnJump();
|
_inputActions.Player.Jump.performed += ctx => OnJump();
|
||||||
_inputActions.Player.Attack.performed += ctx => OnAttackServerRpc(); // 서버에 공격 요청
|
_inputActions.Player.Attack.performed += ctx => OnAttackServerRpc();
|
||||||
_inputActions.Player.Interact.performed += ctx => OnInteractTap();
|
_inputActions.Player.Interact.performed += ctx => OnInteractTap(); // 탭 상호작용
|
||||||
|
|
||||||
_inputActions.Player.Interact.started += ctx => _isHoldingInteract = true;
|
_inputActions.Player.Interact.started += ctx => _isHoldingInteract = true;
|
||||||
_inputActions.Player.Interact.canceled += ctx => _isHoldingInteract = false;
|
_inputActions.Player.Interact.canceled += ctx => _isHoldingInteract = false;
|
||||||
@@ -60,17 +60,16 @@ public class PlayerNetworkController : NetworkBehaviour
|
|||||||
|
|
||||||
void Update()
|
void Update()
|
||||||
{
|
{
|
||||||
// [중요] '나'의 캐릭터가 아니면 조종 로직을 아예 실행하지 않습니다.
|
|
||||||
if (!IsOwner) return;
|
if (!IsOwner) return;
|
||||||
if (_traveler != null && _traveler.IsTraveling) return;
|
if (_traveler != null && _traveler.IsTraveling) return;
|
||||||
|
|
||||||
HandleGravity();
|
HandleGravity();
|
||||||
HandleMovement();
|
HandleMovement();
|
||||||
|
|
||||||
// 건설 가속 로직
|
|
||||||
if (_isHoldingInteract) PerformConstructionSupport();
|
if (_isHoldingInteract) PerformConstructionSupport();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- 이동 관련 로직 (기존 유지) ---
|
||||||
private void HandleMovement()
|
private void HandleMovement()
|
||||||
{
|
{
|
||||||
_isGrounded = _controller.isGrounded;
|
_isGrounded = _controller.isGrounded;
|
||||||
@@ -80,7 +79,6 @@ public class PlayerNetworkController : NetworkBehaviour
|
|||||||
_moveInput = _inputActions.Player.Move.ReadValue<Vector2>();
|
_moveInput = _inputActions.Player.Move.ReadValue<Vector2>();
|
||||||
Vector3 move = new Vector3(_moveInput.x, 0, _moveInput.y).normalized;
|
Vector3 move = new Vector3(_moveInput.x, 0, _moveInput.y).normalized;
|
||||||
|
|
||||||
// 지상/공중 및 공격 중 관성 처리
|
|
||||||
if (isAttacking) move = _isGrounded ? Vector3.zero : _currentMoveDir;
|
if (isAttacking) move = _isGrounded ? Vector3.zero : _currentMoveDir;
|
||||||
else if (move.magnitude > 0.1f) _currentMoveDir = move;
|
else if (move.magnitude > 0.1f) _currentMoveDir = move;
|
||||||
|
|
||||||
@@ -93,7 +91,6 @@ public class PlayerNetworkController : NetworkBehaviour
|
|||||||
}
|
}
|
||||||
_controller.Move(move * moveSpeed * Time.deltaTime);
|
_controller.Move(move * moveSpeed * Time.deltaTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
_animator.SetFloat("MoveSpeed", isAttacking && _isGrounded ? 0 : move.magnitude);
|
_animator.SetFloat("MoveSpeed", isAttacking && _isGrounded ? 0 : move.magnitude);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,49 +106,56 @@ public class PlayerNetworkController : NetworkBehaviour
|
|||||||
if (_isGrounded) _velocity.y = Mathf.Sqrt(jumpHeight * -2f * gravity);
|
if (_isGrounded) _velocity.y = Mathf.Sqrt(jumpHeight * -2f * gravity);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 기존 OnAttackServerRpc를 수정하거나 호출되는 시점에 아래 로직 포함
|
// --- 채광 로직 (기존 유지) ---
|
||||||
[Rpc(SendTo.Server, InvokePermission = RpcInvokePermission.Owner)]
|
[Rpc(SendTo.Server, InvokePermission = RpcInvokePermission.Owner)]
|
||||||
private void OnAttackServerRpc()
|
private void OnAttackServerRpc()
|
||||||
{
|
{
|
||||||
// 모든 유저에게 애니메이션 재생 신호 전송
|
|
||||||
OnAttackClientRpc();
|
OnAttackClientRpc();
|
||||||
|
|
||||||
// [채광 판정] 서버에서 물리 연산을 통해 전방의 블록 탐색
|
|
||||||
Collider[] hitBlocks = Physics.OverlapSphere(transform.position + transform.forward, attackRange, mineableLayer);
|
Collider[] hitBlocks = Physics.OverlapSphere(transform.position + transform.forward, attackRange, mineableLayer);
|
||||||
|
|
||||||
foreach (var col in hitBlocks)
|
foreach (var col in hitBlocks)
|
||||||
{
|
{
|
||||||
MineableBlock block = col.GetComponentInParent<MineableBlock>();
|
MineableBlock block = col.GetComponentInParent<MineableBlock>();
|
||||||
if (block != null)
|
if (block != null) block.TakeDamageRpc(miningDamage);
|
||||||
{
|
|
||||||
// 서버가 직접 블록의 대미지 함수 호출
|
|
||||||
block.TakeDamageRpc(miningDamage);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[ClientRpc]
|
[ClientRpc]
|
||||||
private void OnAttackClientRpc()
|
private void OnAttackClientRpc() => _animator.SetTrigger("Attack");
|
||||||
{
|
|
||||||
_animator.SetTrigger("Attack");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnInteractTap()
|
private void OnInteractTap()
|
||||||
{
|
{
|
||||||
|
if (!IsOwner) return;
|
||||||
_animator.SetTrigger("Interact");
|
_animator.SetTrigger("Interact");
|
||||||
|
|
||||||
|
// 1. 캐릭터 주변 반경 내의 모든 콜라이더 감지
|
||||||
Collider[] colliders = Physics.OverlapSphere(transform.position, interactRange, interactableLayer);
|
Collider[] colliders = Physics.OverlapSphere(transform.position, interactRange, interactableLayer);
|
||||||
|
|
||||||
|
IInteractable closestTarget = null;
|
||||||
|
float minDistance = float.MaxValue;
|
||||||
|
|
||||||
foreach (var col in colliders)
|
foreach (var col in colliders)
|
||||||
|
{
|
||||||
|
// 2. 방향 조건 없이 거리만 체크하여 가장 가까운 것 선택
|
||||||
|
float dist = Vector3.Distance(transform.position, col.transform.position);
|
||||||
|
if (dist < minDistance)
|
||||||
{
|
{
|
||||||
IInteractable interactable = col.GetComponentInParent<IInteractable>();
|
IInteractable interactable = col.GetComponentInParent<IInteractable>();
|
||||||
if (interactable != null)
|
if (interactable != null)
|
||||||
{
|
{
|
||||||
interactable.Interact(gameObject);
|
minDistance = dist;
|
||||||
break;
|
closestTarget = interactable;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 3. 가장 가까운 대상이 있다면 상호작용 실행
|
||||||
|
if (closestTarget != null)
|
||||||
|
{
|
||||||
|
closestTarget.Interact(gameObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 건설 지원 로직 (범위 내 지속 작업이므로 OverlapSphere 유지 또는 Raycast로 변경 가능)
|
||||||
private void PerformConstructionSupport()
|
private void PerformConstructionSupport()
|
||||||
{
|
{
|
||||||
Collider[] targets = Physics.OverlapSphere(transform.position, interactRange, constructionLayer);
|
Collider[] targets = Physics.OverlapSphere(transform.position, interactRange, constructionLayer);
|
||||||
@@ -160,7 +164,6 @@ public class PlayerNetworkController : NetworkBehaviour
|
|||||||
ConstructionSite site = col.GetComponentInParent<ConstructionSite>();
|
ConstructionSite site = col.GetComponentInParent<ConstructionSite>();
|
||||||
if (site != null)
|
if (site != null)
|
||||||
{
|
{
|
||||||
// 건설 진행도는 서버에서 관리하는 것이 안전하므로 나중에 RPC로 전환 필요
|
|
||||||
site.AdvanceConstruction(Time.deltaTime * buildSpeedMultiplier);
|
site.AdvanceConstruction(Time.deltaTime * buildSpeedMultiplier);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -168,6 +171,6 @@ public class PlayerNetworkController : NetworkBehaviour
|
|||||||
|
|
||||||
public override void OnNetworkDespawn()
|
public override void OnNetworkDespawn()
|
||||||
{
|
{
|
||||||
if (IsOwner) _inputActions.Disable();
|
if (IsOwner && _inputActions != null) _inputActions.Disable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -14,6 +14,8 @@ public class ConstructionSite : NetworkBehaviour
|
|||||||
if (!IsServer) return;
|
if (!IsServer) return;
|
||||||
_syncTurretIndex.Value = index;
|
_syncTurretIndex.Value = index;
|
||||||
_gridPos = pos;
|
_gridPos = pos;
|
||||||
|
|
||||||
|
// BuildManager로부터 데이터 참조
|
||||||
_targetBuildTime = BuildManager.Instance.GetTurretData(index).buildTime;
|
_targetBuildTime = BuildManager.Instance.GetTurretData(index).buildTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,8 +37,8 @@ public class ConstructionSite : NetworkBehaviour
|
|||||||
_isCompleted = true;
|
_isCompleted = true;
|
||||||
var data = BuildManager.Instance.GetTurretData(_syncTurretIndex.Value);
|
var data = BuildManager.Instance.GetTurretData(_syncTurretIndex.Value);
|
||||||
|
|
||||||
// [중요] 저장된 그리드 좌표로부터 정확한 월드 좌표 복원
|
// 고스트가 있던 그 위치 그대로 생성
|
||||||
Vector3 finalPos = BuildManager.Instance.GridToWorld(_gridPos);
|
Vector3 finalPos = transform.position;
|
||||||
GameObject finalObj = Instantiate(data.finalPrefab, finalPos, Quaternion.identity);
|
GameObject finalObj = Instantiate(data.finalPrefab, finalPos, Quaternion.identity);
|
||||||
|
|
||||||
finalObj.GetComponent<NetworkObject>().Spawn();
|
finalObj.GetComponent<NetworkObject>().Spawn();
|
||||||
|
|||||||
@@ -1,41 +1,79 @@
|
|||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using Unity.Netcode;
|
|
||||||
|
|
||||||
public class TunnelNode : NetworkBehaviour, IInteractable
|
public class TunnelNode : MonoBehaviour, IInteractable
|
||||||
{
|
{
|
||||||
public TunnelNode aboveNode;
|
public bool isTop;
|
||||||
public TunnelNode belowNode;
|
[SerializeField] private TunnelNode partnerNode; // 같은 프리팹 내 반대쪽 노드
|
||||||
|
|
||||||
public override void OnNetworkSpawn()
|
// [자동 연결] 닿아있는 다른 터널의 노드
|
||||||
|
private TunnelNode neighborNode;
|
||||||
|
|
||||||
|
// 물리적으로 닿으면 자동으로 연결 고리 생성
|
||||||
|
private void OnTriggerEnter(Collider other)
|
||||||
{
|
{
|
||||||
// 모든 클라이언트에서 각자 장부에 등록
|
if (other.TryGetComponent<TunnelNode>(out var neighbor))
|
||||||
Vector3Int myPos = BuildManager.Instance.WorldToGrid3D(transform.position);
|
{
|
||||||
BuildManager.Instance.RegisterTunnel(myPos, this);
|
// 상단 노드는 하단 노드와만, 하단은 상단과만 연결 (잘못된 조립 방지)
|
||||||
|
if (this.isTop != neighbor.isTop)
|
||||||
// 모든 노드가 등록될 시간을 벌기 위해 서버에서 약간 지연 후 연결 명령
|
{
|
||||||
if (IsServer) Invoke(nameof(SyncLinks), 0.2f);
|
neighborNode = neighbor;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SyncLinks() => LinkVerticalRpc();
|
private void OnTriggerExit(Collider other)
|
||||||
|
|
||||||
[Rpc(SendTo.Everyone)]
|
|
||||||
private void LinkVerticalRpc() => LinkVertical();
|
|
||||||
|
|
||||||
public void LinkVertical()
|
|
||||||
{
|
{
|
||||||
Vector3Int myPos = BuildManager.Instance.WorldToGrid3D(transform.position);
|
if (other.TryGetComponent<TunnelNode>(out var neighbor))
|
||||||
aboveNode = BuildManager.Instance.GetTunnelAt(myPos + Vector3Int.up);
|
|
||||||
belowNode = BuildManager.Instance.GetTunnelAt(myPos + Vector3Int.down);
|
|
||||||
|
|
||||||
if (aboveNode != null) aboveNode.belowNode = this;
|
|
||||||
if (belowNode != null) belowNode.aboveNode = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Interact(GameObject user)
|
|
||||||
{
|
{
|
||||||
var traveler = user.GetComponent<TunnelTraveler>();
|
if (neighborNode == neighbor) neighborNode = null;
|
||||||
if (traveler && !traveler.IsTraveling) traveler.StartTravel(this);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetInteractionText() => "터널 이용 [E]";
|
public void Interact(GameObject player)
|
||||||
|
{
|
||||||
|
// 내가 체인의 끝단(입구/출구)일 때만 작동
|
||||||
|
if (neighborNode != null) return;
|
||||||
|
|
||||||
|
// 반대쪽 끝 노드를 찾아서 이동 요청
|
||||||
|
TunnelNode endNode = FindUltimateEndNode();
|
||||||
|
if (player.TryGetComponent<TunnelTraveler>(out var traveler))
|
||||||
|
{
|
||||||
|
traveler.StartTravel(endNode.transform.position, !endNode.isTop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private TunnelNode FindUltimateEndNode()
|
||||||
|
{
|
||||||
|
TunnelNode current = this;
|
||||||
|
|
||||||
|
// [연결 고리 추적] 파트너 -> 이웃 -> 파트너 -> 이웃...
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
// 1. 현재 노드의 프리팹 반대쪽(파트너)으로 이동
|
||||||
|
TunnelNode partner = current.partnerNode;
|
||||||
|
|
||||||
|
// 2. 파트너와 연결된 다음 터널의 노드가 있는지 확인
|
||||||
|
if (partner.neighborNode != null)
|
||||||
|
{
|
||||||
|
current = partner.neighborNode; // 다음 터널로 점프
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return partner; // 더 이상 연결이 없으면 파트너가 최종 목적지
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 디버깅: 연결 상태를 씬 뷰에서 확인
|
||||||
|
private void OnDrawGizmos()
|
||||||
|
{
|
||||||
|
Gizmos.color = isTop ? Color.cyan : Color.blue;
|
||||||
|
Gizmos.DrawWireSphere(transform.position, 0.2f);
|
||||||
|
|
||||||
|
if (neighborNode != null)
|
||||||
|
{
|
||||||
|
Gizmos.color = Color.red;
|
||||||
|
Gizmos.DrawLine(transform.position, neighborNode.transform.position);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
public class TunnelTraveler : MonoBehaviour
|
public class TunnelTraveler : MonoBehaviour
|
||||||
{
|
{
|
||||||
@@ -8,58 +7,33 @@ public class TunnelTraveler : MonoBehaviour
|
|||||||
private bool _isTraveling;
|
private bool _isTraveling;
|
||||||
public bool IsTraveling => _isTraveling;
|
public bool IsTraveling => _isTraveling;
|
||||||
|
|
||||||
public void StartTravel(TunnelNode start)
|
public void StartTravel(Vector3 targetPos, bool isBottom)
|
||||||
{
|
{
|
||||||
if (_isTraveling) return;
|
if (_isTraveling) return;
|
||||||
|
|
||||||
// [디버그 1] 상호작용한 노드의 연결 상태 확인
|
// 하단 노드가 목적지라면 캐릭터 중심 좌표를 고려해 약간 더 아래(바닥)로 설정
|
||||||
Debug.Log($"<color=white>[Travel] 시작 노드: {start.name} | 위: {(start.aboveNode != null)} | 아래: {(start.belowNode != null)}</color>");
|
Vector3 finalDestination = isBottom ? targetPos + Vector3.down * 0.5f : targetPos;
|
||||||
|
StartCoroutine(TravelRoutine(finalDestination));
|
||||||
List<Vector3> path = new List<Vector3>();
|
|
||||||
|
|
||||||
// 이동 방향 결정 (아래가 있으면 아래로, 없으면 위로)
|
|
||||||
bool goDown = start.belowNode != null;
|
|
||||||
TunnelNode curr = goDown ? start.belowNode : start.aboveNode;
|
|
||||||
|
|
||||||
while (curr != null)
|
|
||||||
{
|
|
||||||
path.Add(curr.transform.position);
|
|
||||||
curr = goDown ? curr.belowNode : curr.aboveNode;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// [디버그 2] 최종 경로 개수 확인
|
private IEnumerator TravelRoutine(Vector3 destination)
|
||||||
Debug.Log($"<color=yellow>[Travel] 생성된 경로 포인트 개수: {path.Count}</color>");
|
|
||||||
|
|
||||||
if (path.Count > 0)
|
|
||||||
{
|
{
|
||||||
StartCoroutine(Travel(path));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Debug.LogWarning("<color=red>[Travel] 이동할 경로가 없습니다! 노드 연결을 확인하세요.</color>");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private IEnumerator Travel(List<Vector3> path)
|
|
||||||
{
|
|
||||||
Debug.Log("<color=green>[Travel] 코루틴 이동 시작!</color>");
|
|
||||||
_isTraveling = true;
|
_isTraveling = true;
|
||||||
var cc = GetComponent<CharacterController>();
|
var cc = GetComponent<CharacterController>();
|
||||||
if (cc) cc.enabled = false;
|
if (cc) cc.enabled = false;
|
||||||
|
|
||||||
foreach (var point in path)
|
// X, Z는 유지한 채 Y축 중심으로 이동
|
||||||
{
|
float halfHeight = cc ? cc.height / 2f : 0f;
|
||||||
Vector3 target = new Vector3(point.x, point.y - (cc ? cc.height / 2 : 0), point.z);
|
Vector3 target = new Vector3(destination.x, destination.y - halfHeight, destination.z);
|
||||||
while (Vector3.Distance(transform.position, target) > 0.1f)
|
|
||||||
|
while (Vector3.Distance(transform.position, target) > 0.05f)
|
||||||
{
|
{
|
||||||
transform.position = Vector3.MoveTowards(transform.position, target, travelSpeed * Time.deltaTime);
|
transform.position = Vector3.MoveTowards(transform.position, target, travelSpeed * Time.deltaTime);
|
||||||
yield return null;
|
yield return null;
|
||||||
}
|
}
|
||||||
transform.position = target;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
transform.position = target;
|
||||||
if (cc) cc.enabled = true;
|
if (cc) cc.enabled = true;
|
||||||
_isTraveling = false;
|
_isTraveling = false;
|
||||||
Debug.Log("<color=green>[Travel] 이동 완료!</color>");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -21,7 +21,7 @@ TagManager:
|
|||||||
- Interactable
|
- Interactable
|
||||||
- Tunnel
|
- Tunnel
|
||||||
- Mineable
|
- Mineable
|
||||||
-
|
- TunnelNode
|
||||||
-
|
-
|
||||||
-
|
-
|
||||||
-
|
-
|
||||||
|
|||||||
Reference in New Issue
Block a user