【Godot4自学手册】第十八节主人公的血量显示

news2024/11/15 23:30:57

今天,我主要学习主人公的血量显示,在屏幕的左上角,会显示主人公的头像,后面会显示血量进度条,当主人公受到伤害时,血量会实时变动,显示绿色血条减少,后是红色血量逐渐减少到正确位置,效果如下:
请添加图片描述

一、修改BUG

再开始今天的学习之前,修改以前的一些Bug,在敌人攻击和受伤,主人公的攻击和受伤学习中又许多Bug,主要是因为我把需要执行一次的状态代码和需要执行一段时间的代码全都写到_physics_process函数的原因,所以我将需要执行一次的代码加到state状态的set(v):函数内执行一次;把需要多次执行的代码加到了_physics_process函数内。

1.Player代码修改如下:
extends CharacterBody2D
@onready var anima_tree=$AnimationTree
@onready var animation_player = $AnimationPlayer
@onready var stats = $Stats
@onready var direcion = Vector2.ZERO
enum {
	IDLE,
	WALK,
	SWORD,
	HURT
}
var state = IDLE:
	set(v):
		state=v
		match  state:
			IDLE:
				idle_state()
			WALK:
				walk_state()
			SWORD:
				sword_state()
			HURT:
				hurt_state()
		
const SPEED = 120.0
var enemyPositon
var knockback=200


func _ready():
	Globals.last_Player = position
	

func _physics_process(delta):	
	direcion = Vector2.ZERO
	direcion.x = Input.get_axis("left", "right")
	direcion.y = Input.get_axis("up", "down")
	if state!=SWORD and state!=HURT:
		if direcion:
			anima_tree.set("parameters/Idle/blend_position",direcion)
			anima_tree.set("parameters/Walk/blend_position",direcion)
			anima_tree.set("parameters/Sword/blend_position",direcion)
			anima_tree.set("parameters/Hurt/blend_position",Vector2(direcion.x*-1,direcion.y))
			state = WALK
		else:
			velocity=Vector2.ZERO
			state=IDLE
	if Input.is_action_just_pressed("sword"):
		velocity= Vector2.ZERO
		state = SWORD
	Globals.last_Player = position
	move_and_slide()	

func idle_state():#待机状态
	anima_tree["parameters/playback"].travel("Idle")
func walk_state():#行走状态
	anima_tree["parameters/playback"].travel("Walk")
	velocity = direcion * SPEED	

func sword_state():#进攻状态
	anima_tree["parameters/playback"].travel("Sword")
	await anima_tree.animation_finished	
	state=IDLE
	
func hurt_state():#受伤状态
	var dir = enemyPositon.direction_to(global_position).normalized()
	anima_tree["parameters/playback"].travel("Hurt")
	if dir.x>0:
		velocity.x +=knockback
	else :
		velocity.x -=knockback
	await anima_tree.animation_finished
	stats.health -=10	
	state=WALK
	
func _on_hurt_box_area_entered(area):
	enemyPositon = area.owner.global_position
	state=HURT	
2.Enemy代码修改如下:
extends CharacterBody2D
enum {
	IDLE,
	CHASE,
	ATTACK,
	HURT,
	DEATH
}
const SPEED = 1.0
var hitCount=2
@onready var anima_s = $AnimaS
@onready var anima_p = $AnimaP
@onready var hit_box = $CheckBox/HitBox
@onready var trace_collsion = $CheckBox/TraceArea/Trace_Collsion

var knockback=300
var hurtdirecion

var state = IDLE:
	set(v):
		state = v
		match state:
			IDLE:
				idle_state()
			CHASE:
				chase_state()
			ATTACK:
				attack_state()
			HURT:
				hurt_state()
			DEATH:
				death_state()

func _physics_process(delta):	
	var direction =  Globals.last_Player-global_position
	if direction.x<0:
		anima_s.flip_h=true
		hit_box.scale.x =-1
	else:
		anima_s.flip_h = false
		hit_box.scale.x=1
	if state==CHASE:		
		global_position = global_position.move_toward(Globals.last_Player,SPEED)
	
	move_and_slide()
	
	
func idle_state():#待机状态
	anima_p.play("Idle")

func chase_state():#跟踪状态
	anima_p.play("Run")

func attack_state():#攻击状态
	anima_p.play("Attack")	
	await  anima_p.animation_finished
	state = CHASE
	
	
	
	
func hurt_state():	#受伤状态
	var dir = hurtdirecion.direction_to(global_position).normalized()
	if abs(dir.x)>abs(dir.y):
		if dir.x<0:
			velocity.x =-knockback
		else :
			velocity.x =knockback
	else:
		if dir.y<0:
			velocity.y =-knockback
		else :
			velocity.y =knockback
	anima_p.play("TakeHit")
	await  anima_p.animation_finished	
	velocity = Vector2.ZERO
	state=CHASE


func death_state():	
	pass

func _on_trace_area_body_entered(body):
	if body.name=="Player" and state!=HURT and state!=ATTACK:
			state = CHASE


func _on_trace_area_body_exited(body):	
		if body.name=="Player" and state!=HURT and state!=ATTACK:
			state = IDLE


func _on_hit_area_body_entered(body):#进入攻击范围
	if body.name=="Player" :
		state = ATTACK


func _on_hurt_box_area_entered(area):	#进入受伤范围
	hurtdirecion = area.owner.global_position
	state = HURT

二、新建血量显示场景

1.新建场景。

单击新建场景按钮,选择其它节点,在创建Node对话框中,选择HBoxContainer节点,单击创建按钮,创建新的场景,如下:
请添加图片描述

将新场景保存到Scenes文件夹下,命名为:StatusUI。
将根节点重新命名为StatusPanel,检查器中将Transform->Size重置为0,0。

2.添加节点

首先为根节点添加PanelContainer子节点,再为PanelContainer添加TextureRect子节点,显示头像。为根节点添加TextureProgressBar子节点,显示血量,最终效果如下:
请添加图片描述

最后将PanelContainer命名为HeadBox;将TextureRect命名为Head;将TextureProgressBar命名为HeadthBar。

3.设置人物头像背景框

将HeadBox检查器中Theme Overrides->style->Panel属性设置为StyleBoxTextture。并将我们事先做好的背景边框图片结合托人到Texture内。
请添加图片描述

单击Sub-Region属性,展开界面选择编辑区域,在区域编辑框中选择像素吸附,画出图像背景框,然后选择关闭按钮,如下:
请添加图片描述

检查器中Custom Minimum Size属性设置为23,23,表示图像框的大小为23.如下:
请添加图片描述

检查器中Theme Overrides->Styles->Content Margins中,Left,Top,Right,Bottom均设置为3,表示填充边框为3,如下:
请添加图片描述

4.设置头像

切换到Head节点,在检查器中Texture选择AtlasTexture图集纹理,将主人公的Player.png图片拖入到Atlas属性,如下:
请添加图片描述

在检查器中选择Region,单击下面的编辑区域按钮,在弹出的区域编辑框中选择像素吸附,选取主人公正面头像,然后关闭。
请添加图片描述

head检查器中Stretch Mode属性选择 Keep Aspect Centered,表示图像保持比例缩放,居中。如下:
请添加图片描述

最后的头像效果如下:
请添加图片描述

5.设置血量显示条

切换到HealthBar节点,在检查器中Textures展开属性,在Progress选择AtlasTexture,将HUD.png拖入到Atlas中,如下:
请添加图片描述

在Region属性下单击编辑按钮,在区域编辑框中选择自动剪裁,然后选择绿色图片,最后单击关闭。
请添加图片描述

然后突出Progress属性,在HealthBar检查器中Range属性中Max Value设置为1,step设置为0,Value设置为1,如下所示:
请添加图片描述

同样方法设置Texture下的Over,图片选择黑色背景边框,然后设置Progress Offset设置为1,1,这样保证绿色图片在黑色背景边框中间,设置如下所示:
请添加图片描述

在HealthBar的检查器中Control->Layout->Container Sizing->Vertical属性选择居中收缩。如下:
请添加图片描述

最后显示效果如下:
请添加图片描述

6.设置红色血量显示调

复制HealthBar命名为HealthBarRead,并将其设为HealthBar的子节点,如下所示:
请添加图片描述

