UX updates
This commit is contained in:
@@ -79,6 +79,21 @@ public class ColoringController : IColoringController, IDisposable
|
||||
if (from != color)
|
||||
_history.Push(new ColorRegionCommand(region, from, color));
|
||||
_bus.Publish(new ColorAppliedSignal(regionId, color));
|
||||
if (AllRegionsColored())
|
||||
_bus.Publish(new AllRegionsColoredSignal());
|
||||
}
|
||||
|
||||
// True once every region has been painted away from its authored (uncoloured) default.
|
||||
private bool AllRegionsColored()
|
||||
{
|
||||
if (_regions.Count == 0) return false;
|
||||
foreach (var region in _regions)
|
||||
{
|
||||
if (region == null) continue;
|
||||
if (_authoredColors.TryGetValue(region.RegionId, out var authored) && region.Color == authored)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public IReadOnlyDictionary<string, Color> GetCurrentColors()
|
||||
|
||||
@@ -97,7 +97,10 @@ namespace Darkmatter.Features.ShapeBuilder.UI
|
||||
_bus = bus;
|
||||
_undo = undo;
|
||||
_trayPos = trayPos;
|
||||
_traySize = shape.DefaultSizeDelta;
|
||||
// Keep the piece at the prefab Image's authored size. Sourcing this from
|
||||
// shape.DefaultSizeDelta let each ShapeSO override the image's default size;
|
||||
// the prefab (with preserveAspect) is the single source of truth now.
|
||||
_traySize = RectTransform.sizeDelta;
|
||||
_dragRoot = dragRoot;
|
||||
|
||||
_homeParent = RectTransform.parent;
|
||||
@@ -151,7 +154,7 @@ namespace Darkmatter.Features.ShapeBuilder.UI
|
||||
if (_locked) return;
|
||||
|
||||
var pointerLocal = ScreenToLocal(e.position) + _grabOffset;
|
||||
var hovered = FindSlotUnder(e.position);
|
||||
var hovered = FindSlotForCatch(e.position);
|
||||
bool insidePreview = hovered != null;
|
||||
|
||||
if (insidePreview && !_inPreview)
|
||||
@@ -187,7 +190,11 @@ namespace Darkmatter.Features.ShapeBuilder.UI
|
||||
|
||||
SetAlpha(_dragOrigAlpha);
|
||||
|
||||
var target = FindSlotUnder(e.position);
|
||||
// If a slot is already previewing, releasing commits to it. Otherwise catch
|
||||
// a quick drop with no prior preview using the same per-slot radius.
|
||||
var target = _inPreview && _activeSlot != null
|
||||
? _activeSlot
|
||||
: FindSlotForCatch(e.position);
|
||||
if (target != null)
|
||||
{
|
||||
_activeSlot = target;
|
||||
@@ -200,17 +207,45 @@ namespace Darkmatter.Features.ShapeBuilder.UI
|
||||
}
|
||||
}
|
||||
|
||||
private SlotMarker FindSlotUnder(Vector2 screenPos)
|
||||
// Nearest slot whose per-slot catch circle contains the pointer. Each slot's
|
||||
// radius is derived from its own size (see SlotCatchRadius), so big slots catch
|
||||
// from farther than small ones. All distances are in PaperRoot local space —
|
||||
// ScreenPointToLocalPointInRectangle and InverseTransformVector both strip the
|
||||
// CanvasScaler factor, so the catch feels identical on every screen resolution.
|
||||
private SlotMarker FindSlotForCatch(Vector2 screenPos)
|
||||
{
|
||||
if (_candidateSlots == null) return null;
|
||||
Vector2 pointerLocal = ScreenToLocal(screenPos);
|
||||
SlotMarker best = null;
|
||||
float bestSqr = float.MaxValue;
|
||||
foreach (var s in _candidateSlots)
|
||||
{
|
||||
if (s == null) continue;
|
||||
if (s.IsOccupied && s != _activeSlot) continue;
|
||||
if (RectTransformUtility.RectangleContainsScreenPoint(s.RectTransform, screenPos, _eventCam))
|
||||
return s;
|
||||
var srt = s.RectTransform;
|
||||
Vector2 slotLocal = _parentRect.InverseTransformPoint(srt.position);
|
||||
float sqr = (slotLocal - pointerLocal).sqrMagnitude;
|
||||
float radius = SlotCatchRadius(srt);
|
||||
if (sqr <= radius * radius && sqr < bestSqr)
|
||||
{
|
||||
bestSqr = sqr;
|
||||
best = s;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return best;
|
||||
}
|
||||
|
||||
// Catch radius for one slot, in PaperRoot local units: half the slot's diagonal
|
||||
// (its size mapped into parent space, so any slot rotation/mirror/scale is
|
||||
// accounted for) times the config multiplier. Scales per slot — not one flat
|
||||
// distance for all.
|
||||
private float SlotCatchRadius(RectTransform slot)
|
||||
{
|
||||
Vector2 size = slot.rect.size;
|
||||
Vector2 prX = _parentRect.InverseTransformVector(slot.TransformVector(new Vector3(size.x, 0f, 0f)));
|
||||
Vector2 prY = _parentRect.InverseTransformVector(slot.TransformVector(new Vector3(0f, size.y, 0f)));
|
||||
float halfDiagonal = 0.5f * new Vector2(prX.magnitude, prY.magnitude).magnitude;
|
||||
return halfDiagonal * _cfg.CatchRadiusScale;
|
||||
}
|
||||
|
||||
private void AnimatePreviewPose(bool toSlot)
|
||||
|
||||
@@ -11,6 +11,7 @@ using Darkmatter.Core.Data.Signals.Features.GameplayFlow; // DrawingCompletedSig
|
||||
using Darkmatter.Core.Data.Signals.Features.ShapeBuilder; // ShapeBuilderStarted/PieceSnapped/ShapeAssembled
|
||||
using Darkmatter.Core.Data.Signals.Features.Tutorial;
|
||||
using Darkmatter.Core.Data.Static.Features.Tutorial;
|
||||
using Darkmatter.Core.Enums.Features.Tutorial; // BubblePlacement
|
||||
using Darkmatter.Features.Coloring.UI; // ColorButton, ColorRegionView
|
||||
using Darkmatter.Features.DrawingCatalog; // DrawingCatalogButton
|
||||
using Darkmatter.Features.GameplayFlow.UI; // NextButtonView
|
||||
@@ -44,6 +45,7 @@ namespace Darkmatter.Features.Tutorial.Systems
|
||||
private CancellationTokenSource _runCts;
|
||||
private CancellationToken _ct;
|
||||
private bool _completed;
|
||||
private bool _drawingCompleted;
|
||||
private bool _suspended;
|
||||
private Action _reshow;
|
||||
private int _stepIndex;
|
||||
@@ -74,6 +76,10 @@ namespace Darkmatter.Features.Tutorial.Systems
|
||||
_navSubs.Add(_bus.Subscribe<ReturnToMainMenuSignal>(_ => Suspend()));
|
||||
_navSubs.Add(_bus.Subscribe<OpenColorBookSignal>(_ => Resume()));
|
||||
_navSubs.Add(_bus.Subscribe<DrawingCatalogReadySignal>(OnCatalogReadyGlobal));
|
||||
// The drawing being completed (Next pressed) is the run's true end — even if the child
|
||||
// finds Next early during free-paint. Flag it so the catalog reload that follows isn't
|
||||
// mistaken for a Back-navigation and doesn't restart the tutorial.
|
||||
_navSubs.Add(_bus.Subscribe<DrawingCompletedSignal>(_ => _drawingCompleted = true));
|
||||
|
||||
StartRun(skipCatalogWait: false);
|
||||
}
|
||||
@@ -86,6 +92,7 @@ namespace Darkmatter.Features.Tutorial.Systems
|
||||
_ct = _runCts.Token;
|
||||
_gen++;
|
||||
_suspended = false;
|
||||
_drawingCompleted = false;
|
||||
_reshow = null;
|
||||
_stepIndex = 0;
|
||||
_overlay.HideInstant();
|
||||
@@ -108,11 +115,11 @@ namespace Darkmatter.Features.Tutorial.Systems
|
||||
}
|
||||
|
||||
// The catalog re-appeared while we were mid-gameplay -> the player went Back. Restart from
|
||||
// step 1 (the catalog is already on screen, so skip the wait). Excludes step 6+, whose own
|
||||
// completion loads the catalog.
|
||||
// step 1 (the catalog is already on screen, so skip the wait). Excludes step 7+ (the Next
|
||||
// press), whose own completion loads the catalog.
|
||||
private void OnCatalogReadyGlobal(DrawingCatalogReadySignal _)
|
||||
{
|
||||
if (!_completed && _stepIndex >= 2 && _stepIndex <= 5)
|
||||
if (!_completed && !_drawingCompleted && _stepIndex >= 2 && _stepIndex <= 6)
|
||||
StartRun(skipCatalogWait: true);
|
||||
}
|
||||
|
||||
@@ -154,7 +161,7 @@ namespace Darkmatter.Features.Tutorial.Systems
|
||||
if (!await WaitForSignalAsync<DrawingCatalogReadySignal>()) return;
|
||||
}
|
||||
await UniTask.NextFrame(_ct);
|
||||
ShowStep(() => ShowBlockingTap(FindFirstCatalogCell(), _config.PickText));
|
||||
ShowStep(() => ShowBlockingTap(FindFirstCatalogCell(), _config.PickText, _config.PickBubble, _config.PickBubbleOffset));
|
||||
if (!await WaitForActionAsync<DrawingSelectedSignal>()) return;
|
||||
EndStep("pick", 1);
|
||||
|
||||
@@ -165,7 +172,7 @@ namespace Darkmatter.Features.Tutorial.Systems
|
||||
ShowStep(() =>
|
||||
{
|
||||
var (pieceRect, slotRect) = FindFirstPieceAndSlot();
|
||||
if (pieceRect != null) _overlay.ShowDrag(pieceRect, slotRect, _config.DragText);
|
||||
if (pieceRect != null) _overlay.ShowDrag(pieceRect, slotRect, _config.DragText, _config.DragBubble, _config.DragBubbleOffset);
|
||||
else Debug.LogWarning("[Tutorial] No draggable piece found for the drag step.");
|
||||
});
|
||||
if (!await WaitForActionAsync<PieceSnappedSignal>()) return; // any snap teaches the gesture
|
||||
@@ -173,7 +180,7 @@ namespace Darkmatter.Features.Tutorial.Systems
|
||||
|
||||
// Step 3 — finish the rest of the puzzle freely (non-blocking hint).
|
||||
_stepIndex = 3;
|
||||
ShowStep(() => _overlay.ShowTap(null, _config.FinishText, blockInput: false));
|
||||
ShowStep(() => _overlay.ShowTap(null, _config.FinishText, blockInput: false, _config.FinishBubble, _config.FinishBubbleOffset));
|
||||
if (!await WaitForActionAsync<ShapeAssembledSignal>()) return;
|
||||
EndStep("finish", 3);
|
||||
|
||||
@@ -181,32 +188,55 @@ namespace Darkmatter.Features.Tutorial.Systems
|
||||
_stepIndex = 4;
|
||||
if (!await WaitForSignalAsync<RegionsInitializedSignal>()) return;
|
||||
await UniTask.NextFrame(_ct);
|
||||
ShowStep(() => ShowBlockingTap(FindFirstColorButton(), _config.ColorText));
|
||||
ShowStep(() => ShowBlockingTap(FindFirstColorButton(), _config.ColorText, _config.ColorBubble, _config.ColorBubbleOffset));
|
||||
if (!await WaitForActionAsync<ColorSelectedSignal>()) return;
|
||||
EndStep("color", 4);
|
||||
|
||||
// Step 5 — paint a region.
|
||||
// Step 5 — paint the first region (teaches the tap by spotlighting one region).
|
||||
_stepIndex = 5;
|
||||
ShowStep(() => ShowBlockingTap(FindLargestRegion(), _config.PaintText));
|
||||
// Watch full-colour completion across both this taught tap and the free-paint step that
|
||||
// follows. A one-region drawing is finished by this very tap, so its signal would fire
|
||||
// before step 6 could subscribe — the flag lets us skip the free-paint step instead of
|
||||
// stranding the child on a hint with nothing left to colour.
|
||||
var fullyColored = false;
|
||||
using var fullColorSub = _bus.Subscribe<AllRegionsColoredSignal>(_ => fullyColored = true);
|
||||
ShowStep(() => ShowBlockingTap(FindLargestRegion(), _config.PaintText, _config.PaintBubble, _config.PaintBubbleOffset));
|
||||
if (!await WaitForActionAsync<ColorAppliedSignal>()) return;
|
||||
EndStep("paint", 5);
|
||||
|
||||
// Step 6 — press Next.
|
||||
// Step 6 — colour the rest of the picture freely (non-blocking hint). Hold here until
|
||||
// every region is filled, so the "Tap Next" prompt only appears once colouring is done.
|
||||
// The child can still reach the live Next button during this open step; if they finish
|
||||
// the drawing early that way, take it as done rather than waiting on a fill that can't come.
|
||||
_stepIndex = 6;
|
||||
await UniTask.NextFrame(_ct);
|
||||
ShowStep(() => ShowBlockingTap(FindNextButton(), _config.NextText));
|
||||
if (!await WaitForActionAsync<DrawingCompletedSignal>()) return;
|
||||
EndStep("next", 6);
|
||||
if (!fullyColored)
|
||||
{
|
||||
ShowStep(() => _overlay.ShowTap(null, _config.FinishColoringText, blockInput: false, _config.FinishColoringBubble, _config.FinishColoringBubbleOffset));
|
||||
await UniTask.WhenAny(
|
||||
WaitForActionAsync<AllRegionsColoredSignal>(),
|
||||
WaitForActionAsync<DrawingCompletedSignal>());
|
||||
}
|
||||
EndStep("finishColoring", 6);
|
||||
|
||||
// Step 7 — celebrate.
|
||||
_stepIndex = 7;
|
||||
await _overlay.ShowToastAsync(_config.DoneText, _ct);
|
||||
// Step 7 — press Next (skipped if the drawing was already completed early).
|
||||
if (!_drawingCompleted)
|
||||
{
|
||||
_stepIndex = 7;
|
||||
await UniTask.NextFrame(_ct);
|
||||
ShowStep(() => ShowBlockingTap(FindNextButton(), _config.NextText, _config.NextBubble, _config.NextBubbleOffset));
|
||||
if (!await WaitForActionAsync<DrawingCompletedSignal>()) return;
|
||||
EndStep("next", 7);
|
||||
}
|
||||
|
||||
// Step 8 — celebrate.
|
||||
_stepIndex = 8;
|
||||
await _overlay.ShowToastAsync(_config.DoneText, _ct, _config.DoneBubble, _config.DoneBubbleOffset);
|
||||
}
|
||||
|
||||
private void ShowBlockingTap(RectTransform target, string message)
|
||||
private void ShowBlockingTap(RectTransform target, string message, BubblePlacement placement, Vector2 offset)
|
||||
{
|
||||
if (target == null) Debug.LogWarning($"[Tutorial] No target found for step: \"{message}\"");
|
||||
_overlay.ShowTap(target, message, blockInput: target != null);
|
||||
_overlay.ShowTap(target, message, blockInput: target != null, placement, offset);
|
||||
}
|
||||
|
||||
private void EndStep(string id, int index)
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Threading;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using Darkmatter.Core.Contracts.Features.Tutorial;
|
||||
using Darkmatter.Core.Enums.Features.Tutorial;
|
||||
using PrimeTween;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
@@ -45,6 +46,8 @@ namespace Darkmatter.Features.Tutorial.UI
|
||||
[SerializeField] private float fadeDuration = 0.25f;
|
||||
[SerializeField] private float toastSeconds = 1.6f;
|
||||
[SerializeField] private float bubbleGap = 60f;
|
||||
[Tooltip("Margin (px) from the top/bottom edge for the Top/Bottom bubble placement presets.")]
|
||||
[SerializeField] private float bubbleEdgeMargin = 150f;
|
||||
[SerializeField] private float haloPulseScale = 1.18f;
|
||||
[SerializeField] private float pulseDuration = 0.6f;
|
||||
[SerializeField] private float dragHandDuration = 1.1f;
|
||||
@@ -60,6 +63,8 @@ namespace Darkmatter.Features.Tutorial.UI
|
||||
private RectTransform _area;
|
||||
private Camera _overlayCam;
|
||||
private Mode _mode = Mode.Hidden;
|
||||
private BubblePlacement _placement = BubblePlacement.Auto;
|
||||
private Vector2 _bubbleOffset;
|
||||
private RectTransform _target; // tap target
|
||||
private RectTransform _dragFrom; // the piece (drag start)
|
||||
private RectTransform _dragTo; // the slot (drag destination)
|
||||
@@ -91,11 +96,13 @@ namespace Darkmatter.Features.Tutorial.UI
|
||||
|
||||
// ── ITutorialOverlay ─────────────────────────────────────────────────
|
||||
|
||||
public void ShowTap(RectTransform target, string message, bool blockInput)
|
||||
public void ShowTap(RectTransform target, string message, bool blockInput, BubblePlacement placement = BubblePlacement.Auto, Vector2 offset = default)
|
||||
{
|
||||
CacheRefs();
|
||||
KillAnims();
|
||||
SetText(message);
|
||||
_placement = placement;
|
||||
_bubbleOffset = offset;
|
||||
_dragFrom = null;
|
||||
_dragTo = null;
|
||||
|
||||
@@ -122,11 +129,13 @@ namespace Darkmatter.Features.Tutorial.UI
|
||||
FadeInQuick();
|
||||
}
|
||||
|
||||
public void ShowDrag(RectTransform from, RectTransform to, string message)
|
||||
public void ShowDrag(RectTransform from, RectTransform to, string message, BubblePlacement placement = BubblePlacement.Auto, Vector2 offset = default)
|
||||
{
|
||||
CacheRefs();
|
||||
KillAnims();
|
||||
SetText(message);
|
||||
_placement = placement;
|
||||
_bubbleOffset = offset;
|
||||
|
||||
// No dim and no blocking — the piece lives under the overlay and must stay visible and
|
||||
// draggable. Halo + hand travel together along the piece -> slot path, recomputed live.
|
||||
@@ -153,11 +162,13 @@ namespace Darkmatter.Features.Tutorial.UI
|
||||
FadeInQuick();
|
||||
}
|
||||
|
||||
public async UniTask ShowToastAsync(string message, CancellationToken ct)
|
||||
public async UniTask ShowToastAsync(string message, CancellationToken ct, BubblePlacement placement = BubblePlacement.Auto, Vector2 offset = default)
|
||||
{
|
||||
CacheRefs();
|
||||
KillAnims();
|
||||
SetText(message);
|
||||
_placement = placement;
|
||||
_bubbleOffset = offset;
|
||||
|
||||
_mode = Mode.Centered;
|
||||
_target = null;
|
||||
@@ -238,8 +249,10 @@ namespace Darkmatter.Features.Tutorial.UI
|
||||
if (hand != null)
|
||||
hand.anchoredPosition = centerLocal + new Vector2(0f, -radiusLocal * 0.5f);
|
||||
|
||||
PositionBubble(centerLocal.y, centerLocal.y + radiusLocal, centerLocal.y - radiusLocal,
|
||||
_area.rect.height * 0.5f);
|
||||
if (_placement == BubblePlacement.Auto)
|
||||
PositionBubble(centerLocal.y, centerLocal.y + radiusLocal, centerLocal.y - radiusLocal);
|
||||
else
|
||||
PositionBubblePreset(_placement);
|
||||
}
|
||||
|
||||
// Drag: halo + hand glide piece -> slot, recomputed live so it tracks the real positions.
|
||||
@@ -283,27 +296,59 @@ namespace Darkmatter.Features.Tutorial.UI
|
||||
|
||||
// The drag path spans the play area, so pin the bubble to a screen edge instead of hugging
|
||||
// the piece — keeps it off the shapes.
|
||||
PositionBubbleAtEdge(dragBubbleAtTop, dragBubbleEdgeMargin);
|
||||
if (_placement == BubblePlacement.Auto)
|
||||
PositionBubbleAtEdge(dragBubbleAtTop, dragBubbleEdgeMargin);
|
||||
else
|
||||
PositionBubblePreset(_placement);
|
||||
}
|
||||
|
||||
private void LayoutCentered()
|
||||
{
|
||||
if (bubbleRoot == null) return;
|
||||
if (_placement != BubblePlacement.Auto) { PositionBubblePreset(_placement); return; }
|
||||
float halfH = _area.rect.height * 0.5f;
|
||||
float bubbleHalf = bubbleRoot.rect.height * 0.5f;
|
||||
bubbleRoot.anchoredPosition =
|
||||
new Vector2(0f, Mathf.Clamp(halfH * 0.45f, -halfH + bubbleHalf, halfH - bubbleHalf));
|
||||
SetBubbleAnchored(new Vector2(0f, halfH * 0.45f));
|
||||
}
|
||||
|
||||
private void PositionBubble(float holeCenterY, float holeTopY, float holeBottomY, float halfH)
|
||||
// Applies the per-step offset to a computed base position and clamps so the bubble always
|
||||
// stays fully on-screen, then commits it. Every bubble-positioning path routes through here.
|
||||
private void SetBubbleAnchored(Vector2 baseLocal)
|
||||
{
|
||||
if (bubbleRoot == null) return;
|
||||
float halfW = _area.rect.width * 0.5f;
|
||||
float halfH = _area.rect.height * 0.5f;
|
||||
float bubbleHalfW = bubbleRoot.rect.width * 0.5f;
|
||||
float bubbleHalfH = bubbleRoot.rect.height * 0.5f;
|
||||
Vector2 p = baseLocal + _bubbleOffset;
|
||||
p.x = Mathf.Clamp(p.x, -halfW + bubbleHalfW, halfW - bubbleHalfW);
|
||||
p.y = Mathf.Clamp(p.y, -halfH + bubbleHalfH, halfH - bubbleHalfH);
|
||||
bubbleRoot.anchoredPosition = p;
|
||||
}
|
||||
|
||||
private void PositionBubble(float holeCenterY, float holeTopY, float holeBottomY)
|
||||
{
|
||||
if (bubbleRoot == null) return;
|
||||
float bubbleHalf = bubbleRoot.rect.height * 0.5f;
|
||||
float y = holeCenterY <= 0f
|
||||
? holeTopY + bubbleGap + bubbleHalf
|
||||
: holeBottomY - bubbleGap - bubbleHalf;
|
||||
y = Mathf.Clamp(y, -halfH + bubbleHalf, halfH - bubbleHalf);
|
||||
bubbleRoot.anchoredPosition = new Vector2(0f, y);
|
||||
SetBubbleAnchored(new Vector2(0f, y));
|
||||
}
|
||||
|
||||
// Fixed-slot placement chosen per step (Top/Center/Bottom). Horizontally centred; clamped so
|
||||
// the bubble always stays fully on-screen.
|
||||
private void PositionBubblePreset(BubblePlacement placement)
|
||||
{
|
||||
if (bubbleRoot == null) return;
|
||||
float halfH = _area.rect.height * 0.5f;
|
||||
float bubbleHalf = bubbleRoot.rect.height * 0.5f;
|
||||
float y = placement switch
|
||||
{
|
||||
BubblePlacement.Top => halfH - bubbleHalf - bubbleEdgeMargin,
|
||||
BubblePlacement.Bottom => -halfH + bubbleHalf + bubbleEdgeMargin,
|
||||
_ => 0f, // Center
|
||||
};
|
||||
SetBubbleAnchored(new Vector2(0f, y));
|
||||
}
|
||||
|
||||
// Pins the bubble to the top or bottom edge (used for the drag step, whose target spans the
|
||||
@@ -314,8 +359,7 @@ namespace Darkmatter.Features.Tutorial.UI
|
||||
float halfH = _area.rect.height * 0.5f;
|
||||
float bubbleHalf = bubbleRoot.rect.height * 0.5f;
|
||||
float y = top ? halfH - bubbleHalf - margin : -halfH + bubbleHalf + margin;
|
||||
y = Mathf.Clamp(y, -halfH + bubbleHalf, halfH - bubbleHalf);
|
||||
bubbleRoot.anchoredPosition = new Vector2(0f, y);
|
||||
SetBubbleAnchored(new Vector2(0f, y));
|
||||
}
|
||||
|
||||
// ── Coordinate conversion (camera-agnostic) ──────────────────────────
|
||||
@@ -405,6 +449,8 @@ namespace Darkmatter.Features.Tutorial.UI
|
||||
private void ApplyHiddenState()
|
||||
{
|
||||
_mode = Mode.Hidden;
|
||||
_placement = BubblePlacement.Auto;
|
||||
_bubbleOffset = Vector2.zero;
|
||||
_target = null;
|
||||
_dragFrom = null;
|
||||
_dragTo = null;
|
||||
|
||||
Reference in New Issue
Block a user