为什么同一个对象会调用不同的两个事件方法? (鼠标点击-拖动-取消点击)

Why would supposedly different two event methods be called by the same object? (Mouse Click-Drag-Unclick)

版本:Unity3D 4.6.2f1

我有一个方块网格(附有 Tile 脚本的立方体 GameObjects)。它们位于它们的索引位置 [x][y] = (x, y, z=0) World Coordinates.

我正在尝试实现类似于扫雷器的行为:当释放左键时,除非有向下的右键单击,否则会显示释放鼠标的瓦片。下面,我尝试获取释放鼠标左键的图块的坐标。

进入代码之前:

  • OnMouseDown() - OnMouseDown is called when the user has pressed the mouse button while over the GUIElement or Collider.
  • OnMouseUp() - OnMouseUp is called when the user has released the mouse button.

根据这些定义,我假设(错误?)OnMouseUp() 会被其上方的对象调用,鼠标左键单击被释放。

Tile 脚本中,我有以下内容:

public class Tile : MonoBehaviour
{
    // variables
    private Vector2 _gridPosition = Vector2.zero;

    public Vector2 GridPosition
    {
        get { return _gridPosition; }
        set { _gridPosition = value; }
    }

    // functions
    string Vec2toText(Vector2 v)
    {
        return String.Format("(" + v.x + ", " + v.y + ")");
    }

    void OnMouseDown()
    {
        Debug.Log("TILE:: ON_MOUSE_DOWN:: " + Vec2toText(_gridPosition));
    }

    void OnMouseUp()
    {
        Debug.Log("TILE:: ON_MOUSE_UP:: " + Vec2toText(_gridPosition));
    }

    void OnMouseEnter()
    {
        Debug.Log("TILE:: MOUSE_ENTER: " + Vec2toText(_gridPosition));
    }

    void OnMouseExit()
    {
        Debug.Log("TILE:: MOUSE_EXIT: " + Vec2toText(_gridPosition));
    }
}

here是输出:

TILE:: MOUSE_ENTER: (15, 15)
TILE:: MOUSE_DOWN: (15, 15)
TILE:: MOUSE_EXIT: (15, 15)
TILE:: MOUSE_ENTER: (14, 15)
TILE:: MOUSE_UP: (15, 15)    %% This line
TILE:: MOUSE_EXIT: (14, 15)

如果我可以在 OnMouseUp() 函数上获得 (14,15),我会直接找到要显示的图块,但显然我做不到。我认为我的目标的解决方法是保留 static bool _isDragging 并在 if 块中的 OnMouseEnter() 中使用它来分配要正确显示的图块。这是——通过同一个对象调用两个函数,即使我按下左键单击某个对象并将其释放到其他对象上? - 故意的?对于这种情况有更好的解决方法吗?

OnMouseDown 仅针对鼠标左键发出。所以你必须自己实现右键单击。

一种选择是在按下按钮时检查光线投射。

void Update()
{
    if (Input.GetMouseButtonDown(1))
    {
        var ray = camera.ScreenPointToRay(Input.mousePosition);
        RaycastHit hitInfo;
        if (Physics.Raycast(ray, out hitInfo))
        {
            var myObject = hitInfo.collider.GetComponent<MyScriptOrSomething>();
            // Do something here
        }
    }
}

另一个选项是使用 OnMouseEnter 检查右键单击。类似于:

void OnMouseOver()
{
    if (Input.GetMouseButtonDown(1))
    {
        // Do something here
    }
}

编辑:

OnMouseUp 调用您单击的磁贴的原因很简单。这种行为在大多数时候是最安全,也是最值得期待的方式。将游戏对象的鼠标事件视为 "user clicked on me" 和 "user released the button after clicked on me".

假设在释放鼠标按钮时为光标下的对象调用 OnMouseUp。想一想从鼠标单击开始并在释放鼠标按钮(如拖放)时完成的操作。然后你会痛苦地发现如果鼠标不再指向你最初点击的对象, OnMouseUp 将不会被调用。您将无法完成操作。你必须自己捕捉鼠标释放事件。

对于您的情况,仅靠 OnMouseUp 和 OnMouseDown 无法满足您的需求。您正在尝试做的事情对您的应用程序来说太具体了。不幸的是,Unity 的输入系统默认不涵盖这种交互。幸运的是 Unity 足够灵活,可以轻松实现这一点。

作为更好的解决方法,不使用 static bool _isDragging,而是使用 void OnMouseOver() 函数:

void OnMouseOver()
{
    if(Input.GetMouseButtonUp(0))  // left click released
        RevealTile();
    if(Input.GetMouseButtonUp(1))  // right click released
        FlagTile();
}