【Godot】数据响应的方式执行功能
创始人
2024-02-10 18:23:03
0

Godot Engine 版本:4.0 beta 6

下载地址:Index of /godotengine/4.0/beta6/ (downloads.tuxfamily.org)

在这个教程中,学会理解以数据为主的进行处理执行逻辑的代码编写方式,虽然看似简单,但是确是方便又好用。

以及下方会有一个 buff 的示例,触类旁通,你可以设计更高复杂更强大的功能

基础代码

在游戏开发中,对数据的处理那绝对是重中之重。角色的属性、游戏配置等一系列的玩家数据都要进行保存处理。有时还要根据数据的变化进行响应处理其他代码逻辑,那有没有一种比较方便的方式对数据的变化进行处理呢?那肯定是有的,要不我来干嘛(#滑稽)

算了,我也不太会说话,直接先看代码:

data_management.gd

#============================================================
#    Data Management
#============================================================
# - datetime: 2022-11-23 19:35:39
#============================================================
## 数据管理
class_name DataManagement
extends Node##  数据发生改变
##[br]
##[br][code]property[/code]  属性名
##[br][code]previous[/code]  改变前的属性的值
##[br][code]current[/code]  当前的属性值
signal property_changed(property, previous, current)var _data : Dictionary = {}
var _tmp_value#============================================================
#  SetGet
#============================================================
##  设置属性值
func set_property(property, value):_tmp_value = _data.get(property)if _tmp_value != value:_data[property] = valueproperty_changed.emit(property, _tmp_value, value)##  获取属性值
##[br]
##[br][code]default[/code]  如果没有这个属性时返回的默认值
func get_property(property, default = null):return _data.get(property, default)##  添加属性
func add_property(property, value):if value is float or value is int:set_property(property, _data.get(property, 0) + value )else:set_property(property, value)## 减去属性值
func sub_property(property, value):if value is float or value is int:set_property(property, _data.get(property, 0) - value )else:set_property(property, value)##  移除属性值
func remove_property(property):_data.erase(property)

很简单的代码,几个对属性进行增删改的操作,有个地方很关键:signal property_changed(property, previous, current),属性改变信号,对属性发生改变能够有方便判断监听的地方,好在 Godot 的信号非常方便就能实现这一功能。

使用 Dictionary 对属性进行记录管理很方便,最终通过 set_property 方法对属性的改变进行判断处理。这样,只要连接完信号,通过调用这些方法进行操作,就能判断属性何时发生了变化。

创建一个编辑器脚本进行测试

data_mana_test.gd

#============================================================
#    Data Mana Test
#============================================================
# - datetime: 2022-11-23 19:58:42
#============================================================
@tool
extends EditorScriptvar data_management = DataManagement.new()func _run():# 连接 property_changed 属性到当前对象的 _property_changed 方法上 (4.0 版本之后的新的连接方式)data_management.property_changed.connect(_property_changed)# 设置属性data_management.set_property("health", 2)# 添加属性值data_management.add_property("health", 1)func _property_changed(property, previous, current):print(property, " 属性发生改变: previous = ", previous, ", current = ", current)

点击脚本代码左上角的“文件 > 运行”菜单或者按下 ctrl + shift + x 快捷键运行当前脚本,可以看到底部“输出”面板中的如下信息

health 属性发生改变: previous = , current = 2
health 属性发生改变: previous = 2, current = 3

是的,非常简陋,但是核心就是这个,非常重要,必不可少,现在我们需要稍微增强一下这个类,比如我想知道这是否是新添加的属性数据:

#============================================================
#    Data Management
#============================================================
# - datetime: 2022-11-23 19:35:39
#============================================================
## 数据管理
class_name DataManagement
extends Node##  数据发生改变
##[br]
##[br][code]property[/code]  属性名
##[br][code]previous[/code]  改变前的属性的值
##[br][code]current[/code]  当前的属性值
signal property_changed(property, previous, current)
## 新添加属性
signal newly_added_property(property, value)
## 移除了属性
signal removed_property(property, value)var _data : Dictionary = {}
var _tmp_value#============================================================
#  SetGet
#============================================================
##  设置属性值
##  set_property
##[br]
##[br][code]force_change[/code]  强制进行修改
func set_property(property, value, force_change: bool = false):_tmp_value = _data.get(property)if _data.has(property):if _tmp_value != value or force_change:_data[property] = valueproperty_changed.emit(property, _tmp_value, value)else:_data[property] = valuenewly_added_property.emit(property, value)##  获取属性值
##[br]
##[br][code]default[/code]  如果没有这个属性时返回的默认值
func get_property(property, default = null):return _data.get(property, default)##  添加属性
func add_property(property, value):if value is float or value is int:set_property(property, _data.get(property, 0) + value )else:set_property(property, value, true)## 减去属性值
func sub_property(property, value):if value is float or value is int:set_property(property, _data.get(property, 0) - value )else:set_property(property, value, true)##  移除属性值
func remove_property(property):if _data.has(property):removed_property.emit(property, _data[property])_data.erase(property)

添加了两个新的信号:newly_added_propertyremoved_property,这样我们就可以做更多判断操作,以及部分更新,尤其是 func set_property(property, value, force_change: bool = false) 方法部分新增了 force_change 参数,这样在增加和减少非数字属性的时候也会发出信号。

修改上次的编辑器脚本,再次运行测试

#============================================================
#    Data Mana Test
#============================================================
# - datetime: 2022-11-23 19:58:42
#============================================================
@tool
extends EditorScriptvar data_management = DataManagement.new()func _run():# 连接信号当前对象的方法 (4.0 版本之后的新的连接方式)data_management.property_changed.connect(_property_changed)data_management.newly_added_property.connect(_newly_added_property)data_management.removed_property.connect(_removed_property)# 设置属性data_management.set_property("health", 2)# 添加属性值data_management.add_property("health", 1)# 移除属性data_management.remove_property("health")print("移除 health 属性后的值:", data_management.get_property("health") )func _newly_added_property(property, value):print("新增属性:", property, ", value = ", value)func _removed_property(property, value):print("移除属性:", property)func _property_changed(property, previous, current):print(property, " 属性发生改变: previous = ", previous, ", current = ", current)

运行结果:

新增属性:health, value = 2
health 属性发生改变: previous = 2, current = 3
移除属性:health
移除 health 属性后的值:

非常方便就可以对属性发生改变进行响应

简单的生命发生改变的示例

我们对这个功能进行一个实例操作演示。

创建一个场景,创建一个名称为 test_01 的文件夹保存到这个文件夹下。节点名称和类型如下,左边名称,右边括号内的是类名

|- test (Node2D)|- data_management (DataManagement,刚刚创建的那个脚本类)|- health_bar (ProgressBar)|- add (Button)|- sub (Button)

给 test 节点添加一个脚本,然后修改 add 和 sub 的 text 属性为 addsub,连接两个按钮的 pressed 信号到脚本里,调整一下几个节点的位置

在这里插入图片描述

然后就是写代码逻辑,test 代码如下

extends Node2D@onready var health_bar = $health_bar as ProgressBar
@onready var data_management = $data_management as DataManagement#============================================================
#  内置
#============================================================
func _ready():health_bar.max_value = 10data_management.property_changed.connect(_property_changed)data_management.set_property("health", 0)#============================================================
#  连接信号
#============================================================
func _property_changed(property, previous, current):if property == "health":health_bar.value = currentfunc _on_add_pressed():data_management.add_property("health", 1)func _on_sub_pressed():data_management.sub_property("health", 1)

按 F6 运行场景进行测试,点击 add 和 sub 按钮,可以看到点击之后属性发生了改变

在这里插入图片描述

但是还不完善,因为点击完会一直增加或减少,我们稍作一点限制即可,添加一个 health_max 属性,根据这个属性进行判断

extends Node2D@onready var health_bar = $health_bar as ProgressBar
@onready var data_management = $data_management as DataManagement#============================================================
#  内置
#============================================================
func _ready():data_management.newly_added_property.connect(_newly_added_property)data_management.property_changed.connect(_property_changed)data_management.set_property("health", 0)data_management.set_property("health_max", 10)#============================================================
#  连接信号
#============================================================
func _newly_added_property(property, value):if property == "health_max":health_bar.max_value = valuefunc _property_changed(property, previous, current):if property == "health":health_bar.value = currentfunc _on_add_pressed():if data_management.get_property("health") < data_management.get_property("health_max"):data_management.add_property("health", 1)func _on_sub_pressed():if data_management.get_property("health") > 0:data_management.sub_property("health", 1)

再次运行,可以不断点击,再点击减少,就可以看到超出最大值则不再增加了

不过像上面不断手动输入属性值,这样需要手动输入的值我们把它叫做“魔法值”,这种值有时候可能会因为手误输入错误,导致逻辑错误,所以我们需要给他专门做一个类存储这个属性名称

property_consts.gd

## 属性名常量
class_name PropertyConstsconst HEALTH = "health"
const HEALTH_MAX = "health_max"

将手动输入的值替换成常量

extends Node2D@onready var health_bar = $health_bar as ProgressBar
@onready var data_management = $data_management as DataManagement#============================================================
#  内置
#============================================================
func _ready():data_management.newly_added_property.connect(_newly_added_property)data_management.property_changed.connect(_property_changed)data_management.set_property(PropertyConsts.HEALTH, 0)data_management.set_property(PropertyConsts.HEALTH_MAX, 10)#============================================================
#  连接信号
#============================================================
func _newly_added_property(property, value):if property == PropertyConsts.HEALTH_MAX:health_bar.max_value = valuefunc _property_changed(property, previous, current):if property == PropertyConsts.HEALTH:health_bar.value = currentfunc _on_add_pressed():if data_management.get_property(PropertyConsts.HEALTH) < data_management.get_property(PropertyConsts.HEALTH_MAX):data_management.add_property(PropertyConsts.HEALTH, 1)func _on_sub_pressed():if data_management.get_property(PropertyConsts.HEALTH) > 0:data_management.sub_property(PropertyConsts.HEALTH, 1)

可以看到完全没有手动输入的值了,当然这里不仅仅只适用于这个地方,魔法值,其他数据的改变都可以通过次方式进行

Buff 功能示例

上面的生命改变功能还是比较简单的,这里我们可以进阶做个比较高级点的功能,通过数据属性新增移除进行增加 buff 的功能,其实游戏就是这样,关键在于对数据的操控

创建一个测试场景 test,保存到一个文件夹名为 test_02 的文件夹下,场景节点结构如下

|- test (Node2D)|- data_management (DataManagement)|- fire_buff (Node,给这个节点添加下面的 fire_buff.gd 脚本)|- add_buff (Button)

我们创建一个 FireBuff 脚本,意为火焰buff

fire_buff.gd

#============================================================
#    Fire Buff
#============================================================
# - datetime: 2022-11-23 21:26:51
#============================================================
##火焰魔法buff
##[br]
##[br]放到 DataManagement 节点下边自动连接信号,每次添加属性值和下方的 FireBuff.NAME 的值一样的
##时候会自动添加这个buff
class_name FireBuff
extends Node@onready 
var data_management := get_parent() as DataManagement# 叠加的伤害数据
var _damage_data : Array[Dictionary] = []#============================================================
#  常量
#============================================================
## 当前 Buff 名称常量
const NAME = "火焰魔法BUFF"## Buff 参数值常量
class BuffParamConsts:const DURATION = "duration"const DAMAGE = "damage"#============================================================
#  内置
#============================================================
func _ready():if data_management != null:data_management.newly_added_property.connect(_newly_added_property)data_management.property_changed.connect(_property_changed)data_management.removed_property.connect(_removed_property)else:printerr("父节点不是 DataManager 类型的节点!")#============================================================
#  自定义
#============================================================
# 根据添加的数据创建计时器
func _create_timer_by_data(data: Dictionary):var timer := Timer.new()add_child(timer)timer.start(data[BuffParamConsts.DURATION])timer.timeout.connect(_timer_timeout.bind(data))# 结束删除这个计时器timer.timeout.connect(timer.queue_free)_damage_data.append(data)#============================================================
#  连接信号
#============================================================
func _newly_added_property(property, value):if property == FireBuff.NAME:print("-- 新添加buff: ", property)set_physics_process(true)_create_timer_by_data(value)func _property_changed(property, previous, current):# 开始叠加火焰魔法 buffif property == FireBuff.NAME:print("-- 开始叠加 ", property, " 属性")# 额外增加上个 buff 持续时间的一半时间current[BuffParamConsts.DURATION] += previous[BuffParamConsts.DURATION] * 0.5# 额外增加上个 buff 伤害的一半伤害current[BuffParamConsts.DAMAGE] += previous[BuffParamConsts.DAMAGE] * 0.5_create_timer_by_data(current)func _removed_property(property, value):if property == FireBuff.NAME:set_physics_process(false)func _timer_timeout(data):print("倒计时结束,造成伤害:", data[BuffParamConsts.DAMAGE])# 结束时造成伤害data_management.sub_property(PropertyConsts.HEALTH, data[BuffParamConsts.DAMAGE])# 到达时间减去叠加的火焰buff数据,如果没有了,则移除buff状态_damage_data.erase(data)if _damage_data.is_empty():self.data_management.remove_property(NAME)print("-- 没有叠加数据了,移除buff")

给场景根节点 test 添加一个脚本,修改 add_buff 的 text 属性为 Add buff 并连接这个按钮的 pressed 属性到 test 脚本里

在这里插入图片描述

test 脚本里的代码如下

extends Node2D@onready var data_management = $data_management as DataManagement#============================================================
#  内置
#============================================================
func _ready():data_management.set_property(PropertyConsts.HEALTH, 10)# 连接属性发生改变信号data_management.property_changed.connect(_property_changed)#============================================================
#  连接信号
#============================================================
func _on_add_buff_pressed():# 火焰魔法BUFF 数据data_management.add_property(FireBuff.NAME, {FireBuff.BuffParamConsts.DURATION: 1,		# 持续时间FireBuff.BuffParamConsts.DAMAGE: 1,			# 造成伤害})func _property_changed(property, previous, current):if property == PropertyConsts.HEALTH:print("> 生命值发生改变,当前生命值:", current)

点击 在这里插入图片描述
运行按钮或按 F6 运行场景

然后点击 Add buff 按钮开始添加 buff,比如我快速点击了 4 次,输出如下结果:

-- 新添加buff: 火焰魔法BUFF
-- 开始叠加 火焰魔法BUFF 属性
-- 开始叠加 火焰魔法BUFF 属性
-- 开始叠加 火焰魔法BUFF 属性
倒计时结束,造成伤害:1
> 生命值发生改变,当前生命值:9
倒计时结束,造成伤害:1.5
> 生命值发生改变,当前生命值:7.5
倒计时结束,造成伤害:1.75
> 生命值发生改变,当前生命值:5.75
倒计时结束,造成伤害:1.875
> 生命值发生改变,当前生命值:3.875
-- 没有叠加数据了,移除buff

由此你也可以写出其他的 buff 功能,添加到 这个 DataManagement 节点下面,然后以此类推,添加技能功能等等(当然设计通用技能节点又是一个不小的挑战),看看能否触类旁通做个简单的。

相关内容

热门资讯

描写小庭院优美的句子精选18... 描写小庭院优美的句子 精选66句1. 游人如果到当地农家作客,通常都会受到热情的款待,品尝酥油茶,喝...
让男人看了内疚的句子精选31... 让男人看了内疚的句子 精选53句1. 我不是冷血,更不是慢热。我只是害怕,投入太多,离开的时候会难过...
自己内心矛盾的句子精选181... 自己内心矛盾的句子 精选104句1. 如果,最后在身边的真的不是你。如果你经历了那么多的起起落落,最...
赞美校园保洁的句子精选115... 赞美校园保洁的句子 精选102句1. 你们用汗水与辛劳挥舞着手中笨拙的扫帚,给校园一个整洁的容貌,给...
环境描写死气沉沉句子精选98... 环境描写死气沉沉句子 精选69句1. 教室中死气沉沉,同学们个个都泪流满面,惟有几位同学装作一脸苦笑...
一生能遇到的句子精选420句 一生能遇到的句子 精选63句1. 选择你所爱的,然后爱你所选择的。2. 你的温柔,我懂,你的疼爱,我...
诚信的句子 有关诚信的句子大全  诚信是一种美德,会让你更加完美。下面是小编整理的有关诚信的句子大全,欢迎阅读!...
时间过得快的搞笑句子精选26... 时间过得快的搞笑句子 精选132句1. 我们不可能都成为英雄。2. 要找出时间来考虑一下,一天中做了...
你好六月的优美句子 你好六月的优美句子(精选100句)  在学习、工作或生活中,大家都听说过或者使用过一些比较经典的句子...
怀念好句子大全要短的精选38... 怀念好句子大全要短的 精选35句1. 小学同学聚会能聚这么多人真的不容易,好怀念以前小的时候现在大家...
有哲理的唯美句子精选76句 有哲理的唯美句子 精选50句1. 池塘边的榕树上,还有知了在声声叫着;家门口的小路旁,还有小狗在快乐...
自我独特的个性签名 自我独特的个性签名(精选70句)  不管现实多么惨不忍睹,都要持之以恒地相信,这只是黎明前短暂的黑暗...
人类破坏环境污染句子精选30... 人类破坏环境污染句子 精选64句1. 排放的气息,是乌云盖天的狂欢;森林的骤减,是沙漠扩展的心愿;灾...
繁体字情侣个性签名   繁体字情侣个性签名  1、討厭自己想刺猬一樣小心防備。討厭自己想小丑一樣假冒開心。  2、如果決...
抖音名字 抖音名字▼※目录※▼抖音名字(1-100个)抖音名字(101-200个)抖音名字(201-300个)...
爱情的经典个性签名 关于爱情的经典个性签名集锦  1、其实只要两个人幸福就好了,何必在乎别人的眼光和议论。  2、距离让...
女生爱情个性签名 女生爱情个性签名  永远都不好停止微笑,即使是在你难过的时候,说不定有人会正因你的笑容而爱上你。以下...
微信名字最好听的昵称(精选5... 微信名字最好听的昵称(精选500个)  一、什么是网名  网名指在网上使用的名字。由于网络是一个虚拟...
塘尾村 塘尾村广东省东莞市石排镇塘尾村塘尾村(广东省东莞市石排镇塘尾村)塘尾村位于东莞市石排内,古村以古围墙...
父亲节个性签名 父亲节个性签名  引导语:我不害怕前途再多挫折挣扎只是愧疚没能在家给你们报答。下面由yjbys小编与...