[Unity好插件之PlayMaker]PlayMaker如何扩展额外创建更多的脚本
创始人
2024-04-10 22:07:06
0

学习目标:

如果你正在学习使用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{[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();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();if (componet == null){Debug.Log(" could not find a GameManager on this object!");return;}componet.DecrementDataInt(intName.Value);base.Finish();}}
}

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


学习产出:

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

相关内容

热门资讯

监控摄像头接入GB28181平... 流程简介将监控摄像头的视频在网站和APP中直播,要解决的几个问题是:1&...
Windows10添加群晖磁盘... 在使用群晖NAS时,我们需要通过本地映射的方式把NAS映射成本地的一块磁盘使用。 通过...
protocol buffer... 目录 目录 什么是protocol buffer 1.protobuf 1.1安装  1.2使用...
在Word、WPS中插入AxM... 引言 我最近需要写一些文章,在排版时发现AxMath插入的公式竟然会导致行间距异常&#...
Fluent中创建监测点 1 概述某些仿真问题,需要创建监测点,用于获取空间定点的数据࿰...
educoder数据结构与算法...                                                   ...
MySQL下载和安装(Wind... 前言:刚换了一台电脑,里面所有东西都需要重新配置,习惯了所...
MFC文件操作  MFC提供了一个文件操作的基类CFile,这个类提供了一个没有缓存的二进制格式的磁盘...
有效的括号 一、题目 给定一个只包括 '(',')','{','}'...
【PdgCntEditor】解... 一、问题背景 大部分的图书对应的PDF,目录中的页码并非PDF中直接索引的页码...