This script demonstrates how AC characters can be integrated with Kinematic Character Controller.
It integrates with the Example Character Controller, to allow user-control of Players during gameplay, and AC-control of NPCs and players during cutscenes. Note that AC v1.62.5 or later is required.
To use it:
- Prepare your KCC character as normal using the Example Character Controller
- Attach either AC's NPC or Player component (tagging the GameObject as Player if the latter
- Attach the script below as a component named KCCIntegration:
KCCIntegration.cs:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using KinematicCharacterController;
using KinematicCharacterController.Examples;
using AC;
public class KCCIntegration : MonoBehaviour
{
public AC.Char characterAC;
public ExampleCharacterController kinematicController;
private enum ControlState { Null, UnderDirectControl, UnityPathfinding, ACTurning };
private ControlState controlState = ControlState.Null;
private void Update ()
{
// Update what our current control state is
UpdateControlState ();
// Move the character according to the current control state
UpdateMovement ();
}
private void UpdateControlState ()
{
// Check if we want to determine the character's position through AC, or just through direct input
if (!KickStarter.stateHandler.IsInGameplay () || !characterAC.IsPlayer || characterAC.IsMovingAlongPath () || KickStarter.settingsManager.movementMethod == MovementMethod.PointAndClick)
{
// Check if we want to make the character pathfind, or do nothing while AC turns them
if (characterAC.charState == CharState.Move)
{
controlState = ControlState.UnityPathfinding;
}
else
{
controlState = ControlState.ACTurning;
}
}
else
{
controlState = ControlState.UnderDirectControl;
}
}
private void UpdateMovement ()
{
switch (controlState)
{
case ControlState.UnderDirectControl:
UpdateDirectInput ();
break;
case ControlState.UnityPathfinding:
UpdatePathfindInput ();
break;
case ControlState.ACTurning:
UpdateJustTurningInput ();
break;
}
}
private void UpdateDirectInput ()
{
// Pass inputs to controller as normal
PlayerCharacterInputs characterInputs = new PlayerCharacterInputs();
// Build the CharacterInputs struct
characterInputs.MoveAxisForward = Input.GetAxisRaw ("Vertical");
characterInputs.MoveAxisRight = Input.GetAxisRaw ("Horizontal");
characterInputs.CameraRotation = Camera.main.transform.rotation;
characterInputs.JumpDown = Input.GetKeyDown (KeyCode.Space);
characterInputs.CrouchDown = Input.GetKeyDown (KeyCode.C);
characterInputs.CrouchUp = Input.GetKeyUp (KeyCode.C);
// Apply inputs to character
kinematicController.SetInputs (ref characterInputs);
}
private void UpdatePathfindInput ()
{
// Read the AC.Char script's GetTargetPosition and GetTargetRotation methods to dictate how the Controller / Motor should be affected
Vector3 targetPosition = characterAC.GetTargetPosition ();
Vector3 targetDirection = (targetPosition - kinematicController.Motor.TransientPosition).normalized;
KinematicCharacterController.Examples.AICharacterInputs inputs = new KinematicCharacterController.Examples.AICharacterInputs ();
inputs.MoveVector = targetDirection;
kinematicController.SetInputs (ref inputs);
kinematicController.Motor.RotateCharacter (characterAC.GetTargetRotation ());
}
public void UpdateJustTurningInput ()
{
// Stop regular input, halt movement, and use GetFrameRotation to manually enforce the per-frame rotation
KinematicCharacterController.Examples.AICharacterInputs inputs = new KinematicCharacterController.Examples.AICharacterInputs ();
kinematicController.SetInputs (ref inputs);
kinematicController.Motor.BaseVelocity = Vector3.zero;
kinematicController.Motor.SetRotation (characterAC.GetFrameRotation ());
}
private void OnTeleport ()
{
// This function is called by the Char script whenever the character has been teleported.
kinematicController.Motor.BaseVelocity = Vector3.zero;
kinematicController.Motor.SetPosition (characterAC.GetTargetPosition ());
kinematicController.Motor.SetRotation (characterAC.GetTargetRotation ());
if (kinematicController.OrientationMethod == OrientationMethod.TowardsCamera)
{
ACDebug.LogWarning ("Setting rotation to " + characterAC.GetTargetRotation () + ", but may not stick since the Orienation Method is Towards Camera.", gameObject);
}
}
}