当前位置:网站首页>[Unity] 战斗系统学习 12:Switchable
[Unity] 战斗系统学习 12:Switchable
2022-04-22 16:39:00 【廉价喵】
1. 批量 SmoothDamp 变量的需求
1.1 例子
这是我一个改到一半的函数……我懒得改了
这个函数的目的是从一个模式转换到另外一个模式的时候开始对一堆变量在一个时间内 SmoothDamp
目前是只有两个变量,所以我可以这么写,但是万一我都很多个变量呢?万一我要频繁地修改变量的名字,个数啥的呢?
我就感觉很麻烦
// 初始化计时器
var timeLeft = modeTransitionTime;
// 旧层级权重平滑速度
float fromLayerWeightSmoothVelocity = 0f;
// 新层级权重平滑速度
float toLayerWeightSmoothVelocity = 0f;
// 旧层级权重
var fromWeight = Anim.GetLayerWeight(fromLayer);
// 新层级权重
var toWeight = Anim.GetLayerWeight(toLayer);
// 在给定时间内平滑
// 平滑时间结束时,被平滑项接近终点值但不是终点值
// 因此最后需要给被平滑项赋终点值,这可能产生一个抖动
// 因此平滑时间需要在保证效果的同时尽可能小,才能让最后的抖动变小
while (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 啥的需求
所以我就想怎么方便地做这个切换的函数
1.2 分析
一个 mono 中有一个变量组,记为 V,一个表示模式的 Enum 变量,命名为 mode
V 对 mode 的不同值有不同预设值,把这些预设值做成一个 Struct 命名为 Setting
那么如果我想 V 的值在 mode 变化时,可以在与 mode 对应的 Setting 之间切换,我可以这么写
伪代码
EnumXXX mode;
Setting setting0;
Setting setting1;
Setting setting2;
Dictionary<EnumXXX, Setting> SettingDictionary = new Dictionary<EnumXXX, Setting>{
{
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里面可能出现的类型建一个类,叫SwitchableObject
比如Vector3就是SwitchableVector3,float就是SwitchableFloat
以SwitchableFloat为例,它包含一个float Value,一个List<float> SwitchableValueList和一个float SmoothVelocity - 新建一个接口
ISwitchable包含一个void SwitchValue(int index)函数,SwitchableFloat继承ISwitchable,函数内容是float类型的SmoothDamp mono里面有一个List<ISwitchable> switchableObjectList用于批量调用SwitchValue
这样,法一
伪代码
// 法一
float switchTime = 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<EnumXXX, Setting> settingDictionary;
void Start()
{
settingDictionary = new Dictionary<EnumXXX, Setting>{
{
0,setting0},{
1,setting1},{
2,setting2}};
}
现在是
伪代码
// 法二
List<ISwitchable> switchableObjectList;
SwitchableFloat V0;
V0.SwitchableValueList =
{
float target0;
float target1;
float target2;
}
SwitchableVector2 V1;
V1.SwitchableValueList =
{
Vector2 target0;
Vector2 target1;
Vector2 target2;
}
SwitchableVector3 V2;
V2.SwitchableValueList =
{
Vector3 target0;
Vector3 target1;
Vector3 target2;
}
void Start()
{
switchableObjectList.Add(V0);
switchableObjectList.Add(V1);
switchableObjectList.Add(V2);
}
以前我需要
伪代码
// 法一
Enumator SwitchSettingCoroutine(EnumXXX mode)
{
float time = switchTime;
while(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 SwitchSetting(EnumXXX mode)
{
StartCoroutine(SwitchSettingCoroutine(mode));
}
现在我需要
伪代码
// 法二
Enumator SwitchSettingCoroutine(EnumXXX mode)
{
float time = switchTime;
while(time > 0)
{
time -= Time.DeltaTime;
for(ISwitchable s in switchableObjectList)
s.SwitchValue(mode);
}
yield return null;
}
void SwitchSetting(EnumXXX mode)
{
StartCoroutine(SwitchSettingCoroutine(mode));
}
不知道我这样写行不行,会有什么问题……
这个看上去是很好的
一个数据表,假设行号是 Enum 列号是变量名
这样做把数据表的每一列拆到每一个变量里面
但是实际上符合习惯的做法是一行一行的
如果我真的要把数据表的一行放到一起,比如放到 ScriptableObject 里面
那么我取变量的目标值的流程就是:输入一个变量,然后通过反射拿到这个变量的名字,然后根据 Enum 在 字典 <Enum, Setting> 拿到 Setting,然后这个 Setting 也是一个 <string, 变量> 的字典,根据这个变量的名字在这个字典中拿到目标值
但是这样的话,这个函数有不同类型,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 SwitchableFloat : ISwitchable
{
public float value;
public Dictionary<EnumXXX, float> targetValueDict;
public override void SwitchValue(EnumXXX mode)
{
float target = targetValueDict[mode];
// SmoothDamp
}
}
public interface ISwitchable
{
public void SwitchValue(EnumXXX mode);
}
public float switchTime;
public SwitchableFloat value1;
public SwitchableFloat value2;
public SwitchableFloat value3;
public List<ISwitchable> switchableObjectList;
void Start()
{
switchableObjectList.Add(value1);
switchableObjectList.Add(value2);
switchableObjectList.Add(value3);
}
public IEnumerator SwitchSettingCoroutine(EnumXXX mode)
{
float time = switchTime;
while(time > 0)
{
time -= Time.deltaTime;
for(ISwitchable s in switchableObjectList)
s.SwitchValue(mode);
}
yield return null;
}
void SwitchSetting(EnumXXX mode)
{
StartCoroutine(SwitchSettingCoroutine(mode));
}
我不用泛型一个原因是在监视器上配置实现泛型的变量会出错,即使用了 Odin,另一个原因是 SmoothDamp 没有泛型
跟我讨论的朋友问我为什么不用 Animator,我说那是用来做骨骼动画的
但是后来我又想到 timeline,他是可以控制脚本的
首先的问题是我不知道怎么制作变量轨道
就算我知道了,变量轨道也需要一个确定的初值和终值,我这个是需要随时切换状态,比如按住右键瞄准,他可能一会瞄准一会不秒,切换时间小于动画时间,那么如果第一次动画时间,当前值就是初值,第二次相反的退出瞄准动画开始时,当前值也不会是退出瞄准动画的初值
除非我可以让动画的初值为当前值,或者我可以根据当前值,动画的初值终值得到我应该在哪个百分比进度播放动画……
好吧我后面知道了 Playable Track 是可以自定义 Clip 的
好麻烦……不想看……我就是懒hhhh
而且按照我最后的法四,我可以在监视器中对每一项变量设置对每一个 mode 的可能值的目标值,如果我不设置说明我就不用平滑这个变量
这是点开组件就能在监视器中看到的,timeline 可不行
再化简一点就是
伪代码
// 法四
public class SwitchableFloat : ISwitchable
{
public float value;
public Dictionary<EnumXXX, float> targetValueDict;
public override void SwitchValue(EnumXXX mode)
{
float target = targetValueDict[mode];
// SmoothDamp
}
}
public interface ISwitchable
{
public void SwitchValue(EnumXXX mode);
}
public float switchTime;
public SwitchableFloat value1;
public SwitchableFloat value2;
public SwitchableFloat value3;
public List<ISwitchable> switchableObjectList;
private Coroutine switchSettingCoroutine;
private EnumXXX mode;
public EnumXXX Mode
{
get => mode;
set
{
if (mode != value)
{
if (switchSettingCoroutine != null)
StopCoroutine(switchSettingCoroutine);
switchSettingCoroutine = StartCoroutine(SwitchSettingCoroutine(value));
mode = value;
}
}
}
void Start()
{
switchableObjectList.Add(value1);
switchableObjectList.Add(value2);
switchableObjectList.Add(value3);
}
public IEnumerator SwitchSettingCoroutine(EnumXXX mode)
{
float time = switchTime;
while(time > 0)
{
time -= Time.deltaTime;
for(ISwitchable s in switchableObjectList)
s.SwitchValue(mode);
}
yield return null;
}
2. Switchable
正式地开始写了
2.1 ISwitchable v1
Assets/MeowFramework/Core/Switchable/ISwitchable.cs
// ----------------------------------------------
// 作者: 廉价喵
// 创建于: 22/04/2022 9:00
// 最后一次修改于: 22/04/2022 9:11
// 版权所有: CheapMeowStudio
// 描述:
// ----------------------------------------------
using System;
namespace MeowFramework.Core.Switchable
{
/// <summary>
/// 切换变量的接口
/// </summary>
public interface ISwitchable
{
/// <summary>
/// 使变量在不同预设值之间切换
/// </summary>
/// <param name="mode">预设模式</param>
public void SwitchValue(Enum mode);
}
}
2.2 SwitchableFloat v1
Assets/MeowFramework/Core/Switchable/SwitchableFloat.cs
// ----------------------------------------------
// 作者: 廉价喵
// 创建于: 22/04/2022 9:01
// 最后一次修改于: 22/04/2022 9:11
// 版权所有: CheapMeowStudio
// 描述:
// ----------------------------------------------
using System;
using System.Collections.Generic;
using UnityEngine;
namespace MeowFramework.Core.Switchable
{
/// <summary>
/// 可切换浮点
/// </summary>
public class SwitchableFloat : ISwitchable
{
/// <summary>
/// 当前值
/// </summary>
[Tooltip("当前值")]
public float Value;
/// <summary>
/// 预设值字典
/// </summary>
[Tooltip("预设值字典")]
public Dictionary<Enum, float> TargetValueDict;
// 缓存
/// <summary>
/// 平滑速度
/// </summary>
private float smoothVelocity;
/// <summary>
/// 平滑时间
/// </summary>
[Tooltip("平滑时间")]
public float SmoothTime = 0.2f;
// 实现接口
/// <summary>
/// 使变量在不同预设值之间切换
/// </summary>
/// <param name="mode">预设模式</param>
public void SwitchValue(Enum mode)
{
// SmoothDamp
float target = TargetValueDict[mode];
Value = Mathf.SmoothDamp(Value, target, ref smoothVelocity, SmoothTime);
}
}
}
2.3 TPSCharacterAnimationSetting v1
这是第一版方案,是废弃了的
拿出来是为了对比,显示这样做有多笨
Assets/MeowFramework/TPSCharacter/Scripts/Struct/TPSCharacterAnimationSetting.cs
// ----------------------------------------------
// 作者: 廉价喵
// 创建于: 20/04/2022 17:52
// 最后一次修改于: 20/04/2022 17:57
// 版权所有: CheapMeowStudio
// 描述:
// ----------------------------------------------
namespace MeowFramework.TPSCharacter.Struct
{
public struct TPSCharacterAnimationSetting
{
/// <summary>
/// 无武器层的动画层级权重
/// </summary>
public float NoWeaponLayerWeight;
/// <summary>
/// 持枪待机层的动画层级权重
/// </summary>
public float RifleIdleLayerWeight;
/// <summary>
/// 持枪瞄准层的动画层级权重
/// </summary>
public float RifleAimingLayerWeight;
/// <summary>
/// 无武器层的骨骼绑定权重
/// </summary>
public float NoWeaponRigWeight;
/// <summary>
/// 持枪待机层的骨骼绑定权重
/// </summary>
public float RifleIdleRigWeight;
/// <summary>
/// 持枪瞄准层的骨骼绑定权重
/// </summary>
public float RifleAimingRigWeight;
}
}
2.4 TPSCharacterAnimationController.Mode v1
这是一个修改前的半成品……确实有些地方直接就是跑不通的
拿出来是为了对比,显示这样做有多笨

Assets/MeowFramework/TPSCharacter/Scripts/Controller/TPSCharacterAnimationController.Mode.cs
// ----------------------------------------------
// 作者: 廉价喵
// 创建于: 12/04/2022 15:55
// 最后一次修改于: 22/04/2022 9:01
// 版权所有: CheapMeowStudio
// 描述:
// ----------------------------------------------
using System.Collections;
using System.Collections.Generic;
using MeowFramework.TPSCharacter.Struct;
using Sirenix.OdinInspector;
using UnityEngine;
using UnityEngine.Animations.Rigging;
namespace MeowFramework.TPSCharacter
{
/// <summary>
/// 第三人称动画状态机控制器
/// </summary>
public partial class TPSCharacterAnimationController
{
/// <summary>
/// 行动模式
/// </summary>
[BoxGroup("Mode")]
[ShowInInspector]
[Sirenix.OdinInspector.ReadOnly]
[Tooltip("行动模式")]
private TPSCharacterBehaviourMode mode;
/// <summary>
/// 无武器层的动画层级的序号
/// </summary>
[BoxGroup("Mode")]
[ShowInInspector]
[Tooltip("无武器层的动画层级的序号")]
private int noWeaponLayerIndex = 0;
/// <summary>
/// 持步枪待机层的动画层级的序号
/// </summary>
[BoxGroup("Mode")]
[ShowInInspector]
[Tooltip("持步枪待机层的动画层级的序号")]
private int rifleIdleLayerIndex = 0;
/// <summary>
/// 持步枪瞄准层的动画层级的序号
/// </summary>
[BoxGroup("Mode")]
[ShowInInspector]
[Tooltip("持步枪瞄准层的动画层级的序号")]
private int rifleAimingLayerIndex = 0;
/// <summary>
/// 无武器层的动画参数配置
/// </summary>
[BoxGroup("Mode")]
[ShowInInspector]
[Tooltip("无武器层的动画参数配置")]
private TPSCharacterAnimationSetting noWeaponSetting;
/// <summary>
/// 持步枪待机层的动画参数配置
/// </summary>
[BoxGroup("Mode")]
[ShowInInspector]
[Tooltip("持步枪待机层的动画参数配置")]
private TPSCharacterAnimationSetting rifleIdleSetting;
/// <summary>
/// 持步枪瞄准层的动画参数配置
/// </summary>
[BoxGroup("Mode")]
[ShowInInspector]
[Tooltip("持步枪瞄准层的动画参数配置")]
private TPSCharacterAnimationSetting rifleAimingSetting;
/// <summary>
/// 切换模式的过渡时间
/// </summary>
[BoxGroup("Mode")]
[ShowInInspector]
[Tooltip("切换模式的过渡时间")]
private float modeTransitionTime = 1f;
/// <summary>
/// 层级平滑时间
/// </summary>
[BoxGroup("Mode")]
[ShowInInspector]
[Tooltip("层级平滑时间")]
private float layerWeightSmoothTime = 0.2f;
// 缓存 - 模式改变
/// <summary>
/// 平滑切换层级权重的协程
/// </summary>
private Coroutine smoothSwitchLayerWeightCoroutine;
/// <summary>
/// 平滑切换层级权重
/// </summary>
/// <param name="fromLayer">旧层级</param>
/// <param name="toLayer">新层级</param>
/// <returns></returns>
private IEnumerator SwitchAnimationSetting(TPSCharacterBehaviourMode mode)
{
// 初始化计时器
var timeLeft = modeTransitionTime;
// 旧层级权重平滑速度
float fromLayerWeightSmoothVelocity = 0f;
// 新层级权重平滑速度
float toLayerWeightSmoothVelocity = 0f;
// 旧层级权重
var fromWeight = Anim.GetLayerWeight(fromLayer);
// 新层级权重
var toWeight = Anim.GetLayerWeight(toLayer);
// 在给定时间内平滑
// 平滑时间结束时,被平滑项接近终点值但不是终点值
// 因此最后需要给被平滑项赋终点值,这可能产生一个抖动
// 因此平滑时间需要在保证效果的同时尽可能小,才能让最后的抖动变小
while (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 SwitchRigWeight(TPSCharacterBehaviourMode mode)
{
List<Rig> rigs = new List<Rig> {
rifleIdleRig, rifleAimingRig};
switch (mode)
{
case
rigs.Remove(rifleIdleRig);
}
yield return null;
}
/// <summary>
/// 设置动画模式
/// </summary>
/// <param name="mode">模式</param>
public void SetAnimationMode(TPSCharacterBehaviourMode mode)
{
// 更新模式记录
this.mode = mode;
// 如果有正在进行的模式切换相关的协程,就关闭这个协程
if(smoothSwitchLayerWeightCoroutine != null)
StopCoroutine(smoothSwitchLayerWeightCoroutine);
//
switch (mode)
{
case TPSCharacterBehaviourMode.NoWeapon:
smoothSwitchLayerWeightCoroutine = StartCoroutine(SwitchLayerWeight(1, 0));
break;
case TPSCharacterBehaviourMode.RifleIdle:
smoothSwitchLayerWeightCoroutine = StartCoroutine(SwitchLayerWeight(0, 1));
break;
}
}
}
}
2.5 TPSCharacterAnimationController.Mode v2
按照我最后的思路就是写成
Assets/MeowFramework/TPSCharacter/Scripts/Controller/TPSCharacterAnimationController.Mode.cs
// ----------------------------------------------
// 作者: 廉价喵
// 创建于: 12/04/2022 15:55
// 最后一次修改于: 22/04/2022 9:39
// 版权所有: CheapMeowStudio
// 描述:
// ----------------------------------------------
using System.Collections;
using System.Collections.Generic;
using MeowFramework.Core.Switchable;
using Sirenix.OdinInspector;
using UnityEngine;
namespace MeowFramework.TPSCharacter
{
/// <summary>
/// 第三人称动画状态机控制器
/// </summary>
public partial class TPSCharacterAnimationController
{
/// <summary>
/// 行动模式
/// </summary>
[BoxGroup("Mode")]
[ShowInInspector]
[Sirenix.OdinInspector.ReadOnly]
[Tooltip("行动模式")]
private TPSCharacterBehaviourMode mode;
/// <summary>
/// 行动模式
/// </summary>
public TPSCharacterBehaviourMode Mode
{
get => mode;
set
{
if (mode != value)
{
if (switchValueCoroutine != null)
StopCoroutine(switchValueCoroutine);
switchValueCoroutine = StartCoroutine(ModeTransition(value));
mode = value;
}
}
}
/// <summary>
/// 切换模式的过渡时间
/// </summary>
[BoxGroup("Mode")]
[ShowInInspector]
[Tooltip("切换模式的过渡时间")]
private float modeTransitionTime = 1f;
/// <summary>
/// 动画状态机第 0 层的权重
/// </summary>
[BoxGroup("Mode")]
[Tooltip("动画状态机第 0 层的权重")]
public SwitchableFloat AnimLayer0Weight;
/// <summary>
/// 动画状态机第 1 层的权重
/// </summary>
[BoxGroup("Mode")]
[Tooltip("动画状态机第 1 层的权重")]
public SwitchableFloat AnimLayer1Weight;
/// <summary>
/// 动画状态机第 2 层的权重
/// </summary>
[BoxGroup("Mode")]
[Tooltip("动画状态机第 2 层的权重")]
public SwitchableFloat AnimLayer2Weight;
/// <summary>
/// 可切换变量列表
/// </summary>
private List<ISwitchable> switchableObjectList = new List<ISwitchable>();
// 缓存 - 模式改变
/// <summary>
/// 切换变量的协程
/// </summary>
private Coroutine switchValueCoroutine;
/// <summary>
/// 初始化可切换变量列表
/// </summary>
private void InitSwitchableList()
{
switchableObjectList.Add(AnimLayer0Weight);
switchableObjectList.Add(AnimLayer1Weight);
switchableObjectList.Add(AnimLayer2Weight);
}
/// <summary>
/// 模式过渡:使变量在不同预设值之间切换
/// </summary>
/// <param name="mode">预设模式</param>
/// <returns></returns>
private IEnumerator ModeTransition(TPSCharacterBehaviourMode mode)
{
float time = modeTransitionTime;
while(time > 0)
{
time -= Time.deltaTime;
foreach (ISwitchable switchable in switchableObjectList)
{
switchable.SwitchValue(mode);
}
}
yield return null;
}
}
}
但是我发现我还需要做一个数据绑定,把 AnimLayer0Weight, AnimLayer1Weight, AnimLayer2Weight 的值绑定到 Animator
不能在外部替换类里面的属性,那就只能用委托了
所以 SwitchableFloat 还要改
2.6 SwitchableFloat v2
修改后的可切换变量,提供了变量改变时的钩子
Assets/MeowFramework/Core/Switchable/SwitchableFloat.cs
// ----------------------------------------------
// 作者: 廉价喵
// 创建于: 22/04/2022 9:01
// 最后一次修改于: 22/04/2022 10:01
// 版权所有: CheapMeowStudio
// 描述:
// ----------------------------------------------
using System;
using System.Collections.Generic;
using Sirenix.OdinInspector;
using UnityEngine;
namespace MeowFramework.Core.Switchable
{
/// <summary>
/// 可切换浮点
/// </summary>
public class SwitchableFloat : ISwitchable
{
/// <summary>
/// 当前值
/// </summary>
[ShowInInspector]
[Tooltip("当前值")]
private float value;
/// <summary>
/// 当前值
/// </summary>
public float Value
{
get => value;
set
{
if (this.value != value)
{
AfterValueChangeAction?.Invoke(this.value,value);
this.value = value;
}
}
}
/// <summary>
/// 值改变后触发的委托
/// </summary>
[HideInInspector]
public Action<float, float> AfterValueChangeAction;
/// <summary>
/// 预设值字典
/// </summary>
[Tooltip("预设值字典")]
public Dictionary<Enum, float> TargetValueDict = new Dictionary<Enum, float>();
// 缓存
/// <summary>
/// 平滑速度
/// </summary>
private float smoothVelocity;
/// <summary>
/// 平滑时间
/// </summary>
[Tooltip("平滑时间")]
public float SmoothTime = 0.2f;
// 实现接口
/// <summary>
/// 使变量在不同预设值之间切换
/// </summary>
/// <param name="mode">预设模式</param>
public void SwitchValue(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/MeowFramework/TPSCharacter/Scripts/Controller/TPSCharacterAnimationController.Mode.cs
// ----------------------------------------------
// 作者: 廉价喵
// 创建于: 12/04/2022 15:55
// 最后一次修改于: 22/04/2022 14:40
// 版权所有: CheapMeowStudio
// 描述:
// ----------------------------------------------
using System.Collections;
using System.Collections.Generic;
using MeowFramework.Core.Switchable;
using Sirenix.OdinInspector;
using UnityEngine;
namespace MeowFramework.TPSCharacter
{
/// <summary>
/// 第三人称动画状态机控制器
/// </summary>
public partial class TPSCharacterAnimationController
{
/// <summary>
/// 行动模式
/// </summary>
[BoxGroup("Mode")]
[ShowInInspector]
[Sirenix.OdinInspector.ReadOnly]
[Tooltip("行动模式")]
private TPSCharacterBehaviourMode mode;
/// <summary>
/// 行动模式
/// </summary>
public TPSCharacterBehaviourMode Mode
{
get => mode;
set
{
if (mode != value)
{
if (switchValueCoroutine != null)
StopCoroutine(switchValueCoroutine);
switchValueCoroutine = StartCoroutine(ModeTransition(value));
mode = value;
}
}
}
/// <summary>
/// 切换模式的过渡时间
/// </summary>
[BoxGroup("Mode")]
[ShowInInspector]
[Tooltip("切换模式的过渡时间")]
private float modeTransitionTime = 1f;
/// <summary>
/// 动画状态机第 0 层的权重
/// </summary>
[BoxGroup("Mode")]
[Tooltip("动画状态机第 0 层的权重")]
public SwitchableFloat AnimLayer0Weight = new SwitchableFloat();
/// <summary>
/// 动画状态机第 1 层的权重
/// </summary>
[BoxGroup("Mode")]
[Tooltip("动画状态机第 1 层的权重")]
public SwitchableFloat AnimLayer1Weight = new SwitchableFloat();
/// <summary>
/// 动画状态机第 2 层的权重
/// </summary>
[BoxGroup("Mode")]
[Tooltip("动画状态机第 2 层的权重")]
public SwitchableFloat AnimLayer2Weight = new SwitchableFloat();
/// <summary>
/// 步枪待机姿态所用到的骨骼绑定的权重
/// </summary>
[BoxGroup("Mode")]
[Tooltip("步枪待机姿态所用到的骨骼绑定的权重")]
public SwitchableFloat RifleIdleRigWeight = new SwitchableFloat();
/// <summary>
/// 步枪瞄准姿态所用到的骨骼绑定的权重
/// </summary>
[BoxGroup("Mode")]
[Tooltip("步枪瞄准姿态所用到的骨骼绑定的权重")]
public SwitchableFloat RifleAimingRigWeight = new SwitchableFloat();
/// <summary>
/// 可切换变量列表
/// </summary>
private List<ISwitchable> switchableObjectList = new List<ISwitchable>();
// 缓存 - 模式改变
/// <summary>
/// 切换变量的协程
/// </summary>
private Coroutine switchValueCoroutine;
/// <summary>
/// 初始化可切换变量列表
/// </summary>
private void InitSwitchableList()
{
switchableObjectList.Add(AnimLayer0Weight);
switchableObjectList.Add(AnimLayer1Weight);
switchableObjectList.Add(AnimLayer2Weight);
switchableObjectList.Add(RifleIdleRigWeight);
switchableObjectList.Add(RifleAimingRigWeight);
AnimLayer0Weight.AfterValueChangeAction += (oldValue, newValue) => {
Anim.SetLayerWeight(0, newValue); };
AnimLayer1Weight.AfterValueChangeAction += (oldValue, newValue) => {
Anim.SetLayerWeight(1, newValue); };
AnimLayer2Weight.AfterValueChangeAction += (oldValue, newValue) => {
Anim.SetLayerWeight(2, newValue); };
RifleIdleRigWeight.AfterValueChangeAction += (oldValue, newValue) => {
rifleIdleRig.weight = newValue; };
RifleAimingRigWeight.AfterValueChangeAction += (oldValue, newValue) => {
rifleAimingRig.weight = newValue; };
}
/// <summary>
/// 清空可切换变量列表
/// </summary>
private void ClearSwitchableList()
{
AnimLayer0Weight.AfterValueChangeAction = null;
AnimLayer1Weight.AfterValueChangeAction = null;
AnimLayer2Weight.AfterValueChangeAction = null;
RifleIdleRigWeight.AfterValueChangeAction = null;
RifleAimingRigWeight.AfterValueChangeAction = null;
switchableObjectList.Clear();
}
/// <summary>
/// 模式过渡:使变量在不同预设值之间切换
/// </summary>
/// <param name="mode">预设模式</param>
/// <returns></returns>
private IEnumerator ModeTransition(TPSCharacterBehaviourMode mode)
{
float time = modeTransitionTime;
while(time > 0)
{
time -= Time.deltaTime;
foreach (ISwitchable switchable in switchableObjectList)
{
switchable.SwitchValue(mode);
}
}
yield return null;
}
}
}
版权声明
本文为[廉价喵]所创,转载请带上原文链接,感谢
https://blog.csdn.net/PriceCheap/article/details/124334943
边栏推荐
- BACKBONE,NECK,HEAD
- Huawei machine test questions -- hj54 expression evaluation
- Solidity: source file structure
- 实验四、数据挖掘之KNN,Naive Bayes
- 京东一面:子线程如何获取父线程 ThreadLocal 的值?我蒙了。。。
- Blue Bridge Cup practice 014
- Parameter setting of Sanke inverter pipe bender
- 蓝桥杯练习019
- In SolidWorks, why can I only set direction 1 but not direction 2 for linear sketch array?
- 坚持做正确的事情
猜你喜欢

i.MX6ULL驱动开发 | 21 - 按键驱动使用 input 子系统上报事件

Industry research: the development trend of domestic databases from the perspective of manufacturers

小练习:二分查找及实现

Transforme结构:位置编码 | Transformer Architecture: The Positional Encoding

DB107-ASEMI整流桥详细数据

RT-Thread Studio 对代码进行格式化

wps excel 怎么复制工作表?(移动或复制工作表)

从思维走向实践,数字化转型 IT 经营的成功路径

Blue Bridge Cup practice 018

漫谈同源策略(SOP)和跨域资源共享(CORS)
随机推荐
Circular linked list application - Joseph Ring
Multithreading uses redis to accumulate. The result is incorrect. Solution
Dynamically drag the width of two divs
测试人生 | 毕业2年未满,0经验拿下知名互联网企业30W 年薪,他是怎么做到的?
面向全球市场,PlatoFarm今日登录HUOBI等全球四大平台
STM32的一种串口数据接受方式
Transforme结构:位置编码 | Transformer Architecture: The Positional Encoding
NLP之TM:基于gensim库调用20newsgr学习doc-topic分布并保存为train-svm-lda.txt、test-svm-lda.txt
redis优化系列(一)基于docker搭建Redis主从
Unittest - unit test 2
领域驱动模型DDD(三)——使用Saga管理事务
员工健康码数据如何自动汇总?
Blue Bridge Cup practice 011
Gome's new actions "true selection" and "strict selection" enable multi-dimensional escort quality consumption
最新流程引擎 flowable 6.7.2 更新说明
Test life | less than 2 years after graduation, 0 experience and won the 30W annual salary of a well-known Internet enterprise. How did he do it?
MiniUSB管脚接口引脚定义
小练习:二分查找及实现
Huawei machine test question - hj60 find the two prime numbers that form an even number closest to each other
蓝桥杯练习011