【Godot4自学手册】第三十节代码调用受伤闪白效果及组合与自由状态机有效结合

ThisarticledelvesintocodingshaderinteractionsandintegratingstatemachinestocreatehurtanddeathanimationsforenemiesinGodot.ItcoversimplementingflashingeffectsusingshadersandmanagingenemystatesthroughGDScript.
摘要由CSDN通过智能技术生成

这一节,我主要学习用代码调用shader,组合和自由状态机结合到一起来完成怪物的一些功能。其实用代码调用shader实现敌人的闪白效果是怪物里面的一个受伤状态,所以先从怪物受伤状态开始。先看一下效果:
在这里插入图片描述

一、新建怪物的受伤状态EnemyHurt

新建脚本文件保存在States->EnemyState文件夹下命名为:EnemyHurt.gd。代码如下:

extends State
class_name  EnemyHurt  #定义类
@export var enemy:CharacterBody2D #敌人,出现在该类的检查器,可拖入敌人的CharacterBody2D对象
@export var anima:AnimatedSprite2D #敌人播放动画类,出现在该类的检查器
var attack:Attack #定义攻击力,攻击力类在组合中定义
func get_Attack(att): #通过函数设置攻击力,外部可以调用
	attack = att

func  Physics_Update(delta:float):	
	enemy.velocity= Vector2()	#敌人的速度设为0
	if anima:#播放攻击动画
		anima.material["shader_parameter/Eable"]=true #开启定义的shader参数,敌人汇变成白色,也就是闪白效果
		anima.play("Idle") #播放Idle动画
		if attack:  #判断攻击力是否设置
			#设置怪物的击退效果,先计算怪物的击退方向,归一化后乘以击退的力量
			enemy.velocity =(enemy.global_position-attack.attack_position).normalized()*attack.konckback_force
		await  get_tree().create_timer(0.1).timeout #等待0.1秒
		enemy.velocity.x =0  #怪物停止击退效果
		anima.material["shader_parameter/Eable"]=false #怪物的闪白效果停止
		Transitioned.emit(self,"Idle") #发出进入Idle信号

切换到Monster场景,选择StateMachine节点单击右键选择添加子场景,在对话框中选择EnemyHurt节点,然后重名为Hurt。
请添加图片描述

二、新建怪物的死亡状态EnemyDeath

新建脚本文件保存在States->EnemyState文件夹下命名为:EnemyDeath.gd。代码如下:

extends State
class_name  EnemyDeath  #定义类
var coinpre = preload("res://Scenes/Coins.tscn")#预加载金币场景
func Enter():#进入函数,直接进入死亡状态
	death()

func death():
	#播放死亡动画
	Globals.animation_scene_obj.run_animation({
		"box":Globals.duplicate_node,
		"ani_name":"Dealth",
		"position":get_parent().global_position,
		"scale":Vector2(1,1)
	})
	for i in randi_range(1,6):#随机产生1到6个金币
		coin_spawn()
	var tween = get_tree().create_tween()#在根节点添加tween函数
	tween.tween_property(get_parent().get_parent(),"modulate:a",0.2,0.3)#设置怪物的透明度逐渐减少
	tween.tween_callback(get_parent().get_parent().queue_free)#怪物透明度达到0.2后释放怪物
func coin_spawn():#产生金币
	var coin = coinpre.instantiate() #实例化金币场景
	coin.position = get_parent().get_parent().position #设置金币位置等于怪物位置
	Globals.duplicate_node.add_child(coin)  #将金币添加到主场景的duplicate_node的节点下

切换到Monster场景,选择StateMachine节点单击右键选择添加子场景,在对话框中选择EnemyDeath节点,然后重名为Deaht。
这是敌人的状态节点目录如下:
请添加图片描述

三、修改完善状态机StateMachine代码

切换到状态机StateMachine的代码状态,添加一个状态转换函数

func _charage_currentstate(new_state_name,attack):
	#该函数两个参数,第一个表示要转换到的状态名称,第二个市怪物受到的攻击力
	var new_state = states.get(new_state_name.to_lower())#通过状态名称获取状态对象
	if !new_state:#如果状态对象不存在,立即退出
		return
	if attack:#判断攻击力是否存在,该语句表示如果攻击力存在执行
		if new_state.has_method("get_Attack"):#判断新状态是否存在get_Attack函数,该语句表示新状态含有get_Attack函数后,执行
			new_state.get_Attack(attack)#执行新状态的get_Attack函数
	new_state.Enter() #调用状态进入函数
	current_state = new_state #将当前状态设置为新状态

这时我们的状态机代码如下:

extends Node
@export var inital_state:State  #初始状态
var current_state:State  #当前状态
var states:Dictionary={}  #状态字典

func _ready():
	#完成状态字典数据
	for child in get_children():
		if child is State:
			states[child.name.to_lower()]= child
			child.Transitioned.connect(on_child_transition)  #连接信号到本页脚本
	#设置初始状态
	if inital_state:
		inital_state.Enter() #调用状态进入函数
		current_state = inital_state
	pass 
	
