Files
daluandou/Assets/Script/AttackTargetController.cs
T
huadexuan 53d95802e5 ```
feat(gameplay): 更新单位属性和材质配置

- 调整多个单位的基础生命值和攻击力数值平衡
- 修改CoreBattleGround材质的纹理缩放比例
- 更新地砖材质颜色为白色
- 优化纹理资源设置,调整最大纹理尺寸并添加PlayableAds平台支持
- 修复弓箭手技能目标查找逻辑,改用统一的敌人检测方法
- 为水晶矿添加中立阵营设置和光球视觉效果同步
- 调整预制体中的全局缩放参数
- 为狼人和石巨人添加NavMeshAgent组件支持
- 优化水晶矿血条显示位置计算方式
```
2026-09-20 21:52:31 +08:00

306 lines
12 KiB
C#

using UnityEngine;
using UnityEngine.EventSystems;
/// <summary>
/// 建筑攻击标志控制器(双阵营):每个阵营各自一个攻击标志,默认指向敌方主城堡。
/// 本地玩家点击建筑切换自己阵营的标志;两方标志同时显示,指向同一建筑时并排错开。
/// 单位寻敌:周围有敌人打敌人,否则直奔己方标志建筑;到达后是我方阵营(增援完成)→ 己方标志切回主城堡。
/// </summary>
public class AttackTargetController : MonoBehaviour
{
public static AttackTargetController Instance { get; private set; }
[Header("蓝方(teamId=0)攻击目标")]
public GameObject blueTarget;
[Header("红方(teamId=1)攻击目标")]
public GameObject redTarget;
[Header("标志视觉(头顶箭头)")]
[Tooltip("标志 Prefab:拖入自定义图标替换默认箭头;留空时代码生成默认箭头")]
[SerializeField] private GameObject markerPrefab;
[Tooltip("标志相对建筑包围盒顶部的额外高度")]
[SerializeField] private float markerHeightOffset = 0.8f;
[Tooltip("双方标志指向同一建筑时的并排错开距离")]
[SerializeField] private float markerSideOffset = 1.1f;
[Tooltip("建筑子级下该名字的空物体 = 标志锚点(可选,编辑器可调位置)")]
[SerializeField] private string anchorName = "标志锚点";
private Transform blueMarker;
private Transform redMarker;
private Renderer[] blueMarkerRenderers;
private Renderer[] redMarkerRenderers;
private GameObject cachedBlueEnemyCastle; // teamId 0 的敌方主城堡(红色城堡)
private GameObject cachedRedEnemyCastle; // teamId 1 的敌方主城堡(蓝色城堡)
private float nextCastleRefreshTime;
private const float CastleRefreshInterval = 0.5f;
private Camera mainCamera;
private static readonly int TintColorId = Shader.PropertyToID("_TintColor");
private static readonly int ColorId = Shader.PropertyToID("_Color");
private static readonly Color BlueColor = new Color(0.2f, 0.55f, 1f, 0.95f);
private static readonly Color RedColor = new Color(1f, 0.2f, 0.15f, 0.95f);
private MaterialPropertyBlock propertyBlock;
private void Awake()
{
if (Instance != null && Instance != this) { Destroy(gameObject); return; }
Instance = this;
}
private void Start()
{
mainCamera = Camera.main;
CreateMarkers();
blueTarget = GetEnemyCastleOfTeam(0);
redTarget = GetEnemyCastleOfTeam(1);
Debug.Log($"攻击标志初始: 蓝方→{blueTarget?.name ?? "未找到"} 红方→{redTarget?.name ?? "未找到"}");
}
private void Update()
{
// 主城堡引用失效时定期重查(场景加载时序容错)
if ((cachedBlueEnemyCastle == null || cachedRedEnemyCastle == null) && Time.time >= nextCastleRefreshTime)
{
nextCastleRefreshTime = Time.time + CastleRefreshInterval;
cachedBlueEnemyCastle = FindBuilding("红色城堡");
cachedRedEnemyCastle = FindBuilding("蓝色城堡");
if (blueTarget == null && cachedBlueEnemyCastle != null) blueTarget = cachedBlueEnemyCastle;
if (redTarget == null && cachedRedEnemyCastle != null) redTarget = cachedRedEnemyCastle;
}
HandleClick();
}
// 点击建筑:只切换本地玩家阵营的攻击标志
private void HandleClick()
{
if (mainCamera == null) mainCamera = Camera.main;
if (mainCamera == null) return;
bool clicked = Input.GetMouseButtonDown(0)
|| (Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Began);
if (!clicked) return;
// 点击落在 UI 上不处理
if (EventSystem.current != null)
{
if (Input.touchCount > 0)
{
if (EventSystem.current.IsPointerOverGameObject(Input.GetTouch(0).fingerId)) return;
}
else if (EventSystem.current.IsPointerOverGameObject()) return;
}
Vector2 screenPos = Input.touchCount > 0 ? (Vector2)Input.GetTouch(0).position : Input.mousePosition;
Ray ray = mainCamera.ScreenPointToRay(screenPos);
if (Physics.Raycast(ray, out RaycastHit hit, 500f))
{
Transform building = FindBuildingInParents(hit.collider.transform);
if (building == null) return;
int localTeam = GameManager.Instance != null ? GameManager.Instance.LocalPlayerTeamId : 0;
// 点自己方主城堡无效
if (building.name == GetTeamCastleName(localTeam)) return;
if (localTeam == 0)
{
blueTarget = building.gameObject;
}
else
{
redTarget = building.gameObject;
}
Debug.Log($"{(localTeam == 0 ? "蓝方" : "红方")}攻击标志 → {building.name}");
}
}
/// <summary>获取阵营当前的攻击目标(无效时兜底敌方主城堡)。单位出生时调用一次做使命快照。</summary>
public GameObject GetTeamTarget(int teamId)
{
GameObject t = teamId == 0 ? blueTarget : redTarget;
if (t != null && t.activeInHierarchy) return t;
return GetEnemyCastleOfTeam(teamId);
}
/// <summary>获取阵营的敌方主城堡(teamId 0→红色城堡,1→蓝色城堡),带缓存。</summary>
public GameObject GetEnemyCastleOfTeam(int teamId)
{
if (teamId == 0)
{
if (cachedBlueEnemyCastle == null || !cachedBlueEnemyCastle.activeInHierarchy)
{
cachedBlueEnemyCastle = FindBuilding("红色城堡");
}
return cachedBlueEnemyCastle;
}
if (cachedRedEnemyCastle == null || !cachedRedEnemyCastle.activeInHierarchy)
{
cachedRedEnemyCastle = FindBuilding("蓝色城堡");
}
return cachedRedEnemyCastle;
}
private static GameObject FindBuilding(string name)
{
var found = GameObject.Find(name);
return found != null && found.CompareTag("Building") ? found : null;
}
private static string GetTeamCastleName(int teamId)
{
return teamId == 0 ? "蓝色城堡" : "红色城堡";
}
private static Transform FindBuildingInParents(Transform t)
{
while (t != null)
{
if (t.CompareTag("Building")) return t;
t = t.parent;
}
return null;
}
// 双标志:位置/朝向/颜色(指向同一建筑时并排)
private void LateUpdate()
{
if (mainCamera == null) mainCamera = Camera.main;
bool sameBuilding = blueTarget != null && blueTarget == redTarget;
float blueSide = sameBuilding ? markerSideOffset : 0f;
float redSide = sameBuilding ? -markerSideOffset : 0f;
UpdateMarker(blueMarker, blueMarkerRenderers, blueTarget, BlueColor, blueSide);
UpdateMarker(redMarker, redMarkerRenderers, redTarget, RedColor, redSide);
}
private void UpdateMarker(Transform marker, Renderer[] renderers, GameObject target, Color color, float sideOffset)
{
if (marker == null) return;
if (target == null)
{
marker.gameObject.SetActive(false);
return;
}
marker.gameObject.SetActive(true);
// 位置:锚点优先(可选),否则建筑包围盒顶部 + 高度偏移
Vector3 pos;
Transform anchor = target.transform.Find(anchorName);
if (anchor != null)
{
pos = anchor.position;
}
else
{
Bounds bounds = CalculateTargetBounds(target);
pos = new Vector3(target.transform.position.x, bounds.max.y + markerHeightOffset, target.transform.position.z);
}
// 指向同一建筑时沿相机右方向并排错开
if (sideOffset != 0f && mainCamera != null)
{
pos += mainCamera.transform.right * sideOffset;
}
marker.position = pos;
// billboard:箭头尖朝下指着建筑
if (mainCamera != null)
{
Vector3 toCamera = mainCamera.transform.position - marker.position;
marker.rotation = Quaternion.LookRotation(toCamera) * Quaternion.Euler(90f, 0f, 0f);
}
// 颜色按标志所属阵营
if (renderers == null) return;
propertyBlock ??= new MaterialPropertyBlock();
foreach (Renderer rend in renderers)
{
if (rend == null) continue;
rend.GetPropertyBlock(propertyBlock);
propertyBlock.SetColor(TintColorId, color);
propertyBlock.SetColor(ColorId, color);
rend.SetPropertyBlock(propertyBlock);
}
}
private static Bounds CalculateTargetBounds(GameObject target)
{
var renderers = target.GetComponentsInChildren<Renderer>();
if (renderers.Length == 0) return new Bounds(target.transform.position, Vector3.one);
Bounds b = renderers[0].bounds;
for (int i = 1; i < renderers.Length; i++) b.Encapsulate(renderers[i].bounds);
return b;
}
private void CreateMarkers()
{
if (blueMarker != null && redMarker != null) return;
blueMarker = CreateMarker("蓝方攻击标志", BlueColor);
blueMarkerRenderers = blueMarker.GetComponentsInChildren<Renderer>();
redMarker = CreateMarker("红方攻击标志", RedColor);
redMarkerRenderers = redMarker.GetComponentsInChildren<Renderer>();
}
private Transform CreateMarker(string name, Color color)
{
GameObject markerGo;
// 优先用可替换的标志 Prefab(可在 Inspector 拖入自定义图标)
if (markerPrefab != null)
{
markerGo = Instantiate(markerPrefab, transform);
markerGo.name = name;
}
else
{
// 兜底:代码生成默认倒锥箭头(尖朝下指着建筑)
markerGo = new GameObject(name, typeof(MeshFilter), typeof(MeshRenderer));
markerGo.transform.SetParent(transform, false);
markerGo.GetComponent<MeshFilter>().sharedMesh = CreateArrowMesh();
var mat = new Material(Shader.Find("Legacy Shaders/Particles/Additive"));
mat.SetColor(TintColorId, color);
markerGo.GetComponent<Renderer>().sharedMaterial = mat;
}
return markerGo.transform;
}
// 4面锥体:尖朝下,高2,底半径0.7
private static Mesh CreateArrowMesh()
{
var mesh = new Mesh();
mesh.name = "AttackMarkerArrow";
Vector3 tip = new Vector3(0f, -1.2f, 0f);
Vector3[] baseQuad = {
new Vector3(-0.7f, 0.8f, -0.7f), new Vector3(0.7f, 0.8f, -0.7f),
new Vector3(0.7f, 0.8f, 0.7f), new Vector3(-0.7f, 0.8f, 0.7f)
};
var vertices = new System.Collections.Generic.List<Vector3>();
var normals = new System.Collections.Generic.List<Vector3>();
void AddFace(Vector3 a, Vector3 b, Vector3 c, Vector3 n)
{
vertices.AddRange(new[] { a, b, c });
normals.AddRange(new[] { n, n, n });
}
AddFace(baseQuad[0], baseQuad[1], tip, new Vector3(0f, 0f, -1f)); // 后
AddFace(baseQuad[1], baseQuad[2], tip, new Vector3(1f, 0f, 0f)); // 右
AddFace(baseQuad[2], baseQuad[3], tip, new Vector3(0f, 0f, 1f)); // 前
AddFace(baseQuad[3], baseQuad[0], tip, new Vector3(-1f, 0f, 0f)); // 左
mesh.SetVertices(vertices);
mesh.SetNormals(normals);
mesh.triangles = new int[] { 0,1,2, 3,4,5, 6,7,8, 9,10,11 };
mesh.RecalculateBounds();
return mesh;
}
}