1
1
mirror of https://github.com/Rundll86/Dog-Lynx-And-HCN.git synced 2026-05-28 06:51:54 +08:00

feat(角色): 为熊角色添加阶段转换功能

添加阶段转换系统,当熊的生命值低于50%时进入第二阶段
- 添加mask精灵用于阶段变化视觉效果
- 实现enterStage和exitStage方法处理阶段转换
- 添加stageAnimator处理转换动画
- 调整攻击7的音效播放位置
This commit is contained in:
2025-09-14 13:35:02 +08:00
parent 997d6d1fe8
commit 054baa4a71
4 changed files with 212 additions and 4 deletions
+124 -2
View File
@@ -1,4 +1,4 @@
[gd_scene load_steps=36 format=3 uid="uid://cvogxi7mktumf"]
[gd_scene load_steps=40 format=3 uid="uid://cvogxi7mktumf"]
[ext_resource type="Script" path="res://scripts/Statemachine/EntityBase.gd" id="1_mvol6"]
[ext_resource type="Texture2D" uid="uid://dwwpkn4q07ja2" path="res://icon.svg" id="2_7lpu0"]
@@ -169,6 +169,123 @@ _data = {
"hurt": SubResource("Animation_wl2we")
}
[sub_resource type="Animation" id="Animation_n7kxn"]
length = 0.001
tracks/0/type = "bezier"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath(".:scale:x")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"handle_modes": PackedInt32Array(0),
"points": PackedFloat32Array(1, -0.25, 0, 0.25, 0),
"times": PackedFloat32Array(0)
}
tracks/1/type = "bezier"
tracks/1/imported = false
tracks/1/enabled = true
tracks/1/path = NodePath(".:scale:y")
tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/keys = {
"handle_modes": PackedInt32Array(0),
"points": PackedFloat32Array(1, -0.25, 0, 0.25, 0),
"times": PackedFloat32Array(0)
}
tracks/2/type = "bezier"
tracks/2/imported = false
tracks/2/enabled = true
tracks/2/path = NodePath(".:modulate:a")
tracks/2/interp = 1
tracks/2/loop_wrap = true
tracks/2/keys = {
"handle_modes": PackedInt32Array(0),
"points": PackedFloat32Array(1, -0.25, 0, 0.25, 0),
"times": PackedFloat32Array(0)
}
[sub_resource type="Animation" id="Animation_lg0kn"]
resource_name = "exit"
step = 0.1
tracks/0/type = "bezier"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath(".:scale:x")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"handle_modes": PackedInt32Array(0, 0),
"points": PackedFloat32Array(2, -0.25, 0, 0.25, 0, 1, -0.25, 0, 0.25, 0),
"times": PackedFloat32Array(0, 1)
}
tracks/1/type = "bezier"
tracks/1/imported = false
tracks/1/enabled = true
tracks/1/path = NodePath(".:scale:y")
tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/keys = {
"handle_modes": PackedInt32Array(0, 0),
"points": PackedFloat32Array(0, -0.25, 0, 0.25, 0, 1, -0.25, 0, 0.25, 0),
"times": PackedFloat32Array(0, 1)
}
tracks/2/type = "bezier"
tracks/2/imported = false
tracks/2/enabled = true
tracks/2/path = NodePath(".:modulate:a")
tracks/2/interp = 1
tracks/2/loop_wrap = true
tracks/2/keys = {
"handle_modes": PackedInt32Array(0, 0),
"points": PackedFloat32Array(0, -0.25, 0, 0.25, 0, 1, -0.25, 0, 0.25, 0),
"times": PackedFloat32Array(0, 1)
}
[sub_resource type="Animation" id="Animation_nfj4q"]
resource_name = "exit"
step = 0.1
tracks/0/type = "bezier"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath(".:scale:x")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"handle_modes": PackedInt32Array(0, 0),
"points": PackedFloat32Array(1, -0.25, 0, 0.25, 0, 2, -0.25, 0, 0.25, 0),
"times": PackedFloat32Array(0, 1)
}
tracks/1/type = "bezier"
tracks/1/imported = false
tracks/1/enabled = true
tracks/1/path = NodePath(".:scale:y")
tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/keys = {
"handle_modes": PackedInt32Array(0, 0),
"points": PackedFloat32Array(1, -0.25, 0, 0.25, 0, 0, -0.25, 0, 0.25, 0),
"times": PackedFloat32Array(0, 1)
}
tracks/2/type = "bezier"
tracks/2/imported = false
tracks/2/enabled = true
tracks/2/path = NodePath(".:modulate:a")
tracks/2/interp = 1
tracks/2/loop_wrap = true
tracks/2/keys = {
"handle_modes": PackedInt32Array(0, 0),
"points": PackedFloat32Array(1, -0.25, 0, 0.25, 0, 0, -0.25, 0, 0.25, 0),
"times": PackedFloat32Array(0, 1)
}
[sub_resource type="AnimationLibrary" id="AnimationLibrary_vvkcp"]
_data = {
"RESET": SubResource("Animation_n7kxn"),
"enter": SubResource("Animation_lg0kn"),
"exit": SubResource("Animation_nfj4q")
}
[sub_resource type="Curve" id="Curve_cs3iy"]
_data = [Vector2(0, 1), 0.0, 0.0, 0, 0, Vector2(1, 0), 0.0, 0.0, 0, 0]
point_count = 2
@@ -280,7 +397,6 @@ stream = ExtResource("2_qt0aq")
[node name="texture" type="AnimatedSprite2D" parent="."]
unique_name_in_owner = true
position = Vector2(0, -50)
scale = Vector2(-1, 1)
sprite_frames = SubResource("SpriteFrames_wcqpm")
animation = &"idle"
@@ -309,6 +425,12 @@ libraries = {
"": SubResource("AnimationLibrary_kbmff")
}
[node name="stageAnimator" type="AnimationPlayer" parent="texture"]
unique_name_in_owner = true
libraries = {
"": SubResource("AnimationLibrary_vvkcp")
}
[node name="weapons" type="Node2D" parent="texture"]
unique_name_in_owner = true
+60 -1
View File
@@ -1,4 +1,4 @@
[gd_scene load_steps=26 format=3 uid="uid://m60bwhdpbem8"]
[gd_scene load_steps=30 format=3 uid="uid://m60bwhdpbem8"]
[ext_resource type="PackedScene" uid="uid://cvogxi7mktumf" path="res://components/Abstracts/EntityBase.tscn" id="1_3ves7"]
[ext_resource type="Script" path="res://scripts/Contents/Characters/Bear.gd" id="2_kh2af"]
@@ -34,6 +34,58 @@ animations = [{
[sub_resource type="RectangleShape2D" id="RectangleShape2D_akdh6"]
size = Vector2(335, 344)
[sub_resource type="Animation" id="Animation_sxh2u"]
length = 0.001
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath(".:scale")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [Vector2(-1, 1)]
}
[sub_resource type="Animation" id="Animation_vxanw"]
resource_name = "left"
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath(".:scale")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [Vector2(-1, 1)]
}
[sub_resource type="Animation" id="Animation_nuh11"]
resource_name = "left"
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath(".:scale")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [Vector2(1, 1)]
}
[sub_resource type="AnimationLibrary" id="AnimationLibrary_dwflo"]
_data = {
"RESET": SubResource("Animation_sxh2u"),
"left": SubResource("Animation_vxanw"),
"right": SubResource("Animation_nuh11")
}
[sub_resource type="Curve" id="Curve_u430e"]
_data = [Vector2(0, 0), 0.0, 0.0, 0, 0, Vector2(0.2, 1), 0.0, 0.0, 0, 0, Vector2(1, 0), 0.0, 0.0, 0, 0]
point_count = 3
@@ -125,6 +177,11 @@ visible = false
position = Vector2(-2.5, -1)
shape = SubResource("RectangleShape2D_akdh6")
[node name="animator" parent="texture" index="1"]
libraries = {
"": SubResource("AnimationLibrary_dwflo")
}
[node name="normal" type="Node2D" parent="texture/weapons" index="0"]
[node name="sprintParticle" type="GPUParticles2D" parent="texture" index="6"]
@@ -136,6 +193,8 @@ process_material = SubResource("ParticleProcessMaterial_5v3nj")
texture = ExtResource("13_v2sdb")
[node name="mask" type="Sprite2D" parent="texture" index="7"]
unique_name_in_owner = true
visible = false
material = SubResource("ShaderMaterial_6h05p")
texture = ExtResource("13_v2sdb")
+9 -1
View File
@@ -2,6 +2,7 @@ extends EntityBase
class_name Bear # 攻击方式模仿泰拉瑞亚光之女皇
@onready var sprintParticle: GPUParticles2D = $"%sprintParticle"
@onready var mask: Sprite2D = $"%mask"
func register():
fields[FieldStore.Entity.MAX_HEALTH] = 2000
@@ -15,12 +16,19 @@ func register():
attackCooldownMap[6] = 10000
attackCooldownMap[7] = 9000
sprintMultiplier = 80
healthChanged.connect(
func(newHealth):
setStage(1 if newHealth < fields[FieldStore.Entity.MAX_HEALTH] * 0.5 else 0)
)
func spawn():
texture.play("walk")
func ai():
PresetEntityAI.follow(self, currentFocusedBoss, 400)
for i in len(attackCooldownMap.keys()):
tryAttack(i)
func enterStage(stage):
mask.visible = !!stage
await TickTool.millseconds(2000)
func attack(type):
var weaponPos = findWeaponAnchor("normal")
if type == 0:
@@ -82,11 +90,11 @@ func attack(type):
await TickTool.millseconds(100)
return false
elif type == 7:
playSound("attack7")
var angle = deg_to_rad(70)
for j in 4:
var initAngle = randf_range(0, 360)
if !is_instance_valid(currentFocusedBoss): return false
playSound("attack7")
for i in 16:
for bullet in BulletBase.generate(preload("res://components/Bullets/BossAttack/Bear/LightGun.tscn"), self, currentFocusedBoss.position, 0):
bullet.rotation_degrees += initAngle
+19
View File
@@ -78,6 +78,7 @@ var inventoryMax = {
@onready var hurtbox: Area2D = $"%hurtbox"
@onready var sounds: Node2D = $"%sounds"
@onready var hurtAnimator: AnimationPlayer = $"%hurtAnimator"
@onready var stageAnimator: AnimationPlayer = $"%stageAnimator"
@onready var damageAnchor: Node2D = $"%damageAnchor"
@onready var trailParticle: GPUParticles2D = $"%trailParticle"
@onready var weaponStore = $"%weaponStore"
@@ -94,6 +95,7 @@ var currentFocusedBoss: EntityBase = null
var charginup: bool = false
var weapons: Array[Weapon] = []
var canRunAi: bool = true
var currentStage: int = 0
func _ready():
register()
@@ -164,6 +166,19 @@ func _physics_process(_delta: float) -> void:
trailParticle.emitting = trailing
# 通用方法
func setStage(stage: int):
if currentStage == stage:
return
canRunAi = false
var oldStage = currentStage
currentStage = stage
stageAnimator.play("exit")
await stageAnimator.animation_finished
await exitStage(oldStage)
await enterStage(stage)
stageAnimator.play("enter")
await stageAnimator.animation_finished
canRunAi = true
func applyLevel():
fields[FieldStore.Entity.MAX_HEALTH] *= (1 + GameRule.entityHealthIncreasePerWave * (GameRule.difficulty + 1)) ** level
fields[FieldStore.Entity.DAMAGE_MULTIPILER] *= (1 + GameRule.entityDamageIncreasePerWave * (GameRule.difficulty + 1)) ** level
@@ -342,6 +357,10 @@ func register():
pass
func spawn():
pass
func exitStage(_stage: int):
pass
func enterStage(_stage: int):
pass
static func generate(
entity: PackedScene,