using System.Collections.Generic; using UnityEngine; namespace TKS { /// /// 슬링샷 조작: 아군 유닛을 잡고(누름) 뒤로 당긴 뒤(드래그) 놓으면(드롭) 반대 방향으로 이동·공격. /// 빈 곳 드래그 = 전장 스크롤 · 휠 = 줌 · 미니맵 탭 = 그 지점으로 이동. /// 버튼 없음. 결과 화면에서는 탭으로 계속. 추가 이동만 남았을 때 빈 곳 탭 = 건너뛰기. /// public class SlingshotInput : MonoBehaviour { BattleManager battle; Camera cam; BattleHUD hud; CameraController camCtrl; Unit selected; bool dragging; bool panning; bool emptyPress; bool previewValid; Vector2 lastScreen; float panPixels; Vector2 currentTarget; LineRenderer aimLine; LineRenderer pullLine; SpriteRenderer targetRing; readonly List highlighted = new List(); public Unit Selected { get { return selected; } } float TapPixels { get { return Mathf.Max(8f, Screen.height * 0.012f); } } public void Init(BattleManager b, Camera c, BattleHUD h, CameraController cc) { battle = b; cam = c; hud = h; camCtrl = cc; aimLine = MakeLine("AimLine", 0.08f, new Color(1f, 1f, 1f, 0.9f), 10); pullLine = MakeLine("PullLine", 0.035f, new Color(1f, 1f, 1f, 0.35f), 9); targetRing = ProceduralSprites.NewSprite(transform, "TargetRing", ProceduralSprites.Ring, Color.white, 0.7f, 11); targetRing.enabled = false; } LineRenderer MakeLine(string name, float width, Color color, int order) { var go = new GameObject(name); go.transform.SetParent(transform, false); var lr = go.AddComponent(); lr.useWorldSpace = true; lr.positionCount = 2; lr.startWidth = width; lr.endWidth = width; lr.material = new Material(Shader.Find("Sprites/Default")); lr.startColor = color; lr.endColor = color; lr.sortingOrder = order; lr.numCapVertices = 4; lr.enabled = false; return lr; } Vector2 ScreenToWorld(Vector2 screen) { Vector3 p = screen; p.z = -cam.transform.position.z; return cam.ScreenToWorldPoint(p); } void Update() { if (battle == null || battle.Config == null) return; var phase = battle.Phase; if (phase == BattlePhase.StageClear || phase == BattlePhase.Defeat) { if (Input.GetMouseButtonDown(0)) battle.ContinueAfterResult(); return; } Vector2 screen = Input.mousePosition; if (Input.GetMouseButtonDown(0)) OnPress(screen); else if (Input.GetMouseButton(0)) { if (dragging) OnDrag(ScreenToWorld(screen)); else if (panning) OnPan(screen); } if (Input.GetMouseButtonUp(0)) OnRelease(screen); float wheel = Input.mouseScrollDelta.y; if (Mathf.Abs(wheel) > 0.01f && camCtrl != null) camCtrl.Zoom(1f - wheel * 0.1f); } void OnPress(Vector2 screen) { lastScreen = screen; panPixels = 0f; dragging = false; panning = false; emptyPress = false; previewValid = false; Vector2 mapWorld; if (hud != null && camCtrl != null && hud.TryMinimapTap(screen, out mapWorld)) { camCtrl.FocusOn(mapWorld); return; } Vector2 w = ScreenToWorld(screen); var u = battle.UnitNear(w, Faction.Player); if (u == null) u = battle.UnitNear(w, null); Select(u); if (u != null && battle.CanAct(u)) { dragging = true; } else if (u == null) { panning = true; emptyPress = true; } } void OnPan(Vector2 screen) { Vector2 d = screen - lastScreen; lastScreen = screen; panPixels += d.magnitude; if (panPixels > TapPixels && camCtrl != null) { camCtrl.PanBy(-d * camCtrl.WorldUnitsPerPixel); } } void OnDrag(Vector2 w) { if (selected == null) return; Vector2 pull = w - selected.Position; Vector2 launch = -pull; float mag = launch.magnitude; if (mag < battle.Config.minDragDistance) { HidePreview(); previewValid = false; return; } if (mag > selected.SlingMaxDistance) launch = launch / mag * selected.SlingMaxDistance; currentTarget = battle.Board.ClampInside(selected.Position + launch, selected.Radius); previewValid = true; ShowPreview(selected, currentTarget, w); } void OnRelease(Vector2 screen) { if (dragging && selected != null && previewValid && battle.CanAct(selected)) { var t = currentTarget; var u = selected; HidePreview(); Select(null); battle.RequestPlayerAction(u, t); } else if (emptyPress && panPixels <= TapPixels) { battle.OnEmptyTap(); } else { HidePreview(); } dragging = false; panning = false; emptyPress = false; previewValid = false; } void Select(Unit u) { if (selected != null && selected.View != null) selected.View.SetSelected(false); selected = u; if (u != null && u.View != null) u.View.SetSelected(true); if (hud != null) hud.SetSelected(u); } /// 조준 표시(자동 플레이 캡처에서도 호출). public void ShowPreview(Unit u, Vector2 target, Vector2? pointer) { aimLine.enabled = true; aimLine.SetPosition(0, new Vector3(u.Position.x, u.Position.y, 0f)); aimLine.SetPosition(1, new Vector3(target.x, target.y, 0f)); if (pointer.HasValue) { pullLine.enabled = true; pullLine.SetPosition(0, new Vector3(u.Position.x, u.Position.y, 0f)); pullLine.SetPosition(1, new Vector3(pointer.Value.x, pointer.Value.y, 0f)); } else { pullLine.enabled = false; } targetRing.enabled = true; targetRing.transform.position = new Vector3(target.x, target.y, 0f); bool moveOnly = u.BonusMoveAvailable; var finalHit = moveOnly ? null : Combat.EnemyAtPoint(u, target, battle.Config.finalHitTolerance); targetRing.color = finalHit != null ? new Color(1f, 0.6f, 0.1f) : Color.white; targetRing.transform.localScale = finalHit != null ? new Vector3(0.9f, 0.9f, 1f) : new Vector3(0.7f, 0.7f, 1f); ClearHighlights(); foreach (var e in battle.Resolver.PredictTargets(u, target, moveOnly)) { if (e.View != null) e.View.SetHighlight(true); highlighted.Add(e); } } public void HidePreview() { aimLine.enabled = false; pullLine.enabled = false; targetRing.enabled = false; ClearHighlights(); } void ClearHighlights() { foreach (var h in highlighted) { if (h != null && h.View != null) h.View.SetHighlight(false); } highlighted.Clear(); } } }