IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> [Unity] ACT 战斗系统学习 4:重构前的第三人称控制器 -> 正文阅读

[游戏开发][Unity] ACT 战斗系统学习 4:重构前的第三人称控制器

重构前,我的控制器是这样子

Assets/MeowACT/Scripts/ThirdPerson/ThirdPerson.cs

// ----------------------------------------------
// 作者: 廉价喵
// 创建于: 25/03/2022 23:35
// 最后一次修改于: 28/03/2022 20:03
// 版权所有: CheapMiaoStudio
// 描述:
// ----------------------------------------------

using Cinemachine;
using UnityEngine;

namespace MeowACT
{
    /// <summary>
    /// 第三人称主角
    /// </summary>
    public class ThirdPerson : MonoBehaviour
    {
        // Perfab 自带组件

        public CharacterController CharacterCtr;
        public Animator Anim;
        public MeowACTInputController Input;
        public GameObject CMCameraFollowTarget;
        public GameObject PlayerFollowCamera;
        public Camera MainCamera;
        public GameObject BattleGUI;
        
        // 控制组件

        public ThirdPersonAttributeManager AttributeManager;
        public ThirdPersonEventManager EventManager;
        private ThirdPersonActionController actionController;
        private ThirdPersonLocomotionController locomotionController;
        private ThirdPersonAnimationController animationController;
        private ThirdPersonBattleController battleController;
        private ThirdPersonUIController uiController;

        private void Awake()
        {
            // 初始化 Perfab 自带组件

            CharacterCtr = GetComponent<CharacterController>();
            Anim = GetComponent<Animator>();
            Input = gameObject.AddComponent<MeowACTInputController>();
            CMCameraFollowTarget = transform.Find("PlayerCameraRoot").gameObject;
            PlayerFollowCamera = GameObject.Find("PlayerFollowCamera");
            PlayerFollowCamera.GetComponent<CinemachineVirtualCamera>().Follow = CMCameraFollowTarget.transform;
            MainCamera = GameObject.FindGameObjectWithTag("MainCamera").GetComponent<Camera>();
            BattleGUI = transform.Find("BattleGUI").gameObject;
            BattleGUI.GetComponent<Canvas>().worldCamera = MainCamera;
            BattleGUI.GetComponent<Canvas>().planeDistance = 1;
            
            // 初始化控制组件
            
            AttributeManager = new ThirdPersonAttributeManager(this);
            EventManager = new ThirdPersonEventManager(this);
            actionController = new ThirdPersonActionController(this);
            locomotionController = new ThirdPersonLocomotionController(this);
            animationController = new ThirdPersonAnimationController(this);
            battleController = new ThirdPersonBattleController(this);
            uiController = new ThirdPersonUIController(this);
            
            // 初始化动画状态机参数
            
            animationController.AssignAnimationIDs();

            // 初始化属性

            AttributeManager.Init();
        }

        protected void Update()
        {
            // 行为

            actionController.TryAction();

            // 运动

            locomotionController.ApplyGravity();
            locomotionController.GroundedCheck();
            locomotionController.Move();
            locomotionController.RotateToMoveDir();

            // 动画

            animationController.SetAnimatorValue();
        }

        protected void LateUpdate()
        {
            // 运动

            locomotionController.CameraRotate();
        }

        private void OnControllerColliderHit(ControllerColliderHit hit)
        {
            // 运动

            if (AttributeManager.canPush) PhysicsUtility.PushRigidBodies(hit, AttributeManager.pushLayers, AttributeManager.strength);
        }

        /// <summary>
        /// 由动画触发的动画事件,还需要在 mgr 中触发事件:尝试造成伤害
        /// </summary>
        public void OnTryDoDamageAnimEvent()
        {
            EventManager.Fire("TryDoDamageEvent", null);
            Debug.Log("OnTryDoDamageAnimEvent");
        }
        
        /// <summary>
        /// 由动画触发的动画事件,还需要在行为控制器中触发事件:结束近战攻击
        /// </summary>
        public void OnEndMeleeAttackAnimEvent()
        {
            actionController.FireEndMeleeAttackEvent();
            Debug.Log("OnEndMeleeAttackAnimEvent");
        }
    }
}

Assets/MeowACT/Scripts/ThirdPerson/ThirdPersonActionController.cs

// ----------------------------------------------
// 作者: 廉价喵
// 创建于: 26/03/2022 2:15
// 最后一次修改于: 28/03/2022 15:28
// 版权所有: CheapMiaoStudio
// 描述:
// ----------------------------------------------

using UnityEngine;

namespace MeowACT
{
    /// <summary>
    /// 第三人称行为控制器
    /// </summary>
    public class ThirdPersonActionController
    {
        /// <summary>
        /// 第三人称行为控制器的主人
        /// </summary>
        public ThirdPerson Owner;
        
        /// <summary>
        /// 冲刺的冷却时间
        /// </summary>
        private float sprintTimeout = 0.5f;

