using System.Collections.Generic; using Cielonos.MainGame.Characters; using Cielonos.MainGame.FunctionalAnimation; using Opsive.BehaviorDesigner.Runtime.Tasks; using Opsive.BehaviorDesigner.Runtime.Tasks.Actions; using Opsive.GraphDesigner.Runtime; using Opsive.GraphDesigner.Runtime.Variables; using Opsive.Shared.Utility; using SLSUtilities.FunctionalAnimation; using UnityEngine; namespace Cielonos.MainGame.Characters.AI { [Description("让角色播放一个功能动画,播放过程中会检测打断情况。")] [NodeIcon("Assets/Sprites/Icon/Play.png")] [Category("Cielonos")] public class PlayFuncAnim : AutomataActionBase { protected CharacterBase target; [Tooltip("是否选择其他目标,默认目标为玩家。")] public bool isOtherTarget = false; [Tooltip("如果选择了其他目标,则需要指定目标对象。")] public SharedVariable targetGameObject; private AnimationSubcontrollerBase animationSc => self.animationSc; private FunctionalAnimationSubmodule funcAnimSm; private RuntimeFuncAnim funcAnim; private float currentPlayTime => funcAnimSm.currentPlayTime; private float currentTotalPlayTime => funcAnimSm.currentTotalPlayTime; [Header("Animation Settings")] [Tooltip("要播放的功能动画名称。")] public string animationName; [Tooltip("是否选择其他功能动画子模块,默认子模块为全身动作子模块。")] public bool isOtherFuncAnimSm = false; [Tooltip("如果选择了其他功能动画子模块,则需要指定子模块名称。")] public string funcAnimSmName = ""; [Tooltip("动画过渡时间,单位秒。")] public float transitionDuration = 0.1f; [Tooltip("如果勾选此项,当动画被打断,返回Success(执行下一步),否则返回Failure(重新执行此步骤)。")] public bool disruptionReturnSuccess = true; [Tooltip("是否在开始播放动画时转向目标。")] public bool willTurnToTargetAtStart = true; [Tooltip("是否在播放过程中持续转向目标。")] public bool willTurnToTargetDuringPlaying = false; [Tooltip("是否调整根吸附以贴近目标。")] public bool willAdjustAdsorption = true; [Tooltip("是否持续调整根吸附以贴近目标,直到动画结束。")] public bool willKeepAdjustingAdsorption = false; [Tooltip("根吸附调整的最小距离。")] public float adsorptionMinDistance = 2; [Header("Other Settings")] [Tooltip("是否记录此动作到动作记录子模块中。")] public bool willRecordAction = true; private bool successPlayed = false; private bool frameBuffer; public override void OnAwake() { base.OnAwake(); funcAnimSm = isOtherFuncAnimSm ? null : animationSc.fullBodyFuncAnimSm; target = isOtherTarget ? targetGameObject.Value.GetComponent() : MainGameManager.Player; } public override void OnStart() { successPlayed = funcAnimSm.Play(animationName); frameBuffer = false; if (successPlayed) { funcAnim = funcAnimSm.currentRuntimeFuncAnim; if (willTurnToTargetAtStart) { self.movementSc.SmartTurnToTarget(target); } if (willAdjustAdsorption) { if (!willKeepAdjustingAdsorption) { funcAnim.AddUpdateUntilEvent(new SetRootAdsorptionAdjustment.Once(target, adsorptionMinDistance)); } else { funcAnim.AddUpdateEvent(new SetRootAdsorptionAdjustment.Keep(target, adsorptionMinDistance)); } } if (willRecordAction) { self.actionRecordSm.AddRecord(animationName); } } } public override TaskStatus OnUpdate() { if (!successPlayed) { if (!frameBuffer) { frameBuffer = true; return TaskStatus.Running; } else { return TaskStatus.Failure; } } if (funcAnimSm.currentRuntimeFuncAnim == funcAnim && funcAnimSm.CheckDisruption(DisruptionType.NormalAction)) { if (!frameBuffer) { frameBuffer = true; return TaskStatus.Running; } else { return TaskStatus.Success; } } if (funcAnim.isDisrupted || funcAnimSm.currentRuntimeFuncAnim != funcAnim) { //if (self.isDuringGetHit) return TaskStatus.Running; if (!frameBuffer) { frameBuffer = true; return TaskStatus.Running; } else { return disruptionReturnSuccess ? TaskStatus.Success : TaskStatus.Failure; } } if (willTurnToTargetDuringPlaying) { self.movementSc.TurnToTargetByAngle(target, 360f); } return TaskStatus.Running; } } }