[Unity好插件之PlayMaker]PlayMaker如何扩展额外创建更多的脚本

2023-10-27

学习目标:

如果你正在学习使用PlayMaker的话,那么本篇文章将非常的适用。关于如何连线则是你自己的想法。本篇侧重于扩展适用更多的PlayMaker行为Action,那么什么是PlayMaker行为Action呢?

就是这个列表。当我们要给PlayMaker行为树的每一个状态state添加行为的时候,就是在这个行为菜单上找到你想要的行为并添加上去。

那既然有这么多的行为我们为什么还要扩展嗯?那当然是不是所有的行为都能满足要求的,比如你可能希望材质的颜色能线性插值变化,或者控制动画的播放,更为精确的检测地面碰撞体。而有些插件会自带与PlayMaker有关的扩展行为脚本。比如我正在使用的2D Toolkit插件,就有与PlayMaker合作使用的资源包,可以更好的在PlayMaker行为树上控制动画的播放。当然不是什么都和PlayMaker合作的,自然就需要自己造轮子了。


 

学习内容:

 我们将从最简单的扩展脚本。

比如我觉得行为菜单中的IntAdd不适合我,我就可以创建一个新的脚本IntAddV2.内容如下

using System;

namespace HutongGames.PlayMaker.Actions
{
    [ActionCategory(ActionCategory.Math)]
    [Tooltip("Adds a value to an Integer Variable. Uses FixedUpdate")]
    public class IntAddV2 : FsmStateAction
    {
        [RequiredField]
        [UIHint(UIHint.Variable)]
        public FsmInt intVariable;

        [RequiredField]
        public FsmInt add;

        public bool everyFrame;

        public override void Reset()
        {
            intVariable = null;
            add = null;
            everyFrame = false;
        }

        public override void OnPreprocess()
        {
            Fsm.HandleFixedUpdate = true;
        }

        public override void OnEnter()
        {
            intVariable.Value += add.Value;
            if (!everyFrame)
            {
                base.Finish();
            }
        }

        public override void OnFixedUpdate()
        {
            intVariable.Value += add.Value;
        }
    }
}

如脚本内容可见,我们要想在行为菜单上找到这个需要将其归纳进命名空间 
namespace HutongGames.PlayMaker.Actions

还需要让它继承FsmStateAction来使用 这个类的回调函数

特性[ActionCategory(ActionCategory.Math)] 则是标记你这个扩展脚本在行为菜单的类型,比如我这个是数学型,我们就可以在这里找到它

 [Tooltip("")]则是提示这个行为脚本的功能用途作用,可以看到我们点击的时候下面就会显示

你可能已经注意会有一个重载了基类的override方法,这些就类似于继承MonoBehaviour的Awake,Start,Update等的回调函数,在行为树运行到这个行为的时候它们就会相应的被调用。

而Reset()同样也是对应的,你需要在一开始的时候重置你设置的变量

 

当你需要使用Variable中的变量时,就需要使用PlayMaker中的变量,即Fsm+基础数据类型,

例如本案例中的FsmInt,其他还有什么FsmBool,最后一个变量public bool everyFrame;也是非常常见的。这决定你是否需要每帧循环这些这个动作Action,我们在OnEnter()中需要判断是否是开启everyFrame ,base.Finish()表示结束这个回调。

在保存好脚本后回到我们的PlayMaker编辑器,在一个状态上添加这个IntAddV2

可以看到添加的特性【RequireField】会要求你添加某个类型的字段,

而[UIHint(UIHint.Variable)]特性则会有最右边两条杠提示你要使用变量,如果你强制使用变量则需要在这个变量上添加这两个特性。

 

看完最简单我再来分享几个其它类型的行为脚本扩展吧

其它扩展:

我们再为敌人写一个判断脚本,就写一个判断敌人和玩家的方向吧

using System;

namespace HutongGames.PlayMaker.Actions
{
    [ActionCategory("Enemy")]
    [Tooltip("Check whether target wher to object")]
    public class CheckTargetDirection : FsmStateAction
    {
        [RequiredField]
        public FsmOwnerDefault gameObject;

        [RequiredField]
        public FsmGameObject target;

        public FsmEvent aboveEvent;
        public FsmEvent belowEvent;
        public FsmEvent rightEvent;
        public FsmEvent leftEvent;

        [UIHint(UIHint.Variable)]
        public FsmBool aboveBool;

        [UIHint(UIHint.Variable)]
        public FsmBool belowBool;

        [UIHint(UIHint.Variable)]
        public FsmBool rightBool;

        [UIHint(UIHint.Variable)]
        public FsmBool leftBool;

        private FsmGameObject self;
        private FsmFloat x;
        private FsmFloat y;

        public bool everyFrame;

        public override void Reset()
        {
            gameObject = null;
            target = null;
            everyFrame = false;
        }

        public override void OnEnter()
        {
            self = base.Fsm.GetOwnerDefaultTarget(gameObject);
            DoCheckDirection();
            if (!everyFrame)
            {
                base.Finish();
            }
        }

        public override void OnUpdate()
        {
            DoCheckDirection();
        }

        private void DoCheckDirection()
        {
            float num = self.Value.transform.position.x;
            float num2 = self.Value.transform.position.y;
            float num3 = target.Value.transform.position.x;
            float num4 = target.Value.transform.position.y;
            if(num < num3)
            {
                base.Fsm.Event(rightEvent);
                rightBool.Value = true;
            }
            else
            {
                rightBool.Value = false;
            }
            if (num > num3)
            {
                base.Fsm.Event(leftEvent);
                leftBool.Value = true;
            }
            else
            {
                leftBool.Value = false;
            }
            if (num2 < num4)
            {
                base.Fsm.Event(aboveEvent);
                aboveBool.Value = true;
            }
            else
            {
                aboveBool.Value = false;
            }
            if (num2 > num4)
            {
                base.Fsm.Event(belowEvent);
                belowBool.Value = true;
                return;
            }
            belowBool.Value = false;
        }
    }
}

 此处我们需要了解什么是FsmOwnerDefault类型,这个是判断行为是使用与使用者本身还是特殊的游戏对象,在OnEnter函数中,我们使用base.Fsm.GetOwnerDefaultTarget(gameObject);初始化并赋值给self变量名,然后再我们的DoCheckDirection来判断。当判断条件通过时,我们需要发送相应的事件。

用FsmEvnet来创建FSM事件变量,用base.Fsm.Event() 发送事件,事件就是这个里面的,可作为行为的过渡条件

我们再写一个行为Action与碰撞物理检测有关的吧,比如控制Rigibody2d上

 

using System;
using UnityEngine;

namespace HutongGames.PlayMaker.Actions
{
    [ActionCategory(ActionCategory.Physics2D)]
    [Tooltip("Set rigidbody 2D interpolation mode to Extrapolate")]
    public class SetExtrapolate : ComponentAction<Rigidbody2D>
    {
        [RequiredField]
        [CheckForComponent(typeof(Rigidbody2D))]
        [Tooltip("This Object requires aRigibody2D componennt attached")]
        public FsmOwnerDefault gameObject;

        public override void Reset()
        {
            gameObject = null;
        }

        public override void OnEnter()
        {
            DoSetExtrapolate();
            base.Finish();
        }

        private void DoSetExtrapolate()
        {
            GameObject owenrDefaultTarget = base.Fsm.GetOwnerDefaultTarget(gameObject);
            if (base.UpdateCache(owenrDefaultTarget))
            {
                return;
            }
            base.rigidbody2d.interpolation = RigidbodyInterpolation2D.Interpolate;
        }
    }
}

可以看到我们不再继承FsmStateAction,而是更改了能直接访问Unity自带组件功能的类ComponentAction<>

同时我们还使用了        [CheckForComponent(typeof(Rigidbody2D))]特性,

它将检查你是否天剑了typeof()里面的组件类型,如果没有会有红色提示

 以及方法UpdateCache()也是判断是否找到组件的。

再来写一个控制组件SpriteRenderer是否启用的扩展行为脚本

using System;
using UnityEngine;

namespace HutongGames.PlayMaker.Actions
{
    [ActionCategory("GameObject")]
    [Tooltip("Set sprite renderer to active or inactive. Can only be one sprite renderer on object. ")]
    public class SetSpriteRenderer : FsmStateAction
    {
        [RequiredField]
        public FsmOwnerDefault gameObject;

        public FsmBool active;

        public override void Reset()
        {
            gameObject = null;
            active = false;
        }

        public override void OnEnter()
        {
            if(gameObject != null)
            {
                GameObject ownerDefaultTarget = base.Fsm.GetOwnerDefaultTarget(gameObject);
                if(ownerDefaultTarget != null)
                {
                    SpriteRenderer component = ownerDefaultTarget.GetComponent<SpriteRenderer>();
                    if(component != null)
                    {
                        component.enabled = active.Value;
                    }
                }
            }
        }
    }
}

 再来写一个逻辑相关的,比如我们可能需要同时判断多个Bool条件同时的对错,那么原生的BoolTest不能满足我们(太繁琐了),这时候就需要写一个新的BoolTestMore

using System;