        /// <summary>
        /// 攻击的冷却时间
        /// </summary>
        private float attackTimeout = 1f;
        
        /// <summary>
        /// 第三人称行为控制器的构造函数
        /// </summary>
        /// <param name="owner">第三人称行为控制器的主人</param>
        public ThirdPersonActionController(ThirdPerson owner)
        {
            Owner = owner;
        }
        
        public void TryAction()
        {
            if(TrySprint())
                return;
            TryMeleeAttack();
        }
        
        private bool TrySprint()
        {
            if (Owner.Input.Sprint && !Owner.AttributeManager.IsSprinting && !Owner.AttributeManager.IsFreezingMove)
            {
                FireBeginSprintEvent();
                
                var cotimer = TimerSystemSingleton.SingleInstance.CreateCoTimer(0,
                    delegate(object[] args) { FireAfterBeginSprintEvent(); });
                cotimer.Start();
                
                var timer = TimerSystemSingleton.SingleInstance.CreateTimer(sprintTimeout, false,
                    delegate(object[] args) { FireEndSprintEvent(); });
                timer.Start();

                return true;
            }

            return false;
        }

        private bool TryMeleeAttack()
        {   
            if (Owner.Input.Attack && !Owner.AttributeManager.IsMeleeAttacking)
            {
                FireBeginMeleeAttackEvent();

                return true;
            }

            return false;
        }

        private void FireBeginSprintEvent()
        {
            Owner.AttributeManager.IsSprinting = true;
            Owner.AttributeManager.IsSprintBegin = true;

            // 开始冲刺进入霸体
            Owner.AttributeManager.IsSuperArmor = true;
            
            // 开始冲刺消耗体力
            Owner.AttributeManager.NRG -= Owner.AttributeManager.SprintCost;

            Owner.EventManager.Fire("BeginSprintEvent", null);
        }

        private void FireAfterBeginSprintEvent()
        {
            Owner.AttributeManager.IsSprintBegin = false;
            
            Owner.EventManager.Fire("AfterBeginSprintEvent", null);
        }
        
        private void FireEndSprintEvent()
        {
            Owner.AttributeManager.IsSprinting = false;
            
            // 结束冲刺离开霸体
            Owner.AttributeManager.IsSuperArmor = true;
            
            Owner.EventManager.Fire("EndSprintEvent", null);
        }
        
        private void FireBeginMeleeAttackEvent()
        {
            Owner.AttributeManager.IsMeleeAttacking = true;
            Owner.AttributeManager.IsFreezingMove = true;

            // 开始近战攻击消耗体力
            Owner.AttributeManager.NRG -= Owner.AttributeManager.MeleeAttackCost;
            
            Owner.EventManager.Fire("BeginMeleeAttackEvent", null);
        }
        
        public void FireEndMeleeAttackEvent()
        {
            Owner.AttributeManager.IsMeleeAttacking = false;
            Owner.AttributeManager.IsFreezingMove = false;
            
            Owner.EventManager.Fire("EndMeleeAttackEvent", null);
        }
    }
}

Assets/MeowACT/Scripts/ThirdPerson/ThirdPersonAnimationController.cs

// ----------------------------------------------
// 作者: 廉价喵
// 创建于: 25/03/2022 23:24
// 最后一次修改于: 26/03/2022 21:16
// 版权所有: CheapMiaoStudio
// 描述:
// ----------------------------------------------

using UnityEngine;

namespace MeowACT
{
    /// <summary>
    /// 第三人称动画状态机控制器
    /// </summary>
    public class ThirdPersonAnimationController
    {
        /// <summary>
        /// 第三人称动画状态机控制器的主人
        /// </summary>
        public ThirdPerson Owner;

        // id
	    
        /// <summary>
        /// 动画状态机的速度参数的 id
        /// </summary>
        private int animIDSpeed;
        /// <summary>
        /// 动画状态机的落地参数的 id
        /// </summary>
        private int animIDGrounded;
        /// <summary>
        /// 动画状态机的自由落体参数的 id
        /// </summary>
        private int animIDFreeFall;
        /// <summary>
        /// 动画状态机的近战攻击参数的 id
        /// </summary>
        private int animIDMeleeAttack;
        
        // 混合值
        
        /// <summary>
        /// 跑步动画混合值
        /// </summary>
        private float moveAnimBlend;
        /// <summary>
        /// 跑步动画混合值的过渡速度
        /// </summary>
        private float moveAnimBlendSmoothVelocity;
        /// <summary>
        /// 跑步动画混合值的过渡时间
        /// </summary>
        private float moveAnimBlendSmoothTime = 0.2f;
        
        /// <summary>
        /// 第三人称动画状态机控制器的构造函数
        /// </summary>
        /// <param name="owner">第三人称动画状态机控制器的主人</param>
        public ThirdPersonAnimationController(ThirdPerson owner)
        {
            Owner = owner;
            
            // 事件绑定

            Owner.EventManager.BeginMeleeAttackEvent += OnBeginMeleeAttackEvent;
        }

