Skip to content

Instantly share code, notes, and snippets.

@Urethramancer
Forked from stramit/StandaloneInputModule.cs
Last active October 11, 2016 11:24
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save Urethramancer/65cb7e16a489fdd11c01 to your computer and use it in GitHub Desktop.
Save Urethramancer/65cb7e16a489fdd11c01 to your computer and use it in GitHub Desktop.
// Replacement module for Standalone Input Module
// Up to date for Unity 4.6 beta 19
//
// Usage:
// -Remove the standalone module from your event system
// -Add this instead
// -Include ExtraMouseEvents.cs and IExtraMouseEventsHandler.cs
// -Implement IRightPointerClickHandler, IRightPointerDownHandler, IRightPointerUpHandler, IMiddlePointerClickHandler,
// IMiddlePointerDownHandler and/or IMiddlePointerUpHandler.
//
// Example right-click script:
// using UnityEngine;
// using UnityEngine.EventSystems;
//
// public class ButtonDelegates : MonoBehaviour, IRightPointerClickHandler
// {
// public void OnRightPointerClick(PointerEventData data)
// {
// Debug.Log("Right-click");
// }
// }
namespace UnityEngine.EventSystems
{
[AddComponentMenu("Event/Mouse Input Module")]
public class MouseInputModule : PointerInputModule
{
enum MouseButtons
{
Left = 0,
Right,
Middle
};
private float m_NextAction;
private InputMode m_CurrentInputMode = InputMode.Buttons;
private Vector2 m_LastMousePosition;
private Vector2 m_MousePosition;
protected MouseInputModule()
{}
private enum InputMode
{
Mouse,
Buttons
}
[SerializeField]
private string m_HorizontalAxis = "Horizontal";
/// <summary>
/// Name of the vertical axis for movement (if axis events are used).
/// </summary>
[SerializeField]
private string m_VerticalAxis = "Vertical";
/// <summary>
/// Name of the submit button.
/// </summary>
[SerializeField]
private string m_SubmitButton = "Submit";
/// <summary>
/// Name of the submit button.
/// </summary>
[SerializeField]
private string m_CancelButton = "Cancel";
[SerializeField]
private float m_InputActionsPerSecond = 10;
[SerializeField]
private bool _allowRMBDrag;
public bool allowRMBDrag
{
get {return _allowRMBDrag;}
set {_allowRMBDrag = value;}
}
[SerializeField]
private bool _allowMMBDrag;
public bool allowMMBDrag
{
get {return _allowMMBDrag;}
set {_allowMMBDrag = value;}
}
[SerializeField]
private bool m_AllowActivationOnMobileDevice;
public bool allowActivationOnMobileDevice
{
get { return m_AllowActivationOnMobileDevice; }
set { m_AllowActivationOnMobileDevice = value; }
}
public float inputActionsPerSecond
{
get { return m_InputActionsPerSecond; }
set { m_InputActionsPerSecond = value; }
}
/// <summary>
/// Name of the horizontal axis for movement (if axis events are used).
/// </summary>
public string horizontalAxis
{
get { return m_HorizontalAxis; }
set { m_HorizontalAxis = value; }
}
/// <summary>
/// Name of the vertical axis for movement (if axis events are used).
/// </summary>
public string verticalAxis
{
get { return m_VerticalAxis; }
set { m_VerticalAxis = value; }
}
public string submitButton
{
get { return m_SubmitButton; }
set { m_SubmitButton = value; }
}
public string cancelButton
{
get { return m_CancelButton; }
set { m_CancelButton = value; }
}
public override void UpdateModule()
{
m_LastMousePosition = m_MousePosition;
m_MousePosition = Input.mousePosition;
}
public override bool IsModuleSupported()
{
return m_AllowActivationOnMobileDevice || !Application.isMobilePlatform;
}
public override bool ShouldActivateModule()
{
if (!base.ShouldActivateModule ())
return false;
var shouldActivate = Input.GetButtonDown (m_SubmitButton);
shouldActivate |= Input.GetButtonDown (m_CancelButton);
shouldActivate |= !Mathf.Approximately (Input.GetAxis (m_HorizontalAxis), 0.0f);
shouldActivate |= !Mathf.Approximately (Input.GetAxis (m_VerticalAxis), 0.0f);
shouldActivate |= (m_MousePosition - m_LastMousePosition).sqrMagnitude > 0.0f;
shouldActivate |= Input.GetMouseButtonDown (0);
return shouldActivate;
}
public override void ActivateModule()
{
base.ActivateModule ();
m_MousePosition = Input.mousePosition;
m_LastMousePosition = Input.mousePosition;
var toSelect = eventSystem.currentSelectedGameObject;
if (toSelect == null)
toSelect = eventSystem.lastSelectedGameObject;
if (toSelect == null)
toSelect = eventSystem.firstSelectedGameObject;
eventSystem.SetSelectedGameObject (null, GetBaseEventData ());
eventSystem.SetSelectedGameObject (toSelect, GetBaseEventData ());
}
public override void DeactivateModule()
{
base.DeactivateModule ();
ClearSelection ();
}
public override void Process()
{
bool usedEvent = SendUpdateEventToSelectedObject ();
if (!usedEvent)
usedEvent |= SendMoveEventToSelectedObject ();
if (!usedEvent)
SendSubmitEventToSelectedObject ();
ProcessMouseEvent ();
}
/// <summary>
/// Process submit keys.
/// </summary>
private bool SendSubmitEventToSelectedObject()
{
if (eventSystem.currentSelectedGameObject == null || m_CurrentInputMode != InputMode.Buttons)
return false;
var data = GetBaseEventData ();
if (Input.GetButtonDown (m_SubmitButton))
ExecuteEvents.Execute (eventSystem.currentSelectedGameObject, data, ExecuteEvents.submitHandler);
if (Input.GetButtonDown (m_CancelButton))
ExecuteEvents.Execute (eventSystem.currentSelectedGameObject, data, ExecuteEvents.cancelHandler);
return data.used;
}
private bool AllowMoveEventProcessing(float time)
{
bool allow = Input.GetButtonDown (m_HorizontalAxis);
allow |= Input.GetButtonDown (m_VerticalAxis);
allow |= (time > m_NextAction);
return allow;
}
private Vector2 GetRawMoveVector()
{
Vector2 move = Vector2.zero;
move.x = Input.GetAxis (m_HorizontalAxis);
move.y = Input.GetAxis (m_VerticalAxis);
if (Input.GetButtonDown (m_HorizontalAxis))
{
if (move.x < 0)
move.x = -1f;
if (move.x > 0)
move.x = 1f;
}
if (Input.GetButtonDown (m_VerticalAxis))
{
if (move.y < 0)
move.y = -1f;
if (move.y > 0)
move.y = 1f;
}
return move;
}
/// <summary>
/// Process keyboard events.
/// </summary>
private bool SendMoveEventToSelectedObject()
{
float time = Time.unscaledTime;
if (!AllowMoveEventProcessing (time))
return false;
Vector2 movement = GetRawMoveVector ();
//Debug.Log(m_ProcessingEvent.rawType + " axis:" + m_AllowAxisEvents + " value:" + "(" + x + "," + y + ")");
var axisEventData = GetAxisEventData (movement.x, movement.y, 0.6f);
if (!Mathf.Approximately (axisEventData.moveVector.x, 0f)
|| !Mathf.Approximately (axisEventData.moveVector.y, 0f))
{
if (m_CurrentInputMode != InputMode.Buttons)
{
// so if we are chaning to keyboard
m_CurrentInputMode = InputMode.Buttons;
// if we are doing a 'fresh selection'
// return as we don't want to do a move.
if (ResetSelection ())
{
m_NextAction = time + 1f / m_InputActionsPerSecond;
return true;
}
}
ExecuteEvents.Execute (eventSystem.currentSelectedGameObject, axisEventData, ExecuteEvents.moveHandler);
}
m_NextAction = time + 1f / m_InputActionsPerSecond;
return axisEventData.used;
}
private bool ResetSelection()
{
var baseEventData = GetBaseEventData ();
// clear all selection
// & figure out what the mouse is over
var lastMousePointer = GetLastPointerEventData (kMouseId);
var hoveredObject = lastMousePointer == null ? null : lastMousePointer.pointerEnter;
HandlePointerExitAndEnter (lastMousePointer, null);
eventSystem.SetSelectedGameObject (null, baseEventData);
// if we were hovering something...
// use this as the basis for the selection
bool resetSelection = false;
GameObject toSelect = ExecuteEvents.GetEventHandler<ISelectHandler> (hoveredObject);
if (toSelect == null)
{
// if there was no hover
// then use the last selected
toSelect = eventSystem.lastSelectedGameObject;
resetSelection = true;
}
eventSystem.SetSelectedGameObject (toSelect, baseEventData);
return resetSelection;
}
/// <summary>
/// Process all mouse events.
/// </summary>
private void ProcessMouseEvent()
{
bool leftPressed = Input.GetMouseButtonDown(0);
bool leftReleased = Input.GetMouseButtonUp(0);
bool rightPressed = Input.GetMouseButtonDown(1);
bool rightReleased = Input.GetMouseButtonUp(1);
bool middlePressed = Input.GetMouseButtonDown(2);
bool middleReleased = Input.GetMouseButtonUp(2);
var pointerData = GetMousePointerEventData();
// Take care of the scroll wheel
float scroll = Input.GetAxis ("Mouse ScrollWheel");
pointerData.scrollDelta.x = 0f;
pointerData.scrollDelta.y = scroll;
bool useLeft = UseMouse (leftPressed, leftReleased, pointerData);
bool useRight = UseMouse (rightPressed, rightReleased, pointerData);
bool useMiddle = UseMouse (middlePressed, middleReleased, pointerData);
bool mouse = useLeft | useRight | useMiddle;
if (!mouse) return;
// Process the mouse buttons fully
if(useLeft) ProcessMousePress(pointerData, leftPressed, leftReleased, MouseButtons.Left);
if(useRight) ProcessMousePress(pointerData, rightPressed, rightReleased, MouseButtons.Right);
if(useMiddle) ProcessMousePress(pointerData, middlePressed, middleReleased, MouseButtons.Middle);
ProcessMove(pointerData);
if (!Mathf.Approximately(scroll, 0.0f))
{
var scrollHandler = ExecuteEvents.GetEventHandler<IScrollHandler>(pointerData.pointerCurrentRaycast.go);
ExecuteEvents.ExecuteHierarchy(scrollHandler, pointerData, ExecuteEvents.scrollHandler);
}
}
protected override void ProcessMove(PointerEventData pointerEvent)
{
base.ProcessMove (pointerEvent);
var targetGO = pointerEvent.pointerCurrentRaycast.go;
HandlePointerExitAndEnter (pointerEvent, targetGO);
}
private bool UseMouse(bool pressed, bool released, PointerEventData pointerData)
{
if (m_CurrentInputMode == InputMode.Mouse) return true;
// On mouse action switch back to mouse control scheme
if (pressed || released || pointerData.IsPointerMoving() || pointerData.IsScrolling())
{
m_CurrentInputMode = InputMode.Mouse;
eventSystem.SetSelectedGameObject(null, pointerData);
}
return m_CurrentInputMode == InputMode.Mouse;
}
private bool SendUpdateEventToSelectedObject()
{
if (eventSystem.currentSelectedGameObject == null)
return false;
var data = GetBaseEventData ();
ExecuteEvents.Execute (eventSystem.currentSelectedGameObject, data, ExecuteEvents.updateSelectedHandler);
return data.used;
}
/// <summary>
/// Process the current mouse press.
/// </summary>
private void ProcessMousePress(PointerEventData pointerEvent, bool pressed, bool released, MouseButtons button)
{
var currentOverGo = pointerEvent.pointerCurrentRaycast.go;
// Limit drag events to allowed buttons
bool allowDrag = true;
switch(button)
{
// We skip LMB, because we're just looking for exceptions
case MouseButtons.Right:
allowDrag = allowRMBDrag;
break;
case MouseButtons.Middle:
allowDrag = allowMMBDrag;
break;
}
// PointerDown notification
if(pressed)
{
pointerEvent.eligibleForClick = true;
pointerEvent.delta = Vector2.zero;
pointerEvent.pressPosition = pointerEvent.position;
pointerEvent.pointerPressRaycast = pointerEvent.pointerCurrentRaycast;
// Search for the relevant handler on the control to receive a press,
// based on which button was pressed
GameObject newPressed = null;
switch(button)
{
case MouseButtons.Left:
newPressed = ExecuteEvents.ExecuteHierarchy (currentOverGo, pointerEvent, ExecuteEvents.pointerDownHandler);
// Didn't find a press handler - search for a click handler
if(newPressed == null) newPressed = ExecuteEvents.GetEventHandler<IPointerClickHandler>(currentOverGo);
break;
case MouseButtons.Right:
newPressed = ExecuteEvents.ExecuteHierarchy (currentOverGo, pointerEvent, ExtraMouseEvents.rightDownHandler);
// Didn't find a press handler - search for a click handler
if(newPressed == null) newPressed = ExecuteEvents.GetEventHandler<IRightPointerClickHandler>(currentOverGo);
break;
case MouseButtons.Middle:
newPressed = ExecuteEvents.ExecuteHierarchy (currentOverGo, pointerEvent, ExtraMouseEvents.middleDownHandler);
// Didn't find a press handler - search for a click handler
if(newPressed == null) newPressed = ExecuteEvents.GetEventHandler<IMiddlePointerClickHandler>(currentOverGo);
break;
}
if (newPressed != pointerEvent.pointerPress)
{
pointerEvent.pointerPress = newPressed;
pointerEvent.rawPointerPress = currentOverGo;
pointerEvent.clickCount = 0;
}
// Save the drag handler as well
if(allowDrag)
{
pointerEvent.pointerDrag = ExecuteEvents.GetEventHandler<IDragHandler>(currentOverGo);
if(pointerEvent.pointerDrag != null) ExecuteEvents.Execute (pointerEvent.pointerDrag, pointerEvent, ExecuteEvents.beginDragHandler);
}
// Selection tracking
var selectHandlerGO = ExecuteEvents.GetEventHandler<ISelectHandler>(currentOverGo);
eventSystem.SetSelectedGameObject (selectHandlerGO, pointerEvent);
}
// PointerUp notification
if(released)
{
GameObject pointerUpHandler = null;
switch(button)
{
case MouseButtons.Left:
ExecuteEvents.Execute (pointerEvent.pointerPress, pointerEvent, ExecuteEvents.pointerUpHandler);
// see if we mouse up on the same element that we clicked on...
pointerUpHandler = ExecuteEvents.GetEventHandler<IPointerClickHandler>(currentOverGo);
break;
case MouseButtons.Right:
ExecuteEvents.Execute (pointerEvent.pointerPress, pointerEvent, ExtraMouseEvents.rightUpHandler);
// see if we mouse up on the same element that we clicked on...
pointerUpHandler = ExecuteEvents.GetEventHandler<IRightPointerClickHandler>(currentOverGo);
break;
case MouseButtons.Middle:
ExecuteEvents.Execute (pointerEvent.pointerPress, pointerEvent, ExtraMouseEvents.middleUpHandler);
// see if we mouse up on the same element that we clicked on...
pointerUpHandler = ExecuteEvents.GetEventHandler<IMiddlePointerClickHandler>(currentOverGo);
break;
}
// PointerClick and Drop events
if (pointerEvent.pointerPress == pointerUpHandler && pointerEvent.eligibleForClick)
{
float time = Time.unscaledTime;
if (time - pointerEvent.clickTime < 0.3f)
++pointerEvent.clickCount;
else
pointerEvent.clickCount = 1;
pointerEvent.clickTime = time;
switch(button)
{
case MouseButtons.Left:
ExecuteEvents.Execute (pointerEvent.pointerPress, pointerEvent, ExecuteEvents.pointerClickHandler);
break;
case MouseButtons.Right:
ExecuteEvents.Execute (pointerEvent.pointerPress, pointerEvent, ExtraMouseEvents.rightClickHandler);
break;
case MouseButtons.Middle:
ExecuteEvents.Execute (pointerEvent.pointerPress, pointerEvent, ExtraMouseEvents.middleClickHandler);
break;
}
}
else if(pointerEvent.pointerDrag != null && allowDrag)
{
ExecuteEvents.ExecuteHierarchy (currentOverGo, pointerEvent, ExecuteEvents.dropHandler);
}
pointerEvent.eligibleForClick = false;
pointerEvent.pointerPress = null;
pointerEvent.rawPointerPress = null;
if (pointerEvent.pointerDrag != null && allowDrag) ExecuteEvents.Execute (pointerEvent.pointerDrag, pointerEvent, ExecuteEvents.endDragHandler);
pointerEvent.pointerDrag = null;
// redo pointer enter / exit to refresh state
// so that if we moused over somethign that ignored it before
// due to having pressed on something else
// it now gets it.
HandlePointerExitAndEnter(pointerEvent, null);
HandlePointerExitAndEnter(pointerEvent, currentOverGo);
}
}
}
}
@herpdederp
Copy link

I tried this but it does not seem to be picking up my right and middle clicks. Are there some other settings I need to change?

@Urethramancer
Copy link
Author

I've updated the source for beta 19 and included some brief instructions.

Remember to include https://gist.github.com/Urethramancer/77a2df0658373d81aa5c and https://gist.github.com/Urethramancer/a35bdbdd7de030f6f487 to make it all work.

@Urethramancer
Copy link
Author

Broken again (and not necessary!) in beta 20.

@chris-hatton
Copy link

I've encountered this page somewhat randomly (I want to implement my own InputModule as well, so thanks for the example).
In your comments you don't say what advantage your implementation gives, why should anyone use it over the standard MouseInputModule?

@Urethramancer
Copy link
Author

This WAS useful before the main version had full support for all buttons. Now it's probably not of interest to anyone but archaeologists.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment