修复旋转问题 + 防御塔范围可视化
- BasicBearingComp 和 ShooterBullet 的旋转由之前的 3D 方式调整为只旋转 Z 轴 - DefenseTowerController 通过 LineRenderer 添加了攻击范围可视化
This commit is contained in:
parent
564817d752
commit
344191a91c
|
|
@ -98,10 +98,10 @@ MonoBehaviour:
|
||||||
m_Script: {fileID: 11500000, guid: 20a2c131403122146a41148cb72fcd43, type: 3}
|
m_Script: {fileID: 11500000, guid: 20a2c131403122146a41148cb72fcd43, type: 3}
|
||||||
m_Name:
|
m_Name:
|
||||||
m_EditorClassIdentifier:
|
m_EditorClassIdentifier:
|
||||||
_attackDamage: 10
|
_attackDamage: 100
|
||||||
_attackMethodType: 1
|
_attackMethodType: 1
|
||||||
_bulletTypeId: 501
|
_bulletTypeId: 501
|
||||||
_muzzlePoint: {fileID: 0}
|
_muzzlePoint: {fileID: 7637292285124107611}
|
||||||
_bulletSpeed: 12
|
_bulletSpeed: 12
|
||||||
--- !u!1 &1221576993898367501
|
--- !u!1 &1221576993898367501
|
||||||
GameObject:
|
GameObject:
|
||||||
|
|
@ -114,6 +114,7 @@ GameObject:
|
||||||
- component: {fileID: 6791423131335728073}
|
- component: {fileID: 6791423131335728073}
|
||||||
- component: {fileID: 8183383920109690380}
|
- component: {fileID: 8183383920109690380}
|
||||||
- component: {fileID: 3255949411223456789}
|
- component: {fileID: 3255949411223456789}
|
||||||
|
- component: {fileID: 4014432302095443276}
|
||||||
m_Layer: 0
|
m_Layer: 0
|
||||||
m_Name: TowerEntity
|
m_Name: TowerEntity
|
||||||
m_TagString: Untagged
|
m_TagString: Untagged
|
||||||
|
|
@ -155,6 +156,11 @@ MonoBehaviour:
|
||||||
_scanOrigin: {fileID: 6791423131335728073}
|
_scanOrigin: {fileID: 6791423131335728073}
|
||||||
_retargetInterval: 0.1
|
_retargetInterval: 0.1
|
||||||
_autoUpdate: 0
|
_autoUpdate: 0
|
||||||
|
_attackRangeRenderer: {fileID: 4014432302095443276}
|
||||||
|
_attackRangeSegments: 64
|
||||||
|
_attackRangeLineWidth: 0.08
|
||||||
|
_attackRangeColor: {r: 0.1, g: 1, b: 0.4, a: 0.8}
|
||||||
|
_attackRangeZOffset: -0.01
|
||||||
--- !u!114 &3255949411223456789
|
--- !u!114 &3255949411223456789
|
||||||
MonoBehaviour:
|
MonoBehaviour:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
|
|
@ -167,6 +173,110 @@ MonoBehaviour:
|
||||||
m_Script: {fileID: 11500000, guid: d87f56efd9024709a3baf8ef8a6f4fd3, type: 3}
|
m_Script: {fileID: 11500000, guid: d87f56efd9024709a3baf8ef8a6f4fd3, type: 3}
|
||||||
m_Name:
|
m_Name:
|
||||||
m_EditorClassIdentifier:
|
m_EditorClassIdentifier:
|
||||||
|
--- !u!120 &4014432302095443276
|
||||||
|
LineRenderer:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 1221576993898367501}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_CastShadows: 1
|
||||||
|
m_ReceiveShadows: 1
|
||||||
|
m_DynamicOccludee: 1
|
||||||
|
m_StaticShadowCaster: 0
|
||||||
|
m_MotionVectors: 0
|
||||||
|
m_LightProbeUsage: 0
|
||||||
|
m_ReflectionProbeUsage: 0
|
||||||
|
m_RayTracingMode: 0
|
||||||
|
m_RayTraceProcedural: 0
|
||||||
|
m_RenderingLayerMask: 1
|
||||||
|
m_RendererPriority: 0
|
||||||
|
m_Materials:
|
||||||
|
- {fileID: 0}
|
||||||
|
m_StaticBatchInfo:
|
||||||
|
firstSubMesh: 0
|
||||||
|
subMeshCount: 0
|
||||||
|
m_StaticBatchRoot: {fileID: 0}
|
||||||
|
m_ProbeAnchor: {fileID: 0}
|
||||||
|
m_LightProbeVolumeOverride: {fileID: 0}
|
||||||
|
m_ScaleInLightmap: 1
|
||||||
|
m_ReceiveGI: 1
|
||||||
|
m_PreserveUVs: 0
|
||||||
|
m_IgnoreNormalsForChartDetection: 0
|
||||||
|
m_ImportantGI: 0
|
||||||
|
m_StitchLightmapSeams: 1
|
||||||
|
m_SelectedEditorRenderState: 3
|
||||||
|
m_MinimumChartSize: 4
|
||||||
|
m_AutoUVMaxDistance: 0.5
|
||||||
|
m_AutoUVMaxAngle: 89
|
||||||
|
m_LightmapParameters: {fileID: 0}
|
||||||
|
m_SortingLayerID: 0
|
||||||
|
m_SortingLayer: 0
|
||||||
|
m_SortingOrder: 0
|
||||||
|
m_Positions:
|
||||||
|
- {x: 0, y: 0, z: 0}
|
||||||
|
- {x: 0, y: 0, z: 1}
|
||||||
|
m_Parameters:
|
||||||
|
serializedVersion: 3
|
||||||
|
widthMultiplier: 1
|
||||||
|
widthCurve:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_Curve:
|
||||||
|
- serializedVersion: 3
|
||||||
|
time: 0
|
||||||
|
value: 1
|
||||||
|
inSlope: 0
|
||||||
|
outSlope: 0
|
||||||
|
tangentMode: 0
|
||||||
|
weightedMode: 0
|
||||||
|
inWeight: 0.33333334
|
||||||
|
outWeight: 0.33333334
|
||||||
|
m_PreInfinity: 2
|
||||||
|
m_PostInfinity: 2
|
||||||
|
m_RotationOrder: 4
|
||||||
|
colorGradient:
|
||||||
|
serializedVersion: 2
|
||||||
|
key0: {r: 1, g: 1, b: 1, a: 1}
|
||||||
|
key1: {r: 1, g: 1, b: 1, a: 1}
|
||||||
|
key2: {r: 0, g: 0, b: 0, a: 0}
|
||||||
|
key3: {r: 0, g: 0, b: 0, a: 0}
|
||||||
|
key4: {r: 0, g: 0, b: 0, a: 0}
|
||||||
|
key5: {r: 0, g: 0, b: 0, a: 0}
|
||||||
|
key6: {r: 0, g: 0, b: 0, a: 0}
|
||||||
|
key7: {r: 0, g: 0, b: 0, a: 0}
|
||||||
|
ctime0: 0
|
||||||
|
ctime1: 65535
|
||||||
|
ctime2: 0
|
||||||
|
ctime3: 0
|
||||||
|
ctime4: 0
|
||||||
|
ctime5: 0
|
||||||
|
ctime6: 0
|
||||||
|
ctime7: 0
|
||||||
|
atime0: 0
|
||||||
|
atime1: 65535
|
||||||
|
atime2: 0
|
||||||
|
atime3: 0
|
||||||
|
atime4: 0
|
||||||
|
atime5: 0
|
||||||
|
atime6: 0
|
||||||
|
atime7: 0
|
||||||
|
m_Mode: 0
|
||||||
|
m_ColorSpace: -1
|
||||||
|
m_NumColorKeys: 2
|
||||||
|
m_NumAlphaKeys: 2
|
||||||
|
numCornerVertices: 0
|
||||||
|
numCapVertices: 0
|
||||||
|
alignment: 0
|
||||||
|
textureMode: 0
|
||||||
|
textureScale: {x: 1, y: 1}
|
||||||
|
shadowBias: 0.5
|
||||||
|
generateLightingData: 0
|
||||||
|
m_MaskInteraction: 0
|
||||||
|
m_UseWorldSpace: 1
|
||||||
|
m_Loop: 0
|
||||||
|
m_ApplyActiveColorSpace: 1
|
||||||
--- !u!1 &2017874305906296486
|
--- !u!1 &2017874305906296486
|
||||||
GameObject:
|
GameObject:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
|
|
@ -269,8 +379,7 @@ MonoBehaviour:
|
||||||
_rotateSpeed: 180
|
_rotateSpeed: 180
|
||||||
_attackRange: 5
|
_attackRange: 5
|
||||||
_aimToleranceAngle: 2
|
_aimToleranceAngle: 2
|
||||||
_rotateRoot: {fileID: 0}
|
_rotateRoot: {fileID: 5517541809701307552}
|
||||||
_yawOnly: 1
|
|
||||||
--- !u!1 &4867507345079921359
|
--- !u!1 &4867507345079921359
|
||||||
GameObject:
|
GameObject:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ namespace Components
|
||||||
[SerializeField] [Min(0.1f)] private float _attackRange = 5f;
|
[SerializeField] [Min(0.1f)] private float _attackRange = 5f;
|
||||||
[SerializeField] [Min(0.1f)] private float _aimToleranceAngle = 2f;
|
[SerializeField] [Min(0.1f)] private float _aimToleranceAngle = 2f;
|
||||||
[SerializeField] private Transform _rotateRoot;
|
[SerializeField] private Transform _rotateRoot;
|
||||||
[SerializeField] private bool _yawOnly = true;
|
|
||||||
|
|
||||||
public float RotateSpeed => _rotateSpeed;
|
public float RotateSpeed => _rotateSpeed;
|
||||||
public float AttackRange => _attackRange;
|
public float AttackRange => _attackRange;
|
||||||
|
|
@ -47,20 +46,19 @@ namespace Components
|
||||||
}
|
}
|
||||||
|
|
||||||
Transform rotateRoot = _rotateRoot != null ? _rotateRoot : transform;
|
Transform rotateRoot = _rotateRoot != null ? _rotateRoot : transform;
|
||||||
Vector3 toTarget = target.position - rotateRoot.position;
|
Vector2 toTarget = (Vector2)(target.position - rotateRoot.position);
|
||||||
if (_yawOnly)
|
|
||||||
{
|
|
||||||
toTarget.y = 0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (toTarget.sqrMagnitude <= Mathf.Epsilon)
|
if (toTarget.sqrMagnitude <= Mathf.Epsilon)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Quaternion targetRotation = Quaternion.LookRotation(toTarget.normalized, Vector3.up);
|
float targetZAngle = Vector2.SignedAngle(Vector2.up, toTarget);
|
||||||
float rotateAngle = _rotateSpeed * Mathf.Max(0f, deltaTime);
|
float currentZAngle = rotateRoot.eulerAngles.z;
|
||||||
rotateRoot.rotation = Quaternion.RotateTowards(rotateRoot.rotation, targetRotation, rotateAngle);
|
float maxStep = _rotateSpeed * Mathf.Max(0f, deltaTime);
|
||||||
|
float angleDelta = Mathf.DeltaAngle(currentZAngle, targetZAngle);
|
||||||
|
float nextZAngle = currentZAngle + Mathf.Clamp(angleDelta, -maxStep, maxStep);
|
||||||
|
rotateRoot.rotation = Quaternion.Euler(0f, 0f, nextZAngle);
|
||||||
return IsTargetAligned(target);
|
return IsTargetAligned(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -72,18 +70,16 @@ namespace Components
|
||||||
}
|
}
|
||||||
|
|
||||||
Transform rotateRoot = _rotateRoot != null ? _rotateRoot : transform;
|
Transform rotateRoot = _rotateRoot != null ? _rotateRoot : transform;
|
||||||
Vector3 toTarget = target.position - rotateRoot.position;
|
Vector2 toTarget = (Vector2)(target.position - rotateRoot.position);
|
||||||
if (_yawOnly)
|
|
||||||
{
|
|
||||||
toTarget.y = 0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (toTarget.sqrMagnitude <= Mathf.Epsilon)
|
if (toTarget.sqrMagnitude <= Mathf.Epsilon)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
float angle = Vector3.Angle(rotateRoot.forward, toTarget.normalized);
|
float targetZAngle = Vector2.SignedAngle(Vector2.up, toTarget);
|
||||||
|
float currentZAngle = rotateRoot.eulerAngles.z;
|
||||||
|
float angle = Mathf.Abs(Mathf.DeltaAngle(currentZAngle, targetZAngle));
|
||||||
return angle <= _aimToleranceAngle;
|
return angle <= _aimToleranceAngle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,15 +7,24 @@ namespace Components
|
||||||
[DisallowMultipleComponent]
|
[DisallowMultipleComponent]
|
||||||
public class DefenseTowerController : MonoBehaviour
|
public class DefenseTowerController : MonoBehaviour
|
||||||
{
|
{
|
||||||
|
private const string AttackRangeIndicatorObjectName = "AttackRangeIndicator";
|
||||||
|
private static Material s_AttackRangeSharedMaterial;
|
||||||
|
|
||||||
[SerializeField] private ShooterMuzzleComp _muzzleComp;
|
[SerializeField] private ShooterMuzzleComp _muzzleComp;
|
||||||
[SerializeField] private BasicBearingComp _bearingComp;
|
[SerializeField] private BasicBearingComp _bearingComp;
|
||||||
[SerializeField] private BasicBaseComp _baseComp;
|
[SerializeField] private BasicBaseComp _baseComp;
|
||||||
[SerializeField] private Transform _scanOrigin;
|
[SerializeField] private Transform _scanOrigin;
|
||||||
[SerializeField] [Min(0.02f)] private float _retargetInterval = 0.1f;
|
[SerializeField] [Min(0.02f)] private float _retargetInterval = 0.1f;
|
||||||
[SerializeField] private bool _autoUpdate = true;
|
[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;
|
||||||
|
[SerializeField] private Color _attackRangeColor = new Color(0.1f, 1f, 0.4f, 0.8f);
|
||||||
|
[SerializeField] private float _attackRangeZOffset = -0.01f;
|
||||||
|
|
||||||
private Transform _currentTarget;
|
private Transform _currentTarget;
|
||||||
private float _retargetTimer;
|
private float _retargetTimer;
|
||||||
|
private float _attackRange;
|
||||||
|
|
||||||
public Transform CurrentTarget => _currentTarget;
|
public Transform CurrentTarget => _currentTarget;
|
||||||
|
|
||||||
|
|
@ -45,12 +54,15 @@ namespace Components
|
||||||
_muzzleComp?.OnInit(stats.AttackDamage, stats.AttackMethodType);
|
_muzzleComp?.OnInit(stats.AttackDamage, stats.AttackMethodType);
|
||||||
_bearingComp?.OnInit(stats.RotateSpeed, stats.AttackRange);
|
_bearingComp?.OnInit(stats.RotateSpeed, stats.AttackRange);
|
||||||
_baseComp?.OnInit(stats.AttackSpeed, stats.AttackPropertyType);
|
_baseComp?.OnInit(stats.AttackSpeed, stats.AttackPropertyType);
|
||||||
|
SetAttackRange(stats.AttackRange);
|
||||||
|
SetAttackRangeVisible(false);
|
||||||
_currentTarget = null;
|
_currentTarget = null;
|
||||||
_retargetTimer = 0f;
|
_retargetTimer = 0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnReset()
|
public void OnReset()
|
||||||
{
|
{
|
||||||
|
SetAttackRangeVisible(false);
|
||||||
_currentTarget = null;
|
_currentTarget = null;
|
||||||
_retargetTimer = 0f;
|
_retargetTimer = 0f;
|
||||||
_muzzleComp?.OnReset();
|
_muzzleComp?.OnReset();
|
||||||
|
|
@ -58,6 +70,22 @@ namespace Components
|
||||||
_baseComp?.OnReset();
|
_baseComp?.OnReset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetAttackRangeVisible(bool visible)
|
||||||
|
{
|
||||||
|
EnsureAttackRangeRenderer();
|
||||||
|
if (_attackRangeRenderer != null)
|
||||||
|
{
|
||||||
|
_attackRangeRenderer.enabled = visible;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetAttackRange(float range)
|
||||||
|
{
|
||||||
|
_attackRange = Mathf.Max(0.05f, range);
|
||||||
|
EnsureAttackRangeRenderer();
|
||||||
|
RebuildAttackRangeGeometry();
|
||||||
|
}
|
||||||
|
|
||||||
public void SetAutoUpdate(bool autoUpdate)
|
public void SetAutoUpdate(bool autoUpdate)
|
||||||
{
|
{
|
||||||
_autoUpdate = autoUpdate;
|
_autoUpdate = autoUpdate;
|
||||||
|
|
@ -153,6 +181,8 @@ namespace Components
|
||||||
{
|
{
|
||||||
_baseComp = GetComponent<BasicBaseComp>();
|
_baseComp = GetComponent<BasicBaseComp>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EnsureAttackRangeRenderer();
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool HasCoreComponents()
|
private bool HasCoreComponents()
|
||||||
|
|
@ -164,5 +194,72 @@ namespace Components
|
||||||
{
|
{
|
||||||
return target != null && target.gameObject.activeInHierarchy;
|
return target != null && target.gameObject.activeInHierarchy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void EnsureAttackRangeRenderer()
|
||||||
|
{
|
||||||
|
if (_attackRangeRenderer == null)
|
||||||
|
{
|
||||||
|
Transform indicatorTransform = transform.Find(AttackRangeIndicatorObjectName);
|
||||||
|
if (indicatorTransform == null)
|
||||||
|
{
|
||||||
|
GameObject indicatorObject = new GameObject(AttackRangeIndicatorObjectName);
|
||||||
|
indicatorTransform = indicatorObject.transform;
|
||||||
|
indicatorTransform.SetParent(transform, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
_attackRangeRenderer = indicatorTransform.GetComponent<LineRenderer>();
|
||||||
|
if (_attackRangeRenderer == null)
|
||||||
|
{
|
||||||
|
_attackRangeRenderer = indicatorTransform.gameObject.AddComponent<LineRenderer>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_attackRangeRenderer.useWorldSpace = false;
|
||||||
|
_attackRangeRenderer.loop = true;
|
||||||
|
_attackRangeRenderer.positionCount = Mathf.Max(12, _attackRangeSegments);
|
||||||
|
_attackRangeRenderer.widthMultiplier = _attackRangeLineWidth;
|
||||||
|
_attackRangeRenderer.startColor = _attackRangeColor;
|
||||||
|
_attackRangeRenderer.endColor = _attackRangeColor;
|
||||||
|
_attackRangeRenderer.numCapVertices = 4;
|
||||||
|
_attackRangeRenderer.numCornerVertices = 4;
|
||||||
|
_attackRangeRenderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
|
||||||
|
_attackRangeRenderer.receiveShadows = false;
|
||||||
|
_attackRangeRenderer.allowOcclusionWhenDynamic = false;
|
||||||
|
_attackRangeRenderer.enabled = false;
|
||||||
|
|
||||||
|
if (s_AttackRangeSharedMaterial == null)
|
||||||
|
{
|
||||||
|
Shader lineShader = Shader.Find("Sprites/Default");
|
||||||
|
if (lineShader != null)
|
||||||
|
{
|
||||||
|
s_AttackRangeSharedMaterial = new Material(lineShader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s_AttackRangeSharedMaterial != null)
|
||||||
|
{
|
||||||
|
_attackRangeRenderer.sharedMaterial = s_AttackRangeSharedMaterial;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RebuildAttackRangeGeometry()
|
||||||
|
{
|
||||||
|
if (_attackRangeRenderer == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int segmentCount = Mathf.Max(12, _attackRangeSegments);
|
||||||
|
_attackRangeRenderer.positionCount = segmentCount;
|
||||||
|
|
||||||
|
float stepAngle = Mathf.PI * 2f / segmentCount;
|
||||||
|
for (int i = 0; i < segmentCount; i++)
|
||||||
|
{
|
||||||
|
float angle = stepAngle * i;
|
||||||
|
float x = Mathf.Cos(angle) * _attackRange;
|
||||||
|
float y = Mathf.Sin(angle) * _attackRange;
|
||||||
|
_attackRangeRenderer.SetPosition(i, new Vector3(x, y, _attackRangeZOffset));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,12 @@ namespace Components
|
||||||
_lifetime = 0f;
|
_lifetime = 0f;
|
||||||
_despawnRequested = false;
|
_despawnRequested = false;
|
||||||
_isRunning = _target != null;
|
_isRunning = _target != null;
|
||||||
|
if (_isRunning)
|
||||||
|
{
|
||||||
|
Vector2 initialDirection = (Vector2)(_target.position - transform.position);
|
||||||
|
RotateToDirection(initialDirection);
|
||||||
|
}
|
||||||
|
|
||||||
if (!_isRunning)
|
if (!_isRunning)
|
||||||
{
|
{
|
||||||
_despawnRequested = true;
|
_despawnRequested = true;
|
||||||
|
|
@ -63,7 +69,8 @@ namespace Components
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector3 toTarget = _target.position - transform.position;
|
Vector3 currentPosition = transform.position;
|
||||||
|
Vector2 toTarget = (Vector2)(_target.position - currentPosition);
|
||||||
float hitDistanceSquared = _hitDistance * _hitDistance;
|
float hitDistanceSquared = _hitDistance * _hitDistance;
|
||||||
if (toTarget.sqrMagnitude <= hitDistanceSquared)
|
if (toTarget.sqrMagnitude <= hitDistanceSquared)
|
||||||
{
|
{
|
||||||
|
|
@ -71,17 +78,18 @@ namespace Components
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector3 forward = toTarget.normalized;
|
Vector2 direction = toTarget.normalized;
|
||||||
|
RotateToDirection(direction);
|
||||||
float moveDistance = _speed * Mathf.Max(0f, deltaTime);
|
float moveDistance = _speed * Mathf.Max(0f, deltaTime);
|
||||||
if (moveDistance * moveDistance >= toTarget.sqrMagnitude)
|
if (moveDistance * moveDistance >= toTarget.sqrMagnitude)
|
||||||
{
|
{
|
||||||
transform.position = _target.position;
|
Vector3 targetPosition = _target.position;
|
||||||
|
transform.position = new Vector3(targetPosition.x, targetPosition.y, currentPosition.z);
|
||||||
HitTarget();
|
HitTarget();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
transform.position += forward * moveDistance;
|
transform.position += (Vector3)(direction * moveDistance);
|
||||||
transform.forward = forward;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryConsumeDespawnRequest()
|
public bool TryConsumeDespawnRequest()
|
||||||
|
|
@ -105,9 +113,9 @@ namespace Components
|
||||||
}
|
}
|
||||||
|
|
||||||
MonoBehaviour[] components = _target.GetComponentsInParent<MonoBehaviour>();
|
MonoBehaviour[] components = _target.GetComponentsInParent<MonoBehaviour>();
|
||||||
for (int i = 0; i < components.Length; i++)
|
foreach (var mono in components)
|
||||||
{
|
{
|
||||||
if (components[i] is IDamageReceiver damageReceiver)
|
if (mono is IDamageReceiver damageReceiver)
|
||||||
{
|
{
|
||||||
damageReceiver.TakeDamage(_damage, _attackPropertyType);
|
damageReceiver.TakeDamage(_damage, _attackPropertyType);
|
||||||
break;
|
break;
|
||||||
|
|
@ -117,5 +125,16 @@ namespace Components
|
||||||
_isRunning = false;
|
_isRunning = false;
|
||||||
_despawnRequested = true;
|
_despawnRequested = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void RotateToDirection(Vector2 direction)
|
||||||
|
{
|
||||||
|
if (direction.sqrMagnitude <= Mathf.Epsilon)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
float targetZAngle = Vector2.SignedAngle(Vector2.up, direction);
|
||||||
|
transform.rotation = Quaternion.Euler(0f, 0f, targetZAngle);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ namespace Components
|
||||||
[DisallowMultipleComponent]
|
[DisallowMultipleComponent]
|
||||||
public class ShooterMuzzleComp : MonoBehaviour
|
public class ShooterMuzzleComp : MonoBehaviour
|
||||||
{
|
{
|
||||||
[SerializeField] [Min(1f)] private int _attackDamage = 10;
|
[SerializeField] [Min(1f)] private int _attackDamage = 100;
|
||||||
[SerializeField] private AttackMethodType _attackMethodType = AttackMethodType.NormalBullet;
|
[SerializeField] private AttackMethodType _attackMethodType = AttackMethodType.NormalBullet;
|
||||||
[SerializeField] [Min(1)] private int _bulletTypeId = 501;
|
[SerializeField] [Min(1)] private int _bulletTypeId = 501;
|
||||||
[SerializeField] private Transform _muzzlePoint;
|
[SerializeField] private Transform _muzzlePoint;
|
||||||
|
|
@ -54,7 +54,6 @@ namespace Components
|
||||||
bulletEntityId,
|
bulletEntityId,
|
||||||
_bulletTypeId,
|
_bulletTypeId,
|
||||||
spawnPoint.position,
|
spawnPoint.position,
|
||||||
spawnPoint.rotation,
|
|
||||||
target,
|
target,
|
||||||
_attackDamage,
|
_attackDamage,
|
||||||
_bulletSpeed,
|
_bulletSpeed,
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,6 @@ namespace GeometryTD.Entity.EntityData
|
||||||
int entityId,
|
int entityId,
|
||||||
int typeId,
|
int typeId,
|
||||||
Vector3 position,
|
Vector3 position,
|
||||||
Quaternion rotation,
|
|
||||||
Transform target,
|
Transform target,
|
||||||
int damage,
|
int damage,
|
||||||
float speed,
|
float speed,
|
||||||
|
|
@ -25,7 +24,7 @@ namespace GeometryTD.Entity.EntityData
|
||||||
float maxLifetime = 3f) : base(entityId, typeId)
|
float maxLifetime = 3f) : base(entityId, typeId)
|
||||||
{
|
{
|
||||||
Position = position;
|
Position = position;
|
||||||
Rotation = rotation;
|
|
||||||
_target = target;
|
_target = target;
|
||||||
_damage = damage;
|
_damage = damage;
|
||||||
_speed = speed;
|
_speed = speed;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
using Components;
|
using Components;
|
||||||
using GeometryTD.Entity.EntityData;
|
using GeometryTD.Entity.EntityData;
|
||||||
|
using UnityEngine;
|
||||||
using UnityGameFramework.Runtime;
|
using UnityGameFramework.Runtime;
|
||||||
|
|
||||||
namespace GeometryTD.Entity
|
namespace GeometryTD.Entity
|
||||||
|
|
@ -36,6 +37,7 @@ namespace GeometryTD.Entity
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ConstrainToZRotation();
|
||||||
_shooterBullet.OnShow(bulletData);
|
_shooterBullet.OnShow(bulletData);
|
||||||
if (_shooterBullet.TryConsumeDespawnRequest())
|
if (_shooterBullet.TryConsumeDespawnRequest())
|
||||||
{
|
{
|
||||||
|
|
@ -64,5 +66,11 @@ namespace GeometryTD.Entity
|
||||||
_shooterBullet?.OnReset();
|
_shooterBullet?.OnReset();
|
||||||
base.OnHide(isShutdown, userData);
|
base.OnHide(isShutdown, userData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ConstrainToZRotation()
|
||||||
|
{
|
||||||
|
Vector3 localEulerAngles = CachedTransform.localEulerAngles;
|
||||||
|
CachedTransform.localRotation = Quaternion.Euler(0f, 0f, localEulerAngles.z);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,11 @@ namespace GeometryTD.Entity
|
||||||
{
|
{
|
||||||
private DefenseTowerController _towerController;
|
private DefenseTowerController _towerController;
|
||||||
|
|
||||||
|
public void SetAttackRangeVisible(bool visible)
|
||||||
|
{
|
||||||
|
_towerController?.SetAttackRangeVisible(visible);
|
||||||
|
}
|
||||||
|
|
||||||
protected override void OnInit(object userData)
|
protected override void OnInit(object userData)
|
||||||
{
|
{
|
||||||
base.OnInit(userData);
|
base.OnInit(userData);
|
||||||
|
|
@ -48,6 +53,7 @@ namespace GeometryTD.Entity
|
||||||
|
|
||||||
protected override void OnHide(bool isShutdown, object userData)
|
protected override void OnHide(bool isShutdown, object userData)
|
||||||
{
|
{
|
||||||
|
_towerController?.SetAttackRangeVisible(false);
|
||||||
_towerController?.OnReset();
|
_towerController?.OnReset();
|
||||||
base.OnHide(isShutdown, userData);
|
base.OnHide(isShutdown, userData);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||||
using GeometryTD.Definition;
|
using GeometryTD.Definition;
|
||||||
using GeometryTD.Entity.EntityData;
|
using GeometryTD.Entity.EntityData;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
using UnityGameFramework.Runtime;
|
||||||
|
|
||||||
namespace GeometryTD.Entity
|
namespace GeometryTD.Entity
|
||||||
{
|
{
|
||||||
|
|
@ -116,11 +117,14 @@ namespace GeometryTD.Entity
|
||||||
_currentHealth = Mathf.Max(0, _currentHealth - damage);
|
_currentHealth = Mathf.Max(0, _currentHealth - damage);
|
||||||
if (_maxHealth > 0)
|
if (_maxHealth > 0)
|
||||||
{
|
{
|
||||||
GameEntry.HPBar?.ShowHPBar(this, (float)previousHealth / _maxHealth, (float)_currentHealth / _maxHealth);
|
GameEntry.HPBar?.ShowHPBar(this, (float)previousHealth / _maxHealth,
|
||||||
|
(float)_currentHealth / _maxHealth);
|
||||||
|
Log.Info($"ShowBar: {_currentHealth}/{_maxHealth}");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_currentHealth <= 0)
|
if (_currentHealth <= 0)
|
||||||
{
|
{
|
||||||
|
Log.Info("Enemy Dead");
|
||||||
_killedEnemyEntityIds.Add(Id);
|
_killedEnemyEntityIds.Add(Id);
|
||||||
RequestDespawn();
|
RequestDespawn();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,6 @@ namespace GeometryTD.Entity
|
||||||
Name = Utility.Text.Format("[Entity {0}]", Id);
|
Name = Utility.Text.Format("[Entity {0}]", Id);
|
||||||
CachedTransform.localPosition = _entityData.Position;
|
CachedTransform.localPosition = _entityData.Position;
|
||||||
CachedTransform.localRotation = _entityData.Rotation;
|
CachedTransform.localRotation = _entityData.Rotation;
|
||||||
CachedTransform.localScale = Vector3.one;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if UNITY_2017_3_OR_NEWER
|
#if UNITY_2017_3_OR_NEWER
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@ namespace GeometryTD.Entity
|
||||||
private bool _hasSelectedFoundationCell;
|
private bool _hasSelectedFoundationCell;
|
||||||
private Vector3Int _selectedFoundationCell;
|
private Vector3Int _selectedFoundationCell;
|
||||||
private int _selectedTowerEntityId;
|
private int _selectedTowerEntityId;
|
||||||
|
private int _attackRangeVisibleTowerEntityId;
|
||||||
|
|
||||||
public IReadOnlyList<Vector3Int> PathCells => _pathCells;
|
public IReadOnlyList<Vector3Int> PathCells => _pathCells;
|
||||||
public IReadOnlyList<Vector3Int> FoundationCells => _foundationCells;
|
public IReadOnlyList<Vector3Int> FoundationCells => _foundationCells;
|
||||||
|
|
@ -486,6 +487,8 @@ namespace GeometryTD.Entity
|
||||||
ClearSelectedObject();
|
ClearSelectedObject();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UpdateTowerAttackRangeDisplay(_selectedTowerEntityId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool TryBuildTower(int buildIndex)
|
private bool TryBuildTower(int buildIndex)
|
||||||
|
|
@ -517,6 +520,7 @@ namespace GeometryTD.Entity
|
||||||
_foundationCellByTowerEntityId[towerEntityId] = _selectedFoundationCell;
|
_foundationCellByTowerEntityId[towerEntityId] = _selectedFoundationCell;
|
||||||
_towerStatsByEntityId[towerEntityId] = CloneTowerStats(towerStats);
|
_towerStatsByEntityId[towerEntityId] = CloneTowerStats(towerStats);
|
||||||
_selectedTowerEntityId = towerEntityId;
|
_selectedTowerEntityId = towerEntityId;
|
||||||
|
UpdateTowerAttackRangeDisplay(_selectedTowerEntityId);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -552,6 +556,11 @@ namespace GeometryTD.Entity
|
||||||
_foundationCellByTowerEntityId[fallbackTowerEntityId] = foundationCell;
|
_foundationCellByTowerEntityId[fallbackTowerEntityId] = foundationCell;
|
||||||
_towerStatsByEntityId[fallbackTowerEntityId] = CloneTowerStats(oldStats);
|
_towerStatsByEntityId[fallbackTowerEntityId] = CloneTowerStats(oldStats);
|
||||||
_selectedTowerEntityId = fallbackTowerEntityId;
|
_selectedTowerEntityId = fallbackTowerEntityId;
|
||||||
|
UpdateTowerAttackRangeDisplay(_selectedTowerEntityId);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UpdateTowerAttackRangeDisplay(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
GameEntry.CombatNode?.AddCoin(upgradeCost);
|
GameEntry.CombatNode?.AddCoin(upgradeCost);
|
||||||
|
|
@ -564,6 +573,7 @@ namespace GeometryTD.Entity
|
||||||
_hasSelectedFoundationCell = true;
|
_hasSelectedFoundationCell = true;
|
||||||
_selectedFoundationCell = foundationCell;
|
_selectedFoundationCell = foundationCell;
|
||||||
_selectedTowerEntityId = newTowerEntityId;
|
_selectedTowerEntityId = newTowerEntityId;
|
||||||
|
UpdateTowerAttackRangeDisplay(_selectedTowerEntityId);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -664,11 +674,54 @@ namespace GeometryTD.Entity
|
||||||
|
|
||||||
private void ClearSelectedObject()
|
private void ClearSelectedObject()
|
||||||
{
|
{
|
||||||
|
UpdateTowerAttackRangeDisplay(0);
|
||||||
_hasSelectedFoundationCell = false;
|
_hasSelectedFoundationCell = false;
|
||||||
_selectedFoundationCell = default;
|
_selectedFoundationCell = default;
|
||||||
_selectedTowerEntityId = 0;
|
_selectedTowerEntityId = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void UpdateTowerAttackRangeDisplay(int towerEntityId)
|
||||||
|
{
|
||||||
|
if (_attackRangeVisibleTowerEntityId != 0 && _attackRangeVisibleTowerEntityId != towerEntityId)
|
||||||
|
{
|
||||||
|
SetTowerAttackRangeVisible(_attackRangeVisibleTowerEntityId, false);
|
||||||
|
_attackRangeVisibleTowerEntityId = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (towerEntityId == 0)
|
||||||
|
{
|
||||||
|
if (_attackRangeVisibleTowerEntityId != 0)
|
||||||
|
{
|
||||||
|
SetTowerAttackRangeVisible(_attackRangeVisibleTowerEntityId, false);
|
||||||
|
_attackRangeVisibleTowerEntityId = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SetTowerAttackRangeVisible(towerEntityId, true))
|
||||||
|
{
|
||||||
|
_attackRangeVisibleTowerEntityId = towerEntityId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool SetTowerAttackRangeVisible(int towerEntityId, bool visible)
|
||||||
|
{
|
||||||
|
if (towerEntityId == 0 || GameEntry.Entity == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
EntityBase gameEntity = GameEntry.Entity.GetGameEntity(towerEntityId);
|
||||||
|
if (gameEntity is not DefenseTowerEntity towerEntity)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
towerEntity.SetAttackRangeVisible(visible);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private int GetBuildTowerCost(int buildIndex)
|
private int GetBuildTowerCost(int buildIndex)
|
||||||
{
|
{
|
||||||
if (_buildTowerCosts == null || buildIndex < 0 || buildIndex >= _buildTowerCosts.Length)
|
if (_buildTowerCosts == null || buildIndex < 0 || buildIndex >= _buildTowerCosts.Length)
|
||||||
|
|
@ -707,7 +760,7 @@ namespace GeometryTD.Entity
|
||||||
case 0:
|
case 0:
|
||||||
return new DefenseTowerStatsData
|
return new DefenseTowerStatsData
|
||||||
{
|
{
|
||||||
AttackDamage = 10,
|
AttackDamage = 100,
|
||||||
DamageRandomRate = 0f,
|
DamageRandomRate = 0f,
|
||||||
RotateSpeed = 200f,
|
RotateSpeed = 200f,
|
||||||
AttackRange = 4.5f,
|
AttackRange = 4.5f,
|
||||||
|
|
@ -755,7 +808,7 @@ namespace GeometryTD.Entity
|
||||||
default:
|
default:
|
||||||
return new DefenseTowerStatsData
|
return new DefenseTowerStatsData
|
||||||
{
|
{
|
||||||
AttackDamage = 10,
|
AttackDamage = 100,
|
||||||
DamageRandomRate = 0f,
|
DamageRandomRate = 0f,
|
||||||
RotateSpeed = 180f,
|
RotateSpeed = 180f,
|
||||||
AttackRange = 5f,
|
AttackRange = 5f,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue