[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);
	        
	        // 在给定时间内平滑
	        // 平滑时间结束时,被平滑项接近终点值但不是终点值
	        // 因此最后需要给被平滑项赋终点值,这可能产生一个抖动
	        // 因此平滑时间需要在保证效果的同时尽可能小,才能让最后的抖动变小
	        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
Vmode 的不同值有不同预设值,把这些预设值做成一个 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];

但是如果我想要 Vsetting 之间使用 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 的缓存变量

那要解决这个问题的话,我目前只能想到是

  1. 对每一种 setting 里面可能出现的类型建一个类,叫 SwitchableObject
    比如 Vector3 就是 SwitchableVector3float 就是 SwitchableFloat
    SwitchableFloat 为例,它包含一个 float Value,一个 List<float> SwitchableValueList 和一个 float SmoothVelocity
  2. 新建一个接口 ISwitchable 包含一个 void SwitchValue(int index) 函数,SwitchableFloat 继承 ISwitchable,函数内容是 float 类型的 SmoothDamp
  3. 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
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值