Difference between revisions of "Click through"

From PowerUI
Jump to: navigation, search
(Avoiding it)
Line 10: Line 10:
  
  
* Use Input.Unhandled
+
=== Use Input.Unhandled ====
  
 
See also the [[Event Flow|event flow]]. This special EventTarget receives all events which were not handled by your UI. If you click 'through' your UI, the mouse/touch events are sent here. Grab them using addEventListener:
 
See also the [[Event Flow|event flow]]. This special EventTarget receives all events which were not handled by your UI. If you click 'through' your UI, the mouse/touch events are sent here. Grab them using addEventListener:
Line 40: Line 40:
 
PowerUI tries to give your input system as much flexibility as possible, so the above route is considered relatively low level and which allows you to pipe those events wherever you need them, but requires a little more scripting to hook it up.
 
PowerUI tries to give your input system as much flexibility as possible, so the above route is considered relatively low level and which allows you to pipe those events wherever you need them, but requires a little more scripting to hook it up.
  
* Setup your MonoBehaviour's as an EventTarget (or have an EventTarget attached to your game objects). This is the easier route, but involves PowerUI defining more of your input system. This also has the advantage of being able to correctly handle 3D [[Context menu|context menu's]] too:
+
=== Setup an EventTarget MonoBehaviour ===
 +
 
 +
This is the easier route, but involves PowerUI defining more of your input system. This also has the advantage of being able to correctly handle 3D [[Context Menu|context menu's]] too. Firstly, add something like the following to your project:
  
 
<syntaxhighlight lang="csharp">
 
<syntaxhighlight lang="csharp">
Line 50: Line 52:
  
 
// First make it a Dom.IEventTarget:
 
// First make it a Dom.IEventTarget:
public class MyEventTarget : MonoBehaviour,IEventTarget {
+
public class GameObjectEventTarget : MonoBehaviour,IEventTarget {
+
   
 +
    // An underlying event target which allows us to addEventListener etc:
 +
    public EventTarget Target;
 +
   
 
     // IEventTarget requires the dispatchEvent method.
 
     // IEventTarget requires the dispatchEvent method.
 
     // It's the same as the standard W3C dispatchEvent.
 
     // It's the same as the standard W3C dispatchEvent.
Line 60: Line 65:
 
         // This typically happens when the UI did not handle it,  
 
         // This typically happens when the UI did not handle it,  
 
         // or because an event was specifically dispatched to this gameObject.
 
         // or because an event was specifically dispatched to this gameObject.
 +
        return Target.dispatchEvent(e);
 +
   
 +
    }
 +
   
 +
}
 +
 +
// Extend GameObject with a 'GetEventTarget' method.
 +
public static class GameObjectExtensions{
 +
 +
    public static EventTarget GetEventTarget(this GameObject go){
 +
        // Get the monobehaviour:
 +
        GameObjectEventTarget monoBehaviour = go.GetComponent<GameObjectEventTarget>();
 +
     
 +
        if(monoBehaviour == null){
 +
            // Add it:
 +
            monoBehaviour = go.AddComponent<GameObjectEventTarget>();
 +
        }
 +
       
 +
        // Return the target property:
 +
        return monoBehaviour.Target;
 +
    }
 +
}
 +
 +
</syntaxhighlight>
 +
 +
Next, to use the above, simply use GetEventTarget from within your MonoBeheviour:
 +
 +
<syntaxhighlight lang="csharp">
 +
 +
public class MyGameworldEvents : MonoBehaviour{
 +
   
 +
    public void Awake(){
 +
        // Get the event target:
 +
        var target = gameObject.GetEventTarget();
 +
       
 +
        // Now we're getting somewhere interesting!
 +
       
 +
        // Add a mouse down event:
 +
        target.addEventListener("mousedown",delegate(MouseEvent e){
 
              
 
              
        // So, first, what kind of event was it?
+
             // This runs when this gameobject got clicked on
        if(e.type == "mousedown"){
+
             // and the UI was not.
             // This gameobject was clicked/ tapped!
 
             // Fire off onMouseDown:
 
            OnMouseDown(e as MouseEvent);
 
               
 
        }
 
 
              
 
              
         // By default the event will bubble up the scene hierarchy
+
         });
        // Unless you block it by returning false (or set e.peventDefault etc):
 
        return false;
 
    }
 
 
    public void OnMouseDown(MouseEvent e){
 
        // Called when this game object was clicked/tapped.
 
        // Importantly, we actually have a pointer specific event object.
 
        // That means it works much better with multi-touch
 
        // and we have a lot more information available about the actual event.
 
 
     }
 
     }
       
 
 
}
 
}
 +
 
