Unity中如何点击GameObject创建类似Windows右键的悬浮菜单?
Got it, let's fix this context menu issue for your agent GameObject. The problem with your initial approach is likely a mix of incorrect input timing, missing collider setup, or mishandling GUI coordinates. Here's a complete, working solution—first with IMGUI (since you started with OnGUI) and then a better UGUI approach for modern Unity projects:
IMGUI Solution (Using OnGUI/OnMouseOver)
First, make sure your agent GameObject has a non-trigger Collider (like BoxCollider or SphereCollider) attached—OnMouseOver only works if there's a collider to detect the mouse hover. Then use this script:
using UnityEngine; public class AgentContextMenu : MonoBehaviour { private bool _isMouseOverAgent; private bool _showContextMenu; private Vector2 _menuPosition; private void OnMouseOver() { _isMouseOverAgent = true; // Trigger menu on left (0) or right (1) mouse button press if (Input.GetMouseButtonDown(0) || Input.GetMouseButtonDown(1)) { _showContextMenu = true; // Convert screen mouse position to GUI coordinates (Y-axis is flipped) _menuPosition = new Vector2(Input.mousePosition.x, Screen.height - Input.mousePosition.y); } } private void OnMouseExit() { _isMouseOverAgent = false; _showContextMenu = false; // Close menu when mouse leaves the agent } private void OnGUI() { if (_showContextMenu) { // Draw the menu window—adjust size/title as needed _menuPosition = GUI.Window(0, new Rect(_menuPosition.x, _menuPosition.y, 150, 110), DrawMenuItems, "Agent Actions"); } } private void DrawMenuItems(int windowId) { // Add your menu options here if (GUILayout.Button("Move to Position")) { Debug.Log("Move command executed!"); _showContextMenu = false; } if (GUILayout.Button("Attack Target")) { Debug.Log("Attack command executed!"); _showContextMenu = false; } GUILayout.FlexibleSpace(); // Push cancel button to bottom if (GUILayout.Button("Cancel")) { _showContextMenu = false; } GUI.DragWindow(); // Allow dragging the menu (optional) } // Close menu when clicking outside of it private void Update() { if (_showContextMenu && Input.GetMouseButtonDown(0) && !_isMouseOverAgent) { _showContextMenu = false; } } }
Key Fixes & Notes:
- Collider Requirement: Without a non-trigger collider,
OnMouseOverwon't fire—this is probably the most common oversight. - Coordinate Conversion:
Input.mousePositionuses a bottom-origin Y-axis, while GUI uses top-origin. The conversion ensures the menu pops up where your mouse is. - Input Timing:
GetMouseButtonDowndetects the exact press moment, so you don't get repeated menu triggers if you hold the button. - Close Logic: The menu closes when you leave the agent, click a menu item, or click outside the menu.
UGUI Solution (Recommended for Modern Projects)
IMGUI is functional but limited for styling/animation. If you're using Unity's UGUI system, this approach is more flexible:
Set Up the Menu Prefab:
- Create a Canvas, add a Panel (style it like a context menu: add a background image, round corners, etc.).
- Add Button components inside the Panel for your menu options (e.g., "Move", "Attack").
- Disable the Panel initially (toggle the active checkbox in the Inspector).
- Save this as a prefab.
Attach This Script to Your Agent:
using UnityEngine; using UnityEngine.UI; public class AgentUGUIContMenu : MonoBehaviour { [SerializeField] private GameObject _contextMenuPrefab; // Assign your prefab here private GameObject _activeMenu; private bool _isMouseOverAgent; private void OnMouseOver() { _isMouseOverAgent = true; if (Input.GetMouseButtonDown(0) || Input.GetMouseButtonDown(1)) { // Clean up existing menu if it exists if (_activeMenu != null) Destroy(_activeMenu); // Spawn the menu under the canvas _activeMenu = Instantiate(_contextMenuPrefab, FindObjectOfType<Canvas>().transform); // Position the menu at the mouse location RectTransform menuRect = _activeMenu.GetComponent<RectTransform>(); Vector2 screenPos = Input.mousePosition; RectTransformUtility.ScreenPointToLocalPointInRectangle( FindObjectOfType<Canvas>().transform as RectTransform, screenPos, Camera.main, out Vector2 localPos ); menuRect.localPosition = localPos; // Bind actions to buttons (adjust names to match your prefab's buttons) _activeMenu.transform.Find("MoveBtn").GetComponent<Button>().onClick.AddListener(() => { Debug.Log("Move via UGUI menu!"); Destroy(_activeMenu); }); _activeMenu.transform.Find("AttackBtn").GetComponent<Button>().onClick.AddListener(() => { Debug.Log("Attack via UGUI menu!"); Destroy(_activeMenu); }); } } private void OnMouseExit() { _isMouseOverAgent = false; if (_activeMenu != null) Destroy(_activeMenu); } // Close menu when clicking outside of it private void Update() { if (_activeMenu == null) return; if (Input.GetMouseButtonDown(0) && !_isMouseOverAgent) { // Check if the click is outside the menu bounds if (!RectTransformUtility.RectangleContainsScreenPoint( _activeMenu.GetComponent<RectTransform>(), Input.mousePosition, Camera.main)) { Destroy(_activeMenu); } } } }
Why UGUI is Better:
- You can fully customize the menu's look (colors, fonts, animations) using Unity's UI system.
- It supports responsive design and works better with other UI elements in your project.
- Easier to add complex interactions (like hover effects on buttons).
Common Mistakes You Might Have Made
- Missing Collider: No collider =
OnMouseOvernever triggers. - Incorrect Coordinates: Forgetting to flip the Y-axis leads to the menu appearing at the bottom of the screen.
- Bad Input Detection: Using
Input.GetMouseButton(continuous press) instead ofGetMouseButtonDown(single press) causes the menu to toggle repeatedly. - No Close Logic: Not handling menu closure when clicking outside or leaving the agent leads to stuck menus.
内容的提问来源于stack exchange,提问作者AmyLiz