namespace HutongGames.PlayMaker.Actions
{
    [ActionCategory(ActionCategory.Logic)]
    [Tooltip("set true if all the given Bool Variables are are equal to thier Bool States.")]
    public class BoolTestMore : FsmStateAction
    {
        [RequiredField]
        [UIHint(UIHint.Variable)]
        [Tooltip("This must be the number used for Bool States")]
        public FsmBool[] boolVariables;

        [RequiredField]
        [Tooltip("This must be the number used for Bool States")]
        public FsmBool[] boolStates;

        public FsmEvent trueEvent;
        public FsmEvent falseEvent;

        [UIHint(UIHint.Variable)]
        public FsmBool storeResult;

        public bool everyFrame;

        public override void Reset()
        {
            boolVariables = null;
            boolStates = null;
            trueEvent = null;
            falseEvent = null;
            storeResult = null;
            everyFrame = false;
        }

        public override void OnEnter()
        {
            DoAllTrue();
            if (everyFrame)
            {
                base.Finish();
            }
        }

        public override void OnUpdate()
        {
            DoAllTrue();
        }

        private void DoAllTrue()
        {
            if(boolVariables.Length == 0 || boolStates.Length == 0)
            {
                return;
            }
            if(boolVariables.Length != boolStates.Length)
            {
                return;
            }
            bool flag = true;
            for (int i = 0; i < boolVariables.Length; i++)
            {
                if(boolVariables[i].Value != boolStates[i].Value)
                {
                    flag = false;
                    break;
                }
            }
            storeResult.Value = flag;
            if (flag)
            {
                base.Fsm.Event(trueEvent);
                return;
            }
            base.Fsm.Event(falseEvent);
        }
    }
}

给定的两个Bool数组成员的数量要相等同时不为零才会执行判断,多考虑一点我们可能需要将判断结果保持在一个FsmBool变量上。并且当flag为true的时候我们发送trueEvent,false 的时候发送falseEvent。

最后再来写一个如何自己平常写的脚本使用,比如我们再自己的脚本上创建了一个int值,我们需要在PlayMaker达到某个条件的时候减一下值,

using System;
using UnityEngine;

namespace HutongGames.PlayMaker.Actions
{
    [ActionCategory("Data")]
    [Tooltip("Sends a Message to PlayerData to send and receive data.")]
    public class DecrementPlayerDataInt : FsmStateAction
    {
        [RequiredField]
        [Tooltip("GameManager reference)]
        public FsmOwnerDefault gameObject;

        [RequiredField]
        public FsmString intName;

        public override void Reset()
        {
            gameObject = null;
            intName = null;
        }

        public override void OnEnter()
        {
            GameObject ownerDefaultTarget = base.Fsm.GetOwnerDefaultTarget(gameObject);
            if (ownerDefaultTarget == null)
            {
                return;
            }
            GameManager componet = ownerDefaultTarget.GetComponent<GameManager>();
            if (componet == null)
            {
                Debug.Log(" could not find a GameManager on this object!");
                return;
            }
            componet.DecrementDataInt(intName.Value);
            base.Finish();
        }

    }
}

我们通过调用GameManager上的方法,实现减去我们储存的int变量的值。 


学习产出:

 就此我们实现了多个种类的扩展方法,但其实仅仅这些还不足以支撑起整个游戏的行为逻辑,而网路上有关扩展的视频文章也是屈指可数,所以想要全面学习使用,建议打开PlayMaker原生配的行为脚本,学习仿造它的脚本结构,并结合自己的想法,这样才能写出全面的行为扩展来。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

[Unity好插件之PlayMaker]PlayMaker如何扩展额外创建更多的脚本 的相关文章

随机推荐

  • 索引实现原理

    1 索引的介绍 一种数据结构 为了快速找到数据记录 索引存储在内存中 主要作用是 加快查询速度 提高数据库性能 2 数据库中索引的分类 普通索引 最基本的索引没有任何限制 唯一索引 与普通索引类似 索引列的值必须唯一 允许null 如果是组
  • 为何推荐首选enum class 而非 enum

    首先 我们比较下这两种枚举类型的表示方式上有何差异 一 异同点 enum class 官方解释 unscoped enum enum identifier type enum list scoped enum enum class stru
  • AS修改快捷键以及AS设置

    1 修改快捷键方式 2 设置代码提示忽略大小写 3 设置AS中Java代码各个部分的字体颜色
  • JCreator 使用技巧

    转自 http wenku baidu com view 32f22a287375a417866f8fcf html JCreator 使用技巧 JCreator 使用技巧 1 导语 在众多的JAVA开发环境中 我对 JCreator 情有
  • 第十天栈和队列

    栈 和队列的原理大家应该很熟悉了 队列是先进先出 栈是先进后出 首先大家要知道 栈和队列是STL C 标准库 里面的两个数据结构 接下来介绍的栈和队列也是SGI STL里面的数据结构 知道了使用版本 才知道对应的底层实现 来说一说栈 栈先进
  • mdk中error: L6047U: The size of this image exceeds

