1. Port 与 BBParameter
我一开始就是公开组件接口,那个时候是因为我还只会用 AddValueInput,不会用 BBParameter
但是后来我发现 BBParameter 很简单之后,我就想在每个地方都用上 BBParameter 了,因为我希望连线尽可能少
但是后面我又发现其实直接用接口的话,他会设置默认的 self 传进来,这样其实对于有默认值的接口,也不用连很多线
那么把这个接口暴露出来的意义就是,让人知道明确知道有这么一个东西需要连,一眼看过去就知道,而不需要点开这个节点看需要什么黑板值
而黑板值用在一些具体细节的布置上,比如我摄像机的偏移为多少,角色速度调到多少,很多时候这些都是一个死的东西,不需要连线,也不需要黑板上有值,给个字面值就可以了的这种
但是由于 FlowCanvas 只会对多 Component 接口的第一个接口设置默认的 self,这就强迫设计者一个函数只能用一个组件
某种程度上也算是强迫解耦了?
1.1 冲刺 v6
例如原来我会把禁止玩家输入和速度覆盖放在一起,然后用两个组件,然后这两个组件都是黑板值,现在我把组件接口暴露出来,然后解耦了禁止输入和速度覆盖
2. 自定义节点与公有函数
一开始我想什么都用自定义节点
但是我发现了单一组件原则之后,对比自定义节点与公有函数,它们都是处理一个组件内的事情,那为什么不直接在组件里面写函数呢?
组件内写函数反而方便在 FlowScript 中组合
只有对于那些真的很基础的节点才需要自定义节点,比如我想要给 wait 添加一个时间缩放啥的,不得不新建一个带缩放因子变量的节点
2.1 冲刺 v7
比如我原来会把速度覆盖与计时放在一起,现在想想确实没必要,新建一个这样的节点浪费工作量
3. LatentActionNode 的 Break
LatentActionNode 的 Break 会直接触发 Finish
3.1 瞄准 v4
我原来是想在协程结束之后把瞄准图标 SetActive(false)
的
但是 Break
会直接触发协程的 Finish
,Finish
又会触发 SetActive(false)
,因此这就相当于我要显示图标的同时把他 SetActive(true)
SetActive(false)
,这就很迷惑
所以干脆不要这个 SetActive
了,直接一直为 true
把
4. 运动模式
4.1 瞄准 v5
后面我又觉得其实没有必要特意把一个 CanvasGroup 连出来
于是瞄准变成这样
在这里我使用了第二个 set style 然后我还注意到所有的功能都是迫不得已一个组件的
然后接下来我又发现 locomotion 的计算也需要改变,因为我的基本运动是人物会朝着摄像机方向+输入方向的,持枪的时候人物应该始终朝着前面,所以人物移动应该是朝着摄像机的前方向和右方向的
这就要求我再在一个地方注意更改
这样我就想到,为什么我不给一个组件做一个模式改变的函数呢?模式改变所需要的参数,模式改变具体要用的函数,感觉也不需要在运行时用到,暴露出来也没有意义
4.2 瞄准 v6
于是修改之后就变成这样
看上去就很简洁
4.3 Locomotion v4
Assets/MeowFramework/TPSCharacter/Scripts/TPSCharacterLocomotionController.cs
// ----------------------------------------------
// 作者: 廉价喵
// 创建于: 16/03/2022 16:53
// 最后一次修改于: 10/04/2022 22:09
// 版权所有: CheapMeowStudio
// 描述:
// ----------------------------------------------
using System.Collections;
using System.ComponentModel;
using Cinemachine;
using MeowFramework.Core;
using Sirenix.OdinInspector;
using UnityEngine;
namespace MeowFramework.TPSCharacter
{
/// <summary>
/// 第三人称运动控制器
/// </summary>
public class TPSCharacterLocomotionController : SerializedMonoBehaviour
{
// 常量
/// <summary>
/// 微量
/// </summary>
private const float Threshold = 0.01f;
// 组件相关
/// <summary>
/// 角色控制器
/// </summary>
[BoxGroup("Component")]
[Required]
[Tooltip("角色控制器")]
public CharacterController CharacterCtr;
/// <summary>
/// ACT 输入控制器
/// </summary>
[BoxGroup("Component")]
[Required]
[Tooltip("ACT 输入控制器")]
public TPSCharacterInputController ACTInput;
/// <summary>
/// 摄像机跟随点
/// </summary>
[BoxGroup("Component")]
[Required]
[Tooltip("摄像机跟随点")]
public GameObject CMCameraFollowTarget;
/// <summary>
/// 主摄像机
/// </summary>
[BoxGroup("Component")]
[Sirenix.OdinInspector.ReadOnly]
[Tooltip("主摄像机")]
public Camera MainCamera;
/// <summary>
/// 跟随主角的摄像机
/// </summary>
[BoxGroup("Component")]
[Sirenix.OdinInspector.ReadOnly]
[Tooltip("跟随主角的摄像机")]
public CinemachineVirtualCamera PlayerFollowCamera;
// 模式
/// <summary>
/// 行动模式
/// </summary>
[BoxGroup("Mode")]
[ShowInInspector]
[Sirenix.OdinInspector.ReadOnly]
[Description("行动模式")]
private TPSCharacterBehaviourMode mode;
/// <summary>
/// 切换模式的过渡时间
/// </summary>
[BoxGroup("Mode")]
[ShowInInspector]
[Description("切换模式的过渡时间")]
private float modeTransitionTime = 1f;
/// <summary>
/// 没有武器时摄像机的 FOV
/// </summary>
[BoxGroup("Mode")]
[ShowInInspector]
[Description("摄像机的目标 FOV")]
private float noWeaponFOV = 40f;
/// <summary>
/// 持步枪时摄像机的 FOV
/// </summary>
[BoxGroup("Mode")]
[ShowInInspector]
[Description("摄像机的目标 FOV")]
private float rifleFOV = 30f;
/// <summary>
/// 摄像机的 FOV 的平滑时间
/// </summary>
[BoxGroup("Mode")]
[ShowInInspector]
[Description("摄像机的目标 FOV 的平滑时间")]
private float fovSmoothTime = 0.2f;
/// <summary>
/// 没有武器时摄像机的侧向位置
/// </summary>
[BoxGroup("Mode")]
[ShowInInspector]
[Description("摄像机的目标侧向位置")]
private float noWeaponSide = 0.5f;
/// <summary>
/// 持步枪时摄像机的侧向位置
/// </summary>
[BoxGroup("Mode")]
[ShowInInspector]
[Description("摄像机的目标侧向位置")]
private float rifleSide = 1f;
/// <summary>
/// 摄像机侧向位置的平滑时间
/// </summary>
[BoxGroup("Mode")]
[ShowInInspector]
[Description("摄像机侧向位置的平滑时间")]
private float cameraSideSmoothTime = 0.2f;
// 运动相关
/// <summary>
/// 可资产化水平速度
/// </summary>
[BoxGroup("Velocity")]
[Required]
[Tooltip("可资产化水平速度")]
public ScriptableVector3Variable HorizontalVelocity;
/// <summary>
/// 可资产化竖直速度
/// </summary>
[BoxGroup("Velocity")]
[Required]
[Tooltip("可资产化竖直速度")]
public ScriptableFloatVariable VerticalVelocity;
/// <summary>
/// 水平速度是否被覆盖了
/// </summary>
[BoxGroup("Velocity")]
[HorizontalGroup("Velocity/HorizontalVelocityOverride")]
[ShowInInspector]
[Sirenix.OdinInspector.ReadOnly]
[Tooltip("水平速度覆盖值")]
private bool isHorizontalVelocityOverrided;
/// <summary>
/// 水平速度覆盖值
/// </summary>
[BoxGroup("Velocity")]
[HorizontalGroup("Velocity/HorizontalVelocityOverride")]
[ShowInInspector]
[Sirenix.OdinInspector.ReadOnly]
[Tooltip("水平速度覆盖值")]
private float horizontalVelocityOverride;
// 行走相关
/// <summary>
/// 移动速度
/// </summary>
[BoxGroup("Walk")]
[ShowInInspector]
[Tooltip("移动速度")]
private float walkSpeed = 7f;
/// <summary>
/// 玩家行走的过渡时间
/// </summary>
[BoxGroup("Walk")]
[ShowInInspector]
[Tooltip("玩家行走的过渡时间")]
private float walkSmoothTime = 0.2f;
/// <summary>
/// 玩家旋转的过渡时间
/// 如果这个值过大,由于使用了 SmoothDamp,会让镜头移动出现明显的粘滞感
/// </summary>
[BoxGroup("Walk")]
[ShowInInspector]
[Tooltip("玩家旋转的过渡时间")]
private float rotationSmoothTime = 0.1f;
// 物理相关
/// <summary>
/// 重力系数
/// </summary>
[BoxGroup("Gravity")]
[ShowInInspector]
[Tooltip("重力系数")]
private float gravity = -9.8f;
/// <summary>
/// 最大下落速度
/// </summary>
[BoxGroup("Gravity")]
[ShowInInspector]
[Tooltip("最大下落速度")]
private float terminalVelocity = 53f;
// 落地相关
/// <summary>
/// 是否落地
/// </summary>
[BoxGroup("GroundCheck")]
[ShowInInspector]
[Sirenix