Skip to content

Commit

Permalink
Merge pull request #111 from nowsprinting/feature/create_interactivec…
Browse files Browse the repository at this point in the history
…omponent

Add CreateInteractableComponent(MonoBehaviour) static method
  • Loading branch information
nowsprinting authored Mar 31, 2024
2 parents 288035f + 7bfd08f commit 4965f6f
Show file tree
Hide file tree
Showing 5 changed files with 50 additions and 20 deletions.
37 changes: 30 additions & 7 deletions Runtime/InteractiveComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,21 +48,42 @@ private readonly Func<GameObject, Func<GameObject, Vector2>, PointerEventData, L
private readonly PointerEventData _eventData = new PointerEventData(EventSystem.current);
private readonly List<RaycastResult> _results = new List<RaycastResult>();

internal InteractiveComponent(MonoBehaviour component,
Func<GameObject, Vector2> getScreenPoint = null,
Func<GameObject, Func<GameObject, Vector2>, PointerEventData, List<RaycastResult>, bool> isReachable = null)
{
this.component = component;
_getScreenPoint = getScreenPoint ?? DefaultScreenPointStrategy.GetScreenPoint;
_isReachable = isReachable ?? DefaultReachableStrategy.IsReachable;
}

/// <summary>
/// Constructor
/// Create <c>InteractableComponent</c> instance from MonoBehaviour.
/// </summary>
/// <param name="component"></param>
/// <param name="getScreenPoint">The function returns the screen position where raycast for the found <c>GameObject</c>.
/// Default is <c>DefaultScreenPointStrategy.GetScreenPoint</c>.</param>
/// <param name="isReachable">The function returns the <c>GameObject</c> is reachable from user or not.
/// Default is <c>DefaultReachableStrategy.IsReachable</c>.</param>
public InteractiveComponent(MonoBehaviour component,
/// <param name="isComponentInteractable">The function returns the <c>Component</c> is interactable or not.
/// Default is <c>DefaultComponentInteractableStrategy.IsInteractable</c>.</param>
/// <returns>Returns new InteractableComponent instance from MonoBehaviour. If MonoBehaviour is not interactable so, return null.</returns>
public static InteractiveComponent CreateInteractableComponent(MonoBehaviour component,
Func<GameObject, Vector2> getScreenPoint = null,
Func<GameObject, Func<GameObject, Vector2>, PointerEventData, List<RaycastResult>, bool> isReachable = null)
Func<GameObject, Func<GameObject, Vector2>, PointerEventData, List<RaycastResult>, bool> isReachable = null,
Func<Component, bool> isComponentInteractable = null)
{
this.component = component;
_getScreenPoint = getScreenPoint ?? DefaultScreenPointStrategy.GetScreenPoint;
_isReachable = isReachable ?? DefaultReachableStrategy.IsReachable;
getScreenPoint = getScreenPoint ?? DefaultScreenPointStrategy.GetScreenPoint;
isReachable = isReachable ?? DefaultReachableStrategy.IsReachable;
isComponentInteractable = isComponentInteractable ?? DefaultComponentInteractableStrategy.IsInteractable;

if (isComponentInteractable.Invoke(component))
{
return new InteractiveComponent(component, getScreenPoint, isReachable);
}

Debug.LogWarning($"Component `{component}` is not interactable.");
return null;
}