        /// <summary>
        /// 第三人称动画状态机控制器的析构函数
        /// </summary>
        ~ThirdPersonAnimationController()
        {
            // 事件解绑

            Owner.EventManager.BeginMeleeAttackEvent -= OnBeginMeleeAttackEvent;
        }
        
        /// <summary>
        /// 初始化动画状态机参数
        /// </summary>
        public void AssignAnimationIDs()
        {
            animIDSpeed = Animator.StringToHash("ForwardSpeed");
            animIDGrounded = Animator.StringToHash("Grounded");
            animIDFreeFall = Animator.StringToHash("FreeFall");
            animIDMeleeAttack = Animator.StringToHash("Attack");
        }

        /// <summary>
        /// 设置动画状态机参数
        /// </summary>
        public void SetAnimatorValue()
        {
            // 着地
            Owner.Anim.SetBool(animIDGrounded, Owner.AttributeManager.IsGrounded);
            
            // 跳跃
            if (Owner.AttributeManager.IsGrounded)
                Owner.Anim.SetBool(animIDFreeFall, false);
            // 自由下落
            else
                Owner.Anim.SetBool(animIDFreeFall, true);
            
            // 移动
            moveAnimBlend = Mathf.SmoothDamp(moveAnimBlend, Owner.AttributeManager.HorizontalVelocity.magnitude, ref moveAnimBlendSmoothVelocity, moveAnimBlendSmoothTime);
            Owner.Anim.SetFloat(animIDSpeed, moveAnimBlend);
        }

        private void OnBeginMeleeAttackEvent(object[] args)
        {
            Owner.Anim.SetTrigger(animIDMeleeAttack);
        }
    }
}

Assets/MeowACT/Scripts/ThirdPerson/ThirdPersonAttributeManager.cs

// ----------------------------------------------
// 作者: 廉价喵
// 创建于: 26/03/2022 15:00
// 最后一次修改于: 26/03/2022 22:15
// 版权所有: CheapMiaoStudio
// 描述:
// ----------------------------------------------

using UnityEngine;

namespace MeowACT
{
    /// <summary>
    /// 第三人称属性管理器
    /// </summary>
    public class ThirdPersonAttributeManager
    {
        // 刚体模拟

        public LayerMask pushLayers;
        public bool canPush;
        [Range(0.5f, 5f)] public float strength = 1.1f;

        // 运动参数

        /// <summary>
        /// 水平速度
        /// </summary>
        /// <returns></returns>
        public Vector3 HorizontalVelocity;

        /// <summary>
        /// 竖向速度
        /// </summary>
        public Vector3 VerticalVelocity;

        /// <summary>
        /// 血量
        /// </summary>
        private float hp;
        
        /// <summary>
        /// 最大血量
        /// </summary>
        public float MaxHP = 100f;
        
        /// <summary>
        /// 血量
        /// </summary>
        public float HP
        {
            get => hp;
            set
            {
                Owner.EventManager.Fire("OnHPChangeEvent", new object[]{hp/MaxHP, value/MaxHP});
                hp = value;
            }
        }

        /// <summary>
        /// 体力
        /// </summary>
        private float nrg;

        /// <summary>
        /// 最大体力
        /// </summary>
        public float MaxNRG = 100f;
        
        /// <summary>
        /// 体力
        /// </summary>
        public float NRG
        {
            get => nrg;
            set
            {
                Owner.EventManager.Fire("OnNRGChangeEvent", new object[]{nrg/MaxNRG, value/MaxNRG});
                nrg = value;
            }
        }

        /// <summary>
        /// 冲刺的体力消耗
        /// </summary>
        public float SprintCost = 10f;
        
        /// <summary>
        /// 近战攻击的体力消耗
        /// </summary>
        public float MeleeAttackCost = 10f;
        
        // 运动状态

        // 运动状态 - 冲刺相关

        /// <summary>
        /// 是否落地
        /// </summary>
        public bool IsGrounded = true;

        /// <summary>
        /// 是否正在静止运动
        /// </summary>
        public bool IsFreezingMove = false;

        /// <summary>
        /// 是否正在冲刺
        /// </summary>
        public bool IsSprinting = false;

        /// <summary>
        /// 是否开始冲刺
        /// </summary>
        public bool IsSprintBegin = false;

        // 运动状态 - 攻击相关

        /// <summary>
        /// 是否正在攻击
        /// </summary>
        public bool IsMeleeAttacking = false;

        /// <summary>
        /// 是否霸体
        /// </summary>
        public bool IsSuperArmor = false;
        
        /// <summary>
        /// 第三人称属性管理器的主人
        /// </summary>
        public ThirdPerson Owner;

        /// <summary>
        /// 第三人称属性管理器的构造函数
        /// </summary>
        /// <param name="owner">第三人称属性管理器的主人</param>
        public ThirdPersonAttributeManager(ThirdPerson owner)
        {
            Owner = owner;
        }
        
        public void Init()
        {
            HP = MaxHP;
            NRG = MaxNRG;
        }
    }
}

