引入 DOTween 来实现部分动画,具体有:
- 鼠标与 Part 交互时的缩放 - Part 放置失败回到原位的位移
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using CustomComponent;
|
||||
using DG.Tweening;
|
||||
using Definition.Enum;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
@@ -10,7 +11,7 @@ namespace UI
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(RectTransform))]
|
||||
[DisallowMultipleComponent]
|
||||
public class CombineDraggablePart : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
|
||||
public class CombineDraggablePart : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler, IPointerEnterHandler, IPointerExitHandler
|
||||
{
|
||||
#region Inspector Config
|
||||
|
||||
@@ -26,6 +27,18 @@ namespace UI
|
||||
|
||||
[SerializeField] private CanvasGroup _canvasGroup;
|
||||
|
||||
[SerializeField] private float _defaultScaleMultiplier = 0.8f;
|
||||
|
||||
[SerializeField] private float _hoverScaleMultiplier = 1f;
|
||||
|
||||
[SerializeField] private float _hoverTweenDuration = 0.15f;
|
||||
|
||||
[SerializeField] private Ease _hoverTweenEase = Ease.OutQuad;
|
||||
|
||||
[SerializeField] private float _returnToSpawnDuration = 0.4f;
|
||||
|
||||
[SerializeField] private Ease _returnToSpawnEase = Ease.OutCubic;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Spawn State
|
||||
@@ -54,6 +67,14 @@ namespace UI
|
||||
|
||||
private bool _isLocked;
|
||||
|
||||
private bool _isDragging;
|
||||
|
||||
private Vector3 _originalLocalScale = Vector3.one;
|
||||
|
||||
private Tween _scaleTween;
|
||||
|
||||
private Tween _returnTween;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Query
|
||||
@@ -64,6 +85,32 @@ namespace UI
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity Lifecycle
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (_rectTransform == null)
|
||||
{
|
||||
_rectTransform = transform as RectTransform;
|
||||
}
|
||||
|
||||
if (_canvasGroup == null)
|
||||
{
|
||||
_canvasGroup = gameObject.GetOrAddComponent<CanvasGroup>();
|
||||
}
|
||||
|
||||
_originalLocalScale = _rectTransform.localScale;
|
||||
ApplyScaleImmediate(_defaultScaleMultiplier);
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
KillScaleTween();
|
||||
KillReturnTween();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Setup
|
||||
|
||||
/// <summary>
|
||||
@@ -111,6 +158,7 @@ namespace UI
|
||||
public void ResetToSpawn()
|
||||
{
|
||||
ClearSlotOccupancy();
|
||||
_isDragging = false;
|
||||
_isPlaced = false;
|
||||
_isLocked = false;
|
||||
ReturnToSpawn();
|
||||
@@ -130,6 +178,8 @@ namespace UI
|
||||
return;
|
||||
}
|
||||
|
||||
KillReturnTween();
|
||||
_isDragging = true;
|
||||
_canvasGroup.blocksRaycasts = false;
|
||||
MoveToDragRoot();
|
||||
_rectTransform.SetAsLastSibling();
|
||||
@@ -153,6 +203,8 @@ namespace UI
|
||||
/// </summary>
|
||||
public void OnEndDrag(PointerEventData eventData)
|
||||
{
|
||||
_isDragging = false;
|
||||
|
||||
if (_isLocked)
|
||||
{
|
||||
return;
|
||||
@@ -162,10 +214,30 @@ namespace UI
|
||||
|
||||
if (!_isPlaced)
|
||||
{
|
||||
ReturnToSpawn();
|
||||
ReturnToSpawnAnimated();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnPointerEnter(PointerEventData eventData)
|
||||
{
|
||||
if (_isLocked || _isPlaced || _isDragging)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PlayScaleTween(_hoverScaleMultiplier);
|
||||
}
|
||||
|
||||
public void OnPointerExit(PointerEventData eventData)
|
||||
{
|
||||
if (_isLocked || _isPlaced || _isDragging)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PlayScaleTween(_defaultScaleMultiplier);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 校验是否允许开始拖拽。
|
||||
/// </summary>
|
||||
@@ -208,6 +280,8 @@ namespace UI
|
||||
/// </summary>
|
||||
public void ReturnToSpawn()
|
||||
{
|
||||
KillReturnTween();
|
||||
|
||||
if (_spawnParent == null)
|
||||
{
|
||||
return;
|
||||
@@ -225,6 +299,9 @@ namespace UI
|
||||
/// </summary>
|
||||
internal void PlaceToSlot(CombineSlot slot)
|
||||
{
|
||||
KillScaleTween();
|
||||
KillReturnTween();
|
||||
_isDragging = false;
|
||||
_currentSlot = slot;
|
||||
_isPlaced = true;
|
||||
_isLocked = _lockAfterPlaced;
|
||||
@@ -249,6 +326,66 @@ namespace UI
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyScaleImmediate(float multiplier)
|
||||
{
|
||||
_rectTransform.localScale = _originalLocalScale * Mathf.Max(0f, multiplier);
|
||||
}
|
||||
|
||||
private void PlayScaleTween(float targetMultiplier)
|
||||
{
|
||||
KillScaleTween();
|
||||
_scaleTween = _rectTransform
|
||||
.DOScale(_originalLocalScale * Mathf.Max(0f, targetMultiplier), _hoverTweenDuration)
|
||||
.SetEase(_hoverTweenEase)
|
||||
.SetUpdate(true);
|
||||
}
|
||||
|
||||
private void ReturnToSpawnAnimated()
|
||||
{
|
||||
if (_spawnParent == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_returnToSpawnDuration <= 0f)
|
||||
{
|
||||
ReturnToSpawn();
|
||||
return;
|
||||
}
|
||||
|
||||
KillReturnTween();
|
||||
_rectTransform.SetParent(_spawnParent, true);
|
||||
_rectTransform.SetSiblingIndex(_spawnSiblingIndex);
|
||||
|
||||
_returnTween = DOTween.Sequence()
|
||||
.Append(_rectTransform.DOMove(_spawnWorldPosition, _returnToSpawnDuration))
|
||||
.Join(_rectTransform.DORotateQuaternion(_spawnWorldRotation, _returnToSpawnDuration))
|
||||
.Join(_rectTransform.DOScale(_spawnWorldScale, _returnToSpawnDuration))
|
||||
.SetEase(_returnToSpawnEase)
|
||||
.SetUpdate(true)
|
||||
.OnKill(() => _returnTween = null);
|
||||
}
|
||||
|
||||
private void KillScaleTween()
|
||||
{
|
||||
if (_scaleTween != null && _scaleTween.IsActive())
|
||||
{
|
||||
_scaleTween.Kill();
|
||||
}
|
||||
|
||||
_scaleTween = null;
|
||||
}
|
||||
|
||||
private void KillReturnTween()
|
||||
{
|
||||
if (_returnTween != null && _returnTween.IsActive())
|
||||
{
|
||||
_returnTween.Kill();
|
||||
}
|
||||
|
||||
_returnTween = null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,14 @@ namespace UI
|
||||
|
||||
[SerializeField] private float _partVerticalSpacing = 120f;
|
||||
|
||||
[SerializeField] [Range(0f, 1f)] private float _partSpawnMinXNormalized = 0.55f;
|
||||
|
||||
[SerializeField] [Range(0f, 1f)] private float _partSpawnMaxXNormalized = 0.95f;
|
||||
|
||||
[SerializeField] [Range(0f, 1f)] private float _partSpawnMinYNormalized = 0.1f;
|
||||
|
||||
[SerializeField] [Range(0f, 1f)] private float _partSpawnMaxYNormalized = 0.9f;
|
||||
|
||||
private readonly List<GameObject> _runtimeNodes = new();
|
||||
|
||||
#endregion
|
||||
@@ -194,14 +202,65 @@ namespace UI
|
||||
_runtimeNodes.Add(slot.gameObject);
|
||||
}
|
||||
|
||||
foreach (var partData in context.Parts)
|
||||
List<CombinePartContext> partContexts = BuildPartContextList(context);
|
||||
for (int i = 0; i < partContexts.Count; i++)
|
||||
{
|
||||
CombinePartContext partData = partContexts[i];
|
||||
CombineDraggablePart part = Instantiate(_partPrefab, _partRoot, false);
|
||||
part.Initialize(partData);
|
||||
SetPartSpawnPosition(part, i);
|
||||
_runtimeNodes.Add(part.gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
private List<CombinePartContext> BuildPartContextList(CombineFormContext context)
|
||||
{
|
||||
if (context.Parts != null && context.Parts.Count > 0)
|
||||
{
|
||||
return context.Parts;
|
||||
}
|
||||
|
||||
List<CombinePartContext> fallbackParts = new List<CombinePartContext>(context.Slots.Count);
|
||||
for (int i = 0; i < context.Slots.Count; i++)
|
||||
{
|
||||
CombineSlotContext slot = context.Slots[i];
|
||||
fallbackParts.Add(new CombinePartContext
|
||||
{
|
||||
PartType = slot.RequiredPartType,
|
||||
PartDisplayName = slot.RequiredPartType.ToString(),
|
||||
MechanicsExplanation = slot.MechanicsExplanation,
|
||||
LockAfterPlaced = true
|
||||
});
|
||||
}
|
||||
|
||||
return fallbackParts;
|
||||
}
|
||||
|
||||
private void SetPartSpawnPosition(CombineDraggablePart part, int index)
|
||||
{
|
||||
RectTransform partRect = part.transform as RectTransform;
|
||||
if (partRect == null || _partRoot == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Rect rootRect = _partRoot.rect;
|
||||
if (rootRect.width <= 0f || rootRect.height <= 0f)
|
||||
{
|
||||
partRect.anchoredPosition = _partStartAnchoredPosition + Vector2.down * (_partVerticalSpacing * index);
|
||||
return;
|
||||
}
|
||||
|
||||
float minX = Mathf.Lerp(rootRect.xMin, rootRect.xMax, Mathf.Min(_partSpawnMinXNormalized, _partSpawnMaxXNormalized));
|
||||
float maxX = Mathf.Lerp(rootRect.xMin, rootRect.xMax, Mathf.Max(_partSpawnMinXNormalized, _partSpawnMaxXNormalized));
|
||||
float minY = Mathf.Lerp(rootRect.yMin, rootRect.yMax, Mathf.Min(_partSpawnMinYNormalized, _partSpawnMaxYNormalized));
|
||||
float maxY = Mathf.Lerp(rootRect.yMin, rootRect.yMax, Mathf.Max(_partSpawnMinYNormalized, _partSpawnMaxYNormalized));
|
||||
|
||||
partRect.anchoredPosition = new Vector2(
|
||||
Random.Range(minX, maxX),
|
||||
Random.Range(minY, maxY));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Handlers
|
||||
@@ -244,4 +303,4 @@ namespace UI
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,6 +118,13 @@ namespace UI
|
||||
_requireStrictOrder = data.RequireStrictOrder;
|
||||
_mechanicsExplanation = data.MechanicsExplanation ?? string.Empty;
|
||||
_mismatchHint = data.MismatchHint ?? string.Empty;
|
||||
|
||||
RectTransform rectTransform = transform as RectTransform;
|
||||
if (rectTransform != null)
|
||||
{
|
||||
rectTransform.anchoredPosition = data.AnchoredPosition;
|
||||
rectTransform.sizeDelta = data.SizeDelta;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -192,4 +199,4 @@ namespace UI
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user