/// <summary> /// Mix together a relative and absolute pose. For inputs, which pose is which doesn't matter. /// </summary> /// <param name="player">The player character</param> /// <param name="poseA">The first pose</param> /// <param name="weightA">Interpolation weight for the first pose</param> /// <param name="poseB">The second pose</param> /// <param name="weightB">Interpolation weight for the second pose</param> private void MixAbsoluteRelative(PlayerCharacter player, PoseInfo poseA, float weightA, PoseInfo poseB, float weightB) { AbsolutePoseInfo absPose; RelativePoseInfo relPose; float absWeight; float relWeight; if (PoseTools.IsAbsolute(poseA)) { absPose = (AbsolutePoseInfo)poseA; relPose = (RelativePoseInfo)poseB; absWeight = weightA; relWeight = weightB; } else { absPose = (AbsolutePoseInfo)poseB; relPose = (RelativePoseInfo)poseA; absWeight = weightB; relWeight = weightA; } MixAbsolute(player, absPose, absWeight); VirtualSnapshot.RestoreTransform(player); MixRelative(player, relPose, relWeight); }
/// <summary> /// Update which way the player is facing if necessary. /// </summary> /// <param name="player">The player character.</param> /// <param name="pose">The target pose for the player.</param> private void UpdateFacing(PlayerCharacter player, PoseInfo pose) { if (pose.Flipped) { player.SetFacing(Facing.Left); } else { player.SetFacing(Facing.Right); } }
//------------------------------------------------------------------------- // Helper Methods //------------------------------------------------------------------------- /// <summary> /// Get the state driver for the current state. /// </summary> /// <param name="pose">The pose clip.</param> /// <returns>The state driver for the current state.</returns> private StateDriver GetDriver(PoseInfo pose) { if (pose == null || pose.State == null) { return(null); } if (!stateDrivers.ContainsKey(pose.State)) { stateDrivers.Add(pose.State, StateDriver.For(pose.State)); } return(stateDrivers[pose.State]); }
/// <summary> /// Create a pose asset. /// </summary> /// <param name="graph">The playable graph this pose will belong to.</param> /// <param name="owner">IDK my BFF jill.</param> /// <returns>A script playable for the pose</returns> public override Playable CreatePlayable(PlayableGraph graph, GameObject owner) { // Use hook method to get the right type of pose info ScriptPlayable <PoseInfo> playable = CreateScriptPlayable(graph); PoseInfo p = playable.GetBehaviour(); if (State != null) { p.State = Type.GetType(State); } p.StartTime = (float)clip.start; p.EndTime = (float)clip.end; p.Duration = (float)clip.duration; return(playable); }
//------------------------------------------------------------------------- // Clip Mixing //------------------------------------------------------------------------- /// <summary> /// Mix a single clip. /// </summary> /// <param name="playable">The track's playable</param> /// <param name="player">The player character</param> /// <param name="clipIndex">The index of the clip to mix</param> private void MixSingle(Playable playable, PlayerCharacter player, int clipIndex) { PoseInfo pose = TimelineTools.GetClipInfo <PoseInfo>(playable, clipIndex); player.gameObject.SetActive(pose.Active); if (PoseTools.IsAbsolute(pose)) // Absolute { MixAbsolute(player, (AbsolutePoseInfo)pose); } else // Relative { VirtualSnapshot.RestoreTransform(player); MixRelative(player, (RelativePoseInfo)pose); } StateDriver driver = GetDriver(pose); UpdateFacing(player, pose); UpdateState(player, driver, playable, pose); }
/// <summary> /// Mix together two active clips based on what type of clips they are. /// </summary> /// <param name="playable">The track's playable</param> /// <param name="player">The player character</param> /// <param name="clipIndexA">The index of the first clip to mix</param> /// <param name="clipIndexB">The index of the second clip to mix</param> private void MixMultiple(Playable playable, PlayerCharacter player, int clipIndexA, int clipIndexB) { PoseInfo poseA = TimelineTools.GetClipInfo <PoseInfo>(playable, clipIndexA); float weightA = playable.GetInputWeight(clipIndexA); PoseInfo poseB = TimelineTools.GetClipInfo <PoseInfo>(playable, clipIndexB); float weightB = playable.GetInputWeight(clipIndexB); // Mix together clips based on their typing. if (PoseTools.IsAbsolute(poseA) && PoseTools.IsAbsolute(poseB)) { MixAbsolute(player, (AbsolutePoseInfo)poseA, weightA); MixAbsolute(player, (AbsolutePoseInfo)poseB, weightB); } else if (PoseTools.IsRelative(poseA) && PoseTools.IsRelative(poseB)) { VirtualSnapshot.RestoreTransform(player); RelativePoseInfo mixed = new RelativePoseInfo(); mixed.Position = poseA.Position * weightA + poseB.Position * weightB; mixed.Rotation = poseA.Rotation * weightA + poseB.Rotation * weightB; mixed.Scale = poseA.Scale * weightA + poseB.Scale * weightB; MixRelative(player, mixed); } else { MixAbsoluteRelative(player, poseA, weightA, poseB, weightB); } // Apply player facing and FSM state based on which pose is weighted heavier. PoseInfo dominantPose = weightA > weightB ? poseA : poseB; player.gameObject.SetActive(dominantPose.Active); StateDriver driver = GetDriver(dominantPose); UpdateFacing(player, dominantPose); UpdateState(player, driver, playable, dominantPose); }
/// <summary> /// Whether or not a pose clip is meant for relative positioning. /// </summary> /// <param name="template">The pose information</param> /// <returns>True if the pose information is for a RelativePose clip</returns> public static bool IsRelative(PoseInfo template) => template.GetType() == typeof(RelativePoseInfo);
/// <summary> /// Whether or not a pose clip is meant for absolute positioning. /// </summary> /// <param name="template">The pose information</param> /// <returns>True if the pose information is for an AbsolutePose clip</returns> public static bool IsAbsolute(PoseInfo template) => template.GetType() == typeof(AbsolutePoseInfo);
/// <summary> /// Update the animation/FSM state for the player. /// </summary> /// <param name="player">The player character.</param> /// <param name="driver">The state driver for the target state.</param> /// <param name="playable">The playable associated with this clip.</param> /// <param name="pose">Information about the player's pose.</param> private void UpdateState(PlayerCharacter player, StateDriver driver, Playable playable, PoseInfo pose) { if (driver == null) { return; } #if UNITY_EDITOR if (Application.isPlaying) { #endif if (!driver.IsInState(player.FSM)) { driver.ForceStateChangeOn(player.FSM); } #if UNITY_EDITOR } else { // In-Editor we don't want to actually change the Finite State Machine, // so just play the appropriate animation. float totalTrackTime = (float)playable.GetTime(); float currentTimeInClip = totalTrackTime - pose.StartTime; driver.SampleClip(player.FSM, currentTimeInClip); } #endif }