Assets/MeowACT/Scripts/ThirdPerson/ThirdPersonBattleController.cs

// ----------------------------------------------
// 作者: 廉价喵
// 创建于: 25/03/2022 22:33
// 最后一次修改于: 26/03/2022 22:21
// 版权所有: CheapMiaoStudio
// 描述:
// ----------------------------------------------

using UnityEngine;

namespace MeowACT
{
    /// <summary>
    /// 第三人称战斗控制器
    /// </summary>
    public class ThirdPersonBattleController
    {
        /// <summary>
        /// 第三人称战斗控制器的主人
        /// </summary>
        public ThirdPerson Owner;

        /// <summary>
        /// 第三人称战斗控制器的构造函数
        /// </summary>
        /// <param name="owner">第三人称战斗控制器的主人</param>
        public ThirdPersonBattleController(ThirdPerson owner)
        {
            Owner = owner;
            
            // 事件绑定

            Owner.EventManager.TryDoDamageEvent += TryDoDamage;
        }

        /// <summary>
        /// 第三人称战斗控制器的析构函数
        /// </summary>
        ~ThirdPersonBattleController()
        {
            // 事件解绑

            Owner.EventManager.TryDoDamageEvent -= TryDoDamage;
        }
        
        public void MeleeAttack()
        {
            
        }
        
        /// <summary>
        /// 动画事件:尝试攻击
        /// </summary>
        private void TryDoDamage(object[] args)
        {
            
        }
    }
}

Assets/MeowACT/Scripts/ThirdPerson/ThirdPersonEventManager.cs

// ----------------------------------------------
// 作者: 廉价喵
// 创建于: 26/03/2022 14:46
// 最后一次修改于: 28/03/2022 21:15
// 版权所有: CheapMiaoStudio
// 描述:
// ----------------------------------------------

using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;

namespace MeowACT
{
    /// <summary>
    /// 第三人称事件管理器
    /// </summary>
    public class ThirdPersonEventManager
    {
        // 事件相关

        /// <summary>
        /// 开始冲刺
        /// </summary>
        public event Action<object[]> BeginSprintEvent
        {
            add => eventMap["BeginSprintEvent"] = value;
            remove => eventMap["BeginSprintEvent"] = value;
        }
        
        /// <summary>
        /// 开始冲刺之后一帧
        /// </summary>
        public event Action<object[]> AfterBeginSprintEvent
        {
            add => eventMap["AfterBeginSprintEvent"] = value;
            remove => eventMap["AfterBeginSprintEvent"] = value;
        }
        
        /// <summary>
        /// 结束冲刺
        /// </summary>
        public event Action<object[]> EndSprintEvent
        {
            add => eventMap["EndSprintEvent"] = value;
            remove => eventMap["EndSprintEvent"] = value;
        }
        
        /// <summary>
        /// 开始近战攻击
        /// </summary>
        public event Action<object[]> BeginMeleeAttackEvent
        {
            add => eventMap["BeginMeleeAttackEvent"] = value;
            remove => eventMap["BeginMeleeAttackEvent"] = value;
        }
        
        /// <summary>
        /// 结束近战攻击
        /// </summary>
        public event Action<object[]> EndMeleeAttackEvent
        {
            add => eventMap["EndMeleeAttackEvent"] = value;
            remove => eventMap["EndMeleeAttackEvent"] = value;
        }
        
        /// <summary>
        /// 尝试造成伤害
        /// </summary>
        public event Action<object[]> TryDoDamageEvent
        {
            add => eventMap["TryDoDamageEvent"] = value;
            remove => eventMap["TryDoDamageEvent"] = value;
        }
        
        /// <summary>
        /// 结束连击
        /// </summary>
        public event Action<object[]> EndComboEvent
        {
            add => eventMap["EndComboEvent"] = value;
            remove => eventMap["EndComboEvent"] = value;
        }
        
        /// <summary>
        /// 血量改变
        /// </summary>
        public event Action<object[]> OnHPChangeEvent
        {
            add => eventMap["OnHPChangeEvent"] = value;
            remove => eventMap["OnHPChangeEvent"] = value;
        }
        
        /// <summary>
        /// 体力改变
        /// </summary>
        public event Action<object[]> OnNRGChangeEvent
        {
            add => eventMap["OnNRGChangeEvent"] = value;
            remove => eventMap["OnNRGChangeEvent"] = value;
        }
            
        /// <summary>
        /// 开始自动恢复体力
        /// </summary>
        public event Action<object[]> BeginNRGRecoverEvent
        {
            add => eventMap["BeginNRGRecoverEvent"] = value;
            remove => eventMap["BeginNRGRecoverEvent"] = value;
        }
        
        /// <summary>
        /// 结束自动恢复体力
        /// </summary>
        public event Action<object[]> EndNRGRecoverEvent
        {
            add => eventMap["EndNRGRecoverEvent"] = value;
            remove => eventMap["EndNRGRecoverEvent"] = value;
        }

