| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413 |
- using UnityEngine;
- using UnityEngine.UI;
- using System.Collections.Generic;
- using System;
- using TMPro;
- /// <summary>
- /// Radial menu (decision wheel) for selecting battle actions
- /// </summary>
- public class BattleActionWheel : MonoBehaviour
- {
- [Header("UI References")]
- public Canvas wheelCanvas;
- public GameObject wheelBackground;
- public GameObject actionButtonPrefab;
- public Transform wheelCenter;
- [Header("Wheel Settings")]
- public float wheelRadius = 100f;
- public float buttonSize = 60f;
- public float animationDuration = 0.3f;
- public AnimationCurve scaleCurve = AnimationCurve.EaseInOut(0f, 0f, 1f, 1f);
- [Header("Action Icons")]
- public Sprite moveIcon;
- public Sprite attackIcon;
- public Sprite itemIcon;
- public Sprite spellIcon;
- public Sprite runAwayIcon;
- public Sprite defendIcon;
- public Sprite waitIcon;
- [Header("Colors")]
- public Color defaultColor = Color.white;
- public Color hoverColor = Color.yellow;
- public Color disabledColor = Color.gray;
- private List<ActionButton> actionButtons = new List<ActionButton>();
- private Character currentCharacter;
- private bool isVisible = false;
- private Vector3 worldPosition;
- private Camera mainCamera;
- // Events
- public event Action<BattleActionType> OnActionSelected;
- public event Action OnWheelClosed;
- [System.Serializable]
- private class ActionButton
- {
- public GameObject buttonObject;
- public Button button;
- public Image icon;
- public TextMeshProUGUI label;
- public BattleActionType actionType;
- public bool isEnabled = true;
- public void SetEnabled(bool enabled)
- {
- isEnabled = enabled;
- button.interactable = enabled;
- icon.color = enabled ? Color.white : Color.gray;
- }
- public void SetHighlight(bool highlighted, Color normalColor, Color highlightColor)
- {
- icon.color = highlighted ? highlightColor : (isEnabled ? normalColor : Color.gray);
- }
- }
- void Awake()
- {
- mainCamera = Camera.main;
- if (wheelCanvas == null)
- wheelCanvas = GetComponentInChildren<Canvas>();
- if (wheelCanvas != null)
- wheelCanvas.worldCamera = mainCamera;
- SetVisible(false);
- }
- void Start()
- {
- InitializeWheel();
- }
- void Update()
- {
- if (isVisible)
- {
- // Update wheel position to follow character or world position
- UpdateWheelPosition();
- // Handle input for closing wheel
- if (Input.GetKeyDown(KeyCode.Escape) || Input.GetMouseButtonDown(1))
- {
- HideWheel();
- }
- }
- }
- private void InitializeWheel()
- {
- if (actionButtonPrefab == null)
- {
- Debug.LogError("BattleActionWheel: Action button prefab not assigned!");
- return;
- }
- CreateActionButtons();
- }
- private void CreateActionButtons()
- {
- // Define actions and their properties
- var actionDefinitions = new[]
- {
- new { type = BattleActionType.Attack, icon = attackIcon, label = "Attack" },
- new { type = BattleActionType.Move, icon = moveIcon, label = "Move" },
- new { type = BattleActionType.UseItem, icon = itemIcon, label = "Use Item" },
- new { type = BattleActionType.CastSpell, icon = spellIcon, label = "Cast Spell" },
- new { type = BattleActionType.Defend, icon = defendIcon, label = "Defend" },
- new { type = BattleActionType.RunAway, icon = runAwayIcon, label = "Run Away" }
- };
- int actionCount = actionDefinitions.Length;
- float angleStep = 360f / actionCount;
- for (int i = 0; i < actionCount; i++)
- {
- var actionDef = actionDefinitions[i];
- float angle = i * angleStep - 90f; // Start from top
- Vector3 position = GetPositionOnCircle(angle, wheelRadius);
- CreateActionButton(actionDef.type, actionDef.icon, actionDef.label, position, i);
- }
- }
- private void CreateActionButton(BattleActionType actionType, Sprite icon, string label, Vector3 position, int index)
- {
- GameObject buttonObj = Instantiate(actionButtonPrefab, wheelCenter);
- buttonObj.name = $"ActionButton_{actionType}";
- // Position the button
- RectTransform rectTransform = buttonObj.GetComponent<RectTransform>();
- if (rectTransform != null)
- {
- rectTransform.anchoredPosition = position;
- rectTransform.sizeDelta = Vector2.one * buttonSize;
- }
- // Get components
- Button button = buttonObj.GetComponent<Button>();
- Image iconImage = buttonObj.transform.Find("Icon")?.GetComponent<Image>();
- TextMeshProUGUI labelText = buttonObj.transform.Find("Label")?.GetComponent<TextMeshProUGUI>();
- // If components don't exist, create them
- if (iconImage == null)
- {
- GameObject iconObj = new GameObject("Icon");
- iconObj.transform.SetParent(buttonObj.transform, false);
- iconImage = iconObj.AddComponent<Image>();
- RectTransform iconRect = iconImage.GetComponent<RectTransform>();
- iconRect.anchorMin = Vector2.zero;
- iconRect.anchorMax = Vector2.one;
- iconRect.offsetMin = Vector2.zero;
- iconRect.offsetMax = Vector2.zero;
- }
- if (labelText == null)
- {
- GameObject labelObj = new GameObject("Label");
- labelObj.transform.SetParent(buttonObj.transform, false);
- labelText = labelObj.AddComponent<TextMeshProUGUI>();
- RectTransform labelRect = labelText.GetComponent<RectTransform>();
- labelRect.anchorMin = new Vector2(0f, -0.5f);
- labelRect.anchorMax = new Vector2(1f, -0.1f);
- labelRect.offsetMin = Vector2.zero;
- labelRect.offsetMax = Vector2.zero;
- labelText.text = label;
- labelText.fontSize = 12;
- labelText.alignment = TextAlignmentOptions.Center;
- }
- // Setup button
- if (icon != null && iconImage != null)
- iconImage.sprite = icon;
- if (labelText != null)
- labelText.text = label;
- // Create action button data
- ActionButton actionButton = new ActionButton
- {
- buttonObject = buttonObj,
- button = button,
- icon = iconImage,
- label = labelText,
- actionType = actionType
- };
- // Setup button events
- if (button != null)
- {
- button.onClick.AddListener(() => OnActionButtonClicked(actionType));
- // Add hover effects
- var eventTrigger = buttonObj.GetComponent<UnityEngine.EventSystems.EventTrigger>();
- if (eventTrigger == null)
- eventTrigger = buttonObj.AddComponent<UnityEngine.EventSystems.EventTrigger>();
- // Hover enter
- var pointerEnter = new UnityEngine.EventSystems.EventTrigger.Entry();
- pointerEnter.eventID = UnityEngine.EventSystems.EventTriggerType.PointerEnter;
- pointerEnter.callback.AddListener((data) => { actionButton.SetHighlight(true, defaultColor, hoverColor); });
- eventTrigger.triggers.Add(pointerEnter);
- // Hover exit
- var pointerExit = new UnityEngine.EventSystems.EventTrigger.Entry();
- pointerExit.eventID = UnityEngine.EventSystems.EventTriggerType.PointerExit;
- pointerExit.callback.AddListener((data) => { actionButton.SetHighlight(false, defaultColor, hoverColor); });
- eventTrigger.triggers.Add(pointerExit);
- }
- actionButtons.Add(actionButton);
- buttonObj.SetActive(false); // Start hidden
- }
- public void ShowWheel(Character character, Vector3 worldPos)
- {
- currentCharacter = character;
- worldPosition = worldPos;
- UpdateActionAvailability();
- SetVisible(true);
- AnimateWheelIn();
- }
- public void HideWheel()
- {
- AnimateWheelOut(() =>
- {
- SetVisible(false);
- OnWheelClosed?.Invoke();
- });
- }
- private void UpdateActionAvailability()
- {
- if (currentCharacter == null) return;
- foreach (var actionButton in actionButtons)
- {
- bool isAvailable = IsActionAvailable(actionButton.actionType);
- actionButton.SetEnabled(isAvailable);
- }
- }
- private bool IsActionAvailable(BattleActionType actionType)
- {
- if (currentCharacter == null) return false;
- switch (actionType)
- {
- case BattleActionType.Attack:
- return currentCharacter.Weapon != null;
- case BattleActionType.Move:
- return true; // Always available
- case BattleActionType.UseItem:
- // Check if character has any items (placeholder - implement item system)
- return HasUsableItems();
- case BattleActionType.CastSpell:
- // Check if character can cast spells (placeholder - implement spell system)
- return HasSpells();
- case BattleActionType.Defend:
- case BattleActionType.RunAway:
- return true; // Usually always available
- default:
- return true;
- }
- }
- private bool HasUsableItems()
- {
- // Placeholder implementation
- // TODO: Check character's inventory for usable items
- return true; // For now, assume characters always have items
- }
- private bool HasSpells()
- {
- // Placeholder implementation
- // TODO: Check character's spell list or mana
- return true; // For now, assume characters can cast spells
- }
- private void OnActionButtonClicked(BattleActionType actionType)
- {
- OnActionSelected?.Invoke(actionType);
- HideWheel();
- }
- private Vector3 GetPositionOnCircle(float angleDegrees, float radius)
- {
- float angleRadians = angleDegrees * Mathf.Deg2Rad;
- return new Vector3(
- Mathf.Cos(angleRadians) * radius,
- Mathf.Sin(angleRadians) * radius,
- 0f
- );
- }
- private void UpdateWheelPosition()
- {
- if (wheelCanvas == null || mainCamera == null) return;
- // Convert world position to screen position, then to canvas position
- Vector3 screenPos = mainCamera.WorldToScreenPoint(worldPosition);
- if (wheelCanvas.renderMode == RenderMode.ScreenSpaceOverlay)
- {
- wheelCenter.position = screenPos;
- }
- else if (wheelCanvas.renderMode == RenderMode.ScreenSpaceCamera)
- {
- Vector2 canvasPos;
- RectTransformUtility.ScreenPointToLocalPointInRectangle(
- wheelCanvas.transform as RectTransform,
- screenPos,
- wheelCanvas.worldCamera,
- out canvasPos
- );
- wheelCenter.localPosition = canvasPos;
- }
- }
- private void SetVisible(bool visible)
- {
- isVisible = visible;
- if (wheelCanvas != null)
- wheelCanvas.gameObject.SetActive(visible);
- }
- private void AnimateWheelIn()
- {
- // Simple scale animation using Unity's built-in animation
- if (wheelCenter != null)
- {
- wheelCenter.localScale = Vector3.zero;
- StartCoroutine(AnimateScale(wheelCenter, Vector3.zero, Vector3.one, animationDuration, () =>
- {
- // Show all buttons after animation
- foreach (var button in actionButtons)
- {
- button.buttonObject.SetActive(true);
- }
- }));
- }
- }
- private void AnimateWheelOut(Action onComplete = null)
- {
- // Hide buttons first
- foreach (var button in actionButtons)
- {
- button.buttonObject.SetActive(false);
- }
- if (wheelCenter != null)
- {
- StartCoroutine(AnimateScale(wheelCenter, Vector3.one, Vector3.zero, animationDuration * 0.5f, onComplete));
- }
- else
- {
- onComplete?.Invoke();
- }
- }
- private System.Collections.IEnumerator AnimateScale(Transform target, Vector3 from, Vector3 to, float duration, Action onComplete = null)
- {
- float elapsed = 0f;
- while (elapsed < duration)
- {
- elapsed += Time.deltaTime;
- float t = elapsed / duration;
- // Apply easing curve if available
- if (scaleCurve != null && scaleCurve.keys.Length > 0)
- {
- t = scaleCurve.Evaluate(t);
- }
- target.localScale = Vector3.Lerp(from, to, t);
- yield return null;
- }
- target.localScale = to;
- onComplete?.Invoke();
- }
- void OnDestroy()
- {
- // Clean up any running coroutines
- StopAllCoroutines();
- }
- }
|