[Unity] 战斗系统学习 12:Switchable 1
1. 批量 SmoothDamp 变量的需求 1.1 例子
这是我一个改到一半的函数……我懒得改了
这个函数的目的是从一个模式转换到一个模式的时候开始对一堆变量在一个时间内 SmoothDamp
目前是只有两个变量,所以我可以这么写,万一我都很多个变量呢?万一我要频繁地修改变量的名字,个数啥的呢?
我就感觉很麻烦
// 初始化计时器
var timeLeft = modeTransitionTime;
// 旧层级权重平滑速度
float fromLayerWeightSmoothVelocity = 0f;
// 新层级权重平滑速度
float toLayerWeightSmoothVelocity = 0f;
// 旧层级权重
var fromWeight = Anim.GetLayerWeight(fromLayer);
// 新层级权重
var toWeight = Anim.GetLayerWeight(toLayer);
// 在给定时间内平滑
// 平滑时间结束时,被平滑项接近终点值但不是终点值
// 需要给被平滑项赋终点值,这可能产生一个抖动
// 平滑时间需要在保证效果的尽可能小,才能让的抖动变小
hile (timeLeft > 0)
{
timeLeft -= Time.deltaTime;
fromWeight = Mathf.SmoothDamp(fromWeight, 0,
ref fromLayerWeightSmoothVelocity, layerWeightSmoothTime);
toWeight = Mathf.SmoothDamp(toWeight, 1,
ref toLayerWeightSmoothVelocity, layerWeightSmoothTime);
Anim.SetLayerWeight(fromLayer, fromWeight);
Anim.SetLayerWeight(toLayer, toWeight);
yield return null;
}
// 赋终点值
Anim.SetLayerWeight(fromLayer, 0);
Anim.SetLayerWeight(toLayer, 1);
yield return null;
}
本来其实在不同模式之间切换的时候,需要 SmoothDamp 的变量其实是不同的,从 A 到 B 要动三个变量,从 B 到 C 可能就只需要动一个
你要说用状态机把,其实也没必要,因为这并没有 OnEnter OnUpdate OnExist 啥的需求
所以我就想怎么方便地做这个切换的函数
一个 mono 中有一个变量组,记为 V,一个表示模式的 Enum 变量,命名为 mode
V 对 mode 的不同值有不同预设值,把这些预设值做成一个 Struct 命名为 Setting
那么如果我想 V 的值在 mode 变化时,可以在与 mode 对应的 Setting 之间切换,我可以这么写
伪代码
EnumXXX mode; Setting setting0; Setting setting1; Setting setting2; DictionarySettingDictionary = ne Dictionary {{0,setting0},{1,setting1},{2,setting2}} V = SettingDictionary[mode];
如果我想要 V 在 setting 之间使用 smoothdamp 过渡,我就必须要对 setting 中的每一项都创建一个缓存变量 velocity 输入到 smoothdamp 函数里面,感觉这样有点麻烦
并且对于每一个不同的 V 我都要写一堆 smoothdamp 代码
比如如果 V 要转到 setting0
伪代码
V1 = Math.SmoothDamp(V1, setting0.V1, Velocity1, smoothTime); V2 = Math.SmoothDamp(V2, setting0.V2, Velocity2, smoothTime); V3 = Math.SmoothDamp(V3, setting0.V3, Velocity3, smoothTime);
这样就会很冗余……又不能做成一个大的列表然后遍历这个列表 smoothdamp
比如
伪代码
for(int i = 1;i < V.Count; ++i)
{
V[i] = Math.SmoothDamp(V[i], setting0[i], Velocity[i], smoothTime);
}
因为 V 中的变量的类型可能是不同的,不能放到一个列表中
要放到一个列表中也可以,那就装箱成 object,然后多一个数组记录第 i 个变量的类型,再转回去,这样效率就太低了,而且感觉很蠢
所以问题就是,当我需要批量给一组变量插值的时候,我会写出数量为 n 的插值语句和数量为 n 的缓存变量
那要解决这个问题的话,我目前只能想到是
- 对每一种 setting 里面可能出现的类型建一个类,叫 SitchableObject
比如 Vector3 就是 SitchableVector3,float 就是 SitchableFloat
以 SitchableFloat 为例,它包含一个 float Value,一个 ListSitchableValueList 和一个 float SmoothVelocity - 新建一个接口 ISitchable 包含一个 void SitchValue(int index) 函数,SitchableFloat 继承 ISitchable,函数内容是 float 类型的 SmoothDamp
- mono 里面有一个 List
sitchableObjectList 用于批量调用 SitchValue
这样,法一
伪代码
// 法一
float sitchTime = 1f;
float smoothTime = 0.2f;
T0 V0;
T0 velocity0;
T1 V1;
T1 velocity1;
T2 V2;
T2 velocity2;
Setting setting0;
Setting setting1;
Setting setting2;
Setting setting0 =
{
T0 V0;
T1 V1;
T2 V2;
}
Setting setting1 =
{
T0 V0;
T1 V1;
T2 V2;
}
Setting setting2 =
{
T0 V0;
T1 V1;
T2 V2;
}
Dictionary settingDictionary;
void Start()
{
settingDictionary = ne Dictionary{{0,setting0},{1,setting1},{2,setting2}};
}
现在是
伪代码
// 法二 ListsitchableObjectList; SitchableFloat V0; V0.SitchableValueList = { float target0; float target1; float target2; } SitchableVector2 V1; V1.SitchableValueList = { Vector2 target0; Vector2 target1; Vector2 target2; } SitchableVector3 V2; V2.SitchableValueList = { Vector3 target0; Vector3 target1; Vector3 target2; } void Start() { sitchableObjectList.Add(V0); sitchableObjectList.Add(V1); sitchableObjectList.Add(V2); }
以前我需要
伪代码
// 法一
Enumator SitchSettingCoroutine(EnumXXX mode)
{
float time = sitchTime;
hile(time > 0)
{
time -= Time.DeltaTime;
V0 = Math.SmoothDamp(V0, settingDictionary[mode].V0, ref velocity0, smoothTime);
V1 = Math.SmoothDamp(V1, settingDictionary[mode].V1, ref velocity1, smoothTime);
V2 = Math.SmoothDamp(V2, settingDictionary[mode].V2, ref velocity2, smoothTime);
}
yield return null;
}
void SitchSetting(EnumXXX mode)
{
StartCoroutine(SitchSettingCoroutine(mode));
}
现在我需要
伪代码
// 法二
Enumator SitchSettingCoroutine(EnumXXX mode)
{
float time = sitchTime;
hile(time > 0)
{
time -= Time.DeltaTime;
for(ISitchable s in sitchableObjectList)
s.SitchValue(mode);
}
yield return null;
}
void SitchSetting(EnumXXX mode)
{
StartCoroutine(SitchSettingCoroutine(mode));
}
不知道我这样写行不行,会有什么问题……
这个看上去是很好的
一个数据表,假设行号是 Enum 列号是变量名
这样做把数据表的每一列拆到每一个变量里面
实际上符合习惯的做法是一行一行的
如果我真的要把数据表的一行放到一起,比如放到 ScriptableObject 里面
那么我取变量的目标值的流程就是输入一个变量,然后通过反射拿到这个变量的名字,然后根据 Enum 在 字典
这样的话,这个函数有不同类型,Setting 中的字典也有不同类型,Setting 中还要写一个初始化函数把目标值放到不同类型的字典中
要不然就写成 Setting 里面只有不同类型的字典,这样就省去了初始化的麻烦
那么用的时候就是
伪代码
// 法三
private float value1;
private Vector3 value2;
private Vector2 value3;
private Setting setting;
private void GetTargetValueFromSetting()
{
string name;
name = nameof(value1);
float target = setting.GetFloatDict()[name];
name = nameof(value2);
Vector3 target = setting.GetVector3Dict()[name];
name = nameof(value3);
Vector2 target = setting.GetVector2Dict()[name];
}
由于要获取名字,所以不可避免写 n 条语句……这就太麻烦了
伪代码
// 法四
public class SitchableFloat : ISitchable
{
public float value;
public Dictionary targetValueDict;
public override void SitchValue(EnumXXX mode)
{
float target = targetValueDict[mode];
// SmoothDamp
}
}
public interface ISitchable
{
public void SitchValue(EnumXXX mode);
}
public float sitchTime;
public SitchableFloat value1;
public SitchableFloat value2;
public SitchableFloat value3;
public List sitchableObjectList;
void Start()
{
sitchableObjectList.Add(value1);
sitchableObjectList.Add(value2);
sitchableObjectList.Add(value3);
}
public IEnumerator SitchSettingCoroutine(EnumXXX mode)
{
float time = sitchTime;
hile(time > 0)
{
time -= Time.deltaTime;
for(ISitchable s in sitchableObjectList)
s.SitchValue(mode);
}
yield return null;
}
void SitchSetting(EnumXXX mode)
{
StartCoroutine(SitchSettingCoroutine(mode));
}
我不用泛型一个原因是在监视器上配置实现泛型的变量会出错,即使用了 Odin,另一个原因是 SmoothDamp 没有泛型
跟我讨论的朋友问我为什么不用 Animator,我说那是用来做骨骼动画的
后来我又想到 timeline,他是可以控制脚本的
的问题是我不知道怎么制作变量轨道
就算我知道了,变量轨道也需要一个确定的初值和终值,我这个是需要随时切换状态,比如按住右键瞄准,他可能一会瞄准一会不秒,切换时间小于动画时间,那么如果第一次动画时间,当前值就是初值,第二次相反的退出瞄准动画开始时,当前值也不会是退出瞄准动画的初值
除非我可以让动画的初值为当前值,或者我可以根据当前值,动画的初值终值得到我应该在哪个百分比进度播放动画……
好吧我后面知道了 Playable Track 是可以自定义 Clip 的
好麻烦……不想看……我就是懒hhhh
而且按照我的法四,我可以在监视器中对每一项变量设置对每一个 mode 的可能值的目标值,如果我不设置说明我就不用平滑这个变量
这是点开组件就能在监视器中看到的,timeline 可不行
再化简一点就是
伪代码
// 法四
public class SitchableFloat : ISitchable
{
public float value;
public Dictionary targetValueDict;
public override void SitchValue(EnumXXX mode)
{
float target = targetValueDict[mode];
// SmoothDamp
}
}
public interface ISitchable
{
public void SitchValue(EnumXXX mode);
}
public float sitchTime;
public SitchableFloat value1;
public SitchableFloat value2;
public SitchableFloat value3;
public List sitchableObjectList;
private Coroutine sitchSettingCoroutine;
private EnumXXX mode;
public EnumXXX Mode
{
get => mode;
set
{
if (mode != value)
{
if (sitchSettingCoroutine != null)
SCoroutine(sitchSettingCoroutine);
sitchSettingCoroutine = StartCoroutine(SitchSettingCoroutine(value));
mode = value;
}
}
}
void Start()
{
sitchableObjectList.Add(value1);
sitchableObjectList.Add(value2);
sitchableObjectList.Add(value3);
}
public IEnumerator SitchSettingCoroutine(EnumXXX mode)
{
float time = sitchTime;
hile(time > 0)
{
time -= Time.deltaTime;
for(ISitchable s in sitchableObjectList)
s.SitchValue(mode);
}
yield return null;
}
2. Sitchable v1
正式地开始写了
2.1 ISitchable v1Assets/MeoFrameork/Core/Sitchable/ISitchable.cs
// ----------------------------------------------
// 作者: 廉价喵
// 创建于: 22/04/2022 9:00
// 一次修改于: 22/04/2022 9:11
// 版权所有: CheapMeoStudio
// 描述:
// ----------------------------------------------
using System;
namespace MeoFrameork.Core.Sitchable
{
///
/// 切换变量的接口
///
public interface ISitchable
{
///
/// 使变量在不同预设值之间切换
///
/// 预设模式
public void SitchValue(Enum mode);
}
}
2.2 SitchableFloat v1
Assets/MeoFrameork/Core/Sitchable/SitchableFloat.cs
// ----------------------------------------------
// 作者: 廉价喵
// 创建于: 22/04/2022 9:01
// 一次修改于: 22/04/2022 9:11
// 版权所有: CheapMeoStudio
// 描述:
// ----------------------------------------------
using System;
using System.Collections.Generic;
using UnityEngine;
namespace MeoFrameork.Core.Sitchable
{
///
/// 可切换浮点
///
public class SitchableFloat : ISitchable
{
///
/// 当前值
///
[Tooltip("当前值")]
public float Value;
///
/// 预设值字典
///
[Tooltip("预设值字典")]
public Dictionary TargetValueDict;
// 缓存
///
/// 平滑速度
///
private float smoothVelocity;
///
/// 平滑时间
///
[Tooltip("平滑时间")]
public float SmoothTime = 0.2f;
// 实现接口
///
/// 使变量在不同预设值之间切换
///
/// 预设模式
public void SitchValue(Enum mode)
{
// SmoothDamp
float target = TargetValueDict[mode];
Value = Mathf.SmoothDamp(Value, target, ref smoothVelocity, SmoothTime);
}
}
}
2.3 TPSCharacterAnimationSetting v1
这是第一版方案,是废弃了的
拿出来是为了对比,显示这样做有多笨
Assets/MeoFrameork/TPSCharacter/Scripts/Struct/TPSCharacterAnimationSetting.cs
// ----------------------------------------------
// 作者: 廉价喵
// 创建于: 20/04/2022 17:52
// 一次修改于: 20/04/2022 17:57
// 版权所有: CheapMeoStudio
// 描述:
// ----------------------------------------------
namespace MeoFrameork.TPSCharacter.Struct
{
public struct TPSCharacterAnimationSetting
{
///
/// 无武器层的动画层级权重
///
public float NoWeaponLayerWeight;
///
/// 持枪待机层的动画层级权重
///
public float RifleIdleLayerWeight;
///
/// 持枪瞄准层的动画层级权重
///
public float RifleAimingLayerWeight;
///
/// 无武器层的骨骼绑定权重
///
public float NoWeaponRigWeight;
///
/// 持枪待机层的骨骼绑定权重
///
public float RifleIdleRigWeight;
///
/// 持枪瞄准层的骨骼绑定权重
///
public float RifleAimingRigWeight;
}
}
2.4 TPSCharacterAnimationController.Mode v1
这是一个修改前的半成品……确实有些地方直接就是跑不通的
拿出来是为了对比,显示这样做有多笨
Assets/MeoFrameork/TPSCharacter/Scripts/Controller/TPSCharacterAnimationController.Mode.cs
// ----------------------------------------------
// 作者: 廉价喵
// 创建于: 12/04/2022 15:55
// 一次修改于: 22/04/2022 9:01
// 版权所有: CheapMeoStudio
// 描述:
// ----------------------------------------------
using System.Collections;
using System.Collections.Generic;
using MeoFrameork.TPSCharacter.Struct;
using Sirenix.OdinInspector;
using UnityEngine;
using UnityEngine.Animations.Rigging;
namespace MeoFrameork.TPSCharacter
{
///
/// 第三人称动画状态机控制器
///
public partial class TPSCharacterAnimationController
{
///
/// 行动模式
///
[BoxGroup("Mode")]
[ShoInInspector]
[Sirenix.OdinInspector.ReadOnly]
[Tooltip("行动模式")]
private TPSCharacterBehaviourMode mode;
///
/// 无武器层的动画层级的序号
///
[BoxGroup("Mode")]
[ShoInInspector]
[Tooltip("无武器层的动画层级的序号")]
private int noWeaponLayerIndex = 0;
///
/// 持步枪待机层的动画层级的序号
///
[BoxGroup("Mode")]
[ShoInInspector]
[Tooltip("持步枪待机层的动画层级的序号")]
private int rifleIdleLayerIndex = 0;
///
/// 持步枪瞄准层的动画层级的序号
///
[BoxGroup("Mode")]
[ShoInInspector]
[Tooltip("持步枪瞄准层的动画层级的序号")]
private int rifleAimingLayerIndex = 0;
///
/// 无武器层的动画参数配置
///
[BoxGroup("Mode")]
[ShoInInspector]
[Tooltip("无武器层的动画参数配置")]
private TPSCharacterAnimationSetting noWeaponSetting;
///
/// 持步枪待机层的动画参数配置
///
[BoxGroup("Mode")]
[ShoInInspector]
[Tooltip("持步枪待机层的动画参数配置")]
private TPSCharacterAnimationSetting rifleIdleSetting;
///
/// 持步枪瞄准层的动画参数配置
///
[BoxGroup("Mode")]
[ShoInInspector]
[Tooltip("持步枪瞄准层的动画参数配置")]
private TPSCharacterAnimationSetting rifleAimingSetting;
///
/// 切换模式的过渡时间
///
[BoxGroup("Mode")]
[ShoInInspector]
[Tooltip("切换模式的过渡时间")]
private float modeTransitionTime = 1f;
///
/// 层级平滑时间
///
[BoxGroup("Mode")]
[ShoInInspector]
[Tooltip("层级平滑时间")]
private float layerWeightSmoothTime = 0.2f;
// 缓存 - 模式改变
///
/// 平滑切换层级权重的协程
///
private Coroutine smoothSitchLayerWeightCoroutine;
///
/// 平滑切换层级权重
///
/// 旧层级
/// 新层级
///
private IEnumerator SitchAnimationSetting(TPSCharacterBehaviourMode mode)
{
// 初始化计时器
var timeLeft = modeTransitionTime;
// 旧层级权重平滑速度
float fromLayerWeightSmoothVelocity = 0f;
// 新层级权重平滑速度
float toLayerWeightSmoothVelocity = 0f;
// 旧层级权重
var fromWeight = Anim.GetLayerWeight(fromLayer);
// 新层级权重
var toWeight = Anim.GetLayerWeight(toLayer);
// 在给定时间内平滑
// 平滑时间结束时,被平滑项接近终点值但不是终点值
// 需要给被平滑项赋终点值,这可能产生一个抖动
// 平滑时间需要在保证效果的尽可能小,才能让的抖动变小
hile (timeLeft > 0)
{
timeLeft -= Time.deltaTime;
fromWeight = Mathf.SmoothDamp(fromWeight, 0,
ref fromLayerWeightSmoothVelocity, layerWeightSmoothTime);
toWeight = Mathf.SmoothDamp(toWeight, 1,
ref toLayerWeightSmoothVelocity, layerWeightSmoothTime);
Anim.SetLayerWeight(fromLayer, fromWeight);
Anim.SetLayerWeight(toLayer, toWeight);
yield return null;
}
// 赋终点值
Anim.SetLayerWeight(fromLayer, 0);
Anim.SetLayerWeight(toLayer, 1);
yield return null;
}
private IEnumerator SitchRigWeight(TPSCharacterBehaviourMode mode)
{
List rigs = ne List {rifleIdleRig, rifleAimingRig};
sitch (mode)
{
case
rigs.Remove(rifleIdleRig);
}
yield return null;
}
///
/// 设置动画模式
///
/// 模式
public void SetAnimationMode(TPSCharacterBehaviourMode mode)
{
// 更新模式记录
this.mode = mode;
// 如果有正在进行的模式切换相关的协程,就关闭这个协程
if(smoothSitchLayerWeightCoroutine != null)
SCoroutine(smoothSitchLayerWeightCoroutine);
//
sitch (mode)
{
case TPSCharacterBehaviourMode.NoWeapon:
smoothSitchLayerWeightCoroutine = StartCoroutine(SitchLayerWeight(1, 0));
break;
case TPSCharacterBehaviourMode.RifleIdle:
smoothSitchLayerWeightCoroutine = StartCoroutine(SitchLayerWeight(0, 1));
break;
}
}
}
}
2.5 TPSCharacterAnimationController.Mode v2
按照我的思路就是写成
Assets/MeoFrameork/TPSCharacter/Scripts/Controller/TPSCharacterAnimationController.Mode.cs
// ----------------------------------------------
// 作者: 廉价喵
// 创建于: 12/04/2022 15:55
// 一次修改于: 22/04/2022 9:39
// 版权所有: CheapMeoStudio
// 描述:
// ----------------------------------------------
using System.Collections;
using System.Collections.Generic;
using MeoFrameork.Core.Sitchable;
using Sirenix.OdinInspector;
using UnityEngine;
namespace MeoFrameork.TPSCharacter
{
///
/// 第三人称动画状态机控制器
///
public partial class TPSCharacterAnimationController
{
///
/// 行动模式
///
[BoxGroup("Mode")]
[ShoInInspector]
[Sirenix.OdinInspector.ReadOnly]
[Tooltip("行动模式")]
private TPSCharacterBehaviourMode mode;
///
/// 行动模式
///
public TPSCharacterBehaviourMode Mode
{
get => mode;
set
{
if (mode != value)
{
if (sitchValueCoroutine != null)
SCoroutine(sitchValueCoroutine);
sitchValueCoroutine = StartCoroutine(ModeTransition(value));
mode = value;
}
}
}
///
/// 切换模式的过渡时间
///
[BoxGroup("Mode")]
[ShoInInspector]
[Tooltip("切换模式的过渡时间")]
private float modeTransitionTime = 1f;
///
/// 动画状态机第 0 层的权重
///
[BoxGroup("Mode")]
[Tooltip("动画状态机第 0 层的权重")]
public SitchableFloat AnimLayer0Weight;
///
/// 动画状态机第 1 层的权重
///
[BoxGroup("Mode")]
[Tooltip("动画状态机第 1 层的权重")]
public SitchableFloat AnimLayer1Weight;
///
/// 动画状态机第 2 层的权重
///
[BoxGroup("Mode")]
[Tooltip("动画状态机第 2 层的权重")]
public SitchableFloat AnimLayer2Weight;
///
/// 可切换变量列表
///
private List sitchableObjectList = ne List();
// 缓存 - 模式改变
///
/// 切换变量的协程
///
private Coroutine sitchValueCoroutine;
///
/// 初始化可切换变量列表
///
private void InitSitchableList()
{
sitchableObjectList.Add(AnimLayer0Weight);
sitchableObjectList.Add(AnimLayer1Weight);
sitchableObjectList.Add(AnimLayer2Weight);
}
///
/// 模式过渡使变量在不同预设值之间切换
///
/// 预设模式
///
private IEnumerator ModeTransition(TPSCharacterBehaviourMode mode)
{
float time = modeTransitionTime;
hile(time > 0)
{
time -= Time.deltaTime;
foreach (ISitchable sitchable in sitchableObjectList)
{
sitchable.SitchValue(mode);
}
}
yield return null;
}
}
}
我发现我还需要做一个数据绑定,把 AnimLayer0Weight, AnimLayer1Weight, AnimLayer2Weight 的值绑定到 Animator
不能在外部替换类里面的属性,那就只能用委托了
所以 SitchableFloat 还要改
修改后的可切换变量,提供了变量改变时的钩子
Assets/MeoFrameork/Core/Sitchable/SitchableFloat.cs
// ----------------------------------------------
// 作者: 廉价喵
// 创建于: 22/04/2022 9:01
// 一次修改于: 22/04/2022 10:01
// 版权所有: CheapMeoStudio
// 描述:
// ----------------------------------------------
using System;
using System.Collections.Generic;
using Sirenix.OdinInspector;
using UnityEngine;
namespace MeoFrameork.Core.Sitchable
{
///
/// 可切换浮点
///
public class SitchableFloat : ISitchable
{
///
/// 当前值
///
[ShoInInspector]
[Tooltip("当前值")]
private float value;
///
/// 当前值
///
public float Value
{
get => value;
set
{
if (this.value != value)
{
AfterValueChangeAction?.Invoke(this.value,value);
this.value = value;
}
}
}
///
/// 值改变后触发的委托
///
[HideInInspector]
public Action AfterValueChangeAction;
///
/// 预设值字典
///
[Tooltip("预设值字典")]
public Dictionary TargetValueDict = ne Dictionary();
// 缓存
///
/// 平滑速度
///
private float smoothVelocity;
///
/// 平滑时间
///
[Tooltip("平滑时间")]
public float SmoothTime = 0.2f;
// 实现接口
///
/// 使变量在不同预设值之间切换
///
/// 预设模式
public void SitchValue(Enum mode)
{
// SmoothDamp
if (TargetValueDict.ContainsKey(mode))
{
float target = TargetValueDict[mode];
Value = Mathf.SmoothDamp(Value, target, ref smoothVelocity, SmoothTime);
}
}
}
}
2.7 TPSCharacterAnimationController.Mode v3
添加了数值绑定
Assets/MeoFrameork/TPSCharacter/Scripts/Controller/TPSCharacterAnimationController.Mode.cs
// ----------------------------------------------
// 作者: 廉价喵
// 创建于: 12/04/2022 15:55
// 一次修改于: 22/04/2022 14:40
// 版权所有: CheapMeoStudio
// 描述:
// ----------------------------------------------
using System.Collections;
using System.Collections.Generic;
using MeoFrameork.Core.Sitchable;
using Sirenix.OdinInspector;
using UnityEngine;
namespace MeoFrameork.TPSCharacter
{
///
/// 第三人称动画状态机控制器
///
public partial class TPSCharacterAnimationController
{
///
/// 行动模式
///
[BoxGroup("Mode")]
[ShoInInspector]
[Sirenix.OdinInspector.ReadOnly]
[Tooltip("行动模式")]
private TPSCharacterBehaviourMode mode;
///
/// 行动模式
///
public TPSCharacterBehaviourMode Mode
{
get => mode;
set
{
if (mode != value)
{
if (sitchValueCoroutine != null)
SCoroutine(sitchValueCoroutine);
sitchValueCoroutine = StartCoroutine(ModeTransition(value));
mode = value;
}
}
}
///
/// 切换模式的过渡时间
///
[BoxGroup("Mode")]
[ShoInInspector]
[Tooltip("切换模式的过渡时间")]
private float modeTransitionTime = 1f;
///
/// 动画状态机第 0 层的权重
///
[BoxGroup("Mode")]
[Tooltip("动画状态机第 0 层的权重")]
public SitchableFloat AnimLayer0Weight = ne SitchableFloat();
///
/// 动画状态机第 1 层的权重
///
[BoxGroup("Mode")]
[Tooltip("动画状态机第 1 层的权重")]
public SitchableFloat AnimLayer1Weight = ne SitchableFloat();
///
/// 动画状态机第 2 层的权重
///
[BoxGroup("Mode")]
[Tooltip("动画状态机第 2 层的权重")]
public SitchableFloat AnimLayer2Weight = ne SitchableFloat();
///
/// 步枪待机姿态所用到的骨骼绑定的权重
///
[BoxGroup("Mode")]
[Tooltip("步枪待机姿态所用到的骨骼绑定的权重")]
public SitchableFloat RifleIdleRigWeight = ne SitchableFloat();
///
/// 步枪瞄准姿态所用到的骨骼绑定的权重
///
[BoxGroup("Mode")]
[Tooltip("步枪瞄准姿态所用到的骨骼绑定的权重")]
public SitchableFloat RifleAimingRigWeight = ne SitchableFloat();
///
/// 可切换变量列表
///
private List sitchableObjectList = ne List();
// 缓存 - 模式改变
///
/// 切换变量的协程
///
private Coroutine sitchValueCoroutine;
///
/// 初始化可切换变量列表
///
private void InitSitchableList()
{
sitchableObjectList.Add(AnimLayer0Weight);
sitchableObjectList.Add(AnimLayer1Weight);
sitchableObjectList.Add(AnimLayer2Weight);
sitchableObjectList.Add(RifleIdleRigWeight);
sitchableObjectList.Add(RifleAimingRigWeight);
AnimLayer0Weight.AfterValueChangeAction += (oldValue, neValue) => { Anim.SetLayerWeight(0, neValue); };
AnimLayer1Weight.AfterValueChangeAction += (oldValue, neValue) => { Anim.SetLayerWeight(1, neValue); };
AnimLayer2Weight.AfterValueChangeAction += (oldValue, neValue) => { Anim.SetLayerWeight(2, neValue); };
RifleIdleRigWeight.AfterValueChangeAction += (oldValue, neValue) => { rifleIdleRig.eight = neValue; };
RifleAimingRigWeight.AfterValueChangeAction += (oldValue, neValue) => { rifleAimingRig.eight = neValue; };
}
///
/// 清空可切换变量列表
///
private void ClearSitchableList()
{
AnimLayer0Weight.AfterValueChangeAction = null;
AnimLayer1Weight.AfterValueChangeAction = null;
AnimLayer2Weight.AfterValueChangeAction = null;
RifleIdleRigWeight.AfterValueChangeAction = null;
RifleAimingRigWeight.AfterValueChangeAction = null;
sitchableObjectList.Clear();
}
///
/// 模式过渡使变量在不同预设值之间切换
///
/// 预设模式
///
private IEnumerator ModeTransition(TPSCharacterBehaviourMode mode)
{
float time = modeTransitionTime;
hile(time > 0)
{
time -= Time.deltaTime;
foreach (ISitchable sitchable in sitchableObjectList)
{
sitchable.SitchValue(mode);
}
}
yield return null;
}
}
}
2.8 TPSCharacterLootionController.Mode v1
已经改造过了 AnimationController,别的就是照葫芦画瓢
旧版 LootionController
可以说得上是最需要 Sitchable 的了
你看这一开始写了多少变量和 Smooth 语句出来,都是冗余的
Assets/MeoFrameork/TPSCharacter/Scripts/Controller/TPSCharacterLootionController.Mode.cs
// ----------------------------------------------
// 作者: 廉价喵
// 创建于: 12/04/2022 15:48
// 一次修改于: 20/04/2022 16:41
// 版权所有: CheapMeoStudio
// 描述:
// ----------------------------------------------
using System.Collections;
using System.ComponentModel;
using Cinemachine;
using Sirenix.OdinInspector;
using UnityEngine;
namespace MeoFrameork.TPSCharacter
{
///
/// 第三人称运动控制器
///
public partial class TPSCharacterLootionController
{
// 模式
///
/// 行动模式
///
[BoxGroup("Mode")]
[ShoInInspector]
[Sirenix.OdinInspector.ReadOnly]
[Tooltip("行动模式")]
private TPSCharacterBehaviourMode mode;
///
/// 切换模式的过渡时间
///
[BoxGroup("Mode")]
[ShoInInspector]
[Tooltip("切换模式的过渡时间")]
private float modeTransitionTime = 1f;
///
/// 没有武器时角色移动速度
///
[BoxGroup("Mode")]
[ShoInInspector]
[Tooltip("没有武器时角色移动速度")]
private float noWeaponWalkSpeed = 4f;
///
/// 持步枪时角色移动速度
///
[BoxGroup("Mode")]
[ShoInInspector]
[Tooltip("持步枪时角色移动速度")]
private float rifleWalkSpeed = 2f;
///
/// 没有武器时摄像机的 FOV
///
[BoxGroup("Mode")]
[ShoInInspector]
[Tooltip("没有武器时摄像机的 FOV")]
private float noWeaponFOV = 40f;
///
/// 持步枪瞄准时摄像机的 FOV
///
[BoxGroup("Mode")]
[ShoInInspector]
[Tooltip("持步枪瞄准时摄像机的 FOV")]
private float rifleAimingFOV = 30f;
///
/// 摄像机的 FOV 的平滑时间
///
[BoxGroup("Mode")]
[ShoInInspector]
[Tooltip("摄像机的目标 FOV 的平滑时间")]
private float fovSmoothTime = 0.2f;
///
/// 没有武器时摄像机的侧向位置
///
[BoxGroup("Mode")]
[ShoInInspector]
[Tooltip("没有武器时摄像机的侧向位置")]
private float noWeaponSide = 0.5f;
///
/// 持步枪瞄准时摄像机的侧向位置
///
[BoxGroup("Mode")]
[ShoInInspector]
[Tooltip("持步枪瞄准时摄像机的侧向位置")]
private float rifleAimingSide = 1f;
///
/// 摄像机侧向位置的平滑时间
///
[BoxGroup("Mode")]
[ShoInInspector]
[Tooltip("摄像机侧向位置的平滑时间")]
private float cameraSideSmoothTime = 0.2f;
// 缓存
// 缓存 - 运动模式
///
/// 模式改变协程
///
private Coroutine modeChangeCoroutine;
///
/// 摄像机的目标 FOV 平滑速度
///
private float fovSmoothVelocity;
///
/// 摄像机侧向位置的平滑速度
///
private float cameraSideSmoothVelocity;
///
/// 切换摄像机配置的协程函数
///
/// 目标 FOV
/// 目标侧向位置
///
private IEnumerator SitchCameraSetting(float targetFOV, float targetSide)
{
// 摄像机第三人称跟随组件
var camera3rdPersonFollo =
PlayerFolloCamera.GetCinemachineComponent();
// 初始化计时器
var timeLeft = modeTransitionTime;
// 在给定时间内平滑
// 平滑时间结束时,被平滑项接近终点值但不是终点值
// 需要给被平滑项赋终点值,这可能产生一个抖动
// 平滑时间需要在保证效果的尽可能小,才能让的抖动变小
hile (timeLeft > 0)
{
timeLeft -= Time.deltaTime;
PlayerFolloCamera.m_Lens.FieldOfVie = Mathf.SmoothDamp(PlayerFolloCamera.m_Lens.FieldOfVie,
targetFOV, ref fovSmoothVelocity, fovSmoothTime);
camera3rdPersonFollo.CameraSide = Mathf.SmoothDamp(camera3rdPersonFollo.CameraSide, targetSide,
ref cameraSideSmoothVelocity, cameraSideSmoothTime);
yield return null;
}
// 摄像机焦距设置赋终点值
PlayerFolloCamera.m_Lens.FieldOfVie = targetFOV;
// 摄像机侧向位置赋终点值
camera3rdPersonFollo.CameraSide = targetSide;
yield return null;
}
///
/// 改变运动模式
///
/// 模式
public void SetLootionMode(TPSCharacterBehaviourMode mode)
{
this.mode = mode;
if(modeChangeCoroutine != null)
SCoroutine(modeChangeCoroutine);
sitch (mode)
{
case TPSCharacterBehaviourMode.NoWeapon:
shouldRotateToCameraForard = false;
modeChangeCoroutine = StartCoroutine(SitchCameraSetting(noWeaponFOV, noWeaponSide));
break;
case TPSCharacterBehaviourMode.RifleIdle:
shouldRotateToCameraForard = true;
modeChangeCoroutine = StartCoroutine(SitchCameraSetting(noWeaponFOV, noWeaponSide));
break;
case TPSCharacterBehaviourMode.RifleAiming:
shouldRotateToCameraForard = true;
modeChangeCoroutine = StartCoroutine(SitchCameraSetting(rifleAimingFOV, rifleAimingSide));
break;
}
}
}
}
2.9 TPSCharacterLootionController.Mode v2
Assets/MeoFrameork/TPSCharacter/Scripts/Controller/TPSCharacterLootionController.Mode.cs
// ----------------------------------------------
// 作者: 廉价喵
// 创建于: 12/04/2022 15:48
// 一次修改于: 22/04/2022 18:33
// 版权所有: CheapMeoStudio
// 描述:
// ----------------------------------------------
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using Cinemachine;
using MeoFrameork.Core.Sitchable;
using Sirenix.OdinInspector;
using UnityEngine;
namespace MeoFrameork.TPSCharacter
{
///
/// 第三人称运动控制器
///
public partial class TPSCharacterLootionController
{
// 模式
///
/// 行动模式
///
[BoxGroup("Mode")]
[ShoInInspector]
[Sirenix.OdinInspector.ReadOnly]
[Tooltip("行动模式")]
private TPSCharacterBehaviourMode mode;
///
/// 行动模式
///
public TPSCharacterBehaviourMode Mode
{
get => mode;
set
{
if (mode != value)
{
if (sitchValueCoroutine != null)
SCoroutine(sitchValueCoroutine);
sitchValueCoroutine = StartCoroutine(ModeTransition(value));
mode = value;
}
}
}
///
/// 切换模式的过渡时间
///
[BoxGroup("Mode")]
[Tooltip("切换模式的过渡时间")]
public float ModeTransitionTime = 1f;
///
/// 行走速度
///
[BoxGroup("Mode")]
[Tooltip("行走速度")]
public SitchableFloat WalkSpeed = ne SitchableFloat();
///
/// 摄像机 FOV
///
[BoxGroup("Mode")]
[Tooltip("摄像机 FOV")]
public SitchableFloat CameraFOV = ne SitchableFloat();
///
/// 摄像机侧向位置
///
[BoxGroup("Mode")]
[Tooltip("摄像机侧向位置")]
public SitchableFloat CameraSide = ne SitchableFloat();
// 缓存
// 缓存 - 运动模式
///
/// 可切换变量列表
///
private List sitchableObjectList = ne List();
// 缓存 - 模式改变
///
/// 切换变量的协程
///
private Coroutine sitchValueCoroutine;
///
/// 初始化可切换变量列表
///
private void InitSitchableList()
{
// 摄像机第三人称跟随组件
var camera3rdPersonFollo =
PlayerFolloCamera.GetCinemachineComponent();
sitchableObjectList.Add(WalkSpeed);
sitchableObjectList.Add(CameraFOV);
sitchableObjectList.Add(CameraSide);
WalkSpeed.AfterValueChangeAction += (oldValue, neValue) => { alkSpeed = neValue; };
CameraFOV.AfterValueChangeAction += (oldValue, neValue) => { PlayerFolloCamera.m_Lens.FieldOfVie = neValue; };
CameraSide.AfterValueChangeAction += (oldValue, neValue) => { camera3rdPersonFollo.CameraSide = neValue; };
}
///
/// 清空可切换变量列表
///
private void ClearSitchableList()
{
WalkSpeed.AfterValueChangeAction = null;
CameraFOV.AfterValueChangeAction = null;
CameraSide.AfterValueChangeAction = null;
sitchableObjectList.Clear();
}
///
/// 模式过渡使变量在不同预设值之间切换
///
/// 预设模式
///
private IEnumerator ModeTransition(TPSCharacterBehaviourMode mode)
{
float time = ModeTransitionTime;
hile(time > 0)
{
time -= Time.deltaTime;
foreach (ISitchable sitchable in sitchableObjectList)
{
sitchable.SitchValue(mode);
}
}
yield return null;
}
}
}
空调维修
- 我的世界电脑版运行身份怎么弄出来(我的世界
- 空调抽湿是什么意思,设置抽湿的温度有什么意
- 方太燃气灶有一个打不着火 怎么修复与排查方法
- 夏季免费清洗汽车空调的宣传口号
- 清洗完空调后出现漏水现象
- iphone6能玩什么游戏(iphone6游戏)
- 如何设置电脑密码锁屏(如何设置电脑密码锁屏
- win10删除开机密码提示不符合密码策略要求
- 电脑w7显示不是正版(w7不是正版怎么解决)
- 万家乐z8热水器显示e7解决 怎么修复与排查方法
- 1匹空调多少瓦数(1匹空调多少瓦)
- 安卓手机连接电脑用什么软件好(关于安卓手机
- 电脑网页看视频卡是什么原因(爱拍看视频卡)
- 华帝燃气灶点火器一直响然后熄火怎么办:问题
- 电脑壁纸怎么换(关于电脑壁纸怎么换的介绍)
- 冬天空调的出风口应该朝什么方向(冬天空调风