Turn characters along a pre-defined curve

The following Action is an alternative to the **Character: Face object** Action, and lets you define an Animation Curve that describes the exact rate at which the character turns.

To install:

1. Copy/paste the code below into a new C# script named **ActionCharFaceCurve**, and place in a unique folder
2. Point to this folder in the Actions Manager's **Custom Actions** section
3. Use the new **Character: Face object curve** Action

ActionCharFaceCurve.cs:

```
using UnityEngine;
using System.Collections.Generic;

#if UNITY\_EDITOR
using UnityEditor;
#endif

namespace AC
{

 [System.Serializable]
 public class ActionCharFaceCurve : Action
 {

 public int charToMoveParameterID = -1;
 public int faceObjectParameterID = -1;

 public int charToMoveID = 0;
 public int faceObjectID = 0;

 public AnimationCurve customCurve = new AnimationCurve (new Keyframe(0, 0), new Keyframe(1, 1));

 public Char charToMove;
 protected Char runtimeCharToMove;
 public GameObject faceObject;
 protected GameObject runtimeFaceObject;
 public bool copyRotation;

 public bool facePlayer;
 public int facePlayerID = -1;

 public bool isPlayer;
 public int playerID = -1;
 public int playerParameterID = -1;

 private float startTime;
 private Vector3 startLookDirection;

 public override ActionCategory Category { get { return ActionCategory.Character; }}
 public override string Title { get { return "Face object"; }}
 public override string Description { get { return "Makes a Character turn, either instantly or over time. Can turn to face another object, or copy that object's facing direction."; }}

 public override void AssignValues (List<ActionParameter> parameters)
 {
 if (isPlayer)
 {
 runtimeCharToMove = AssignPlayer (playerID, parameters, playerParameterID);
 }
 else
 {
 runtimeCharToMove = AssignFile <Char> (parameters, charToMoveParameterID, charToMoveID, charToMove);
 }

 if (facePlayer)
 {
 Player \_player = AssignPlayer (facePlayerID, parameters, faceObjectParameterID);
 runtimeFaceObject = (\_player != null) ? \_player.gameObject : null;
 }
 else
 {
 runtimeFaceObject = AssignFile (parameters, faceObjectParameterID, faceObjectID, faceObject);
 }
 }

 public override float Run ()
 {
 if (!isRunning)
 {
 isRunning = true;

 if (runtimeFaceObject == null || runtimeCharToMove == null)
 {
 return 0f;
 }

 if (runtimeCharToMove.IsMovingAlongPath ())
 {
 runtimeCharToMove.EndPath ();
 }

 startTime = Time.time;
 startLookDirection = runtimeCharToMove.transform.forward;
 bool isFinished = ApplyCurve ();
 return isFinished ? 0f : defaultPauseTime;
 }
 else
 {
 if (ApplyCurve ())
 {
 isRunning = false;
 return 0f;
 }
 return defaultPauseTime;
 }
 }

 public override void Skip ()
 {
 if (runtimeCharToMove && runtimeFaceObject)
 {
 runtimeCharToMove.SetLookDirection (GetLookVector (), true);
 }
 }

 private bool ApplyCurve ()
 {
 Vector3 endDirection = GetLookVector ();
 float timeValue = Time.time - startTime;
 float endTime = customCurve.keys[customCurve.keys.Length - 1].time;

 if (timeValue >= endTime)
 {
 runtimeCharToMove.SetLookDirection (endDirection, true);
 return true;
 }

 float amount = customCurve.Evaluate (timeValue);
 Vector3 lookDirection = Vector3.Slerp (startLookDirection, endDirection, amount);
 runtimeCharToMove.SetLookDirection (lookDirection, true);
 return false;
 }

 protected Vector3 GetLookVector ()
 {
 if (copyRotation)
 {
 Marker runtimeMarker = runtimeFaceObject.GetComponent <Marker>();
 if (runtimeMarker)
 {
 return runtimeMarker.ForwardDirection;
 }

 return runtimeFaceObject.transform.forward;
 }

 Hotspot faceObjectHotspot = runtimeFaceObject.GetComponent<Hotspot> ();
 if (faceObjectHotspot && faceObjectHotspot.centrePoint && faceObjectHotspot.centrePointOverrides != CentrePointOverrides.IconPositionOnly)
 {
 runtimeFaceObject = faceObjectHotspot.centrePoint.gameObject;
 }

 if (SceneSettings.ActInScreenSpace ())
 {
 return AdvGame.GetScreenDirection (runtimeCharToMove.Transform.position, runtimeFaceObject.transform.position);
 }

 return runtimeFaceObject.transform.position - runtimeCharToMove.transform.position;
 }

 #if UNITY\_EDITOR

 public override void ShowGUI (List<ActionParameter> parameters)
 {
 bool playerSwitching = (KickStarter.settingsManager && KickStarter.settingsManager.playerSwitching == PlayerSwitching.Allow);

 isPlayer = EditorGUILayout.Toggle ("Affect Player?", isPlayer);
 if (isPlayer)
 {
 if (playerSwitching)
 {
 playerParameterID = ChooseParameterGUI ("Player ID:", parameters, playerParameterID, ParameterType.Integer);
 if (playerParameterID < 0)
 playerID = ChoosePlayerGUI (playerID, true);
 }
 }
 else
 {
 charToMoveParameterID = Action.ChooseParameterGUI ("Character to turn:", parameters, charToMoveParameterID, ParameterType.GameObject);
 if (charToMoveParameterID >= 0)
 {
 charToMoveID = 0;
 charToMove = null;
 }
 else
 {
 charToMove = (Char) EditorGUILayout.ObjectField ("Character to turn:", charToMove, typeof(Char), true);

 charToMoveID = FieldToID <Char> (charToMove, charToMoveID);
 charToMove = IDToField <Char> (charToMove, charToMoveID, false);
 }
 }

 if (isPlayer && !playerSwitching)
 {
 facePlayer = false;
 }
 else
 {
 facePlayer = EditorGUILayout.Toggle ("Face player?", facePlayer);

 if (facePlayer)
 {
 if (playerSwitching)
 {
 faceObjectParameterID = ChooseParameterGUI ("Player ID:", parameters, faceObjectParameterID, ParameterType.Integer);
 if (faceObjectParameterID < 0)
 facePlayerID = ChoosePlayerGUI (facePlayerID, true);

 if (isPlayer && playerParameterID < 0 && faceObjectParameterID < 0 && playerID == facePlayerID)
 {
 EditorGUILayout.HelpBox ("A Player cannot face themselves!", MessageType.Warning);
 }
 }
 }
 }

 if (!facePlayer)
 {
 faceObjectParameterID = Action.ChooseParameterGUI ("Object to face:", parameters, faceObjectParameterID, ParameterType.GameObject);
 if (faceObjectParameterID >= 0)
 {
 faceObjectID = 0;
 faceObject = null;
 }
 else
 {
 faceObject = (GameObject) EditorGUILayout.ObjectField ("Object to face:", faceObject, typeof(GameObject), true);

 faceObjectID = FieldToID (faceObject, faceObjectID);
 faceObject = IDToField (faceObject, faceObjectID, false);
 }
 }

 copyRotation = EditorGUILayout.Toggle ("Use object's rotation?", copyRotation);
 customCurve = EditorGUILayout.CurveField ("Curve:", customCurve);
 }

 public override void AssignConstantIDs (bool saveScriptsToo, bool fromAssetFile)
 {
 if (saveScriptsToo)
 {
 if (!isPlayer && charToMove != null && !charToMove.IsPlayer)
 {
 AddSaveScript <RememberNPC> (charToMove);
 }
 }

 if (!isPlayer)
 {
 charToMoveID = AssignConstantID<Char> (charToMove, charToMoveID, charToMoveParameterID);
 }
 faceObjectID = AssignConstantID (faceObject, faceObjectID, faceObjectParameterID);
 }

 public override string SetLabel ()
 {
 if (faceObject != null)
 {
 if (charToMove != null)
 {
 return charToMove.name + " to " + faceObject.name;
 }
 else if (isPlayer)
 {
 return "Player to " + faceObject.name;
 }
 }
 return string.Empty;
 }

 public override bool ReferencesObjectOrID (GameObject \_gameObject, int id)
 {
 if (!isPlayer && charToMoveParameterID < 0)
 {
 if (charToMove && charToMove.gameObject == \_gameObject) return true;
 if (charToMoveID == id) return true;
 }
 if (isPlayer && \_gameObject && \_gameObject.GetComponent <Player>()) return true;
 if (!facePlayer && faceObjectParameterID < 0)
 {
 if (faceObject != null && faceObject.gameObject == \_gameObject) return true;
 if (faceObjectID == id) return true;
 }
 if (facePlayer && \_gameObject && \_gameObject.GetComponent <Player>() != null) return true;
 return base.ReferencesObjectOrID (\_gameObject, id);
 }

 public override bool ReferencesPlayer (int \_playerID = -1)
 {
 if (isPlayer)
 {
 if (\_playerID < 0) return true;
 if (playerID < 0 && playerParameterID < 0) return true;
 if (playerParameterID < 0 && playerID == \_playerID) return true;
 }
 else if (facePlayer)
 {
 return true;
 }
 return false;
 }

 #endif

 }

}

```