        public UnityEvent<object[]> TestEvent;
        
        /// <summary>
        /// 事件字典
        /// </summary>
        private Dictionary<string, Action<object[]>> eventMap = new Dictionary<string, Action<object[]>>();
        
        /// <summary>
        /// 第三人称事件管理器的主人
        /// </summary>
        public ThirdPerson Owner;

        /// <summary>
        /// 第三人称事件管理器的构造函数
        /// </summary>
        /// <param name="owner">第三人称事件管理器的主人</param>
        public ThirdPersonEventManager(ThirdPerson owner)
        {
            Owner = owner;
            
            // 事件字典的键初始化
            
            eventMap.Add("BeginSprintEvent", null);
            eventMap.Add("AfterBeginSprintEvent", null);
            eventMap.Add("EndSprintEvent", null);
            eventMap.Add("BeginMeleeAttackEvent", null);
            eventMap.Add("EndMeleeAttackEvent",null);
            eventMap.Add("TryDoDamageEvent", null);
            eventMap.Add("EndComboEvent", null);
            eventMap.Add("OnHPChangeEvent", null);
            eventMap.Add("OnNRGChangeEvent", null);
            eventMap.Add("BeginNRGRecoverEvent", null);
            eventMap.Add("EndNRGRecoverEvent", null);
        }

        /// <summary>
        /// 触发事件
        /// </summary>
        /// <param name="eventName">事件名</param>
        /// <param name="args">事件参数</param>
        public void Fire(string eventName, object[] args)
        {
            eventMap[eventName]?.Invoke(args);
        }
    }
}

Assets/MeowACT/Scripts/ThirdPerson/ThirdPersonLocomotionController.cs

// ----------------------------------------------
// 作者: 廉价喵
// 创建于: 16/03/2022 16:53
// 最后一次修改于: 28/03/2022 19:55
// 版权所有: CheapMiaoStudio
// 描述:
// ----------------------------------------------

using UnityEngine;

namespace MeowACT
{
	/// <summary>
	/// 第三人称运动控制器
	/// </summary>
    public class ThirdPersonLocomotionController
    {
	    // 常量
	    
	    /// <summary>
	    /// 微量
	    /// </summary>
	    private const float Threshold = 0.01f;
	    
	    
	    // 运动相关

	    // 行走相关
	    
	    /// <summary>
	    /// 移动速度
	    /// </summary>
	    private float walkSpeed = 7f;
	    /// <summary>
	    /// 玩家旋转的过渡时间
	    /// </summary>
	    private float rotationSmoothTime = 0.2f;
	    /// <summary>
	    /// 旋转角的过渡速度
	    /// </summary>
	    private float rotationSmoothVelocity;
	    /// <summary>
	    /// 行走速度的过渡速度
	    /// </summary>
	    private Vector3 walkSmoothVelocity;
	    /// <summary>
	    /// 玩家行走的过渡时间
	    /// </summary>
	    private float walkSmoothTime = 0.2f;

	    // 冲刺相关
	    
	    /// <summary>
	    /// 冲刺速度
	    /// </summary>
	    private float sprintSpeed = 15f;
	    /// <summary>
	    /// 冲刺速度的过渡速度
	    /// </summary>
	    private Vector3 sprintSmoothVelocity;
	    /// <summary>
	    /// 玩家冲刺速度的过渡时间
	    /// </summary>
	    private float sprintSmoothTime = 0.5f;
	    
	    // 物理相关
	    
	    /// <summary>
	    /// 重力系数
	    /// </summary>
	    private float gravity = -9.8f;
	    /// <summary>
	    /// 最大下落速度
	    /// </summary>
	    private float terminalVelocity = 53f;
	    
	    // 落地相关
	    
	    /// <summary>
	    /// 落地球形碰撞检测中心点的竖向偏移量
	    /// </summary>
	    private float groundedOffset = -0.14f;
	    /// <summary>
	    /// 落地球形碰撞检测的半径
	    /// </summary>
	    private float groundedRadius = 0.28f;
	    /// <summary>
	    /// 落地球形碰撞检测的层级
	    /// </summary>
	    private int groundLayers = 1;

	    // 摄像机相关
		
