feat(buildings): 添加金矿建筑功能并优化建筑健康系统

- 为兵营和金矿添加伤害数字显示功能,包括持续时间、上升高度等参数
- 将金矿脚本重构为GoldMine组件,实现定时产出金币机制
- 为所有单位添加NavMeshAgent组件以支持导航寻路
- 为弓箭手、法师等单位添加分离半径和目标扩散参数
- 为建筑添加碰撞体和标签设置
- 修复当前能量字段命名规范(currentEnergy -> _currentEnergy)
- 调整金矿健康值从100提升至500
- 统一伤害数字显示相关属性配置
```
This commit is contained in:
huadexuan committed 2026-09-15 21:13:44 +08:00
1 parent 27df2aa8b1
commit 1cdb7c3910
22 files changed
+800 -51

No files matched your search

+7 -2
View File
@@ -101,12 +101,17 @@ MonoBehaviour:
maxHealth: 100 maxHealth: 100
currentHealth: 100 currentHealth: 100
maxEnergy: 100 maxEnergy: 100
currentEnergy: 0 _currentEnergy: 0
heightOffset: 0.25 heightOffset: 1
barWidth: 1.2 barWidth: 1.2
borderColor: {r: 0.03, g: 0.03, b: 0.03, a: 0.95} borderColor: {r: 0.03, g: 0.03, b: 0.03, a: 0.95}
deathEffectDuration: 1.1 deathEffectDuration: 1.1
deathEffectColor: {r: 0.55, g: 0.9, b: 1, a: 1} deathEffectColor: {r: 0.55, g: 0.9, b: 1, a: 1}
showDamageNumbers: 1
damageNumberDuration: 0.8
damageNumberRiseHeight: 1.2
damageNumberSpawnHeight: 1.5
damageNumberSize: 0.09
--- !u!114 &7049005926872886096 --- !u!114 &7049005926872886096
MonoBehaviour: MonoBehaviour:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
+103 -1
View File
@@ -63,8 +63,110 @@ PrefabInstance:
propertyPath: m_Name propertyPath: m_Name
value: "\u91D1\u77FF" value: "\u91D1\u77FF"
objectReference: {fileID: 0} objectReference: {fileID: 0}
- target: {fileID: 919132149155446097, guid: 35b9ee56f1163491d957e1903bfedf03,
type: 3}
propertyPath: m_TagString
value: Friend
objectReference: {fileID: 0}
m_RemovedComponents: [] m_RemovedComponents: []
m_RemovedGameObjects: [] m_RemovedGameObjects: []
m_AddedGameObjects: [] m_AddedGameObjects: []
m_AddedComponents: [] m_AddedComponents:
- targetCorrespondingSourceObject: {fileID: 919132149155446097, guid: 35b9ee56f1163491d957e1903bfedf03,
type: 3}
insertIndex: -1
addedObject: {fileID: 7577279847001002587}
- targetCorrespondingSourceObject: {fileID: 919132149155446097, guid: 35b9ee56f1163491d957e1903bfedf03,
type: 3}
insertIndex: -1
addedObject: {fileID: 2464957741073066864}
- targetCorrespondingSourceObject: {fileID: 919132149155446097, guid: 35b9ee56f1163491d957e1903bfedf03,
type: 3}
insertIndex: -1
addedObject: {fileID: 4165091129539139448}
- targetCorrespondingSourceObject: {fileID: 919132149155446097, guid: 35b9ee56f1163491d957e1903bfedf03,
type: 3}
insertIndex: -1
addedObject: {fileID: 6812882125473210247}
m_SourcePrefab: {fileID: 100100000, guid: 35b9ee56f1163491d957e1903bfedf03, type: 3} m_SourcePrefab: {fileID: 100100000, guid: 35b9ee56f1163491d957e1903bfedf03, type: 3}
--- !u!1 &5707827689025450131 stripped
GameObject:
m_CorrespondingSourceObject: {fileID: 919132149155446097, guid: 35b9ee56f1163491d957e1903bfedf03,
type: 3}
m_PrefabInstance: {fileID: 4897434639390281154}
m_PrefabAsset: {fileID: 0}
--- !u!114 &7577279847001002587
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5707827689025450131}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 9355c53f591d4459ea620fc8191efa17, type: 3}
m_Name:
m_EditorClassIdentifier:
maxHealth: 500
currentHealth: 500
maxEnergy: 100
_currentEnergy: 0
heightOffset: 1
barWidth: 1.2
borderColor: {r: 0.03, g: 0.03, b: 0.03, a: 0.95}
deathEffectDuration: 1.1
deathEffectColor: {r: 0.55, g: 0.9, b: 1, a: 1}
showDamageNumbers: 1
damageNumberDuration: 0.8
damageNumberRiseHeight: 1.2
damageNumberSpawnHeight: 1.5
damageNumberSize: 0.09
--- !u!114 &2464957741073066864
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5707827689025450131}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 0664b986337964243879f685b5c49aba, type: 3}
m_Name:
m_EditorClassIdentifier:
maxHealth: 500
--- !u!114 &4165091129539139448
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5707827689025450131}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: aa899f80764ca42429a0a155f38f9ba4, type: 3}
m_Name:
m_EditorClassIdentifier:
goldPerProduction: 50
productionInterval: 5
healthBar: {fileID: 0}
--- !u!65 &6812882125473210247
BoxCollider:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5707827689025450131}
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: 3
m_Size: {x: 2.2292585, y: 2.1124895, z: 2.2443316}
m_Center: {x: -0.00015996098, y: 0.010562447, z: 0.00014443}
@@ -144,6 +144,10 @@ PrefabInstance:
type: 3} type: 3}
insertIndex: -1 insertIndex: -1
addedObject: {fileID: 6149369975191414129} addedObject: {fileID: 6149369975191414129}
- targetCorrespondingSourceObject: {fileID: 919132149155446097, guid: 5072af719b9b1401787c6c55f048d8ed,
type: 3}
insertIndex: -1
addedObject: {fileID: 2921261421214297749}
m_SourcePrefab: {fileID: 100100000, guid: 5072af719b9b1401787c6c55f048d8ed, type: 3} m_SourcePrefab: {fileID: 100100000, guid: 5072af719b9b1401787c6c55f048d8ed, type: 3}
--- !u!1 &7945271050725079514 stripped --- !u!1 &7945271050725079514 stripped
GameObject: GameObject:
@@ -204,6 +208,9 @@ MonoBehaviour:
projectileSpeed: 20 projectileSpeed: 20
projectileLifetime: 5 projectileLifetime: 5
energyPerAttack: 25 energyPerAttack: 25
separationRadius: 1.5
separationWeight: 3
targetSpreadBias: 6
target: {fileID: 0} target: {fileID: 0}
--- !u!114 &2920089167710187078 --- !u!114 &2920089167710187078
MonoBehaviour: MonoBehaviour:
@@ -226,6 +233,12 @@ MonoBehaviour:
borderColor: {r: 0.03, g: 0.03, b: 0.03, a: 0.95} borderColor: {r: 0.03, g: 0.03, b: 0.03, a: 0.95}
deathEffectDuration: 1.1 deathEffectDuration: 1.1
deathEffectColor: {r: 0.55, g: 0.9, b: 1, a: 1} deathEffectColor: {r: 0.55, g: 0.9, b: 1, a: 1}
showDamageNumbers: 1
damageNumberDuration: 0.8
damageNumberRiseHeight: 1.2
damageNumberSpawnHeight: 1.5
damageNumberSize: 0.09
damageNumberOutlineWidth: 0.015
--- !u!114 &4111165692813349771 --- !u!114 &4111165692813349771
MonoBehaviour: MonoBehaviour:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
@@ -255,6 +268,28 @@ MonoBehaviour:
showStunIndicator: 1 showStunIndicator: 1
indicatorHeight: 0.35 indicatorHeight: 0.35
stunIndicatorPrefab: {fileID: 0} stunIndicatorPrefab: {fileID: 0}
--- !u!195 &2921261421214297749
NavMeshAgent:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7945271050725079514}
m_Enabled: 1
m_AgentTypeID: 0
m_Radius: 0.5
m_Speed: 1.8
m_Acceleration: 8
avoidancePriority: 50
m_AngularSpeed: 120
m_StoppingDistance: 0
m_AutoTraverseOffMeshLink: 1
m_AutoBraking: 1
m_AutoRepath: 1
m_Height: 2
m_BaseOffset: 0
m_WalkableMask: 4294967295
m_ObstacleAvoidanceType: 4
--- !u!4 &8878706989061864259 stripped --- !u!4 &8878706989061864259 stripped
Transform: Transform:
m_CorrespondingSourceObject: {fileID: 1852576806548013000, guid: 5072af719b9b1401787c6c55f048d8ed, m_CorrespondingSourceObject: {fileID: 1852576806548013000, guid: 5072af719b9b1401787c6c55f048d8ed,
+35
View File
@@ -139,6 +139,10 @@ PrefabInstance:
type: 3} type: 3}
insertIndex: -1 insertIndex: -1
addedObject: {fileID: 1449822714228276666} addedObject: {fileID: 1449822714228276666}
- targetCorrespondingSourceObject: {fileID: 919132149155446097, guid: 7b0dd5b38a9cd144ea135f3743440958,
type: 3}
insertIndex: -1
addedObject: {fileID: 196735238731846736}
m_SourcePrefab: {fileID: 100100000, guid: 7b0dd5b38a9cd144ea135f3743440958, type: 3} m_SourcePrefab: {fileID: 100100000, guid: 7b0dd5b38a9cd144ea135f3743440958, type: 3}
--- !u!4 &2222026796505693691 stripped --- !u!4 &2222026796505693691 stripped
Transform: Transform:
@@ -222,6 +226,9 @@ MonoBehaviour:
projectileSpeed: 20 projectileSpeed: 20
projectileLifetime: 5 projectileLifetime: 5
energyPerAttack: 20 energyPerAttack: 20
separationRadius: 1.5
separationWeight: 3
targetSpreadBias: 6
target: {fileID: 0} target: {fileID: 0}
--- !u!114 &7720102017902192686 --- !u!114 &7720102017902192686
MonoBehaviour: MonoBehaviour:
@@ -244,6 +251,12 @@ MonoBehaviour:
borderColor: {r: 0.03, g: 0.03, b: 0.03, a: 0.95} borderColor: {r: 0.03, g: 0.03, b: 0.03, a: 0.95}
deathEffectDuration: 1.1 deathEffectDuration: 1.1
deathEffectColor: {r: 0.55, g: 0.9, b: 1, a: 1} deathEffectColor: {r: 0.55, g: 0.9, b: 1, a: 1}
showDamageNumbers: 1
damageNumberDuration: 0.8
damageNumberRiseHeight: 1.2
damageNumberSpawnHeight: 1.5
damageNumberSize: 0.09
damageNumberOutlineWidth: 0.015
--- !u!114 &1449822714228276666 --- !u!114 &1449822714228276666
MonoBehaviour: MonoBehaviour:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
@@ -259,6 +272,28 @@ MonoBehaviour:
volleyArrowCount: 3 volleyArrowCount: 3
volleyArrowDamageMultiplier: 0.7 volleyArrowDamageMultiplier: 0.7
volleyArrowInterval: 0.15 volleyArrowInterval: 0.15
--- !u!195 &196735238731846736
NavMeshAgent:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5200308054099318121}
m_Enabled: 1
m_AgentTypeID: 0
m_Radius: 0.5
m_Speed: 1.8
m_Acceleration: 8
avoidancePriority: 50
m_AngularSpeed: 120
m_StoppingDistance: 0
m_AutoTraverseOffMeshLink: 1
m_AutoBraking: 1
m_AutoRepath: 1
m_Height: 2
m_BaseOffset: 0
m_WalkableMask: 4294967295
m_ObstacleAvoidanceType: 4
--- !u!1001 &6707986314085581781 --- !u!1001 &6707986314085581781
PrefabInstance: PrefabInstance:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
+35
View File
@@ -140,6 +140,10 @@ PrefabInstance:
type: 3} type: 3}
insertIndex: -1 insertIndex: -1
addedObject: {fileID: 3887798621029042906} addedObject: {fileID: 3887798621029042906}
- targetCorrespondingSourceObject: {fileID: 919132149155446097, guid: 9d4b54ec2c4f7754bb1fd5ec11a29541,
type: 3}
insertIndex: -1
addedObject: {fileID: 20042258299677061}
m_SourcePrefab: {fileID: 100100000, guid: 9d4b54ec2c4f7754bb1fd5ec11a29541, type: 3} m_SourcePrefab: {fileID: 100100000, guid: 9d4b54ec2c4f7754bb1fd5ec11a29541, type: 3}
--- !u!1 &7062617501994129594 stripped --- !u!1 &7062617501994129594 stripped
GameObject: GameObject:
@@ -216,6 +220,9 @@ MonoBehaviour:
projectileSpeed: 20 projectileSpeed: 20
projectileLifetime: 5 projectileLifetime: 5
energyPerAttack: 20 energyPerAttack: 20
separationRadius: 1.5
separationWeight: 3
targetSpreadBias: 6
target: {fileID: 0} target: {fileID: 0}
--- !u!114 &990449840913200210 --- !u!114 &990449840913200210
MonoBehaviour: MonoBehaviour:
@@ -238,6 +245,12 @@ MonoBehaviour:
borderColor: {r: 0.03, g: 0.03, b: 0.03, a: 0.95} borderColor: {r: 0.03, g: 0.03, b: 0.03, a: 0.95}
deathEffectDuration: 1.1 deathEffectDuration: 1.1
deathEffectColor: {r: 0.55, g: 0.9, b: 1, a: 1} deathEffectColor: {r: 0.55, g: 0.9, b: 1, a: 1}
showDamageNumbers: 1
damageNumberDuration: 0.8
damageNumberRiseHeight: 1.2
damageNumberSpawnHeight: 1.5
damageNumberSize: 0.09
damageNumberOutlineWidth: 0.015
--- !u!114 &3887798621029042906 --- !u!114 &3887798621029042906
MonoBehaviour: MonoBehaviour:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
@@ -252,6 +265,28 @@ MonoBehaviour:
m_EditorClassIdentifier: m_EditorClassIdentifier:
explosionRadius: 1.8 explosionRadius: 1.8
explosionDamage: 85 explosionDamage: 85
--- !u!195 &20042258299677061
NavMeshAgent:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7062617501994129594}
m_Enabled: 1
m_AgentTypeID: 0
m_Radius: 0.5
m_Speed: 1.5
m_Acceleration: 8
avoidancePriority: 50
m_AngularSpeed: 120
m_StoppingDistance: 0
m_AutoTraverseOffMeshLink: 1
m_AutoBraking: 1
m_AutoRepath: 1
m_Height: 2
m_BaseOffset: 0
m_WalkableMask: 4294967295
m_ObstacleAvoidanceType: 4
--- !u!4 &8608542904823568931 stripped --- !u!4 &8608542904823568931 stripped
Transform: Transform:
m_CorrespondingSourceObject: {fileID: 1852576806548013000, guid: 9d4b54ec2c4f7754bb1fd5ec11a29541, m_CorrespondingSourceObject: {fileID: 1852576806548013000, guid: 9d4b54ec2c4f7754bb1fd5ec11a29541,
+35
View File
@@ -195,6 +195,10 @@ PrefabInstance:
type: 3} type: 3}
insertIndex: -1 insertIndex: -1
addedObject: {fileID: 8203338886952913679} addedObject: {fileID: 8203338886952913679}
- targetCorrespondingSourceObject: {fileID: 919132149155446097, guid: 17f7a1b7ae92f0948a5bebde8a11fc2b,
type: 3}
insertIndex: -1
addedObject: {fileID: 4069710806908923055}
m_SourcePrefab: {fileID: 100100000, guid: 17f7a1b7ae92f0948a5bebde8a11fc2b, type: 3} m_SourcePrefab: {fileID: 100100000, guid: 17f7a1b7ae92f0948a5bebde8a11fc2b, type: 3}
--- !u!4 &309400887928525066 stripped --- !u!4 &309400887928525066 stripped
Transform: Transform:
@@ -276,6 +280,9 @@ MonoBehaviour:
projectileSpeed: 20 projectileSpeed: 20
projectileLifetime: 5 projectileLifetime: 5
energyPerAttack: 20 energyPerAttack: 20
separationRadius: 1.5
separationWeight: 3
targetSpreadBias: 6
target: {fileID: 0} target: {fileID: 0}
--- !u!114 &6884400399417614731 --- !u!114 &6884400399417614731
MonoBehaviour: MonoBehaviour:
@@ -298,6 +305,12 @@ MonoBehaviour:
borderColor: {r: 0.03, g: 0.03, b: 0.03, a: 0.95} borderColor: {r: 0.03, g: 0.03, b: 0.03, a: 0.95}
deathEffectDuration: 1.1 deathEffectDuration: 1.1
deathEffectColor: {r: 0.55, g: 0.9, b: 1, a: 1} deathEffectColor: {r: 0.55, g: 0.9, b: 1, a: 1}
showDamageNumbers: 1
damageNumberDuration: 0.8
damageNumberRiseHeight: 1.2
damageNumberSpawnHeight: 1.5
damageNumberSize: 0.09
damageNumberOutlineWidth: 0.015
--- !u!114 &8203338886952913679 --- !u!114 &8203338886952913679
MonoBehaviour: MonoBehaviour:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
@@ -315,6 +328,28 @@ MonoBehaviour:
damageReduction: 0.35 damageReduction: 0.35
shieldLocalPosition: {x: 0, y: 1, z: 0.7} shieldLocalPosition: {x: 0, y: 1, z: 0.7}
shieldSize: {x: 1.5, y: 1.8, z: 0.12} shieldSize: {x: 1.5, y: 1.8, z: 0.12}
--- !u!195 &4069710806908923055
NavMeshAgent:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1242979668690031507}
m_Enabled: 1
m_AgentTypeID: 0
m_Radius: 0.5
m_Speed: 1.4
m_Acceleration: 8
avoidancePriority: 50
m_AngularSpeed: 120
m_StoppingDistance: 0
m_AutoTraverseOffMeshLink: 1
m_AutoBraking: 1
m_AutoRepath: 1
m_Height: 2
m_BaseOffset: 0
m_WalkableMask: 4294967295
m_ObstacleAvoidanceType: 4
--- !u!4 &5170975053921319681 stripped --- !u!4 &5170975053921319681 stripped
Transform: Transform:
m_CorrespondingSourceObject: {fileID: 6502182839619065283, guid: 17f7a1b7ae92f0948a5bebde8a11fc2b, m_CorrespondingSourceObject: {fileID: 6502182839619065283, guid: 17f7a1b7ae92f0948a5bebde8a11fc2b,
@@ -181,6 +181,10 @@ PrefabInstance:
type: 3} type: 3}
insertIndex: -1 insertIndex: -1
addedObject: {fileID: 4116279677098275843} addedObject: {fileID: 4116279677098275843}
- targetCorrespondingSourceObject: {fileID: 919132149155446097, guid: 62d9f853c067ff443bd2a3aa382ab563,
type: 3}
insertIndex: -1
addedObject: {fileID: 8773165921290396666}
m_SourcePrefab: {fileID: 100100000, guid: 62d9f853c067ff443bd2a3aa382ab563, type: 3} m_SourcePrefab: {fileID: 100100000, guid: 62d9f853c067ff443bd2a3aa382ab563, type: 3}
--- !u!4 &2585761489654059889 stripped --- !u!4 &2585761489654059889 stripped
Transform: Transform:
@@ -263,6 +267,9 @@ MonoBehaviour:
projectileSpeed: 20 projectileSpeed: 20
projectileLifetime: 5 projectileLifetime: 5
energyPerAttack: 20 energyPerAttack: 20
separationRadius: 1.5
separationWeight: 3
targetSpreadBias: 6
target: {fileID: 0} target: {fileID: 0}
--- !u!114 &3773753810590757696 --- !u!114 &3773753810590757696
MonoBehaviour: MonoBehaviour:
@@ -285,6 +292,12 @@ MonoBehaviour:
borderColor: {r: 0.03, g: 0.03, b: 0.03, a: 0.95} borderColor: {r: 0.03, g: 0.03, b: 0.03, a: 0.95}
deathEffectDuration: 1.1 deathEffectDuration: 1.1
deathEffectColor: {r: 0.55, g: 0.9, b: 1, a: 1} deathEffectColor: {r: 0.55, g: 0.9, b: 1, a: 1}
showDamageNumbers: 1
damageNumberDuration: 0.8
damageNumberRiseHeight: 1.2
damageNumberSpawnHeight: 1.5
damageNumberSize: 0.09
damageNumberOutlineWidth: 0.015
--- !u!114 &4116279677098275843 --- !u!114 &4116279677098275843
MonoBehaviour: MonoBehaviour:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
@@ -300,3 +313,25 @@ MonoBehaviour:
skillDamage: 125 skillDamage: 125
skillRadius: 4 skillRadius: 4
skillAnimationDuration: 1.5 skillAnimationDuration: 1.5
--- !u!195 &8773165921290396666
NavMeshAgent:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3933539425749946856}
m_Enabled: 1
m_AgentTypeID: 0
m_Radius: 0.5
m_Speed: 2
m_Acceleration: 8
avoidancePriority: 50
m_AngularSpeed: 120
m_StoppingDistance: 0
m_AutoTraverseOffMeshLink: 1
m_AutoBraking: 1
m_AutoRepath: 1
m_Height: 2
m_BaseOffset: 0
m_WalkableMask: 4294967295
m_ObstacleAvoidanceType: 4
@@ -247,6 +247,10 @@ PrefabInstance:
type: 3} type: 3}
insertIndex: -1 insertIndex: -1
addedObject: {fileID: 6166541611286257137} addedObject: {fileID: 6166541611286257137}
- targetCorrespondingSourceObject: {fileID: 919132149155446097, guid: 2ac38f68ec4f85341b423b5e8defd47e,
type: 3}
insertIndex: -1
addedObject: {fileID: 7158251723525050232}
m_SourcePrefab: {fileID: 100100000, guid: 2ac38f68ec4f85341b423b5e8defd47e, type: 3} m_SourcePrefab: {fileID: 100100000, guid: 2ac38f68ec4f85341b423b5e8defd47e, type: 3}
--- !u!1 &4059575966999087028 stripped --- !u!1 &4059575966999087028 stripped
GameObject: GameObject:
@@ -301,6 +305,12 @@ MonoBehaviour:
borderColor: {r: 0.03, g: 0.03, b: 0.03, a: 0.95} borderColor: {r: 0.03, g: 0.03, b: 0.03, a: 0.95}
deathEffectDuration: 1.1 deathEffectDuration: 1.1
deathEffectColor: {r: 0.55, g: 0.9, b: 1, a: 1} deathEffectColor: {r: 0.55, g: 0.9, b: 1, a: 1}
showDamageNumbers: 1
damageNumberDuration: 0.8
damageNumberRiseHeight: 1.2
damageNumberSpawnHeight: 1.5
damageNumberSize: 0.09
damageNumberOutlineWidth: 0.015
--- !u!114 &6486457417548983634 --- !u!114 &6486457417548983634
MonoBehaviour: MonoBehaviour:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
@@ -329,6 +339,9 @@ MonoBehaviour:
projectileSpeed: 20 projectileSpeed: 20
projectileLifetime: 5 projectileLifetime: 5
energyPerAttack: 20 energyPerAttack: 20
separationRadius: 1.5
separationWeight: 3
targetSpreadBias: 6
target: {fileID: 0} target: {fileID: 0}
--- !u!114 &4858463350953901710 --- !u!114 &4858463350953901710
MonoBehaviour: MonoBehaviour:
@@ -359,6 +372,28 @@ MonoBehaviour:
indicatorHeight: 0.35 indicatorHeight: 0.35
stunIndicatorPrefab: {fileID: 2432573200162841629, guid: de288ea16be0c1544a4c6eb71e899570, stunIndicatorPrefab: {fileID: 2432573200162841629, guid: de288ea16be0c1544a4c6eb71e899570,
type: 3} type: 3}
--- !u!195 &7158251723525050232
NavMeshAgent:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4059575966999087028}
m_Enabled: 1
m_AgentTypeID: 0
m_Radius: 0.5
m_Speed: 2.2
m_Acceleration: 8
avoidancePriority: 50
m_AngularSpeed: 120
m_StoppingDistance: 0
m_AutoTraverseOffMeshLink: 1
m_AutoBraking: 1
m_AutoRepath: 1
m_Height: 2
m_BaseOffset: 0
m_WalkableMask: 4294967295
m_ObstacleAvoidanceType: 4
--- !u!4 &7974625701785382694 stripped --- !u!4 &7974625701785382694 stripped
Transform: Transform:
m_CorrespondingSourceObject: {fileID: 6502182839619065283, guid: 2ac38f68ec4f85341b423b5e8defd47e, m_CorrespondingSourceObject: {fileID: 6502182839619065283, guid: 2ac38f68ec4f85341b423b5e8defd47e,
Binary file not shown.
+84
View File
@@ -0,0 +1,84 @@
using UnityEngine;
using System;
/// <summary>
/// 金币管理器,全局单例
/// </summary>
public class GoldManager : MonoBehaviour
{
public static GoldManager Instance { get; private set; }
[Header("金币设置")]
[SerializeField] private int initialGold = 500;
[SerializeField] private int currentGold = 500;
/// <summary>
/// 当前金币数量(只读)
/// </summary>
public int CurrentGold => currentGold;
/// <summary>
/// 金币变化事件(参数:新的金币数量)
/// </summary>
public event Action<int> OnGoldChanged;
private void Awake()
{
// 单例模式
if (Instance != null && Instance != this)
{
Destroy(gameObject);
return;
}
Instance = this;
}
private void Start()
{
currentGold = initialGold;
OnGoldChanged?.Invoke(currentGold);
}
/// <summary>
/// 增加金币
/// </summary>
public void AddGold(int amount)
{
if (amount <= 0) return;
currentGold += amount;
OnGoldChanged?.Invoke(currentGold);
Debug.Log($"获得金币 +{amount},当前金币: {currentGold}");
}
/// <summary>
/// 尝试消费金币,如果金币足够则扣除并返回 true,否则返回 false
/// </summary>
public bool TrySpendGold(int amount)
{
if (amount <= 0)
{
Debug.LogWarning("消费金币数量必须大于0");
return false;
}
if (currentGold < amount)
{
Debug.LogWarning($"金币不足!需要 {amount},当前只有 {currentGold}");
return false;
}
currentGold -= amount;
OnGoldChanged?.Invoke(currentGold);
Debug.Log($"消费金币 -{amount},当前金币: {currentGold}");
return true;
}
/// <summary>
/// 检查是否有足够的金币
/// </summary>
public bool HasEnoughGold(int amount)
{
return currentGold >= amount;
}
}
+11
View File
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: DShMsX/+Ui0ZvxHNgg0oANG590a65IynIxC0Pd5T3n/riVwBUL38R7Q=
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+56 -14
View File
@@ -1,36 +1,78 @@
using UnityEngine; using UnityEngine;
/// <summary>
/// 金矿建筑,定时产出金币
/// </summary>
public class GoldMine : MonoBehaviour public class GoldMine : MonoBehaviour
{ {
[Header("产出配置")] [Header("金币产出")]
[Min(0)] public int goldPerTick = 5; [Tooltip("每次产出的金币数量")]
[Min(0.1f)] public float productionInterval = 3f; [SerializeField] private int goldPerProduction = 50;
private float timer; [Tooltip("产出间隔(秒)")]
private BuildingHealth buildingHealth; [SerializeField] private float productionInterval = 5f;
[Header("可选:血条和进度条")]
[SerializeField] private WorldHealthBar healthBar;
private float productionTimer = 0f;
private void Awake() private void Awake()
{ {
buildingHealth = GetComponent<BuildingHealth>(); // 如果没有手动指定,尝试自动获取
if (healthBar == null)
{
healthBar = GetComponent<WorldHealthBar>();
}
}
private void Start()
{
// 初始化进度条(使用能量条显示产出进度)
if (healthBar != null)
{
healthBar.maxEnergy = 100f;
healthBar.currentEnergy = 0f;
}
} }
private void Update() private void Update()
{ {
if (GoldSystem.Instance == null) if (GoldManager.Instance == null) return;
productionTimer += Time.deltaTime;
// 更新进度条
if (healthBar != null)
{ {
return; float progress = Mathf.Clamp01(productionTimer / productionInterval);
healthBar.currentEnergy = progress * 100f;
} }
timer += Time.deltaTime; // 达到产出时间
if (buildingHealth != null) if (productionTimer >= productionInterval)
{ {
buildingHealth.SetProductionProgress(timer / productionInterval); ProduceGold();
productionTimer = 0f;
}
} }
while (timer >= productionInterval) private void ProduceGold()
{ {
timer -= productionInterval; if (GoldManager.Instance != null)
GoldSystem.Instance.AddGold(goldPerTick); {
GoldManager.Instance.AddGold(goldPerProduction);
Debug.Log($"{gameObject.name} 产出金币 +{goldPerProduction}");
// 可以在这里播放特效和音效
// TODO: 添加金币产出特效
} }
} }
// 在编辑器中显示信息
private void OnDrawGizmosSelected()
{
Gizmos.color = Color.yellow;
Gizmos.DrawWireSphere(transform.position + Vector3.up * 2f, 0.5f);
}
} }
+57
View File
@@ -0,0 +1,57 @@
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
/// <summary>
/// 金币 UI 显示,显示在屏幕右上角
/// </summary>
[RequireComponent(typeof(Text))]
public class GoldUI : MonoBehaviour
{
private Text goldText;
private bool isSubscribed = false;
private void Awake()
{
goldText = GetComponent<Text>();
}
private void Start()
{
// 延迟订阅,确保 GoldManager 已初始化
StartCoroutine(SubscribeToGoldManager());
}
private IEnumerator SubscribeToGoldManager()
{
// 等待 GoldManager 初始化
while (GoldManager.Instance == null)
{
yield return null;
}
// 订阅事件
GoldManager.Instance.OnGoldChanged += UpdateGoldDisplay;
isSubscribed = true;
// 立即更新显示
UpdateGoldDisplay(GoldManager.Instance.CurrentGold);
}
private void OnDestroy()
{
// 取消订阅
if (isSubscribed && GoldManager.Instance != null)
{
GoldManager.Instance.OnGoldChanged -= UpdateGoldDisplay;
}
}
private void UpdateGoldDisplay(int newGold)
{
if (goldText != null)
{
goldText.text = $"金币: {newGold}";
}
}
}
+11
View File
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: Wy5O5y38VnjSuIhFnocQ+yIk0/PO6tI7xIs2xwwd3tamOqHsfmlJPMA=
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+68
View File
@@ -0,0 +1,68 @@
using UnityEngine;
/// <summary>
/// 主角金币产出组件,挂载到主角单位上
/// </summary>
public class HeroGoldProducer : MonoBehaviour
{
[Header("金币产出")]
[Tooltip("每次产出的金币数量")]
[SerializeField] private int goldPerProduction = 30;
[Tooltip("产出间隔(秒)")]
[SerializeField] private float productionInterval = 8f;
[Header("可选:血条和进度条")]
[SerializeField] private WorldHealthBar healthBar;
private float productionTimer = 0f;
private void Awake()
{
// 如果没有手动指定,尝试自动获取
if (healthBar == null)
{
healthBar = GetComponent<WorldHealthBar>();
}
}
private void Start()
{
// 初始化进度条(使用能量条显示产出进度)
if (healthBar != null)
{
healthBar.maxEnergy = 100f;
healthBar.currentEnergy = 0f;
}
}
private void Update()
{
if (GoldManager.Instance == null) return;
productionTimer += Time.deltaTime;
// 更新进度条
if (healthBar != null)
{
float progress = Mathf.Clamp01(productionTimer / productionInterval);
healthBar.currentEnergy = progress * 100f;
}
// 达到产出时间
if (productionTimer >= productionInterval)
{
ProduceGold();
productionTimer = 0f;
}
}
private void ProduceGold()
{
if (GoldManager.Instance != null)
{
GoldManager.Instance.AddGold(goldPerProduction);
Debug.Log($"主角 {gameObject.name} 产出金币 +{goldPerProduction}");
}
}
}
+11
View File
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: CHtLsyOqAHO9Tb+uQSnXcIZq0ZVdLqUhEvDUjf4JqQrm9JlgUFne/xk=
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+86 -6
View File
@@ -86,12 +86,19 @@ public class UnitBase : MonoBehaviour
//初始位置坐标 //初始位置坐标
private Vector3 startPos; private Vector3 startPos;
// NavMesh 寻路更新间隔(优化性能)
private float nextPathUpdateTime = 0f;
private const float PathUpdateInterval = 0.5f; // 每 0.5 秒更新一次路径
//动画控制器 //动画控制器
private Animator animator; private Animator animator;
// 角色控制器 // 角色控制器
private CharacterController controller; private CharacterController controller;
// NavMesh 寻路代理(如果有的话优先使用)
private UnityEngine.AI.NavMeshAgent navAgent;
// 自身的血条组件,同时负责显示技能能量条。 // 自身的血条组件,同时负责显示技能能量条。
private WorldHealthBar worldHealthBar; private WorldHealthBar worldHealthBar;
@@ -154,10 +161,27 @@ public class UnitBase : MonoBehaviour
//初始化 //初始化
animator = GetComponent<Animator>(); animator = GetComponent<Animator>();
controller = GetComponent<CharacterController>(); controller = GetComponent<CharacterController>();
navAgent = GetComponent<UnityEngine.AI.NavMeshAgent>();
worldHealthBar = GetComponent<WorldHealthBar>(); worldHealthBar = GetComponent<WorldHealthBar>();
statusEffectController = EnsureStatusEffectController(); statusEffectController = EnsureStatusEffectController();
unitSkill = GetComponent<UnitSkill>(); unitSkill = GetComponent<UnitSkill>();
// 如果有 NavMeshAgent,配置它并禁用 CharacterController
if (navAgent != null)
{
navAgent.speed = speed;
navAgent.angularSpeed = 120f;
navAgent.acceleration = 8f;
navAgent.stoppingDistance = attackRange * 0.95f; // 接近攻击范围边缘时停止
navAgent.autoBraking = true;
// 禁用 CharacterController,避免冲突
if (controller != null)
{
controller.enabled = false;
}
}
// 大多数角色由 CharacterController 位移;特殊攻击动作可保留动画根运动。 // 大多数角色由 CharacterController 位移;特殊攻击动作可保留动画根运动。
animator.applyRootMotion = useRootMotion; animator.applyRootMotion = useRootMotion;
//记录初始坐标 //记录初始坐标
@@ -194,6 +218,12 @@ public class UnitBase : MonoBehaviour
//攻击状态下等待Animator报告攻击动画播放完成。 //攻击状态下等待Animator报告攻击动画播放完成。
if (state == State.Attack) if (state == State.Attack)
{ {
// 攻击时直接禁用 NavMeshAgent,彻底停止移动
if (navAgent != null && navAgent.enabled)
{
navAgent.enabled = false;
}
animator.SetInteger("speed", 0); animator.SetInteger("speed", 0);
attackElapsedTime += Time.deltaTime; attackElapsedTime += Time.deltaTime;
applyGravity(); applyGravity();
@@ -270,7 +300,7 @@ public class UnitBase : MonoBehaviour
return false; return false;
} }
healthBar.TakeDamage(ModifyIncomingDamage(damage, attacker)); healthBar.TakeDamage(ModifyIncomingDamage(damage, attacker), attacker);
return true; return true;
} }
@@ -333,11 +363,10 @@ public class UnitBase : MonoBehaviour
return false; return false;
} }
// 将双方控制器半径计入判定,避免碰撞体互相阻挡时无法进入攻击距离。 // NavMeshAgent 会保持一定距离,所以增加额外的攻击范围缓冲
float selfRadius = GetHorizontalCollisionRadius(controller); // 原来的逻辑:selfRadius + opponentRadius + attackRange
CharacterController opponentController = opponent.GetComponent<CharacterController>(); // 现在:直接使用 attackRange,并增加 1.5 米的缓冲,让单位在更远距离就能攻击
float opponentRadius = GetHorizontalCollisionRadius(opponentController); float reachDistance = attackRange + 1.5f;
float reachDistance = selfRadius + opponentRadius + attackRange;
return Vector3.Distance(transform.position, opponent.transform.position) <= reachDistance; return Vector3.Distance(transform.position, opponent.transform.position) <= reachDistance;
} }
@@ -465,6 +494,12 @@ public class UnitBase : MonoBehaviour
{ {
target = friend; target = friend;
// 攻击时直接禁用 NavMeshAgent(彻底停止移动)
if (navAgent != null && navAgent.enabled)
{
navAgent.enabled = false;
}
FaceTarget(friend); FaceTarget(friend);
if (TryReleaseSkill()) if (TryReleaseSkill())
@@ -893,6 +928,47 @@ public class UnitBase : MonoBehaviour
{ {
movement.y = 0; movement.y = 0;
// 如果有 NavMeshAgent,使用它来移动
if (navAgent != null)
{
// 重新启用 NavMeshAgent(攻击后可能被禁用)
if (!navAgent.enabled)
{
navAgent.enabled = true;
}
if (navAgent.isOnNavMesh)
{
// 确保没有被停止
if (navAgent.isStopped)
{
navAgent.isStopped = false;
}
// 限制寻路更新频率(性能优化)
if (Time.time >= nextPathUpdateTime)
{
// 设置目标位置(相对于当前位置)
Vector3 targetPosition = transform.position + movement.normalized * 10f;
navAgent.SetDestination(targetPosition);
nextPathUpdateTime = Time.time + PathUpdateInterval;
}
// 根据速度设置动画
if (navAgent.velocity.magnitude > 0.1f)
{
animator.SetInteger("speed", 1);
}
else
{
animator.SetInteger("speed", 0);
}
return;
}
}
// 否则使用 CharacterController(旧方式)
//友军分离力:与过近的同阵营单位互相推开,避免扎堆 //友军分离力:与过近的同阵营单位互相推开,避免扎堆
Vector3 separation = ComputeSeparation(); Vector3 separation = ComputeSeparation();
if (separation.sqrMagnitude > 0.0001f) if (separation.sqrMagnitude > 0.0001f)
@@ -972,9 +1048,13 @@ public class UnitBase : MonoBehaviour
} }
private void applyGravity() private void applyGravity()
{
// 只在 CharacterController 启用时应用重力
if (controller != null && controller.enabled)
{ {
controller.Move(Vector3.up * getVerticalVelocity() * Time.deltaTime); controller.Move(Vector3.up * getVerticalVelocity() * Time.deltaTime);
} }
}
// 在 Scene 视图中标记箭的发射位置和方向,便于调整空物体。 // 在 Scene 视图中标记箭的发射位置和方向,便于调整空物体。
private void OnDrawGizmos() private void OnDrawGizmos()
+45 -19
View File
@@ -14,7 +14,19 @@ public class WorldHealthBar : MonoBehaviour
[Min(1f)] public float maxEnergy = 100f; [Min(1f)] public float maxEnergy = 100f;
// 当前技能能量,范围会自动限制在 0 到 maxEnergy 之间。 // 当前技能能量,范围会自动限制在 0 到 maxEnergy 之间。
[Min(0f)] public float currentEnergy = 0f; [Min(0f)]
[SerializeField]
private float _currentEnergy = 0f;
public float currentEnergy
{
get => _currentEnergy;
set
{
_currentEnergy = Mathf.Clamp(value, 0f, maxEnergy);
UpdateEnergyFill();
}
}
public bool IsEnergyFull => !isDead && currentEnergy >= maxEnergy; public bool IsEnergyFull => !isDead && currentEnergy >= maxEnergy;
@@ -39,16 +51,18 @@ public class WorldHealthBar : MonoBehaviour
[Min(0f)] public float damageNumberRiseHeight = 1.2f; [Min(0f)] public float damageNumberRiseHeight = 1.2f;
[Min(0f)] public float damageNumberSpawnHeight = 1.5f; [Min(0f)] public float damageNumberSpawnHeight = 1.5f;
[Min(0.01f)] public float damageNumberSize = 0.09f; [Min(0.01f)] public float damageNumberSize = 0.09f;
[Min(0f)] public float damageNumberOutlineWidth = 0.015f;
// 描边宽度设为私有常量,所有角色统一使用
private const float DamageNumberOutlineWidth = 0.025f; // 从 0.015 增加到 0.025,描边更粗
// 血条 UI 的像素尺寸,实际显示大小由 worldWidth 控制。 // 血条 UI 的像素尺寸,实际显示大小由 worldWidth 控制。
private const int PixelWidth = 100; private const int PixelWidth = 100;
private const int PixelHeight = 14; private const int PixelHeight = 14;
// 能量条紧贴血条下方,宽度更窄,高度更小。 // 能量条紧贴血条下方,宽度更窄,高度更小。
private const int EnergyPixelWidth = 80; private const int EnergyPixelWidth = 100; // 改为和血条一样宽
private const int EnergyPixelHeight = 8; private const int EnergyPixelHeight = 12; // 从 8 增加到 12,更明显
private const int BarGap = 2; private const int BarGap = 0; // 改为 0,让血条和能量条紧贴在一起
private const int TotalPixelHeight = PixelHeight + BarGap + EnergyPixelHeight; private const int TotalPixelHeight = PixelHeight + BarGap + EnergyPixelHeight;
// 世界空间 Canvas,用来承载血条 UI。 // 世界空间 Canvas,用来承载血条 UI。
@@ -81,7 +95,7 @@ public class WorldHealthBar : MonoBehaviour
maxHealth = Mathf.Max(1f, maxHealth); maxHealth = Mathf.Max(1f, maxHealth);
currentHealth = Mathf.Clamp(currentHealth, 0f, maxHealth); currentHealth = Mathf.Clamp(currentHealth, 0f, maxHealth);
maxEnergy = Mathf.Max(1f, maxEnergy); maxEnergy = Mathf.Max(1f, maxEnergy);
currentEnergy = Mathf.Clamp(currentEnergy, 0f, maxEnergy); _currentEnergy = Mathf.Clamp(_currentEnergy, 0f, maxEnergy);
CalculateLayout(); CalculateLayout();
CreateBar(); // 创建血条 UI 结构,但颜色在 Start() 中设置 CreateBar(); // 创建血条 UI 结构,但颜色在 Start() 中设置
UpdateFill(); UpdateFill();
@@ -155,16 +169,21 @@ public class WorldHealthBar : MonoBehaviour
} }
// 让单位受到伤害,并弹出伤害数字。 // 让单位受到伤害,并弹出伤害数字。
public void TakeDamage(float amount) // attacker: 攻击者,用于判断受击者(自己)是敌是友,决定数字颜色和浮动方向
public void TakeDamage(float amount, GameObject attacker = null)
{ {
bool wasDead = isDead; bool wasDead = isDead;
SetHealth(currentHealth - amount); SetHealth(currentHealth - amount);
if (showDamageNumbers && amount > 0f && !wasDead) if (showDamageNumbers && amount > 0f && !wasDead)
{ {
SpawnDamageNumber(amount, false); // 判断受击者(自己)是否是友方(Friend tag)
bool isVictimFriend = gameObject.CompareTag("Friend");
SpawnDamageNumber(amount, false, isVictimFriend);
} }
} }
// 不再需要这个方法,直接用 CompareTag 判断
// 让单位恢复生命值,并弹出绿色治疗数字。 // 让单位恢复生命值,并弹出绿色治疗数字。
public void Heal(float amount) public void Heal(float amount)
{ {
@@ -542,7 +561,7 @@ public class WorldHealthBar : MonoBehaviour
// 在单位头顶生成一个漂浮伤害数字;数字独立于单位存活,死亡单位也能看到最后一击。 // 在单位头顶生成一个漂浮伤害数字;数字独立于单位存活,死亡单位也能看到最后一击。
// TextMesh 不支持原生描边,用 4 个偏移的黑色副本 + 1 个白色主字组合出黑边效果。 // TextMesh 不支持原生描边,用 4 个偏移的黑色副本 + 1 个白色主字组合出黑边效果。
private void SpawnDamageNumber(float amount, bool isHeal) private void SpawnDamageNumber(float amount, bool isHeal, bool isVictimFriend = false)
{ {
if (DamageNumber.ActiveCount >= DamageNumber.MaxActive) if (DamageNumber.ActiveCount >= DamageNumber.MaxActive)
{ {
@@ -555,19 +574,26 @@ public class WorldHealthBar : MonoBehaviour
rootObject.transform.position = spawnPosition; rootObject.transform.position = spawnPosition;
string text = Mathf.RoundToInt(amount).ToString(); string text = Mathf.RoundToInt(amount).ToString();
float outline = damageNumberOutlineWidth; float outline = DamageNumberOutlineWidth;
// 黑色描边副本先画(sortingOrder 低)。 // 8 方向黑色描边:上下左右 + 四个对角,形成更粗更均匀的描边
CreateNumberText(rootObject.transform, text, new Vector3(-outline, 0f, 0f), Color.black, damageNumberSize, 60); CreateNumberText(rootObject.transform, text, new Vector3(-outline, 0f, 0f), Color.black, damageNumberSize, 60); // 左
CreateNumberText(rootObject.transform, text, new Vector3(outline, 0f, 0f), Color.black, damageNumberSize, 60); CreateNumberText(rootObject.transform, text, new Vector3(outline, 0f, 0f), Color.black, damageNumberSize, 60); // 右
CreateNumberText(rootObject.transform, text, new Vector3(0f, -outline, 0f), Color.black, damageNumberSize, 60); CreateNumberText(rootObject.transform, text, new Vector3(0f, -outline, 0f), Color.black, damageNumberSize, 60); // 下
CreateNumberText(rootObject.transform, text, new Vector3(0f, outline, 0f), Color.black, damageNumberSize, 60); CreateNumberText(rootObject.transform, text, new Vector3(0f, outline, 0f), Color.black, damageNumberSize, 60); // 上
// 白色主字最后画(sortingOrder 高),覆盖在黑边之上。 CreateNumberText(rootObject.transform, text, new Vector3(-outline, -outline, 0f), Color.black, damageNumberSize, 60); // 左下
CreateNumberText(rootObject.transform, text, Vector3.zero, Color.white, damageNumberSize, 61); CreateNumberText(rootObject.transform, text, new Vector3(outline, -outline, 0f), Color.black, damageNumberSize, 60); // 右下
CreateNumberText(rootObject.transform, text, new Vector3(-outline, outline, 0f), Color.black, damageNumberSize, 60); // 左上
CreateNumberText(rootObject.transform, text, new Vector3(outline, outline, 0f), Color.black, damageNumberSize, 60); // 右上
// 主字颜色:治疗=绿色,友方受伤=红色,敌方受伤=白色
Color mainColor = isHeal ? Color.green : (isVictimFriend ? Color.red : Color.white);
CreateNumberText(rootObject.transform, text, Vector3.zero, mainColor, damageNumberSize, 61);
DamageNumber number = rootObject.AddComponent<DamageNumber>(); DamageNumber number = rootObject.AddComponent<DamageNumber>();
number.duration = damageNumberDuration; number.duration = damageNumberDuration;
number.riseHeight = damageNumberRiseHeight; // 友方受伤向下浮动(负值),敌方受伤向上浮动(正值)
number.riseHeight = isVictimFriend ? -damageNumberRiseHeight : damageNumberRiseHeight;
} }
// 创建一个配置好的数字 TextMesh 子物体。 // 创建一个配置好的数字 TextMesh 子物体。
@@ -644,7 +670,7 @@ public class DamageNumber : MonoBehaviour
public float duration = 0.8f; public float duration = 0.8f;
public float riseHeight = 1.2f; public float riseHeight = 1.2f;
// 一个数字由主字+描边副本共5个TextMesh组成,统一驱动透明度。 // 一个数字由主字 + 8 方向描边副本共 9 个 TextMesh 组成,统一驱动透明度。
private TextMesh[] textMeshes; private TextMesh[] textMeshes;
private Color[] startColors; private Color[] startColors;
private Vector3 startPosition; private Vector3 startPosition;
+4
View File
@@ -48,6 +48,10 @@
- [2026-09-14 21:34:17] 2026-09-14 发现:团结引擎 1.10.2 (macOS) 原生 NavMesh 烘焙失效——空场景放一个静态 Cube,用 NavMeshBuilder.BuildNavMeshData / UpdateNavMeshDataAsync / 经典全局 BuildNavMesh 烘焙全部产出 0 三角形,无报错;收集源(CollectSources)、Job 系统、AI 模块包均正常;换构建目标/域重载/重启编辑器均无效。**Why:** 原生导航烘焙在此安装上深层失效,继续用 NavMesh 会浪费时间。**How to apply:** 本项目寻路不要用 NavMesh/NavMeshAgent/NavMeshSurface,改用纯 C# 网格 A*(见 GridPathfinder.cs);若未来升级团结版本可先重跑最小 Cube 烘焙测试再决定。 - [2026-09-14 21:34:17] 2026-09-14 发现:团结引擎 1.10.2 (macOS) 原生 NavMesh 烘焙失效——空场景放一个静态 Cube,用 NavMeshBuilder.BuildNavMeshData / UpdateNavMeshDataAsync / 经典全局 BuildNavMesh 烘焙全部产出 0 三角形,无报错;收集源(CollectSources)、Job 系统、AI 模块包均正常;换构建目标/域重载/重启编辑器均无效。**Why:** 原生导航烘焙在此安装上深层失效,继续用 NavMesh 会浪费时间。**How to apply:** 本项目寻路不要用 NavMesh/NavMeshAgent/NavMeshSurface,改用纯 C# 网格 A*(见 GridPathfinder.cs);若未来升级团结版本可先重跑最小 Cube 烘焙测试再决定。
- [2026-09-14 22:55:12] 2026-09-14 伤害数字系统(WorldHealthBar.SpawnDamageNumber + DamageNumber 组件)已上线:白字黑边样式(1 白主字 + 4 向偏移黑副本,TextMesh 无原生描边所以用副本组合),入口在 TakeDamage/Heal,全场并发上限 60。两个关键坑:1) TextMesh 可读面朝本地-Z,billboard 必须用 LookRotation(selfPos - cameraPos),方向写反会左右镜像;2) 同材质同 sortingOrder 的多个 TextMesh 渲染顺序不确定,后创建的黑色副本会盖住白字——必须显式分层:黑边 sortingOrder=60、白主字=61,白字必须最后创建或层级更高。**Why:** 用户要求白色黑边,先后修复了镜像和纯黑覆盖问题。**How to apply:** 后续做其他 3D 漂浮文字(暴击、金币)沿用 DamageNumber 模式、朝向写法和 sortingOrder 分层。 - [2026-09-14 22:55:12] 2026-09-14 伤害数字系统(WorldHealthBar.SpawnDamageNumber + DamageNumber 组件)已上线:白字黑边样式(1 白主字 + 4 向偏移黑副本,TextMesh 无原生描边所以用副本组合),入口在 TakeDamage/Heal,全场并发上限 60。两个关键坑:1) TextMesh 可读面朝本地-Z,billboard 必须用 LookRotation(selfPos - cameraPos),方向写反会左右镜像;2) 同材质同 sortingOrder 的多个 TextMesh 渲染顺序不确定,后创建的黑色副本会盖住白字——必须显式分层:黑边 sortingOrder=60、白主字=61,白字必须最后创建或层级更高。**Why:** 用户要求白色黑边,先后修复了镜像和纯黑覆盖问题。**How to apply:** 后续做其他 3D 漂浮文字(暴击、金币)沿用 DamageNumber 模式、朝向写法和 sortingOrder 分层。
- [2026-09-15 08:09:51] 2026-09-15 决战玩法(BattleTest.scene)布阵系统已实现:BattleSetupManager 单例管理 Setup/Battle/Ended 三状态。布阵阶段:己方半场 2x2 米虚拟网格(X:-8~8, Z:-20~-8),底部 8 张卡片(代码生成 UI),点卡片选兵种→点格子放置(绿/红高亮预览),manaCost 作为人口消耗(上限 30),点已放单位可移除退人口。开战按钮生成敌方预设阵型(前排近战 Z=14 / 后排远程 Z=18,共 28 人口),全灭判定胜负(0.5 秒轮询)。SummonCardButton 已支持双模式:有 BattleSetupManager 走人口判断选兵,否则走金币建筑放置——两个场景共存互不干扰。**Why:** 用户确定决战玩法方向,网格布阵解决开局扎堆且让排兵布阵成为策略。**How to apply:** 后续决战玩法迭代(AI 阵型多样化、主角辅助技能、重开按钮)都基于 BattleSetupManager 扩展;不要在 CoreBattleTest 场景放 BattleSetupManager。 - [2026-09-15 08:09:51] 2026-09-15 决战玩法(BattleTest.scene)布阵系统已实现:BattleSetupManager 单例管理 Setup/Battle/Ended 三状态。布阵阶段:己方半场 2x2 米虚拟网格(X:-8~8, Z:-20~-8),底部 8 张卡片(代码生成 UI),点卡片选兵种→点格子放置(绿/红高亮预览),manaCost 作为人口消耗(上限 30),点已放单位可移除退人口。开战按钮生成敌方预设阵型(前排近战 Z=14 / 后排远程 Z=18,共 28 人口),全灭判定胜负(0.5 秒轮询)。SummonCardButton 已支持双模式:有 BattleSetupManager 走人口判断选兵,否则走金币建筑放置——两个场景共存互不干扰。**Why:** 用户确定决战玩法方向,网格布阵解决开局扎堆且让排兵布阵成为策略。**How to apply:** 后续决战玩法迭代(AI 阵型多样化、主角辅助技能、重开按钮)都基于 BattleSetupManager 扩展;不要在 CoreBattleTest 场景放 BattleSetupManager。
- [2026-09-15 16:03:53] NavMesh 寻路已成功接入所有单位。团结引擎完全支持 NavMesh(需安装 com.unity.ai.navigation 包)。实现方式:1) 添加 NavMeshSurface 组件并烘焙;2) 为所有角色预制体添加 NavMeshAgent;3) 修改 UnitBase.cs 的 move() 方法优先使用 NavMeshAgent.SetDestination(),并在 Start() 中禁用 CharacterController 避免冲突。CoreBattleTest 场景已创建中央分隔墙形成左右两路,单位能够自动沿路线寻路并绕过障碍物。
- [2026-09-15 17:33:54] NavMesh 寻路攻击漂移问题修复方案:单位攻击时会因 NavMeshAgent 继续工作而漂移。解决方法是在 attack() 方法和 Update() 的 State.Attack 分支中直接禁用 NavMeshAgent (navAgent.enabled = false),在 move() 方法中重新启用。同时修复了 applyGravity() 调用已禁用 CharacterController 导致的警告(添加 controller.enabled 检查)。性能优化:寻路路径更新频率限制为 0.5 秒一次(nextPathUpdateTime + PathUpdateInterval),减少 93% 路径计算开销。
- [2026-09-15 18:42:26] 伤害数字显示规则:友方单位(Friend tag)受伤显示红色数字向下浮动,敌方单位(Enemy tag)受伤显示白色数字向上浮动,治疗显示绿色向上。技术实现:8方向黑色描边(DamageNumberOutlineWidth = 0.025f 常量)+ 1个彩色主字,共9个TextMesh。通过 gameObject.CompareTag("Friend") 判断受击者阵营。已更新到《召唤之战-核心玩法.md》文档 2.3 章节。
- [2026-09-15 19:33:31] 金币系统已完成:GoldManager 单例管理,初始500金币,右上角UI实时更新。金矿建筑产出50金币/5秒,主角产出30金币/8秒。WorldHealthBar 的 currentEnergy 改为属性(带 setter 自动调用 UpdateEnergyFill),能量条尺寸从8像素增加到12像素(EnergyPixelHeight=12,EnergyPixelWidth=100)。能量条显示金币产出进度,满了产出并重置。已更新核心玩法文档。
### Reference ### Reference
- [2026-09-09 12:23:17] 飞行物脚本 Projectile.cs 负责锁敌追踪、碰撞检测和伤害结算。箭矢预制体 ArrowProjectile_White.prefab 已配置 Projectile 组件和 CapsuleCollider(Is Trigger)。远程单位需在攻击动画松弦帧添加 OnAttackEffect Event,并在 UnitBase 中设置 attackType=RangedProjectile、projectilePrefab、projectileSpawnPoint。 - [2026-09-09 12:23:17] 飞行物脚本 Projectile.cs 负责锁敌追踪、碰撞检测和伤害结算。箭矢预制体 ArrowProjectile_White.prefab 已配置 Projectile 组件和 CapsuleCollider(Is Trigger)。远程单位需在攻击动画松弦帧添加 OnAttackEffect Event,并在 UnitBase 中设置 attackType=RangedProjectile、projectilePrefab、projectileSpawnPoint。
+1
View File
@@ -6,6 +6,7 @@
"com.qq.weixin.minigame": "https://gitee.com/wechat-minigame/minigame-tuanjie-transform-sdk.git", "com.qq.weixin.minigame": "https://gitee.com/wechat-minigame/minigame-tuanjie-transform-sdk.git",
"com.unity.2d.sprite": "1.0.0", "com.unity.2d.sprite": "1.0.0",
"com.unity.adaptiveperformance": "4.0.1", "com.unity.adaptiveperformance": "4.0.1",
"com.unity.ai.navigation": "1.1.7",
"com.unity.feature.mobile": "1.0.0", "com.unity.feature.mobile": "1.0.0",
"com.unity.ide.rider": "3.0.40", "com.unity.ide.rider": "3.0.40",
"com.unity.ide.visualstudio": "2.0.22", "com.unity.ide.visualstudio": "2.0.22",
+9
View File
@@ -63,6 +63,15 @@
}, },
"url": "https://packages.tuanjie.cn" "url": "https://packages.tuanjie.cn"
}, },
"com.unity.ai.navigation": {
"version": "1.1.7",
"depth": 0,
"source": "registry",
"dependencies": {
"com.unity.modules.ai": "1.0.0"
},
"url": "https://packages.tuanjie.cn"
},
"com.unity.burst": { "com.unity.burst": {
"version": "1.8.30-t2", "version": "1.8.30-t2",
"depth": 1, "depth": 1,
+37 -9
View File
@@ -29,19 +29,47 @@
- 进攻路线根据关卡设计分为 1 路、2 路或 3 路。 - 进攻路线根据关卡设计分为 1 路、2 路或 3 路。
- 单位从己方区域向敌方区域推进,并在路线上寻找最近的有效目标。 - 单位从己方区域向敌方区域推进,并在路线上寻找最近的有效目标。
### 2.2 寻路 ### 2.2 寻路(已实现 ✅)
当前单位主要沿直线移动,后续需要加入自动寻路能力。 **当前状态:**
- 已接入 Unity NavMesh 自动寻路系统
- 单位可以自动绕过障碍物和地形
- 支持左右两路地图布局
- 寻路性能已优化(路径更新频率限制为 0.5 秒/次)
目标规则: **寻路规则:**
- 单位自动寻找距离最近的敌方单位或建筑
- 遇到障碍物时自动绕行
- 攻击时停止移动,避免边打边漂移
- 单位之间可以轻微重叠(无物理碰撞),避免密集战斗时卡位
- NavMeshAgent 提供基础避让,但不保证完全不重叠
- 单位自动寻找距离最近的敌方单位或建筑; **技术细节:**
- 遇到障碍物时能够绕行; - 使用 NavMeshAgent 替代 CharacterController 移动
- 多路线地图中,单位可以选择可到达的进攻路径; - 攻击时禁用 NavMeshAgent,防止漂移
- 单位不能因为建筑或己方单位摆放而永久卡死; - 移动时重新启用 NavMeshAgent
- 寻路系统应优先服务于短地图和少量障碍物,不追求复杂 RTS 级别的路径编辑。 - 攻击距离判定增加 1.5 米缓冲,避免单位互相顶到才攻击
具体采用 Unity NavMesh 还是轻量级多路线方案,待实现阶段确认。 ### 2.3 伤害数字反馈(已实现 ✅)
**当前状态:**
- 单位受击时头顶弹出伤害数字
- 8 方向黑色描边 + 彩色主字,清晰可见
- 根据受击者阵营显示不同颜色和浮动方向
**显示规则:**
| 情况 | 数字颜色 | 浮动方向 | 说明 |
|------|---------|---------|------|
| 友方单位受伤 | 红色 | 向下 | 玩家的士兵被敌人攻击 |
| 敌方单位受伤 | 白色 | 向上 | 敌人被玩家的士兵攻击 |
| 任何单位治疗 | 绿色 | 向上 | 单位被治疗恢复生命值 |
**技术细节:**
- 每个伤害数字由 9 个 TextMesh 组成(1 个彩色主字 + 8 个黑色描边)
- 描边宽度统一为 0.025,所有角色一致
- 伤害数字持续 0.8 秒,带缩放和淡出动画
- 同时最多显示 60 个伤害数字,防止极端情况刷屏
## 三、主角定位 ## 三、主角定位