Created
August 24, 2025 09:23
-
-
Save adammyhre/382802233e66356c1bd4fe9183da2bab to your computer and use it in GitHub Desktop.
Unity 6.2 World Space UI
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using System.Collections.Generic; | |
| using TMPro; | |
| using Unity.Cinemachine; | |
| using UnityEngine; | |
| using UnityEngine.UI; | |
| using UnityUtils; | |
| [DisallowMultipleComponent] | |
| public class EnemyWorldUI : MonoBehaviour { | |
| public enum FacingTarget { Camera, Player, Hybrid } | |
| [Header("References")] | |
| public Canvas canvas; | |
| public TMP_Dropdown dropdown; | |
| public Button button; | |
| [Header("Behavior")] | |
| public Transform player; | |
| public Camera uiCamera; | |
| [Min(0f)] public float triggerRadius = 5f; | |
| public FacingTarget faceTarget = FacingTarget.Camera; | |
| public string[] dropdownOptions = { "Talk", "Trade", "Inspect" }; | |
| [Header("Cinemachine")] | |
| public CinemachineInputAxisController inputAxisController; | |
| [Header("Group Easing")] | |
| public CinemachineTargetGroup targetGroup; | |
| [Min(0f)] public float groupWeight = 0.25f; | |
| [Min(0f)] public float groupRadius = 0.5f; | |
| float targetWeight, currentWeight, weightVelocity; | |
| float targetRadius, currentRadius, radiusVelocity; | |
| int targetGroupIndex = -1; | |
| [Header("UI Fade")] | |
| CanvasGroup canvasGroup; | |
| bool pendingHide; | |
| public float fadeSmoothTime = 0.2f; | |
| float targetAlpha, currentAlpha, alphaVelocity; | |
| SphereCollider trigger; | |
| EnemyMover mover; | |
| Action targetAction; | |
| void Start() { | |
| if (!player) player = GameObject.FindGameObjectWithTag("Player").transform; | |
| if (!uiCamera) uiCamera = Camera.main; | |
| trigger = gameObject.GetOrAdd<SphereCollider>(); | |
| trigger.isTrigger = true; | |
| trigger.radius = triggerRadius; | |
| mover = gameObject.GetOrAdd<EnemyMover>(); | |
| inputAxisController = player.GetComponentInChildren<CinemachineInputAxisController>(); | |
| UpdateTargetAction(); | |
| } | |
| void UpdateTargetAction() { | |
| targetAction = faceTarget switch { | |
| FacingTarget.Camera => FaceCamera, | |
| FacingTarget.Player => FacePlayer, | |
| FacingTarget.Hybrid => FaceHybrid, | |
| _ => null | |
| }; | |
| } | |
| void Update() { | |
| UpdateTargetAction(); | |
| if (targetGroup && canvas) { | |
| currentWeight = Mathf.SmoothDamp(currentWeight, targetWeight, ref weightVelocity, 0.25f); | |
| currentRadius = Mathf.SmoothDamp(currentRadius, targetRadius, ref radiusVelocity, 0.25f); | |
| ApplyGroupInfluence(currentWeight, currentRadius); | |
| } | |
| if (canvasGroup) { | |
| currentAlpha = Mathf.SmoothDamp(currentAlpha, targetAlpha, ref alphaVelocity, fadeSmoothTime); | |
| canvasGroup.alpha = currentAlpha; | |
| bool interact = currentAlpha > 0.5f; | |
| canvasGroup.interactable = interact; | |
| canvasGroup.blocksRaycasts = interact; | |
| } | |
| if (pendingHide) { | |
| bool alphaDone = !canvasGroup || currentAlpha <= 0.01f || Mathf.Approximately(currentAlpha, 0f); | |
| if (alphaDone) { | |
| canvas.gameObject.SetActive(false); | |
| mover.Resume(); | |
| inputAxisController.enabled = true; | |
| pendingHide = false; | |
| } | |
| } | |
| } | |
| void FacePlayer() { | |
| if (!canvas || !player) return; | |
| var toPlayer = canvas.transform.position - player.position; | |
| var flat = Vector3.ProjectOnPlane(toPlayer, Vector3.up); // pitch=0, roll=0 | |
| if (flat.sqrMagnitude <= Vector3.kEpsilon) return; | |
| canvas.transform.rotation = Quaternion.LookRotation(flat, Vector3.up); | |
| } | |
| void FaceHybrid() { | |
| if (!canvas || !uiCamera) return; | |
| var camFwdFlat = Vector3.ProjectOnPlane(uiCamera.transform.forward, Vector3.up); | |
| if (camFwdFlat.sqrMagnitude <= Vector3.kEpsilon) return; | |
| canvas.transform.rotation = Quaternion.LookRotation(camFwdFlat, Vector3.up); | |
| } | |
| void FaceCamera() { | |
| if (!canvas || !uiCamera) return; | |
| var toCam = canvas.transform.position - uiCamera.transform.position; | |
| canvas.transform.rotation = Quaternion.LookRotation(toCam, Vector3.up); | |
| } | |
| void LateUpdate() { | |
| if (canvas.gameObject.activeSelf) targetAction?.Invoke(); | |
| } | |
| void OnTriggerEnter(Collider other) { | |
| if (!other.CompareTag("Player")) return; | |
| ShowUI(); | |
| } | |
| void OnTriggerExit(Collider other) { | |
| if (!other.CompareTag("Player")) return; | |
| HideUI(); | |
| } | |
| void ApplyGroupInfluence(float weight, float radius) { | |
| if (!targetGroup) return; | |
| int idx = targetGroupIndex >= 0 ? targetGroupIndex : targetGroup.FindMember(canvas.transform); | |
| if (idx < 0) return; | |
| var targets = targetGroup.Targets; | |
| var t = targets[idx]; | |
| t.Weight = weight; | |
| t.Radius = radius; | |
| targets[idx] = t; | |
| targetGroup.Targets = targets; | |
| targetGroupIndex = idx; | |
| } | |
| void ShowUI() { | |
| canvas.gameObject.SetActive(true); | |
| mover.Pause(); | |
| inputAxisController.enabled = false; | |
| targetWeight = groupWeight; | |
| targetRadius = groupRadius; | |
| targetAlpha = 1f; | |
| } | |
| void HideUI() { | |
| targetWeight = 0f; | |
| targetRadius = 0f; | |
| targetAlpha = 0f; | |
| pendingHide = true; | |
| } | |
| void OnEnable() { | |
| if (!canvas) { | |
| Debug.LogError("[EnemyEmbeddedUI] Canvas reference missing.", this); | |
| return; | |
| } | |
| canvas.renderMode = RenderMode.WorldSpace; | |
| canvas.worldCamera = uiCamera; | |
| canvas.gameObject.SetActive(false); | |
| if (!canvasGroup) canvas.TryGetComponent(out canvasGroup); | |
| if (canvasGroup) { | |
| currentAlpha = targetAlpha = 0f; | |
| alphaVelocity = 0f; | |
| canvasGroup.alpha = 0f; | |
| canvasGroup.interactable = false; | |
| canvasGroup.blocksRaycasts = false; | |
| } | |
| if (dropdown) { | |
| if (!dropdown.template) { | |
| dropdown.template = dropdown.transform.Find("Template") as RectTransform; | |
| } | |
| if (dropdown.template.gameObject.activeSelf) dropdown.template.gameObject.SetActive(false); | |
| dropdown.onValueChanged.RemoveAllListeners(); | |
| dropdown.options.Clear(); | |
| foreach (var o in dropdownOptions) dropdown.options.Add(new TMP_Dropdown.OptionData(o)); | |
| dropdown.onValueChanged.AddListener(i => Debug.Log($"[{name}] {dropdown.options[i].text}")); | |
| dropdown.RefreshShownValue(); | |
| } | |
| if (button) { | |
| button.onClick.RemoveAllListeners(); | |
| button.onClick.AddListener(() => Debug.Log($"[{name}] Click me")); | |
| } | |
| if (targetGroup && canvas) { | |
| targetGroup.AddMember(canvas.transform, 0f, 0f); | |
| currentWeight = targetWeight = 0f; | |
| currentRadius = targetRadius = 0f; | |
| weightVelocity = radiusVelocity = 0f; | |
| targetGroupIndex = targetGroup.FindMember(canvas.transform); | |
| } | |
| } | |
| void OnDisable() { | |
| HideUI(); | |
| if (targetGroup && canvas) { | |
| targetGroup.RemoveMember(canvas.transform); | |
| targetGroupIndex = -1; | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment