Playmaker节点工具使用(三)—扩展playmaker

为什么要扩展

在unity购买的playmaker 是没有源码的,很多情况下,playmaker无法满足我们的定制需求,这时候我们就需要对playmaker 进行扩展,改造

那么在没有源码的情况下,如何进行playmaker的扩展和改造呢

扩展方法

一个封闭的动态链接库,想要扩展方法是可以的,因为方法本质不包含数据,只是处理数据的方式,也不涉及到数据结构的改造

只要使用C#的扩展方法即可,假设我们需要在状态间传递数据,我们在Action中扩展四个方法
获取上一个状态数据,
设置下一个状态数据,
获取指定状态数据,
设置指定状态数据
FsmStateExtensions 部分参考代码:

    /// <summary>
    /// 以当前FsmState和当前Action为名,传递数据
    /// </summary>
    /// <param name="fsmState"></param>
    /// <param name="values"></param>
    public static void SetTransValues(this FsmStateAction fsmStateAction, params object[] values)
    {
        string fsmName = fsmStateAction.State.Name;
        string actionName = fsmStateAction.Name;

        var fsmListen = FsmListen.GetGameObjectFsmListen(fsmStateAction);

        var fsmCache = fsmListen.GetFsmCache(fsmStateAction.Fsm);
        var _stateDic = fsmCache.stateDic;

        bool isOk = _stateDic.TryGetValue(fsmName, out var value);
        if (!isOk)
        {
            _stateDic.Add(fsmName, new ActionObj(actionName, values));
        }
        else
        {
            value.valuesDic[actionName].AddRange(values);
        }

        return;
    }

    /// <summary>
    /// 从状态获取数据
    /// </summary>
    /// <param name="fsmStateAction"></param>
    /// <param name="fsmStateName"></param>
    /// <param name="actionName"></param>
    /// <returns></returns>
    public static object[] GetTransValues(this FsmStateAction fsmStateAction, string fsmStateName, string actionName)
    {
        var fsmListen = FsmListen.GetGameObjectFsmListen(fsmStateAction);

        var fsmCache = fsmListen.GetFsmCache(fsmStateAction.Fsm);
        var _stateDic = fsmCache.stateDic;

        var values = _stateDic[fsmStateName]?.valuesDic[actionName].ToArray();
        if (values == null || values.Length <= 0)
        {
            return null;
        }
        _stateDic.Remove(fsmStateName);
        return values;
    }

    /// <summary>
    /// 从上一个状态获取数据,返回状态数据合集
    /// </summary>
    /// <param name="fsmStateAction"></param>
    /// <param name="fsmStateName"></param>
    /// <param name="actionName"></param>
    /// <returns></returns>
    public static ActionObj GetTransValues(this FsmStateAction fsmStateAction)
    {
        var fsmListen = FsmListen.GetGameObjectFsmListen(fsmStateAction);

        var fsmCache = fsmListen.GetFsmCache(fsmStateAction.Fsm);
        var _stateDic = fsmCache.stateDic;
        var lastFsmState = fsmCache.lastFsmState;

        if (lastFsmState == null)
        {
            return null;
        }

        _stateDic.TryGetValue(lastFsmState.Name, out var actionObj);
        return actionObj;
    }
    /// <summary>
    /// 得到上一个状态
    /// </summary>
    /// <param name="fsmStateAction"></param>
    /// <returns></returns>
    public static FsmState GetLastFsmState(this FsmStateAction fsmStateAction)
    {
        var fsmListen = FsmListen.GetGameObjectFsmListen(fsmStateAction);

        var fsmCache = fsmListen.GetFsmCache(fsmStateAction.Fsm);
        var lastFsmState = fsmCache.lastFsmState;

        if (lastFsmState == null)
        {
            return null;
        }
        return lastFsmState;
    }
    /// <summary>
    /// 设置下一个状态
    /// </summary>
    /// <param name="fsmStateAction"></param>
    public static void SetLastFsmState(this FsmStateAction fsmStateAction)
    {
        var fsmListen = FsmListen.GetGameObjectFsmListen(fsmStateAction);

        var fsmCache = fsmListen.GetFsmCache(fsmStateAction.Fsm);
        fsmCache.lastFsmState = fsmStateAction.State;
    }

扩展属性

只有扩展方法,大部分时候还是无法满足需求,如上,设置下一个状态,设置上一个状态,我们需要有一个对象去存储,当一个状态结束,将需要的数据放入,当一个状态开始,按需又从对应的对象去获取,这样才能实现跨状态的数据传递。