在其检查器中Textures属性重置Over属性,Progress属性选择唯一化,然后从新编辑区域,选择红色血条,效果如下:
请添加图片描述

然后在编辑器上方控制节点锚点和偏移值的预设中选择整个矩形,这样红色血量和绿色血量最终重合,如下:
请添加图片描述

然后在检查器中找到Visibility->Show Behind Parent并启用,这样绿色血量将会在红色血量的上方。
请添加图片描述

通过以上步骤所有节点都已布置完成,先忙开始编写代码。

三、编写代码

1.抽象状态类。

无论是敌人还是主人公都有一些相同状态,如血量,我把这些相同的状态定义成一个类,需要的时候继承就好了。首先切换到脚本,单击文件新建脚本,新建Class文件夹,将新建的脚步保存到Class文件夹内,命名为Status。编写如下脚本:

class_name Stats
extends Node
signal  health_changed

@export var max_health: int = 100
@onready var health: int = max_health:
	set(v):
		v = clampi(v, 0, max_health)
		if health == v:
			return
		health = v
		health_changed.emit()  #发出信号

第一行代码主要把该脚本命名为Stats类,这样的目的是,把所有跟敌人或者主人公有关状态的量抽象一个类,复用。
第二行代码说明该类继承了Node节点,Node节点的方法和属性都可以使用。
第三行定义了一个信号,表示当血量发生变化时,出现该信号。
第四行定义了最大血量,默认是100,@export表示该变量可以在编辑器中看到和修改。
第五行定义了当前血量,@onready表示该变量会在Node节点就绪状态时赋值,默认赋值为最大血量。
第六行表示当给health变量赋值时,发生。
第七行表示给health复制时,取值范围为0到max_health,当超过这个范围会自动变换到这个范围。
第八行、第九行表示如果复完值就跳出该方法。
第十行表示把用户输入的值付给health。
第十一行表示复完值后发出血量变化信号。

2.StatusUI场景代码

切换到StatusUI场景,选择根节点,单击创建脚本按钮,将代码保存到Scripts文件夹内,命名为StatusUI。如下:
请添加图片描述

编写如下代码:

extends HBoxContainer
@export var stats:Stats
@onready var health_bar = $HealthBar
@onready var health_bar_red = $HealthBar/HealthBarRED


func _ready():
	stats.health_changed.connect(update_health)  #血量变化信号连接到血量更新UI
	update_health()

func update_health()->void:
	var percentage :=stats.health/ float(stats.max_health)
	health_bar.value = percentage
	create_tween().tween_property(health_bar_red,"value",percentage,.3)

这段代码中第二行定义了一个状态变量,继承Stats。
第三至四行代码表示获取绿色血量和红色血量节点。
_ready函数中的stats.health_changed.connect(update_health)代码表示血量变化信号发生后会调用update_health函数。
update_health自定义函数表示血量发生变化后血量显示进度条适时变化。该函数前两行代码对绿色血量进行调整,后一行代码表示红色血量变化会在0.3秒内完成,并显示间补动画,就是红色血量有个减少过程,绿色血量直接发生变化。
这里面有个引擎自带函数tween_propert方法解释一下。create_tween()表示创建一个间补动画,tween_property方法第一个参数表示要发生变化的对象,第二个参数表示该对象的哪个属性发生变化,第三个参数表示该属性到达的最后状态,第四个参数表示达到最后状态需要的持续时间。

四、给主人公添加血量显示

1.添加状态类

切换到Player场景,选择根节点,然后点击添加节点按钮,选择Stats,我们自己编写的类,如下:
请添加图片描述

单击Stats节点,在其检查器中,我们会看到我们编写该类时,暴漏的max_health的变量,我们可以在检查器中进行修改。
请添加图片描述

2.添加StatusUI场景到Player场景

切换到Player场景,选择根节点,单击实例化子场景按钮,在实例化子场景中选择StatusUI点击打开,就把StatusUI场景加入到Player场景内了,重名为PlayerStatus,然后适当调整位置。
请添加图片描述

