using System; using UnityEngine; using Framework.Services; using Framework.State; namespace Framework.Actors { /// Controls an animated character in a scene public class ActorController : ScriptComponent { #if UNITY_EDITOR /// Name of the current move the actor is running (for debugging) public string ActiveMoveName; #endif // UNITY_EDITOR /// Presenter driving the Mecanim animation state machine public ActorPresenter Presenter { get { if(this.presenter == null) { this.presenter = gameObject.GetComponentInChildren(); } #if UNITY_EDITOR if((this.presenter == null) && !this.warnedAboutMissingPresenter) { Debug.LogWarning( "Actor controller on '" + gameObject.name + "' could not locate a presenter. " + "The actor's animation state will not be updated." ); this.warnedAboutMissingPresenter = true; } #endif return this.presenter; } } /// Helper used to check whether the actor is grounded or airborne /// /// This is component is created lazily - if your actor doesn't need ground /// public GroundChecker GroundChecker { get { if(this.groundChecker == null) { this.groundChecker = new GroundChecker(); } return this.groundChecker; } } /// Actor physics provider, if present public ActorPhysics ActorPhysics { get { if(!this.checkedForActorPhysics) { this.actorPhysics = GetComponent(); this.checkedForActorPhysics = true; } return this.actorPhysics; } } /// Rigid body physics component, if present public Rigidbody Rigidbody { get { if(!this.checkedForRigidBody) { this.rigidBody = GetComponent(); this.checkedForRigidBody = true; } return this.rigidBody; } } /// Move that is currently being executed by the actor controller /// /// 'Moves' are micro-actions that can be performed by actors, such as jumping, /// falling or sitting down. See the class for a more thorough /// description. If no move is active, the actor will remain passive. /// public Move ActiveMove { get { return this.activeMove; } set { if(this.activeMove != null) { this.activeMove.OnEnded(); } this.activeMove = value; #if UNITY_EDITOR if(this.activeMove == null) { this.ActiveMoveName = null; } else { this.ActiveMoveName = this.activeMove.GetType().Name; } #endif if(value != null) { value.Initialize(this); // Does nothing if already initialized value.OnStarted(); } } } /// Called when the component gets added to a gameObject /// /// Unity calls this method right after all components have been constructed. /// This is the place to look up other components and GameObjects, but no calls /// should be made to them yet (because the Awake() order is undefined and /// the other component might not have set up its dependencies yet). /// protected override void Awake() { base.Awake(); // If the control framework is used (lets players 'possess' actors to decide // which actor should respond to input), sign up for it. this.controllable = GetComponent(); if(this.controllable == null) { OnControlTaken(0); } else { this.onControlTakenDelegate = new Action(OnControlTaken); this.onControlReleasedDelegate = new Action(OnControlReleased); this.controllable.ControlTaken += this.onControlTakenDelegate; this.controllable.ControlReleased += this.onControlReleasedDelegate; } } /// Called once per physics frame to update the positions of game object protected virtual void FixedUpdate() { if(this.activeMove != null) { this.activeMove.FixedUpdate(); if(this.activeMove.IsCompleted) { OnMoveCompleted(); } } } /// Called once per visual frame protected virtual void Update() { if(this.activeMove != null) { this.activeMove.Update(); if(this.activeMove.IsCompleted) { OnMoveCompleted(); } } } /// Called when the currently active move has completed protected virtual void OnMoveCompleted() { ActiveMove = null; // Using property setter to ensure OnEnded() call. } /// Called when a player assumes control of the actor /// Index of the player that has assumed control protected virtual void OnControlTaken(int playerIndex) {} /// Called when a player releases control of the actor /// Index of the player that has released control protected virtual void OnControlReleased(int playerIndex) {} /// Helper used to check whether the actor is grounded or airborne private GroundChecker groundChecker; /// Presenter driving the Mecanim animation state machine private ActorPresenter presenter; /// Rigid body physics component, if present private Rigidbody rigidBody; /// Whether the presence of a Rigidbody component has been checked private bool checkedForRigidBody; /// Actor physics provider, if present private ActorPhysics actorPhysics; /// Whether the presence of an ActorPhysics component has been checked private bool checkedForActorPhysics; /// Move the actor is currently executing private Move activeMove; /// Tracks, if present, whether the player is controlling this actor private IControllable controllable; /// Delegate for the OnControlTaken() method private Action onControlTakenDelegate; /// Delegate for the OnControlReleased() method private Action onControlReleasedDelegate; #if UNITY_EDITOR /// Whether the missing presenter warning has been displayed private bool warnedAboutMissingPresenter; #endif } } // namespace Framework.Actors