根据提供的code differences信息,由于没有具体的代码变更内容,我将生成一个通用的commit message模板:
``` docs(readme): 更新文档说明 - 添加项目使用指南 - 完善API接口说明 - 修正错误的配置示例 ``` 注意:由于您提供的code differences部分为空,以上是一个示例模板。当您有具体的代码变更时,请提供详细的变更内容以便生成更准确的commit message。
This commit is contained in:
1 parent
11f1af357d
commit
7d9d0f52d1
83 files changed
+3510
-1493
No files matched your search
@@ -0,0 +1,291 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
/// <summary>
|
||||
/// 英雄卡片拖动组件(布阵阶段使用)。
|
||||
/// 拖动时在场景里生成一个半透明的英雄"幽灵"跟随指针,并吸附到最近的部署格位;
|
||||
/// 松手后把该卡对应的英雄登记到格位上,卡片进入"已使用"状态。
|
||||
/// </summary>
|
||||
public class HeroCardDrag : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
|
||||
{
|
||||
[Header("配置(由 DefenseGameFlowController 注入)")]
|
||||
public int cardIndex; // 卡片序号,0 起
|
||||
public GameObject heroPrefab; // 对应英雄 Prefab
|
||||
public CharacterData characterData; // 角色数据
|
||||
|
||||
[Header("幽灵外观")]
|
||||
[Range(0.1f, 1f)] public float ghostAlpha = 0.55f;
|
||||
|
||||
private Canvas canvas;
|
||||
private CanvasGroup canvasGroup;
|
||||
private GameObject ghost;
|
||||
private Material ghostHiddenMaterial;
|
||||
|
||||
private bool dragging;
|
||||
private int currentCell = -1;
|
||||
|
||||
private const float PlacedCardAlpha = 0.35f;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
canvas = GetComponentInParent<Canvas>();
|
||||
canvasGroup = GetComponent<CanvasGroup>();
|
||||
if (canvasGroup == null)
|
||||
{
|
||||
canvasGroup = gameObject.AddComponent<CanvasGroup>();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>由流程控制器注入卡片配置。</summary>
|
||||
public void Setup(int index, GameObject prefab, CharacterData data)
|
||||
{
|
||||
cardIndex = index;
|
||||
heroPrefab = prefab;
|
||||
characterData = data;
|
||||
SetPlaced(false);
|
||||
}
|
||||
|
||||
/// <summary>标记该卡是否已经放下去了(已放置的卡片变暗且不能再拖)。</summary>
|
||||
public void SetPlaced(bool placed)
|
||||
{
|
||||
if (canvasGroup != null)
|
||||
{
|
||||
canvasGroup.alpha = placed ? PlacedCardAlpha : 1f;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnBeginDrag(PointerEventData eventData)
|
||||
{
|
||||
var flow = DefenseGameFlowController.Instance;
|
||||
if (flow == null || !flow.IsDeployPhase) return;
|
||||
if (flow.IsCardPlaced(cardIndex)) return; // 已经放置过了
|
||||
if (heroPrefab == null) return;
|
||||
|
||||
dragging = true;
|
||||
currentCell = -1;
|
||||
|
||||
if (canvasGroup != null)
|
||||
{
|
||||
canvasGroup.alpha = 0.5f;
|
||||
canvasGroup.blocksRaycasts = false;
|
||||
}
|
||||
|
||||
CreateGhost();
|
||||
FollowPointer(eventData.position);
|
||||
}
|
||||
|
||||
public void OnDrag(PointerEventData eventData)
|
||||
{
|
||||
if (!dragging) return;
|
||||
FollowPointer(eventData.position);
|
||||
}
|
||||
|
||||
public void OnEndDrag(PointerEventData eventData)
|
||||
{
|
||||
if (!dragging) return;
|
||||
dragging = false;
|
||||
|
||||
if (canvasGroup != null)
|
||||
{
|
||||
canvasGroup.alpha = 1f;
|
||||
canvasGroup.blocksRaycasts = true;
|
||||
}
|
||||
|
||||
if (DeployHighlight.Instance != null) DeployHighlight.Instance.Hide();
|
||||
|
||||
int cell = currentCell;
|
||||
currentCell = -1;
|
||||
|
||||
DestroyGhost();
|
||||
|
||||
if (cell >= 0)
|
||||
{
|
||||
var flow = DefenseGameFlowController.Instance;
|
||||
if (flow != null && flow.PlaceHeroFromCard(cardIndex, cell))
|
||||
{
|
||||
SetPlaced(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── 幽灵 ────────────────────────────────────────────────
|
||||
|
||||
private void CreateGhost()
|
||||
{
|
||||
ghost = Instantiate(heroPrefab);
|
||||
ghost.name = "Ghost_" + heroPrefab.name;
|
||||
|
||||
// 只保留外观:关掉所有逻辑组件与碰撞体
|
||||
foreach (var behaviour in ghost.GetComponentsInChildren<Behaviour>(true))
|
||||
{
|
||||
if (behaviour is Animator) continue; // 保留 Animator 让它做待机动作
|
||||
behaviour.enabled = false;
|
||||
}
|
||||
foreach (var collider in ghost.GetComponentsInChildren<Collider>(true))
|
||||
{
|
||||
collider.enabled = false;
|
||||
}
|
||||
foreach (var rb in ghost.GetComponentsInChildren<Rigidbody>(true))
|
||||
{
|
||||
rb.isKinematic = true;
|
||||
rb.detectCollisions = false;
|
||||
}
|
||||
|
||||
// Animator 不能带动根位移,否则幽灵会自己漂走
|
||||
foreach (var animator in ghost.GetComponentsInChildren<Animator>(true))
|
||||
{
|
||||
animator.applyRootMotion = false;
|
||||
}
|
||||
|
||||
// 血条 UI 是 WorldHealthBar.Awake 生成的,幽灵不需要
|
||||
var healthBar = ghost.transform.Find("WorldHealthBar");
|
||||
if (healthBar != null)
|
||||
{
|
||||
Destroy(healthBar.gameObject);
|
||||
}
|
||||
|
||||
BuildGhostMaterials();
|
||||
}
|
||||
|
||||
private void BuildGhostMaterials()
|
||||
{
|
||||
if (ghostHiddenMaterial == null)
|
||||
{
|
||||
var hiddenShader = Shader.Find("Sprites/Default") ?? Shader.Find("Unlit/Color");
|
||||
ghostHiddenMaterial = new Material(hiddenShader);
|
||||
ghostHiddenMaterial.name = "GhostHidden";
|
||||
ghostHiddenMaterial.color = new Color(1f, 1f, 1f, 0f);
|
||||
}
|
||||
|
||||
var bodyShader = Shader.Find("Standard");
|
||||
|
||||
foreach (var renderer in ghost.GetComponentsInChildren<Renderer>(true))
|
||||
{
|
||||
var source = renderer.sharedMaterials;
|
||||
var result = new Material[source.Length];
|
||||
|
||||
for (int i = 0; i < source.Length; i++)
|
||||
{
|
||||
// 第 0 个材质是本体,做成半透明;其余(描边等附加材质)隐藏掉
|
||||
if (i == 0 && source[i] != null && bodyShader != null)
|
||||
{
|
||||
result[i] = MakeTransparent(source[i], ghostAlpha, bodyShader);
|
||||
}
|
||||
else
|
||||
{
|
||||
result[i] = ghostHiddenMaterial;
|
||||
}
|
||||
}
|
||||
|
||||
renderer.sharedMaterials = result;
|
||||
renderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
|
||||
}
|
||||
}
|
||||
|
||||
private static Material MakeTransparent(Material source, float alpha, Shader standard)
|
||||
{
|
||||
var mat = new Material(source);
|
||||
mat.name = source.name + "_Ghost";
|
||||
|
||||
// 统一用 Standard 的透明混合模式,保证任何贴图都能变成半透明
|
||||
if (mat.shader != standard)
|
||||
{
|
||||
var mainTex = mat.HasProperty("_MainTex") ? mat.GetTexture("_MainTex") : null;
|
||||
var color = mat.HasProperty("_Color") ? mat.GetColor("_Color") : Color.white;
|
||||
mat.shader = standard;
|
||||
if (mainTex != null) mat.SetTexture("_MainTex", mainTex);
|
||||
mat.SetColor("_Color", color);
|
||||
}
|
||||
|
||||
if (mat.HasProperty("_Mode"))
|
||||
{
|
||||
mat.SetFloat("_Mode", 3f); // Transparent
|
||||
mat.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
|
||||
mat.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
|
||||
mat.SetInt("_ZWrite", 0);
|
||||
mat.DisableKeyword("_ALPHATEST_ON");
|
||||
mat.EnableKeyword("_ALPHABLEND_ON");
|
||||
mat.DisableKeyword("_ALPHAPREMULTIPLY_ON");
|
||||
mat.renderQueue = 3000;
|
||||
}
|
||||
|
||||
if (mat.HasProperty("_Color"))
|
||||
{
|
||||
var c = mat.GetColor("_Color");
|
||||
c.a = alpha;
|
||||
mat.SetColor("_Color", c);
|
||||
}
|
||||
|
||||
return mat;
|
||||
}
|
||||
|
||||
private void FollowPointer(Vector2 screenPosition)
|
||||
{
|
||||
if (ghost == null) return;
|
||||
|
||||
var cam = Camera.main;
|
||||
if (cam == null) return;
|
||||
|
||||
var ray = cam.ScreenPointToRay(screenPosition);
|
||||
var ground = new Plane(Vector3.up, new Vector3(0f, DeployGrid.UnitY, 0f));
|
||||
|
||||
if (!ground.Raycast(ray, out float distance))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Vector3 world = ray.GetPoint(distance);
|
||||
int cell = DeployGrid.SnapToCellClamped(world);
|
||||
|
||||
if (cell == currentCell) return;
|
||||
currentCell = cell;
|
||||
|
||||
var flow = DefenseGameFlowController.Instance;
|
||||
bool blocked = cell < 0 || (flow != null && flow.IsCellOccupied(cell));
|
||||
|
||||
if (cell >= 0)
|
||||
{
|
||||
ghost.transform.position = DeployGrid.CellPosition(cell);
|
||||
if (DeployHighlight.Instance != null) DeployHighlight.Instance.Show(cell, blocked);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 没吸附到格位:幽灵跟着指针但压暗提示不可放置
|
||||
ghost.transform.position = new Vector3(world.x, DeployGrid.UnitY, world.z);
|
||||
if (DeployHighlight.Instance != null) DeployHighlight.Instance.Hide();
|
||||
}
|
||||
}
|
||||
|
||||
private void DestroyGhost()
|
||||
{
|
||||
if (ghost == null) return;
|
||||
|
||||
foreach (var renderer in ghost.GetComponentsInChildren<Renderer>(true))
|
||||
{
|
||||
if (renderer.sharedMaterials == null) continue;
|
||||
foreach (var mat in renderer.sharedMaterials)
|
||||
{
|
||||
if (mat != null && mat != ghostHiddenMaterial) Destroy(mat);
|
||||
}
|
||||
}
|
||||
|
||||
Destroy(ghost);
|
||||
ghost = null;
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
if (dragging)
|
||||
{
|
||||
dragging = false;
|
||||
if (canvasGroup != null)
|
||||
{
|
||||
canvasGroup.alpha = 1f;
|
||||
canvasGroup.blocksRaycasts = true;
|
||||
}
|
||||
if (DeployHighlight.Instance != null) DeployHighlight.Instance.Hide();
|
||||
}
|
||||
DestroyGhost();
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user