# FunctionTree **Repository Path**: LaoDie1/function-tree ## Basic Information - **Project Name**: FunctionTree - **Description**: Godot 的行为功能相关插件 - **Primary Language**: 其他 - **License**: MIT - **Default Branch**: master - **Homepage**: https://gitee.com/LaoDie1/function-tree - **GVP Project**: No ## Statistics - **Stars**: 4 - **Forks**: 2 - **Created**: 2021-12-15 - **Last Updated**: 2022-06-20 ## Categories & Tags **Categories**: Uncategorized **Tags**: GDscript, Godot ## README # Godot 功能树 > 项目地址:[Apprentice/FunctionTree (gitee.com)](https://gitee.com/LaoDie1/function-tree) 使用 FunctionTree 快速开发角色功能 ## 示例教程 > *在 test 文件夹中有个 test01 示例,可进入参考查看* ### 添加基本节点 添加一个 `KinematicBody2D` 节点,选中点击![image-20211218102323185](https://gitee.com/LaoDie1/function-tree/raw/master/readme/image-20211218102323185.png)添加节点,搜索 `FunctionRoot` 点击添加。添加后选中 `FunctionRoot` ,右侧将会出现一列可添加的节点列表,双击添加 ~~Blackboard~~(已更改为 `Reference` 类型,下方请忽略这个节点)、`Standard`、`Custom` 节点,如下图所示 image-20211217154403609![image-20211217154627633](https://gitee.com/LaoDie1/function-tree/raw/master/readme/image-20211217154627633.png) 场景根节点更名为 `Player`,创建一个 **player** 文件夹,保存场景到里面。 image-20211217155013008 ### 添加移动功能 选中 `Standard` 节点,添加 `Actions` 节点,然后选中 `Actions` 节点,双击添加右侧节点列表中的 `Move` 节点 image-20211217155152539image-20211217155323059 选中 `Custom` 节点,添加 `Controllers` 节点,然后选中 `Controllers` 节点,双击添加 `CustomFunction` 节点 image-20211217160027116image-20211217155947332 双击场景树中的 `CustomFunction` 节点,重命名为 `CMove`,意为 Control Move(控制移动),然后在编辑器最右侧检查器面板中给这个脚本扩展一下 image-20211217160240822image-20211217160312606image-20211217160434998 在代码编辑视图中按下 `Ctrl + Alt + Shift + S` ,或者点击代码编辑上的**代码**菜单(这个是我自己的一个脚本插件添加上的,默认没有这个),如下图,然后弹出重写函数弹窗,勾选 `_process_input` 方法,点击 OK 按钮 image-20211217160556680image-20211217160706498 生成如下代码: image-20211217160813976 ```swift extends "res://addons/function_tree/src/custom/CustomFunction.gd" #(override) func _process_input(arg0): ._process_input(arg0) pass ``` 重写 `_process_input` ,获取用户输入,根据玩家按下的键盘控制节点移动 ```swift extends "res://addons/function_tree/src/custom/CustomFunction.gd" #(override) func _process_input(arg0): # 获取用户输入(下面四个是小键盘上的按键,按下进行控制)(这个是Godot3.4中的方法) var dir = Input.get_vector("ui_left","ui_right","ui_up","ui_down") # 获取 Move 功能节点,操控其移动 get_function("Move").control(dir) ``` 我们将文件系统中 **icon.png** ,Godot 小图标拖拽到场景中 image-20211217161419330 还差一点,给场景根节点 Player 添加一个脚本,并写入如下代码 ```swift extends KinematicBody2D export var move_speed : int = 300 onready var root = $FunctionRoot func _ready(): # 设置移动速度 root.get_property().move_speed = move_speed ``` 让我们按下 F6 ,进行运行当前场景,按下小键盘进行移动 image-20211217161926811 ### 添加旋转功能 选中场景树中的 `Actions` 节点,双击添加 `TurnTo` 节点,选中 `Controllers` 节点,双击右侧 `CustomFunction` 添加节点,并重命名为 `CTurnTo`,选中这个 `CTurnTo` 节点,在编辑器最右侧检查器面板中进行扩展脚本 image-20211217162054356![image-20211217163151935](https://gitee.com/LaoDie1/function-tree/raw/master/readme/image-20211217163151935.png)image-20211217163248410 在代码编辑视图中按下 `Ctrl + Alt + Shift + S` ,或者点击代码编辑上的**代码**菜单,弹出窗口中勾选 `_process_input` ,点击 OK,写入控制旋转代码,让节点随着鼠标位置进行旋转,脚本代码如下 ```swift extends "res://addons/function_tree/src/custom/CustomFunction.gd" #(override) func _process_input(arg0): # 获取鼠标的全局位置 var mouse_pos = (host as Node2D).get_global_mouse_position() # 旋转到鼠标位置 get_function("TurnTo").control(mouse_pos) ``` 按下 F6 运行当前场景,按小键盘让角色移动到场景中,鼠标移动一下查看是否进行了旋转 image-20211217164019273 ### 添加技能 选中 `Standard` 节点,添加 `Skills` 节点,然后选中 `Skills` 节点,双击添加右侧节点列表中的 `Sprint` 节点,选中 `Sprint` 节点,修改 `duration` 属性持续时间设为 `0.5` 秒 image-20211217164259530image-20211217164420223 添加键位映射,添加鼠标的映射,添加 `click` , image-20211217164828831image-20211217164924667 设置为鼠标左键点击 image-20211217165219027 Godot 的 Input 类获取 `click` 事件,就代表要获取鼠标点击的事件,点击 `关闭` 按钮 image-20211217165233744image-20211217165247386 选中场景树中的 `Controllers` 节点,双击右侧 `CustomFunction` 添加节点,并重命名为 `CSprint`,选中这个 `CSprint` 节点,在编辑器最右侧检查器面板中进行扩展脚本,重写 `_process_input` 方法,控制 `Sprint` 技能的使用 ,脚本代码如下 ```swift extends "res://addons/function_tree/src/custom/CustomFunction.gd" #(override) func _process_input(arg0): if Input.is_action_pressed("click"): # 获取鼠标的全局位置 var mouse_pos = (host as Node2D).get_global_mouse_position() # 操作 Sprint 方法,冲刺到鼠标位置 get_function("Sprint").control(mouse_pos) ``` 按下 F6 运行当前场景,点击鼠标,看看节点是否进行了冲刺img --- ## 横版游戏角色教程 跟上面创建角色相同,创建如下节点(只要选中节点的父节点,场景树旁的面板会显示出其可选子节点),先 Ctrl + A * FunctionRoot * Blackboard * Standard * Actions * PlatformMove(平台游戏移动) * Gravity(重力) * Jump(跳跃) * Custom * Controllers 选中 `Controllers` 节点,双击添加 `CustomFunction`,重命名为 `CMove`,并扩展脚本,脚本代码如下: ```swift extends "res://addons/function_tree/src/custom/CustomFunction.gd" #(override) func _process_input(arg0): var d = Input.get_action_strength("ui_right") - Input.get_action_strength("ui_left") if d: get_function("Move").control(Vector2(d, 0)) ``` 然后选中场景根节点,添加 `CollisionShape2D` 节点,再将 `icon.png` 图标拖拽到场景中,给 `CollisionShape2D` 节点添加一个 `RectangleShape2D` 碰撞形状,设置成添加的 icon 图像大小。 场景节点如下: image-20211217201319275 现在给 Player 节点添加脚本,脚本代码如下 ```swift extends KinematicBody2D export var move_speed : int = 300 export (float, 0, 1.0) var gravity : float = 0.7 export var max_gravity : int = 2000 export var jump_height : int = 700 onready var root = $FunctionRoot func _ready(): root.property.move_speed = move_speed root.property.gravity = gravity root.property.max_gravity = max_gravity root.property.jump_height = jump_height ``` 可以将角色移动到场景中间,现在按下 F6 运行当前场景,就可以看到角色一直下坠 再在上面添加一个 `StaticBody2D` ,给 `StaticBody2D` 添加一个 `CollisionShape2D`,`CollisionShape2D` 设置一个 `RectangleShape2D`,调整其大小 image-20211217202117987 然后按 F6 运行当前场景,按下小键盘左右键,可以进行左右移动 现在给他添加跳跃控制,点击 `Controllers` 节点,场景树节点右侧面板双击添加一个 `CustomFunction` 节点,重命名为 `CJump`,并扩展脚本,脚本代码如下: ```swift extends "res://addons/function_tree/src/custom/CustomFunction.gd" #(override) func _process_input(arg0): if (Input.is_action_pressed("ui_up") && host.is_on_floor() ): get_function("Jump").control() ``` 最基本的横版游戏角色的功能就做出来了,其中还有爬墙,多级跳跃等功能,快上手试试吧 ;) --- 经过这个简单的教程之后,不知你对这个插件是否有了兴趣?现在我将介绍一下这个插件的一个制作思路。**分为 Standard 类的节点和 Custom 类的节点。将通用功能放在 Standard 类下的节点,将各种各样的逻辑和操作写在 Custom 类的节点下,在 Custom 下的节点中调用 Standard 下的节点的功能**。如此进行分类。如下流程图所示 ```mermaid graph TD Root(根节点)-->Standard(标准节点) Root(根节点)-->Custom(自定义节点) Standard-->Layer1(标准节点层1) Standard-->Layer...(标准节点层...) Custom-->CustomLayer(自定义节点层...) Layer1-->Function1(标准功能子节点...) Layer1-->Function2(标准功能子节点...) CustomLayer-->CustomFunction(自定义功能子节点...) CustomLayer-->CustomFunction1(自定义功能子节点...) ``` ## 插件介绍 ### 方法和属性 `blackboard` 属性或 `get_blackboard()` 方法获取黑板,黑板中记录有整个功能树的全局数据 `property` 属性或 `get_property()` 方法获取全局属性 `enabled` 属性或 `get_enabled()` 方法获取全局功能可用性属性 注册为功能节点:`register_function(名称)`,注册后可使用 `get_function` 方法进行获取这个节点 获取功能层节点:`get_layer(注册的功能节点名)` 获取功能节点:`get_function(注册的功能节点名)` 执行功能节点的功能:`get_function(注册的功能节点名).control(传入的数据)` ### 节点 不要动态的增减节点,因为场景运行后 FunctionRoot 根节点会扫描一遍子节点并记录,子节点也会注册到数据中,增减节点会出现问题。 有两个类别节点:Standard、Custom。 Standard 下的节点是标准节点,不进行扩展脚本,只修改属性连接信号,Custom 下的节点是自定义节点,必须要进行扩展脚本才有功能。这两个类别节点下是各个功能层节点,用以对功能进行划分。 #### FunctionRoot `FunctionRoot` 功能树根节点 `_ready()` 时会先遍历得到所有子功能节点,然后给这些节点设置基本属性,调用节点的初始化方法,执行步骤如下 * `_init_data` > `_init_node` > `_init_finished` 子节点的 `_process` 和 `_physics_process` 都是 false,不会执行,需要重写 `_process_input` 、`_process_execute`、`_process_finish` 方法进行使用,执行顺序为 * `_process_input` > `_process_execute` > `_process_finish` - `_process_input` 先接收用户输入 - `_process_execute` 进行执行所有功能 - `_process_finish` 对执行完成后进行一些处理 #### Standard 与 Custom 下的节点的区别 Standard 类下方的子节点尽量都不要重写(如果你了解这个节点的执行逻辑的话,也可以扩展脚本,但不建议这样做) Custom 类下方的子节点都要进行重写才能使用其功能,否则其只有逻辑,没有功能没有效果 Standard 下的节点提供功能,节点只进行属性的设置,增加功能节点;在 Custom 下的节点中进行扩展脚本调用 Standard 下的节点的功能。 #### Layer 节点 每个 Layer 都是一个功能类别的分类,Actions 是动作层,Skills 是技能层等等,每个层下面都是一个具体功能的节点。使用 `register_layer` 方法注册层节点到全局数据中 #### Function 节点 执行具体的功能逻辑,调用这个节点的 `control` 方法进行使用这个节点的功能。使用 `register_function` 方法注册层节点到全局数据中。