Skip to content

Commit

Permalink
Merge pull request #107 from nowsprinting/feature/strategies_into_mon…
Browse files Browse the repository at this point in the history
…keyconfig

Add reachable and interactable examine strategies into MonkeyConfig
  • Loading branch information
nowsprinting authored Mar 28, 2024
2 parents e50bc17 + 244f290 commit 87b7e4c
Show file tree
Hide file tree
Showing 8 changed files with 65 additions and 75 deletions.
30 changes: 0 additions & 30 deletions Runtime/DefaultStrategies/DefaultGameObjectInteractableStrategy.cs

This file was deleted.

This file was deleted.

15 changes: 13 additions & 2 deletions Runtime/DefaultStrategies/DefaultReachableStrategy.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// Copyright (c) 2023-2024 Koji Hasegawa.
// This software is released under the MIT License.

using System;
using System.Collections.Generic;
using TestHelper.Monkey.ScreenPointStrategies;
using UnityEngine;
using UnityEngine.EventSystems;

Expand All @@ -17,13 +19,22 @@ public static class DefaultReachableStrategy
/// Hit test using raycaster
/// </summary>
/// <param name="gameObject"></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="eventData">Specify if avoid GC memory allocation</param>
/// <param name="results">Specify if avoid GC memory allocation</param>
/// <returns>True if this GameObject is reachable from user</returns>
public static bool IsReachable(GameObject gameObject,
PointerEventData eventData,
List<RaycastResult> results)
Func<GameObject, Vector2> getScreenPoint = null,
PointerEventData eventData = null,
List<RaycastResult> results = null)
{
getScreenPoint = getScreenPoint ?? DefaultScreenPointStrategy.GetScreenPoint;

eventData = eventData ?? new PointerEventData(EventSystem.current);
eventData.position = getScreenPoint.Invoke(gameObject);

results = results ?? new List<RaycastResult>();
results.Clear();

EventSystem.current.RaycastAll(eventData, results);
Expand Down
26 changes: 11 additions & 15 deletions Runtime/GameObjectFinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@ public class GameObjectFinder
{
private readonly double _timeoutSeconds;
private readonly Func<GameObject, Vector2> _getScreenPoint;

private readonly Func<GameObject, Func<GameObject, Vector2>, PointerEventData, List<RaycastResult>, bool>
_isReachable;

private readonly Func<Component, bool> _isComponentInteractable;
private readonly Func<GameObject, Func<Component, bool>, bool> _isGameObjectInteractable;
private readonly Func<GameObject, PointerEventData, List<RaycastResult>, bool> _isReachable;
private readonly PointerEventData _eventData = new PointerEventData(EventSystem.current);
private readonly List<RaycastResult> _results = new List<RaycastResult>();

Expand All @@ -33,24 +35,19 @@ public class GameObjectFinder
/// <param name="timeoutSeconds">Seconds to wait until <c>GameObject</c> appear.</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="isComponentInteractable">The function returns the <c>Component</c> is interactable or not.
/// Default is <c>DefaultComponentInteractableStrategy.IsInteractable</c>.</param>
/// <param name="isGameObjectInteractable">The function returns the <c>GameObject</c> is interactable or not.
/// Default is <c>DefaultGameObjectInteractableStrategy.IsInteractable</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>
/// <param name="isComponentInteractable">The function returns the <c>Component</c> is interactable or not.
/// Default is <c>DefaultComponentInteractableStrategy.IsInteractable</c>.</param>
public GameObjectFinder(double timeoutSeconds = 1.0d,
Func<GameObject, Vector2> getScreenPoint = null,
Func<Component, bool> isComponentInteractable = null,
Func<GameObject, Func<Component, bool>, bool> isGameObjectInteractable = null,
Func<GameObject, PointerEventData, List<RaycastResult>, bool> isReachable = null)
Func<GameObject, Func<GameObject, Vector2>, PointerEventData, List<RaycastResult>, bool> isReachable = null,
Func<Component, bool> isComponentInteractable = null)
{
_timeoutSeconds = timeoutSeconds;
_getScreenPoint = getScreenPoint ?? DefaultScreenPointStrategy.GetScreenPoint;
_isComponentInteractable = isComponentInteractable ?? DefaultComponentInteractableStrategy.IsInteractable;
_isGameObjectInteractable =
isGameObjectInteractable ?? DefaultGameObjectInteractableStrategy.IsInteractable;
_isReachable = isReachable ?? DefaultReachableStrategy.IsReachable;
_isComponentInteractable = isComponentInteractable ?? DefaultComponentInteractableStrategy.IsInteractable;
}

private enum Reason
Expand All @@ -73,16 +70,15 @@ private enum Reason

if (reachable)
{
_eventData.position = _getScreenPoint.Invoke(foundObject);
if (!_isReachable.Invoke(foundObject, _eventData, _results))
if (!_isReachable.Invoke(foundObject, _getScreenPoint, _eventData, _results))
{
return (null, Reason.NotReachable);
}
}

if (interactable)
{
if (!_isGameObjectInteractable.Invoke(foundObject, _isComponentInteractable))
if (!foundObject.GetComponents<Component>().Any(_isComponentInteractable))
{
return (null, Reason.NotInteractable);
}
Expand Down
23 changes: 11 additions & 12 deletions Runtime/InteractiveComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,10 @@ public class InteractiveComponent
public GameObject gameObject => component.gameObject;

private readonly Func<GameObject, Vector2> _getScreenPoint;
private readonly Func<GameObject, PointerEventData, List<RaycastResult>, bool> _isReachable;

private readonly Func<GameObject, Func<GameObject, Vector2>, PointerEventData, List<RaycastResult>, bool>
_isReachable;

private readonly PointerEventData _eventData = new PointerEventData(EventSystem.current);
private readonly List<RaycastResult> _results = new List<RaycastResult>();

Expand All @@ -55,7 +58,7 @@ public class InteractiveComponent
/// Default is <c>DefaultReachableStrategy.IsReachable</c>.</param>
public InteractiveComponent(MonoBehaviour component,
Func<GameObject, Vector2> getScreenPoint = null,
Func<GameObject, PointerEventData, List<RaycastResult>, bool> isReachable = null)
Func<GameObject, Func<GameObject, Vector2>, PointerEventData, List<RaycastResult>, bool> isReachable = null)
{
this.component = component;
_getScreenPoint = getScreenPoint ?? DefaultScreenPointStrategy.GetScreenPoint;
Expand All @@ -68,15 +71,15 @@ public InteractiveComponent(MonoBehaviour component,
/// <param name="gameObject"></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="isComponentInteractable">The function returns the <c>Component</c> is interactable or not.
/// Default is <c>DefaultComponentInteractableStrategy.IsInteractable</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>
/// <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>
public static InteractiveComponent CreateInteractableComponent(GameObject gameObject,
Func<GameObject, Vector2> getScreenPoint = null,
Func<Component, bool> isComponentInteractable = null,
Func<GameObject, PointerEventData, List<RaycastResult>, bool> isReachable = 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;
Expand All @@ -86,9 +89,7 @@ public static InteractiveComponent CreateInteractableComponent(GameObject gameOb
{
if (isComponentInteractable.Invoke(component))
{
return new InteractiveComponent(component,
getScreenPoint,
isReachable);
return new InteractiveComponent(component, getScreenPoint, isReachable);
}
}

Expand All @@ -115,8 +116,7 @@ public bool IsReallyInteractiveFromUser(Func<GameObject, Vector2> screenPointStr
/// <returns>true: this object can control by user</returns>
public bool IsReachable()
{
_eventData.position = _getScreenPoint.Invoke(gameObject);
return _isReachable.Invoke(gameObject, _eventData, _results);
return _isReachable.Invoke(gameObject, _getScreenPoint, _eventData, _results);
}

/// <summary>
Expand Down Expand Up @@ -150,7 +150,6 @@ public bool IsReachable()
/// <summary>
/// Touch-and-hold inner component
/// </summary>
/// <param name="screenPointStrategy">Function returns the screen position where monkey operators operate on for the specified gameObject</param>
/// <param name="delayMillis">Delay time between down to up</param>
/// <param name="cancellationToken">Task cancellation token</param>
public async UniTask TouchAndHold(int delayMillis = 1000, CancellationToken cancellationToken = default)
Expand Down
22 changes: 12 additions & 10 deletions Runtime/InteractiveComponentCollector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,11 @@ namespace TestHelper.Monkey
public class InteractiveComponentCollector
{
private readonly Func<GameObject, Vector2> _getScreenPoint;
private readonly Func<Component, bool> _isComponentInteractable;
private readonly Func<GameObject, PointerEventData, List<RaycastResult>, bool> _isReachable;

private readonly Func<GameObject, Func<GameObject, Vector2>, PointerEventData, List<RaycastResult>, bool>
_isReachable;

private readonly Func<Component, bool> _isInteractable;
private readonly PointerEventData _eventData = new PointerEventData(EventSystem.current);
private readonly List<RaycastResult> _results = new List<RaycastResult>();

Expand All @@ -28,18 +31,18 @@ public class InteractiveComponentCollector
/// </summary>
/// <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="isComponentInteractable">The function returns the <c>Component</c> is interactable or not.
/// Default is <c>DefaultComponentInteractableStrategy.IsInteractable</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>
/// <param name="isInteractable">The function returns the <c>Component</c> is interactable or not.
/// Default is <c>DefaultComponentInteractableStrategy.IsInteractable</c>.</param>
public InteractiveComponentCollector(
Func<GameObject, Vector2> getScreenPoint = null,
Func<Component, bool> isComponentInteractable = null,
Func<GameObject, PointerEventData, List<RaycastResult>, bool> isReachable = null)
Func<GameObject, Func<GameObject, Vector2>, PointerEventData, List<RaycastResult>, bool> isReachable = null,
Func<Component, bool> isInteractable = null)
{
_getScreenPoint = getScreenPoint ?? DefaultScreenPointStrategy.GetScreenPoint;
_isComponentInteractable = isComponentInteractable ?? DefaultComponentInteractableStrategy.IsInteractable;
_isReachable = isReachable ?? DefaultReachableStrategy.IsReachable;
_isInteractable = isInteractable ?? DefaultComponentInteractableStrategy.IsInteractable;
}

/// <summary>
Expand All @@ -53,7 +56,7 @@ public IEnumerable<InteractiveComponent> FindInteractableComponents()
{
foreach (var component in FindMonoBehaviours())
{
if (_isComponentInteractable.Invoke(component))
if (_isInteractable.Invoke(component))
{
yield return new InteractiveComponent(component,
_getScreenPoint,
Expand All @@ -80,8 +83,7 @@ public IEnumerable<InteractiveComponent> FindReachableInteractableComponents()
{
foreach (var interactiveComponent in FindInteractableComponents())
{
_eventData.position = _getScreenPoint.Invoke(interactiveComponent.gameObject);
if (_isReachable.Invoke(interactiveComponent.gameObject, _eventData, _results))
if (_isReachable.Invoke(interactiveComponent.gameObject, _getScreenPoint, _eventData, _results))
{
yield return interactiveComponent;
}
Expand Down
6 changes: 3 additions & 3 deletions Runtime/Monkey.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@ public static async UniTask Run(MonkeyConfig config, CancellationToken cancellat
}

var interactiveComponentCollector = new InteractiveComponentCollector(
getScreenPoint: config.ScreenPointStrategy
);
// TODO: Set other strategies
getScreenPoint: config.ScreenPointStrategy,
isReachable: config.IsReachable,
isInteractable: config.IsInteractable);

config.Logger.Log($"Using {config.Random}");

Expand Down
15 changes: 15 additions & 0 deletions Runtime/MonkeyConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@
// This software is released under the MIT License.

using System;
using System.Collections.Generic;
using TestHelper.Monkey.DefaultStrategies;
using TestHelper.Monkey.Random;
using TestHelper.Monkey.ScreenPointStrategies;
using TestHelper.Random;
using UnityEngine;
using UnityEngine.EventSystems;

namespace TestHelper.Monkey
{
Expand Down Expand Up @@ -54,6 +57,18 @@ public class MonkeyConfig
/// </summary>
public Func<GameObject, Vector2> ScreenPointStrategy { get; set; } = DefaultScreenPointStrategy.GetScreenPoint;

/// <summary>
/// Function returns the <c>GameObject</c> is reachable from user or not.
/// </summary>
public Func<GameObject, Func<GameObject, Vector2>, PointerEventData, List<RaycastResult>, bool>
IsReachable { get; set; } = DefaultReachableStrategy.IsReachable;

/// <summary>
/// Function returns the <c>Component</c> is interactable or not.
/// </summary>
public Func<Component, bool>
IsInteractable { get; set; } = DefaultComponentInteractableStrategy.IsInteractable;

/// <summary>
/// Function returns the random string generation parameters
/// </summary>
Expand Down

0 comments on commit 87b7e4c

Please sign in to comment.