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

news2025/1/11 10:15:06

学习目标:

如果你正在学习使用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原生配的行为脚本,学习仿造它的脚本结构,并结合自己的想法,这样才能写出全面的行为扩展来。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/21453.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CSS的元素显示模式和CSS的背景

&#x1f353;个人主页&#xff1a;bit.. &#x1f352;系列专栏&#xff1a;Linux(Ubuntu)入门必看 C语言刷题 数据结构与算法 HTML和CSS3 目录 一.CSS的元素显示模式 1.1什么是元素的显示模式 1.2块元素 1.3行内元素 1.4 行内块元素 1.5元素显示模式总结 1.6…

JavaEE——HttpServletRequest

HttpServletRequest 核心方法 方法功能String getProtocol()返回请求协议的名称和版本。String getMethod()返回请求的 HTTP 方法的名称String getRequestURI()从协议名称直到 HTTP 请求的第一行的查询字符串中&#xff0c;返回该请求的 URL 的一部分。String getContextPath…

DolphinScheduler3.1简介、部署、功能介绍以及架构设计

DolphinScheduler3.1简介、部署、功能介绍以及架构设计1.DolphinScheduler简介 1-1.关于DolphinScheduler Apache DolphinScheduler 是一个分布式易扩展的可视化DAG工作流任务调度开源系统。适用于企业级场景&#xff0c;提供了一个可视化操作任务、工作流和全生命周期数据处…

Day08--自定义组件的数据监听器案例

1.案例效果&#xff1a; 我的操作&#xff1a; 1》新建一个组件test4 2》在app.json里面将test4设置为全局组件 3》在home.wxml里面是用这个test4组件。 4》在test4.js中编写代码&#xff1a;【需要的配置项都弄一下呗。】 *********************************************…

anaconda3报错Can‘t find libdevice directory解决方案

anaconda3报错Cant find libdevice directory解决方案1. 问题描述2. 解决方案3. 原理分析4. 其他解决方案1. 问题描述 使用anaconda3运行tensorflow进行单机多GPU运算时报错&#xff1a; error: Cant find libdevice directory ${CUDA_DIR}/nvvm/libdevice较的全报错如下&…

【Wayland】QtWayland框架分析

QtWayland框架分析 QtWayland概念介绍 QtWayland是Qt官方基于Wayland开发的一款Toolbox&#xff0c;根据其官网介绍 The QtWayland module consists of two parts: Wayland platform plugin: Enables Qt applications to be run as Wayland clients. QtWaylandCompositor API…

地理空间技术改变世界的未来

摘要: 地理空间技术是一项重大的科学发现&#xff0c;它将人类的可能性推向了一个全新的水平。那么什么是地理空间技术呢&#xff1f;事实上&#xff0c;它与普通的空间数据不同&#xff0c;地理空间技术的创新使我们能够确定物体或人在地球上的确切位置。人们将地理空间技术应…

网络安全系列-四十: suricata 的运行模型Running mode讲解

1. 什么是Running mode 1.1. 基本概念 Sruciata由线程、线程模块、队列组成。 数据包在线程间传递通过队列实现,线程由多个线程模块组成,每个线程模块实现一种功能一个数据包可以由多个线程处理,数据包将通过队列传递到下一个线程。包每次由一个线程处理,但是引擎可以同时…

docker (网卡设置、namespace、网络互通)

1 查看网卡信息 查看网卡的三种命令 ip a lo&#xff1a; 表示本地网络 127.0.0.1eth0&#xff1a; 连接网卡的网络docker0&#xff1a; docker的网卡 ip link show ls /sys/class/net 以文件的形式查看 2 网卡的操作 网卡中增加IP ip addr add 192.168.100.120/24 dev et…

springboot+jsp大学图书借阅管理系统idea maven

本论文是以构建图书借阅为目标&#xff0c;使用 jsp制作&#xff0c;由前台用户借阅图书、后台管理员添加图书两大部分组成。着重论述了系统设计分析&#xff0c;系统的实现&#xff08;用户注册模块&#xff0c;用户登录&#xff0c;用户浏览图书模块&#xff0c;图书借阅模块…

代码复杂度分析