在PlayerStatus节点单击右键选择重设父节点为新节点,在对话框中选择CanvasLayer节点,这样的目的是把主人公的血条显示固定在一个位置。操作如下:
请添加图片描述

调整PlayerStatus节点位置到屏幕的左上角,如下:
请添加图片描述

在检查器中将Stats选择Stats,操作如下:
请添加图片描述

ok,这样就应该好了,看一下最终效果:
请添加图片描述

注意观看左上角的血量变化。今天就到这,下节见,同学们。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1479982.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

进阶了解C++(4)——多态

在上篇文章中&#xff0c;简单的介绍了多态中的概念以及其相关原理。本文将针对多态中其他的概念进一步进行介绍&#xff0c;并且更加深入的介绍关于多态的相关原理。 目录 1. 抽象类&#xff1a; 2. 再谈虚表&#xff1a; 3. 多继承中的虚函数表&#xff1a; 1. 抽象类&am…

(每日持续更新)jdk api之PipedInputStream基础、应用、实战

博主18年的互联网软件开发经验&#xff0c;从一名程序员小白逐步成为了一名架构师&#xff0c;我想通过平台将经验分享给大家&#xff0c;因此博主每天会在各个大牛网站点赞量超高的博客等寻找该技术栈的资料结合自己的经验&#xff0c;晚上进行用心精简、整理、总结、定稿&…

Web APIs知识点讲解(阶段二)

DOM-事件基础 一.事件 1.事件 目标&#xff1a;能够给 DOM元素添加事件监听 事件:事件是在编程时系统内发生的动作或者发生的事情&#xff0c;比如用户在网页上单击一个按钮 事件监听:就是让程序检测是否有事件产生&#xff0c;一旦有事件触发&#xff0c;就立即调用一个函…

【BBuf的CUDA笔记】十四,OpenAI Triton入门笔记三 FusedAttention

0x0. 前言 继续Triton的学习&#xff0c;这次来到 https://triton-lang.org/main/getting-started/tutorials/06-fused-attention.html 教程。也就是如何使用Triton来实现FlashAttention V2。对于FlashAttention和FlashAttention V2网上已经有非常多的介绍了&#xff0c;大家如…

华为配置WLAN高密业务示例

配置WLAN高密业务示例 组网图形 图1 配置高密WLAN环境网络部署组网图 业务需求组网需求数据规划配置思路配置注意事项操作步骤配置文件 业务需求 体育场由于需要接入用户数量很大&#xff0c;AP间部署距离较小&#xff0c;因此AP间的干扰较大&#xff0c;可能导致用户上网网…

ShardingJdbc实战-ShardingJdbc配置及读写分离

文章目录 一、项目搭建二、测试结果1.访问http://localhost:8085/user/save2.访问http://localhost:8085/user/listuser 一、项目搭建 新建一个Spring Boot工程 引入依赖-sharding、ssm、数据库驱动 <properties><java.version>1.8</java.version><shardi…

linux系统Jenkins工具的node节点配置

Jenkins工具添加节点 node 节点的作用node节点配置 node 节点的作用 分布式构建&#xff1a;通过添加多个节点&#xff0c;可以在多台计算机上并行执行构建任务&#xff0c;从而加快构建速度和提高效率。节点可以是物理计算机、虚拟机、云实例或容器等。扩展计算能力&#xff…

仓储自动化新解:托盘四向穿梭车驶入智能工厂 智能仓储与产线紧密结合

目前&#xff0c;由于对仓库存储量的要求越来越高&#xff0c;拣选、输送以及出入库频率等要求也越来越高&#xff0c;对此&#xff0c;在物流仓储领域&#xff0c;自动化与智能化控制技术得以快速发展&#xff0c;货架穿梭车在自动库领域的应用越来越广泛。现阶段&#xff0c;…

皇冠测评:网络电视盒子哪个品牌好?电视盒子排行榜

欢迎各位来到我们的测评频道&#xff0c;本期我们要分享的产品是电视盒子&#xff0c;因很多网友留言不知道网络电视盒子哪个品牌好&#xff0c;我们通过为期一个月的测评后整理了电视盒子排行榜&#xff0c;想买电视盒子的可以看看下面这五款产品&#xff0c;它们各方面表现非…