    跳出error L6047U The size of this image 192324 bytes exceeds the maximum all的原因是由于mdk未破解 亲测已解决 破解工具的链接 http www xue51 com
  • ubuntu设置pac代理

    前言 由于公司ubuntu没有对代理做有线代理去搜索资料 所以下面得内容是为了记录一下 设置代理pac代理有两种方式 可能还有其他得 1 浏览器设置自动代理配置得URL PAC 2 写入profile配置文件 1 浏览器设置自动代理配置的U
  • YOLOv5-Shufflenetv2

    YOLOv5中修改网络结构的一般步骤 models common py 在common py文件中 加入要修改的模块代码 models yolo py 在yolo py文件内的parse model函数里添加新模块的名称 models ne
  • 【100天精通Python】Day51:Python 数据分析_数据分析入门基础与Anaconda 环境搭建

    目录 1 科学计算和数据分析概述 2 数据收集和准备 2 1 数据收集 2 1 1 文件导入 2 1 2 数据库连接 2 1 3 API请求 2 1 4 网络爬虫 2 2 数据清洗 2 2 1 处理缺失值 2 2 2 去除重复值 2 2 3
  • 浪潮服务器NF5280M5配置管理口BMC的IP web界面登录 ipmi 代外【详细】

    开启服务器以后等待按del或f2 进入bios选择第五项Server Mgmt界面选择BMC Network Configuration 回车 选择BMC IPv4 Network Configuration 回车 注意 只需要配置BMC
  • MySQL——必考面试题 ①

    一 为什么要使用数据库 数据保存在内存 优点 存取速度快 缺点 数据不能永久保存 数据保存在文件 优点 数据永久保存 缺点 速度比内存操作慢 频繁的IO操作 查询数据不方便 数据保存在数据库 数据永久保存 使用SQL语句 查询方便效率高 管
  • unity生成vr效果

    这是一个谷歌的插件 GoogleVRForUnity unitypackage 谷歌插件下载地址 开始制作最简单的 VR 盒子 导入 GoogleVRForUnity unitypackage 将项目的平台设置为 Android 平台 在项
  • web前端DOM

    1 2 1 什么是DOM 文档对象模型 Document Object Model 简称DOM 是 W3C 组织推荐的处理可扩展标记语言 html或者xhtml 的标准编程接口 W3C 已经定义了一系列的 DOM 接口 通过这些 DOM 接
  • 2023.1.30日学习内容(多线程接收,发送文件)

    1 多线程接收文件 1 线程文件 public Socket socket public MyThread Socket socket this socket socket Override public void run try Stri
  • WordGo导出word(list)

    导出word文档 param userResume public String getWord BasUserResume userResume WordGo wordGo new WordGo wordGo add userResume
  • 计算机网络期中测验

    目录 一 单选题 二 填空题 三 判断题 一 单选题 1 单选题 采用全双工通信方式 数据传输的方向为 A 可以在两个方向上传输但不能同时进行 B 只能在一个方向上传输 C 可以在两个方向上同时传输 D 以上均不对 答案 C 解析 三种通信
  • 百度移动统计热力图和事件分析的坑

    埋点是这2年比较火的一项技术 友盟 极光推送 腾讯云 百度移动统计都相继开发了增加埋点的SDK 方便开发者使用 其中最为先进的是百度移动统计的无埋点技术 无埋点技术是不用开发者手动埋点的一项技术 很方便使用 对开发减少了开发量 太赞 集成步
  • jieba如何自行 split 或 join ?

    目录 jieba suggest freq 源码 split 关键运行过程解释 注意 使用此函数也有可能分不开 join 关键运行过程解释 jieba add word del word 源码 参考文献 jieba suggest freq
  • 联想拯救者R720 - i5-7300HQ/1050ti(macOS Big Sur/Windows) 双系统在 OpenCore (6.0.3)/ OCC (2.5.0)引导下的安装过程

    前言 重要 硬件列表 拯救者R720 处理器 型号 i5 7300HQ 架构 kaby lake 显卡 核显 UHD630 独显无效 忽略 主板 系列 100 Series 网卡 型号自选自购 不做陈列 声卡 批次不同 型号不同 不做陈列
  • [Unity好插件之PlayMaker]PlayMaker如何扩展额外创建更多的脚本

    学习目标 如果你正在学习使用PlayMaker的话 那么本篇文章将非常的适用 关于如何连线则是你自己的想法 本篇侧重于扩展适用更多的PlayMaker行为Action 那么什么是PlayMaker行为Action呢 就是这个列表 当我们要给