		private float cameraAngleOverride = 0f;
	    /// <summary>
	    /// 摄像机转动的速度
	    /// </summary>
	    private float cameraRotSpeed = 25f;
	    /// <summary>
	    /// 摄像机最大俯仰角
	    /// </summary>
	    private float topClamp = 70f; 
	    /// <summary>
	    /// 摄像机最小俯仰角
	    /// </summary>
	    private float bottomClamp = -30f;
	    /// <summary>
	    /// 摄像机跟随点的当前俯仰角的过渡时间
	    /// </summary>
	    private float cinemachinePitchSmoothTime = 0.1f;
	    /// <summary>
	    /// 摄像机跟随点的当前偏航角的过渡时间
	    /// </summary>
	    private float cinemachineYawSmoothTime = 0.1f;
	    /// <summary>
	    /// 摄像机是否固定
	    /// </summary>
	    private bool isCameraFixed = false;
	    /// <summary>
	    /// 摄像机跟随点的期望俯仰角
	    /// </summary>
	    private float cinemachineTargetPitch;
	    /// <summary>
	    /// 摄像机跟随点的期望偏航角
	    /// </summary>
	    private float cinemachineTargetYaw;
	    /// <summary>
	    /// 摄像机跟随点的当前俯仰角和摄像机跟随点的当前偏航角组成的向量
	    /// </summary>
	    private Vector2 cinemachineCurrPY;
	    /// <summary>
	    /// 摄像机跟随点的当前俯仰角的过渡速度
	    /// </summary>
	    private float cinemachinePitchSmoothVelocity;
	    /// <summary>
	    /// 摄像机跟随点的当前俯仰角的过渡速度
	    /// </summary>
	    private float cinemachineYawSmoothVelocity;

	    /// <summary>
		/// 第三人称运动控制器的主人
		/// </summary>
		public ThirdPerson Owner;
		
		/// <summary>
		/// 第三人称运动控制器的构造函数
		/// </summary>
		/// <param name="owner">第三人称运动控制器的主人</param>
		public ThirdPersonLocomotionController(ThirdPerson owner)
	    {
		    Owner = owner;
	    }

		/// <summary>
	    /// 落地检查
	    /// </summary>
	    public void GroundedCheck()
        {
	        var spherePosition = new Vector3(Owner.transform.position.x, Owner.transform.position.y - groundedOffset, Owner.transform.position.z);
            Owner.AttributeManager.IsGrounded = Physics.CheckSphere(spherePosition, groundedRadius, groundLayers, QueryTriggerInteraction.Ignore);
        }
	    
	    /// <summary>
	    /// 应用重力
	    /// </summary>
        public void ApplyGravity()
        {
	        if (Owner.AttributeManager.IsGrounded && Owner.AttributeManager.VerticalVelocity.y < 0.0f)
		        Owner.AttributeManager.VerticalVelocity.y = -2f;
	        else if (Owner.AttributeManager.VerticalVelocity.y < terminalVelocity)
		        Owner.AttributeManager.VerticalVelocity.y += gravity * Time.deltaTime;
        }

        /// <summary>
        /// 移动
        /// </summary>
        public void Move()
        {
	        if (Owner.AttributeManager.IsFreezingMove)
	        {
		        Owner.AttributeManager.HorizontalVelocity = Vector3.zero;
		        return;
	        }

	        Owner.AttributeManager.HorizontalVelocity = Owner.AttributeManager.IsSprinting ? GetSprintSpeed() : GetNormalSpeed();

	        Owner.CharacterCtr.Move((Owner.AttributeManager.HorizontalVelocity + Owner.AttributeManager.VerticalVelocity) * Time.deltaTime);
        }

        private Vector3 GetSprintSpeed()
        {
	        // 输入移动方向
	        Vector3 inputDirection = new Vector3(Owner.Input.Move.x, 0.0f, Owner.Input.Move.y).normalized;
	        // 期望旋转
	        // 因为摄像机呼吸,Owner.MainCamera.transform.eulerAngles.y 会发生抖动,进而导致玩家在起步的时候有一个微小抖动
	        // 而 cinemachineTargetYaw 不会抖动,因此采用 cinemachineTargetYaw
	        float targetRotation = Mathf.Atan2(inputDirection.x, inputDirection.z) * Mathf.Rad2Deg + cinemachineTargetYaw;
	        // 期望移动方向
	        Vector3 targetDirection = Quaternion.Euler(0.0f, targetRotation, 0.0f) * Vector3.forward;
	        
	        // 如果按下冲刺,那么初始化冲刺
	        if (Owner.AttributeManager.IsSprintBegin)
	        {
		        // 当前附加速度初始化为冲刺速度
		        // 不需要 SmoothDamp,这是突变的
		        // 如果没有运动输入的话,那么冲刺方向为角色当前朝向
		        if (Owner.Input.Move == Vector2.zero)
			        return Owner.transform.forward * sprintSpeed;
		        // 有运动的输入的话,那么冲刺方向为运动输入的方向
		        else
			        return targetDirection.normalized * sprintSpeed;
	        }
	        // 否则冲刺速度趋向 0
	        else
				return Vector3.SmoothDamp(Owner.AttributeManager.HorizontalVelocity, Vector3.zero, ref sprintSmoothVelocity, sprintSmoothTime);
        }

