### Initialize and Use SKMecanimStateMachine Source: https://context7.com/prime31/statekit/llms.txt This example demonstrates how to initialize an SKMecanimStateMachine, add states, and update it within a MonoBehaviour. It requires an Animator component on the same GameObject. The state transitions are logged to the console. ```csharp using UnityEngine; using Prime31.StateKit; public class MecanimPlayerController : MonoBehaviour { private SKMecanimStateMachine _stateMachine; void Start() { var animator = GetComponent(); // Constructor requires the Animator, the context object, and the initial state _stateMachine = new SKMecanimStateMachine( animator, this, new RunStraightState()); _stateMachine.addState(new RunLeftState()); _stateMachine.addState(new RunRightState()); // Log every state transition _stateMachine.onStateChanged += () => Debug.Log("Mecanim state → " + _stateMachine.currentState.GetType().Name); } void Update() { // Internally reads AnimatorStateInfo and guards calls to reason/update _stateMachine.update(Time.deltaTime); } void OnGUI() { GUILayout.Label("Current state: " + _stateMachine.currentState.GetType().Name); // Direct access to the animator via the machine GUILayout.Label("Animator state hash: " + _stateMachine.animator.GetCurrentAnimatorStateInfo(0).fullPathHash); } } ``` -------------------------------- ### StateKitLite Player Controller Example Source: https://context7.com/prime31/statekit/llms.txt Implements a convention-based FSM for player character states (Idle, Walking, Running, Jumping). Define state logic by naming methods like 'StateName_Enter', 'StateName_Tick', and 'StateName_Exit'. Transitions occur by assigning a new value to 'currentState'. ```csharp using UnityEngine; using Prime31.StateKitLite; public enum PlayerState { Idle, Walking, Running, Jumping } public class PlayerController : StateKitLite { [SerializeField] private float walkSpeed = 3f; [SerializeField] private float runSpeed = 6f; private Rigidbody _rb; void Start() { _rb = GetComponent(); initialState = PlayerState.Idle; // triggers Idle_Enter if it exists } // --- Idle --- void Idle_Enter() { Debug.Log("Now idle"); } void Idle_Tick() { if (Input.GetAxis("Horizontal") != 0 || Input.GetAxis("Vertical") != 0) currentState = PlayerState.Walking; // transition — calls Idle_Exit then Walking_Enter } void Idle_Exit() { Debug.Log("Leaving idle"); } // --- Walking --- void Walking_Enter() => Debug.Log("Started walking"); void Walking_Tick() { var input = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical")); _rb.MovePosition(transform.position + input * walkSpeed * Time.deltaTime); if (input == Vector3.zero) currentState = PlayerState.Idle; else if (Input.GetKey(KeyCode.LeftShift)) currentState = PlayerState.Running; else if (Input.GetButtonDown("Jump")) currentState = PlayerState.Jumping; // elapsedTimeInState and previousState are available as protected members if (elapsedTimeInState > 5f) Debug.Log("Been walking a while…"); } void Walking_Exit() => Debug.Log("Stopped walking"); // --- Running --- void Running_Tick() { var input = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical")); _rb.MovePosition(transform.position + input * runSpeed * Time.deltaTime); if (!Input.GetKey(KeyCode.LeftShift)) currentState = PlayerState.Walking; } // --- Jumping (no Enter/Exit needed — they're simply omitted) --- void Jumping_Tick() { if (_rb.velocity.y <= 0 && IsGrounded()) currentState = previousState; // return to whatever state we were in } private bool IsGrounded() => Physics.Raycast(transform.position, Vector3.down, 0.1f); } ``` -------------------------------- ### Initialize and Manage EnemyController State Machine Source: https://context7.com/prime31/statekit/llms.txt Set up SKStateMachine in a MonoBehaviour context. Register states using addState and drive the machine by calling update() each frame. Subscribe to onStateChanged for notifications. ```csharp using UnityEngine; using System.Collections.Generic; using Prime31.StateKit; public class EnemyController : MonoBehaviour { public Transform playerTransform; public List waypoints = new List(); private SKStateMachine _machine; void Start() { // Collect waypoints from the scene var waypointRoot = GameObject.Find("Waypoints"); foreach (Transform t in waypointRoot.GetComponentsInChildren()) { if (t != waypointRoot.transform) waypoints.Add(t.position); } // Create machine — initial state is passed in the constructor and auto-registered _machine = new SKStateMachine(this, new EnemyPatrol()); // Register additional states _machine.addState(new EnemyChase()); // Subscribe to state-change notifications _machine.onStateChanged += () => { Debug.Log($"State changed → {_machine.currentState.GetType().Name} " + $"(was: {_machine.previousState?.GetType().Name})"); }; } void Update() { // Tick the machine — calls reason() then update() on the current state _machine.update(Time.deltaTime); // Elapsed time since the last state change is tracked automatically if (_machine.elapsedTimeInState > 10f) Debug.Log("Been in this state for 10 seconds!"); } } ``` -------------------------------- ### Create and Use SKStateMachine Source: https://github.com/prime31/statekit/blob/master/README.md Demonstrates how to initialize an SKStateMachine with a target object and initial state, add additional states, and change states during runtime. Typically called within an object's Update/FixedUpdate method. ```csharp var machine = new SKStateMachine( someClass, new PatrollingState() ); // we can now add any additional states _machine.addState( new AttackState() ); _machine.addState( new ChaseState() ); // this method would typically be called in an Update/FixedUpdate of an object machine.update( Time.deltaTime ); // change states. the state machine will automatically create and cache an instance of the class (in this case ChasingState) machine.changeState(); ``` -------------------------------- ### Implement StateKitLite FSM Source: https://github.com/prime31/statekit/blob/master/README.md Shows how to subclass StateKitLite and define states using an enum. State methods (Enter, Tick, Exit) are optional and cached at startup. Ensure base methods are called if overridden. ```csharp enum SomeEnum { Walking, Idle } public class YourClass : StateKitLite() { void Start() { initialState = SomeEnum.Idle; } void Walking_Enter() {} void Walking_Tick() {} void Walking_Exit() {} void Idle_Enter() {} void Idle_Tick() {} void Idle_Exit() {} } ``` -------------------------------- ### Transition Between States with SKStateMachine.changeState Source: https://context7.com/prime31/statekit/llms.txt Transitions the machine to a previously registered state of type R. Calls end() on the outgoing state, swaps the active state, resets elapsedTimeInState to zero, calls begin() on the new state, and fires onStateChanged. Returns the new state instance cast to R. Changing to the already-active state is a no-op. ```csharp using Prime31.StateKit; public class EnemyChase : SKState { private float _speed = 8f; private float _losePlayerDistance = 15f; public override void begin() { Debug.Log("EnemyChase: target acquired!"); } public override void reason() { // Give up chase if the player is too far away float dist = Vector3.Distance( _context.transform.position, _context.playerTransform.position); if (dist > _losePlayerDistance) { // changeState returns the new state if further setup is needed var patrol = _machine.changeState(); Debug.Log("Lost the player, returning to patrol: " + patrol.GetType().Name); } } public override void update(float deltaTime) { var dir = (_context.playerTransform.position - _context.transform.position).normalized; _context.transform.rotation = Quaternion.LookRotation(dir); _context.transform.position += dir * _speed * deltaTime; } public override void end() { Debug.Log("EnemyChase: lost target"); } } ``` -------------------------------- ### Register a State with SKStateMachine.addState Source: https://context7.com/prime31/statekit/llms.txt Registers a state instance with the machine. Internally calls setMachineAndContext on the state, injecting the machine and context references and triggering onInitialized(). The initial state passed to the constructor is registered automatically; all additional states must be added explicitly before changeState is called. ```csharp using Prime31.StateKit; // Additional state public class EnemyAttack : SKState { public override void onInitialized() { // Safe to read _context here — called once during addState Debug.Log("AttackState ready for: " + _context.name); } public override void begin() { Debug.Log("Attacking!"); } public override void update(float deltaTime) { /* attack logic */ } public override void end() { Debug.Log("Attack finished"); } } // In EnemyController.Start(): _machine = new SKStateMachine(this, new EnemyPatrol()); _machine.addState(new EnemyChase()); _machine.addState(new EnemyAttack()); // register before ever calling changeState ``` -------------------------------- ### SKStateMachine.addState() Source: https://context7.com/prime31/statekit/llms.txt Registers a state instance with the state machine. This method internally calls setMachineAndContext on the state, injecting the machine and context references and triggering onInitialized(). All states, except the initial one, must be added explicitly before changeState is called. ```APIDOC ## SKStateMachine.addState() — Register a State Registers a state instance with the machine. Internally calls `setMachineAndContext` on the state, injecting the machine and context references and triggering `onInitialized()`. The initial state passed to the constructor is registered automatically; all additional states must be added explicitly before `changeState` is called. ``` -------------------------------- ### Define Enemy Patrol State with SKState Source: https://context7.com/prime31/statekit/llms.txt Inherit from SKState to define custom states. Implement lifecycle hooks like begin, reason, update, and end. The 'reason' method is ideal for transition checks before updates. ```csharp using UnityEngine; using Prime31.StateKit; // A patrol state for an EnemyController context object public class EnemyPatrol : SKState { private int _currentWaypoint = 0; private float _closeEnoughToWaypoint = 1f; private float _speed = 5f; // Called once when this state becomes active public override void begin() { Debug.Log("EnemyPatrol: started patrolling"); _currentWaypoint = 0; } // Called every frame BEFORE update — ideal for transition checks public override void reason() { RaycastHit hit; var toPlayer = _context.playerTransform.position - _context.transform.position; if (Physics.Raycast(_context.transform.position, toPlayer, out hit)) { if (hit.collider.CompareTag("Player")) _machine.changeState(); // transition to chase } } // Called every frame — required override public override void update(float deltaTime) { var target = _context.waypoints[_currentWaypoint]; if (Vector3.Distance(_context.transform.position, target) < _closeEnoughToWaypoint) { _currentWaypoint = (_currentWaypoint + 1) % _context.waypoints.Count; } var dir = (target - _context.transform.position).normalized; _context.transform.rotation = Quaternion.LookRotation(dir); _context.transform.position += dir * _speed * deltaTime; } // Called once when this state is exited public override void end() { Debug.Log("EnemyPatrol: stopped patrolling"); } // Optional: called right after _machine and _context are injected public override void onInitialized() { Debug.Log("EnemyPatrol: initialized with context " + _context.name); } } ``` -------------------------------- ### SKMecanimState RunStraight State Source: https://context7.com/prime31/statekit/llms.txt A Mecanim-aware state that is tied to a specific Mecanim animator state ('Base Layer.Run'). Its 'reason' and 'update' methods are only called when the animator is fully within this state, preventing logic execution during transitions. ```csharp using UnityEngine; using Prime31.StateKit; // Tied to the "Base Layer.Run" animator state public class RunStraightState : SKMecanimState { // Pass the exact Mecanim state path; update/reason blocked during transitions public RunStraightState() : base("Base Layer.Run") {} public override void begin() { Debug.Log("RunStraight: entered"); } public override void reason() { if (Input.GetKey(KeyCode.RightArrow)) _machine.changeState(); else if (Input.GetKey(KeyCode.LeftArrow)) _machine.changeState(); } // update receives AnimatorStateInfo for animation-specific queries public override void update(float deltaTime, AnimatorStateInfo stateInfo) { Debug.Log($"Normalized time in Run anim: {stateInfo.normalizedTime:F2}"); // drive player movement here } public override void end() { Debug.Log("RunStraight: exited"); } } ``` -------------------------------- ### SKStateMachine.changeState() Source: https://context7.com/prime31/statekit/llms.txt Transitions the state machine to a previously registered state of type R. This method handles the lifecycle of states by calling end() on the outgoing state, beginning() on the new state, and firing onStateChanged. It returns an instance of the new state. ```APIDOC ## SKStateMachine.changeState() — Transition Between States Transitions the machine to a previously registered state of type `R`. Calls `end()` on the outgoing state, swaps the active state, resets `elapsedTimeInState` to zero, calls `begin()` on the new state, and fires `onStateChanged`. Returns the new state instance cast to `R`. Changing to the already-active state is a no-op. ``` -------------------------------- ### SKMecanimState RunRight State Source: https://context7.com/prime31/statekit/llms.txt A Mecanim-aware state not locked to a specific Mecanim state, meaning its 'reason' and 'update' methods are always called. It manages a boolean parameter 'RunRight' on the animator. ```csharp // State NOT locked to a Mecanim state — reason/update always called public class RunRightState : SKMecanimState { public RunRightState() {} // no base() call → mecanimStateHash = 0 public override void begin() { _machine.animator.SetBool("RunRight", true); } public override void reason() { if (!Input.GetKey(KeyCode.RightArrow)) _machine.changeState(); } public override void update(float deltaTime, AnimatorStateInfo stateInfo) {} public override void end() { _machine.animator.SetBool("RunRight", false); } } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.