#2 Follow Pointer Coroutine
Saturday, May 25, 2019
In this article we will focus on creating the code for a coroutine that we can use to allow our gameobject to follow the users cursor around when it is being dragged. To facilitate this we will see how to determine where the pointer is relative to the rect transform of the object that will be dragging around the screen.
Parts
- Part 4: Confine Using View Box
- Part 3: UI Camera
- Part 2: Follow Pointer Coroutine
- Part 1: Draggable Pointer Events
Getting or creating a component
- Assets
- Utilities
- ComponentUtilities.cs
- Utilities
As I have mentioned previously my general attitude when creating code that others will use, including myself, I like to make it work with as little configuration as possible. To that end as we saw in the last video we checked to see if an event trigger was present on our gameobject and if it was not we created one. This pattern is a very common one and as such our first order of business is to make it easier to do. As you may have guess it is time for another extension method (a).
ComponentUtilities.cs
using UnityEngine;
namespace Utilities
{
public static class ComponentUtilities
{
public static T GetOrCreateComponent<T>(this Transform transform)
where T : Component
{
var component = transform.GetComponent<T>();
if (component == null)
{
component = transform.gameObject.AddComponent<T>();
}
return component;
}
}
}
Cleaning up
- Assets
- UserInterface
- DraggableBehavior.cs
- UserInterface
With our new extension method in our pocket we can update our draggable behavior to make use of it (b).
DraggableBehavior.cs
...
namespace UserInterface
{
public sealed class DraggableBehavior : MonoBehaviour
{
...
private void ConfigureEventHandling()
{
Handle.GetOrCreateComponent<EventTrigger>()
.AddTrigger(EventTriggerType.PointerDown, OnPointerDown)
.AddTrigger(EventTriggerType.PointerUp, OnPointerUp);
}
...
}
}
Follow that pointer
- Assets
- UserInterface
- DraggableBehavior.cs
- UserInterface
Unity offers us a couple of different ways to make sure that some action is performed
on a repeated basis. There are a few different methods that we could take advantage of
such as Update
and
FixedUpdate
which would in fact work. But using these
methods would be over kill for our particular use case since we are able to very easily identify when
our code needs to run. What we need then is a way to start a repeating process when a user
clicks and drags our object which is a perfect place to use a
Coroutine
(c).
DraggableBehavior.cs
using System.Collections;
...
namespace UserInterface
{
public sealed class DraggableBehavior : MonoBehaviour
{
...
public float followPointerWait = 0.01f;
...
private Coroutine _followPointerCoroutine;
private WaitForSeconds _followPointerWaitForSeconds;
...
public bool IsBeingDragged { get; private set; }
...
private IEnumerator FollowPointer()
{
while (true)
{
if (IsBeingDragged == false)
{
yield break;
}
Debug.Log("following");
yield return _followPointerWaitForSeconds;
}
}
...
private void Init()
{
_followPointerWaitForSeconds = new WaitForSeconds(followPointerWait);
...
}
...
private void OnPointerDown(BaseEventData data)
{
Debug.Log($"pointer down: {data}");
IsBeingDragged = true;
_followPointerCoroutine = StartCoroutine(FollowPointer());
}
private void OnPointerUp(BaseEventData data)
{
Debug.Log($"pointer up: {data}");
IsBeingDragged = false;
if (_followPointerCoroutine != null)
{
StopCoroutine(_followPointerCoroutine);
}
}
}
}
Now that we have our coroutine code added if we switch over to Unity and play our scene when we click and hold our mouse button down on the panel we should see output in the console very similar to what is shown in (d).
Just where is the pointer?
- Assets
- Utilities
- UiUtilities.cs
- Utilities
In order for us to move our object in response to a user dragging it we are going to need to know the location of the pointer within the objects transform. Although that is our current question the underlying question is if given a two dimensional vector what is its position relative to the transform. Luckily Unity already provides us a method to determine this which we will once again wrap in an extension method to make it easier to use (e). And while we are at it we will create a method that will assume the position we are talking about is the mouse pointer position.
UiUtilities.cs
using UnityEngine;
...
namespace Utilities
{
public static class UiUtilities
{
...
public static Vector2 PointerPositionInTransform(this RectTransform transform,
Camera camera)
{
return PositionInTransform(transform, camera, Input.mousePosition);
}
public static Vector2 PositionInTransform(this RectTransform transform,
Camera camera, Vector2 position)
{
RectTransformUtility.ScreenPointToLocalPointInRectangle(
transform, position, camera, out var pos);
return pos;
}
}
}
Pass the pointer
- Assets
- UserInterface
- DraggableBehavior.cs
- UserInterface
For everything to work we of course need to pass the pointer position when the user presses the mouse button to the follow coroutine (f).
DraggableBehavior.cs
...
namespace UserInterface
{
public sealed class DraggableBehavior : MonoBehaviour
{
...
private RectTransform RectTransform => (RectTransform)transform;
...
private IEnumerator FollowPointer(Vector2 position)
{
while (true)
{
...
Debug.Log($"following: {position}");
...
}
}
...
private void OnPointerDown(BaseEventData data)
{
...
var pos = RectTransform.PointerPositionInTransform(((PointerEventData) data).pressEventCamera);
_followPointerCoroutine = StartCoroutine(FollowPointer(pos));
}
...
}
}