        private Vector3 GetNormalSpeed()
        {
	        // 输入移动方向
	        Vector3 inputDirection = new Vector3(Owner.Input.Move.x, 0.0f, Owner.Input.Move.y).normalized;
	        
	        // 期望旋转
	        // 因为摄像机呼吸,Owner.MainCamera.transform.eulerAngles.y 会发生抖动,进而导致玩家在起步的时候有一个微小抖动
	        // 而 cinemachineTargetYaw 不会抖动,因此采用 cinemachineTargetYaw
	        float targetRotation = Mathf.Atan2(inputDirection.x, inputDirection.z) * Mathf.Rad2Deg + cinemachineTargetYaw;
	        // 期望移动方向
	        Vector3 targetDirection = Quaternion.Euler(0.0f, targetRotation, 0.0f) * Vector3.forward;

	        Vector3 targetVelocity = (Owner.Input.Move == Vector2.zero) ? Vector3.zero : targetDirection * walkSpeed;
	    
	        return Vector3.SmoothDamp(Owner.AttributeManager.HorizontalVelocity, targetVelocity, ref walkSmoothVelocity, walkSmoothTime);
        }

        /// <summary>
        /// 向移动方向旋转
        /// </summary>
        public void RotateToMoveDir()
        {
	        // 移动方向
	        Vector3 inputDirection = new Vector3(Owner.Input.Move.x, 0.0f, Owner.Input.Move.y).normalized;
	        
	        // note: Vector2's != operator uses approximation so is not floating point error prone, and is cheaper than magnitude
	        // if there is a move input rotate player when the player is moving
	        if (Owner.Input.Move != Vector2.zero)
	        {
		        float targetRotation = Mathf.Atan2(inputDirection.x, inputDirection.z) * Mathf.Rad2Deg + Owner.MainCamera.transform.eulerAngles.y;
		        float rotation = Mathf.SmoothDampAngle(Owner.transform.eulerAngles.y, targetRotation, ref rotationSmoothVelocity, rotationSmoothTime);

		        // 玩家旋转
		        Owner.transform.rotation = Quaternion.Euler(0.0f, rotation, 0.0f);
	        }
        }

        /// <summary>
        /// 摄像机旋转
        /// </summary>
        public void CameraRotate()
        {
	        // if there is an input and camera position is not fixed
	        if (Owner.Input.Look.sqrMagnitude >= Threshold && !isCameraFixed)
	        {
		        cinemachineTargetPitch += Owner.Input.Look.y * Time.deltaTime * cameraRotSpeed / 100.0f;
		        cinemachineTargetYaw += Owner.Input.Look.x * Time.deltaTime * cameraRotSpeed / 100.0f;
	        }

	        // clamp our rotations so our values are limited 360 degrees
	        cinemachineTargetPitch = MathUtility.ClampAngle(cinemachineTargetPitch, bottomClamp, topClamp);
	        cinemachineTargetYaw = MathUtility.ClampAngle(cinemachineTargetYaw, float.MinValue, float.MaxValue);

	        // 平滑
	        cinemachineCurrPY.x = Mathf.SmoothDampAngle(cinemachineCurrPY.x, cinemachineTargetPitch,
		        ref cinemachinePitchSmoothVelocity, cinemachinePitchSmoothTime);
	        cinemachineCurrPY.y = Mathf.SmoothDampAngle(cinemachineCurrPY.y, cinemachineTargetYaw,
		        ref cinemachineYawSmoothVelocity, cinemachineYawSmoothTime);
	  
	        // Cinemachine will follow this target
	        Owner.CMCameraFollowTarget.transform.rotation = Quaternion.Euler(cinemachineCurrPY.x + cameraAngleOverride, cinemachineCurrPY.y, 0.0f);
        }
    }
}

Assets/MeowACT/Scripts/ThirdPerson/ThirdPersonUIController.cs

// ----------------------------------------------
// 作者: 廉价喵
// 创建于: 26/03/2022 10:38
// 最后一次修改于: 26/03/2022 21:16
// 版权所有: CheapMiaoStudio
// 描述:
// ----------------------------------------------

using UnityEngine;

namespace MeowACT
{
    /// <summary>
    /// 第三人称 UI 控制器
    /// </summary>
    public class ThirdPersonUIController
    {
        /// <summary>
        /// 第三人称 UI 控制器的主人
        /// </summary>
        public ThirdPerson Owner;
        
        /// <summary>
        /// 血条实例
        /// </summary>
        private UIBar hpBar;
        
        /// <summary>
        /// 耐力条实例
        /// </summary>
        private UIBar nrgBar;
        
        /// <summary>
        /// 第三人称 UI 控制器的构造函数
        /// </summary>
        /// <param name="owner">第三人称 UI 控制器的主人</param>
        public ThirdPersonUIController(ThirdPerson owner)
        {
            Owner = owner;

            hpBar = Owner.transform.Find("HPBarRoot").Find("UIBar").GetComponent<UIBar>();
            if(hpBar == null)
                Debug.LogError("玩家 Perfab 身上没有血条");
            
            nrgBar = Owner.transform.Find("NRGBarRoot").Find("UIBar").GetComponent<UIBar>();
            if(nrgBar == null)
                Debug.LogError("玩家 Perfab 身上没有体力条");
            
            // 事件绑定

            Owner.EventManager.OnHPChangeEvent += UpdateHPBar;
            Owner.EventManager.OnNRGChangeEvent += UpdateNRGBar;
        }

