• 【Godot】时间线(技能)节点


    4.1

    游戏中一般都会有各种各样的技能,或者其他需要按一定的时间顺序去执行的功能。

    这里我写出了一个时间线节点,就像是在播放动画一样,按一定的阶段去执行某些功能

    #============================================================
    #    Timeline
    #============================================================
    # - author: zhangxuetu
    # - datetime: 2023-09-24 23:10:53
    # - version: 4.1
    #============================================================
    class_name _TimeLine
    extends Node
    
    
    ## 执行完这个阶段时发出这个信号
    signal executed_stage(stage, data)
    ## 手动停止执行
    signal stopped
    ## 暂停执行
    signal paused
    ## 继续执行
    signal resumed
    ## 执行完成
    signal finished
    
    
    ## process 执行方式
    enum ProcessExecuteMode {
    	PROCESS, ## _process 执行
    	PHYSICS, ## _physics_process 执行
    }
    
    enum {
    	UNEXECUTED, ## 未执行
    	EXECUTING, ## 执行中
    	PAUSED, ## 暂停中
    }
    
    
    ## 时间阶段名称。这关系到 [method execute] 方法中的数据获取的时间数据
    @export var stages : Array = []
    ## process 执行方式。如果设置为 [member PROCESS][member PHYSICS] 以外的值,
    ## 则当前节点的线程将不会执行
    @export var process_execute_mode : ProcessExecuteMode = ProcessExecuteMode.PROCESS
    
    
    var _last_data : Dictionary
    var _point : int = -1:
    	set(v):
    		if _point != v:
    			_point = v
    			if _point >= 0 and _point < stages.size():
    				self.executed_stage.emit(stages[_point], _last_data)
    var _time : float
    var _execute_state : int = UNEXECUTED:
    	set(v):
    		if _execute_state == v:
    			return
    
    		_execute_state = v
    		match _execute_state:
    			UNEXECUTED:
    				set_process(false)
    				set_physics_process(false)
    			EXECUTING:
    				if process_execute_mode == ProcessExecuteMode.PROCESS:
    					set_process(true)
    				elif process_execute_mode == ProcessExecuteMode.PHYSICS:
    					set_physics_process(true)
    			PAUSED:
    				set_process(false)
    				set_physics_process(false)
    
    
    func _ready():
    	set_process(false)
    	set_physics_process(false)
    
    func _process(delta):
    	_exec(delta)
    
    func _physics_process(delta):
    	_exec(delta)
    
    func _exec(delta):
    	_time -= delta
    	while _time <= 0:
    		_point += 1
    		if _point < stages.size():
    			_time += _last_data[stages[_point]]
    		else:
    			_point = -1
    			_execute_state = UNEXECUTED
    			self.finished.emit()
    			break
    
    func get_time_left():
    	return _time
    
    func get_last_data() -> Dictionary:
    	return _last_data
    
    func get_last_stage():
    	return stages[_point]
    
    ## 执行功能。这个数据里需要有 [member stages] 中的 key 的数据,且需要是 [int][float]
    ## 类型作为判断执行的时间。否则默认时间为 0
    func execute(data: Dictionary):
    	_last_data = data
    	_time = 0
    	_point = 0
    	if not stages.is_empty():
    		_execute_state = EXECUTING
    		for stage in stages:
    			_last_data[stage] = float(data.get(stage, 0))
    		# 执行时会先执行一下
    		_time = _last_data[stages[0]]
    		_exec(0)
    
    	else:
    		printerr("没有设置 stages,必须要设置每个执行的阶段的 key 值!")
    
    ## 获取执行状态
    func get_execute_state():
    	return _execute_state
    
    ## 是否正在执行
    func is_executing():
    	return _execute_state == EXECUTING
    
    ## 停止执行
    func stop():
    	if _execute_state == EXECUTING:
    		_execute_state = UNEXECUTED
    		self.stopped.emit()
    
    ## 暂停执行
    func pause():
    	if _execute_state == EXECUTING:
    		_execute_state = PAUSED
    
    ## 恢复执行
    func resume():
    	if _execute_state == PAUSED:
    		_execute_state = EXECUTING
    		self.resumed.emit()
    
    ## 跳跃到这个阶段
    func goto(stage, emit_stage_changed_signal: bool = true):
    	if _execute_state == EXECUTING:
    		if stages.has(stage):
    			_point = stage
    			_time = 0
    			if emit_stage_changed_signal:
    				self.executed_stage.emit()
    		else:
    			printerr("stages 中没有 ", stage, ". 所有 stage: ", stages)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154

    这里我进行添加整个时间中有哪些阶段,我想一个技能需要有下面几个阶段:

    start(开始执行功能,一般用于判断这个条件下是否可以进行执行这个功能,以便进行功能的打断等操作);before 执行功能时的施放动作前摇;execute 具体执行出的功能;after 执行功能后的动画结束;refresh 技能刷新阶段。

    这是我的理解,可以自行随意设计。

    下面的代码我创建了只有一个 Node2D 作为根节点的场景,进行测试

    #============================================================
    #    Time Line Test
    #============================================================
    # - author: zhangxuetu
    # - datetime: 2023-09-24 21:51:11
    # - version: 4.1
    #============================================================
    extends Node2D
    
    
    var timeline = _TimeLine.new()
    
    func _ready():
    	timeline.stages = [&"start", &"before", &"execute", &"after", &"refresh"]
    	add_child(timeline)
    
    	timeline.executed_stage.connect(_executed_stage)
    
    	timeline.execute({
    		"name": "技能名称",
    		&"start": 0,
    		&"before": 0.4,
    		&"execute": 0.2,
    		&"after": 0.2,
    		&"refresh": 3,
    	})
    
    
    func _executed_stage(stage, data):
    	match stage:
    		&"start":
    			print("   开始执行: ", data["name"])
    		&"before":
    			print("   播放动作")
    			# 临时修改执行时间,延长或缩短时间
    			data[&"execute"] = 2
    		&"execute":
    			print("   实际执行功能,这里写这个阶段要做的事情")
    		&"after":
    			print("   已经执行完功能,进行后续结束动作")
    		&"refresh":
    			print("   ", data["name"], " 结束动作完成,开始进行倒计时刷新")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    这里我在执行这个技能的时候,传入上面各个阶段所需要的时间的数据,然后在 executed_stage 信号中进行判断各个阶段所需要处理的功能。

    这样一个技能节点即可完成,使用时调用 execute 方法即可

  • 相关阅读:
    【附源码】计算机毕业设计SSM社区养老院管理系统
    HTML5七夕情人节表白代码 (动态3D相册) HTML+CSS+JS
    Gson - 一个Java序列化/反序列化库
    wireshark提取视频数据之RTP包中提取H264和H265
    05-前端基础CSS第三天
    Spring Boot 整合 Redis,使用 RedisTemplate 客户端
    现代_复习_第3章:向量
    ABB变频器使用PROFINET IO通信协议时的输入和输出介绍
    api安全测试的学习
    van-popup滑动卡顿并且在有时候在ios上经常性滑动卡顿的情况
  • 原文地址:https://blog.csdn.net/qq_37280924/article/details/133638807