问题在于,数据存在哪?存在哪个对象身上?

按照Playmaker二的几个对象传递方法,第一种采用playmaker values,这样虽然可以成功,但是所有的变量类型都需要在playmaker 面板进行声明,很麻烦,我们实际上不需要知道是哪个对象进行了存储,我们只需要数据由上一个状态传递到下一个状态,中间具体是由什么变量和对象传递,并不关心,所以这个方法不是最合适的

如果采用全局方式,那么就更不行了,静态对象会全局存在,如果超过一个fsm,就很不好管理,Action 和State会相互影响,数据乱串

最后还有一个方法是采用实例,如果我们能再fsm对象本身加入字段,由这个跟随Fsm生命周期的类去存储与传递数据,显然是最合适的。
这样做既可以实现不用每次声明变量,相对于playmaker values的优势,
又可以摆脱静态对象只能有一个的限制。

完美,但是因为面向对象的关系,只有子类可以扩展属性,然而我们无法继承playmaker 从而实现扩展属性,

因此退而求其次,我们在每次fsm创造时,加入一个FsmListen对象,由这个对象跟着fsm,每个fsm都拥有一个FsmListen,我们将需要扩展的属性全部加入到这个FsmListen中,就好像实现了扩展属性

在fsm中加入FsmListen,使用单例的设计模式,不过这个单例的范围只限于fsm的生命周期,在fsm生命周期中,每个fsm只拥有唯一一个FsmListen作为扩展属性、

可以理解为背了一个背包,把无法扩展的属性放到背包中,每个人是fsm,然后都拥有一个背包

FsmStateExtensions 完整参考代码:

using HutongGames.PlayMaker;
using System.Collections.Generic;
using UnityEngine;
using static FsmStateExtensions.FsmListen;

public static class FsmStateExtensions
{
    /// <summary>
    /// 以当前FsmState和当前Action为名,传递数据
    /// </summary>
    /// <param name="fsmState"></param>
    /// <param name="values"></param>
    public static void SetTransValues(this FsmStateAction fsmStateAction, params object[] values)
    {
        string fsmName = fsmStateAction.State.Name;
        string actionName = fsmStateAction.Name;

        var fsmListen = FsmListen.GetFsmListen(fsmStateAction);

        var fsmCache = fsmListen.GetFsmCache(fsmStateAction.Fsm);
        var _stateDic = fsmCache.stateDic;

        bool isOk = _stateDic.TryGetValue(fsmStateAction, out var value);
        if (!isOk)
        {
            _stateDic.Add(fsmStateAction, new ActionObj(actionName, values));
        }
        else
        {
            value.valuesDic[actionName].AddRange(values);
        }

        return;
    }

    /// <summary>
    /// 从状态获取数据
    /// </summary>
    /// <param name="fsmStateAction"></param>
    /// <param name="fsmStateName"></param>
    /// <param name="actionName"></param>
    /// <returns></returns>
    public static object[] GetTransValues(this FsmStateAction fsmStateAction, string fsmStateName, string actionName)
    {
        var fsmListen = FsmListen.GetFsmListen(fsmStateAction);

        var fsmCache = fsmListen.GetFsmCache(fsmStateAction.Fsm);
        var _stateDic = fsmCache.stateDic;

        var values = _stateDic[fsmStateAction]?.valuesDic[actionName].ToArray();
        if (values == null || values.Length <= 0)
        {
            return null;
        }
        _stateDic.Remove(fsmStateAction);
        return values;
    }

    /// <summary>
    /// 从上一个状态获取数据,返回状态数据合集
    /// </summary>
    /// <param name="fsmStateAction"></param>
    /// <param name="fsmStateName"></param>
    /// <param name="actionName"></param>
    /// <returns></returns>
    public static ActionObj GetTransValues(this FsmStateAction fsmStateAction)
    {
        var fsmListen = FsmListen.GetFsmListen(fsmStateAction);

        var fsmCache = fsmListen.GetFsmCache(fsmStateAction.Fsm);
        var _stateDic = fsmCache.stateDic;
        var lastFsmState = fsmCache.lastFsmState;

        if (lastFsmState == null)
        {
            return null;
        }

        _stateDic.TryGetValue(fsmStateAction, out var actionObj);
        return actionObj;
    }

    public static FsmState GetLastFsmState(this FsmStateAction fsmStateAction)
    {
        var fsmListen = FsmListen.GetFsmListen(fsmStateAction);

        var fsmCache = fsmListen.GetFsmCache(fsmStateAction.Fsm);
        var lastFsmState = fsmCache.lastFsmState;

        if (lastFsmState == null)
        {
            return null;
        }
        return lastFsmState;
    }

    public static void SetLastFsmState(this FsmStateAction fsmStateAction)
    {
        var fsmListen = FsmListen.GetFsmListen(fsmStateAction);

        var fsmCache = fsmListen.GetFsmCache(fsmStateAction.Fsm);
        fsmCache.lastFsmState = fsmStateAction.State;
    }
    /// <summary>
    /// 获取当前Fsm的FsmListen
    /// </summary>
    /// <param name="fsmStateAction"></param>
    /// <returns></returns>
    public static FsmListen GetFsmListen(this FsmStateAction fsmStateAction)
    {
        var fsmListen = fsmStateAction.Fsm.GameObject.GetComponent<FsmListen>();
        if (fsmListen != null)
        {
            return fsmListen;
        }
        else
        {
            //没有对应的FsmListen,创建,一个Fsm监听Gameobject上的所有Fsm
            fsmListen = fsmStateAction.Fsm.GameObject.AddComponent<FsmListen>();
        }
        //没有对应的fsm,加入fsm且把当前状态,当前Action Name缓存
        fsmListen.fsmCacheDic.TryGetValue(fsmStateAction.Fsm, out var fsmCache);
        if (fsmCache == null)
        {
            var fsmCacheValue = new FsmCache();
            fsmCacheValue.stateDic.Add(fsmStateAction, null);
            fsmListen.fsmCacheDic.Add(fsmStateAction.Fsm, fsmCacheValue);
        }

        return fsmListen;
    }

    public class ActionObj
    {
        public Dictionary<string, List<object>> valuesDic = new Dictionary<string, List<object>>();

        public ActionObj(string actionName, params object[] values)
        {
            valuesDic.Add(actionName, new List<object>(values));
        }
    }

    public class FsmListen : MonoBehaviour
    {
        public float offsetTime=0;

        public FsmCache GetFsmCache(Fsm fsm)
        {
            fsmCacheDic.TryGetValue(fsm, out var cache);
            return cache;
        }

        public Dictionary<Fsm, FsmCache> fsmCacheDic = new Dictionary<Fsm, FsmCache>();

        public class FsmCache
        {
            /// <summary>
            /// state name
            /// </summary>
            public Dictionary<FsmStateAction, ActionObj> stateDic = new Dictionary<FsmStateAction, ActionObj>();

            public FsmState lastFsmState;
        }

        public static FsmListen GetFsmListen(FsmStateAction fsmStateAction)
        {
            var fsmListen = fsmStateAction.Fsm.GameObject.GetComponent<FsmListen>();
            if (fsmListen != null)
            {
                return fsmListen;
            }
            else
            {
                //没有对应的FsmListen,创建,一个Fsm监听Gameobject上的所有Fsm
                fsmListen = fsmStateAction.Fsm.GameObject.AddComponent<FsmListen>();
            }
            //没有对应的fsm,加入fsm且把当前状态,当前Action Name缓存
            fsmListen.fsmCacheDic.TryGetValue(fsmStateAction.Fsm, out var fsmCache);
            if (fsmCache == null)
            {
                var fsmCacheValue = new FsmCache();
                fsmCacheValue.stateDic.Add(fsmStateAction, null);
                fsmListen.fsmCacheDic.Add(fsmStateAction.Fsm, fsmCacheValue);
            }

            return fsmListen;
        }
    }
}

最后,在Action的结构中还能做文章,因为我们能比较自由的扩展Action,我们将FsmListen 添加到Action里
最终效果:Playmaker 播放Cutscene,互相不会干扰(动作融合需状态间传递动画)
在这里插入图片描述

在Action每次结束调用设置下一个状态,加入属性上一个Fsm状态

public class FsmStateActionBase : FsmStateAction
{
    public FsmState LastFsmState
    {
        get
        {
            return this.GetLastFsmState();
        }
    }

    public override sealed void OnExit()
    {   //需要向下一个状态state 传递当前cutscene动画
        this.SetLastFsmState();
        Exit();
    }

    public virtual void Exit()
    {
    }
}

经过三篇Playmaker的使用与总结,
playmaker对于个人制作来说优势很大,劣势也很明显,无源码。
看官网说明,源码要发邮件联系,可能要单独购买,比较麻烦,如果免费给源码,playmaker将完美,对于开发者,可以更加针对项目需求进行定制改造。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值