건설 인터랙션 관련 버그 수정 및 건설 데이터 구조 개선
건설 인터랙션 시 움직이지 못하는 문제 수정 2개로 분리되어 있던 타워 데이터를 하나로 통합 - 대신 타워가 아닌 건물도 공격력 등을 정의할 수 있음
This commit is contained in:
@@ -72,8 +72,6 @@
|
|||||||
<Compile Include="Assets\Data\Scripts\DataClasses\PlayerData.cs" />
|
<Compile Include="Assets\Data\Scripts\DataClasses\PlayerData.cs" />
|
||||||
<Compile Include="Assets\Scripts\TeamManager.cs" />
|
<Compile Include="Assets\Scripts\TeamManager.cs" />
|
||||||
<Compile Include="Assets\Scripts\TeamGate.cs" />
|
<Compile Include="Assets\Scripts\TeamGate.cs" />
|
||||||
<Compile Include="Assets\Scripts\BuildingData.cs" />
|
|
||||||
<Compile Include="Assets\Scripts\TowerDataComponent.cs" />
|
|
||||||
<Compile Include="Assets\Scripts\Resource.cs" />
|
<Compile Include="Assets\Scripts\Resource.cs" />
|
||||||
<Compile Include="Assets\Scripts\AutoTargetSystem.cs" />
|
<Compile Include="Assets\Scripts\AutoTargetSystem.cs" />
|
||||||
<Compile Include="Assets\Scripts\BuildingSlotButton.cs" />
|
<Compile Include="Assets\Scripts\BuildingSlotButton.cs" />
|
||||||
@@ -87,7 +85,6 @@
|
|||||||
<Compile Include="Assets\InputSystem_Actions.cs" />
|
<Compile Include="Assets\InputSystem_Actions.cs" />
|
||||||
<Compile Include="Assets\FlatKit\Demos\[Demo] Desert\Scripts\BillboardLineRendererCircle.cs" />
|
<Compile Include="Assets\FlatKit\Demos\[Demo] Desert\Scripts\BillboardLineRendererCircle.cs" />
|
||||||
<Compile Include="Assets\Scripts\BuildingHealthBar.cs" />
|
<Compile Include="Assets\Scripts\BuildingHealthBar.cs" />
|
||||||
<Compile Include="Assets\Scripts\CameraZFollow.cs" />
|
|
||||||
<Compile Include="Assets\Scripts\PlayerSpawnPoint.cs" />
|
<Compile Include="Assets\Scripts\PlayerSpawnPoint.cs" />
|
||||||
<Compile Include="Assets\FlatKit\Demos\Common\Scripts\AutoLoadPipelineAsset.cs" />
|
<Compile Include="Assets\FlatKit\Demos\Common\Scripts\AutoLoadPipelineAsset.cs" />
|
||||||
<Compile Include="Assets\Scripts\AutoHost.cs" />
|
<Compile Include="Assets\Scripts\AutoHost.cs" />
|
||||||
|
|||||||
@@ -15,38 +15,21 @@ MonoBehaviour:
|
|||||||
buildingName: "\uD0C0\uC6CC"
|
buildingName: "\uD0C0\uC6CC"
|
||||||
prefab: {fileID: 8512676738329978770, guid: 3f7838db2c2fc424d9bd9a0d243b43be, type: 3}
|
prefab: {fileID: 8512676738329978770, guid: 3f7838db2c2fc424d9bd9a0d243b43be, type: 3}
|
||||||
icon: {fileID: 0}
|
icon: {fileID: 0}
|
||||||
width: 3
|
|
||||||
length: 3
|
|
||||||
height: 3
|
|
||||||
placementOffset: {x: 0, y: 0, z: 0}
|
|
||||||
allowRotation: 1
|
|
||||||
requiredWorkAmount: 10
|
|
||||||
workPerInteraction: 10
|
|
||||||
interactionCooldown: 1
|
|
||||||
constructionAnimationTrigger: Build
|
|
||||||
constructionEquipment:
|
|
||||||
socketName: RightHand
|
|
||||||
equipmentPrefab: {fileID: 0}
|
|
||||||
attachOnStart: 1
|
|
||||||
detachOnEnd: 1
|
|
||||||
keepEquipped: 0
|
|
||||||
attachDelay: 0
|
|
||||||
detachDelay: 0
|
|
||||||
maxHealth: 50
|
|
||||||
isIndestructible: 0
|
|
||||||
autoRegenerate: 0
|
|
||||||
regenPerSecond: 1
|
|
||||||
providesVision: 1
|
|
||||||
visionRange: 10
|
|
||||||
id: 1
|
id: 1
|
||||||
memo: "\uD0C0\uC6CC"
|
memo: "\uD0C0\uC6CC"
|
||||||
mana: 25
|
mana: 25
|
||||||
manpower: 10
|
|
||||||
sizeX: 3
|
sizeX: 3
|
||||||
sizeY: 3
|
sizeY: 3
|
||||||
sizeZ: 3
|
sizeZ: 3
|
||||||
|
placementOffset: {x: 0, y: 0, z: 0}
|
||||||
|
allowRotation: 1
|
||||||
|
manpower: 10
|
||||||
maxHp: 50
|
maxHp: 50
|
||||||
|
isIndestructible: 0
|
||||||
|
autoRegenerate: 0
|
||||||
|
regenPerSecond: 1
|
||||||
atkRange: 10
|
atkRange: 10
|
||||||
|
providesVision: 1
|
||||||
atkDamage: 5
|
atkDamage: 5
|
||||||
atkIntervalSec: 2
|
atkIntervalSec: 2
|
||||||
modelPath: Assets/Models/building_tower_B_blue.fbx
|
modelPath: Assets/Models/building_tower_B_blue.fbx
|
||||||
|
|||||||
@@ -15,38 +15,21 @@ MonoBehaviour:
|
|||||||
buildingName: "\uBCBD"
|
buildingName: "\uBCBD"
|
||||||
prefab: {fileID: 3671057791414486316, guid: ae9a9b515e1792a45887f0d967b943d6, type: 3}
|
prefab: {fileID: 3671057791414486316, guid: ae9a9b515e1792a45887f0d967b943d6, type: 3}
|
||||||
icon: {fileID: 0}
|
icon: {fileID: 0}
|
||||||
width: 2
|
|
||||||
length: 2
|
|
||||||
height: 1
|
|
||||||
placementOffset: {x: 0, y: 0, z: 0}
|
|
||||||
allowRotation: 1
|
|
||||||
requiredWorkAmount: 5
|
|
||||||
workPerInteraction: 10
|
|
||||||
interactionCooldown: 1
|
|
||||||
constructionAnimationTrigger: Build
|
|
||||||
constructionEquipment:
|
|
||||||
socketName: RightHand
|
|
||||||
equipmentPrefab: {fileID: 0}
|
|
||||||
attachOnStart: 1
|
|
||||||
detachOnEnd: 1
|
|
||||||
keepEquipped: 0
|
|
||||||
attachDelay: 0
|
|
||||||
detachDelay: 0
|
|
||||||
maxHealth: 30
|
|
||||||
isIndestructible: 0
|
|
||||||
autoRegenerate: 0
|
|
||||||
regenPerSecond: 1
|
|
||||||
providesVision: 1
|
|
||||||
visionRange: 0
|
|
||||||
id: 2
|
id: 2
|
||||||
memo: "\uBCBD"
|
memo: "\uBCBD"
|
||||||
mana: 5
|
mana: 5
|
||||||
|
sizeX: 4
|
||||||
|
sizeY: 3
|
||||||
|
sizeZ: 2
|
||||||
|
placementOffset: {x: 0, y: 0, z: 0}
|
||||||
|
allowRotation: 1
|
||||||
manpower: 5
|
manpower: 5
|
||||||
sizeX: 2
|
|
||||||
sizeY: 2
|
|
||||||
sizeZ: 1
|
|
||||||
maxHp: 30
|
maxHp: 30
|
||||||
|
isIndestructible: 0
|
||||||
|
autoRegenerate: 0
|
||||||
|
regenPerSecond: 1
|
||||||
atkRange: 0
|
atkRange: 0
|
||||||
|
providesVision: 1
|
||||||
atkDamage: 0
|
atkDamage: 0
|
||||||
atkIntervalSec: 0
|
atkIntervalSec: 0
|
||||||
modelPath: Assets/Models/wall_straight.fbx
|
modelPath: Assets/Models/wall_straight.fbx
|
||||||
|
|||||||
@@ -1,72 +1,83 @@
|
|||||||
// 이 파일은 자동 생성되었습니다. 직접 수정하지 마세요!
|
|
||||||
// 생성 스크립트: DataTools/generate_csharp_classes.py
|
|
||||||
|
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using System.Collections.Generic; // 리스트 지원을 위해 추가
|
|
||||||
using Northbound;
|
|
||||||
|
|
||||||
namespace Northbound.Data
|
namespace Northbound.Data
|
||||||
{
|
{
|
||||||
[CreateAssetMenu(fileName = "TowerData", menuName = "Northbound/Tower Data")]
|
[CreateAssetMenu(fileName = "TowerData", menuName = "Northbound/Tower Data")]
|
||||||
public class TowerData : BuildingData
|
public class TowerData : ScriptableObject
|
||||||
{
|
{
|
||||||
[Header("기본 정보")]
|
[Header("Building Info")]
|
||||||
/// <summary>고유 ID</summary>
|
public string buildingName;
|
||||||
|
public GameObject prefab;
|
||||||
|
[Tooltip("UI에 표시될 건물 아이콘")]
|
||||||
|
public Sprite icon;
|
||||||
|
|
||||||
|
[Header("Basic Info")]
|
||||||
|
[Tooltip("고유 ID")]
|
||||||
public int id;
|
public int id;
|
||||||
/// <summary>기획 메모</summary>
|
[Tooltip("기획 메모")]
|
||||||
public string memo;
|
public string memo;
|
||||||
/// <summary>건설 비용 (mana=20) (mana=50; iron=10)</summary>
|
[Tooltip("건설 비용")]
|
||||||
public int mana;
|
public int mana;
|
||||||
/// <summary>건설 노동량</summary>
|
|
||||||
public float manpower;
|
[Header("Grid Size")]
|
||||||
/// <summary>X 그리드 차지 공간</summary>
|
[Tooltip("X 그리드 차지 공간")]
|
||||||
public int sizeX;
|
public int sizeX = 1;
|
||||||
/// <summary>Y 그리드 차지 공간</summary>
|
[Tooltip("Y 그리드 차지 공간")]
|
||||||
public int sizeY;
|
public int sizeY = 1;
|
||||||
/// <summary>Z 차지 공간</summary>
|
[Tooltip("Z 차지 공간")]
|
||||||
public int sizeZ;
|
public int sizeZ = 2;
|
||||||
/// <summary>체력</summary>
|
|
||||||
public int maxHp;
|
[Header("Placement Settings")]
|
||||||
/// <summary>사정거리</summary>
|
[Tooltip("Offset from grid position")]
|
||||||
public int atkRange;
|
public Vector3 placementOffset = Vector3.zero;
|
||||||
/// <summary>데미지</summary>
|
[Tooltip("Can rotate this building?")]
|
||||||
public int atkDamage;
|
public bool allowRotation = true;
|
||||||
/// <summary>공격 주기</summary>
|
|
||||||
public float atkIntervalSec;
|
[Header("Construction Settings")]
|
||||||
/// <summary>모델 경로</summary>
|
[Tooltip("건설 완료에 필요한 총 작업량")]
|
||||||
|
public float manpower = 100f;
|
||||||
|
|
||||||
|
[Header("Health Settings")]
|
||||||
|
[Tooltip("체력")]
|
||||||
|
public int maxHp = 100;
|
||||||
|
[Tooltip("Can this building be damaged?")]
|
||||||
|
public bool isIndestructible = false;
|
||||||
|
[Tooltip("Auto-regenerate health over time")]
|
||||||
|
public bool autoRegenerate = false;
|
||||||
|
[Tooltip("Health regeneration per second")]
|
||||||
|
public int regenPerSecond = 1;
|
||||||
|
|
||||||
|
[Header("Vision Settings")]
|
||||||
|
[Tooltip("사정거리")]
|
||||||
|
public int atkRange = 15;
|
||||||
|
[Tooltip("Does this building provide vision?")]
|
||||||
|
public bool providesVision = true;
|
||||||
|
|
||||||
|
[Header("Attack Settings")]
|
||||||
|
[Tooltip("데미지")]
|
||||||
|
public int atkDamage = 10;
|
||||||
|
[Tooltip("공격 주기")]
|
||||||
|
public float atkIntervalSec = 1f;
|
||||||
|
|
||||||
|
[Header("Model Settings")]
|
||||||
|
[Tooltip("모델 경로")]
|
||||||
public string modelPath;
|
public string modelPath;
|
||||||
|
|
||||||
private bool fieldsSynced = false;
|
[Header("Properties for convenience")]
|
||||||
|
public int width => sizeX;
|
||||||
|
public int length => sizeY;
|
||||||
|
public float height => sizeZ;
|
||||||
|
public int maxHealth => maxHp;
|
||||||
|
public float visionRange => atkRange;
|
||||||
|
public float requiredWorkAmount => manpower;
|
||||||
|
|
||||||
private void Awake()
|
public Vector3 GetSize(int rotation)
|
||||||
{
|
{
|
||||||
SyncFields();
|
// Rotation 0,180 = normal, 90,270 = swap width/length
|
||||||
}
|
bool isRotated = (rotation == 1 || rotation == 3);
|
||||||
|
float w = isRotated ? length : width;
|
||||||
private void SyncFields()
|
float l = isRotated ? width : length;
|
||||||
{
|
return new Vector3(w, height, l);
|
||||||
if (fieldsSynced) return;
|
|
||||||
fieldsSynced = true;
|
|
||||||
|
|
||||||
// Map TowerData fields to BuildingData fields
|
|
||||||
if (string.IsNullOrEmpty(base.buildingName))
|
|
||||||
{
|
|
||||||
base.buildingName = memo;
|
|
||||||
}
|
|
||||||
base.width = sizeX;
|
|
||||||
base.length = sizeY;
|
|
||||||
base.height = sizeZ;
|
|
||||||
base.maxHealth = maxHp;
|
|
||||||
base.visionRange = atkRange;
|
|
||||||
base.requiredWorkAmount = manpower;
|
|
||||||
base.workPerInteraction = 10f;
|
|
||||||
base.interactionCooldown =1f;
|
|
||||||
base.providesVision = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void EnsureSynced()
|
|
||||||
{
|
|
||||||
SyncFields();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ MonoBehaviour:
|
|||||||
m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3}
|
m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3}
|
||||||
m_Name:
|
m_Name:
|
||||||
m_EditorClassIdentifier: Unity.Netcode.Runtime::Unity.Netcode.NetworkObject
|
m_EditorClassIdentifier: Unity.Netcode.Runtime::Unity.Netcode.NetworkObject
|
||||||
GlobalObjectIdHash: 1879467101
|
GlobalObjectIdHash: 4292538740
|
||||||
InScenePlacedSourceGlobalObjectIdHash: 4292538740
|
InScenePlacedSourceGlobalObjectIdHash: 4292538740
|
||||||
DeferredDespawnTick: 0
|
DeferredDespawnTick: 0
|
||||||
Ownership: 1
|
Ownership: 1
|
||||||
@@ -75,6 +75,15 @@ MonoBehaviour:
|
|||||||
buildingData: {fileID: 11400000, guid: 23c12a82ea534b34299700b86fffd524, type: 2}
|
buildingData: {fileID: 11400000, guid: 23c12a82ea534b34299700b86fffd524, type: 2}
|
||||||
gridPosition: {x: 0, y: 0, z: 0}
|
gridPosition: {x: 0, y: 0, z: 0}
|
||||||
rotation: 0
|
rotation: 0
|
||||||
|
constructionAnimationTrigger: Mining
|
||||||
|
constructionEquipment:
|
||||||
|
socketName: handslot.r
|
||||||
|
equipmentPrefab: {fileID: 919132149155446097, guid: 804d477fc7f114c498aa6f95452be893, type: 3}
|
||||||
|
attachOnStart: 1
|
||||||
|
detachOnEnd: 1
|
||||||
|
keepEquipped: 0
|
||||||
|
attachDelay: 0
|
||||||
|
detachDelay: 0
|
||||||
foundationVisual: {fileID: 2851644658348875061}
|
foundationVisual: {fileID: 2851644658348875061}
|
||||||
progressBarPrefab: {fileID: 0}
|
progressBarPrefab: {fileID: 0}
|
||||||
--- !u!1001 &3121692064363536484
|
--- !u!1001 &3121692064363536484
|
||||||
|
|||||||
@@ -195,7 +195,6 @@ GameObject:
|
|||||||
- component: {fileID: 100877884298911200}
|
- component: {fileID: 100877884298911200}
|
||||||
- component: {fileID: 8485093670801034058}
|
- component: {fileID: 8485093670801034058}
|
||||||
- component: {fileID: 7262612124217315611}
|
- component: {fileID: 7262612124217315611}
|
||||||
- component: {fileID: 3089566480349729}
|
|
||||||
m_Layer: 0
|
m_Layer: 0
|
||||||
m_Name: Tower1
|
m_Name: Tower1
|
||||||
m_TagString: Untagged
|
m_TagString: Untagged
|
||||||
@@ -327,16 +326,3 @@ MonoBehaviour:
|
|||||||
minHeightForDistantVisibility: 3
|
minHeightForDistantVisibility: 3
|
||||||
useExploredMaterial: 0
|
useExploredMaterial: 0
|
||||||
exploredMaterial: {fileID: 0}
|
exploredMaterial: {fileID: 0}
|
||||||
--- !u!114 &3089566480349729
|
|
||||||
MonoBehaviour:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 8512676738329978770}
|
|
||||||
m_Enabled: 1
|
|
||||||
m_EditorHideFlags: 0
|
|
||||||
m_Script: {fileID: 11500000, guid: 56c4536effc49fe47af593bf9d17e979, type: 3}
|
|
||||||
m_Name:
|
|
||||||
m_EditorClassIdentifier: Assembly-CSharp::Northbound.TowerDataComponent
|
|
||||||
towerData: {fileID: 11400000, guid: 3e2e145df85a3ee4eb615f87efba4554, type: 2}
|
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ GameObject:
|
|||||||
- component: {fileID: 2615519446934682856}
|
- component: {fileID: 2615519446934682856}
|
||||||
- component: {fileID: 3203720634638459019}
|
- component: {fileID: 3203720634638459019}
|
||||||
- component: {fileID: 3906797260079127802}
|
- component: {fileID: 3906797260079127802}
|
||||||
- component: {fileID: 3692219876976097854}
|
|
||||||
m_Layer: 0
|
m_Layer: 0
|
||||||
m_Name: Tower2
|
m_Name: Tower2
|
||||||
m_TagString: Untagged
|
m_TagString: Untagged
|
||||||
@@ -82,8 +81,8 @@ BoxCollider:
|
|||||||
m_ProvidesContacts: 0
|
m_ProvidesContacts: 0
|
||||||
m_Enabled: 1
|
m_Enabled: 1
|
||||||
serializedVersion: 3
|
serializedVersion: 3
|
||||||
m_Size: {x: 2, y: 1, z: 2}
|
m_Size: {x: 4, y: 2, z: 3}
|
||||||
m_Center: {x: 0, y: 0.5, z: 0}
|
m_Center: {x: 0, y: 1, z: 0}
|
||||||
--- !u!208 &2615519446934682856
|
--- !u!208 &2615519446934682856
|
||||||
NavMeshObstacle:
|
NavMeshObstacle:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
@@ -94,11 +93,11 @@ NavMeshObstacle:
|
|||||||
m_Enabled: 1
|
m_Enabled: 1
|
||||||
serializedVersion: 3
|
serializedVersion: 3
|
||||||
m_Shape: 1
|
m_Shape: 1
|
||||||
m_Extents: {x: 1, y: 0.5, z: 1}
|
m_Extents: {x: 2, y: 1, z: 1.5}
|
||||||
m_MoveThreshold: 0.1
|
m_MoveThreshold: 0.1
|
||||||
m_Carve: 0
|
m_Carve: 0
|
||||||
m_CarveOnlyStationary: 1
|
m_CarveOnlyStationary: 1
|
||||||
m_Center: {x: 0, y: 0.5, z: 0}
|
m_Center: {x: 0, y: 1, z: 0}
|
||||||
m_TimeToStationary: 0.5
|
m_TimeToStationary: 0.5
|
||||||
--- !u!114 &3203720634638459019
|
--- !u!114 &3203720634638459019
|
||||||
MonoBehaviour:
|
MonoBehaviour:
|
||||||
@@ -146,19 +145,6 @@ MonoBehaviour:
|
|||||||
minHeightForDistantVisibility: 3
|
minHeightForDistantVisibility: 3
|
||||||
useExploredMaterial: 0
|
useExploredMaterial: 0
|
||||||
exploredMaterial: {fileID: 0}
|
exploredMaterial: {fileID: 0}
|
||||||
--- !u!114 &3692219876976097854
|
|
||||||
MonoBehaviour:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 3671057791414486316}
|
|
||||||
m_Enabled: 1
|
|
||||||
m_EditorHideFlags: 0
|
|
||||||
m_Script: {fileID: 11500000, guid: 56c4536effc49fe47af593bf9d17e979, type: 3}
|
|
||||||
m_Name:
|
|
||||||
m_EditorClassIdentifier: Assembly-CSharp::Northbound.TowerDataComponent
|
|
||||||
towerData: {fileID: 11400000, guid: 03a521eb1160745439ba2d0efeb12f3c, type: 2}
|
|
||||||
--- !u!1 &8947776510381915047
|
--- !u!1 &8947776510381915047
|
||||||
GameObject:
|
GameObject:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
@@ -187,7 +173,7 @@ Transform:
|
|||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||||
m_LocalScale: {x: 2, y: 1, z: 2}
|
m_LocalScale: {x: 4, y: 2, z: 3}
|
||||||
m_ConstrainProportionsScale: 0
|
m_ConstrainProportionsScale: 0
|
||||||
m_Children: []
|
m_Children: []
|
||||||
m_Father: {fileID: 1657799771882240}
|
m_Father: {fileID: 1657799771882240}
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
using System;
|
using System;
|
||||||
using Unity.Netcode;
|
using Unity.Netcode;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
using Northbound.Data;
|
||||||
|
|
||||||
namespace Northbound
|
namespace Northbound
|
||||||
{
|
{
|
||||||
public class Building : NetworkBehaviour, IDamageable, IVisionProvider, ITeamMember
|
public class Building : NetworkBehaviour, IDamageable, IVisionProvider, ITeamMember
|
||||||
{
|
{
|
||||||
[Header("References")]
|
[Header("References")]
|
||||||
public BuildingData buildingData;
|
public TowerData buildingData;
|
||||||
|
|
||||||
[Header("Runtime Info")]
|
[Header("Runtime Info")]
|
||||||
public Vector3Int gridPosition;
|
public Vector3Int gridPosition;
|
||||||
@@ -152,7 +153,7 @@ namespace Northbound
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 건물 초기화 (BuildingManager가 동적 생성 시 호출)
|
/// 건물 초기화 (BuildingManager가 동적 생성 시 호출)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void Initialize(BuildingData data, Vector3Int gridPos, int rot, ulong ownerId, TeamType team = TeamType.Player)
|
public void Initialize(TowerData data, Vector3Int gridPos, int rot, ulong ownerId, TeamType team = TeamType.Player)
|
||||||
{
|
{
|
||||||
buildingData = data;
|
buildingData = data;
|
||||||
gridPosition = gridPos;
|
gridPosition = gridPos;
|
||||||
@@ -462,7 +463,7 @@ namespace Northbound
|
|||||||
#region Grid Bounds
|
#region Grid Bounds
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the grid-based bounds (from BuildingData width/length/height)
|
/// Gets the grid-based bounds (from TowerData width/length/height)
|
||||||
/// This is used for placement validation, NOT the actual collider bounds
|
/// This is used for placement validation, NOT the actual collider bounds
|
||||||
/// Bounds are slightly shrunk to allow adjacent buildings to touch
|
/// Bounds are slightly shrunk to allow adjacent buildings to touch
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -1,65 +0,0 @@
|
|||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace Northbound
|
|
||||||
{
|
|
||||||
[CreateAssetMenu(fileName = "NewBuilding", menuName = "Northbound/Building Data")]
|
|
||||||
public class BuildingData : ScriptableObject
|
|
||||||
{
|
|
||||||
[Header("Building Info")]
|
|
||||||
public string buildingName;
|
|
||||||
public GameObject prefab;
|
|
||||||
[Tooltip("UI에 표시될 건물 아이콘")]
|
|
||||||
public Sprite icon;
|
|
||||||
|
|
||||||
[Header("Grid Size")]
|
|
||||||
[Tooltip("Width in grid units")]
|
|
||||||
public int width = 1;
|
|
||||||
[Tooltip("Length in grid units")]
|
|
||||||
public int length = 1;
|
|
||||||
[Tooltip("Height for placement validation")]
|
|
||||||
public float height = 2f;
|
|
||||||
|
|
||||||
[Header("Placement Settings")]
|
|
||||||
[Tooltip("Offset from grid position")]
|
|
||||||
public Vector3 placementOffset = Vector3.zero;
|
|
||||||
[Tooltip("Can rotate this building?")]
|
|
||||||
public bool allowRotation = true;
|
|
||||||
|
|
||||||
[Header("Construction Settings")]
|
|
||||||
[Tooltip("건설 완료에 필요한 총 작업량")]
|
|
||||||
public float requiredWorkAmount = 100f;
|
|
||||||
[Tooltip("1회 상호작용당 작업량")]
|
|
||||||
public float workPerInteraction = 10f;
|
|
||||||
[Tooltip("상호작용 쿨다운 (초)")]
|
|
||||||
public float interactionCooldown = 1f;
|
|
||||||
[Tooltip("건설 시 플레이어가 재생할 애니메이션 트리거 (예: Build, Hammer, Construct)")]
|
|
||||||
public string constructionAnimationTrigger = "Build";
|
|
||||||
[Tooltip("건설 시 사용할 도구 (선택사항)")]
|
|
||||||
public EquipmentData constructionEquipment;
|
|
||||||
|
|
||||||
[Header("Health Settings")]
|
|
||||||
[Tooltip("Maximum health of the building")]
|
|
||||||
public int maxHealth = 100;
|
|
||||||
[Tooltip("Can this building be damaged?")]
|
|
||||||
public bool isIndestructible = false;
|
|
||||||
[Tooltip("Auto-regenerate health over time")]
|
|
||||||
public bool autoRegenerate = false;
|
|
||||||
[Tooltip("Health regeneration per second")]
|
|
||||||
public int regenPerSecond = 1;
|
|
||||||
|
|
||||||
[Header("Vision Settings")]
|
|
||||||
[Tooltip("Does this building provide vision?")]
|
|
||||||
public bool providesVision = true;
|
|
||||||
[Tooltip("Vision range in world units")]
|
|
||||||
public float visionRange = 15f;
|
|
||||||
|
|
||||||
public Vector3 GetSize(int rotation)
|
|
||||||
{
|
|
||||||
// Rotation 0,180 = normal, 90,270 = swap width/length
|
|
||||||
bool isRotated = (rotation == 1 || rotation == 3);
|
|
||||||
float w = isRotated ? length : width;
|
|
||||||
float l = isRotated ? width : length;
|
|
||||||
return new Vector3(w, height, l);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 937e64980d44d6b46acb35b8046adf34
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using Unity.Netcode;
|
using Unity.Netcode;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
using Northbound.Data;
|
||||||
|
|
||||||
namespace Northbound
|
namespace Northbound
|
||||||
{
|
{
|
||||||
@@ -9,11 +10,19 @@ namespace Northbound
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class BuildingFoundation : NetworkBehaviour, IInteractable, ITeamMember
|
public class BuildingFoundation : NetworkBehaviour, IInteractable, ITeamMember
|
||||||
{
|
{
|
||||||
[Header("Building Info")]
|
[Header("Target Building")]
|
||||||
public BuildingData buildingData;
|
public TowerData buildingData;
|
||||||
public Vector3Int gridPosition;
|
public Vector3Int gridPosition;
|
||||||
public int rotation;
|
public int rotation;
|
||||||
|
|
||||||
|
[Header("Construction Settings")]
|
||||||
|
[Tooltip("상호작용 쿨다운 (초)")]
|
||||||
|
public float interactionCooldown = 1f;
|
||||||
|
[Tooltip("건설 시 플레이어가 재생할 애니메이션 트리거")]
|
||||||
|
public string constructionAnimationTrigger = "Build";
|
||||||
|
[Tooltip("건설 시 사용할 도구 (선택사항)")]
|
||||||
|
public EquipmentData constructionEquipment;
|
||||||
|
|
||||||
[Header("Visual")]
|
[Header("Visual")]
|
||||||
public GameObject foundationVisual;
|
public GameObject foundationVisual;
|
||||||
public GameObject progressBarPrefab;
|
public GameObject progressBarPrefab;
|
||||||
@@ -73,7 +82,7 @@ namespace Northbound
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 토대 초기화
|
/// 토대 초기화
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void Initialize(BuildingData data, Vector3Int pos, int rot, ulong ownerId, TeamType team)
|
public void Initialize(TowerData data, Vector3Int pos, int rot, ulong ownerId, TeamType team)
|
||||||
{
|
{
|
||||||
if (!IsServer) return;
|
if (!IsServer) return;
|
||||||
|
|
||||||
@@ -84,7 +93,7 @@ namespace Northbound
|
|||||||
_team.Value = team;
|
_team.Value = team;
|
||||||
_currentProgress.Value = 0f;
|
_currentProgress.Value = 0f;
|
||||||
|
|
||||||
// BuildingData의 크기를 기반으로 스케일 설정
|
// TowerData의 크기를 기반으로 스케일 설정
|
||||||
Vector3 size = data.GetSize(rot);
|
Vector3 size = data.GetSize(rot);
|
||||||
|
|
||||||
// foundationVisual의 스케일만 조정 (토대 자체의 pivot은 중앙에 유지)
|
// foundationVisual의 스케일만 조정 (토대 자체의 pivot은 중앙에 유지)
|
||||||
@@ -127,19 +136,19 @@ namespace Northbound
|
|||||||
|
|
||||||
public bool CanInteract(ulong playerId)
|
public bool CanInteract(ulong playerId)
|
||||||
{
|
{
|
||||||
if (buildingData == null)
|
|
||||||
{
|
|
||||||
Debug.LogWarning($"[BuildingFoundation] buildingData is null");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 이미 완성됨
|
// 이미 완성됨
|
||||||
if (_currentProgress.Value >= buildingData.requiredWorkAmount)
|
if (_currentProgress.Value >= buildingData.requiredWorkAmount)
|
||||||
{
|
{
|
||||||
Debug.Log($"[BuildingFoundation] Already completed");
|
Debug.Log($"[BuildingFoundation] Already completed");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 쿨다운 확인
|
||||||
|
if (Time.time - _lastInteractionTime < interactionCooldown)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// 같은 팀만 건설 가능 - 플레이어의 팀을 가져와서 비교
|
// 같은 팀만 건설 가능 - 플레이어의 팀을 가져와서 비교
|
||||||
TeamType playerTeam = GetPlayerTeam(playerId);
|
TeamType playerTeam = GetPlayerTeam(playerId);
|
||||||
if (playerTeam != _team.Value)
|
if (playerTeam != _team.Value)
|
||||||
@@ -153,17 +162,20 @@ namespace Northbound
|
|||||||
|
|
||||||
public void Interact(ulong playerId)
|
public void Interact(ulong playerId)
|
||||||
{
|
{
|
||||||
if (!IsServer || buildingData == null) return;
|
if (!IsServer) return;
|
||||||
|
|
||||||
if (!CanInteract(playerId))
|
if (!CanInteract(playerId))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_lastInteractionTime = Time.time;
|
_lastInteractionTime = Time.time;
|
||||||
|
|
||||||
// 건설 진행
|
// 플레이어의 작업량 가져오기
|
||||||
_currentProgress.Value += buildingData.workPerInteraction;
|
float playerWorkPower = GetPlayerWorkPower(playerId);
|
||||||
|
|
||||||
Debug.Log($"<color=green>[BuildingFoundation] 건설 진행: {_currentProgress.Value}/{buildingData.requiredWorkAmount} ({(_currentProgress.Value / buildingData.requiredWorkAmount * 100f):F1}%)</color>");
|
// 건설 진행
|
||||||
|
_currentProgress.Value += playerWorkPower;
|
||||||
|
|
||||||
|
Debug.Log($"<color=green>[BuildingFoundation] 건설 진행: {_currentProgress.Value}/{buildingData.requiredWorkAmount} ({(_currentProgress.Value / buildingData.requiredWorkAmount * 100f):F1}%) - 작업량: {playerWorkPower}</color>");
|
||||||
|
|
||||||
// 완성 체크
|
// 완성 체크
|
||||||
if (_currentProgress.Value >= buildingData.requiredWorkAmount)
|
if (_currentProgress.Value >= buildingData.requiredWorkAmount)
|
||||||
@@ -174,34 +186,19 @@ namespace Northbound
|
|||||||
|
|
||||||
public string GetInteractionPrompt()
|
public string GetInteractionPrompt()
|
||||||
{
|
{
|
||||||
if (buildingData == null)
|
string buildingName = buildingData != null ? buildingData.buildingName : "건물";
|
||||||
return "[E] 건설하기";
|
|
||||||
|
|
||||||
float percentage = (_currentProgress.Value / buildingData.requiredWorkAmount) * 100f;
|
float percentage = (_currentProgress.Value / buildingData.requiredWorkAmount) * 100f;
|
||||||
return $"[E] {buildingData.buildingName} 건설 ({percentage:F0}%)";
|
return $"[E] {buildingName} 건설 ({percentage:F0}%)";
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetInteractionAnimation()
|
public string GetInteractionAnimation()
|
||||||
{
|
{
|
||||||
// BuildingData에서 애니메이션 트리거 가져오기
|
return constructionAnimationTrigger;
|
||||||
if (buildingData != null && !string.IsNullOrEmpty(buildingData.constructionAnimationTrigger))
|
|
||||||
{
|
|
||||||
return buildingData.constructionAnimationTrigger;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 기본값: 빈 문자열 (애니메이션 없음)
|
|
||||||
return "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public EquipmentData GetEquipmentData()
|
public EquipmentData GetEquipmentData()
|
||||||
{
|
{
|
||||||
// BuildingData에 건설 도구가 정의되어 있으면 반환
|
return constructionEquipment;
|
||||||
if (buildingData != null && buildingData.constructionEquipment != null)
|
|
||||||
{
|
|
||||||
return buildingData.constructionEquipment;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null; // 특별한 도구 불필요
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Transform GetTransform()
|
public Transform GetTransform()
|
||||||
@@ -251,6 +248,32 @@ namespace Northbound
|
|||||||
return TeamType.Player;
|
return TeamType.Player;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 플레이어의 작업량 가져오기 (PlayerData.manpower)
|
||||||
|
/// </summary>
|
||||||
|
private float GetPlayerWorkPower(ulong playerId)
|
||||||
|
{
|
||||||
|
// PlayerInteraction 컴포넌트에서 workPower 가져오기
|
||||||
|
if (NetworkManager.Singleton != null && NetworkManager.Singleton.SpawnManager != null)
|
||||||
|
{
|
||||||
|
if (NetworkManager.Singleton.ConnectedClients.TryGetValue(playerId, out var client))
|
||||||
|
{
|
||||||
|
if (client.PlayerObject != null)
|
||||||
|
{
|
||||||
|
var playerInteraction = client.PlayerObject.GetComponent<PlayerInteraction>();
|
||||||
|
if (playerInteraction != null)
|
||||||
|
{
|
||||||
|
return playerInteraction.WorkPower;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 기본값: 10
|
||||||
|
Debug.LogWarning($"[BuildingFoundation] 플레이어 {playerId}의 workPower를 찾을 수 없어 기본값 10을 사용합니다.");
|
||||||
|
return 10f;
|
||||||
|
}
|
||||||
|
|
||||||
private void CompleteConstruction()
|
private void CompleteConstruction()
|
||||||
{
|
{
|
||||||
if (!IsServer) return;
|
if (!IsServer) return;
|
||||||
@@ -301,16 +324,13 @@ namespace Northbound
|
|||||||
|
|
||||||
private void OnProgressValueChanged(float oldValue, float newValue)
|
private void OnProgressValueChanged(float oldValue, float newValue)
|
||||||
{
|
{
|
||||||
if (buildingData != null)
|
OnProgressChanged?.Invoke(newValue, buildingData.requiredWorkAmount);
|
||||||
{
|
|
||||||
OnProgressChanged?.Invoke(newValue, buildingData.requiredWorkAmount);
|
|
||||||
}
|
|
||||||
UpdateProgressBar();
|
UpdateProgressBar();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateProgressBar()
|
private void UpdateProgressBar()
|
||||||
{
|
{
|
||||||
if (_progressBarInstance == null || buildingData == null) return;
|
if (_progressBarInstance == null) return;
|
||||||
|
|
||||||
// 진행바 UI 업데이트 (BuildingHealthBar와 유사한 구조 사용 가능)
|
// 진행바 UI 업데이트 (BuildingHealthBar와 유사한 구조 사용 가능)
|
||||||
var progressBar = _progressBarInstance.GetComponent<BuildingHealthBar>();
|
var progressBar = _progressBarInstance.GetComponent<BuildingHealthBar>();
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Unity.Netcode;
|
using Unity.Netcode;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
using Northbound.Data;
|
||||||
|
|
||||||
namespace Northbound
|
namespace Northbound
|
||||||
{
|
{
|
||||||
@@ -13,7 +14,7 @@ namespace Northbound
|
|||||||
public LayerMask groundLayer;
|
public LayerMask groundLayer;
|
||||||
|
|
||||||
[Header("Building Database")]
|
[Header("Building Database")]
|
||||||
public List<BuildingData> availableBuildings = new List<BuildingData>();
|
public List<TowerData> availableBuildings = new List<TowerData>();
|
||||||
|
|
||||||
[Header("Foundation Settings")]
|
[Header("Foundation Settings")]
|
||||||
public GameObject foundationPrefab; // 토대 프리팹 (Inspector에서 할당)
|
public GameObject foundationPrefab; // 토대 프리팹 (Inspector에서 할당)
|
||||||
@@ -58,7 +59,7 @@ namespace Northbound
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsValidPlacement(BuildingData data, Vector3 position, int rotation, out Vector3 groundPosition)
|
public bool IsValidPlacement(TowerData data, Vector3 position, int rotation, out Vector3 groundPosition)
|
||||||
{
|
{
|
||||||
groundPosition = position;
|
groundPosition = position;
|
||||||
|
|
||||||
@@ -71,7 +72,7 @@ namespace Northbound
|
|||||||
Vector3 snappedPosition = SnapToGrid(groundPosition);
|
Vector3 snappedPosition = SnapToGrid(groundPosition);
|
||||||
groundPosition = snappedPosition; // Update groundPosition to snapped value
|
groundPosition = snappedPosition; // Update groundPosition to snapped value
|
||||||
|
|
||||||
// Overlap check using GRID SIZE from BuildingData (not actual colliders)
|
// Overlap check using GRID SIZE from TowerData (not actual colliders)
|
||||||
Vector3 gridSize = data.GetSize(rotation);
|
Vector3 gridSize = data.GetSize(rotation);
|
||||||
|
|
||||||
// 프리팹의 실제 배치 위치 계산 (placementOffset 포함)
|
// 프리팹의 실제 배치 위치 계산 (placementOffset 포함)
|
||||||
@@ -130,7 +131,7 @@ namespace Northbound
|
|||||||
/// Get the grid bounds for a building at a given position
|
/// Get the grid bounds for a building at a given position
|
||||||
/// Useful for preview visualization
|
/// Useful for preview visualization
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Bounds GetPlacementBounds(BuildingData data, Vector3 position, int rotation)
|
public Bounds GetPlacementBounds(TowerData data, Vector3 position, int rotation)
|
||||||
{
|
{
|
||||||
Vector3 gridSize = data.GetSize(rotation);
|
Vector3 gridSize = data.GetSize(rotation);
|
||||||
// position은 이미 placementOffset이 적용된 위치
|
// position은 이미 placementOffset이 적용된 위치
|
||||||
@@ -183,7 +184,7 @@ namespace Northbound
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
BuildingData data = availableBuildings[buildingIndex];
|
TowerData data = availableBuildings[buildingIndex];
|
||||||
|
|
||||||
// 보안 검증 3: 건물 데이터 유효성 확인
|
// 보안 검증 3: 건물 데이터 유효성 확인
|
||||||
if (data == null || data.prefab == null)
|
if (data == null || data.prefab == null)
|
||||||
@@ -335,7 +336,7 @@ namespace Northbound
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
BuildingData data = availableBuildings[buildingIndex];
|
TowerData data = availableBuildings[buildingIndex];
|
||||||
|
|
||||||
// 보안 검증 3: 건물 데이터 유효성 확인
|
// 보안 검증 3: 건물 데이터 유효성 확인
|
||||||
if (data == null)
|
if (data == null)
|
||||||
@@ -427,8 +428,8 @@ namespace Northbound
|
|||||||
[Rpc(SendTo.Server, InvokePermission = RpcInvokePermission.Everyone)]
|
[Rpc(SendTo.Server, InvokePermission = RpcInvokePermission.Everyone)]
|
||||||
public void SpawnCompletedBuildingServerRpc(string buildingDataName, Vector3Int gridPosition, int rotation, ulong ownerId, TeamType team)
|
public void SpawnCompletedBuildingServerRpc(string buildingDataName, Vector3Int gridPosition, int rotation, ulong ownerId, TeamType team)
|
||||||
{
|
{
|
||||||
// BuildingData 찾기
|
// TowerData 찾기
|
||||||
BuildingData data = availableBuildings.Find(b => b.name == buildingDataName);
|
TowerData data = availableBuildings.Find(b => b.name == buildingDataName);
|
||||||
if (data == null || data.prefab == null)
|
if (data == null || data.prefab == null)
|
||||||
{
|
{
|
||||||
Debug.LogError($"<color=red>[BuildingManager] 건물 데이터를 찾을 수 없습니다: {buildingDataName}</color>");
|
Debug.LogError($"<color=red>[BuildingManager] 건물 데이터를 찾을 수 없습니다: {buildingDataName}</color>");
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using Unity.Netcode;
|
|||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.InputSystem;
|
using UnityEngine.InputSystem;
|
||||||
using UnityEngine.EventSystems;
|
using UnityEngine.EventSystems;
|
||||||
|
using Northbound.Data;
|
||||||
|
|
||||||
namespace Northbound
|
namespace Northbound
|
||||||
{
|
{
|
||||||
@@ -248,20 +249,20 @@ namespace Northbound
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
BuildingData data = BuildingManager.Instance.availableBuildings[selectedBuildingIndex];
|
TowerData data = BuildingManager.Instance.availableBuildings[selectedBuildingIndex];
|
||||||
if (data == null)
|
if (data == null)
|
||||||
{
|
{
|
||||||
Debug.LogError($"[BuildingPlacement] BuildingData is NULL at index {selectedBuildingIndex}");
|
Debug.LogError($"[BuildingPlacement] TowerData is NULL at index {selectedBuildingIndex}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.prefab == null)
|
if (data.prefab == null)
|
||||||
{
|
{
|
||||||
Debug.LogError($"[BuildingPlacement] BuildingData.prefab is NULL at index {selectedBuildingIndex}. Run 'Northbound > Diagnose Tower System'");
|
Debug.LogError($"[BuildingPlacement] TowerData.prefab is NULL at index {selectedBuildingIndex}. Run 'Northbound > Diagnose Tower System'");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Debug.Log($"<color=green>[BuildingPlacement] BuildingData: {data.buildingName}, Prefab: {data.prefab.name}</color>");
|
Debug.Log($"<color=green>[BuildingPlacement] TowerData: {data.buildingName}, Prefab: {data.prefab.name}</color>");
|
||||||
Debug.Log($"<color=green>[BuildingPlacement] Prefab scale: {data.prefab.transform.localScale}</color>");
|
Debug.Log($"<color=green>[BuildingPlacement] Prefab scale: {data.prefab.transform.localScale}</color>");
|
||||||
|
|
||||||
// 완성 건물 프리팹으로 프리뷰 생성 (사용자가 완성 모습을 볼 수 있도록)
|
// 완성 건물 프리팹으로 프리뷰 생성 (사용자가 완성 모습을 볼 수 있도록)
|
||||||
@@ -319,7 +320,7 @@ namespace Northbound
|
|||||||
if (selectedBuildingIndex < 0 || selectedBuildingIndex >= BuildingManager.Instance.availableBuildings.Count)
|
if (selectedBuildingIndex < 0 || selectedBuildingIndex >= BuildingManager.Instance.availableBuildings.Count)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
BuildingData data = BuildingManager.Instance.availableBuildings[selectedBuildingIndex];
|
TowerData data = BuildingManager.Instance.availableBuildings[selectedBuildingIndex];
|
||||||
if (data == null || data.prefab == null)
|
if (data == null || data.prefab == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -444,10 +445,10 @@ namespace Northbound
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
BuildingData data = BuildingManager.Instance.availableBuildings[selectedBuildingIndex];
|
TowerData data = BuildingManager.Instance.availableBuildings[selectedBuildingIndex];
|
||||||
if (data == null || data.prefab == null)
|
if (data == null || data.prefab == null)
|
||||||
{
|
{
|
||||||
Debug.LogError($"[BuildingPlacement] BuildingData or prefab is null at index {selectedBuildingIndex}. Please run 'Northbound > Populate Towers from Prefabs' and update BuildingManager.");
|
Debug.LogError($"[BuildingPlacement] TowerData or prefab is null at index {selectedBuildingIndex}. Please run 'Northbound > Populate Towers from Prefabs' and update BuildingManager.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -516,7 +517,7 @@ namespace Northbound
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Vector3> CalculateDragBuildingPositions(Vector3 start, Vector3 end, BuildingData data)
|
private List<Vector3> CalculateDragBuildingPositions(Vector3 start, Vector3 end, TowerData data)
|
||||||
{
|
{
|
||||||
List<Vector3> positions = new List<Vector3>();
|
List<Vector3> positions = new List<Vector3>();
|
||||||
|
|
||||||
@@ -566,7 +567,7 @@ namespace Northbound
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
BuildingData selectedData = BuildingManager.Instance.availableBuildings[selectedBuildingIndex];
|
TowerData selectedData = BuildingManager.Instance.availableBuildings[selectedBuildingIndex];
|
||||||
|
|
||||||
int successCount = 0;
|
int successCount = 0;
|
||||||
foreach (var position in dragBuildingPositions)
|
foreach (var position in dragBuildingPositions)
|
||||||
@@ -606,7 +607,7 @@ namespace Northbound
|
|||||||
Ray ray = Camera.main.ScreenPointToRay(Mouse.current.position.ReadValue());
|
Ray ray = Camera.main.ScreenPointToRay(Mouse.current.position.ReadValue());
|
||||||
if (Physics.Raycast(ray, out RaycastHit hit, maxPlacementDistance, groundLayer))
|
if (Physics.Raycast(ray, out RaycastHit hit, maxPlacementDistance, groundLayer))
|
||||||
{
|
{
|
||||||
BuildingData selectedData = BuildingManager.Instance.availableBuildings[selectedBuildingIndex];
|
TowerData selectedData = BuildingManager.Instance.availableBuildings[selectedBuildingIndex];
|
||||||
|
|
||||||
if (BuildingManager.Instance.IsValidPlacement(selectedData, hit.point, currentRotation, out Vector3 groundPosition))
|
if (BuildingManager.Instance.IsValidPlacement(selectedData, hit.point, currentRotation, out Vector3 groundPosition))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using Unity.Netcode;
|
|||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.InputSystem;
|
using UnityEngine.InputSystem;
|
||||||
using TMPro;
|
using TMPro;
|
||||||
|
using Northbound.Data;
|
||||||
|
|
||||||
namespace Northbound
|
namespace Northbound
|
||||||
{
|
{
|
||||||
@@ -196,7 +197,7 @@ namespace Northbound
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 개별 슬롯 버튼 생성
|
/// 개별 슬롯 버튼 생성
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void CreateSlot(BuildingData buildingData, int index)
|
private void CreateSlot(TowerData buildingData, int index)
|
||||||
{
|
{
|
||||||
GameObject slotObj = Instantiate(slotButtonPrefab, slotContainer);
|
GameObject slotObj = Instantiate(slotButtonPrefab, slotContainer);
|
||||||
BuildingSlotButton slotButton = slotObj.GetComponent<BuildingSlotButton>();
|
BuildingSlotButton slotButton = slotObj.GetComponent<BuildingSlotButton>();
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using UnityEngine;
|
|||||||
using UnityEngine.UI;
|
using UnityEngine.UI;
|
||||||
using UnityEngine.EventSystems;
|
using UnityEngine.EventSystems;
|
||||||
using TMPro;
|
using TMPro;
|
||||||
|
using Northbound.Data;
|
||||||
|
|
||||||
namespace Northbound
|
namespace Northbound
|
||||||
{
|
{
|
||||||
@@ -23,7 +24,7 @@ namespace Northbound
|
|||||||
[SerializeField] private Color selectedColor = new Color(0.3f, 0.6f, 0.3f, 1f);
|
[SerializeField] private Color selectedColor = new Color(0.3f, 0.6f, 0.3f, 1f);
|
||||||
[SerializeField] private Color hoverColor = new Color(0.3f, 0.3f, 0.3f, 1f);
|
[SerializeField] private Color hoverColor = new Color(0.3f, 0.3f, 0.3f, 1f);
|
||||||
|
|
||||||
private BuildingData buildingData;
|
private TowerData buildingData;
|
||||||
private int slotIndex;
|
private int slotIndex;
|
||||||
private BuildingQuickslotUI quickslotUI;
|
private BuildingQuickslotUI quickslotUI;
|
||||||
private bool isSelected = false;
|
private bool isSelected = false;
|
||||||
@@ -42,7 +43,7 @@ namespace Northbound
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 슬롯 초기화
|
/// 슬롯 초기화
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void Initialize(BuildingData data, int index, BuildingQuickslotUI ui)
|
public void Initialize(TowerData data, int index, BuildingQuickslotUI ui)
|
||||||
{
|
{
|
||||||
buildingData = data;
|
buildingData = data;
|
||||||
slotIndex = index;
|
slotIndex = index;
|
||||||
|
|||||||
@@ -123,10 +123,9 @@ namespace Northbound.Editor
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If towers were imported, auto-configure BuildingManager
|
// If towers were imported, auto-configure BuildingManager
|
||||||
// TowerData now extends BuildingData, so it can be used directly!
|
|
||||||
if (typeName == "Tower")
|
if (typeName == "Tower")
|
||||||
{
|
{
|
||||||
Debug.Log($"<color=cyan>[CSVToSOImporter] Tower import complete, TowerData extends BuildingData now!</color>");
|
Debug.Log($"<color=cyan>[CSVToSOImporter] Tower import complete!</color>");
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -203,10 +202,10 @@ namespace Northbound.Editor
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Now set the prefab reference on data
|
// Now set the prefab reference on data
|
||||||
if (data is BuildingData buildingData)
|
if (data is TowerData towerData)
|
||||||
{
|
{
|
||||||
buildingData.prefab = prefabObj;
|
towerData.prefab = prefabObj;
|
||||||
Debug.Log($"[CSVToSOImporter] Set prefab reference: {buildingData.name} -> {prefabObj.name}");
|
Debug.Log($"[CSVToSOImporter] Set prefab reference: {towerData.name} -> {prefabObj.name}");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save data asset
|
// Save data asset
|
||||||
@@ -236,9 +235,9 @@ namespace Northbound.Editor
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load TowerData (which extends BuildingData)
|
// Load TowerData
|
||||||
string[] towerDataGuids = AssetDatabase.FindAssets("t:TowerData", new[] { "Assets/Data/ScriptableObjects" });
|
string[] towerDataGuids = AssetDatabase.FindAssets("t:TowerData", new[] { "Assets/Data/ScriptableObjects" });
|
||||||
List<BuildingData> allTowers = new List<BuildingData>();
|
List<TowerData> allTowers = new List<TowerData>();
|
||||||
|
|
||||||
Debug.Log($"<color=cyan>[CSVToSOImporter] Found {towerDataGuids.Length} TowerData assets</color>");
|
Debug.Log($"<color=cyan>[CSVToSOImporter] Found {towerDataGuids.Length} TowerData assets</color>");
|
||||||
|
|
||||||
|
|||||||
@@ -84,6 +84,15 @@ namespace Northbound.Editor
|
|||||||
Debug.Log($"[PlayerPrefabSetup] Updated PlayerResourceInventory: maxResourceCapacity={playerData.capacity}");
|
Debug.Log($"[PlayerPrefabSetup] Updated PlayerResourceInventory: maxResourceCapacity={playerData.capacity}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var playerInteraction = prefab.GetComponent<PlayerInteraction>();
|
||||||
|
if (playerInteraction != null)
|
||||||
|
{
|
||||||
|
SerializedObject so = new SerializedObject(playerInteraction);
|
||||||
|
so.FindProperty("workPower").floatValue = playerData.manpower;
|
||||||
|
so.ApplyModifiedProperties();
|
||||||
|
Debug.Log($"[PlayerPrefabSetup] Updated PlayerInteraction: workPower={playerData.manpower}");
|
||||||
|
}
|
||||||
|
|
||||||
EditorUtility.SetDirty(prefab);
|
EditorUtility.SetDirty(prefab);
|
||||||
PrefabUtility.SavePrefabAsset(prefab);
|
PrefabUtility.SavePrefabAsset(prefab);
|
||||||
Debug.Log($"[PlayerPrefabSetup] Player prefab updated successfully from {playerData.name}");
|
Debug.Log($"[PlayerPrefabSetup] Player prefab updated successfully from {playerData.name}");
|
||||||
|
|||||||
@@ -159,9 +159,6 @@ namespace Northbound.Editor
|
|||||||
if (go.GetComponent<Building>() == null)
|
if (go.GetComponent<Building>() == null)
|
||||||
go.AddComponent<Building>();
|
go.AddComponent<Building>();
|
||||||
|
|
||||||
if (go.GetComponent<TowerDataComponent>() == null)
|
|
||||||
go.AddComponent<TowerDataComponent>();
|
|
||||||
|
|
||||||
if (go.GetComponent<BoxCollider>() == null)
|
if (go.GetComponent<BoxCollider>() == null)
|
||||||
{
|
{
|
||||||
BoxCollider collider = go.AddComponent<BoxCollider>();
|
BoxCollider collider = go.AddComponent<BoxCollider>();
|
||||||
|
|||||||
@@ -30,10 +30,10 @@ namespace Northbound.Editor
|
|||||||
{
|
{
|
||||||
string assetPath = AssetDatabase.GUIDToAssetPath(guid);
|
string assetPath = AssetDatabase.GUIDToAssetPath(guid);
|
||||||
GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(assetPath);
|
GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(assetPath);
|
||||||
TowerDataComponent tower = prefab?.GetComponent<TowerDataComponent>();
|
Building building = prefab?.GetComponent<Building>();
|
||||||
string towerStatus = tower != null && tower.towerData != null ? "<color=green>✓</color>" : "<color=red>✗</color>";
|
string towerStatus = building != null && building.buildingData != null ? "<color=green>✓</color>" : "<color=red>✗</color>";
|
||||||
string towerDataName = tower?.towerData?.name ?? "MISSING";
|
string towerDataName = building?.buildingData?.name ?? "MISSING";
|
||||||
Debug.Log($" {towerStatus} {prefab.name} - TowerDataComponent: {tower != null}, TowerData: {towerDataName}");
|
Debug.Log($" {towerStatus} {prefab.name} - Building: {building != null}, TowerData: {towerDataName}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,24 +20,9 @@ namespace Northbound.Editor
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var towerDataComponent = prefab.GetComponent<TowerDataComponent>();
|
// Set prefab reference
|
||||||
if (towerDataComponent == null)
|
|
||||||
{
|
|
||||||
towerDataComponent = prefab.AddComponent<TowerDataComponent>();
|
|
||||||
Debug.Log($"[TowerPrefabSetup] Added TowerDataComponent component");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (towerDataComponent != null)
|
|
||||||
{
|
|
||||||
towerDataComponent.towerData = towerData;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TowerData now extends BuildingData, so set prefab reference
|
|
||||||
towerData.prefab = prefab;
|
towerData.prefab = prefab;
|
||||||
|
|
||||||
// Ensure TowerData fields are synced to BuildingData
|
|
||||||
towerData.EnsureSynced();
|
|
||||||
|
|
||||||
Transform modelTransform = null;
|
Transform modelTransform = null;
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(towerData.modelPath))
|
if (!string.IsNullOrEmpty(towerData.modelPath))
|
||||||
|
|||||||
@@ -12,19 +12,20 @@ namespace Northbound
|
|||||||
[Header("Interaction Settings")]
|
[Header("Interaction Settings")]
|
||||||
public float interactionRange = 3f;
|
public float interactionRange = 3f;
|
||||||
public LayerMask interactableLayer = ~0;
|
public LayerMask interactableLayer = ~0;
|
||||||
|
public float workPower = 10f;
|
||||||
|
|
||||||
[Header("Detection")]
|
[Header("Detection")]
|
||||||
public Transform rayOrigin;
|
public Transform rayOrigin;
|
||||||
public bool useForwardDirection = true;
|
public bool useForwardDirection = true;
|
||||||
|
|
||||||
[Header("Animation")]
|
[Header("Animation")]
|
||||||
public bool playAnimations = true;
|
public bool playAnimations = true;
|
||||||
public bool useAnimationEvents = true;
|
public bool useAnimationEvents = true;
|
||||||
public bool blockDuringAnimation = true;
|
public bool blockDuringAnimation = true;
|
||||||
|
|
||||||
[Header("Equipment")]
|
[Header("Equipment")]
|
||||||
public bool useEquipment = true;
|
public bool useEquipment = true;
|
||||||
|
|
||||||
[Header("Debug")]
|
[Header("Debug")]
|
||||||
public bool showDebugRay = true;
|
public bool showDebugRay = true;
|
||||||
|
|
||||||
@@ -41,6 +42,7 @@ namespace Northbound
|
|||||||
|
|
||||||
// 다른 컴포넌트가 이동 차단 여부를 확인할 수 있도록 public 프로퍼티 제공
|
// 다른 컴포넌트가 이동 차단 여부를 확인할 수 있도록 public 프로퍼티 제공
|
||||||
public bool IsInteracting => _isInteracting;
|
public bool IsInteracting => _isInteracting;
|
||||||
|
public float WorkPower => workPower;
|
||||||
|
|
||||||
public override void OnNetworkSpawn()
|
public override void OnNetworkSpawn()
|
||||||
{
|
{
|
||||||
@@ -71,6 +73,17 @@ namespace Northbound
|
|||||||
private void Update()
|
private void Update()
|
||||||
{
|
{
|
||||||
if (!IsOwner) return;
|
if (!IsOwner) return;
|
||||||
|
|
||||||
|
// Check if current interactable is no longer valid (e.g., building completed)
|
||||||
|
if (_isInteracting && _currentInteractable != null)
|
||||||
|
{
|
||||||
|
if (!_currentInteractable.CanInteract(OwnerClientId))
|
||||||
|
{
|
||||||
|
Debug.Log("[PlayerInteraction] Interactable no longer valid - ending interaction");
|
||||||
|
EndInteraction();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
DetectInteractable();
|
DetectInteractable();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,6 +256,16 @@ namespace Northbound
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void EndInteraction()
|
||||||
|
{
|
||||||
|
_isInteracting = false;
|
||||||
|
if (_interactionTimeoutCoroutine != null)
|
||||||
|
{
|
||||||
|
StopCoroutine(_interactionTimeoutCoroutine);
|
||||||
|
_interactionTimeoutCoroutine = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void OnGUI()
|
private void OnGUI()
|
||||||
{
|
{
|
||||||
if (!IsOwner || _currentInteractable == null) return;
|
if (!IsOwner || _currentInteractable == null) return;
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
using Northbound.Data;
|
|
||||||
using Unity.Netcode;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace Northbound
|
|
||||||
{
|
|
||||||
[RequireComponent(typeof(Building))]
|
|
||||||
[RequireComponent(typeof(NetworkObject))]
|
|
||||||
public class TowerDataComponent : MonoBehaviour
|
|
||||||
{
|
|
||||||
[Header("Data Reference")]
|
|
||||||
[Tooltip("ScriptableObject containing tower data")]
|
|
||||||
public TowerData towerData;
|
|
||||||
|
|
||||||
private void Awake()
|
|
||||||
{
|
|
||||||
// TowerData now extends BuildingData, so just pass it directly
|
|
||||||
Building building = GetComponent<Building>();
|
|
||||||
if (building != null && towerData != null)
|
|
||||||
{
|
|
||||||
building.buildingData = towerData;
|
|
||||||
building.initialTeam = TeamType.Player;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 56c4536effc49fe47af593bf9d17e979
|
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
id,memo,mana,manpower,size_x,size_y,size_z,max_hp,atk_range,atk_damage,atk_interval_sec,model_path
|
id,memo,mana,manpower,size_x,size_y,size_z,max_hp,atk_range,atk_damage,atk_interval_sec,model_path
|
||||||
1,타워,25,10,3,3,3,50,10,5,2,Assets/Models/building_tower_B_blue.fbx
|
1,타워,25,10,3,3,3,50,10,5,2,Assets/Models/building_tower_B_blue.fbx
|
||||||
2,벽,5,5,2,2,1,30,0,0,0,Assets/Models/wall_straight.fbx
|
2,벽,5,5,4,3,2,30,0,0,0,Assets/Models/wall_straight.fbx
|
||||||
|
|||||||
|
Reference in New Issue
Block a user