fixes
This commit is contained in:
@@ -328,7 +328,7 @@ namespace Darkmatter.Features.DrawingTemplates.Editor
|
||||
foreach (var m in markers)
|
||||
{
|
||||
if (m.Shape == null) { missing++; continue; }
|
||||
if (!pieces.Contains(m.Shape)) pieces.Add(m.Shape);
|
||||
pieces.Add(m.Shape);
|
||||
}
|
||||
Undo.RecordObject(t, "Scan Drawing Prefab");
|
||||
t.EditorSet(t.Id, t.DisplayName, t.DefaultThumbnail, t.DrawingPrefab, t.ColoringPrefab,
|
||||
|
||||
@@ -12,6 +12,7 @@ namespace Darkmatter.Features.ShapeBuilder.Commands
|
||||
private readonly Quaternion _prevRot;
|
||||
private readonly Transform _prevParent;
|
||||
private readonly int _prevSiblingIndex;
|
||||
private SlotMarker _snappedSlot;
|
||||
|
||||
public SnapPieceCommand(ShapePiece piece)
|
||||
{
|
||||
@@ -23,7 +24,12 @@ namespace Darkmatter.Features.ShapeBuilder.Commands
|
||||
_prevRot = Quaternion.identity;
|
||||
}
|
||||
|
||||
public void Execute() => _piece.SnapInternal();
|
||||
public void Execute()
|
||||
{
|
||||
if (_snappedSlot != null) _piece.ReassignActiveSlot(_snappedSlot);
|
||||
_piece.SnapInternal();
|
||||
if (_snappedSlot == null) _snappedSlot = _piece.ActiveSlot;
|
||||
}
|
||||
|
||||
public void Undo() => _piece.UnsnapInternal(_prevParent, _prevSiblingIndex, _prevPos, _prevSize, _prevRot);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,6 @@ namespace Darkmatter.Features.ShapeBuilder.Systems
|
||||
{
|
||||
public interface IShapePieceFactory
|
||||
{
|
||||
ShapePiece Create(GameObject prefab, ShapeSO shape, SlotMarker slot, Vector2 trayPos);
|
||||
ShapePiece Create(GameObject prefab, ShapeSO shape, SlotMarker[] candidateSlots, Vector2 trayPos);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,28 +117,40 @@ namespace Darkmatter.Features.ShapeBuilder.Systems
|
||||
int count,
|
||||
SlotMarker[] slots, float pitch, float trayW)
|
||||
{
|
||||
var preSnapCounts = new Dictionary<string, int>();
|
||||
if (preSnappedIds != null)
|
||||
foreach (var id in preSnappedIds)
|
||||
{
|
||||
if (string.IsNullOrEmpty(id)) continue;
|
||||
preSnapCounts[id] = preSnapCounts.GetValueOrDefault(id) + 1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var shape = template.Pieces[i];
|
||||
var slot = FindSlotForShape(slots, shape);
|
||||
if (slot == null)
|
||||
var candidates = FindSlotsForShape(slots, shape);
|
||||
if (candidates.Length == 0)
|
||||
{
|
||||
Debug.LogError($"[ShapeBuilder] No SlotMarker for '{shape.Id}' in '{template.Id}'");
|
||||
continue;
|
||||
}
|
||||
|
||||
var trayPos = new Vector2(pitch * (i + 1) - trayW * 0.5f, 0f);
|
||||
bool preSnapped = preSnappedIds != null && preSnappedIds.Contains(shape.Id);
|
||||
|
||||
var piece = _factory.Create(_piecePrefab, shape, slot, trayPos);
|
||||
var piece = _factory.Create(_piecePrefab, shape, candidates, trayPos);
|
||||
_pieces.Add(piece);
|
||||
|
||||
if (preSnapped)
|
||||
if (preSnapCounts.TryGetValue(shape.Id, out var remaining) && remaining > 0)
|
||||
{
|
||||
_undo.Append(new SnapPieceCommand(piece));
|
||||
piece.SnapInstantly();
|
||||
_snapped++;
|
||||
_snappedPieceIds.Add(shape.Id);
|
||||
var unoccupied = FindFirstUnoccupied(candidates);
|
||||
if (unoccupied != null)
|
||||
{
|
||||
_undo.Append(new SnapPieceCommand(piece));
|
||||
piece.SnapInstantlyTo(unoccupied);
|
||||
unoccupied.SetOccupied(true);
|
||||
_snapped++;
|
||||
_snappedPieceIds.Add(shape.Id);
|
||||
preSnapCounts[shape.Id] = remaining - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -160,10 +172,19 @@ namespace Darkmatter.Features.ShapeBuilder.Systems
|
||||
}
|
||||
}
|
||||
|
||||
private static SlotMarker FindSlotForShape(SlotMarker[] slots, ShapeSO shape)
|
||||
private static SlotMarker[] FindSlotsForShape(SlotMarker[] slots, ShapeSO shape)
|
||||
{
|
||||
var list = new List<SlotMarker>();
|
||||
foreach (var s in slots)
|
||||
if (s != null && s.Shape == shape)
|
||||
list.Add(s);
|
||||
return list.ToArray();
|
||||
}
|
||||
|
||||
private static SlotMarker FindFirstUnoccupied(SlotMarker[] slots)
|
||||
{
|
||||
foreach (var s in slots)
|
||||
if (s.Shape == shape)
|
||||
if (s != null && !s.IsOccupied)
|
||||
return s;
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -33,13 +33,13 @@ namespace Darkmatter.Features.ShapeBuilder.Systems
|
||||
_refs = refs;
|
||||
}
|
||||
|
||||
public ShapePiece Create(GameObject prefab, ShapeSO shape, SlotMarker slot, Vector2 trayPos)
|
||||
public ShapePiece Create(GameObject prefab, ShapeSO shape, SlotMarker[] candidateSlots, Vector2 trayPos)
|
||||
{
|
||||
var go = Object.Instantiate(prefab, _holder.SpawnRoot);
|
||||
go.name = $"Piece_{shape.Id}";
|
||||
|
||||
var piece = go.GetComponent<ShapePiece>();
|
||||
piece.Setup(shape, slot, _cfg, _sfx, _bus, _undo, trayPos, _refs.PaperRoot);
|
||||
piece.Setup(shape, candidateSlots, _cfg, _sfx, _bus, _undo, trayPos, _refs.PaperRoot);
|
||||
return piece;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,8 @@ namespace Darkmatter.Features.ShapeBuilder.UI
|
||||
|
||||
// Bound by Setup
|
||||
private ShapeSO _shape;
|
||||
private SlotMarker _slot;
|
||||
private SlotMarker[] _candidateSlots;
|
||||
private SlotMarker _activeSlot;
|
||||
private ShapeBuilderConfig _cfg;
|
||||
private ISfxPlayer _sfx;
|
||||
private IEventBus _bus;
|
||||
@@ -30,6 +31,9 @@ namespace Darkmatter.Features.ShapeBuilder.UI
|
||||
private RectTransform _dragRoot;
|
||||
private Transform _homeParent;
|
||||
private int _homeSiblingIndex;
|
||||
private Vector2 _origAnchorMin;
|
||||
private Vector2 _origAnchorMax;
|
||||
private Vector2 _origPivot;
|
||||
|
||||
// Per-drag state
|
||||
private RectTransform _rt;
|
||||
@@ -51,10 +55,13 @@ namespace Darkmatter.Features.ShapeBuilder.UI
|
||||
public int HomeSiblingIndex => _homeSiblingIndex;
|
||||
public Vector2 TrayPosition => _trayPos;
|
||||
public Vector2 TraySize => _traySize;
|
||||
public SlotMarker ActiveSlot => _activeSlot;
|
||||
|
||||
public void ReassignActiveSlot(SlotMarker slot) => _activeSlot = slot;
|
||||
|
||||
public void Setup(
|
||||
ShapeSO shape,
|
||||
SlotMarker slot,
|
||||
SlotMarker[] candidateSlots,
|
||||
ShapeBuilderConfig cfg,
|
||||
ISfxPlayer sfx,
|
||||
IEventBus bus,
|
||||
@@ -62,9 +69,10 @@ namespace Darkmatter.Features.ShapeBuilder.UI
|
||||
Vector2 trayPos,
|
||||
RectTransform dragRoot)
|
||||
{
|
||||
_shape = shape;
|
||||
_slot = slot;
|
||||
_cfg = cfg;
|
||||
_shape = shape;
|
||||
_candidateSlots = candidateSlots;
|
||||
_activeSlot = null;
|
||||
_cfg = cfg;
|
||||
_sfx = sfx;
|
||||
_bus = bus;
|
||||
_undo = undo;
|
||||
@@ -74,6 +82,9 @@ namespace Darkmatter.Features.ShapeBuilder.UI
|
||||
|
||||
_homeParent = RectTransform.parent;
|
||||
_homeSiblingIndex = RectTransform.GetSiblingIndex();
|
||||
_origAnchorMin = RectTransform.anchorMin;
|
||||
_origAnchorMax = RectTransform.anchorMax;
|
||||
_origPivot = RectTransform.pivot;
|
||||
|
||||
image.sprite = shape.Sprite;
|
||||
ApplyTrayPose();
|
||||
@@ -103,17 +114,25 @@ namespace Darkmatter.Features.ShapeBuilder.UI
|
||||
if (_locked) return;
|
||||
|
||||
var pointerLocal = ScreenToLocal(e.position) + _grabOffset;
|
||||
bool insidePreview = IsOverSlot(e.position);
|
||||
var hovered = FindSlotUnder(e.position);
|
||||
bool insidePreview = hovered != null;
|
||||
|
||||
if (insidePreview && !_inPreview)
|
||||
{
|
||||
_activeSlot = hovered;
|
||||
_inPreview = true;
|
||||
_sfx.Play(SfxId.ShapeHover);
|
||||
AnimatePreviewPose(toSlot: true);
|
||||
}
|
||||
else if (insidePreview && _inPreview && hovered != _activeSlot)
|
||||
{
|
||||
_activeSlot = hovered;
|
||||
AnimatePreviewPose(toSlot: true);
|
||||
}
|
||||
else if (!insidePreview && _inPreview)
|
||||
{
|
||||
_inPreview = false;
|
||||
_activeSlot = null;
|
||||
AnimatePreviewPose(toSlot: false);
|
||||
}
|
||||
|
||||
@@ -125,25 +144,39 @@ namespace Darkmatter.Features.ShapeBuilder.UI
|
||||
{
|
||||
if (_locked) return;
|
||||
|
||||
if (IsOverSlot(e.position))
|
||||
var target = FindSlotUnder(e.position);
|
||||
if (target != null)
|
||||
{
|
||||
_activeSlot = target;
|
||||
_undo.Push(new SnapPieceCommand(this));
|
||||
}
|
||||
else
|
||||
{
|
||||
_activeSlot = null;
|
||||
ReturnToTray();
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsOverSlot(Vector2 screenPos)
|
||||
private SlotMarker FindSlotUnder(Vector2 screenPos)
|
||||
{
|
||||
return RectTransformUtility.RectangleContainsScreenPoint(
|
||||
_slot.RectTransform, screenPos, _eventCam);
|
||||
if (_candidateSlots == null) return null;
|
||||
foreach (var s in _candidateSlots)
|
||||
{
|
||||
if (s == null) continue;
|
||||
if (s.IsOccupied && s != _activeSlot) continue;
|
||||
if (RectTransformUtility.RectangleContainsScreenPoint(s.RectTransform, screenPos, _eventCam))
|
||||
return s;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void AnimatePreviewPose(bool toSlot)
|
||||
{
|
||||
if (_previewSeq.isAlive) _previewSeq.Stop();
|
||||
|
||||
if (toSlot)
|
||||
if (toSlot && _activeSlot != null)
|
||||
{
|
||||
var slot = _slot.RectTransform;
|
||||
var slot = _activeSlot.RectTransform;
|
||||
_previewSeq = Sequence.Create()
|
||||
.Group(Tween.UIAnchoredPosition(RectTransform, SlotPosInDragSpace(), _cfg.SnapDuration, Ease.OutQuad))
|
||||
.Group(Tween.LocalScale(RectTransform, SlotScaleInDragSpace(), _cfg.SnapDuration, Ease.OutQuad))
|
||||
@@ -161,7 +194,7 @@ namespace Darkmatter.Features.ShapeBuilder.UI
|
||||
|
||||
private Vector2 SlotPosInDragSpace()
|
||||
{
|
||||
Vector3 worldPos = _slot.RectTransform.position;
|
||||
Vector3 worldPos = _activeSlot.RectTransform.position;
|
||||
Vector3 local = _parentRect.InverseTransformPoint(worldPos);
|
||||
Vector2 parentSize = _parentRect.rect.size;
|
||||
Vector2 anchorRef = (RectTransform.anchorMin - _parentRect.pivot) * parentSize;
|
||||
@@ -177,13 +210,13 @@ namespace Darkmatter.Features.ShapeBuilder.UI
|
||||
|
||||
private Quaternion SlotRotInDragSpace()
|
||||
{
|
||||
return Quaternion.Inverse(_parentRect.rotation) * _slot.RectTransform.rotation;
|
||||
return Quaternion.Inverse(_parentRect.rotation) * _activeSlot.RectTransform.rotation;
|
||||
}
|
||||
|
||||
private Vector3 SlotScaleInDragSpace()
|
||||
{
|
||||
Vector3 parentLossy = _parentRect.lossyScale;
|
||||
Vector3 slotLossy = _slot.RectTransform.lossyScale;
|
||||
Vector3 slotLossy = _activeSlot.RectTransform.lossyScale;
|
||||
return new Vector3(
|
||||
slotLossy.x / Mathf.Max(0.0001f, parentLossy.x),
|
||||
slotLossy.y / Mathf.Max(0.0001f, parentLossy.y),
|
||||
@@ -194,27 +227,49 @@ namespace Darkmatter.Features.ShapeBuilder.UI
|
||||
{
|
||||
StopPreviewTweens();
|
||||
Lock();
|
||||
var slot = _slot.RectTransform;
|
||||
var slot = _activeSlot.RectTransform;
|
||||
|
||||
Tween.Position(RectTransform, slot.position, _cfg.SnapDuration, Ease.OutBack);
|
||||
Tween.Rotation(RectTransform, slot.rotation, _cfg.SnapDuration, Ease.OutBack);
|
||||
Tween.LocalScale(RectTransform, slot.localScale, _cfg.SnapDuration, Ease.OutBack);
|
||||
Tween.UISizeDelta(RectTransform, slot.sizeDelta, _cfg.SnapDuration, Ease.OutBack);
|
||||
Sequence.Create()
|
||||
.Group(Tween.Position(RectTransform, slot.position, _cfg.SnapDuration, Ease.OutBack))
|
||||
.Group(Tween.Rotation(RectTransform, slot.rotation, _cfg.SnapDuration, Ease.OutBack))
|
||||
.Group(Tween.LocalScale(RectTransform, slot.localScale, _cfg.SnapDuration, Ease.OutBack))
|
||||
.Group(Tween.UISizeDelta(RectTransform, slot.sizeDelta, _cfg.SnapDuration, Ease.OutBack))
|
||||
.ChainCallback(AlignRectToSlot);
|
||||
|
||||
_sfx.Play(SfxId.ShapeSnap);
|
||||
_bus.Publish(new PieceSnappedSignal(_shape.Id));
|
||||
}
|
||||
|
||||
private void AlignRectToSlot()
|
||||
{
|
||||
if (this == null || _activeSlot == null) return;
|
||||
var rt = RectTransform;
|
||||
var slot = _activeSlot.RectTransform;
|
||||
rt.anchorMin = Vector2.zero;
|
||||
rt.anchorMax = Vector2.one;
|
||||
rt.pivot = slot.pivot;
|
||||
rt.anchoredPosition = Vector2.zero;
|
||||
rt.sizeDelta = Vector2.zero;
|
||||
rt.localRotation = Quaternion.identity;
|
||||
rt.localScale = Vector3.one;
|
||||
}
|
||||
|
||||
internal void UnsnapInternal(Transform parent, int siblingIndex, Vector2 pos, Vector2 size, Quaternion rot)
|
||||
{
|
||||
_locked = false;
|
||||
image.raycastTarget = true;
|
||||
|
||||
if (_activeSlot != null) _activeSlot.SetOccupied(false);
|
||||
_activeSlot = null;
|
||||
|
||||
Tween.StopAll(onTarget: RectTransform);
|
||||
|
||||
RectTransform.SetParent(parent, worldPositionStays: false);
|
||||
if (siblingIndex >= 0) RectTransform.SetSiblingIndex(siblingIndex);
|
||||
|
||||
RectTransform.anchorMin = _origAnchorMin;
|
||||
RectTransform.anchorMax = _origAnchorMax;
|
||||
RectTransform.pivot = _origPivot;
|
||||
RectTransform.anchoredPosition = pos;
|
||||
RectTransform.sizeDelta = size;
|
||||
RectTransform.localRotation = rot;
|
||||
@@ -224,14 +279,11 @@ namespace Darkmatter.Features.ShapeBuilder.UI
|
||||
_bus.Publish(new PieceUnsnappedSignal(_shape.Id));
|
||||
}
|
||||
|
||||
public void SnapInstantly()
|
||||
public void SnapInstantlyTo(SlotMarker slot)
|
||||
{
|
||||
_activeSlot = slot;
|
||||
Lock();
|
||||
var slot = _slot.RectTransform;
|
||||
RectTransform.position = slot.position;
|
||||
RectTransform.rotation = slot.rotation;
|
||||
RectTransform.localScale = slot.localScale;
|
||||
RectTransform.sizeDelta = slot.sizeDelta;
|
||||
AlignRectToSlot();
|
||||
}
|
||||
|
||||
private void ReturnToTray()
|
||||
@@ -258,7 +310,11 @@ namespace Darkmatter.Features.ShapeBuilder.UI
|
||||
{
|
||||
_locked = true;
|
||||
image.raycastTarget = false;
|
||||
RectTransform.SetParent(_slot.RectTransform.parent, worldPositionStays: true);
|
||||
if (_activeSlot != null)
|
||||
{
|
||||
_activeSlot.SetOccupied(true);
|
||||
RectTransform.SetParent(_activeSlot.RectTransform, worldPositionStays: true);
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyTrayPose()
|
||||
|
||||
@@ -12,6 +12,9 @@ namespace Darkmatter.Features.ShapeBuilder.UI
|
||||
public ShapeSO Shape => shape;
|
||||
public string SlotId => shape != null ? shape.Id : null;
|
||||
public RectTransform RectTransform => (RectTransform)transform;
|
||||
public bool IsOccupied { get; private set; }
|
||||
|
||||
public void SetOccupied(bool value) => IsOccupied = value;
|
||||
|
||||
public void SetOutlineVisible(bool visible)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user