using UnityEngine;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Rive.Utils;
namespace Rive
{
///
/// Represents a Rive StateMachine from an Artboard. A StateMachine contains Inputs.
///
public class StateMachine : IDisposable
{
private readonly IntPtr m_nativeStateMachine;
private ViewModelInstance m_currentViewModelInstance;
private string m_stateMachineName;
private bool m_isDisposed = false;
internal IntPtr NativeStateMachine => m_nativeStateMachine;
///
/// Returns true if the state machine has been disposed.
///
public bool IsDisposed { get => m_isDisposed; }
internal StateMachine(IntPtr nativeStateMachine)
{
m_nativeStateMachine = nativeStateMachine;
}
///
/// Dispose of the StateMachine and release native resources.
///
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!m_isDisposed)
{
if (m_nativeStateMachine != IntPtr.Zero)
{
unrefStateMachine(m_nativeStateMachine);
}
m_isDisposed = true;
}
}
~StateMachine()
{
Dispose(false);
}
public string Name
{
get
{
if (m_stateMachineName == null)
{
m_stateMachineName = Marshal.PtrToStringAnsi(stateMachineGetName(m_nativeStateMachine));
}
return m_stateMachineName;
}
}
///
/// The current ViewModelInstance set as the data context of the StateMachine.
///
public ViewModelInstance ViewModelInstance
{
get { return m_currentViewModelInstance; }
}
public bool Advance(float seconds)
{
return advanceStateMachine(m_nativeStateMachine, seconds);
}
/// The number of Inputs stored in the StateMachine.
public uint InputCount()
{
return getSMIInputCountStateMachine(m_nativeStateMachine);
}
/// The SMIInput at the given index.
public SMIInput Input(uint index)
{
IntPtr ptr = getSMIInputFromIndexStateMachine(m_nativeStateMachine, index);
return ptr == IntPtr.Zero ? null : new SMIInput(ptr, this);
}
private SMIInput ConvertInput(SMIInput input)
{
if (input.IsBoolean)
{
return new SMIBool(input.NativeSMI, this);
}
else if (input.IsTrigger)
{
return new SMITrigger(input.NativeSMI, this);
}
else if (input.IsNumber)
{
return new SMINumber(input.NativeSMI, this);
}
else
{
return null;
}
}
/// A list of all the SMIInputs stored in the StateMachine.
public List Inputs()
{
var list = new List();
for (uint i = 0; i < InputCount(); i++)
{
var inputAtIndex = Input(i);
if (inputAtIndex == null)
{
continue;
}
var converted = ConvertInput(inputAtIndex);
if (converted != null)
{
list.Add(converted);
}
}
return list;
}
///
/// Get a SMIBool by name.
///
///
/// A SMIBool.value is a boolean that can be get/set
///
public SMIBool GetBool(string name)
{
IntPtr ptr = getSMIBoolStateMachine(m_nativeStateMachine, name);
if (ptr != IntPtr.Zero)
return new SMIBool(ptr, this);
DebugLogger.Instance.Log($"No SMIBool found with name: {name}.");
return null;
}
///
/// Get a SMITrigger by name.
///
///
/// A SMITrigger contains a fire method to trigger.
///
public SMITrigger GetTrigger(string name)
{
IntPtr ptr = getSMITriggerStateMachine(m_nativeStateMachine, name);
if (ptr != IntPtr.Zero)
return new SMITrigger(ptr, this);
DebugLogger.Instance.Log($"No SMITrigger found with name: {name}.");
return null;
}
///
/// Get a SMINumber by name.
///
///
/// A SMINumber.value is a float that can be get/set
///
public SMINumber GetNumber(string name)
{
IntPtr ptr = getSMINumberStateMachine(m_nativeStateMachine, name);
if (ptr != IntPtr.Zero)
return new SMINumber(ptr, this);
DebugLogger.Instance.Log($"No SMINumber found with name: {name}.");
return null;
}
///
/// Move the pointer to the given position
///
public HitResult PointerMove(Vector2 position, int pointerId = 0)
{
return (HitResult)pointerMoveStateMachineWithHit(m_nativeStateMachine, position.x, position.y, pointerId);
}
///
/// Press the pointer at the given position
///
public HitResult PointerDown(Vector2 position, int pointerId = 0)
{
return (HitResult)pointerDownStateMachineWithHit(m_nativeStateMachine, position.x, position.y, pointerId);
}
///
/// Release the pointer at the given position
///
public HitResult PointerUp(Vector2 position, int pointerId = 0)
{
return (HitResult)pointerUpStateMachineWithHit(m_nativeStateMachine, position.x, position.y, pointerId);
}
///
/// Exit the pointer at the given position
///
public HitResult PointerExit(Vector2 position, int pointerId = 0)
{
return (HitResult)pointerExitStateMachineWithHit(m_nativeStateMachine, position.x, position.y, pointerId);
}
///
/// Performs a hit test at the given position
///
/// The position to test in local coordinates
/// True if the position hits a component with a listener, false otherwise
public bool HitTest(Vector2 position)
{
return hitTestStateMachine(m_nativeStateMachine, position.x, position.y);
}
///
/// A list of all the reported events received in the past frame.
///
public List ReportedEvents()
{
uint count = getReportedEventCount(m_nativeStateMachine);
var list = new List();
for (uint i = 0; i < count; i++)
{
list.Add(ReportedEvent.GetPooled(getReportedEventAt(m_nativeStateMachine, i)));
}
return list;
}
///
/// Fetches the reported events received by the StateMachine in the past frame and populates the given list.
///
/// The list to populate with reported events.
public void ReportedEvents(List reportedEvents)
{
uint count = getReportedEventCount(m_nativeStateMachine);
for (uint i = 0; i < count; i++)
{
reportedEvents.Add(ReportedEvent.GetPooled(getReportedEventAt(m_nativeStateMachine, i)));
}
}
///
/// Enumerates through all reported events received by the StateMachine in the past frame.
///
/// An IEnumerable of ReportedEvents
public IEnumerable EnumerateReportedEvents()
{
uint count = getReportedEventCount(m_nativeStateMachine);
for (uint i = 0; i < count; i++)
{
yield return ReportedEvent.GetPooled(getReportedEventAt(m_nativeStateMachine, i));
}
}
///
/// Sets the data context of the StateMachine from the given ViewModelInstance.
///
///
/// This method also binds the underlying Artboard to the ViewModelInstance. It is recommended to call this method to automatically bind the ViewModelInstance to the StateMachine and the Artboard.
///
/// The ViewModelInstance to bind to the StateMachine.
public void BindViewModelInstance(ViewModelInstance viewModelInstance)
{
if (viewModelInstance == null)
{
DebugLogger.Instance.LogError("ViewModelInstance is null.");
return;
}
bindViewModelInstanceToStateMachine(m_nativeStateMachine, viewModelInstance.NativeSafeHandle);
m_currentViewModelInstance = viewModelInstance; // Store the current ViewModelInstance to keep the VM alive
}
#region Native Methods
[DllImport(NativeLibrary.name)]
internal static extern void unrefStateMachine(IntPtr stateMachine);
[DllImport(NativeLibrary.name)]
internal static extern bool advanceStateMachine(IntPtr stateMachine, float seconds);
[DllImport(NativeLibrary.name)]
internal static extern uint getSMIInputCountStateMachine(IntPtr stateMachine);
[DllImport(NativeLibrary.name)]
internal static extern IntPtr getSMIInputFromIndexStateMachine(
IntPtr stateMachine,
uint index
);
[DllImport(NativeLibrary.name)]
internal static extern IntPtr getSMIBoolStateMachine(IntPtr stateMachine, string name);
[DllImport(NativeLibrary.name)]
internal static extern IntPtr getSMITriggerStateMachine(IntPtr stateMachine, string name);
[DllImport(NativeLibrary.name)]
internal static extern IntPtr getSMINumberStateMachine(IntPtr stateMachine, string name);
[DllImport(NativeLibrary.name)]
internal static extern byte pointerMoveStateMachineWithHit(IntPtr smi, float x, float y, int pointerId);
[DllImport(NativeLibrary.name)]
internal static extern byte pointerDownStateMachineWithHit(IntPtr smi, float x, float y, int pointerId);
[DllImport(NativeLibrary.name)]
internal static extern byte pointerUpStateMachineWithHit(IntPtr smi, float x, float y, int pointerId);
[DllImport(NativeLibrary.name)]
internal static extern byte pointerExitStateMachineWithHit(IntPtr smi, float x, float y, int pointerId);
[DllImport(NativeLibrary.name)]
internal static extern bool hitTestStateMachine(IntPtr stateMachine, float x, float y);
[DllImport(NativeLibrary.name)]
internal static extern uint getReportedEventCount(IntPtr stateMachine);
[DllImport(NativeLibrary.name)]
internal static extern ReportedEventData getReportedEventAt(
IntPtr stateMachine,
uint index
);
[DllImport(NativeLibrary.name)]
internal static extern IntPtr stateMachineGetName(IntPtr stateMachine);
// Data binding
[DllImport(NativeLibrary.name)]
internal static extern void bindViewModelInstanceToStateMachine(IntPtr stateMachine, ViewModelInstanceSafeHandle viewModelInstance);
#endregion
}
}