        /// <summary>
        /// 第三人称 UI 控制器的析构函数
        /// </summary>
        ~ThirdPersonUIController()
        {
            // 事件解绑

            Owner.EventManager.OnHPChangeEvent -= UpdateHPBar;
            Owner.EventManager.OnNRGChangeEvent -= UpdateNRGBar;
        }
        
        /// <summary>
        /// 血条 UI 更新
        /// </summary>
        /// <param name="args">血量参数</param>
        private void UpdateHPBar(object[] args)
        {
            hpBar.SmoothValueAndShow((float) args[0], (float) args[1]);
        }
        
        /// <summary>
        /// 体力条 UI 更新
        /// </summary>
        /// <param name="args">体力参数</param>
        private void UpdateNRGBar(object[] args)
        {
            nrgBar.SmoothValueAndShow((float)args[0], (float)args[1]);
        }
    }
}
// ----------------------------------------------
// 作者: 廉价喵
// 创建于: 12/03/2022 16:08
// 最后一次修改于: 28/03/2022 16:43
// 版权所有: CheapMiaoStudio
// 描述:
// ----------------------------------------------

using System.Collections;
using UnityEngine;
using UnityEngine.UI;

namespace MeowACT
{
    public class UIBar : MonoBehaviour
    {
        /// <summary>
        /// 值变化动画的时长
        /// </summary>
        private const float AnimationDuration = 1f;
        /// <summary>
        /// 值变化动画的过渡时间
        /// </summary>
        private const float AnimationSmoothTime = 0.2f;
        /// <summary>
        /// 值变化动画完成之后的等待时间
        /// </summary>
        private const float KeepSeconds = 0.4f;
        /// <summary>
        /// 值变化动画完成,等待完成之后,淡出动画的时长
        /// </summary>
        private const float FadeOutDuration = 1f;
        /// <summary>
        /// 值变化动画完成,等待完成之后,淡出动画的过渡时间
        /// </summary>
        private const float FadeOutSmoothTime = 0.2f;

        /// <summary>
        /// 填充图像
        /// </summary>
        [SerializeField] private Image fillImage;
        
        /// <summary>
        /// 填充图像的画布组
        /// </summary>
        private CanvasGroup canvasGroup;

        /// <summary>
        /// 填充图像的填充值
        /// </summary>
        public float FillAmount => fillImage.fillAmount;
        
        private void Awake()
        {
            canvasGroup = GetComponent<CanvasGroup>();
            if (canvasGroup == null)
            {
                Debug.LogError("UI 缺少画布组");
            }
        }

        /// <summary>
        /// 根据增量的值变化,同时显示
        /// </summary>
        /// <param name="add">增量</param>
        public void SmoothAddAndShow(float add)
        {
            SmoothValueAndShow(Mathf.Clamp(fillImage.fillAmount + add, 0, 1));
        }
        
        /// <summary>
        /// 根据终点值的值变化,同时显示
        /// </summary>
        /// <param name="toRatio">终点值</param>
        public void SmoothValueAndShow(float toRatio)
        {
            SmoothValueAndShow(FillAmount, toRatio);
        }
        
        /// <summary>
        /// 根据起点值和终点值的值变化,同时显示
        /// </summary>
        /// <param name="fromRatio">初始值</param>
        /// <param name="toRatio">终点值</param>
        public void SmoothValueAndShow(float fromRatio, float toRatio)
        {
            fillImage.gameObject.SetActive(true);
            
            // 停掉当前 MonoBehavior 的所有协程
            // 这就停掉了之前的值变化
            StopAllCoroutines();

            canvasGroup.alpha = 1f;
            fillImage.fillAmount = fromRatio;
            
            // 开始值变化协程
            StartCoroutine(BarCo(toRatio, AnimationDuration, AnimationSmoothTime, KeepSeconds, FadeOutDuration,
                FadeOutSmoothTime));
        }

        public void Reset()
        {
            StopAllCoroutines();
            canvasGroup.alpha = 1f;
            fillImage.fillAmount = 1f;
            fillImage.gameObject.SetActive(false);
        }

        private IEnumerator BarCo(float value, float animationDuration, float animationSmoothTime, float keepDuration,
            float fadeOutDuration, float fadeOutSmoothTime)
        {
            yield return fillImage.SmoothFillAmount(value, animationDuration, animationSmoothTime);
            yield return new WaitForSeconds(keepDuration);
            yield return canvasGroup.FadeToAlpha(0f, fadeOutDuration, fadeOutSmoothTime);
            fillImage.gameObject.SetActive(false);
        }
    }
}

我是把逻辑全部都放到一个类里面了,从外面看就是

在这里插入图片描述

此外,UI,Animator,InputSetting 就不展示了

虽然我写得爽了,但是策划完全调不了

接下来要根据 ScriptableObject 的思想重构

  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2022-04-01 23:44:40  更:2022-04-01 23:46:27 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年1日历 -2025/1/16 19:05:37-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码