step-timeline/Scripts/Steps/StepGroup.cs

353 lines
12 KiB
C#

using UnityEngine;
using System.Collections.Generic;
using UnityEngine.Events;
using TriInspector;
namespace R0bbie.Timeline
{
public class StepGroup : Step
{
// Exposed Variables
[Title("Events")]
public UnityEvent onGroupPlayEvent;
public UnityEvent onGroupCompleteEvent;
// Private Variables
List<Step> childSteps = new List<Step>();
int activeChildStepIndex;
// Properties
public Step activeChildStep { get; private set; }
public bool activeChildStepIsSwitch { get; private set; }
/// <summary>
/// Initialise the StepGroup, setting up required external refs, and getting refs to all child steps
/// </summary>
public override void Init(StepTimeline _stepTimeline)
{
// Setup necessary refs to parent timeline, and child steps (only 1 level deep to avoid getting children of StepSwitches etc)
attachedStepTimeline = _stepTimeline;
foreach (Transform t in transform)
{
if (t.GetComponent<Step>())
{
childSteps.Add(t.GetComponent<Step>());
}
}
// Init all child steps
foreach (var step in childSteps)
{
step.Init(attachedStepTimeline, this);
}
init = true;
}
/// <summary>
/// Initialise the StepGroup if StepController initialising
/// </summary>
public override void Init(StepController _controller)
{
// Setup necessary refs to parent Controller, and child steps (only 1 level deep to avoid getting children of StepSwitches etc)
attachedController = _controller;
foreach (Transform t in transform)
{
if (t.GetComponent<Step>())
{
childSteps.Add(t.GetComponent<Step>());
}
}
// Init all child steps
foreach (var step in childSteps)
{
step.Init(attachedController, this);
}
init = true;
}
/// <summary>
/// Play this StepGroup. Starting with the first step in it.
/// </summary>
public override void Play()
{
// Set the index to start at the first step
activeChildStepIndex = 0;
Debug.Log(name + " - Started");
wasCompleted = false;
isActive = true;
onGroupPlayEvent?.Invoke();
activeChildStep = childSteps[activeChildStepIndex];
activeChildStep.Play();
}
/// <summary>
/// Continue and play the next step in the group, unless we're at the end of the group
/// </summary>
public override void Continue()
{
activeChildStepIndex++;
// Check we've not reached the end of the step group, before playing the next step (if there is one)
if (activeChildStepIndex >= childSteps.Count)
{
GroupCompleted();
}
else
{
activeChildStep = childSteps[activeChildStepIndex];
if (activeChildStep is StepSwitch)
activeChildStepIsSwitch = true;
activeChildStep.Play();
}
}
/// <summary>
/// Skip this entire step group
/// </summary>
public override void Skip()
{
wasSkipped = true;
isActive = false;
// TODO: Check if currently active child step needs tidied up before skipping?
// TODO: Mark all steps within the step group not played before Skip was called as skipped also?
if (parentStepGroup)
{
parentStepGroup.Continue();
}
else if (parentStepSwitch)
{
parentStepSwitch.Continue();
}
else
{
if (attachedStepTimeline != null)
attachedStepTimeline.PlayNextStep();
else
attachedController.StepCompleted();
}
}
/// <summary>
/// Play a specific step which may not be the "next" one (may mean replaying an already played step or jumping to a future one), and update activeStepIndex to the desired one
/// </summary>
/// <param name="_step">Reference to the Step the timeline should now play, as part of the group</param>
public void GoToStepAndPlay(Step _step)
{
// If group wasn't already active, we'll set it active now
if (!isActive)
{
isActive = true;
onGroupPlayEvent?.Invoke();
}
// If requested step has parent step groups above this one, make sure their status is set to active. Otherwise we're at the highest group in the hierarchy, so make sure this is active in the timeline
if (parentStepGroup)
parentStepGroup.SetActiveOnGoToChild(this);
else
attachedStepTimeline.SetSpecifiedHighestStepGroupActive(this);
// Set the index to the newly requested step
activeChildStepIndex = childSteps.IndexOf(_step);
// Set new active step, then play it
activeChildStep = childSteps[activeChildStepIndex];
activeChildStep.Play();
}
/// <summary>
/// When a child step is played using GoToStep, if that step was nested within multiple step groups, this function may be called by a child step group to tell this step group that it's now active
/// </summary>
private void SetActiveOnGoToChild(StepGroup _childStepGroup)
{
// If group wasn't already active, we'll set it active now
if (!isActive)
{
isActive = true;
onGroupPlayEvent?.Invoke();
}
// Set the index and active step on this group
activeChildStepIndex = childSteps.IndexOf(_childStepGroup);
activeChildStep = childSteps[activeChildStepIndex];
// Then set any other parent step groups active too (all the way up the hierarchy, till we make it to the timeline, then till timeline to mark highest level group as active)
if (parentStepGroup)
parentStepGroup.SetActiveOnGoToChild(this);
else
attachedStepTimeline.SetSpecifiedHighestStepGroupActive(this);
}
/// <summary>
/// Called externally to force this step to end, without automatically continuing
/// </summary>
public override void ForceEnd()
{
isActive = false;
if (activeChildStep)
activeChildStep.ForceEnd();
}
/// <summary>
/// Called when the last step in the group has completed
/// </summary>
private void GroupCompleted()
{
if (wasCompleted)
return;
wasCompleted = true;
isActive = false;
// We are currently having just one depth child, so it's likely that parentStepGroup & parentStepSwitch here won't ever play on a GroupCompleted call
// Tell the timeline to play the next step (or step group, or controller)
if (parentStepGroup)
{
parentStepGroup.Continue();
}
else if (parentStepSwitch)
{
parentStepSwitch.Continue();
}
else
{
if (attachedStepTimeline != null)
attachedStepTimeline.PlayNextStep();
else
attachedController.StepCompleted();
}
// Play the event at the end to make sure all the functionality and bools have been disabled
onGroupCompleteEvent?.Invoke();
}
#if UNITY_EDITOR
// ODIN INSPECTOR BUTTONS
[Title("Add to Group")]
[Button("Add Step")]
private void Editor_AddStep()
{
// Create new GameObject, and reparent it as a child of the StepGroup parent object
GameObject stepGo = new GameObject();
stepGo.transform.parent = transform;
// Add Step component to this new GameObject
Step step = stepGo.AddComponent<Step>();
// Rename object
int parentStepNo = transform.parent.GetSiblingIndex() + 1;
int childStepNo = step.transform.GetSiblingIndex() + 1;
step.gameObject.name = "#" + parentStepNo + "." + childStepNo + " - *ADD STEP NAME*";
}
[Button("Add Step Switch")]
private void Editor_AddStepSwitch()
{
// Create new GameObject, and reparent it as a child of the StepGroup parent object
GameObject stepSwitchGo = new GameObject();
stepSwitchGo.transform.parent = transform;
// Player must add the specific StepSwitch type they like, remind them
Debug.Log("Empty StepSwitch object created as child of StepGroup, now remember to add the specific StepSwitch type you want to it.");
// Rename object
int parentStepNo = transform.parent.GetSiblingIndex() + 1;
int childStepNo = stepSwitchGo.transform.GetSiblingIndex() + 1;
stepSwitchGo.gameObject.name = "#" + parentStepNo + "." + childStepNo + " - SWITCH - *ADD DESIRED SWITCH COMPONENT*";
}
[Button("Add Step Looper")]
private void Editor_AddStepLooper()
{
// Create new GameObject, and reparent it as a child of the Timeline parent object
GameObject stepLooperGo = new GameObject();
stepLooperGo.transform.parent = transform;
// Add StepLoop component to this new GameObject
StepLooper step = stepLooperGo.AddComponent<StepLooper>();
// Rename object
int parentStepNo = transform.parent.GetSiblingIndex() + 1;
int childStepNo = step.transform.GetSiblingIndex() + 1;
step.gameObject.name = "#" + parentStepNo + "." + childStepNo + " - LOOP - *ADD STEP NAME*";
}
[Button("Add GoTo Step")]
private void Editor_AddGoToStep()
{
// Create new GameObject, and reparent it as a child of the Timeline parent object
GameObject gotoStepGo = new GameObject();
gotoStepGo.transform.parent = transform;
// Add GoToStep component to this new GameObject
GoToStep step = gotoStepGo.AddComponent<GoToStep>();
// Rename object
int parentStepNo = transform.parent.GetSiblingIndex() + 1;
int childStepNo = step.transform.GetSiblingIndex() + 1;
gotoStepGo.gameObject.name = "#" + parentStepNo + "." + childStepNo + " - GOTO - *ADD STEP NAME*";
}
[Title("Editor Functions")]
[Button("Rename Child Steps")]
private void Editor_RenameChildren()
{
RenameChildren();
}
// OTHER EDITOR HELPER FUNCTIONS
public void RenameChildren()
{
foreach (Transform child in transform)
{
if (child.GetComponent<Step>())
child.GetComponent<Step>().UpdateStepName();
}
}
#endif
}
}