1.复杂度分析原则 1.1 最大循环原则 只看高阶部分 public class SumNum {public static void main(String[] args) {System.out.println(sum2(4));}/*** 1-n 的累加* param n* return*/public static int sum1(int n){int sum 0;// 执行1次for (int i0;i<n;i){//这是三个…

轻量应用云服务器如何部署SpringBoot项目(jar包形式)?

这篇文章将介绍如何在腾讯云的轻量应用服务器上部署我们的java项目&#xff0c;本次演示的是java项目是使用springboot开发的单体项目&#xff0c;项目开发完成之后需要打包成jar&#xff0c;然后上传至云服务器&#xff0c;安装运行数据库文件之后&#xff0c;使用java -jar 的…

企业经营欺诈评分模型的开发与划定

近两周&#xff0c;番茄风控上线的课程中&#xff0c;涉及的财税票贷产品课程干货满满。 今天我们跟大家讲讲在模型中最干货的内容&#xff0c;也就是在税票贷产品中最干货的欺诈模型的开发与使用&#xff0c;本文会基于税票中票&#xff08;也就是发票&#xff09;相关的数据进…

双缝函数的编程

摘要 在傅立叶光学中&#xff0c;光学元件通常可以建模为传递函数&#xff0c;该函数对输入场的振幅和相位进行调制。VirtualLab Fusion提供了可编程功能&#xff0c;用户可以在其中定义元件的函数体现。 此示例提供了具有自定义缝隙宽度的双缝函数的代码段。 狭缝在y方向上是无…

CUDA 编程基础

1. GPU与CPU并行计算框架2. CUDA编程模型3 CUDA程序1. GPU与CPU并行计算框架 GPU并不是一个独立运行的计算平台&#xff0c;而需要与CPU协同工作&#xff0c;可以看成是CPU的协处理器&#xff0c;因此当我们在说GPU并行计算时&#xff0c;其实是指的基于CPUGPU的异构计算架构。…

学习日记(XML 文件解析、检索、工厂设计模式、装饰设计模式)

文章目录学习日记&#xff08;XML 文件解析、检索、工厂设计模式、装饰设计模式&#xff09;一、XML 概述1. 概念2. XML 的创建及语法规则3. XML 文档约束方式二、XML 文件的解析技术1. 使用 Dom4j 解析 XML 文件2. XML 解析案例三、XML 文件的数据检索技术&#xff1a;XPath四…

Nacos 注册中心、配置文件中心的常用配置(springcloud)

Nacos Discovery Starter 更多的配置项 1.服务端地址 spring.cloud.nacos.discovery.server-addr 无 Nacos Server 启动监听的 ip 地址和端口2.服务名 spring.cloud.nacos.discovery.s ervice ${spring.application.name} 给当前的服务命名3.服务分组spring.cloud.nacos.disc…

Nuttx系统学习笔记(三)——使用Nuttx操作STM32F429外设

在上一篇&#xff0c;我们已经学会了如何将Nuttx进行烧录&#xff0c;以及学会了如何部署这个操作系统&#xff0c;接下来我们就要使用这个操作系统来实现我们对嵌入式设备的控制&#xff0c;当然也是从点灯开始的。这个基于Posix架构的操作系统使用起来是跟FreeRTOS那些操作系…

【微信开发第五章】SpringBoot实现微信分享

前言 在进行微信公众号业务开发的时候&#xff0c;有些时候需要用到微信分享&#xff0c;以卡片的形式分享出去&#xff0c;例如订单信息&#xff0c;用户信息。该篇文章每一步都有记录&#xff0c;力争理解的同时各位小伙伴也能够实现功能 效果图如下&#xff1a; 文章目录前…

centos 7.9 部署 harbor 镜像仓库实践

centos 7.9 harbor 部署镜像仓库 文章目录centos 7.9 harbor 部署镜像仓库1. 安装 docker1.1 配置 docker2. 安装 docker-compose3. 下载 harbor4. 定制配置文件 harbor.yml5. 配置证书5.1 生成证书颁发机构证书5.2 生成服务器证书5.3 向 Harbor 和 Docker 提供证书6. 部署 har…