仓库组件 + 防御塔升级

- `PlayerInventoryComponent`:玩家库存组件,收集金币/组件/防御塔等道具
- 补全防御塔的升级逻辑,最高 5 级
This commit is contained in:
SepComet
2026-03-02 19:50:12 +08:00
parent 5ba94828a8
commit daba9cbdf9
20 changed files with 753 additions and 138 deletions
@@ -8,6 +8,8 @@ namespace Components
public class DefenseTowerController : MonoBehaviour
{
private const string AttackRangeIndicatorObjectName = "AttackRangeIndicator";
private const int MinTowerLevel = 0;
private const int MaxTowerLevel = 4;
private static Material s_AttackRangeSharedMaterial;
[SerializeField] private ShooterMuzzleComp _muzzleComp;
@@ -15,7 +17,6 @@ namespace Components
[SerializeField] private BasicBaseComp _baseComp;
[SerializeField] private Transform _scanOrigin;
[SerializeField] [Min(0.02f)] private float _retargetInterval = 0.1f;
[SerializeField] private bool _autoUpdate = true;
[SerializeField] private LineRenderer _attackRangeRenderer;
[SerializeField] [Min(12)] private int _attackRangeSegments = 64;
[SerializeField] [Min(0.005f)] private float _attackRangeLineWidth = 0.08f;
@@ -25,6 +26,7 @@ namespace Components
private Transform _currentTarget;
private float _retargetTimer;
private float _attackRange;
private int _towerLevel;
public Transform CurrentTarget => _currentTarget;
@@ -33,17 +35,12 @@ namespace Components
ResolveComponents();
}
private void Update()
public void OnInit(DefenseTowerStatsData stats)
{
if (!_autoUpdate)
{
return;
}
OnUpdate(Time.deltaTime);
OnInit(stats, MinTowerLevel);
}
public void OnInit(DefenseTowerStatsData stats)
public void OnInit(DefenseTowerStatsData stats, int towerLevel)
{
ResolveComponents();
if (stats == null)
@@ -51,10 +48,16 @@ namespace Components
return;
}
_muzzleComp?.OnInit(stats.AttackDamage, stats.AttackMethodType);
_bearingComp?.OnInit(stats.RotateSpeed, stats.AttackRange);
_baseComp?.OnInit(stats.AttackSpeed, stats.AttackPropertyType);
SetAttackRange(stats.AttackRange);
_towerLevel = Mathf.Clamp(towerLevel, MinTowerLevel, MaxTowerLevel);
int attackDamage = ResolveIntValue(stats.AttackDamage, _towerLevel, 1, 1);
float rotateSpeed = ResolveFloatValue(stats.RotateSpeed, _towerLevel, 180f, 1f);
float attackRange = ResolveFloatValue(stats.AttackRange, _towerLevel, 5f, 0.1f);
float attackSpeed = ResolveFloatValue(stats.AttackSpeed, _towerLevel, 1f, 0.01f);
_muzzleComp?.OnInit(attackDamage, stats.AttackMethodType);
_bearingComp?.OnInit(rotateSpeed, attackRange);
_baseComp?.OnInit(attackSpeed, stats.AttackPropertyType);
SetAttackRange(attackRange);
SetAttackRangeVisible(false);
_currentTarget = null;
_retargetTimer = 0f;
@@ -65,6 +68,7 @@ namespace Components
SetAttackRangeVisible(false);
_currentTarget = null;
_retargetTimer = 0f;
_towerLevel = MinTowerLevel;
_muzzleComp?.OnReset();
_bearingComp?.OnReset();
_baseComp?.OnReset();
@@ -86,11 +90,6 @@ namespace Components
RebuildAttackRangeGeometry();
}
public void SetAutoUpdate(bool autoUpdate)
{
_autoUpdate = autoUpdate;
}
public void SetTarget(Transform target)
{
_currentTarget = target;
@@ -195,6 +194,30 @@ namespace Components
return target != null && target.gameObject.activeInHierarchy;
}
private static int ResolveIntValue(int[] values, int level, int fallback, int minValue)
{
int resolved = Mathf.Max(minValue, fallback);
if (values == null || values.Length <= 0)
{
return resolved;
}
int index = Mathf.Clamp(level, 0, values.Length - 1);
return Mathf.Max(minValue, values[index]);
}
private static float ResolveFloatValue(float[] values, int level, float fallback, float minValue)
{
float resolved = Mathf.Max(minValue, fallback);
if (values == null || values.Length <= 0)
{
return resolved;
}
int index = Mathf.Clamp(level, 0, values.Length - 1);
return Mathf.Max(minValue, values[index]);
}
private void EnsureAttackRangeRenderer()
{
if (_attackRangeRenderer == null)