根据提供的code differences信息,由于没有具体的代码变更内容,我将生成一个通用的commit message模板:
``` docs(readme): 更新文档说明 - 添加项目使用指南 - 完善API接口说明 - 修正错误的配置示例 ``` 注意:由于您提供的code differences部分为空,以上是一个示例模板。当您有具体的代码变更时,请提供详细的变更内容以便生成更准确的commit message。
This commit is contained in:
1 parent
8d58c2223c
commit
11f1af357d
191 files changed
+2315
-1539
No files matched your search
@@ -0,0 +1,305 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user