recursal/Fanatic-Fandom huggingface dataset:
Music track addressables

By default, music track data is stored in the Settings Manager - meaning it will be included in the build and loaded upon startup. This script allows you to instead use Unity's Addressables system, so that track data is only loaded when you request it.

To use it:

1. Importing and set up Unity's Addressables package from the Package Manager.
2. Use AC's Music Editor window to define your tracks - but only give each track a label. Don't assign a track's AudioClip asset file directly.
3. For each AudioClip, instead mark it as Addressable.
4. Copy/paste the code below into a C# file named MusicAddressables.
5. Create a new GameObject, attach the new Music Addressables component, make it a prefab and remove from the scene.
6. In the Music Addressables component Inspector, fill in details of each track - pairing each track's ID number (to the left of its name in the Music Editor window) with its associated clip's Addressable.
7. When you wish to load a track into memory, use the Object: Call event Action to run the prefab's Load function, passing the ID number of the track to load.
8. (Optional) To unload a track, use the same Action to run the Unload function.

MusicAddressables.cs:

```
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.AddressableAssets;
using System.Collections;
using UnityEngine;
using AC;

public class MusicAddressables : MonoBehaviour
{

 [SerializeField] private MusicAddressable[] musicAddressables = new MusicAddressable[0];

 public void Load (int trackID)
 {
 foreach (MusicAddressable musicAddressable in musicAddressables)
 {
 if (musicAddressable.trackID == trackID) StartCoroutine (LoadCo (musicAddressable));
 }
 }

 public void Unload (int trackID)
 {
 foreach (MusicAddressable musicAddressable in musicAddressables)
 {
 if (musicAddressable.trackID == trackID) musicAddressable.Unload ();
 }
 }

 public void LoadAll ()
 {
 foreach (MusicAddressable musicAddressable in musicAddressables)
 {
 StartCoroutine (LoadCo (musicAddressable));
 }
 }

 private void OnDisable ()
 {
 foreach (MusicAddressable musicAddressable in musicAddressables)
 {
 musicAddressable.Unload ();
 }
 }

 private IEnumerator LoadCo (MusicAddressable musicAddressable)
 {
 if (!musicAddressable.handle.IsValid ())
 {
 AsyncOperationHandle<AudioClip> handle = Addressables.LoadAssetAsync<AudioClip> (musicAddressable.reference);
 yield return handle;
 musicAddressable.handle = handle;
 }

 musicAddressable.Load ();
 }

 [System.Serializable]
 private class MusicAddressable
 {

 public int trackID;
 public AssetReference reference;
 public AsyncOperationHandle<AudioClip> handle;

 public void Load ()
 {
 if (handle.Status == AsyncOperationStatus.Succeeded && handle.Result != null)
 {
 KickStarter.settingsManager.musicStorages[trackID].audioClip = handle.Result;
 }
 }

 public void Unload ()
 {
 if (handle.Status == AsyncOperationStatus.Succeeded && handle.Result != null)
 {
 Addressables.Release (handle);
 }
 KickStarter.settingsManager.musicStorages[trackID].audioClip = null;
 }

 }

}

```
Journal page snapping

This script ensures that a Journal menu element always shows a page index with an odd number (1, 3, 5, etc) - so that another page next to it can always show even numbers.

It also prevents shift left/right buttons showing if the two pages cannot be shifted further.

To use it:

1. Copy the code below into a C# script named JournalSnapPage.cs
2. Attach the Journal Snap Page component to a GameObject in the scene
3. Configure the component's Inspector fields, setting the name of the Menu, the Journal page (left-hand-side), and the button that shifts the pages right (all are assumed to be in the same Menu)

JournalSnapPage.cs:

```
using UnityEngine;

using AC;

public class JournalSnapPage : MonoBehaviour

{

	public string journalMenuName = "Journal";

	public string journalElementName = "PageLeft";

	public string shiftRightButtonName = "ShiftRight";

	private void OnEnable ()

	{

		EventManager.OnMenuElementShift += OnMenuElementShift;

		EventManager.OnMenuTurnOn += OnMenuTurnOn;

	}

	private void OnDisable ()

	{

		EventManager.OnMenuElementShift -= OnMenuElementShift;

		EventManager.OnMenuTurnOn -= OnMenuTurnOn;

	}

	private void OnMenuElementShift (MenuElement _element, AC_ShiftInventory shiftType)

	{

		if (PlayerMenus.GetMenuWithName (journalMenuName).elements.Contains (_element))

		{

			MenuJournal journal = PlayerMenus.GetElementWithName (journalMenuName, journalElementName) as MenuJournal;

			bool isEven = (journal.showPage%2 == 0);

			if (isEven)

			{

				int newShowPage = journal.showPage + ((shiftType == AC_ShiftInventory.ShiftNext) ? 1 : -1);

				if (newShowPage < 1)

				{

					newShowPage = 1;

				}

				if (newShowPage >= journal.pages.Count - 1)

				{

					newShowPage = journal.pages.Count - 1;

				}

				journal.showPage = newShowPage;

			}

			UpdateShiftRightButton ();

		}

	}

	private void OnMenuTurnOn (Menu _menu, bool isInstant)

	{

		if (_menu.title == journalMenuName)

		{

			UpdateShiftRightButton ();

		}

	}

	private void UpdateShiftRightButton ()

	{

		MenuJournal journal = PlayerMenus.GetElementWithName (journalMenuName, journalElementName) as MenuJournal;

		MenuButton shiftRightButton = PlayerMenus.GetElementWithName (journalMenuName, shiftRightButtonName) as MenuButton;

		int pageDiff = journal.pages.Count - journal.showPage;

		shiftRightButton.IsVisible = (pageDiff > 1);

	}

}

```
Auto-move items to a Container

When using the default Container menu, the Player can transfer items from their own inventory into the Container by first clicking the item, and then the Container element.  This script reduces this down to just the first click, by automatically tranferring any clicked item into the Container element.

To use it, first bring up the default Container menu in the Menu Manager, select the PlayerInventory element, and set its **Inventory box type** field to **Custom Script**.

Next, attach the script below to a GameObject in the scene:

**AutoTransferToContainer.cs**

```
using UnityEngine;
using System.Collections;
using AC;

public class AutoTransferToContainer : MonoBehaviour
{

    private void OnEnable ()
    {
        EventManager.OnMenuElementClick += OnClickInventory;
    }

    private void OnDisable ()
    {
        EventManager.OnMenuElementClick -= OnClickInventory;
    }

    private void OnClickInventory (Menu menu, MenuElement menuElement, int slot, int buttonPressed)
    {
        if (menu.title == "Container" && menuElement.title == "PlayerInventory")
        {
            MenuInventoryBox inventory = menuElement as MenuInventoryBox;

            InvItem clickedItem = inventory.GetItem (slot);
            if (clickedItem != null)
            {
                if (KickStarter.playerInput.activeContainer.Add (clickedItem.id, clickedItem.count))
                {
                    KickStarter.runtimeInventory.Remove (clickedItem.id, clickedItem.count);
                }
            }
        }
    }

}

```
Showing Hotspot icons with a keypress

Hotspots can have icons that show either a fixed graphic, or represent the first-available "Use" interaction. These are set under the "Hotspot settings" panel in the Settings Manager.

This script demonstrates how such icons can be shown at any time - in this case, when the player holds down an input button.

To use it:

* Define an input named "HighlightAll" in Unity's Input Manager and map it to e.g. "space"
* In the Settings Manager, set **Display Hotspot icons** to **Via Script Only.**
* Paste the code below into a C# script named **ShowHotspotIcons**, and attach it to a GameObject in your scene

ShowHotspotIcons.cs:

```
using UnityEngine;
using System.Collections;
using AC;

public class ShowHotspotIcons : MonoBehaviour
{

 public float speed = 5f;
 Hotspot[] hotspots;

 void Start ()
 {
 hotspots = FindObjectsOfType (typeof (Hotspot)) as Hotspot[];
 }

 void Update ()
 {
 if (!KickStarter.stateHandler.IsInGameplay () || KickStarter.playerInput.InputGetButtonUp ("HighlightAll"))
 {
 SetHighlights (false);
 }
 else if (KickStarter.playerInput.InputGetButtonDown ("HighlightAll"))
 {
 SetHighlights (true);
 }
 }

 void SetHighlights (bool show)
 {
 foreach (Hotspot hotspot in hotspots)
 {
 if (hotspot.IsOn () && hotspot.highlight)
 {
 hotspot.SetIconVisibility (show, speed);
 }
 else
 {
 hotspot.SetIconVisibility (false, speed);
 }
 }
 }

}

```
Creating a global Container

Containers are GameObjects that store a collection of inventory items that the player can retrieve. They are typically a per-scene object, but this script allows you to create a Container whose contents are the same in all scenes.

To use it:

1. Create a new C# script named DontDestroyMe.cs, and paste in the contents below
2. Drop a new Container in your game's first scene and add the new Dont Destroy Me component
3. Make sure there is a Remember Container component attached

DontDestroyMe.cs:

```
using UnityEngine;
using System.Collections;
using AC;

public class DontDestroyMe : MonoBehaviour
{

	private void Start ()
	{
		DontDestroyOnLoad (gameObject);
	}

}

```
Per-language Menus

This script updates a Unity UI-based menu's fonts based on the current language. It works by replacing the font field in all-found Text components when the menu is turned on, or when the language is changed.

To use it:

1. Create a new C# file named DynamicFontLanguage.cs, and paste in the code below
2. Attach the new Dynamic Font Language component to the root of your Unity UI menu prefab
3. Assign a default Font in the Inspector, then create as many instances of the "Per Language Fonts" array as you have different fonts. For each, assign both a font, and the language index (as listed in the Speech Manager) it should display for.

DynamicFontLanguage.cs:

```
 using UnityEngine;
 using UnityEngine.UI;
 using AC;

 public class DynamicFontLanguage : MonoBehaviour
 {

 [SerializeField] private Font defaultFont;
 [SerializeField] private PerLanguageFont[] perLanguageFonts = new PerLanguageFont[0];
 private Text[] texts;

 private void OnEnable ()
 {
 EventManager.OnChangeLanguage += OnChangeLanguage;
 EventManager.OnMenuTurnOn += OnMenuTurnOn;
 }

 private void OnDisable ()
 {
 EventManager.OnChangeLanguage -= OnChangeLanguage;
 EventManager.OnMenuTurnOn -= OnMenuTurnOn;
 }

 private void OnMenuTurnOn (Menu \_menu, bool isInstant)
 {
 OnChangeLanguage (Options.GetLanguage ());
 }

 private void OnChangeLanguage (int languageIndex)
 {
 Font newFont = defaultFont;
 foreach (PerLanguageFont perLanguageFont in perLanguageFonts)
 {
 if (languageIndex == perLanguageFont.languageIndex && perLanguageFont.font)
 {
 newFont = perLanguageFont.font;
 break;
 }
 }

 if (newFont)
 {
 if (texts == null)
 {
 texts = GetComponentsInChildren <Text>();
 }

 foreach (Text text in texts)
 {
 text.font = newFont;
 }
 }
 }

 [System.Serializable]
 private class PerLanguageFont
 {

 public int languageIndex;
 public Font font;

 }

 }

```
First Person speed parameters

If you are using First Person movement and with a **Turning type** of **Relative To Camera**, you'll find that the Player's "Move speed float" parameter does not account for direction.  Moving forwards, sideways and backwards always gives a positive value.

For games that show the player's body in this mode, it's likely that you want to be able to accurately animate them according to the direction they are moving in.  This script controls Animator speed parameters in the forward and right directions, allowing for 2D Blend Tree creation.

To use it:

1. Add two new float parameters, ForwardSpeed and RightSpeed, to your player's Animator Controller
2. Create a new C# script named FirstPersonSpeedParameters and paste in the code below
3. Attach the new First Person Speed Parameters component to your Player's root object
4. Use the new float parameters to control animation as desired

FirstPersonSpeedParameters.cs:

```
using UnityEngine;

using System.Collections;

using AC;

public class FirstPersonSpeedParameters : MonoBehaviour

{

	public string forwardSpeedParameter = "ForwardSpeed";

	public string rightSpeedParameter = "RightSpeed";

	private Animator _animator;

	private AC.Char character;

	void Start ()

	{

		character = GetComponent <AC.Char>();

		_animator = character.GetAnimator ();

	}

	void Update ()

	{

		if (!string.IsNullOrEmpty (forwardSpeedParameter))

		{

			float forwardDot = Vector3.Dot (character.TransformForward, character.GetMoveDirection ());

			_animator.SetFloat (forwardSpeedParameter, character.GetMoveSpeed () * forwardDot);

		}

		if (!string.IsNullOrEmpty (rightSpeedParameter))

		{

			float rightDot = Vector3.Dot (character.TransformRight, character.GetMoveDirection ());

			_animator.SetFloat (rightSpeedParameter, character.GetMoveSpeed () * rightDot);

		}

	}

}

```
Press space to finish loading

By default, the game will automatically switch to the next scene once it has finished loading. However, it is possible to wait for some other event (e.g. user input) before this occurs. In this example script, the game will wait for the Space bar to be pressed before the scene transition completes. This works both with and without dedicated loading screens.

To use it:

1. Under the Settings Manager's "Scene loading" section, check both Load scenes asynchronously? and Scene loading requires manual activation?.
2. Create a new C# script named CompleteLoadingExample, and paste in the code below.
3. Place the new Complete Loading Example in the scene you are loading from. To have it work in all scenes automatically, attach it to the PersistentEngine prefab in /Assets/AdventureCreator/Resources.

CompleteLoadingExample.cs:

```
using UnityEngine;
using AC;

public class CompleteLoadingExample : MonoBehaviour
{

 private bool isAwaitingInput = false;

 void OnEnable ()
 {
 AC.EventManager.OnAwaitSceneActivation += OnAwaitSceneActivation;
 }

 void OnDisable ()
 {
 AC.EventManager.OnAwaitSceneActivation -= OnAwaitSceneActivation;
 }

 void Update ()
 {
 if (isAwaitingInput && Input.GetKeyDown (KeyCode.Space))
 {
 KickStarter.sceneChanger.ActivateLoadedScene ();
 isAwaitingInput = false;
 }
 }

 void OnAwaitSceneActivation (string nextSceneName)
 {
 Debug.Log ("Scene loaded - press the Space bar to continue");
 isAwaitingInput = true;
 }

}

```
Jumping with a Character Controller

**NOTE: This script is no longer necessary in AC v1.74.0 or later.**

This script allows you to jump when the Player character has a Character Controller.

To use it, create a new C# script named CCJump, paste in the code below, and attach to the Player.

To animate the character while jumping, optionally define a new Trigger parameter in the Animator, and enter its name into the Inspector's "Jump Trigger" field.

CCJump.cs:

```
using UnityEngine;
using AC;

public class CCJump : MonoBehaviour
{

 [SerializeField] private float midAirTurnSpeed = 10f;
 [SerializeField] private string jumpTrigger;

 private Vector3 movingDirection = Vector3.zero;
 private CharacterController controller;
 private const float gravity = 10.0f;
 private Char acCharacter;

 private void Start ()
 {
 acCharacter = GetComponent <Char>();
 controller = GetComponent <CharacterController>();
 }

 private void Update ()
 {
 if (controller.isGrounded && Input.GetButtonDown ("Jump") && KickStarter.stateHandler.IsInGameplay ())
 {
 acCharacter.motionControl = MotionControl.Manual;
 movingDirection.y = KickStarter.settingsManager.jumpSpeed;
 if (!string.IsNullOrEmpty (jumpTrigger))
 {
 acCharacter.GetAnimator ().SetTrigger (jumpTrigger);
 }
 }

 if (acCharacter.motionControl == MotionControl.Manual)
 {
 Vector2 moveKeys = KickStarter.playerInput.GetMoveKeys ();
 Vector3 moveDirectionInput = (moveKeys.y \* MainCamera.ForwardVector ()) + (moveKeys.x \* MainCamera.RightVector ());

 if (moveDirectionInput.sqrMagnitude > 0f)
 {
 Quaternion lookRotation = Quaternion.LookRotation (moveDirectionInput, Vector3.up);
 acCharacter.TransformRotation = Quaternion.Slerp (acCharacter.TransformRotation, lookRotation, Time.deltaTime \* midAirTurnSpeed);
 }

 float flatSpeedMultiplier = KickStarter.playerInput.IsPlayerControlledRunning () ? (acCharacter.runSpeedScale / acCharacter.walkSpeedScale) : 1f;
 flatSpeedMultiplier \*= acCharacter.walkSpeedScale;

 movingDirection = new Vector3 (moveDirectionInput.x \* flatSpeedMultiplier, movingDirection.y, moveDirectionInput.z \* flatSpeedMultiplier);
 movingDirection.y -= gravity \* Time.deltaTime;
 controller.Move (movingDirection \* Time.deltaTime);
 }
 }

 private void LateUpdate ()
 {
 if (controller.isGrounded && !Input.GetButtonDown ("Jump"))
 {
 acCharacter.motionControl = MotionControl.Automatic;
 }
 }

}

```
Adding a recipe timer

AC's crafting system allows for multiple Inventory items to be combined to create a single resulting item.  Such crafting typically occurs instantly - once the correct items are in the Crafting slots, or when the user presses the "Create" button, the result appears in the Output box.

The following script causes the result to appear after a delay - so that the Crafting process appears to take time to "cook" the resulting item.  It also allows for the creation time to be displayed visually in a UI Slider component.

To use it:

* Copy the script below into a C# file named CookingTimer.cs.
* Attach the new Cooking Timer component to a GameObject in the scene.  This can be a Unity UI prefab spawned in by the Menu Manager
* Fill in its Inspector, entering the name of the Crafting menu, and the Crafting element that has its "Crafting element type" property set to Output.
* Find the same "Output" Crafting element in the Menu Manager, and uncheck "Result is automatic?" in its list of properties
* (Optional) Assign a UI Slider component that represents the cooking time.