func _charage_currentstate(new_state_name,attack):
	#该函数两个参数,第一个表示要转换到的状态名称,第二个市怪物受到的攻击力
	var new_state = states.get(new_state_name.to_lower())#通过状态名称获取状态对象
	if !new_state:#如果状态对象不存在,立即退出
		return
	if attack:#判断攻击力是否存在,该语句表示如果攻击力存在执行
		if new_state.has_method("get_Attack"):#判断新状态是否存在get_Attack函数,该语句表示新状态含有get_Attack函数后,执行
			new_state.get_Attack(attack)#执行新状态的get_Attack函数
	new_state.Enter() #调用状态进入函数
	current_state = new_state #将当前状态设置为新状态


func _process(delta):
	#调用当前状态更新函数
	if current_state:
		current_state.Update(delta)
		
func _physics_process(delta):
	#调用当前状态物理更新函数
	if current_state:
		current_state.Physics_Update(delta)

#状态改变信号调用函数,第一个参数表示目前处于状态,也就是进入新的状态有哪个状态发起的;第二个参数表示要进行新状态的名称
func on_child_transition(state,new_state_name):
	#如果传入的状态部署当前状态,退出信号
	if state!=current_state:
		return
	#根据状态名称调出状态数据字典中对应的状态
	var new_state = states.get(new_state_name.to_lower())
	#如果状态数据字典中不存在对应的状态退出
	if !new_state:
		return
	#退出当前状态,调用状态退出函数
	if current_state:
		current_state.Exit()
	#进入新的状态,调用进入函数
	new_state.Enter()
	#将当前状态设置为新的状态
	current_state = new_state

四、修改完善受伤组件HealthComponent代码

切换到受伤组件HealthComponent代码状态,在damage函数中将死亡代码进行如下修改:

if health<=0:  #如果当前血量小于0,调用状态机的改变状态函数,敌人进入死亡状态
		get_parent().get_node("StateMachine")._charage_currentstate("Death",attack)
	else:#如果血量不等于0,调用状态机的改变状态函数,敌人进入被攻击状态
		get_parent().get_node("StateMachine")._charage_currentstate("Hurt",attack)

这时我们的受伤组件代码如下:

extends Node2D
class_name HealthComponent  #类名
@onready var health_bar = $HealthBar  #获取血量显示节点
@onready var back_bar = $HealthBar/BackBar #获取血量显示背景节点
@onready var charge_health = $ChargeHealth  #获取掉血量显示节点
@onready var animation_player = $AnimationPlayer  #获取掉血动画播放器

@export var MAX_HEALTH:=100.0  #定义最大血量
var health:float  #定义当前血量

func _ready():
	health = MAX_HEALTH  #初始化时,当前血量等于最大血量
	health_bar.value = health  #将血量显示节点设置为当前血量   黄色血条
	back_bar.value = health  #将血量显示背景节点设置为当前血量  红色血条
	
### 受伤函数
func damage(attack:Attack):
	charge_health.text = str("-") + str(attack.attack_damage)  #将当前掉血量赋值给掉血量显示节点
	health -=attack.attack_damage  #计算当前血量
	health_bar.value = health  #更新血量条
	var tween = get_tree().create_tween()  #创建create_tween类
	tween.tween_property(back_bar,"value",health,0.5)	#将血量显示背景血量条调整到当前血量,0.5秒的一个减血过程
	animation_player.play("damage") #播放掉血量动画
	if health<=0:  #如果当前血量小于0,调用状态机的改变状态函数,敌人进入死亡状态
		get_parent().get_node("StateMachine")._charage_currentstate("Death",attack)
	else:#如果血量不等于0,调用状态机的改变状态函数,敌人进入被攻击状态
		get_parent().get_node("StateMachine")._charage_currentstate("Hurt",attack)

这样就完成了本节的内容,来看一下效果:
请添加图片描述

  • 6
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
在 Godot 中制作类似于小地图的效果可以使用视口(Viewport)和纹理(Texture)。 首先,创建一个新的 2D 场景。在这个场景中,创建一个视口点,并将其命名为“MiniMap”。设置视口的宽度和高度,使其适合你想要的小地图大小。接下来,在视口点下创建一个新点,并将其命名为“TextureRect”。将纹理设置为视口的渲染纹理(Viewport texture),这样就可以在纹理矩形(TextureRect)中显示小地图了。 为了使小地图显示玩家当前位置,需要在场景中创建一个表示玩家的点。将这个点添加到视口中,然后将其缩放到适当的大小,使其在小地图上能够清晰地看到。在每一帧更新时,将玩家点的位置转换为在小地图上的坐标,并将纹理矩形的视口偏移设置为该坐标。 这里是一个简单的 GDScript 示例代码: ``` extends Node2D var viewport : Viewport var texture_rect : TextureRect var player : Node2D func _ready(): viewport = get_node("MiniMap/Viewport") texture_rect = viewport.get_node("TextureRect") player = get_node("Player") func _process(delta): var player_pos = viewport.get_camera().project_position(player.global_position) texture_rect.set_anchors_and_margins_preset(Control.PRESET_RIGHT_BOTTOM, Control.PRESET_MODE_MINSIZE, Vector2(10, 10)) texture_rect.set_position(player_pos) ``` 请注意,此代码仅提供了一个简单的示例,以帮助你开始制作小地图。你需要自己根据自己的需要进行修改和调整。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

游戏自学

生活不易,打赏随意

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值