/// <summary>
Expand All @@ -76,14 +97,15 @@ public InteractiveComponent(MonoBehaviour component,
/// <param name="isComponentInteractable">The function returns the <c>Component</c> is interactable or not.
/// Default is <c>DefaultComponentInteractableStrategy.IsInteractable</c>.</param>
/// <returns>Returns new InteractableComponent instance from GameObject. If GameObject is not interactable so, return null.</returns>
[Obsolete("Obsolete due to non-deterministic behavior when GameObject has multiple interactable components.")]
public static InteractiveComponent CreateInteractableComponent(GameObject gameObject,
Func<GameObject, Vector2> getScreenPoint = null,
Func<GameObject, Func<GameObject, Vector2>, PointerEventData, List<RaycastResult>, bool> isReachable = null,
Func<Component, bool> isComponentInteractable = null)
{
getScreenPoint = getScreenPoint ?? DefaultScreenPointStrategy.GetScreenPoint;
isComponentInteractable = isComponentInteractable ?? DefaultComponentInteractableStrategy.IsInteractable;
isReachable = isReachable ?? DefaultReachableStrategy.IsReachable;
isComponentInteractable = isComponentInteractable ?? DefaultComponentInteractableStrategy.IsInteractable;

foreach (var component in gameObject.GetComponents<MonoBehaviour>())
{
Expand All @@ -93,6 +115,7 @@ public static InteractiveComponent CreateInteractableComponent(GameObject gameOb
}
}

Debug.LogWarning($"GameObject `{gameObject}` has not interactable component.");
return null;
}

Expand Down
2 changes: 1 addition & 1 deletion Runtime/InteractiveComponentCollector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public IEnumerable<InteractiveComponent> FindInteractableComponents()
{
if (_isInteractable.Invoke(component))
{
yield return new InteractiveComponent(component,
yield return InteractiveComponent.CreateInteractableComponent(component,
_getScreenPoint,
_isReachable);
}
Expand Down
13 changes: 10 additions & 3 deletions Samples~/uGUI Demo/Tests/Runtime/ScenarioTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Threading.Tasks;
using NUnit.Framework;
using TestHelper.Attributes;
using UnityEngine.UI;

namespace TestHelper.Monkey.Samples.UGUIDemo
{
Expand All @@ -25,17 +26,23 @@ public async Task OpenSubScreens(string target)

// When click Start button, then open Home screen.
var startButton = await _finder.FindByNameAsync("StartButton", interactable: true);
InteractiveComponent.CreateInteractableComponent(startButton).Click();
var startComponent = InteractiveComponent.CreateInteractableComponent(startButton.GetComponent<Button>());
Assume.That(startComponent.CanClick(), Is.True);
startComponent.Click();
await _finder.FindByNameAsync("Home");

// When click target button, then open target screen.
var targetButton = await _finder.FindByNameAsync($"{target}Button", interactable: true);
InteractiveComponent.CreateInteractableComponent(targetButton).Click();
var targetComponent = InteractiveComponent.CreateInteractableComponent(targetButton.GetComponent<Button>());
Assume.That(targetComponent.CanClick(), Is.True);
targetComponent.Click();
await _finder.FindByNameAsync(target);

// When click Back button, then return Home screen.
var backButton = await _finder.FindByPathAsync($"**/{target}/BackButton", interactable: true);
InteractiveComponent.CreateInteractableComponent(backButton).Click();
var backComponent = InteractiveComponent.CreateInteractableComponent(backButton.GetComponent<Button>());
Assume.That(backComponent.CanClick(), Is.True);
backComponent.Click();
await _finder.FindByNameAsync("Home");
}
}
Expand Down
6 changes: 3 additions & 3 deletions Tests/Runtime/InteractiveComponentTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public class InteractiveComponentTest
public void CreateInteractableComponent_CreateInstance()
{
var button = new GameObject("InteractableButton").AddComponent<Button>();
var actual = InteractiveComponent.CreateInteractableComponent(button.gameObject);
var actual = InteractiveComponent.CreateInteractableComponent(button);
Assert.That(actual, Is.Not.Null);
}

Expand All @@ -28,7 +28,7 @@ public void CreateInteractableComponent_NotInteractable_ReturnNull()
{
var button = new GameObject("NotInteractableButton").AddComponent<Button>();
button.interactable = false;
var actual = InteractiveComponent.CreateInteractableComponent(button.gameObject);
var actual = InteractiveComponent.CreateInteractableComponent(button);
Assert.That(actual, Is.Null);
}

Expand All @@ -47,7 +47,7 @@ public void Fields()
{
var gameObject = GameObject.Find("UsingEventTrigger");
var component = gameObject.GetComponent<EventTrigger>();
var sut = new InteractiveComponent(component);
var sut = InteractiveComponent.CreateInteractableComponent(component);

Assert.That(sut.component, Is.EqualTo(component));
Assert.That(sut.gameObject, Is.EqualTo(gameObject));
Expand Down
12 changes: 6 additions & 6 deletions Tests/Runtime/MonkeyTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -206,13 +206,13 @@ public void Lottery_hitNotInteractiveComponent_returnNextLotteryComponent()
{
var components = new List<InteractiveComponent>()
{
new InteractiveComponent(
InteractiveComponent.CreateInteractableComponent(
GameObject.Find("UsingOnPointerClickHandler").GetComponent<SpyOnPointerClickHandler>()),
new InteractiveComponent(
InteractiveComponent.CreateInteractableComponent(
GameObject.Find("UsingPointerClickEventTrigger").GetComponent<EventTrigger>()),
new InteractiveComponent(
InteractiveComponent.CreateInteractableComponent(
GameObject.Find("UsingOnPointerDownUpHandler").GetComponent<SpyOnPointerDownUpHandler>()),
new InteractiveComponent(
InteractiveComponent.CreateInteractableComponent(
GameObject.Find("UsingPointerDownUpEventTrigger").GetComponent<EventTrigger>()),
};
components[0].gameObject.SetActive(false);
Expand All @@ -231,7 +231,7 @@ public void Lottery_noInteractiveComponent_returnNull()
{
var components = new List<InteractiveComponent>()
{
new InteractiveComponent(
InteractiveComponent.CreateInteractableComponent(
GameObject.Find("UsingOnPointerClickHandler").GetComponent<SpyOnPointerClickHandler>()),
};
components[0].gameObject.SetActive(false);
Expand All @@ -249,7 +249,7 @@ public void Lottery_withIgnoreAnnotation_returnNull()
{
var components = new List<InteractiveComponent>()
{
new InteractiveComponent(
InteractiveComponent.CreateInteractableComponent(
GameObject.Find("UsingMultipleEventTriggers").GetComponent<EventTrigger>()),
};
components[0].gameObject.AddComponent<IgnoreAnnotation>();
Expand Down

0 comments on commit 4965f6f

Please sign in to comment.