```
using UnityEngine;
using UnityEngine.UI;
using AC;

public class CookingTimer : MonoBehaviour
{

 public string menuName = "Crafting";
 public string craftingOutputName = "Crafting";
 public Slider slider;
 public readonly float cookingTimer;
 public readonly float maxCookingTime;

 void OnEnable ()
 {
 EventManager.OnInventoryAdd\_Alt += OnInventoryUpdate;
 EventManager.OnInventoryRemove\_Alt += OnInventoryUpdate;
 }

 void OnEnable ()
 {
 EventManager.OnInventoryAdd\_Alt -= OnInventoryUpdate;
 EventManager.OnInventoryRemove\_Alt -= OnInventoryUpdate;
 }

 void OnInventoryUpdate (InvCollection invCollection, InvInstance invInstance, int amount)
 {
 MenuCrafting outputBox = PlayerMenus.GetElementWithName (menuName, craftingOutputName) as MenuCrafting;
 InvCollection ingredients = outputBox.IngredientsInvCollection;
 if (ingredients == invCollection)
 {
 Recipe activeRecipe = KickStarter.runtimeInventory.CalculateRecipe (ingredients);
 if (activeRecipe != null)
 {
 // Calculate cooking time here
 cookingTimer = 1f;
 }
 else
 {
 cookingTimer = 0f;
 }
 maxCookingTime = cookingTimer;
 }
 }

 void Update ()
 {
 if (cookingTimer > 0f)
 {
 cookingTimer -= Time.deltaTime;
 if (cookingTimer <= 0f)
 {
 MenuCrafting outputBox = PlayerMenus.GetElementWithName (menuName, craftingOutputName) as MenuCrafting;
 outputBox.SetOutput ();
 }
 }

 if (slider)
 {
 slider.value = cookingTimer / maxCookingTime;
 }
 }

}

```
Check if a Timer is running

This custom Action can be used to check if a given Timer is currently running.

ActionTimerCheck.cs:

```
using UnityEngine;
#if UNITY\_EDITOR
using UnityEditor;
#endif

namespace AC
{

 [System.Serializable]
 public class ActionTimerCheck : ActionCheck
 {

 public int timerID;

 public override ActionCategory Category { get { return ActionCategory.Variable; }}
 public override string Title { get { return "Check timer"; }}
 public override string Description { get { return "Checks if a Timer is running"; }}

 public override bool CheckCondition ()
 {
 if (KickStarter.variablesManager != null && KickStarter.variablesManager.timers != null)
 {
 foreach (Timer timer in KickStarter.variablesManager.timers)
 {
 if (timer.ID == timerID)
 {
 return timer.IsRunning;
 }
 }
 }
 return false;
 }

 #if UNITY\_EDITOR

 protected override string GetSocketLabel (int i)
 {
 if (i == 1)
 {
 return "If running:";
 }
 return "If not running:";
 }

 public override void ShowGUI ()
 {
 if (GUILayout.Button ("Timers window"))
 {
 TimersEditor.Init ();
 }

 int tempNumber = -1;

 if (KickStarter.variablesManager != null && KickStarter.variablesManager.timers != null && KickStarter.variablesManager.timers.Count > 0)
 {
 string[] labelList = new string[KickStarter.variablesManager.timers.Count];
 for (int i=0; i<KickStarter.variablesManager.timers.Count; i++)
 {
 labelList[i] = i.ToString () + ": " + KickStarter.variablesManager.timers[i].Label;

 if (KickStarter.variablesManager.timers[i].ID == timerID)
 {
 tempNumber = i;
 }
 }

 if (tempNumber == -1)
 {
 // Wasn't found (was deleted?), so revert to zero
 if (timerID != 0)
 LogWarning ("Previously chosen Timer no longer exists!");
 tempNumber = 0;
 timerID = 0;
 }

 tempNumber = EditorGUILayout.Popup ("Timer:", tempNumber, labelList);
 timerID = KickStarter.variablesManager.timers [tempNumber].ID;
 }
 else
 {
 EditorGUILayout.HelpBox ("No Timers exist!", MessageType.Info);
 timerID = 0;
 }
 }

 #endif

 }

}

```
Double-tap to run

By default, Direct-controlled running occurs when the user presses an input named either "Run" or "ToggleRun". This script causes running to instead be based on whether or not the user presses the same directional input twice in quick succession.

To use it:

1. Set your game's **Movement method** to **Direct**
2. If defined, remove the inputs **Run** and **ToggleRun** from your Project -> Input settings.
3. Create a new C# script named **DoubleTapRunning**, and paste in the code below
4. Attach the new Double Tap Running component to an object in the scene

DoubleTapRunning.cs:

```
 using UnityEngine;
 using AC;

 public class DoubleTapRunning : MonoBehaviour
 {

 private float timeSinceLastInput;
 private Vector2 lastInputDirection;
 private bool hadNoInput;
 private bool run;

 private const float doubleTapTime = 0.5f;
 private const float minInput = 0.01f;
 private const float minDotProduct = 0.9f;

 void Start ()
 {
 KickStarter.playerInput.InputGetButtonDelegate = GetButton;
 }

 bool GetButton (string inputName)
 {
 if (inputName == "Run") return run;
 try
 {
 return Input.GetButton (inputName);
 }
 catch {}
 return false;
 }

 void Update ()
 {
 Vector2 moveInput = KickStarter.playerInput.GetMoveKeys ();
 bool hasInput = moveInput.sqrMagnitude > minInput;

 if (hasInput)
 {
 if (hadNoInput)
 {
 float timeGap = Time.time - timeSinceLastInput;
 if (timeGap < doubleTapTime)
 {
 float dotProduct = Vector2.Dot (moveInput.normalized, lastInputDirection);
 if (dotProduct >= minDotProduct)
 {
 run = true;
 }
 }
 }

 timeSinceLastInput = Time.time;
 hadNoInput = false;
 lastInputDirection = moveInput.normalized;
 }
 else
 {
 hadNoInput = true;
 run = false;
 }
 }

 }

```
Unlock Steam Achievement (AC Action)

Notes: this Action lets you unlock steam achievements in an actionlist. It requires the steamworks plugin instaleld on the project.

To use it replace the text in a .cs file named ActionUnlockAchievement with this text, or create a new blank text file, then paste the text and rename the file ActionUnlockAchievement.cs. Then drop the file into the folder named actions inside the Adventure creator folder (or define your own folder in the Actions manager). Then, just use the action in an action list (from the custom category).

```
using UnityEngine;
using System;
using Steamworks;

#if UNITY\_EDITOR
using UnityEditor;
#endif
/\* By Alvaro Urrutia Luna (Alverik)
\* Custom action to
\* Unlock Steam achievements.
\*/
namespace AC
{

 [System.Serializable]
 public class ActionUnlockAchievement : Action
 {

 public string AchievementID = "";

 public override ActionCategory Category { get { return ActionCategory.Custom; }}
 public override string Title { get { return "Unlock Steam Achievement."; }}
 public override string Description { get { return "Unlocks an steam Achievement."; }}

 override public float Run ()
 {
 //your action:
 if (SteamManager.Initialized)
 {
 if(AchievementID!=String.Empty || AchievementID!="")
 {
 try
 {
 SteamUserStats.SetAchievement(AchievementID);
 SteamUserStats.StoreStats();
 }
 catch(Exception e)
 {
 Debug.LogError("Error while setting steam achievements... " + e);
 }

 }
 }
 //end the action
 return 0f;
 }

 #if UNITY\_EDITOR

 override public void ShowGUI ()
 {
 // Action-specific Inspector GUI code here
 AchievementID = EditorGUILayout.TextField("Achievement ID: ", AchievementID , GUILayout.ExpandWidth(true));
 }

 public override string SetLabel ()
 {
 // Return a string used to describe the specific action's job.
 string labelAdd = "Unlocks an steam Achievement.";
 return labelAdd;
 }

 #endif

 }

}

```
Rogo Lipsync (No audio) 01 - Play Action

Note: This is an alternative integration for Rogo LipSync and is intended to allow emotions and mouth movement to be played independently from any audio. The integration consists of three AC actions. These will be posted in separate articles to avoid clutter.

Author's quote:

> Use this as a means to have emotions/mouth movement that plays with text (no audio) and limit the .asset files to just a handful - short speech, medium speech, long speech. I also use it for asset files that just sync gestures and emotions.
>
>

To use it replace the text in a .cs file named RogoLipSync\_Play with this text, or create a new blank text file, then paste the text and rename the file RogoLipSync\_Play.cs. Then drop the file into the folder named actions inside the Adventure creator folder. Then, just use the action in an action list.

The code for the play action is as follows:

```
using UnityEngine;
using System.Collections;
using RogoDigital.Lipsync;

#if UNITY_EDITOR
using UnityEditor;
#endif

 namespace AC
 {
     [System.Serializable]
    public class RogoLipSync_Play : Action
     {
         public bool isPlayer;
        public LipSync lipSyncTarget;
        public LipSyncData  dataClip;
        public bool canInterrupt;

        public RogoLipSync_Play()
        {
             this.isDisplayed = true;
            category = ActionCategory.Custom;
            title = "Rogo LipSync - Play";
            description = "Starts playing a LipSyncData clip on a LipSync component";
            isPlayer = true;
            canInterrupt = true;
        }

         override public float Run()
        {
             if (isPlayer)
            {
                 lipSyncTarget = KickStarter.player.GetComponent<LipSync>();

                if (lipSyncTarget == null)
                {
                    Debug.LogWarning("RogoLipSync_Play: No LipSync component found on Player.");
                    return 0f;
                }
            }

             else if (lipSyncTarget == null)
            {
                 Debug.LogWarning("RogoLipSync_Play: No LipSync component defined.");
                return 0f;
            }

             if (dataClip == null)
            {
                 Debug.LogWarning("RogoLipSync_Play: No LipSync clip defined.");
                return 0f;
            }

             lipSyncTarget.Play(dataClip);
            return 0f;
        }

         override public void Skip(){

        }

 #if UNITY_EDITOR
        override public void ShowGUI()
        {
             isPlayer = EditorGUILayout.Toggle("Is player?", isPlayer);

            if (!isPlayer)
            {
                 lipSyncTarget = (LipSync)EditorGUILayout.ObjectField("Character:", lipSyncTarget, typeof(LipSync), true);
            }
             dataClip = (LipSyncData)EditorGUILayout.ObjectField("Clip:", dataClip, typeof(LipSyncData), true);
            canInterrupt = EditorGUILayout.Toggle("Can interrupt?", canInterrupt);
            AfterRunningOption();
        }
 #endif
     }
 }

```

-Original code provided by user Larsos-
Referencing last used hotspot

Note: this is useful if you need your script to use, change or otherwise affect the hotspot the player just used, in a painless no hassle manner.

```
        Hotspot myHotspot = AC.KickStarter.playerInteraction.GetLastOrActiveHotspot();
        // validate : dont want to have a bad object ref ....
        if (myHotspot != null)
		{
		 /*do your stuff here*/

		}
		else
		{
		 Debug.Log ("Oh Duckpants - I must have made a Boo Boo ! Lost the Hotspot");
		}

```

-Original tip provided by Simonr. Edited by Alverik-
Unity Third Person Controller integration

This script allows for integration with Unity's Starter Assets - Third Person Controller asset.

To use it:

1. Import AC into a project that is already set up with the Third Person Controller asset
2. Copy/paste the script below into a C# file named StarterAssetsInput\_AC
3. Locate the asset's PlayerArmature prefab, and remove its Starter Assets Input component
4. Add the new Starter Assets Input AC component, as well as AC's Player component
5. In the Player component's Inspector, set the Animation engine to Mecanim, and clear the parameter fields beneath

StarterAssetsInput\_AC.cs:

```
using UnityEngine;
#if ENABLE\_INPUT\_SYSTEM
using UnityEngine.InputSystem;
#endif
using AC;

namespace StarterAssets
{

 public class StarterAssetsInputs\_AC : StarterAssetsInputs
 {

 public bool acTurnsCamera;

 AC.Char acCharacter;
 bool underACControl;

 void Awake ()
 {
 acCharacter = GetComponent<AC.Char> ();
 acCharacter.motionControl = MotionControl.Manual;
 }

 void Update ()
 {
 acCharacter.IsMovingAlongPath ();

 if (underACControl)
 {
 Vector3 moveDirection = acCharacter.GetTargetPosition () - transform.position;
 MoveInput (moveDirection);

 if (acTurnsCamera)
 {
 Vector3 lookDirection = acCharacter.GetTargetRotation () \* Vector3.forward;
 lookDirection.y = 0f;
 LookInput (lookDirection);
 }
 }
 }

 private Vector2 GetInputVector (Vector3 worldDirection)
 {
 worldDirectionworldDirection.y = 0f;
 if (worldDirection.sqrMagnitude > 1f) worldDirection.Normalize ();
 Vector3 cameraForward = KickStarter.CameraMainTransform.forward;
 cameraForward.y = 0f;
 cameraForward.Normalize ();

 Vector3 cameraRight = KickStarter.CameraMainTransform.right;
 cameraRight.y = 0f;
 cameraRight.Normalize ();

 float forwardAmount = Vector3.Dot (cameraForward, worldDirection.normalized);
 float rightAmount = Vector3.Dot (cameraRight, worldDirection.normalized);

 return new Vector2 (rightAmount, forwardAmount);
 }

#if ENABLE\_INPUT\_SYSTEM
 public new void OnMove(InputValue value)
 {
 if (underACControl) return;
 MoveInput(value.Get<Vector2>());
 }

 public new void OnLook(InputValue value)
 {
 if(cursorInputForLook)
 {
 LookInput(value.Get<Vector2>());
 }
 }

 public new void OnJump(InputValue value)
 {
 if (underACControl) return;
 JumpInput(value.isPressed);
 }

 public new void OnSprint(InputValue value)
 {
 if (underACControl) { SprintInput (acCharacter.isRunning); return; }
 SprintInput(value.isPressed);
 }
#endif

 }

}

```
Sync character rotation after Timeline

While using Unity's Timeline, a 3D character's position and rotation can be updated if they are included as an Animation track. However, when control is resumed to AC, the character's rotation will - by default - return to their original direction. Attaching the following script to the character will prevent this:

UpdateCharTimelineRotation.cs:

```
using UnityEngine;
using UnityEngine.Playables;

namespace AC
{

	public class UpdateCharTimelineRotation : MonoBehaviour
	{

		#region Variables

		private PlayableDirector activeDirector;
		private Char character;

		#endregion

		#region UnityStandards

		private void OnEnable ()
		{
			character = GetComponent <Char>();

			EventManager.OnCharacterEnterTimeline += OnCharacterEnterTimeline;
			EventManager.OnCharacterExitTimeline += OnCharacterExitTimeline;
		}

		private void OnDisable ()
		{
			EventManager.OnCharacterEnterTimeline -= OnCharacterEnterTimeline;
			EventManager.OnCharacterExitTimeline -= OnCharacterExitTimeline;
		}

		private void Update ()
		{
			if (activeDirector != null)
			{
				character.SetRotation (character.GetAnimator ().rootRotation);
			}
		}

		#endregion

		#region CustomEvents

		private void OnCharacterEnterTimeline (Char character, PlayableDirector director, int trackIndex)
		{
			activeDirector = director;
		}

		private void OnCharacterExitTimeline(Char character, PlayableDirector director, int trackIndex)
		{
			if (activeDirector == director)
			{
				activeDirector = null;
			}
		}

		#endregion

	}

}

```
Action to change a character's motion control

A character's "Motion control" property determines whether AC will move the character automatically or not. When set to Manual, the character is assumed to be moved by something other than the NPC or Player component, i.e. a third-party motion controller script.

This Action lets you dynamically change this property in ActionLists, so that you can have the character rely on AC for motion control at some times, and another way of moving them for others.

ActionMotionControl.cs:

```
 using UnityEngine;
using System.Collections.Generic;
#if UNITY_EDITOR
using UnityEditor;
#endif

namespace AC
{

   [System.Serializable]
   public class ActionMotionControl : Action
   {

       public bool isPlayer;
       public Char character;
       public int characterConstantID = 0;
       public MotionControl motionControl;

       private Char runtimeCharacter;

       public override ActionCategory Category { get { return ActionCategory.Character; } }
       public override string Title { get { return "Set motion control"; } }

       public override void AssignValues (List<ActionParameter> parameters)
       {
           runtimeCharacter = (isPlayer) ? KickStarter.player : AssignFile <Char> (characterConstantID, character);
       }

       public override float Run ()
       {
           if (runtimeCharacter)
           {
               runtimeCharacter.motionControl = motionControl;
           }
           return 0f;
       }

       #if UNITY_EDITOR

       public override void ShowGUI ()
       {
           isPlayer = EditorGUILayout.Toggle ("Affect Player?", isPlayer);
           if (!isPlayer)
           {
               character = (Char) EditorGUILayout.ObjectField ("Character to affect:", character, typeof (Char), true);

               characterConstantID = FieldToID <Char> (character, characterConstantID);
               character = IDToField <Char> (character, characterConstantID, false);
           }

           motionControl = (MotionControl) EditorGUILayout.EnumPopup ("New motion control:", motionControl);
       }

       #endif

   }

}

```
2D characters in mirrors

This script allows you to create the effect of 2D characters appearing reflected in mirrors - while respecting their facing direction. It works by controlling a second character that is updated each frame according to the original character's state.

To use it:

1. Set up your character to use Sprites Unity Complex animation.
2. Duplicate the character you wish to mirror
3. Remove the Player or NPC component for duplicated character (used for the mirror) but keep their Animator and sprite
4. Create a new C# file called MirrorCharacter (below) and attach to the duplicated character.
5. Assign the fields in the Mirror Character component's Inspector
6. Use Unity's Sprite Mask component to limit rendering of the mirror character to the boundary of the mirror.

MirrorCharacter.cs:

```
using UnityEngine;
using AC;

public class MirrorCharacter : MonoBehaviour
{

 public bool isPlayer;
 public AC.Char originalCharacter;
 public Animator \_animator;
 public Vector3 positionOffset;

 private void Update ()
 {
 if (isPlayer)
 {
 originalCharacter = KickStarter.player;
 }

 if (originalCharacter == null)
 {
 return;
 }

 // Position
 transform.position = originalCharacter.transform.position + positionOffset;

 // Move speed
 if (!string.IsNullOrEmpty (originalCharacter.moveSpeedParameter))
 {
 float moveSpeed = originalCharacter.GetAnimator ().GetFloat (originalCharacter.moveSpeedParameter);
 \_animator.SetFloat (originalCharacter.moveSpeedParameter, moveSpeed);
 }

 // Direction
 if (!string.IsNullOrEmpty (originalCharacter.directionParameter))
 {
 int direction = originalCharacter.GetAnimator ().GetInteger (originalCharacter.directionParameter);
 int flippedDirection = FlipDirection (direction);
 \_animator.SetInteger (originalCharacter.directionParameter, flippedDirection);
 }

 // Talking
 if (!string.IsNullOrEmpty (originalCharacter.talkParameter))
 {
 bool isTalking = originalCharacter.isTalking;
 \_animator.SetBool (originalCharacter.talkParameter, isTalking);
 }
 }

 private int FlipDirection (int direction)
 {
 // 0 = Down, 1 = Left, 2 = Right, 3 = Up, 4 = DownLeft, 5 = DownRight, 6 = UpLeft, 7 = UpRight
 switch (direction)
 {
 case 0:
 return 3;

 case 1:
 return 2;

 case 2:
 return 1;

 case 3:
 return 0;

 case 4:
 return 7;

 case 5:
 return 6;

 case 6:
 return 5;

 case 7:
 return 4;

 default:
 return direction;
 }
 }

}

```
Restore Animator parameters when re-enabling

When a GameObject is disabled and later re-enabled, its Animator component resets its parameter values back to their default states. This component backs up these values when disabled, and restores them when re-enabled.

This is useful when using an Animator inside a Unity UI-based Menu, since UI menus are disabled when not in use.

To use it:

1. Copy the script below into a new C# file named **SaveRememberOnDisable.cs**
2. Attach the new **Save Remember On Disable** component to the Animator
3. Attach a **Remember Animator** component to the Animator if it doesn't yet have one

**NOTE**: Make sure the Remember Animator component to below the Save Remember On Disable component in the Inspector

SaveRememberOnDisable.cs:

```
using UnityEngine;

namespace AC

{

	public class SaveRememberOnDisable : MonoBehaviour

	{

		private Remember remember;

		private string disabledData;

		private void Awake ()

		{

			remember = GetComponent<Remember> ();

		}

		private void OnEnable ()

		{

			if (!string.IsNullOrEmpty (disabledData))

			{

				remember.LoadData (disabledData);

				disabledData = string.Empty;

			}

		}

		private void OnDisable ()

		{

			disabledData = remember.SaveData ();

		}

	}

}

```
Animal Controller integration

This page covers how to integrate AC with Animal Controller https://assetstore.unity.com/packages/tools/animation/animal-controller-148877

* If using Cinemachine, attach AC's **Basic Camera** to the **CM Brain** GameObject, set its **Tag** to **Untagged**, and remove the **Audio Listener** component
* Use the Scene Manager to convert the scene into an AC scene, and assign the **CM Brain** as the scene's **Default camera**
* Attach AC's **Player** component to your Animal Controller Player character, setting the **Animation engine** to **Custom**, and **Motion control** to **Manual**
* For any NPC you wish AC to control, attach the **NPC** component and make the same field changes as above
* Copy/paste the script below into a new C# file named **AnimalControllerIntegration**
* Attach the **Animal Controller Integration** component to your character, and assign the fields in its Inspector
* The **Point Click** field is only necessary if you wish to use Animal Controller's Point And Click movement
* If the AI object is disabled, enable it and assign the **AI Control** component in the Inspector - this will be disabled automatically by the integration
* At runtime, the integration script will now prevent input during cutscenes, and have the character respond to AC movement commands

AnimalControllerIntegration.cs:

```
using UnityEngine;
using MalbersAnimations.Controller.AI;
using MalbersAnimations;

namespace AC
{

 public class AnimalControllerIntegration : MonoBehaviour
 {

 #region Variables

 [SerializeField] private Char acCharacter = null;
 [SerializeField] private MInput mInput = null;
 [SerializeField] private MAnimalAIControl aiControl = null;
 [SerializeField] private PointClick pointClick = null;

 #endregion

 #region UnityStandards

 private void OnValidate ()
 {
 if (acCharacter == null) acCharacter = GetComponent<Char> ();
 if (mInput == null) mInput = GetComponent<MInput> ();
 if (aiControl == null) aiControl = GetComponentInChildren<MAnimalAIControl> ();
 if (pointClick == null) pointClick = GetComponentInChildren<PointClick> ();
 }

 private void OnEnable ()
 {
 EventManager.OnEnterGameState += OnEnterGameState;
 EventManager.OnCharacterSetPath += OnCharacterSetPath;
 }

 private void OnDisable ()
 {
 EventManager.OnEnterGameState -= OnEnterGameState;
 EventManager.OnCharacterSetPath -= OnCharacterSetPath;
 }

 private void Update ()
 {
 if (aiControl && aiControl.DestinationPosition == aiControl.NullVector)
 {
 acCharacter.EndPath ();
 }
 }

 #endregion

 #region CustomEvents

 private void OnEnterGameState (GameState gameState)
 {
 bool allowInput = acCharacter.IsActivePlayer () && gameState == GameState.Normal;
 if (mInput) mInput.SetEnable (allowInput);
 if (pointClick) pointClick.SetEnable (allowInput);
 if (aiControl) aiControl.SetEnable (!allowInput);
 }

 private void OnCharacterSetPath (Char character, Paths path)
 {
 if (aiControl && character == acCharacter)
 {
 aiControl.SetDestinationClearTarget (path.Destination);
 }
 }

 #endregion

 }

}

```
Changing character speed with slope angle

This script allows characters to slow down when moving on slopes.  It works by comparing the angle of the floor they're on with a graph set by the user in its Inspector.

Note that this script will override the "Walk speed scale" and "Run speed scale" values set in the regular Player / NPC Inspectors.

To use it:

1. Create a new C# script named SlopeSpeedControl, and paste in the code below
2. Attach the Slope Speed Control component to your Player or NPC
3. Set its "Flat Walk Speed" and "Flat Run Speed" Inspector values to match the character's "Walk speed scale" and "Run speed scale" values respectively
4. Tweak the "Slope Curve" graph to suit.  The x-axis represents the floor's slope angle (negative values equates to a downward slope), and the y-axis represents the multiplier to apply to the character's regular speed

**SlopeSpeedControl.cs:**

```
using UnityEngine;

using System.Collections;

namespace AC

{

    public class SlopeSpeedControl : MonoBehaviour

    {

        [SerializeField] private AnimationCurve slopeCurve = new AnimationCurve (new Keyframe(-45f, 0.7f), new Keyframe(0f, 1f), new Keyframe (45f, 0.7f));

        [SerializeField] private float flatWalkSpeed = 2f;

        [SerializeField] private float flatRunSpeed = 6f;

        [SerializeField] private bool showDebug = false;

        private Char character;

        private RaycastHit hit = new RaycastHit ();

        private void Awake ()

        {

            character = GetComponent <Char>();

        }

        private void Update ()

        {

            if (character != null)

            {

                Vector3 rayStart = character.transform.position + new Vector3 (0f, 0.1f, 0f);

                Ray floorRay = new Ray (rayStart, Vector3.down);

                if (showDebug)

                {

                    Debug.DrawRay (rayStart, -Vector3.up);

                }

                if (Physics.Raycast (floorRay, out hit, 1f, character.groundCheckLayerMask))

                {

                    float slopeAngle = ((Vector3.Angle (hit.normal, character.TransformForward)) - 90);

                    float speedFactor = slopeCurve.Evaluate (slopeAngle);

                    if (showDebug)

                    {

                        Debug.Log ("Character: " + character.GetName () +", Slope angle: " + slopeAngle + ", Speed factor: " + speedFactor);

                    }

                    character.walkSpeedScale = flatWalkSpeed * speedFactor;

                    character.runSpeedScale = flatRunSpeed * speedFactor;

                }

            }

        }

    }

}

```
Skipping Timeline speech

This script allows Timeline speech to be skipped, causing the Timeline to jump to the end of the current Speech track clip.

To use it:

1. Create a new C# file named SkipTimelineSpeech and paste in the code below
2. Attach the new Skip Timeline Speech component a Playable Director that contains an AC Speech track.
3. Assign the Playable Director in the component's Inspector
4. Optionally check "Pause When Begin Speech" to have the Timeline pause when speech tracks begin

SkipTimelineSpeech.cs:

```
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Timeline;
using AC;

public class SkipTimelineSpeech : MonoBehaviour
{

 [SerializeField] private PlayableDirector playableDirector = null;
 [SerializeField] private bool pauseWhenBeginSpeech;
 private double forceTime;

 private void Update ()
 {
 if (pauseWhenBeginSpeech && IsInSpeech ())
 {
 playableDirector.time = forceTime;
 }
 if (SkipSpeechInput ())
 {
 DoSkip ();
 if (pauseWhenBeginSpeech)
 {
 playableDirector.Resume ();
 }
 }
 }

 protected bool SkipSpeechInput ()
 {
 KickStarter.playerInput.GetMouseState () == MouseState.RightClick))
 {
 return true;
 }

 if (KickStarter.playerInput.InputGetButtonDown ("SkipSpeech"))
 {
 return true;
 }

 return false;
 }

 private void DoSkip ()
 {
 if (playableDirector.state != PlayState.Playing) return;

 TimelineAsset timeline = (TimelineAsset) playableDirector.playableAsset;
 for (int i = 0; i < timeline.outputTrackCount; i++)
 {
 TrackAsset track = timeline.GetOutputTrack (i);
 SpeechTrack speechTrack = track as SpeechTrack;
 if (speechTrack)
 {
 foreach (TimelineClip clip in speechTrack.GetClips ())
 {
 if (clip != null && clip.asset is SpeechPlayableClip)
 {
 SpeechPlayableClip speechPlayableClip = clip.asset as SpeechPlayableClip;
 if (speechPlayableClip.LastSetBehaviour != null && speechPlayableClip.LastSetBehaviour.Speech != null && speechPlayableClip.LastSetBehaviour.Speech.isAlive)
 {
 double skipToTime = clip.start + clip.duration;
 KickStarter.dialog.KillDialog (speechPlayableClip.LastSetBehaviour.Speech);
 if (playableDirector.time < skipToTime)
 {
 playableDirector.time = skipToTime;
 }
 return;
 }
 }
 }
 }
 }
 }

 private bool IsInSpeech ()
 {
 if (playableDirector.state != PlayState.Playing) return false;

 TimelineAsset timeline = (TimelineAsset) playableDirector.playableAsset;
 for (int i = 0; i < timeline.outputTrackCount; i++)
 {
 TrackAsset track = timeline.GetOutputTrack (i);
 SpeechTrack speechTrack = track as SpeechTrack;
 if (speechTrack)
 {
 foreach (TimelineClip clip in speechTrack.GetClips ())
 {
 if (clip != null && clip.asset is SpeechPlayableClip)
 {
 SpeechPlayableClip speechPlayableClip = clip.asset as SpeechPlayableClip;
 if (speechPlayableClip.LastSetBehaviour != null && speechPlayableClip.LastSetBehaviour.Speech != null && speechPlayableClip.LastSetBehaviour.Speech.isAlive)
 {
 forceTime = playableDirector.time;
 return true;
 }
 }
 }
 }
 }

 return false;
 }

}

```