271 lines
9.4 KiB
C#
271 lines
9.4 KiB
C#
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Globalization;
|
|
using DG.Tweening;
|
|
using UnityEngine;
|
|
using UnityEngine.EventSystems;
|
|
using UnityEngine.UI;
|
|
|
|
public class CharacterCardView : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler,
|
|
IPointerEnterHandler, IPointerExitHandler, IPointerMoveHandler
|
|
{
|
|
public int characterID;
|
|
public string currentBoundaryLevel;
|
|
public GameObject charactCard;
|
|
public Image characterImage;
|
|
public Image levelIcon;
|
|
public Image occupationIcon;
|
|
public bool isSelected=false;
|
|
|
|
// drag
|
|
private GameObject dragGhost;
|
|
private Canvas rootCanvas;
|
|
private CanvasGroup canvasGroup;
|
|
private LayoutElement layoutElement;
|
|
|
|
[Header("Hover Anim")]
|
|
[SerializeField] bool enableHoverAnim = true;
|
|
[SerializeField] float hoverScale = 1.06f;
|
|
[SerializeField] float hoverTweenTime = 0.12f;
|
|
[SerializeField] float hoverExpand = 0.18f;
|
|
[SerializeField] float hoverCompress = 0.08f;
|
|
[SerializeField] float gyroMaxAngle = 6f;
|
|
[SerializeField] float gyroFollow = 8f;
|
|
|
|
private bool isHovering;
|
|
private bool isDragging;
|
|
private Camera lastEventCamera;
|
|
|
|
class LayoutInfo
|
|
{
|
|
public CharacterCardView card;
|
|
public LayoutElement layout;
|
|
public float baseSize;
|
|
}
|
|
|
|
static readonly Dictionary<Transform, List<LayoutInfo>> layoutCache = new();
|
|
static readonly Dictionary<Transform, bool> layoutAxisIsHorizontal = new();
|
|
|
|
void Start()
|
|
{
|
|
charactCard = gameObject;
|
|
rootCanvas = GetComponentInParent<Canvas>();
|
|
canvasGroup = GetComponent<CanvasGroup>() ?? gameObject.AddComponent<CanvasGroup>();
|
|
layoutElement = GetComponent<LayoutElement>() ?? gameObject.AddComponent<LayoutElement>();
|
|
if (characterImage != null) characterImage.raycastTarget = true;
|
|
}
|
|
/// <summary>
|
|
/// Documentation text normalized.
|
|
public void beSelected()
|
|
{
|
|
if (!isSelected)
|
|
{
|
|
isSelected = true;
|
|
TeamManager.Instance.currentSelectedCharacterCard = new CharacterView(characterID, currentBoundaryLevel);
|
|
characterImage.color = new Color(0.5f, 0.5f, 0.5f,1);
|
|
}
|
|
else beUnSelected();
|
|
}
|
|
|
|
public void beUnSelected()
|
|
{
|
|
// Documentation text normalized.
|
|
isSelected = false;
|
|
TeamManager.Instance.removeTeamCharacter(characterID);
|
|
characterImage.color = new Color(1f, 1f, 1f,1);
|
|
}
|
|
|
|
// Drag handlers: create ghost based on characterImage.sprite
|
|
public void OnBeginDrag(PointerEventData eventData)
|
|
{
|
|
Debug.Log($"CharacterCardView OnBeginDrag: {gameObject.name}");
|
|
if (characterImage == null || characterImage.sprite == null) return;
|
|
if (rootCanvas == null) rootCanvas = GetComponentInParent<Canvas>();
|
|
if (rootCanvas == null) return;
|
|
|
|
// prevent the original from blocking raycasts so drop targets can receive events
|
|
if (canvasGroup == null) canvasGroup = GetComponent<CanvasGroup>() ?? gameObject.AddComponent<CanvasGroup>();
|
|
canvasGroup.blocksRaycasts = false;
|
|
canvasGroup.alpha = 0f;
|
|
isDragging = true;
|
|
ApplyHoverLayout(false);
|
|
StopHoverEffects();
|
|
if (layoutElement == null) layoutElement = GetComponent<LayoutElement>() ?? gameObject.AddComponent<LayoutElement>();
|
|
layoutElement.ignoreLayout = true;
|
|
ForceRebuildParentLayout();
|
|
|
|
// ensure pointerDrag references this object for drop handlers
|
|
eventData.pointerDrag = gameObject;
|
|
|
|
dragGhost = new GameObject("_RoleDragGhost");
|
|
dragGhost.transform.SetParent(rootCanvas.transform, false);
|
|
var img = dragGhost.AddComponent<Image>();
|
|
img.raycastTarget = false;
|
|
img.sprite = characterImage.sprite;
|
|
img.SetNativeSize();
|
|
var cg = dragGhost.AddComponent<CanvasGroup>();
|
|
cg.blocksRaycasts = false;
|
|
cg.alpha = 0.5f; // Set transparency to half
|
|
|
|
UpdateGhostPosition(eventData);
|
|
}
|
|
|
|
public void OnDrag(PointerEventData eventData)
|
|
{
|
|
if (dragGhost == null) return;
|
|
UpdateGhostPosition(eventData);
|
|
}
|
|
|
|
public void OnEndDrag(PointerEventData eventData)
|
|
{
|
|
Debug.Log($"CharacterCardView OnEndDrag: {gameObject.name}");
|
|
if (dragGhost != null)
|
|
{
|
|
Destroy(dragGhost);
|
|
dragGhost = null;
|
|
}
|
|
// restore raycast blocking on the original
|
|
if (canvasGroup != null)
|
|
canvasGroup.blocksRaycasts = true;
|
|
if (canvasGroup != null)
|
|
canvasGroup.alpha = 1f;
|
|
if (layoutElement != null)
|
|
layoutElement.ignoreLayout = false;
|
|
isDragging = false;
|
|
ForceRebuildParentLayout();
|
|
}
|
|
|
|
private void UpdateGhostPosition(PointerEventData eventData)
|
|
{
|
|
if (rootCanvas == null) return;
|
|
RectTransformUtility.ScreenPointToLocalPointInRectangle(
|
|
rootCanvas.transform as RectTransform,
|
|
eventData.position,
|
|
eventData.pressEventCamera,
|
|
out Vector2 localPoint);
|
|
if (dragGhost != null)
|
|
dragGhost.transform.localPosition = localPoint;
|
|
}
|
|
|
|
public void OnPointerEnter(PointerEventData eventData)
|
|
{
|
|
if (!enableHoverAnim || isDragging) return;
|
|
isHovering = true;
|
|
lastEventCamera = eventData.enterEventCamera;
|
|
ApplyHoverLayout(true);
|
|
transform.DOKill();
|
|
transform.DOScale(hoverScale, hoverTweenTime).SetEase(Ease.OutCubic);
|
|
}
|
|
|
|
public void OnPointerExit(PointerEventData eventData)
|
|
{
|
|
if (!enableHoverAnim) return;
|
|
isHovering = false;
|
|
ApplyHoverLayout(false);
|
|
StopHoverEffects();
|
|
}
|
|
|
|
public void OnPointerMove(PointerEventData eventData)
|
|
{
|
|
if (!enableHoverAnim) return;
|
|
lastEventCamera = eventData.enterEventCamera;
|
|
}
|
|
|
|
void LateUpdate()
|
|
{
|
|
if (!enableHoverAnim || !isHovering || isDragging) return;
|
|
RectTransform rt = transform as RectTransform;
|
|
if (rt == null) return;
|
|
if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(rt, Input.mousePosition, lastEventCamera, out Vector2 localPoint))
|
|
{
|
|
return;
|
|
}
|
|
Vector2 size = rt.rect.size;
|
|
if (size.x <= 0f || size.y <= 0f) return;
|
|
Vector2 norm = new Vector2(localPoint.x / size.x, localPoint.y / size.y);
|
|
norm = Vector2.ClampMagnitude(norm * 2f, 1f);
|
|
float xRot = -norm.y * gyroMaxAngle;
|
|
float yRot = norm.x * gyroMaxAngle;
|
|
Quaternion target = Quaternion.Euler(xRot, yRot, 0f);
|
|
transform.localRotation = Quaternion.Slerp(transform.localRotation, target, Time.unscaledDeltaTime * gyroFollow);
|
|
}
|
|
|
|
void StopHoverEffects()
|
|
{
|
|
transform.DOKill();
|
|
transform.DOScale(1f, hoverTweenTime).SetEase(Ease.OutCubic);
|
|
transform.localRotation = Quaternion.identity;
|
|
}
|
|
|
|
void ApplyHoverLayout(bool active)
|
|
{
|
|
Transform parent = transform.parent;
|
|
if (parent == null) return;
|
|
List<LayoutInfo> group = GetOrCacheGroup(parent);
|
|
if (group == null || group.Count == 0) return;
|
|
|
|
bool horizontal = layoutAxisIsHorizontal.TryGetValue(parent, out bool h) ? h : true;
|
|
for (int i = 0; i < group.Count; i++)
|
|
{
|
|
LayoutInfo info = group[i];
|
|
if (info == null || info.layout == null) continue;
|
|
float baseSize = info.baseSize;
|
|
float target = baseSize;
|
|
if (active)
|
|
{
|
|
target = info.card == this
|
|
? baseSize * (1f + hoverExpand)
|
|
: baseSize * Mathf.Max(0.1f, 1f - hoverCompress);
|
|
}
|
|
if (horizontal)
|
|
info.layout.preferredWidth = target;
|
|
else
|
|
info.layout.preferredHeight = target;
|
|
}
|
|
ForceRebuildParentLayout();
|
|
}
|
|
|
|
List<LayoutInfo> GetOrCacheGroup(Transform parent)
|
|
{
|
|
if (layoutCache.TryGetValue(parent, out var cached) && cached != null && cached.Count > 0)
|
|
{
|
|
return cached;
|
|
}
|
|
|
|
bool horizontal = parent.GetComponent<HorizontalLayoutGroup>() != null;
|
|
bool vertical = parent.GetComponent<VerticalLayoutGroup>() != null;
|
|
layoutAxisIsHorizontal[parent] = !vertical || horizontal;
|
|
|
|
var list = new List<LayoutInfo>();
|
|
CharacterCardView[] cards = parent.GetComponentsInChildren<CharacterCardView>(true);
|
|
for (int i = 0; i < cards.Length; i++)
|
|
{
|
|
var card = cards[i];
|
|
if (card == null) continue;
|
|
var le = card.layoutElement ?? card.GetComponent<LayoutElement>() ?? card.gameObject.AddComponent<LayoutElement>();
|
|
RectTransform rt = card.transform as RectTransform;
|
|
float baseSize = 100f;
|
|
if (rt != null)
|
|
{
|
|
baseSize = horizontal ? rt.rect.width : rt.rect.height;
|
|
}
|
|
if (horizontal)
|
|
le.preferredWidth = baseSize;
|
|
else
|
|
le.preferredHeight = baseSize;
|
|
|
|
list.Add(new LayoutInfo { card = card, layout = le, baseSize = baseSize });
|
|
}
|
|
layoutCache[parent] = list;
|
|
return list;
|
|
}
|
|
|
|
void ForceRebuildParentLayout()
|
|
{
|
|
if (transform.parent == null) return;
|
|
RectTransform prt = transform.parent as RectTransform;
|
|
if (prt != null)
|
|
LayoutRebuilder.ForceRebuildLayoutImmediate(prt);
|
|
}
|
|
}
|