An easy to use Unity 3D library for creating illustrated Interactive Fiction games and more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

379 lines
13 KiB

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using UnityEngine;
using Debug = UnityEngine.Debug;
using Object = UnityEngine.Object;
namespace UnityTest
{
[Serializable]
public class AssertionComponent : MonoBehaviour, IAssertionComponentConfigurator
{
[SerializeField] public float checkAfterTime = 1f;
[SerializeField] public bool repeatCheckTime = true;
[SerializeField] public float repeatEveryTime = 1f;
[SerializeField] public int checkAfterFrames = 1;
[SerializeField] public bool repeatCheckFrame = true;
[SerializeField] public int repeatEveryFrame = 1;
[SerializeField] public bool hasFailed;
[SerializeField] public CheckMethod checkMethods = CheckMethod.Start;
[SerializeField] private ActionBase m_ActionBase;
[SerializeField] public int checksPerformed = 0;
private int m_CheckOnFrame;
private string m_CreatedInFilePath = "";
private int m_CreatedInFileLine = -1;
public ActionBase Action
{
get { return m_ActionBase; }
set
{
m_ActionBase = value;
m_ActionBase.go = gameObject;
}
}
public Object GetFailureReferenceObject()
{
#if UNITY_EDITOR
if (!string.IsNullOrEmpty(m_CreatedInFilePath))
{
return UnityEditor.AssetDatabase.LoadAssetAtPath(m_CreatedInFilePath, typeof(Object));
}
#endif
return this;
}
public string GetCreationLocation()
{
if (!string.IsNullOrEmpty(m_CreatedInFilePath))
{
var idx = m_CreatedInFilePath.LastIndexOf("\\") + 1;
return string.Format("{0}, line {1} ({2})", m_CreatedInFilePath.Substring(idx), m_CreatedInFileLine, m_CreatedInFilePath);
}
return "";
}
public void Awake()
{
if (!Debug.isDebugBuild)
Destroy(this);
OnComponentCopy();
}
public void OnValidate()
{
if (Application.isEditor)
OnComponentCopy();
}
private void OnComponentCopy()
{
if (m_ActionBase == null) return;
var oldActionList = Resources.FindObjectsOfTypeAll(typeof(AssertionComponent)).Where(o => ((AssertionComponent)o).m_ActionBase == m_ActionBase && o != this);
// if it's not a copy but a new component don't do anything
if (!oldActionList.Any()) return;
if (oldActionList.Count() > 1)
Debug.LogWarning("More than one refence to comparer found. This shouldn't happen");
var oldAction = oldActionList.First() as AssertionComponent;
m_ActionBase = oldAction.m_ActionBase.CreateCopy(oldAction.gameObject, gameObject);
}
public void Start()
{
CheckAssertionFor(CheckMethod.Start);
if (IsCheckMethodSelected(CheckMethod.AfterPeriodOfTime))
{
StartCoroutine("CheckPeriodically");
}
if (IsCheckMethodSelected(CheckMethod.Update))
{
m_CheckOnFrame = Time.frameCount + checkAfterFrames;
}
}
public IEnumerator CheckPeriodically()
{
yield return new WaitForSeconds(checkAfterTime);
CheckAssertionFor(CheckMethod.AfterPeriodOfTime);
while (repeatCheckTime)
{
yield return new WaitForSeconds(repeatEveryTime);
CheckAssertionFor(CheckMethod.AfterPeriodOfTime);
}
}
public bool ShouldCheckOnFrame()
{
if (Time.frameCount > m_CheckOnFrame)
{
if (repeatCheckFrame)
m_CheckOnFrame += repeatEveryFrame;
else
m_CheckOnFrame = Int32.MaxValue;
return true;
}
return false;
}
public void OnDisable()
{
CheckAssertionFor(CheckMethod.OnDisable);
}
public void OnEnable()
{
CheckAssertionFor(CheckMethod.OnEnable);
}
public void OnDestroy()
{
CheckAssertionFor(CheckMethod.OnDestroy);
}
public void Update()
{
if (IsCheckMethodSelected(CheckMethod.Update) && ShouldCheckOnFrame())
{
CheckAssertionFor(CheckMethod.Update);
}
}
public void FixedUpdate()
{
CheckAssertionFor(CheckMethod.FixedUpdate);
}
public void LateUpdate()
{
CheckAssertionFor(CheckMethod.LateUpdate);
}
public void OnControllerColliderHit()
{
CheckAssertionFor(CheckMethod.OnControllerColliderHit);
}
public void OnParticleCollision()
{
CheckAssertionFor(CheckMethod.OnParticleCollision);
}
public void OnJointBreak()
{
CheckAssertionFor(CheckMethod.OnJointBreak);
}
public void OnBecameInvisible()
{
CheckAssertionFor(CheckMethod.OnBecameInvisible);
}
public void OnBecameVisible()
{
CheckAssertionFor(CheckMethod.OnBecameVisible);
}
public void OnTriggerEnter()
{
CheckAssertionFor(CheckMethod.OnTriggerEnter);
}
public void OnTriggerExit()
{
CheckAssertionFor(CheckMethod.OnTriggerExit);
}
public void OnTriggerStay()
{
CheckAssertionFor(CheckMethod.OnTriggerStay);
}
public void OnCollisionEnter()
{
CheckAssertionFor(CheckMethod.OnCollisionEnter);
}
public void OnCollisionExit()
{
CheckAssertionFor(CheckMethod.OnCollisionExit);
}
public void OnCollisionStay()
{
CheckAssertionFor(CheckMethod.OnCollisionStay);
}
public void OnTriggerEnter2D()
{
CheckAssertionFor(CheckMethod.OnTriggerEnter2D);
}
public void OnTriggerExit2D()
{
CheckAssertionFor(CheckMethod.OnTriggerExit2D);
}
public void OnTriggerStay2D()
{
CheckAssertionFor(CheckMethod.OnTriggerStay2D);
}
public void OnCollisionEnter2D()
{
CheckAssertionFor(CheckMethod.OnCollisionEnter2D);
}
public void OnCollisionExit2D()
{
CheckAssertionFor(CheckMethod.OnCollisionExit2D);
}
public void OnCollisionStay2D()
{
CheckAssertionFor(CheckMethod.OnCollisionStay2D);
}
private void CheckAssertionFor(CheckMethod checkMethod)
{
if (IsCheckMethodSelected(checkMethod))
{
Assertions.CheckAssertions(this);
}
}
public bool IsCheckMethodSelected(CheckMethod method)
{
return method == (checkMethods & method);
}
#region Assertion Component create methods
public static T Create<T>(CheckMethod checkOnMethods, GameObject gameObject, string propertyPath) where T : ActionBase
{
IAssertionComponentConfigurator configurator;
return Create<T>(out configurator, checkOnMethods, gameObject, propertyPath);
}
public static T Create<T>(out IAssertionComponentConfigurator configurator, CheckMethod checkOnMethods, GameObject gameObject, string propertyPath) where T : ActionBase
{
return CreateAssertionComponent<T>(out configurator, checkOnMethods, gameObject, propertyPath);
}
public static T Create<T>(CheckMethod checkOnMethods, GameObject gameObject, string propertyPath, GameObject gameObject2, string propertyPath2) where T : ComparerBase
{
IAssertionComponentConfigurator configurator;
return Create<T>(out configurator, checkOnMethods, gameObject, propertyPath, gameObject2, propertyPath2);
}
public static T Create<T>(out IAssertionComponentConfigurator configurator, CheckMethod checkOnMethods, GameObject gameObject, string propertyPath, GameObject gameObject2, string propertyPath2) where T : ComparerBase
{
var comparer = CreateAssertionComponent<T>(out configurator, checkOnMethods, gameObject, propertyPath);
comparer.compareToType = ComparerBase.CompareToType.CompareToObject;
comparer.other = gameObject2;
comparer.otherPropertyPath = propertyPath2;
return comparer;
}
public static T Create<T>(CheckMethod checkOnMethods, GameObject gameObject, string propertyPath, object constValue) where T : ComparerBase
{
IAssertionComponentConfigurator configurator;
return Create<T>(out configurator, checkOnMethods, gameObject, propertyPath, constValue);
}
public static T Create<T>(out IAssertionComponentConfigurator configurator, CheckMethod checkOnMethods, GameObject gameObject, string propertyPath, object constValue) where T : ComparerBase
{
var comparer = CreateAssertionComponent<T>(out configurator, checkOnMethods, gameObject, propertyPath);
if (constValue == null)
{
comparer.compareToType = ComparerBase.CompareToType.CompareToNull;
return comparer;
}
comparer.compareToType = ComparerBase.CompareToType.CompareToConstantValue;
comparer.ConstValue = constValue;
return comparer;
}
private static T CreateAssertionComponent<T>(out IAssertionComponentConfigurator configurator, CheckMethod checkOnMethods, GameObject gameObject, string propertyPath) where T : ActionBase
{
var ac = gameObject.AddComponent<AssertionComponent>();
ac.checkMethods = checkOnMethods;
var comparer = ScriptableObject.CreateInstance<T>();
ac.Action = comparer;
ac.Action.go = gameObject;
ac.Action.thisPropertyPath = propertyPath;
configurator = ac;
#if !UNITY_METRO
var stackTrace = new StackTrace(true);
var thisFileName = stackTrace.GetFrame(0).GetFileName();
for (int i = 1; i < stackTrace.FrameCount; i++)
{
var stackFrame = stackTrace.GetFrame(i);
if (stackFrame.GetFileName() != thisFileName)
{
string filePath = stackFrame.GetFileName().Substring(Application.dataPath.Length - "Assets".Length);
ac.m_CreatedInFilePath = filePath;
ac.m_CreatedInFileLine = stackFrame.GetFileLineNumber();
break;
}
}
#endif // if !UNITY_METRO
return comparer;
}
#endregion
#region AssertionComponentConfigurator
public int UpdateCheckStartOnFrame { set { checkAfterFrames = value; } }
public int UpdateCheckRepeatFrequency { set { repeatEveryFrame = value; } }
public bool UpdateCheckRepeat { set { repeatCheckFrame = value; } }
public float TimeCheckStartAfter { set { checkAfterTime = value; } }
public float TimeCheckRepeatFrequency { set { repeatEveryTime = value; } }
public bool TimeCheckRepeat { set { repeatCheckTime = value; } }
public AssertionComponent Component { get { return this; } }
#endregion
}
public interface IAssertionComponentConfigurator
{
/// <summary>
/// If the assertion is evaluated in Update, after how many frame should the evaluation start. Deafult is 1 (first frame)
/// </summary>
int UpdateCheckStartOnFrame { set; }
/// <summary>
/// If the assertion is evaluated in Update and UpdateCheckRepeat is true, how many frame should pass between evaluations
/// </summary>
int UpdateCheckRepeatFrequency { set; }
/// <summary>
/// If the assertion is evaluated in Update, should the evaluation be repeated after UpdateCheckRepeatFrequency frames
/// </summary>
bool UpdateCheckRepeat { set; }
/// <summary>
/// If the assertion is evaluated after a period of time, after how many seconds the first evaluation should be done
/// </summary>
float TimeCheckStartAfter { set; }
/// <summary>
/// If the assertion is evaluated after a period of time and TimeCheckRepeat is true, after how many seconds should the next evaluation happen
/// </summary>
float TimeCheckRepeatFrequency { set; }
/// <summary>
/// If the assertion is evaluated after a period, should the evaluation happen again after TimeCheckRepeatFrequency seconds
/// </summary>
bool TimeCheckRepeat { set; }
AssertionComponent Component { get; }
}
}