MySQL安装部署-NDB版

NDB&#xff08;Network Database&#xff09;是网络数据库&#xff0c;其架构是由MySQL Server集群以及NDB存储引擎集群组成&#xff0c;是存算分离架构&#xff0c;MySQL Server主要是负责计算、NDB存储引擎主要负责数据存储&#xff0c;其特点是支持高可用、支持无单点故障、…

SOCKS55代理与Http代理有何区别?如何选择?

在使用IPFoxy全球代理时&#xff0c;选择 SOCKS55代理还是HTTP代理&#xff1f;IPFoxy代理可以SOCKS55、Http协议自主切换&#xff0c;但要怎么选择&#xff1f;为解决这个问题&#xff0c;得充分了解两种代理的工作原理和配置情况。 在这篇文章中&#xff0c;我们会简要介绍 …

输入一个字符串,将其中的数字字符移动到非数字字符之后

输入一个字符串&#xff0c;将其中的数字字符移动到非数字字符之后&#xff0c;并保持数字字符贺非数字字符输入时的顺序。 代码&#xff1a; #include <cstdio> #include <queue> using namespace std; int main() {char str[200];fgets(str, 200, stdin);//读入…

10分钟SkyWalking与SpringBoot融合并整合到Linux中

1.依赖配置 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><version>2.2.0.RELEASE</version></dependency><dependency><groupId>org.springframe…

经销商文件分发 怎样兼顾安全和效率?

经销商文件分发是指将文件、资料、产品信息等从制造商或经销商传递给经销商的过程。这一过程对于确保经销商能够获取最新的产品信息、销售策略、市场活动资料等至关重要。 想要管理众多经销商合作伙伴之间的文件传输并提高效率&#xff0c;可以采取以下措施&#xff1a; 1、建…

2024 2.24~3.1 周报

目录 一、本周计划 二、DD-Net整体介绍 三、DDNet的体系结构 四、损失函数 五、课程学习 六、实验环境 A. SEG盐数据集 B. OpenFWI数据集 C. 训练和前沿设置&#xff08;未完&#xff09; 七、结论 八、跑代码——对比试验结果&#xff08;CBAM&#xff09; 1. In…

Redis 之五:Redis 的主从复制

概念 主从复制&#xff0c;是指将一台 Redis 服务器的数据&#xff0c;复制到其他的Redis服务器。前者称为主节点(master)&#xff0c;后者称为从节点(slave)&#xff1b;数据的复制是单向的&#xff0c;只能由主节点到从节点。 默认情况下&#xff0c;每台Redis服务器都是主节…

Spark Shuffle Tracking 原理分析

Shuffle Tracking Shuffle Tracking 是 Spark 在没有 ESS(External Shuffle Service)情况&#xff0c;并且开启 Dynamic Allocation 的重要功能。如在 K8S 上运行 spark 没有 ESS。本文档所有的前提都是基于以上条件的。 如果开启了 ESS&#xff0c;那么 Executor 计算完后&a…

C#用户界面,UI设置密码隐藏显示

两种方法&#xff0c;第一种&#xff0c;在文本框属性设置UseSystemPasswordChar为True。 这里默认是以黑点隐藏显示。 第二种&#xff0c;在文本框属性设置隐藏显示的符号&#xff0c;这里设置为星号*。

探索Android多屏互动技术:构建无缝交互体验

探索Android多屏互动技术&#xff1a;构建无缝交互体验 1. 简介 在当前移动设备和智能家居应用中&#xff0c;多屏互动技术已经成为一个备受关注的话题。随着移动设备&#xff08;如智能手机、平板电脑&#xff09;和智能家居设备的普及&#xff0c;用户对于多屏协同工作、娱…

基于springboot实现流浪动物救助网站系统项目【项目源码+论文说明】

基于springboot实现流浪动物救助网站系统演示 摘要 然而随着生活的加快&#xff0c;也使很多潜在的危险日益突显出来&#xff0c;比如在各种地方会发现很多无家可归的、伤痕累累的、可怜兮兮的动物&#xff0c;当碰到这种情况&#xff0c;是否会立马伸出双手去帮助、救助它们&…