geometry-tower-defense/Assets/GameMain/Scripts/UI/General/View/ItemDescForm.cs

352 lines
11 KiB
C#

using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
using UnityGameFramework.Runtime;
namespace GeometryTD.UI
{
public class ItemDescForm : UGuiForm
{
[SerializeField] private RectTransform _content;
[SerializeField] private TMP_Text _itemTitle;
[SerializeField] private TMP_Text _itemTypeText;
[SerializeField] private TMP_Text _itemDescription;
[SerializeField] private TMP_Text _itemPrice;
[SerializeField] private Transform _tagAreaParent;
[SerializeField] private GameObject _tagItemPrefab;
[SerializeField] private bool _autoResizeHeight = true;
[SerializeField] private float _fixedTopHeight = 150f;
[SerializeField] private float _fixedBottomHeight = 150f;
[SerializeField] private float _fixedWidth = 300f;
[SerializeField] private float _screenEdgePadding = 0f;
[SerializeField] private float _sideGap = 16f;
[SerializeField] private float _anchorItemWidth = 0f;
[SerializeField] private Graphic _blankAreaGraphic;
[SerializeField] private bool _disableBlankAreaRaycast = true;
[SerializeField] private bool _closeOnPointerDownOutsideContent = true;
private readonly List<GameObject> _runtimeTagItems = new List<GameObject>();
private ItemDescFormContext _context;
private Vector3 _targetPos;
private bool _isOpened;
public void RefreshUI(ItemDescFormContext context)
{
if (context == null)
{
Log.Warning("ItemDescForm context is invalid.");
return;
}
_context = context;
_targetPos = ConvertWorldToAnchored(_context.TargetPos);
if (_content != null)
{
_content.anchoredPosition3D = _targetPos;
}
if (_itemTitle != null) _itemTitle.text = _context.Title ?? string.Empty;
if (_itemTypeText != null) _itemTypeText.text = _context.TypeText ?? string.Empty;
if (_itemDescription != null) _itemDescription.text = _context.Description ?? string.Empty;
if (_itemPrice != null) _itemPrice.text = $"Price: {_context.Price} Gold";
RefreshTags(_context.Tags);
ResizeToFitContent();
ApplySideAnchoredTargetPos();
}
protected override void OnOpen(object userData)
{
base.OnOpen(userData);
_isOpened = true;
if (_disableBlankAreaRaycast && _blankAreaGraphic != null)
{
_blankAreaGraphic.raycastTarget = false;
}
if (!(userData is ItemDescFormContext context))
{
Log.Error("ItemDescFormContext is invalid.");
return;
}
RefreshUI(context);
}
protected override void OnClose(bool isShutdown, object userData)
{
_isOpened = false;
ClearTags();
_context = null;
base.OnClose(isShutdown, userData);
}
public void OnBlankAreaClick()
{
CloseSelf();
}
public void OnBackgroundClick()
{
CloseSelf();
}
private void Update()
{
if (!_isOpened || !_closeOnPointerDownOutsideContent || _content == null)
{
return;
}
if (!TryGetPointerDownScreenPosition(out Vector2 screenPoint))
{
return;
}
if (IsPointerInContent(screenPoint))
{
return;
}
CloseSelf();
}
private void ResizeToFitContent()
{
if (!_autoResizeHeight || _content == null)
{
return;
}
if (_itemDescription == null)
{
return;
}
_itemDescription.ForceMeshUpdate();
var descriptionSize = _itemDescription.rectTransform.sizeDelta;
descriptionSize.y = _itemDescription.preferredHeight;
_itemDescription.rectTransform.sizeDelta = descriptionSize;
float descriptionHeight = Mathf.Max(0f, _itemDescription.preferredHeight);
float targetHeight = _fixedTopHeight + descriptionHeight + _fixedBottomHeight;
Vector2 size = new Vector2(_fixedWidth, targetHeight);
_content.sizeDelta = size;
}
private void ApplySideAnchoredTargetPos()
{
if (_content == null)
{
return;
}
RectTransform parent = _content.parent as RectTransform;
if (parent == null)
{
return;
}
Vector2 size = _content.sizeDelta;
if (size.x <= 0f || size.y <= 0f)
{
size = _content.rect.size;
}
Vector2 pivot = _content.pivot;
float halfWidth = parent.rect.width * 0.5f;
float halfHeight = parent.rect.height * 0.5f;
float minX = -halfWidth + size.x * pivot.x + _screenEdgePadding;
float maxX = halfWidth - size.x * (1f - pivot.x) - _screenEdgePadding;
float minY = -halfHeight + size.y * pivot.y + _screenEdgePadding;
float maxY = halfHeight - size.y * (1f - pivot.y) - _screenEdgePadding;
float clampedY = minY <= maxY ? Mathf.Clamp(_targetPos.y, minY, maxY) : 0f;
float anchorHalfWidth = Mathf.Max(0f, _anchorItemWidth * 0.5f);
float sideGap = Mathf.Max(0f, _sideGap);
float horizontalOffset = anchorHalfWidth + size.x * 0.5f + sideGap;
float leftCandidateX = _targetPos.x - horizontalOffset;
float rightCandidateX = _targetPos.x + horizontalOffset;
float finalX;
if (IsInsideBounds(leftCandidateX, minX, maxX))
{
finalX = leftCandidateX;
}
else if (IsInsideBounds(rightCandidateX, minX, maxX))
{
finalX = rightCandidateX;
}
else
{
finalX = minX <= maxX ? Mathf.Clamp(leftCandidateX, minX, maxX) : 0f;
}
_content.anchoredPosition3D = new Vector3(finalX, clampedY, _targetPos.z);
}
private Vector3 ConvertWorldToAnchored(Vector3 worldPos)
{
if (_content == null)
{
return worldPos;
}
RectTransform parent = _content.parent as RectTransform;
if (parent == null)
{
return worldPos;
}
Canvas canvas = _content.GetComponentInParent<Canvas>();
Canvas rootCanvas = canvas != null ? canvas.rootCanvas : null;
Camera uiCamera = null;
if (rootCanvas != null && rootCanvas.renderMode != RenderMode.ScreenSpaceOverlay)
{
uiCamera = rootCanvas.worldCamera != null ? rootCanvas.worldCamera : Camera.main;
}
Vector2 screenPoint = RectTransformUtility.WorldToScreenPoint(uiCamera, worldPos);
if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(parent, screenPoint, uiCamera,
out Vector2 localPoint))
{
return worldPos;
}
float z = _content.anchoredPosition3D.z;
return new Vector3(localPoint.x, localPoint.y, z);
}
private static bool IsInsideBounds(float value, float min, float max)
{
return value >= min && value <= max;
}
private void RefreshTags(TagItemContext[] tags)
{
ClearTags();
if (_tagAreaParent == null || _tagItemPrefab == null || tags == null || tags.Length <= 0)
{
return;
}
if (_tagItemPrefab.scene.IsValid() && _tagItemPrefab.transform.IsChildOf(_tagAreaParent))
{
_tagItemPrefab.SetActive(false);
}
for (int i = 0; i < tags.Length; i++)
{
TagItemContext tagContext = tags[i];
if (tagContext == null || string.IsNullOrWhiteSpace(tagContext.TagName))
{
continue;
}
GameObject tagGo = Instantiate(_tagItemPrefab, _tagAreaParent);
tagGo.SetActive(true);
if (tagGo.TryGetComponent(out TagItem tagItem))
{
tagItem.OnInit(tagContext);
}
else
{
TMP_Text tagText = tagGo.GetComponentInChildren<TMP_Text>(true);
if (tagText != null)
{
tagText.text = tagContext.TagName;
}
}
_runtimeTagItems.Add(tagGo);
}
}
private void ClearTags()
{
for (int i = _runtimeTagItems.Count - 1; i >= 0; i--)
{
GameObject tagGo = _runtimeTagItems[i];
if (tagGo != null)
{
Destroy(tagGo);
}
}
_runtimeTagItems.Clear();
}
private void CloseSelf()
{
GameEntry.UI.CloseUIForm(this);
}
private bool IsPointerInContent(Vector2 screenPoint)
{
Canvas canvas = _content.GetComponentInParent<Canvas>();
Canvas rootCanvas = canvas != null ? canvas.rootCanvas : null;
Camera uiCamera = null;
if (rootCanvas != null && rootCanvas.renderMode != RenderMode.ScreenSpaceOverlay)
{
uiCamera = rootCanvas.worldCamera != null ? rootCanvas.worldCamera : Camera.main;
}
return RectTransformUtility.RectangleContainsScreenPoint(_content, screenPoint, uiCamera);
}
private static bool TryGetPointerDownScreenPosition(out Vector2 screenPoint)
{
if (Input.touchCount > 0)
{
for (int i = 0; i < Input.touchCount; i++)
{
Touch touch = Input.GetTouch(i);
if (touch.phase != TouchPhase.Began)
{
continue;
}
screenPoint = touch.position;
return true;
}
}
if (Input.GetMouseButtonDown(0))
{
screenPoint = Input.mousePosition;
return true;
}
screenPoint = default;
return false;
}
}
}