</syntaxhighlight>
 
</syntaxhighlight>

Revision as of 06:41, 15 April 2017

It's very common to have game world objects which can be clicked/ tapped on. However, if your UI is over one of those objects, you might get something called 'click through' - where both the UI and your game world object handle the click.

Why click through happens

Unity doesn't provide a way to block the built in ray cast which occurs the screen is pressed or the mouse is clicked. Click through happens for Unity's built in GUI too. So, because PowerUI can't block it from happening, both Unity's raycast and PowerUI's element resolve occur at the same time - when both happen to get handled, then the click through scenario arises.

Avoiding it

Unity GUI and older versions of PowerUI have a flag which you check from your MonoBehaviour click methods (like OnMouseDown). Due to a wider variety of events (e.g. the touch events) and as PowerUI fully supports multi-touch, there's two potential routes - both involve catching the event after they've passed through the UI:


Use Input.Unhandled =

See also the event flow. This special EventTarget receives all events which were not handled by your UI. If you click 'through' your UI, the mouse/touch events are sent here. Grab them using addEventListener:

PowerUI.Input.Unhandled.addEventListener("mousedown",delegate(MouseEvent e){
    
    // They clicked on *nothing!* (straight 'through' the UI).
    // Send this event wherever you'd like.
    // Note that the original raycast (and any GameObject it hit) are available:
    if(e.raySuccess){
        // It hit something! Use rayHit next.

        // Get the clicked gameObject:
        GameObject go = e.rayHit.gameObject;
        
        // Try getting a MonoBehaviour called 'MyInputScript':
        MyInputScript myInput = go.GetComponent<MyInputScript>();
        
        if(myInput != null){
            // Great - forward the event to it:
            myInput.MouseDown(e);
        }
    }
    
});

PowerUI tries to give your input system as much flexibility as possible, so the above route is considered relatively low level and which allows you to pipe those events wherever you need them, but requires a little more scripting to hook it up.

Setup an EventTarget MonoBehaviour

This is the easier route, but involves PowerUI defining more of your input system. This also has the advantage of being able to correctly handle 3D context menu's too. Firstly, add something like the following to your project:

using UnityEngine;
using System.Collections;
using PowerUI;
using Dom;


// First make it a Dom.IEventTarget:
public class GameObjectEventTarget : MonoBehaviour,IEventTarget {
    
    // An underlying event target which allows us to addEventListener etc:
    public EventTarget Target;
    
    // IEventTarget requires the dispatchEvent method.
    // It's the same as the standard W3C dispatchEvent.
    // Define that too:
    public bool dispatchEvent(Dom.Event e){
    
        // We received some event!
        // This typically happens when the UI did not handle it, 
        // or because an event was specifically dispatched to this gameObject.
        return Target.dispatchEvent(e);
    
    }
    
}

// Extend GameObject with a 'GetEventTarget' method.
public static class GameObjectExtensions{
 
    public static EventTarget GetEventTarget(this GameObject go){
        // Get the monobehaviour:
        GameObjectEventTarget monoBehaviour = go.GetComponent<GameObjectEventTarget>();
       
        if(monoBehaviour == null){
            // Add it:
            monoBehaviour = go.AddComponent<GameObjectEventTarget>();
        }
        
        // Return the target property:
        return monoBehaviour.Target;
    }
}

Next, to use the above, simply use GetEventTarget from within your MonoBeheviour:

public class MyGameworldEvents : MonoBehaviour{
    
    public void Awake(){
        // Get the event target:
        var target = gameObject.GetEventTarget();
        
        // Now we're getting somewhere interesting!
        
        // Add a mouse down event:
        target.addEventListener("mousedown",delegate(MouseEvent e){
            
            // This runs when this gameobject got clicked on
            // and the UI was not.
            
        });
    }
}