简体   繁体   English

在3D空间中具有2D对象的Unity IDropHandler

[英]Unity IDropHandler with 2D objects in 3D space

I have implemented a Unity component which implements IDragHandler, and another that implements IDropHandler. 我已经实现了一个实现IDragHandler的Unity组件,另一个实现了IDropHandler的组件。 The IDragHandler component is placed on an object that I want to drag, but the IDropHandler component is attached to objects that I can drop objects on , not objects that I can drop . 所述IDragHandler组件放置,我要拖动对象上,但IDropHandler组分附着对象,我可以删除对象 ,不,我可以删除对象。

I'm working in 3D with a perspective camera. 我正在使用透视相机进行3D工作。

My code below works fine for dropping 3D objects on 3D objects, and 3D objects on 2D UI objects, but not for dropping 2D UI objects on 3D objects or 2D UI objects on 2D UI Objects. 我的以下代码适用于在3D对象上放置3D对象以及在2D UI对象上放置3D对象,但不适用于将3D对象放在2D UI对象上或将2D UI对象放在2D UI对象上。

The problem I've had is that the object I'm dragging around blocks the OnDrop and takes it for itself due to EventSystem's Raycasting (even if I don't implement IDropHandler on that object). 我遇到的问题是,由于EventSystem的Raycasting(即使我未在该对象上实现IDropHandler),我拖动的对象也会阻塞OnDrop并自行处理。

For 3D objects I temporarily disable the Collider while dragging in my IDragHandler component, to make sure that the Raycast which decides which object to give OnDrop passes by the dragged object and on to the object behind it. 对于3D对象,在拖动IDragHandler组件时,我暂时禁用了对撞机,以确保决定要赋予OnDrop的对象的Raycast可以通过拖动的对象传递到其后面的对象。 However, 2D UI objects don't work with colliders for dragging and dropping, so there's no collider available to disable. 但是,二维UI对象不能与碰撞器一起拖放,因此没有可用的碰撞器可以禁用。 I don't know how to make sure that the EventSystem Raycast bypasses the dragged object to pass on OnDrop to the object behind it. 我不知道如何确保EventSystem Raycast绕过拖动的对象,以将OnDrop传递到它后面的对象。

    public void OnBeginDrag(PointerEventData eventData)
    {
        ...

        if (UIObject)
        {
            // What to do here?
        }
        else
        {
            var collider = gameObject.GetComponent<Collider>();
            collider.enabled = false;
        }

    }

    public void OnDrag(PointerEventData eventData)
    {
        // Move object and stuff
        ...
    }

    public void OnEndDrag(PointerEventData eventData)
    {
        if (UIObject)
        {
            // What to do here?
        }
        else
        {
            var collider = gameObject.GetComponent<Collider>();
            collider.enabled = true;
        }
    }

The things I have tried so far is to: 到目前为止,我尝试过的事情是:

1) Temporarily place the gameObject in the IgnoreRaycast layer 1)暂时将gameObject放置在IgnoreRaycast层中

2) Add a canvas group to the object and temporarily turn off "Block Raycast" on it. 2)将画布组添加到对象,并暂时关闭其上的“阻止射线广播”。

Nothing I do in OnBeginDrag seems to have an effect until I have actually dropped the object for some reason. 在我由于某种原因实际删除对象之前,我在OnBeginDrag中所做的一切似乎没有任何效果。 Try #2 mentioned above resulted in it not turning off "Block Raycast" until after the object was dropped, which caused the object to not even be draggable anymore. 上面提到的尝试#2导致直到放下对象后才关闭“阻止Raycast”,这导致该对象甚至不再可以拖动。

I did a workaround where the dragged object in OnEndDrag raycasts and find if it was dropped on something, and if it was, calls OnDrop on that object. 我做了一个变通方法,在OnEndDrag射线广播中拖动对象,并确定是否将其拖放到某个对象上,如果是,则在该对象上调用OnDrop。 The disabling and enabling of colliders on the 3D object is obsolete now, so it was removed. 现在已禁用和启用3D对象上的碰撞器,因此将其删除。

    public void OnBeginDrag(PointerEventData eventData)
    {
        ...
    }

    public void OnDrag(PointerEventData eventData)
    {
        // Move object and stuff
        ...
    }

    public void OnEndDrag(PointerEventData eventData)
    {
        // Currently using transform.position as raycast origin, could easily be something else if desired.
        var rayCastOrigin = transform.position;

        // Save the current layer the dropped object is in,
        // and then temporarily place the object in the IgnoreRaycast layer to avoid hitting self with Raycast.
        int oldLayer = gameObject.layer;
        gameObject.layer = 2;

        var raycastOriginInScreenSpace = Camera.main.WorldToScreenPoint(rayCastOrigin);
        var screenRay = Camera.main.ScreenPointToRay(new Vector3(raycastOriginInScreenSpace.x, raycastOriginInScreenSpace.y, 0.0f));

        // Perform Physics.Raycast from transform and see if any 3D object was under transform.position on drop.
        RaycastHit hit3D;
        if (Physics.Raycast(screenRay, out hit3D))
        {
            var dropComponent = hit3D.transform.gameObject.GetComponent<IDropHandler>();
            if (dropComponent != null)
                dropComponent.OnDrop(eventData);
        }

        // Perform Physics2D.GetRayIntersection from transform and see if any 2D object was under transform.position on drop.
        RaycastHit2D hit2D = Physics2D.GetRayIntersection(screenRay);
        if (hit2D)
        {
            var dropComponent = hit2D.transform.gameObject.GetComponent<IDropHandler>();
            if (dropComponent != null)
                dropComponent.OnDrop(eventData);
        }

        // Reset the object's layer to the layer it was in before the drop.
        gameObject.layer = oldLayer;
    }

The behaviour I wanted is that when the transform.position of an object is dropped on something, it is counted as dropped. 我想要的行为是,将对象的transform.position放置在某物上时,将其计为放置。 The default behaviour, that when the mouse pointer is inside dropped object it triggers onDrop, could easily be achieved by exchanging 默认行为是,当鼠标指针位于放置的对象内时,它会触发onDrop,这可以通过交换来轻松实现

var raycastOriginInScreenSpace = Camera.main.WorldToScreenPoint(rayCastOrigin);
var screenRay = Camera.main.ScreenPointToRay(new Vector3(raycastOriginInScreenSpace.x, raycastOriginInScreenSpace.y, 0.0f));

with

var screenRay = Camera.main.ScreenPointToRay(new Vector3(eventData.position.x, eventData.position.y, 0.0f));

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM