# Apple Hills Interaction System A concise, code-first guide to creating and extending interactions using `Interactable` and modular action/requirement components. Designed to match the style of the other updated docs (TOC, inline code, case studies). ## Table of Contents - [What This Solves](#what-this-solves) - [Architecture at a Glance](#architecture-at-a-glance) - [Quick Start (Code-First)](#quick-start-code-first) - [Subscribe to Interaction Events](#subscribe-to-interaction-events) - [Create a Custom Action](#create-a-custom-action) - [Trigger Programmatically](#trigger-programmatically) - [Core Components](#core-components) - [`Interactable`](#interactable) - [`CharacterMoveToTarget`](#charactermovetotarget) - [`InteractionActionBase` and concrete actions](#interactionactionbase-and-concrete-actions) - [`InteractionRequirementBase`](#interactionrequirementbase) - [Interaction Event Flow](#interaction-event-flow) - [Case Studies](#case-studies) - [Open a Door on Arrival](#open-a-door-on-arrival) - [Pick Up an Item then Play Timeline](#pick-up-an-item-then-play-timeline) - [Kick Off Dialogue When Player Arrives](#kick-off-dialogue-when-player-arrives) - [Troubleshooting / FAQ](#troubleshooting--faq) - [Paths & Namespaces](#paths--namespaces) - [Change Log](#change-log) ## What This Solves - Standardized interaction lifecycle with reliable events (`InteractionStarted`, `PlayerArrived`, `InteractingCharacterArrived`, `InteractionComplete`, `InteractionInterrupted`). - Composable behavior via components derived from `InteractionActionBase` and `InteractionRequirementBase`. - Clean separation of input, locomotion-to-target, cinematic timelines, and game logic. ## Architecture at a Glance - Driver: `Interactable` — owns lifecycle, input hook, character selection via `CharacterToInteract`, one‑shot/cooldown, and event dispatch. - Targets: `CharacterMoveToTarget` — editor-authored world points for `Trafalgar`/`Pulver` to path to before executing actions. - Actions: `InteractionActionBase` (abstract) — modular responses to specific `InteractionEventType` values; can pause the flow with async tasks. - Requirements: `InteractionRequirementBase` (abstract) — gatekeepers for availability; multiple can be attached. - Cinematics: `InteractionTimelineAction` — plays one or more `PlayableAsset` timelines per event; optional character auto-binding. ## Quick Start (Code-First) ### Subscribe to Interaction Events ```csharp using Interactions; using UnityEngine; public class InteractDebugHooks : MonoBehaviour { [SerializeField] private Interactable interactable; private void OnEnable() { interactable.interactionStarted.AddListener(OnStarted); interactable.characterArrived.AddListener(OnCharacterArrived); interactable.interactionInterrupted.AddListener(OnInterrupted); interactable.interactionComplete.AddListener(OnComplete); } private void OnDisable() { interactable.interactionStarted.RemoveListener(OnStarted); interactable.characterArrived.RemoveListener(OnCharacterArrived); interactable.interactionInterrupted.RemoveListener(OnInterrupted); interactable.interactionComplete.RemoveListener(OnComplete); } private void OnStarted(Input.PlayerTouchController player, FollowerController follower) => Debug.Log("Interaction started"); private void OnCharacterArrived() => Debug.Log("Character arrived"); private void OnInterrupted() => Debug.Log("Interaction interrupted"); private void OnComplete(bool success) => Debug.Log($"Interaction complete: {success}"); } ``` ### Create a Custom Action ```csharp using System.Threading.Tasks; using Interactions; using Input; using UnityEngine; public class PlaySfxOnArrivalAction : InteractionActionBase { [SerializeField] private AudioSource sfx; private void Reset() { // React to the arrival event; don't block the flow respondToEvents = new() { InteractionEventType.InteractingCharacterArrived }; pauseInteractionFlow = false; } protected override bool ShouldExecute(InteractionEventType evt, PlayerTouchController player, FollowerController follower) { return sfx != null; } protected override async Task ExecuteAsync(InteractionEventType evt, PlayerTouchController player, FollowerController follower) { sfx.Play(); // non-blocking action returns immediately when pauseInteractionFlow == false return true; } } ``` Attach this component under the same hierarchy as an `Interactable`. Registration is automatic via `OnEnable()`/`OnDisable()` in `InteractionActionBase`. ### Trigger Programmatically Normally input goes through `ITouchInputConsumer.OnTap(...)`. For testing, you can call the public tap handler: ```csharp using UnityEngine; using Interactions; public class TestTrigger : MonoBehaviour { [SerializeField] private Interactable interactable; [ContextMenu("Trigger Interact (dev)")] private void Trigger() { interactable.OnTap(interactable.transform.position); } } ``` ## Core Components ### `Interactable` - Handles input, cooldowns (`cooldown`), one‑shot (`isOneTime`), and which character participates (`characterToInteract`). - Exposes events: `interactionStarted`, `characterArrived`, `interactionInterrupted`, `interactionComplete`. - Discovers and dispatches to child `InteractionActionBase` components; awaits those that request to pause. ![Interactable Inspector](media/interactable_inspector.png) ### `CharacterMoveToTarget` Defines the world positions characters should reach before actions evaluate. - Can target `Trafalgar`, `Pulver`, or `Both` via configuration. - Supports offsets and editor gizmos; multiple instances allowed. ![Character Move Target Inspector](media/character_move_target_inspector.png) ### `InteractionActionBase` and concrete actions - Filter by `InteractionEventType` using `respondToEvents`. - Control flow with `pauseInteractionFlow` and async `ExecuteAsync(...)`. - Built‑in example: `InteractionTimelineAction` for cinematics. ![InteractionTimelineAction Inspector](media/interaction_timeline_action_inspector.png) ### `InteractionRequirementBase` - Attach one or more to gate the interaction based on items, puzzles, proximity, etc. ## Interaction Event Flow 1. `InteractionStarted` 2. `PlayerArrived` 3. `InteractingCharacterArrived` 4. `InteractionComplete` (bool success) 5. `InteractionInterrupted` Actions receive these events in order and may run concurrently; those with `pauseInteractionFlow` true are awaited. ## Case Studies ### Open a Door on Arrival ```csharp using System.Threading.Tasks; using Interactions; using Input; using UnityEngine; public class DoorOpenOnArrival : InteractionActionBase { [SerializeField] private Animator animator; // expects a bool parameter "Open" private void Reset() { respondToEvents = new() { InteractionEventType.InteractingCharacterArrived }; pauseInteractionFlow = false; } protected override async Task ExecuteAsync(InteractionEventType evt, PlayerTouchController p, FollowerController f) { animator.SetBool("Open", true); return true; } } ``` ### Pick Up an Item then Play Timeline Attach two actions: your `PickupItemAction` that pauses until the item is collected, and an `InteractionTimelineAction` mapped to `InteractionEventType.InteractionComplete` to celebrate. ### Kick Off Dialogue When Player Arrives ```csharp using System.Threading.Tasks; using Dialogue; using Input; using Interactions; using UnityEngine; public class StartDialogueOnArrival : InteractionActionBase { [SerializeField] private DialogueComponent dialogue; private void Reset() { respondToEvents = new() { InteractionEventType.PlayerArrived }; pauseInteractionFlow = false; } protected override async Task ExecuteAsync(InteractionEventType evt, PlayerTouchController p, FollowerController f) { dialogue.StartDialogue(); return true; } } ``` ## Troubleshooting / FAQ - Interaction doesn’t fire: - Confirm `Interactable` is active and not in cooldown or already completed (`isOneTime`). - Ensure `CharacterMoveToTarget` exists for the selected `CharacterToInteract`. - Actions not running: - Verify `respondToEvents` includes the lifecycle moment you expect. - Check that the component sits under the same hierarchy so it registers with the `Interactable`. - Timeline never finishes: - Make sure `InteractionTimelineAction` has valid `PlayableAsset` entries and binding flags. - Double triggers: - Guard reentry in your actions or check `_interactionInProgress` usage in `Interactable` by following logs. ## Paths & Namespaces - Scripts: `Assets/Scripts/Interactions/` - `Interactable.cs` - `InteractionActionBase.cs` - `InteractionTimelineAction.cs` - `InteractionEventType.cs` - `InteractionRequirementBase.cs` - Editor tooling: `Assets/Editor/InteractableEditor.cs` - Primary namespace: `Interactions` ## Additional Editor Visuals - Timeline mapping configuration UI: ![Timeline Mapping Editor](media/timeline_mapping_editor.png) - Unity Timeline editor when authoring cinematics for interactions: ![Timeline Editor](media/timeline_editor.png) - Example target placement in Scene view: ![Target Positioning In Scene](media/target_positioning_scene.png) ## Change Log - v1.1: Added Table of Contents, code-first snippets, case studies, standardized inline code references, preserved existing editor images, and added troubleshooting/paths. - v1.0: Original overview and setup guide.